Example #1
0
 /**
  * Returns a number between 0 and 1 inclusive that indicates the percentage
  * of characters in the string which are covered by glyphs in this font.
  *
  * Since no one font will contain glyphs for the entire Unicode character
  * range, this method can be used to help locate a suitable font when the
  * actual contents of the string are not known.
  *
  * Note that some fonts lie about the characters they support. Additionally,
  * fonts don't usually contain glyphs for control characters such as tabs
  * and line breaks, so it is rare that you will get back a full 1.0 score.
  * The resulting value should be considered informational only.
  *
  * @param string $string
  * @param string $charEncoding (optional) Character encoding of source text.
  *   If omitted, uses 'current locale'.
  * @return float
  */
 public function getCoveredPercentage($string, $charEncoding = '')
 {
     /* Convert the string to UTF-16BE encoding so we can match the string's
      * character codes to those found in the cmap.
      */
     if ($charEncoding != 'UTF-16BE') {
         if (PHP_OS != 'AIX') {
             // AIX doesnt know what UTF-16BE is
             $string = iconv($charEncoding, 'UTF-16BE', $string);
         }
     }
     $charCount = PHP_OS != 'AIX' ? iconv_strlen($string, 'UTF-16BE') : strlen($string);
     if ($charCount == 0) {
         return 0;
     }
     /* Fetch the covered character code list from the font's cmap.
      */
     $coveredCharacters = $this->_cmap->getCoveredCharacters();
     /* Calculate the score by doing a lookup for each character.
      */
     $score = 0;
     $maxIndex = strlen($string);
     for ($i = 0; $i < $maxIndex; $i++) {
         /**
          * @todo Properly handle characters encoded as surrogate pairs.
          */
         $charCode = ord($string[$i]) << 8 | ord($string[++$i]);
         /* This could probably be optimized a bit with a binary search...
          */
         if (in_array($charCode, $coveredCharacters)) {
             $score++;
         }
     }
     return $score / $charCount;
 }
Example #2
0
 /**
  * Object constructor
  */
 public function __construct()
 {
     parent::__construct();
     /* Object properties */
     /* The font names are stored internally as Unicode UTF-16BE-encoded
      * strings. Since this information is static, save unnecessary trips
      * through iconv() and just use pre-encoded hexidecimal strings.
      */
     $this->_fontNames[Pdf\Font::NAME_COPYRIGHT]['en'] = "Copyrigh" . "t (c) 19" . "89, 1990" . ", 1991, " . "1993, 19" . "97 Adobe" . " Systems" . " Incorpo" . "rated.  " . "All Righ" . "ts Reser" . "ved.";
     $this->_fontNames[Pdf\Font::NAME_FAMILY]['en'] = "Courier";
     $this->_fontNames[Pdf\Font::NAME_STYLE]['en'] = "Bold";
     $this->_fontNames[Pdf\Font::NAME_ID]['en'] = "43049";
     $this->_fontNames[Pdf\Font::NAME_FULL]['en'] = "Courier-" . "BoldObli" . "que Bold";
     $this->_fontNames[Pdf\Font::NAME_VERSION]['en'] = "003.000";
     $this->_fontNames[Pdf\Font::NAME_POSTSCRIPT]['en'] = "Courier-" . "BoldObli" . "que";
     $this->_isBold = true;
     $this->_isItalic = true;
     $this->_isMonospaced = true;
     $this->_underlinePosition = -100;
     $this->_underlineThickness = 50;
     $this->_strikePosition = 225;
     $this->_strikeThickness = 50;
     $this->_unitsPerEm = 1000;
     $this->_ascent = 629;
     $this->_descent = -157;
     $this->_lineGap = 414;
     /* The glyph numbers assigned here are synthetic; they do not match the
      * actual glyph numbers used by the font. This is not a big deal though
      * since this data never makes it to the PDF file. It is only used
      * internally for layout calculations.
      */
     $this->_glyphWidths = array(0x0 => 0x1f4, 0x1 => 0x258, 0x2 => 0x258, 0x3 => 0x258, 0x4 => 0x258, 0x5 => 0x258, 0x6 => 0x258, 0x7 => 0x258, 0x8 => 0x258, 0x9 => 0x258, 0xa => 0x258, 0xb => 0x258, 0xc => 0x258, 0xd => 0x258, 0xe => 0x258, 0xf => 0x258, 0x10 => 0x258, 0x11 => 0x258, 0x12 => 0x258, 0x13 => 0x258, 0x14 => 0x258, 0x15 => 0x258, 0x16 => 0x258, 0x17 => 0x258, 0x18 => 0x258, 0x19 => 0x258, 0x1a => 0x258, 0x1b => 0x258, 0x1c => 0x258, 0x1d => 0x258, 0x1e => 0x258, 0x1f => 0x258, 0x20 => 0x258, 0x21 => 0x258, 0x22 => 0x258, 0x23 => 0x258, 0x24 => 0x258, 0x25 => 0x258, 0x26 => 0x258, 0x27 => 0x258, 0x28 => 0x258, 0x29 => 0x258, 0x2a => 0x258, 0x2b => 0x258, 0x2c => 0x258, 0x2d => 0x258, 0x2e => 0x258, 0x2f => 0x258, 0x30 => 0x258, 0x31 => 0x258, 0x32 => 0x258, 0x33 => 0x258, 0x34 => 0x258, 0x35 => 0x258, 0x36 => 0x258, 0x37 => 0x258, 0x38 => 0x258, 0x39 => 0x258, 0x3a => 0x258, 0x3b => 0x258, 0x3c => 0x258, 0x3d => 0x258, 0x3e => 0x258, 0x3f => 0x258, 0x40 => 0x258, 0x41 => 0x258, 0x42 => 0x258, 0x43 => 0x258, 0x44 => 0x258, 0x45 => 0x258, 0x46 => 0x258, 0x47 => 0x258, 0x48 => 0x258, 0x49 => 0x258, 0x4a => 0x258, 0x4b => 0x258, 0x4c => 0x258, 0x4d => 0x258, 0x4e => 0x258, 0x4f => 0x258, 0x50 => 0x258, 0x51 => 0x258, 0x52 => 0x258, 0x53 => 0x258, 0x54 => 0x258, 0x55 => 0x258, 0x56 => 0x258, 0x57 => 0x258, 0x58 => 0x258, 0x59 => 0x258, 0x5a => 0x258, 0x5b => 0x258, 0x5c => 0x258, 0x5d => 0x258, 0x5e => 0x258, 0x5f => 0x258, 0x60 => 0x258, 0x61 => 0x258, 0x62 => 0x258, 0x63 => 0x258, 0x64 => 0x258, 0x65 => 0x258, 0x66 => 0x258, 0x67 => 0x258, 0x68 => 0x258, 0x69 => 0x258, 0x6a => 0x258, 0x6b => 0x258, 0x6c => 0x258, 0x6d => 0x258, 0x6e => 0x258, 0x6f => 0x258, 0x70 => 0x258, 0x71 => 0x258, 0x72 => 0x258, 0x73 => 0x258, 0x74 => 0x258, 0x75 => 0x258, 0x76 => 0x258, 0x77 => 0x258, 0x78 => 0x258, 0x79 => 0x258, 0x7a => 0x258, 0x7b => 0x258, 0x7c => 0x258, 0x7d => 0x258, 0x7e => 0x258, 0x7f => 0x258, 0x80 => 0x258, 0x81 => 0x258, 0x82 => 0x258, 0x83 => 0x258, 0x84 => 0x258, 0x85 => 0x258, 0x86 => 0x258, 0x87 => 0x258, 0x88 => 0x258, 0x89 => 0x258, 0x8a => 0x258, 0x8b => 0x258, 0x8c => 0x258, 0x8d => 0x258, 0x8e => 0x258, 0x8f => 0x258, 0x90 => 0x258, 0x91 => 0x258, 0x92 => 0x258, 0x93 => 0x258, 0x94 => 0x258, 0x95 => 0x258, 0x96 => 0x258, 0x97 => 0x258, 0x98 => 0x258, 0x99 => 0x258, 0x9a => 0x258, 0x9b => 0x258, 0x9c => 0x258, 0x9d => 0x258, 0x9e => 0x258, 0x9f => 0x258, 0xa0 => 0x258, 0xa1 => 0x258, 0xa2 => 0x258, 0xa3 => 0x258, 0xa4 => 0x258, 0xa5 => 0x258, 0xa6 => 0x258, 0xa7 => 0x258, 0xa8 => 0x258, 0xa9 => 0x258, 0xaa => 0x258, 0xab => 0x258, 0xac => 0x258, 0xad => 0x258, 0xae => 0x258, 0xaf => 0x258, 0xb0 => 0x258, 0xb1 => 0x258, 0xb2 => 0x258, 0xb3 => 0x258, 0xb4 => 0x258, 0xb5 => 0x258, 0xb6 => 0x258, 0xb7 => 0x258, 0xb8 => 0x258, 0xb9 => 0x258, 0xba => 0x258, 0xbb => 0x258, 0xbc => 0x258, 0xbd => 0x258, 0xbe => 0x258, 0xbf => 0x258, 0xc0 => 0x258, 0xc1 => 0x258, 0xc2 => 0x258, 0xc3 => 0x258, 0xc4 => 0x258, 0xc5 => 0x258, 0xc6 => 0x258, 0xc7 => 0x258, 0xc8 => 0x258, 0xc9 => 0x258, 0xca => 0x258, 0xcb => 0x258, 0xcc => 0x258, 0xcd => 0x258, 0xce => 0x258, 0xcf => 0x258, 0xd0 => 0x258, 0xd1 => 0x258, 0xd2 => 0x258, 0xd3 => 0x258, 0xd4 => 0x258, 0xd5 => 0x258, 0xd6 => 0x258, 0xd7 => 0x258, 0xd8 => 0x258, 0xd9 => 0x258, 0xda => 0x258, 0xdb => 0x258, 0xdc => 0x258, 0xdd => 0x258, 0xde => 0x258, 0xdf => 0x258, 0xe0 => 0x258, 0xe1 => 0x258, 0xe2 => 0x258, 0xe3 => 0x258, 0xe4 => 0x258, 0xe5 => 0x258, 0xe6 => 0x258, 0xe7 => 0x258, 0xe8 => 0x258, 0xe9 => 0x258, 0xea => 0x258, 0xeb => 0x258, 0xec => 0x258, 0xed => 0x258, 0xee => 0x258, 0xef => 0x258, 0xf0 => 0x258, 0xf1 => 0x258, 0xf2 => 0x258, 0xf3 => 0x258, 0xf4 => 0x258, 0xf5 => 0x258, 0xf6 => 0x258, 0xf7 => 0x258, 0xf8 => 0x258, 0xf9 => 0x258, 0xfa => 0x258, 0xfb => 0x258, 0xfc => 0x258, 0xfd => 0x258, 0xfe => 0x258, 0xff => 0x258, 0x100 => 0x258, 0x101 => 0x258, 0x102 => 0x258, 0x103 => 0x258, 0x104 => 0x258, 0x105 => 0x258, 0x106 => 0x258, 0x107 => 0x258, 0x108 => 0x258, 0x109 => 0x258, 0x10a => 0x258, 0x10b => 0x258, 0x10c => 0x258, 0x10d => 0x258, 0x10e => 0x258, 0x10f => 0x258, 0x110 => 0x258, 0x111 => 0x258, 0x112 => 0x258, 0x113 => 0x258, 0x114 => 0x258, 0x115 => 0x258, 0x116 => 0x258, 0x117 => 0x258, 0x118 => 0x258, 0x119 => 0x258, 0x11a => 0x258, 0x11b => 0x258, 0x11c => 0x258, 0x11d => 0x258, 0x11e => 0x258, 0x11f => 0x258, 0x120 => 0x258, 0x121 => 0x258, 0x122 => 0x258, 0x123 => 0x258, 0x124 => 0x258, 0x125 => 0x258, 0x126 => 0x258, 0x127 => 0x258, 0x128 => 0x258, 0x129 => 0x258, 0x12a => 0x258, 0x12b => 0x258, 0x12c => 0x258, 0x12d => 0x258, 0x12e => 0x258, 0x12f => 0x258, 0x130 => 0x258, 0x131 => 0x258, 0x132 => 0x258, 0x133 => 0x258, 0x134 => 0x258, 0x135 => 0x258, 0x136 => 0x258, 0x137 => 0x258, 0x138 => 0x258, 0x139 => 0x258, 0x13a => 0x258, 0x13b => 0x258);
     /* The cmap table is similarly synthesized.
      */
     $cmapData = array(0x20 => 0x1, 0x21 => 0x2, 0x22 => 0x3, 0x23 => 0x4, 0x24 => 0x5, 0x25 => 0x6, 0x26 => 0x7, 0x2019 => 0x8, 0x28 => 0x9, 0x29 => 0xa, 0x2a => 0xb, 0x2b => 0xc, 0x2c => 0xd, 0x2d => 0xe, 0x2e => 0xf, 0x2f => 0x10, 0x30 => 0x11, 0x31 => 0x12, 0x32 => 0x13, 0x33 => 0x14, 0x34 => 0x15, 0x35 => 0x16, 0x36 => 0x17, 0x37 => 0x18, 0x38 => 0x19, 0x39 => 0x1a, 0x3a => 0x1b, 0x3b => 0x1c, 0x3c => 0x1d, 0x3d => 0x1e, 0x3e => 0x1f, 0x3f => 0x20, 0x40 => 0x21, 0x41 => 0x22, 0x42 => 0x23, 0x43 => 0x24, 0x44 => 0x25, 0x45 => 0x26, 0x46 => 0x27, 0x47 => 0x28, 0x48 => 0x29, 0x49 => 0x2a, 0x4a => 0x2b, 0x4b => 0x2c, 0x4c => 0x2d, 0x4d => 0x2e, 0x4e => 0x2f, 0x4f => 0x30, 0x50 => 0x31, 0x51 => 0x32, 0x52 => 0x33, 0x53 => 0x34, 0x54 => 0x35, 0x55 => 0x36, 0x56 => 0x37, 0x57 => 0x38, 0x58 => 0x39, 0x59 => 0x3a, 0x5a => 0x3b, 0x5b => 0x3c, 0x5c => 0x3d, 0x5d => 0x3e, 0x5e => 0x3f, 0x5f => 0x40, 0x2018 => 0x41, 0x61 => 0x42, 0x62 => 0x43, 0x63 => 0x44, 0x64 => 0x45, 0x65 => 0x46, 0x66 => 0x47, 0x67 => 0x48, 0x68 => 0x49, 0x69 => 0x4a, 0x6a => 0x4b, 0x6b => 0x4c, 0x6c => 0x4d, 0x6d => 0x4e, 0x6e => 0x4f, 0x6f => 0x50, 0x70 => 0x51, 0x71 => 0x52, 0x72 => 0x53, 0x73 => 0x54, 0x74 => 0x55, 0x75 => 0x56, 0x76 => 0x57, 0x77 => 0x58, 0x78 => 0x59, 0x79 => 0x5a, 0x7a => 0x5b, 0x7b => 0x5c, 0x7c => 0x5d, 0x7d => 0x5e, 0x7e => 0x5f, 0xa1 => 0x60, 0xa2 => 0x61, 0xa3 => 0x62, 0x2044 => 0x63, 0xa5 => 0x64, 0x192 => 0x65, 0xa7 => 0x66, 0xa4 => 0x67, 0x27 => 0x68, 0x201c => 0x69, 0xab => 0x6a, 0x2039 => 0x6b, 0x203a => 0x6c, 0xfb01 => 0x6d, 0xfb02 => 0x6e, 0x2013 => 0x6f, 0x2020 => 0x70, 0x2021 => 0x71, 0xb7 => 0x72, 0xb6 => 0x73, 0x2022 => 0x74, 0x201a => 0x75, 0x201e => 0x76, 0x201d => 0x77, 0xbb => 0x78, 0x2026 => 0x79, 0x2030 => 0x7a, 0xbf => 0x7b, 0x60 => 0x7c, 0xb4 => 0x7d, 0x2c6 => 0x7e, 0x2dc => 0x7f, 0xaf => 0x80, 0x2d8 => 0x81, 0x2d9 => 0x82, 0xa8 => 0x83, 0x2da => 0x84, 0xb8 => 0x85, 0x2dd => 0x86, 0x2db => 0x87, 0x2c7 => 0x88, 0x2014 => 0x89, 0xc6 => 0x8a, 0xaa => 0x8b, 0x141 => 0x8c, 0xd8 => 0x8d, 0x152 => 0x8e, 0xba => 0x8f, 0xe6 => 0x90, 0x131 => 0x91, 0x142 => 0x92, 0xf8 => 0x93, 0x153 => 0x94, 0xdf => 0x95, 0xcf => 0x96, 0xe9 => 0x97, 0x103 => 0x98, 0x171 => 0x99, 0x11b => 0x9a, 0x178 => 0x9b, 0xf7 => 0x9c, 0xdd => 0x9d, 0xc2 => 0x9e, 0xe1 => 0x9f, 0xdb => 0xa0, 0xfd => 0xa1, 0x219 => 0xa2, 0xea => 0xa3, 0x16e => 0xa4, 0xdc => 0xa5, 0x105 => 0xa6, 0xda => 0xa7, 0x173 => 0xa8, 0xcb => 0xa9, 0x110 => 0xaa, 0xf6c3 => 0xab, 0xa9 => 0xac, 0x112 => 0xad, 0x10d => 0xae, 0xe5 => 0xaf, 0x145 => 0xb0, 0x13a => 0xb1, 0xe0 => 0xb2, 0x162 => 0xb3, 0x106 => 0xb4, 0xe3 => 0xb5, 0x116 => 0xb6, 0x161 => 0xb7, 0x15f => 0xb8, 0xed => 0xb9, 0x25ca => 0xba, 0x158 => 0xbb, 0x122 => 0xbc, 0xfb => 0xbd, 0xe2 => 0xbe, 0x100 => 0xbf, 0x159 => 0xc0, 0xe7 => 0xc1, 0x17b => 0xc2, 0xde => 0xc3, 0x14c => 0xc4, 0x154 => 0xc5, 0x15a => 0xc6, 0x10f => 0xc7, 0x16a => 0xc8, 0x16f => 0xc9, 0xb3 => 0xca, 0xd2 => 0xcb, 0xc0 => 0xcc, 0x102 => 0xcd, 0xd7 => 0xce, 0xfa => 0xcf, 0x164 => 0xd0, 0x2202 => 0xd1, 0xff => 0xd2, 0x143 => 0xd3, 0xee => 0xd4, 0xca => 0xd5, 0xe4 => 0xd6, 0xeb => 0xd7, 0x107 => 0xd8, 0x144 => 0xd9, 0x16b => 0xda, 0x147 => 0xdb, 0xcd => 0xdc, 0xb1 => 0xdd, 0xa6 => 0xde, 0xae => 0xdf, 0x11e => 0xe0, 0x130 => 0xe1, 0x2211 => 0xe2, 0xc8 => 0xe3, 0x155 => 0xe4, 0x14d => 0xe5, 0x179 => 0xe6, 0x17d => 0xe7, 0x2265 => 0xe8, 0xd0 => 0xe9, 0xc7 => 0xea, 0x13c => 0xeb, 0x165 => 0xec, 0x119 => 0xed, 0x172 => 0xee, 0xc1 => 0xef, 0xc4 => 0xf0, 0xe8 => 0xf1, 0x17a => 0xf2, 0x12f => 0xf3, 0xd3 => 0xf4, 0xf3 => 0xf5, 0x101 => 0xf6, 0x15b => 0xf7, 0xef => 0xf8, 0xd4 => 0xf9, 0xd9 => 0xfa, 0x2206 => 0xfb, 0xfe => 0xfc, 0xb2 => 0xfd, 0xd6 => 0xfe, 0xb5 => 0xff, 0xec => 0x100, 0x151 => 0x101, 0x118 => 0x102, 0x111 => 0x103, 0xbe => 0x104, 0x15e => 0x105, 0x13e => 0x106, 0x136 => 0x107, 0x139 => 0x108, 0x2122 => 0x109, 0x117 => 0x10a, 0xcc => 0x10b, 0x12a => 0x10c, 0x13d => 0x10d, 0xbd => 0x10e, 0x2264 => 0x10f, 0xf4 => 0x110, 0xf1 => 0x111, 0x170 => 0x112, 0xc9 => 0x113, 0x113 => 0x114, 0x11f => 0x115, 0xbc => 0x116, 0x160 => 0x117, 0x218 => 0x118, 0x150 => 0x119, 0xb0 => 0x11a, 0xf2 => 0x11b, 0x10c => 0x11c, 0xf9 => 0x11d, 0x221a => 0x11e, 0x10e => 0x11f, 0x157 => 0x120, 0xd1 => 0x121, 0xf5 => 0x122, 0x156 => 0x123, 0x13b => 0x124, 0xc3 => 0x125, 0x104 => 0x126, 0xc5 => 0x127, 0xd5 => 0x128, 0x17c => 0x129, 0x11a => 0x12a, 0x12e => 0x12b, 0x137 => 0x12c, 0x2212 => 0x12d, 0xce => 0x12e, 0x148 => 0x12f, 0x163 => 0x130, 0xac => 0x131, 0xf6 => 0x132, 0xfc => 0x133, 0x2260 => 0x134, 0x123 => 0x135, 0xf0 => 0x136, 0x17e => 0x137, 0x146 => 0x138, 0xb9 => 0x139, 0x12b => 0x13a, 0x20ac => 0x13b);
     $this->_cmap = Cmap\AbstractCmap::cmapWithTypeData(Cmap\AbstractCmap::TYPE_BYTE_ENCODING_STATIC, $cmapData);
     /* Resource dictionary */
     /* The resource dictionary for the standard fonts is sparse because PDF
      * viewers already have all of the metrics data. We only need to provide
      * the font name and encoding method.
      */
     $this->_resource->BaseFont = new Pdf\InternalType\NameObject('Courier-BoldOblique');
 }
Example #3
0
 /**
  * Object constructor
  *
  * @param \ZendPdf\BinaryParser\Font\OpenType\AbstractOpenType $fontParser Font parser object
  *   containing OpenType file.
  * @param integer $embeddingOptions Options for font embedding.
  * @throws \ZendPdf\Exception\ExceptionInterface
  */
 public function __construct(Pdf\BinaryParser\Font\OpenType\AbstractOpenType $fontParser)
 {
     parent::__construct();
     $fontParser->parse();
     /* Object properties */
     $this->_fontNames = $fontParser->names;
     $this->_isBold = $fontParser->isBold;
     $this->_isItalic = $fontParser->isItalic;
     $this->_isMonospaced = $fontParser->isMonospaced;
     $this->_underlinePosition = $fontParser->underlinePosition;
     $this->_underlineThickness = $fontParser->underlineThickness;
     $this->_strikePosition = $fontParser->strikePosition;
     $this->_strikeThickness = $fontParser->strikeThickness;
     $this->_unitsPerEm = $fontParser->unitsPerEm;
     $this->_ascent = $fontParser->ascent;
     $this->_descent = $fontParser->descent;
     $this->_lineGap = $fontParser->lineGap;
     $this->_cmap = $fontParser->cmap;
     /* Resource dictionary */
     $baseFont = $this->getFontName(Pdf\Font::NAME_POSTSCRIPT, 'en', 'UTF-8');
     $this->_resource->BaseFont = new InternalType\NameObject($baseFont);
     /**
      * Prepare widths array.
      */
     /* Constract characters widths array using font CMap and glyphs widths array */
     $glyphWidths = $fontParser->glyphWidths;
     $charGlyphs = $this->_cmap->getCoveredCharactersGlyphs();
     $charWidths = array();
     foreach ($charGlyphs as $charCode => $glyph) {
         $charWidths[$charCode] = $glyphWidths[$glyph];
     }
     $this->_charWidths = $charWidths;
     $this->_missingCharWidth = $glyphWidths[0];
     /* Width array optimization. Step1: extract default value */
     $widthFrequencies = array_count_values($charWidths);
     $defaultWidth = null;
     $defaultWidthFrequency = -1;
     foreach ($widthFrequencies as $width => $frequency) {
         if ($frequency > $defaultWidthFrequency) {
             $defaultWidth = $width;
             $defaultWidthFrequency = $frequency;
         }
     }
     // Store default value in the font dictionary
     $this->_resource->DW = new InternalType\NumericObject($this->toEmSpace($defaultWidth));
     // Remove characters which corresponds to default width from the widths array
     $defWidthChars = array_keys($charWidths, $defaultWidth);
     foreach ($defWidthChars as $charCode) {
         unset($charWidths[$charCode]);
     }
     // Order cheracter widths aray by character codes
     ksort($charWidths, SORT_NUMERIC);
     /* Width array optimization. Step2: Compact character codes sequences */
     $lastCharCode = -1;
     $widthsSequences = array();
     foreach ($charWidths as $charCode => $width) {
         if ($lastCharCode == -1) {
             $charCodesSequense = array();
             $sequenceStartCode = $charCode;
         } elseif ($charCode != $lastCharCode + 1) {
             // New chracters sequence detected
             $widthsSequences[$sequenceStartCode] = $charCodesSequense;
             $charCodesSequense = array();
             $sequenceStartCode = $charCode;
         }
         $charCodesSequense[] = $width;
         $lastCharCode = $charCode;
     }
     // Save last sequence, if widths array is not empty (it may happens for monospaced fonts)
     if (count($charWidths) != 0) {
         $widthsSequences[$sequenceStartCode] = $charCodesSequense;
     }
     $pdfCharsWidths = array();
     foreach ($widthsSequences as $startCode => $widthsSequence) {
         /* Width array optimization. Step3: Compact widths sequences */
         $pdfWidths = array();
         $lastWidth = -1;
         $widthsInSequence = 0;
         foreach ($widthsSequence as $width) {
             if ($lastWidth != $width) {
                 // New width is detected
                 if ($widthsInSequence != 0) {
                     // Previous width value was a part of the widths sequence. Save it as 'c_1st c_last w'.
                     $pdfCharsWidths[] = new InternalType\NumericObject($startCode);
                     // First character code
                     $pdfCharsWidths[] = new InternalType\NumericObject($startCode + $widthsInSequence - 1);
                     // Last character code
                     $pdfCharsWidths[] = new InternalType\NumericObject($this->toEmSpace($lastWidth));
                     // Width
                     // Reset widths sequence
                     $startCode = $startCode + $widthsInSequence;
                     $widthsInSequence = 0;
                 }
                 // Collect new width
                 $pdfWidths[] = new InternalType\NumericObject($this->toEmSpace($width));
                 $lastWidth = $width;
             } else {
                 // Width is equal to previous
                 if (count($pdfWidths) != 0) {
                     // We already have some widths collected
                     // So, we've just detected new widths sequence
                     // Remove last element from widths list, since it's a part of widths sequence
                     array_pop($pdfWidths);
                     // and write the rest if it's not empty
                     if (count($pdfWidths) != 0) {
                         // Save it as 'c_1st [w1 w2 ... wn]'.
                         $pdfCharsWidths[] = new InternalType\NumericObject($startCode);
                         // First character code
                         $pdfCharsWidths[] = new InternalType\ArrayObject($pdfWidths);
                         // Widths array
                         // Reset widths collection
                         $startCode += count($pdfWidths);
                         $pdfWidths = array();
                     }
                     $widthsInSequence = 2;
                 } else {
                     // Continue widths sequence
                     $widthsInSequence++;
                 }
             }
         }
         // Check if we have widths collection or widths sequence to wite it down
         if (count($pdfWidths) != 0) {
             // We have some widths collected
             // Save it as 'c_1st [w1 w2 ... wn]'.
             $pdfCharsWidths[] = new InternalType\NumericObject($startCode);
             // First character code
             $pdfCharsWidths[] = new InternalType\ArrayObject($pdfWidths);
             // Widths array
         } elseif ($widthsInSequence != 0) {
             // We have widths sequence
             // Save it as 'c_1st c_last w'.
             $pdfCharsWidths[] = new InternalType\NumericObject($startCode);
             // First character code
             $pdfCharsWidths[] = new InternalType\NumericObject($startCode + $widthsInSequence - 1);
             // Last character code
             $pdfCharsWidths[] = new InternalType\NumericObject($this->toEmSpace($lastWidth));
             // Width
         }
     }
     /* Create the \ZendPdf\InternalType\ArrayObject object and add it to the font's
      * object factory and resource dictionary.
      */
     $widthsArrayElement = new InternalType\ArrayObject($pdfCharsWidths);
     $widthsObject = $this->_objectFactory->newObject($widthsArrayElement);
     $this->_resource->W = $widthsObject;
     /* CIDSystemInfo dictionary */
     $cidSystemInfo = new InternalType\DictionaryObject();
     $cidSystemInfo->Registry = new InternalType\StringObject('Adobe');
     $cidSystemInfo->Ordering = new InternalType\StringObject('UCS');
     $cidSystemInfo->Supplement = new InternalType\NumericObject(0);
     $cidSystemInfoObject = $this->_objectFactory->newObject($cidSystemInfo);
     $this->_resource->CIDSystemInfo = $cidSystemInfoObject;
 }
Example #4
0
 /**
  * Parses the OpenType cmap (Character to Glyph Mapping) table.
  *
  * The cmap table provides the maps from character codes to font glyphs.
  * There are usually at least two character maps in a font: Microsoft Unicode
  * and Macintosh Roman. For very complex fonts, there may also be mappings
  * for the characters in the Unicode Surrogates Area, which are UCS-4
  * characters.
  *
  * @todo Need to rework the selection logic for picking a subtable. We should
  *   have an explicit list of preferences, followed by a list of those that
  *   are tolerable. Most specifically, since everything above this layer deals
  *   in Unicode, we need to be sure to only accept format 0 MacRoman tables.
  *
  * @throws \ZendPdf\Exception\ExceptionInterface
  */
 protected function _parseCMapTable()
 {
     $this->_jumpToTable('cmap');
     $baseOffset = $this->_tableDirectory['cmap']['offset'];
     /* We only understand version 0 tables.
      */
     $tableVersion = $this->readUInt(2);
     if ($tableVersion != 0) {
         throw new Exception\CorruptedFontException("Unable to read version {$tableVersion} table");
     }
     $this->_debugLog('Version %d table', $tableVersion);
     $subtableCount = $this->readUInt(2);
     $this->_debugLog('%d subtables', $subtableCount);
     /* Like the name table, there may be many different encoding subtables
      * present. Ideally, we are looking for an acceptable Unicode table.
      */
     $subtables = array();
     for ($subtableIndex = 0; $subtableIndex < $subtableCount; $subtableIndex++) {
         $platformID = $this->readUInt(2);
         $encodingID = $this->readUInt(2);
         if (!($platformID == 0 && $encodingID == 3 || $platformID == 0 && $encodingID == 0 || $platformID == 3 && $encodingID == 1 || $platformID == 1 && $encodingID == 0)) {
             $this->_debugLog('Unsupported encoding: platformID: %d; encodingID: %d; skipping', $platformID, $encodingID);
             $this->skipBytes(4);
             continue;
         }
         $subtableOffset = $this->readUInt(4);
         if ($subtableOffset < 0) {
             // Sanity check for 4-byte unsigned on 32-bit platform
             $this->_debugLog('Offset 0x%x out of range for platformID: %d; skipping', $subtableOffset, $platformID);
             continue;
         }
         $this->_debugLog('Found subtable; platformID: %d; encodingID: %d; offset: 0x%x (0x%x)', $platformID, $encodingID, $baseOffset + $subtableOffset, $subtableOffset);
         $subtables[$platformID][$encodingID][] = $subtableOffset;
     }
     /* In preferred order, find a subtable to use.
      */
     $offsets = array();
     /* Unicode 2.0 or later semantics
      */
     if (isset($subtables[0][3])) {
         foreach ($subtables[0][3] as $offset) {
             $offsets[] = $offset;
         }
     }
     /* Unicode default semantics
      */
     if (isset($subtables[0][0])) {
         foreach ($subtables[0][0] as $offset) {
             $offsets[] = $offset;
         }
     }
     /* Microsoft Unicode
      */
     if (isset($subtables[3][1])) {
         foreach ($subtables[3][1] as $offset) {
             $offsets[] = $offset;
         }
     }
     /* Mac Roman.
      */
     if (isset($subtables[1][0])) {
         foreach ($subtables[1][0] as $offset) {
             $offsets[] = $offset;
         }
     }
     $cmapType = -1;
     foreach ($offsets as $offset) {
         $cmapOffset = $baseOffset + $offset;
         $this->moveToOffset($cmapOffset);
         $format = $this->readUInt(2);
         $language = -1;
         switch ($format) {
             case 0x0:
                 $cmapLength = $this->readUInt(2);
                 $language = $this->readUInt(2);
                 if ($language != 0) {
                     $this->_debugLog('Type 0 cmap tables must be language-independent;' . ' language: %d; skipping', $language);
                     continue;
                 }
                 break;
             case 0x4:
                 // break intentionally omitted
             // break intentionally omitted
             case 0x6:
                 $cmapLength = $this->readUInt(2);
                 $language = $this->readUInt(2);
                 if ($language != 0) {
                     $this->_debugLog('Warning: cmap tables must be language-independent - this font' . ' may not work properly; language: %d', $language);
                 }
                 break;
             case 0x2:
                 // break intentionally omitted
             // break intentionally omitted
             case 0x8:
                 // break intentionally omitted
             // break intentionally omitted
             case 0xa:
                 // break intentionally omitted
             // break intentionally omitted
             case 0xc:
                 $this->_debugLog('Format: 0x%x currently unsupported; skipping', $format);
                 continue;
                 //$this->skipBytes(2);
                 //$cmapLength = $this->readUInt(4);
                 //$language = $this->readUInt(4);
                 //if ($language != 0) {
                 //    $this->_debugLog('Warning: cmap tables must be language-independent - this font'
                 //                     . ' may not work properly; language: %d', $language);
                 //}
                 //break;
             //$this->skipBytes(2);
             //$cmapLength = $this->readUInt(4);
             //$language = $this->readUInt(4);
             //if ($language != 0) {
             //    $this->_debugLog('Warning: cmap tables must be language-independent - this font'
             //                     . ' may not work properly; language: %d', $language);
             //}
             //break;
             default:
                 $this->_debugLog('Unknown subtable format: 0x%x; skipping', $format);
                 continue;
         }
         $cmapType = $format;
         break;
     }
     if ($cmapType == -1) {
         throw new Exception\CorruptedFontException('Unable to find usable cmap table');
     }
     /* Now extract the subtable data and create a \ZendPdf\Cmap\AbstractCmap object.
      */
     $this->_debugLog('Using cmap type %d; offset: 0x%x; length: %d', $cmapType, $cmapOffset, $cmapLength);
     $this->moveToOffset($cmapOffset);
     $cmapData = $this->readBytes($cmapLength);
     $this->cmap = Cmap\AbstractCmap::cmapWithTypeData($cmapType, $cmapData);
 }