PageRenderTime 31ms CodeModel.GetById 11ms app.highlight 14ms RepoModel.GetById 1ms app.codeStats 1ms

/framework/vendor/zend/Zend/Pdf/Resource/Font/CidFont.php

http://zoop.googlecode.com/
PHP | 490 lines | 190 code | 54 blank | 246 comment | 32 complexity | e40cda733c9fa1c9c5c7d3f1ff380272 MD5 | raw file
  1<?php
  2/**
  3 * Zend Framework
  4 *
  5 * LICENSE
  6 *
  7 * This source file is subject to the new BSD license that is bundled
  8 * with this package in the file LICENSE.txt.
  9 * It is also available through the world-wide-web at this URL:
 10 * http://framework.zend.com/license/new-bsd
 11 * If you did not receive a copy of the license and are unable to
 12 * obtain it through the world-wide-web, please send an email
 13 * to license@zend.com so we can send you a copy immediately.
 14 *
 15 * @category   Zend
 16 * @package    Zend_Pdf
 17 * @subpackage Fonts
 18 * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
 19 * @license    http://framework.zend.com/license/new-bsd     New BSD License
 20 * @version    $Id: CidFont.php 20096 2010-01-06 02:05:09Z bkarwin $
 21 */
 22
 23/** Internally used classes */
 24require_once 'Zend/Pdf/Element/Array.php';
 25require_once 'Zend/Pdf/Element/Dictionary.php';
 26require_once 'Zend/Pdf/Element/Name.php';
 27require_once 'Zend/Pdf/Element/Numeric.php';
 28require_once 'Zend/Pdf/Element/String.php';
 29
 30
 31/** Zend_Pdf_Resource_Font */
 32require_once 'Zend/Pdf/Resource/Font.php';
 33
 34/**
 35 * Adobe PDF CIDFont font object implementation
 36 *
 37 * A CIDFont program contains glyph descriptions that are accessed using a CID as
 38 * the character selector. There are two types of CIDFont. A Type 0 CIDFont contains
 39 * glyph descriptions based on AdobeĆ¢&#x20AC;&#x2122;s Type 1 font format, whereas those in a
 40 * Type 2 CIDFont are based on the TrueType font format.
 41 *
 42 * A CIDFont dictionary is a PDF object that contains information about a CIDFont program.
 43 * Although its Type value is Font, a CIDFont is not actually a font. It does not have an Encoding
 44 * entry, it cannot be listed in the Font subdictionary of a resource dictionary, and it cannot be
 45 * used as the operand of the Tf operator. It is used only as a descendant of a Type 0 font.
 46 * The CMap in the Type 0 font is what defines the encoding that maps character codes to CIDs
 47 * in the CIDFont.
 48 *
 49 * Font objects should be normally be obtained from the factory methods
 50 * {@link Zend_Pdf_Font::fontWithName} and {@link Zend_Pdf_Font::fontWithPath}.
 51 *
 52 * @package    Zend_Pdf
 53 * @subpackage Fonts
 54 * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
 55 * @license    http://framework.zend.com/license/new-bsd     New BSD License
 56 */
 57abstract class Zend_Pdf_Resource_Font_CidFont extends Zend_Pdf_Resource_Font
 58{
 59    /**
 60     * Object representing the font's cmap (character to glyph map).
 61     * @var Zend_Pdf_Cmap
 62     */
 63    protected $_cmap = null;
 64
 65    /**
 66     * Array containing the widths of each character that have entries in used character map.
 67     *
 68     * @var array
 69     */
 70    protected $_charWidths = null;
 71
 72    /**
 73     * Width for characters missed in the font
 74     *
 75     * @var integer
 76     */
 77    protected $_missingCharWidth = 0;
 78
 79
 80    /**
 81     * Object constructor
 82     *
 83     * @param Zend_Pdf_FileParser_Font_OpenType $fontParser Font parser object
 84     *   containing OpenType file.
 85     * @param integer $embeddingOptions Options for font embedding.
 86     * @throws Zend_Pdf_Exception
 87     */
 88    public function __construct(Zend_Pdf_FileParser_Font_OpenType $fontParser)
 89    {
 90        parent::__construct();
 91
 92        $fontParser->parse();
 93
 94
 95        /* Object properties */
 96
 97        $this->_fontNames = $fontParser->names;
 98
 99        $this->_isBold       = $fontParser->isBold;
100        $this->_isItalic     = $fontParser->isItalic;
101        $this->_isMonospaced = $fontParser->isMonospaced;
102
103        $this->_underlinePosition  = $fontParser->underlinePosition;
104        $this->_underlineThickness = $fontParser->underlineThickness;
105        $this->_strikePosition     = $fontParser->strikePosition;
106        $this->_strikeThickness    = $fontParser->strikeThickness;
107
108        $this->_unitsPerEm = $fontParser->unitsPerEm;
109
110        $this->_ascent  = $fontParser->ascent;
111        $this->_descent = $fontParser->descent;
112        $this->_lineGap = $fontParser->lineGap;
113
114
115        $this->_cmap = $fontParser->cmap;
116
117
118        /* Resource dictionary */
119
120        $baseFont = $this->getFontName(Zend_Pdf_Font::NAME_POSTSCRIPT, 'en', 'UTF-8');
121        $this->_resource->BaseFont = new Zend_Pdf_Element_Name($baseFont);
122
123
124        /**
125         * Prepare widths array.
126         */
127        /* Constract characters widths array using font CMap and glyphs widths array */
128        $glyphWidths = $fontParser->glyphWidths;
129        $charGlyphs  = $this->_cmap->getCoveredCharactersGlyphs();
130        $charWidths  = array();
131        foreach ($charGlyphs as $charCode => $glyph) {
132            $charWidths[$charCode] = $glyphWidths[$glyph];
133        }
134        $this->_charWidths       = $charWidths;
135        $this->_missingCharWidth = $glyphWidths[0];
136
137        /* Width array optimization. Step1: extract default value */
138        $widthFrequencies = array_count_values($charWidths);
139        $defaultWidth          = null;
140        $defaultWidthFrequency = -1;
141        foreach ($widthFrequencies as $width => $frequency) {
142            if ($frequency > $defaultWidthFrequency) {
143                $defaultWidth          = $width;
144                $defaultWidthFrequency = $frequency;
145            }
146        }
147
148        // Store default value in the font dictionary
149        $this->_resource->DW = new Zend_Pdf_Element_Numeric($this->toEmSpace($defaultWidth));
150
151        // Remove characters which corresponds to default width from the widths array
152        $defWidthChars = array_keys($charWidths, $defaultWidth);
153        foreach ($defWidthChars as $charCode) {
154            unset($charWidths[$charCode]);
155        }
156
157        // Order cheracter widths aray by character codes
158        ksort($charWidths, SORT_NUMERIC);
159
160        /* Width array optimization. Step2: Compact character codes sequences */
161        $lastCharCode = -1;
162        $widthsSequences = array();
163        foreach ($charWidths as $charCode => $width) {
164            if ($lastCharCode == -1) {
165                $charCodesSequense = array();
166                $sequenceStartCode = $charCode;
167            } else if ($charCode != $lastCharCode + 1) {
168                // New chracters sequence detected
169                $widthsSequences[$sequenceStartCode] = $charCodesSequense;
170                $charCodesSequense = array();
171                $sequenceStartCode = $charCode;
172            }
173            $charCodesSequense[] = $width;
174            $lastCharCode = $charCode;
175        }
176        // Save last sequence, if widths array is not empty (it may happens for monospaced fonts)
177        if (count($charWidths) != 0) {
178            $widthsSequences[$sequenceStartCode] = $charCodesSequense;
179        }
180
181        $pdfCharsWidths = array();
182        foreach ($widthsSequences as $startCode => $widthsSequence) {
183            /* Width array optimization. Step3: Compact widths sequences */
184            $pdfWidths        = array();
185            $lastWidth        = -1;
186            $widthsInSequence = 0;
187            foreach ($widthsSequence as $width) {
188                if ($lastWidth != $width) {
189                    // New width is detected
190                    if ($widthsInSequence != 0) {
191                        // Previous width value was a part of the widths sequence. Save it as 'c_1st c_last w'.
192                        $pdfCharsWidths[] = new Zend_Pdf_Element_Numeric($startCode);                         // First character code
193                        $pdfCharsWidths[] = new Zend_Pdf_Element_Numeric($startCode + $widthsInSequence - 1); // Last character code
194                        $pdfCharsWidths[] = new Zend_Pdf_Element_Numeric($this->toEmSpace($lastWidth));       // Width
195
196                        // Reset widths sequence
197                        $startCode = $startCode + $widthsInSequence;
198                        $widthsInSequence = 0;
199                    }
200
201                    // Collect new width
202                    $pdfWidths[] = new Zend_Pdf_Element_Numeric($this->toEmSpace($width));
203
204                    $lastWidth = $width;
205                } else {
206                    // Width is equal to previous
207                    if (count($pdfWidths) != 0) {
208                        // We already have some widths collected
209                        // So, we've just detected new widths sequence
210
211                        // Remove last element from widths list, since it's a part of widths sequence
212                        array_pop($pdfWidths);
213
214                        // and write the rest if it's not empty
215                        if (count($pdfWidths) != 0) {
216                            // Save it as 'c_1st [w1 w2 ... wn]'.
217                            $pdfCharsWidths[] = new Zend_Pdf_Element_Numeric($startCode); // First character code
218                            $pdfCharsWidths[] = new Zend_Pdf_Element_Array($pdfWidths);   // Widths array
219
220                            // Reset widths collection
221                            $startCode += count($pdfWidths);
222                            $pdfWidths = array();
223                        }
224
225                        $widthsInSequence = 2;
226                    } else {
227                        // Continue widths sequence
228                        $widthsInSequence++;
229                    }
230                }
231            }
232
233            // Check if we have widths collection or widths sequence to wite it down
234            if (count($pdfWidths) != 0) {
235                // We have some widths collected
236                // Save it as 'c_1st [w1 w2 ... wn]'.
237                $pdfCharsWidths[] = new Zend_Pdf_Element_Numeric($startCode); // First character code
238                $pdfCharsWidths[] = new Zend_Pdf_Element_Array($pdfWidths);   // Widths array
239            } else if ($widthsInSequence != 0){
240                // We have widths sequence
241                // Save it as 'c_1st c_last w'.
242                $pdfCharsWidths[] = new Zend_Pdf_Element_Numeric($startCode);                         // First character code
243                $pdfCharsWidths[] = new Zend_Pdf_Element_Numeric($startCode + $widthsInSequence - 1); // Last character code
244                $pdfCharsWidths[] = new Zend_Pdf_Element_Numeric($this->toEmSpace($lastWidth));       // Width
245            }
246        }
247
248        /* Create the Zend_Pdf_Element_Array object and add it to the font's
249         * object factory and resource dictionary.
250         */
251        $widthsArrayElement = new Zend_Pdf_Element_Array($pdfCharsWidths);
252        $widthsObject = $this->_objectFactory->newObject($widthsArrayElement);
253        $this->_resource->W = $widthsObject;
254
255
256        /* CIDSystemInfo dictionary */
257        $cidSystemInfo = new Zend_Pdf_Element_Dictionary();
258        $cidSystemInfo->Registry   = new Zend_Pdf_Element_String('Adobe');
259        $cidSystemInfo->Ordering   = new Zend_Pdf_Element_String('UCS');
260        $cidSystemInfo->Supplement = new Zend_Pdf_Element_Numeric(0);
261        $cidSystemInfoObject            = $this->_objectFactory->newObject($cidSystemInfo);
262        $this->_resource->CIDSystemInfo = $cidSystemInfoObject;
263    }
264
265
266
267    /**
268     * Returns an array of glyph numbers corresponding to the Unicode characters.
269     *
270     * If a particular character doesn't exist in this font, the special 'missing
271     * character glyph' will be substituted.
272     *
273     * See also {@link glyphNumberForCharacter()}.
274     *
275     * @param array $characterCodes Array of Unicode character codes (code points).
276     * @return array Array of glyph numbers.
277     */
278    public function glyphNumbersForCharacters($characterCodes)
279    {
280        /**
281         * CIDFont object is not actually a font. It does not have an Encoding entry,
282         * it cannot be listed in the Font subdictionary of a resource dictionary, and
283         * it cannot be used as the operand of the Tf operator.
284         *
285         * Throw an exception.
286         */
287        require_once 'Zend/Pdf/Exception.php';
288        throw new Zend_Pdf_Exception('CIDFont PDF objects could not be used as the operand of the text drawing operators');
289    }
290
291    /**
292     * Returns the glyph number corresponding to the Unicode character.
293     *
294     * If a particular character doesn't exist in this font, the special 'missing
295     * character glyph' will be substituted.
296     *
297     * See also {@link glyphNumbersForCharacters()} which is optimized for bulk
298     * operations.
299     *
300     * @param integer $characterCode Unicode character code (code point).
301     * @return integer Glyph number.
302     */
303    public function glyphNumberForCharacter($characterCode)
304    {
305        /**
306         * CIDFont object is not actually a font. It does not have an Encoding entry,
307         * it cannot be listed in the Font subdictionary of a resource dictionary, and
308         * it cannot be used as the operand of the Tf operator.
309         *
310         * Throw an exception.
311         */
312        require_once 'Zend/Pdf/Exception.php';
313        throw new Zend_Pdf_Exception('CIDFont PDF objects could not be used as the operand of the text drawing operators');
314    }
315
316
317    /**
318     * Returns a number between 0 and 1 inclusive that indicates the percentage
319     * of characters in the string which are covered by glyphs in this font.
320     *
321     * Since no one font will contain glyphs for the entire Unicode character
322     * range, this method can be used to help locate a suitable font when the
323     * actual contents of the string are not known.
324     *
325     * Note that some fonts lie about the characters they support. Additionally,
326     * fonts don't usually contain glyphs for control characters such as tabs
327     * and line breaks, so it is rare that you will get back a full 1.0 score.
328     * The resulting value should be considered informational only.
329     *
330     * @param string $string
331     * @param string $charEncoding (optional) Character encoding of source text.
332     *   If omitted, uses 'current locale'.
333     * @return float
334     */
335    public function getCoveredPercentage($string, $charEncoding = '')
336    {
337        /* Convert the string to UTF-16BE encoding so we can match the string's
338         * character codes to those found in the cmap.
339         */
340        if ($charEncoding != 'UTF-16BE') {
341            $string = iconv($charEncoding, 'UTF-16BE', $string);
342        }
343
344        $charCount = iconv_strlen($string, 'UTF-16BE');
345        if ($charCount == 0) {
346            return 0;
347        }
348
349        /* Calculate the score by doing a lookup for each character.
350         */
351        $score = 0;
352        $maxIndex = strlen($string);
353        for ($i = 0; $i < $maxIndex; $i++) {
354            /**
355             * @todo Properly handle characters encoded as surrogate pairs.
356             */
357            $charCode = (ord($string[$i]) << 8) | ord($string[++$i]);
358            /* This could probably be optimized a bit with a binary search...
359             */
360            if (isset($this->_charWidths[$charCode])) {
361                $score++;
362            }
363        }
364        return $score / $charCount;
365    }
366
367    /**
368     * Returns the widths of the Chars.
369     *
370     * The widths are expressed in the font's glyph space. You are responsible
371     * for converting to user space as necessary. See {@link unitsPerEm()}.
372     *
373     * See also {@link widthForChar()}.
374     *
375     * @param array &$glyphNumbers Array of glyph numbers.
376     * @return array Array of glyph widths (integers).
377     */
378    public function widthsForChars($charCodes)
379    {
380        $widths = array();
381        foreach ($charCodes as $key => $charCode) {
382            if (!isset($this->_charWidths[$charCode])) {
383                $widths[$key] = $this->_missingCharWidth;
384            } else {
385                $widths[$key] = $this->_charWidths[$charCode];
386            }
387        }
388        return $widths;
389    }
390
391    /**
392     * Returns the width of the character.
393     *
394     * Like {@link widthsForChars()} but used for one char at a time.
395     *
396     * @param integer $charCode
397     * @return integer
398     */
399    public function widthForChar($charCode)
400    {
401        if (!isset($this->_charWidths[$charCode])) {
402            return $this->_missingCharWidth;
403        }
404        return $this->_charWidths[$charCode];
405    }
406
407    /**
408     * Returns the widths of the glyphs.
409     *
410     * @param array &$glyphNumbers Array of glyph numbers.
411     * @return array Array of glyph widths (integers).
412     * @throws Zend_Pdf_Exception
413     */
414    public function widthsForGlyphs($glyphNumbers)
415    {
416        /**
417         * CIDFont object is not actually a font. It does not have an Encoding entry,
418         * it cannot be listed in the Font subdictionary of a resource dictionary, and
419         * it cannot be used as the operand of the Tf operator.
420         *
421         * Throw an exception.
422         */
423        require_once 'Zend/Pdf/Exception.php';
424        throw new Zend_Pdf_Exception('CIDFont PDF objects could not be used as the operand of the text drawing operators');
425    }
426
427    /**
428     * Returns the width of the glyph.
429     *
430     * Like {@link widthsForGlyphs()} but used for one glyph at a time.
431     *
432     * @param integer $glyphNumber
433     * @return integer
434     * @throws Zend_Pdf_Exception
435     */
436    public function widthForGlyph($glyphNumber)
437    {
438        /**
439         * CIDFont object is not actually a font. It does not have an Encoding entry,
440         * it cannot be listed in the Font subdictionary of a resource dictionary, and
441         * it cannot be used as the operand of the Tf operator.
442         *
443         * Throw an exception.
444         */
445        require_once 'Zend/Pdf/Exception.php';
446        throw new Zend_Pdf_Exception('CIDFont PDF objects could not be used as the operand of the text drawing operators');
447    }
448
449    /**
450     * Convert string to the font encoding.
451     *
452     * @param string $string
453     * @param string $charEncoding Character encoding of source text.
454     * @return string
455     * @throws Zend_Pdf_Exception
456     *      */
457    public function encodeString($string, $charEncoding)
458    {
459        /**
460         * CIDFont object is not actually a font. It does not have an Encoding entry,
461         * it cannot be listed in the Font subdictionary of a resource dictionary, and
462         * it cannot be used as the operand of the Tf operator.
463         *
464         * Throw an exception.
465         */
466        require_once 'Zend/Pdf/Exception.php';
467        throw new Zend_Pdf_Exception('CIDFont PDF objects could not be used as the operand of the text drawing operators');
468    }
469
470    /**
471     * Convert string from the font encoding.
472     *
473     * @param string $string
474     * @param string $charEncoding Character encoding of resulting text.
475     * @return string
476     * @throws Zend_Pdf_Exception
477     */
478    public function decodeString($string, $charEncoding)
479    {
480        /**
481         * CIDFont object is not actually a font. It does not have an Encoding entry,
482         * it cannot be listed in the Font subdictionary of a resource dictionary, and
483         * it cannot be used as the operand of the Tf operator.
484         *
485         * Throw an exception.
486         */
487        require_once 'Zend/Pdf/Exception.php';
488        throw new Zend_Pdf_Exception('CIDFont PDF objects could not be used as the operand of the text drawing operators');
489    }
490}