| PDF-EasyPDF documentation | Contained in the PDF-EasyPDF distribution. |
PDF::EasyPDF - PDF creation from a one-file module, with postscript-like controls
use PDF::EasyPDF;
my $pdf = PDF::EasyPDF->new({file=>"mypdffile.pdf",x=>mm(297),y=>mm(210)});
$pdf->setStrokeColor("CC0000");
$pdf->setStrokeWidth(8);
$pdf->rectangle(mm(10),mm(10),mm(297-20),mm(210-20));
$pdf->setFillColor("FFCC00");
$pdf->filledRectangle(mm(20),mm(20),mm(297-40),mm(210-40));
$pdf->setFillColor("CC0000");
$pdf->setFontFamily("Helvetica-Bold");
$pdf->setFontSize(24);
$pdf->text(mm(105),mm(210-22.5),"PDF::EasyPDF Demo");
$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));
$pdf->setStrokeColor("000099");
$pdf->curve(300,300,300,400,400,400,400,300);
$pdf->setStrokeColor("0066FF");
$pdf->setFillColor("00FFFF");
$pdf->polygon(100,100,250,200,250,400,200,500);
$pdf->filledPolygon(100,100,250,200,250,400,200,500);
$pdf->close;
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.
Mark Howe, <melonman@cpan.org>
The mm and inch functions.
Creates a new PDF::EasyPDF object. The arguments are passed as an anonymous hash to allow, eventually, for different combinations of arguments. filename is the name of the PDF to be created (although nothing is output until the close method is called. x and y are the x and y dimensions of the page in points (see the mm and inch functions for a more convenient way to specify page sizes).
Writes a pdf file.
Returns a list of supported fonts (currently the fourteen standard Adobe fonts).
Sets the stroke (more or less 'line') colour using an html-like rrggbb string, ie FFFF00 = bright yellow.
Sets the fill colour (including the text colour).
Sets the stroke (more or less 'line') width in points.
Sets the font.
Sets the font size in points
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
Set the cap style for the ends of lines. Options are round, square or butt.
Set the join style for lines. Options are round, bevel or miter.
Places text at x,y
Prints one or more lines, using alternative x and y coordinates.
Prints a closed, unfilled polygon using alternative x and y coordinates.
Prints a closed, filled polygon with no border using alternative x and y coordinates.
Prints a bezier curve.
Prints a filled bezier curve without a border.
Prints an unfilled bezier curve, with the first and last points joined by a straight line.
Inserts a move operation (use to start new paths)
Inserts a line segment
Inserts a curve segment
Closes a path
Strokes the path
Fills the path using the non-zero winding number rule
Fills the path using the odd-even winding rule (f*, hence the 'star')
Fills the path using the non-zero winding number rule and then strokes it
Fills the path using the odd-even winding rule and then fills it (B*, hence the 'star')
Prints an unfilled rectangle.
Prints a filled rectangle with no border.
Converts inches into points
Converts millimetres into points
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).
A first stab at encoding text, arrowheads.
0.04: Consistent capitalisation of methods, generic arbitrary path drawing mechanism. 0.03: Beat module into something approaching standard CPAN shape.
Copyright (C) 2006 by Mark Howe
This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.8.5 or, at your option, any later version of Perl 5 you may have available.
| PDF-EasyPDF documentation | Contained in the PDF-EasyPDF distribution. |
package PDF::EasyPDF; use 5.0005; use strict; use warnings; require Exporter; our @ISA = qw(Exporter); our @EXPORT = qw(inch mm); our $VERSION = 0.04; use utf8;
my $fonts = {"Times-Roman" => "TIM", "Times-Bold" => "TIMB", "Times-Italic" => "TIMI", "Times-BoldItalic" => "TIMBI", "Helvetica" => "HEL", "Helvetica-Bold" => "HELB", "Helvetica-Oblique" => "HELO", "Helvetica-BoldOblique" => "HELBO", "Courier" => "COU", "Courier-Bold" => "COUB", "Courier-Oblique" => "COUO", "Courier-BoldOblique" =>"COUBO", "Symbol" => "SYM", "ZapfDingbats" => "ZAP"}; my $standard_objects = <<STANDARD_OBJECTS; 1 0 obj << /Type /Catalog /Outlines 2 0 R /Pages 3 0 R >> endobj 2 0 obj << /Type Outlines /Count 0 >> endobj 3 0 obj << /Type /Pages /Kids [4 0 R] /Count 1 >> endobj 4 0 obj << /Type /Page /Parent 3 0 R /MediaBox [0 0 !!X!! !!Y!!] /Contents 20 0 R /Resources << /ProcSet 5 0 R /Font << /TIM 6 0 R /TIMB 7 0 R /TIMI 8 0 R /TIMBI 9 0 R /HEL 10 0 R /HELB 11 0 R /HELO 12 0 R /HELBO 13 0 R /COU 14 0 R /COUB 15 0 R /COUO 16 0 R /COUBO 17 0 R /SYM 18 0 R /ZAP 19 0 R >> >> >> endobj 5 0 obj [/PDF /Text] endobj 6 0 obj << /Type /Font /Subtype /Type1 /Name /TIM /BaseFont /Times-Roman /Encoding /MacRomanEncoding >> endobj 7 0 obj << /Type /Font /Subtype /Type1 /Name /TIMB /BaseFont /Times-Bold /Encoding /MacRomanEncoding >> endobj 8 0 obj << /Type /Font /Subtype /Type1 /Name /TIMI /BaseFont /Times-Italic /Encoding /MacRomanEncoding >> endobj 9 0 obj << /Type /Font /Subtype /Type1 /Name /TIMBI /BaseFont /Times-BoldItalic /Encoding /MacRomanEncoding >> endobj 10 0 obj << /Type /Font /Subtype /Type1 /Name /HEL /BaseFont /Helvetica /Encoding /MacRomanEncoding >> endobj 11 0 obj << /Type /Font /Subtype /Type1 /Name /HELB /BaseFont /Helvetica-Bold /Encoding /MacRomanEncoding >> endobj 12 0 obj << /Type /Font /Subtype /Type1 /Name /HELO /BaseFont /Helvetica-Oblique /Encoding /MacRomanEncoding >> endobj 13 0 obj << /Type /Font /Subtype /Type1 /Name /HELBO /BaseFont /Helvetica-BoldOblique /Encoding /MacRomanEncoding >> endobj 14 0 obj << /Type /Font /Subtype /Type1 /Name /COU /BaseFont /Courier /Encoding /MacRomanEncoding >> endobj 15 0 obj << /Type /Font /Subtype /Type1 /Name /COUB /BaseFont /Courier-Bold /Encoding /MacRomanEncoding >> endobj 16 0 obj << /Type /Font /Subtype /Type1 /Name /COUO /BaseFont /Courier-Oblique /Encoding /MacRomanEncoding >> endobj 17 0 obj << /Type /Font /Subtype /Type1 /Name /COUBO /BaseFont /Courier-BoldOblique /Encoding /MacRomanEncoding >> endobj 18 0 obj << /Type /Font /Subtype /Type1 /Name /SYM /BaseFont /Symbol /Encoding /MacRomanEncoding >> endobj 19 0 obj << /Type /Font /Subtype /Type1 /Name /ZAP /BaseFont /ZapfDingbats /Encoding /MacRomanEncoding >> endobj STANDARD_OBJECTS my $content_object = <<CONTENT_OBJECT; 20 0 obj << /Length !!LENGTH!! >> stream !!STREAM!!endstream endobj CONTENT_OBJECT
sub new {my $type = shift; my $hash = shift; my $self={}; my @args = ('file','x','y'); foreach my $arg (@args) {$self->{$arg} = $hash->{$arg}}; $self->{stream} = ""; $self->{font_name} = $fonts->{Courier}; $self->{font_size} = 10; bless($self,$type); return $self};
sub close {my $self = shift; my @offsets = (); my $out="%PDF-1.4\n"; foreach my $ob (split /\n\n+/,$standard_objects . $self->content_object) {if ($ob =~/!!LENGTH!!/) {$ob=~/stream\n(.*)endstream/s; my $length=length($1); $ob=~s/!!LENGTH!!/$length/e}; $ob=~s/!!X!!/int($self->{x}+0.5)/e; $ob=~s/!!Y!!/int($self->{y}+0.5)/e; push @offsets,length($out); $out .= "$ob\n\n"}; my $xrefoffset = length($out); $out .= sprintf "xref\n0 %i\n0000000000 65535 f \n",$#offsets+2; foreach (@offsets) {$out .= sprintf "%10.10i 00000 n \n",$_} $out .= sprintf "\n\ntrailer\n<< /Size %i\n /Root 1 0 R\n>>\nstartxref\n$xrefoffset\n%%%%EOF",$#offsets+2; open (EASYPDF,">$self->{file}") or die "EasyPDF could not write PDF file '$self->{file}' : $!"; print EASYPDF $out; close EASYPDF} sub content_object {my $self = shift; my $ret=$content_object; $ret =~s/!!STREAM!!/$self->{stream}/s; return $ret}
sub fonts {return sort keys %{$fonts}}
sub setStrokeColor {my $self = shift; my ($r,$g,$b) = rrggbb(shift); $self->{stream} .= "$r $g $b RG\n"}
sub setFillColor {my $self = shift; my ($r,$g,$b) = rrggbb(shift); $self->{stream} .= "$r $g $b rg\n"} sub rrggbb {my $hexstring = shift; $hexstring =~/([0-9A-F][0-9A-F])([0-9A-F][0-9A-F])([0-9A-F][0-9A-F])/i; return (hex($1)/255,hex($2)/255,hex($3)/255)}
sub setStrokeWidth {my $self = shift; my $w = shift; $self->{stream} .= "$w w\n"}
sub setFontFamily {my $self = shift; my $font = shift; die "Unknown font '$font'" unless defined $fonts->{$font}; $self->{font_name} = $fonts->{$font}}
sub setFontSize {my $self = shift; my $size = shift; $size+=0; die "Bad font size '$size'" unless $size > 0; $self->{font_size} = $size}
sub setDash {my $self = shift; if (defined $_[1]) {$self->{stream}.= "[ "; while (@_) {$self->{stream}.= shift(@_) . " "}; $self->{stream} .= "] 0 d\n"} else {$self->{stream} .= "[] 0 d\n"}}
sub setCap {my $self = shift; my $captype = shift; if (lc($captype) eq 'round') {$self->{stream}.= "1 J\n"} elsif ((lc($captype) eq 'square') or (lc($captype) eq 'projecting')) {$self->{stream} .= "2 J\n"} else {$self->{stream} .= "0 J\n"}}
sub setJoin {my $self = shift; my $captype = shift; if (lc($captype) eq 'round') {$self->{stream}.= "1 j\n"} elsif (lc($captype) eq 'bevel') {$self->{stream} .= "2 j\n"} else {$self->{stream} .= "0 j\n"}}
sub text {my $self = shift; my ($x,$y,$text) = @_; $self->{stream} .="BT\n/$self->{font_name} $self->{font_size} Tf\n$x $y Td\n($text) Tj\nET\n"}
sub lines {my $self = shift; my $startx = shift; my $starty = shift; $self->{stream} .= "$startx $starty m\n"; while (@_) {my $nextx = shift(@_); my $nexty = shift(@_); $self->{stream} .= "$nextx $nexty l\n"}; $self->{stream} .= "S\n"}
sub polygon {my $self = shift; my $startx = shift; my $starty = shift; $self->{stream} .= "$startx $starty m\n"; while (@_) {my $nextx = shift(@_); my $nexty = shift(@_); $self->{stream} .= "$nextx $nexty l\n"}; $self->{stream} .= "h\nS\n"}
sub filledPolygon {my $self = shift; my $startx = shift; my $starty = shift; $self->{stream} .= "$startx $starty m\n"; while (@_) {my $nextx = shift(@_); my $nexty = shift(@_); $self->{stream} .= "$nextx $nexty l\n"}; $self->{stream} .= "h\nf\n"}
sub curve {my $self = shift; my $startx = shift; my $starty = shift; $self->{stream} .= "$startx $starty m\n$_[0] $_[1] $_[2] $_[3] $_[4] $_[5] c\nS\n"}
sub filledCurve {my $self = shift; my $startx = shift; my $starty = shift; $self->{stream} .= "$startx $starty m\n$_[0] $_[1] $_[2] $_[3] $_[4] $_[5] c\nh\nf\n"}
sub closedCurve {my $self = shift; my $startx = shift; my $starty = shift; $self->{stream} .= "$startx $starty m\n$_[0] $_[1] $_[2] $_[3] $_[4] $_[5] c\nh\nS\n"}
sub moveSegment {my $self = shift; my $x = shift; my $y = shift; $self->{stream} .= "$x $y m\n"}
sub lineSegment {my $self = shift; my $x = shift; my $y = shift; $self->{stream} .= "$x $y l\n"}
sub curveSegment {my $self = shift; $self->{stream} .= "$_[0] $_[1] $_[2] $_[3] $_[4] $_[5] c\n"}
sub closePath {my $self = shift; $self->{stream} .= "h\n"}
sub strokePath {my $self = shift; $self->{stream} .= "S\n"}
sub fillPath {my $self = shift; $self->{stream} .= "f\n"}
sub fillStarPath {my $self = shift; $self->{stream} .= "f*\n"}
sub fillAndStrokePath {my $self = shift; $self->{stream} .= "B\n"}
sub fillStarAndStrokePath {my $self = shift; $self->{stream} .= "B*\n"}
sub rectangle {my $self = shift; my ($x,$y,$dx,$dy) = @_; $self->{stream} .="$x $y $dx $dy re\nS\n"}
sub filledRectangle {my $self = shift; my ($x,$y,$dx,$dy) = @_; $self->{stream} .="$x $y $dx $dy re\nF\n"}
sub inch {my $inches = shift; return $inches * 72}
sub mm {my $mm = shift; return ($mm/25.4) * 72}
1;