NAME
PDF::ReportWriter
DESCRIPTION
PDF::ReportWriter is designed to create high-quality business reports, for archiving or printing.
USAGE
The example below is purely as a reference inside this documentation to give you an idea of what goes where. It is not intended as a working example - for a working example, see the demo application package, distributed separately at http://entropy.homelinux.org/axis_not_evil
First we set up the top-level report definition and create a new PDF::ReportWriter object ...
$report = {
destination => "/home/dan/my_fantastic_report.pdf",
paper => "A4",
orientation => "portrait",
font_list => [ "Times" ],
default_font => "Times",
default_font_size => "10",
x_margin => 10 * mm,
y_margin => 10 * mm,
info => {
Author => "Daniel Kasak",
Keywords => "Fantastic, Amazing, Superb",
Subject => "Stuff",
Title => "My Fantastic Report"
}
};
my $pdf = PDF::ReportWriter->new( $report );
Next we define our page setup, with a page header ( we can also put a 'footer' object in here as well )
my $page = {
header => [
{
percent => 60,
font_size => 15,
align => "left",
text => "My Fantastic Report"
},
{
percent => 40,
align => "right",
image => {
path => "/home/dan/fantastic_stuff.png",
scale_to_fit => TRUE
}
}
]
};
Define our fields - which will make up most of the report
my $fields = [
{
name => "Date", # 'Date' will appear in field headers
percent => 35, # The percentage of X-space the cell will occupy
align => "centre", # Content will be centred
colour => "blue", # Text will be blue
font_size => 12, # Override the default_font_size with '12' for this cell
header_colour => "white" # Field headers will be rendered in white
},
{
name => "Item",
percent => 35,
align => "centre",
header_colour => "white",
},
{
name => "Appraisal",
percent => 30,
align => "centre",
colour_func => sub { red_if_fantastic(@_); }, # red_if_fantastic() will be called to calculate colour for this cell
aggregate_function => "count" # Items will be counted, and the results stored against this cell
}
];
I've defined a custom colour_func for the 'Appraisal' field, so here's the sub:
sub red_if_fantastic {
my $data = shift;
if ( $data eq "Fantastic" ) {
return "red";
} else {
return "black";
}
}
Define some groups ( or in this case, a single group )
my $groups = [
{
name => "DateGroup", # Not particularly important - apart from the special group "GrandTotals"
data_column => 0, # Which column to group on ( 'Date' in this case )
header => [
{
percent => 100,
align => "right",
colour => "white",
background => { # Draw a background for this cell ...
{
shape => "ellipse", # ... a filled ellipse ...
colour => "blue" # ... and make it blue
}
}
text => "Entries for ?" # ? will be replaced by the current group value ( ie the date )
}
footer => [
{
percent => 70,
align => "right",
text => "Total entries for ?"
},
{
percent => 30,
align => "centre",
aggregate_source => 2 # Take figure from field 2 ( which has the aggregate_function on it )
}
}
];
We need a data array ...
my $data_array = $dbh->selectall_arrayref( "select Date, Item, Appraisal from Entries order by Date" );
Note that you MUST order the data array, as above, if you want to use grouping. PDF::ReportWriter doesn't do any ordering of data for you.
Now we put everything together ...
my $data = {
background => { # Set up a default background for all cells ...
border => "grey" # ... a grey border
},
fields => $fields,
groups => $groups,
page => $page,
data_array => $data_array,
headings => { # This is where we set up field header properties ( not a perfect idea, I know )
background => {
shape => "box",
colour => "darkgrey"
}
}
};
... and finally pass this into PDF::ReportWriter
$pdf->render_data( $data );
At this point, we can do something like assemble a *completely* new $data object, and then run $pdf->render_data( $data ) again, or else we can just finish things off here:
$pdf->save;
CELL DEFINITIONS
PDF::ReportWriter renders all content the same way - in cells. Each cell is defined by a hash. A report definition is basically a collection of cells, arranged at various levels in the report.
Each 'level' to be rendered is defined by an array of cells. ie an array of cells for the data, an array of cells for the group header, and an array of cells for page footers.
Cell spacing is relative. You define a percentage for each cell, and the actual length of the cell is calculated based on the page dimensions ( in the top-level report definition ).
A cell can have the following attributes
name
The 'name' is used when rendering data headers, which happens whenever a new group or page is started. It's not used for anything else - data must be arranged in the same order as the cells to 'line up' in the right place.
You can disable rendering of field headers by setting no_field_headers in your data definition ( ie the hash that you pass to the render() method ).
percent
The width of the cell, as a percentage of the total available width. The actual width will depend on the paper definition ( size and orientation ) and the x_margin in your report_definition.
font
The font to use. In most cases, you would set up a report-wide default_font. Only use this setting to override the default.
font_size
The font size. Nothing special here...
colour
The colour to use for rendering data ( and also group headers / footers ).
header_colour
The colour to use for rendering data headers ( ie field names ).
text
The text to display in the cell.
image
A hash with details of the image to render. See below for details.
colour_func
A user-defined sub that returns a colour based on the current data ( ie receives 1 argument: the current value )
align
Possible values are "left", "right" and "centre" ( or now "center", also ).
aggregate_function
Possible values are "sum" and "count". Setting this attribute will make PDF::ReportWriter carry out the selected function and store the results ( attached to the cell ) for later use in group footers.
type
This key turns on formatting of data. The only possible values currrently are 'currency' and 'currency:no_fill', which are achived via Number::Format ( which spews warnings everywhere - they're harmless )
background
A hash containing details on how to render the background of the cell. See below.
IMAGES
You can define images in any cell ( data, or group header / footer ). The default behaviour is to render the image at its original size. If the image won't fit horizontally, it is scaled down until it will. Images can be aligned in the same way as other fields, with the 'align' key.
The images hash has the following keys:
path
The full path to the image to render ( currently only supports png and jpg ) This key is the only required one
scale_to_fit
A boolean value, indicating whether the image should be scaled to fit the current cell or not. Whether this is set or not, scaling will still occur if the image is too wide for the cell.
height
You can hard-code a height value if you like. The image will be scaled to the given height value, to the extent that it still fits length-wise in the cell.
BACKGROUNDS
You can define a background for any cell, including normal fields, group header & footers, etc. For data headers ONLY, you must ( currently ) set them up per data set, instead of per field. In this case, you add the background key to the 'headings' hash in the main data hash.
The background hash has the following keys:
shape
Current options are 'box' or 'ellipse'. 'ellipse' is good for group headers. 'box' is good for data headers or 'normal' cell backgrounds. If you use an 'ellipse', it tends to look better if the text is centred. More shapes are needed. A 'round_box', with nice rounded edges, would be great. Send patches.
colour
The colour to use to fill the background's shape. Keep in mind with data headers ( the automatic headers that appear at the top of each data set ), that you set the *foreground* colour via the field's 'header_colour' key, as there are ( currently ) no explicit definitions for data headers.
border
The colour ( if any ) to use to render the cell's border. If this is set, the border will be a rectangle, around the very outside of the cell. You can have a shaped background and a border rendererd in the same cell.
GROUP DEFINITIONS
Groups have the following attributes:
name
The name is used to identify which value to use in rendering aggregate functions ( see aggregate_source, below ). Also, a special name, "GrandTotals" will cause PDF::ReportWriter to fetch *Grand* totals instead of group totals. This negates the need to have an extra column of data in your data_array with all the same value ... which is the only other way I can see of 'cleanly' getting GrandTotal functionality.
data_column
The data_column refers to the column ( starting at 0 ) of the data_array that you want to group on.
header / footer
Group headers and footers are defined in a similar way to field definitions ( and rendered by the same code ). The difference is that the cell definition is contained in the 'header' and 'footer' hashes, ie the header and footer hashes resemble a field hash. Consequently, most attributes that work for field cells also work for group cells. Additional attributes in the header and footer hashes are:
aggregate_source
This is used to retrieve the results of an aggregate_function ( see above ).
REPORT DEFINITION
Possible attributes for the report defintion are:
destination
The path to the destination ( the pdf that you want to create ).
paper
Supported types are:
A4
Letter
bsize
legal
orientation
portrait or landscape
font_list
An array of font names ( from the corefonts supported by PDF::API2 ) to set up. When you include a font 'family', a range of fonts ( roman, italic, bold, etc ) are created.
default_font
The name of the font type ( from the above list ) to use as a default ( ie if one isn't set up for a cell ).
default_font_size
The default font size to use if one isn't set up for a cell. This is no longer required and defaults to 12 if one is not given.
x_margin
The amount of space ( left and right ) to leave as a margin for the report.
y_margin
The amount of space ( top and bottom ) to leave as a margin for the report.
DATA DEFINITION
The data definition wraps up most of the previous definitions, apart from the report definition. My goal ( with a future release ) is to support unlimited 'sections', which you'll be able to achieve by passing new data definitions and calling the 'render' method. Currently this does not work, but should not take too much to get it going.
Attributes for the data definition:
cell_borders
Whether to render cell borders or not. This is a legacy option - not that there's any pressing need to remove it - but this is a precursor to background->{border} support, which can be defined per-cell.
Setting cell_borders in the data definition will cause all data cells to be filled out with: background->{border} = "grey"
no_field_headers
Set to disable rendering field headers when beginning a new page or group.
fields
This is your field definition hash, from above.
groups
This is your group definition hash, from above.
data_array
This is the data to render. You *MUST* sort the data yourself. If you are grouping by A, then B and you want all data sorted by C, then make sure you sort by A, B, C. We currently don't do *any* sorting of data, as I only intended this module to be used in conjunction with a database server, and database servers are perfect for sorting data :)
page
This is a hash describing page headers and footers - see below.
PAGE DEFINITION
The page definition is a hash describing page headers and footers. Possible keys are:
header
footer
Each of these keys is an array of cell definitions. Unique to the page *footer* is the ability to define the following special tags:
%TIME%
%PAGE%
%PAGES%
These will be replaced with the relevant data when rendered.
If you don't specify a page footer, one will be supplied for you. This is to provide maximum compatibility with previous versions, which had page footers hard-coded. If you want to supress this behaviour, then set a value for $self->{data}->{page}->{footerless}
METHODS
new ( report_definition )
Object constructor. Pass the report definition in.
render_data ( data_definition )
Renders the data passed in You can call 'render_data' as many times as you want, with different data and definitions.
save
Saves the pdf file ( in the location specified in the report definition ).
AUTHORS
Dan <dan@entropy.homelinux.org>
BUGS
I think you must be mistaken.
Other cool things you should know about:
This module is part of an umbrella project, 'Axis Not Evil', which aims to make Rapid Application Development of database apps using open-source tools a reality. The project includes:
Gtk2::Ex::DBI - forms
Gtk2::Ex::Datasheet::DBI - datasheets
PDF::ReportWriter - reports
All the above modules are available via cpan, or for more information, screenshots, etc, see: http://entropy.homelinux.org/axis_not_evil