Skip to content

Commit

Permalink
- width of border edges can now be set individually
Browse files Browse the repository at this point in the history
- let dzil do git stuff for release.
- added more docs.
- build pdf->gfx object before text object so text shows over graphics.
- enable multi-page documents to be specified in markup.
  • Loading branch information
lecstor committed Nov 9, 2011
1 parent e3b8d5e commit f232e75
Show file tree
Hide file tree
Showing 8 changed files with 183 additions and 41 deletions.
7 changes: 1 addition & 6 deletions .gitignore
@@ -1,8 +1,3 @@
test.pdf
test_boxer.pdf
test_invoice.pdf
*.pdf
/PDF-Boxer-*
.build
t/full_tests/invoice.pdf
synopsis_output.pdf
full_test_invoice.pdf
6 changes: 6 additions & 0 deletions Changes
@@ -1,5 +1,11 @@
Revision history for PDF-Boxer

- width of border edges can now be set individually
- let dzil do git stuff for release.
- added more docs.
- build pdf->gfx object before text object so text shows over graphics.
- enable multi-page documents to be specified in markup.

0.01 04/11/11
First version, released on an unsuspecting world.

2 changes: 1 addition & 1 deletion dist.ini
Expand Up @@ -2,7 +2,7 @@ name = PDF-Boxer
author = Jason Galea <lecstor@cpan.org>
license = Perl_5
copyright_holder = Jason Galea
version = 0.001
version = 0.002

[NextRelease]
[@Git]
Expand Down
117 changes: 106 additions & 11 deletions lib/PDF/Boxer.pm
Expand Up @@ -71,14 +71,20 @@ PDF::Boxer
=head1 DESCRIPTION
Use my version of a "box model" layout to create PDFs.
Use PDF::Boxer::SpecParser to parse a template written in the not so patented PDFML.
PDF::Boxer enables the creation of pdf documents using rows, columns, and grids
for layout. An xml styled document is used to specify the contents of the
document and is parsed into a block of data by PDF::Boxer::SpecParser and
passed to PDF::Boxer
Suggestion: Use L<Template> to dynamically create your PDFML template.
=head1 MARKUP
There should always be a single parent element which would commonly be a column
allowing other elements to be stacked vertically on the page.
For a single page document the parent element may be a row, column, or grid.
Multiple pages can be generated by wrapping more than one of these elements
with a doc element.
=head2 ELEMENTS
=item column
Expand Down Expand Up @@ -107,6 +113,52 @@ container if necessary.
the image element places an image in the PDF.. whoda thunkit, eh?
the image can be scaled to a percentage of it's original size.
=head2 ATTRIBUTES
=over
=item align
align="right"
align right or center instead of the default left.
=item background
background="#FF0000"
background is set as a hexadecimal color.
=item border
border width set in pixels
=item border_color
=item font
=item grow
when set to true, the element will expand to take up any available space.
=item margin, padding, border
size set in pixels as a string for top, right, bottom, left.
eg:
margin="5 10 15 20"; top = 5, right = 10, bottom = 15, left = 20.
margin="5 10"; top = 5, right = 10, bottom = 5, left = 10.
margin is space outside the border.
padding is space inside the border.
border IS the border..
=item name
I use this for debugging mostly.
It can be used to get an element object via the box_lookup method.
=back
=method add_to_pdf
$boxer->add_to_pdf($spec);
Expand All @@ -117,6 +169,34 @@ Coverts markup to PDF.
Writes the generated PDF to the file specified in the call to new.
=method register_box
each named element is added to an internal register upon creation.
=method box_lookup
$boxer->box_lookup('elementName');
get an element from the register.
=head1 BUGS
positioning of elements not pixel perfect. eg. in a column the bottom of one
child overlaps the top of the next by 1 pixel.
=head1 TODO
=over
=item paging
- enable a single element to be nominated for paging so if it's content is too
large to fit on a page it is continued on the next page.
- enable elements to be marked as first or last page only.
=back
=head1 SEE ALSO
=for :list
Expand Down Expand Up @@ -175,13 +255,28 @@ sub add_to_pdf{
$spec->{boxer} = $weak_me;
$spec->{debug} = $self->debug;

my $class = 'PDF::Boxer::Content::'.$spec->{type};
my $node = $class->new($spec);
$self->register_box($node);
$node->initialize;
# $node->ruler_h;
$node->render;
return $node;
if ($spec->{type} eq 'Doc'){
foreach my $page (@{$spec->{children}}){
$page->{boxer} = $weak_me;
$page->{debug} = $self->debug;

my $class = 'PDF::Boxer::Content::'.$page->{type};
my $node = $class->new($page);
$self->register_box($node);
$node->initialize;
$node->render;
$self->doc->new_page;
}
} else {
my $class = 'PDF::Boxer::Content::'.$spec->{type};
my $node = $class->new($spec);
$self->register_box($node);
$node->initialize;
$node->render;
}

return 1;
#return $node;
}

sub finish{
Expand Down
56 changes: 45 additions & 11 deletions lib/PDF/Boxer/Content/Box.pm
Expand Up @@ -20,6 +20,7 @@ has 'background' => ( isa => 'Str', is => 'ro' );
has 'border_color' => ( isa => 'Str', is => 'ro' );
has 'font' => ( isa => 'Str', is => 'ro', default => 'Helvetica' );
has 'align' => ( isa => 'Str', is => 'ro', default => '' );
has 'valign' => ( isa => 'Str', is => 'ro', default => '' );

=attr debug
Expand Down Expand Up @@ -77,6 +78,11 @@ Non-text boxes will pass this to their children.
The alignment of this box (text string; right or center)
No align means left.
=attr valign
The vertical alignment of this box (text string; bottom or center)
No align means top.
=cut

sub BUILDARGS{
Expand Down Expand Up @@ -247,20 +253,48 @@ sub render{
$gfx->fill;
}

# === Need to change to respect all border sides sizes ===
# increasing linewidth thickens the border "around" the lines of the rectangle.
# we want to thinken "inside" the rectangle..
if (my $width = $self->border->[0]){
$gfx->linewidth(1);
if ($self->border){

my $left = $self->border_left;
my $top = $self->border_top;
my $right = $left + $self->border_width;
my $bottom = $top - $self->border_height;

$gfx->strokecolor($self->border_color || 'black');
my ($bl,$bt,$bw,$bh) = ($self->border_left, $self->border_top, $self->border_width, $self->border_height);
foreach(1..$width){
$gfx->rect($bl,$bt,$bw,-$bh);

if ($self->border->[0]){
$gfx->linewidth($self->border->[0]);
$gfx->move($left, $top);
$gfx->line($right, $top);
$gfx->stroke;
}

if ($self->border->[1]){
$gfx->linewidth($self->border->[1]);
$gfx->move($right, $top);
$gfx->line($right, $bottom);
$gfx->stroke;
$bl++; $bt--;
$bw -= 2;
$bh -= 2;
}

if ($self->border->[2]){
$gfx->linewidth($self->border->[2]);
$gfx->move($right, $bottom);
$gfx->line($left, $bottom);
$gfx->stroke;
}

if ($self->border->[3]){
$gfx->linewidth($self->border->[3]);
$gfx->move($left, $bottom);
$gfx->line($left, $top);
$gfx->stroke;
}
}

if ($self->name eq 'Head' || $self->name eq 'Header' || $self->name eq 'Details' || $self->name eq 'ContentGrid'){
warn "Name: ".$self->name."\n";
warn sprintf "Top: %s\tRight: %s\tBottom: %s\tLeft: %s\n",
$self->margin_top, $self->margin_right, $self->margin_bottom, $self->margin_left;
}

foreach(@{$self->children}){
Expand Down
18 changes: 8 additions & 10 deletions lib/PDF/Boxer/Content/Image.pm
Expand Up @@ -73,24 +73,22 @@ around 'render' => sub{

my @args = $self->scale ? ($self->scale/100) : ($self->image->width, $self->image->height);

if (my $al = $self->valign){
if ($al eq 'top'){
$y = $self->content_top - $self->image_height;
} elsif ($al eq 'center'){
foreach($self->valign || ()){
/^top/ && do { $y = $self->content_top - $self->image_height };
/^cen/ && do {
my $bc = $self->content_top - ($self->content_height / 2);
my $ic = $self->image_height / 2;
$y = $bc - $ic;
}
};
}

if (my $al = $self->align){
if ($al eq 'right'){
$x = $self->content_right - $self->image_width;
} elsif ($al eq 'center'){
foreach($self->align || ()){
/^rig/ && do { $x = $self->content_right - $self->image_width };
/^cen/ && do {
my $bc = $self->content_left + ($self->content_width / 2);
my $ic = $self->image_width / 2;
$x = $bc - $ic;
}
};
}

$gfx->image($img, $x, $y, @args);
Expand Down
13 changes: 11 additions & 2 deletions lib/PDF/Boxer/Doc.pm
Expand Up @@ -18,7 +18,7 @@ sub _build_page{
my ($self) = @_;
my $page = $self->pdf->page;
$page->mediabox(0,0,$self->page_width, $self->page_height);
# $page->cropbox($self->page_width-10, $self->page_height);
#$page->cropbox($self->page_width-10, $self->page_height);
return $page;
}

Expand All @@ -33,7 +33,9 @@ sub _build_gfx{ shift->page->gfx }

has 'text' => ( isa => 'Object', is => 'rw', lazy_build => 1 );
sub _build_text{
my $txt = shift->page->text;
my ($self) = @_;
$self->gfx;
my $txt = $self->page->text;
$txt->compressFlate;
return $txt;
}
Expand All @@ -50,6 +52,13 @@ sub _build_fonts{
}
}

sub new_page{
my ($self) = @_;
$self->clear_page;
$self->clear_text;
$self->clear_gfx;
}

sub font{
my ($self, $name) = @_;
my $font = $self->fonts->{$name};
Expand Down
5 changes: 5 additions & 0 deletions lib/PDF/Boxer/SpecParser.pm
Expand Up @@ -58,6 +58,10 @@ sub mangle_spec{
$element->[0]{type} = 'Grid';
push(@{$spec->{children}}, shift @$element);
$self->mangle_spec($spec->{children}->[-1], $element);
} elsif (lc($tag) eq 'doc'){
$element->[0]{type} = 'Doc';
push(@{$spec->{children}}, shift @$element);
$self->mangle_spec($spec->{children}->[-1], $element);
} else {
$element->[0]{type} = 'Box';
push(@{$spec->{children}}, shift @$element);
Expand All @@ -68,6 +72,7 @@ sub mangle_spec{

sub clean_text{
my ($self, $element) = @_;
return unless $element;
return if $element =~ /^[\s\n\r]*$/;
if ($self->clean_whitespace){
$element =~ s/^[\s\n\r]+//;
Expand Down

0 comments on commit f232e75

Please sign in to comment.