diff --git a/library/Icinga/File/Pdf.php b/library/Icinga/File/Pdf.php index 771ec9dee..d95d0df3e 100644 --- a/library/Icinga/File/Pdf.php +++ b/library/Icinga/File/Pdf.php @@ -3,6 +3,8 @@ namespace Icinga\File; use \DOMPDF; +use \DOMDocument; +use \DOMXPath; require_once 'vendor/dompdf/dompdf_config.inc.php'; @@ -10,6 +12,20 @@ spl_autoload_register("DOMPDF_autoload"); class Pdf extends DOMPDF { + /** + * The amount of table rows that fit on one page before a page-break is inserted. + * + * @var int + */ + public $rowsPerPage = 10; + + /** + * If occuring tables should be split up into smaller tables to avoid errors in the document layout. + * + * @var bool + */ + public $paginateTable = true; + public function __construct() { $this->set_paper(DOMPDF_DEFAULT_PAPER_SIZE, "portrait"); parent::__construct(); @@ -28,10 +44,86 @@ class Pdf extends DOMPDF . $body . '' . ''; + if ($this->paginateTable === true) { + $html = $this->paginateHtmlTables($html); + } $this->load_html($html); $this->render(); } + /** + * Split up tables into multiple elements that each contain $rowsPerPage of all original rows + * + * NOTE: This is a workaround to fix the buggy page-break on table-rows in dompdf. + * + * @param string A html-string. + * + * @return string The html string with the paginated rows. + */ + private function paginateHtmlTables($html) + { + $doc = new DOMDocument(); + @$doc->loadHTML($html); + $xpath = new DOMXPath($doc); + + $tables = $xpath->query('.//table'); + foreach ($tables as $table) { + $containerType = null; + $rows = $xpath->query('.//tr', $table); + $rowCnt = $rows->length; + $tableCnt = (Integer)ceil($rowCnt / $this->rowsPerPage); + if ($rowCnt <= $this->rowsPerPage) { + continue; + } + // remove all rows from the original parent + foreach ($rows as $row) { + if (!isset($containerType)) { + $containerType = $row->parentNode->nodeName; + } + $row->parentNode->removeChild($row); + } + + // clone table for each additional page and fetch the row containers + $containers = array(); + $pages = array(); + + // insert page-break + $div = $doc->createElement('div'); + $div->setAttribute('style', 'page-break-before: always;'); + $table->parentNode->insertBefore($div, $table); + + for ($i = 0; $i < $tableCnt; $i++) { + // clone table + $currentPage = $table->cloneNode(true); + $pages[$i] = $currentPage; + $table->parentNode->insertBefore($currentPage, $table); + + // insert page-break + if ($i < $tableCnt - 1) { + $div = $doc->createElement('div'); + $div->setAttribute('style', 'page-break-before: always;'); + $table->parentNode->insertBefore($div, $table); + } + + // fetch row container + $container = $xpath->query('.//' . $containerType, $currentPage)->item(0); + $containers[$i] = $container; + } + + $i = 0; + foreach ($rows as $row) { + $p = (Integer)floor($i / $this->rowsPerPage); + $containers[$p]->appendChild($row); + //echo "Inserting row $i into container $p
"; + $i++; + } + + // remove original table + $table->parentNode->removeChild($table); + } + return $doc->saveHTML(); + } + /** * Prepare the given css for rendering with DOMPDF, by removing or hiding all incompatible * styles @@ -45,14 +137,20 @@ class Pdf extends DOMPDF $css = preg_replace('/\*:\s*before\s*,\s*/', '', $css); $css = preg_replace('/\*\s*:\s*after\s*\{[^\}]*\}/', '', $css); + // TODO: Move into own .css file that is loaded when requesting a pdf - return $css . "\n" + return $css . "\n" + . '*, html { font-size: 100%; } ' . "\n" + . 'form { display: none; }' . "\n" + // Insert page breaks + . 'div.pdf-page { page-break-before: always; } ' . "\n" + // Don't show any link outline . 'a { outline: 0; }' . "\n" - // Fix badge positioning TODO: Badge should be at the right border + // Fix badge positioning . 'span.badge { float: right; max-width: 5px; }' // prevent table rows from growing too big on page breaks @@ -60,6 +158,7 @@ class Pdf extends DOMPDF // Hide buttons . '*.button { display: none; }' . "\n" + . '*.btn-group { display: none; }' . "\n" . 'button > i { display: none; }' . "\n" // Hide navigation diff --git a/library/Icinga/Web/Controller/ActionController.php b/library/Icinga/Web/Controller/ActionController.php index 49814f7f3..682bd25cd 100755 --- a/library/Icinga/Web/Controller/ActionController.php +++ b/library/Icinga/Web/Controller/ActionController.php @@ -241,21 +241,40 @@ class ActionController extends Zend_Controller_Action } } if ($this->_request->getParam('format') === 'pdf' && $this->_request->getControllerName() !== 'static') { - $html = $this->view->render( - $this->_request->getControllerName() . '/' . $this->_request->getActionName() . '.phtml' - ); - $this->sendAsPdf($html); + $this->sendAsPdf(); die(); } } - protected function sendAsPdf($body) + protected function sendAsPdf() { + $body = $this->view->render( + $this->_request->getControllerName() . '/' . $this->_request->getActionName() . '.phtml' + ); if (!headers_sent()) { $css = $this->view->getHelper('action')->action('stylesheet', 'static', 'application'); $pdf = new PDF(); + + if ($this->_request->getControllerName() === 'list') { + switch ($this->_request->getActionName()) { + case 'notifications': + $pdf->rowsPerPage = 8; + break; + case 'comments': + $pdf->rowsPerPage = 8; + break; + default: + $pdf->rowsPerPage = 12; + break; + } + } else { + $pdf->paginateTable = false; + } + $pdf->renderPage($body, $css); - $pdf->stream($this->_request->getControllerName() . '-' . $this->_request->getActionName() . '.pdf'); + $pdf->stream( + $this->_request->getControllerName() . '-' . $this->_request->getActionName() . '-' . time() . '.pdf' + ); } else { Logger::error('Could not send pdf-response, content already written to output.'); die(); diff --git a/library/vendor/dompdf/dompdf_config.inc.php b/library/vendor/dompdf/dompdf_config.inc.php index 540901a4a..0333f5fd0 100644 --- a/library/vendor/dompdf/dompdf_config.inc.php +++ b/library/vendor/dompdf/dompdf_config.inc.php @@ -4,7 +4,7 @@ * @link http://www.dompdf.com/ * @author Benj Carson * @author Helmut Tischer - * @author Fabien Ménager + * @author Fabien M�nager * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License * @version $Id: dompdf_config.inc.php 468 2012-02-05 10:51:40Z fabien.menager $ */ @@ -199,7 +199,7 @@ def("DOMPDF_DEFAULT_MEDIA_TYPE", "screen"); * * @see CPDF_Adapter::PAPER_SIZES for valid sizes */ -def("DOMPDF_DEFAULT_PAPER_SIZE", "letter"); +def("DOMPDF_DEFAULT_PAPER_SIZE", "a4"); /** * The default font family @@ -285,7 +285,7 @@ def("DOMPDF_ENABLE_JAVASCRIPT", true); * * @var bool */ -def("DOMPDF_ENABLE_REMOTE", false); +def("DOMPDF_ENABLE_REMOTE", true); /** * The debug output log @@ -304,7 +304,7 @@ def("DOMPDF_FONT_HEIGHT_RATIO", 1.1); * Allows people to disabled CSS float support * @var bool */ -def("DOMPDF_ENABLE_CSS_FLOAT", false); +def("DOMPDF_ENABLE_CSS_FLOAT", true); /** * Prepend the DOMPDF autoload function the spl_autoload stack @@ -378,7 +378,7 @@ def('DEBUGCSS', false); * Visible in the PDF itself. */ def('DEBUG_LAYOUT', false); -def('DEBUG_LAYOUT_LINES', true); -def('DEBUG_LAYOUT_BLOCKS', true); -def('DEBUG_LAYOUT_INLINE', true); -def('DEBUG_LAYOUT_PADDINGBOX', true); +def('DEBUG_LAYOUT_LINES', false); +def('DEBUG_LAYOUT_BLOCKS', false); +def('DEBUG_LAYOUT_INLINE', false); +def('DEBUG_LAYOUT_PADDINGBOX', false);