Oh, well, this is just another rewrite of a program that outputs commercial labels as PDF files, but could be usefull, and stills cool. Hope somebuddy here enjoys it...
OK, enough talk. To the code:
##################################################
package Tag::Builder;
##################################################
use strict;
use PDF::API2;
use base 'Class::Accessor';
use constant UNIT => 2.83462;
# Revision control;
$__PACKAGE__::VERSION = '0.01';
# Accessor methods
__PACKAGE__->mk_accessors( qw( page tag data colwidth rowheight pdf
font printGridLines
)
);
sub new{
my ( $class, $page, $tag, $data ) = ( shift, shift, shift, shift );
my $self = bless { page => $page,
tag => $tag,
data => $data
}, $class;
# Convert between metric system and PDF measurement system.
map { $self->page->{$_} = $self->page->{$_} * UNIT }
keys %{$self->page};
$self->tag->{vspace} *= UNIT;
$self->tag->{hspace} *= UNIT;
# Calculate tag dimension
$self->recalculateTagDimensions;
# Create a new PDF aggregate object.
$self->pdf( new PDF::API2 );
## Font handling
$self->font( {} );
$self->font->{plain} = $self->pdf->corefont( 'Helvetica', 1 );
$self->font->{bold} = $self->pdf->corefont( 'Helvetica-Bold', 1 );
map $_->encode( 'latin1' ), values %{ $self->font };
# Set to true to ask module to print grid lines.
# Setted to false is default, we don't need grid
# lines (this is a tag sheet, not a common paper
# sheet)
$self->printGridLines( undef );
return $self;
}
sub recalculateTagDimensions{
my $self = shift;
# Calculates the column width.
$self->colwidth( ( $self->page->{width}
- $self->page->{leftmargin}
- $self->page->{rightmargin}
- ( $self->tag->{columns} - 1 )
* $self->tag->{hspace}
) / $self->tag->{columns}
);
# Calculates the row height
$self->rowheight( ( $self->page->{height}
- $self->page->{topmargin}
- $self->page->{bottommargin}
- ( $self->tag->{rows} - 1 )
* $self->tag->{vspace}
) / $self->tag->{rows}
);
## Debug
# print STDERR "Calculados: ",
# $self->tag->{rows} . " linhas X " . $self->tag->{columns} .
# "colunas, com largura " . $self->colwidth .
# " e altura ". $self->rowheight . "\n";
return $self;
}
sub genTagFile{
my $self = shift;
my $page;
PAGE: while( @{ $self->data } ){
# Create a new page
$page = $self->pdf->page;
# Set page dimensions
$page->mediabox( $self->page->{width}, $self->page->{height} );
# Get the page's graphic state element
my $graphic = $page->gfx;
$graphic->strokecolor( "#CCCCCC" );
if ( $self->printGridLines ) {
$graphic->rect( $self->page->{leftmargin}, # Upper-left X
$self->page->{bottommargin}, # Upper-left Y
$self->page->{width}
- $self->page->{leftmargin}
- $self->page->{rightmargin}, # Lower-right X
$self->page->{height}
- $self->page->{topmargin}
- $self->page->{bottommargin} # Lower-right Y
);
$graphic->stroke;
$graphic->endpath;
}
# Add a text block to hold all text on this page.
my $text = $page->text;
# Loop trought the columns
foreach my $c ( 0..$self->tag->{columns}-1 ) {
# Calculate the distance of this column from the page border
my $x = $self->page->{leftmargin}
+ $c * ( $self->colwidth + $self->tag->{hspace} );
# Trace vertical grid lines
if ( ($c > 0) && $self->printGridLines ) {
$graphic->move( $x, 0 );
$graphic->line( $x, $self->page->{height} );
$graphic->move( $x - $self->tag->{hspace}, 0 );
$graphic->line( $x - $self->tag->{hspace}, $self->page->{height} );
$graphic->stroke;
$graphic->endpath;
}
# Loop trought the rows
foreach my $r ( 0..$self->tag->{rows}-1 ) {
# Calculate the distance of this row from the top margin
my $y = $self->page->{height} - $self->page->{topmargin}
- $r * ( $self->rowheight + $self->tag->{vspace} );
# Trace horizontal grid lines
if ( ($c > 0) && $self->printGridLines ) {
$graphic->move( 0, $y );
$graphic->line( $self->page->{width}, $y );
$graphic->move( 0, $y + $self->tag->{vspace} );
$graphic->line( $self->page->{width}, $y + $self->tag->{vspace} );
$graphic->stroke;
$graphic->endpath;
}
# Add text lines
$text->translate( $x + 6, $y - 16 );
# Address the next recipient using the row and column counters
my $label = shift @{$self->data};
# Write the tag text...
$text->font( $self->font->{'bold'}, 10 );
$text->text( $label->{'name'} );
$text->cr( -16 );
$text->font( $self->font->{'plain'}, 10 );
$text->text( $label->{'address'} );
$text->cr( -16 );
$text->text( $label->{zipcode} );
unless( $label ){
last PAGE;
}
} # foreach row
} # foreach column
} # PAGE: while...
} #sub
sub asString{
my $self = shift;
$self->genTagFile;
return $self->pdf->stringify;
}
sub writeFile{
my ( $self, $filename ) = ( shift, shift );
die "File exists, covardly refusing overwrite it.\n"
if -f $filename;
$self->genTagFile;
open PDF, '>', $filename
or die $!;
print PDF $self->pdf->stringify;
close PDF
or die $!;
return $self;
}
1;################################################
__END__
=pod
=head1 Tag::Builder - Commercial tag in PDF for printing.
=over 4
=item NAME
Tag::Builder - A commercial PDF-based tag builder module.
=item SYNOPSIS
use Tag::Builder;
my ( $page, $tag, $data ) =
( { width => 210, height => 297,
topmargin => 36, bottommargin => 36,
leftmargin => 36, rightmargin => 36 },
{ rows => 11, columns => 3,
hspace => 6, vspace => 0 },
[
{ name => "J. Nobody",
address => "Nowhere st, 1, Nothing Hill",
zipcode => "NT0304 - London, UK"
},
{ name => "T. Buddy",
address => "Somewhere st, 120, Nowhere Valley",
zipcode => "04321-098 - San Diego, US"
},
]
);
my $tbuilder = new Tag::Builder( $page, $tag, $data );
$tbuilder->writeFile( "/path/to/file.pdf" );
or
$pdf_file = $tbuilder->asString;
=item DESCRIPTION
Tag::Builder gets some postal address info and build
a printable PDF file with tags for each postal
address record given.
=item OPTIONS
=over 4
=item printGridLines
When setted to true (see printGridLines() accessor,
below), asks the module tio print grid lines when
generating the tag sheets. false (off) is the
default, as we expect no-one will desire grid lines
on a label sheet at all. This serves for debugging
purposes mainly.
=item METHODS
=over 4
=item new()
Constructor.
Needs three parameters:
=over 4
=item * Page Specification
This is just a hash reference holding values for
the following keys:
=over 4
=item ** width
This is the page width, in milimeters.
=item ** height
This is the page height in milimeters.
=item ** topmargin
The distance from the top of the page to the top
margin, in milimeters.
=item ** bottommargin
The distance from the bottom of the page to the
bottom margin, in milimeters.
=item ** leftmargin
The distance from the left of the page to the left
margin, in milimeters.
=item ** rightmargin
The distance from the right of the page to the
right margin, in milimeters.
=back
=item * Tag Specification
The tag specification is just another hash
reference, with the following keys:
=over 4
=item ** rows
Tells the number of tags we have on a row.
Must be greather than zero.
=item ** columns
Tells the number of tags we have on a column.
Must be greather than zero.
=item ** hspace
This is the horizontal distance between adjacent
tags, in milimeters.
=item ** vspace
This is the vertical distance between adjacent
tags, in milimeters.
=back
=item * Address Book
The address book is just an array reference with
the necessary data to fill in the tags. You
can pass as many hash references as you need to
into this array reference.
The hash reference fields needed to fill in the
tags are "name", "address" and "zipcode":
=over 4
=item ** name
This is the name of the recipient of the mail
this tag is for.
=item ** address
This is the address of the recipient of the mail
this tag is for.
=item ** zipcode
This field englobes city, state, country and
zipcode, all in one string.
=back
=back
=item recalculateTagDimensions()
This method recalculate tag dimensions, so you
can "resize" your tag after creating the object
and force a recalculation.
=item writeFile()
This method creates a PDF file containing the
requested tags. You must pass in a filename,
as a parameter.
This method die()s if there is something wrong
during the PDF File creation.
=item genTagFile()
This method is responsible for the tag rendering.
You shall call it everytime you change any tag
parameter the tag data.
=item asString()
This method calls genTagFile() for you, and
returns a scalar value containing a string
representation of the generated PDF file. Use
this while generating tags from a web server, so
you don't need to touch the disk nor write files
just for sending the contents trought the web.
=item printGridLines()
Accessor method to the printGridLines option.
=back
=item EXAMPLE
##################################################
# "tag.pl" - uses Tag::Builder
##################################################
#!/usr/bin/perl
use strict;
use warnings;
use lib '/home/champs/src/lib';
use Tag::Builder;
my $data = do './data.txt';
my $builder = new Tag::Builder(
{ width => 210, height => 297,
topmargin => 20, bottommargin => 25,
leftmargin => 25, rightmargin => 20
},
{ rows => 6, columns => 3,
hspace => 1, vspace => 1
},
$data
);
$builder->writeFile( 'test.pdf' );
__END__
##################################################
# data.txt - data file
##################################################
[
{ name => "J. Nobody",
address => "Nowhere st, 1",
zipcode => "NT0 304 - London, UK"
},
{ name => "T. Buddy",
address => "Somewhere st, 120",
zipcode => "04321-098 - San Diego, US"
},
]
#EOF
=item KNOW BUGS
=over 4
=item Bug0001
I don't know why I need to multiply distance
values in milimeters by the UNIT constant so they
actualy are rendered in milimeters.
=back
=item TODO LIST
=over 4
=item * Allow user to choose font and font size;
=item * Incorporate Avery Standard tag specifications;
=item * Allow user to specify tag and page by names;
=back
=item REVISION HISTORY
=item PURPOSE
This module just takes over all calculation and (basic)
diagramation effort needed to generate address labels by
hand... its just a reproductible add-on functionality to
programs.
=item AUTHOR
Luis Campos de Carvalho - [monsieur_champs AT yahoo DOT com DOT br]
=back
=cut
2005-01-10 Janitored by Arunbear - added readmore tags, as per Monastery guidelines