RADICORE for PHP - creating PDF output

By Tony Marston

28th August 2006
Amended 1st November 2008

Introduction
Database access
PDF style file
Report Structure File
- List (summary) view
- Detail view
Structure File contents
- Page definition
- Zone styles
- Title Contents
- Column specs (list view)
- Body contents (list view)
- Column specs (detail view)
- Body contents (detail view - single)
- Body contents (detail view - multiple)
- Body contents (detail view - additional data)
- Footer contents
Barcode Generation
- Barcode Styles
- Barcode Structure
- Barcode Examples
Examples
- Example Title
- Example List view
- Example List view (with totals)
- Example List view (with line breaks)
- Example Detail view (single)
- Example Detail view (multiple)
- Example Detail view (additional)
- Example Footer
- Example Invoice
Miscellaneous
Amendment History

Introduction

By default all output produced by RADICORE components is in HTML format so that it can be displayed in the browser window. However, there may be occasions when some other format is required, such as CSV (Comma Separated Values) so that data can be imported into a spreadsheet, or PDF (Portable Document Format) so that a printable file can be produced.

In order to cater for different output formats a new series of output patterns has been created, and the purpose of this document is to describe how to use these patterns to create PDF documents with a variety of structures.

All HTML pages produced by RADICORE are generated from XML documents which are transformed using XSL stylesheets. Although XSL-FO can be used to transform an XML document into printable format, this requires an additional third-party component (such as FOP from Apache) before it can be converted into a PDF document. This seems like too many steps, and perhaps not very efficient, so it has been decided to eliminate the creation of an intermediate XML document and produce PDF output direct from the database data by using the TCPDF library. This means that instead of reading all database records, writing out the data from all those records to an intermediate file, then transforming the contents of that intermediate file into the desired output format, each record is read in then written out to the PDF document before the next record is read in.

Please note that the PDF document is constructed in memory, so avoid giving it overly large amounts of data to process. This may cause the PHP process to run out of memory, or it may run out of time.

In order to make it easy to produce PDF documents from database data in standard data structures the following files have been introduced into the RADICORE framework:


Database access

Each of the transaction patterns described below will access the database through a single database table class, but this does not mean that the contents of the output are restricted to data from that single table. The report will use the contents of the object's internal fieldarray, and this can be constructed in a variety of ways:

There is also the option in Detail view to make callbacks to the database object so that it can provide additional data which can be appended to each page. Please refer to Body contents (detail view - additional data) for details.

By default the number of database records retrieved is not controlled. It is governed entirely by the selection criteria passed down by the previous task (although this may be overridden) which may or may not include a primary key. If it is necessary to restrict a particular report to a single database record this can be achieved by setting $this->checkPrimaryKey to TRUE in the _cm_pre_getData() method. This will cause any selection criteria which does not include the primary key of the current database table to be rejected. As it is possible for the user to select more than one primary key it is possible to limit the number of rows retrieved to 1 by setting $this->rows_per_page to 1.


PDF style file

With HTML it is possible to create a document which contains style names where the attributes of those styles can be specified in a separate CSS file. It is only when the document is rendered by a browser that the style attributes are actually applied to the document to produce a formatted result. This allows the style of the document to be changed simply by switching to a different CSS file which contains different attributes for each of those style names.

With a PDF document all style requirements are built into the document and cannot be changed, thus the document can only be read in the style that it was written. PDF does not understand the concept of style names. Instead a series of style attributes are declared, and those attributes will be used for all subsequent output operations until they are changed.

In order to make it easy to use different styles within a PDF document each of these styles must be defined within a file called pdf.styles.inc. A copy of this file must reside in the subsystem directory, which allows each subsystem to have its own set of styles and attributes. This file will be accessed at runtime, and the relevant style attributes will be applied to each report element as specified in the report structure file.

Here is an example of a PDF style file:

<?php
//*****************************************************************************
// This file contains styles to be used in PDF output
//*****************************************************************************

// heading for LIST view
$style['hdg']['font'] = array('family' => 'Times',      // Font family
                              'style' => 'B',           // Font style
                              'size' => 12,             // font size
                              'height' => 7,            // line height
                              'draw' => .4,             // width of drawn lines
                              'align' => 'center');     // text alignment

$style['hdg']['fillcolour'] = array(24,116,205);        // colour for background
$style['hdg']['textcolour'] = array(255,255,255);       // colour for foreground
$style['hdg']['drawcolour'] = array(128,0,0);           // colour for line drawing

// main body for LIST view
$style['body']['font'] = array('family' => 'Times',     // Font family
                               'style' => '',           // Font style
                               'size' => 12,            // font size
                               'height' => 6,           // line height
                               'draw' => .3,            // width of drawn lines
                               'align' => 'left');      // text alignment

$style['body']['fillcolour'] = array(224,235,255);      // colour for background
$style['body']['textcolour'] = array(0,0,0);            // colour for foreground
$style['body']['drawcolour'] = array(128,0,0);          // colour for line drawing

// allow text alignment within a cell to be changed
$style['textalignleft']['font'] = array('align' => 'left');     // text alignment = left
$style['textalignright']['font'] = array('align' => 'right');   // text alignment = right
$style['textaligncenter']['font'] = array('align' => 'center'); // text alignment = center
$style['noborder']['drawcolour'] = array(255,255,255);          // border = white

// barcode styles
$style['barcode1']['type'] = 'I25';         // I25, C128A, C128B, C128C, C39
$style['barcode1']['style'] = 'CDS';        // B=Border, D=Draw Text, S=Stretch Text, N=Negative,
                                            // L=Align Left, C=Align Center, R=Align Right
$style['barcode1']['xres'] = 2;             // 1,2,3
$style['barcode1']['fontsize'] = 5;         // 1,2,3,4,5

$style['barcode2'].....

?>

The various components and their options are as follows:

$style['name'] This provides a name by which each style can be referenced. The above example contains two full styles, hdg and body, and three partial styles, textalignleft, textalignright and textaligncenter, which can be used as temporary overrides to a full style.
['name']['font'] This identifies all the font attributes for this style.
['name']['font']['family'] This identifies the font family. Allowable values are:
  • Courier (fixed width)
  • Helvetica (sans serif)
  • Times (serif)
It is possible to add other fonts. Please refer the TCPDF documentation for details. Some other pre-built fonts are available within the TCPDF download. These were left out of the Radicore download in order to reduce the total size of the file.
['name']['font']['style'] This is the font style. Possible values are:
  • '' = Regular (the default)
  • 'B' = Bold
  • 'I' = Italic
  • 'U' = Underline
Any combination of these values is allowed.
['name']['font']['size'] This is the font size in points.
['name']['font']['height'] This is the line height in the units identified in the report structure file. Each piece of text will be printed in a cell, and this will be the height of each line in that cell. Line spacing within the cell is created by defining a line height which is greater than the font size.
['name']['font']['draw'] The width, in millimeters, for any line drawing, such as the border around any cells.
['name']['font']['align'] This identifies the alignment of text within the cell. Possible values are:
  • L = Left (the default)
  • R = Right
  • C = Center
['name']['fillcolour'] This colour will be used for the background, and is specified as a triplet of RGB colour values.
['name']['textcolour'] This colour will be used for all text, and is specified as a triplet of RGB colour values.
['name']['drawcolour'] This colour will be used for any line drawing, and is specified as a triplet of RGB colour values.
['barcodeN']['type']
['barcodeN']['style']
['barcodeN']['xres']
['barcodeN']['fontsize']
This group defines the basic settings for the generation of barcodes. It is possible to have multiple choices by giving each group a unique name, usually by incrementing the value of the trailing 'N'.

For full details please refer to Barcode Generation.

Note that it is possible to define a style with only a subset of attributes. This allows minor variations to be made to the current settings. Only ['font']['family'] and ['font']['style'] must be defined in pairs - all other attributes can be defined on their own.


Report Structure File

This is similar to a screen structure file in that it allows the developer to specify a report's structure by identifying what needs to be printed where, then lets the framework process those instructions to produce the desired output. There is a different type of structure file for different views, although there are elements which are common to all views.

List (summary) view

This is implemented using the Output 2 transaction pattern. It will show the contents of one database object, one record per line. There will be column headings across the top of each page, plus an optional title and an optional footer, as shown in the following structure diagram:

Structure of List View

dialog-types-output2 (2K)

Sample List View

dialog-types-output2-sample (19K)

Here is the structure file which produced that view:

<?php
// report structure for a LIST view

// page definition  <--(1)
$structure['pdf'] = array('orientation' => 'L',     // L=Landscape, P=Portrait
                          'units' => 'mm',          // pt=Point, mm=Millimeter, cm=Centimeter, in=Inch
                          'format' => 'A4',         // A3, A4, A5, Letter, Legal
                          'name' => '',             // filename (if destination = D or F)
                          'destination' => 'I');    // I=Inline, D=Download, F=Filename, S=String

// zone styles  <--(2)
$structure['title']['style'] = 'title';             // style for page title
$structure['head']['style']  = 'hdg';               // style for column headings
$structure['body']['style']  = 'body';              // style for main body
$structure['foot']['style']  = 'foot';              // style for page footer

// define contents of page title  <--(3)
$structure['title'][] = array('text' => "- Contents of MNU_TASK table -",
                              'width' => 100,
                              'align' => 'center',
                              'border' => 'y');

// set column widths  <--(4)
$structure['columns'][] = array('width' => 60);
$structure['columns'][] = array('width' => 130);
$structure['columns'][] = array('width' => 30);
$structure['columns'][] = array('width' => 30);
$structure['columns'][] = array('width' => 25, 'align' => 'center');

// identify column names and associated labels  <--(5)
$structure['body']['fields'][] = array('task_id' => 'Task Id');
$structure['body']['fields'][] = array('task_desc' => 'Description');
$structure['body']['fields'][] = array('subsys_id' => 'Subsys Id');
$structure['body']['fields'][] = array('pattern_id' => 'Pattern Id');
$structure['body']['fields'][] = array('task_type' => 'Task Type');

// define contents of page footer  <--(6)
$structure['foot1'][] = array('type' => 'date',   'align' => 'left', 'style' => 'textalignleft');  
$structure['foot1'][] = array('type' => 'pageno', 'align' => 'center');
$structure['foot1'][] = array('type' => 'time',   'align' => 'right', 'style' => 'textalignright');

?>

This structure file contains the following elements:

  1. Page definition
  2. Zone styles
  3. Title Contents
  4. Column specs (list view)
  5. Body contents (list view)
  6. Footer contents

Processing is performed in the following steps:

  1. Call getData_serial() to identify all those records which satisfy the current selection criteria.
  2. Call outputPDF_ListView() to read and process the qualifying records one at a time.

Detail view

This is implemented using the Output 3 transaction pattern. It will show the contents of one database object, one record per page. There will be column labels to the left and column data on the right. The default is one label plus associated field data to each line, but several fields and labels can be painted on the same line. There will also be an optional title and an optional footer, as shown in the following structure diagram:

Structure of Detail View

dialog-types-output3 (2K)

Sample Detail View

dialog-types-output3-sample (10K)

Here is part of the structure file which produced that view:

<?php
// report structure for a DETAIL view

// page definition  <--(1)
$structure['pdf'] = array('orientation' => 'P',     // L=Landscape, P=Portrait
                          'units' => 'mm',          // pt=Point, mm=Millimeter, cm=Centimeter, in=Inch
                          'format' => 'A4',         // A3, A4, A5, Letter, Legal
                          'name' => '',             // filename (if destination = D or F)
                          'destination' => 'I');    // I=Inline, D=Download, F=Filename, S=String

// zone styles  <--(2)
$structure['title']['style'] = 'title';             // style for page heading
$structure['label']['style'] = 'label';             // style for field labels
$structure['data']['style']  = 'data';              // style for field data
$structure['foot']['style']  = 'foot';              // style for page footer

// define contents of page title  <--(3)
$structure['title'][] = array('text' => "- Contents of DICT_COLUMN table -",
                              'width' => 100,
                              'align' => 'center',
                              'border' => 'y');

// set column widths  <--(4)
$structure['columns'][] = array('width' => 40, 'align' => 'right');
$structure['columns'][] = array('width' => 150, 'align' => 'left');

// identify labels and data to be output

// multiple fields per line <--(5)
$structure['body']['fields'][1][] = array('label' => 'Database Id', 'width' => 40);
$structure['body']['fields'][1][] = array('field' => 'database_id', 'width' => 55);
$structure['body']['fields'][1][] = array('label' => 'Table Id', 'width' => 40);
$structure['body']['fields'][1][] = array('field' => 'table_id', 'width' => 55);

// single field per line  <--(6)
$structure['body']['fields'][2] = array('column_seq' => 'Seq');
$structure['body']['fields'][3] = array('column_id' => 'Column Id');
$structure['body']['fields'][4] = array('column_desc' => 'Description');
$structure['body']['fields'][5] = array('comment' => 'Comments');

// multiple fields per line <--(5)
$structure['body']['fields'][36][] = array('label' => 'Revised Date', 'width' => 40);
$structure['body']['fields'][36][] = array('field' => 'revised_date', 'width' => 55);
$structure['body']['fields'][36][] = array('label' => 'Revised By', 'width' => 40);
$structure['body']['fields'][36][] = array('field' => 'revised_user', 'width' => 55);

// define contents of page footer  <--(7)
$structure['foot1'][] = array('type' => 'date',   'align' => 'left', 'style' => 'textalignleft');
$structure['foot1'][] = array('type' => 'pageno', 'align' => 'center');
$structure['foot1'][] = array('type' => 'time',   'align' => 'right', 'style' => 'textalignright');

?>

This structure file contains the following elements:

  1. Page definition
  2. Zone styles
  3. Title Contents
  4. Column specs (detail view)
  5. Body contents (detail view - multiple)
  6. Body contents (detail view - single)
  7. Footer contents

There is also the option to append additional data from other database tables. Please refer to Body contents (detail view - additional data) for details.

Processing is performed in the following steps:

  1. Call getData_serial() to identify all those records which satisfy the current selection criteria.
  2. Call outputPDF_DetailView() to read and process the qualifying records one at a time.

Structure File contents

Each file must contain the specifications for each of the relevant zones. The zones vary depending on the type of report being produced.

Page definition

This defines the page size and orientation and is required in every structure file.

$structure['pdf'] = array('orientation' => 'L',     // L=Landscape, P=Portrait
                          'units' => 'mm',          // pt=Point, mm=Millimeter, cm=Centimeter, in=Inch
                          'format' => 'A4',         // A3, A4, A5, Letter, Legal
                          'name' => '',             // filename (if destination = D or F)
                          'destination' => 'I');    // I=Inline, D=Download, F=Filename, S=String
KeywordValues
destination I=Inline (browser), D=Download (browser), F=Filename, S=String
format A3, A4, A5, Letter, Legal
name This is the file name to be used for the output, and is only valid if destination = D or F.

Valid formats for destination 'F' are either:

  • foobar.pdf to create the file in the current working directory
  • reports/foobar.pdf to create the file in a subdirectory.

The valid format for destination 'D' is:

  • foobar.pdf to force the downloaded file to be saved with this name.

It is possible for this file name to be customised as it passes through the _cm_pre_output() method.

orientation L=Landscape, P=Portrait
units This will be the unit of measurement for all subsequent cell widths and line heights. Allowable values are:

pt=Point, mm=Millimeter, cm=Centimeter, in=Inch

Zone styles

This defines the output style for each of the zones within the current report layout.

$structure['<zone>']['style'] = '<style_id>';
$structure['<zone>']['style'] = '<style_id>';
     .....     .....     .....     .....
KeywordValues
<zone>

Each type of report has a fixed set of zones, so only those names can be used. Anything else will be ignored.

  • List view = title, head, body, foot
  • Detail view = title, label, data, foot
<style_id> This is the name of one of the entries in the PDF style file. If the style is not found a default style will be used.

Title Contents

The report title can have zero or more elements, and each element can be either and image (which includes a barcode) or a piece of text. The elements will be processed in the order in which they were defined, and unless absolute positioning is specified each element will follow the previous element on the same line. It is possible to specify a number of elements which will appear on different lines.

If the text within a cell is longer than the width of that cell, or it contains the newline character (\n), it will overflow onto the following line(s) by creating cells of the same width which will appear vertically below the first cell.

$structure['title'][] = array('image'       => '<imagename>',
                              'imagewidth'  => <imagewidth>,
                              'imageheight' => <imageheight>,
                              'align'  => '<align>',
                              'border' => 'y',
                              'marginleft'  => <marginleft>,
                              'marginright' => <marginright>,
                              'newline'     => 'y',
                              'x'           => <x>,
                              'y'           => <y>,
                              'y_relative'  => '<y_relative>');

$structure['title'][] = array('barcode'       => '<text>', or 'barcode' => '%%<text>',
                              'barcode_style' => '<barcode_style>', 
                              'width'         => <width>,
                              'height'        => <height>,
                              'border'      => 'y',
                              'marginleft'  => <marginleft>,
                              'marginright' => <marginright>,
                              'newline'     => 'y',
                              'x'           => <x>,
                              'y'           => <y>,
                              'y_relative'  => '<y_relative>');

$structure['title'][] = array('text'   => '<text>', or 'text' => '%%<text>', or 'type' => '<type>',
                              'width'  => <width>,
                              'height' => <height>,
                              'align'  => '<align>',
                              'border' => 'y',
                              'nofill' => 'y',
                              'style'  => '<style>',
                              'marginleft'  => <marginleft>,
                              'marginright' => <marginright>,
                              'newline'     => 'y',
                              'x'           => <x>,
                              'y'           => <y>,
                              'y_relative'  => '<y_relative>');

$structure['title'][] = array( ... );
KeywordValues
<align>

(optional) This is absolute positioning. It will cause this cell to be printed in one of three places in the current line without any regard to other elements which may appear in the same line. Allowable values are:

  • left - the left hand edge of the cell will be against the page's left margin.
  • center - the cell will be positioned in the center of the line equidistant between the page margins.
  • right - the right hand edge of the cell will be against the page's right margin.
Note that this alignment is for the cell itself and not the text within the cell. Text alignment is controlled by the style definition in the PDF style file.
<barcode_style> Please refer to Barcode Generation.
border=y (optional) This will cause a border to be placed around the cell using the line width and colour of the current style, as defined in the PDF style file.
<height> (optional) This is the height of the cell into which the text will be placed. It is expressed in the units defined in the page definition. The spacing between lines is defined by making the cell height greater than the font size.
<imagename> The path to the image.
<imagewidth> The image width, in pixels. Required if <imagename> is supplied.
<imageheight> The image height, in pixels. Required if <imagename> is supplied.
<marginleft> (optional) This will define blank space which will appear to the left of this cell. It is expressed in the units defined in the page definition.
<marginright> (optional) This will define blank space which will appear to the right of this cell. It is expressed in the units defined in the page definition.
newline=y (optional) This will cause a 'newline' to be issued after the current cell has been written out, thus forcing the next element to start at the beginning of the next line below the current cell.
nofill=y (optional) This will cause this cell to be written without any background colour (i.e. transparent). This is useful when writing one cell over another when it is desired not to overwrite the bottom cell with the background colour of the top cell.
<style> (optional) This will override any style settings for the current zone with new settings, which will apply to this cell only. The default style for title is as defined in zone styles.

This cannot be used with barcode_style.

<text>
%%<text>
The text to be printed. If this contains newline characters (\n), or is greater than the cell width, then it will overflow onto the following lines. Any overflow cells will be the same width as the original, and will appear vertically below the original. Any border will be drawn around the group of cells and not around individual cells in the group.

Newline characters are not allowed in barcodes.

If <text> begins with '%%' it will be assumed to be a field name, so the data for that field will be extracted from the current data and used instead. For example, '%%foobar' will be replaced with the data for field 'foobar'.

<type> This is an alternative to 'text'.
  • pageno - will print 'Page n of n'
  • date - will print the current date in the format specified by $GLOBALS['date_format'].
  • time - will print the current time in the format 'HH:MM:SS'
<width> This is the width of the cell into which the text will be placed. It is expressed in the units defined in the page definition.
<x> (optional) This is absolute positioning. It identifies the X axis (along the horizontal plane) for the top left hand corner of the current cell. It is expressed in the units defined in the page definition, and will be relative to the page's left margin.
<y> (optional) This is absolute positioning. It identifies the Y axis (along the vertical plane) for the top left hand corner of the current cell. It is expressed in the units defined in the page definition, and will be relative to the page's top margin.
<y_relative> (optional) Unlike <y> which supplies an absolute position on the current page this supplies a position which is relative to the bottom of the previous cell. A value of '1' would therefore create a space of 1 unit between the bottom of the previous cell and the top of this cell.

Click here to see some sample output.

Column specs (list view)

This is used in List view to identify the number of columns to be printed, the width of each of those columns, and optionally the alignment of the text in those columns. The data which it acts upon is defined in Body contents (list view).

$structure['columns'][] = array('width' => <width>);
$structure['columns'][] = array('width' => <width>, 'overflow' => 'y');
     .....    .....     .....     .....     .....
$structure['columns'][] = array('width' => <width>, 'align' => '<align>');
KeywordValues
<align>

(optional) This aligns the text for all data cells within this column in the report body. Allowable options are:

  • Left (default)
  • Center
  • Right
The alignment of label text in the column headings is controlled by the alignment option for the head style in the PDF style file.
overflow=y (optional) By default any text in the report body which cannot fit into a single line will be truncated. If this keyword is present then the text will be allowed to overflow onto as many lines as necessary.

Any label text in the column headings will always be allowed to overflow.

<width> This is expressed in the units defined in the page definition. This applies to both the column headings and the column data in the report body.

Click here to see some sample output.

Body contents (list view)

This is used in List view to identify which fields from the current record in the database object are to be inserted into the report, and in which order. The field values for each record will appear in lines across the page, one below the other, while the field values will appear in a single line across the top of the page with each field label above the data to which it is related.

$structure['body']['fields'][] = array('<fieldname>' => '<labeltext>');
$structure['body']['fields'][] = array('<fieldname>' => '<labeltext>');
     .....    .....     .....     .....     .....     .....     .....
$structure['body']['fields'][] = array('<fieldname>' => '<labeltext>');
KeywordValues
<fieldname> This data for this field will be extracted from $this->fieldarray which is the data from the current occurrence inside the database object being used by the reporting task. This will be displayed in the report body.

Please visit here for additional information on how the data may be altered before it is displayed.

<labeltext> This is the label for this piece of data, which will be displayed in the column heading at the top of the page. Different translations of this label can be provided in different languages by using Radicore's Internationalisation feature.

Click here to see some sample output.

Column specs (detail view)

This is used in Detail view to define the column width and text alignment for Body contents (detail view - single). Only two entries are allowed - the first is for the labels while the second is for the data.

$structure['columns'][] = array('width' => <width>, 'align' => '<align>');  <-- 1st entry for labels
$structure['columns'][] = array('width' => <width>, 'align' => '<align>');  <-- 2nd entry for data
KeywordValues
<align>

(optional) This aligns the text within each of these data cells. Allowable options are:

  • Left (default)
  • Center
  • Right
<width> This is expressed in the units defined in the page definition.

Click here to see some sample output.

Body contents (detail view - single)

This is used in Detail view to create a line in the report containing the specified field and its associated label, with the label on the left and the data on the right. The column widths and text alignment are a specified in Column specs (detail view).

This is a "per line" specification, so it is possible to mix multi-field and single-field lines in the same report.

$structure['body']['fields'][<lineno>] = array('<fieldname>' => '<labeltext>');
$structure['body']['fields'][<lineno>] = array('<fieldname>' => '<labeltext>');
     .....    .....     .....     .....     .....     .....     .....
$structure['body']['fields'][<lineno>] = array('<fieldname>' => '<labeltext>', 'notext' => 'y');
KeywordValues
<fieldname> This data for this field will be extracted from $this->fieldarray which is the data from the current occurrence inside the database object being used by the reporting task.

The way that the data is displayed may be affected by settings with the $this->fieldspec array, as follows:

  • If <fieldname> has an optionlist entry then the field's value will be replaced by the corresponding value in optionlist.
  • If <fieldname> has a foreign_field entry then the field's value will be replaced by the corresponding value in foreign_field.
  • If <fieldname> has a 'subtype' = 'image' entry then the image will be displayed using the values supplied for imagewidth and imageheight. This will then be followed by the image name as plain text (unless the entry also contains 'notext' => 'y').

Is this behaviour is not required the solution is to remove the triggering entry from the $this->fieldspec array by inserting code into the _cm_pre_output() method inside the table class.

<labeltext> This is the label for this piece of data. Different translations of this label can be provided in different languages by using Radicore's Internationalisation feature.
<lineno> (optional) This should normally be left blank so that PHP can automatically assign the next available number. However, when combined with Body contents (detail view - multiple) in the same report it may be a good idea to provide explicit line numbers. Line numbers must be sequential but need not be consecutive.
'notext' => 'y' If the field is an image then this can be used to omit the filename from the output.

Click here to see some sample output.

Body contents (detail view - multiple)

This is used in Detail view to create a line in the report containing multiple fields and/or labels. This is a "per line" specification, so it is possible to mix multi-field and single-field lines in the same report.

It is also possible to specify a line which consists of nothing but labels, or a line which consists of nothing but data. Because each cell in the line is specified individually any combination is possible. Note that each cell must be either a label or a field (which includes a barcode), but not both.

$structure['body']['fields'][<lineno>][] = array('label' => '<labeltext>',
                                                 'align' => '<align>',
                                                 'style' => '<style>',
                                                 'width' => <width>);

$structure['body']['fields'][<lineno>][] = array('field' => '<fieldname>',
                                                 'align' => '<align>',
                                                 'style' => '<style>',
                                                 'width' => <width>,
                                                 'notext' => 'y');

$structure['body']['fields'][<lineno>][] = array('barcode'       => '<fieldname>',
                                                 'barcode_style' => '<barcode_style>', 
                                                 'width'         => <width>, 
                                                 'height'        => <height>);
     .....    .....     .....     .....     .....     .....     .....
$structure['body']['fields'][<lineno>][] = array('label' => '<labeltext>',
                                                 .......     .......
                                                 'width' => <width>);
$structure['body']['fields'][<lineno>][] = array('field' => '<fieldname>',
                                                 .......     .......
                                                 'width' => <width>);
KeywordValues
<align>

(optional) This aligns the text within this data cell. Allowable options are:

  • Left (default)
  • Center
  • Right
<barcode_style> Please refer to Barcode Generation.
<fieldname> This data for this field will be extracted from $this->fieldarray which is the data from the current occurrence inside the database object being used by the reporting task. Note that each cell must be either a label or a field (which includes a barcode), but not both.

Please visit here for additional information on how the data may be altered before it is displayed.

<height> This is expressed in the units defined in the page definition.
<labeltext> This is the label for this piece of data. Different translations of this label can be provided in different languages by using Radicore's Internationalisation feature. Note that each cell must be either a label or a field, but not both.
<lineno> All fields which are to appear on the same line in the report must have the same line number. Line numbers must be sequential but need not be consecutive.
'notext' => 'y' If the field is an image then this can be used to omit the filename from the output.
<style> (optional) This will override any style settings for the current zone with new settings, which will apply to this cell only. The default style for label and data is as defined in zone styles.

This cannot be used with barcode_style.

<width> This is expressed in the units defined in the page definition.

Click here to see some sample output.

Body contents (detail view - additional data)

By default each page is constructed from the data which is found in one row in the object's internal fieldarray. Although each row may contain data from several sources there may sometimes be a need to append several rows of data from several other database tables.

This can be achieved by specifying different data zones which are to be appended to each page of the report following on from the normal data area. These zones have the following characteristics:

These optional additional data zones can be included in a report's structure file using code similar to the following:

// identify output for 1st additional zone
$structure['multi1']['fields'][1][] = array('label' => '<labeltext>',  <-- line # 1 in this zone
                                            'align' => '<align>',
                                            'style' => '<style>',
                                            'width' => <width>,
                                            'height' => <height>,
                                            'notext' => 'y',
                                            'marginleft'  => '<marginleft>',
                                            'marginright' => '<marginright>',
                                            'x'           => '<x>',
                                            'y_minimum'   => '<y_minimum>',
                                            'y_relative'  => '<y_relative>',
                                            'y'           => '<y>');
                                            
$structure['multi1']['fields'][1][] = array('field' => '<fieldname>',
                                            ......     ......
                                            'y'           => '<y>');

$structure['multi1']['fields'][1][] = array('barcode'       => '<text>', or 'barcode' => '%%<text>',
                                            'barcode_style' => '<barcode_style>', 
                                            'width'         => <width>, 
                                            'height'        => <height>,
                                            ......     ......
                                            'y'           => '<y>');

// identify output for 2nd additional zone
$structure['multi2']['fields'][1][] = array('label' => '<labeltext>',  <-- line # 1 in this zone
                                            ......     ......
                                            'y'           => '<y>');
$structure['multi2']['fields'][1][] = array('field' => '<fieldname>',
                                            ......     ......
                                            'y'           => '<y>');
$structure['multi2']['fields'][2][] = array('label' => '<labeltext>',  <-- line # 2 in this zone
                                            ......     ......
                                            'y'           => '<y>');
$structure['multi2']['fields'][2][] = array('field' => '<fieldname>',
                                            ......     ......
                                            'y'           => '<y>');

// identify output for 3rd additional zone
$structure['<zone>']['fields'][<lineno>][] = array( ... );  <-- line # 1 in this zone
KeywordValues
<align>

optional) This aligns the text within this data cell. Allowable options are:

  • left (default)
  • center
  • right
<barcode_style> Please refer to Barcode Generation.
<fieldname> This data for this field will be extracted from the fieldarray which was generated for this zone by the database object.
<height> (optional) This is the height of the cell into which the text will be placed. It is expressed in the units defined in the page definition. The spacing between lines is defined by making the cell height greater than the font size.
<labeltext> This is the label for this piece of data. Different translations of this label can be provided in different languages by using Radicore's Internationalisation feature.
<lineno> All fields which are to appear on the same line must have the same line number. Line numbers must be sequential but need not be consecutive.
<marginleft> (optional) This will define blank space which will appear to the left of this cell. It is expressed in the units defined in the page definition.
<marginright> (optional) This will define blank space which will appear to the right of this cell. It is expressed in the units defined in the page definition.
'notext' => 'y' If the field is an image then this can be used to omit the filename from the output.
<style> (optional) This will override any style settings for the current zone with new settings, which will apply to this cell only. The default style for label and data is as defined in zone styles.

This cannot be used with barcode_style.

<text>
%%<text>
The text to be printed. If this contains newline characters (\n), or is greater than the cell width, then it will overflow onto the following lines. Any overflow cells will be the same width as the original, and will appear vertically below the original. Any border will be drawn around the group of cells and not around individual cells in the group.

Newline characters are not allowed in barcodes.

If <text> begins with '%%' it will be assumed to be a field name, so the data for that field will be extracted from the current data and used instead. For example, '%%foobar' will be replaced with the data for field 'foobar'.

<width> (optional) This is the width of the cell into which the text will be placed. It is expressed in the units defined in the page definition. If not defined the width of the string will be calculated as the width of the current page divided by the number of elements in the current line.
<x> (optional) This is absolute positioning. It identifies the X axis (along the horizontal plane) for the top left hand corner of the current cell. It is expressed in the units defined in the page definition, and will be relative to the page's left margin.
<y> (optional) This is absolute positioning. It identifies the Y axis (along the vertical plane) for the top left hand corner of the current cell. It is expressed in the units defined in the page definition, and will be relative to the page's top margin. This should only be used on the first cell in a line.
<y_minimum> (optional) If the current value for the Y axis is greater than this value then a new page will be started before this cell is printed. This ensures that there is enough room on the current page for this line (or group of lines). This should only be used on the first cell in a line.
<y_relative> (optional) Unlike <y> which supplies an absolute position on the current page this supplies a position which is relative to the bottom of the previous cell. A value of '1' would therefore create a space of 1 unit between the bottom of the previous cell and the top of this cell. This should only be used on the first cell in a line.
<zone> The name of the zone in the format 'multiN' where 'N' is in the range 1-99.

Click here to see some sample output.

Footer contents

This provides an optional footer at the bottom of each page. It can span either one or two lines, and each line can have one or more elements.

$structure['<id>'][] = array('type' => '<type>',
                             'align' => '<align>',
                             'border' => 'y',
                             'height' => <height>,
                             'width'  => <width>,
                             'marginleft'  => <marginleft>,
                             'marginright' => <marginright>,
                             'style'       => '<style>',
                             'x'           => <x>);

$structure['<id>'][] = array('text' => '<text>', or 'text' => '%%<text>',
                             'align' => '<align>',
                             'border' => 'y',
                             'height' => <height>,
                             'width'  => <width>,
                             'marginleft'  => <marginleft>,
                             'marginright' => <marginright>,
                             'style'       => '<style>',
                             'x'           => <x>);
                              
$structure['<id>'][] = array( ... );
KeywordValues
<align>

(optional) This is absolute positioning. It will cause this cell to be printed in one of three places in the current line without any regard to other elements which may appear in the same line. Allowable values are:

  • left - the left hand edge of the cell will be against the page's left margin.
  • center - the cell will be positioned in the center of the line equidistant between the page margins.
  • right - the right hand edge of the cell will be against the page's right margin.
Note that this alignment is for the cell itself and not the text within the cell. Text alignment is controlled by the style definition in the PDF style file.
border=y (optional) This will cause a border to be placed around the cell using the line width and colour of the current style, as defined in the PDF style file.
<height> (optional) This is the height of the cell into which the text will be placed. It is expressed in the units defined in the page definition. The spacing between lines is defined by making the cell height greater than the font size.
<id> Up to two lines are allowed in the footer, identified as foot1 and foot2.
<marginleft> (optional) This will define blank space which will appear to the left of this cell. It is expressed in the units defined in the page definition.
<marginright> (optional) This will define blank space which will appear to the right of this cell. It is expressed in the units defined in the page definition.
<style> (optional) This will override any style settings for the current zone with new settings, which will apply to this cell only. The default style for foot is as defined in zone styles.
<text>
%%<text>
The text to be printed. It cannot span more than one line, so should not exceed the specified width or contain the newline character (\n).

If <text> begins with '%%' it will be assumed to be a field name, so the data for that field will be extracted from the current data and used instead. For example, '%%foobar' will be replaced with the data for field 'foobar'.

<type> This is an alternative to 'text'.
  • pageno - will print 'Page n of n'
  • date - will print the current date in the format specified by $GLOBALS['date_format'].
  • time - will print the current time in the format 'HH:MM:SS'
<width> (optional) This is the width of the cell into which the text will be placed. It is expressed in the units defined in the page definition. If not defined the width of the string will be calculated.
<x> (optional) This is absolute positioning. It identifies the X axis (along the horizontal plane) for the top left hand corner of the current cell. It is expressed in the units defined in the page definition, and will be relative to the page's left margin.

Click here to see some sample output.


Barcode Generation

The creation of barcodes in the TCPDF library uses a modified version of "Generic Barcode Render Class" by Karim Mribti (http://www.mribti.com/barcode/). In order to utilise this class within the Radicore framework there must be settings in the global PDF style file and the individual Report Structure File, as shown in the following:

PDF style file

You may define one or more barcode styles with the following settings in your PDF style file:

// barcode styles
$style['barcode1']['type'] = 'I25';         // I25, C128A, C128B, C128C, C39,
                                            // C39+, C39E, C39E+, EAN13, UPCA, POSTNET, CODABAR
$style['barcode1']['style'] = 'CDS';        // B=Border, D=Draw Text, S=Stretch Text, N=Negative, 
                                            // L=Align Left, C=Align Center, R=Align Right, X=Stretch Image
$style['barcode1']['xres'] = 2;             // 1,2,3
$style['barcode1']['fontsize'] = 5;         // 1,2,3,4,5
['barcodeN']['type']

This identifies the barcode type. Possible values are:

  • I25 = Interleaved 2 of 5
  • C128A = Code 128-A
  • C128B = Code 128-B
  • C128C = Code 128-C
  • C39 = Code 39
  • C39+ = Code 39 with checksum
  • C39E = Code 39 EXTENDED
  • C39E+ = Code 39 EXTENDED with checksum
  • EAN13 = EAN 13
  • UPCA = UPC-A
  • POSTNET = POSTNET
  • CODABAR = CODABAR
['barcodeN']['style']

This identifies the barcode style by combining any of the following characters:

  • B = draw a border around the image
  • D = draw text under the image
  • S = stretch the text under the image
  • N = Negative (white on black)

The following will align the image within the cell (the printable area):

  • L = Left
  • C = Centre
  • R = Right
  • X = Stretch Image to fit cell

The default setting is 'C' (a black on white image aligned in the centre of the cell).

['barcodeN']['xres'] This identifies the size/resolution of the barcode image. 1=small, 2=medium, 3=large.
['barcodeN']['fontsize'] This identifies the size of the optional text. 1=small, 2=Small+, 3=medium, 4=Medium+, 5=large.

If you want more options than are available with the single ['barcodeN']['style'] setting you can replace it with any combination of the following:

['barcodeN']['type'] (as above)
['barcodeN']['fgcolor'] Foreground colour, specified as a triplet of RGB colour values.
['barcodeN']['bgcolor'] Background colour, specified as a triplet of RGB colour values, or false for transparent.
['barcodeN']['position'] Position of the barcode image within the enclosing cell. Possible options are:
  • L = Align Left
  • C = Align centre (the default)
  • R = Align Right
  • S = Stretch to fit
['barcodeN']['border'] If set to true this will print a border around the image.
['barcodeN']['padding'] Padding to leave around the barcode in user units (default is 1).
['barcodeN']['xres'] Width of the smallest bar in user units (default is 0.4).
['barcodeN']['text'] If set to true this will print text below the barcode image.
['barcodeN']['font'] The font to be used for the text (default is courier).
['barcodeN']['fontsize'] The font size (in points) to be used for the text (default is 8).
['barcodeN']['fontstyle'] The font style to be used for the text. Options are:
  • '' = Regular (the default)
  • 'B' = Bold
  • 'I' = Italic
  • 'U' = Underline
Any combination of these values is allowed.
['barcodeN']['stretchtext'] Must be one of the following:
  • 0 = Disabled
  • 1 = horizontal scaling only if necessary
  • 2 = forced horizontal scaling
  • 3 = character spacing only if necessary
  • 4 = forced character spacing (the default)

Report Structure File

You define the size and position of each barcode in your output with one of the following settings in your Report Structure File.

Note that in all the following samples these values are required:

This is entry is available in Body contents (detail view - multiple):

$structure['body']['fields'][<lineno>][] = array('barcode'       => '<fieldname>',
                                                 'barcode_style' => '<barcode_style>', 
                                                 'width'         => <width>, 
                                                 'height'        => <height>);

This is entry is available in Title Contents:

$structure['title'][] = array('barcode'       => '<text>', or 'barcode' => '%%<text>',
                              'barcode_style' => '<barcode_style>', 
                              'width'         => <width>,
                              'height'        => <height>);

This is entry is available in Body contents (detail view - additional data):

$structure['multi1']['fields'][1][] = array('barcode'       => '<text>', or 'barcode' => '%%<text>',
                                            'barcode_style' => '<barcode_style>', 
                                            'width'         => <width>, 
                                            'height'        => <height>);

Barcode Examples

Here is some sample barcode output. Note that the border around each image is the cell border (produced with 'border' => 'y') and not the image border (produced with 'style' => 'B'). The image which is produced will always be slightly smaller than the cell in which it is contained so that if both have a border you will see two sets of lines with a slight gap between them.

Centre aligned, with stretched text ('style' => 'CDS')

output-to-pdf-009 (1K)

Centre aligned, without text ('style' => 'C')

output-to-pdf-010 (1K)

Left aligned ('style' => 'L')

output-to-pdf-011 (1K)

Right aligned ('style' => 'R')

output-to-pdf-012 (1K)

Centered, with border ('style' => 'CB')

output-to-pdf-013 (1K)

Negative, centered, with stretched text ('style' => 'CDSN')

output-to-pdf-014 (1K)

Stretched image with stretched text ('style' => 'DSX')

output-to-pdf-01 (1K)

Sample PDF output which includes barcodes is available in the Prototype EXAMPLE application on the 'List Person' screen, specifically on the 'Output to PDF (D)' and 'Invoice (PDF)' buttons.


Examples

Here are some examples of the output which can be produced, and the code necessary to produce it.

Example Title

Here is a sample Title that can appear at the top of every page.

Example Title

output-to-pdf-001 (1K)

The above sample title was produced with the following code:

$structure['title'][] = array('text' => '<- first element ->',
                              'width' => 100,
                              'align' => 'center',
                              'border' => 'y',
                              'newline' => 'y');
$structure['title'][] = array('text' => '<- second element',
                              'width' => 40,
                              'align' => 'left',
                              'border' => 'y');
$structure['title'][] = array('text' => 'third element ->',
                              'width' => 40,
                              'align' => 'right',
                              'border' => 'y',
                              'newline' => 'y');

If any text element contains a name prefixed with '%%' it will cause the contents of the field with that name to be extracted from the current record data and inserted into the title. Please see _cm_ListView_header() for details.

Example List view

Here is a sample List view that shows multiple database rows per page, with one row on each line and topped off by a row containing column headings.

Example List view

output-to-pdf-002 (4K)

The above sample List view was produced with the following code:

// set column widths
$structure['columns'][] = array('width' => 42);
$structure['columns'][] = array('width' => 145);
$structure['columns'][] = array('width' => 45);
$structure['columns'][] = array('width' => 30);
$structure['columns'][] = array('width' => 15, 'align' => 'right');

// identify column names and associated labels
$structure['body']['fields'][] = array('subsys_id' => 'Subsys Id');
$structure['body']['fields'][] = array('subsys_desc' => 'Description');
$structure['body']['fields'][] = array('subsys_dir' => 'Directory');
$structure['body']['fields'][] = array('task_prefix' => 'Task Prefix');
$structure['body']['fields'][] = array('count' => 'Count');

Example List view (with totals)

By default the report body in the List View will only contain one line for each record read from the database, but there is the option to print an additional line of accumulated totals, as shown in the following sample:

Example List View with additional totals line

output-to-pdf-008 (17K)

After the last database record has been printed the _cm_ListView_total() method will be called to obtain an additional row of data. If a non-empty associative array is returned the details will be appended to the report. Note that this array should contain the same column (field) names that were printed in the body of the report

Example List view (with line breaks)

By default the report body in the List View will only contain one line for each record read from the database, but there is the option to print any number of additional lines both before and after each database record, as shown in the following sample:

Example List View with additional line breaks

output-to-pdf-015 (17K)

After each database record has been read, but before it is printed, the _cm_ListView_pre_print() method will be called.

Note that the above sample also includes the totals from Example List view (with totals).

Example Detail view (single)

Here is a sample of a Detail view that shows each database record on a separate page. The data is shown in a simple vertical arrangement, one field on each line with labels on the left and data on the right

Example Detail view (single)

output-to-pdf-003 (7K)

The above sample Detail view was produced with the following code:

// set column widths
$structure['columns'][] = array('width' => 40, 'align' => 'right');
$structure['columns'][] = array('width' => 150, 'align' => 'left');

// identify labels and data to be output
$structure['body']['fields'][] = array('pattern_id' => 'Pattern Id');
$structure['body']['fields'][] = array('pattern_desc' => 'Description');
$structure['body']['fields'][] = array('pattern_long_desc' => 'Long Description');
$structure['body']['fields'][] = array('visible_screen' => 'Visible Screen?');
$structure['body']['fields'][] = array('context_preselect' => 'Context Preselect?');
$structure['body']['fields'][] = array('keep_data' => 'Keep Data?');
$structure['body']['fields'][] = array('created_date' => 'Created Date');
$structure['body']['fields'][] = array('created_user' => 'Created By');
$structure['body']['fields'][] = array('revised_date' => 'Revised Date');
$structure['body']['fields'][] = array('revised_user' => 'Revised By');

Example Detail view (multiple)

This is a variation of the Detail view which shows more than one field and associated label on the same line.

Example Detail view (multiple)

output-to-pdf-004 (7K)

The above sample Detail view was produced with the following code:

// set column widths
$structure['columns'][] = array('width' => 40, 'align' => 'right');
$structure['columns'][] = array('width' => 150, 'align' => 'left');

// identify labels and data to be output
$structure['body']['fields'][1][] = array('label' => 'Database Id', 'width' => 40);
$structure['body']['fields'][1][] = array('field' => 'database_id', 'width' => 55);
$structure['body']['fields'][1][] = array('label' => 'Table Id', 'width' => 40);
$structure['body']['fields'][1][] = array('field' => 'table_id', 'width' => 55);

$structure['body']['fields'][2] = array('column_seq' => 'Seq');
$structure['body']['fields'][3] = array('column_id' => 'Column Id');
$structure['body']['fields'][4] = array('column_desc' => 'Description');
$structure['body']['fields'][5] = array('comment' => 'Comments');

$structure['body']['fields'][6][] = array('label' => 'Column Type', 'width' => 40);
$structure['body']['fields'][6][] = array('field' => 'col_type', 'width' => 55);
$structure['body']['fields'][6][] = array('label' => 'Array Type', 'width' => 40);
$structure['body']['fields'][6][] = array('field' => 'col_array_type', 'width' => 55);

$structure['body']['fields'][7] = array('col_values' => 'Column Values');

$structure['body']['fields'][9][] = array('label' => 'Column Size', 'width' => 40);
$structure['body']['fields'][9][] = array('field' => 'user_size', 'width' => 20);
$structure['body']['fields'][9][] = array('label' => 'Precision', 'width' => 40);
$structure['body']['fields'][9][] = array('field' => 'col_precision', 'width' => 20);
$structure['body']['fields'][9][] = array('label' => 'Scale', 'width' => 40);
$structure['body']['fields'][9][] = array('field' => 'col_scale', 'width' => 30);

$structure['body']['fields'][10][] = array('label' => 'Null?', 'width' => 40);
$structure['body']['fields'][10][] = array('field' => 'col_null', 'width' => 55);
$structure['body']['fields'][10][] = array('label' => 'Required?', 'width' => 40);
$structure['body']['fields'][10][] = array('field' => 'is_required', 'width' => 55);

$structure['body']['fields'][12] = array('col_key' => 'Key?');

Example Detail view (additional)

This is a variation of the Detail view which shows multiple rows of additional data which have been obtained from another database table.

Example Detail view (additional)

output-to-pdf-005 (4K)

The above sample Detail view was produced with the following code:

     .....     .....     .....     .....     .....     .....
$structure['body']['fields'][15][] = array('label' => 'Revised Date', 'width' => 40);
$structure['body']['fields'][15][] = array('field' => 'revised_date', 'width' => 55);
$structure['body']['fields'][15][] = array('label' => 'Revised By', 'width' => 40);
$structure['body']['fields'][15][] = array('field' => 'revised_user', 'width' => 55);

// label for related columns
$structure['multi1']['fields'][1][] = array('label' => '#', 'width' => 10, 'align' => 'center');
$structure['multi1']['fields'][1][] = array('label' => 'Address', 'width' => 80, 'align' => 'center');
$structure['multi1']['fields'][1][] = array('label' => 'Telephone', 'width' => 40, 'align' => 'center');
$structure['multi1']['fields'][1][] = array('label' => 'Start', 'width' => 30, 'align' => 'center');
$structure['multi1']['fields'][1][] = array('label' => 'End', 'width' => 30, 'align' => 'center');

// data for related columns
$structure['multi2']['fields'][1][] = array('field' => 'address_no', 'width' => 10, 'align' => 'center');
$structure['multi2']['fields'][1][] = array('field' => 'address_long', 'width' => 80);
$structure['multi2']['fields'][1][] = array('field' => 'telephone_no', 'width' => 40);
$structure['multi2']['fields'][1][] = array('field' => 'start_date', 'width' => 30);
$structure['multi2']['fields'][1][] = array('field' => 'end_date', 'width' => 30);

Example Footer

Here is an example Footer that can appear at the bottom of every page.

Example Footer

output-to-pdf-006 (1K)

The above sample Footer was produced with the following code:

// define contents of page footer
$structure['foot1'][] = array('type' => 'date',   'align' => 'left', 'style' => 'textalignleft');
$structure['foot1'][] = array('type' => 'pageno', 'align' => 'center');
$structure['foot1'][] = array('type' => 'time',   'align' => 'right', 'style' => 'textalignright');

$structure['foot2'][] = array('text' => 'Radicore Software Limited', 'align' => 'center');
 - or - 
$structure['foot2'][] = array('text' => '%%company_name', 'align' => 'center');

Example Invoice

Because each page can be constructed from one or more occurrences from one or more database tables, and each element can be styled as positioned as required, it is possible to construct output documents which are quite complex. Below is a sample invoice which contains the following:

Example Invoice

output-to-pdf-007 (21K)

The code to produce this output is included in the software download as part of the Example Prototype.

Miscellaneous

Here are some miscellaneous functions which you can use:

$this->pdf->AddPage(); Will start a new page, causing Footer() to be called to close the current page followed by Header() to write the headings at the top of a new page.
$this->pdf->SetLeftMargin(n); Will set the left margin to a custom value.
$this->pdf->SetTopMargin(n); Will set the top margin to a custom value.
$this->pdf->SetRightMargin(n); Will set the right mar