]>
git.immae.eu Git - github/wallabag/wallabag.git/blob - inc/3rdparty/libraries/mpdf/classes/ttfontsuni.php
3 /*******************************************************************************
8 * Author: Ian Back <ianb@bpm1.com> *
10 * Copyright (c) Ian Back, 2010 *
11 * This class is based on The ReportLab Open Source PDF library *
12 * written in Python - http://www.reportlab.com/software/opensource/ *
13 * together with ideas from the OpenOffice source code and others. *
14 * This header must be retained in any redistribution or *
15 * modification of the file. *
17 *******************************************************************************/
19 // Define the value used in the "head" table of a created TTF file
20 // 0x74727565 "true" for Mac
21 // 0x00010000 for Windows
22 // Either seems to work for a font embedded in a PDF file
23 // when read by Adobe Reader on a Windows PC(!)
24 if (! defined ( '_TTF_MAC_HEADER' )) define ( "_TTF_MAC_HEADER" , false );
26 // Recalculate correct metadata/profiles when making subset fonts (not SIP/SMP)
27 // e.g. xMin, xMax, maxNContours
28 if (! defined ( '_RECALC_PROFILE' )) define ( "_RECALC_PROFILE" , false );
30 // TrueType Font Glyph operators
31 define ( "GF_WORDS" ,( 1 << 0 ));
32 define ( "GF_SCALE" ,( 1 << 3 ));
33 define ( "GF_MORE" ,( 1 << 5 ));
34 define ( "GF_XYSCALE" ,( 1 << 6 ));
35 define ( "GF_TWOBYTWO" ,( 1 << 7 ));
41 var $unAGlyphs ; // mPDF 5.4.05
72 var $underlinePosition ;
73 var $underlineThickness ;
82 function TTFontFile () {
83 $this- > maxStrLenRead
= 200000 ; // Maximum size of glyf table to read in as string (otherwise reads each glyph from file)
87 function getMetrics ( $file , $TTCfontID = 0 , $debug = false , $BMPonly = false , $kerninfo = false , $unAGlyphs = false ) { // mPDF 5.4.05
88 $this- > unAGlyphs
= $unAGlyphs ; // mPDF 5.4.05
89 $this- > filename
= $file ;
90 $this- > fh
= fopen ( $file , 'rb' ) or die ( 'Can \' t open file ' . $file );
92 $this- > charWidths
= '' ;
93 $this- > glyphPos
= array ();
94 $this- > charToGlyph
= array ();
95 $this- > tables
= array ();
96 $this- > otables
= array ();
97 $this- > kerninfo
= array ();
100 $this- > numTTCFonts
= 0 ;
101 $this- > TTCFonts
= array ();
102 $this- > version
= $version = $this- > read_ulong ();
103 $this- > panose
= array ();
104 if ( $version == 0x4F54544F )
105 die ( "Postscript outlines are not supported" );
106 if ( $version == 0x74746366 && ! $TTCfontID )
107 die ( "ERROR - You must define the TTCfontID for a TrueType Collection in config_fonts.php (" . $file . ")" );
108 if (! in_array ( $version , array ( 0x00010000 , 0x74727565 )) && ! $TTCfontID )
109 die ( "Not a TrueType font: version=" . $version );
110 if ( $TTCfontID > 0 ) {
111 $this- > version
= $version = $this- > read_ulong (); // TTC Header version now
112 if (! in_array ( $version , array ( 0x00010000 , 0x00020000 )))
113 die ( "ERROR - Error parsing TrueType Collection: version=" . $version . " - " . $file );
114 $this- > numTTCFonts
= $this- > read_ulong ();
115 for ( $i = 1 ; $i <= $this- > numTTCFonts
; $i ++
) {
116 $this- > TTCFonts
[ $i ][ 'offset' ] = $this- > read_ulong ();
118 $this- > seek ( $this- > TTCFonts
[ $TTCfontID ][ 'offset' ]);
119 $this- > version
= $version = $this- > read_ulong (); // TTFont version again now
121 $this- > readTableDirectory ( $debug );
122 $this- > extractInfo ( $debug , $BMPonly , $kerninfo );
127 function readTableDirectory ( $debug = false ) {
128 $this- > numTables
= $this- > read_ushort ();
129 $this- > searchRange
= $this- > read_ushort ();
130 $this- > entrySelector
= $this- > read_ushort ();
131 $this- > rangeShift
= $this- > read_ushort ();
132 $this- > tables
= array ();
133 for ( $i = 0 ; $i < $this- > numTables
; $i ++
) {
135 $record [ 'tag' ] = $this- > read_tag ();
136 $record [ 'checksum' ] = array ( $this- > read_ushort (), $this- > read_ushort ());
137 $record [ 'offset' ] = $this- > read_ulong ();
138 $record [ 'length' ] = $this- > read_ulong ();
139 $this- > tables
[ $record [ 'tag' ]] = $record ;
141 if ( $debug ) $this- > checksumTables ();
144 function checksumTables () {
145 // Check the checksums for all tables
146 foreach ( $this- > tables
AS $t ) {
147 if ( $t [ 'length' ] > 0 && $t [ 'length' ] < $this- > maxStrLenRead
) { // 1.02
148 $table = $this- > get_chunk ( $t [ 'offset' ], $t [ 'length' ]);
149 $checksum = $this- > calcChecksum ( $table );
150 if ( $t [ 'tag' ] == 'head' ) {
151 $up = unpack ( 'n*' , substr ( $table , 8 , 4 ));
152 $adjustment [ 0 ] = $up [ 1 ];
153 $adjustment [ 1 ] = $up [ 2 ];
154 $checksum = $this- > sub32 ( $checksum , $adjustment );
156 $xchecksum = $t [ 'checksum' ];
157 if ( $xchecksum != $checksum )
158 die ( sprintf ( 'TTF file "%s": invalid checksum %s table: %s (expected %s)' , $this- > filename
, dechex ( $checksum [ 0 ]). dechex ( $checksum [ 1 ]), $t [ 'tag' ], dechex ( $xchecksum [ 0 ]). dechex ( $xchecksum [ 1 ])));
163 function sub32 ( $x , $y ) {
168 if ( $ylo > $xlo ) { $xlo +
= 1 << 16 ; $yhi +
= 1 ; }
170 if ( $yhi > $xhi ) { $xhi +
= 1 << 16 ; }
172 $reshi = $reshi & 0xFFFF ;
173 return array ( $reshi , $reslo );
176 function calcChecksum ( $data ) {
177 if ( strlen ( $data ) %
4 ) { $data
.= str_repeat ( "\0" ,( 4 -( strlen ( $data
) %
4 ))); }
178 $len = strlen ( $data );
181 for ( $i = 0 ; $i < $len ; $i +
= 4 ) {
182 $hi +
= ( ord ( $data [ $i ])<< 8 ) +
ord ( $data [ $i +
1 ]);
183 $lo +
= ( ord ( $data [ $i +
2 ])<< 8 ) +
ord ( $data [ $i +
3 ]);
184 $hi +
= ( $lo >> 16 ) & 0xFFFF ;
187 return array ( $hi , $lo );
190 function get_table_pos ( $tag ) {
191 $offset = $this- > tables
[ $tag ][ 'offset' ];
192 $length = $this- > tables
[ $tag ][ 'length' ];
193 return array ( $offset , $length );
196 function seek ( $pos ) {
198 fseek ( $this- > fh
, $this- > _pos
);
201 function skip ( $delta ) {
202 $this- > _pos
= $this- > _pos +
$delta ;
203 fseek ( $this- > fh
, $delta , SEEK_CUR
);
206 function seek_table ( $tag , $offset_in_table = 0 ) {
207 $tpos = $this- > get_table_pos ( $tag );
208 $this- > _pos
= $tpos [ 0 ] +
$offset_in_table ;
209 fseek ( $this- > fh
, $this- > _pos
);
213 function read_tag () {
215 return fread ( $this- > fh
, 4 );
218 function read_short () {
220 $s = fread ( $this- > fh
, 2 );
221 $a = ( ord ( $s [ 0 ])<< 8 ) +
ord ( $s [ 1 ]);
222 if ( $a & ( 1 << 15 ) ) {
223 $a = ( $a - ( 1 << 16 ));
228 function unpack_short ( $s ) {
229 $a = ( ord ( $s [ 0 ])<< 8 ) +
ord ( $s [ 1 ]);
230 if ( $a & ( 1 << 15 ) ) {
231 $a = ( $a - ( 1 << 16 ));
236 function read_ushort () {
238 $s = fread ( $this- > fh
, 2 );
239 return ( ord ( $s [ 0 ])<< 8 ) +
ord ( $s [ 1 ]);
242 function read_ulong () {
244 $s = fread ( $this- > fh
, 4 );
245 // if large uInt32 as an integer, PHP converts it to -ve
246 return ( ord ( $s [ 0 ])* 16777216 ) +
( ord ( $s [ 1 ])<< 16 ) +
( ord ( $s [ 2 ])<< 8 ) +
ord ( $s [ 3 ]); // 16777216 = 1<<24
249 function get_ushort ( $pos ) {
250 fseek ( $this- > fh
, $pos );
251 $s = fread ( $this- > fh
, 2 );
252 return ( ord ( $s [ 0 ])<< 8 ) +
ord ( $s [ 1 ]);
255 function get_ulong ( $pos ) {
256 fseek ( $this- > fh
, $pos );
257 $s = fread ( $this- > fh
, 4 );
258 // iF large uInt32 as an integer, PHP converts it to -ve
259 return ( ord ( $s [ 0 ])* 16777216 ) +
( ord ( $s [ 1 ])<< 16 ) +
( ord ( $s [ 2 ])<< 8 ) +
ord ( $s [ 3 ]); // 16777216 = 1<<24
262 function pack_short ( $val ) {
268 return pack ( "n" , $val );
271 function splice ( $stream , $offset , $value ) {
272 return substr ( $stream , 0 , $offset ) . $value . substr ( $stream , $offset +
strlen ( $value ));
275 function _set_ushort ( $stream , $offset , $value ) {
276 $up = pack ( "n" , $value );
277 return $this- > splice ( $stream , $offset , $up );
280 function _set_short ( $stream , $offset , $val ) {
286 $up = pack ( "n" , $val );
287 return $this- > splice ( $stream , $offset , $up );
290 function get_chunk ( $pos , $length ) {
291 fseek ( $this- > fh
, $pos );
292 if ( $length < 1 ) { return '' ; }
293 return ( fread ( $this- > fh
, $length ));
296 function get_table ( $tag ) {
297 list ( $pos , $length ) = $this- > get_table_pos ( $tag );
298 if ( $length == 0 ) { return '' ; }
299 fseek ( $this- > fh
, $pos );
300 return ( fread ( $this- > fh
, $length ));
303 function add ( $tag , $data ) {
304 if ( $tag == 'head' ) {
305 $data = $this- > splice ( $data , 8 , "\0\0\0\0" );
307 $this- > otables
[ $tag ] = $data ;
312 /////////////////////////////////////////////////////////////////////////////////////////
313 function getCTG ( $file , $TTCfontID = 0 , $debug = false , $unAGlyphs = false ) { // mPDF 5.4.05
314 $this- > unAGlyphs
= $unAGlyphs ; // mPDF 5.4.05
315 $this- > filename
= $file ;
316 $this- > fh
= fopen ( $file , 'rb' ) or die ( 'Can \' t open file ' . $file );
318 $this- > charWidths
= '' ;
319 $this- > glyphPos
= array ();
320 $this- > charToGlyph
= array ();
321 $this- > tables
= array ();
322 $this- > numTTCFonts
= 0 ;
323 $this- > TTCFonts
= array ();
325 if ( $TTCfontID > 0 ) {
326 $this- > version
= $version = $this- > read_ulong (); // TTC Header version now
327 if (! in_array ( $version , array ( 0x00010000 , 0x00020000 )))
328 die ( "ERROR - Error parsing TrueType Collection: version=" . $version . " - " . $file );
329 $this- > numTTCFonts
= $this- > read_ulong ();
330 for ( $i = 1 ; $i <= $this- > numTTCFonts
; $i ++
) {
331 $this- > TTCFonts
[ $i ][ 'offset' ] = $this- > read_ulong ();
333 $this- > seek ( $this- > TTCFonts
[ $TTCfontID ][ 'offset' ]);
334 $this- > version
= $version = $this- > read_ulong (); // TTFont version again now
336 $this- > readTableDirectory ( $debug );
339 // cmap - Character to glyph index mapping table
340 $cmap_offset = $this- > seek_table ( "cmap" );
342 $cmapTableCount = $this- > read_ushort ();
343 $unicode_cmap_offset = 0 ;
344 for ( $i = 0 ; $i < $cmapTableCount ; $i ++
) {
345 $platformID = $this- > read_ushort ();
346 $encodingID = $this- > read_ushort ();
347 $offset = $this- > read_ulong ();
348 $save_pos = $this- > _pos
;
349 if ( $platformID == 3 && $encodingID == 1 ) { // Microsoft, Unicode
350 $format = $this- > get_ushort ( $cmap_offset +
$offset );
352 $unicode_cmap_offset = $cmap_offset +
$offset ;
356 else if ( $platformID == 0 ) { // Unicode -- assume all encodings are compatible
357 $format = $this- > get_ushort ( $cmap_offset +
$offset );
359 $unicode_cmap_offset = $cmap_offset +
$offset ;
363 $this- > seek ( $save_pos );
366 $glyphToChar = array ();
367 $charToGlyph = array ();
368 $this- > getCMAP4 ( $unicode_cmap_offset , $glyphToChar , $charToGlyph );
371 return ( $charToGlyph );
374 /////////////////////////////////////////////////////////////////////////////////////////
375 function getTTCFonts ( $file ) {
376 $this- > filename
= $file ;
377 $this- > fh
= fopen ( $file , 'rb' );
378 if (! $this- > fh
) { return ( 'ERROR - Can \' t open file ' . $file
); }
379 $this- > numTTCFonts
= 0 ;
380 $this- > TTCFonts
= array ();
381 $this- > version
= $version = $this- > read_ulong ();
382 if ( $version == 0x74746366 ) {
383 $this- > version
= $version = $this- > read_ulong (); // TTC Header version now
384 if (! in_array ( $version , array ( 0x00010000 , 0x00020000 )))
385 return ( "ERROR - Error parsing TrueType Collection: version=" . $version . " - " . $file );
388 return ( "ERROR - Not a TrueType Collection: version=" . $version . " - " . $file );
390 $this- > numTTCFonts
= $this- > read_ulong ();
391 for ( $i = 1 ; $i <= $this- > numTTCFonts
; $i ++
) {
392 $this- > TTCFonts
[ $i ][ 'offset' ] = $this- > read_ulong ();
398 /////////////////////////////////////////////////////////////////////////////////////////
400 /////////////////////////////////////////////////////////////////////////////////////////
402 function extractInfo ( $debug = false , $BMPonly = false , $kerninfo = false ) {
403 $this- > panose
= array ();
404 $this- > sFamilyClass
= 0 ;
405 $this- > sFamilySubClass
= 0 ;
406 ///////////////////////////////////
407 // name - Naming table
408 ///////////////////////////////////
409 $name_offset = $this- > seek_table ( "name" );
410 $format = $this- > read_ushort ();
411 if ( $format != 0 && $format != 1 )
412 die ( "Unknown name table format " . $format );
413 $numRecords = $this- > read_ushort ();
414 $string_data_offset = $name_offset +
$this- > read_ushort ();
415 $names = array ( 1 => '' , 2 => '' , 3 => '' , 4 => '' , 6 => '' );
416 $K = array_keys ( $names );
417 $nameCount = count ( $names );
418 for ( $i = 0 ; $i < $numRecords ; $i ++
) {
419 $platformId = $this- > read_ushort ();
420 $encodingId = $this- > read_ushort ();
421 $languageId = $this- > read_ushort ();
422 $nameId = $this- > read_ushort ();
423 $length = $this- > read_ushort ();
424 $offset = $this- > read_ushort ();
425 if (! in_array ( $nameId , $K )) continue ;
427 if ( $platformId == 3 && $encodingId == 1 && $languageId == 0x409 ) { // Microsoft, Unicode, US English, PS Name
429 $this- > seek ( $string_data_offset +
$offset );
430 if ( $length %
2 != 0 )
431 die ( "PostScript name is UTF-16BE string of odd length" );
434 while ( $length > 0 ) {
435 $char = $this- > read_ushort ();
442 else if ( $platformId == 1 && $encodingId == 0 && $languageId == 0 ) { // Macintosh, Roman, English, PS Name
444 $N = $this- > get_chunk ( $string_data_offset +
$offset , $length );
448 if ( $N && $names [ $nameId ]== '' ) {
449 $names [ $nameId ] = $N ;
451 if ( $nameCount == 0 ) break ;
457 $psName = preg_replace ( '/ /' , '-' , $names [ 4 ]);
459 $psName = preg_replace ( '/ /' , '-' , $names [ 1 ]);
463 die ( "Could not find PostScript font name: " . $this- > filename
);
465 for ( $i = 0 ; $i < count ( $psName ); $i ++
) {
468 if ( $oc > 126 || strpos ( ' [](){}<>/%' , $c )!== false )
469 die ( "psName=" . $psName . " contains invalid character " . $c . " ie U+" . ord ( c
));
472 $this- > name
= $psName ;
473 if ( $names [ 1 ]) { $this
-> familyName
= $names
[ 1 ]; } else { $this
-> familyName
= $psName
; }
474 if ( $names [ 2 ]) { $this
-> styleName
= $names
[ 2 ]; } else { $this
-> styleName
= 'Regular' ; }
475 if ( $names [ 4 ]) { $this
-> fullName
= $names
[ 4 ]; } else { $this
-> fullName
= $psName
; }
476 if ( $names [ 3 ]) { $this
-> uniqueFontID
= $names
[ 3 ]; } else { $this
-> uniqueFontID
= $psName
; }
478 if ( $names [ 6 ]) { $this
-> fullName
= $names
[ 6 ]; }
480 ///////////////////////////////////
481 // head - Font header table
482 ///////////////////////////////////
483 $this- > seek_table ( "head" );
485 $ver_maj = $this- > read_ushort ();
486 $ver_min = $this- > read_ushort ();
488 die ( 'Unknown head table version ' . $ver_maj . '.' . $ver_min );
489 $this- > fontRevision
= $this- > read_ushort () . $this- > read_ushort ();
492 $magic = $this- > read_ulong ();
493 if ( $magic != 0x5F0F3CF5 )
494 die ( 'Invalid head table magic ' . $magic );
500 $this- > unitsPerEm
= $unitsPerEm = $this- > read_ushort ();
501 $scale = 1000 / $unitsPerEm ;
503 $xMin = $this- > read_short ();
504 $yMin = $this- > read_short ();
505 $xMax = $this- > read_short ();
506 $yMax = $this- > read_short ();
507 $this- > bbox
= array (( $xMin * $scale ), ( $yMin * $scale ), ( $xMax * $scale ), ( $yMax * $scale ));
509 $indexToLocFormat = $this- > read_ushort ();
510 $glyphDataFormat = $this- > read_ushort ();
511 if ( $glyphDataFormat != 0 )
512 die ( 'Unknown glyph data format ' . $glyphDataFormat );
514 ///////////////////////////////////
515 // hhea metrics table
516 ///////////////////////////////////
517 // ttf2t1 seems to use this value rather than the one in OS/2 - so put in for compatibility
518 if ( isset ( $this- > tables
[ "hhea" ])) {
519 $this- > seek_table ( "hhea" );
521 $hheaAscender = $this- > read_short ();
522 $hheaDescender = $this- > read_short ();
523 $this- > ascent
= ( $hheaAscender * $scale );
524 $this- > descent
= ( $hheaDescender * $scale );
527 ///////////////////////////////////
528 // OS/2 - OS/2 and Windows metrics table
529 ///////////////////////////////////
530 if ( isset ( $this- > tables
[ "OS/2" ])) {
531 $this- > seek_table ( "OS/2" );
532 $version = $this- > read_ushort ();
534 $usWeightClass = $this- > read_ushort ();
536 $fsType = $this- > read_ushort ();
537 if ( $fsType == 0x0002 || ( $fsType & 0x0300 ) != 0 ) {
538 global $overrideTTFFontRestriction ;
539 if (! $overrideTTFFontRestriction ) die ( 'ERROR - Font file ' . $this- > filename
. ' cannot be embedded due to copyright restrictions.' );
540 $this- > restrictedUse
= true ;
543 $sF = $this- > read_short ();
544 $this- > sFamilyClass
= ( $sF >> 8 );
545 $this- > sFamilySubClass
= ( $sF & 0xFF );
546 $this- > _pos +
= 10 ; //PANOSE = 10 byte length
547 $panose = fread ( $this- > fh
, 10 );
548 $this- > panose
= array ();
549 for ( $p = 0 ; $p < strlen ( $panose ); $p ++
) { $this
-> panose
[] = ord ( $panose
[ $p
]); }
551 $sTypoAscender = $this- > read_short ();
552 $sTypoDescender = $this- > read_short ();
553 if (! $this- > ascent
) $this- > ascent
= ( $sTypoAscender * $scale );
554 if (! $this- > descent
) $this- > descent
= ( $sTypoDescender * $scale );
557 $sCapHeight = $this- > read_short ();
558 $this- > capHeight
= ( $sCapHeight * $scale );
561 $this- > capHeight
= $this- > ascent
;
565 $usWeightClass = 500 ;
566 if (! $this- > ascent
) $this- > ascent
= ( $yMax * $scale );
567 if (! $this- > descent
) $this- > descent
= ( $yMin * $scale );
568 $this- > capHeight
= $this- > ascent
;
570 $this- > stemV
= 50 +
intval ( pow (( $usWeightClass / 65.0 ), 2 ));
572 ///////////////////////////////////
573 // post - PostScript table
574 ///////////////////////////////////
575 $this- > seek_table ( "post" );
577 $ver_maj = $this- > read_ushort ();
578 $ver_min = $this- > read_ushort ();
579 if ( $ver_maj < 1 || $ver_maj > 4 )
580 die ( 'Unknown post table version ' . $ver_maj );
585 $this- > italicAngle
= $this- > read_short () +
$this- > read_ushort () / 65536.0 ;
586 $this- > underlinePosition
= $this- > read_short () * $scale ;
587 $this- > underlineThickness
= $this- > read_short () * $scale ;
588 $isFixedPitch = $this- > read_ulong ();
592 if ( $this- > italicAngle
!= 0 )
593 $this- > flags
= $this- > flags
| 64 ;
594 if ( $usWeightClass >= 600 )
595 $this- > flags
= $this- > flags
| 262144 ;
597 $this- > flags
= $this- > flags
| 1 ;
599 ///////////////////////////////////
600 // hhea - Horizontal header table
601 ///////////////////////////////////
602 $this- > seek_table ( "hhea" );
604 $ver_maj = $this- > read_ushort ();
605 $ver_min = $this- > read_ushort ();
607 die ( 'Unknown hhea table version ' . $ver_maj );
613 $metricDataFormat = $this- > read_ushort ();
614 if ( $metricDataFormat != 0 )
615 die ( 'Unknown horizontal metric data format ' . $metricDataFormat );
616 $numberOfHMetrics = $this- > read_ushort ();
617 if ( $numberOfHMetrics == 0 )
618 die ( 'Number of horizontal metrics is 0' );
620 ///////////////////////////////////
621 // maxp - Maximum profile table
622 ///////////////////////////////////
623 $this- > seek_table ( "maxp" );
625 $ver_maj = $this- > read_ushort ();
626 $ver_min = $this- > read_ushort ();
628 die ( 'Unknown maxp table version ' . $ver_maj );
633 $numGlyphs = $this- > read_ushort ();
636 ///////////////////////////////////
637 // cmap - Character to glyph index mapping table
638 ///////////////////////////////////
639 $cmap_offset = $this- > seek_table ( "cmap" );
641 $cmapTableCount = $this- > read_ushort ();
642 $unicode_cmap_offset = 0 ;
643 for ( $i = 0 ; $i < $cmapTableCount ; $i ++
) {
644 $platformID = $this- > read_ushort ();
645 $encodingID = $this- > read_ushort ();
646 $offset = $this- > read_ulong ();
647 $save_pos = $this- > _pos
;
648 if (( $platformID == 3 && $encodingID == 1 ) || $platformID == 0 ) { // Microsoft, Unicode
649 $format = $this- > get_ushort ( $cmap_offset +
$offset );
651 if (! $unicode_cmap_offset ) $unicode_cmap_offset = $cmap_offset +
$offset ;
655 // Microsoft, Unicode Format 12 table HKCS
656 else if ((( $platformID == 3 && $encodingID == 10 ) || $platformID == 0 ) && ! $BMPonly ) {
657 $format = $this- > get_ushort ( $cmap_offset +
$offset );
659 $unicode_cmap_offset = $cmap_offset +
$offset ;
663 $this- > seek ( $save_pos );
665 if (! $unicode_cmap_offset )
666 die ( 'Font (' . $this- > filename
. ') does not have cmap for Unicode (platform 3, encoding 1, format 4, or platform 0, any encoding, format 4)' );
671 // Format 12 CMAP does characters above Unicode BMP i.e. some HKCS characters U+20000 and above
672 if ( $format == 12 && ! $BMPonly ) {
673 $this- > maxUniChar
= 0 ;
674 $this- > seek ( $unicode_cmap_offset +
4 );
675 $length = $this- > read_ulong ();
676 $limit = $unicode_cmap_offset +
$length ;
679 $nGroups = $this- > read_ulong ();
681 $glyphToChar = array ();
682 $charToGlyph = array ();
683 for ( $i = 0 ; $i < $nGroups ; $i ++
) {
684 $startCharCode = $this- > read_ulong ();
685 $endCharCode = $this- > read_ulong ();
686 $startGlyphCode = $this- > read_ulong ();
687 if (( $endCharCode > 0x20000 && $endCharCode < 0x2A6DF ) || ( $endCharCode > 0x2F800 && $endCharCode < 0x2FA1F )) {
690 else if ( $endCharCode > 0x10000 && $endCharCode < 0x1FFFF ) {
694 for ( $unichar = $startCharCode ; $unichar <= $endCharCode ; $unichar ++
) {
695 $glyph = $startGlyphCode +
$offset ;
697 $charToGlyph [ $unichar ] = $glyph ;
698 if ( $unichar < 196608 ) { $this
-> maxUniChar
= max ( $unichar
, $this
-> maxUniChar
); }
699 $glyphToChar [ $glyph ][] = $unichar ;
705 $glyphToChar = array ();
706 $charToGlyph = array ();
707 $this- > getCMAP4 ( $unicode_cmap_offset , $glyphToChar , $charToGlyph );
710 $this- > sipset
= $sipset ;
711 $this- > smpset
= $smpset ;
713 ///////////////////////////////////
714 // hmtx - Horizontal metrics table
715 ///////////////////////////////////
716 $this- > getHMTX ( $numberOfHMetrics , $numGlyphs , $glyphToChar , $scale );
718 ///////////////////////////////////
719 // kern - Kerning pair table
720 ///////////////////////////////////
722 // Recognises old form of Kerning table - as required by Windows - Format 0 only
723 $kern_offset = $this- > seek_table ( "kern" );
724 $version = $this- > read_ushort ();
725 $nTables = $this- > read_ushort ();
727 $sversion = $this- > read_ushort ();
728 $slength = $this- > read_ushort ();
729 $scoverage = $this- > read_ushort ();
730 $format = $scoverage >> 8 ;
731 if ( $kern_offset && $version == 0 && $format == 0 ) {
733 $nPairs = $this- > read_ushort ();
735 for ( $i = 0 ; $i < $nPairs ; $i ++
) {
736 $left = $this- > read_ushort ();
737 $right = $this- > read_ushort ();
738 $val = $this- > read_short ();
739 if ( count ( $glyphToChar [ $left ])== 1 && count ( $glyphToChar [ $right ])== 1 ) {
740 if ( $left != 32 && $right != 32 ) {
741 $this- > kerninfo
[ $glyphToChar [ $left ][ 0 ]][ $glyphToChar [ $right ][ 0 ]] = intval ( $val * $scale );
750 /////////////////////////////////////////////////////////////////////////////////////////
753 function makeSubset ( $file , & $subset , $TTCfontID = 0 , $debug = false , $unAGlyphs = false ) { // mPDF 5.4.05
754 $this- > unAGlyphs
= $unAGlyphs ; // mPDF 5.4.05
755 $this- > filename
= $file ;
756 $this- > fh
= fopen ( $file , 'rb' ) or die ( 'Can \' t open file ' . $file );
758 $this- > charWidths
= '' ;
759 $this- > glyphPos
= array ();
760 $this- > charToGlyph
= array ();
761 $this- > tables
= array ();
762 $this- > otables
= array ();
765 $this- > numTTCFonts
= 0 ;
766 $this- > TTCFonts
= array ();
769 if ( $TTCfontID > 0 ) {
770 $this- > version
= $version = $this- > read_ulong (); // TTC Header version now
771 if (! in_array ( $version , array ( 0x00010000 , 0x00020000 )))
772 die ( "ERROR - Error parsing TrueType Collection: version=" . $version . " - " . $file );
773 $this- > numTTCFonts
= $this- > read_ulong ();
774 for ( $i = 1 ; $i <= $this- > numTTCFonts
; $i ++
) {
775 $this- > TTCFonts
[ $i ][ 'offset' ] = $this- > read_ulong ();
777 $this- > seek ( $this- > TTCFonts
[ $TTCfontID ][ 'offset' ]);
778 $this- > version
= $version = $this- > read_ulong (); // TTFont version again now
780 $this- > readTableDirectory ( $debug );
782 ///////////////////////////////////
783 // head - Font header table
784 ///////////////////////////////////
785 $this- > seek_table ( "head" );
787 $indexToLocFormat = $this- > read_ushort ();
788 $glyphDataFormat = $this- > read_ushort ();
790 ///////////////////////////////////
791 // hhea - Horizontal header table
792 ///////////////////////////////////
793 $this- > seek_table ( "hhea" );
795 $metricDataFormat = $this- > read_ushort ();
796 $orignHmetrics = $numberOfHMetrics = $this- > read_ushort ();
798 ///////////////////////////////////
799 // maxp - Maximum profile table
800 ///////////////////////////////////
801 $this- > seek_table ( "maxp" );
803 $numGlyphs = $this- > read_ushort ();
806 ///////////////////////////////////
807 // cmap - Character to glyph index mapping table
808 ///////////////////////////////////
809 $cmap_offset = $this- > seek_table ( "cmap" );
811 $cmapTableCount = $this- > read_ushort ();
812 $unicode_cmap_offset = 0 ;
813 for ( $i = 0 ; $i < $cmapTableCount ; $i ++
) {
814 $platformID = $this- > read_ushort ();
815 $encodingID = $this- > read_ushort ();
816 $offset = $this- > read_ulong ();
817 $save_pos = $this- > _pos
;
818 if (( $platformID == 3 && $encodingID == 1 ) || $platformID == 0 ) { // Microsoft, Unicode
819 $format = $this- > get_ushort ( $cmap_offset +
$offset );
821 $unicode_cmap_offset = $cmap_offset +
$offset ;
825 $this- > seek ( $save_pos );
828 if (! $unicode_cmap_offset )
829 die ( 'Font (' . $this- > filename
. ') does not have Unicode cmap (platform 3, encoding 1, format 4, or platform 0 [any encoding] format 4)' );
832 $glyphToChar = array ();
833 $charToGlyph = array ();
834 $this- > getCMAP4 ( $unicode_cmap_offset , $glyphToChar , $charToGlyph );
836 $this- > charToGlyph
= $charToGlyph ;
839 ///////////////////////////////////
840 // hmtx - Horizontal metrics table
841 ///////////////////////////////////
842 $scale = 1 ; // not used
843 $this- > getHMTX ( $numberOfHMetrics , $numGlyphs , $glyphToChar , $scale );
845 ///////////////////////////////////
846 // loca - Index to location
847 ///////////////////////////////////
848 $this- > getLOCA ( $indexToLocFormat , $numGlyphs );
850 $subsetglyphs = array ( 0 => 0 , 1 => 1 , 2 => 2 );
851 $subsetCharToGlyph = array ();
852 foreach ( $subset AS $code ) {
853 if ( isset ( $this- > charToGlyph
[ $code ])) {
854 $subsetglyphs [ $this- > charToGlyph
[ $code ]] = $code ; // Old Glyph ID => Unicode
855 $subsetCharToGlyph [ $code ] = $this- > charToGlyph
[ $code ]; // Unicode to old GlyphID
858 $this- > maxUni
= max ( $this- > maxUni
, $code );
861 list ( $start , $dummy ) = $this- > get_table_pos ( 'glyf' );
864 ksort ( $subsetglyphs );
866 $fsLastCharIndex = 0 ; // maximum Unicode index (character code) in this font, according to the cmap subtable for platform ID 3 and platform- specific encoding ID 0 or 1.
867 foreach ( $subsetglyphs AS $originalGlyphIdx => $uni ) {
868 $fsLastCharIndex = max ( $fsLastCharIndex , $uni );
869 $glyphSet [ $originalGlyphIdx ] = $n ; // old glyphID to new glyphID
873 ksort ( $subsetCharToGlyph );
874 foreach ( $subsetCharToGlyph AS $uni => $originalGlyphIdx ) {
875 $codeToGlyph [ $uni ] = $glyphSet [ $originalGlyphIdx ] ;
877 $this- > codeToGlyph
= $codeToGlyph ;
879 ksort ( $subsetglyphs );
880 foreach ( $subsetglyphs AS $originalGlyphIdx => $uni ) {
881 $this- > getGlyphs ( $originalGlyphIdx , $start , $glyphSet , $subsetglyphs );
884 $numGlyphs = $numberOfHMetrics = count ( $subsetglyphs );
886 ///////////////////////////////////
887 // name - table copied from the original
888 ///////////////////////////////////
889 $this- > add ( 'name' , $this- > get_table ( 'name' ));
891 ///////////////////////////////////
892 //tables copied from the original
893 ///////////////////////////////////
894 $tags = array ( 'cvt ' , 'fpgm' , 'prep' , 'gasp' );
895 foreach ( $tags AS $tag ) {
896 if ( isset ( $this- > tables
[ $tag ])) { $this
-> add ( $tag
, $this
-> get_table ( $tag
)); }
899 ///////////////////////////////////
901 ///////////////////////////////////
902 if ( isset ( $this- > tables
[ 'post' ])) {
903 $opost = $this- > get_table ( 'post' );
904 $post = " \x00\x03\x00\x00 " . substr ( $opost , 4 , 12 ) . " \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00 " ;
905 $this- > add ( 'post' , $post );
908 ///////////////////////////////////
909 // Sort CID2GID map into segments of contiguous codes
910 ///////////////////////////////////
912 unset ( $codeToGlyph [ 0 ]);
913 //unset($codeToGlyph[65535]);
918 // for each character
919 foreach ( $codeToGlyph as $cid => $glidx ) {
920 if ( $cid == ( $prevcid +
1 ) && $glidx == ( $prevglidx +
1 )) {
921 $range [ $rangeid ][] = $glidx ;
925 $range [ $rangeid ] = array ();
926 $range [ $rangeid ][] = $glidx ;
934 ///////////////////////////////////
936 ///////////////////////////////////
937 // cmap - Character to glyph mapping
938 $segCount = count ( $range ) +
1 ; // + 1 Last segment has missing character 0xFFFF
941 while ( $searchRange * 2 <= $segCount ) {
942 $searchRange = $searchRange * 2 ;
943 $entrySelector = $entrySelector +
1 ;
945 $searchRange = $searchRange * 2 ;
946 $rangeShift = $segCount * 2 - $searchRange ;
947 $length = 16 +
( 8 * $segCount ) +
( $numGlyphs +
1 );
948 $cmap = array ( 0 , 3 , // Index : version, number of encoding subtables
949 0 , 0 , // Encoding Subtable : platform (UNI=0), encoding 0
950 0 , 28 , // Encoding Subtable : offset (hi,lo)
951 0 , 3 , // Encoding Subtable : platform (UNI=0), encoding 3
952 0 , 28 , // Encoding Subtable : offset (hi,lo)
953 3 , 1 , // Encoding Subtable : platform (MS=3), encoding 1
954 0 , 28 , // Encoding Subtable : offset (hi,lo)
955 4 , $length , 0 , // Format 4 Mapping subtable: format, length, language
962 foreach ( $range AS $start => $subrange ) {
963 $endCode = $start +
( count ( $subrange )- 1 );
964 $cmap [] = $endCode ; // endCode(s)
966 $cmap [] = 0xFFFF ; // endCode of last Segment
967 $cmap [] = 0 ; // reservedPad
970 foreach ( $range AS $start => $subrange ) {
971 $cmap [] = $start ; // startCode(s)
973 $cmap [] = 0xFFFF ; // startCode of last Segment
975 foreach ( $range AS $start => $subrange ) {
976 $idDelta = -( $start-$subrange [ 0 ]);
977 $n +
= count ( $subrange );
978 $cmap [] = $idDelta ; // idDelta(s)
980 $cmap [] = 1 ; // idDelta of last Segment
982 foreach ( $range AS $subrange ) {
983 $cmap [] = 0 ; // idRangeOffset[segCount] Offset in bytes to glyph indexArray, or 0
986 $cmap [] = 0 ; // idRangeOffset of last Segment
987 foreach ( $range AS $subrange ) {
988 foreach ( $subrange AS $glidx ) {
992 $cmap [] = 0 ; // Mapping for last character
994 foreach ( $cmap AS $cm ) { $cmapstr
.= pack ( "n" , $cm
); }
995 $this- > add ( 'cmap' , $cmapstr );
998 ///////////////////////////////////
1000 ///////////////////////////////////
1001 list ( $glyfOffset , $glyfLength ) = $this- > get_table_pos ( 'glyf' );
1002 if ( $glyfLength < $this- > maxStrLenRead
) {
1003 $glyphData = $this- > get_table ( 'glyf' );
1014 $advanceWidthMax = 0 ;
1015 $minLeftSideBearing = 0 ;
1016 $minRightSideBearing = 0 ;
1018 $maxPoints = 0 ; // points in non-compound glyph
1019 $maxContours = 0 ; // contours in non-compound glyph
1020 $maxComponentPoints = 0 ; // points in compound glyph
1021 $maxComponentContours = 0 ; // contours in compound glyph
1022 $maxComponentElements = 0 ; // number of glyphs referenced at top level
1023 $maxComponentDepth = 0 ; // levels of recursion, set to 0 if font has only simple glyphs
1024 $this- > glyphdata
= array ();
1026 foreach ( $subsetglyphs AS $originalGlyphIdx => $uni ) {
1027 // hmtx - Horizontal Metrics
1028 $hm = $this- > getHMetric ( $orignHmetrics , $originalGlyphIdx );
1032 $glyphPos = $this- > glyphPos
[ $originalGlyphIdx ];
1033 $glyphLen = $this- > glyphPos
[ $originalGlyphIdx +
1 ] - $glyphPos ;
1034 if ( $glyfLength < $this- > maxStrLenRead
) {
1035 $data = substr ( $glyphData , $glyphPos , $glyphLen );
1038 if ( $glyphLen > 0 ) $data = $this- > get_chunk ( $glyfOffset +
$glyphPos , $glyphLen );
1042 if ( $glyphLen > 0 ) {
1043 if ( _RECALC_PROFILE
) {
1044 $xMin = $this- > unpack_short ( substr ( $data , 2 , 2 ));
1045 $yMin = $this- > unpack_short ( substr ( $data , 4 , 2 ));
1046 $xMax = $this- > unpack_short ( substr ( $data , 6 , 2 ));
1047 $yMax = $this- > unpack_short ( substr ( $data , 8 , 2 ));
1048 $xMinT = min ( $xMinT , $xMin );
1049 $yMinT = min ( $yMinT , $yMin );
1050 $xMaxT = max ( $xMaxT , $xMax );
1051 $yMaxT = max ( $yMaxT , $yMax );
1052 $aw = $this- > unpack_short ( substr ( $hm , 0 , 2 ));
1053 $lsb = $this- > unpack_short ( substr ( $hm , 2 , 2 ));
1054 $advanceWidthMax = max ( $advanceWidthMax , $aw );
1055 $minLeftSideBearing = min ( $minLeftSideBearing , $lsb );
1056 $minRightSideBearing = min ( $minRightSideBearing ,( $aw - $lsb - ( $xMax - $xMin )));
1057 $xMaxExtent = max ( $xMaxExtent ,( $lsb +
( $xMax - $xMin )));
1059 $up = unpack ( "n" , substr ( $data , 0 , 2 ));
1061 if ( $glyphLen > 2 && ( $up [ 1 ] & ( 1 << 15 )) ) { // If number of contours <= -1 i.e. composiste glyph
1064 $nComponentElements = 0 ;
1065 while ( $flags & GF_MORE
) {
1066 $nComponentElements +
= 1 ; // number of glyphs referenced at top level
1067 $up = unpack ( "n" , substr ( $data , $pos_in_glyph , 2 ));
1069 $up = unpack ( "n" , substr ( $data , $pos_in_glyph +
2 , 2 ));
1071 $this- > glyphdata
[ $originalGlyphIdx ][ 'compGlyphs' ][] = $glyphIdx ;
1072 $data = $this- > _set_ushort ( $data , $pos_in_glyph +
2 , $glyphSet [ $glyphIdx ]);
1074 if ( $flags & GF_WORDS
) { $pos_in_glyph +
= 4 ; }
1075 else { $pos_in_glyph +
= 2 ; }
1076 if ( $flags & GF_SCALE
) { $pos_in_glyph +
= 2 ; }
1077 else if ( $flags & GF_XYSCALE
) { $pos_in_glyph +
= 4 ; }
1078 else if ( $flags & GF_TWOBYTWO
) { $pos_in_glyph +
= 8 ; }
1080 $maxComponentElements = max ( $maxComponentElements , $nComponentElements );
1083 else if ( _RECALC_PROFILE
&& $glyphLen > 2 && $up [ 1 ] < ( 1 << 15 ) && $up [ 1 ] > 0 ) { // Number of contours > 0 simple glyph
1084 $nContours = $up [ 1 ];
1085 $this- > glyphdata
[ $originalGlyphIdx ][ 'nContours' ] = $nContours ;
1086 $maxContours = max ( $maxContours , $nContours );
1088 // Count number of points in simple glyph
1089 $pos_in_glyph = 10 +
( $nContours * 2 ) - 2 ; // Last endContourPoint
1090 $up = unpack ( "n" , substr ( $data , $pos_in_glyph , 2 ));
1092 $this- > glyphdata
[ $originalGlyphIdx ][ 'nPoints' ] = $points ;
1093 $maxPoints = max ( $maxPoints , $points );
1098 if ( $pos %
4 != 0 ) {
1099 $padding = 4 - ( $pos %
4 );
1100 $glyf .= str_repeat ( "\0" , $padding );
1105 if ( _RECALC_PROFILE
) {
1106 foreach ( $this- > glyphdata
AS $originalGlyphIdx => $val ) {
1107 $maxdepth = $depth = - 1 ;
1110 $this- > getGlyphData ( $originalGlyphIdx , $maxdepth , $depth , $points , $contours ) ;
1111 $maxComponentDepth = max ( $maxComponentDepth , $maxdepth );
1112 $maxComponentPoints = max ( $maxComponentPoints , $points );
1113 $maxComponentContours = max ( $maxComponentContours , $contours );
1119 $this- > add ( 'glyf' , $glyf );
1121 ///////////////////////////////////
1122 // hmtx - Horizontal Metrics
1123 ///////////////////////////////////
1124 $this- > add ( 'hmtx' , $hmtxstr );
1127 ///////////////////////////////////
1128 // loca - Index to location
1129 ///////////////////////////////////
1131 if ((( $pos +
1 ) >> 1 ) > 0xFFFF ) {
1132 $indexToLocFormat = 1 ; // long format
1133 foreach ( $offsets AS $offset ) { $locastr
.= pack ( "N" , $offset
); }
1136 $indexToLocFormat = 0 ; // short format
1137 foreach ( $offsets AS $offset ) { $locastr
.= pack ( "n" ,( $offset
/ 2 )); }
1139 $this- > add ( 'loca' , $locastr );
1141 ///////////////////////////////////
1142 // head - Font header
1143 ///////////////////////////////////
1144 $head = $this- > get_table ( 'head' );
1145 $head = $this- > _set_ushort ( $head , 50 , $indexToLocFormat );
1146 if ( _RECALC_PROFILE
) {
1147 $head = $this- > _set_short ( $head , 36 , $xMinT ); // for all glyph bounding boxes
1148 $head = $this- > _set_short ( $head , 38 , $yMinT ); // for all glyph bounding boxes
1149 $head = $this- > _set_short ( $head , 40 , $xMaxT ); // for all glyph bounding boxes
1150 $head = $this- > _set_short ( $head , 42 , $yMaxT ); // for all glyph bounding boxes
1151 $head [ 17 ] = chr ( $head [ 17 ] & ~
( 1 << 4 )); // Unset Bit 4 (as hdmx/LTSH tables not included)
1153 $this- > add ( 'head' , $head );
1156 ///////////////////////////////////
1157 // hhea - Horizontal Header
1158 ///////////////////////////////////
1159 $hhea = $this- > get_table ( 'hhea' );
1160 $hhea = $this- > _set_ushort ( $hhea , 34 , $numberOfHMetrics );
1161 if ( _RECALC_PROFILE
) {
1162 $hhea = $this- > _set_ushort ( $hhea , 10 , $advanceWidthMax );
1163 $hhea = $this- > _set_short ( $hhea , 12 , $minLeftSideBearing );
1164 $hhea = $this- > _set_short ( $hhea , 14 , $minRightSideBearing );
1165 $hhea = $this- > _set_short ( $hhea , 16 , $xMaxExtent );
1167 $this- > add ( 'hhea' , $hhea );
1169 ///////////////////////////////////
1170 // maxp - Maximum Profile
1171 ///////////////////////////////////
1172 $maxp = $this- > get_table ( 'maxp' );
1173 $maxp = $this- > _set_ushort ( $maxp , 4 , $numGlyphs );
1174 if ( _RECALC_PROFILE
) {
1175 $maxp = $this- > _set_ushort ( $maxp , 6 , $maxPoints ); // points in non-compound glyph
1176 $maxp = $this- > _set_ushort ( $maxp , 8 , $maxContours ); // contours in non-compound glyph
1177 $maxp = $this- > _set_ushort ( $maxp , 10 , $maxComponentPoints ); // points in compound glyph
1178 $maxp = $this- > _set_ushort ( $maxp , 12 , $maxComponentContours ); // contours in compound glyph
1179 $maxp = $this- > _set_ushort ( $maxp , 28 , $maxComponentElements ); // number of glyphs referenced at top level
1180 $maxp = $this- > _set_ushort ( $maxp , 30 , $maxComponentDepth ); // levels of recursion, set to 0 if font has only simple glyphs
1182 $this- > add ( 'maxp' , $maxp );
1185 ///////////////////////////////////
1187 ///////////////////////////////////
1188 if ( isset ( $this- > tables
[ 'OS/2' ])) {
1189 $os2_offset = $this- > seek_table ( "OS/2" );
1190 if ( _RECALC_PROFILE
) {
1191 $fsSelection = $this- > get_ushort ( $os2_offset +
62 );
1192 $fsSelection = ( $fsSelection & ~
( 1 << 6 )); // 2-byte bit field containing information concerning the nature of the font patterns
1193 // bit#0 = Italic; bit#5=Bold
1194 // Match name table's font subfamily string
1195 // Clear bit#6 used for 'Regular' and optional
1198 // NB Currently this method never subsets characters above BMP
1199 // Could set nonBMP bit according to $this->maxUni
1200 $nonBMP = $this- > get_ushort ( $os2_offset +
46 );
1201 $nonBMP = ( $nonBMP & ~
( 1 << 9 )); // Unset Bit 57 (indicates non-BMP) - for interactive forms
1203 $os2 = $this- > get_table ( 'OS/2' );
1204 if ( _RECALC_PROFILE
) {
1205 $os2 = $this- > _set_ushort ( $os2 , 62 , $fsSelection );
1206 $os2 = $this- > _set_ushort ( $os2 , 66 , $fsLastCharIndex );
1207 $os2 = $this- > _set_ushort ( $os2 , 42 , 0x0000 ); // ulCharRange (ulUnicodeRange) bits 24-31 | 16-23
1208 $os2 = $this- > _set_ushort ( $os2 , 44 , 0x0000 ); // ulCharRange (Unicode ranges) bits 8-15 | 0-7
1209 $os2 = $this- > _set_ushort ( $os2 , 46 , $nonBMP ); // ulCharRange (Unicode ranges) bits 56-63 | 48-55
1210 $os2 = $this- > _set_ushort ( $os2 , 48 , 0x0000 ); // ulCharRange (Unicode ranges) bits 40-47 | 32-39
1211 $os2 = $this- > _set_ushort ( $os2 , 50 , 0x0000 ); // ulCharRange (Unicode ranges) bits 88-95 | 80-87
1212 $os2 = $this- > _set_ushort ( $os2 , 52 , 0x0000 ); // ulCharRange (Unicode ranges) bits 72-79 | 64-71
1213 $os2 = $this- > _set_ushort ( $os2 , 54 , 0x0000 ); // ulCharRange (Unicode ranges) bits 120-127 | 112-119
1214 $os2 = $this- > _set_ushort ( $os2 , 56 , 0x0000 ); // ulCharRange (Unicode ranges) bits 104-111 | 96-103
1216 $os2 = $this- > _set_ushort ( $os2 , 46 , $nonBMP ); // Unset Bit 57 (indicates non-BMP) - for interactive forms
1218 $this- > add ( 'OS/2' , $os2 );
1222 // Put the TTF file together
1224 $this- > endTTFile ( $stm );
1225 //file_put_contents('testfont.ttf', $stm); exit;
1229 //================================================================================
1232 function makeSubsetSIP ( $file , & $subset , $TTCfontID = 0 , $debug = false ) {
1233 $this- > fh
= fopen ( $file , 'rb' ) or die ( 'Can \' t open file ' . $file );
1234 $this- > filename
= $file ;
1236 $this- > unAGlyphs
= false ; // mPDF 5.4.05
1237 $this- > charWidths
= '' ;
1238 $this- > glyphPos
= array ();
1239 $this- > charToGlyph
= array ();
1240 $this- > tables
= array ();
1241 $this- > otables
= array ();
1244 $this- > numTTCFonts
= 0 ;
1245 $this- > TTCFonts
= array ();
1247 if ( $TTCfontID > 0 ) {
1248 $this- > version
= $version = $this- > read_ulong (); // TTC Header version now
1249 if (! in_array ( $version , array ( 0x00010000 , 0x00020000 )))
1250 die ( "ERROR - Error parsing TrueType Collection: version=" . $version . " - " . $file );
1251 $this- > numTTCFonts
= $this- > read_ulong ();
1252 for ( $i = 1 ; $i <= $this- > numTTCFonts
; $i ++
) {
1253 $this- > TTCFonts
[ $i ][ 'offset' ] = $this- > read_ulong ();
1255 $this- > seek ( $this- > TTCFonts
[ $TTCfontID ][ 'offset' ]);
1256 $this- > version
= $version = $this- > read_ulong (); // TTFont version again now
1258 $this- > readTableDirectory ( $debug );
1262 ///////////////////////////////////
1263 // head - Font header table
1264 ///////////////////////////////////
1265 $this- > seek_table ( "head" );
1267 $indexToLocFormat = $this- > read_ushort ();
1268 $glyphDataFormat = $this- > read_ushort ();
1270 ///////////////////////////////////
1271 // hhea - Horizontal header table
1272 ///////////////////////////////////
1273 $this- > seek_table ( "hhea" );
1275 $metricDataFormat = $this- > read_ushort ();
1276 $orignHmetrics = $numberOfHMetrics = $this- > read_ushort ();
1278 ///////////////////////////////////
1279 // maxp - Maximum profile table
1280 ///////////////////////////////////
1281 $this- > seek_table ( "maxp" );
1283 $numGlyphs = $this- > read_ushort ();
1286 ///////////////////////////////////
1287 // cmap - Character to glyph index mapping table
1288 ///////////////////////////////////
1290 $cmap_offset = $this- > seek_table ( "cmap" );
1292 $cmapTableCount = $this- > read_ushort ();
1293 $unicode_cmap_offset = 0 ;
1294 for ( $i = 0 ; $i < $cmapTableCount ; $i ++
) {
1295 $platformID = $this- > read_ushort ();
1296 $encodingID = $this- > read_ushort ();
1297 $offset = $this- > read_ulong ();
1298 $save_pos = $this- > _pos
;
1299 if (( $platformID == 3 && $encodingID == 10 ) || $platformID == 0 ) { // Microsoft, Unicode Format 12 table HKCS
1300 $format = $this- > get_ushort ( $cmap_offset +
$offset );
1301 if ( $format == 12 ) {
1302 $unicode_cmap_offset = $cmap_offset +
$offset ;
1306 $this- > seek ( $save_pos );
1309 if (! $unicode_cmap_offset )
1310 die ( 'Font does not have cmap for Unicode (platform 3, encoding 1, format 4, or platform 0, any encoding, format 4)' );
1311 // Format 12 CMAP does characters above Unicode BMP i.e. some HKCS characters U+20000 and above
1312 if ( $format == 12 ) {
1313 $this- > maxUniChar
= 0 ;
1314 $this- > seek ( $unicode_cmap_offset +
4 );
1315 $length = $this- > read_ulong ();
1316 $limit = $unicode_cmap_offset +
$length ;
1319 $nGroups = $this- > read_ulong ();
1321 $glyphToChar = array ();
1322 $charToGlyph = array ();
1323 for ( $i = 0 ; $i < $nGroups ; $i ++
) {
1324 $startCharCode = $this- > read_ulong ();
1325 $endCharCode = $this- > read_ulong ();
1326 $startGlyphCode = $this- > read_ulong ();
1328 for ( $unichar = $startCharCode ; $unichar <= $endCharCode ; $unichar ++
) {
1329 $glyph = $startGlyphCode +
$offset ;
1331 $charToGlyph [ $unichar ] = $glyph ;
1332 if ( $unichar < 196608 ) { $this
-> maxUniChar
= max ( $unichar
, $this
-> maxUniChar
); }
1333 $glyphToChar [ $glyph ][] = $unichar ;
1338 die ( 'Font does not have cmap for Unicode (format 12)' );
1341 ///////////////////////////////////
1342 // hmtx - Horizontal metrics table
1343 ///////////////////////////////////
1344 $scale = 1 ; // not used here
1345 $this- > getHMTX ( $numberOfHMetrics , $numGlyphs , $glyphToChar , $scale );
1347 ///////////////////////////////////
1348 // loca - Index to location
1349 ///////////////////////////////////
1350 $this- > getLOCA ( $indexToLocFormat , $numGlyphs );
1352 ///////////////////////////////////////////////////////////////////
1354 $glyphMap = array ( 0 => 0 );
1355 $glyphSet = array ( 0 => 0 );
1356 $codeToGlyph = array ();
1357 // Set a substitute if ASCII characters do not have glyphs
1358 if ( isset ( $charToGlyph [ 0x3F ])) { $subs
= $charToGlyph
[ 0x3F ]; } // Question mark
1359 else { $subs
= $charToGlyph
[ 32 ]; }
1360 foreach ( $subset AS $code ) {
1361 if ( isset ( $charToGlyph [ $code ]))
1362 $originalGlyphIdx = $charToGlyph [ $code ];
1363 else if ( $code < 128 ) {
1364 $originalGlyphIdx = $subs ;
1366 else { $originalGlyphIdx
= 0 ; }
1367 if (! isset ( $glyphSet [ $originalGlyphIdx ])) {
1368 $glyphSet [ $originalGlyphIdx ] = count ( $glyphMap );
1369 $glyphMap [] = $originalGlyphIdx ;
1371 $codeToGlyph [ $code ] = $glyphSet [ $originalGlyphIdx ];
1374 list ( $start , $dummy ) = $this- > get_table_pos ( 'glyf' );
1377 while ( $n < count ( $glyphMap )) {
1378 $originalGlyphIdx = $glyphMap [ $n ];
1379 $glyphPos = $this- > glyphPos
[ $originalGlyphIdx ];
1380 $glyphLen = $this- > glyphPos
[ $originalGlyphIdx +
1 ] - $glyphPos ;
1382 if (! $glyphLen ) continue ;
1383 $this- > seek ( $start +
$glyphPos );
1384 $numberOfContours = $this- > read_short ();
1385 if ( $numberOfContours < 0 ) {
1388 while ( $flags & GF_MORE
) {
1389 $flags = $this- > read_ushort ();
1390 $glyphIdx = $this- > read_ushort ();
1391 if (! isset ( $glyphSet [ $glyphIdx ])) {
1392 $glyphSet [ $glyphIdx ] = count ( $glyphMap );
1393 $glyphMap [] = $glyphIdx ;
1395 if ( $flags & GF_WORDS
)
1399 if ( $flags & GF_SCALE
)
1401 else if ( $flags & GF_XYSCALE
)
1403 else if ( $flags & GF_TWOBYTWO
)
1409 $numGlyphs = $n = count ( $glyphMap );
1410 $numberOfHMetrics = $n ;
1412 ///////////////////////////////////
1414 ///////////////////////////////////
1415 // Needs to have a name entry in 3,0 (e.g. symbol) - original font will be 3,1 (i.e. Unicode)
1416 $name = $this- > get_table ( 'name' );
1417 $name_offset = $this- > seek_table ( "name" );
1418 $format = $this- > read_ushort ();
1419 $numRecords = $this- > read_ushort ();
1420 $string_data_offset = $name_offset +
$this- > read_ushort ();
1421 for ( $i = 0 ; $i < $numRecords ; $i ++
) {
1422 $platformId = $this- > read_ushort ();
1423 $encodingId = $this- > read_ushort ();
1424 if ( $platformId == 3 && $encodingId == 1 ) {
1425 $pos = 6 +
( $i * 12 ) +
2 ;
1426 $name = $this- > _set_ushort ( $name , $pos , 0x00 ); // Change encoding to 3,0 rather than 3,1
1430 $this- > add ( 'name' , $name );
1432 ///////////////////////////////////
1434 ///////////////////////////////////
1435 if ( isset ( $this- > tables
[ 'OS/2' ])) {
1436 $os2 = $this- > get_table ( 'OS/2' );
1437 $os2 = $this- > _set_ushort ( $os2 , 42 , 0x00 ); // ulCharRange (Unicode ranges)
1438 $os2 = $this- > _set_ushort ( $os2 , 44 , 0x00 ); // ulCharRange (Unicode ranges)
1439 $os2 = $this- > _set_ushort ( $os2 , 46 , 0x00 ); // ulCharRange (Unicode ranges)
1440 $os2 = $this- > _set_ushort ( $os2 , 48 , 0x00 ); // ulCharRange (Unicode ranges)
1442 $os2 = $this- > _set_ushort ( $os2 , 50 , 0x00 ); // ulCharRange (Unicode ranges)
1443 $os2 = $this- > _set_ushort ( $os2 , 52 , 0x00 ); // ulCharRange (Unicode ranges)
1444 $os2 = $this- > _set_ushort ( $os2 , 54 , 0x00 ); // ulCharRange (Unicode ranges)
1445 $os2 = $this- > _set_ushort ( $os2 , 56 , 0x00 ); // ulCharRange (Unicode ranges)
1446 // Set Symbol character only in ulCodePageRange
1447 $os2 = $this- > _set_ushort ( $os2 , 78 , 0x8000 ); // ulCodePageRange = Bit #31 Symbol **** 78 = Bit 16-31
1448 $os2 = $this- > _set_ushort ( $os2 , 80 , 0x0000 ); // ulCodePageRange = Bit #31 Symbol **** 80 = Bit 0-15
1449 $os2 = $this- > _set_ushort ( $os2 , 82 , 0x0000 ); // ulCodePageRange = Bit #32- Symbol **** 82 = Bits 48-63
1450 $os2 = $this- > _set_ushort ( $os2 , 84 , 0x0000 ); // ulCodePageRange = Bit #32- Symbol **** 84 = Bits 32-47
1452 $os2 = $this- > _set_ushort ( $os2 , 64 , 0x01 ); // FirstCharIndex
1453 $os2 = $this- > _set_ushort ( $os2 , 66 , count ( $subset )); // LastCharIndex
1454 // Set PANOSE first bit to 5 for Symbol
1455 $os2 = $this- > splice ( $os2 , 32 , chr ( 5 ). chr ( 0 ). chr ( 1 ). chr ( 0 ). chr ( 1 ). chr ( 0 ). chr ( 0 ). chr ( 0 ). chr ( 0 ). chr ( 0 ));
1456 $this- > add ( 'OS/2' , $os2 );
1460 ///////////////////////////////////
1461 //tables copied from the original
1462 ///////////////////////////////////
1463 $tags = array ( 'cvt ' , 'fpgm' , 'prep' , 'gasp' );
1464 foreach ( $tags AS $tag ) { // 1.02
1465 if ( isset ( $this- > tables
[ $tag ])) { $this
-> add ( $tag
, $this
-> get_table ( $tag
)); }
1468 ///////////////////////////////////
1469 // post - PostScript
1470 ///////////////////////////////////
1471 if ( isset ( $this- > tables
[ 'post' ])) {
1472 $opost = $this- > get_table ( 'post' );
1473 $post = " \x00\x03\x00\x00 " . substr ( $opost , 4 , 12 ) . " \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00 " ;
1475 $this- > add ( 'post' , $post );
1477 ///////////////////////////////////
1478 // hhea - Horizontal Header
1479 ///////////////////////////////////
1480 $hhea = $this- > get_table ( 'hhea' );
1481 $hhea = $this- > _set_ushort ( $hhea , 34 , $numberOfHMetrics );
1482 $this- > add ( 'hhea' , $hhea );
1484 ///////////////////////////////////
1485 // maxp - Maximum Profile
1486 ///////////////////////////////////
1487 $maxp = $this- > get_table ( 'maxp' );
1488 $maxp = $this- > _set_ushort ( $maxp , 4 , $numGlyphs );
1489 $this- > add ( 'maxp' , $maxp );
1492 ///////////////////////////////////
1493 // CMap table Formats [1,0,]6 and [3,0,]4
1494 ///////////////////////////////////
1495 ///////////////////////////////////
1496 // Sort CID2GID map into segments of contiguous codes
1497 ///////////////////////////////////
1502 // for each character
1503 foreach ( $subset as $cid => $code ) {
1504 $glidx = $codeToGlyph [ $code ];
1505 if ( $cid == ( $prevcid +
1 ) && $glidx == ( $prevglidx +
1 )) {
1506 $range [ $rangeid ][] = $glidx ;
1510 $range [ $rangeid ] = array ();
1511 $range [ $rangeid ][] = $glidx ;
1514 $prevglidx = $glidx ;
1516 // cmap - Character to glyph mapping
1517 $segCount = count ( $range ) +
1 ; // + 1 Last segment has missing character 0xFFFF
1520 while ( $searchRange * 2 <= $segCount ) {
1521 $searchRange = $searchRange * 2 ;
1522 $entrySelector = $entrySelector +
1 ;
1524 $searchRange = $searchRange * 2 ;
1525 $rangeShift = $segCount * 2 - $searchRange ;
1526 $length = 16 +
( 8 * $segCount ) +
( $numGlyphs +
1 );
1528 4 , $length , 0 , // Format 4 Mapping subtable: format, length, language
1535 foreach ( $range AS $start => $subrange ) {
1536 $endCode = $start +
( count ( $subrange )- 1 );
1537 $cmap [] = $endCode ; // endCode(s)
1539 $cmap [] = 0xFFFF ; // endCode of last Segment
1540 $cmap [] = 0 ; // reservedPad
1543 foreach ( $range AS $start => $subrange ) {
1544 $cmap [] = $start ; // startCode(s)
1546 $cmap [] = 0xFFFF ; // startCode of last Segment
1548 foreach ( $range AS $start => $subrange ) {
1549 $idDelta = -( $start-$subrange [ 0 ]);
1550 $n +
= count ( $subrange );
1551 $cmap [] = $idDelta ; // idDelta(s)
1553 $cmap [] = 1 ; // idDelta of last Segment
1555 foreach ( $range AS $subrange ) {
1556 $cmap [] = 0 ; // idRangeOffset[segCount] Offset in bytes to glyph indexArray, or 0
1559 $cmap [] = 0 ; // idRangeOffset of last Segment
1560 foreach ( $range AS $subrange ) {
1561 foreach ( $subrange AS $glidx ) {
1565 $cmap [] = 0 ; // Mapping for last character
1567 foreach ( $cmap AS $cm ) { $cmapstr4
.= pack ( "n" , $cm
); }
1569 ///////////////////////////////////
1570 // cmap - Character to glyph mapping
1571 ///////////////////////////////////
1572 $entryCount = count ( $subset );
1573 $length = 10 +
$entryCount * 2 ;
1575 $off = 20 +
$length ;
1577 $loff = $off & 0xFFFF ;
1579 $cmap = array ( 0 , 2 , // Index : version, number of subtables
1580 1 , 0 , // Subtable : platform, encoding
1581 0 , 20 , // offset (hi,lo)
1582 3 , 0 , // Subtable : platform, encoding
1583 $hoff , $loff , // offset (hi,lo)
1584 6 , $length , // Format 6 Mapping table: format, length
1585 0 , 1 , // language, First char code
1589 foreach ( $subset AS $code ) { $cmap
[] = $codeToGlyph
[ $code
]; }
1590 foreach ( $cmap AS $cm ) { $cmapstr
.= pack ( "n" , $cm
); }
1591 $cmapstr .= $cmapstr4 ;
1592 $this- > add ( 'cmap' , $cmapstr );
1594 ///////////////////////////////////
1595 // hmtx - Horizontal Metrics
1596 ///////////////////////////////////
1598 for ( $n = 0 ; $n < $numGlyphs ; $n ++
) {
1599 $originalGlyphIdx = $glyphMap [ $n ];
1600 $hm = $this- > getHMetric ( $orignHmetrics , $originalGlyphIdx );
1603 $this- > add ( 'hmtx' , $hmtxstr );
1605 ///////////////////////////////////
1606 // glyf - Glyph data
1607 ///////////////////////////////////
1608 list ( $glyfOffset , $glyfLength ) = $this- > get_table_pos ( 'glyf' );
1609 if ( $glyfLength < $this- > maxStrLenRead
) {
1610 $glyphData = $this- > get_table ( 'glyf' );
1616 for ( $n = 0 ; $n < $numGlyphs ; $n ++
) {
1618 $originalGlyphIdx = $glyphMap [ $n ];
1619 $glyphPos = $this- > glyphPos
[ $originalGlyphIdx ];
1620 $glyphLen = $this- > glyphPos
[ $originalGlyphIdx +
1 ] - $glyphPos ;
1621 if ( $glyfLength < $this- > maxStrLenRead
) {
1622 $data = substr ( $glyphData , $glyphPos , $glyphLen );
1625 if ( $glyphLen > 0 ) $data = $this- > get_chunk ( $glyfOffset +
$glyphPos , $glyphLen );
1628 if ( $glyphLen > 0 ) $up = unpack ( "n" , substr ( $data , 0 , 2 ));
1629 if ( $glyphLen > 2 && ( $up [ 1 ] & ( 1 << 15 )) ) {
1632 while ( $flags & GF_MORE
) {
1633 $up = unpack ( "n" , substr ( $data , $pos_in_glyph , 2 ));
1635 $up = unpack ( "n" , substr ( $data , $pos_in_glyph +
2 , 2 ));
1637 $data = $this- > _set_ushort ( $data , $pos_in_glyph +
2 , $glyphSet [ $glyphIdx ]);
1639 if ( $flags & GF_WORDS
) { $pos_in_glyph +
= 4 ; }
1640 else { $pos_in_glyph +
= 2 ; }
1641 if ( $flags & GF_SCALE
) { $pos_in_glyph +
= 2 ; }
1642 else if ( $flags & GF_XYSCALE
) { $pos_in_glyph +
= 4 ; }
1643 else if ( $flags & GF_TWOBYTWO
) { $pos_in_glyph +
= 8 ; }
1648 if ( $pos %
4 != 0 ) {
1649 $padding = 4 - ( $pos %
4 );
1650 $glyf .= str_repeat ( "\0" , $padding );
1655 $this- > add ( 'glyf' , $glyf );
1657 ///////////////////////////////////
1658 // loca - Index to location
1659 ///////////////////////////////////
1661 if ((( $pos +
1 ) >> 1 ) > 0xFFFF ) {
1662 $indexToLocFormat = 1 ; // long format
1663 foreach ( $offsets AS $offset ) { $locastr
.= pack ( "N" , $offset
); }
1666 $indexToLocFormat = 0 ; // short format
1667 foreach ( $offsets AS $offset ) { $locastr
.= pack ( "n" ,( $offset
/ 2 )); }
1669 $this- > add ( 'loca' , $locastr );
1671 ///////////////////////////////////
1672 // head - Font header
1673 ///////////////////////////////////
1674 $head = $this- > get_table ( 'head' );
1675 $head = $this- > _set_ushort ( $head , 50 , $indexToLocFormat );
1676 $this- > add ( 'head' , $head );
1680 // Put the TTF file together
1682 $this- > endTTFile ( $stm );
1683 //file_put_contents('testfont.ttf', $stm); exit;
1687 //////////////////////////////////////////////////////////////////////////////////
1688 // Recursively get composite glyph data
1689 function getGlyphData ( $originalGlyphIdx , & $maxdepth , & $depth , & $points , & $contours ) {
1691 $maxdepth = max ( $maxdepth , $depth );
1692 if ( count ( $this- > glyphdata
[ $originalGlyphIdx ][ 'compGlyphs' ])) {
1693 foreach ( $this- > glyphdata
[ $originalGlyphIdx ][ 'compGlyphs' ] AS $glyphIdx ) {
1694 $this- > getGlyphData ( $glyphIdx , $maxdepth , $depth , $points , $contours );
1697 else if (( $this- > glyphdata
[ $originalGlyphIdx ][ 'nContours' ] > 0 ) && $depth > 0 ) { // simple
1698 $contours +
= $this- > glyphdata
[ $originalGlyphIdx ][ 'nContours' ];
1699 $points +
= $this- > glyphdata
[ $originalGlyphIdx ][ 'nPoints' ];
1705 //////////////////////////////////////////////////////////////////////////////////
1706 // Recursively get composite glyphs
1707 function getGlyphs ( $originalGlyphIdx , & $start , & $glyphSet , & $subsetglyphs ) {
1708 $glyphPos = $this- > glyphPos
[ $originalGlyphIdx ];
1709 $glyphLen = $this- > glyphPos
[ $originalGlyphIdx +
1 ] - $glyphPos ;
1713 $this- > seek ( $start +
$glyphPos );
1714 $numberOfContours = $this- > read_short ();
1715 if ( $numberOfContours < 0 ) {
1718 while ( $flags & GF_MORE
) {
1719 $flags = $this- > read_ushort ();
1720 $glyphIdx = $this- > read_ushort ();
1721 if (! isset ( $glyphSet [ $glyphIdx ])) {
1722 $glyphSet [ $glyphIdx ] = count ( $subsetglyphs ); // old glyphID to new glyphID
1723 $subsetglyphs [ $glyphIdx ] = true ;
1725 $savepos = ftell ( $this- > fh
);
1726 $this- > getGlyphs ( $glyphIdx , $start , $glyphSet , $subsetglyphs );
1727 $this- > seek ( $savepos );
1728 if ( $flags & GF_WORDS
)
1732 if ( $flags & GF_SCALE
)
1734 else if ( $flags & GF_XYSCALE
)
1736 else if ( $flags & GF_TWOBYTWO
)
1742 //////////////////////////////////////////////////////////////////////////////////
1744 function getHMTX ( $numberOfHMetrics , $numGlyphs , & $glyphToChar , $scale ) {
1745 $start = $this- > seek_table ( "hmtx" );
1747 $this- > charWidths
= str_pad ( '' , 256 * 256 * 2 , " \x00 " );
1748 if ( $this- > maxUniChar
> 65536 ) { $this
-> charWidths
.= str_pad ( '' , 256 * 256 * 2 , " \x00 " ); } // Plane 1 SMP
1749 if ( $this- > maxUniChar
> 131072 ) { $this
-> charWidths
.= str_pad ( '' , 256 * 256 * 2 , " \x00 " ); } // Plane 2 SMP
1751 if (( $numberOfHMetrics * 4 ) < $this- > maxStrLenRead
) {
1752 $data = $this- > get_chunk ( $start ,( $numberOfHMetrics * 4 ));
1753 $arr = unpack ( "n*" , $data );
1755 else { $this
-> seek ( $start
); }
1756 for ( $glyph = 0 ; $glyph < $numberOfHMetrics ; $glyph ++
) {
1757 if (( $numberOfHMetrics * 4 ) < $this- > maxStrLenRead
) {
1758 $aw = $arr [( $glyph * 2 ) +
1 ];
1761 $aw = $this- > read_ushort ();
1762 $lsb = $this- > read_ushort ();
1764 if ( isset ( $glyphToChar [ $glyph ]) || $glyph == 0 ) {
1766 if ( $aw >= ( 1 << 15 ) ) { $aw
= 0 ; } // 1.03 Some (arabic) fonts have -ve values for width
1767 // although should be unsigned value - comes out as e.g. 65108 (intended -50)
1769 $this- > defaultWidth
= $scale * $aw ;
1772 foreach ( $glyphToChar [ $glyph ] AS $char ) {
1773 //$this->charWidths[$char] = intval(round($scale*$aw));
1774 if ( $char != 0 && $char != 65535 ) {
1775 $w = intval ( round ( $scale * $aw ));
1776 if ( $w == 0 ) { $w
= 65535 ; }
1777 if ( $char < 196608 ) {
1778 $this- > charWidths
[ $char * 2 ] = chr ( $w >> 8 );
1779 $this- > charWidths
[ $char * 2 +
1 ] = chr ( $w & 0xFF );
1786 $data = $this- > get_chunk (( $start +
$numberOfHMetrics * 4 ),( $numGlyphs * 2 ));
1787 $arr = unpack ( "n*" , $data );
1788 $diff = $numGlyphs-$numberOfHMetrics ;
1789 $w = intval ( round ( $scale * $aw ));
1790 if ( $w == 0 ) { $w
= 65535 ; }
1791 for ( $pos = 0 ; $pos < $diff ; $pos ++
) {
1792 $glyph = $pos +
$numberOfHMetrics ;
1793 if ( isset ( $glyphToChar [ $glyph ])) {
1794 foreach ( $glyphToChar [ $glyph ] AS $char ) {
1795 if ( $char != 0 && $char != 65535 ) {
1796 if ( $char < 196608 ) {
1797 $this- > charWidths
[ $char * 2 ] = chr ( $w >> 8 );
1798 $this- > charWidths
[ $char * 2 +
1 ] = chr ( $w & 0xFF );
1805 // NB 65535 is a set width of 0
1806 // First bytes define number of chars in font
1807 $this- > charWidths
[ 0 ] = chr ( $nCharWidths >> 8 );
1808 $this- > charWidths
[ 1 ] = chr ( $nCharWidths & 0xFF );
1811 function getHMetric ( $numberOfHMetrics , $gid ) {
1812 $start = $this- > seek_table ( "hmtx" );
1813 if ( $gid < $numberOfHMetrics ) {
1814 $this- > seek ( $start +
( $gid * 4 ));
1815 $hm = fread ( $this- > fh
, 4 );
1818 $this- > seek ( $start +
(( $numberOfHMetrics-1 )* 4 ));
1819 $hm = fread ( $this- > fh
, 2 );
1820 $this- > seek ( $start +
( $numberOfHMetrics * 2 ) +
( $gid * 2 ));
1821 $hm .= fread ( $this- > fh
, 2 );
1826 function getLOCA ( $indexToLocFormat , $numGlyphs ) {
1827 $start = $this- > seek_table ( 'loca' );
1828 $this- > glyphPos
= array ();
1829 if ( $indexToLocFormat == 0 ) {
1830 $data = $this- > get_chunk ( $start ,( $numGlyphs * 2 ) +
2 );
1831 $arr = unpack ( "n*" , $data );
1832 for ( $n = 0 ; $n <= $numGlyphs ; $n ++
) {
1833 $this- > glyphPos
[] = ( $arr [ $n +
1 ] * 2 );
1836 else if ( $indexToLocFormat == 1 ) {
1837 $data = $this- > get_chunk ( $start ,( $numGlyphs * 4 ) +
4 );
1838 $arr = unpack ( "N*" , $data );
1839 for ( $n = 0 ; $n <= $numGlyphs ; $n ++
) {
1840 $this- > glyphPos
[] = ( $arr [ $n +
1 ]);
1844 die ( 'Unknown location table format ' . $indexToLocFormat );
1849 function getCMAP4 ( $unicode_cmap_offset , & $glyphToChar , & $charToGlyph ) {
1850 $this- > maxUniChar
= 0 ;
1851 $this- > seek ( $unicode_cmap_offset +
2 );
1852 $length = $this- > read_ushort ();
1853 $limit = $unicode_cmap_offset +
$length ;
1856 $segCount = $this- > read_ushort () / 2 ;
1858 $endCount = array ();
1859 for ( $i = 0 ; $i < $segCount ; $i ++
) { $endCount
[] = $this
-> read_ushort (); }
1861 $startCount = array ();
1862 for ( $i = 0 ; $i < $segCount ; $i ++
) { $startCount
[] = $this
-> read_ushort (); }
1864 for ( $i = 0 ; $i < $segCount ; $i ++
) { $idDelta
[] = $this
-> read_short (); } // ???? was unsigned short
1865 $idRangeOffset_start = $this- > _pos
;
1866 $idRangeOffset = array ();
1867 for ( $i = 0 ; $i < $segCount ; $i ++
) { $idRangeOffset
[] = $this
-> read_ushort (); }
1869 for ( $n = 0 ; $n < $segCount ; $n ++
) {
1870 $endpoint = ( $endCount [ $n ] +
1 );
1871 for ( $unichar = $startCount [ $n ]; $unichar < $endpoint ; $unichar ++
) {
1872 if ( $idRangeOffset [ $n ] == 0 )
1873 $glyph = ( $unichar +
$idDelta [ $n ]) & 0xFFFF ;
1875 $offset = ( $unichar - $startCount [ $n ]) * 2 +
$idRangeOffset [ $n ];
1876 $offset = $idRangeOffset_start +
2 * $n +
$offset ;
1877 if ( $offset >= $limit )
1880 $glyph = $this- > get_ushort ( $offset );
1882 $glyph = ( $glyph +
$idDelta [ $n ]) & 0xFFFF ;
1885 $charToGlyph [ $unichar ] = $glyph ;
1886 if ( $unichar < 196608 ) { $this
-> maxUniChar
= max ( $unichar
, $this
-> maxUniChar
); }
1887 $glyphToChar [ $glyph ][] = $unichar ;
1892 if ( $this- > unAGlyphs
) {
1893 if ( isset ( $this- > tables
[ 'post' ])) {
1894 $this- > seek_table ( "post" );
1895 $formata = $this- > read_ushort ();
1896 $formatb = $this- > read_ushort ();
1897 // Only works on Format 2.0
1898 if ( $formata != 2 || $formatb != 0 ) { die ( "Cannot set unAGlyphs for this font (" . $file
. "). POST table must be in Format 2." ); }
1900 $nGlyfs = $this- > read_ushort ();
1901 $glyphNameIndex = array ();
1902 for ( $i = 0 ; $i < $nGlyfs ; $i ++
) {
1903 $glyphNameIndex [( $this- > read_ushort ())] = $i ;
1906 $opost = $this- > get_table ( 'post' );
1907 $ptr = 34 +
( $nGlyfs * 2 );
1908 for ( $i = 0 ; $i < $nGlyfs ; $i ++
) {
1909 $len = ord ( substr ( $opost , $ptr , 1 ));
1911 $name = substr ( $opost , $ptr , $len );
1912 $gid = $glyphNameIndex [ $i +
258 ];
1913 // Select uni0600.xxx(x) - uni06FF.xxx(x)
1914 if ( preg_match ( '/^uni(06[0-9a-f] {2} )\.(fina|medi|init|fin|med|ini)$/i' , $name , $m )) {
1915 if (! isset ( $glyphToChar [ $gid ]) || ( isset ( $glyphToChar [ $gid ]) && is_array ( $glyphToChar [ $gid ]) && count ( $glyphToChar [ $gid ])== 1 && $glyphToChar [ $gid ][ 0 ]> 57343 && $glyphToChar [ $gid ][ 0 ]< 63489 )) { // if set in PUA private use area E000-F8FF, or NOT Unicode mapped
1916 $uni = hexdec ( $m [ 1 ]);
1917 $form = strtoupper ( substr ( $m [ 2 ], 0 , 1 ));
1918 // Assign new PUA Unicode between F500 - F7FF
1920 if ( $form == 'I' ) { $bit +
= 0xF600 ; }
1921 else if ( $form == 'M' ) { $bit +
= 0xF700 ; }
1922 else { $bit +
= 0xF500 ; }
1924 $glyphToChar [ $gid ][] = $bit ;
1925 $charToGlyph [ $bit ] = $gid ;
1928 // LAM with ALEF ligatures (Mandatory ligatures)
1929 else if ( preg_match ( '/^uni064406(22|23|25|27)(\.fina|\.fin) {0,1} $/i' , $name , $m )) {
1931 if ( $m [ 2 ]) { $uni
= hexdec ( 'FEF6' ); } else { $uni
= hexdec ( 'FEF5' ); }
1933 else if ( $m [ 1 ]== '23' ) {
1934 if ( $m [ 2 ]) { $uni
= hexdec ( 'FEF8' ); } else { $uni
= hexdec ( 'FEF7' ); }
1936 else if ( $m [ 1 ]== '25' ) {
1937 if ( $m [ 2 ]) { $uni
= hexdec ( 'FEFA' ); } else { $uni
= hexdec ( 'FEF9' ); }
1939 else if ( $m [ 1 ]== '27' ) {
1940 if ( $m [ 2 ]) { $uni
= hexdec ( 'FEFC' ); } else { $uni
= hexdec ( 'FEFB' ); }
1942 if (! isset ( $glyphToChar [ $gid ]) || ( isset ( $glyphToChar [ $gid ]) && is_array ( $glyphToChar [ $gid ]) && count ( $glyphToChar [ $gid ])== 1 && $glyphToChar [ $gid ][ 0 ]> 57343 && $glyphToChar [ $gid ][ 0 ]< 63489 )) { // if set in PUA private use area E000-F8FF, or NOT Unicode mapped
1944 $glyphToChar [ $gid ][] = $uni ;
1945 $charToGlyph [ $uni ] = $gid ;
1956 // Put the TTF file together
1957 function endTTFile (& $stm ) {
1959 $numTables = count ( $this- > otables
);
1962 while ( $searchRange * 2 <= $numTables ) {
1963 $searchRange = $searchRange * 2 ;
1964 $entrySelector = $entrySelector +
1 ;
1966 $searchRange = $searchRange * 16 ;
1967 $rangeShift = $numTables * 16 - $searchRange ;
1970 if ( _TTF_MAC_HEADER
) {
1971 $stm .= ( pack ( "Nnnnn" , 0x74727565 , $numTables , $searchRange , $entrySelector , $rangeShift )); // Mac
1974 $stm .= ( pack ( "Nnnnn" , 0x00010000 , $numTables , $searchRange , $entrySelector , $rangeShift )); // Windows
1978 $tables = $this- > otables
;
1980 $offset = 12 +
$numTables * 16 ;
1981 foreach ( $tables AS $tag => $data ) {
1982 if ( $tag == 'head' ) { $head_start
= $offset
; }
1984 $checksum = $this- > calcChecksum ( $data );
1985 $stm .= pack ( "nn" , $checksum [ 0 ], $checksum [ 1 ]);
1986 $stm .= pack ( "NN" , $offset , strlen ( $data ));
1987 $paddedLength = ( strlen ( $data ) +
3 )& ~
3 ;
1988 $offset = $offset +
$paddedLength ;
1992 foreach ( $tables AS $tag => $data ) {
1994 $stm .= substr ( $data , 0 ,( strlen ( $data )& ~
3 ));
1997 $checksum = $this- > calcChecksum ( $stm );
1998 $checksum = $this- > sub32 ( array ( 0xB1B0 , 0xAFBA ), $checksum );
1999 $chk = pack ( "nn" , $checksum [ 0 ], $checksum [ 1 ]);
2000 $stm = $this- > splice ( $stm ,( $head_start +
8 ), $chk );
2005 function repackageTTF ( $file , $TTCfontID = 0 , $debug = false , $unAGlyphs = false ) { // mPDF 5.4.05
2006 $this- > unAGlyphs
= $unAGlyphs ; // mPDF 5.4.05
2007 $this- > filename
= $file ;
2008 $this- > fh
= fopen ( $file , 'rb' ) or die ( 'Can \' t open file ' . $file );
2010 $this- > charWidths
= '' ;
2011 $this- > glyphPos
= array ();
2012 $this- > charToGlyph
= array ();
2013 $this- > tables
= array ();
2014 $this- > otables
= array ();
2017 $this- > numTTCFonts
= 0 ;
2018 $this- > TTCFonts
= array ();
2021 if ( $TTCfontID > 0 ) {
2022 $this- > version
= $version = $this- > read_ulong (); // TTC Header version now
2023 if (! in_array ( $version , array ( 0x00010000 , 0x00020000 )))
2024 die ( "ERROR - Error parsing TrueType Collection: version=" . $version . " - " . $file );
2025 $this- > numTTCFonts
= $this- > read_ulong ();
2026 for ( $i = 1 ; $i <= $this- > numTTCFonts
; $i ++
) {
2027 $this- > TTCFonts
[ $i ][ 'offset' ] = $this- > read_ulong ();
2029 $this- > seek ( $this- > TTCFonts
[ $TTCfontID ][ 'offset' ]);
2030 $this- > version
= $version = $this- > read_ulong (); // TTFont version again now
2032 $this- > readTableDirectory ( $debug );
2033 $tags = array ( 'OS/2' , 'cmap' , 'glyf' , 'head' , 'hhea' , 'hmtx' , 'loca' , 'maxp' , 'name' , 'post' , 'cvt ' , 'fpgm' , 'gasp' , 'prep' );
2035 Tables which require glyphIndex
2040 Tables which do NOT require glyphIndex
2049 PCLT - not recommended
2052 foreach ( $tags AS $tag ) {
2053 if ( isset ( $this- > tables
[ $tag ])) { $this
-> add ( $tag
, $this
-> get_table ( $tag
)); }
2057 $this- > endTTFile ( $stm );