| <?php |
| /** |
| * File containing the ezcDocumentPdfSvgDriver 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// |
| * @copyright Copyright (C) 2005-2010 eZ Systems AS. All rights reserved. |
| * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0 |
| * @access private |
| */ |
| |
| /** |
| * SVG renderer for PDF driver, useful for manual introspection and test |
| * comparisions. |
| * |
| * ONLY FOR TESTING - especially, since the text width estimation does not |
| * work properly without using SVG glyph support. The generated XML files are |
| * therefore only used for easy comparision of the general rendering results. |
| * |
| * @package Document |
| * @access private |
| * @version //autogen// |
| */ |
| class ezcDocumentPdfSvgDriver extends ezcDocumentPdfDriver |
| { |
| /** |
| * Svg Document instance |
| * |
| * @var DOMDocument |
| */ |
| protected $document; |
| |
| /** |
| * Node of SVG root element |
| * |
| * @var DOMElement |
| */ |
| protected $svg; |
| |
| /** |
| * Root node for page elements |
| * |
| * @var DOMElement |
| */ |
| protected $pages; |
| |
| /** |
| * Root node for metadata |
| * |
| * @var DOMElement |
| */ |
| protected $metadata; |
| |
| /** |
| * Root node of current page |
| * |
| * @var DOMElement |
| */ |
| protected $currentpage; |
| |
| /** |
| * Current inner document offset |
| * |
| * @var float |
| */ |
| protected $offset = 0; |
| |
| /** |
| * Next inner document offset after page creation |
| * |
| * @var float |
| */ |
| protected $nextOffset = 0; |
| |
| /** |
| * Array with fonts, and their equivalents for bold and italic markup. This |
| * array will be extended when loading new fonts, but contains the builtin |
| * fonts by default. |
| * |
| * The fourth value for each font is bold + oblique, the index is the |
| * bitwise and combination of the repective combinations. Each font MUST |
| * have at least a value for FONT_PLAIN assigned. |
| * |
| * @var array |
| */ |
| protected $fonts = array( |
| 'sans-serif' => array( |
| self::FONT_PLAIN => 'Bitstream Vera Sans', |
| self::FONT_BOLD => 'Bitstream Vera Sans', |
| self::FONT_OBLIQUE => 'Bitstream Vera Sans', |
| 3 => 'Bitstream Vera Sans', |
| ), |
| 'serif' => array( |
| self::FONT_PLAIN => 'Bitstream Vera Serif', |
| self::FONT_BOLD => 'Bitstream Vera Serif', |
| self::FONT_OBLIQUE => 'Bitstream Vera Serif', |
| 3 => 'Bitstream Vera Serif', |
| ), |
| 'monospace' => array( |
| self::FONT_PLAIN => 'Bitstream Vera Sans Mono', |
| self::FONT_BOLD => 'Bitstream Vera Sans Mono', |
| self::FONT_OBLIQUE => 'Bitstream Vera Sans Mono', |
| 3 => 'Bitstream Vera Sans Mono', |
| ), |
| 'Symbol' => array( |
| self::FONT_PLAIN => 'Symbol', |
| ), |
| 'ZapfDingbats' => array( |
| self::FONT_PLAIN => 'ZapfDingbats', |
| ), |
| ); |
| |
| /** |
| * Name and style of default font / currently used font |
| * |
| * @var array |
| */ |
| protected $currentFont = array( |
| 'name' => 'sans-serif', |
| 'style' => self::FONT_PLAIN, |
| 'size' => 28.5, |
| 'font' => null, |
| 'color' => '#000000', |
| ); |
| |
| /** |
| * Construct driver |
| * |
| * Creates a new document instance maintaining all document context. |
| * |
| * @return void |
| */ |
| public function __construct() |
| { |
| parent::__construct(); |
| |
| $this->document = new DOMDocument( '1.0' ); |
| $this->document->formatOutput = true; |
| |
| $this->svg = $this->document->createElementNS( 'http://www.w3.org/2000/svg', 'svg' ); |
| $this->svg = $this->document->appendChild( $this->svg ); |
| |
| $this->svg->setAttribute( 'version', '1.2' ); |
| $this->svg->setAttribute( 'streamable', 'true' ); |
| |
| $this->pages = $this->document->createElement( 'g' ); |
| $this->pages = $this->svg->appendChild( $this->pages ); |
| $this->pages->setAttribute( 'id', 'pages' ); |
| |
| $metadata = $this->document->createElement( 'metadata' ); |
| $metadata = $this->svg->appendChild( $metadata ); |
| |
| $rdfNs = 'http://www.w3.org/1999/02/22-rdf-syntax-ns'; |
| $rdf = $this->document->createElementNS( $rdfNs, 'rdf:RDF' ); |
| $metadata->appendChild( $rdf ); |
| |
| $this->metadata = $this->document->createElementNS( $rdfNs, 'rdf:Description' ); |
| $this->metadata = $rdf->appendChild( $this->metadata ); |
| $this->setMetaData( 'creator', 'eZ Components - Document //autogen//' ); |
| } |
| |
| /** |
| * Create a new page |
| * |
| * Create a new page in the PDF document with the given width and height. |
| * |
| * @param float $width |
| * @param float $height |
| * @return void |
| */ |
| public function createPage( $width, $height ) |
| { |
| $this->offset = $this->nextOffset; |
| $this->nextOffset += $width + 10; |
| |
| $this->currentPage = $this->document->createElement( 'g' ); |
| $this->currentPage = $this->pages->appendChild( $this->currentPage ); |
| |
| // Render a containing box visually representing a page in the box |
| $page = $this->document->createElement( 'rect' ); |
| $page = $this->currentPage->appendChild( $page ); |
| $page->setAttribute( 'x', $this->offset . 'mm' ); |
| $page->setAttribute( 'y', '0mm' ); |
| $page->setAttribute( 'width', $width . 'mm' ); |
| $page->setAttribute( 'height', $height . 'mm' ); |
| $page->setAttribute( 'style', 'fill: #ffffff; stroke: #000000; stroke-width: 1px; fill-opacity: 1; stroke-opacity: 1;' ); |
| } |
| |
| /** |
| * Set text formatting option |
| * |
| * Set a text formatting option. The names of the options are the same used |
| * in the PCSS files and need to be translated by the driver to the proper |
| * backend calls. |
| * |
| * |
| * @param string $type |
| * @param mixed $value |
| * @return void |
| */ |
| public function setTextFormatting( $type, $value ) |
| { |
| switch ( $type ) |
| { |
| case 'font-style': |
| if ( ( $value === 'oblique' ) || |
| ( $value === 'italic' ) ) |
| { |
| $this->currentFont['style'] |= self::FONT_OBLIQUE; |
| } |
| else |
| { |
| $this->currentFont['style'] &= ~self::FONT_OBLIQUE; |
| } |
| break; |
| |
| case 'font-weight': |
| if ( ( $value === 'bold' ) || |
| ( $value === 'bolder' ) ) |
| { |
| $this->currentFont['style'] |= self::FONT_BOLD; |
| } |
| else |
| { |
| $this->currentFont['style'] &= ~self::FONT_BOLD; |
| } |
| break; |
| |
| case 'font-family': |
| if ( isset( $this->fonts[$value] ) ) |
| { |
| $this->currentFont['name'] = $value; |
| } |
| break; |
| |
| case 'font-size': |
| $this->currentFont['size'] = ezcDocumentPcssMeasure::create( $value )->get( 'pt' ); |
| break; |
| |
| case 'color': |
| $this->currentFont['color'] = sprintf( '#%02x%02x%02x', |
| $value['red'] * 255, |
| $value['green'] * 255, |
| $value['blue'] * 255 |
| ); |
| break; |
| |
| default: |
| // @todo: Error reporting. |
| } |
| } |
| |
| /** |
| * Calculate the rendered width of the current word |
| * |
| * Calculate the width of the passed word, using the currently set text |
| * formatting options. |
| * |
| * @param string $word |
| * @return float |
| */ |
| public function calculateWordWidth( $word ) |
| { |
| return ezcDocumentPcssMeasure::create( |
| ( $this->currentFont['size'] * iconv_strlen( $word, 'UTF-8' ) * .43 ) . 'pt' |
| )->get(); |
| } |
| |
| /** |
| * Get current line height |
| * |
| * Return the current line height in millimeter based on the current font |
| * and text rendering settings. |
| * |
| * @return float |
| */ |
| public function getCurrentLineHeight() |
| { |
| return ezcDocumentPcssMeasure::create( $this->currentFont['size'] . 'pt' )->get(); |
| } |
| |
| /** |
| * Draw word at given position |
| * |
| * Draw the given word at the given position using the currently set text |
| * formatting options. |
| * |
| * @param float $x |
| * @param float $y |
| * @param string $word |
| * @return void |
| */ |
| public function drawWord( $x, $y, $word ) |
| { |
| $textNode = $this->document->createElement( 'text', htmlspecialchars( $word, ENT_QUOTES, 'UTF-8' ) ); |
| $textNode->setAttribute( 'x', sprintf( '%.4Fmm', $x + $this->offset ) ); |
| $textNode->setAttribute( 'y', sprintf( '%.4Fmm', $y ) ); |
| $textNode->setAttribute( |
| 'style', |
| sprintf( |
| 'font-size: %.2Fpt; font-family: %s; font-style: %s; font-weight: %s; stroke: none; fill: %s', |
| $this->currentFont['size'], |
| $this->fonts[$this->currentFont['name']][self::FONT_PLAIN], |
| ( $this->currentFont['style'] & self::FONT_OBLIQUE ) ? 'oblique' : 'normal', |
| ( $this->currentFont['style'] & self::FONT_BOLD ) ? 'bold' : 'normal', |
| $this->currentFont['color'] |
| ) |
| ); |
| $this->currentPage->appendChild( $textNode ); |
| } |
| |
| /** |
| * Draw image |
| * |
| * Draw image at the defined position. The first parameter is the |
| * (absolute) path to the image file, and the second defines the type of |
| * the image. If the driver cannot handle this aprticular image type, it |
| * should throw an exception. |
| * |
| * The further parameters define the location where the image should be |
| * rendered and the dimensions of the image in the rendered output. The |
| * dimensions do not neccesarily match the real image dimensions, and might |
| * require some kind of scaling inside the driver depending on the used |
| * backend. |
| * |
| * @param string $file |
| * @param string $type |
| * @param float $x |
| * @param float $y |
| * @param float $width |
| * @param float $height |
| * @return void |
| */ |
| public function drawImage( $file, $type, $x, $y, $width, $height ) |
| { |
| $image = $this->document->createElement( 'image' ); |
| |
| $image->setAttribute( 'x', sprintf( '%.4Fmm', $x + $this->offset ) ); |
| $image->setAttribute( 'y', sprintf( '%.4Fmm', $y ) ); |
| $image->setAttribute( 'width', sprintf( '%.4Fmm', $width ) ); |
| $image->setAttribute( 'height', sprintf( '%.4Fmm', $height ) ); |
| $image->setAttributeNS( |
| 'http://www.w3.org/1999/xlink', |
| 'xlink:href', |
| sprintf( 'data:%s;base64,%s', |
| $type, |
| base64_encode( file_get_contents( $file ) ) |
| ) |
| ); |
| |
| $this->currentPage->appendChild( $image ); |
| } |
| |
| /** |
| * Get SVG path string |
| * |
| * Transform the points array into a SVG path string. |
| * |
| * @param array $points |
| * @param bool $close |
| * @return string |
| */ |
| protected function getPointString( array $points, $close = true ) |
| { |
| $pointString = 'M '; |
| foreach ( $points as $point ) |
| { |
| $pointString .= sprintf( '%.4F,%.4F L ', |
| ezcDocumentPcssMeasure::create( $point[0] )->get( 'px', 90 ) + |
| ezcDocumentPcssMeasure::create( $this->offset )->get( 'px', 90 ), |
| ezcDocumentPcssMeasure::create( $point[1] )->get( 'px', 90 ) |
| ); |
| } |
| |
| return substr( $pointString, 0, -3 ) . ( $close ? ' z ' : '' ); |
| } |
| |
| /** |
| * Draw a fileld polygon |
| * |
| * Draw any filled polygon, filled using the defined color. The color |
| * should be passed as an array with the keys "red", "green", "blue" and |
| * optionally "alpha". Each key should have a value between 0 and 1 |
| * associated. |
| * |
| * The polygon itself is specified as an array of two-tuples, specifying |
| * the x and y coordinate of the point. |
| * |
| * @param array $points |
| * @param array $color |
| * @return void |
| */ |
| public function drawPolygon( array $points, array $color ) |
| { |
| $polygon = $this->document->createElement( 'path' ); |
| $polygon->setAttribute( 'd', $this->getPointString( $points ) ); |
| $polygon->setAttribute( |
| 'style', |
| sprintf( |
| 'stroke: none; fill: #%02x%02x%02x;', |
| $color['red'] * 255, |
| $color['green'] * 255, |
| $color['blue'] * 255 |
| ) |
| ); |
| $this->currentPage->appendChild( $polygon ); |
| } |
| |
| /** |
| * Draw a polyline |
| * |
| * Draw any non-filled polygon, filled using the defined color. The color |
| * should be passed as an array with the keys "red", "green", "blue" and |
| * optionally "alpha". Each key should have a value between 0 and 1 |
| * associated. |
| * |
| * The polyline itself is specified as an array of two-tuples, specifying |
| * the x and y coordinate of the point. |
| * |
| * The thrid parameter defines the width of the border and the last |
| * parameter may optionally be set to false to not close the polygon (draw |
| * another line from the last point to the first one). |
| * |
| * @param array $points |
| * @param array $color |
| * @param float $width |
| * @param bool $close |
| * @return void |
| */ |
| public function drawPolyline( array $points, array $color, $width, $close = true ) |
| { |
| |
| $polygon = $this->document->createElement( 'path' ); |
| $polygon->setAttribute( 'd', $this->getPointString( $points, $close ) ); |
| $polygon->setAttribute( |
| 'style', |
| sprintf( |
| 'stroke: #%02x%02x%02x; stroke-width: %.4Fmm; fill: none;', |
| $color['red'] * 255, |
| $color['green'] * 255, |
| $color['blue'] * 255, |
| $width |
| ) |
| ); |
| $this->currentPage->appendChild( $polygon ); |
| } |
| |
| /** |
| * Add an external link |
| * |
| * Add an external link to the rectangle specified by its top-left |
| * position, width and height. The last parameter is the actual URL to link |
| * to. |
| * |
| * @param float $x |
| * @param float $y |
| * @param float $width |
| * @param float $height |
| * @param string $url |
| * @return void |
| */ |
| public function addExternalLink( $x, $y, $width, $height, $url ) |
| { |
| // Not yet supported by SVG driver. |
| } |
| |
| /** |
| * Add an internal link |
| * |
| * Add an internal link to the rectangle specified by its top-left |
| * position, width and height. The last parameter is the target identifier |
| * to link to. |
| * |
| * @param float $x |
| * @param float $y |
| * @param float $width |
| * @param float $height |
| * @param string $target |
| * @return void |
| */ |
| public function addInternalLink( $x, $y, $width, $height, $target ) |
| { |
| // Not yet supported by SVG driver. |
| } |
| |
| /** |
| * Add an internal link target |
| * |
| * Add an internal link to the current page. The last parameter |
| * is the target identifier. |
| * |
| * @param string $id |
| * @return void |
| */ |
| public function addInternalLinkTarget( $id ) |
| { |
| // Not yet supported by SVG driver. |
| } |
| |
| /** |
| * Register a font |
| * |
| * Registers a font, which can be used by its name later in the driver. The |
| * given type is either self::FONT_PLAIN or a bitwise combination of self::FONT_BOLD |
| * and self::FONT_OBLIQUE. |
| * |
| * The third paramater specifies an array of pathes with references to font |
| * definition files. Multiple pathes may be specified to provide the same |
| * font using different types, because not all drivers may process all font |
| * types. |
| * |
| * @param string $name |
| * @param int $type |
| * @param array $pathes |
| * @return void |
| */ |
| public function registerFont( $name, $type, array $pathes ) |
| { |
| // This is a bit stupid, but this is only a test driver anyways. |
| $this->fonts[$name][$type] = "$name (" . pathinfo( $pathes[0], PATHINFO_FILENAME ) . ")"; |
| } |
| |
| /** |
| * Set metadata |
| * |
| * Set document meta data. The meta data types are identified by a list of |
| * keys, common to PDF, like: title, author, subject, created, modified. |
| * |
| * The values are passed like embedded in the docbook document and might |
| * need to be reformatted. |
| * |
| * @param string $key |
| * @param string $value |
| * @return void |
| */ |
| public function setMetaData( $key, $value ) |
| { |
| // We ignore dates here, because it would make stuff harder to test. |
| $mapping = array( |
| 'title' => 'title', |
| 'author' => 'author', |
| 'subject' => 'subject', |
| 'creator' => 'creator', |
| ); |
| |
| if ( !isset( $mapping[$key] ) ) |
| { |
| return; |
| } |
| |
| $info = $this->document->createElementNS( 'http://purl.org/dc/elements/1.1/', 'dc:' . $mapping[$key], htmlspecialchars( $value ) ); |
| $this->metadata->appendChild( $info ); |
| } |
| |
| /** |
| * Generate and return PDF |
| * |
| * Return the generated binary PDF content as a string. |
| * |
| * @return string |
| */ |
| public function save() |
| { |
| return $this->document->saveXml(); |
| } |
| } |
| ?> |