diff options
Diffstat (limited to 'inc/3rdparty/libraries/mpdf/classes/ttfontsuni.php')
-rw-r--r-- | inc/3rdparty/libraries/mpdf/classes/ttfontsuni.php | 2065 |
1 files changed, 2065 insertions, 0 deletions
diff --git a/inc/3rdparty/libraries/mpdf/classes/ttfontsuni.php b/inc/3rdparty/libraries/mpdf/classes/ttfontsuni.php new file mode 100644 index 00000000..1c93b600 --- /dev/null +++ b/inc/3rdparty/libraries/mpdf/classes/ttfontsuni.php | |||
@@ -0,0 +1,2065 @@ | |||
1 | <?php | ||
2 | |||
3 | /******************************************************************************* | ||
4 | * TTFontFile class * | ||
5 | * * | ||
6 | * Version: 2.01 * | ||
7 | * Date: 2012-02-25 * | ||
8 | * Author: Ian Back <ianb@bpm1.com> * | ||
9 | * License: LGPL * | ||
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. * | ||
16 | * * | ||
17 | *******************************************************************************/ | ||
18 | |||
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); | ||
25 | |||
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); | ||
29 | |||
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)); | ||
36 | |||
37 | |||
38 | |||
39 | class TTFontFile { | ||
40 | |||
41 | var $unAGlyphs; // mPDF 5.4.05 | ||
42 | var $panose; | ||
43 | var $maxUni; | ||
44 | var $sFamilyClass; | ||
45 | var $sFamilySubClass; | ||
46 | var $sipset; | ||
47 | var $smpset; | ||
48 | var $_pos; | ||
49 | var $numTables; | ||
50 | var $searchRange; | ||
51 | var $entrySelector; | ||
52 | var $rangeShift; | ||
53 | var $tables; | ||
54 | var $otables; | ||
55 | var $filename; | ||
56 | var $fh; | ||
57 | var $glyphPos; | ||
58 | var $charToGlyph; | ||
59 | var $ascent; | ||
60 | var $descent; | ||
61 | var $name; | ||
62 | var $familyName; | ||
63 | var $styleName; | ||
64 | var $fullName; | ||
65 | var $uniqueFontID; | ||
66 | var $unitsPerEm; | ||
67 | var $bbox; | ||
68 | var $capHeight; | ||
69 | var $stemV; | ||
70 | var $italicAngle; | ||
71 | var $flags; | ||
72 | var $underlinePosition; | ||
73 | var $underlineThickness; | ||
74 | var $charWidths; | ||
75 | var $defaultWidth; | ||
76 | var $maxStrLenRead; | ||
77 | var $numTTCFonts; | ||
78 | var $TTCFonts; | ||
79 | var $maxUniChar; | ||
80 | var $kerninfo; | ||
81 | |||
82 | function TTFontFile() { | ||
83 | $this->maxStrLenRead = 200000; // Maximum size of glyf table to read in as string (otherwise reads each glyph from file) | ||
84 | } | ||
85 | |||
86 | |||
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); | ||
91 | $this->_pos = 0; | ||
92 | $this->charWidths = ''; | ||
93 | $this->glyphPos = array(); | ||
94 | $this->charToGlyph = array(); | ||
95 | $this->tables = array(); | ||
96 | $this->otables = array(); | ||
97 | $this->kerninfo = array(); | ||
98 | $this->ascent = 0; | ||
99 | $this->descent = 0; | ||
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(); | ||
117 | } | ||
118 | $this->seek($this->TTCFonts[$TTCfontID]['offset']); | ||
119 | $this->version = $version = $this->read_ulong(); // TTFont version again now | ||
120 | } | ||
121 | $this->readTableDirectory($debug); | ||
122 | $this->extractInfo($debug, $BMPonly, $kerninfo); | ||
123 | fclose($this->fh); | ||
124 | } | ||
125 | |||
126 | |||
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++) { | ||
134 | $record = array(); | ||
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; | ||
140 | } | ||
141 | if ($debug) $this->checksumTables(); | ||
142 | } | ||
143 | |||
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); | ||
155 | } | ||
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]))); | ||
159 | } | ||
160 | } | ||
161 | } | ||
162 | |||
163 | function sub32($x, $y) { | ||
164 | $xlo = $x[1]; | ||
165 | $xhi = $x[0]; | ||
166 | $ylo = $y[1]; | ||
167 | $yhi = $y[0]; | ||
168 | if ($ylo > $xlo) { $xlo += 1 << 16; $yhi += 1; } | ||
169 | $reslo = $xlo-$ylo; | ||
170 | if ($yhi > $xhi) { $xhi += 1 << 16; } | ||
171 | $reshi = $xhi-$yhi; | ||
172 | $reshi = $reshi & 0xFFFF; | ||
173 | return array($reshi, $reslo); | ||
174 | } | ||
175 | |||
176 | function calcChecksum($data) { | ||
177 | if (strlen($data) % 4) { $data .= str_repeat("\0",(4-(strlen($data) % 4))); } | ||
178 | $len = strlen($data); | ||
179 | $hi=0x0000; | ||
180 | $lo=0x0000; | ||
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; | ||
185 | $lo = $lo & 0xFFFF; | ||
186 | } | ||
187 | return array($hi, $lo); | ||
188 | } | ||
189 | |||
190 | function get_table_pos($tag) { | ||
191 | $offset = $this->tables[$tag]['offset']; | ||
192 | $length = $this->tables[$tag]['length']; | ||
193 | return array($offset, $length); | ||
194 | } | ||
195 | |||
196 | function seek($pos) { | ||
197 | $this->_pos = $pos; | ||
198 | fseek($this->fh,$this->_pos); | ||
199 | } | ||
200 | |||
201 | function skip($delta) { | ||
202 | $this->_pos = $this->_pos + $delta; | ||
203 | fseek($this->fh,$delta,SEEK_CUR); | ||
204 | } | ||
205 | |||
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); | ||
210 | return $this->_pos; | ||
211 | } | ||
212 | |||
213 | function read_tag() { | ||
214 | $this->_pos += 4; | ||
215 | return fread($this->fh,4); | ||
216 | } | ||
217 | |||
218 | function read_short() { | ||
219 | $this->_pos += 2; | ||
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)); | ||
224 | } | ||
225 | return $a; | ||
226 | } | ||
227 | |||
228 | function unpack_short($s) { | ||
229 | $a = (ord($s[0])<<8) + ord($s[1]); | ||
230 | if ($a & (1 << 15) ) { | ||
231 | $a = ($a - (1 << 16)); | ||
232 | } | ||
233 | return $a; | ||
234 | } | ||
235 | |||
236 | function read_ushort() { | ||
237 | $this->_pos += 2; | ||
238 | $s = fread($this->fh,2); | ||
239 | return (ord($s[0])<<8) + ord($s[1]); | ||
240 | } | ||
241 | |||
242 | function read_ulong() { | ||
243 | $this->_pos += 4; | ||
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 | ||
247 | } | ||
248 | |||
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]); | ||
253 | } | ||
254 | |||
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 | ||
260 | } | ||
261 | |||
262 | function pack_short($val) { | ||
263 | if ($val<0) { | ||
264 | $val = abs($val); | ||
265 | $val = ~$val; | ||
266 | $val += 1; | ||
267 | } | ||
268 | return pack("n",$val); | ||
269 | } | ||
270 | |||
271 | function splice($stream, $offset, $value) { | ||
272 | return substr($stream,0,$offset) . $value . substr($stream,$offset+strlen($value)); | ||
273 | } | ||
274 | |||
275 | function _set_ushort($stream, $offset, $value) { | ||
276 | $up = pack("n", $value); | ||
277 | return $this->splice($stream, $offset, $up); | ||
278 | } | ||
279 | |||
280 | function _set_short($stream, $offset, $val) { | ||
281 | if ($val<0) { | ||
282 | $val = abs($val); | ||
283 | $val = ~$val; | ||
284 | $val += 1; | ||
285 | } | ||
286 | $up = pack("n",$val); | ||
287 | return $this->splice($stream, $offset, $up); | ||
288 | } | ||
289 | |||
290 | function get_chunk($pos, $length) { | ||
291 | fseek($this->fh,$pos); | ||
292 | if ($length <1) { return ''; } | ||
293 | return (fread($this->fh,$length)); | ||
294 | } | ||
295 | |||
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)); | ||
301 | } | ||
302 | |||
303 | function add($tag, $data) { | ||
304 | if ($tag == 'head') { | ||
305 | $data = $this->splice($data, 8, "\0\0\0\0"); | ||
306 | } | ||
307 | $this->otables[$tag] = $data; | ||
308 | } | ||
309 | |||
310 | |||
311 | |||
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); | ||
317 | $this->_pos = 0; | ||
318 | $this->charWidths = ''; | ||
319 | $this->glyphPos = array(); | ||
320 | $this->charToGlyph = array(); | ||
321 | $this->tables = array(); | ||
322 | $this->numTTCFonts = 0; | ||
323 | $this->TTCFonts = array(); | ||
324 | $this->skip(4); | ||
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(); | ||
332 | } | ||
333 | $this->seek($this->TTCFonts[$TTCfontID]['offset']); | ||
334 | $this->version = $version = $this->read_ulong(); // TTFont version again now | ||
335 | } | ||
336 | $this->readTableDirectory($debug); | ||
337 | |||
338 | |||
339 | // cmap - Character to glyph index mapping table | ||
340 | $cmap_offset = $this->seek_table("cmap"); | ||
341 | $this->skip(2); | ||
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); | ||
351 | if ($format == 4) { | ||
352 | $unicode_cmap_offset = $cmap_offset + $offset; | ||
353 | break; | ||
354 | } | ||
355 | } | ||
356 | else if ($platformID == 0) { // Unicode -- assume all encodings are compatible | ||
357 | $format = $this->get_ushort($cmap_offset + $offset); | ||
358 | if ($format == 4) { | ||
359 | $unicode_cmap_offset = $cmap_offset + $offset; | ||
360 | break; | ||
361 | } | ||
362 | } | ||
363 | $this->seek($save_pos ); | ||
364 | } | ||
365 | |||
366 | $glyphToChar = array(); | ||
367 | $charToGlyph = array(); | ||
368 | $this->getCMAP4($unicode_cmap_offset, $glyphToChar, $charToGlyph ); | ||
369 | |||
370 | fclose($this->fh); | ||
371 | return ($charToGlyph); | ||
372 | } | ||
373 | |||
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); | ||
386 | } | ||
387 | else { | ||
388 | return("ERROR - Not a TrueType Collection: version=".$version." - " . $file); | ||
389 | } | ||
390 | $this->numTTCFonts = $this->read_ulong(); | ||
391 | for ($i=1; $i<=$this->numTTCFonts; $i++) { | ||
392 | $this->TTCFonts[$i]['offset'] = $this->read_ulong(); | ||
393 | } | ||
394 | } | ||
395 | |||
396 | |||
397 | |||
398 | ///////////////////////////////////////////////////////////////////////////////////////// | ||
399 | |||
400 | ///////////////////////////////////////////////////////////////////////////////////////// | ||
401 | |||
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; | ||
426 | $N = ''; | ||
427 | if ($platformId == 3 && $encodingId == 1 && $languageId == 0x409) { // Microsoft, Unicode, US English, PS Name | ||
428 | $opos = $this->_pos; | ||
429 | $this->seek($string_data_offset + $offset); | ||
430 | if ($length % 2 != 0) | ||
431 | die("PostScript name is UTF-16BE string of odd length"); | ||
432 | $length /= 2; | ||
433 | $N = ''; | ||
434 | while ($length > 0) { | ||
435 | $char = $this->read_ushort(); | ||
436 | $N .= (chr($char)); | ||
437 | $length -= 1; | ||
438 | } | ||
439 | $this->_pos = $opos; | ||
440 | $this->seek($opos); | ||
441 | } | ||
442 | else if ($platformId == 1 && $encodingId == 0 && $languageId == 0) { // Macintosh, Roman, English, PS Name | ||
443 | $opos = $this->_pos; | ||
444 | $N = $this->get_chunk($string_data_offset + $offset, $length); | ||
445 | $this->_pos = $opos; | ||
446 | $this->seek($opos); | ||
447 | } | ||
448 | if ($N && $names[$nameId]=='') { | ||
449 | $names[$nameId] = $N; | ||
450 | $nameCount -= 1; | ||
451 | if ($nameCount==0) break; | ||
452 | } | ||
453 | } | ||
454 | if ($names[6]) | ||
455 | $psName = $names[6]; | ||
456 | else if ($names[4]) | ||
457 | $psName = preg_replace('/ /','-',$names[4]); | ||
458 | else if ($names[1]) | ||
459 | $psName = preg_replace('/ /','-',$names[1]); | ||
460 | else | ||
461 | $psName = ''; | ||
462 | if (!$psName) | ||
463 | die("Could not find PostScript font name: ".$this->filename); | ||
464 | if ($debug) { | ||
465 | for ($i=0;$i<count($psName);$i++) { | ||
466 | $c = $psName[$i]; | ||
467 | $oc = ord($c); | ||
468 | if ($oc>126 || strpos(' [](){}<>/%',$c)!==false) | ||
469 | die("psName=".$psName." contains invalid character ".$c." ie U+".ord(c)); | ||
470 | } | ||
471 | } | ||
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; } | ||
477 | |||
478 | if ($names[6]) { $this->fullName = $names[6]; } | ||
479 | |||
480 | /////////////////////////////////// | ||
481 | // head - Font header table | ||
482 | /////////////////////////////////// | ||
483 | $this->seek_table("head"); | ||
484 | if ($debug) { | ||
485 | $ver_maj = $this->read_ushort(); | ||
486 | $ver_min = $this->read_ushort(); | ||
487 | if ($ver_maj != 1) | ||
488 | die('Unknown head table version '. $ver_maj .'.'. $ver_min); | ||
489 | $this->fontRevision = $this->read_ushort() . $this->read_ushort(); | ||
490 | |||
491 | $this->skip(4); | ||
492 | $magic = $this->read_ulong(); | ||
493 | if ($magic != 0x5F0F3CF5) | ||
494 | die('Invalid head table magic ' .$magic); | ||
495 | $this->skip(2); | ||
496 | } | ||
497 | else { | ||
498 | $this->skip(18); | ||
499 | } | ||
500 | $this->unitsPerEm = $unitsPerEm = $this->read_ushort(); | ||
501 | $scale = 1000 / $unitsPerEm; | ||
502 | $this->skip(16); | ||
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)); | ||
508 | $this->skip(3*2); | ||
509 | $indexToLocFormat = $this->read_ushort(); | ||
510 | $glyphDataFormat = $this->read_ushort(); | ||
511 | if ($glyphDataFormat != 0) | ||
512 | die('Unknown glyph data format '.$glyphDataFormat); | ||
513 | |||
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"); | ||
520 | $this->skip(4); | ||
521 | $hheaAscender = $this->read_short(); | ||
522 | $hheaDescender = $this->read_short(); | ||
523 | $this->ascent = ($hheaAscender *$scale); | ||
524 | $this->descent = ($hheaDescender *$scale); | ||
525 | } | ||
526 | |||
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(); | ||
533 | $this->skip(2); | ||
534 | $usWeightClass = $this->read_ushort(); | ||
535 | $this->skip(2); | ||
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; | ||
541 | } | ||
542 | $this->skip(20); | ||
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]); } | ||
550 | $this->skip(26); | ||
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); | ||
555 | if ($version > 1) { | ||
556 | $this->skip(16); | ||
557 | $sCapHeight = $this->read_short(); | ||
558 | $this->capHeight = ($sCapHeight*$scale); | ||
559 | } | ||
560 | else { | ||
561 | $this->capHeight = $this->ascent; | ||
562 | } | ||
563 | } | ||
564 | else { | ||
565 | $usWeightClass = 500; | ||
566 | if (!$this->ascent) $this->ascent = ($yMax*$scale); | ||
567 | if (!$this->descent) $this->descent = ($yMin*$scale); | ||
568 | $this->capHeight = $this->ascent; | ||
569 | } | ||
570 | $this->stemV = 50 + intval(pow(($usWeightClass / 65.0),2)); | ||
571 | |||
572 | /////////////////////////////////// | ||
573 | // post - PostScript table | ||
574 | /////////////////////////////////// | ||
575 | $this->seek_table("post"); | ||
576 | if ($debug) { | ||
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); | ||
581 | } | ||
582 | else { | ||
583 | $this->skip(4); | ||
584 | } | ||
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(); | ||
589 | |||
590 | $this->flags = 4; | ||
591 | |||
592 | if ($this->italicAngle!= 0) | ||
593 | $this->flags = $this->flags | 64; | ||
594 | if ($usWeightClass >= 600) | ||
595 | $this->flags = $this->flags | 262144; | ||
596 | if ($isFixedPitch) | ||
597 | $this->flags = $this->flags | 1; | ||
598 | |||
599 | /////////////////////////////////// | ||
600 | // hhea - Horizontal header table | ||
601 | /////////////////////////////////// | ||
602 | $this->seek_table("hhea"); | ||
603 | if ($debug) { | ||
604 | $ver_maj = $this->read_ushort(); | ||
605 | $ver_min = $this->read_ushort(); | ||
606 | if ($ver_maj != 1) | ||
607 | die('Unknown hhea table version '.$ver_maj); | ||
608 | $this->skip(28); | ||
609 | } | ||
610 | else { | ||
611 | $this->skip(32); | ||
612 | } | ||
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'); | ||
619 | |||
620 | /////////////////////////////////// | ||
621 | // maxp - Maximum profile table | ||
622 | /////////////////////////////////// | ||
623 | $this->seek_table("maxp"); | ||
624 | if ($debug) { | ||
625 | $ver_maj = $this->read_ushort(); | ||
626 | $ver_min = $this->read_ushort(); | ||
627 | if ($ver_maj != 1) | ||
628 | die('Unknown maxp table version '.$ver_maj); | ||
629 | } | ||
630 | else { | ||
631 | $this->skip(4); | ||
632 | } | ||
633 | $numGlyphs = $this->read_ushort(); | ||
634 | |||
635 | |||
636 | /////////////////////////////////// | ||
637 | // cmap - Character to glyph index mapping table | ||
638 | /////////////////////////////////// | ||
639 | $cmap_offset = $this->seek_table("cmap"); | ||
640 | $this->skip(2); | ||
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); | ||
650 | if ($format == 4) { | ||
651 | if (!$unicode_cmap_offset) $unicode_cmap_offset = $cmap_offset + $offset; | ||
652 | if ($BMPonly) break; | ||
653 | } | ||
654 | } | ||
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); | ||
658 | if ($format == 12) { | ||
659 | $unicode_cmap_offset = $cmap_offset + $offset; | ||
660 | break; | ||
661 | } | ||
662 | } | ||
663 | $this->seek($save_pos ); | ||
664 | } | ||
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)'); | ||
667 | |||
668 | |||
669 | $sipset = false; | ||
670 | $smpset = false; | ||
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; | ||
677 | $this->skip(4); | ||
678 | |||
679 | $nGroups = $this->read_ulong(); | ||
680 | |||
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)) { | ||
688 | $sipset = true; | ||
689 | } | ||
690 | else if ($endCharCode > 0x10000 && $endCharCode < 0x1FFFF) { | ||
691 | $smpset = true; | ||
692 | } | ||
693 | $offset = 0; | ||
694 | for ($unichar=$startCharCode;$unichar<=$endCharCode;$unichar++) { | ||
695 | $glyph = $startGlyphCode + $offset ; | ||
696 | $offset++; | ||
697 | $charToGlyph[$unichar] = $glyph; | ||
698 | if ($unichar < 196608) { $this->maxUniChar = max($unichar,$this->maxUniChar); } | ||
699 | $glyphToChar[$glyph][] = $unichar; | ||
700 | } | ||
701 | } | ||
702 | } | ||
703 | else { | ||
704 | |||
705 | $glyphToChar = array(); | ||
706 | $charToGlyph = array(); | ||
707 | $this->getCMAP4($unicode_cmap_offset, $glyphToChar, $charToGlyph ); | ||
708 | |||
709 | } | ||
710 | $this->sipset = $sipset ; | ||
711 | $this->smpset = $smpset ; | ||
712 | |||
713 | /////////////////////////////////// | ||
714 | // hmtx - Horizontal metrics table | ||
715 | /////////////////////////////////// | ||
716 | $this->getHMTX($numberOfHMetrics, $numGlyphs, $glyphToChar, $scale); | ||
717 | |||
718 | /////////////////////////////////// | ||
719 | // kern - Kerning pair table | ||
720 | /////////////////////////////////// | ||
721 | if ($kerninfo) { | ||
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(); | ||
726 | // subtable header | ||
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) { | ||
732 | // Format 0 | ||
733 | $nPairs = $this->read_ushort(); | ||
734 | $this->skip(6); | ||
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); | ||
742 | } | ||
743 | } | ||
744 | } | ||
745 | } | ||
746 | } | ||
747 | } | ||
748 | |||
749 | |||
750 | ///////////////////////////////////////////////////////////////////////////////////////// | ||
751 | |||
752 | |||
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); | ||
757 | $this->_pos = 0; | ||
758 | $this->charWidths = ''; | ||
759 | $this->glyphPos = array(); | ||
760 | $this->charToGlyph = array(); | ||
761 | $this->tables = array(); | ||
762 | $this->otables = array(); | ||
763 | $this->ascent = 0; | ||
764 | $this->descent = 0; | ||
765 | $this->numTTCFonts = 0; | ||
766 | $this->TTCFonts = array(); | ||
767 | $this->skip(4); | ||
768 | $this->maxUni = 0; | ||
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(); | ||
776 | } | ||
777 | $this->seek($this->TTCFonts[$TTCfontID]['offset']); | ||
778 | $this->version = $version = $this->read_ulong(); // TTFont version again now | ||
779 | } | ||
780 | $this->readTableDirectory($debug); | ||
781 | |||
782 | /////////////////////////////////// | ||
783 | // head - Font header table | ||
784 | /////////////////////////////////// | ||
785 | $this->seek_table("head"); | ||
786 | $this->skip(50); | ||
787 | $indexToLocFormat = $this->read_ushort(); | ||
788 | $glyphDataFormat = $this->read_ushort(); | ||
789 | |||
790 | /////////////////////////////////// | ||
791 | // hhea - Horizontal header table | ||
792 | /////////////////////////////////// | ||
793 | $this->seek_table("hhea"); | ||
794 | $this->skip(32); | ||
795 | $metricDataFormat = $this->read_ushort(); | ||
796 | $orignHmetrics = $numberOfHMetrics = $this->read_ushort(); | ||
797 | |||
798 | /////////////////////////////////// | ||
799 | // maxp - Maximum profile table | ||
800 | /////////////////////////////////// | ||
801 | $this->seek_table("maxp"); | ||
802 | $this->skip(4); | ||
803 | $numGlyphs = $this->read_ushort(); | ||
804 | |||
805 | |||
806 | /////////////////////////////////// | ||
807 | // cmap - Character to glyph index mapping table | ||
808 | /////////////////////////////////// | ||
809 | $cmap_offset = $this->seek_table("cmap"); | ||
810 | $this->skip(2); | ||
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); | ||
820 | if ($format == 4) { | ||
821 | $unicode_cmap_offset = $cmap_offset + $offset; | ||
822 | break; | ||
823 | } | ||
824 | } | ||
825 | $this->seek($save_pos ); | ||
826 | } | ||
827 | |||
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)'); | ||
830 | |||
831 | |||
832 | $glyphToChar = array(); | ||
833 | $charToGlyph = array(); | ||
834 | $this->getCMAP4($unicode_cmap_offset, $glyphToChar, $charToGlyph ); | ||
835 | |||
836 | $this->charToGlyph = $charToGlyph; | ||
837 | |||
838 | |||
839 | /////////////////////////////////// | ||
840 | // hmtx - Horizontal metrics table | ||
841 | /////////////////////////////////// | ||
842 | $scale = 1; // not used | ||
843 | $this->getHMTX($numberOfHMetrics, $numGlyphs, $glyphToChar, $scale); | ||
844 | |||
845 | /////////////////////////////////// | ||
846 | // loca - Index to location | ||
847 | /////////////////////////////////// | ||
848 | $this->getLOCA($indexToLocFormat, $numGlyphs); | ||
849 | |||
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 | ||
856 | |||
857 | } | ||
858 | $this->maxUni = max($this->maxUni, $code); | ||
859 | } | ||
860 | |||
861 | list($start,$dummy) = $this->get_table_pos('glyf'); | ||
862 | |||
863 | $glyphSet = array(); | ||
864 | ksort($subsetglyphs); | ||
865 | $n = 0; | ||
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 | ||
870 | $n++; | ||
871 | } | ||
872 | |||
873 | ksort($subsetCharToGlyph); | ||
874 | foreach($subsetCharToGlyph AS $uni => $originalGlyphIdx) { | ||
875 | $codeToGlyph[$uni] = $glyphSet[$originalGlyphIdx] ; | ||
876 | } | ||
877 | $this->codeToGlyph = $codeToGlyph; | ||
878 | |||
879 | ksort($subsetglyphs); | ||
880 | foreach($subsetglyphs AS $originalGlyphIdx => $uni) { | ||
881 | $this->getGlyphs($originalGlyphIdx, $start, $glyphSet, $subsetglyphs); | ||
882 | } | ||
883 | |||
884 | $numGlyphs = $numberOfHMetrics = count($subsetglyphs ); | ||
885 | |||
886 | /////////////////////////////////// | ||
887 | // name - table copied from the original | ||
888 | /////////////////////////////////// | ||
889 | $this->add('name', $this->get_table('name')); | ||
890 | |||
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)); } | ||
897 | } | ||
898 | |||
899 | /////////////////////////////////// | ||
900 | // post - PostScript | ||
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); | ||
906 | } | ||
907 | |||
908 | /////////////////////////////////// | ||
909 | // Sort CID2GID map into segments of contiguous codes | ||
910 | /////////////////////////////////// | ||
911 | ksort($codeToGlyph); | ||
912 | unset($codeToGlyph[0]); | ||
913 | //unset($codeToGlyph[65535]); | ||
914 | $rangeid = 0; | ||
915 | $range = array(); | ||
916 | $prevcid = -2; | ||
917 | $prevglidx = -1; | ||
918 | // for each character | ||
919 | foreach ($codeToGlyph as $cid => $glidx) { | ||
920 | if ($cid == ($prevcid + 1) && $glidx == ($prevglidx + 1)) { | ||
921 | $range[$rangeid][] = $glidx; | ||
922 | } else { | ||
923 | // new range | ||
924 | $rangeid = $cid; | ||
925 | $range[$rangeid] = array(); | ||
926 | $range[$rangeid][] = $glidx; | ||
927 | } | ||
928 | $prevcid = $cid; | ||
929 | $prevglidx = $glidx; | ||
930 | } | ||
931 | |||
932 | |||
933 | |||
934 | /////////////////////////////////// | ||
935 | // CMap table | ||
936 | /////////////////////////////////// | ||
937 | // cmap - Character to glyph mapping | ||
938 | $segCount = count($range) + 1; // + 1 Last segment has missing character 0xFFFF | ||
939 | $searchRange = 1; | ||
940 | $entrySelector = 0; | ||
941 | while ($searchRange * 2 <= $segCount ) { | ||
942 | $searchRange = $searchRange * 2; | ||
943 | $entrySelector = $entrySelector + 1; | ||
944 | } | ||
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 | ||
956 | $segCount*2, | ||
957 | $searchRange, | ||
958 | $entrySelector, | ||
959 | $rangeShift); | ||
960 | |||
961 | // endCode(s) | ||
962 | foreach($range AS $start=>$subrange) { | ||
963 | $endCode = $start + (count($subrange)-1); | ||
964 | $cmap[] = $endCode; // endCode(s) | ||
965 | } | ||
966 | $cmap[] = 0xFFFF; // endCode of last Segment | ||
967 | $cmap[] = 0; // reservedPad | ||
968 | |||
969 | // startCode(s) | ||
970 | foreach($range AS $start=>$subrange) { | ||
971 | $cmap[] = $start; // startCode(s) | ||
972 | } | ||
973 | $cmap[] = 0xFFFF; // startCode of last Segment | ||
974 | // idDelta(s) | ||
975 | foreach($range AS $start=>$subrange) { | ||
976 | $idDelta = -($start-$subrange[0]); | ||
977 | $n += count($subrange); | ||
978 | $cmap[] = $idDelta; // idDelta(s) | ||
979 | } | ||
980 | $cmap[] = 1; // idDelta of last Segment | ||
981 | // idRangeOffset(s) | ||
982 | foreach($range AS $subrange) { | ||
983 | $cmap[] = 0; // idRangeOffset[segCount] Offset in bytes to glyph indexArray, or 0 | ||
984 | |||
985 | } | ||
986 | $cmap[] = 0; // idRangeOffset of last Segment | ||
987 | foreach($range AS $subrange) { | ||
988 | foreach($subrange AS $glidx) { | ||
989 | $cmap[] = $glidx; | ||
990 | } | ||
991 | } | ||
992 | $cmap[] = 0; // Mapping for last character | ||
993 | $cmapstr = ''; | ||
994 | foreach($cmap AS $cm) { $cmapstr .= pack("n",$cm); } | ||
995 | $this->add('cmap', $cmapstr); | ||
996 | |||
997 | |||
998 | /////////////////////////////////// | ||
999 | // glyf - Glyph data | ||
1000 | /////////////////////////////////// | ||
1001 | list($glyfOffset,$glyfLength) = $this->get_table_pos('glyf'); | ||
1002 | if ($glyfLength < $this->maxStrLenRead) { | ||
1003 | $glyphData = $this->get_table('glyf'); | ||
1004 | } | ||
1005 | |||
1006 | $offsets = array(); | ||
1007 | $glyf = ''; | ||
1008 | $pos = 0; | ||
1009 | $hmtxstr = ''; | ||
1010 | $xMinT = 0; | ||
1011 | $yMinT = 0; | ||
1012 | $xMaxT = 0; | ||
1013 | $yMaxT = 0; | ||
1014 | $advanceWidthMax = 0; | ||
1015 | $minLeftSideBearing = 0; | ||
1016 | $minRightSideBearing = 0; | ||
1017 | $xMaxExtent = 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(); | ||
1025 | |||
1026 | foreach($subsetglyphs AS $originalGlyphIdx => $uni) { | ||
1027 | // hmtx - Horizontal Metrics | ||
1028 | $hm = $this->getHMetric($orignHmetrics, $originalGlyphIdx); | ||
1029 | $hmtxstr .= $hm; | ||
1030 | |||
1031 | $offsets[] = $pos; | ||
1032 | $glyphPos = $this->glyphPos[$originalGlyphIdx]; | ||
1033 | $glyphLen = $this->glyphPos[$originalGlyphIdx + 1] - $glyphPos; | ||
1034 | if ($glyfLength < $this->maxStrLenRead) { | ||
1035 | $data = substr($glyphData,$glyphPos,$glyphLen); | ||
1036 | } | ||
1037 | else { | ||
1038 | if ($glyphLen > 0) $data = $this->get_chunk($glyfOffset+$glyphPos,$glyphLen); | ||
1039 | else $data = ''; | ||
1040 | } | ||
1041 | |||
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))); | ||
1058 | } | ||
1059 | $up = unpack("n", substr($data,0,2)); | ||
1060 | } | ||
1061 | if ($glyphLen > 2 && ($up[1] & (1 << 15)) ) { // If number of contours <= -1 i.e. composiste glyph | ||
1062 | $pos_in_glyph = 10; | ||
1063 | $flags = GF_MORE; | ||
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)); | ||
1068 | $flags = $up[1]; | ||
1069 | $up = unpack("n", substr($data,$pos_in_glyph+2,2)); | ||
1070 | $glyphIdx = $up[1]; | ||
1071 | $this->glyphdata[$originalGlyphIdx]['compGlyphs'][] = $glyphIdx; | ||
1072 | $data = $this->_set_ushort($data, $pos_in_glyph + 2, $glyphSet[$glyphIdx]); | ||
1073 | $pos_in_glyph += 4; | ||
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; } | ||
1079 | } | ||
1080 | $maxComponentElements = max($maxComponentElements, $nComponentElements); | ||
1081 | } | ||
1082 | // Simple Glyph | ||
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); | ||
1087 | |||
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)); | ||
1091 | $points = $up[1]+1; | ||
1092 | $this->glyphdata[$originalGlyphIdx]['nPoints'] = $points; | ||
1093 | $maxPoints = max($maxPoints, $points); | ||
1094 | } | ||
1095 | |||
1096 | $glyf .= $data; | ||
1097 | $pos += $glyphLen; | ||
1098 | if ($pos % 4 != 0) { | ||
1099 | $padding = 4 - ($pos % 4); | ||
1100 | $glyf .= str_repeat("\0",$padding); | ||
1101 | $pos += $padding; | ||
1102 | } | ||
1103 | } | ||
1104 | |||
1105 | if (_RECALC_PROFILE) { | ||
1106 | foreach($this->glyphdata AS $originalGlyphIdx => $val) { | ||
1107 | $maxdepth = $depth = -1; | ||
1108 | $points = 0; | ||
1109 | $contours = 0; | ||
1110 | $this->getGlyphData($originalGlyphIdx, $maxdepth, $depth, $points, $contours) ; | ||
1111 | $maxComponentDepth = max($maxComponentDepth , $maxdepth); | ||
1112 | $maxComponentPoints = max($maxComponentPoints , $points); | ||
1113 | $maxComponentContours = max($maxComponentContours , $contours); | ||
1114 | } | ||
1115 | } | ||
1116 | |||
1117 | |||
1118 | $offsets[] = $pos; | ||
1119 | $this->add('glyf', $glyf); | ||
1120 | |||
1121 | /////////////////////////////////// | ||
1122 | // hmtx - Horizontal Metrics | ||
1123 | /////////////////////////////////// | ||
1124 | $this->add('hmtx', $hmtxstr); | ||
1125 | |||
1126 | |||
1127 | /////////////////////////////////// | ||
1128 | // loca - Index to location | ||
1129 | /////////////////////////////////// | ||
1130 | $locastr = ''; | ||
1131 | if ((($pos + 1) >> 1) > 0xFFFF) { | ||
1132 | $indexToLocFormat = 1; // long format | ||
1133 | foreach($offsets AS $offset) { $locastr .= pack("N",$offset); } | ||
1134 | } | ||
1135 | else { | ||
1136 | $indexToLocFormat = 0; // short format | ||
1137 | foreach($offsets AS $offset) { $locastr .= pack("n",($offset/2)); } | ||
1138 | } | ||
1139 | $this->add('loca', $locastr); | ||
1140 | |||
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) | ||
1152 | } | ||
1153 | $this->add('head', $head); | ||
1154 | |||
1155 | |||
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); | ||
1166 | } | ||
1167 | $this->add('hhea', $hhea); | ||
1168 | |||
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 | ||
1181 | } | ||
1182 | $this->add('maxp', $maxp); | ||
1183 | |||
1184 | |||
1185 | /////////////////////////////////// | ||
1186 | // OS/2 - OS/2 | ||
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 | ||
1196 | } | ||
1197 | |||
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 | ||
1202 | |||
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 | ||
1215 | } | ||
1216 | $os2 = $this->_set_ushort($os2, 46, $nonBMP); // Unset Bit 57 (indicates non-BMP) - for interactive forms | ||
1217 | |||
1218 | $this->add('OS/2', $os2 ); | ||
1219 | } | ||
1220 | |||
1221 | fclose($this->fh); | ||
1222 | // Put the TTF file together | ||
1223 | $stm = ''; | ||
1224 | $this->endTTFile($stm); | ||
1225 | //file_put_contents('testfont.ttf', $stm); exit; | ||
1226 | return $stm ; | ||
1227 | } | ||
1228 | |||
1229 | //================================================================================ | ||
1230 | |||
1231 | // Also does SMP | ||
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; | ||
1235 | $this->_pos = 0; | ||
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(); | ||
1242 | $this->ascent = 0; | ||
1243 | $this->descent = 0; | ||
1244 | $this->numTTCFonts = 0; | ||
1245 | $this->TTCFonts = array(); | ||
1246 | $this->skip(4); | ||
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(); | ||
1254 | } | ||
1255 | $this->seek($this->TTCFonts[$TTCfontID]['offset']); | ||
1256 | $this->version = $version = $this->read_ulong(); // TTFont version again now | ||
1257 | } | ||
1258 | $this->readTableDirectory($debug); | ||
1259 | |||
1260 | |||
1261 | |||
1262 | /////////////////////////////////// | ||
1263 | // head - Font header table | ||
1264 | /////////////////////////////////// | ||
1265 | $this->seek_table("head"); | ||
1266 | $this->skip(50); | ||
1267 | $indexToLocFormat = $this->read_ushort(); | ||
1268 | $glyphDataFormat = $this->read_ushort(); | ||
1269 | |||
1270 | /////////////////////////////////// | ||
1271 | // hhea - Horizontal header table | ||
1272 | /////////////////////////////////// | ||
1273 | $this->seek_table("hhea"); | ||
1274 | $this->skip(32); | ||
1275 | $metricDataFormat = $this->read_ushort(); | ||
1276 | $orignHmetrics = $numberOfHMetrics = $this->read_ushort(); | ||
1277 | |||
1278 | /////////////////////////////////// | ||
1279 | // maxp - Maximum profile table | ||
1280 | /////////////////////////////////// | ||
1281 | $this->seek_table("maxp"); | ||
1282 | $this->skip(4); | ||
1283 | $numGlyphs = $this->read_ushort(); | ||
1284 | |||
1285 | |||
1286 | /////////////////////////////////// | ||
1287 | // cmap - Character to glyph index mapping table | ||
1288 | /////////////////////////////////// | ||
1289 | |||
1290 | $cmap_offset = $this->seek_table("cmap"); | ||
1291 | $this->skip(2); | ||
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; | ||
1303 | break; | ||
1304 | } | ||
1305 | } | ||
1306 | $this->seek($save_pos ); | ||
1307 | } | ||
1308 | |||
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; | ||
1317 | $this->skip(4); | ||
1318 | |||
1319 | $nGroups = $this->read_ulong(); | ||
1320 | |||
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(); | ||
1327 | $offset = 0; | ||
1328 | for ($unichar=$startCharCode;$unichar<=$endCharCode;$unichar++) { | ||
1329 | $glyph = $startGlyphCode + $offset ; | ||
1330 | $offset++; | ||
1331 | $charToGlyph[$unichar] = $glyph; | ||
1332 | if ($unichar < 196608) { $this->maxUniChar = max($unichar,$this->maxUniChar); } | ||
1333 | $glyphToChar[$glyph][] = $unichar; | ||
1334 | } | ||
1335 | } | ||
1336 | } | ||
1337 | else | ||
1338 | die('Font does not have cmap for Unicode (format 12)'); | ||
1339 | |||
1340 | |||
1341 | /////////////////////////////////// | ||
1342 | // hmtx - Horizontal metrics table | ||
1343 | /////////////////////////////////// | ||
1344 | $scale = 1; // not used here | ||
1345 | $this->getHMTX($numberOfHMetrics, $numGlyphs, $glyphToChar, $scale); | ||
1346 | |||
1347 | /////////////////////////////////// | ||
1348 | // loca - Index to location | ||
1349 | /////////////////////////////////// | ||
1350 | $this->getLOCA($indexToLocFormat, $numGlyphs); | ||
1351 | |||
1352 | /////////////////////////////////////////////////////////////////// | ||
1353 | |||
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; | ||
1365 | } | ||
1366 | else { $originalGlyphIdx = 0; } | ||
1367 | if (!isset($glyphSet[$originalGlyphIdx])) { | ||
1368 | $glyphSet[$originalGlyphIdx] = count($glyphMap); | ||
1369 | $glyphMap[] = $originalGlyphIdx; | ||
1370 | } | ||
1371 | $codeToGlyph[$code] = $glyphSet[$originalGlyphIdx]; | ||
1372 | } | ||
1373 | |||
1374 | list($start,$dummy) = $this->get_table_pos('glyf'); | ||
1375 | |||
1376 | $n = 0; | ||
1377 | while ($n < count($glyphMap)) { | ||
1378 | $originalGlyphIdx = $glyphMap[$n]; | ||
1379 | $glyphPos = $this->glyphPos[$originalGlyphIdx]; | ||
1380 | $glyphLen = $this->glyphPos[$originalGlyphIdx + 1] - $glyphPos; | ||
1381 | $n += 1; | ||
1382 | if (!$glyphLen) continue; | ||
1383 | $this->seek($start + $glyphPos); | ||
1384 | $numberOfContours = $this->read_short(); | ||
1385 | if ($numberOfContours < 0) { | ||
1386 | $this->skip(8); | ||
1387 | $flags = GF_MORE; | ||
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; | ||
1394 | } | ||
1395 | if ($flags & GF_WORDS) | ||
1396 | $this->skip(4); | ||
1397 | else | ||
1398 | $this->skip(2); | ||
1399 | if ($flags & GF_SCALE) | ||
1400 | $this->skip(2); | ||
1401 | else if ($flags & GF_XYSCALE) | ||
1402 | $this->skip(4); | ||
1403 | else if ($flags & GF_TWOBYTWO) | ||
1404 | $this->skip(8); | ||
1405 | } | ||
1406 | } | ||
1407 | } | ||
1408 | |||
1409 | $numGlyphs = $n = count($glyphMap); | ||
1410 | $numberOfHMetrics = $n; | ||
1411 | |||
1412 | /////////////////////////////////// | ||
1413 | // name | ||
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 | ||
1427 | } | ||
1428 | $this->skip(8); | ||
1429 | } | ||
1430 | $this->add('name', $name); | ||
1431 | |||
1432 | /////////////////////////////////// | ||
1433 | // OS/2 | ||
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) | ||
1441 | |||
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 | ||
1451 | |||
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 ); | ||
1457 | } | ||
1458 | |||
1459 | |||
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)); } | ||
1466 | } | ||
1467 | |||
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"; | ||
1474 | } | ||
1475 | $this->add('post', $post); | ||
1476 | |||
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); | ||
1483 | |||
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); | ||
1490 | |||
1491 | |||
1492 | /////////////////////////////////// | ||
1493 | // CMap table Formats [1,0,]6 and [3,0,]4 | ||
1494 | /////////////////////////////////// | ||
1495 | /////////////////////////////////// | ||
1496 | // Sort CID2GID map into segments of contiguous codes | ||
1497 | /////////////////////////////////// | ||
1498 | $rangeid = 0; | ||
1499 | $range = array(); | ||
1500 | $prevcid = -2; | ||
1501 | $prevglidx = -1; | ||
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; | ||
1507 | } else { | ||
1508 | // new range | ||
1509 | $rangeid = $cid; | ||
1510 | $range[$rangeid] = array(); | ||
1511 | $range[$rangeid][] = $glidx; | ||
1512 | } | ||
1513 | $prevcid = $cid; | ||
1514 | $prevglidx = $glidx; | ||
1515 | } | ||
1516 | // cmap - Character to glyph mapping | ||
1517 | $segCount = count($range) + 1; // + 1 Last segment has missing character 0xFFFF | ||
1518 | $searchRange = 1; | ||
1519 | $entrySelector = 0; | ||
1520 | while ($searchRange * 2 <= $segCount ) { | ||
1521 | $searchRange = $searchRange * 2; | ||
1522 | $entrySelector = $entrySelector + 1; | ||
1523 | } | ||
1524 | $searchRange = $searchRange * 2; | ||
1525 | $rangeShift = $segCount * 2 - $searchRange; | ||
1526 | $length = 16 + (8*$segCount ) + ($numGlyphs+1); | ||
1527 | $cmap = array( | ||
1528 | 4, $length, 0, // Format 4 Mapping subtable: format, length, language | ||
1529 | $segCount*2, | ||
1530 | $searchRange, | ||
1531 | $entrySelector, | ||
1532 | $rangeShift); | ||
1533 | |||
1534 | // endCode(s) | ||
1535 | foreach($range AS $start=>$subrange) { | ||
1536 | $endCode = $start + (count($subrange)-1); | ||
1537 | $cmap[] = $endCode; // endCode(s) | ||
1538 | } | ||
1539 | $cmap[] = 0xFFFF; // endCode of last Segment | ||
1540 | $cmap[] = 0; // reservedPad | ||
1541 | |||
1542 | // startCode(s) | ||
1543 | foreach($range AS $start=>$subrange) { | ||
1544 | $cmap[] = $start; // startCode(s) | ||
1545 | } | ||
1546 | $cmap[] = 0xFFFF; // startCode of last Segment | ||
1547 | // idDelta(s) | ||
1548 | foreach($range AS $start=>$subrange) { | ||
1549 | $idDelta = -($start-$subrange[0]); | ||
1550 | $n += count($subrange); | ||
1551 | $cmap[] = $idDelta; // idDelta(s) | ||
1552 | } | ||
1553 | $cmap[] = 1; // idDelta of last Segment | ||
1554 | // idRangeOffset(s) | ||
1555 | foreach($range AS $subrange) { | ||
1556 | $cmap[] = 0; // idRangeOffset[segCount] Offset in bytes to glyph indexArray, or 0 | ||
1557 | |||
1558 | } | ||
1559 | $cmap[] = 0; // idRangeOffset of last Segment | ||
1560 | foreach($range AS $subrange) { | ||
1561 | foreach($subrange AS $glidx) { | ||
1562 | $cmap[] = $glidx; | ||
1563 | } | ||
1564 | } | ||
1565 | $cmap[] = 0; // Mapping for last character | ||
1566 | $cmapstr4 = ''; | ||
1567 | foreach($cmap AS $cm) { $cmapstr4 .= pack("n",$cm); } | ||
1568 | |||
1569 | /////////////////////////////////// | ||
1570 | // cmap - Character to glyph mapping | ||
1571 | /////////////////////////////////// | ||
1572 | $entryCount = count($subset); | ||
1573 | $length = 10 + $entryCount * 2; | ||
1574 | |||
1575 | $off = 20 + $length; | ||
1576 | $hoff = $off >> 16; | ||
1577 | $loff = $off & 0xFFFF; | ||
1578 | |||
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 | ||
1586 | $entryCount | ||
1587 | ); | ||
1588 | $cmapstr = ''; | ||
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); | ||
1593 | |||
1594 | /////////////////////////////////// | ||
1595 | // hmtx - Horizontal Metrics | ||
1596 | /////////////////////////////////// | ||
1597 | $hmtxstr = ''; | ||
1598 | for($n=0;$n<$numGlyphs;$n++) { | ||
1599 | $originalGlyphIdx = $glyphMap[$n]; | ||
1600 | $hm = $this->getHMetric($orignHmetrics, $originalGlyphIdx); | ||
1601 | $hmtxstr .= $hm; | ||
1602 | } | ||
1603 | $this->add('hmtx', $hmtxstr); | ||
1604 | |||
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'); | ||
1611 | } | ||
1612 | |||
1613 | $offsets = array(); | ||
1614 | $glyf = ''; | ||
1615 | $pos = 0; | ||
1616 | for ($n=0;$n<$numGlyphs;$n++) { | ||
1617 | $offsets[] = $pos; | ||
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); | ||
1623 | } | ||
1624 | else { | ||
1625 | if ($glyphLen > 0) $data = $this->get_chunk($glyfOffset+$glyphPos,$glyphLen); | ||
1626 | else $data = ''; | ||
1627 | } | ||
1628 | if ($glyphLen > 0) $up = unpack("n", substr($data,0,2)); | ||
1629 | if ($glyphLen > 2 && ($up[1] & (1 << 15)) ) { | ||
1630 | $pos_in_glyph = 10; | ||
1631 | $flags = GF_MORE; | ||
1632 | while ($flags & GF_MORE) { | ||
1633 | $up = unpack("n", substr($data,$pos_in_glyph,2)); | ||
1634 | $flags = $up[1]; | ||
1635 | $up = unpack("n", substr($data,$pos_in_glyph+2,2)); | ||
1636 | $glyphIdx = $up[1]; | ||
1637 | $data = $this->_set_ushort($data, $pos_in_glyph + 2, $glyphSet[$glyphIdx]); | ||
1638 | $pos_in_glyph += 4; | ||
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; } | ||
1644 | } | ||
1645 | } | ||
1646 | $glyf .= $data; | ||
1647 | $pos += $glyphLen; | ||
1648 | if ($pos % 4 != 0) { | ||
1649 | $padding = 4 - ($pos % 4); | ||
1650 | $glyf .= str_repeat("\0",$padding); | ||
1651 | $pos += $padding; | ||
1652 | } | ||
1653 | } | ||
1654 | $offsets[] = $pos; | ||
1655 | $this->add('glyf', $glyf); | ||
1656 | |||
1657 | /////////////////////////////////// | ||
1658 | // loca - Index to location | ||
1659 | /////////////////////////////////// | ||
1660 | $locastr = ''; | ||
1661 | if ((($pos + 1) >> 1) > 0xFFFF) { | ||
1662 | $indexToLocFormat = 1; // long format | ||
1663 | foreach($offsets AS $offset) { $locastr .= pack("N",$offset); } | ||
1664 | } | ||
1665 | else { | ||
1666 | $indexToLocFormat = 0; // short format | ||
1667 | foreach($offsets AS $offset) { $locastr .= pack("n",($offset/2)); } | ||
1668 | } | ||
1669 | $this->add('loca', $locastr); | ||
1670 | |||
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); | ||
1677 | |||
1678 | fclose($this->fh); | ||
1679 | |||
1680 | // Put the TTF file together | ||
1681 | $stm = ''; | ||
1682 | $this->endTTFile($stm); | ||
1683 | //file_put_contents('testfont.ttf', $stm); exit; | ||
1684 | return $stm ; | ||
1685 | } | ||
1686 | |||
1687 | ////////////////////////////////////////////////////////////////////////////////// | ||
1688 | // Recursively get composite glyph data | ||
1689 | function getGlyphData($originalGlyphIdx, &$maxdepth, &$depth, &$points, &$contours) { | ||
1690 | $depth++; | ||
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); | ||
1695 | } | ||
1696 | } | ||
1697 | else if (($this->glyphdata[$originalGlyphIdx]['nContours'] > 0) && $depth > 0) { // simple | ||
1698 | $contours += $this->glyphdata[$originalGlyphIdx]['nContours']; | ||
1699 | $points += $this->glyphdata[$originalGlyphIdx]['nPoints']; | ||
1700 | } | ||
1701 | $depth--; | ||
1702 | } | ||
1703 | |||
1704 | |||
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; | ||
1710 | if (!$glyphLen) { | ||
1711 | return; | ||
1712 | } | ||
1713 | $this->seek($start + $glyphPos); | ||
1714 | $numberOfContours = $this->read_short(); | ||
1715 | if ($numberOfContours < 0) { | ||
1716 | $this->skip(8); | ||
1717 | $flags = GF_MORE; | ||
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; | ||
1724 | } | ||
1725 | $savepos = ftell($this->fh); | ||
1726 | $this->getGlyphs($glyphIdx, $start, $glyphSet, $subsetglyphs); | ||
1727 | $this->seek($savepos); | ||
1728 | if ($flags & GF_WORDS) | ||
1729 | $this->skip(4); | ||
1730 | else | ||
1731 | $this->skip(2); | ||
1732 | if ($flags & GF_SCALE) | ||
1733 | $this->skip(2); | ||
1734 | else if ($flags & GF_XYSCALE) | ||
1735 | $this->skip(4); | ||
1736 | else if ($flags & GF_TWOBYTWO) | ||
1737 | $this->skip(8); | ||
1738 | } | ||
1739 | } | ||
1740 | } | ||
1741 | |||
1742 | ////////////////////////////////////////////////////////////////////////////////// | ||
1743 | |||
1744 | function getHMTX($numberOfHMetrics, $numGlyphs, &$glyphToChar, $scale) { | ||
1745 | $start = $this->seek_table("hmtx"); | ||
1746 | $aw = 0; | ||
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 | ||
1750 | $nCharWidths = 0; | ||
1751 | if (($numberOfHMetrics*4) < $this->maxStrLenRead) { | ||
1752 | $data = $this->get_chunk($start,($numberOfHMetrics*4)); | ||
1753 | $arr = unpack("n*", $data); | ||
1754 | } | ||
1755 | else { $this->seek($start); } | ||
1756 | for( $glyph=0; $glyph<$numberOfHMetrics; $glyph++) { | ||
1757 | if (($numberOfHMetrics*4) < $this->maxStrLenRead) { | ||
1758 | $aw = $arr[($glyph*2)+1]; | ||
1759 | } | ||
1760 | else { | ||
1761 | $aw = $this->read_ushort(); | ||
1762 | $lsb = $this->read_ushort(); | ||
1763 | } | ||
1764 | if (isset($glyphToChar[$glyph]) || $glyph == 0) { | ||
1765 | |||
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) | ||
1768 | if ($glyph == 0) { | ||
1769 | $this->defaultWidth = $scale*$aw; | ||
1770 | continue; | ||
1771 | } | ||
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); | ||
1780 | $nCharWidths++; | ||
1781 | } | ||
1782 | } | ||
1783 | } | ||
1784 | } | ||
1785 | } | ||
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); | ||
1799 | $nCharWidths++; | ||
1800 | } | ||
1801 | } | ||
1802 | } | ||
1803 | } | ||
1804 | } | ||
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); | ||
1809 | } | ||
1810 | |||
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); | ||
1816 | } | ||
1817 | else { | ||
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); | ||
1822 | } | ||
1823 | return $hm; | ||
1824 | } | ||
1825 | |||
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); | ||
1834 | } | ||
1835 | } | ||
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]); | ||
1841 | } | ||
1842 | } | ||
1843 | else | ||
1844 | die('Unknown location table format '.$indexToLocFormat); | ||
1845 | } | ||
1846 | |||
1847 | |||
1848 | // CMAP Format 4 | ||
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; | ||
1854 | $this->skip(2); | ||
1855 | |||
1856 | $segCount = $this->read_ushort() / 2; | ||
1857 | $this->skip(6); | ||
1858 | $endCount = array(); | ||
1859 | for($i=0; $i<$segCount; $i++) { $endCount[] = $this->read_ushort(); } | ||
1860 | $this->skip(2); | ||
1861 | $startCount = array(); | ||
1862 | for($i=0; $i<$segCount; $i++) { $startCount[] = $this->read_ushort(); } | ||
1863 | $idDelta = array(); | ||
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(); } | ||
1868 | |||
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; | ||
1874 | else { | ||
1875 | $offset = ($unichar - $startCount[$n]) * 2 + $idRangeOffset[$n]; | ||
1876 | $offset = $idRangeOffset_start + 2 * $n + $offset; | ||
1877 | if ($offset >= $limit) | ||
1878 | $glyph = 0; | ||
1879 | else { | ||
1880 | $glyph = $this->get_ushort($offset); | ||
1881 | if ($glyph != 0) | ||
1882 | $glyph = ($glyph + $idDelta[$n]) & 0xFFFF; | ||
1883 | } | ||
1884 | } | ||
1885 | $charToGlyph[$unichar] = $glyph; | ||
1886 | if ($unichar < 196608) { $this->maxUniChar = max($unichar,$this->maxUniChar); } | ||
1887 | $glyphToChar[$glyph][] = $unichar; | ||
1888 | } | ||
1889 | } | ||
1890 | |||
1891 | // mPDF 5.4.05 | ||
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."); } | ||
1899 | $this->skip(28); | ||
1900 | $nGlyfs = $this->read_ushort(); | ||
1901 | $glyphNameIndex = array(); | ||
1902 | for ($i=0; $i<$nGlyfs; $i++) { | ||
1903 | $glyphNameIndex[($this->read_ushort())] = $i; | ||
1904 | } | ||
1905 | |||
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)); | ||
1910 | $ptr++; | ||
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 | ||
1919 | $bit = $uni & 0xFF; | ||
1920 | if ($form == 'I') { $bit += 0xF600; } | ||
1921 | else if ($form == 'M') { $bit += 0xF700; } | ||
1922 | else { $bit += 0xF500; } | ||
1923 | // ADD TO CMAP | ||
1924 | $glyphToChar[$gid][] = $bit; | ||
1925 | $charToGlyph[$bit] = $gid; | ||
1926 | } | ||
1927 | } | ||
1928 | // LAM with ALEF ligatures (Mandatory ligatures) | ||
1929 | else if (preg_match('/^uni064406(22|23|25|27)(\.fina|\.fin){0,1}$/i',$name,$m)) { | ||
1930 | if ($m[1]=='22') { | ||
1931 | if ($m[2]) { $uni = hexdec('FEF6'); } else { $uni = hexdec('FEF5'); } | ||
1932 | } | ||
1933 | else if ($m[1]=='23') { | ||
1934 | if ($m[2]) { $uni = hexdec('FEF8'); } else { $uni = hexdec('FEF7'); } | ||
1935 | } | ||
1936 | else if ($m[1]=='25') { | ||
1937 | if ($m[2]) { $uni = hexdec('FEFA'); } else { $uni = hexdec('FEF9'); } | ||
1938 | } | ||
1939 | else if ($m[1]=='27') { | ||
1940 | if ($m[2]) { $uni = hexdec('FEFC'); } else { $uni = hexdec('FEFB'); } | ||
1941 | } | ||
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 | ||
1943 | // ADD TO CMAP | ||
1944 | $glyphToChar[$gid][] = $uni; | ||
1945 | $charToGlyph[$uni] = $gid; | ||
1946 | } | ||
1947 | } | ||
1948 | $ptr += $len; | ||
1949 | } | ||
1950 | } | ||
1951 | } | ||
1952 | |||
1953 | } | ||
1954 | |||
1955 | |||
1956 | // Put the TTF file together | ||
1957 | function endTTFile(&$stm) { | ||
1958 | $stm = ''; | ||
1959 | $numTables = count($this->otables); | ||
1960 | $searchRange = 1; | ||
1961 | $entrySelector = 0; | ||
1962 | while ($searchRange * 2 <= $numTables) { | ||
1963 | $searchRange = $searchRange * 2; | ||
1964 | $entrySelector = $entrySelector + 1; | ||
1965 | } | ||
1966 | $searchRange = $searchRange * 16; | ||
1967 | $rangeShift = $numTables * 16 - $searchRange; | ||
1968 | |||
1969 | // Header | ||
1970 | if (_TTF_MAC_HEADER) { | ||
1971 | $stm .= (pack("Nnnnn", 0x74727565, $numTables, $searchRange, $entrySelector, $rangeShift)); // Mac | ||
1972 | } | ||
1973 | else { | ||
1974 | $stm .= (pack("Nnnnn", 0x00010000 , $numTables, $searchRange, $entrySelector, $rangeShift)); // Windows | ||
1975 | } | ||
1976 | |||
1977 | // Table directory | ||
1978 | $tables = $this->otables; | ||
1979 | ksort ($tables); | ||
1980 | $offset = 12 + $numTables * 16; | ||
1981 | foreach ($tables AS $tag=>$data) { | ||
1982 | if ($tag == 'head') { $head_start = $offset; } | ||
1983 | $stm .= $tag; | ||
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; | ||
1989 | } | ||
1990 | |||
1991 | // Table data | ||
1992 | foreach ($tables AS $tag=>$data) { | ||
1993 | $data .= "\0\0\0"; | ||
1994 | $stm .= substr($data,0,(strlen($data)&~3)); | ||
1995 | } | ||
1996 | |||
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); | ||
2001 | return $stm ; | ||
2002 | } | ||
2003 | |||
2004 | |||
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); | ||
2009 | $this->_pos = 0; | ||
2010 | $this->charWidths = ''; | ||
2011 | $this->glyphPos = array(); | ||
2012 | $this->charToGlyph = array(); | ||
2013 | $this->tables = array(); | ||
2014 | $this->otables = array(); | ||
2015 | $this->ascent = 0; | ||
2016 | $this->descent = 0; | ||
2017 | $this->numTTCFonts = 0; | ||
2018 | $this->TTCFonts = array(); | ||
2019 | $this->skip(4); | ||
2020 | $this->maxUni = 0; | ||
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(); | ||
2028 | } | ||
2029 | $this->seek($this->TTCFonts[$TTCfontID]['offset']); | ||
2030 | $this->version = $version = $this->read_ulong(); // TTFont version again now | ||
2031 | } | ||
2032 | $this->readTableDirectory($debug); | ||
2033 | $tags = array ('OS/2', 'cmap', 'glyf', 'head', 'hhea', 'hmtx', 'loca', 'maxp', 'name', 'post', 'cvt ', 'fpgm', 'gasp', 'prep'); | ||
2034 | /* | ||
2035 | Tables which require glyphIndex | ||
2036 | hdmx | ||
2037 | kern | ||
2038 | LTSH | ||
2039 | |||
2040 | Tables which do NOT require glyphIndex | ||
2041 | VDMX | ||
2042 | |||
2043 | GDEF | ||
2044 | GPOS | ||
2045 | GSUB | ||
2046 | JSTF | ||
2047 | |||
2048 | DSIG | ||
2049 | PCLT - not recommended | ||
2050 | */ | ||
2051 | |||
2052 | foreach($tags AS $tag) { | ||
2053 | if (isset($this->tables[$tag])) { $this->add($tag, $this->get_table($tag)); } | ||
2054 | } | ||
2055 | fclose($this->fh); | ||
2056 | $stm = ''; | ||
2057 | $this->endTTFile($stm); | ||
2058 | return $stm ; | ||
2059 | } | ||
2060 | |||
2061 | |||
2062 | } | ||
2063 | |||
2064 | |||
2065 | ?> \ No newline at end of file | ||