File Coverage

blib/lib/CAD/Drawing/IO/PostScript.pm
Criterion Covered Total %
statement 1 3 33.3
branch n/a
condition n/a
subroutine 1 1 100.0
pod n/a
total 2 4 50.0


line stmt bran cond sub pod time code
1             package CAD::Drawing::IO::PostScript;
2             our $VERSION = '0.03';
3              
4 1     1   31712 use CAD::Drawing;
  0            
  0            
5             use CAD::Drawing::Defined;
6             use PostScript::Simple;
7              
8              
9             use strict;
10             use Carp;
11             ########################################################################
12             =pod
13              
14             =head1 NAME
15              
16             CAD::Drawing::IO::PostScript - PostScript output methods
17              
18             =head1 Description
19              
20             I would like this module to both load and save PostScript vector
21             graphics, but I have not yet found a suitable PostScript parsing
22             package.
23              
24             =head1 NOTICE
25              
26             This module should be considered pre-ALPHA and untested. Some features
27             rely on the author's hacks to PostScript::Simple, which may or may not
28             have been incorporated into the CPAN distribution of PostScript::Simple.
29             For bleeding-edge code, see http://ericwilhelm.homeip.net.
30              
31             =head1 AUTHOR
32              
33             Eric L. Wilhelm
34              
35             http://scratchcomputing.com
36              
37             =head1 COPYRIGHT
38              
39             This module is copyright (C) 2005-2006 by Eric L. Wilhelm. Portions
40             copyright (C) 2003 by Eric L. Wilhelm and A. Zahner Co.
41              
42             =head1 LICENSE
43              
44             This module is distributed under the same terms as Perl. See the Perl
45             source package for details.
46              
47             You may use this software under one of the following licenses:
48              
49             (1) GNU General Public License
50             (found at http://www.gnu.org/copyleft/gpl.html)
51             (2) Artistic License
52             (found at http://www.perl.com/pub/language/misc/Artistic.html)
53              
54             =head1 NO WARRANTY
55              
56             This software is distributed with ABSOLUTELY NO WARRANTY. The author,
57             his former employer, and any other contributors will in no way be held
58             liable for any loss or damages resulting from its use.
59              
60             =head1 Modifications
61              
62             The source code of this module is made freely available and
63             distributable under the GPL or Artistic License. Modifications to and
64             use of this software must adhere to one of these licenses. Changes to
65             the code should be noted as such and this notification (as well as the
66             above copyright information) must remain intact on all copies of the
67             code.
68              
69             Additionally, while the author is actively developing this code,
70             notification of any intended changes or extensions would be most helpful
71             in avoiding repeated work for all parties involved. Please contact the
72             author with any such development plans.
73              
74             =head1 SEE ALSO
75              
76             CAD::Drawing
77             CAD::Drawing::IO
78             PostScript::Simple
79              
80             =cut
81              
82             ########################################################################
83             # the following are required to be a disc I/O plugin:
84             our $can_save_type = "ps";
85              
86             =head1 Requisite Plug-in Functions
87              
88             See CAD::Drawing::IO for a description of the plug-in architecture.
89              
90             =head2 check_type
91              
92             Returns true if $type is "circ" or $filename is a directory containing a
93             ".circ" file.
94              
95             $fact = check_type($filename, $type);
96              
97             =cut
98             sub check_type {
99             my ($filename, $type) = @_;
100             if(defined($type)) {
101             ($type eq "ps") && return("ps");
102             return();
103             }
104             elsif($filename =~ m/\.e?ps$/) {
105             return("ps");
106             }
107             return();
108             } # end subroutine check_type definition
109              
110             ########################################################################
111             =head1 Methods
112              
113             =head2 load
114              
115             load();
116              
117             =cut
118             sub load {
119             croak("cannot yet load postscript!");
120             } # end subroutine load definition
121             ########################################################################
122              
123             =head2 save
124              
125             $drw->save($filename, \%opts);
126              
127             =cut
128             sub save {
129             my $self = shift;
130             my($filename, $opt) = @_;
131             my %opts;
132             my $accuracy = 1; # digits of accuracy with which to bother
133             my $sp = 30;
134             (ref($opt) eq "HASH") && (%opts = %$opt);
135             my $outobj;
136             if($filename =~ m/\.eps/) {
137             # implies eps fit
138             my @ext = $self->OrthExtents($opt);
139             my ($x, $y) = map({$_->[1] - $_->[0]} @ext);
140             $sp = 0;
141             # print "eps will be $x by $y\n";
142             my $obj = PostScript::Simple->new(
143             eps => 1,
144             xsize => $x,
145             ysize => $y,
146             colour => 1,
147             );
148             $opts{readymadeobject} = $obj;
149             }
150             unless($opts{"readymadeobject"} ) {
151             $outobj = new PostScript::Simple(
152             landscape => 1,
153             eps => 0,
154             papersize => "Letter",
155             colour => 1,
156             );
157             $outobj->newpage;
158             }
159             else {
160             $outobj = $opts{"readymadeobject"};
161             }
162              
163             # now can get the size from the object and use it to set the scale of
164             # things
165             my(@fitsize) = ($$outobj{bbx2}, $$outobj{bby2});
166             # print "got size: @fitsize\n";
167             my(@bound) = ([0,0], [@fitsize]);
168             my $drw = $self; # default is to smash $self
169            
170             # FIXME: why did I have this here?
171             # my $worklist = $drw->select_addr();
172            
173             unless($opts{"noclone"}) {
174             $drw = CAD::Drawing->new;
175             # passing original opts allows selective save
176             $self->GroupClone($drw, $opt);
177             }
178             ####################################################################
179             # Setup border
180             my @border;
181             if(ref($opts{"border"}) eq "ARRAY") {
182             # @border = ( [@sp] , [$fitsize[0]-$sp[0] , $fitsize[1]-$sp[1] ]);
183             @border = @{$opts{"border"}};
184             }
185             elsif(defined($opts{"border"})) {
186             my $num = $opts{"border"};
187             @border = ([$num,$num], [-$num,-$num]);
188             }
189             else {
190             @border = ([$sp, $sp], [-$sp, -$sp]);
191             }
192             ####################################################################
193             # Perform fit
194             # $outobj->line(0,0, @fitsize);
195             my $scaling = $drw->fit_to_bound([@bound], [@border],
196             {"center" =>[$fitsize[0] / 2, $fitsize[1]/2 ] , %opts} );
197             ####################################################################
198             if($opts{show_border} ) {
199             $drw->addrec(
200             [
201             [
202             $bound[0][0] + $border[0][0] / 2 ,
203             $bound[0][1] + $border[0][1] / 2
204             ],
205             [
206             $bound[1][0] + $border[1][0] / 2 ,
207             $bound[1][1] + $border[1][1] / 2
208             ]
209             ]
210             );
211             } # end if show border
212             # now must draw all of the resultant geometry
213             my $filledopt = 0;
214             if($opts{"filled"}) {
215             # FIXME: need some way to make this selective?
216             $filledopt = $opts{filled};
217             }
218             my $font_choice = "Helvetica";
219             $opts{font} && ($font_choice = $opts{font});
220             # NOTE NOTE NOTE NOTE NOTE NOTE:not using $self here!
221             my %ps_data = (
222             psobj => $outobj,
223             font => $font_choice,
224             filled => $filledopt,
225             accuracy => $accuracy,
226             );
227              
228             our %ps_functions;
229             $drw->outloop(\%ps_functions, \%ps_data);
230             $opts{show} && ($drw->show(hang => 1));
231             return($outobj->output($filename));
232             } # end subroutine save definition
233             ########################################################################
234              
235             =head2 PostScript::Simple::setpscolor
236              
237             PostScript::Simple::setpscolor();
238              
239             =cut
240             sub PostScript::Simple::setpscolor {
241             my $self = shift;
242             my($ac_color) = @_;
243             my %no = map( { $_ => 1} 0, 7, 256);
244             $no{$ac_color} && return();
245             my $ps_color = $aci2rgb[$ac_color];
246             $self->setcolour(@$ps_color);
247             } # end subroutine PostScript::Simple::setpscolor definition
248             ########################################################################
249              
250             our %ps_functions = (
251             before => sub {
252             my ($obj, $data) = @_;
253             my $ps = $data->{psobj};
254             $ps->setpscolor($obj->{color});
255             defined($obj->{linewidth}) && $ps->setlinewidth($obj->{linewidth});
256             },
257             after => sub {
258             my ($obj, $data) = @_;
259             my $ps = $data->{psobj};
260             $ps->setpscolor(255);
261             defined($obj->{linewidth}) && $ps->setlinewidth(1);
262             },
263             lines => sub {
264             my ($line, $data) = @_;
265             my $ps = $data->{psobj};
266             my $acc = $data->{accuracy};
267             my @pspts = map({@{$line->{pts}[$_]}[0,1]} 0,1);
268             $ps->line(map({sprintf("%0.${acc}f", $_)} @pspts));
269             },
270             plines => sub {
271             my ($pline, $data) = @_;
272             my $ps = $data->{psobj};
273             my $filled = $data->{filled};
274             my $acc = $data->{accuracy};
275             my @points = map({@{$_}[0,1]} @{$pline->{pts}});
276             foreach my $point (@points) {
277             $point = sprintf("%0.${acc}f", $point);
278             }
279             $pline->{closed} && (push(@points, @points[0,1]));
280             # $pline->{closed} && print "closed polyline\n";
281             # print "points:\n\t", join("\n\t", map({join(",", @{$pline->{pts}})}));
282             $ps->polygon({filled => $filled}, @points);
283             },
284             circles => sub {
285             my ($circ, $data) = @_;
286             my $ps = $data->{psobj};
287             my $filled = $data->{filled};
288             my $acc = $data->{accuracy};
289             my @pt = map({sprintf("%0.${acc}f", $_)} @{$circ->{pt}}[0,1]);
290             my $rad = sprintf("%0.${acc}f", $circ->{rad});
291             $ps->circle({filled=>$filled}, @pt, $rad);
292             },
293             # points are a fake circle:
294             points => sub {
295             my ($circ, $data) = @_;
296             my $ps = $data->{psobj};
297             my $filled = $data->{filled};
298             my $acc = $data->{accuracy};
299             my @pt = map({sprintf("%0.${acc}f", $_)} @{$circ->{pt}}[0,1]);
300             # XXX this is SO lame!
301             my $rad = 0.01;
302             $ps->circle({filled=>$filled}, @pt, $rad);
303             },
304             arcs => sub {
305             my ($arc, $data) = @_;
306             my $ps = $data->{psobj};
307             my $acc = $data->{accuracy};
308             my @pt = map({sprintf("%0.${acc}f", $_)} @{$arc->{pt}}[0,1]);
309             my $rad = sprintf("%0.${acc}f", $arc->{rad});
310             my @angs = map({sprintf("%0.0f", $_ * 180 / $pi)} @{$arc->{angs}});
311             $ps->arc(@pt, $rad, @angs);
312             },
313             texts => sub {
314             my ($text, $data) = @_;
315             my $ps = $data->{psobj};
316             my $acc = $data->{accuracy};
317             my @pt = map({sprintf("%0.${acc}f", $_)} @{$text->{pt}}[0,1]);
318             my $font = $text->{font} ? $text->{font} : $data->{font};
319             $ps->setfont($font, $text->{height});
320             my @call = (@pt, $text->{string});
321             # XXX no rotation support
322             my %options;
323             if($text->{angle}) {
324             $options{rotate} = $text->{angle} * 180 / $pi;
325             }
326             $text->{align} and ($options{align} = $text->{align});
327             $text->{valign} and ($options{valign} = $text->{valign});
328             %options and unshift(@call, \%options);
329             $ps->text(@call);
330             },
331             );
332              
333             1;