/**
  * 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;
 }
Beispiel #2
0
 /**
  * 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;
 }
Beispiel #3
0
 /**
  * Get next rendering position.
  *
  * If the current space has been exceeded this method calculates
  * a new rendering position, optionally creates a new page for
  * this, or switches to the next column. The new rendering;
  * position is set on the returned page object.
  *
  * As the parameter you need to pass the required width for the object to
  * place on the page.
  *
  * @param float $move
  * @param float $width
  * @return ezcDocumentPdfPage
  */
 public function getNextRenderingPosition($move, $width)
 {
     // Close all table cells
     $oldPage = $this->driver->currentPage();
     foreach ($this->cellBoxes as $nr => $cell) {
         if (isset($this->additionalBorders[$oldPage->orderedId]) && isset($this->additionalBorders[$oldPage->orderedId][$nr])) {
             continue;
         }
         $cell['box']->height = $oldPage->startY + $oldPage->innerHeight - $cell['box']->y;
         $this->additionalBorders[$oldPage->orderedId][$nr] = array(clone $cell['box'], $cell['styles'], false);
     }
     // Close table border
     if (!isset($this->additionalBorders[$oldPage->orderedId]) || !isset($this->additionalBorders[$oldPage->orderedId]['table'])) {
         $this->tableBox['box']->height = $oldPage->startY + $oldPage->innerHeight - $this->tableBox['box']->y;
         $this->additionalBorders[$oldPage->orderedId]['table'] = array(clone $this->tableBox['box'], $this->tableBox['styles'], false, false);
     }
     // Call parent handler to get next rendering position
     $page = parent::getNextRenderingPosition($move, $width);
     if ($page === $oldPage) {
         $this->lastPageForCell = $page;
         return $page;
     }
     // Update all boxes to start on top of the new page
     foreach ($this->cellBoxes as $cell) {
         $cell['box']->y = $page->y;
         $this->renderTopBorder($cell['styles'], $cell['box']);
     }
     // Maintain horizontal rendering position
     $page->x = $oldPage->x;
     $page->y = $page->startY;
     $this->tableBox['box']->y = $page->y;
     return $this->lastPageForCell = $page;
 }