*:not(:last-child),
footer > *:not(:last-child) {
margin-right: 10px;
}
span {
line-height: 7px;
white-space: nowrap;
}
p {
margin: 0;
line-height: 7px;
word-break: break-word;
text-overflow: ellipsis;
overflow: hidden;
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
}
CSS;
/** @var string Document title */
protected $title;
/**
* Paper orientation
*
* Defaults to false.
*
* @var bool
*/
protected $landscape;
/**
* Print background graphics
*
* Defaults to false.
*
* @var bool
*/
protected $printBackground;
/**
* Scale of the webpage rendering
*
* Defaults to 1.
*
* @var float
*/
protected $scale;
/**
* Paper width in inches
*
* Defaults to 8.5 inches.
*
* @var float
*/
protected $paperWidth;
/**
* Paper height in inches
*
* Defaults to 11 inches.
*
* @var float
*/
protected $paperHeight;
/**
* Top margin in inches
*
* Defaults to 1cm (~0.4 inches).
*
* @var float
*/
protected $marginTop;
/**
* Bottom margin in inches
*
* Defaults to 1cm (~0.4 inches).
*
* @var float
*/
protected $marginBottom;
/**
* Left margin in inches
*
* Defaults to 1cm (~0.4 inches).
*
* @var float
*/
protected $marginLeft;
/**
* Right margin in inches
*
* Defaults to 1cm (~0.4 inches).
*
* @var float
*/
protected $marginRight;
/**
* Paper ranges to print, e.g., '1-5, 8, 11-13'
*
* Defaults to the empty string, which means print all pages
*
* @var string
*/
protected $pageRanges;
/**
* Page height in pixels
*
* Minus the default vertical margins, this is 1035.
* If the vertical margins are zero, it's 1160.
* Whether there's a header or footer doesn't matter in any case.
*
* @var int
*/
protected $pagePixelHeight = 1035;
/**
* HTML template for the print header
*
* Should be valid HTML markup with following classes used to inject printing values into them:
* * date: formatted print date
* * title: document title
* * url: document location
* * pageNumber: current page number
* * totalPages: total pages in the document
*
* For example, `` would generate span containing the title.
*
* Note that the header cannot exceed a height of 21px regardless of the margin's height or document's scale.
* With the default style, this height is separated by three lines, each accommodating 7px.
* Use `span`'s for single line text and `p`'s for multiline text.
*
* @var ValidHtml
*/
protected $headerTemplate;
/**
* HTML template for the print footer
*
* Should be valid HTML markup with following classes used to inject printing values into them:
* * date: formatted print date
* * title: document title
* * url: document location
* * pageNumber: current page number
* * totalPages: total pages in the document
*
* For example, `` would generate span containing the title.
*
* Note that the footer cannot exceed a height of 21px regardless of the margin's height or document's scale.
* With the default style, this height is separated by three lines, each accommodating 7px.
* Use `span`'s for single line text and `p`'s for multiline text.
*
* @var ValidHtml
*/
protected $footerTemplate;
/**
* HTML for the cover page
*
* @var ValidHtml
*/
protected $coverPage;
/**
* Whether or not to prefer page size as defined by css
*
* Defaults to false, in which case the content will be scaled to fit the paper size.
*
* @var bool
*/
protected $preferCSSPageSize;
protected $tag = 'body';
/**
* Get the document title
*
* @return string
*/
public function getTitle()
{
return $this->title;
}
/**
* Set the document title
*
* @param string $title
*
* @return $this
*/
public function setTitle($title)
{
$this->title = $title;
return $this;
}
/**
* Set page header
*
* @param ValidHtml $header
*
* @return $this
*/
public function setHeader(ValidHtml $header)
{
$this->headerTemplate = $header;
return $this;
}
/**
* Set page footer
*
* @param ValidHtml $footer
*
* @return $this
*/
public function setFooter(ValidHtml $footer)
{
$this->footerTemplate = $footer;
return $this;
}
/**
* Get the cover page
*
* @return ValidHtml|null
*/
public function getCoverPage()
{
return $this->coverPage;
}
/**
* Set cover page
*
* @param ValidHtml $coverPage
*
* @return $this
*/
public function setCoverPage(ValidHtml $coverPage)
{
$this->coverPage = $coverPage;
return $this;
}
/**
* Remove page margins
*
* @return $this
*/
public function removeMargins()
{
$this->marginBottom = 0;
$this->marginLeft = 0;
$this->marginRight = 0;
$this->marginTop = 0;
return $this;
}
/**
* Finalize document to be printed
*/
protected function assemble()
{
$this->setWrapper(new HtmlElement(
'html',
null,
new HtmlElement(
'head',
null,
new HtmlElement(
'title',
null,
Text::create($this->title)
),
$this->createStylesheet(),
$this->createLayoutScript()
)
));
$this->getAttributes()->registerAttributeCallback('data-content-height', function () {
return $this->pagePixelHeight;
});
$this->getAttributes()->registerAttributeCallback('style', function () {
return sprintf('width: %sin;', $this->paperWidth ?: 8.5);
});
}
/**
* Get the parameters for Page.printToPDF
*
* @return array
*/
public function getPrintParameters()
{
$parameters = [];
if (isset($this->landscape)) {
$parameters['landscape'] = $this->landscape;
}
if (isset($this->printBackground)) {
$parameters['printBackground'] = $this->printBackground;
}
if (isset($this->scale)) {
$parameters['scale'] = $this->scale;
}
if (isset($this->paperWidth)) {
$parameters['paperWidth'] = $this->paperWidth;
}
if (isset($this->paperHeight)) {
$parameters['paperHeight'] = $this->paperHeight;
}
if (isset($this->marginTop)) {
$parameters['marginTop'] = $this->marginTop;
}
if (isset($this->marginBottom)) {
$parameters['marginBottom'] = $this->marginBottom;
}
if (isset($this->marginLeft)) {
$parameters['marginLeft'] = $this->marginLeft;
}
if (isset($this->marginRight)) {
$parameters['marginRight'] = $this->marginRight;
}
if (isset($this->pageRanges)) {
$parameters['pageRanges'] = $this->pageRanges;
}
if (isset($this->headerTemplate)) {
$parameters['headerTemplate'] = $this->createHeader()->render();
$parameters['displayHeaderFooter'] = true;
if (! isset($parameters['marginTop'])) {
$parameters['marginTop'] = 0.6;
}
} else {
$parameters['headerTemplate'] = ' '; // An empty string is ignored
}
if (isset($this->footerTemplate)) {
$parameters['footerTemplate'] = $this->createFooter()->render();
$parameters['displayHeaderFooter'] = true;
if (! isset($parameters['marginBottom'])) {
$parameters['marginBottom'] = 0.6;
}
} else {
$parameters['footerTemplate'] = ' '; // An empty string is ignored
}
if (isset($this->preferCSSPageSize)) {
$parameters['preferCSSPageSize'] = $this->preferCSSPageSize;
}
return $parameters;
}
/**
* Create CSS stylesheet
*
* @return ValidHtml
*/
protected function createStylesheet(): ValidHtml
{
$app = Icinga::app();
$css = preg_replace_callback(
'~(?<=url\()[\'"]?([^(\'"]*)[\'"]?(?=\))~',
function ($matches) use ($app) {
if (substr($matches[1], 0, 3) !== '../') {
return $matches[1];
}
$path = substr($matches[1], 3);
if (substr($path, 0, 4) === 'lib/') {
$assetPath = substr($path, 4);
$library = null;
foreach ($app->getLibraries() as $candidate) {
if (substr($assetPath, 0, strlen($candidate->getName())) === $candidate->getName()) {
$library = $candidate;
$assetPath = ltrim(substr($assetPath, strlen($candidate->getName())), '/');
break;
}
}
if ($library === null) {
return $matches[1];
}
$path = $library->getStaticAssetPath() . DIRECTORY_SEPARATOR . $assetPath;
} elseif (substr($matches[1], 0, 14) === '../static/img?') {
list($_, $query) = explode('?', $matches[1], 2);
$params = UrlParams::fromQueryString($query);
if (! $app->getModuleManager()->hasEnabled($params->get('module_name'))) {
return $matches[1];
}
$module = $app->getModuleManager()->getModule($params->get('module_name'));
$imgRoot = $module->getBaseDir() . '/public/img/';
$path = realpath($imgRoot . $params->get('file'));
} else {
$path = $app->getBootstrapDirectory() . '/' . $path;
}
if (! $path || ! file_exists($path) || ! is_file($path)) {
return $matches[1];
}
$mimeType = @mime_content_type($path);
if ($mimeType === false) {
return $matches[1];
}
$fileContent = @file_get_contents($path);
if ($fileContent === false) {
return $matches[1];
}
return "'data:$mimeType; base64, " . base64_encode($fileContent) . "'";
},
(new PrintStyleSheet())->render(true)
);
return new HtmlElement('style', null, HtmlString::create($css));
}
/**
* Create layout javascript
*
* @return ValidHtml
*/
protected function createLayoutScript(): ValidHtml
{
$module = Icinga::app()->getModuleManager()->getModule('pdfexport');
if (! method_exists($module, 'getJsDir')) {
$jsPath = join(DIRECTORY_SEPARATOR, [$module->getBaseDir(), 'public', 'js']);
} else {
$jsPath = $module->getJsDir();
}
$layoutJS = file_get_contents($jsPath . '/layout.js') . "\n\n\n";
$layoutJS .= file_get_contents($jsPath . '/layout-plugins/page-breaker.js') . "\n\n\n";
return new HtmlElement(
'script',
Attributes::create(['type' => 'application/javascript']),
HtmlString::create($layoutJS)
);
}
/**
* Create document header
*
* @return ValidHtml
*/
protected function createHeader(): ValidHtml
{
return (new HtmlDocument())
->addHtml(
new HtmlElement('style', null, HtmlString::create(
static::DEFAULT_HEADER_FOOTER_STYLE
)),
new HtmlElement('header', null, $this->headerTemplate)
);
}
/**
* Create document footer
*
* @return ValidHtml
*/
protected function createFooter(): ValidHtml
{
return (new HtmlDocument())
->addHtml(
new HtmlElement('style', null, HtmlString::create(
static::DEFAULT_HEADER_FOOTER_STYLE
)),
new HtmlElement('footer', null, $this->footerTemplate)
);
}
}