web/enmi/Zend/Pdf/Resource/Font/CidFont.php
changeset 19 1c2f13fd785c
parent 0 4eba9c11703f
equal deleted inserted replaced
18:bd595ad770fc 19:1c2f13fd785c
       
     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 */
       
    24 require_once 'Zend/Pdf/Element/Array.php';
       
    25 require_once 'Zend/Pdf/Element/Dictionary.php';
       
    26 require_once 'Zend/Pdf/Element/Name.php';
       
    27 require_once 'Zend/Pdf/Element/Numeric.php';
       
    28 require_once 'Zend/Pdf/Element/String.php';
       
    29 
       
    30 
       
    31 /** Zend_Pdf_Resource_Font */
       
    32 require_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’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  */
       
    57 abstract 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 }