source-class-Com.Tecnick.Pdf.Font.Import.TrueType

It appears that you are using AdBlocking software. The cost of running this website is covered by advertisements. If you like it please feel free to a small amount of money to secure the future of this website.
  1: <?php
  2: /**
  3:  * TrueType.php
  4:  *
  5:  * @since       2011-05-23
  6:  * @category    Library
  7:  * @package     PdfFont
  8:  * @author      Nicola Asuni <info@tecnick.com>
  9:  * @copyright   2011-2016 Nicola Asuni - Tecnick.com LTD
 10:  * @license     http://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE.TXT)
 11:  * @link        https://github.com/tecnickcom/tc-lib-pdf-font
 12:  *
 13:  * This file is part of tc-lib-pdf-font software library.
 14:  */
 15: 
 16: namespace Com\Tecnick\Pdf\Font\Import;
 17: 
 18: use \Com\Tecnick\File\File;
 19: use \Com\Tecnick\Unicode\Data\Encoding;
 20: use \Com\Tecnick\Pdf\Font\Exception as FontException;
 21: 
 22: /**
 23:  * Com\Tecnick\Pdf\Font\Import\TrueType
 24:  *
 25:  * @since       2011-05-23
 26:  * @category    Library
 27:  * @package     PdfFont
 28:  * @author      Nicola Asuni <info@tecnick.com>
 29:  * @copyright   2011-2016 Nicola Asuni - Tecnick.com LTD
 30:  * @license     http://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE.TXT)
 31:  * @link        https://github.com/tecnickcom/tc-lib-pdf-font
 32:  */
 33: class TrueType extends \Com\Tecnick\Pdf\Font\Import\TrueTypeFormat
 34: {
 35:     /**
 36:      * Content of the input font file
 37:      *
 38:      * @var string
 39:      */
 40:     protected $font = '';
 41: 
 42:     /**
 43:      * Extracted font metrics
 44:      *
 45:      * @var array
 46:      */
 47:     protected $fdt = array();
 48:     
 49:     /**
 50:      * Object used to read font bytes
 51:      *
 52:      * @var \Com\Tecnick\File\Byte
 53:      */
 54:     protected $fbyte;
 55:     
 56:     /**
 57:      * Pointer position on the original font data
 58:      *
 59:      * @var int
 60:      */
 61:     protected $offset = 0;
 62: 
 63:     /**
 64:      * Process TrueType font
 65:      *
 66:      * @param string $font     Content of the input font file
 67:      * @param array  $fdt      Extracted font metrics
 68:      * @param Byte   $fbyte    Object used to read font bytes
 69:      * @param array  $subchars Array containing subset chars
 70:      *
 71:      * @throws FontException in case of error
 72:      */
 73:     public function __construct($font, $fdt, $fbyte, $subchars = array())
 74:     {
 75:         $this->font = $font;
 76:         $this->fdt = $fdt;
 77:         $this->fbyte = $fbyte;
 78:         ksort($subchars);
 79:         $this->subchars = $subchars;
 80:         $this->subglyphs = array(0 => true);
 81:         $this->process();
 82:     }
 83: 
 84:     /**
 85:      * Get all the extracted font metrics
 86:      *
 87:      * @return string
 88:      */
 89:     public function getFontMetrics()
 90:     {
 91:         return $this->fdt;
 92:     }
 93: 
 94:     /**
 95:      * Get glyphs in the subset
 96:      *
 97:      * @return array
 98:      */
 99:     public function getSubGlyphs()
100:     {
101:         return $this->subglyphs;
102:     }
103: 
104:     /**
105:      * Process TrueType font
106:      */
107:     protected function process()
108:     {
109:         $this->isValidType();
110:         $this->setFontFile();
111:         $this->getTables();
112:         $this->checkMagickNumber();
113:         $this->offset += 2; // skip flags
114:         $this->getBbox();
115:         $this->getIndexToLoc();
116:         $this->getEncodingTables();
117:         $this->getOS2Metrics();
118:         $this->getFontName();
119:         $this->getPostData();
120:         $this->getHheaData();
121:         $this->getMaxpData();
122:         $this->getCIDToGIDMap();
123:         $this->getHeights();
124:         $this->getWidths();
125:     }
126: 
127:     /**
128:      * Check if the font is a valid type
129:      *
130:      * @throws FontException if the font is invalid
131:      */
132:     protected function isValidType()
133:     {
134:         if ($this->fbyte->getULong($this->offset) != 0x10000) {
135:             throw new FontException('sfnt version must be 0x00010000 for TrueType version 1.0.');
136:         }
137:         $this->offset += 4;
138:     }
139: 
140:     /**
141:      * Copy or link the original font file
142:      */
143:     protected function setFontFile()
144:     {
145:         if (!empty($this->fdt['desc'])) {
146:             // subsetting mode
147:             $this->fdt['Flags'] = $this->fdt['desc']['Flags'];
148:             return;
149:         }
150:         if ($this->fdt['type'] != 'cidfont0') {
151:             if ($this->fdt['linked']) {
152:                 // creates a symbolic link to the existing font
153:                 symlink($this->fdt['input_file'], $this->fdt['dir'].$this->fdt['file_name']);
154:             } else {
155:                 // store compressed font
156:                 $this->fdt['file'] = $this->fdt['file_name'].'.z';
157:                 $file = new File();
158:                 $fpt = $file->fopenLocal($this->fdt['dir'].$this->fdt['file'], 'wb');
159:                 fwrite($fpt, gzcompress($this->font));
160:                 fclose($fpt);
161:             }
162:         }
163:     }
164: 
165:     /**
166:      * Get the font tables
167:      *
168:      */
169:     protected function getTables()
170:     {
171:         // get number of tables
172:         $numTables = $this->fbyte->getUShort($this->offset);
173:         $this->offset += 2;
174:         // skip searchRange, entrySelector and rangeShift
175:         $this->offset += 6;
176:         // tables array
177:         $this->fdt['table'] = array();
178:         // ---------- get tables ----------
179:         for ($idx = 0; $idx < $numTables; ++$idx) {
180:             // get table info
181:             $tag = substr($this->font, $this->offset, 4);
182:             $this->offset += 4;
183:             $this->fdt['table'][$tag] = array();
184:             $this->fdt['table'][$tag]['checkSum'] = $this->fbyte->getULong($this->offset);
185:             $this->offset += 4;
186:             $this->fdt['table'][$tag]['offset'] = $this->fbyte->getULong($this->offset);
187:             $this->offset += 4;
188:             $this->fdt['table'][$tag]['length'] = $this->fbyte->getULong($this->offset);
189:             $this->offset += 4;
190:         }
191:     }
192: 
193:     /**
194:      * Check if the font is a valid type
195:      *
196:      * @throws FontException if the font is invalid
197:      */
198:     protected function checkMagickNumber()
199:     {
200:         $this->offset = ($this->fdt['table']['head']['offset'] + 12);
201:         if ($this->fbyte->getULong($this->offset) != 0x5F0F3CF5) {
202:             // magicNumber must be 0x5F0F3CF5
203:             throw new FontException('magicNumber must be 0x5F0F3CF5');
204:         }
205:         $this->offset += 4;
206:     }
207: 
208:     /**
209:      * Get BBox, units and flags
210:      */
211:     protected function getBbox()
212:     {
213:         // get FUnits
214:         $this->fdt['unitsPerEm'] = $this->fbyte->getUShort($this->offset);
215:         $this->offset += 2;
216:         // units ratio constant
217:         $this->fdt['urk'] = (1000 / $this->fdt['unitsPerEm']);
218:         $this->offset += 16; // skip created, modified
219:         $xMin = round($this->fbyte->getFWord($this->offset) * $this->fdt['urk']);
220:         $this->offset += 2;
221:         $yMin = round($this->fbyte->getFWord($this->offset) * $this->fdt['urk']);
222:         $this->offset += 2;
223:         $xMax = round($this->fbyte->getFWord($this->offset) * $this->fdt['urk']);
224:         $this->offset += 2;
225:         $yMax = round($this->fbyte->getFWord($this->offset) * $this->fdt['urk']);
226:         $this->offset += 2;
227:         $this->fdt['bbox'] = $xMin.' '.$yMin.' '.$xMax.' '.$yMax;
228:         $macStyle = $this->fbyte->getUShort($this->offset);
229:         $this->offset += 2;
230:         // PDF font flags
231:         if (($macStyle & 2) == 2) {
232:             // italic flag
233:             $this->fdt['Flags'] |= 64;
234:         }
235:     }
236: 
237:     /**
238:      * Get index to loc map
239:      */
240:     protected function getIndexToLoc()
241:     {
242:         // get offset mode (indexToLocFormat : 0 = short, 1 = long)
243:         $this->offset = ($this->fdt['table']['head']['offset'] + 50);
244:         $this->fdt['short_offset'] = ($this->fbyte->getShort($this->offset) == 0);
245:         $this->offset += 2;
246:         // get the offsets to the locations of the glyphs in the font, relative to the beginning of the glyphData table
247:         $this->fdt['indexToLoc'] = array();
248:         $this->offset = $this->fdt['table']['loca']['offset'];
249:         if ($this->fdt['short_offset']) {
250:             // short version
251:             $this->fdt['tot_num_glyphs'] = floor($this->fdt['table']['loca']['length'] / 2); // numGlyphs + 1
252:             for ($idx = 0; $idx < $this->fdt['tot_num_glyphs']; ++$idx) {
253:                 $this->fdt['indexToLoc'][$idx] = $this->fbyte->getUShort($this->offset) * 2;
254:                 if (isset($this->fdt['indexToLoc'][($idx - 1)])
255:                     && ($this->fdt['indexToLoc'][$idx] == $this->fdt['indexToLoc'][($idx - 1)])
256:                 ) {
257:                     // the last glyph didn't have an outline
258:                     unset($this->fdt['indexToLoc'][($idx - 1)]);
259:                 }
260:                 $this->offset += 2;
261:             }
262:         } else {
263:             // long version
264:             $this->fdt['tot_num_glyphs'] = floor($this->fdt['table']['loca']['length'] / 4); // numGlyphs + 1
265:             for ($idx = 0; $idx < $this->fdt['tot_num_glyphs']; ++$idx) {
266:                 $this->fdt['indexToLoc'][$idx] = $this->fbyte->getULong($this->offset);
267:                 if (isset($this->fdt['indexToLoc'][($idx - 1)])
268:                     && ($this->fdt['indexToLoc'][$idx] == $this->fdt['indexToLoc'][($idx - 1)])
269:                 ) {
270:                     // the last glyph didn't have an outline
271:                     unset($this->fdt['indexToLoc'][($idx - 1)]);
272:                 }
273:                 $this->offset += 4;
274:             }
275:         }
276:     }
277: 
278:     /**
279:      * Get encoding tables
280:      */
281:     protected function getEncodingTables()
282:     {
283:         // get glyphs indexes of chars from cmap table
284:         $this->offset = $this->fdt['table']['cmap']['offset'] + 2;
285:         $numEncodingTables = $this->fbyte->getUShort($this->offset);
286:         $this->offset += 2;
287:         $this->fdt['encodingTables'] = array();
288:         for ($idx = 0; $idx < $numEncodingTables; ++$idx) {
289:             $this->fdt['encodingTables'][$idx]['platformID'] = $this->fbyte->getUShort($this->offset);
290:             $this->offset += 2;
291:             $this->fdt['encodingTables'][$idx]['encodingID'] = $this->fbyte->getUShort($this->offset);
292:             $this->offset += 2;
293:             $this->fdt['encodingTables'][$idx]['offset'] = $this->fbyte->getULong($this->offset);
294:             $this->offset += 4;
295:         }
296:     }
297: 
298:     /**
299:      * Get encoding tables
300:      */
301:     protected function getOS2Metrics()
302:     {
303:         $this->offset = $this->fdt['table']['OS/2']['offset'];
304:         $this->offset += 2; // skip version
305:         // xAvgCharWidth
306:         $this->fdt['AvgWidth'] = round($this->fbyte->getFWord($this->offset) * $this->fdt['urk']);
307:         $this->offset += 2;
308:         // usWeightClass
309:         $usWeightClass = round($this->fbyte->getUFWord($this->offset) * $this->fdt['urk']);
310:         // estimate StemV and StemH (400 = usWeightClass for Normal - Regular font)
311:         $this->fdt['StemV'] = round((70 * $usWeightClass) / 400);
312:         $this->fdt['StemH'] = round((30 * $usWeightClass) / 400);
313:         $this->offset += 2;
314:         $this->offset += 2; // usWidthClass
315:         $fsType = $this->fbyte->getShort($this->offset);
316:         $this->offset += 2;
317:         if ($fsType == 2) {
318:             throw new FontException(
319:                 'This Font cannot be modified, embedded or exchanged in any manner'
320:                 .' without first obtaining permission of the legal owner.'
321:             );
322:         }
323:     }
324: 
325:     /**
326:      * Get font name
327:      */
328:     protected function getFontName()
329:     {
330:         $this->fdt['name'] = '';
331:         $this->offset = $this->fdt['table']['name']['offset'];
332:         $this->offset += 2; // skip Format selector (=0).
333:         // Number of NameRecords that follow n.
334:         $numNameRecords = $this->fbyte->getUShort($this->offset);
335:         $this->offset += 2;
336:         // Offset to start of string storage (from start of table).
337:         $stringStorageOffset = $this->fbyte->getUShort($this->offset);
338:         $this->offset += 2;
339:         for ($idx = 0; $idx < $numNameRecords; ++$idx) {
340:             $this->offset += 6; // skip Platform ID, Platform-specific encoding ID, Language ID.
341:             // Name ID.
342:             $nameID = $this->fbyte->getUShort($this->offset);
343:             $this->offset += 2;
344:             if ($nameID == 6) {
345:                 // String length (in bytes).
346:                 $stringLength = $this->fbyte->getUShort($this->offset);
347:                 $this->offset += 2;
348:                 // String offset from start of storage area (in bytes).
349:                 $stringOffset = $this->fbyte->getUShort($this->offset);
350:                 $this->offset += 2;
351:                 $this->offset = ($this->fdt['table']['name']['offset'] + $stringStorageOffset + $stringOffset);
352:                 $this->fdt['name'] = substr($this->font, $this->offset, $stringLength);
353:                 $this->fdt['name'] = preg_replace('/[^a-zA-Z0-9_\-]/', '', $this->fdt['name']);
354:                 break;
355:             } else {
356:                 $this->offset += 4; // skip String length, String offset
357:             }
358:         }
359:     }
360: 
361:     /**
362:      * Get post data
363:      */
364:     protected function getPostData()
365:     {
366:         $this->offset = $this->fdt['table']['post']['offset'];
367:         $this->offset += 4; // skip Format Type
368:         $this->fdt['italicAngle'] = $this->fbyte->getFixed($this->offset);
369:         $this->offset += 4;
370:         $this->fdt['underlinePosition'] = round($this->fbyte->getFWord($this->offset) * $this->fdt['urk']);
371:         $this->offset += 2;
372:         $this->fdt['underlineThickness'] = round($this->fbyte->getFWord($this->offset) * $this->fdt['urk']);
373:         $this->offset += 2;
374:         $isFixedPitch = (($this->fbyte->getULong($this->offset) == 0) ? false : true);
375:         $this->offset += 2;
376:         if ($isFixedPitch) {
377:             $this->fdt['Flags'] |= 1;
378:         }
379:     }
380: 
381:     /**
382:      * Get hhea data
383:      */
384:     protected function getHheaData()
385:     {
386:         // ---------- get hhea data ----------
387:         $this->offset = $this->fdt['table']['hhea']['offset'];
388:         $this->offset += 4; // skip Table version number
389:         // Ascender
390:         $this->fdt['Ascent'] = round($this->fbyte->getFWord($this->offset) * $this->fdt['urk']);
391:         $this->offset += 2;
392:         // Descender
393:         $this->fdt['Descent'] = round($this->fbyte->getFWord($this->offset) * $this->fdt['urk']);
394:         $this->offset += 2;
395:         // LineGap
396:         $this->fdt['Leading'] = round($this->fbyte->getFWord($this->offset) * $this->fdt['urk']);
397:         $this->offset += 2;
398:         // advanceWidthMax
399:         $this->fdt['MaxWidth'] = round($this->fbyte->getUFWord($this->offset) * $this->fdt['urk']);
400:         $this->offset += 2;
401:         $this->offset += 22; // skip some values
402:         // get the number of hMetric entries in hmtx table
403:         $this->fdt['numHMetrics'] = $this->fbyte->getUShort($this->offset);
404:     }
405: 
406:     /**
407:      * Get maxp data
408:      */
409:     protected function getMaxpData()
410:     {
411:         $this->offset = $this->fdt['table']['maxp']['offset'];
412:         $this->offset += 4; // skip Table version number
413:         // get the the number of glyphs in the font.
414:         $this->fdt['numGlyphs'] = $this->fbyte->getUShort($this->offset);
415:     }
416: 
417:     /**
418:      * Get font heights
419:      */
420:     protected function getHeights()
421:     {
422:         // get xHeight (height of x)
423:         $this->fdt['XHeight'] = ($this->fdt['Ascent'] + $this->fdt['Descent']);
424:         if (!empty($this->fdt['ctgdata'][120])) {
425:             $this->offset = ($this->fdt['table']['glyf']['offset']
426:                 + $this->fdt['indexToLoc'][$this->fdt['ctgdata'][120]]
427:                 + 4
428:             );
429:             $yMin = $this->fbyte->getFWord($this->offset);
430:             $this->offset += 4;
431:             $yMax = $this->fbyte->getFWord($this->offset);
432:             $this->offset += 2;
433:             $this->fdt['XHeight'] = round(($yMax - $yMin) * $this->fdt['urk']);
434:         }
435:     
436:         // get CapHeight (height of H)
437:         $this->fdt['CapHeight'] = $this->fdt['Ascent'];
438:         if (!empty($this->fdt['ctgdata'][72])) {
439:             $this->offset = ($this->fdt['table']['glyf']['offset']
440:                 + $this->fdt['indexToLoc'][$this->fdt['ctgdata'][72]]
441:                 + 4
442:             );
443:             $yMin = $this->fbyte->getFWord($this->offset);
444:             $this->offset += 4;
445:             $yMax = $this->fbyte->getFWord($this->offset);
446:             $this->offset += 2;
447:             $this->fdt['CapHeight'] = round(($yMax - $yMin) * $this->fdt['urk']);
448:         }
449:     }
450: 
451:     /**
452:      * Get font widths
453:      */
454:     protected function getWidths()
455:     {
456:         // create widths array
457:         $chw = array();
458:         $this->offset = $this->fdt['table']['hmtx']['offset'];
459:         for ($i = 0; $i < $this->fdt['numHMetrics']; ++$i) {
460:             $chw[$i] = round($this->fbyte->getUFWord($this->offset) * $this->fdt['urk']);
461:             $this->offset += 4; // skip lsb
462:         }
463:         if ($this->fdt['numHMetrics'] < $this->fdt['numGlyphs']) {
464:             // fill missing widths with the last value
465:             $chw = array_pad($chw, $this->fdt['numGlyphs'], $chw[($this->fdt['numHMetrics'] - 1)]);
466:         }
467:         $this->fdt['MissingWidth'] = $chw[0];
468:         $this->fdt['cw'] = '';
469:         $this->fdt['cbbox'] = '';
470:         for ($cid = 0; $cid <= 65535; ++$cid) {
471:             if (isset($this->fdt['ctgdata'][$cid])) {
472:                 if (($cid >= 0) && isset($chw[$this->fdt['ctgdata'][$cid]])) {
473:                     $this->fdt['cw'] .= ',"'.$cid.'":'.$chw[$this->fdt['ctgdata'][$cid]];
474:                 }
475:                 if (isset($this->fdt['indexToLoc'][$this->fdt['ctgdata'][$cid]])) {
476:                     $this->offset = ($this->fdt['table']['glyf']['offset']
477:                         + $this->fdt['indexToLoc'][$this->fdt['ctgdata'][$cid]]
478:                     );
479:                     $xMin = round($this->fbyte->getFWord($this->offset + 2) * $this->fdt['urk']);
480:                     $yMin = round($this->fbyte->getFWord($this->offset + 4) * $this->fdt['urk']);
481:                     $xMax = round($this->fbyte->getFWord($this->offset + 6) * $this->fdt['urk']);
482:                     $yMax = round($this->fbyte->getFWord($this->offset + 8) * $this->fdt['urk']);
483:                     $this->fdt['cbbox'] .= ',"'.$cid.'":['.$xMin.','.$yMin.','.$xMax.','.$yMax.']';
484:                 }
485:             }
486:         }
487:     }
488: }
489: 
 

© 2004-2017 – Nicola Asuni - Tecnick.com - All rights reserved.
about - disclaimer - privacy