views_pdf_template.php in Views PDF 7.3
Same filename and directory in other branches
PDF Class to generate PDFs with native PHP. This class based on FPDF and FPDI.
A direct include of this class is not realy possible. The basic functions of drupal must be present.
File
views_pdf_template.phpView source
<?php
/**
* @file
* PDF Class to generate PDFs with native PHP. This class based on FPDF and FPDI.
*
* A direct include of this class is not realy possible. The basic functions of drupal must be
* present.
*
*/
/**
* Get the depending classes.
*/
require_once views_pdf_get_library('tcpdf') . '/tcpdf.php';
if (file_exists(views_pdf_get_library('fpdi') . '/fpdi_bridge.php')) {
require_once views_pdf_get_library('fpdi') . '/fpdi_bridge.php';
}
else {
require_once views_pdf_get_library('fpdi') . '/fpdi2tcpdf_bridge.php';
}
require_once views_pdf_get_library('fpdi') . '/fpdi.php';
/**
* The main class to generate the PDF.
*/
class PdfTemplate extends FPDI {
protected static $fontList = NULL;
protected static $fontListClean = NULL;
protected static $templateList = NULL;
protected static $hyphenatePatterns = NULL;
protected $defaultFontStyle = '';
protected $defaultFontFamily = 'helvetica';
protected $defaultFontSize = '11';
protected $defaultTextAlign = 'L';
protected $defaultFontColor = '000000';
protected $defaultPageTemplateFiles = array();
protected $mainContentPageNumber = 0;
protected $rowContentPageNumber = 0;
protected $defaultOrientation = 'P';
protected $defaultFormat = 'A4';
protected $addNewPageBeforeNextContent = FALSE;
protected $elements = array();
protected $headerFooterData = array();
protected $headerFooterOptions = array();
protected $view = NULL;
protected $display = NULL;
protected $y_header = 0;
protected $y_footer = 0;
protected $lastWritingPage = 1;
protected $lastWritingPositions;
protected $position = '';
protected $tableHeader = array();
protected static $defaultFontList = array(
'almohanad' => 'AlMohanad',
'arialunicid0' => 'ArialUnicodeMS',
'courier' => 'Courier',
'courierb' => 'Courier Bold',
'courierbi' => 'Courier Bold Italic',
'courieri' => 'Courier Italic',
'dejavusans' => 'DejaVuSans',
'dejavusansb' => 'DejaVuSans-Bold',
'dejavusansbi' => 'DejaVuSans-BoldOblique',
'dejavusansi' => 'DejaVuSans-Oblique',
'dejavusanscondensed' => 'DejaVuSansCondensed',
'dejavusanscondensedb' => 'DejaVuSansCondensed-Bold',
'dejavusanscondensedbi' => 'DejaVuSansCondensed-BoldOblique',
'dejavusanscondensedi' => 'DejaVuSansCondensed-Oblique',
'dejavusansmono' => 'DejaVuSansMono',
'dejavusansmonob' => 'DejaVuSansMono-Bold',
'dejavusansmonobi' => 'DejaVuSansMono-BoldOblique',
'dejavusansmonoi' => 'DejaVuSansMono-Oblique',
'dejavuserif' => 'DejaVuSerif',
'dejavuserifb' => 'DejaVuSerif-Bold',
'dejavuserifbi' => 'DejaVuSerif-BoldItalic',
'dejavuserifi' => 'DejaVuSerif-Italic',
'dejavuserifcondensed' => 'DejaVuSerifCondensed',
'dejavuserifcondensedb' => 'DejaVuSerifCondensed-Bold',
'dejavuserifcondensedbi' => 'DejaVuSerifCondensed-BoldItalic',
'dejavuserifcondensedi' => 'DejaVuSerifCondensed-Italic',
'freemono' => 'FreeMono',
'freemonob' => 'FreeMonoBold',
'freemonobi' => 'FreeMonoBoldOblique',
'freemonoi' => 'FreeMonoOblique',
'freesans' => 'FreeSans',
'freesansb' => 'FreeSansBold',
'freesansbi' => 'FreeSansBoldOblique',
'freesansi' => 'FreeSansOblique',
'freeserif' => 'FreeSerif',
'freeserifb' => 'FreeSerifBold',
'freeserifbi' => 'FreeSerifBoldItalic',
'freeserifi' => 'FreeSerifItalic',
'hysmyeongjostdmedium' => 'HYSMyeongJoStd-Medium-Acro',
'helvetica' => 'Helvetica',
'helveticab' => 'Helvetica Bold',
'helveticabi' => 'Helvetica Bold Italic',
'helveticai' => 'Helvetica Italic',
'kozgopromedium' => 'KozGoPro-Medium-Acro',
'kozminproregular' => 'KozMinPro-Regular-Acro',
'msungstdlight' => 'MSungStd-Light-Acro',
'stsongstdlight' => 'STSongStd-Light-Acro',
'symbol' => 'Symbol',
'times' => 'Times New Roman',
'timesb' => 'Times New Roman Bold',
'timesbi' => 'Times New Roman Bold Italic',
'timesi' => 'Times New Roman Italic',
'zapfdingbats' => 'Zapf Dingbats',
'zarbold' => 'ZarBold',
);
/**
* This method overrides the parent constructor method.
* this is need to reset the default values.
*/
public function __construct($orientation = 'P', $unit = 'mm', $format = 'A4', $unicode = TRUE, $encoding = 'UTF-8', $diskcache = FALSE) {
parent::__construct($orientation, $unit, $format, $unicode, $encoding, $diskcache);
$this->defaultOrientation = $orientation;
$this->defaultFormat = $format;
}
public function setDefaultFontSize($size) {
$this->defaultFontSize = $size;
}
public function setDefaultFontFamily($family) {
$this->defaultFontFamily = $family;
}
public function setDefaultFontStyle($style) {
$this->defaultFontStyle = $style;
}
public function setDefaultTextAlign($style) {
$this->defaultTextAlign = $style;
}
public function setDefaultFontColor($color) {
$this->defaultFontColor = $color;
}
public function setDefaultPageTemplate($path, $key, $pageNumbering = 'main') {
$this->defaultPageTemplateFiles[$key] = array(
'path' => $path,
'numbering' => $pageNumbering,
);
}
// Sets up the header & footer.
// Rendering is deferred to the Header() and Footer() functions.
public function setViewsHeaderFooter($display) {
$this->display = $display;
$this->y_header = $display
->get_option('header_margin');
$this
->SetY(-($this->bMargin - $display
->get_option('footer_spacing')));
$this->y_footer = $this->y;
}
/**
* Function override to output the header.
*/
function Header() {
$this
->_format_header_footer('header');
// Output the table header if required.
if (!empty($this->tableHeader)) {
$this->cell_padding = $this->cellPaddings;
foreach ($this->tableHeader as $header) {
list($x, $y, $label, $headerOptions, $view, $id) = $header;
$this
->renderItem($x, $y, $label, NULL, $headerOptions, $view, $id, FALSE, TRUE);
}
}
}
/**
* Function override to output the footer.
*/
function Footer() {
$this
->_format_header_footer('footer');
}
/*
* Common function to output header or footer.
*
* @param $h_f 'header' or 'footer'.
*/
function _format_header_footer($h_f) {
$display = $this->display;
// Get the leading/trailing header/footer option (e.g. "succeed_header").
if ((empty($this->position) || $this->position == 'closing' || $display
->get_option("{$this->position}_{$h_f}")) && !empty($rendered = $display
->render_area("{$h_f}"))) {
$this
->SetTextColorArray($display
->get_option("{$h_f}_font_color"));
$this
->SetFont($display
->get_option("{$h_f}_font_family"), implode('', $display
->get_option("{$h_f}_font_style")), $display
->get_option("{$h_f}_font_size"));
$yvar = "y_{$h_f}";
$this
->writeHTMLCell(0, 0, (double) $this->lMargin, (double) $this->{$yvar}, format_string($rendered, array(
'!page' => $this
->getPage(),
)), 0, 0, FALSE, TRUE, $display
->get_option("{$h_f}_text_align"));
}
if ($h_f == 'footer' && $this->position == 'closing') {
$this->position = 'succeed';
}
}
/**
* Converts a hex color into an array with RGB colors.
*/
public function convertHexColorToArray($hex) {
if (drupal_strlen($hex) == 6) {
$r = drupal_substr($hex, 0, 2);
$g = drupal_substr($hex, 2, 2);
$b = drupal_substr($hex, 4, 2);
return array(
hexdec($r),
hexdec($g),
hexdec($b),
);
}
elseif (drupal_strlen($hex) == 3) {
$r = drupal_substr($hex, 0, 1);
$g = drupal_substr($hex, 1, 1);
$b = drupal_substr($hex, 2, 1);
return array(
hexdec($r),
hexdec($g),
hexdec($b),
);
}
else {
return array();
}
}
/**
* Parse color input into an array.
*
* @param string $color
* Color entered by the user
*
* @return array
* Color as an array
*/
public function parseColor($color) {
$color = trim($color, ', ');
$components = explode(',', $color);
if (count($components) == 1) {
return $this
->convertHexColorToArray($color);
}
else {
// Remove white spaces from comonents:
foreach ($components as $id => $component) {
$components[$id] = trim($component);
}
return $components;
}
}
/**
* This method draws a field on the PDF.
*/
public function drawContent($row, $options, &$view = NULL, $key = NULL, $printLabels = TRUE) {
if (empty($view) && empty($key)) {
return;
}
$content = $view->field[$key]
->theme($row);
if (!is_array($options)) {
$options = array();
}
// Set defaults:
$options += array(
'position' => array(),
'text' => array(),
'render' => array(),
);
$options['position'] += array(
'corner' => 'top_left',
'x' => 0,
'y' => 0,
'object' => 'last_position',
'width' => 0,
'height' => 0,
);
$options['text'] += array(
'font_family' => 'default',
'font_style' => '',
);
$options['render'] += array(
'eval_before' => '',
'eval_after' => '',
'bypass_eval_before' => FALSE,
'bypass_eval_after' => FALSE,
'custom_layout' => FALSE,
'custom_post' => FALSE,
);
// Grid-mode flag, true if grid options are provided.
$isgrid = !empty($options['grid']);
// Get the page dimensions
$pageDim = $this
->getPageDimensions();
// Check if there is a minimum space defined. If so, then ensure
// that we have enough space left on this page. If not force adding
// a new one.
if (isset($options['render']['minimal_space'])) {
$enoughSpace = $this->y + $this->bMargin + $options['render']['minimal_space'] < $pageDim['hk'];
}
else {
$enoughSpace = TRUE;
}
// Check if there is a page, if not add it:
if (!$enoughSpace or $this
->getPage() == 0 or $this->addNewPageBeforeNextContent) {
$this->addNewPageBeforeNextContent = FALSE;
$this
->addPage();
}
// Get the page dimenstions again, because it can be that a new
// page was added with new dimensions.
$pageDim = $this
->getPageDimensions();
// Calculate pseudo-margins, in grid mode these define the limits of the cell.
$lBound = (double) $this->lMargin;
$tBound = (double) $this->tMargin;
// For a grid cell, increase margin by cell offset.
if ($isgrid) {
if ($options['grid']['new_cell']) {
// Temporarily set the left margin to the cell boundary,
// as this is the position the X co-ordinate automatically resets to.
$lBound = (double) $this->original_lMargin + $options['grid']['x'];
$this
->SetLeftMargin($lBound);
$this
->SetY($tBound);
}
$tBound += $options['grid']['y'];
$rBound = $lBound + $options['grid']['w'];
$bBound = $tBound + $options['grid']['h'];
}
else {
$rBound = (double) $pageDim['wk'] - (double) $this->rMargin;
$bBound = (double) $pageDim['hk'] - (double) $this->bMargin;
}
// Determine the last writing y coordinate, if we are on a new
// page we need to reset it back to the top margin.
if ($this->lastWritingPage != $this
->getPage() or $this->y + $this->bMargin > $pageDim['hk']) {
$last_writing_y_position = $this->tMargin;
}
else {
$last_writing_y_position = $this->y;
}
// Determine the x and y coordinates
if ($options['position']['object'] == 'last_position') {
$x = (double) $this->x + (double) $options['position']['x'];
$y = (double) $this->y + (double) $options['position']['y'];
}
elseif ($options['position']['object'] == 'page') {
switch ($options['position']['corner']) {
default:
case 'top_left':
$x = (double) $options['position']['x'] + $lBound;
$y = (double) $options['position']['y'] + $tBound;
break;
case 'top_right':
$x = (double) $options['position']['x'] + $rBound;
$y = (double) $options['position']['y'] + $tBound;
break;
case 'bottom_left':
$x = (double) $options['position']['x'] + $lBound;
$y = (double) $options['position']['y'] + $bBound;
break;
case 'bottom_right':
$x = (double) $options['position']['x'] + $rBound;
$y = (double) $options['position']['y'] + $bBound;
break;
}
}
elseif ($options['position']['object'] == 'self' or preg_match('/field\\_(.*)/', $options['position']['object'], $rs)) {
if ($options['position']['object'] == 'last') {
$relative_to_element = $this->lastWritingElement;
}
elseif ($options['position']['object'] == 'self') {
$relative_to_element = $key;
}
else {
$relative_to_element = $rs[1];
}
if (isset($this->elements[$relative_to_element])) {
switch ($options['position']['corner']) {
default:
case 'top_left':
$x = (double) $options['position']['x'] + (double) $this->elements[$relative_to_element]['x'];
$y = (double) $options['position']['y'] + (double) $this->elements[$relative_to_element]['y'];
break;
case 'top_right':
$x = (double) $options['position']['x'] + (double) $this->elements[$relative_to_element]['x'] + (double) $this->elements[$relative_to_element]['width'];
$y = (double) $options['position']['y'] + (double) $this->elements[$relative_to_element]['y'];
break;
case 'bottom_left':
$x = (double) $options['position']['x'] + (double) $this->elements[$relative_to_element]['x'];
$y = (double) $options['position']['y'] + (double) $this->elements[$relative_to_element]['y'] + (double) $this->elements[$relative_to_element]['height'];
break;
case 'bottom_right':
$x = (double) $options['position']['x'] + (double) $this->elements[$relative_to_element]['x'] + (double) $this->elements[$relative_to_element]['width'];
$y = (double) $options['position']['y'] + (double) $this->elements[$relative_to_element]['y'] + (double) $this->elements[$relative_to_element]['height'];
break;
}
// Handle if the relative element is on another page. So using the
// the last writing position instead for y.
if ($this
->getPage() != $this->elements[$relative_to_element]['page'] && $options['position']['object'] != 'self') {
$this
->setPage($this->elements[$relative_to_element]['page']);
}
elseif ($this
->getPage() != $this->elements[$relative_to_element]['page'] && $options['position']['object'] == 'self') {
$y -= $this->elements[$relative_to_element]['y'] + $last_writing_y_position;
$this
->SetPage($this->lastWritingPage);
}
}
else {
$x = (double) $this->x;
$y = (double) $last_writing_y_position;
}
}
else {
return;
}
// In grid mode, set width and height not to exceed edge of grid cell.
if ($isgrid) {
// If start point is outside the cell, just return.
if ($x >= $rBound || $y >= $bBound) {
return;
}
$maxw = $rBound - $x;
$maxh = $bBound - $y;
$options['position']['width'] = $options['position']['width'] == 0 ? $maxw : min($options['position']['width'], $maxw);
$options['position']['height'] = $options['position']['height'] == 0 ? $maxh : min($options['position']['height'], $maxh);
}
$this
->SetX($x);
$this
->SetY($y);
$this
->renderItem($x, $y, $content, $row, $options, $view, $key, $printLabels, FALSE, $isgrid);
}
protected function renderItem($x, $y, $content, $row, $options, &$view, $key, $printLabels = TRUE, $istable = FALSE, $isgrid = FALSE) {
// Only render if not excluded, and not a header or footer field.
if (empty($view->field[$key]->options['exclude']) && $options['position']['object'] !== 'header_footer') {
$pageDim = $this
->getPageDimensions();
// Apply the hyphenation patterns to the content:
if (!isset($options['text']['hyphenate']) && is_object($view) && is_object($view->display_handler)) {
$options['text']['hyphenate'] = $view->display_handler
->get_option('default_text_hyphenate');
}
if (isset($options['text']['hyphenate']) && $options['text']['hyphenate'] != 'none') {
$patternFile = $options['text']['hyphenate'];
if ($options['text']['hyphenate'] == 'auto' && is_object($row)) {
// Workaround:
// Since "$nodeLanguage = $row->node_language;" does not work anymore,
// we using this:
if (isset($row->_field_data['nid']['entity']->language)) {
$nodeLanguage = $row->_field_data['nid']['entity']->language;
foreach (self::getAvailableHyphenatePatterns() as $file => $pattern) {
if (stristr($pattern, $nodeLanguage) !== FALSE) {
$patternFile = $file;
break;
}
}
}
}
$patternFile = views_pdf_get_library('tcpdf') . '/hyphenate_patterns/' . $patternFile;
if (file_exists($patternFile)) {
if (method_exists('TCPDF_STATIC', 'getHyphenPatternsFromTEX')) {
$hyphen_patterns = TCPDF_STATIC::getHyphenPatternsFromTEX($patternFile);
}
else {
$hyphen_patterns = $this
->getHyphenPatternsFromTEX($patternFile);
}
// Bugfix if you like to print some html code to the PDF, we
// need to prevent the replacement of this tags.
$content = str_replace('>', '&gt;', $content);
$content = str_replace('<', '&lt;', $content);
$content = $this
->hyphenateText($content, $hyphen_patterns);
}
}
// Set css variable
if (is_object($view) && is_object($view->display_handler)) {
$css_file = $view->display_handler
->get_option('css_file');
}
$font_size = empty($options['text']['font_size']) ? $this->defaultFontSize : $options['text']['font_size'];
$font_family = $options['text']['font_family'] == 'default' || empty($options['text']['font_family']) ? $this->defaultFontFamily : $options['text']['font_family'];
$font_style = is_array($options['text']['font_style']) ? $options['text']['font_style'] : $this->defaultFontStyle;
$textColor = !empty($options['text']['color']) ? $this
->parseColor($options['text']['color']) : $this
->parseColor($this->defaultFontColor);
$w = $options['position']['width'];
$h = $options['position']['height'];
$border = !empty($options['text']['border']) ? $options['text']['border'] : 0;
$align = isset($options['text']['align']) ? $options['text']['align'] : $this->defaultTextAlign;
$fill = 0;
$ln = 1;
$reseth = TRUE;
$stretch = 0;
$ishtml = $istable || $isgrid ? 0 : (isset($options['render']['is_html']) ? $options['render']['is_html'] : 1);
$stripHTML = !$ishtml;
$autopadding = TRUE;
if ($istable) {
// For table mode we use a precise height.
$maxh = $h;
}
elseif ($isgrid) {
// For grid mode we use auto-height, with max height defining the grid boundary.
$maxh = $h;
$h = 0;
}
else {
$maxh = 0;
}
// Render Labels
$prefix = '';
if ($printLabels && !empty($view->field[$key]->options['label'])) {
$prefix = $view->field[$key]->options['label'];
if ($view->field[$key]->options['element_label_colon']) {
$prefix .= ':';
}
$prefix .= ' ';
}
if (!empty($prefix) && !$stripHTML) {
// If label HTML has been customised, add tag and classes as required.
$label_info = $view->field[$key]->options;
if ($tag = $label_info['element_label_type']) {
$classes = array();
if ($label_info['element_label_class']) {
$classes[] = $label_info['element_label_class'];
}
if ($label_info['element_default_classes']) {
$classes[] = 'views-label';
$classes[] = "views-label-{$label_info['id']}";
}
$class = !empty($classes) ? ' class="' . implode(' ', $classes) . '"' : '';
$prefix = "<{$tag}{$class}>{$prefix}</{$tag}>";
}
}
// Run eval before.
if (VIEWS_PDF_PHP && !empty($options['render']['eval_before'])) {
if (empty($options['render']['bypass_eval_before'])) {
$content = php_eval($options['render']['eval_before']);
}
else {
eval($options['render']['eval_before']);
}
}
if (!empty($options['render']['custom_layout'])) {
// Custom layout hook.
$layout_data = array(
'x' => &$x,
'y' => &$y,
'h' => &$h,
'w' => &$w,
'content' => &$content,
'key' => &$key,
'view' => &$view,
'this' => &$this,
'border' => &$border,
'color' => &$textColor,
'font' => &$font_family,
'font_style' => &$font_style,
'font_size' => &$font_size,
);
drupal_alter('views_pdf_custom_layout', $layout_data);
}
// Add css if there is a css file set and stripHTML is not active.
if (!empty($css_file) && is_string($css_file) && !$stripHTML && !empty($content)) {
$content = '<link type="text/css" rel="stylesheet" media="all" href="' . $css_file . '" />' . PHP_EOL . $content;
}
// Set Text Color.
$this
->SetTextColorArray($textColor);
// Set font.
$this
->SetFont($font_family, implode('', $font_style), $font_size);
// Save the last page before starting writing, this
// is needed to detect if we write over a page. Then we need
// to reset the y coordinate for the 'last_writing' position option.
$this->lastWritingPage = $this
->getPage();
if ($stripHTML) {
$content = html_entity_decode(strip_tags($content), ENT_QUOTES | ENT_HTML401);
}
// Write the content of a field to the pdf file:
if ($istable) {
$this->cell_padding['T'] = $this->cell_padding['B'] = !empty($options['text']['vpad']) ? $options['text']['vpad'] : 0;
}
$this
->MultiCell($w, $h, $prefix . $content, $border, $align, $fill, $ln, $x, $y, $reseth, $stretch, $ishtml, $autopadding, $maxh);
// Post render.
if (!empty($options['render']['custom_post'])) {
drupal_alter('views_pdf_custom_post', $view);
}
// Run eval after.
if (VIEWS_PDF_PHP && !empty($options['render']['eval_after'])) {
// Questionable whether there's any point supporting php_eval()
// after render. It has no access to the local context, and can
// only return a value. But the output has already been written
// so there's nothing that can be done with the return value!
if (empty($options['render']['bypass_eval_after'])) {
php_eval($options['render']['eval_after']);
}
else {
eval($options['render']['eval_after']);
}
}
// Write Coordinates of element.
$this->elements[$key] = array(
'x' => $x,
'y' => $y,
'width' => empty($w) ? $pageDim['wk'] - $this->rMargin - $x : $w,
'height' => $this->y - $y,
'page' => $this->lastWritingPage,
);
$this->lastWritingElement = $key;
}
}
/**
* This method draws a table on the PDF.
*/
public function drawTable(&$view, $options) {
$rows = $view->result;
$columns = $view->field;
$pageDim = $this
->getPageDimensions();
$width = (double) $pageDim['wk'] - (double) $this->lMargin - (double) $this->rMargin;
$sumWidth = 0;
$numberOfColumnsWithoutWidth = 0;
// Default header height is height of default font.
$options['position']['header_style']['height'] = $this
->getCellHeight($this->defaultFontSize / $this->k);
// Compute the row height as the max of default font size and explicit row height.
$options['position']['body_style']['height'] = max($options['position']['row_height'], $options['position']['header_style']['height']);
// Create a safety height padding, in current units.
// Without this, FP rounding errors can result in the computed row height
// being fractionally too small on occasional rows, making them invisible.
$safety = 0.01 / $this->k;
// Pre-process column info to determine layout parameters.
$borderpad = array(
'header_style' => $safety,
'body_style' => $safety,
);
foreach ($columns as $id => $column) {
// Check for hide-empty columns and scan results to check if they are empty.
if (!empty($options['info'][$id]['empty']['hide_empty']) && ($row = reset($rows))) {
do {
$content = $view->field[$id]
->theme($row);
} while (empty($content) && ($row = next($rows)));
// If the loop got to the end of the rows, then they are all empty.
if (!$row) {
$column->options['exclude'] = TRUE;
}
}
// Skip excluded fields and the page-break field.
if (empty($column->options['exclude']) && $id != 'page_break') {
// Options are merge of specific options and defaults.
if (isset($options['info']['_default_'])) {
if (!empty($options['info'][$id])) {
// Merge column specifics with column defaults.
$options['info'][$id] = array_replace_recursive($options['info']['_default_'], $options['info'][$id]);
}
else {
$options['info'][$id] = $options['info']['_default_'];
}
}
elseif (empty($options['info'][$id])) {
$options['info'][$id] = array();
}
if (!empty($options['info'][$id]['position']['width'])) {
$sumWidth += $options['info'][$id]['position']['width'];
}
else {
$numberOfColumnsWithoutWidth++;
}
foreach (array(
'header_style',
'body_style',
) as $style) {
$font_size = empty($options['info'][$id][$style]['text']['font_size']) ? $this->defaultFontSize : $options['info'][$id][$style]['text']['font_size'];
$cell_height = $this
->getCellHeight($font_size / $this->k);
// Add extra padding if specified.
if (!empty($options['info'][$id][$style]['text']['vpad'])) {
$cell_height += $options['info'][$id][$style]['text']['vpad'] * 2;
}
// Increase row height if column font size requires.
$options['position'][$style]['height'] = max($options['position'][$style]['height'], $cell_height);
// Get extra vertical padding required if borders are present.
if (!empty($options['info'][$id][$style]['text']['border'])) {
$this->cell_padding['T'] = $this->cell_padding['B'] = 0;
$extra_paddings = $this
->adjustCellPadding($options['info'][$id][$style]['text']['border']);
$borderpad[$style] = max($borderpad[$style], $extra_paddings['T'] + $extra_paddings['B']);
}
}
}
}
$defaultColumnWidth = $numberOfColumnsWithoutWidth > 0 ? $defaultColumnWidth = ($width - $sumWidth) / $numberOfColumnsWithoutWidth : 0;
// Increase heights to allow for borders.
foreach (array(
'header_style',
'body_style',
) as $style) {
$options['position'][$style]['height'] += $borderpad[$style];
}
// Get table header spacing, or set to 0 if not using header.
// Note the value of $hspace is the distance from the top of the header to the first line,
// but the option value in the settings UI is the space from the bottom of the header
// to the first line.
if (empty($options['position']['use_header'])) {
$hspace = 0;
}
else {
$hspace = $options['position']['header_style']['height'];
if (is_numeric($options['position']['h'])) {
$hspace += $options['position']['h'];
}
}
// Increase the top margin by the table header spacing.
$this->tMargin += $hspace;
$y = (double) $this->tMargin;
$x = (double) $this->lMargin;
// Add default option values, and gather all the column header data into an array for use by the Header() function.
$xh = $x;
$yh = $y - $hspace;
foreach ($columns as $id => $column) {
// Skip excluded fields and the page-break field.
if (empty($column->options['exclude']) && $id != 'page_break') {
foreach (array(
'header_style',
'body_style',
) as $style) {
$options['info'][$id][$style] += array(
'position' => array(),
'text' => array(),
'render' => array(),
);
$options['info'][$id][$style]['position'] += array(
'corner' => 'top_left',
'x' => NULL,
'y' => NULL,
'object' => '',
'width' => NULL,
'height' => NULL,
);
$options['info'][$id][$style]['text'] += array(
'font_family' => 'default',
'font_style' => array(),
);
$options['info'][$id][$style]['render'] += array(
'eval_before' => '',
'eval_after' => '',
);
}
if ($hspace) {
$headerOptions = $options['info'][$id]['header_style'];
$headerOptions['position']['width'] = !empty($options['info'][$id]['position']['width']) ? $options['info'][$id]['position']['width'] : $defaultColumnWidth;
$headerOptions['position']['height'] = $options['position']['header_style']['height'];
// Save the parameters for rendering the column headers for use by the Header() function.
$this->tableHeader[] = array(
$xh,
$yh,
$column->options['label'],
$headerOptions,
&$view,
$id,
);
$xh += $headerOptions['position']['width'];
}
}
}
// Save default paddings for use in the header.
$this->cellPaddings = $this->cell_padding;
// Add the first page, this will print the header.
$this
->addPage();
$rowX = $x;
$view->row_index = 0;
$break = FALSE;
foreach ($rows as $row) {
$x = $rowX;
// If last row forced a new page, or this row would overflow page, add a page.
if ($break || $this->y + $this->bMargin + $options['position']['body_style']['height'] > $pageDim['hk']) {
$break = FALSE;
$this
->addPage();
}
$y = (double) $this->y;
foreach ($columns as $id => $column) {
// Always render the field in order to generate tokens for other fields.
$content = $view->field[$id]
->theme($row);
if (empty($column->options['exclude'])) {
if ($id == 'page_break') {
$break = !empty($content);
}
else {
$bodyOptions = $options['info'][$id]['body_style'];
$bodyOptions['position']['width'] = !empty($options['info'][$id]['position']['width']) ? $options['info'][$id]['position']['width'] : $defaultColumnWidth;
$bodyOptions['position']['height'] = $options['position']['body_style']['height'];
$this
->renderItem($x, $y, $content, $row, $bodyOptions, $view, $id, FALSE, TRUE);
// Set x to start position of next cell
$x += (double) $bodyOptions['position']['width'];
}
}
}
// Set y position to start of next row.
$this
->SetY($y + (double) $options['position']['body_style']['height']);
$view->row_index++;
}
// Empty the table header data so as not to print on a trailing template.
$this->tableHeader = array();
}
/**
* This method adds a existing PDF document to the current document. If
* the file does not exists this method will return 0. In all other cases
* it will returns the number of the added pages.
*
* @param $path string Path to the file
* @param $position 'leading' or 'succeed' according to position of document
* @return integer Number of added pages
*/
public function addPdfDocument($path = '', $position = '') {
$this->position = $position;
if (empty($path) || !file_exists($path)) {
return 0;
}
$numberOfPages = $this
->setSourceFile($path);
for ($i = 1; $i <= $numberOfPages; $i++) {
$dim = $this
->getTemplateSize($i);
$format[0] = $dim['w'];
$format[1] = $dim['h'];
if ($dim['w'] > $dim['h']) {
$orientation = 'L';
}
else {
$orientation = 'P';
}
$this
->setPageFormat($format, $orientation);
parent::addPage();
// Ensure that all new content is printed to a new page
$this->y = 0;
$page = $this
->importPage($i);
$this
->useTemplate($page, 0, 0);
$this->addNewPageBeforeNextContent = TRUE;
}
return $numberOfPages;
}
/**
* This method resets the page number. This is useful if you want to start
* the numbering by zero.
*/
public function resetRowPageNumber() {
$this->rowContentPageNumber = 0;
}
/**
* This method adds a new page to the PDF.
*/
public function addPage($orientation = '', $format = '', $keepmargins = false, $tocpage = false, $path = NULL, $reset = FALSE, $numbering = 'main') {
// Do not add any new page, if we are writing
// in the footer or header.
if ($this->InFooter) {
return;
}
$this->mainContentPageNumber++;
$this->rowContentPageNumber++;
// Prevent a reset without any template
if ($reset == TRUE && (empty($path) || !file_exists($path))) {
parent::addPage();
$this
->setPageFormat($this->defaultFormat, $this->defaultOrientation);
return;
}
$files = $this->defaultPageTemplateFiles;
// Reset with new template
if ($reset) {
$files = array();
}
if ($path != NULL) {
$files[] = array(
'path' => $path,
'numbering' => $numbering,
);
}
$format = FALSE;
foreach ($files as $file) {
if (!empty($file['path']) && file_exists($file['path'])) {
$path = realpath($file['path']);
$numberOfPages = $this
->setSourceFile($path);
if ($file['numbering'] == 'row') {
$index = min($this->rowContentPageNumber, $numberOfPages);
}
else {
$index = min($this->mainContentPageNumber, $numberOfPages);
}
$page = $this
->importPage($index);
// ajust the page format (only for the first template)
if ($format == FALSE) {
$dim = $this
->getTemplateSize($index);
$format[0] = $dim['w'];
$format[1] = $dim['h'];
//$this->setPageFormat($format);
if ($dim['w'] > $dim['h']) {
$orientation = 'L';
}
else {
$orientation = 'P';
}
parent::addPage();
$this
->setPageFormat($format, $orientation);
}
// Apply the template
$this
->useTemplate($page, 0, 0);
}
}
// if all paths were empty, ensure that at least the page is added
if ($format == FALSE) {
parent::addPage();
$this
->setPageFormat($this->defaultFormat, $this->defaultOrientation);
}
}
/**
* Sets the current header and footer of the page.
*/
public function setHeaderFooter($record, $options, $view) {
$this->headerFooterData[$this
->getPage()] = $record;
$this->headerFooterOptions = $options;
$this->view = $view;
}
/**
* Close the document. This is called automatically by
* TCPDF::Output().
*/
public function Close() {
// Print the Header & Footer
for ($page = 1; $page <= $this
->getNumPages(); $page++) {
$this
->setPage($page);
if (isset($this->headerFooterData[$page])) {
if (is_array($this->headerFooterOptions['formats'])) {
$record = $this->headerFooterData[$page];
foreach ($this->headerFooterOptions['formats'] as $id => $options) {
if ($options['position']['object'] == 'header_footer') {
$fieldOptions = $options;
$fieldOptions['position']['object'] = 'page';
$this->InFooter = TRUE;
// backup margins
$ml = $this->lMargin;
$mr = $this->rMargin;
$mt = $this->tMargin;
$this
->SetMargins(0, 0, 0);
$this
->drawContent($record, $fieldOptions, $this->view, $id);
$this->InFooter = FALSE;
// restore margins
$this
->SetMargins($ml, $mt, $mr);
}
}
}
}
}
// call parent:
parent::Close();
}
/**
* This method returns a list of current uploaded files.
*/
public static function getAvailableTemplates() {
if (self::$templateList != NULL) {
return self::$templateList;
}
$files_path = drupal_realpath('public://');
$template_dir = variable_get('views_pdf_template_path', 'views_pdf_templates');
$dir = $files_path . '/' . $template_dir;
$templatesFiles = file_scan_directory($dir, '/.pdf$/', array(
'nomask' => '/(\\.\\.?|CVS)$/',
), 1);
$templates = array();
foreach ($templatesFiles as $file) {
$templates[$file->filename] = $file->name;
}
self::$templateList = $templates;
return $templates;
}
/**
* This method returns the path to a specific template.
*/
public static function getTemplatePath($template, $row = NULL, $view = NULL) {
if (empty($template)) {
return '';
}
if ($row != NULL && $view != NULL && !preg_match('/\\.pdf/', $template)) {
return drupal_realpath($row->field_data_field_file_node_values[0]['uri']);
}
$template_dir = variable_get('views_pdf_template_stream', 'public://views_pdf_templates');
return drupal_realpath($template_dir . '/' . $template);
}
/**
* This method returns a list of available fonts.
*/
public static function getAvailableFonts() {
if (self::$fontList != NULL) {
return self::$fontList;
}
// Get all pdf files with the font list: K_PATH_FONTS
$fonts = file_scan_directory(K_PATH_FONTS, '/.php$/', array(
'nomask' => '/(\\.\\.?|CVS)$/',
'recurse' => FALSE,
), 1);
$cache = cache_get('views_pdf_cached_fonts');
$cached_font_mapping = NULL;
if (is_object($cache)) {
$cached_font_mapping = $cache->data;
}
if (is_array($cached_font_mapping)) {
$font_mapping = array_merge(self::$defaultFontList, $cached_font_mapping);
}
else {
$font_mapping = self::$defaultFontList;
}
foreach ($fonts as $font) {
$name = self::getFontNameByFileName($font->uri);
if (isset($name)) {
$font_mapping[$font->name] = $name;
}
}
asort($font_mapping);
cache_set('views_pdf_cached_fonts', $font_mapping);
// Remove all fonts without name
foreach ($font_mapping as $key => $font) {
if (empty($font)) {
unset($font_mapping[$key]);
}
}
self::$fontList = $font_mapping;
return $font_mapping;
}
/**
* This method returns a cleaned up version of the font list.
*/
public static function getAvailableFontsCleanList() {
if (self::$fontListClean != NULL) {
return self::$fontListClean;
}
$clean = self::getAvailableFonts();
foreach ($clean as $key => $font) {
// Unset bold, italic, italic/bold fonts
unset($clean[$key . 'b']);
unset($clean[$key . 'bi']);
unset($clean[$key . 'i']);
}
self::$fontListClean = $clean;
return $clean;
}
/**
* This method returns a list of hyphenation patterns, that are
* available.
*/
public static function getAvailableHyphenatePatterns() {
if (self::$hyphenatePatterns != NULL) {
return self::$hyphenatePatterns;
}
self::$hyphenatePatterns = array();
$files = file_scan_directory(views_pdf_get_library('tcpdf') . '/hyphenate_patterns', '/.tex$/', array(
'nomask' => '/(\\.\\.?|CVS)$/',
), 1);
foreach ($files as $file) {
self::$hyphenatePatterns[basename($file->uri)] = str_replace('hyph-', '', $file->name);
}
return self::$hyphenatePatterns;
}
/**
* This method returns the name of a given font.
*/
protected static function getFontNameByFileName($path) {
include $path;
if (isset($name)) {
return $name;
}
else {
return NULL;
}
}
}
Classes
Name | Description |
---|---|
PdfTemplate | The main class to generate the PDF. |