/Document/src/document/pdf/renderer/main.php
PHP | 933 lines | 381 code | 81 blank | 471 comment | 21 complexity | a9d3ea11009c99b0e2387d88e3e1b8c6 MD5 | raw file
- <?php
- /**
- * File containing the ezcDocumentPdfMainRenderer class
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- * @package Document
- * @version //autogen//
- * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
- * @access private
- */
- /**
- * Main PDF renderer class, dispatching to sub renderer, maintaining page
- * contexts and transactions.
- *
- * The basic principles behind the used stacked backtracking rendering
- * algorithm are explained below.
- *
- * The basics
- * ==========
- *
- * The rendering size of a single block (paragraph, image) cannot be guessed
- * properly beforehand, because the dimensions depend on the associated styles
- * and the driver which needs to render the styles. Because of different fonts
- * the size of a single simple string may notably vary. The renderer do not
- * know about font properties, but only the drivers do.
- *
- * Because of that the renderers (like the paragraph renderer) can only
- * request the used dimensions for each word, or word part from the current
- * driver and try to fit that word into the currently available space.
- *
- * Some general constraints, like the handling of orphans and widows, require
- * the renderer to backtrack. If a orphans constraint could not be fulfilled
- * with the first rendering try, the renderer needs to decide to render less
- * lines on the prior page and therefore needs to revert all local rendering
- * steps and retry the rendering with the additional knowledge.
- *
- * For widow constraints this may mean, that a full paragraph is moved to the
- * next page, which could mean, that the title before that paragraph might
- * also be relocated to the following page, which would mean to revert
- * multiple renderers and elements. To make this possible the renderer wraps
- * the driver in a transactional driver wrapper.
- *
- * The transactional driver wrapper
- * --------------------------------
- *
- * Like the last paragraph explained it might be necessary to revert (large)
- * amounts of rendering operations. Once the rendering operations (like
- * drawWord) hit the driver they are immediately serialized into the
- * respective output format (PDF), and could not be reverted anymore.
- *
- * So an additional layer has been implemented in the class
- * ezcDocumentPdfTransactionalDriverWrapper, which implements the same
- * interface as all the other drivers, as well as some additional methods to
- * handle "transactions".
- *
- * A renderer, like the paragraph renderer, may start a transaction, receives
- * an ID identifying the started transaction, and may then start its rendering
- * operations. If the rendering reached a dead end, it may revert everything
- * using the initially given transaction ID. The revert will affect all
- * operations since the original call to startTransaction(), even if other
- * sub-renderers also started transactions in the meantime.
- *
- * The logged calls to the driver are passed up to the real driver once save()
- * is called explicitly for the given transaction ID, or the main renderer
- * attempts to write the PDF into a file.
- *
- * Depending on the type of the call the driver wrapper logs and / or passes
- * the call directly up to the actual driver.
- *
- * Calls which are logged only:
- * - Everything performing actual rendering, like drawLine(), drawWord(), ...
- *
- * Calls which are logged and passed:
- * - Everything setting the current style configuration, which might also be
- * relevant for font width estimation, especially: setStyle()
- *
- * Calls which are not logged, but passed:
- * - Everything, which only requests properties, but does not change the
- * driver state, like getTextWidth()
- *
- * The stacked renderers
- * ---------------------
- *
- * The main renderer, which is defined in this class, is responsible for
- * managing the pages, the available horizontal space on the current page and
- * calling the sub renderers for the distinct parts in the Docbook document.
- *
- * For each part there is a specialized renderer, which is only responsible
- * for rendering such a part, like a list renderer, a list item renderer or a
- * paragraph renderer. The main renderer traverses the Docbook document and calls
- * the appropriate renderer. You may register additional renderers with the
- * main renderer, for your custom elements, or overwrite the defined default
- * renderers.
- *
- * The main renderer also handles special page elements, like headers and
- * footers for each page.
- *
- * The sub renderers ask the main renderer for new space, if they exceeded the
- * available space in the current column / on the current page. This is
- * implemented in the method getNextRenderingPosition(). This method might
- * request a new page from the driver.
- *
- * The sub renderer may as well call other sub renderer, for stacked element
- * definitions or may request rendering for all those elements by the main
- * renderer calling back to the process() method.
- *
- * The table sub renderer
- * ----------------------
- *
- * The table renderer is a special sub renderer, since the common space
- * estimation does not apply here. Tables are structured into cells and the
- * elements contained in one cell may only use the space defined by the cell.
- * The table renderer therefore mimics (and extends) the main renderer. So
- * when the contents of one cell are rendered the sub renderers for the cell
- * contents (paragraphs, lists, ...) receive an instance of the table renderer
- * as their "new" main renderer. The table renderer overwrites the methods
- * like process() and getNextRenderingPosition(), so the sub renderers render
- * their stuff at the correct positions in the cell.
- *
- * The table renderer itself again dispatches to its main renderer, when, for
- * example, allocating new pages. In case of a stacked table, the main
- * renderer of a table renderer may again be a table renderer, which then
- * dispatches to the original main renderer.
- *
- * Style inheritance
- * -----------------
- *
- * The definition of styles works just like CSS with HTML. Each element
- * inherits the styles from its parent element, which are then overwritten by
- * the defined styles in the (P)CSS file.
- *
- * The inferring of the styles for a given element is implemented in the
- * ezcDocumentPcssStyleInferencer class. An instance of this class containing
- * the currently defined styles is available during the whole rendering
- * process and will provide the styles for any element, which is passed to the
- * object.
- *
- * Hyphenation
- * -----------
- *
- * Hyphenation is a critical task for proper text rendering. A custom
- * hyphenator may be defined and passed to the renderer. Each text renderer
- * will the ask the hyphenator to split words, if the whole word does not fit
- * into one line any more. It would be sensible to implement a hyphenator
- * based on some available dictionary files.
- *
- * Tokenizer
- * ---------
- *
- * For some languages it might be necessary to implement a different text
- * tokenizer, which does not just split words at whitespaces. To accomplish
- * that you may implement and pass a custom tokenizer, which is the
- * responsible for splitting texts.
- *
- * Some renderers, like the literal box renderer, may already use custom
- * tokenizers, to implement special rendering tasks.
- *
- * @package Document
- * @access private
- * @version //autogen//
- */
- class ezcDocumentPdfMainRenderer extends ezcDocumentPdfRenderer implements ezcDocumentErrorReporting
- {
- /**
- * Hyphenator used to split up words
- *
- * @var ezcDocumentPdfHyphenator
- */
- protected $hyphenator;
- /**
- * Tokenizer used to split up strings into words
- *
- * @var ezcDocumentPdfTokenizer
- */
- protected $tokenizer;
- /**
- * Document to render
- *
- * @var ezcDocumentDocbook
- */
- protected $document;
- /**
- * Last transactions started before rendering a new title. This is used to
- * determine, if a title is positioned as a single item in a column or on a
- * page and switch it to the next page in this case.
- *
- * @var mixed
- */
- protected $titleTransaction = null;
- /**
- * Indicator to restart rendering with an earlier item on the same level in
- * the DOM document tree.
- *
- * @var mixed
- */
- protected $restart = false;
- /**
- * Errors occured during the conversion process
- *
- * @var array
- */
- protected $errors = array();
- /**
- * Maps document elements to handler functions
- *
- * Maps each document element of the associated namespace to its handler
- * method in the current class.
- *
- * @var array
- */
- protected $handlerMapping = array(
- 'http://docbook.org/ns/docbook' => array(
- 'article' => 'initializeDocument',
- 'section' => 'renderBlock',
- 'sectioninfo' => 'appendMetaData',
- 'para' => 'renderParagraph',
- 'title' => 'renderTitle',
- 'mediaobject' => 'renderMediaObject',
- 'literallayout' => 'renderLiteralLayout',
- 'blockquote' => 'renderBlockquote',
- 'table' => 'renderTable',
- 'itemizedlist' => 'renderList',
- 'orderedlist' => 'renderList',
- 'variablelist' => 'renderBlock',
- 'varlistentry' => 'renderBlock',
- 'listitem' => 'renderListItem',
- 'term' => 'renderTitle',
- ),
- );
- /**
- * Additional PDF parts.
- *
- * @var array
- */
- protected $parts = array();
- /**
- * Error reporting level
- *
- * @var int
- */
- protected $errorReporting = 15;
- /**
- * PDF renderer options
- *
- * @var ezcDocumentPdfOptions
- */
- protected $options;
- /**
- * Construct renderer from driver to use
- *
- * @param ezcDocumentPdfDriver $driver
- * @param ezcDocumentPcssStyleInferencer $styles
- * @param ezcDocumentPdfOptions $options
- * @return void
- */
- public function __construct( ezcDocumentPdfDriver $driver, ezcDocumentPcssStyleInferencer $styles, ezcDocumentPdfOptions $options = null )
- {
- $this->driver = new ezcDocumentPdfTransactionalDriverWrapper();
- $this->driver->setDriver( $driver );
- $this->styles = $styles;
- $this->options = $options;
- $this->errorReporting = $options !== null ? $options->errorReporting : 15;
- }
- /**
- * Trigger visitor error
- *
- * Emit a vistitor error, and convert it to an exception depending on the
- * error reporting settings.
- *
- * @param int $level
- * @param string $message
- * @param string $file
- * @param int $line
- * @param int $position
- * @return void
- */
- public function triggerError( $level, $message, $file = null, $line = null, $position = null )
- {
- if ( $level & $this->errorReporting )
- {
- throw new ezcDocumentVisitException( $level, $message, $file, $line, $position );
- }
- else
- {
- // If the error should not been reported, we aggregate it to maybe
- // display it later.
- $this->errors[] = new ezcDocumentVisitException( $level, $message, $file, $line, $position );
- }
- }
- /**
- * Return list of errors occured during visiting the document.
- *
- * May be an empty array, if on errors occured, or a list of
- * ezcDocumentVisitException objects.
- *
- * @return array
- */
- public function getErrors()
- {
- return $this->errors;
- }
- /**
- * Tries to locate a file
- *
- * Tries to locate a file, referenced in a docbook document. If available
- * the document path is used a base for relative paths.
- *
- * @param string $file
- * @return string
- */
- public function locateFile( $file )
- {
- if ( !ezcBaseFile::isAbsolutePath( $file ) )
- {
- $file = $this->document->getPath() . $file;
- }
- if ( !is_file( $file ) )
- {
- throw new ezcBaseFileNotFoundException( $file );
- }
- return $file;
- }
- /**
- * Register an additional PDF part
- *
- * Register additional parts, like footnotes, headers or title pages.
- *
- * @param ezcDocumentPdfPart $part
- * @return void
- */
- public function registerPdfPart( ezcDocumentPdfPart $part )
- {
- $this->parts[] = $part;
- $part->registerContext( $this, $this->driver, $this->styles );
- }
- /**
- * Render given document
- *
- * Returns the rendered PDF as string
- *
- * @param ezcDocumentDocbook $document
- * @param ezcDocumentPdfHyphenator $hyphenator
- * @param ezcDocumentPdfTokenizer $tokenizer
- * @return string
- */
- public function render( ezcDocumentDocbook $document, ezcDocumentPdfHyphenator $hyphenator = null, ezcDocumentPdfTokenizer $tokenizer = null )
- {
- $this->hyphenator = $hyphenator !== null ? $hyphenator : new ezcDocumentPdfDefaultHyphenator();
- $this->tokenizer = $tokenizer !== null ? $tokenizer : new ezcDocumentPdfDefaultTokenizer();
- $this->document = $document;
- // Register custom fonts in driver
- $this->registerFonts();
- // Inject custom element class, for style inferencing
- $dom = $document->getDomDocument();
- // Reload the XML document with to a DOMDocument with a custom element
- // class. Just registering it on the existing document seems not to
- // work in all cases.
- $reloaded = new DOMDocument();
- $reloaded->registerNodeClass( 'DOMElement', 'ezcDocumentLocateableDomElement' );
- $reloaded->loadXml( $dom->saveXml() );
- $this->process( $reloaded );
- return $this->driver->save();
- }
- /**
- * Register fonts in driver
- *
- * Register the font classes specified in the styles with the driver, so
- * the driver can use the fonts during the rendering.
- *
- * @return void
- */
- protected function registerFonts()
- {
- foreach ( $this->styles->getDefinitions( 'font-face' ) as $font )
- {
- if ( !isset( $font->formats['font-family'] ) )
- {
- $this->triggerError( E_WARNING, "Missing font-family declaration in @font-face specification.", $font->file, $font->line );
- continue;
- }
- $name = $font->formats['font-family']->value;
- if ( !isset( $font->formats['src'] ) )
- {
- $this->triggerError( E_WARNING, "Missing src declaration in @font-face specification.", $font->file, $font->line );
- continue;
- }
- $pathes = $font->formats['src']->value;
- $style = ezcDocumentPdfDriver::FONT_PLAIN;
- if ( isset( $font->formats['font-style'] ) &&
- ( ( $font->formats['font-style']->value === 'oblique' ) ||
- ( $font->formats['font-style']->value === 'italic' ) ) )
- {
- $style |= ezcDocumentPdfDriver::FONT_OBLIQUE;
- }
- if ( isset( $font->formats['font-weight'] ) &&
- ( ( $font->formats['font-weight']->value === 'bold' ) ||
- ( $font->formats['font-weight']->value === 'bolder' ) ) )
- {
- $style |= ezcDocumentPdfDriver::FONT_BOLD;
- }
- $this->driver->registerFont( $name, $style, $pathes );
- }
- }
- /**
- * Check column or page skip prerequisite
- *
- * If no content has been rendered any more in the current column, this
- * method should be called to check prerequisite for the skip, which is
- * especially important for already rendered items, which impose
- * assumptions on following contents.
- *
- * One example for this are titles, which should always be followed by at
- * least some content in the same column.
- *
- * Returns false, if prerequisite are not fulfileld and rendering should be
- * aborted.
- *
- * @param float $move
- * @param float $width
- * @return bool
- */
- public function checkSkipPrerequisites( $move, $width )
- {
- // Ensure the paragraph is on the same page / in the same column
- // like a title, of it is the first paragraph
- if ( $this->titleTransaction === null )
- {
- return true;
- }
- $this->driver->revert( $this->titleTransaction['transaction'] );
- // The rendering should now start again with the title on the
- // next column / page.
- $this->getNextRenderingPosition( $move, $width );
- $this->restart = $this->titleTransaction['position'] - 1;
- $this->titleTransaction = null;
- return false;
- }
- /**
- * Calculate text width
- *
- * Calculate the available horizontal space for texts depending on the
- * page layout settings.
- *
- * @param ezcDocumentPdfPage $page
- * @param ezcDocumentLocateableDomElement $text
- * @return float
- */
- public function calculateTextWidth( ezcDocumentPdfPage $page, ezcDocumentLocateableDomElement $text )
- {
- // Inference page styles
- $rules = $this->styles->inferenceFormattingRules( $text );
- return ( $page->innerWidth -
- ( $rules['text-column-spacing']->value * ( $rules['text-columns']->value - 1 ) )
- ) / $rules['text-columns']->value
- - $page->xOffset - $page->xReduce;
- }
- /**
- * 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 )
- {
- // Then move paragraph into next column / page;
- $trans = $this->driver->startTransaction();
- $page = $this->driver->currentPage();
- if ( ( ( $newX = $page->x + $move ) < ( $page->startX + $page->innerWidth ) ) &&
- ( ( $space = $page->testFitRectangle( $newX, null, $width, 2 ) ) !== false ) )
- {
- // Another column fits on the current page, find starting Y
- // position
- $page->x = $space->x;
- $page->y = $space->y;
- return $page;
- }
- // If there is no space for a new column, create a new page
- $oldPage = $page;
- $page = $this->driver->appendPage( $this->styles );
- $page->xOffset = $oldPage->xOffset;
- $page->xReduce = $oldPage->xReduce;
- foreach ( $this->parts as $part )
- {
- $part->hookPageCreation( $page );
- }
- return $page;
- }
- /**
- * Process a single element with the registered renderers.
- *
- * @param DOMElement $element
- * @param int $number
- * @return int
- */
- public function processNode( DOMElement $element, $number = 0 )
- {
- // Default to docbook namespace, if no namespace is defined
- $namespace = $element->namespaceURI === null ? 'http://docbook.org/ns/docbook' : $element->namespaceURI;
- if ( !isset( $this->handlerMapping[$namespace] ) ||
- !isset( $this->handlerMapping[$namespace][$element->tagName] ) )
- {
- $this->triggerError(
- E_NOTICE,
- "Unknown and unhandled element: {$namespace}:{$element->tagName}."
- );
- return $number;
- }
- $method = $this->handlerMapping[$namespace][$element->tagName];
- $this->$method( $element, $number );
- // Check if the rendering process should be restarted at an earlier
- // point
- if ( $this->restart !== false )
- {
- $number = $this->restart;
- $this->restart = false;
- return $number;
- }
- return $number;
- }
- /**
- * Recurse into DOMDocument tree and call appropriate element handlers
- *
- * @param DOMNode $element
- * @return void
- */
- public function process( DOMNode $element )
- {
- $childNodes = $element->childNodes;
- $nodeCount = $childNodes->length;
- for ( $i = 0; $i < $nodeCount; ++$i )
- {
- $child = $childNodes->item( $i );
- if ( $child->nodeType !== XML_ELEMENT_NODE )
- {
- continue;
- }
- $i = $this->processNode( $child, $i );
- }
- }
- /**
- * Ignore elements, which should not be rendered
- *
- * @param ezcDocumentLocateableDomElement $element
- * @return void
- */
- private function ignore( ezcDocumentLocateableDomElement $element )
- {
- // Just do nothing.
- }
- /**
- * Initialize document according to detected root node
- *
- * @param ezcDocumentLocateableDomElement $element
- * @return void
- */
- private function initializeDocument( ezcDocumentLocateableDomElement $element )
- {
- // Call hooks for started document
- foreach ( $this->parts as $part )
- {
- $part->hookDocumentCreation( $element );
- }
- $page = $this->driver->appendPage( $this->styles );
- // Call hooks for fresh new first page
- foreach ( $this->parts as $part )
- {
- $part->hookPageCreation( $page );
- }
- // Continue processing sub nodes
- $this->process( $element );
- // Call hooks for finished document
- foreach ( $this->parts as $part )
- {
- $part->hookDocumentRendering( $element );
- }
- }
- /**
- * Append document metadata
- *
- * @param ezcDocumentLocateableDomElement $element
- * @return void
- */
- private function appendMetaData( ezcDocumentLocateableDomElement $element )
- {
- $childNodes = $element->childNodes;
- $nodeCount = $childNodes->length;
- // Default metadata values
- $metadata = array(
- 'created' => date( 'r' ),
- 'modified' => date( 'r' ),
- );
- // Fields mapped to metadata identifiers
- $fields = array(
- 'http://docbook.org/ns/docbook' => array(
- 'title' => 'title',
- 'author' => 'author',
- 'authors' => 'author',
- 'subtitle' => 'subject',
- 'pubdate' => 'created',
- 'date' => 'modified',
- ),
- );
- for ( $i = 0; $i < $nodeCount; ++$i )
- {
- $child = $childNodes->item( $i );
- if ( $child->nodeType !== XML_ELEMENT_NODE )
- {
- continue;
- }
- $namespace = $element->namespaceURI === null ? 'http://docbook.org/ns/docbook' : $element->namespaceURI;
- if ( isset( $fields[$namespace] ) &&
- isset( $fields[$namespace][$child->tagName] ) )
- {
- $metadata[$fields[$namespace][$child->tagName]] = $child->textContent;
- }
- }
- foreach ( $metadata as $key => $value )
- {
- $this->driver->setMetaData( $key, $value );
- }
- }
- /**
- * Handle calls to block element renderer
- *
- * @param ezcDocumentLocateableDomElement $element
- * @return void
- */
- private function renderBlock( ezcDocumentLocateableDomElement $element )
- {
- $renderer = new ezcDocumentPdfBlockRenderer( $this->driver, $this->styles );
- $page = $this->driver->currentPage();
- return $renderer->renderNode( $page, $this->hyphenator, $this->tokenizer, $element, $this );
- }
- /**
- * Handle calls to block element renderer
- *
- * @param ezcDocumentLocateableDomElement $element
- * @return void
- */
- private function renderBlockquote( ezcDocumentLocateableDomElement $element )
- {
- $renderer = new ezcDocumentPdfBlockquoteRenderer( $this->driver, $this->styles );
- $page = $this->driver->currentPage();
- return $renderer->renderNode( $page, $this->hyphenator, $this->tokenizer, $element, $this );
- }
- /**
- * Handle calls to table element renderer
- *
- * @param ezcDocumentLocateableDomElement $element
- * @return void
- */
- private function renderTable( ezcDocumentLocateableDomElement $element )
- {
- $renderer = new ezcDocumentPdfTableRenderer( $this->driver, $this->styles, $this->options );
- $page = $this->driver->currentPage();
- return $renderer->renderNode( $page, $this->hyphenator, $this->tokenizer, $element, $this );
- }
- /**
- * Handle calls to List element renderer
- *
- * @param ezcDocumentLocateableDomElement $element
- * @return void
- */
- private function renderList( ezcDocumentLocateableDomElement $element )
- {
- $renderer = new ezcDocumentPdfListRenderer( $this->driver, $this->styles );
- $page = $this->driver->currentPage();
- return $renderer->renderNode( $page, $this->hyphenator, $this->tokenizer, $element, $this );
- }
- /**
- * Handle calls to list item element renderer
- *
- * @param ezcDocumentLocateableDomElement $element
- * @return void
- */
- private function renderListItem( ezcDocumentLocateableDomElement $element )
- {
- $renderer = new ezcDocumentPdfListItemRenderer( $this->driver, $this->styles, new ezcDocumentNoListItemGenerator(), 0 );
- $page = $this->driver->currentPage();
- return $renderer->renderNode( $page, $this->hyphenator, $this->tokenizer, $element, $this );
- }
- /**
- * Handle calls to paragraph renderer
- *
- * @param ezcDocumentLocateableDomElement $element
- * @return void
- */
- private function renderParagraph( ezcDocumentLocateableDomElement $element )
- {
- $renderer = new ezcDocumentPdfWrappingTextBoxRenderer( $this->driver, $this->styles );
- $page = $this->driver->currentPage();
- $styles = $this->styles->inferenceFormattingRules( $element );
- // Just try to render at current position first
- $trans = $this->driver->startTransaction();
- if ( $renderer->renderNode( $page, $this->hyphenator, $this->tokenizer, $element, $this ) )
- {
- $this->titleTransaction = null;
- $this->handleAnchors( $element );
- return true;
- }
- // Check if something requested a rendering restart at a prior point,
- // only continue otherwise.
- if ( ( $this->restart !== false ) ||
- ( !$this->checkSkipPrerequisites(
- ( $pWidth = $this->calculateTextWidth( $page, $element ) ) +
- $styles['text-column-spacing']->value,
- $pWidth
- ) ) )
- {
- return false;
- }
- // If that did not work, switch to the next possible location and start
- // there.
- $this->driver->revert( $trans );
- $this->getNextRenderingPosition(
- ( $pWidth = $this->calculateTextWidth( $page, $element ) ) +
- $styles['text-column-spacing']->value,
- $pWidth
- );
- return $this->renderParagraph( $element );
- }
- /**
- * Handle calls to title renderer
- *
- * @param ezcDocumentLocateableDomElement $element
- * @param int $position
- */
- private function renderTitle( ezcDocumentLocateableDomElement $element, $position )
- {
- $styles = $this->styles->inferenceFormattingRules( $element );
- $renderer = new ezcDocumentPdfTitleRenderer( $this->driver, $this->styles );
- $page = $this->driver->currentPage();
- // Just try to render at current position first
- $this->titleTransaction = array(
- 'transaction' => $this->driver->startTransaction(),
- 'page' => $page,
- 'xPos' => $page->x,
- 'position' => $position,
- );
- if ( $renderer->renderNode( $page, $this->hyphenator, $this->tokenizer, $element, $this ) )
- {
- $this->handleAnchors( $element );
- return true;
- }
- $this->driver->revert( $this->titleTransaction['transaction'] );
- $this->getNextRenderingPosition(
- ( $pWidth = $this->calculateTextWidth( $page, $element ) ) +
- $styles['text-column-spacing']->value,
- $pWidth
- );
- return $this->renderTitle( $element, $position );
- }
- /**
- * Handle calls to media object renderer
- *
- * @param ezcDocumentLocateableDomElement $element
- * @return void
- */
- private function renderMediaObject( ezcDocumentLocateableDomElement $element )
- {
- $renderer = new ezcDocumentPdfMediaObjectRenderer( $this->driver, $this->styles );
- $page = $this->driver->currentPage();
- // Just try to render at current position first
- $trans = $this->driver->startTransaction();
- $renderer->renderNode( $page, $this->hyphenator, $this->tokenizer, $element, $this );
- $this->handleAnchors( $element );
- }
- /**
- * Handle calls to paragraph renderer
- *
- * @param ezcDocumentLocateableDomElement $element
- * @return void
- */
- private function renderLiteralLayout( ezcDocumentLocateableDomElement $element )
- {
- $renderer = new ezcDocumentPdfLiteralBlockRenderer( $this->driver, $this->styles );
- $page = $this->driver->currentPage();
- $styles = $this->styles->inferenceFormattingRules( $element );
- // Just try to render at current position first
- $trans = $this->driver->startTransaction();
- if ( $renderer->renderNode( $page, $this->hyphenator, $this->tokenizer, $element, $this ) )
- {
- $this->titleTransaction = null;
- $this->handleAnchors( $element );
- return true;
- }
- // Check if something requested a rendering restart at a prior point,
- // only continue otherwise.
- if ( ( $this->restart !== false ) ||
- ( !$this->checkSkipPrerequisites(
- ( $pWidth = $this->calculateTextWidth( $page, $element ) ) +
- $styles['text-column-spacing']->value,
- $pWidth
- ) ) )
- {
- return false;
- }
- // If that did not work, switch to the next possible location and start
- // there.
- $this->driver->revert( $trans );
- $this->getNextRenderingPosition(
- ( $pWidth = $this->calculateTextWidth( $page, $element ) ) +
- $styles['text-column-spacing']->value,
- $pWidth
- );
- return $this->renderParagraph( $element );
- }
- /**
- * Handle all anchors inside the current element
- *
- * Finds all anchors somewhere in the current element and adds reference
- * targets for them.
- *
- * @param ezcDocumentLocateableDomElement $element
- * @return void
- */
- private function handleAnchors( ezcDocumentLocateableDomElement $element )
- {
- $xpath = new DOMXPath( $element->ownerDocument );
- $xpath->registerNamespace( 'doc', 'http://docbook.org/ns/docbook' );
- foreach ( $xpath->query( './/doc:anchor', $element ) as $anchor )
- {
- $this->driver->addInternalLinkTarget( $anchor->getAttribute( 'id' ) );
- }
- }
- }
- ?>