Magento 2 Documentation  2.3
Documentation for Magento 2 CMS v2.3 (December 2018)
OpenType.php
Go to the documentation of this file.
1 <?php
24 #require_once 'Zend/Pdf/FileParser/Font.php';
25 
52 {
53  /**** Instance Variables ****/
54 
55 
61  protected $_scalerType = 0;
62 
67  protected $_tableDirectory = array();
68 
69 
70 
71  /**** Public Interface ****/
72 
73 
74  /* Semi-Concrete Class Implementation */
75 
84  public function screen()
85  {
86  if ($this->_isScreened) {
87  return;
88  }
89  $this->_readScalerType();
90  }
91 
100  public function parse()
101  {
102  if ($this->_isParsed) {
103  return;
104  }
105 
106  /* Screen the font file first, if it hasn't been done yet.
107  */
108  $this->screen();
109 
110  /* Start by reading the table directory.
111  */
112  $this->_parseTableDirectory();
113 
114  /* Then parse all of the required tables.
115  */
116  $this->_parseHeadTable();
117  $this->_parseNameTable();
118  $this->_parsePostTable();
119  $this->_parseHheaTable();
120  $this->_parseMaxpTable();
121  $this->_parseOs2Table();
122  $this->_parseHmtxTable();
123  $this->_parseCmapTable();
124 
125  /* If present, parse the optional tables.
126  */
132  }
133 
134 
135 
136  /**** Internal Methods ****/
137 
138 
139  /* Parser Methods */
140 
149  protected function _parseTableDirectory()
150  {
151  $this->moveToOffset(4);
152 
153  $tableCount = $this->readUInt(2);
154  $this->_debugLog('%d tables', $tableCount);
155 
156  /* Sanity check, in case we're not actually reading a OpenType file and
157  * the first four bytes coincidentally matched an OpenType signature in
158  * screen() above.
159  *
160  * There are at minimum 7 required tables: cmap, head, hhea, hmtx, maxp,
161  * name, and post. In the current OpenType standard, only 32 table types
162  * are defined, so use 50 as a practical limit.
163  */
164  if (($tableCount < 7) || ($tableCount > 50)) {
165  #require_once 'Zend/Pdf/Exception.php';
166  throw new Zend_Pdf_Exception('Table count not within expected range',
168  }
169 
170  /* Skip the next 6 bytes, which contain values to aid a binary search.
171  */
172  $this->skipBytes(6);
173 
174  /* The directory contains four values: the name of the table, checksum,
175  * offset to the table from the beginning of the font, and actual data
176  * length of the table.
177  */
178  for ($tableIndex = 0; $tableIndex < $tableCount; $tableIndex++) {
179  $tableName = $this->readBytes(4);
180 
181  /* We ignore the checksum here for two reasons: First, the PDF viewer
182  * will do this later anyway; Second, calculating the checksum would
183  * require unsigned integers, which PHP does not currently provide.
184  * We may revisit this in the future.
185  */
186  $this->skipBytes(4);
187 
188  $tableOffset = $this->readUInt(4);
189  $tableLength = $this->readUInt(4);
190  $this->_debugLog('%s offset: 0x%x; length: %d', $tableName, $tableOffset, $tableLength);
191 
192  /* Sanity checks for offset and length values.
193  */
194  $fileSize = $this->_dataSource->getSize();
195  if (($tableOffset < 0) || ($tableOffset > $fileSize)) {
196  #require_once 'Zend/Pdf/Exception.php';
197  throw new Zend_Pdf_Exception("Table offset ($tableOffset) not within expected range",
199  }
200  if (($tableLength < 0) || (($tableOffset + $tableLength) > $fileSize)) {
201  #require_once 'Zend/Pdf/Exception.php';
202  throw new Zend_Pdf_Exception("Table length ($tableLength) not within expected range",
204  }
205 
206  $this->_tableDirectory[$tableName]['offset'] = $tableOffset;
207  $this->_tableDirectory[$tableName]['length'] = $tableLength;
208  }
209  }
210 
211 
220  protected function _parseHeadTable()
221  {
222  $this->_jumpToTable('head');
223 
224  /* We can read any version 1 table.
225  */
226  $tableVersion = $this->_readTableVersion(1, 1);
227 
228  /* Skip the font revision number and checksum adjustment.
229  */
230  $this->skipBytes(8);
231 
232  $magicNumber = $this->readUInt(4);
233  if ($magicNumber != 0x5f0f3cf5) {
234  #require_once 'Zend/Pdf/Exception.php';
235  throw new Zend_Pdf_Exception('Wrong magic number. Expected: 0x5f0f3cf5; actual: '
236  . sprintf('%x', $magicNumber),
238  }
239 
240  /* Most of the flags we ignore, but there are a few values that are
241  * useful for our layout routines.
242  */
243  $flags = $this->readUInt(2);
244  $this->baselineAtZero = $this->isBitSet(0, $flags);
245  $this->useIntegerScaling = $this->isBitSet(3, $flags);
246 
247  $this->unitsPerEm = $this->readUInt(2);
248  $this->_debugLog('Units per em: %d', $this->unitsPerEm);
249 
250  /* Skip creation and modification date/time.
251  */
252  $this->skipBytes(16);
253 
254  $this->xMin = $this->readInt(2);
255  $this->yMin = $this->readInt(2);
256  $this->xMax = $this->readInt(2);
257  $this->yMax = $this->readInt(2);
258  $this->_debugLog('Font bounding box: %d %d %d %d',
259  $this->xMin, $this->yMin, $this->xMax, $this->yMax);
260 
261  /* The style bits here must match the fsSelection bits in the OS/2
262  * table, if present.
263  */
264  $macStyleBits = $this->readUInt(2);
265  $this->isBold = $this->isBitSet(0, $macStyleBits);
266  $this->isItalic = $this->isBitSet(1, $macStyleBits);
267 
268  /* We don't need the remainder of data in this table: smallest readable
269  * size, font direction hint, indexToLocFormat, and glyphDataFormat.
270  */
271  }
272 
273 
282  protected function _parseNameTable()
283  {
284  $this->_jumpToTable('name');
285  $baseOffset = $this->_tableDirectory['name']['offset'];
286 
287  /* The name table begins with a short header, followed by each of the
288  * fixed-length name records, followed by the variable-length strings.
289  */
290 
291  /* We only understand version 0 tables.
292  */
293  $tableFormat = $this->readUInt(2);
294  if ($tableFormat != 0) {
295  #require_once 'Zend/Pdf/Exception.php';
296  throw new Zend_Pdf_Exception("Unable to read format $tableFormat table",
298  }
299  $this->_debugLog('Format %d table', $tableFormat);
300 
301  $nameCount = $this->readUInt(2);
302  $this->_debugLog('%d name strings', $nameCount);
303 
304  $storageOffset = $this->readUInt(2) + $baseOffset;
305  $this->_debugLog('Storage offset: 0x%x', $storageOffset);
306 
307  /* Scan the name records for those we're interested in. We'll skip over
308  * encodings and languages we don't understand or support. Prefer the
309  * Microsoft Unicode encoding for a given name/language combination, but
310  * use Mac Roman if nothing else is available. We will extract the
311  * actual strings later.
312  */
313  $nameRecords = array();
314  for ($nameIndex = 0; $nameIndex < $nameCount; $nameIndex++) {
315 
316  $platformID = $this->readUInt(2);
317  $encodingID = $this->readUInt(2);
318 
319  if (! ( (($platformID == 3) && ($encodingID == 1)) || // Microsoft Unicode
320  (($platformID == 1) && ($encodingID == 0)) // Mac Roman
321  ) ) {
322  $this->skipBytes(8); // Not a supported encoding. Move on.
323  continue;
324  }
325 
326  $languageID = $this->readUInt(2);
327  $nameID = $this->readUInt(2);
328  $nameLength = $this->readUInt(2);
329  $nameOffset = $this->readUInt(2);
330 
331  $languageCode = $this->_languageCodeForPlatform($platformID, $languageID);
332  if ($languageCode === null) {
333  $this->_debugLog('Skipping languageID: 0x%x; platformID %d', $languageID, $platformID);
334  continue; // Not a supported language. Move on.
335  }
336 
337  $this->_debugLog('Adding nameID: %d; languageID: 0x%x; platformID: %d; offset: 0x%x (0x%x); length: %d',
338  $nameID, $languageID, $platformID, $baseOffset + $nameOffset, $nameOffset, $nameLength);
339 
340  /* Entries in the name table are sorted by platform ID. If an entry
341  * exists for both Mac Roman and Microsoft Unicode, the Unicode entry
342  * will prevail since it is processed last.
343  */
344  $nameRecords[$nameID][$languageCode] = array('platform' => $platformID,
345  'offset' => $nameOffset,
346  'length' => $nameLength );
347  }
348 
349  /* Now go back and extract the interesting strings.
350  */
351  $fontNames = array();
352  foreach ($nameRecords as $name => $languages) {
353  foreach ($languages as $language => $attributes) {
354  $stringOffset = $storageOffset + $attributes['offset'];
355  $this->moveToOffset($stringOffset);
356  if ($attributes['platform'] == 3) {
357  $string = $this->readStringUTF16($attributes['length']);
358  } else {
359  $string = $this->readStringMacRoman($attributes['length']);
360  }
361  $fontNames[$name][$language] = $string;
362  }
363  }
364 
365  $this->names = $fontNames;
366  }
367 
368 
378  protected function _parsePostTable()
379  {
380  $this->_jumpToTable('post');
381 
382  /* We can read versions 1-4 tables.
383  */
384  $tableVersion = $this->_readTableVersion(1, 4);
385 
386  $this->italicAngle = $this->readFixed(16, 16);
387 
388  $this->underlinePosition = $this->readInt(2);
389  $this->underlineThickness = $this->readInt(2);
390 
391  $fixedPitch = $this->readUInt(4);
392  $this->isMonospaced = ($fixedPitch !== 0);
393 
394  /* Skip over PostScript virtual memory usage.
395  */
396  $this->skipBytes(16);
397 
398  /* The format of the remainder of this table is dependent on the table
399  * version. However, since it contains glyph ordering information and
400  * PostScript names which we don't use, move on. (This may change at
401  * some point in the future though...)
402  */
403  }
404 
405 
415  protected function _parseHheaTable()
416  {
417  $this->_jumpToTable('hhea');
418 
419  /* We can read any version 1 table.
420  */
421  $tableVersion = $this->_readTableVersion(1, 1);
422 
423  /* The typographic ascent, descent, and line gap values are Apple-
424  * specific. Similar values exist in the OS/2 table. We'll use these
425  * values unless better values are found in OS/2.
426  */
427  $this->ascent = $this->readInt(2);
428  $this->descent = $this->readInt(2);
429  $this->lineGap = $this->readInt(2);
430 
431  /* The descent value is supposed to be negative--it's the distance
432  * relative to the baseline. However, some fonts improperly store a
433  * positive value in this field. If a positive value is found, flip the
434  * sign and record a warning in the debug log that we did this.
435  */
436  if ($this->descent > 0) {
437  $this->_debugLog('Warning: Font should specify negative descent. Actual: %d; Using %d',
438  $this->descent, -$this->descent);
439  $this->descent = -$this->descent;
440  }
441 
442  /* Skip over advance width, left and right sidebearing, max x extent,
443  * caret slope rise, run, and offset, and the four reserved fields.
444  */
445  $this->skipBytes(22);
446 
447  /* These values are needed to read the hmtx table.
448  */
449  $this->metricDataFormat = $this->readInt(2);
450  $this->numberHMetrics = $this->readUInt(2);
451  $this->_debugLog('hmtx data format: %d; number of metrics: %d',
452  $this->metricDataFormat, $this->numberHMetrics);
453  }
454 
455 
465  protected function _parseMaxpTable()
466  {
467  $this->_jumpToTable('maxp');
468 
469  /* We don't care about table version.
470  */
471  $this->_readTableVersion(0, 1);
472 
473  /* The number of glyphs in the font.
474  */
475  $this->numGlyphs = $this->readUInt(2);
476  $this->_debugLog('number of glyphs: %d', $this->numGlyphs);
477 
478  // Skip other maxp table entries (if presented with table version 1.0)...
479  }
480 
481 
493  protected function _parseOs2Table()
494  {
495  if (! $this->numberHMetrics) {
496  #require_once 'Zend/Pdf/Exception.php';
497  throw new Zend_Pdf_Exception("hhea table must be parsed prior to calling this method",
499  }
500 
501  try {
502  $this->_jumpToTable('OS/2');
503  } catch (Zend_Pdf_Exception $e) {
504  /* This table is not always present. If missing, use default values.
505  */
506  #require_once 'Zend/Pdf/Exception.php';
507  if ($e->getCode() == Zend_Pdf_Exception::REQUIRED_TABLE_NOT_FOUND) {
508  $this->_debugLog('No OS/2 table found. Using default values');
509  $this->fontWeight = Zend_Pdf_Font::WEIGHT_NORMAL;
510  $this->fontWidth = Zend_Pdf_Font::WIDTH_NORMAL;
511  $this->isEmbeddable = true;
512  $this->isSubsettable = true;
513  $this->strikeThickness = $this->unitsPerEm * 0.05;
514  $this->strikePosition = $this->unitsPerEm * 0.225;
515  $this->isSerifFont = false; // the style of the font is unknown
516  $this->isSansSerifFont = false;
517  $this->isOrnamentalFont = false;
518  $this->isScriptFont = false;
519  $this->isSymbolicFont = false;
520  $this->isAdobeLatinSubset = false;
521  $this->vendorID = '';
522  $this->xHeight = 0;
523  $this->capitalHeight = 0;
524  return;
525  } else {
526  /* Something else went wrong. Throw this exception higher up the chain.
527  */
528  throw $e;
529  throw new Zend_Pdf_Exception($e->getMessage(), $e->getCode(), $e);
530  }
531  }
532 
533  /* Version 0 tables are becoming rarer these days. They are only found
534  * in older fonts.
535  *
536  * Version 1 formally defines the Unicode character range bits and adds
537  * two new fields to the end of the table.
538  *
539  * Version 2 defines several additional flags to the embedding bits
540  * (fsType field) and five new fields to the end of the table.
541  *
542  * Versions 2 and 3 are structurally identical. There are only two
543  * significant differences between the two: First, in version 3, the
544  * average character width (xAvgCharWidth field) is calculated using all
545  * non-zero width glyphs in the font instead of just the Latin lower-
546  * case alphabetic characters; this doesn't affect us. Second, in
547  * version 3, the embedding bits (fsType field) have been made mutually
548  * exclusive; see additional discusson on this below.
549  *
550  * We can understand all four of these table versions.
551  */
552  $tableVersion = $this->readUInt(2);
553  if (($tableVersion < 0) || ($tableVersion > 3)) {
554  #require_once 'Zend/Pdf/Exception.php';
555  throw new Zend_Pdf_Exception("Unable to read version $tableVersion table",
557  }
558  $this->_debugLog('Version %d table', $tableVersion);
559 
560  $this->averageCharWidth = $this->readInt(2);
561 
562  /* Indicates the visual weight and aspect ratio of the characters. Used
563  * primarily to logically sort fonts in lists. Also used to help choose
564  * a more appropriate substitute font when necessary. See the WEIGHT_
565  * and WIDTH_ constants defined in Zend_Pdf_Font.
566  */
567  $this->fontWeight = $this->readUInt(2);
568  $this->fontWidth = $this->readUInt(2);
569 
570  /* Describes the font embedding licensing rights. We can only embed and
571  * subset a font when given explicit permission.
572  *
573  * NOTE: We always interpret these bits according to the rules defined
574  * in version 3 of this table, regardless of the actual version. This
575  * means we will perform our checks in order from the most-restrictive
576  * to the least.
577  */
578  $embeddingFlags = $this->readUInt(2);
579  $this->_debugLog('Embedding flags: %d', $embeddingFlags);
580  if ($this->isBitSet(9, $embeddingFlags)) {
581  /* Only bitmaps may be embedded. We don't have the ability to strip
582  * outlines from fonts yet, so this means no embed.
583  */
584  $this->isEmbeddable = false;
585  } elseif ($this->isBitSet(2, $embeddingFlags)
586  || $this->isBitSet(3, $embeddingFlags)
587  || $this->isBitSet(4, $embeddingFlags)
588  ) {
589  /* One of:
590  * Restricted License embedding (0x0002)
591  * Preview & Print embedding (0x0004)
592  * Editable embedding (0x0008)
593  * is set.
594  */
595  $this->isEmbeddable = true;
596  } elseif ($this->isBitSet(1, $embeddingFlags)) {
597  /* Restricted license embedding & no other embedding is set.
598  * We currently don't have any way to
599  * enforce this, so interpret this as no embed. This may be revised
600  * in the future...
601  */
602  $this->isEmbeddable = false;
603  } else {
604  /* The remainder of the bit settings grant us permission to embed
605  * the font. There may be additional usage rights granted or denied
606  * but those only affect the PDF viewer application, not our code.
607  */
608  $this->isEmbeddable = true;
609  }
610  $this->_debugLog('Font ' . ($this->isEmbeddable ? 'may' : 'may not') . ' be embedded');
611  $isSubsettable = $this->isBitSet($embeddingFlags, 8);
612 
613  /* Recommended size and offset for synthesized subscript characters.
614  */
615  $this->subscriptXSize = $this->readInt(2);
616  $this->subscriptYSize = $this->readInt(2);
617  $this->subscriptXOffset = $this->readInt(2);
618  $this->subscriptYOffset = $this->readInt(2);
619 
620  /* Recommended size and offset for synthesized superscript characters.
621  */
622  $this->superscriptXSize = $this->readInt(2);
623  $this->superscriptYSize = $this->readInt(2);
624  $this->superscriptXOffset = $this->readInt(2);
625  $this->superscriptYOffset = $this->readInt(2);
626 
627  /* Size and vertical offset for the strikethrough.
628  */
629  $this->strikeThickness = $this->readInt(2);
630  $this->strikePosition = $this->readInt(2);
631 
632  /* Describes the class of font: serif, sans serif, script. etc. These
633  * values are defined here:
634  * http://www.microsoft.com/OpenType/OTSpec/ibmfc.htm
635  */
636  $familyClass = ($this->readUInt(2) >> 8); // don't care about subclass
637  $this->_debugLog('Font family class: %d', $familyClass);
638  $this->isSerifFont = ((($familyClass >= 1) && ($familyClass <= 5)) ||
639  ($familyClass == 7));
640  $this->isSansSerifFont = ($familyClass == 8);
641  $this->isOrnamentalFont = ($familyClass == 9);
642  $this->isScriptFont = ($familyClass == 10);
643  $this->isSymbolicFont = ($familyClass == 12);
644 
645  /* Skip over the PANOSE number. The interesting values for us overlap
646  * with the font family class defined above.
647  */
648  $this->skipBytes(10);
649 
650  /* The Unicode range is made up of four 4-byte unsigned long integers
651  * which are used as bitfields covering a 128-bit range. Each bit
652  * represents a Unicode code block. If the bit is set, this font at
653  * least partially covers the characters in that block.
654  */
655  $unicodeRange1 = $this->readUInt(4);
656  $unicodeRange2 = $this->readUInt(4);
657  $unicodeRange3 = $this->readUInt(4);
658  $unicodeRange4 = $this->readUInt(4);
659  $this->_debugLog('Unicode ranges: 0x%x 0x%x 0x%x 0x%x',
660  $unicodeRange1, $unicodeRange2, $unicodeRange3, $unicodeRange4);
661 
662  /* The Unicode range is currently only used to decide if the character
663  * set covered by the font is a subset of the Adobe Latin set, meaning
664  * it only has the basic latin set. If it covers any other characters,
665  * even any of the extended latin characters, it is considered symbolic
666  * to PDF and must be described differently in the Font Descriptor.
667  */
674  $this->isAdobeLatinSubset = (($unicodeRange1 == 1) && ($unicodeRange2 == 0) &&
675  ($unicodeRange3 == 0) && ($unicodeRange4 == 0));
676  $this->_debugLog(($this->isAdobeLatinSubset ? 'Is' : 'Is not') . ' a subset of Adobe Latin');
677 
678  $this->vendorID = $this->readBytes(4);
679 
680  /* Skip the font style bits. We use the values found in the 'head' table.
681  * Also skip the first Unicode and last Unicode character indicies. Our
682  * cmap implementation does not need these values.
683  */
684  $this->skipBytes(6);
685 
686  /* Typographic ascender, descender, and line gap. These values are
687  * preferred to those in the 'hhea' table.
688  */
689  $this->ascent = $this->readInt(2);
690  $this->descent = $this->readInt(2);
691  $this->lineGap = $this->readInt(2);
692 
693  /* The descent value is supposed to be negative--it's the distance
694  * relative to the baseline. However, some fonts improperly store a
695  * positive value in this field. If a positive value is found, flip the
696  * sign and record a warning in the debug log that we did this.
697  */
698  if ($this->descent > 0) {
699  $this->_debugLog('Warning: Font should specify negative descent. Actual: %d; Using %d',
700  $this->descent, -$this->descent);
701  $this->descent = -$this->descent;
702  }
703 
704  /* Skip over Windows-specific ascent and descent.
705  */
706  $this->skipBytes(4);
707 
708  /* Versions 0 and 1 tables do not contain the x or capital height
709  * fields. Record zero for unknown.
710  */
711  if ($tableVersion < 2) {
712  $this->xHeight = 0;
713  $this->capitalHeight = 0;
714  } else {
715 
716  /* Skip over the Windows code page coverages. We are only concerned
717  * with Unicode coverage.
718  */
719  $this->skipBytes(8);
720 
721  $this->xHeight = $this->readInt(2);
722  $this->capitalHeight = $this->readInt(2);
723 
724  /* Ignore the remaining fields in this table. They are Windows-specific.
725  */
726  }
731  }
732 
733 
743  protected function _parseHmtxTable()
744  {
745  $this->_jumpToTable('hmtx');
746 
747  if (! $this->numberHMetrics) {
748  #require_once 'Zend/Pdf/Exception.php';
749  throw new Zend_Pdf_Exception("hhea table must be parsed prior to calling this method",
751  }
752 
753  /* We only understand version 0 tables.
754  */
755  if ($this->metricDataFormat != 0) {
756  #require_once 'Zend/Pdf/Exception.php';
757  throw new Zend_Pdf_Exception("Unable to read format $this->metricDataFormat table.",
759  }
760 
761  /* The hmtx table has no header. For each glpyh in the font, it contains
762  * the glyph's advance width and its left side bearing. We don't use the
763  * left side bearing.
764  */
765  $glyphWidths = array();
766  for ($i = 0; $i < $this->numberHMetrics; $i++) {
767  $glyphWidths[$i] = $this->readUInt(2);
768  $this->skipBytes(2);
769  }
770  /* Populate last value for the rest of array
771  */
772  while (count($glyphWidths) < $this->numGlyphs) {
773  $glyphWidths[] = end($glyphWidths);
774  }
775  $this->glyphWidths = $glyphWidths;
776 
777  /* There is an optional table of left side bearings which is sometimes
778  * used for monospaced fonts. We don't use the left side bearing, so
779  * we can safely ignore it.
780  */
781  }
782 
783 
800  protected function _parseCmapTable()
801  {
802  $this->_jumpToTable('cmap');
803  $baseOffset = $this->_tableDirectory['cmap']['offset'];
804 
805  /* We only understand version 0 tables.
806  */
807  $tableVersion = $this->readUInt(2);
808  if ($tableVersion != 0) {
809  #require_once 'Zend/Pdf/Exception.php';
810  throw new Zend_Pdf_Exception("Unable to read version $tableVersion table",
812  }
813  $this->_debugLog('Version %d table', $tableVersion);
814 
815  $subtableCount = $this->readUInt(2);
816  $this->_debugLog('%d subtables', $subtableCount);
817 
818  /* Like the name table, there may be many different encoding subtables
819  * present. Ideally, we are looking for an acceptable Unicode table.
820  */
821  $subtables = array();
822  for ($subtableIndex = 0; $subtableIndex < $subtableCount; $subtableIndex++) {
823 
824  $platformID = $this->readUInt(2);
825  $encodingID = $this->readUInt(2);
826 
827  if (! ( (($platformID == 0) && ($encodingID == 3)) || // Unicode 2.0 or later
828  (($platformID == 0) && ($encodingID == 0)) || // Unicode
829  (($platformID == 3) && ($encodingID == 1)) || // Microsoft Unicode
830  (($platformID == 1) && ($encodingID == 0)) // Mac Roman
831  ) ) {
832  $this->_debugLog('Unsupported encoding: platformID: %d; encodingID: %d; skipping',
833  $platformID, $encodingID);
834  $this->skipBytes(4);
835  continue;
836  }
837 
838  $subtableOffset = $this->readUInt(4);
839  if ($subtableOffset < 0) { // Sanity check for 4-byte unsigned on 32-bit platform
840  $this->_debugLog('Offset 0x%x out of range for platformID: %d; skipping',
841  $subtableOffset, $platformID);
842  continue;
843  }
844 
845  $this->_debugLog('Found subtable; platformID: %d; encodingID: %d; offset: 0x%x (0x%x)',
846  $platformID, $encodingID, $baseOffset + $subtableOffset, $subtableOffset);
847 
848  $subtables[$platformID][$encodingID][] = $subtableOffset;
849  }
850 
851  /* In preferred order, find a subtable to use.
852  */
853  $offsets = array();
854 
855  /* Unicode 2.0 or later semantics
856  */
857  if (isset($subtables[0][3])) {
858  foreach ($subtables[0][3] as $offset) {
859  $offsets[] = $offset;
860  }
861  }
862 
863  /* Unicode default semantics
864  */
865  if (isset($subtables[0][0])) {
866  foreach ($subtables[0][0] as $offset) {
867  $offsets[] = $offset;
868  }
869  }
870 
871  /* Microsoft Unicode
872  */
873  if (isset($subtables[3][1])) {
874  foreach ($subtables[3][1] as $offset) {
875  $offsets[] = $offset;
876  }
877  }
878 
879  /* Mac Roman.
880  */
881  if (isset($subtables[1][0])) {
882  foreach ($subtables[1][0] as $offset) {
883  $offsets[] = $offset;
884  }
885  }
886 
887  $cmapType = -1;
888 
889  foreach ($offsets as $offset) {
890  $cmapOffset = $baseOffset + $offset;
891  $this->moveToOffset($cmapOffset);
892  $format = $this->readUInt(2);
893  $language = -1;
894  switch ($format) {
895  case 0x0:
896  $cmapLength = $this->readUInt(2);
897  $language = $this->readUInt(2);
898  if ($language != 0) {
899  $this->_debugLog('Type 0 cmap tables must be language-independent;'
900  . ' language: %d; skipping', $language);
901  continue;
902  }
903  break;
904 
905  case 0x4: // break intentionally omitted
906  case 0x6:
907  $cmapLength = $this->readUInt(2);
908  $language = $this->readUInt(2);
909  if ($language != 0) {
910  $this->_debugLog('Warning: cmap tables must be language-independent - this font'
911  . ' may not work properly; language: %d', $language);
912  }
913  break;
914 
915  case 0x2: // break intentionally omitted
916  case 0x8: // break intentionally omitted
917  case 0xa: // break intentionally omitted
918  case 0xc:
919  $this->_debugLog('Format: 0x%x currently unsupported; skipping', $format);
920  continue;
921  //$this->skipBytes(2);
922  //$cmapLength = $this->readUInt(4);
923  //$language = $this->readUInt(4);
924  //if ($language != 0) {
925  // $this->_debugLog('Warning: cmap tables must be language-independent - this font'
926  // . ' may not work properly; language: %d', $language);
927  //}
928  //break;
929 
930  default:
931  $this->_debugLog('Unknown subtable format: 0x%x; skipping', $format);
932  continue;
933  }
934  $cmapType = $format;
935  break;
936  }
937  if ($cmapType == -1) {
938  #require_once 'Zend/Pdf/Exception.php';
939  throw new Zend_Pdf_Exception('Unable to find usable cmap table',
941  }
942 
943  /* Now extract the subtable data and create a Zend_Pdf_FontCmap object.
944  */
945  $this->_debugLog('Using cmap type %d; offset: 0x%x; length: %d',
946  $cmapType, $cmapOffset, $cmapLength);
947  $this->moveToOffset($cmapOffset);
948  $cmapData = $this->readBytes($cmapLength);
949 
950  #require_once 'Zend/Pdf/Cmap.php';
951  $this->cmap = Zend_Pdf_Cmap::cmapWithTypeData($cmapType, $cmapData);
952  }
953 
954 
966  protected function _readScalerType()
967  {
968  if ($this->_scalerType != 0) {
969  return $this->_scalerType;
970  }
971 
972  $this->moveToOffset(0);
973 
974  $this->_scalerType = $this->readUInt(4);
975 
976  switch ($this->_scalerType) {
977  case 0x00010000: // version 1.0 - Windows TrueType signature
978  $this->_debugLog('Windows TrueType signature');
979  break;
980 
981  case 0x74727565: // 'true' - Macintosh TrueType signature
982  $this->_debugLog('Macintosh TrueType signature');
983  break;
984 
985  case 0x4f54544f: // 'OTTO' - the CFF signature
986  $this->_debugLog('PostScript CFF signature');
987  break;
988 
989  case 0x74797031: // 'typ1'
990  #require_once 'Zend/Pdf/Exception.php';
991  throw new Zend_Pdf_Exception('Unsupported font type: PostScript in sfnt wrapper',
993 
994  default:
995  #require_once 'Zend/Pdf/Exception.php';
996  throw new Zend_Pdf_Exception('Not an OpenType font file',
998  }
999  return $this->_scalerType;
1000  }
1001 
1009  protected function _jumpToTable($tableName)
1010  {
1011  if (empty($this->_tableDirectory[$tableName])) { // do not allow NULL or zero
1012  #require_once 'Zend/Pdf/Exception.php';
1013  throw new Zend_Pdf_Exception("Required table '$tableName' not found!",
1015  }
1016  $this->_debugLog("Parsing $tableName table...");
1017  $this->moveToOffset($this->_tableDirectory[$tableName]['offset']);
1018  }
1019 
1030  protected function _readTableVersion($minVersion, $maxVersion)
1031  {
1032  $tableVersion = $this->readFixed(16, 16);
1033  if (($tableVersion < $minVersion) || ($tableVersion > $maxVersion)) {
1034  #require_once 'Zend/Pdf/Exception.php';
1035  throw new Zend_Pdf_Exception("Unable to read version $tableVersion table",
1037  }
1038  $this->_debugLog('Version %.2f table', $tableVersion);
1039  return $tableVersion;
1040  }
1041 
1051  protected function _languageCodeForPlatform($platformID, $languageID)
1052  {
1053  if ($platformID == 3) { // Microsoft encoding.
1054  /* The low-order bytes specify the language, the high-order bytes
1055  * specify the dialect. We just care about the language. For the
1056  * complete list, see:
1057  * http://www.microsoft.com/globaldev/reference/lcid-all.mspx
1058  */
1059  $languageID &= 0xff;
1060  switch ($languageID) {
1061  case 0x09:
1062  return 'en';
1063  case 0x0c:
1064  return 'fr';
1065  case 0x07:
1066  return 'de';
1067  case 0x10:
1068  return 'it';
1069  case 0x13:
1070  return 'nl';
1071  case 0x1d:
1072  return 'sv';
1073  case 0x0a:
1074  return 'es';
1075  case 0x06:
1076  return 'da';
1077  case 0x16:
1078  return 'pt';
1079  case 0x14:
1080  return 'no';
1081  case 0x0d:
1082  return 'he';
1083  case 0x11:
1084  return 'ja';
1085  case 0x01:
1086  return 'ar';
1087  case 0x0b:
1088  return 'fi';
1089  case 0x08:
1090  return 'el';
1091 
1092  default:
1093  return null;
1094  }
1095 
1096  } else if ($platformID == 1) { // Macintosh encoding.
1097  switch ($languageID) {
1098  case 0:
1099  return 'en';
1100  case 1:
1101  return 'fr';
1102  case 2:
1103  return 'de';
1104  case 3:
1105  return 'it';
1106  case 4:
1107  return 'nl';
1108  case 5:
1109  return 'sv';
1110  case 6:
1111  return 'es';
1112  case 7:
1113  return 'da';
1114  case 8:
1115  return 'pt';
1116  case 9:
1117  return 'no';
1118  case 10:
1119  return 'he';
1120  case 11:
1121  return 'ja';
1122  case 12:
1123  return 'ar';
1124  case 13:
1125  return 'fi';
1126  case 14:
1127  return 'el';
1128 
1129  default:
1130  return null;
1131  }
1132 
1133  } else { // Unknown encoding.
1134  return null;
1135  }
1136  }
1137 
1138 }
readInt($size, $byteOrder=Zend_Pdf_FileParser::BYTE_ORDER_BIG_ENDIAN)
Definition: FileParser.php:228
const DONT_UNDERSTAND_TABLE_VERSION
Definition: Exception.php:236
const CANT_FIND_GOOD_CMAP
Definition: Exception.php:246
$tableName
Definition: trigger.php:13
const PARSED_OUT_OF_ORDER
Definition: Exception.php:212
isBitSet($bit, $bitField)
Definition: FileParser.php:339
const WIDTH_NORMAL
Definition: Font.php:364
_languageCodeForPlatform($platformID, $languageID)
Definition: OpenType.php:1051
elseif(isset( $params[ 'redirect_parent']))
Definition: iframe.phtml:17
const REQUIRED_TABLE_NOT_FOUND
Definition: Exception.php:231
const INDEX_OUT_OF_RANGE
Definition: Exception.php:100
_debugLog($message)
Definition: Font.php:202
readBytes($byteCount)
Definition: FileParser.php:195
_readTableVersion($minVersion, $maxVersion)
Definition: OpenType.php:1030
$format
Definition: list.phtml:12
readFixed($mantissaBits, $fractionBits, $byteOrder=Zend_Pdf_FileParser::BYTE_ORDER_BIG_ENDIAN)
Definition: FileParser.php:363
static cmapWithTypeData($cmapType, $cmapData)
Definition: Cmap.php:145
$attributes
Definition: matrix.phtml:13
readStringUTF16($byteCount, $byteOrder=Zend_Pdf_FileParser::BYTE_ORDER_BIG_ENDIAN, $characterSet='')
Definition: Font.php:118
skipBytes($byteCount)
Definition: FileParser.php:206
readStringMacRoman($byteCount, $characterSet='')
Definition: Font.php:134
const WEIGHT_NORMAL
Definition: Font.php:311
$i
Definition: gallery.phtml:31
readUInt($size, $byteOrder=Zend_Pdf_FileParser::BYTE_ORDER_BIG_ENDIAN)
Definition: FileParser.php:303
if(!isset($_GET['name'])) $name
Definition: log.php:14