File indexing completed on 2024-06-23 05:55:35

0001 <?php
0002 /**
0003  * Zend Framework
0004  *
0005  * LICENSE
0006  *
0007  * This source file is subject to the new BSD license that is bundled
0008  * with this package in the file LICENSE.txt.
0009  * It is also available through the world-wide-web at this URL:
0010  * http://framework.zend.com/license/new-bsd
0011  * If you did not receive a copy of the license and are unable to
0012  * obtain it through the world-wide-web, please send an email
0013  * to license@zend.com so we can send you a copy immediately.
0014  *
0015  * @category   Zend
0016  * @package    Zend_Pdf
0017  * @subpackage Fonts
0018  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0019  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0020  * @version    $Id$
0021  */
0022 
0023 /** Internally used classes */
0024 // require_once 'Zend/Pdf/Element/Array.php';
0025 // require_once 'Zend/Pdf/Element/Dictionary.php';
0026 // require_once 'Zend/Pdf/Element/Name.php';
0027 // require_once 'Zend/Pdf/Element/Numeric.php';
0028 // require_once 'Zend/Pdf/Element/String.php';
0029 
0030 
0031 /** Zend_Pdf_Resource_Font */
0032 // require_once 'Zend/Pdf/Resource/Font.php';
0033 
0034 /**
0035  * Adobe PDF CIDFont font object implementation
0036  *
0037  * A CIDFont program contains glyph descriptions that are accessed using a CID as
0038  * the character selector. There are two types of CIDFont. A Type 0 CIDFont contains
0039  * glyph descriptions based on Adobe’s Type 1 font format, whereas those in a
0040  * Type 2 CIDFont are based on the TrueType font format.
0041  *
0042  * A CIDFont dictionary is a PDF object that contains information about a CIDFont program.
0043  * Although its Type value is Font, a CIDFont is not actually a font. It does not have an Encoding
0044  * entry, it cannot be listed in the Font subdictionary of a resource dictionary, and it cannot be
0045  * used as the operand of the Tf operator. It is used only as a descendant of a Type 0 font.
0046  * The CMap in the Type 0 font is what defines the encoding that maps character codes to CIDs
0047  * in the CIDFont.
0048  *
0049  * Font objects should be normally be obtained from the factory methods
0050  * {@link Zend_Pdf_Font::fontWithName} and {@link Zend_Pdf_Font::fontWithPath}.
0051  *
0052  * @package    Zend_Pdf
0053  * @subpackage Fonts
0054  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0055  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0056  */
0057 abstract class Zend_Pdf_Resource_Font_CidFont extends Zend_Pdf_Resource_Font
0058 {
0059     /**
0060      * Object representing the font's cmap (character to glyph map).
0061      * @var Zend_Pdf_Cmap
0062      */
0063     protected $_cmap = null;
0064 
0065     /**
0066      * Array containing the widths of each character that have entries in used character map.
0067      *
0068      * @var array
0069      */
0070     protected $_charWidths = null;
0071 
0072     /**
0073      * Width for characters missed in the font
0074      *
0075      * @var integer
0076      */
0077     protected $_missingCharWidth = 0;
0078 
0079 
0080     /**
0081      * Object constructor
0082      *
0083      * @param Zend_Pdf_FileParser_Font_OpenType $fontParser Font parser object
0084      *   containing OpenType file.
0085      * @param integer $embeddingOptions Options for font embedding.
0086      * @throws Zend_Pdf_Exception
0087      */
0088     public function __construct(Zend_Pdf_FileParser_Font_OpenType $fontParser)
0089     {
0090         parent::__construct();
0091 
0092         $fontParser->parse();
0093 
0094 
0095         /* Object properties */
0096 
0097         $this->_fontNames = $fontParser->names;
0098 
0099         $this->_isBold       = $fontParser->isBold;
0100         $this->_isItalic     = $fontParser->isItalic;
0101         $this->_isMonospaced = $fontParser->isMonospaced;
0102 
0103         $this->_underlinePosition  = $fontParser->underlinePosition;
0104         $this->_underlineThickness = $fontParser->underlineThickness;
0105         $this->_strikePosition     = $fontParser->strikePosition;
0106         $this->_strikeThickness    = $fontParser->strikeThickness;
0107 
0108         $this->_unitsPerEm = $fontParser->unitsPerEm;
0109 
0110         $this->_ascent  = $fontParser->ascent;
0111         $this->_descent = $fontParser->descent;
0112         $this->_lineGap = $fontParser->lineGap;
0113 
0114 
0115         $this->_cmap = $fontParser->cmap;
0116 
0117 
0118         /* Resource dictionary */
0119 
0120         $baseFont = $this->getFontName(Zend_Pdf_Font::NAME_POSTSCRIPT, 'en', 'UTF-8');
0121         $this->_resource->BaseFont = new Zend_Pdf_Element_Name($baseFont);
0122 
0123 
0124         /**
0125          * Prepare widths array.
0126          */
0127         /* Constract characters widths array using font CMap and glyphs widths array */
0128         $glyphWidths = $fontParser->glyphWidths;
0129         $charGlyphs  = $this->_cmap->getCoveredCharactersGlyphs();
0130         $charWidths  = array();
0131         foreach ($charGlyphs as $charCode => $glyph) {
0132             if(isset($glyphWidths[$glyph]) && !is_null($glyphWidths[$glyph])) {
0133                 $charWidths[$charCode] = $glyphWidths[$glyph];
0134             }
0135         }
0136         $this->_charWidths       = $charWidths;
0137         $this->_missingCharWidth = $glyphWidths[0];
0138 
0139         /* Width array optimization. Step1: extract default value */
0140         $widthFrequencies = array_count_values($charWidths);
0141         $defaultWidth          = null;
0142         $defaultWidthFrequency = -1;
0143         foreach ($widthFrequencies as $width => $frequency) {
0144             if ($frequency > $defaultWidthFrequency) {
0145                 $defaultWidth          = $width;
0146                 $defaultWidthFrequency = $frequency;
0147             }
0148         }
0149 
0150         // Store default value in the font dictionary
0151         $this->_resource->DW = new Zend_Pdf_Element_Numeric($this->toEmSpace($defaultWidth));
0152 
0153         // Remove characters which corresponds to default width from the widths array
0154         $defWidthChars = array_keys($charWidths, $defaultWidth);
0155         foreach ($defWidthChars as $charCode) {
0156             unset($charWidths[$charCode]);
0157         }
0158 
0159         // Order cheracter widths aray by character codes
0160         ksort($charWidths, SORT_NUMERIC);
0161 
0162         /* Width array optimization. Step2: Compact character codes sequences */
0163         $lastCharCode = -1;
0164         $widthsSequences = array();
0165         foreach ($charWidths as $charCode => $width) {
0166             if ($lastCharCode == -1) {
0167                 $charCodesSequense = array();
0168                 $sequenceStartCode = $charCode;
0169             } else if ($charCode != $lastCharCode + 1) {
0170                 // New chracters sequence detected
0171                 $widthsSequences[$sequenceStartCode] = $charCodesSequense;
0172                 $charCodesSequense = array();
0173                 $sequenceStartCode = $charCode;
0174             }
0175             $charCodesSequense[] = $width;
0176             $lastCharCode = $charCode;
0177         }
0178         // Save last sequence, if widths array is not empty (it may happens for monospaced fonts)
0179         if (count($charWidths) != 0) {
0180             $widthsSequences[$sequenceStartCode] = $charCodesSequense;
0181         }
0182 
0183         $pdfCharsWidths = array();
0184         foreach ($widthsSequences as $startCode => $widthsSequence) {
0185             /* Width array optimization. Step3: Compact widths sequences */
0186             $pdfWidths        = array();
0187             $lastWidth        = -1;
0188             $widthsInSequence = 0;
0189             foreach ($widthsSequence as $width) {
0190                 if ($lastWidth != $width) {
0191                     // New width is detected
0192                     if ($widthsInSequence != 0) {
0193                         // Previous width value was a part of the widths sequence. Save it as 'c_1st c_last w'.
0194                         $pdfCharsWidths[] = new Zend_Pdf_Element_Numeric($startCode);                         // First character code
0195                         $pdfCharsWidths[] = new Zend_Pdf_Element_Numeric($startCode + $widthsInSequence - 1); // Last character code
0196                         $pdfCharsWidths[] = new Zend_Pdf_Element_Numeric($this->toEmSpace($lastWidth));       // Width
0197 
0198                         // Reset widths sequence
0199                         $startCode = $startCode + $widthsInSequence;
0200                         $widthsInSequence = 0;
0201                     }
0202 
0203                     // Collect new width
0204                     $pdfWidths[] = new Zend_Pdf_Element_Numeric($this->toEmSpace($width));
0205 
0206                     $lastWidth = $width;
0207                 } else {
0208                     // Width is equal to previous
0209                     if (count($pdfWidths) != 0) {
0210                         // We already have some widths collected
0211                         // So, we've just detected new widths sequence
0212 
0213                         // Remove last element from widths list, since it's a part of widths sequence
0214                         array_pop($pdfWidths);
0215 
0216                         // and write the rest if it's not empty
0217                         if (count($pdfWidths) != 0) {
0218                             // Save it as 'c_1st [w1 w2 ... wn]'.
0219                             $pdfCharsWidths[] = new Zend_Pdf_Element_Numeric($startCode); // First character code
0220                             $pdfCharsWidths[] = new Zend_Pdf_Element_Array($pdfWidths);   // Widths array
0221 
0222                             // Reset widths collection
0223                             $startCode += count($pdfWidths);
0224                             $pdfWidths = array();
0225                         }
0226 
0227                         $widthsInSequence = 2;
0228                     } else {
0229                         // Continue widths sequence
0230                         $widthsInSequence++;
0231                     }
0232                 }
0233             }
0234 
0235             // Check if we have widths collection or widths sequence to wite it down
0236             if (count($pdfWidths) != 0) {
0237                 // We have some widths collected
0238                 // Save it as 'c_1st [w1 w2 ... wn]'.
0239                 $pdfCharsWidths[] = new Zend_Pdf_Element_Numeric($startCode); // First character code
0240                 $pdfCharsWidths[] = new Zend_Pdf_Element_Array($pdfWidths);   // Widths array
0241             } else if ($widthsInSequence != 0){
0242                 // We have widths sequence
0243                 // Save it as 'c_1st c_last w'.
0244                 $pdfCharsWidths[] = new Zend_Pdf_Element_Numeric($startCode);                         // First character code
0245                 $pdfCharsWidths[] = new Zend_Pdf_Element_Numeric($startCode + $widthsInSequence - 1); // Last character code
0246                 $pdfCharsWidths[] = new Zend_Pdf_Element_Numeric($this->toEmSpace($lastWidth));       // Width
0247             }
0248         }
0249 
0250         /* Create the Zend_Pdf_Element_Array object and add it to the font's
0251          * object factory and resource dictionary.
0252          */
0253         $widthsArrayElement = new Zend_Pdf_Element_Array($pdfCharsWidths);
0254         $widthsObject = $this->_objectFactory->newObject($widthsArrayElement);
0255         $this->_resource->W = $widthsObject;
0256 
0257 
0258         /* CIDSystemInfo dictionary */
0259         $cidSystemInfo = new Zend_Pdf_Element_Dictionary();
0260         $cidSystemInfo->Registry   = new Zend_Pdf_Element_String('Adobe');
0261         $cidSystemInfo->Ordering   = new Zend_Pdf_Element_String('UCS');
0262         $cidSystemInfo->Supplement = new Zend_Pdf_Element_Numeric(0);
0263         $cidSystemInfoObject            = $this->_objectFactory->newObject($cidSystemInfo);
0264         $this->_resource->CIDSystemInfo = $cidSystemInfoObject;
0265     }
0266 
0267 
0268 
0269     /**
0270      * Returns an array of glyph numbers corresponding to the Unicode characters.
0271      *
0272      * If a particular character doesn't exist in this font, the special 'missing
0273      * character glyph' will be substituted.
0274      *
0275      * See also {@link glyphNumberForCharacter()}.
0276      *
0277      * @param array $characterCodes Array of Unicode character codes (code points).
0278      * @return array Array of glyph numbers.
0279      */
0280     public function glyphNumbersForCharacters($characterCodes)
0281     {
0282         /**
0283          * CIDFont object is not actually a font. It does not have an Encoding entry,
0284          * it cannot be listed in the Font subdictionary of a resource dictionary, and
0285          * it cannot be used as the operand of the Tf operator.
0286          *
0287          * Throw an exception.
0288          */
0289         // require_once 'Zend/Pdf/Exception.php';
0290         throw new Zend_Pdf_Exception('CIDFont PDF objects could not be used as the operand of the text drawing operators');
0291     }
0292 
0293     /**
0294      * Returns the glyph number corresponding to the Unicode character.
0295      *
0296      * If a particular character doesn't exist in this font, the special 'missing
0297      * character glyph' will be substituted.
0298      *
0299      * See also {@link glyphNumbersForCharacters()} which is optimized for bulk
0300      * operations.
0301      *
0302      * @param integer $characterCode Unicode character code (code point).
0303      * @return integer Glyph number.
0304      */
0305     public function glyphNumberForCharacter($characterCode)
0306     {
0307         /**
0308          * CIDFont object is not actually a font. It does not have an Encoding entry,
0309          * it cannot be listed in the Font subdictionary of a resource dictionary, and
0310          * it cannot be used as the operand of the Tf operator.
0311          *
0312          * Throw an exception.
0313          */
0314         // require_once 'Zend/Pdf/Exception.php';
0315         throw new Zend_Pdf_Exception('CIDFont PDF objects could not be used as the operand of the text drawing operators');
0316     }
0317 
0318 
0319     /**
0320      * Returns a number between 0 and 1 inclusive that indicates the percentage
0321      * of characters in the string which are covered by glyphs in this font.
0322      *
0323      * Since no one font will contain glyphs for the entire Unicode character
0324      * range, this method can be used to help locate a suitable font when the
0325      * actual contents of the string are not known.
0326      *
0327      * Note that some fonts lie about the characters they support. Additionally,
0328      * fonts don't usually contain glyphs for control characters such as tabs
0329      * and line breaks, so it is rare that you will get back a full 1.0 score.
0330      * The resulting value should be considered informational only.
0331      *
0332      * @param string $string
0333      * @param string $charEncoding (optional) Character encoding of source text.
0334      *   If omitted, uses 'current locale'.
0335      * @return float
0336      */
0337     public function getCoveredPercentage($string, $charEncoding = '')
0338     {
0339         /* Convert the string to UTF-16BE encoding so we can match the string's
0340          * character codes to those found in the cmap.
0341          */
0342         if ($charEncoding != 'UTF-16BE') {
0343             $string = iconv($charEncoding, 'UTF-16BE', $string);
0344         }
0345 
0346         $charCount = iconv_strlen($string, 'UTF-16BE');
0347         if ($charCount == 0) {
0348             return 0;
0349         }
0350 
0351         /* Calculate the score by doing a lookup for each character.
0352          */
0353         $score = 0;
0354         $maxIndex = strlen($string);
0355         for ($i = 0; $i < $maxIndex; $i++) {
0356             /**
0357              * @todo Properly handle characters encoded as surrogate pairs.
0358              */
0359             $charCode = (ord($string[$i]) << 8) | ord($string[++$i]);
0360             /* This could probably be optimized a bit with a binary search...
0361              */
0362             if (isset($this->_charWidths[$charCode])) {
0363                 $score++;
0364             }
0365         }
0366         return $score / $charCount;
0367     }
0368 
0369     /**
0370      * Returns the widths of the Chars.
0371      *
0372      * The widths are expressed in the font's glyph space. You are responsible
0373      * for converting to user space as necessary. See {@link unitsPerEm()}.
0374      *
0375      * See also {@link widthForChar()}.
0376      *
0377      * @param array &$glyphNumbers Array of glyph numbers.
0378      * @return array Array of glyph widths (integers).
0379      */
0380     public function widthsForChars($charCodes)
0381     {
0382         $widths = array();
0383         foreach ($charCodes as $key => $charCode) {
0384             if (!isset($this->_charWidths[$charCode])) {
0385                 $widths[$key] = $this->_missingCharWidth;
0386             } else {
0387                 $widths[$key] = $this->_charWidths[$charCode];
0388             }
0389         }
0390         return $widths;
0391     }
0392 
0393     /**
0394      * Returns the width of the character.
0395      *
0396      * Like {@link widthsForChars()} but used for one char at a time.
0397      *
0398      * @param integer $charCode
0399      * @return integer
0400      */
0401     public function widthForChar($charCode)
0402     {
0403         if (!isset($this->_charWidths[$charCode])) {
0404             return $this->_missingCharWidth;
0405         }
0406         return $this->_charWidths[$charCode];
0407     }
0408 
0409     /**
0410      * Returns the widths of the glyphs.
0411      *
0412      * @param array &$glyphNumbers Array of glyph numbers.
0413      * @return array Array of glyph widths (integers).
0414      * @throws Zend_Pdf_Exception
0415      */
0416     public function widthsForGlyphs($glyphNumbers)
0417     {
0418         /**
0419          * CIDFont object is not actually a font. It does not have an Encoding entry,
0420          * it cannot be listed in the Font subdictionary of a resource dictionary, and
0421          * it cannot be used as the operand of the Tf operator.
0422          *
0423          * Throw an exception.
0424          */
0425         // require_once 'Zend/Pdf/Exception.php';
0426         throw new Zend_Pdf_Exception('CIDFont PDF objects could not be used as the operand of the text drawing operators');
0427     }
0428 
0429     /**
0430      * Returns the width of the glyph.
0431      *
0432      * Like {@link widthsForGlyphs()} but used for one glyph at a time.
0433      *
0434      * @param integer $glyphNumber
0435      * @return integer
0436      * @throws Zend_Pdf_Exception
0437      */
0438     public function widthForGlyph($glyphNumber)
0439     {
0440         /**
0441          * CIDFont object is not actually a font. It does not have an Encoding entry,
0442          * it cannot be listed in the Font subdictionary of a resource dictionary, and
0443          * it cannot be used as the operand of the Tf operator.
0444          *
0445          * Throw an exception.
0446          */
0447         // require_once 'Zend/Pdf/Exception.php';
0448         throw new Zend_Pdf_Exception('CIDFont PDF objects could not be used as the operand of the text drawing operators');
0449     }
0450 
0451     /**
0452      * Convert string to the font encoding.
0453      *
0454      * @param string $string
0455      * @param string $charEncoding Character encoding of source text.
0456      * @return string
0457      * @throws Zend_Pdf_Exception
0458      *      */
0459     public function encodeString($string, $charEncoding)
0460     {
0461         /**
0462          * CIDFont object is not actually a font. It does not have an Encoding entry,
0463          * it cannot be listed in the Font subdictionary of a resource dictionary, and
0464          * it cannot be used as the operand of the Tf operator.
0465          *
0466          * Throw an exception.
0467          */
0468         // require_once 'Zend/Pdf/Exception.php';
0469         throw new Zend_Pdf_Exception('CIDFont PDF objects could not be used as the operand of the text drawing operators');
0470     }
0471 
0472     /**
0473      * Convert string from the font encoding.
0474      *
0475      * @param string $string
0476      * @param string $charEncoding Character encoding of resulting text.
0477      * @return string
0478      * @throws Zend_Pdf_Exception
0479      */
0480     public function decodeString($string, $charEncoding)
0481     {
0482         /**
0483          * CIDFont object is not actually a font. It does not have an Encoding entry,
0484          * it cannot be listed in the Font subdictionary of a resource dictionary, and
0485          * it cannot be used as the operand of the Tf operator.
0486          *
0487          * Throw an exception.
0488          */
0489         // require_once 'Zend/Pdf/Exception.php';
0490         throw new Zend_Pdf_Exception('CIDFont PDF objects could not be used as the operand of the text drawing operators');
0491     }
0492 }