File Coverage

blib/lib/PDF/EasyPDF.pm
Criterion Covered Total %
statement 12 166 7.2
branch 0 18 0.0
condition 0 3 0.0
subroutine 4 37 10.8
pod 31 33 93.9
total 47 257 18.2


line stmt bran cond sub pod time code
1             package PDF::EasyPDF;
2 1     1   24795 use 5.0005;
  1         5  
  1         37  
3 1     1   6 use strict;
  1         2  
  1         30  
4 1     1   5 use warnings;
  1         6  
  1         79  
5             require Exporter;
6             our @ISA = qw(Exporter);
7             our @EXPORT = qw(inch mm);
8             our $VERSION = 0.04;
9              
10 1     1   1048 use utf8;
  1         9  
  1         5  
11              
12             =head1 NAME
13              
14             PDF::EasyPDF - PDF creation from a one-file module, with postscript-like controls
15              
16             =head1 SYNOPSIS
17              
18             use PDF::EasyPDF;
19              
20             my $pdf = PDF::EasyPDF->new({file=>"mypdffile.pdf",x=>mm(297),y=>mm(210)});
21              
22             $pdf->setStrokeColor("CC0000");
23              
24             $pdf->setStrokeWidth(8);
25              
26             $pdf->rectangle(mm(10),mm(10),mm(297-20),mm(210-20));
27              
28             $pdf->setFillColor("FFCC00");
29              
30             $pdf->filledRectangle(mm(20),mm(20),mm(297-40),mm(210-40));
31              
32             $pdf->setFillColor("CC0000");
33              
34             $pdf->setFontFamily("Helvetica-Bold");
35              
36             $pdf->setFontSize(24);
37              
38             $pdf->text(mm(105),mm(210-22.5),"PDF::EasyPDF Demo");
39              
40             $pdf->lines(mm(85),mm(35),mm(90),mm(105),mm(95),mm(35),mm(100),mm(105),mm(105),mm(35),mm(110),mm(105));
41              
42             $pdf->setStrokeColor("000099");
43              
44             $pdf->curve(300,300,300,400,400,400,400,300);
45              
46             $pdf->setStrokeColor("0066FF");
47              
48             $pdf->setFillColor("00FFFF");
49              
50             $pdf->polygon(100,100,250,200,250,400,200,500);
51              
52             $pdf->filledPolygon(100,100,250,200,250,400,200,500);
53              
54             $pdf->close;
55              
56             =head1 DESCRIPTION
57              
58             This module started life as a workaround, on discovering that PDF::API2 and friends are extremely tricky to compile using Activestate's PerlApp utility because of the large number of runtime modules and resource files they use. The module consists of a single .pm file. It produces small PDF files, partly because it only uses the 14 standard PDF fonts. Page content is implemented using a single stream object, and the controls are vaguely postscript-like.
59              
60             =head1 AUTHOR
61              
62             Mark Howe, Emelonman@cpan.orgE
63              
64             =head2 EXPORT
65              
66             The C and C functions.
67              
68             =cut
69              
70             my $fonts = {"Times-Roman" => "TIM",
71             "Times-Bold" => "TIMB",
72             "Times-Italic" => "TIMI",
73             "Times-BoldItalic" => "TIMBI",
74             "Helvetica" => "HEL",
75             "Helvetica-Bold" => "HELB",
76             "Helvetica-Oblique" => "HELO",
77             "Helvetica-BoldOblique" => "HELBO",
78             "Courier" => "COU",
79             "Courier-Bold" => "COUB",
80             "Courier-Oblique" => "COUO",
81             "Courier-BoldOblique" =>"COUBO",
82             "Symbol" => "SYM",
83             "ZapfDingbats" => "ZAP"};
84              
85             my $standard_objects = <
86             1 0 obj
87             << /Type /Catalog
88             /Outlines 2 0 R
89             /Pages 3 0 R
90             >>
91             endobj
92              
93             2 0 obj
94             << /Type Outlines
95             /Count 0
96             >>
97             endobj
98              
99             3 0 obj
100             << /Type /Pages
101             /Kids [4 0 R]
102             /Count 1
103             >>
104             endobj
105              
106             4 0 obj
107             << /Type /Page
108             /Parent 3 0 R
109             /MediaBox [0 0 !!X!! !!Y!!]
110             /Contents 20 0 R
111             /Resources << /ProcSet 5 0 R
112             /Font << /TIM 6 0 R
113             /TIMB 7 0 R
114             /TIMI 8 0 R
115             /TIMBI 9 0 R
116             /HEL 10 0 R
117             /HELB 11 0 R
118             /HELO 12 0 R
119             /HELBO 13 0 R
120             /COU 14 0 R
121             /COUB 15 0 R
122             /COUO 16 0 R
123             /COUBO 17 0 R
124             /SYM 18 0 R
125             /ZAP 19 0 R
126             >>
127             >>
128             >>
129             endobj
130              
131             5 0 obj
132             [/PDF /Text]
133             endobj
134              
135             6 0 obj
136             << /Type /Font
137             /Subtype /Type1
138             /Name /TIM
139             /BaseFont /Times-Roman
140             /Encoding /MacRomanEncoding
141             >>
142             endobj
143              
144             7 0 obj
145             << /Type /Font
146             /Subtype /Type1
147             /Name /TIMB
148             /BaseFont /Times-Bold
149             /Encoding /MacRomanEncoding
150             >>
151             endobj
152              
153             8 0 obj
154             << /Type /Font
155             /Subtype /Type1
156             /Name /TIMI
157             /BaseFont /Times-Italic
158             /Encoding /MacRomanEncoding
159             >>
160             endobj
161              
162             9 0 obj
163             << /Type /Font
164             /Subtype /Type1
165             /Name /TIMBI
166             /BaseFont /Times-BoldItalic
167             /Encoding /MacRomanEncoding
168             >>
169             endobj
170              
171             10 0 obj
172             << /Type /Font
173             /Subtype /Type1
174             /Name /HEL
175             /BaseFont /Helvetica
176             /Encoding /MacRomanEncoding
177             >>
178             endobj
179              
180             11 0 obj
181             << /Type /Font
182             /Subtype /Type1
183             /Name /HELB
184             /BaseFont /Helvetica-Bold
185             /Encoding /MacRomanEncoding
186             >>
187             endobj
188              
189             12 0 obj
190             << /Type /Font
191             /Subtype /Type1
192             /Name /HELO
193             /BaseFont /Helvetica-Oblique
194             /Encoding /MacRomanEncoding
195             >>
196             endobj
197              
198             13 0 obj
199             << /Type /Font
200             /Subtype /Type1
201             /Name /HELBO
202             /BaseFont /Helvetica-BoldOblique
203             /Encoding /MacRomanEncoding
204             >>
205             endobj
206              
207             14 0 obj
208             << /Type /Font
209             /Subtype /Type1
210             /Name /COU
211             /BaseFont /Courier
212             /Encoding /MacRomanEncoding
213             >>
214             endobj
215              
216             15 0 obj
217             << /Type /Font
218             /Subtype /Type1
219             /Name /COUB
220             /BaseFont /Courier-Bold
221             /Encoding /MacRomanEncoding
222             >>
223             endobj
224              
225             16 0 obj
226             << /Type /Font
227             /Subtype /Type1
228             /Name /COUO
229             /BaseFont /Courier-Oblique
230             /Encoding /MacRomanEncoding
231             >>
232             endobj
233              
234             17 0 obj
235             << /Type /Font
236             /Subtype /Type1
237             /Name /COUBO
238             /BaseFont /Courier-BoldOblique
239             /Encoding /MacRomanEncoding
240             >>
241             endobj
242              
243             18 0 obj
244             << /Type /Font
245             /Subtype /Type1
246             /Name /SYM
247             /BaseFont /Symbol
248             /Encoding /MacRomanEncoding
249             >>
250             endobj
251              
252             19 0 obj
253             << /Type /Font
254             /Subtype /Type1
255             /Name /ZAP
256             /BaseFont /ZapfDingbats
257             /Encoding /MacRomanEncoding
258             >>
259             endobj
260              
261             STANDARD_OBJECTS
262              
263             my $content_object = <
264             20 0 obj
265             << /Length !!LENGTH!! >>
266             stream
267             !!STREAM!!endstream
268             endobj
269             CONTENT_OBJECT
270              
271             =head1 METHODS
272              
273             =cut
274              
275             =head2 new({file, x, y})
276              
277             Creates a new PDF::EasyPDF object. The arguments are passed as an anonymous hash to allow, eventually, for different combinations of arguments. I is the name of the PDF to be created (although nothing is output until the C method is called. I and I are the x and y dimensions of the page in points (see the C and C functions for a more convenient way to specify page sizes).
278              
279             =cut
280              
281             sub new
282 0     0 1   {my $type = shift;
283 0           my $hash = shift;
284 0           my $self={};
285 0           my @args = ('file','x','y');
286 0           foreach my $arg (@args)
  0            
287             {$self->{$arg} = $hash->{$arg}};
288 0           $self->{stream} = "";
289 0           $self->{font_name} = $fonts->{Courier};
290 0           $self->{font_size} = 10;
291 0           bless($self,$type);
292 0           return $self};
293              
294             =head2 close()
295              
296             Writes a pdf file.
297              
298             =cut
299              
300             sub close
301 0     0 1   {my $self = shift;
302 0           my @offsets = ();
303 0           my $out="%PDF-1.4\n";
304 0           foreach my $ob (split /\n\n+/,$standard_objects . $self->content_object)
  0            
305 0 0         {if
306             ($ob =~/!!LENGTH!!/)
307             {$ob=~/stream\n(.*)endstream/s;
308 0           my $length=length($1);
309 0           $ob=~s/!!LENGTH!!/$length/e};
  0            
310 0           $ob=~s/!!X!!/int($self->{x}+0.5)/e;
  0            
311 0           $ob=~s/!!Y!!/int($self->{y}+0.5)/e;
  0            
312 0           push @offsets,length($out);
313 0           $out .= "$ob\n\n"};
314 0           my $xrefoffset = length($out);
315 0           $out .= sprintf "xref\n0 %i\n0000000000 65535 f \n",$#offsets+2;
316 0           foreach (@offsets)
  0            
317             {$out .= sprintf "%10.10i 00000 n \n",$_}
318 0           $out .= sprintf "\n\ntrailer\n<< /Size %i\n /Root 1 0 R\n>>\nstartxref\n$xrefoffset\n%%%%EOF",$#offsets+2;
319 0 0         open (EASYPDF,">$self->{file}") or die "EasyPDF could not write PDF file '$self->{file}' : $!";
320 0           print EASYPDF $out;
321 0           close EASYPDF}
322              
323             sub content_object
324 0     0 0   {my $self = shift;
325 0           my $ret=$content_object;
326 0           $ret =~s/!!STREAM!!/$self->{stream}/s;
327 0           return $ret}
328              
329             =head2 fonts
330              
331             Returns a list of supported fonts (currently the fourteen standard Adobe fonts).
332              
333             =cut
334              
335             sub fonts
336 0     0 1   {return sort keys %{$fonts}}
  0            
337              
338             =head2 setStrokeColor(rrggbb);
339              
340             Sets the stroke (more or less 'line') colour using an html-like rrggbb string, ie C = bright yellow.
341              
342             =cut
343              
344             sub setStrokeColor
345 0     0 1   {my $self = shift;
346 0           my ($r,$g,$b) = rrggbb(shift);
347 0           $self->{stream} .= "$r $g $b RG\n"}
348              
349             =head2 setFillColor(rrggbb)
350              
351             Sets the fill colour (including the text colour).
352              
353             =cut
354              
355             sub setFillColor
356 0     0 1   {my $self = shift;
357 0           my ($r,$g,$b) = rrggbb(shift);
358 0           $self->{stream} .= "$r $g $b rg\n"}
359              
360             sub rrggbb
361 0     0 0   {my $hexstring = shift;
362 0           $hexstring =~/([0-9A-F][0-9A-F])([0-9A-F][0-9A-F])([0-9A-F][0-9A-F])/i;
363 0           return (hex($1)/255,hex($2)/255,hex($3)/255)}
364              
365             =head2 setStrokeWidth(width)
366              
367             Sets the stroke (more or less 'line') width in points.
368              
369             =cut
370              
371             sub setStrokeWidth
372 0     0 1   {my $self = shift;
373 0           my $w = shift;
374 0           $self->{stream} .= "$w w\n"}
375              
376             =head2 setFontFamily(fontname)
377              
378             Sets the font.
379              
380             =cut
381              
382             sub setFontFamily
383 0     0 1   {my $self = shift;
384 0           my $font = shift;
385 0 0         die "Unknown font '$font'" unless defined $fonts->{$font};
386 0           $self->{font_name} = $fonts->{$font}}
387              
388             =head2 setFontSize(size)
389              
390             Sets the font size in points
391              
392             =cut
393              
394             sub setFontSize
395 0     0 1   {my $self = shift;
396 0           my $size = shift;
397 0           $size+=0;
398 0 0         die "Bad font size '$size'" unless $size > 0;
399 0           $self->{font_size} = $size}
400              
401             =head2 setDash(lengths)
402              
403             Sets the dash pattern. Pass a list of numbers to set the alternating 'on' and 'off' lengths in points, or, with no arguments, to reset to a solid line
404              
405             =cut
406              
407             sub setDash
408 0     0 1   {my $self = shift;
409 0 0         if
410 0           (defined $_[1])
411 0           {$self->{stream}.= "[ ";
412 0           while
413 0           (@_)
414             {$self->{stream}.= shift(@_) . " "};
415 0           $self->{stream} .= "] 0 d\n"}
416             else
417             {$self->{stream} .= "[] 0 d\n"}}
418              
419             =head2 setCap(style)
420              
421             Set the cap style for the ends of lines. Options are C, C or C.
422              
423             =cut
424              
425             sub setCap
426 0     0 1   {my $self = shift;
427 0           my $captype = shift;
428 0 0 0       if
    0          
429 0           (lc($captype) eq 'round')
430 0           {$self->{stream}.= "1 J\n"}
431             elsif
432             ((lc($captype) eq 'square') or (lc($captype) eq 'projecting'))
433 0           {$self->{stream} .= "2 J\n"}
434             else
435             {$self->{stream} .= "0 J\n"}}
436              
437             =head2 setJoin(style)
438              
439             Set the join style for lines. Options are C, C or C.
440              
441             =cut
442              
443             sub setJoin
444 0     0 1   {my $self = shift;
445 0           my $captype = shift;
446 0 0         if
    0          
447 0           (lc($captype) eq 'round')
448 0           {$self->{stream}.= "1 j\n"}
449             elsif
450             (lc($captype) eq 'bevel')
451 0           {$self->{stream} .= "2 j\n"}
452             else
453             {$self->{stream} .= "0 j\n"}}
454              
455             =head2 text(x,y,string)
456              
457             Places text at x,y
458              
459             =cut
460              
461             sub text
462 0     0 1   {my $self = shift;
463 0           my ($x,$y,$text) = @_;
464 0           $self->{stream} .="BT\n/$self->{font_name} $self->{font_size} Tf\n$x $y Td\n($text) Tj\nET\n"}
465              
466             =head2 lines(x1,y1,x2,y2, ...)
467              
468             Prints one or more lines, using alternative x and y coordinates.
469              
470             =cut
471              
472             sub lines
473 0     0 1   {my $self = shift;
474 0           my $startx = shift;
475 0           my $starty = shift;
476 0           $self->{stream} .= "$startx $starty m\n";
477 0           while
478 0           (@_)
479             {my $nextx = shift(@_);
480 0           my $nexty = shift(@_);
481 0           $self->{stream} .= "$nextx $nexty l\n"};
482 0           $self->{stream} .= "S\n"}
483              
484             =head2 polygon(x1,y1,x2,y2, ...)
485              
486             Prints a closed, unfilled polygon using alternative x and y coordinates.
487              
488             =cut
489              
490             sub polygon
491 0     0 1   {my $self = shift;
492 0           my $startx = shift;
493 0           my $starty = shift;
494 0           $self->{stream} .= "$startx $starty m\n";
495 0           while
496 0           (@_)
497             {my $nextx = shift(@_);
498 0           my $nexty = shift(@_);
499 0           $self->{stream} .= "$nextx $nexty l\n"};
500 0           $self->{stream} .= "h\nS\n"}
501              
502             =head2 filledPolygon(x1,y1,x2,y2, ...)
503              
504             Prints a closed, filled polygon with no border using alternative x and y coordinates.
505              
506             =cut
507              
508             sub filledPolygon
509 0     0 1   {my $self = shift;
510 0           my $startx = shift;
511 0           my $starty = shift;
512 0           $self->{stream} .= "$startx $starty m\n";
513 0           while
514 0           (@_)
515             {my $nextx = shift(@_);
516 0           my $nexty = shift(@_);
517 0           $self->{stream} .= "$nextx $nexty l\n"};
518 0           $self->{stream} .= "h\nf\n"}
519              
520             =head2 curve(x1,y1,x2,y2,x3,y3,x4,y4)
521              
522             Prints a bezier curve.
523              
524             =cut
525              
526             sub curve
527 0     0 1   {my $self = shift;
528 0           my $startx = shift;
529 0           my $starty = shift;
530 0           $self->{stream} .= "$startx $starty m\n$_[0] $_[1] $_[2] $_[3] $_[4] $_[5] c\nS\n"}
531              
532             =head2 filledCurve(x1,y1,x2,y2,x3,y3,x4,y4)
533              
534             Prints a filled bezier curve without a border.
535              
536             =cut
537              
538             sub filledCurve
539 0     0 1   {my $self = shift;
540 0           my $startx = shift;
541 0           my $starty = shift;
542 0           $self->{stream} .= "$startx $starty m\n$_[0] $_[1] $_[2] $_[3] $_[4] $_[5] c\nh\nf\n"}
543              
544             =head2 closedCurve(x1,y1,x2,y2,x3,y3,x4,y4)
545              
546             Prints an unfilled bezier curve, with the first and last points joined by a straight line.
547              
548             =cut
549              
550             sub closedCurve
551 0     0 1   {my $self = shift;
552 0           my $startx = shift;
553 0           my $starty = shift;
554 0           $self->{stream} .= "$startx $starty m\n$_[0] $_[1] $_[2] $_[3] $_[4] $_[5] c\nh\nS\n"}
555              
556             =head2 moveSegment(x,y)
557              
558             Inserts a move operation (use to start new paths)
559              
560             =cut
561              
562             sub moveSegment
563 0     0 1   {my $self = shift;
564 0           my $x = shift;
565 0           my $y = shift;
566 0           $self->{stream} .= "$x $y m\n"}
567              
568             =head2 lineSegment(x,y)
569              
570             Inserts a line segment
571              
572             =cut
573              
574             sub lineSegment
575 0     0 1   {my $self = shift;
576 0           my $x = shift;
577 0           my $y = shift;
578 0           $self->{stream} .= "$x $y l\n"}
579              
580             =head2 curveSegment(x,y)
581              
582             Inserts a curve segment
583              
584             =cut
585              
586             sub curveSegment
587 0     0 1   {my $self = shift;
588 0           $self->{stream} .= "$_[0] $_[1] $_[2] $_[3] $_[4] $_[5] c\n"}
589              
590             =head2 closePath()
591              
592             Closes a path
593              
594             =cut
595              
596             sub closePath
597 0     0 1   {my $self = shift;
598 0           $self->{stream} .= "h\n"}
599              
600             =head2 strokePath()
601              
602             Strokes the path
603              
604             =cut
605              
606             sub strokePath
607 0     0 1   {my $self = shift;
608 0           $self->{stream} .= "S\n"}
609              
610             =head2 fillPath()
611              
612             Fills the path using the non-zero winding number rule
613              
614             =cut
615              
616             sub fillPath
617 0     0 1   {my $self = shift;
618 0           $self->{stream} .= "f\n"}
619              
620             =head2 fillStarPath()
621              
622             Fills the path using the odd-even winding rule (f*, hence the 'star')
623              
624             =cut
625              
626             sub fillStarPath
627 0     0 1   {my $self = shift;
628 0           $self->{stream} .= "f*\n"}
629              
630             =head2 fillAndStrokePath()
631              
632             Fills the path using the non-zero winding number rule and then strokes it
633              
634             =cut
635              
636             sub fillAndStrokePath
637 0     0 1   {my $self = shift;
638 0           $self->{stream} .= "B\n"}
639              
640             =head2 fillStarAndStrokePath()
641              
642             Fills the path using the odd-even winding rule and then fills it (B*, hence the 'star')
643              
644             =cut
645              
646             sub fillStarAndStrokePath
647 0     0 1   {my $self = shift;
648 0           $self->{stream} .= "B*\n"}
649              
650             =head2 rectangle(x1,y1,xsize,ysize)
651              
652             Prints an unfilled rectangle.
653              
654             =cut
655              
656             sub rectangle
657 0     0 1   {my $self = shift;
658 0           my ($x,$y,$dx,$dy) = @_;
659 0           $self->{stream} .="$x $y $dx $dy re\nS\n"}
660              
661             =head2 filledRectangle(x1,y1,xsize,ysize)
662              
663             Prints a filled rectangle with no border.
664              
665             =cut
666              
667             sub filledRectangle
668 0     0 1   {my $self = shift;
669 0           my ($x,$y,$dx,$dy) = @_;
670 0           $self->{stream} .="$x $y $dx $dy re\nF\n"}
671              
672             =head1 FUNCTIONS
673              
674             =head2 inch(inches)
675              
676             Converts inches into points
677              
678             =cut
679              
680             sub inch
681 0     0 1   {my $inches = shift;
682 0           return $inches * 72}
683              
684             =head2 mm(mms)
685              
686             Converts millimetres into points
687              
688             =cut
689              
690             sub mm
691 0     0 1   {my $mm = shift;
692 0           return ($mm/25.4) * 72}
693              
694             =head1 BUGS
695              
696             None known, but the methods do relatively little sanity checking, and there is absolutely no encoding yet for text (so it's probably impossible to print parentheses, for example).
697              
698             =head1 COMING SOON
699              
700             A first stab at encoding text, arrowheads.
701              
702             =head1 PREVIOUS VERSIONS
703              
704             B<0.04>: Consistent capitalisation of methods, generic arbitrary path drawing mechanism.
705             B<0.03>: Beat module into something approaching standard CPAN shape.
706              
707             =head1 COPYRIGHT AND LICENSE
708              
709             Copyright (C) 2006 by Mark Howe
710              
711             This library is free software; you can redistribute it and/or modify
712             it under the same terms as Perl itself, either Perl version 5.8.5 or,
713             at your option, any later version of Perl 5 you may have available.
714              
715             =cut
716              
717             1;