/** * Render a media object. * * @param ezcDocumentPdfPage $page * @param ezcDocumentPdfHyphenator $hyphenator * @param ezcDocumentPdfTokenizer $tokenizer * @param ezcDocumentLocateableDomElement $media * @param ezcDocumentPdfMainRenderer $mainRenderer * @return bool */ public function renderNode(ezcDocumentPdfPage $page, ezcDocumentPdfHyphenator $hyphenator, ezcDocumentPdfTokenizer $tokenizer, ezcDocumentLocateableDomElement $media, ezcDocumentPdfMainRenderer $mainRenderer) { // Inference page styles $styles = $this->styles->inferenceFormattingRules($media); // Locate image file $imageData = $media->getElementsByTagName('imagedata')->item(0); $imageFile = $mainRenderer->locateFile((string) $imageData->getAttribute('fileref')); $image = ezcDocumentPdfImage::createFromFile($imageFile); // Estimate size of image box $width = $this->getMediaBoxWidth($styles, $page, $image); // Get available height with the estimated width $dimensions = $this->scaleImage($styles, $image, $page, $width); $switchBack = false; if (($space = $page->testFitRectangle($page->x, $page->y, $dimensions[0], $dimensions[1])) === false) { // Image with estimated dimensions does not fit on current page any // more. $page = $mainRenderer->getNextRenderingPosition(($pWidth = $width->get()) + $styles['text-column-spacing']->value, $pWidth); $switchBack = true; } // Get maximum available space $space = $page->testFitRectangle($page->x, $page->y, $width->get(), null); // Apply margin of mediaobject $space->x += $styles['margin']->value['left']; $space->y += $styles['margin']->value['top']; $space->width -= $styles['margin']->value['left'] + $styles['margin']->value['right']; $space->height -= $styles['margin']->value['top'] + $styles['margin']->value['bottom']; // Estimate required height of text blocks $captions = $media->getElementsByTagName('caption'); $captionHeight = 0; $textRenderer = new ezcDocumentPdfTextBlockRenderer($this->driver, $this->styles); foreach ($captions as $caption) { $captionHeight += $textRenderer->estimateHeight($space->width, $hyphenator, $tokenizer, $caption); } if (($imageHeight = $space->height - $captionHeight) < 0) { return false; } // Reduce the image size, of it does not fit any more because of the captions if ($imageHeight < $dimensions[1]) { $dimensions[0] *= $imageHeight / $dimensions[1]; $dimensions[1] = $imageHeight; } // Render image $this->driver->drawImage($imageFile, $image->getMimeType(), $space->x + ($space->width - $dimensions[0]) / 2, $space->y, $dimensions[0], $dimensions[1]); $space->y += $dimensions[1]; // Render captions foreach ($captions as $caption) { $space->y += $textRenderer->renderBlock($space, $hyphenator, $tokenizer, $caption); } // Set covered space covered $page->setCovered(new ezcDocumentPdfBoundingBox($page->x, $page->y, $space->width, $space->y - $page->y)); $page->y = $space->y; // Go back to previous page, if requested if ($switchBack) { $this->driver->goBackOnePage(); } return true; }
/** * Process to render block contents * * @param ezcDocumentPdfPage $page * @param ezcDocumentPdfHyphenator $hyphenator * @param ezcDocumentPdfTokenizer $tokenizer * @param ezcDocumentLocateableDomElement $block * @param ezcDocumentPdfMainRenderer $mainRenderer * @return void */ protected function process(ezcDocumentPdfPage $page, ezcDocumentPdfHyphenator $hyphenator, ezcDocumentPdfTokenizer $tokenizer, ezcDocumentLocateableDomElement $block, ezcDocumentPdfMainRenderer $mainRenderer) { // Render list item if (($listItem = $this->generator->getListItem($this->item)) !== '') { $styles = $this->styles->inferenceFormattingRules($block); $this->driver->drawWord($page->x + $page->xOffset - $styles['padding']->value['left'], $page->y + $styles['font-size']->value, $listItem); } // Render list contents $mainRenderer->process($block); }
/** * Test rendering of a full document * * Test the rendering of a given full document with an * additional set of user configured styles. * * @param string $file * @param string $fileName * @param array $styles * @return void */ protected function renderFullDocument($file, $fileName, array $styles = array()) { $docbook = new ezcDocumentDocbook(); $docbook->loadFile($file); $style = new ezcDocumentPcssStyleInferencer(); $style->appendStyleDirectives(array(new ezcDocumentPcssLayoutDirective(array('article'), array('font-family' => 'serif', 'line-height' => '1')), new ezcDocumentPcssLayoutDirective(array('title'), array('font-family' => 'sans-serif')))); $style->appendStyleDirectives($styles); $renderer = new ezcDocumentPdfMainRenderer(new ezcDocumentPdfSvgDriver(), $style, new ezcDocumentPdfOptions()); $pdf = $renderer->render($docbook, new ezcDocumentPdfDefaultHyphenator()); file_put_contents($this->tempDir . $fileName, $pdf); $this->assertXmlFileEqualsXmlFile($this->basePath . 'renderer/' . $fileName, $this->tempDir . $fileName); }
public function testRenderBlockquote() { // Additional formatting $mock = $this->getMock('ezcTestDocumentPdfMockDriver', array('createPage', 'drawWord')); // Expectations $mock->expects($this->at(0))->method('createPage')->with($this->equalTo(100, 1.0), $this->equalTo(100, 1.0)); $mock->expects($this->at(1))->method('drawWord')->with($this->equalTo(20, 1.0), $this->equalTo(21, 1.0), $this->equalTo("Some")); $mock->expects($this->at(2))->method('drawWord')->with($this->equalTo(35, 1.0), $this->equalTo(21, 1.0), $this->equalTo("random")); $mock->expects($this->at(4))->method('drawWord')->with($this->equalTo(20, 1.0), $this->equalTo(29.4, 1.0), $this->equalTo("which")); $mock->expects($this->at(13))->method('drawWord')->with($this->equalTo(25, 1.0), $this->equalTo(46.8, 1.0), $this->equalTo("Name")); $docbook = new ezcDocumentDocbook(); $docbook->loadFile(dirname(__FILE__) . '/../files/pdf/blockquote.xml'); $renderer = new ezcDocumentPdfMainRenderer($mock, $this->styles); $pdf = $renderer->render($docbook, new ezcDocumentPdfDefaultHyphenator()); }
public function testRenderDefinitionListWrapped() { $mock = $this->getMock('ezcTestDocumentPdfMockDriver', array('createPage', 'drawWord')); // Expectations $mock->expects($this->at(0))->method('createPage')->with($this->equalTo(100, 1.0), $this->equalTo(100, 1.0)); $mock->expects($this->at(1))->method('drawWord')->with($this->equalTo(10, 1.0), $this->equalTo(18, 1.0), $this->equalTo("A")); $mock->expects($this->at(4))->method('drawWord')->with($this->equalTo(10, 1.0), $this->equalTo(29.2, 1.0), $this->equalTo("is")); $mock->expects($this->at(10))->method('drawWord')->with($this->equalTo(15, 1.0), $this->equalTo(62.8, 1.0), $this->equalTo("The")); $mock->expects($this->at(20))->method('createPage')->with($this->equalTo(100, 1.0), $this->equalTo(100, 1.0)); $mock->expects($this->at(21))->method('drawWord')->with($this->equalTo(15, 1.0), $this->equalTo(18, 1.0), $this->equalTo("to")); $docbook = new ezcDocumentDocbook(); $docbook->loadFile(dirname(__FILE__) . '/../files/pdf/variablelist_long.xml'); $renderer = new ezcDocumentPdfMainRenderer($mock, $this->styles); $pdf = $renderer->render($docbook, new ezcDocumentPdfDefaultHyphenator()); }
public function testRenderLiterallayoutWrapped() { // Additional formatting $mock = $this->getMock('ezcTestDocumentPdfMockDriver', array('createPage', 'drawWord', 'drawPolygon', 'drawPolyline')); // Expectations $mock->expects($this->at(0))->method('createPage')->with($this->equalTo(100, 1.0), $this->equalTo(100, 1.0)); $mock->expects($this->at(5))->method('drawWord')->with($this->equalTo(12, 1.0), $this->equalTo(18, 1.0), $this->equalTo("1")); $mock->expects($this->at(13))->method('drawWord')->with($this->equalTo(12, 1.0), $this->equalTo(85.2, 1.0), $this->equalTo("9")); $mock->expects($this->at(14))->method('createPage')->with($this->equalTo(100, 1.0), $this->equalTo(100, 1.0)); $mock->expects($this->at(27))->method('createPage')->with($this->equalTo(100, 1.0), $this->equalTo(100, 1.0)); $mock->expects($this->at(32))->method('drawWord')->with($this->equalTo(12, 1.0), $this->equalTo(18, 1.0), $this->equalTo("9")); $mock->expects($this->at(33))->method('drawWord')->with($this->equalTo(12, 1.0), $this->equalTo(26.4, 1.0), $this->equalTo("0")); $docbook = new ezcDocumentDocbook(); $docbook->loadFile(dirname(__FILE__) . '/../files/pdf/literallayout_long.xml'); $renderer = new ezcDocumentPdfMainRenderer($mock, $this->styles); $pdf = $renderer->render($docbook, new ezcDocumentPdfDefaultHyphenator()); }
/** * Process to render block contents. * * @param ezcDocumentPdfPage $page * @param ezcDocumentPdfHyphenator $hyphenator * @param ezcDocumentPdfTokenizer $tokenizer * @param ezcDocumentLocateableDomElement $block * @param ezcDocumentPdfMainRenderer $mainRenderer * @return void */ protected function process(ezcDocumentPdfPage $page, ezcDocumentPdfHyphenator $hyphenator, ezcDocumentPdfTokenizer $tokenizer, ezcDocumentLocateableDomElement $block, ezcDocumentPdfMainRenderer $mainRenderer) { $childNodes = $block->childNodes; $nodeCount = $childNodes->length; $attributions = array(); for ($i = 0; $i < $nodeCount; ++$i) { $child = $childNodes->item($i); if ($child->nodeType !== XML_ELEMENT_NODE) { continue; } // Default to docbook namespace, if no namespace is defined $namespace = $child->namespaceURI === null ? 'http://docbook.org/ns/docbook' : $child->namespaceURI; if ($namespace === 'http://docbook.org/ns/docbook' && $child->tagName === 'attribution') { $attributions[] = $child; continue; } $mainRenderer->processNode($child); } // Render attributions below the actual quotes $textRenderer = new ezcDocumentPdfTextBoxRenderer($this->driver, $this->styles); foreach ($attributions as $attribution) { $textRenderer->renderNode($page, $hyphenator, $tokenizer, $attribution, $mainRenderer); } }
/** * Process to render block contents. * * @param ezcDocumentPdfPage $page * @param ezcDocumentPdfHyphenator $hyphenator * @param ezcDocumentPdfTokenizer $tokenizer * @param ezcDocumentLocateableDomElement $block * @param ezcDocumentPdfMainRenderer $mainRenderer */ protected function process(ezcDocumentPdfPage $page, ezcDocumentPdfHyphenator $hyphenator, ezcDocumentPdfTokenizer $tokenizer, ezcDocumentLocateableDomElement $block, ezcDocumentPdfMainRenderer $mainRenderer) { $mainRenderer->process($block); }
public function testRenderParagraphSplittingWithBorderLastPage() { // Additional formatting $this->styles->appendStyleDirectives(array(new ezcDocumentPcssLayoutDirective(array('para'), array('line-height' => '1', 'padding' => '5', 'margin' => '5', 'border' => '1mm solid #A00000', 'background-color' => '#eedbdb')))); $mock = $this->getMock('ezcTestDocumentPdfMockDriver', array('drawPolyline', 'drawPolygon', 'createPage', 'drawWord')); // Expectations for third page $mock->expects($this->at(38))->method('createPage')->with($this->equalTo(100, 1.0), $this->equalTo(100, 1.0)); $mock->expects($this->at(39))->method('drawPolygon')->with($this->equalTo(array(array(15, 15), array(85, 15), array(85, 51), array(15, 51)), 1.0), $this->equalTo(array('red' => 0.93, 'green' => 0.86, 'blue' => 0.86, 'alpha' => 0), 0.01)); $mock->expects($this->at(40))->method('drawPolyline')->with($this->equalTo(array(array(15.5, 15.5), array(15.5, 50.5)), 0.1), $this->equalTo(array('red' => 0.63, 'green' => 0.0, 'blue' => 0.0, 'alpha' => 0), 0.01)); $mock->expects($this->at(41))->method('drawPolyline')->with($this->equalTo(array(array(84.5, 15.5), array(84.5, 50.5)), 0.1), $this->equalTo(array('red' => 0.63, 'green' => 0.0, 'blue' => 0.0, 'alpha' => 0), 0.01)); $mock->expects($this->at(42))->method('drawPolyline')->with($this->equalTo(array(array(84.5, 50.5), array(15.5, 50.5)), 0.1), $this->equalTo(array('red' => 0.63, 'green' => 0.0, 'blue' => 0.0, 'alpha' => 0), 0.01)); $mock->expects($this->at(43))->method('drawWord')->with($this->equalTo(21, 1.0), $this->equalTo(29, 1.0), $this->equalTo('be')); $docbook = new ezcDocumentDocbook(); $docbook->loadFile(dirname(__FILE__) . '/../files/pdf/wrapping.xml'); $renderer = new ezcDocumentPdfMainRenderer($mock, $this->styles); $pdf = $renderer->render($docbook, new ezcDocumentPdfDefaultHyphenator()); }
/** * Render a single text box * * All markup inside of the given string is considered inline markup (in * CSS terms). Inline markup should be given as common docbook inline * markup, like <emphasis>. * * Returns a boolean indicator whether the rendering of the full text * in the available space succeeded or not. * * @param ezcDocumentPdfPage $page * @param ezcDocumentPdfHyphenator $hyphenator * @param ezcDocumentPdfTokenizer $tokenizer * @param ezcDocumentLocateableDomElement $text * @param ezcDocumentPdfMainRenderer $mainRenderer * @return bool * * @todo This method does not respect changes in the available text width, * if a paragraph is wrapped to the next page. This would require token * reordering, which is not implemented yet. */ public function renderNode(ezcDocumentPdfPage $page, ezcDocumentPdfHyphenator $hyphenator, ezcDocumentPdfTokenizer $tokenizer, ezcDocumentLocateableDomElement $text, ezcDocumentPdfMainRenderer $mainRenderer) { // Inference page styles $styles = $this->styles->inferenceFormattingRules($text); $width = $mainRenderer->calculateTextWidth($page, $text); // Evaluate available space if (($space = $this->evaluateAvailableBoundingBox($page, $styles, $width)) === false) { return false; } // Iterate over tokens and try to fit them in the current line, use // hyphenator to split words. $tokens = $this->tokenize($text, $tokenizer); $lines = $this->fitTokensInLines($tokens, $hyphenator, $space->width); // Transaction wrapping around temporary page creations $transaction = $this->driver->startTransaction(); $lineCount = count($lines); $current = 0; $position = $space->y; $pageNr = 0; $wrap = false; $pages = array($pageNr => array('page' => $page, 'lines' => array(), 'space' => $space)); for ($line = 0; $line < $lineCount; ++$line) { // Render on current page, of there is still enough space if (!$wrap && $position + $lines[$line]['height'] < $pages[$pageNr]['space']->y + $pages[$pageNr]['space']->height) { ++$current; // Check widows, if we are at the last line if ($line === $lineCount - 1 && $current < $styles['widows']->value && $lineCount >= $styles['widows']->value) { $difference = $styles['widows']->value - $current; $pages[$pageNr - 1]['lines'] = array_slice($pages[$pageNr - 1]['lines'], 0, -$difference, true); $pages[$pageNr]['lines'] = array(); $line = $lineCount - $styles['widows']->value - 1; $current = 0; continue; } $pages[$pageNr]['lines'][] = array('position' => $position, 'line' => $lines[$line]); $position += $lines[$line]['height'] * $styles['line-height']->value; continue; } // Shift to next page $pages[++$pageNr] = array('page' => $tmpPage = $mainRenderer->getNextRenderingPosition(($pWidth = $mainRenderer->calculateTextWidth($page, $text)) + $styles['text-column-spacing']->value, $pWidth), 'lines' => array(), 'space' => $this->evaluateAvailableBoundingBox($tmpPage, $styles, $width)); $position = $pages[$pageNr]['space']->y; $current = 0; $wrap = false; // Handle orphans if ($line < $styles['orphans']->value && $line < $lineCount) { $pages[0]['lines'] = array(); $line = -1; continue; } --$line; } $this->driver->revert($transaction); // Render lines $lineNr = 0; foreach ($pages as $nr => $content) { if ($nr > 0) { // Get next rendering position $page = $mainRenderer->getNextRenderingPosition(($pWidth = $mainRenderer->calculateTextWidth($page, $text)) + $styles['text-column-spacing']->value, $pWidth); } if (!count($content['lines'])) { continue; } // Render background & border $space = $content['space']; $lastLine = end($content['lines']); $space->height = $lastLine['position'] + $lastLine['line']['height'] - $space->y; $this->renderBoxBackground($space, $styles); $this->renderBoxBorder($space, $styles, $nr === 0, $nr + 1 >= count($pages)); $this->setBoxCovered($page, $space, $styles); // Render actual text contents foreach ($content['lines'] as $line) { $this->renderLine($line['position'], $lineNr++, $line['line'], $space, $styles); } } return true; }
/** * Create document from docbook document * * A document of the docbook format is provided and the internal document * structure should be created out of this. * * This method is required for all formats to have one central format, so * that each format can be compiled into each other format using docbook as * an intermediate format. * * You may of course just call an existing converter for this conversion. * * @param ezcDocumentDocbook $document * @return void */ public function createFromDocbook(ezcDocumentDocbook $document) { if ($this->options->validate && $document->validateString($document) !== true) { $this->triggerError(E_WARNING, "You try to convert an invalid docbook document. This may lead to invalid output."); } $this->path = $document->getPath(); $this->options->driver->setOptions($this->options); $renderer = new ezcDocumentPdfMainRenderer($this->options->driver, $this->styles, $this->options); foreach ($this->pdfParts as $part) { $renderer->registerPdfPart($part); } $this->content = $renderer->render($document, $this->options->hyphenator, $this->options->tokenizer); // Merge errors from renderer $this->errors = array_merge($this->errors, $renderer->getErrors()); }
/** * Process to render the table into its boundings. * * @param ezcDocumentPdfPage $page * @param ezcDocumentPdfHyphenator $hyphenator * @param ezcDocumentPdfTokenizer $tokenizer * @param ezcDocumentLocateableDomElement $block * @param ezcDocumentPdfMainRenderer $mainRenderer */ protected function processTable(ezcDocumentPdfPage $page, ezcDocumentPdfHyphenator $hyphenator, ezcDocumentPdfTokenizer $tokenizer, ezcDocumentLocateableDomElement $block, ezcDocumentPdfMainRenderer $mainRenderer) { $tableColumnWidths = $this->options->tableColumnWidthCalculator->estimateWidths($block); $styles = $this->styles->inferenceFormattingRules($block); $renderWidth = $mainRenderer->calculateTextWidth($page, $block); $space = $this->evaluateAvailableBoundingBox($page, $styles, $renderWidth); $this->tableBox = array('box' => $box = clone $space, 'styles' => $styles); $this->renderTopBorder($styles, $box); $xpath = new DOMXPath($block->ownerDocument); $xPosition = $page->x; foreach ($xpath->query('./*/*/*[local-name() = "row"] | ./*/*[local-name() = "row"]', $block) as $rowNr => $row) { $xOffset = 0; $this->cellBoxes = array(); $positions = array(); $pageStartId = $page->orderedId; $lastPage = array(); foreach ($xpath->query('./*[local-name() = "entry"]', $row) as $nr => $cell) { list($pageId, $position) = $this->renderCell($page, $hyphenator, $tokenizer, $cell, $styles, $space, $xOffset, $tableColumnWidths[$nr]); $positions[$pageId][] = $position; $xOffset += $tableColumnWidths[$nr]; $lastPage[$pageId] = $this->lastPageForCell; // Go back to page, where each cell in the row shoudl start the // rendering $this->driver->selectPage($pageStartId); } // Close borders foreach ($this->additionalBorders as $page => $borders) { $this->driver->selectPage($page); foreach ($borders as $border) { call_user_func_array(array($this, 'renderBoxBorder'), $border); } } $this->additionalBorders = array(); $lastPageId = max(array_keys($positions)); $page = $lastPage[$lastPageId]; // Forward page to last page used during cell rendering $this->driver->selectPage($lastPageId); $page->x = $xPosition; foreach ($this->cellBoxes as $cell) { $cell['box']->height = max($positions[$lastPageId]) - $cell['box']->y; $this->renderBoxBorder($cell['box'], $cell['styles'], false); $this->setCellCovered($page, $cell['box'], $styles); } // Set page->y again, since setBoxCovered() increased it, which we // do not want in this case. $page->y = max($positions[$lastPageId]); $space->y = $page->y = $page->y + $cell['styles']['padding']->value['bottom'] + $cell['styles']['border']->value['bottom']['width'] + $cell['styles']['margin']->value['bottom']; } $box->height = $space->y - $box->y; $this->renderBoxBorder($box, $styles, false); }
public function testRenderCustomFont() { $docbook = new ezcDocumentDocbook(); $docbook->loadFile(dirname(__FILE__) . '/../files/pdf/wrapping.xml'); $style = new ezcDocumentPcssStyleInferencer(); $style->appendStyleDirectives(array(new ezcDocumentPcssLayoutDirective(array('article'), array('font-family' => 'my-font')), new ezcDocumentPcssDeclarationDirective('@font-face', array('font-family' => 'my-font', 'src' => 'url( ' . dirname(__FILE__) . '/../files/fonts/font.ttf )')))); $renderer = new ezcDocumentPdfMainRenderer(new ezcDocumentPdfSvgDriver(), $style); $pdf = $renderer->render($docbook, new ezcDocumentPdfDefaultHyphenator()); $this->assertPdfDocumentsSimilar($pdf, __CLASS__ . '_' . __FUNCTION__); }
/** * @dataProvider getItemizedListTypes */ public function testRenderItemizedListTypes($type, array $items) { $mock = $this->getMock('ezcTestDocumentPdfMockDriver', array('createPage', 'drawWord')); // Expectations $mock->expects($this->at(1))->method('drawWord')->with($this->equalTo(10, 1.0), $this->equalTo(18, 1.0), $this->equalTo($items[0])); $mock->expects($this->at(4))->method('drawWord')->with($this->equalTo(10, 1.0), $this->equalTo(33, 1.0), $this->equalTo($items[1])); $docbook = new ezcDocumentDocbook(); $docbook->loadFile(dirname(__FILE__) . '/../files/pdf/bullet_list.xml'); // Set numeration type in document $dom = $docbook->getDomDocument(); $xpath = new DOMXPath($dom); $xpath->registerNamespace('doc', 'http://docbook.org/ns/docbook'); $list = $xpath->evaluate('//doc:itemizedlist')->item(0); $list->setAttribute('mark', $type); $docbook->setDomDocument($dom); $renderer = new ezcDocumentPdfMainRenderer($mock, $this->styles); $pdf = $renderer->render($docbook, new ezcDocumentPdfDefaultHyphenator()); }