File Coverage

lib/Kite/XML2PS.pm
Criterion Covered Total %
statement 7 9 77.7
branch n/a
condition n/a
subroutine 3 3 100.0
pod n/a
total 10 12 83.3


line stmt bran cond sub pod time code
1             #========================================================================
2             # Kite::XML2PS
3             #
4             # DESCRIPTION
5             # Perl module to convert a curve definition from OpenKite XML format
6             # to PostScript, with automatic page tiling and registration mark
7             # control.
8             #
9             # AUTHORS
10             # Simon Stapleton wrote the original xml2ps.pl
11             # utility which performs the XML -> PostScript conversion.
12             #
13             # Andy Wardley re-packaged it into a module for
14             # integration into the Kite bundle.
15             #
16             # COPYRIGHT
17             # Copyright (C) 2000 Simon Stapleton, Andy Wardley. All Rights Reserved.
18             #
19             # This module is free software; you can redistribute it and/or
20             # modify it under the same terms as Perl itself.
21             #
22             # VERSION
23             # $Id: XML2PS.pm,v 1.3 2000/10/17 12:19:28 abw Exp $
24             #
25             #========================================================================
26              
27             package Kite::XML2PS;
28              
29             require 5.004;
30              
31 1     1   3703 use strict;
  1         3  
  1         47  
32 1     1   436 use Kite::Base;
  1         2  
  1         28  
33 1     1   543 use Kite::XML::Parser;
  0            
  0            
34              
35             use base qw( Kite::Base );
36             use vars qw( $VERSION $ERROR $DEBUG $PARAMS );
37              
38             $VERSION = sprintf("%d.%02d", q$Revision: 1.3 $ =~ /(\d+)\.(\d+)/);
39             $DEBUG = 0 unless defined $DEBUG;
40             $ERROR = '';
41              
42             # define parameters for this class, used by Kite::Base init() method
43             $PARAMS = {
44             FILENAME => undef,
45             TITLE => '',
46             REGMARKS => 1,
47             BORDER => 5,
48             MAP => 1,
49             };
50              
51              
52             #------------------------------------------------------------------------
53             # init($config)
54             #
55             # Initialisation method called by the base class new() constructor
56             # method. Calls the base class init() to set any parameters from the
57             # $PARAMS hash and then calls process_file() to process the XML
58             # file to generate internal PATH and IMAGE definitions.
59             #------------------------------------------------------------------------
60              
61             sub init {
62             my ($self, $config) = @_;
63              
64             # call base class to read config params
65             $self->SUPER::init($config)
66             || return undef;
67              
68             # process file
69             $self->process_file()
70             || return undef;
71              
72             # OK
73             return $self;
74             }
75              
76              
77             #------------------------------------------------------------------------
78             # process_file()
79             # process_file($filename)
80             #
81             # Processes the file specified as a parameter, or set internally as
82             # the FILENAME item, reading the XML contained therein and generating
83             # internal PATH and IMAGE definitions which can be retrieved via the
84             # path() and image() methods (handled automatically by the base class
85             # AUTOLOAD method).
86             #------------------------------------------------------------------------
87              
88             sub process_file {
89             my $self = shift;
90             my $file = @_ ? shift : $self->{ FILENAME };
91             my ($parser, $doc);
92              
93             return $self->error('no filename specified')
94             unless defined $file;
95              
96             # parse XML file, trapping any errors thrown via die()
97             $parser = Kite::XML::Parser->new();
98             eval {
99             $doc = $parser->parsefile($file);
100             };
101             return $self->error($@)
102             if $@;
103              
104             my $path = 'newpath ';
105             my $image = '';
106             my ($x, $y, $xt, $yt, $angle, $anglet);
107              
108             $self->{ KITE } = $doc;
109             $self->{ TITLE } ||= $doc->title();
110              
111             # generate PS for each kite part
112             foreach my $part (@{ $doc->part() })
113             {
114             $xt = $part->layout->x() || 0;
115             $yt = $part->layout->y() || 0;
116             $anglet = $part->layout->angle || 0;
117              
118             $image .= "gsave $xt mm $yt mm translate $anglet rotate ";
119             $path .= "$xt mm $yt mm translate $anglet rotate ";
120              
121             # add path segments as a series of PS moveto/lineto ops
122             foreach my $curve (@{ $part->markup->curve })
123             {
124             $image .= "gsave ";
125              
126             my $linetype = $curve->linetype || "normal";
127              
128             if ($linetype eq 'normal') {
129             $image .= "0.5 setlinewidth ";
130             }
131             elsif ($linetype eq 'heavy') {
132             $image .= "0.75 setlinewidth ";
133             }
134             elsif ($linetype eq "light") {
135             $image .= "0.25 setlinewidth ";
136             }
137             elsif ($linetype eq "dotted") {
138             $image .= "0.55 setlinewidth [3 5 1 5] 0 setdash ";
139             }
140              
141             my $incurve = undef;
142              
143             foreach my $point (@{ $curve->point })
144             {
145             $x = $point->x;
146             $y = $point->y;
147              
148             if (defined $incurve)
149             {
150             $image .= "$x mm $y mm lineto ";
151             $path .= "$x mm $y mm lineto ";
152             }
153             else
154             {
155             $image .= "newpath $x mm $y mm moveto ";
156             $path .= "$x mm $y mm moveto ";
157             $incurve = 1;
158             }
159             }
160              
161             # add text using PS pathtext function
162             foreach my $text (@{ $curve->text || [] })
163             {
164             my $font = $text->font || "Helvetica";
165             my $size = $text->size || "6";
166             $text = $text->char;
167             for ($text) { # remove leading and trailing whitespace
168             s/^\s*//;
169             s/\s*$//;
170             }
171             $image .= "gsave /$font findfont $size mm scalefont setfont ";
172             $image .= "($text) 0 pathtext grestore ";
173             }
174             $image .= "stroke grestore ";
175             }
176              
177             # add transformations
178             $path .= "$anglet neg rotate $xt neg mm $yt neg mm translate ";
179             $image .= "grestore ";
180             }
181              
182             # save image and path definitions internally and return happy
183             $self->{ IMAGE } = $image;
184             $self->{ PATH } = $path;
185              
186             return 1;
187             }
188              
189              
190             #------------------------------------------------------------------------
191             # doc()
192             #
193             # Generate a complete PostScript document to print the kite parts,
194             # with automatic multiple page tiling (page-size independant),
195             # registration marks and many other glorious features. Returns the
196             # generated PostScript as a string.
197             #------------------------------------------------------------------------
198              
199             sub doc {
200             my $self = shift;
201            
202             require Kite::PScript::Defs;
203             require Template;
204            
205             my $doc = $self->ps_template();
206             my $template = Template->new( POST_CHOMP => 1);
207             my $vars = {
208             defs => bless { }, 'Kite::PScript::Defs',
209             };
210             my @keys = qw( kite title regmarks border map image path );
211             @$vars{ @keys } = @$self{ map { uc } @keys };
212              
213             my $out = '';
214             $template->process(\$doc, $vars, \$out)
215             || return $self->error($template->error());
216             return $out;
217             }
218              
219             #------------------------------------------------------------------------
220             # ps_template()
221             #
222             # Returns a Template Toolkit template for generating the PostScript.
223             #------------------------------------------------------------------------
224              
225             sub ps_template {
226             return <<'EOF';
227             [% USE fix = format('%.2f') -%]
228             %!PS-Adobe-3.0
229             [% IF title %]
230             %%Title: [% title %]
231             [% END %]
232             %%EndComments
233              
234             [% defs.mm %]
235             [% defs.lines %]
236             [% defs.cross %]
237             [% defs.dot %]
238             [% defs.circle %]
239             [% defs.crop %]
240              
241             /border [% border %] mm def
242             [% defs.clip +%]
243             [% regmarks ? defs.reg : defs.noreg +%]
244             [% defs.tiles +%]
245             [% defs.tilemap +%]
246             [% defs.pathtext %]
247              
248             % define image, path and page procedures for tiling
249             /tileimage {
250             [% image %]
251             } def
252              
253             /tilepath {
254             [% path %]
255             } def
256              
257             /tilepage {
258             regmarks
259             [% IF title %]
260             /Times-Roman findfont 24 scalefont setfont
261             clipblx 3 mm add clipbly 3 mm add moveto
262             ([% title %]) show
263             [% END %]
264             [% " tilemap\n" IF map %]
265             } def
266              
267             tilepath tiles
268             [% defs.dotiles %]
269              
270             EOF
271             }
272            
273             1;
274              
275             __END__