File Coverage

lib/Graphics/Fig.pm
Criterion Covered Total %
statement 344 444 77.4
branch 43 98 43.8
condition 1 9 11.1
subroutine 38 40 95.0
pod 24 25 96.0
total 450 616 73.0


line stmt bran cond sub pod time code
1             #
2             # XFig Drawing Library
3             #
4             # Copyright (c) 2017 D Scott Guthridge
5             #
6             # This program is free software: you can redistribute it and/or modify it under
7             # the terms of the Artistic License as published by the Perl Foundation, either
8             # version 2.0 of the License, or (at your option) any later version.
9             #
10             # This program is distributed in the hope that it will be useful, but WITHOUT
11             # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12             # FOR A PARTICULAR PURPOSE. See the Artistic License for more details.
13             #
14             # You should have received a copy of the Artistic License along with this
15             # program. If not, see .
16             #
17             package Graphics::Fig;
18             our $VERSION = 'v1.0.8';
19              
20 12     12   1338640 use strict;
  12         106  
  12         292  
21 12     12   57 use warnings;
  12         17  
  12         297  
22 12     12   45 use Carp;
  12         14  
  12         757  
23 12     12   87 use File::Temp qw/ tempfile /;
  12         16  
  12         512  
24 12     12   3975 use Graphics::Fig::Color;
  12         26  
  12         378  
25 12     12   4421 use Graphics::Fig::Matrix;
  12         25  
  12         291  
26 12     12   4954 use Graphics::Fig::Parameters;
  12         36  
  12         487  
27 12     12   5719 use Graphics::Fig::Arc;
  12         32  
  12         364  
28 12     12   5013 use Graphics::Fig::Compound;
  12         29  
  12         385  
29 12     12   63 use Graphics::Fig::Ellipse;
  12         18  
  12         231  
30 12     12   5049 use Graphics::Fig::Polyline;
  12         55  
  12         430  
31 12     12   5186 use Graphics::Fig::Spline;
  12         33  
  12         398  
32 12     12   4746 use Graphics::Fig::Text;
  12         28  
  12         47016  
33              
34             my $FIG2DEV = "fig2dev";
35              
36             #
37             # Graphics::Fig::_figPerInch return fig units per inch
38             # $parameters
39             #
40             sub _figPerInch {
41 304     304   274 my $parameters = shift;
42 304         279 my $unitSystem = ${$parameters}{"units"}[1];
  304         439  
43              
44 304 100       462 if ($unitSystem eq "Metric") {
45 24         35 return 1143.0; # 450 fig units/cm
46             } else {
47 280         447 return 1200.0; # 1200 fig units/in
48             }
49             }
50              
51             #
52             # Graphics::Fig::convertEndAction
53             # $fig: Fig instance
54             # $prefix: error message prefix
55             # $value: end action
56             # $context: parameter context
57             #
58             sub convertEndAction {
59 0     0 0 0 my $fig = shift;
60 0         0 my $prefix = shift;
61 0         0 my $value = shift;
62 0         0 my $context = shift;
63              
64 0 0 0     0 if ($value eq "merge" || $value eq "group" || $value eq "discard") {
      0        
65 0         0 return $value;
66             }
67 0         0 croak("${prefix}: ${value}: error: expected Landscape or Portrait");
68             }
69              
70             #
71             # Global Parameters
72             #
73             my %GlobalParameterTemplate = (
74             positional => {
75             },
76             named => [
77             \%Graphics::Fig::Parameters::UnitsParameter, # must be first
78             \%Graphics::Fig::Parameters::PositionParameter, # must be second
79             @Graphics::Fig::Parameters::ArrowParameters,
80             \%Graphics::Fig::Parameters::CapStyleParameter,
81             \%Graphics::Fig::Parameters::ColorParameter,
82             \%Graphics::Fig::Parameters::CornerRadiusParameter,
83             \%Graphics::Fig::Parameters::DepthParameter,
84             \%Graphics::Fig::Parameters::DetachedLinetoParameter,
85             @Graphics::Fig::Parameters::ExportParameters,
86             @Graphics::Fig::Parameters::FillParameters,
87             \%Graphics::Fig::Parameters::GridParameter,
88             \%Graphics::Fig::Parameters::JoinStyleParameter,
89             @Graphics::Fig::Parameters::LineParameters,
90             @Graphics::Fig::Parameters::SaveParameters,
91             \%Graphics::Fig::Parameters::SplineSubtypeParameter,
92             @Graphics::Fig::Parameters::TextParameters,
93             ],
94             );
95              
96             #
97             # Export Parameters
98             #
99             my %ExportParameterTemplate = (
100             positional => {
101             "." => [ "filename" ],
102             },
103             named => [
104             \%Graphics::Fig::Parameters::UnitsParameter, # must be first
105             \%Graphics::Fig::Parameters::PositionParameter, # must be second
106             @Graphics::Fig::Parameters::SaveParameters,
107             {
108             name => "filename",
109             },
110             {
111             name => "exportFormat", # duplicated for alias
112             aliases => [ "format" ],
113             },
114             {
115             name => "exportOptions", # duplicated for alias
116             aliases => [ "options" ],
117             convert => \&Graphics::Fig::Parameters::convertExportOptions,
118             },
119             ],
120             );
121              
122             #
123             # Move Parameters
124             #
125             my %MovetoParameterTemplate = (
126             positional => {
127             "@" => [ "point" ],
128             ".." => [ "distance", "heading" ],
129             },
130             named => [
131             \%Graphics::Fig::Parameters::UnitsParameter, # must be first
132             \%Graphics::Fig::Parameters::PositionParameter, # must be second
133             \%Graphics::Fig::Parameters::PointParameter,
134             {
135             name => "distance",
136             convert => \&Graphics::Fig::Parameters::convertLength,
137             },
138             {
139             name => "heading",
140             convert => \&Graphics::Fig::Parameters::convertAngle,
141             },
142             ],
143             );
144              
145             #
146             # Translate Parameters
147             #
148             my %TranslateParameterTemplate = (
149             positional => {
150             "@" => [ "offset" ],
151             },
152             named => [
153             \%Graphics::Fig::Parameters::UnitsParameter, # must be first
154             \%Graphics::Fig::Parameters::OffsetParameter,
155             ],
156             );
157              
158             #
159             # Rotate Parameters
160             #
161             my %RotateParameterTemplate = (
162             positional => {
163             "." => [ "rotation" ],
164             },
165             named => [
166             \%Graphics::Fig::Parameters::UnitsParameter, # must be first
167             \%Graphics::Fig::Parameters::PositionParameter, # must be second
168             \%Graphics::Fig::Parameters::CenterParameter,
169             \%Graphics::Fig::Parameters::RotationParameter,
170             ],
171             );
172              
173             #
174             # Scale Parameters
175             #
176             my %ScaleParameterTemplate = (
177             positional => {
178             "." => [ "scale" ],
179             "@" => [ "scale" ],
180             },
181             named => [
182             \%Graphics::Fig::Parameters::UnitsParameter, # must be first
183             \%Graphics::Fig::Parameters::PositionParameter, # must be second
184             \%Graphics::Fig::Parameters::CenterParameter,
185             \%Graphics::Fig::Parameters::ScaleParameter,
186             ],
187             );
188              
189             #
190             # End Parameters
191             #
192             my %EndParameterTemplate = (
193             positional => {
194             "." => [ "action" ],
195             },
196             named => [
197             \%Graphics::Fig::Parameters::UnitsParameter, # must be first
198             \%Graphics::Fig::Parameters::PositionParameter, # must be second
199             \%Graphics::Fig::Parameters::GridParameter,
200             {
201             name => "action",
202             convert => \&convertEndAction,
203             default => "merge",
204             },
205             ],
206             );
207              
208             ##
209             ## Load Parameters
210             ##
211             #my %LoadParameterTemplate = (
212             # positional => {
213             # "." => [ "filename" ],
214             # },
215             # named => [
216             # {
217             # name => "filename",
218             # },
219             # ],
220             #);
221              
222             #
223             # Save Parameters
224             #
225             my %SaveParameterTemplate = (
226             positional => {
227             "." => [ "filename" ],
228             },
229             named => [
230             \%Graphics::Fig::Parameters::UnitsParameter, # must be first
231             \%Graphics::Fig::Parameters::PositionParameter, # must be second
232             @Graphics::Fig::Parameters::SaveParameters,
233             {
234             name => "filename",
235             },
236             ],
237             );
238              
239             #
240             # Get Position and Get Bounding Box Parameters
241             #
242             my %UnitsOnlyParameterTemplate = (
243             positional => {
244             },
245             named => [
246             \%Graphics::Fig::Parameters::UnitsParameter, # must be first
247             ],
248             );
249              
250             #
251             # Graphics::Fig::new: constructor
252             # $proto: prototype
253             # [ { option1=value1, option2=value2, ... } ]
254             #
255             sub new {
256 64     64 1 97173 my $proto = shift;
257 64   33     305 my $class = ref($proto) || $proto;
258              
259 64         337 my $self = {
260             colors => Graphics::Fig::Color->new(),
261             stack => [
262             {
263             options => { },
264             objects => [ ],
265             openLineto => undef,
266             openSplineto => undef,
267             },
268             ],
269             };
270 64         115 bless($self, $class);
271 64         170 $self->options({});
272              
273             #
274             # Process global options.
275             #
276 64         110 my $stack = ${$self}{"stack"};
  64         95  
277 64         68 my $tos = ${$stack}[$#{$stack}];
  64         117  
  64         83  
278 64         81 eval {
279             Graphics::Fig::Parameters::parse($self, "Graphics::Fig::new",
280             \%GlobalParameterTemplate,
281 64         107 undef, ${$tos}{"options"}, @_);
  64         177  
282             };
283 64 50       150 if ($@) {
284 0         0 $@ =~ s/ at [^\s]* line \d+\.\n//;
285 0         0 croak("$@");
286             }
287              
288 64         175 return $self;
289             }
290              
291             #
292             # Graphics::Fig::options: change global options
293             # $self: class instance
294             # [ { option1=value1, option2=value2, ... } ]
295             #
296             sub options {
297 65     65 1 95 my $self = shift;
298 65         115 my $stack = ${$self}{"stack"};
  65         148  
299 65         90 my $tos = ${$stack}[$#{$stack}];
  65         101  
  65         90  
300              
301 65         76 eval {
302             Graphics::Fig::Parameters::parse($self, "options",
303             \%GlobalParameterTemplate,
304 65         90 ${$tos}{"options"},
305 65         115 ${$tos}{"options"}, @_);
  65         216  
306             };
307 65 50       139 if ($@) {
308 0         0 $@ =~ s/ at [^\s]* line \d+\.\n//;
309 0         0 croak("$@");
310             }
311 65         76 return 1;
312             }
313              
314             #
315             # Graphics::Fig::moveto move to a new position
316             # $self: class instance
317             # moveto options...
318             #
319             sub moveto {
320 4     4 1 18 my $self = shift;
321 4         4 my $stack = ${$self}{"stack"};
  4         7  
322 4         6 my $tos = ${$stack}[$#{$stack}];
  4         7  
  4         5  
323 4         5 my $options = ${$tos}{"options"};
  4         6  
324 4         6 my %parameters;
325             my $newPosition;
326              
327 4         4 eval {
328 4         19 Graphics::Fig::Parameters::parse($self, "moveto",
329             \%MovetoParameterTemplate, $options,
330             \%parameters, @_);
331             };
332 4 50       9 if ($@) {
333 0         0 $@ =~ s/ at [^\s]* line \d+\.\n//;
334 0         0 croak("$@");
335             }
336 4 50       11 if (defined($parameters{"point"})) {
    0          
    0          
337 4 50       9 if (defined($parameters{"distance"})) {
338 0         0 croak("moveto error: point and distance cannot be given together");
339             }
340 4 50       8 if (defined($parameters{"heading"})) {
341 0         0 croak("moveto error: point and heading cannot be given together");
342             }
343 4         6 $newPosition = $parameters{"point"};
344              
345             } elsif (defined(my $r = $parameters{"distance"})) {
346 0         0 my $theta = $parameters{"heading"};
347 0 0       0 if (!defined($theta)) {
348 0         0 croak("moveto error: expected point, or distance and heading");
349             }
350             $newPosition = [ $parameters{"position"}[0] + $r * cos($theta),
351 0         0 $parameters{"position"}[1] - $r * sin($theta) ];
352              
353             } elsif (defined($parameters{"position"})) {
354 0         0 $newPosition = $parameters{"position"};
355              
356             } else {
357 0         0 croak("moveto error: point and distance cannot be given together");
358             }
359 4         4 ${$options}{"position"} = $newPosition;
  4         7  
360              
361 4         10 return 1;
362             }
363              
364             #
365             # Graphics::Fig::getposition: return the current position
366             # $self: class instance
367             #
368             sub getposition {
369 5     5 1 20 my $self = shift;
370 5         5 my $stack = ${$self}{"stack"};
  5         6  
371 5         5 my $tos = ${$stack}[$#{$stack}];
  5         6  
  5         6  
372 5         5 my $options = ${$tos}{"options"};
  5         6  
373 5         10 my %parameters;
374             my $position;
375 5         0 my $scale;
376              
377 5         5 eval {
378 5         11 Graphics::Fig::Parameters::parse($self, "getposition",
379             \%UnitsOnlyParameterTemplate, $options, \%parameters, @_);
380             };
381 5 50       9 if ($@) {
382 0         0 $@ =~ s/ at [^\s]* line \d+\.\n//;
383 0         0 croak("$@");
384             }
385 5         7 $scale = $parameters{"units"}[0];
386 5         6 $position = ${$options}{"position"};
  5         6  
387 5         6 return [ ${$position}[0] / $scale, ${$position}[1] / $scale ];
  5         6  
  5         15  
388             }
389              
390             #
391             # Graphics::Fig::begin: begin a sub-environment
392             # $self: class instance
393             # [ { } ]
394             #
395             sub begin {
396 8     8 1 40 my $self = shift;
397 8         8 my $stack = ${$self}{"stack"};
  8         10  
398 8         8 my $tos = ${$stack}[$#{$stack}];
  8         10  
  8         11  
399 8         9 my %parameters;
400              
401 8         10 eval {
402             Graphics::Fig::Parameters::parse($self, "begin",
403             \%GlobalParameterTemplate,
404 8         11 ${$tos}{"options"},
  8         17  
405             \%parameters, @_);
406             };
407 8 50       16 if ($@) {
408 0         0 $@ =~ s/ at [^\s]* line \d+\.\n//;
409 0         0 croak("$@");
410             }
411 8         8 push(@{$stack}, {
  8         25  
412             options => \%parameters,
413             objects => [ ],
414             openLineto => undef,
415             openSplineto => undef,
416             });
417              
418 8         14 return 1;
419             }
420              
421             #
422             # Graphics::Fig::end: end a sub-environment
423             # $self: class instance
424             # [ [ action ] { action={merge|group|discard} }
425             #
426             sub end {
427 8     8 1 26 my $self = shift;
428 8         8 my %parameters;
429              
430 8         7 my $stack = ${$self}{"stack"};
  8         13  
431 8         7 my $oldTos = pop(@{$stack});
  8         11  
432 8         9 my $tos = ${$stack}[$#{$stack}];
  8         11  
  8         9  
433              
434              
435 8         9 eval {
436             Graphics::Fig::Parameters::parse($self, "end", \%EndParameterTemplate,
437 8         8 ${$tos}{"options"}, \%parameters,
  8         16  
438             @_);
439             };
440 8 50       16 if ($@) {
441 0         0 $@ =~ s/ at [^\s]* line \d+\.\n//;
442 0         0 croak("$@");
443             }
444 8 50       16 if ($parameters{"action"} eq "merge") {
    0          
445 8         9 push(@{${$tos}{"objects"}}, @{${$oldTos}{"objects"}});
  8         5  
  8         13  
  8         8  
  8         14  
446              
447             } elsif ($parameters{"action"} eq "group") {
448 0         0 my $objects = ${$oldTos}{"objects"};
  0         0  
449 0         0 Graphics::Fig::Compound->new($self, $objects, \%parameters);
450             }
451 8         34 return 1;
452             }
453              
454             #
455             # Graphics::Fig::arc draw an arc
456             # $self: class instance
457             # arc parameters...
458             #
459             sub arc {
460 11     11 1 114 my $self = shift;
461 11         14 my $obj = eval {
462 11         94 return Graphics::Fig::Arc->arc($self, @_);
463             };
464 11 50       27 if ($@) {
465 0         0 $@ =~ s/ at [^\s]* line \d+\.\n//;
466 0         0 croak("$@");
467             }
468 11         17 return $obj;
469             }
470              
471             #
472             # Graphics::Fig::arc draw an arc
473             # $self: class instance
474             # arc parameters...
475             #
476             sub arcto {
477 14     14 1 76 my $self = shift;
478 14         14 my $obj = eval {
479 14         45 return Graphics::Fig::Arc->arcto($self, @_);
480             };
481 14 50       22 if ($@) {
482 0         0 $@ =~ s/ at [^\s]* line \d+\.\n//;
483 0         0 croak("$@");
484             }
485 14         19 return $obj;
486             }
487              
488             #
489             # Graphics::Fig::circle: draw a circle
490             # $self: class instance
491             # circle parameters...
492             #
493             sub circle {
494 8     8 1 74 my $self = shift;
495 8         11 my $obj = eval {
496 8         55 return Graphics::Fig::Ellipse->circle($self, @_);
497             };
498 8 50       18 if ($@) {
499 0         0 $@ =~ s/ at [^\s]* line \d+\.\n//;
500 0         0 croak("$@");
501             }
502 8         13 return $obj;
503             }
504              
505             #
506             # Graphics::Fig::ellipse: draw an ellipse
507             # $self: class instance
508             # ellipse parameters...
509             #
510             sub ellipse {
511 7     7 1 61 my $self = shift;
512 7         10 my $obj = eval {
513 7         59 return Graphics::Fig::Ellipse->ellipse($self, @_);
514             };
515 7 50       15 if ($@) {
516 0         0 $@ =~ s/ at [^\s]* line \d+\.\n//;
517 0         0 croak("$@");
518             }
519 7         13 return $obj;
520             }
521              
522             #
523             # Graphics::Fig::polyline: draw a polyline object
524             # $self: class instance
525             # polyline parameters...
526             #
527             sub polyline {
528 11     11 1 106 my $self = shift;
529 11         10 my $obj = eval {
530 11         38 return Graphics::Fig::Polyline->polyline($self, @_);
531             };
532 11 50       16 if ($@) {
533 0         0 $@ =~ s/ at [^\s]* line \d+\.\n//;
534 0         0 croak("$@");
535             }
536 11         18 return $obj;
537             }
538              
539             #
540             # Graphics::Fig::lineto: draw a line from position to the given point(s)
541             # $self: class instance
542             # polyline parameters...
543             #
544             sub lineto {
545 29     29 1 150 my $self = shift;
546 29         31 my $obj = eval {
547 29         78 return Graphics::Fig::Polyline->lineto($self, @_);
548             };
549 29 50       44 if ($@) {
550 0         0 $@ =~ s/ at [^\s]* line \d+\.\n//;
551 0         0 croak("$@");
552             }
553 29         63 return $obj;
554             }
555              
556             #
557             # Graphics::Fig::box draw a box object
558             # $self: class instance
559             # box parameters...
560             #
561             sub box {
562 17     17 1 87 my $self = shift;
563 17         20 my $obj = eval {
564 17         83 return Graphics::Fig::Polyline->box($self, @_);
565             };
566 17 50       31 if ($@) {
567 0         0 $@ =~ s/ at [^\s]* line \d+\.\n//;
568 0         0 croak("$@");
569             }
570 17         28 return $obj;
571             }
572              
573             #
574             # Graphics::Fig::polygon draw a polygon object
575             # $self: class instance
576             # polygon parameters...
577             #
578             sub polygon {
579 7     7 1 78 my $self = shift;
580 7         14 my $obj = eval {
581 7         67 return Graphics::Fig::Polyline->polygon($self, @_);
582             };
583 7 50       17 if ($@) {
584 0         0 $@ =~ s/ at [^\s]* line \d+\.\n//;
585 0         0 croak("$@");
586             }
587 7         23 return $obj;
588             }
589              
590             #
591             # Graphics::Fig::picture embed a picture
592             # $self: class instance
593             # picture parameters...
594             #
595             sub picture {
596 19     19 1 97 my $self = shift;
597 19         21 my $obj = eval {
598 19         78 return Graphics::Fig::Polyline->picture($self, @_);
599             };
600 19 50       33 if ($@) {
601 0         0 $@ =~ s/ at [^\s]* line \d+\.\n//;
602 0         0 croak("$@");
603             }
604 19         38 return $obj;
605             }
606              
607             #
608             # Graphics::Fig::spline: draw a spline
609             # $self: class instance
610             # picture parameters...
611             #
612             sub spline {
613 13     13 1 107 my $self = shift;
614 13         14 my $obj = eval {
615 13         49 return Graphics::Fig::Spline->spline($self, @_);
616             };
617 13 50       26 if ($@) {
618 0         0 $@ =~ s/ at [^\s]* line \d+\.\n//;
619 0         0 croak("$@");
620             }
621 13         18 return $obj;
622             }
623              
624             #
625             # Graphics::Fig::splineto: draw a spline from current point
626             # $self: class instance
627             # picture parameters...
628             #
629             sub splineto {
630 6     6 1 36 my $self = shift;
631 6         6 my $obj = eval {
632 6         19 return Graphics::Fig::Spline->splineto($self, @_);
633             };
634 6 50       12 if ($@) {
635 0         0 $@ =~ s/ at [^\s]* line \d+\.\n//;
636 0         0 croak("$@");
637             }
638 6         9 return $obj;
639             }
640              
641             #
642             # Graphics::Fig::text add text
643             # $self: class instance
644             # text parameters...
645             #
646             sub text {
647 1     1 1 5 my $self = shift;
648 1         2 my $obj = eval {
649 1         13 return Graphics::Fig::Text->text($self, @_);
650             };
651 1 50       2 if ($@) {
652 0         0 $@ =~ s/ at [^\s]* line \d+\.\n//;
653 0         0 croak("$@");
654             }
655 1         2 return $obj;
656             }
657              
658             #
659             # Graphics::Fig::translate: translate all objects
660             # $self: class instance
661             # translate parameters...
662             #
663             sub translate {
664 9     9 1 46 my $self = shift;
665              
666 9         7 my $stack = ${$self}{"stack"};
  9         13  
667 9         11 my $tos = ${$stack}[$#{$stack}];
  9         12  
  9         12  
668 9         12 my %parameters;
669 9         8 eval {
670             Graphics::Fig::Parameters::parse($self, "translate",
671             \%TranslateParameterTemplate,
672 9         14 ${$tos}{"options"}, \%parameters, @_);
  9         20  
673             };
674 9 50       21 if ($@) {
675 0         0 $@ =~ s/ at [^\s]* line \d+\.\n//;
676 0         0 croak("$@");
677             }
678 9         12 foreach my $object (@{${$tos}{"objects"}}) {
  9         7  
  9         24  
679 23         54 $object->translate(\%parameters);
680             }
681             }
682              
683             #
684             # Graphics::Fig::rotate: rotate all objects
685             # $self: class instance
686             # rotate parameters...
687             #
688             sub rotate {
689 10     10 1 48 my $self = shift;
690              
691 10         12 my $stack = ${$self}{"stack"};
  10         16  
692 10         10 my $tos = ${$stack}[$#{$stack}];
  10         15  
  10         12  
693 10         12 my %parameters;
694 10         12 eval {
695             Graphics::Fig::Parameters::parse($self, "translate",
696             \%RotateParameterTemplate,
697 10         14 ${$tos}{"options"}, \%parameters, @_);
  10         23  
698             };
699 10 50       25 if ($@) {
700 0         0 $@ =~ s/ at [^\s]* line \d+\.\n//;
701 0         0 croak("$@");
702             }
703 10         11 foreach my $object (@{${$tos}{"objects"}}) {
  10         12  
  10         20  
704 24         58 $object->rotate(\%parameters);
705             }
706             }
707              
708             #
709             # Graphics::Fig::scale: scale all objects
710             # $self: class instance
711             # scale parameters...
712             #
713             sub scale {
714 7     7 1 32 my $self = shift;
715              
716 7         9 my $stack = ${$self}{"stack"};
  7         11  
717 7         12 my $tos = ${$stack}[$#{$stack}];
  7         10  
  7         11  
718 7         10 my %parameters;
719 7         7 eval {
720             Graphics::Fig::Parameters::parse($self, "translate",
721             \%ScaleParameterTemplate,
722 7         8 ${$tos}{"options"}, \%parameters, @_);
  7         20  
723             };
724 7 50       16 if ($@) {
725 0         0 $@ =~ s/ at [^\s]* line \d+\.\n//;
726 0         0 croak("$@");
727             }
728 7         11 foreach my $object (@{${$tos}{"objects"}}) {
  7         7  
  7         15  
729 8         30 $object->scale(\%parameters);
730             }
731             }
732              
733             #
734             # Graphics::Fig::getbbox return the bounding box of all objects
735             # $self: class instance
736             # bbox parameters...
737             #
738             sub getbbox {
739 6     6 1 23 my $self = shift;
740              
741 6         6 my $stack = ${$self}{"stack"};
  6         11  
742 6         16 my $tos = ${$stack}[$#{$stack}];
  6         10  
  6         30  
743 6         9 my $options = ${$tos}{"options"};
  6         9  
744 6         14 my %parameters;
745             my $scale;
746 6         0 my ($x_min, $y_min, $x_max, $y_max);
747 6         8 eval {
748 6         36 Graphics::Fig::Parameters::parse($self, "getbbox",
749             \%UnitsOnlyParameterTemplate, $options, \%parameters, @_);
750             };
751 6 50       16 if ($@) {
752 0         0 $@ =~ s/ at [^\s]* line \d+\.\n//;
753 0         0 croak("$@");
754             }
755 6         8 foreach my $object (@{${$tos}{"objects"}}) {
  6         8  
  6         14  
756 32         74 my $bbox = $object->getbbox(\%parameters);
757 32 100       53 if (!defined($x_min)) {
758 6         7 $x_min = ${$bbox}[0][0];
  6         10  
759 6         6 $y_min = ${$bbox}[0][1];
  6         8  
760 6         14 $x_max = ${$bbox}[1][0];
  6         11  
761 6         6 $y_max = ${$bbox}[1][1];
  6         8  
762 6         14 next;
763             }
764 26 100       21 if (${$bbox}[0][0] < $x_min) {
  26         37  
765 8         7 $x_min = ${$bbox}[0][0];
  8         9  
766             }
767 26 100       24 if (${$bbox}[0][1] < $y_min) {
  26         35  
768 10         8 $y_min = ${$bbox}[0][1];
  10         9  
769             }
770 26 100       23 if (${$bbox}[1][0] > $x_max) {
  26         35  
771 7         5 $x_max = ${$bbox}[1][0];
  7         7  
772             }
773 26 100       24 if (${$bbox}[1][1] > $y_max) {
  26         49  
774 5         3 $y_max = ${$bbox}[1][1];
  5         9  
775             }
776             }
777 6         10 $scale = $parameters{"units"}[0];
778 6         34 return [ [ $x_min / $scale, $y_min / $scale ],
779             [ $x_max / $scale, $y_max / $scale ] ];
780             }
781              
782             # TODO: Implement load.
783             #
784             # Load would allow you to incorporate existing figures, manipulate
785             # them using translate, rotate and scale, form groups around them
786             # and superimpose new objects over them.
787             #
788             # Notes:
789             # - Some older formats should be accepted. Documented versions are: 1.3, 1.4,
790             # 1.5 1.6, 2.0, 2.1, 3.0, 3.1 and 3.2. Version 1.4 was the first to have a
791             # #FIG line. Versions 1.5 and 1.6 seem to have been a dead-end side
792             # development path.
793             #
794             # These are the preambles to a few formats:
795             #
796             # 1.3:
797             # [no #FIG line]
798             # resolution in pixels per inch
799             # origin: 2
800             # canvas width (pixels)
801             # canvas height (pixels)
802             #
803             # 1.4:
804             # #FIG line was added in this version
805             # resolution
806             # coordinate_system: 2
807             #
808             # 2.0:
809             # resolution
810             # coordinate_system: 2
811             #
812             # 3.1:
813             # resolution: 1200
814             # orientation: Landscape or Portrait
815             # justification: Center or Flush Left
816             # units: Metric or Inches
817             # coordinate_system: 2
818             #
819             # 3.2:
820             # orientation: Landscape or Portrait
821             # justification: Center or Flush Left
822             # units: Metric or Inches
823             # papersize: Letter Legal Ledger Tabloid A B C D E A4 A3 A2 A1 A0 B5
824             # magnification:
825             # multiple-page: Single or Multiple
826             # transparent color:
827             # resolution coordinate_system: 1200 2
828             #
829             # - One approach would be to load the file into the current drawing
830             # environment, mapping any new custom colors to new values. Another approach
831             # would be to construct a new Graphics::Fig object and provide a "merge"
832             # function that merges one Fig object into another. The later has the
833             # benefit of providing merge, which may be useful in itself. Like load,
834             # merge also has to reassign color map entries.
835             #
836             ## Graphics::Fig::load: load a .fig file into the current drawing environment
837             ## $self: class instance
838             ## load parameters...
839             ##
840             #sub load {
841             # my $self = shift;
842             #
843             # my $stack = ${$self}{"stack"};
844             # my $tos = ${$stack}[$#{$stack}];
845             # my %parameters;
846             # eval {
847             # Graphics::Fig::Parameters::parse($self, "load", \%LoadParameterTemplate,
848             # ${$tos}{"options"}, \%parameters, @_);
849             # };
850             # if ($@) {
851             # $@ =~ s/ at [^\s]* line \d+\.\n//;
852             # croak("$@");
853             # }
854             #
855             # my $filename = $parameters{"filename"};
856             # if (!defined($filename)) {
857             # croak("load: error: expected filename");
858             # }
859             # my $fh;
860             # open($fh, "<", $filename) || croak("save: $filename: $!");
861             #
862             # if (!<$fh>) {
863             # close($fh):
864             # croak("load: error: can't read header line");
865             # }
866             # if (!/^#FIG (.*)/) {
867             # close($fh):
868             # croak("load: error: exepected FIG file format");
869             # }
870             #
871             # HERE
872             #
873             # close($fh);
874             #}
875              
876             #
877             # Graphics::Fig::_saveCommon: common code for save and export
878             # $self: class instance
879             # $tos: top of drawing stack
880             # $parameters: reference to parameter hash
881             # $fh: open filehandle to the output file
882             #
883             sub _saveCommon {
884 64     64   119 my $self = shift;
885 64         71 my $tos = shift;
886 64         76 my $parameters = shift;
887 64         85 my $fh = shift;
888              
889 64         187 my $figPerInch = _figPerInch($parameters);
890 64         84 my $comment = ${$parameters}{"comment"};
  64         98  
891 64 50       144 if ($comment ne "") {
892 0         0 $comment =~ s/^/# /gm;
893 0 0       0 if (!($comment =~ m/\n$/)) {
894 0         0 $comment .= "\n";
895             }
896             }
897 64         696 printf $fh ("#FIG 3.2 Produced by Graphics::Fig\n");
898 64         148 printf $fh ("%s\n", ${$parameters}{"orientation"});
  64         201  
899 64         106 printf $fh ("%s\n", ${$parameters}{"pageJustification"});
  64         126  
900 64         93 printf $fh ("%s\n", ${$parameters}{"units"}[1]);
  64         149  
901 64         104 printf $fh ("%s\n", ${$parameters}{"paperSize"});
  64         121  
902 64         79 printf $fh ("%.2f\n", ${$parameters}{"magnification"});
  64         559  
903 64         98 printf $fh ("%s\n", ${$parameters}{"multiplePage"});
  64         136  
904 64         90 printf $fh ("%d\n", ${$parameters}{"transparentColor"});
  64         128  
905 64 50       145 if ($comment ne "") {
906 0         0 printf $fh ("%s", $comment);
907             }
908             #
909             # In the imperial unit system, 1200 is the number of fig units per
910             # inch. In metric, it's the number of fig units in 400/381 inches.
911             # In other words, 1200 means 450 fig units per cm or exactly 1143
912             # fig units per inch.
913             #
914 64         107 printf $fh ("1200 2\n");
915              
916             #
917             # Add custom colors.
918             #
919 64         182 my $customHex = $self->{"colors"}->{"customHex"};
920 64         124 for (my $i = 0; $i < scalar(@{$customHex}); ++$i) {
  69         165  
921 5         8 printf $fh ("0 %d %s\n", 32 + $i, ${$customHex}[$i]);
  5         15  
922             }
923              
924             #
925             # Add objects.
926             #
927 64         83 foreach my $object (@{${$tos}{"objects"}}) {
  64         86  
  64         137  
928 128         372 $object->print($fh, $parameters);
929             }
930             }
931              
932             #
933             # Graphics::Fig::save: save the .fig file
934             # $self: class instance
935             # save parameters...
936             #
937             sub save {
938 64     64 1 482 my $self = shift;
939              
940 64         67 my $stack = ${$self}{"stack"};
  64         105  
941 64         71 my $tos = ${$stack}[$#{$stack}];
  64         103  
  64         84  
942 64         73 my %parameters;
943 64         84 eval {
944             Graphics::Fig::Parameters::parse($self, "save", \%SaveParameterTemplate,
945 64         103 ${$tos}{"options"}, \%parameters, @_);
  64         171  
946             };
947 64 50       151 if ($@) {
948 0         0 $@ =~ s/ at [^\s]* line \d+\.\n//;
949 0         0 croak("$@");
950             }
951              
952 64         109 my $filename = $parameters{"filename"};
953 64 50       129 if (!defined($filename)) {
954 0         0 croak("save: error: expected filename");
955             }
956 64 50       6978 open(my $fh, ">", $filename) || croak("save: $filename: $!");
957 64         380 &_saveCommon($self, $tos, \%parameters, $fh);
958 64         3507 close($fh);
959             }
960              
961              
962             #
963             # Graphics::Fig::export: export the drawing to the given format
964             # $self: class instance
965             # save parameters...
966             #
967             sub export {
968 0     0 1   my $self = shift;
969              
970 0           my $stack = ${$self}{"stack"};
  0            
971 0           my $tos = ${$stack}[$#{$stack}];
  0            
  0            
972 0           my %parameters;
973 0           eval {
974             Graphics::Fig::Parameters::parse($self, "export",
975             \%ExportParameterTemplate,
976 0           ${$tos}{"options"}, \%parameters, @_);
  0            
977             };
978 0 0         if ($@) {
979 0           $@ =~ s/ at [^\s]* line \d+\.\n//;
980 0           croak("$@");
981             }
982              
983             #
984             # Validate parameters. Determine the output format either from the
985             # type argument or the filename extension.
986             #
987 0           my $outputFilename = $parameters{"filename"};
988 0 0         if (!defined($outputFilename)) {
989 0           croak("export: error: expected filename");
990             }
991 0           my $type;
992 0 0         if (defined($parameters{"type"})) {
    0          
993 0           $type = $parameters{"type"};
994             } elsif ($outputFilename =~ m/\.([^.]+)$/) {
995 0           $type = $1;
996             } else {
997 0           croak("export: error: cannot determine output file type");
998             }
999              
1000             #
1001             # Save the drawing to a temporary file.
1002             #
1003 0           my ($fh, $tempFilename) = tempfile();
1004 0           &_saveCommon($self, $tos, \%parameters, $fh);
1005 0           close($fh);
1006              
1007             #
1008             # Build the argument list and run fig2dev.
1009             #
1010 0           my @Args = ($FIG2DEV, "-L", $type);
1011 0 0         if (defined($parameters{"options"})) {
1012 0           push(@Args, @{$parameters{"options"}});
  0            
1013             }
1014 0           push(@Args, $tempFilename, $outputFilename);
1015 0 0         if ((system @Args) != 0) {
1016 0           croak("export: error: $!\n");
1017             }
1018             }
1019              
1020             1;
1021              
1022             __END__