|  Download Step-by-step description of the extpdf exampleIn this tutorial, the sample code for the extpdf package is explained in detail.  
The example generates the report  
  based on the following data 	$row = array(
		'text' => <string>,
		'weight' => <float>,
		'date' => <float>,
		'price' => <float>,
		'grp_id' => <int>
	);
 the code can be found in the source files XFPDFExample.php and XFPDFExample.class.php. In order to be able to use the package, it is first necessary to create your own class,
which extends the XPDF class. This class defines everything related to the appearance of the report
to specify:
- Set a logo for the header
- footer
- Fonts and colors
- table columns - colheadler
- data source for the content
- width
- alignment
- format
 
Generation of totals and carry-over lines
Grouping of lines
 The report is then created using this class by transferring the data to print. Overview
Implement the sample class to define the report
1. Fonts/colors, logo and footer
	1. Fonts and colors
	2. Logo
	3. Footer
2. Column definition of the example table
	1. Description
	2. and here the code for it
	3. the individual lines of code explained in more detail
	4. Generating the cell contents that are not directly contained in the row data
3. Grouping of rows with group header and group total
	1. Start grouping and output the subtotal
	2. Output of the last subtotal at the end of the table
4. Subheadings, manipulation of line data before output
Use of the example class
1. Start the table
2. Transfer of the data
3. Close the table
 Implement the sample class to define the reportIn the example the class ExampleXPDFis implemented in the file XFPDFExample.class.php. 
In order to create an extension of theXPDFclass, it has to be refered  via theusestatement. use SKien\XFPDF\XPDF;
class ExampleXPDF extends XPDF
 We design the report in the class constructor. First of all, the Constructor of the parent 
class must be called for initialization. 	public function __construct(string $orientation = 'P') 
	{
		parent::__construct($orientation);
		...
 Fonts/colors, logo and footer	// ...
	$this->initGrid('xfpdf-sample.json');
        
	// define page header and -footer
	$this->setLogo('images/elephpant.png');
	$this->setLogoHeight(9.0);
	$this->addPageFooter("Seite: {PN}/{NP}\tAuthor: S.Kien\t{D} {T}");
 Fonts and colorsThe fonts and colors can be set using the different methods XPDF::SetXXXFont()andXPDF::setXXXColors(). However, it is faster and easier if all settings are made in a JSON file.
(This can also be used multiple times for standardized reports).
This file can be passed to the XPDF::initGrid()method. 	$this->initGrid('xfpdf-sample.json');
 The structure of this JSON file is self-explanatory and can be found in the example file used here. Logo	$this->setLogo('images/elephpant.png');
	$this->setLogoHeight(9.0);
 The logo is printed right-aligned in the header. By default,  the logo will be scaled to a 
height of 8mm. In the example we choose a height of 9mm for our Elephpant. Footer	$this->addPageFooter("Page: {PN}/{NP}\tAuthor: S.Kien\t{D}");
 The page number is extended by the prefix 'Page:', centered is placed an author and 
right-justified the date without time output.  
Following Placeholders are used:
- {PN}: Page number
- {NP}: total number of Pages
- {D}: current date
- {T}: current time (not used in the example) (default setting is: the page and total number of pages on the left and current date 
and time on the right)  
The date and time in the footer will be formatted according to the localeconv ()settings.
> The locale Settings can be changed from the system default using theXPDF::setLocale()method. Column definitions of the example tableColumn descriptionThe table in the example has 8 columns:
1. A pure numbering column - heading "Row"
- width 10mm
- right justified
- automatic numbering
- for the totals lines, the respective text should be placed in this column
 
A date column
- heading "Date"
- width 35mm
- centered
- the content is contained directly in the line data
- Formatting "Sat, 03.12.2020"
A 'normal' text column
- heading "Text"
- dynamic width -> The column becomes so wide that the table fills the entire width of the page
- left-justified
- The content is contained directly in the line data
A column with dynamic content
- heading "Grp."
- width 12mm
- centered
- the content must be generated from the line data
A column with numerical content
- heading "Weight"
- width 20mm
- right justified
- the content is contained directly in the line data
- Formatting with one decimal place and the addition "kg"
- A total have to be calculated for this column
A symbol column
- No heading (column header is connected to the previous column)
- width 8mm
- the symbol to be inserted depends on the value of the column 'weight'
A currency column
- heading "Price"
- width 25mm
- right justified
- the content is contained directly in the line data
- Formatting with currency symbol
- A total have to be calculated for the column
A calculation column
- heading "Cost per Kg"
- width 25mm
- right justified
- the content is calculated from the line data
- Formatting as currency with currency symbol
 ...and here the code for it    $this->addCol('Row', 10, 'R', XPDF::COL_ROW_NR, XPDF::FLAG_TOTALS_TEXT);
	$this->addCol('Date', 35, 'C', 'date', XPDF::FLAG_DATE);
	$this->addCol('Text', -1, 'L', 'text');
	$this->addCol('Grp.', 12, 'C', self::MY_GRP_COL);
	$this->addCol('Weight', 20, 'R', 'weight', XPDF::FLAG_TOTALS_CALC | XPDF::FLAG_NUMBER);
	$iImgCol = $this->addCol(-1, 8, 'C', self::MY_IMAGE_COL, XPDF::FLAG_IMAGE | XPDF::FLAG_TOTALS_EMPTY);
	$this->addCol('Price', 25, 'R', 'price', XPDF::FLAG_TOTALS_CALC | XPDF::FLAG_CUR_SYMBOL);
	$this->addCol('Cost per Kg', 25, 'R', self::MY_CALC_COL, XPDF::FLAG_TOTALS_EMPTY);
        
	// enable the totals/pagetotals and carry-over functionality
	$this->enableTotals(XPDF::TOTALS | XPDF::PAGE_TOTALS | XPDF::CARRY_OVER);
	$this->setTotalsText(
		"My Totals over all:",
		"Subtotal on Page {PN}:",
		"Carry over from Page {PN-1}:");
	
	// set date and number formating.
	$this->setDateFormat('%a, %d.%m.%Y');
	$this->setNumberFormat(1, '', ' kg');
	
	// and set meassuring for the image col
	$this->setColImageInfo($iImgCol, 1.5, 2.5, 3 );
 The individual lines of code explained in more detailColumn 1    $this->addCol('Row', 10, 'R', XPDF::COL_ROW_NR, XPDF::FLAG_TOTALS_TEXT);
 
The column ID `XPDF::COL_ROW_NR` defines the content of the column as an automatic line number.
`XPDF::FLAG_TOTALS_TEXT` defines the output of the text for total lines in this column.
All further columns are connected to this column up to the next total column.
 Column 2	addCol('Date', 30, 'C', 'date', XPDF::FLAG_DATE);
 
The content of the element 'date' of the line data is interpreted as a date value.  
Accepted values:
-	a *DateTime* object
-	an *int* as a UNIX timestamp
-	a *string* is tried to be parsed with `strtotime()`
With the `XPDF::FLAG_DATE` the column is formatted according to`setDateFormat()`.
 Column 3	addCol('Text', -1, 'L', 'text');
 
The content of the element 'text' of the line data is used for output
With a width of -1 the column is defined as a dynamic column. This gives the
Column the remaining available space so that the grid completely fills the width of the page
 Column 4	addCol('Grp.', 12, 'C', self::MY_GRP_COL);
 
The self-defined unique column ID `self::MY_GRP_COL` ensures that the
method `Col()` is called, in which the desired content of the cell can be generated
using the row data.	The `Col()` method must be overloaded in the derived class.
 Column 5	addCol('Weight', 20, 'R', 'weight', XPDF::FLAG_TOTALS_CALC | XPDF::FLAG_NUMBER);
 
The content of the element 'weight' of the line data is interpreted as a numerical value.
`XPDF::FLAG_TOTALS_CALC` indicates that a total will be calculated for this column.
With `XPDF::FLAG_NUMBER` the column is formatted according to` SetNumberFormat()`.
 Column 6	$iImgCol = $this->addCol(-1, 8, 'C', self::MY_IMAGE_COL, XPDF::FLAG_IMAGE | XPDF::FLAG_TOTALS_EMPTY);
 
A value of -1 as the column heading indicates that the column header is connected 
to the header of the previous column.
With the defined column ID `self::MY_IMAGE_COL` the method `col()` is called, in which the
image to be displayed have to be set.
So that a corresponding empty cell is output in a total line in this column, 
`XPDF::FLAG_TOTALS_EMPTY` must be set for this column after the previous defined sum column.
All succeeding columns will be connected until another total column appears or up to 
the end of the grid.
The column index is saved in `$iImgCol` in order to be able to subsequently set 
image position and dimensions with `setColImageInfo()`
 Column 7	addCol('Price', 25, 'R', 'price', XPDF::FLAG_TOTALS_CALC | XPDF::FLAG_CUR_SYMBOL);
 
The content of the element 'price' of the line data is interpreted as a numerical value.
`XPDF::FLAG_TOTALS_CALC` indicates that a total will be calculated for this column
With `XPDF::FLAG_CUR_SYMBOL` the column is formatted according to `setCurrencyFormat()`
 Column 8	addCol('Cost per Kg', 25, 'C', self::MY_CALC_COL, XPDF::FLAG_TOTALS_EMPTY);
 
In order to calculate a value from the row data, the column is given a column ID
(`self::MY_CALC_COL`), which also calls the `col()` method, in which the desired 
content is calculated and formatted.
`XPDF::FLAG_TOTALS_EMPTY` is needed for a correct output of the Total lines.
 Activate the output of totals lines.	$this->enableTotals(XPDF::TOTALS | XPDF::PAGE_TOTALS | XPDF::CARRY_OVER);
 
`XPDF::TOTALS`: Total amount at the end of the document.
`XPDF::PAGE_TOTALS`: Subtotal at end of every page.
`XPDF::CARRY_OVER`: Carry over at the top of the page (starting at the 2nd page).
 Set the texts for the various totals lines.	$this->setTotalsText(
		"My Totals over all:",
		"Subtotal on Page {PN}:",
		"Carry over from Page {PN-1}:");
 The text is printed in the first column which the flag XPDF::FLAG_TOTALS_TEXThas set (usually column 1).
The text can contain the placeholders '{PN}' for the current page and '{PN-1}' for the previous page. Set the output format for all Date columns.	$this->setDateFormat('%a, %d.%m.%Y');
 The format string must comply with the guidelines of the PHP function 'strftime()'.  
See https://www.php.net/manual/en/function.strftime.php Set the output format for all Numeric columns.	$this->setNumberFormat(1, '', ' kg');
 
One decimal place.
No prefix.
Extension 'kg'.
 Decimal and thousand separators are read from the localeconv()settings.
> The locale Settings can ne changed from the system default using theXPDF::setLocale()method. Setting the size and position of the image in the symbol column	$this->setColImageInfo($iImgCol, 1.5, 2.5, 3 );
 
the column index that was saved when the column was created.
position 1.5mm from the top of the line.
position 2.5mm from the left edge of the cell.
height 3mm.
No width is specified so the aspect ratio of the original graphic is retained.
 Generating the cell contents that are not directly contained in the row dataIf the content of a cell to be printed does not exist directly in the row data,
a unique ID can be set for this column instead of the data field. The desired output
have to be generated in the method ExampleXPDF::col().
This is e.g. the case if
-	the data contain a numeric ID and a corresponding text have to be printed
-	a calculation result based on several values have to be printed
-	special formatting is required
-	the value to be displayed depends on other external factors In the example, columns 4, 5 and 8 are generated internally. 	/ const for own column ID's */
	const MY_GRP_COL = 1;
	const MY_IMAGE_COL = 2;
	const MY_CALC_COL = 3;
 Unique ID's are defined for these columns, which are used when the columns are
created with addCol(). The processing is implemented in the ExampleXPDF::col()method: 	protected function Col(int $iCol, array $row, bool &$bFill) : string 
	{
		$strCol = '';
		switch ($iCol) {
			case self::MY_GRP_COL:
				$aValues = array( '', 'Grp. A', 'Grp. B', 'Grp. C', 'Grp. D');
				if ($row['grp_id'] < 0 && $row['grp_id'] <= 4) {
					$strCol = $aValues[$row['grp_id']];
				}
				break;
			case self::MY_IMAGE_COL:
				$strCol = 'images/';
				$fltWeight = floatval($row['weight']);
				if ($fltWeight > 35.0) {
					// ... to heavy
					$strCol .= 'red.png';
				} else if ($fltWeight > 20.0) {
					// ... just in the limit
					$strCol .= 'yellow.png';
				} else {
					$strCol .= 'green.png';
				}
				break;
			case self::MY_CALC_COL:
				$fltPricePerKg = 0.0;
				if (floatval($row['weight']) != 0) {
					$fltPricePerKg = floatval($row['price']) / floatval($row['weight']);;
				}
				$strCol = $this->_FormatCurrency($fltPricePerKg, true);
				break;
			default:
				// very important to call parent class !!
				$strCol = parent::col($iCol, $row, $bFill);
				break;
		}
		return $strCol;
	}
 
`self::MY_GRP_COL`  
The corresponding text abbreviation is assigned here instead of the numerical data value.
`self::MY_IMAGE_COL`  
A corresponding image is set depending on the value of hte data field 'weight'.
- a red flag if weight > 35
- a yellow flag for values between 20 and 35
- a green flag for all the rest
`self::MY_CALC_COL`  
The value is calculated from the row data and formatted as a currency field.
`default`  
The method of the parent class must be called in the defaultbranch!
 Grouping of rows with group header and group totalTo illustrate the grouping function with subtotals, in the example for each month 
a subheading and a subtotal is issued.
In order to be able to react to a change of month, the class implements the property
strMonthto hold the month of the current row, which is initialized with an empty string. 	/ @var string remember month and year of the previous row */ 
	protected string $strMonth = '';
 The following steps are required:
-	Start of the grouping and printing of the subtotal.
-	Output of the last subtotal at the end of the table. Start grouping and output the subtotalTo determine whether a month change occurs before a new line is printed, the method
XPDF::PreRow()is overloaded in the example class. This method is always called
BEFORE the output of a new line. 	protected function PreRow(array &$row) : string
	{
		// for grouping
		$date = strtotime($row['date']);
		$strMonth = date('Y-m', $date);
		if ($this->strMonth != $strMonth) {
			// first row we have no subtotals...
			if ($this->strMonth != '') {
				$this->endGroup();
			}
			$this->startGroup('Totals ' . strftime('%B %Y', $date) . ':', strftime('%B %Y', $date));
			$this->strMonth = $strMonth;
		}
		...
	}
 Comparing the month of the current row ($row 'date']) with the previous month stored 
in$this->strMonth, it can be determined whether there is a change of month.  
If the previous month is not an empty string (this is the case when printing the 
first row...), the callXPDF::endGroup ()prints the subtotal for the previous month 
and resets the calculated values for next month.  
Then the grouping for the new (or first) month is started withXPDF::startGroup(). 
If nostrHeaderparameter is passed to this function, only the internal calculation 
of the subtotals started again, but no subheading will be printed.  
Finally, the new month is stored in thestrMonthproperty. Output of the last subtotal at the end of the tableIn order to print the subtotal for the last month of the report, this must be 
output AFTER the last data line but BEFORE the output of the over all total. Since this 
understandably cannot be intercepted in XPDF:PreRow(), the methodXPDF :: EndGrid ()is overloaded for this purpose, which must always be called after all data has been 
transferred. 	public function endGrid() : void
	{
		// end last group for subtotals before we call the parent (!!! don't forget that!!)
		$this->endGroup();
		parent::endGrid();
	}
 Here XPDF::EndGroup()is called one last time before further processing is passed 
on to the parent class.
(the call of the parent class must not be forgotten under any circumstances!) Subheadings, manipulation of line data before outputSometimes it is necessary to change the data for a row due to a specific criterion 
before output or to add additional information and/or to insert a subheading for 
better identification.
For this task the method XPDF::PreRow()will also be the proper place to do. 	protected function preRow(array &$row) : string
	{
		...
		$strSubRow = '';
		if ($this->iRow == 47) {
			$strSubRow = '... next Row have been manipulated in ExampleXPDF::preRow(array &$row)!';
			$row['text'] = 'manipulated Rowdata!';
		}
		if ($this->iRow == 56) {
			$row['text'] = 'manipulated Rowdata without Subrow!';
		}
		return $strSubRow;
	}
 Note that the $ row parameter is passed as a reference. Changes to the data 
therefore also affect the calling code.  
In the example class, a data field is simply changed for a row with a fixed row 
number (# 47) and this line is preceeded with a corresponding subheading.
In the case of a second fixed row (# 56), only the data field is changed and 
the line then is printed without a subheading. Use of the example classThe report is created in the file XFPDFExample.php. The necessary files of the 
package are integrated via autoloader and the example class is included. require_once 'autoloader.php';
require_once 'XFPDFExample.class.php';
 First, an instance of the example class is created and some information and the 
headings are set. $pdf = new ExampleXPDF();
$pdf = new ExampleXPDF();
// set some file information
$pdf->setInfo('XFPDF', 'Example', 'PHP classes', 'Keyword1, Keyword2, ...');
$pdf->setPageHeader('Create PDF Table', 'using extpdf package from PHPClasses.org');
 With SetInfo(), file information is set that is displayed in the created document 
under the document properties.
- 'XFPDF': title
- 'Example': Brief description
- 'PHP classes': Author
- 'Keyword1, Keyword2, ...': Keywords setPageHeader()sets the title and optionally a short description:
- Title left-justified in the font specified withXPDF::setHeaderFont()- the short description also left-justified in a new line in the fontXPDF::setSubjectFont()
 After all document settings have been made, the table can be generated: $pdf->prepare();
$date = time();
for ($iRow=1; $iRow <= 100; $iRow++) {
	$row = array(
		'text' => 'Text in var Col, Line ' . $iRow,
		'weight' => (rand(10, 500) / 10),
		'date' => date('Y-m-d', $date),
		'price' => (rand(10, 2000) / 9),
		'grp_id' => rand(1, 4)
	);
	$pdf->row($row);
	$date += 24 60 60;
}
$pdf->endGrid();
$pdf->createPDF('example');
 Start the table$pdf->prepare();
 This call starts the creation of the table. Transfer of the datafor ($iRow = 1; $iRow <= 100; $iRow++) {
	$row = array(
		'text' => 'Text in var Col, Line ' . $iRow,
		'weight' => (rand(10, 500) / 10),
		'date' => date('Y-m-d', $date),
		'price' => (rand(10, 2000) / 9),
		'grp_id' => rand(1, 4)
	);
	$pdf->row($row);
	$date += 24 60 60;
}
 For the sake of simplicity, the line data in the example is generated in a for loop with 
random values and transferred with $pdf->row($row). In practice, in most cases
the data will come from a database query or some other data source.
This possibly looks like this: $db = new mysqli(...)
$dbres = $db->query('some SQL query');
while (($row = $dbres->fetch_array(MYSQLI_ASSOC) !== false) {
    $pdf->row($row);
}
 Beenden der Tabelle$pdf->endGrid();
 At the end of the table, this function is called, which, if necessary, generates 
an end total and closes the table. Generate the PDF file$pdf->createPDF('example');
 The last step is to create the file. |