File Coverage

blib/lib/CAD/Drawing/IO.pm
Criterion Covered Total %
statement 21 42 50.0
branch 0 6 0.0
condition n/a
subroutine 7 10 70.0
pod n/a
total 28 58 48.2


line stmt bran cond sub pod time code
1             package CAD::Drawing::IO;
2             our $VERSION = '0.26';
3              
4             #use CAD::Drawing;
5 3     3   16 use CAD::Drawing::Defined;
  3         6  
  3         568  
6              
7 3     3   3564 use Storable;
  3         12082  
  3         265  
8              
9             # value set within BEGIN block:
10             my $plgindbg = $CAD::Drawing::IO::plgindbg;
11              
12              
13 3     3   26 use warnings;
  3         8  
  3         100  
14 3     3   17 use strict;
  3         6  
  3         100  
15 3     3   16 use Carp;
  3         6  
  3         1122  
16             ########################################################################
17             =pod
18              
19             =head1 NAME
20              
21             CAD::Drawing::IO - I/O methods for the CAD::Drawing module
22              
23             =head1 Description
24              
25             This module provides the load() and save() functions for CAD::Drawing
26             and provides a point of flow-control to deal with the inheritance and
27             other trickiness of having multiple formats handled through a single
28             module.
29              
30             =head1 AUTHOR
31              
32             Eric L. Wilhelm
33              
34             http://scratchcomputing.com
35              
36             =head1 COPYRIGHT
37              
38             This module is copyright (C) 2004-2006 by Eric L. Wilhelm. Portions
39             copyright (C) 2003 by Eric L. Wilhelm and A. Zahner Co.
40              
41             =head1 LICENSE
42              
43             This module is distributed under the same terms as Perl. See the Perl
44             source package for details.
45              
46             You may use this software under one of the following licenses:
47              
48             (1) GNU General Public License
49             (found at http://www.gnu.org/copyleft/gpl.html)
50             (2) Artistic License
51             (found at http://www.perl.com/pub/language/misc/Artistic.html)
52              
53             =head1 NO WARRANTY
54              
55             This software is distributed with ABSOLUTELY NO WARRANTY. The author,
56             his former employer, and any other contributors will in no way be held
57             liable for any loss or damages resulting from its use.
58              
59             =head1 Modifications
60              
61             The source code of this module is made freely available and
62             distributable under the GPL or Artistic License. Modifications to and
63             use of this software must adhere to one of these licenses. Changes to
64             the code should be noted as such and this notification (as well as the
65             above copyright information) must remain intact on all copies of the
66             code.
67              
68             Additionally, while the author is actively developing this code,
69             notification of any intended changes or extensions would be most helpful
70             in avoiding repeated work for all parties involved. Please contact the
71             author with any such development plans.
72              
73             =head1 SEE ALSO
74              
75             =over
76              
77             =item L
78              
79             The frontend.
80              
81             =back
82              
83             =head2 Builtin Backends
84              
85             The following modules are included in the main distribution.
86              
87             =over
88              
89             =item L
90              
91             =item L
92              
93             =item L
94              
95             =item L
96              
97             =back
98              
99             =head2 External Backends
100              
101             =over
102              
103             =item L
104              
105             DWG/DXF handling using the OpenDWG toolkit.
106              
107             =item L
108              
109             Postscript output.
110              
111             =item L
112              
113             Image::Magick based output.
114              
115             =item L
116              
117             PostgreSQL connected drawing database.
118              
119             =item L
120              
121             Tk::WorldCanvas popup viewer -- not exactly an input/output backend, but
122             it uses much of the same facility because it is primarily just output to
123             a display.
124              
125             =back
126              
127             =cut
128             ########################################################################
129              
130             =head1 front-end Input and output methods
131              
132             The functions load() and save() are responsible for determining the
133             filetype (with forced types available via $options{type}.) These then
134             call the appropriate ::load() or ::save() functions.
135              
136             See the Plug-In Architecture section for details on how to add support
137             for additional filetypes.
138              
139             Beginning with version 0.26, a string-based type specification is
140             available by using $filename = "$type:filename". While this prevents
141             you from saving files with colons in the names, an explicit type passed
142             in the options will allow it. This gives the added bonus that your
143             program's users may directly control the output type simply by giving a
144             : argument on the command line (if that is where you get
145             your filenames.)
146              
147             =head2 save
148              
149             Saves a file to disk. See the save functions in this file and the
150             other filetype functions in the CAD::Drawing::IO:: modules.
151              
152             See each save function for available options for that type.
153              
154             While you may call the save function directly (if you include the
155             module), it is recommended that you stick to the single point of
156             interface provided here so that your code does not become overwhelmingly
157             infected with hard-coded filetypes.
158              
159             Note that this method also implements forking. If $options{forkokay} is
160             true, save() will return the pid of the child process to the parent
161             process and setup the child to exit after saving (with currently no way
162             for the child to give a return value to the parent (but (-e $filename)
163             might work for you).)
164              
165             $drw->save($filename, \%options);
166              
167             =cut
168             sub save {
169 0     0     my $self = shift;
170 0           my ( $filename, $opt) = @_;
171 0           my $type = $$opt{type};
172 0 0         if($$opt{forkokay}) {
173 0           $SIG{CHLD} = 'IGNORE';
174 0           my $kidpid;
175 0 0         if($kidpid = fork) {
176 0           return($kidpid);
177             }
178 0 0         defined($kidpid) or die "cannot fork $!\n";
179 0           $$opt{forkokay} = 0;
180 0           $self->diskaction("save", $filename, $type, $opt);
181 0           exit;
182             }
183 0           return($self->diskaction("save", $filename, $type, $opt));
184             } # end subroutine save definition
185             ########################################################################
186              
187             =head2 load
188              
189             Loads a file from disk. See the load functions in this file and
190             the other filetype functions in the CAD::Drawing::IO:: modules.
191              
192             See each load function for available options for that type.
193              
194             In most cases %options may contain the selection methods available via
195             the CAD::Drawing::check_select() function.
196              
197             While you may call the load function directly (if you include the
198             module), it is recommended that you stick to the single point of
199             interface provided here.
200              
201             $drw->load($filename, \%options);
202              
203             =cut
204             sub load {
205 0     0     my $self = shift;
206 0           my ($filename, $opt) = @_;
207 0           my $type = $$opt{type};
208 0           return($self->diskaction("load", $filename, $type, $opt));
209             } # end subroutine load definition
210             ########################################################################
211              
212             =head2 can_load
213              
214             Returns true if the plugins think they can load this filename (no
215             test-loading is done, only verification of the type.)
216              
217             $drw->can_load($filename);
218              
219             =cut
220             sub can_load {
221 0     0     my $self = shift;
222 0           my ($filename, $opt) = @_;
223 0           my $type = $$opt{type};
224 0           return($self->diskaction("check", $filename, $type));
225             } # end subroutine can_load definition
226             ########################################################################
227              
228             =head1 Plug-In Architecture
229              
230             Plug-ins are modules which are under the CAD::Drawing::IO::*
231             namespace. This namespace is searched at compile time, and any modules
232             found are require()d inside of an eval() block (see BEGIN.) Compile
233             failure in any one of these modules will be printed to STDERR, but will
234             not halt the running program.
235              
236             Each plug-in is responsible for declaring one or all of the following
237             variables:
238              
239             our $can_save_type = "type";
240             our $can_load_type = "type (or another type)";
241             our $is_inherited = 1; # or 0 (or undef())
242              
243             If a package claims to be able to load or save a type, then it must
244             contain the functions load() or save() (respectively.) Package which
245             declare $is_inherited as a true value will become methods of the
246             CAD::Drawing class (though their load() and save() functions will not
247             be visible due to their location in the inheritance tree.)
248              
249             =head2 BEGIN
250              
251             The BEGIN block implements the module path searching (looking only in
252             directories of @INC which contain a "CAD/Drawing/IO/" directory.)
253              
254             For each plug-in which is found, code references are saved for later
255             use by the diskaction() function.
256              
257             =cut
258             BEGIN {
259 3     3   18 use File::Find;
  3         6  
  3         374  
260             my %found;
261             our %handlers;
262             our %check_type;
263             our @ISA;
264             our $plgindbg = 0;
265 3     3   15 use strict;
  3         6  
  3         4252  
266             foreach my $inc (@INC) {
267             # (if it starts with CAD/Drawing/IO/, then we are good)
268             my $look = "$inc/CAD/Drawing/IO/";
269             (-d "$look") || next;
270             # print "looking in $look\n";
271              
272             # I suppose deeper nested namespaces are allowed
273             find(sub {
274             ($_ =~ m/\.pm$/) or return;
275             my $mod = $File::Find::name;
276             $mod =~ s#^$inc/+##;
277             $mod =~ s#/+#::#g;
278             $mod =~ s/\.pm//;
279             $found{$mod} and return;
280             $found{$mod}++;
281             # print "$File::Find::name\n";
282             # print "mod: $mod\n";
283             }, $look );
284             }
285             foreach my $mod (keys(%found)) {
286             # see if they are usable
287             $plgindbg && print "checking $mod\n";
288             if(eval("require " . $mod)) {
289             my $useful;
290             foreach my $action qw(load save) {
291             my $type = eval(
292             '$' . $mod . '::can_' . $action . '_type'
293             );
294             $type or next;
295             $handlers{$action}{$type} and next;
296             $useful++;
297             $handlers{$action}{$type} = $mod . '::' . $action;
298             $check_type{$type} = $mod . '::check_type';
299             $plgindbg and
300             print "$action ($type) claimed by $mod\n";
301             $plgindbg and
302             print "(found $handlers{$action}{$type})\n";
303             }
304             if(eval('$' . $mod . '::is_inherited')) {
305             push(@ISA, $mod);
306             $useful++;
307             }
308             $plgindbg and ($useful and print "using $mod\n");
309             }
310             else {
311             $@ and warn("warning:\n$@ for $mod\n\n");
312             }
313             } # end foreach $mod
314             } # end BEGIN
315             ########################################################################
316              
317             =head2 diskaction
318              
319             This function is for internal use, intended to consolidate the type
320             selection and calling of load/save methods.
321              
322             $drw->diskaction("load|save", $filename, $type, \%options);
323              
324             For each plug-in package which was located in the BEGIN block, the
325             function ::check_type() will be called, and must return a true
326             value for the package to be used for $action.
327              
328             =cut
329             sub diskaction {
330             my $self = shift;
331             my ($action, $filename, $type, $opt) = @_;
332             my %opts;
333             (ref($opt) eq "HASH") && (%opts = %$opt);
334             ($action =~ m/save|load|check/) or
335             croak("Cannot access disk with action: $action\n");
336             $filename or
337             croak("Cannot $action without filename\n");
338              
339             # Hopefully this is fixed: if type is passed explicitly, we were
340             # still strolling through the list to determine which module to
341             # call. New strategy is to try using the explicit type first.
342            
343             ####################################################################
344             # choose filetype:
345             my %handlers = %CAD::Drawing::IO::handlers;
346             my $og_fn = $filename;
347             unless(defined($type)) {
348             $plgindbg and
349             print "type was undefined, trying split(/:/, \$file)\n";
350             my ($t, $n) = split(/:/, $filename, 2);
351             if(defined($n)) {
352             $plgindbg and print "got type: $t and name $n\n";
353             $filename = $n;
354             $type = $t;
355             }
356             }
357             # now we may have an explicit type (so backends should not be
358             # allowed to claim solely on extension)
359             if(defined($type) and ($action ne "check")) {
360             if(my $call = $handlers{$action}{$type}) {
361             no strict 'refs';
362             $plgindbg and print "quickly trying $call (for $type / $action)\n";
363             return($call->($self, $filename, {%opts, type => $type}));
364             }
365             else {
366             warn("explicit type '$type' bypassed...\n ",
367             "exhaustive checks now");
368             $filename = $og_fn;
369             undef($type);
370             $plgindbg and warn("name now $filename\n");
371             }
372             }
373             my %check = %CAD::Drawing::IO::check_type;
374             my $check_only = ($action eq "check");
375             $check_only and ($action = "load");
376             foreach my $mod (keys(%{$handlers{$action}})) {
377             $plgindbg && print "checking $mod ($check{$mod})\n";
378             no strict 'refs';
379             my $real_type = $check{$mod}($filename, $type);
380             # check must return true
381             $real_type || next;
382             # if we just want to know if it can be loaded, the answer is:
383             $check_only and return(1);
384             # XXX it would be good to have a real_filename here (so we could
385             # do a -e on it when in check_only mode)
386             my $call = $handlers{$action}{$mod};
387             $plgindbg && print "trying $call\n";
388             return($call->($self, $filename, {%opts, type => $real_type}));
389             }
390             # FIXME: # maybe the fallback is a Storable or YAML file?
391             $check_only and return(0);
392             croak("could not $action $filename as type: $type");
393             } # end subroutine diskaction definition
394             ########################################################################
395              
396             =head1 Utility Functions
397              
398             These are simply inherited by the CAD::Drawing module for your direct
399             usage.
400              
401             =head2 outloop
402              
403             Crazy new experimental output method. Each entity supported by the
404             format should have a key to a function in %functions, which is expected
405             to accept the following input data:
406              
407             $functions{$ent_type}->($obj, \%data);
408              
409             The %data hash is passed verbatim to each function.
410              
411             $count = $drw->outloop(\%functions, \%data);
412              
413             In addition to each of the $ent_type keys, functions for the keys
414             'before' and 'after' may also be defined. These (if they are defined)
415             will be called before and after each entity, with the same arguments as
416             the $ent_type functions.
417              
418             =cut
419             sub outloop {
420             my $self = shift;
421             my ($funcs, $data) = @_;
422             my %functions = %$funcs;
423             # we should ignore data here
424             my $count = 0;
425             foreach my $layer (keys(%{$self->{g}})) {
426             foreach my $ent (keys(%{$self->{g}{$layer}})) {
427             if($functions{$ent}) {
428             foreach my $id (keys(%{$self->{g}{$layer}{$ent}})) {
429             my %addr = (
430             "layer" => $layer,
431             "type" => $ent,
432             "id" => $id,
433             );
434             my $obj = $self->getobj(\%addr);
435             $functions{before} && ($functions{before}->($obj, $data));
436             $functions{$ent}->($obj, $data);
437             $functions{after} && ($functions{after}->($obj, $data));
438             $count++;
439             }
440             }
441             else {
442             carp("not supporting type: $ent");
443             }
444            
445             }
446             }
447             return($count);
448             } # end subroutine outloop definition
449             ########################################################################
450              
451             =head2 is_persistent
452              
453             Returns 1 if $filename points to a persistent (directory / db) drawing.
454              
455             $drw->is_persistent($filename);
456              
457             =cut
458             sub is_persistent {
459             my $self = shift;
460             my $filename = shift;
461             # XXX punting here:
462             ($filename =~ m/^split:/) and return(1);
463             # FIXME backends really need to answer this
464             return(0);
465             } # end subroutine is_persistent definition
466             ########################################################################
467              
468              
469             1;