These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | |||
3 | /******************************************************************************* |
||
4 | * otl_dump class * |
||
5 | *******************************************************************************/ |
||
6 | |||
7 | // Define the value used in the "head" table of a created TTF file |
||
8 | // 0x74727565 "true" for Mac |
||
9 | // 0x00010000 for Windows |
||
10 | // Either seems to work for a font embedded in a PDF file |
||
11 | // when read by Adobe Reader on a Windows PC(!) |
||
12 | if (!defined('_TTF_MAC_HEADER')) define("_TTF_MAC_HEADER", false); |
||
13 | |||
14 | // Recalculate correct metadata/profiles when making subset fonts (not SIP/SMP) |
||
15 | // e.g. xMin, xMax, maxNContours |
||
16 | if (!defined('_RECALC_PROFILE')) define("_RECALC_PROFILE", false); |
||
17 | |||
18 | // TrueType Font Glyph operators |
||
19 | define("GF_WORDS",(1 << 0)); |
||
20 | define("GF_SCALE",(1 << 3)); |
||
21 | define("GF_MORE",(1 << 5)); |
||
22 | define("GF_XYSCALE",(1 << 6)); |
||
23 | define("GF_TWOBYTWO",(1 << 7)); |
||
24 | |||
25 | // mPDF 5.7.1 |
||
26 | if(!function_exists('unicode_hex')){ |
||
27 | function unicode_hex($unicode_dec) { |
||
28 | return (sprintf("%05s", strtoupper(dechex($unicode_dec)))); |
||
29 | } |
||
30 | } |
||
31 | class OTLdump { |
||
32 | |||
33 | var $GPOSFeatures; // mPDF 5.7.1 |
||
34 | var $GPOSLookups; // mPDF 5.7.1 |
||
35 | var $GPOSScriptLang; // mPDF 5.7.1 |
||
36 | var $ignoreStrings; // mPDF 5.7.1 |
||
37 | var $MarkAttachmentType; // mPDF 5.7.1 |
||
38 | var $MarkGlyphSets; // mPDF 7.5.1 |
||
39 | var $GlyphClassMarks; // mPDF 5.7.1 |
||
40 | var $GlyphClassLigatures; // mPDF 5.7.1 |
||
41 | var $GlyphClassBases; // mPDF 5.7.1 |
||
42 | var $GlyphClassComponents; // mPDF 5.7.1 |
||
43 | var $GSUBScriptLang; // mPDF 5.7.1 |
||
44 | var $rtlPUAstr; // mPDF 5.7.1 |
||
45 | var $rtlPUAarr; // mPDF 5.7.1 |
||
46 | var $fontkey; // mPDF 5.7.1 |
||
47 | var $useOTL; // mPDF 5.7.1 |
||
48 | var $panose; |
||
49 | var $maxUni; |
||
50 | var $sFamilyClass; |
||
51 | var $sFamilySubClass; |
||
52 | var $sipset; |
||
53 | var $smpset; |
||
54 | var $_pos; |
||
55 | var $numTables; |
||
56 | var $searchRange; |
||
0 ignored issues
–
show
|
|||
57 | var $entrySelector; |
||
58 | var $rangeShift; |
||
59 | var $tables; |
||
60 | var $otables; |
||
61 | var $filename; |
||
62 | var $fh; |
||
63 | var $glyphPos; |
||
64 | var $charToGlyph; |
||
65 | var $ascent; |
||
66 | var $descent; |
||
67 | var $name; |
||
68 | var $familyName; |
||
69 | var $styleName; |
||
70 | var $fullName; |
||
71 | var $uniqueFontID; |
||
72 | var $unitsPerEm; |
||
73 | var $bbox; |
||
74 | var $capHeight; |
||
75 | var $stemV; |
||
76 | var $italicAngle; |
||
77 | var $flags; |
||
78 | var $underlinePosition; |
||
79 | var $underlineThickness; |
||
80 | var $charWidths; |
||
81 | var $defaultWidth; |
||
82 | var $maxStrLenRead; |
||
83 | var $numTTCFonts; |
||
84 | var $TTCFonts; |
||
85 | var $maxUniChar; |
||
86 | var $kerninfo; |
||
87 | |||
88 | function OTLdump(&$mpdf) { |
||
89 | $this->mpdf = $mpdf; |
||
90 | $this->maxStrLenRead = 200000; // Maximum size of glyf table to read in as string (otherwise reads each glyph from file) |
||
91 | } |
||
92 | |||
93 | |||
94 | function getMetrics($file, $fontkey, $TTCfontID=0, $debug=false, $BMPonly=false, $kerninfo=false, $useOTL=0, $mode) { // mPDF 5.7.1 |
||
95 | $this->mode = $mode; |
||
96 | $this->useOTL = $useOTL; // mPDF 5.7.1 |
||
97 | $this->fontkey = $fontkey; // mPDF 5.7.1 |
||
98 | $this->filename = $file; |
||
99 | $this->fh = fopen($file,'rb') or die('Can\'t open file ' . $file); |
||
100 | $this->_pos = 0; |
||
101 | $this->charWidths = ''; |
||
102 | $this->glyphPos = array(); |
||
103 | $this->charToGlyph = array(); |
||
104 | $this->tables = array(); |
||
105 | $this->otables = array(); |
||
106 | $this->kerninfo = array(); |
||
107 | $this->ascent = 0; |
||
108 | $this->descent = 0; |
||
109 | $this->numTTCFonts = 0; |
||
110 | $this->TTCFonts = array(); |
||
111 | $this->version = $version = $this->read_ulong(); |
||
112 | $this->panose = array(); |
||
113 | if ($version==0x4F54544F) |
||
114 | die("Postscript outlines are not supported"); |
||
115 | if ($version==0x74746366 && !$TTCfontID) |
||
116 | die("ERROR - You must define the TTCfontID for a TrueType Collection in config_fonts.php (". $file.")"); |
||
117 | if (!in_array($version, array(0x00010000,0x74727565)) && !$TTCfontID) |
||
118 | die("Not a TrueType font: version=".$version); |
||
119 | View Code Duplication | if ($TTCfontID > 0) { |
|
120 | $this->version = $version = $this->read_ulong(); // TTC Header version now |
||
121 | if (!in_array($version, array(0x00010000,0x00020000))) |
||
122 | die("ERROR - Error parsing TrueType Collection: version=".$version." - " . $file); |
||
123 | $this->numTTCFonts = $this->read_ulong(); |
||
124 | for ($i=1; $i<=$this->numTTCFonts; $i++) { |
||
125 | $this->TTCFonts[$i]['offset'] = $this->read_ulong(); |
||
126 | } |
||
127 | $this->seek($this->TTCFonts[$TTCfontID]['offset']); |
||
128 | $this->version = $version = $this->read_ulong(); // TTFont version again now |
||
129 | } |
||
130 | $this->readTableDirectory($debug); |
||
131 | $this->extractInfo($debug, $BMPonly, $kerninfo, $useOTL); |
||
132 | fclose($this->fh); |
||
133 | } |
||
134 | |||
135 | |||
136 | View Code Duplication | function readTableDirectory($debug=false) { |
|
137 | $this->numTables = $this->read_ushort(); |
||
138 | $this->searchRange = $this->read_ushort(); |
||
139 | $this->entrySelector = $this->read_ushort(); |
||
140 | $this->rangeShift = $this->read_ushort(); |
||
141 | $this->tables = array(); |
||
142 | for ($i=0;$i<$this->numTables;$i++) { |
||
143 | $record = array(); |
||
144 | $record['tag'] = $this->read_tag(); |
||
145 | $record['checksum'] = array($this->read_ushort(),$this->read_ushort()); |
||
146 | $record['offset'] = $this->read_ulong(); |
||
147 | $record['length'] = $this->read_ulong(); |
||
148 | $this->tables[$record['tag']] = $record; |
||
149 | } |
||
150 | if ($debug) $this->checksumTables(); |
||
151 | } |
||
152 | |||
153 | View Code Duplication | function checksumTables() { |
|
154 | // Check the checksums for all tables |
||
155 | foreach($this->tables AS $t) { |
||
156 | if ($t['length'] > 0 && $t['length'] < $this->maxStrLenRead) { // 1.02 |
||
157 | $table = $this->get_chunk($t['offset'], $t['length']); |
||
158 | $checksum = $this->calcChecksum($table); |
||
159 | if ($t['tag'] == 'head') { |
||
160 | $up = unpack('n*', substr($table,8,4)); |
||
161 | $adjustment[0] = $up[1]; |
||
162 | $adjustment[1] = $up[2]; |
||
163 | $checksum = $this->sub32($checksum, $adjustment); |
||
164 | } |
||
165 | $xchecksum = $t['checksum']; |
||
166 | if ($xchecksum != $checksum) |
||
167 | 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]))); |
||
168 | } |
||
169 | } |
||
170 | } |
||
171 | |||
172 | View Code Duplication | function sub32($x, $y) { |
|
173 | $xlo = $x[1]; |
||
174 | $xhi = $x[0]; |
||
175 | $ylo = $y[1]; |
||
176 | $yhi = $y[0]; |
||
177 | if ($ylo > $xlo) { $xlo += 1 << 16; $yhi += 1; } |
||
178 | $reslo = $xlo-$ylo; |
||
179 | if ($yhi > $xhi) { $xhi += 1 << 16; } |
||
180 | $reshi = $xhi-$yhi; |
||
181 | $reshi = $reshi & 0xFFFF; |
||
182 | return array($reshi, $reslo); |
||
183 | } |
||
184 | |||
185 | View Code Duplication | function calcChecksum($data) { |
|
186 | if (strlen($data) % 4) { $data .= str_repeat("\0",(4-(strlen($data) % 4))); } |
||
187 | $len = strlen($data); |
||
188 | $hi=0x0000; |
||
189 | $lo=0x0000; |
||
190 | for($i=0;$i<$len;$i+=4) { |
||
191 | $hi += (ord($data[$i])<<8) + ord($data[$i+1]); |
||
192 | $lo += (ord($data[$i+2])<<8) + ord($data[$i+3]); |
||
193 | $hi += ($lo >> 16) & 0xFFFF; |
||
194 | $lo = $lo & 0xFFFF; |
||
195 | } |
||
196 | return array($hi, $lo); |
||
197 | } |
||
198 | |||
199 | function get_table_pos($tag) { |
||
200 | $offset = $this->tables[$tag]['offset']; |
||
201 | $length = $this->tables[$tag]['length']; |
||
202 | return array($offset, $length); |
||
203 | } |
||
204 | |||
205 | function seek($pos) { |
||
206 | $this->_pos = $pos; |
||
207 | fseek($this->fh,$this->_pos); |
||
208 | } |
||
209 | |||
210 | function skip($delta) { |
||
211 | $this->_pos = $this->_pos + $delta; |
||
212 | fseek($this->fh,$delta,SEEK_CUR); |
||
213 | } |
||
214 | |||
215 | View Code Duplication | function seek_table($tag, $offset_in_table = 0) { |
|
216 | $tpos = $this->get_table_pos($tag); |
||
217 | $this->_pos = $tpos[0] + $offset_in_table; |
||
218 | fseek($this->fh, $this->_pos); |
||
219 | return $this->_pos; |
||
220 | } |
||
221 | |||
222 | function read_tag() { |
||
223 | $this->_pos += 4; |
||
224 | return fread($this->fh,4); |
||
225 | } |
||
226 | |||
227 | View Code Duplication | function read_short() { |
|
228 | $this->_pos += 2; |
||
229 | $s = fread($this->fh,2); |
||
230 | $a = (ord($s[0])<<8) + ord($s[1]); |
||
231 | if ($a & (1 << 15) ) { |
||
232 | $a = ($a - (1 << 16)); |
||
233 | } |
||
234 | return $a; |
||
235 | } |
||
236 | |||
237 | View Code Duplication | function unpack_short($s) { |
|
238 | $a = (ord($s[0])<<8) + ord($s[1]); |
||
239 | if ($a & (1 << 15) ) { |
||
240 | $a = ($a - (1 << 16)); |
||
241 | } |
||
242 | return $a; |
||
243 | } |
||
244 | |||
245 | function read_ushort() { |
||
246 | $this->_pos += 2; |
||
247 | $s = fread($this->fh,2); |
||
248 | return (ord($s[0])<<8) + ord($s[1]); |
||
249 | } |
||
250 | |||
251 | function read_ulong() { |
||
252 | $this->_pos += 4; |
||
253 | $s = fread($this->fh,4); |
||
254 | // if large uInt32 as an integer, PHP converts it to -ve |
||
255 | return (ord($s[0])*16777216) + (ord($s[1])<<16) + (ord($s[2])<<8) + ord($s[3]); // 16777216 = 1<<24 |
||
256 | } |
||
257 | |||
258 | function get_ushort($pos) { |
||
259 | fseek($this->fh,$pos); |
||
260 | $s = fread($this->fh,2); |
||
261 | return (ord($s[0])<<8) + ord($s[1]); |
||
262 | } |
||
263 | |||
264 | function get_ulong($pos) { |
||
265 | fseek($this->fh,$pos); |
||
266 | $s = fread($this->fh,4); |
||
267 | // iF large uInt32 as an integer, PHP converts it to -ve |
||
268 | return (ord($s[0])*16777216) + (ord($s[1])<<16) + (ord($s[2])<<8) + ord($s[3]); // 16777216 = 1<<24 |
||
269 | } |
||
270 | |||
271 | View Code Duplication | function pack_short($val) { |
|
272 | if ($val<0) { |
||
273 | $val = abs($val); |
||
274 | $val = ~$val; |
||
275 | $val += 1; |
||
276 | } |
||
277 | return pack("n",$val); |
||
278 | } |
||
279 | |||
280 | function splice($stream, $offset, $value) { |
||
281 | return substr($stream,0,$offset) . $value . substr($stream,$offset+strlen($value)); |
||
282 | } |
||
283 | |||
284 | function _set_ushort($stream, $offset, $value) { |
||
285 | $up = pack("n", $value); |
||
286 | return $this->splice($stream, $offset, $up); |
||
287 | } |
||
288 | |||
289 | View Code Duplication | function _set_short($stream, $offset, $val) { |
|
290 | if ($val<0) { |
||
291 | $val = abs($val); |
||
292 | $val = ~$val; |
||
293 | $val += 1; |
||
294 | } |
||
295 | $up = pack("n",$val); |
||
296 | return $this->splice($stream, $offset, $up); |
||
297 | } |
||
298 | |||
299 | function get_chunk($pos, $length) { |
||
300 | fseek($this->fh,$pos); |
||
301 | if ($length <1) { return ''; } |
||
302 | return (fread($this->fh,$length)); |
||
303 | } |
||
304 | |||
305 | View Code Duplication | function get_table($tag) { |
|
306 | list($pos, $length) = $this->get_table_pos($tag); |
||
307 | if ($length == 0) { return ''; } |
||
308 | fseek($this->fh,$pos); |
||
309 | return (fread($this->fh,$length)); |
||
310 | } |
||
311 | |||
312 | function add($tag, $data) { |
||
313 | if ($tag == 'head') { |
||
314 | $data = $this->splice($data, 8, "\0\0\0\0"); |
||
315 | } |
||
316 | $this->otables[$tag] = $data; |
||
317 | } |
||
318 | |||
319 | |||
320 | |||
321 | |||
322 | ///////////////////////////////////////////////////////////////////////////////////////// |
||
323 | |||
324 | ///////////////////////////////////////////////////////////////////////////////////////// |
||
325 | |||
326 | function extractInfo($debug=false, $BMPonly=false, $kerninfo=false, $useOTL=0) { |
||
327 | $this->panose = array(); |
||
328 | $this->sFamilyClass = 0; |
||
329 | $this->sFamilySubClass = 0; |
||
330 | /////////////////////////////////// |
||
331 | // name - Naming table |
||
332 | /////////////////////////////////// |
||
333 | $name_offset = $this->seek_table("name"); |
||
334 | $format = $this->read_ushort(); |
||
335 | if ($format != 0 && $format != 1) |
||
336 | die("Unknown name table format ".$format); |
||
337 | $numRecords = $this->read_ushort(); |
||
338 | $string_data_offset = $name_offset + $this->read_ushort(); |
||
339 | $names = array(1=>'',2=>'',3=>'',4=>'',6=>''); |
||
340 | $K = array_keys($names); |
||
341 | $nameCount = count($names); |
||
342 | View Code Duplication | for ($i=0;$i<$numRecords; $i++) { |
|
343 | $platformId = $this->read_ushort(); |
||
344 | $encodingId = $this->read_ushort(); |
||
345 | $languageId = $this->read_ushort(); |
||
346 | $nameId = $this->read_ushort(); |
||
347 | $length = $this->read_ushort(); |
||
348 | $offset = $this->read_ushort(); |
||
349 | if (!in_array($nameId,$K)) continue; |
||
350 | $N = ''; |
||
351 | if ($platformId == 3 && $encodingId == 1 && $languageId == 0x409) { // Microsoft, Unicode, US English, PS Name |
||
352 | $opos = $this->_pos; |
||
353 | $this->seek($string_data_offset + $offset); |
||
354 | if ($length % 2 != 0) |
||
355 | die("PostScript name is UTF-16BE string of odd length"); |
||
356 | $length /= 2; |
||
357 | $N = ''; |
||
358 | while ($length > 0) { |
||
359 | $char = $this->read_ushort(); |
||
360 | $N .= (chr($char)); |
||
361 | $length -= 1; |
||
362 | } |
||
363 | $this->_pos = $opos; |
||
364 | $this->seek($opos); |
||
365 | } |
||
366 | else if ($platformId == 1 && $encodingId == 0 && $languageId == 0) { // Macintosh, Roman, English, PS Name |
||
367 | $opos = $this->_pos; |
||
368 | $N = $this->get_chunk($string_data_offset + $offset, $length); |
||
369 | $this->_pos = $opos; |
||
370 | $this->seek($opos); |
||
371 | } |
||
372 | if ($N && $names[$nameId]=='') { |
||
373 | $names[$nameId] = $N; |
||
374 | $nameCount -= 1; |
||
375 | if ($nameCount==0) break; |
||
376 | } |
||
377 | } |
||
378 | View Code Duplication | if ($names[6]) |
|
379 | $psName = $names[6]; |
||
380 | else if ($names[4]) |
||
381 | $psName = preg_replace('/ /','-',$names[4]); |
||
382 | else if ($names[1]) |
||
383 | $psName = preg_replace('/ /','-',$names[1]); |
||
384 | else |
||
385 | $psName = ''; |
||
386 | if (!$psName) |
||
387 | die("Could not find PostScript font name: ".$this->filename); |
||
388 | if ($debug) { |
||
389 | for ($i=0;$i<count($psName);$i++) { |
||
390 | $c = $psName[$i]; |
||
391 | $oc = ord($c); |
||
392 | if ($oc>126 || strpos(' [](){}<>/%',$c)!==false) |
||
393 | die("psName=".$psName." contains invalid character ".$c." ie U+".ord(c)); |
||
394 | } |
||
395 | } |
||
396 | $this->name = $psName; |
||
397 | if ($names[1]) { $this->familyName = $names[1]; } else { $this->familyName = $psName; } |
||
398 | if ($names[2]) { $this->styleName = $names[2]; } else { $this->styleName = 'Regular'; } |
||
399 | if ($names[4]) { $this->fullName = $names[4]; } else { $this->fullName = $psName; } |
||
400 | if ($names[3]) { $this->uniqueFontID = $names[3]; } else { $this->uniqueFontID = $psName; } |
||
401 | |||
402 | if ($names[6]) { $this->fullName = $names[6]; } |
||
403 | |||
404 | /////////////////////////////////// |
||
405 | // head - Font header table |
||
406 | /////////////////////////////////// |
||
407 | $this->seek_table("head"); |
||
408 | View Code Duplication | if ($debug) { |
|
409 | $ver_maj = $this->read_ushort(); |
||
410 | $ver_min = $this->read_ushort(); |
||
411 | if ($ver_maj != 1) |
||
412 | die('Unknown head table version '. $ver_maj .'.'. $ver_min); |
||
413 | $this->fontRevision = $this->read_ushort() . $this->read_ushort(); |
||
414 | |||
415 | $this->skip(4); |
||
416 | $magic = $this->read_ulong(); |
||
417 | if ($magic != 0x5F0F3CF5) |
||
418 | die('Invalid head table magic ' .$magic); |
||
419 | $this->skip(2); |
||
420 | } |
||
421 | else { |
||
422 | $this->skip(18); |
||
423 | } |
||
424 | $this->unitsPerEm = $unitsPerEm = $this->read_ushort(); |
||
425 | $scale = 1000 / $unitsPerEm; |
||
426 | $this->skip(16); |
||
427 | $xMin = $this->read_short(); |
||
428 | $yMin = $this->read_short(); |
||
429 | $xMax = $this->read_short(); |
||
430 | $yMax = $this->read_short(); |
||
431 | $this->bbox = array(($xMin*$scale), ($yMin*$scale), ($xMax*$scale), ($yMax*$scale)); |
||
432 | $this->skip(3*2); |
||
433 | $indexToLocFormat = $this->read_ushort(); |
||
434 | $glyphDataFormat = $this->read_ushort(); |
||
435 | if ($glyphDataFormat != 0) |
||
436 | die('Unknown glyph data format '.$glyphDataFormat); |
||
437 | |||
438 | /////////////////////////////////// |
||
439 | // hhea metrics table |
||
440 | /////////////////////////////////// |
||
441 | // ttf2t1 seems to use this value rather than the one in OS/2 - so put in for compatibility |
||
442 | if (isset($this->tables["hhea"])) { |
||
443 | $this->seek_table("hhea"); |
||
444 | $this->skip(4); |
||
445 | $hheaAscender = $this->read_short(); |
||
446 | $hheaDescender = $this->read_short(); |
||
447 | $this->ascent = ($hheaAscender *$scale); |
||
448 | $this->descent = ($hheaDescender *$scale); |
||
449 | } |
||
450 | |||
451 | /////////////////////////////////// |
||
452 | // OS/2 - OS/2 and Windows metrics table |
||
453 | /////////////////////////////////// |
||
454 | if (isset($this->tables["OS/2"])) { |
||
455 | $this->seek_table("OS/2"); |
||
456 | $version = $this->read_ushort(); |
||
457 | $this->skip(2); |
||
458 | $usWeightClass = $this->read_ushort(); |
||
459 | $this->skip(2); |
||
460 | $fsType = $this->read_ushort(); |
||
461 | if ($fsType == 0x0002 || ($fsType & 0x0300) != 0) { |
||
462 | global $overrideTTFFontRestriction; |
||
463 | if (!$overrideTTFFontRestriction) die('ERROR - Font file '.$this->filename.' cannot be embedded due to copyright restrictions.'); |
||
464 | $this->restrictedUse = true; |
||
465 | } |
||
466 | $this->skip(20); |
||
467 | $sF = $this->read_short(); |
||
468 | $this->sFamilyClass = ($sF >> 8); |
||
469 | $this->sFamilySubClass = ($sF & 0xFF); |
||
470 | $this->_pos += 10; //PANOSE = 10 byte length |
||
471 | $panose = fread($this->fh,10); |
||
472 | $this->panose = array(); |
||
473 | View Code Duplication | for ($p=0;$p<strlen($panose);$p++) { $this->panose[] = ord($panose[$p]); } |
|
474 | $this->skip(26); |
||
475 | $sTypoAscender = $this->read_short(); |
||
476 | $sTypoDescender = $this->read_short(); |
||
477 | if (!$this->ascent) $this->ascent = ($sTypoAscender*$scale); |
||
478 | if (!$this->descent) $this->descent = ($sTypoDescender*$scale); |
||
479 | if ($version > 1) { |
||
480 | $this->skip(16); |
||
481 | $sCapHeight = $this->read_short(); |
||
482 | $this->capHeight = ($sCapHeight*$scale); |
||
483 | } |
||
484 | else { |
||
485 | $this->capHeight = $this->ascent; |
||
486 | } |
||
487 | } |
||
488 | else { |
||
489 | $usWeightClass = 500; |
||
490 | if (!$this->ascent) $this->ascent = ($yMax*$scale); |
||
491 | if (!$this->descent) $this->descent = ($yMin*$scale); |
||
492 | $this->capHeight = $this->ascent; |
||
493 | } |
||
494 | $this->stemV = 50 + intval(pow(($usWeightClass / 65.0),2)); |
||
495 | |||
496 | /////////////////////////////////// |
||
497 | // post - PostScript table |
||
498 | /////////////////////////////////// |
||
499 | $this->seek_table("post"); |
||
500 | View Code Duplication | if ($debug) { |
|
501 | $ver_maj = $this->read_ushort(); |
||
502 | $ver_min = $this->read_ushort(); |
||
503 | if ($ver_maj <1 || $ver_maj >4) |
||
504 | die('Unknown post table version '.$ver_maj); |
||
505 | } |
||
506 | else { |
||
507 | $this->skip(4); |
||
508 | } |
||
509 | $this->italicAngle = $this->read_short() + $this->read_ushort() / 65536.0; |
||
510 | $this->underlinePosition = $this->read_short() * $scale; |
||
511 | $this->underlineThickness = $this->read_short() * $scale; |
||
512 | $isFixedPitch = $this->read_ulong(); |
||
513 | |||
514 | $this->flags = 4; |
||
515 | |||
516 | if ($this->italicAngle!= 0) |
||
517 | $this->flags = $this->flags | 64; |
||
518 | if ($usWeightClass >= 600) |
||
519 | $this->flags = $this->flags | 262144; |
||
520 | if ($isFixedPitch) |
||
521 | $this->flags = $this->flags | 1; |
||
522 | |||
523 | /////////////////////////////////// |
||
524 | // hhea - Horizontal header table |
||
525 | /////////////////////////////////// |
||
526 | $this->seek_table("hhea"); |
||
527 | View Code Duplication | if ($debug) { |
|
528 | $ver_maj = $this->read_ushort(); |
||
529 | $ver_min = $this->read_ushort(); |
||
530 | if ($ver_maj != 1) |
||
531 | die('Unknown hhea table version '.$ver_maj); |
||
532 | $this->skip(28); |
||
533 | } |
||
534 | else { |
||
535 | $this->skip(32); |
||
536 | } |
||
537 | $metricDataFormat = $this->read_ushort(); |
||
538 | if ($metricDataFormat != 0) |
||
539 | die('Unknown horizontal metric data format '.$metricDataFormat); |
||
540 | $numberOfHMetrics = $this->read_ushort(); |
||
541 | if ($numberOfHMetrics == 0) |
||
542 | die('Number of horizontal metrics is 0'); |
||
543 | |||
544 | /////////////////////////////////// |
||
545 | // maxp - Maximum profile table |
||
546 | /////////////////////////////////// |
||
547 | $this->seek_table("maxp"); |
||
548 | View Code Duplication | if ($debug) { |
|
549 | $ver_maj = $this->read_ushort(); |
||
550 | $ver_min = $this->read_ushort(); |
||
551 | if ($ver_maj != 1) |
||
552 | die('Unknown maxp table version '.$ver_maj); |
||
553 | } |
||
554 | else { |
||
555 | $this->skip(4); |
||
556 | } |
||
557 | $numGlyphs = $this->read_ushort(); |
||
558 | |||
559 | |||
560 | /////////////////////////////////// |
||
561 | // cmap - Character to glyph index mapping table |
||
562 | /////////////////////////////////// |
||
563 | $cmap_offset = $this->seek_table("cmap"); |
||
564 | $this->skip(2); |
||
565 | $cmapTableCount = $this->read_ushort(); |
||
566 | $unicode_cmap_offset = 0; |
||
567 | for ($i=0;$i<$cmapTableCount;$i++) { |
||
568 | $platformID = $this->read_ushort(); |
||
569 | $encodingID = $this->read_ushort(); |
||
570 | $offset = $this->read_ulong(); |
||
571 | $save_pos = $this->_pos; |
||
572 | if (($platformID == 3 && $encodingID == 1) || $platformID == 0) { // Microsoft, Unicode |
||
573 | $format = $this->get_ushort($cmap_offset + $offset); |
||
574 | if ($format == 4) { |
||
575 | if (!$unicode_cmap_offset) $unicode_cmap_offset = $cmap_offset + $offset; |
||
576 | if ($BMPonly) break; |
||
577 | } |
||
578 | } |
||
579 | // Microsoft, Unicode Format 12 table HKCS |
||
580 | else if ((($platformID == 3 && $encodingID == 10) || $platformID == 0) && !$BMPonly) { |
||
581 | $format = $this->get_ushort($cmap_offset + $offset); |
||
582 | if ($format == 12) { |
||
583 | $unicode_cmap_offset = $cmap_offset + $offset; |
||
584 | break; |
||
585 | } |
||
586 | } |
||
587 | $this->seek($save_pos ); |
||
588 | } |
||
589 | |||
590 | if (!$unicode_cmap_offset) |
||
591 | die('Font ('.$this->filename .') does not have cmap for Unicode (platform 3, encoding 1, format 4, or platform 0, any encoding, format 4)'); |
||
592 | |||
593 | |||
594 | $sipset = false; |
||
595 | $smpset = false; |
||
596 | |||
597 | // mPDF 5.7.1 |
||
598 | $this->GSUBScriptLang = array(); |
||
599 | $this->rtlPUAstr = ''; |
||
600 | $this->rtlPUAarr = array(); |
||
601 | $this->GSUBFeatures = array(); |
||
602 | $this->GSUBLookups = array(); |
||
603 | $this->GPOSScriptLang = array(); |
||
604 | $this->GPOSFeatures = array(); |
||
605 | $this->GPOSLookups = array(); |
||
606 | $this->glyphIDtoUni = ''; |
||
607 | |||
608 | // Format 12 CMAP does characters above Unicode BMP i.e. some HKCS characters U+20000 and above |
||
609 | View Code Duplication | if ($format == 12 && !$BMPonly) { |
|
610 | $this->maxUniChar = 0; |
||
611 | $this->seek($unicode_cmap_offset + 4); |
||
612 | $length = $this->read_ulong(); |
||
613 | $limit = $unicode_cmap_offset + $length; |
||
614 | $this->skip(4); |
||
615 | |||
616 | $nGroups = $this->read_ulong(); |
||
617 | |||
618 | $glyphToChar = array(); |
||
619 | $charToGlyph = array(); |
||
620 | for($i=0; $i<$nGroups ; $i++) { |
||
621 | $startCharCode = $this->read_ulong(); |
||
622 | $endCharCode = $this->read_ulong(); |
||
623 | $startGlyphCode = $this->read_ulong(); |
||
624 | if ($endCharCode > 0x20000 && $endCharCode < 0x2FFFF) { |
||
625 | $sipset = true; |
||
626 | } |
||
627 | else if ($endCharCode > 0x10000 && $endCharCode < 0x1FFFF) { |
||
628 | $smpset = true; |
||
629 | } |
||
630 | $offset = 0; |
||
631 | for ($unichar=$startCharCode;$unichar<=$endCharCode;$unichar++) { |
||
632 | $glyph = $startGlyphCode + $offset ; |
||
633 | $offset++; |
||
634 | if ($unichar < 0x30000) { |
||
635 | $charToGlyph[$unichar] = $glyph; |
||
636 | $this->maxUniChar = max($unichar,$this->maxUniChar); |
||
637 | $glyphToChar[$glyph][] = $unichar; |
||
638 | } |
||
639 | } |
||
640 | } |
||
641 | } |
||
642 | else { |
||
643 | |||
644 | $glyphToChar = array(); |
||
645 | $charToGlyph = array(); |
||
646 | $this->getCMAP4($unicode_cmap_offset, $glyphToChar, $charToGlyph ); |
||
647 | |||
648 | } |
||
649 | $this->sipset = $sipset ; |
||
650 | $this->smpset = $smpset ; |
||
651 | |||
652 | |||
653 | /////////////////////////////////// |
||
654 | // mPDF 5.7.1 |
||
655 | // Map Unmapped glyphs - from $numGlyphs |
||
656 | View Code Duplication | if ($this->useOTL) { |
|
657 | $bctr = 0xE000; |
||
658 | for ($gid=1; $gid<$numGlyphs; $gid++) { |
||
659 | if (!isset($glyphToChar[$gid])) { |
||
660 | while(isset($charToGlyph[$bctr])) { $bctr++; } // Avoid overwriting a glyph already mapped in PUA |
||
661 | if (($bctr > 0xF8FF) && ($bctr < 0x2CEB0)) { |
||
662 | if (!$BMPonly) { |
||
663 | $bctr = 0x2CEB0; // Use unassigned area 0x2CEB0 to 0x2F7FF (space for 10,000 characters) |
||
664 | $this->sipset = $sipset = true; // forces subsetting; also ensure charwidths are saved |
||
665 | while(isset($charToGlyph[$bctr])) { $bctr++; } |
||
666 | } |
||
667 | else { die($names[1]." : WARNING - The font does not have enough space to map all (unmapped) included glyphs into Private Use Area U+E000 - U+F8FF"); } |
||
668 | } |
||
669 | $glyphToChar[$gid][] = $bctr; |
||
670 | $charToGlyph[$bctr] = $gid; |
||
671 | $this->maxUniChar = max($bctr,$this->maxUniChar); |
||
672 | $bctr++; |
||
673 | } |
||
674 | } |
||
675 | } |
||
676 | $this->glyphToChar = $glyphToChar; |
||
677 | $this->charToGlyph = $charToGlyph; |
||
678 | /////////////////////////////////// |
||
679 | // mPDF 5.7.1 OpenType Layout tables |
||
680 | $this->GSUBScriptLang=array(); $this->rtlPUAstr = ''; $this->rtlPUAarr = array(); |
||
681 | View Code Duplication | if ($useOTL) { |
|
682 | $this->_getGDEFtables(); |
||
683 | list($this->GSUBScriptLang, $this->GSUBFeatures, $this->GSUBLookups, $this->rtlPUAstr, $this->rtlPUAarr) = $this->_getGSUBtables(); |
||
684 | list($this->GPOSScriptLang, $this->GPOSFeatures, $this->GPOSLookups) = $this->_getGPOStables(); |
||
685 | $this->glyphIDtoUni = str_pad('', 256*256*3, "\x00"); |
||
686 | foreach($glyphToChar AS $gid=>$arr) { |
||
687 | if (isset($glyphToChar[$gid][0])) { |
||
688 | $char = $glyphToChar[$gid][0]; |
||
689 | if ($char != 0 && $char != 65535) { |
||
690 | $this->glyphIDtoUni[$gid*3] = chr($char >> 16); |
||
691 | $this->glyphIDtoUni[$gid*3 + 1] = chr(($char >> 8) & 0xFF); |
||
692 | $this->glyphIDtoUni[$gid*3 + 2] = chr($char & 0xFF); |
||
693 | } |
||
694 | } |
||
695 | } |
||
696 | } |
||
697 | /////////////////////////////////// |
||
698 | |||
699 | /////////////////////////////////// |
||
700 | // hmtx - Horizontal metrics table |
||
701 | /////////////////////////////////// |
||
702 | $this->getHMTX($numberOfHMetrics, $numGlyphs, $glyphToChar, $scale); |
||
703 | |||
704 | /////////////////////////////////// |
||
705 | // kern - Kerning pair table |
||
706 | /////////////////////////////////// |
||
707 | if ($kerninfo) { |
||
708 | // Recognises old form of Kerning table - as required by Windows - Format 0 only |
||
709 | $kern_offset = $this->seek_table("kern"); |
||
710 | $version = $this->read_ushort(); |
||
711 | $nTables = $this->read_ushort(); |
||
712 | // subtable header |
||
713 | $sversion = $this->read_ushort(); |
||
714 | $slength = $this->read_ushort(); |
||
715 | $scoverage = $this->read_ushort(); |
||
716 | $format = $scoverage >> 8; |
||
717 | View Code Duplication | if ($kern_offset && $version==0 && $format==0) { |
|
718 | // Format 0 |
||
719 | $nPairs = $this->read_ushort(); |
||
720 | $this->skip(6); |
||
721 | for ($i=0; $i<$nPairs; $i++) { |
||
722 | $left = $this->read_ushort(); |
||
723 | $right = $this->read_ushort(); |
||
724 | $val = $this->read_short(); |
||
725 | if (count($glyphToChar[$left])==1 && count($glyphToChar[$right])==1) { |
||
726 | if ($left != 32 && $right != 32) { |
||
727 | $this->kerninfo[$glyphToChar[$left][0]][$glyphToChar[$right][0]] = intval($val*$scale); |
||
728 | } |
||
729 | } |
||
730 | } |
||
731 | } |
||
732 | } |
||
733 | } |
||
734 | |||
735 | |||
736 | ///////////////////////////////////////////////////////////////////////////////////////// |
||
737 | function _getGDEFtables() { |
||
738 | /////////////////////////////////// |
||
739 | // GDEF - Glyph Definition |
||
740 | /////////////////////////////////// |
||
741 | // http://www.microsoft.com/typography/otspec/gdef.htm |
||
742 | if (isset($this->tables["GDEF"])) { |
||
743 | if ($this->mode == 'summary') { $this->mpdf->WriteHTML('<h1>GDEF table</h1>'); } |
||
744 | $gdef_offset = $this->seek_table("GDEF"); |
||
745 | // ULONG Version of the GDEF table-currently 0x00010000 |
||
746 | $ver_maj = $this->read_ushort(); |
||
747 | $ver_min = $this->read_ushort(); |
||
748 | // Version 0x00010002 of GDEF header contains additional Offset to a list defining mark glyph set definitions (MarkGlyphSetDef) |
||
749 | $GlyphClassDef_offset = $this->read_ushort(); |
||
750 | $AttachList_offset = $this->read_ushort(); |
||
751 | $LigCaretList_offset = $this->read_ushort(); |
||
752 | $MarkAttachClassDef_offset = $this->read_ushort(); |
||
753 | if ($ver_min == 2) { |
||
754 | $MarkGlyphSetsDef_offset = $this->read_ushort(); |
||
755 | } |
||
756 | |||
757 | // GlyphClassDef |
||
758 | $this->seek($gdef_offset+$GlyphClassDef_offset ); |
||
759 | /* |
||
760 | 1 Base glyph (single character, spacing glyph) |
||
761 | 2 Ligature glyph (multiple character, spacing glyph) |
||
762 | 3 Mark glyph (non-spacing combining glyph) |
||
763 | 4 Component glyph (part of single character, spacing glyph) |
||
764 | */ |
||
765 | $GlyphByClass = $this->_getClassDefinitionTable(); |
||
766 | |||
767 | if ($this->mode == 'summary') { |
||
768 | $this->mpdf->WriteHTML('<h2>Glyph classes</h2>'); |
||
769 | } |
||
770 | |||
771 | View Code Duplication | if (isset($GlyphByClass[1]) && count($GlyphByClass[1])>0) { |
|
772 | $this->GlyphClassBases = $this->formatClassArr($GlyphByClass[1]); |
||
773 | if ($this->mode == 'summary') { |
||
774 | $this->mpdf->WriteHTML('<h3>Glyph class 1</h3>'); |
||
775 | $this->mpdf->WriteHTML('<h5>Base glyph (single character, spacing glyph)</h5>'); |
||
776 | $html = ''; |
||
777 | $html .= '<div class="glyphs">'; |
||
778 | foreach ($GlyphByClass[1] AS $g) { |
||
779 | $html .= '&#x'.$g.'; '; |
||
780 | } |
||
781 | $html .= '</div>'; |
||
782 | $this->mpdf->WriteHTML($html); |
||
783 | } |
||
784 | } |
||
785 | else { $this->GlyphClassBases = ''; } |
||
786 | View Code Duplication | if (isset($GlyphByClass[2]) && count($GlyphByClass[2])>0) { |
|
787 | $this->GlyphClassLigatures = $this->formatClassArr($GlyphByClass[2]); |
||
788 | if ($this->mode == 'summary') { |
||
789 | $this->mpdf->WriteHTML('<h3>Glyph class 2</h3>'); |
||
790 | $this->mpdf->WriteHTML('<h5>Ligature glyph (multiple character, spacing glyph)</h5>'); |
||
791 | $html = ''; |
||
792 | $html .= '<div class="glyphs">'; |
||
793 | foreach ($GlyphByClass[2] AS $g) { |
||
794 | $html .= '&#x'.$g.'; '; |
||
795 | } |
||
796 | $html .= '</div>'; |
||
797 | $this->mpdf->WriteHTML($html); |
||
798 | } |
||
799 | } |
||
800 | else { $this->GlyphClassLigatures = ''; } |
||
801 | View Code Duplication | if (isset($GlyphByClass[3]) && count($GlyphByClass[3])>0) { |
|
802 | $this->GlyphClassMarks = $this->formatClassArr($GlyphByClass[3]); |
||
803 | if ($this->mode == 'summary') { |
||
804 | $this->mpdf->WriteHTML('<h3>Glyph class 3</h3>'); |
||
805 | $this->mpdf->WriteHTML('<h5>Mark glyph (non-spacing combining glyph)</h5>'); |
||
806 | $html = ''; |
||
807 | $html .= '<div class="glyphs">'; |
||
808 | foreach ($GlyphByClass[3] AS $g) { |
||
809 | $html .= '◌&#x'.$g.'; '; |
||
810 | } |
||
811 | $html .= '</div>'; |
||
812 | $this->mpdf->WriteHTML($html); |
||
813 | } |
||
814 | } |
||
815 | else { $this->GlyphClassMarks = ''; } |
||
816 | View Code Duplication | if (isset($GlyphByClass[4]) && count($GlyphByClass[4])>0) { |
|
817 | $this->GlyphClassComponents = $this->formatClassArr($GlyphByClass[4]); |
||
818 | if ($this->mode == 'summary') { |
||
819 | $this->mpdf->WriteHTML('<h3>Glyph class 4</h3>'); |
||
820 | $this->mpdf->WriteHTML('<h5>Component glyph (part of single character, spacing glyph)</h5>'); |
||
821 | $html = ''; |
||
822 | $html .= '<div class="glyphs">'; |
||
823 | foreach ($GlyphByClass[4] AS $g) { |
||
824 | $html .= '&#x'.$g.'; '; |
||
825 | } |
||
826 | $html .= '</div>'; |
||
827 | $this->mpdf->WriteHTML($html); |
||
828 | } |
||
829 | } |
||
830 | else { $this->GlyphClassComponents = ''; } |
||
831 | |||
832 | $Marks = $GlyphByClass[3]; // to use for MarkAttachmentType |
||
833 | |||
834 | |||
835 | /* Required for GPOS |
||
836 | // Attachment List |
||
837 | if ($AttachList_offset) { |
||
838 | $this->seek($gdef_offset+$AttachList_offset ); |
||
839 | } |
||
840 | The Attachment Point List table (AttachmentList) identifies all the attachment points defined in the GPOS table and their associated glyphs so a client can quickly access coordinates for each glyph's attachment points. As a result, the client can cache coordinates for attachment points along with glyph bitmaps and avoid recalculating the attachment points each time it displays a glyph. Without this table, processing speed would be slower because the client would have to decode the GPOS lookups that define attachment points and compile the points in a list. |
||
841 | |||
842 | The Attachment List table (AttachList) may be used to cache attachment point coordinates along with glyph bitmaps. |
||
843 | |||
844 | The table consists of an offset to a Coverage table (Coverage) listing all glyphs that define attachment points in the GPOS table, a count of the glyphs with attachment points (GlyphCount), and an array of offsets to AttachPoint tables (AttachPoint). The array lists the AttachPoint tables, one for each glyph in the Coverage table, in the same order as the Coverage Index. |
||
845 | AttachList table |
||
846 | Type Name Description |
||
847 | Offset Coverage Offset to Coverage table - from beginning of AttachList table |
||
848 | uint16 GlyphCount Number of glyphs with attachment points |
||
849 | Offset AttachPoint[GlyphCount] Array of offsets to AttachPoint tables-from beginning of AttachList table-in Coverage Index order |
||
850 | |||
851 | An AttachPoint table consists of a count of the attachment points on a single glyph (PointCount) and an array of contour indices of those points (PointIndex), listed in increasing numerical order. |
||
852 | |||
853 | AttachPoint table |
||
854 | Type Name Description |
||
855 | uint16 PointCount Number of attachment points on this glyph |
||
856 | uint16 PointIndex[PointCount] Array of contour point indices -in increasing numerical order |
||
857 | |||
858 | See Example 3 - http://www.microsoft.com/typography/otspec/gdef.htm |
||
859 | */ |
||
860 | |||
861 | |||
862 | // Ligature Caret List |
||
863 | // The Ligature Caret List table (LigCaretList) defines caret positions for all the ligatures in a font. |
||
864 | // Not required for mDPF |
||
865 | |||
866 | |||
867 | // MarkAttachmentType |
||
868 | if ($MarkAttachClassDef_offset) { |
||
869 | if ($this->mode == 'summary') { $this->mpdf->WriteHTML('<h1>Mark Attachment Types</h1>'); } |
||
870 | $this->seek($gdef_offset+$MarkAttachClassDef_offset ); |
||
871 | $MarkAttachmentTypes = $this->_getClassDefinitionTable(); |
||
872 | foreach($MarkAttachmentTypes AS $class=>$glyphs) { |
||
873 | |||
874 | View Code Duplication | if (is_array($Marks) && count($Marks)) { |
|
875 | $mat = array_diff($Marks, $MarkAttachmentTypes[$class]); |
||
876 | sort($mat, SORT_STRING); |
||
877 | } |
||
878 | else { $mat = array(); } |
||
879 | |||
880 | $this->MarkAttachmentType[$class] = $this->formatClassArr($mat); |
||
881 | |||
882 | View Code Duplication | if ($this->mode == 'summary') { |
|
883 | $this->mpdf->WriteHTML('<h3>Mark Attachment Type: '.$class.'</h3>'); |
||
884 | $html = ''; |
||
885 | $html .= '<div class="glyphs">'; |
||
886 | foreach ($glyphs AS $g) { |
||
887 | $html .= '◌&#x'.$g.'; '; |
||
888 | } |
||
889 | $html .= '</div>'; |
||
890 | $this->mpdf->WriteHTML($html); |
||
891 | } |
||
892 | } |
||
893 | } |
||
894 | else { $this->MarkAttachmentType = array(); } |
||
895 | |||
896 | |||
897 | // MarkGlyphSets only in Version 0x00010002 of GDEF |
||
898 | if ($ver_min == 2 && $MarkGlyphSetsDef_offset) { |
||
899 | if ($this->mode == 'summary') { $this->mpdf->WriteHTML('<h1>Mark Glyph Sets</h1>'); } |
||
900 | $this->seek($gdef_offset+$MarkGlyphSetsDef_offset); |
||
901 | $MarkSetTableFormat = $this->read_ushort(); |
||
902 | $MarkSetCount = $this->read_ushort(); |
||
903 | $MarkSetOffset = array(); |
||
904 | for ($i=0;$i<$MarkSetCount;$i++) { |
||
905 | $MarkSetOffset[] = $this->read_ulong(); |
||
906 | } |
||
907 | for ($i=0;$i<$MarkSetCount;$i++) { |
||
908 | $this->seek($MarkSetOffset[$i]); |
||
909 | $glyphs = $this->_getCoverage(); |
||
910 | $this->MarkGlyphSets[$i] = $this->formatClassArr($glyphs); |
||
911 | View Code Duplication | if ($this->mode == 'summary') { |
|
912 | $this->mpdf->WriteHTML('<h3>Mark Glyph Set class: '.$i.'</h3>'); |
||
913 | $html = ''; |
||
914 | $html .= '<div class="glyphs">'; |
||
915 | foreach ($glyphs AS $g) { |
||
916 | $html .= '◌&#x'.$g.'; '; |
||
917 | } |
||
918 | $html .= '</div>'; |
||
919 | $this->mpdf->WriteHTML($html); |
||
920 | } |
||
921 | } |
||
922 | } |
||
923 | else { $this->MarkGlyphSets = array(); } |
||
924 | } |
||
925 | else { $this->mpdf->WriteHTML('<div>GDEF table not defined</div>'); } |
||
926 | |||
927 | |||
928 | //echo $this->GlyphClassMarks ; exit; |
||
929 | //print_r($GlyphClass); exit; |
||
930 | //print_r($GlyphByClass); exit; |
||
931 | } |
||
932 | |||
933 | function _getClassDefinitionTable($offset=0) { |
||
934 | |||
935 | if ($offset>0) { |
||
936 | $this->seek($offset); |
||
937 | } |
||
938 | |||
939 | // NB Any glyph not included in the range of covered GlyphIDs automatically belongs to Class 0. This is not returned by this function |
||
940 | $ClassFormat = $this->read_ushort(); |
||
941 | $GlyphByClass = array(); |
||
942 | if ($ClassFormat == 1) { |
||
943 | $StartGlyph = $this->read_ushort(); |
||
944 | $GlyphCount = $this->read_ushort(); |
||
945 | View Code Duplication | for ($i=0;$i<$GlyphCount;$i++) { |
|
946 | $gid = $StartGlyph + $i; |
||
947 | $class = $this->read_ushort(); |
||
948 | $GlyphByClass[$class][] = unicode_hex($this->glyphToChar[$gid][0]); |
||
949 | } |
||
950 | } |
||
951 | else if ($ClassFormat == 2) { |
||
952 | $tableCount = $this->read_ushort(); |
||
953 | for ($i=0;$i<$tableCount;$i++) { |
||
954 | $startGlyphID = $this->read_ushort(); |
||
955 | $endGlyphID = $this->read_ushort(); |
||
956 | $class = $this->read_ushort(); |
||
957 | for($gid=$startGlyphID;$gid<=$endGlyphID;$gid++) { |
||
958 | $GlyphByClass[$class][] = unicode_hex($this->glyphToChar[$gid][0]); |
||
959 | } |
||
960 | } |
||
961 | } |
||
962 | ksort($GlyphByClass); |
||
963 | return $GlyphByClass; |
||
964 | } |
||
965 | |||
966 | function _getGSUBtables() { |
||
967 | /////////////////////////////////// |
||
968 | // GSUB - Glyph Substitution |
||
969 | /////////////////////////////////// |
||
970 | if (isset($this->tables["GSUB"])) { |
||
971 | $this->mpdf->WriteHTML('<h1>GSUB Tables</h1>'); |
||
972 | $ffeats = array(); |
||
973 | $gsub_offset = $this->seek_table("GSUB"); |
||
974 | $this->skip(4); |
||
975 | $ScriptList_offset = $gsub_offset + $this->read_ushort(); |
||
976 | $FeatureList_offset = $gsub_offset + $this->read_ushort(); |
||
977 | $LookupList_offset = $gsub_offset + $this->read_ushort(); |
||
978 | |||
979 | // ScriptList |
||
980 | $this->seek($ScriptList_offset ); |
||
981 | $ScriptCount = $this->read_ushort(); |
||
982 | View Code Duplication | for ($i=0;$i<$ScriptCount;$i++) { |
|
983 | $ScriptTag = $this->read_tag(); // = "beng", "deva" etc. |
||
984 | $ScriptTableOffset = $this->read_ushort(); |
||
985 | $ffeats[$ScriptTag] = $ScriptList_offset + $ScriptTableOffset; |
||
986 | } |
||
987 | |||
988 | // Script Table |
||
989 | View Code Duplication | foreach($ffeats AS $t=>$o) { |
|
990 | $ls = array(); |
||
991 | $this->seek($o); |
||
992 | $DefLangSys_offset = $this->read_ushort(); |
||
993 | if ($DefLangSys_offset > 0) { |
||
994 | $ls['DFLT'] = $DefLangSys_offset + $o; |
||
995 | } |
||
996 | $LangSysCount = $this->read_ushort(); |
||
997 | for ($i=0;$i<$LangSysCount;$i++) { |
||
998 | $LangTag = $this->read_tag(); // = |
||
999 | $LangTableOffset = $this->read_ushort(); |
||
1000 | $ls[$LangTag] = $o + $LangTableOffset; |
||
1001 | } |
||
1002 | $ffeats[$t] = $ls; |
||
1003 | } |
||
1004 | //print_r($ffeats); exit; |
||
1005 | |||
1006 | |||
1007 | // Get FeatureIndexList |
||
1008 | // LangSys Table - from first listed langsys |
||
1009 | View Code Duplication | foreach($ffeats AS $st=>$scripts) { |
|
1010 | foreach($scripts AS $t=>$o) { |
||
1011 | $FeatureIndex = array(); |
||
1012 | $langsystable_offset = $o; |
||
1013 | $this->seek($langsystable_offset); |
||
1014 | $LookUpOrder = $this->read_ushort(); //==NULL |
||
1015 | $ReqFeatureIndex = $this->read_ushort(); |
||
1016 | if ($ReqFeatureIndex != 0xFFFF) { $FeatureIndex[] = $ReqFeatureIndex; } |
||
1017 | $FeatureCount = $this->read_ushort(); |
||
1018 | for ($i=0;$i<$FeatureCount;$i++) { |
||
1019 | $FeatureIndex[] = $this->read_ushort(); // = index of feature |
||
1020 | } |
||
1021 | $ffeats[$st][$t] = $FeatureIndex; |
||
1022 | } |
||
1023 | } |
||
1024 | //print_r($ffeats); exit; |
||
1025 | |||
1026 | |||
1027 | // Feauture List => LookupListIndex es |
||
1028 | $this->seek($FeatureList_offset ); |
||
1029 | $FeatureCount = $this->read_ushort(); |
||
1030 | $Feature = array(); |
||
1031 | View Code Duplication | for ($i=0;$i<$FeatureCount;$i++) { |
|
1032 | $Feature[$i] = array('tag' => $this->read_tag() ); |
||
1033 | $Feature[$i]['offset'] = $FeatureList_offset + $this->read_ushort(); |
||
1034 | } |
||
1035 | View Code Duplication | for ($i=0;$i<$FeatureCount;$i++) { |
|
1036 | $this->seek($Feature[$i]['offset']); |
||
1037 | $this->read_ushort(); // null |
||
1038 | $Feature[$i]['LookupCount'] = $Lookupcount = $this->read_ushort(); |
||
1039 | $Feature[$i]['LookupListIndex'] = array(); |
||
1040 | for ($c=0;$c<$Lookupcount;$c++) { |
||
1041 | $Feature[$i]['LookupListIndex'][] = $this->read_ushort(); |
||
1042 | } |
||
1043 | } |
||
1044 | |||
1045 | |||
1046 | View Code Duplication | foreach($ffeats AS $st=>$scripts) { |
|
1047 | foreach($scripts AS $t=>$o) { |
||
1048 | $FeatureIndex = $ffeats[$st][$t]; |
||
1049 | foreach($FeatureIndex AS $k=>$fi) { |
||
1050 | $ffeats[$st][$t][$k] = $Feature[$fi]; |
||
1051 | } |
||
1052 | } |
||
1053 | } |
||
1054 | //===================================================================================== |
||
1055 | $gsub = array(); |
||
1056 | $GSUBScriptLang = array(); |
||
1057 | View Code Duplication | foreach($ffeats AS $st=>$scripts) { |
|
1058 | foreach($scripts AS $t=>$langsys) { |
||
1059 | $lg = array(); |
||
1060 | foreach($langsys AS $ft) { |
||
1061 | $lg[$ft['LookupListIndex'][0]] = $ft; |
||
1062 | } |
||
1063 | // list of Lookups in order they need to be run i.e. order listed in Lookup table |
||
1064 | ksort($lg); |
||
1065 | foreach($lg AS $ft) { |
||
1066 | $gsub[$st][$t][$ft['tag']] = $ft['LookupListIndex']; |
||
1067 | } |
||
1068 | if (!isset($GSUBScriptLang[$st])) { $GSUBScriptLang[$st] = ''; } |
||
1069 | $GSUBScriptLang[$st] .= $t.' '; |
||
1070 | } |
||
1071 | } |
||
1072 | |||
1073 | //print_r($gsub); exit; |
||
1074 | |||
1075 | View Code Duplication | if ($this->mode == 'summary') { |
|
1076 | $this->mpdf->WriteHTML('<h3>GSUB Scripts & Languages</h3>'); |
||
1077 | $this->mpdf->WriteHTML('<div class="glyphs">'); |
||
1078 | $html = ''; |
||
1079 | if (count($gsub)) { |
||
1080 | foreach ($gsub AS $st=>$g) { |
||
1081 | $html .= '<h5>'.$st.'</h5>'; |
||
1082 | foreach ($g AS $l=>$t) { |
||
1083 | $html .= '<div><a href="font_dump_OTL.php?script='.$st.'&lang='.$l.'">'.$l.'</a></b>: '; |
||
1084 | foreach ($t AS $tag=>$o) { |
||
1085 | $html .= $tag.' '; |
||
1086 | } |
||
1087 | $html .= '</div>'; |
||
1088 | } |
||
1089 | } |
||
1090 | } |
||
1091 | else { |
||
1092 | $html .= '<div>No entries in GSUB table.</div>'; |
||
1093 | } |
||
1094 | $this->mpdf->WriteHTML($html); |
||
1095 | $this->mpdf->WriteHTML('</div>'); |
||
1096 | return 0; |
||
1097 | } |
||
1098 | |||
1099 | |||
1100 | |||
1101 | //===================================================================================== |
||
1102 | // Get metadata and offsets for whole Lookup List table |
||
1103 | $this->seek($LookupList_offset ); |
||
1104 | $LookupCount = $this->read_ushort(); |
||
1105 | $GSLookup = array(); |
||
1106 | $Offsets = array(); |
||
1107 | $SubtableCount = array(); |
||
1108 | View Code Duplication | for ($i=0;$i<$LookupCount;$i++) { |
|
1109 | $Offsets[$i] = $LookupList_offset + $this->read_ushort(); |
||
1110 | } |
||
1111 | View Code Duplication | for ($i=0;$i<$LookupCount;$i++) { |
|
1112 | $this->seek($Offsets[$i]); |
||
1113 | $GSLookup[$i]['Type'] = $this->read_ushort(); |
||
1114 | $GSLookup[$i]['Flag'] = $flag = $this->read_ushort(); |
||
1115 | $GSLookup[$i]['SubtableCount'] = $SubtableCount[$i] = $this->read_ushort(); |
||
1116 | for ($c=0;$c<$SubtableCount[$i] ;$c++) { |
||
1117 | $GSLookup[$i]['Subtables'][$c] = $Offsets[$i] + $this->read_ushort(); |
||
1118 | |||
1119 | } |
||
1120 | // MarkFilteringSet = Index (base 0) into GDEF mark glyph sets structure |
||
1121 | if (($flag & 0x0010) == 0x0010) { |
||
1122 | $GSLookup[$i]['MarkFilteringSet'] = $this->read_ushort(); |
||
1123 | } |
||
1124 | // else { $GSLookup[$i]['MarkFilteringSet'] = ''; } |
||
1125 | |||
1126 | // Lookup Type 7: Extension |
||
1127 | if ($GSLookup[$i]['Type'] == 7) { |
||
1128 | // Overwrites new offset (32-bit) for each subtable, and a new lookup Type |
||
1129 | for ($c=0;$c<$SubtableCount[$i] ;$c++) { |
||
1130 | $this->seek($GSLookup[$i]['Subtables'][$c]); |
||
1131 | $ExtensionPosFormat = $this->read_ushort(); |
||
1132 | $type = $this->read_ushort(); |
||
1133 | $GSLookup[$i]['Subtables'][$c] = $GSLookup[$i]['Subtables'][$c] + $this->read_ulong(); |
||
1134 | } |
||
1135 | $GSLookup[$i]['Type'] = $type; |
||
1136 | } |
||
1137 | |||
1138 | } |
||
1139 | |||
1140 | //print_r($GSLookup); exit; |
||
1141 | //===================================================================================== |
||
1142 | // Process Whole LookupList - Get LuCoverage = Lookup coverage just for first glyph |
||
1143 | $this->GSLuCoverage = array(); |
||
1144 | View Code Duplication | for ($i=0;$i<$LookupCount;$i++) { |
|
1145 | for ($c=0;$c<$GSLookup[$i]['SubtableCount'] ;$c++) { |
||
1146 | |||
1147 | $this->seek($GSLookup[$i]['Subtables'][$c]); |
||
1148 | $PosFormat= $this->read_ushort(); |
||
1149 | |||
1150 | if ($GSLookup[$i]['Type']==5 && $PosFormat==3) { $this->skip(4); } |
||
1151 | else if ($GSLookup[$i]['Type']==6 && $PosFormat==3) { |
||
1152 | $BacktrackGlyphCount= $this->read_ushort(); |
||
1153 | $this->skip(2*$BacktrackGlyphCount + 2); |
||
1154 | } |
||
1155 | // NB Coverage only looks at glyphs for position 1 (i.e. 5.3 and 6.3) // NEEDS TO READ ALL ******************** |
||
1156 | $Coverage = $GSLookup[$i]['Subtables'][$c] + $this->read_ushort(); |
||
1157 | $this->seek($Coverage); |
||
1158 | $glyphs = $this->_getCoverage(); |
||
1159 | $this->GSLuCoverage[$i][$c] = implode('|',$glyphs); |
||
1160 | } |
||
1161 | } |
||
1162 | |||
1163 | // $this->GSLuCoverage and $GSLookup |
||
1164 | |||
1165 | //===================================================================================== |
||
1166 | $s = '<?php |
||
1167 | $GSLuCoverage = '.var_export($this->GSLuCoverage , true).'; |
||
1168 | ?>'; |
||
1169 | |||
1170 | |||
1171 | //===================================================================================== |
||
1172 | $s = '<?php |
||
1173 | $GlyphClassBases = \''.$this->GlyphClassBases.'\'; |
||
1174 | $GlyphClassMarks = \''.$this->GlyphClassMarks.'\'; |
||
1175 | $GlyphClassLigatures = \''.$this->GlyphClassLigatures.'\'; |
||
1176 | $GlyphClassComponents = \''.$this->GlyphClassComponents.'\'; |
||
1177 | $MarkGlyphSets = '.var_export($this->MarkGlyphSets , true).'; |
||
1178 | $MarkAttachmentType = '.var_export($this->MarkAttachmentType , true).'; |
||
1179 | ?>'; |
||
1180 | |||
1181 | |||
1182 | //===================================================================================== |
||
1183 | //===================================================================================== |
||
1184 | //===================================================================================== |
||
1185 | // Now repeats as original to get Substitution rules |
||
1186 | //===================================================================================== |
||
1187 | //===================================================================================== |
||
1188 | //===================================================================================== |
||
1189 | // Get metadata and offsets for whole Lookup List table |
||
1190 | $this->seek($LookupList_offset ); |
||
1191 | $LookupCount = $this->read_ushort(); |
||
1192 | $Lookup = array(); |
||
1193 | View Code Duplication | for ($i=0;$i<$LookupCount;$i++) { |
|
1194 | $Lookup[$i]['offset'] = $LookupList_offset + $this->read_ushort(); |
||
1195 | } |
||
1196 | View Code Duplication | for ($i=0;$i<$LookupCount;$i++) { |
|
1197 | $this->seek($Lookup[$i]['offset']); |
||
1198 | $Lookup[$i]['Type'] = $this->read_ushort(); |
||
1199 | $Lookup[$i]['Flag'] = $flag = $this->read_ushort(); |
||
1200 | $Lookup[$i]['SubtableCount'] = $this->read_ushort(); |
||
1201 | for ($c=0;$c<$Lookup[$i]['SubtableCount'] ;$c++) { |
||
1202 | $Lookup[$i]['Subtable'][$c]['Offset'] = $Lookup[$i]['offset'] + $this->read_ushort(); |
||
1203 | |||
1204 | } |
||
1205 | // MarkFilteringSet = Index (base 0) into GDEF mark glyph sets structure |
||
1206 | if (($flag & 0x0010) == 0x0010) { |
||
1207 | $Lookup[$i]['MarkFilteringSet'] = $this->read_ushort(); |
||
1208 | } |
||
1209 | else { $Lookup[$i]['MarkFilteringSet'] = ''; } |
||
1210 | |||
1211 | // Lookup Type 7: Extension |
||
1212 | if ($Lookup[$i]['Type'] == 7) { |
||
1213 | // Overwrites new offset (32-bit) for each subtable, and a new lookup Type |
||
1214 | for ($c=0;$c<$Lookup[$i]['SubtableCount'] ;$c++) { |
||
1215 | $this->seek($Lookup[$i]['Subtable'][$c]['Offset']); |
||
1216 | $ExtensionPosFormat = $this->read_ushort(); |
||
1217 | $type = $this->read_ushort(); |
||
1218 | $Lookup[$i]['Subtable'][$c]['Offset'] = $Lookup[$i]['Subtable'][$c]['Offset'] + $this->read_ulong(); |
||
1219 | } |
||
1220 | $Lookup[$i]['Type'] = $type; |
||
1221 | } |
||
1222 | |||
1223 | } |
||
1224 | |||
1225 | //print_r($Lookup); exit; |
||
1226 | //===================================================================================== |
||
1227 | // Process (1) Whole LookupList |
||
1228 | View Code Duplication | for ($i=0;$i<$LookupCount;$i++) { |
|
1229 | for ($c=0;$c<$Lookup[$i]['SubtableCount'] ;$c++) { |
||
1230 | |||
1231 | $this->seek($Lookup[$i]['Subtable'][$c]['Offset']); |
||
1232 | $SubstFormat= $this->read_ushort(); |
||
1233 | $Lookup[$i]['Subtable'][$c]['Format'] = $SubstFormat; |
||
1234 | |||
1235 | /* |
||
1236 | Lookup['Type'] Enumeration table for glyph substitution |
||
1237 | Value Type Description |
||
1238 | 1 Single Replace one glyph with one glyph |
||
1239 | 2 Multiple Replace one glyph with more than one glyph |
||
1240 | 3 Alternate Replace one glyph with one of many glyphs |
||
1241 | 4 Ligature Replace multiple glyphs with one glyph |
||
1242 | 5 Context Replace one or more glyphs in context |
||
1243 | 6 Chaining Context Replace one or more glyphs in chained context |
||
1244 | 7 Extension Substitution Extension mechanism for other substitutions (i.e. this excludes the Extension type substitution itself) |
||
1245 | 8 Reverse chaining context single Applied in reverse order, replace single glyph in chaining context |
||
1246 | */ |
||
1247 | |||
1248 | // LookupType 1: Single Substitution Subtable |
||
1249 | if ($Lookup[$i]['Type'] == 1) { |
||
1250 | $Lookup[$i]['Subtable'][$c]['CoverageTableOffset'] = $Lookup[$i]['Subtable'][$c]['Offset'] + $this->read_ushort(); |
||
1251 | if ($SubstFormat==1) { // Calculated output glyph indices |
||
1252 | $Lookup[$i]['Subtable'][$c]['DeltaGlyphID'] = $this->read_short(); |
||
1253 | } |
||
1254 | else if ($SubstFormat==2) { // Specified output glyph indices |
||
1255 | $GlyphCount = $this->read_ushort(); |
||
1256 | for ($g=0;$g<$GlyphCount;$g++) { |
||
1257 | $Lookup[$i]['Subtable'][$c]['Glyphs'][] = $this->read_ushort(); |
||
1258 | } |
||
1259 | } |
||
1260 | } |
||
1261 | // LookupType 2: Multiple Substitution Subtable |
||
1262 | else if ($Lookup[$i]['Type'] == 2) { |
||
1263 | $Lookup[$i]['Subtable'][$c]['CoverageTableOffset'] = $Lookup[$i]['Subtable'][$c]['Offset'] + $this->read_ushort(); |
||
1264 | $Lookup[$i]['Subtable'][$c]['SequenceCount'] = $SequenceCount = $this->read_short(); |
||
1265 | for($s=0;$s<$SequenceCount;$s++) { |
||
1266 | $Lookup[$i]['Subtable'][$c]['Sequences'][$s]['Offset'] = $Lookup[$i]['Subtable'][$c]['Offset'] + $this->read_short(); |
||
1267 | } |
||
1268 | for($s=0;$s<$SequenceCount;$s++) { |
||
1269 | // Sequence Tables |
||
1270 | $this->seek($Lookup[$i]['Subtable'][$c]['Sequences'][$s]['Offset']); |
||
1271 | $Lookup[$i]['Subtable'][$c]['Sequences'][$s]['GlyphCount'] = $this->read_short(); |
||
1272 | for ($g=0;$g<$Lookup[$i]['Subtable'][$c]['Sequences'][$s]['GlyphCount'];$g++) { |
||
1273 | $Lookup[$i]['Subtable'][$c]['Sequences'][$s]['SubstituteGlyphID'][] = $this->read_ushort(); |
||
1274 | } |
||
1275 | } |
||
1276 | } |
||
1277 | // LookupType 3: Alternate Forms |
||
1278 | else if ($Lookup[$i]['Type'] == 3) { |
||
1279 | $Lookup[$i]['Subtable'][$c]['CoverageTableOffset'] = $Lookup[$i]['Subtable'][$c]['Offset'] + $this->read_ushort(); |
||
1280 | $Lookup[$i]['Subtable'][$c]['AlternateSetCount'] = $AlternateSetCount = $this->read_short(); |
||
1281 | for($s=0;$s<$AlternateSetCount;$s++) { |
||
1282 | $Lookup[$i]['Subtable'][$c]['AlternateSets'][$s]['Offset'] = $Lookup[$i]['Subtable'][$c]['Offset'] + $this->read_short(); |
||
1283 | } |
||
1284 | |||
1285 | for($s=0;$s<$AlternateSetCount;$s++) { |
||
1286 | // AlternateSet Tables |
||
1287 | $this->seek($Lookup[$i]['Subtable'][$c]['AlternateSets'][$s]['Offset']); |
||
1288 | $Lookup[$i]['Subtable'][$c]['AlternateSets'][$s]['GlyphCount'] = $this->read_short(); |
||
1289 | for ($g=0;$g<$Lookup[$i]['Subtable'][$c]['AlternateSets'][$s]['GlyphCount'];$g++) { |
||
1290 | $Lookup[$i]['Subtable'][$c]['AlternateSets'][$s]['SubstituteGlyphID'][] = $this->read_ushort(); |
||
1291 | } |
||
1292 | } |
||
1293 | } |
||
1294 | // LookupType 4: Ligature Substitution Subtable |
||
1295 | else if ($Lookup[$i]['Type'] == 4) { |
||
1296 | $Lookup[$i]['Subtable'][$c]['CoverageTableOffset'] = $Lookup[$i]['Subtable'][$c]['Offset'] + $this->read_ushort(); |
||
1297 | $Lookup[$i]['Subtable'][$c]['LigSetCount'] = $LigSetCount = $this->read_short(); |
||
1298 | for($s=0;$s<$LigSetCount;$s++) { |
||
1299 | $Lookup[$i]['Subtable'][$c]['LigSet'][$s]['Offset'] = $Lookup[$i]['Subtable'][$c]['Offset'] + $this->read_short(); |
||
1300 | } |
||
1301 | for($s=0;$s<$LigSetCount;$s++) { |
||
1302 | // LigatureSet Tables |
||
1303 | $this->seek($Lookup[$i]['Subtable'][$c]['LigSet'][$s]['Offset']); |
||
1304 | $Lookup[$i]['Subtable'][$c]['LigSet'][$s]['LigCount'] = $this->read_short(); |
||
1305 | for ($g=0;$g<$Lookup[$i]['Subtable'][$c]['LigSet'][$s]['LigCount'];$g++) { |
||
1306 | $Lookup[$i]['Subtable'][$c]['LigSet'][$s]['LigatureOffset'][$g] = $Lookup[$i]['Subtable'][$c]['LigSet'][$s]['Offset'] + $this->read_ushort(); |
||
1307 | } |
||
1308 | } |
||
1309 | for($s=0;$s<$LigSetCount;$s++) { |
||
1310 | for ($g=0;$g<$Lookup[$i]['Subtable'][$c]['LigSet'][$s]['LigCount'];$g++) { |
||
1311 | // Ligature tables |
||
1312 | $this->seek($Lookup[$i]['Subtable'][$c]['LigSet'][$s]['LigatureOffset'][$g]); |
||
1313 | $Lookup[$i]['Subtable'][$c]['LigSet'][$s]['Ligature'][$g]['LigGlyph'] = $this->read_ushort(); |
||
1314 | $Lookup[$i]['Subtable'][$c]['LigSet'][$s]['Ligature'][$g]['CompCount'] = $this->read_ushort(); |
||
1315 | for ($l=1;$l<$Lookup[$i]['Subtable'][$c]['LigSet'][$s]['Ligature'][$g]['CompCount'];$l++) { |
||
1316 | $Lookup[$i]['Subtable'][$c]['LigSet'][$s]['Ligature'][$g]['GlyphID'][$l] = $this->read_ushort(); |
||
1317 | } |
||
1318 | } |
||
1319 | } |
||
1320 | } |
||
1321 | |||
1322 | // LookupType 5: Contextual Substitution Subtable |
||
1323 | else if ($Lookup[$i]['Type'] == 5) { |
||
1324 | // Format 1: Context Substitution |
||
1325 | if ($SubstFormat==1) { |
||
1326 | $Lookup[$i]['Subtable'][$c]['CoverageTableOffset'] = $Lookup[$i]['Subtable'][$c]['Offset'] + $this->read_ushort(); |
||
1327 | $Lookup[$i]['Subtable'][$c]['SubRuleSetCount'] = $SubRuleSetCount = $this->read_short(); |
||
1328 | for($s=0;$s<$SubRuleSetCount;$s++) { |
||
1329 | $Lookup[$i]['Subtable'][$c]['SubRuleSet'][$s]['Offset'] = $Lookup[$i]['Subtable'][$c]['Offset'] + $this->read_short(); |
||
1330 | } |
||
1331 | for($s=0;$s<$SubRuleSetCount;$s++) { |
||
1332 | // SubRuleSet Tables |
||
1333 | $this->seek($Lookup[$i]['Subtable'][$c]['SubRuleSet'][$s]['Offset']); |
||
1334 | $Lookup[$i]['Subtable'][$c]['SubRuleSet'][$s]['SubRuleCount'] = $this->read_short(); |
||
1335 | for ($g=0;$g<$Lookup[$i]['Subtable'][$c]['SubRuleSet'][$s]['SubRuleCount'];$g++) { |
||
1336 | $Lookup[$i]['Subtable'][$c]['SubRuleSet'][$s]['SubRuleOffset'][$g] = $Lookup[$i]['Subtable'][$c]['SubRuleSet'][$s]['Offset'] + $this->read_ushort(); |
||
1337 | } |
||
1338 | } |
||
1339 | for($s=0;$s<$SubRuleSetCount;$s++) { |
||
1340 | // SubRule Tables |
||
1341 | for ($g=0;$g<$Lookup[$i]['Subtable'][$c]['SubRuleSet'][$s]['SubRuleCount'];$g++) { |
||
1342 | // Ligature tables |
||
1343 | $this->seek($Lookup[$i]['Subtable'][$c]['SubRuleSet'][$s]['SubRuleOffset'][$g]); |
||
1344 | |||
1345 | $Lookup[$i]['Subtable'][$c]['SubRuleSet'][$s]['SubRule'][$g]['GlyphCount'] = $this->read_ushort(); |
||
1346 | $Lookup[$i]['Subtable'][$c]['SubRuleSet'][$s]['SubRule'][$g]['SubstCount'] = $this->read_ushort(); |
||
1347 | // "Input"::[GlyphCount - 1]::Array of input GlyphIDs-start with second glyph |
||
1348 | for ($l=1;$l<$Lookup[$i]['Subtable'][$c]['SubRuleSet'][$s]['SubRule'][$g]['GlyphCount'];$l++) { |
||
1349 | $Lookup[$i]['Subtable'][$c]['SubRuleSet'][$s]['SubRule'][$g]['Input'][$l] = $this->read_ushort(); |
||
1350 | } |
||
1351 | // "SubstLookupRecord"::[SubstCount]::Array of SubstLookupRecords-in design order |
||
1352 | for ($l=0;$l<$Lookup[$i]['Subtable'][$c]['SubRuleSet'][$s]['SubRule'][$g]['SubstCount'];$l++) { |
||
1353 | $Lookup[$i]['Subtable'][$c]['SubRuleSet'][$s]['SubRule'][$g]['SubstLookupRecord'][$l]['SequenceIndex'] = $this->read_ushort(); |
||
1354 | $Lookup[$i]['Subtable'][$c]['SubRuleSet'][$s]['SubRule'][$g]['SubstLookupRecord'][$l]['LookupListIndex'] = $this->read_ushort(); |
||
1355 | } |
||
1356 | |||
1357 | } |
||
1358 | } |
||
1359 | |||
1360 | } |
||
1361 | // Format 2: Class-based Context Glyph Substitution |
||
1362 | else if ($SubstFormat==2) { |
||
1363 | $Lookup[$i]['Subtable'][$c]['CoverageTableOffset'] = $Lookup[$i]['Subtable'][$c]['Offset'] + $this->read_ushort(); |
||
1364 | $Lookup[$i]['Subtable'][$c]['ClassDefOffset'] = $Lookup[$i]['Subtable'][$c]['Offset'] + $this->read_ushort(); |
||
1365 | $Lookup[$i]['Subtable'][$c]['SubClassSetCnt'] = $this->read_ushort(); |
||
1366 | for ($b=0;$b<$Lookup[$i]['Subtable'][$c]['SubClassSetCnt'];$b++) { |
||
1367 | $offset = $this->read_ushort(); |
||
1368 | if ($offset==0x0000) { |
||
1369 | $Lookup[$i]['Subtable'][$c]['SubClassSetOffset'][] = 0; |
||
1370 | } |
||
1371 | else { |
||
1372 | $Lookup[$i]['Subtable'][$c]['SubClassSetOffset'][] = $Lookup[$i]['Subtable'][$c]['Offset'] + $offset; |
||
1373 | } |
||
1374 | } |
||
1375 | } |
||
1376 | else { die("GPOS Lookup Type ".$Lookup[$i]['Type'].", Format ".$SubstFormat." not supported (ttfontsuni.php)."); } |
||
1377 | } |
||
1378 | |||
1379 | // LookupType 6: Chaining Contextual Substitution Subtable |
||
1380 | else if ($Lookup[$i]['Type'] == 6) { |
||
1381 | // Format 1: Simple Chaining Context Glyph Substitution p255 |
||
1382 | if ($SubstFormat==1) { |
||
1383 | $Lookup[$i]['Subtable'][$c]['CoverageTableOffset'] = $Lookup[$i]['Subtable'][$c]['Offset'] + $this->read_ushort(); |
||
1384 | $Lookup[$i]['Subtable'][$c]['ChainSubRuleSetCount'] = $this->read_ushort(); |
||
1385 | for ($b=0;$b<$Lookup[$i]['Subtable'][$c]['ChainSubRuleSetCount'];$b++) { |
||
1386 | $Lookup[$i]['Subtable'][$c]['ChainSubRuleSetOffset'][] = $Lookup[$i]['Subtable'][$c]['Offset'] + $this->read_ushort(); |
||
1387 | } |
||
1388 | } |
||
1389 | // Format 2: Class-based Chaining Context Glyph Substitution p257 |
||
1390 | else if ($SubstFormat==2) { |
||
1391 | $Lookup[$i]['Subtable'][$c]['CoverageTableOffset'] = $Lookup[$i]['Subtable'][$c]['Offset'] + $this->read_ushort(); |
||
1392 | $Lookup[$i]['Subtable'][$c]['BacktrackClassDefOffset'] = $Lookup[$i]['Subtable'][$c]['Offset'] + $this->read_ushort(); |
||
1393 | $Lookup[$i]['Subtable'][$c]['InputClassDefOffset'] = $Lookup[$i]['Subtable'][$c]['Offset'] + $this->read_ushort(); |
||
1394 | $Lookup[$i]['Subtable'][$c]['LookaheadClassDefOffset'] = $Lookup[$i]['Subtable'][$c]['Offset'] + $this->read_ushort(); |
||
1395 | $Lookup[$i]['Subtable'][$c]['ChainSubClassSetCnt'] = $this->read_ushort(); |
||
1396 | for ($b=0;$b<$Lookup[$i]['Subtable'][$c]['ChainSubClassSetCnt'];$b++) { |
||
1397 | $offset = $this->read_ushort(); |
||
1398 | if ($offset==0x0000) { |
||
1399 | $Lookup[$i]['Subtable'][$c]['ChainSubClassSetOffset'][] = $offset; |
||
1400 | } |
||
1401 | else { |
||
1402 | $Lookup[$i]['Subtable'][$c]['ChainSubClassSetOffset'][] = $Lookup[$i]['Subtable'][$c]['Offset'] + $offset; |
||
1403 | } |
||
1404 | } |
||
1405 | } |
||
1406 | // Format 3: Coverage-based Chaining Context Glyph Substitution p259 |
||
1407 | else if ($SubstFormat==3) { |
||
1408 | $Lookup[$i]['Subtable'][$c]['BacktrackGlyphCount'] = $this->read_ushort(); |
||
1409 | for ($b=0;$b<$Lookup[$i]['Subtable'][$c]['BacktrackGlyphCount'];$b++) { |
||
1410 | $Lookup[$i]['Subtable'][$c]['CoverageBacktrack'][] = $Lookup[$i]['Subtable'][$c]['Offset'] + $this->read_ushort(); |
||
1411 | } |
||
1412 | $Lookup[$i]['Subtable'][$c]['InputGlyphCount'] = $this->read_ushort(); |
||
1413 | for ($b=0;$b<$Lookup[$i]['Subtable'][$c]['InputGlyphCount'];$b++) { |
||
1414 | $Lookup[$i]['Subtable'][$c]['CoverageInput'][] = $Lookup[$i]['Subtable'][$c]['Offset'] + $this->read_ushort(); |
||
1415 | } |
||
1416 | $Lookup[$i]['Subtable'][$c]['LookaheadGlyphCount'] = $this->read_ushort(); |
||
1417 | for ($b=0;$b<$Lookup[$i]['Subtable'][$c]['LookaheadGlyphCount'];$b++) { |
||
1418 | $Lookup[$i]['Subtable'][$c]['CoverageLookahead'][] = $Lookup[$i]['Subtable'][$c]['Offset'] + $this->read_ushort(); |
||
1419 | } |
||
1420 | $Lookup[$i]['Subtable'][$c]['SubstCount'] = $this->read_ushort(); |
||
1421 | for ($b=0;$b<$Lookup[$i]['Subtable'][$c]['SubstCount'];$b++) { |
||
1422 | $Lookup[$i]['Subtable'][$c]['SubstLookupRecord'][$b]['SequenceIndex'] = $this->read_ushort(); |
||
1423 | $Lookup[$i]['Subtable'][$c]['SubstLookupRecord'][$b]['LookupListIndex'] = $this->read_ushort(); |
||
1424 | /* |
||
1425 | Substitution Lookup Record |
||
1426 | All contextual substitution subtables specify the substitution data in a Substitution Lookup Record (SubstLookupRecord). Each record contains a SequenceIndex, which indicates the position where the substitution will occur in the glyph sequence. In addition, a LookupListIndex identifies the lookup to be applied at the glyph position specified by the SequenceIndex. |
||
1427 | */ |
||
1428 | |||
1429 | } |
||
1430 | } |
||
1431 | } |
||
1432 | else { die("Lookup Type ".$Lookup[$i]['Type']." not supported."); } |
||
1433 | } |
||
1434 | } |
||
1435 | //print_r($Lookup); exit; |
||
1436 | |||
1437 | |||
1438 | |||
1439 | |||
1440 | //===================================================================================== |
||
1441 | // Process (2) Whole LookupList |
||
1442 | // Get Coverage tables and prepare preg_replace |
||
1443 | for ($i=0;$i<$LookupCount;$i++) { |
||
1444 | for ($c=0;$c<$Lookup[$i]['SubtableCount'] ;$c++) { |
||
1445 | $SubstFormat= $Lookup[$i]['Subtable'][$c]['Format'] ; |
||
1446 | |||
1447 | // LookupType 1: Single Substitution Subtable 1 => 1 |
||
1448 | if ($Lookup[$i]['Type'] == 1) { |
||
1449 | $this->seek($Lookup[$i]['Subtable'][$c]['CoverageTableOffset']); |
||
1450 | $glyphs = $this->_getCoverage(false); |
||
1451 | View Code Duplication | for ($g=0;$g<count($glyphs);$g++) { |
|
1452 | $replace = array(); |
||
1453 | $substitute = array(); |
||
1454 | $replace[] = unicode_hex($this->glyphToChar[$glyphs[$g]][0]); |
||
1455 | // Flag = Ignore |
||
1456 | if ($this->_checkGSUBignore($Lookup[$i]['Flag'], $replace[0], $Lookup[$i]['MarkFilteringSet'])) { continue; } |
||
1457 | if (isset($Lookup[$i]['Subtable'][$c]['DeltaGlyphID'])) { // Format 1 |
||
1458 | $substitute[] = unicode_hex($this->glyphToChar[($glyphs[$g]+$Lookup[$i]['Subtable'][$c]['DeltaGlyphID'])][0]); |
||
1459 | } |
||
1460 | else { // Format 2 |
||
1461 | $substitute[] = unicode_hex($this->glyphToChar[($Lookup[$i]['Subtable'][$c]['Glyphs'][$g])][0]); |
||
1462 | } |
||
1463 | $Lookup[$i]['Subtable'][$c]['subs'][] = array('Replace'=>$replace, 'substitute'=>$substitute); |
||
1464 | } |
||
1465 | } |
||
1466 | |||
1467 | // LookupType 2: Multiple Substitution Subtable 1 => n |
||
1468 | else if ($Lookup[$i]['Type'] == 2) { |
||
1469 | $this->seek($Lookup[$i]['Subtable'][$c]['CoverageTableOffset']); |
||
1470 | $glyphs = $this->_getCoverage(); |
||
1471 | View Code Duplication | for ($g=0;$g<count($glyphs);$g++) { |
|
1472 | $replace = array(); |
||
1473 | $substitute = array(); |
||
1474 | $replace[] = $glyphs[$g]; |
||
1475 | // Flag = Ignore |
||
1476 | if ($this->_checkGSUBignore($Lookup[$i]['Flag'], $replace[0], $Lookup[$i]['MarkFilteringSet'])) { continue; } |
||
1477 | if (!isset($Lookup[$i]['Subtable'][$c]['Sequences'][$g]['SubstituteGlyphID']) || count($Lookup[$i]['Subtable'][$c]['Sequences'][$g]['SubstituteGlyphID'])==0) { continue; } // Illegal for GlyphCount to be 0; either error in font, or something has gone wrong - lets carry on for now! |
||
1478 | foreach($Lookup[$i]['Subtable'][$c]['Sequences'][$g]['SubstituteGlyphID'] AS $sub) { |
||
1479 | $substitute[] = unicode_hex($this->glyphToChar[$sub][0]); |
||
1480 | } |
||
1481 | $Lookup[$i]['Subtable'][$c]['subs'][] = array('Replace'=>$replace, 'substitute'=>$substitute); |
||
1482 | } |
||
1483 | } |
||
1484 | // LookupType 3: Alternate Forms 1 => 1 (only first alternate form is used) |
||
1485 | else if ($Lookup[$i]['Type'] == 3) { |
||
1486 | $this->seek($Lookup[$i]['Subtable'][$c]['CoverageTableOffset']); |
||
1487 | $glyphs = $this->_getCoverage(); |
||
1488 | for ($g=0;$g<count($glyphs);$g++) { |
||
1489 | $replace = array(); |
||
1490 | $substitute = array(); |
||
1491 | $replace[] = $glyphs[$g]; |
||
1492 | // Flag = Ignore |
||
1493 | if ($this->_checkGSUBignore($Lookup[$i]['Flag'], $replace[0], $Lookup[$i]['MarkFilteringSet'])) { continue; } |
||
1494 | |||
1495 | for ($gl=0;$gl<$Lookup[$i]['Subtable'][$c]['AlternateSets'][$g]['GlyphCount'];$gl++) { |
||
1496 | $gid = $Lookup[$i]['Subtable'][$c]['AlternateSets'][$g]['SubstituteGlyphID'][$gl]; |
||
1497 | $substitute[] = unicode_hex($this->glyphToChar[$gid][0]); |
||
1498 | } |
||
1499 | |||
1500 | //$gid = $Lookup[$i]['Subtable'][$c]['AlternateSets'][$g]['SubstituteGlyphID'][0]; |
||
1501 | //$substitute[] = unicode_hex($this->glyphToChar[$gid][0]); |
||
1502 | |||
1503 | $Lookup[$i]['Subtable'][$c]['subs'][] = array('Replace'=>$replace, 'substitute'=>$substitute); |
||
1504 | } |
||
1505 | if ($i==166) { |
||
1506 | print_r($Lookup[$i]['Subtable']); |
||
1507 | exit; |
||
1508 | } |
||
1509 | } |
||
1510 | // LookupType 4: Ligature Substitution Subtable n => 1 |
||
1511 | else if ($Lookup[$i]['Type'] == 4) { |
||
1512 | $this->seek($Lookup[$i]['Subtable'][$c]['CoverageTableOffset']); |
||
1513 | $glyphs = $this->_getCoverage(); |
||
1514 | $LigSetCount = $Lookup[$i]['Subtable'][$c]['LigSetCount']; |
||
1515 | for($s=0;$s<$LigSetCount;$s++) { |
||
1516 | for ($g=0;$g<$Lookup[$i]['Subtable'][$c]['LigSet'][$s]['LigCount'];$g++) { |
||
1517 | $replace = array(); |
||
1518 | $substitute = array(); |
||
1519 | $replace[] = $glyphs[$s]; |
||
1520 | // Flag = Ignore |
||
1521 | if ($this->_checkGSUBignore($Lookup[$i]['Flag'], $replace[0], $Lookup[$i]['MarkFilteringSet'])) { continue; } |
||
1522 | View Code Duplication | for ($l=1;$l<$Lookup[$i]['Subtable'][$c]['LigSet'][$s]['Ligature'][$g]['CompCount'];$l++) { |
|
1523 | $gid = $Lookup[$i]['Subtable'][$c]['LigSet'][$s]['Ligature'][$g]['GlyphID'][$l]; |
||
1524 | $rpl = unicode_hex($this->glyphToChar[$gid][0]); |
||
1525 | // Flag = Ignore |
||
1526 | if ($this->_checkGSUBignore($Lookup[$i]['Flag'], $rpl, $Lookup[$i]['MarkFilteringSet'])) { continue 2; } |
||
1527 | $replace[] = $rpl; |
||
1528 | } |
||
1529 | $gid = $Lookup[$i]['Subtable'][$c]['LigSet'][$s]['Ligature'][$g]['LigGlyph']; |
||
1530 | $substitute[] = unicode_hex($this->glyphToChar[$gid][0]); |
||
1531 | $Lookup[$i]['Subtable'][$c]['subs'][] = array('Replace'=>$replace, 'substitute'=>$substitute, 'CompCount' => $Lookup[$i]['Subtable'][$c]['LigSet'][$s]['Ligature'][$g]['CompCount']); |
||
1532 | } |
||
1533 | } |
||
1534 | } |
||
1535 | |||
1536 | // LookupType 5: Contextual Substitution Subtable |
||
1537 | else if ($Lookup[$i]['Type'] == 5) { |
||
1538 | // Format 1: Context Substitution |
||
1539 | View Code Duplication | if ($SubstFormat==1) { |
|
1540 | $this->seek($Lookup[$i]['Subtable'][$c]['CoverageTableOffset']); |
||
1541 | $Lookup[$i]['Subtable'][$c]['CoverageGlyphs'] = $CoverageGlyphs = $this->_getCoverage(); |
||
1542 | |||
1543 | for ($s=0;$s<$Lookup[$i]['Subtable'][$c]['SubRuleSetCount'];$s++) { |
||
1544 | $SubRuleSet = $Lookup[$i]['Subtable'][$c]['SubRuleSet'][$s]; |
||
1545 | $Lookup[$i]['Subtable'][$c]['SubRuleSet'][$s]['FirstGlyph'] = $CoverageGlyphs[$s]; |
||
1546 | for ($r=0;$r<$Lookup[$i]['Subtable'][$c]['SubRuleSet'][$s]['SubRuleCount'];$r++) { |
||
1547 | $GlyphCount = $Lookup[$i]['Subtable'][$c]['SubRuleSet'][$s]['SubRule'][$r]['GlyphCount']; |
||
1548 | for ($g=1;$g<$GlyphCount;$g++) { |
||
1549 | $glyphID = $Lookup[$i]['Subtable'][$c]['SubRuleSet'][$s]['SubRule'][$r]['Input'][$g]; |
||
1550 | $Lookup[$i]['Subtable'][$c]['SubRuleSet'][$s]['SubRule'][$r]['InputGlyphs'][$g] = unicode_hex($this->glyphToChar[$glyphID][0]); |
||
1551 | } |
||
1552 | |||
1553 | } |
||
1554 | } |
||
1555 | } |
||
1556 | // Format 2: Class-based Context Glyph Substitution |
||
1557 | else if ($SubstFormat==2) { |
||
1558 | $this->seek($Lookup[$i]['Subtable'][$c]['CoverageTableOffset']); |
||
1559 | $Lookup[$i]['Subtable'][$c]['CoverageGlyphs'] = $CoverageGlyphs = $this->_getCoverage(); |
||
1560 | |||
1561 | $InputClasses = $this->_getClasses($Lookup[$i]['Subtable'][$c]['ClassDefOffset']); |
||
1562 | $Lookup[$i]['Subtable'][$c]['InputClasses'] = $InputClasses; |
||
1563 | |||
1564 | for ($s=0;$s<$Lookup[$i]['Subtable'][$c]['SubClassSetCnt'];$s++) { |
||
1565 | if ($Lookup[$i]['Subtable'][$c]['SubClassSetOffset'][$s]>0) { |
||
1566 | $this->seek($Lookup[$i]['Subtable'][$c]['SubClassSetOffset'][$s]); |
||
1567 | $Lookup[$i]['Subtable'][$c]['SubClassSet'][$s]['SubClassRuleCnt'] = $SubClassRuleCnt = $this->read_ushort(); |
||
1568 | $SubClassRule = array(); |
||
1569 | for($b=0;$b<$SubClassRuleCnt;$b++) { |
||
1570 | $SubClassRule[$b] = $Lookup[$i]['Subtable'][$c]['SubClassSetOffset'][$s]+$this->read_ushort(); |
||
1571 | $Lookup[$i]['Subtable'][$c]['SubClassSet'][$s]['SubClassRule'][$b] = $SubClassRule[$b]; |
||
1572 | } |
||
1573 | } |
||
1574 | } |
||
1575 | |||
1576 | for ($s=0;$s<$Lookup[$i]['Subtable'][$c]['SubClassSetCnt'];$s++) { |
||
1577 | $SubClassRuleCnt = $Lookup[$i]['Subtable'][$c]['SubClassSet'][$s]['SubClassRuleCnt']; |
||
1578 | for($b=0;$b<$SubClassRuleCnt;$b++) { |
||
1579 | if ($Lookup[$i]['Subtable'][$c]['SubClassSetOffset'][$s]>0) { |
||
1580 | $this->seek($Lookup[$i]['Subtable'][$c]['SubClassSet'][$s]['SubClassRule'][$b]); |
||
1581 | $Rule = array(); |
||
1582 | $Rule['InputGlyphCount'] = $this->read_ushort(); |
||
1583 | $Rule['SubstCount'] = $this->read_ushort(); |
||
1584 | for ($r=1;$r<$Rule['InputGlyphCount'];$r++) { |
||
1585 | $Rule['Input'][$r] = $this->read_ushort(); |
||
1586 | } |
||
1587 | for ($r=0;$r<$Rule['SubstCount'];$r++) { |
||
1588 | $Rule['SequenceIndex'][$r] = $this->read_ushort(); |
||
1589 | $Rule['LookupListIndex'][$r] = $this->read_ushort(); |
||
1590 | } |
||
1591 | |||
1592 | $Lookup[$i]['Subtable'][$c]['SubClassSet'][$s]['SubClassRule'][$b] = $Rule; |
||
1593 | } |
||
1594 | } |
||
1595 | } |
||
1596 | } |
||
1597 | // Format 3: Coverage-based Context Glyph Substitution |
||
1598 | else if ($SubstFormat==3) { |
||
1599 | for ($b=0;$b<$Lookup[$i]['Subtable'][$c]['InputGlyphCount'];$b++) { |
||
1600 | $this->seek($Lookup[$i]['Subtable'][$c]['CoverageInput'][$b]); |
||
1601 | $glyphs = $this->_getCoverage(); |
||
1602 | $Lookup[$i]['Subtable'][$c]['CoverageInputGlyphs'][] = implode("|",$glyphs); |
||
1603 | } |
||
1604 | die("Lookup Type 5, SubstFormat 3 not tested. Please report this with the name of font used - ".$this->fontkey); |
||
1605 | } |
||
1606 | |||
1607 | } |
||
1608 | |||
1609 | // LookupType 6: Chaining Contextual Substitution Subtable |
||
1610 | else if ($Lookup[$i]['Type'] == 6) { |
||
1611 | // Format 1: Simple Chaining Context Glyph Substitution p255 |
||
1612 | if ($SubstFormat==1) { |
||
1613 | $this->seek($Lookup[$i]['Subtable'][$c]['CoverageTableOffset']); |
||
1614 | $Lookup[$i]['Subtable'][$c]['CoverageGlyphs'] = $CoverageGlyphs = $this->_getCoverage(); |
||
1615 | |||
1616 | $ChainSubRuleSetCnt = $Lookup[$i]['Subtable'][$c]['ChainSubRuleSetCount']; |
||
1617 | |||
1618 | View Code Duplication | for ($s=0;$s<$ChainSubRuleSetCnt;$s++) { |
|
1619 | $this->seek($Lookup[$i]['Subtable'][$c]['ChainSubRuleSetOffset'][$s]); |
||
1620 | $ChainSubRuleCnt = $Lookup[$i]['Subtable'][$c]['ChainSubRuleSet'][$s]['ChainSubRuleCount'] = $this->read_ushort(); |
||
1621 | for ($r=0;$r<$ChainSubRuleCnt;$r++) { |
||
1622 | $Lookup[$i]['Subtable'][$c]['ChainSubRuleSet'][$s]['ChainSubRuleOffset'][$r] = $Lookup[$i]['Subtable'][$c]['ChainSubRuleSetOffset'][$s] + $this->read_ushort(); |
||
1623 | |||
1624 | } |
||
1625 | } |
||
1626 | View Code Duplication | for ($s=0;$s<$ChainSubRuleSetCnt;$s++) { |
|
1627 | $ChainSubRuleCnt = $Lookup[$i]['Subtable'][$c]['ChainSubRuleSet'][$s]['ChainSubRuleCount']; |
||
1628 | for ($r=0;$r<$ChainSubRuleCnt;$r++) { |
||
1629 | // ChainSubRule |
||
1630 | $this->seek($Lookup[$i]['Subtable'][$c]['ChainSubRuleSet'][$s]['ChainSubRuleOffset'][$r]); |
||
1631 | |||
1632 | $BacktrackGlyphCount = $Lookup[$i]['Subtable'][$c]['ChainSubRuleSet'][$s]['ChainSubRule'][$r]['BacktrackGlyphCount'] = $this->read_ushort(); |
||
1633 | for ($g=0;$g<$BacktrackGlyphCount;$g++) { |
||
1634 | $glyphID = $this->read_ushort(); |
||
1635 | $Lookup[$i]['Subtable'][$c]['ChainSubRuleSet'][$s]['ChainSubRule'][$r]['BacktrackGlyphs'][$g] = unicode_hex($this->glyphToChar[$glyphID][0]); |
||
1636 | } |
||
1637 | |||
1638 | $InputGlyphCount = $Lookup[$i]['Subtable'][$c]['ChainSubRuleSet'][$s]['ChainSubRule'][$r]['InputGlyphCount'] = $this->read_ushort(); |
||
1639 | for ($g=1;$g<$InputGlyphCount;$g++) { |
||
1640 | $glyphID = $this->read_ushort(); |
||
1641 | $Lookup[$i]['Subtable'][$c]['ChainSubRuleSet'][$s]['ChainSubRule'][$r]['InputGlyphs'][$g] = unicode_hex($this->glyphToChar[$glyphID][0]); |
||
1642 | } |
||
1643 | |||
1644 | |||
1645 | $LookaheadGlyphCount = $Lookup[$i]['Subtable'][$c]['ChainSubRuleSet'][$s]['ChainSubRule'][$r]['LookaheadGlyphCount'] = $this->read_ushort(); |
||
1646 | for ($g=0;$g<$LookaheadGlyphCount;$g++) { |
||
1647 | $glyphID = $this->read_ushort(); |
||
1648 | $Lookup[$i]['Subtable'][$c]['ChainSubRuleSet'][$s]['ChainSubRule'][$r]['LookaheadGlyphs'][$g] = unicode_hex($this->glyphToChar[$glyphID][0]); |
||
1649 | } |
||
1650 | |||
1651 | $SubstCount = $Lookup[$i]['Subtable'][$c]['ChainSubRuleSet'][$s]['ChainSubRule'][$r]['SubstCount'] = $this->read_ushort(); |
||
1652 | for ($lu=0;$lu<$SubstCount;$lu++) { |
||
1653 | $Lookup[$i]['Subtable'][$c]['ChainSubRuleSet'][$s]['ChainSubRule'][$r]['SequenceIndex'][$lu] = $this->read_ushort(); |
||
1654 | $Lookup[$i]['Subtable'][$c]['ChainSubRuleSet'][$s]['ChainSubRule'][$r]['LookupListIndex'][$lu] = $this->read_ushort(); |
||
1655 | } |
||
1656 | } |
||
1657 | } |
||
1658 | |||
1659 | } |
||
1660 | // Format 2: Class-based Chaining Context Glyph Substitution p257 |
||
1661 | else if ($SubstFormat==2) { |
||
1662 | $this->seek($Lookup[$i]['Subtable'][$c]['CoverageTableOffset']); |
||
1663 | $Lookup[$i]['Subtable'][$c]['CoverageGlyphs'] = $CoverageGlyphs = $this->_getCoverage(); |
||
1664 | |||
1665 | $BacktrackClasses = $this->_getClasses($Lookup[$i]['Subtable'][$c]['BacktrackClassDefOffset']); |
||
1666 | $Lookup[$i]['Subtable'][$c]['BacktrackClasses'] = $BacktrackClasses; |
||
1667 | |||
1668 | $InputClasses = $this->_getClasses($Lookup[$i]['Subtable'][$c]['InputClassDefOffset']); |
||
1669 | $Lookup[$i]['Subtable'][$c]['InputClasses'] = $InputClasses; |
||
1670 | |||
1671 | $LookaheadClasses = $this->_getClasses($Lookup[$i]['Subtable'][$c]['LookaheadClassDefOffset']); |
||
1672 | $Lookup[$i]['Subtable'][$c]['LookaheadClasses'] = $LookaheadClasses; |
||
1673 | |||
1674 | View Code Duplication | for ($s=0;$s<$Lookup[$i]['Subtable'][$c]['ChainSubClassSetCnt'];$s++) { |
|
1675 | if ($Lookup[$i]['Subtable'][$c]['ChainSubClassSetOffset'][$s]>0) { |
||
1676 | $this->seek($Lookup[$i]['Subtable'][$c]['ChainSubClassSetOffset'][$s]); |
||
1677 | $Lookup[$i]['Subtable'][$c]['ChainSubClassSet'][$s]['ChainSubClassRuleCnt'] = $ChainSubClassRuleCnt = $this->read_ushort(); |
||
1678 | $ChainSubClassRule = array(); |
||
1679 | for($b=0;$b<$ChainSubClassRuleCnt;$b++) { |
||
1680 | $ChainSubClassRule[$b] = $Lookup[$i]['Subtable'][$c]['ChainSubClassSetOffset'][$s]+$this->read_ushort(); |
||
1681 | $Lookup[$i]['Subtable'][$c]['ChainSubClassSet'][$s]['ChainSubClassRule'][$b] = $ChainSubClassRule[$b]; |
||
1682 | } |
||
1683 | } |
||
1684 | } |
||
1685 | |||
1686 | for ($s=0;$s<$Lookup[$i]['Subtable'][$c]['ChainSubClassSetCnt'];$s++) { |
||
1687 | $ChainSubClassRuleCnt = $Lookup[$i]['Subtable'][$c]['ChainSubClassSet'][$s]['ChainSubClassRuleCnt']; |
||
1688 | View Code Duplication | for($b=0;$b<$ChainSubClassRuleCnt;$b++) { |
|
1689 | if ($Lookup[$i]['Subtable'][$c]['ChainSubClassSetOffset'][$s]>0) { |
||
1690 | $this->seek($Lookup[$i]['Subtable'][$c]['ChainSubClassSet'][$s]['ChainSubClassRule'][$b]); |
||
1691 | $Rule = array(); |
||
1692 | $Rule['BacktrackGlyphCount'] = $this->read_ushort(); |
||
1693 | for ($r=0;$r<$Rule['BacktrackGlyphCount'];$r++) { |
||
1694 | $Rule['Backtrack'][$r] = $this->read_ushort(); |
||
1695 | } |
||
1696 | $Rule['InputGlyphCount'] = $this->read_ushort(); |
||
1697 | for ($r=1;$r<$Rule['InputGlyphCount'];$r++) { |
||
1698 | $Rule['Input'][$r] = $this->read_ushort(); |
||
1699 | } |
||
1700 | $Rule['LookaheadGlyphCount'] = $this->read_ushort(); |
||
1701 | for ($r=0;$r<$Rule['LookaheadGlyphCount'];$r++) { |
||
1702 | $Rule['Lookahead'][$r] = $this->read_ushort(); |
||
1703 | } |
||
1704 | $Rule['SubstCount'] = $this->read_ushort(); |
||
1705 | for ($r=0;$r<$Rule['SubstCount'];$r++) { |
||
1706 | $Rule['SequenceIndex'][$r] = $this->read_ushort(); |
||
1707 | $Rule['LookupListIndex'][$r] = $this->read_ushort(); |
||
1708 | } |
||
1709 | |||
1710 | $Lookup[$i]['Subtable'][$c]['ChainSubClassSet'][$s]['ChainSubClassRule'][$b] = $Rule; |
||
1711 | } |
||
1712 | } |
||
1713 | } |
||
1714 | } |
||
1715 | // Format 3: Coverage-based Chaining Context Glyph Substitution p259 |
||
1716 | View Code Duplication | else if ($SubstFormat==3) { |
|
1717 | for ($b=0;$b<$Lookup[$i]['Subtable'][$c]['BacktrackGlyphCount'];$b++) { |
||
1718 | $this->seek($Lookup[$i]['Subtable'][$c]['CoverageBacktrack'][$b]); |
||
1719 | $glyphs = $this->_getCoverage(); |
||
1720 | $Lookup[$i]['Subtable'][$c]['CoverageBacktrackGlyphs'][] = implode("|",$glyphs); |
||
1721 | } |
||
1722 | for ($b=0;$b<$Lookup[$i]['Subtable'][$c]['InputGlyphCount'];$b++) { |
||
1723 | $this->seek($Lookup[$i]['Subtable'][$c]['CoverageInput'][$b]); |
||
1724 | $glyphs = $this->_getCoverage(); |
||
1725 | $Lookup[$i]['Subtable'][$c]['CoverageInputGlyphs'][] = implode("|",$glyphs); |
||
1726 | // Don't use above value as these are ordered numerically not as need to process |
||
1727 | } |
||
1728 | for ($b=0;$b<$Lookup[$i]['Subtable'][$c]['LookaheadGlyphCount'];$b++) { |
||
1729 | $this->seek($Lookup[$i]['Subtable'][$c]['CoverageLookahead'][$b]); |
||
1730 | $glyphs = $this->_getCoverage(); |
||
1731 | $Lookup[$i]['Subtable'][$c]['CoverageLookaheadGlyphs'][] = implode("|",$glyphs); |
||
1732 | } |
||
1733 | |||
1734 | } |
||
1735 | } |
||
1736 | } |
||
1737 | } |
||
1738 | |||
1739 | |||
1740 | //===================================================================================== |
||
1741 | //===================================================================================== |
||
1742 | //===================================================================================== |
||
1743 | |||
1744 | |||
1745 | |||
1746 | $st = $this->mpdf->OTLscript; |
||
1747 | $t = $this->mpdf->OTLlang; |
||
1748 | $langsys = $gsub[$st][$t]; |
||
1749 | |||
1750 | |||
1751 | $lul = array(); // array of LookupListIndexes |
||
1752 | $tags = array(); // corresponding array of feature tags e.g. 'ccmp' |
||
1753 | foreach($langsys AS $tag=>$ft) { |
||
1754 | foreach($ft AS $ll) { |
||
1755 | $lul[$ll] = $tag; |
||
1756 | } |
||
1757 | } |
||
1758 | ksort($lul); // Order the Lookups in the order they are in the GUSB table, regardless of Feature order |
||
1759 | $this->_getGSUBarray($Lookup, $lul, $st); |
||
1760 | //print_r($lul); exit; |
||
1761 | |||
1762 | |||
1763 | |||
1764 | |||
1765 | } |
||
1766 | //print_r($Lookup); exit; |
||
1767 | |||
1768 | return array($GSUBScriptLang, $gsub, $GSLookup, $rtlPUAstr, $rtlPUAarr); |
||
1769 | |||
1770 | } |
||
1771 | ///////////////////////////////////////////////////////////////////////////////////////// |
||
1772 | // GSUB functions |
||
1773 | function _getGSUBarray(&$Lookup, &$lul, $scripttag, $level=1, $coverage='', $exB='', $exL='') { |
||
1774 | // Process (3) LookupList for specific Script-LangSys |
||
1775 | // Generate preg_replace |
||
1776 | $html = ''; |
||
1777 | if ($level==1) { $html .= '<bookmark level="0" content="GSUB features">'; } |
||
1778 | foreach($lul AS $i=>$tag) { |
||
1779 | $html .= '<div class="level'.$level.'">'; |
||
1780 | $html .= '<h5 class="level'.$level.'">'; |
||
1781 | if ($level==1) { $html .= '<bookmark level="1" content="'.$tag.' [#'.$i.']">'; } |
||
1782 | $html .= 'Lookup #'.$i.' [tag: <span style="color:#000066;">'.$tag.'</span>]</h5>'; |
||
1783 | $ignore = $this->_getGSUBignoreString($Lookup[$i]['Flag'], $Lookup[$i]['MarkFilteringSet']); |
||
1784 | if ($ignore) { $html .= '<div class="ignore">Ignoring: '.$ignore.'</div> '; } |
||
1785 | |||
1786 | $Type = $Lookup[$i]['Type']; |
||
1787 | $Flag = $Lookup[$i]['Flag']; |
||
1788 | if (($Flag & 0x0001) == 1) { $dir = 'RTL'; } |
||
1789 | else { $dir = 'LTR'; } |
||
1790 | |||
1791 | for ($c=0;$c<$Lookup[$i]['SubtableCount'] ;$c++) { |
||
1792 | $html .= '<div class="subtable">Subtable #'.$c; |
||
1793 | if ($level==1) { $html .= '<bookmark level="2" content="Subtable #'.$c.'">'; } |
||
1794 | $html .= '</div>'; |
||
1795 | |||
1796 | $SubstFormat= $Lookup[$i]['Subtable'][$c]['Format'] ; |
||
1797 | |||
1798 | // LookupType 1: Single Substitution Subtable |
||
1799 | if ($Lookup[$i]['Type'] == 1) { |
||
1800 | $html .= '<div class="lookuptype">LookupType 1: Single Substitution Subtable</div>'; |
||
1801 | View Code Duplication | for ($s=0;$s<count($Lookup[$i]['Subtable'][$c]['subs']);$s++) { |
|
1802 | $inputGlyphs = $Lookup[$i]['Subtable'][$c]['subs'][$s]['Replace']; |
||
1803 | $substitute = $Lookup[$i]['Subtable'][$c]['subs'][$s]['substitute'][0]; |
||
1804 | if ($level==2 && strpos($coverage, $inputGlyphs[0])===false) { continue; } |
||
1805 | $html .= '<div class="substitution">'; |
||
1806 | $html .= '<span class="unicode">'.$this->formatUni($inputGlyphs[0]).' </span> '; |
||
1807 | if ($level==2 && $exB) { $html .= $exB; } |
||
1808 | $html .= '<span class="unchanged"> '.$this->formatEntity($inputGlyphs[0]).'</span>'; |
||
1809 | if ($level==2 && $exL) { $html .= $exL; } |
||
1810 | $html .= ' » » '; |
||
1811 | if ($level==2 && $exB) { $html .= $exB; } |
||
1812 | $html .= '<span class="changed"> '.$this->formatEntity($substitute).'</span>'; |
||
1813 | if ($level==2 && $exL) { $html .= $exL; } |
||
1814 | $html .= ' <span class="unicode">'.$this->formatUni($substitute).'</span> '; |
||
1815 | $html .= '</div>'; |
||
1816 | } |
||
1817 | } |
||
1818 | // LookupType 2: Multiple Substitution Subtable |
||
1819 | else if ($Lookup[$i]['Type'] == 2) { |
||
1820 | $html .= '<div class="lookuptype">LookupType 2: Multiple Substitution Subtable</div>'; |
||
1821 | View Code Duplication | for ($s=0;$s<count($Lookup[$i]['Subtable'][$c]['subs']);$s++) { |
|
1822 | $inputGlyphs = $Lookup[$i]['Subtable'][$c]['subs'][$s]['Replace']; |
||
1823 | $substitute = $Lookup[$i]['Subtable'][$c]['subs'][$s]['substitute']; |
||
1824 | if ($level==2 && strpos($coverage, $inputGlyphs[0])===false) { continue; } |
||
1825 | $html .= '<div class="substitution">'; |
||
1826 | $html .= '<span class="unicode">'.$this->formatUni($inputGlyphs[0]).' </span> '; |
||
1827 | if ($level==2 && $exB) { $html .= $exB; } |
||
1828 | $html .= '<span class="unchanged"> '.$this->formatEntity($inputGlyphs[0]).'</span>'; |
||
1829 | if ($level==2 && $exL) { $html .= $exL; } |
||
1830 | $html .= ' » » '; |
||
1831 | if ($level==2 && $exB) { $html .= $exB; } |
||
1832 | $html .= '<span class="changed"> '.$this->formatEntityArr($substitute).'</span>'; |
||
1833 | if ($level==2 && $exL) { $html .= $exL; } |
||
1834 | $html .= ' <span class="unicode">'.$this->formatUniArr($substitute).'</span> '; |
||
1835 | $html .= '</div>'; |
||
1836 | } |
||
1837 | } |
||
1838 | // LookupType 3: Alternate Forms |
||
1839 | else if ($Lookup[$i]['Type'] == 3) { |
||
1840 | $html .= '<div class="lookuptype">LookupType 3: Alternate Forms</div>'; |
||
1841 | for ($s=0;$s<count($Lookup[$i]['Subtable'][$c]['subs']);$s++) { |
||
1842 | $inputGlyphs = $Lookup[$i]['Subtable'][$c]['subs'][$s]['Replace']; |
||
1843 | $substitute = $Lookup[$i]['Subtable'][$c]['subs'][$s]['substitute'][0]; |
||
1844 | if ($level==2 && strpos($coverage, $inputGlyphs[0])===false) { continue; } |
||
1845 | $html .= '<div class="substitution">'; |
||
1846 | $html .= '<span class="unicode">'.$this->formatUni($inputGlyphs[0]).' </span> '; |
||
1847 | if ($level==2 && $exB) { $html .= $exB; } |
||
1848 | $html .= '<span class="unchanged"> '.$this->formatEntity($inputGlyphs[0]).'</span>'; |
||
1849 | if ($level==2 && $exL) { $html .= $exL; } |
||
1850 | $html .= ' » » '; |
||
1851 | if ($level==2 && $exB) { $html .= $exB; } |
||
1852 | $html .= '<span class="changed"> '.$this->formatEntity($substitute).'</span>'; |
||
1853 | if ($level==2 && $exL) { $html .= $exL; } |
||
1854 | $html .= ' <span class="unicode">'.$this->formatUni($substitute).'</span> '; |
||
1855 | if (count($Lookup[$i]['Subtable'][$c]['subs'][$s]['substitute'])>1) { |
||
1856 | for ($alt=1;$alt<count($Lookup[$i]['Subtable'][$c]['subs'][$s]['substitute']);$alt++) { |
||
1857 | $substitute = $Lookup[$i]['Subtable'][$c]['subs'][$s]['substitute'][$alt]; |
||
1858 | $html .= ' | ALT #'.$alt.' '; |
||
1859 | $html .= '<span class="changed"> '.$this->formatEntity($substitute).'</span>'; |
||
1860 | $html .= ' <span class="unicode">'.$this->formatUni($substitute).'</span> '; |
||
1861 | } |
||
1862 | } |
||
1863 | $html .= '</div>'; |
||
1864 | } |
||
1865 | } |
||
1866 | // LookupType 4: Ligature Substitution Subtable |
||
1867 | else if ($Lookup[$i]['Type'] == 4) { |
||
1868 | $html .= '<div class="lookuptype">LookupType 4: Ligature Substitution Subtable</div>'; |
||
1869 | View Code Duplication | for ($s=0;$s<count($Lookup[$i]['Subtable'][$c]['subs']);$s++) { |
|
1870 | $inputGlyphs = $Lookup[$i]['Subtable'][$c]['subs'][$s]['Replace']; |
||
1871 | $substitute = $Lookup[$i]['Subtable'][$c]['subs'][$s]['substitute'][0]; |
||
1872 | if ($level==2 && strpos($coverage, $inputGlyphs[0])===false) { continue; } |
||
1873 | $html .= '<div class="substitution">'; |
||
1874 | $html .= '<span class="unicode">'.$this->formatUniArr($inputGlyphs).' </span> '; |
||
1875 | if ($level==2 && $exB) { $html .= $exB; } |
||
1876 | $html .= '<span class="unchanged"> '.$this->formatEntityArr($inputGlyphs).'</span>'; |
||
1877 | if ($level==2 && $exL) { $html .= $exL; } |
||
1878 | $html .= ' » » '; |
||
1879 | if ($level==2 && $exB) { $html .= $exB; } |
||
1880 | $html .= '<span class="changed"> '.$this->formatEntity($substitute).'</span>'; |
||
1881 | if ($level==2 && $exL) { $html .= $exL; } |
||
1882 | $html .= ' <span class="unicode">'.$this->formatUni($substitute).'</span> '; |
||
1883 | $html .= '</div>'; |
||
1884 | } |
||
1885 | } |
||
1886 | |||
1887 | // LookupType 5: Contextual Substitution Subtable |
||
1888 | else if ($Lookup[$i]['Type'] == 5) { |
||
1889 | $html .= '<div class="lookuptype">LookupType 5: Contextual Substitution Subtable</div>'; |
||
1890 | // Format 1: Context Substitution |
||
1891 | if ($SubstFormat==1) { |
||
1892 | $html .= '<div class="lookuptypesub">Format 1: Context Substitution</div>'; |
||
1893 | for($s=0;$s<$Lookup[$i]['Subtable'][$c]['SubRuleSetCount'];$s++) { |
||
1894 | // SubRuleSet |
||
1895 | $subRule = array(); |
||
1896 | $html .= '<div class="rule">Subrule Set: '.$s.'</div>'; |
||
1897 | foreach($Lookup[$i]['Subtable'][$c]['SubRuleSet'][$s]['SubRule'] AS $rctr=>$rule) { |
||
1898 | // SubRule |
||
1899 | $html .= '<div class="rule">SubRule: '.$rctr.'</div>'; |
||
1900 | $inputGlyphs = array(); |
||
1901 | if ($rule['GlyphCount']>1) { |
||
1902 | $inputGlyphs = $rule['InputGlyphs']; |
||
1903 | } |
||
1904 | $inputGlyphs[0] = $Lookup[$i]['Subtable'][$c]['SubRuleSet'][$s]['FirstGlyph']; |
||
1905 | ksort($inputGlyphs); |
||
1906 | $nInput = count($inputGlyphs); |
||
1907 | |||
1908 | |||
1909 | $exampleI = array(); |
||
1910 | $html .= '<div class="context">CONTEXT: '; |
||
1911 | View Code Duplication | for ($ff=0;$ff<count($inputGlyphs);$ff++) { |
|
1912 | $html .= '<div>Input #'.$ff.': <span class="unchanged"> '.$this->formatEntityStr($inputGlyphs[$ff]).' </span></div>'; |
||
1913 | $exampleI[] = $this->formatEntityFirst($inputGlyphs[$ff]); |
||
1914 | } |
||
1915 | $html .= '</div>'; |
||
1916 | |||
1917 | |||
1918 | for ($b=0;$b<$rule['SubstCount'];$b++) { |
||
1919 | $lup = $rule['SubstLookupRecord'][$b]['LookupListIndex']; |
||
1920 | $seqIndex = $rule['SubstLookupRecord'][$b]['SequenceIndex']; |
||
1921 | |||
1922 | // GENERATE exampleI[<seqIndex] .... exampleI[>seqIndex] |
||
1923 | $exB = ''; |
||
1924 | $exL = ''; |
||
1925 | View Code Duplication | if ($seqIndex>0) { |
|
1926 | $exB .= '<span class="inputother">'; |
||
1927 | for($ip=0;$ip<$seqIndex;$ip++) { |
||
1928 | $exB .= $this->formatEntity($inputGlyphs[$ip]).'‍'; |
||
1929 | } |
||
1930 | $exB .= '</span>'; |
||
1931 | } |
||
1932 | View Code Duplication | if (count($inputGlyphs)>($seqIndex+1)) { |
|
1933 | $exL .= '<span class="inputother">'; |
||
1934 | for($ip=$seqIndex+1;$ip<count($inputGlyphs);$ip++) { |
||
1935 | $exL .= $this->formatEntity($inputGlyphs[$ip]).'‍'; |
||
1936 | } |
||
1937 | $exL .= '</span>'; |
||
1938 | } |
||
1939 | $html .= '<div class="sequenceIndex">Substitution Position: '.$seqIndex.'</div>'; |
||
1940 | |||
1941 | $lul2 = array($lup=>$tag); |
||
1942 | |||
1943 | // Only apply if the (first) 'Replace' glyph from the |
||
1944 | // Lookup list is in the [inputGlyphs] at ['SequenceIndex'] |
||
1945 | // Pass $inputGlyphs[$seqIndex] e.g. 00636|00645|00656 |
||
1946 | // to level 2 and only apply if first Replace glyph is in this list |
||
1947 | $html .= $this->_getGSUBarray($Lookup, $lul2, $scripttag, 2, $inputGlyphs[$seqIndex], $exB, $exL); |
||
1948 | } |
||
1949 | |||
1950 | |||
1951 | if (count($subRule['rules'])) $volt[] = $subRule; |
||
1952 | |||
1953 | } |
||
1954 | } |
||
1955 | } |
||
1956 | // Format 2: Class-based Context Glyph Substitution |
||
1957 | else if ($SubstFormat==2) { |
||
1958 | $html .= '<div class="lookuptypesub">Format 2: Class-based Context Glyph Substitution</div>'; |
||
1959 | foreach($Lookup[$i]['Subtable'][$c]['SubClassSet'] AS $inputClass=>$cscs) { |
||
1960 | $html .= '<div class="rule">Input Class: '.$inputClass.'</div>'; |
||
1961 | for($cscrule=0;$cscrule<$cscs['SubClassRuleCnt'];$cscrule++) { |
||
1962 | $html .= '<div class="rule">Rule: '.$cscrule.'</div>'; |
||
1963 | $rule = $cscs['SubClassRule'][$cscrule]; |
||
1964 | |||
1965 | $inputGlyphs = array(); |
||
1966 | |||
1967 | $inputGlyphs[0] = $Lookup[$i]['Subtable'][$c]['InputClasses'][$inputClass]; |
||
1968 | |||
1969 | View Code Duplication | if ($rule['InputGlyphCount']>1) { |
|
1970 | // NB starts at 1 |
||
1971 | for ($gcl=1;$gcl<$rule['InputGlyphCount'];$gcl++) { |
||
1972 | $classindex = $rule['Input'][$gcl]; |
||
1973 | $inputGlyphs[$gcl] = $Lookup[$i]['Subtable'][$c]['InputClasses'][$classindex]; |
||
1974 | } |
||
1975 | } |
||
1976 | |||
1977 | // Class 0 contains all the glyphs NOT in the other classes |
||
1978 | $class0excl = implode('|', $Lookup[$i]['Subtable'][$c]['InputClasses']); |
||
1979 | |||
1980 | $exampleI = array(); |
||
1981 | $html .= '<div class="context">CONTEXT: '; |
||
1982 | View Code Duplication | for ($ff=0;$ff<count($inputGlyphs);$ff++) { |
|
1983 | |||
1984 | if (!$inputGlyphs[$ff]) { |
||
1985 | |||
1986 | $html .= '<div>Input #'.$ff.': <span class="unchanged"> [NOT '.$this->formatEntityStr($class0excl).'] </span></div>'; |
||
1987 | $exampleI[] = '[NOT '.$this->formatEntityFirst($class0excl).']'; |
||
1988 | } |
||
1989 | else { |
||
1990 | $html .= '<div>Input #'.$ff.': <span class="unchanged"> '.$this->formatEntityStr($inputGlyphs[$ff]).' </span></div>'; |
||
1991 | $exampleI[] = $this->formatEntityFirst($inputGlyphs[$ff]); |
||
1992 | } |
||
1993 | } |
||
1994 | $html .= '</div>'; |
||
1995 | |||
1996 | |||
1997 | for ($b=0;$b<$rule['SubstCount'];$b++) { |
||
1998 | $lup = $rule['LookupListIndex'][$b]; |
||
1999 | $seqIndex = $rule['SequenceIndex'][$b]; |
||
2000 | |||
2001 | // GENERATE exampleI[<seqIndex] .... exampleI[>seqIndex] |
||
2002 | $exB = ''; |
||
2003 | $exL = ''; |
||
2004 | |||
2005 | View Code Duplication | if ($seqIndex>0) { |
|
2006 | $exB .= '<span class="inputother">'; |
||
2007 | for($ip=0;$ip<$seqIndex;$ip++) { |
||
2008 | if (!$inputGlyphs[$ip]) { |
||
2009 | $exB .= '[*]'; |
||
2010 | } |
||
2011 | else { |
||
2012 | $exB .= $this->formatEntityFirst($inputGlyphs[$ip]).'‍'; |
||
2013 | } |
||
2014 | } |
||
2015 | $exB .= '</span>'; |
||
2016 | } |
||
2017 | |||
2018 | View Code Duplication | if (count($inputGlyphs)>($seqIndex+1)) { |
|
2019 | $exL .= '<span class="inputother">'; |
||
2020 | for($ip=$seqIndex+1;$ip<count($inputGlyphs);$ip++) { |
||
2021 | if (!$inputGlyphs[$ip]) { |
||
2022 | $exL .= '[*]'; |
||
2023 | } |
||
2024 | else { |
||
2025 | $exL .= $this->formatEntityFirst($inputGlyphs[$ip]).'‍'; |
||
2026 | } |
||
2027 | } |
||
2028 | $exL .= '</span>'; |
||
2029 | } |
||
2030 | |||
2031 | $html .= '<div class="sequenceIndex">Substitution Position: '.$seqIndex.'</div>'; |
||
2032 | |||
2033 | $lul2 = array($lup=>$tag); |
||
2034 | |||
2035 | // Only apply if the (first) 'Replace' glyph from the |
||
2036 | // Lookup list is in the [inputGlyphs] at ['SequenceIndex'] |
||
2037 | // Pass $inputGlyphs[$seqIndex] e.g. 00636|00645|00656 |
||
2038 | // to level 2 and only apply if first Replace glyph is in this list |
||
2039 | $html .= $this->_getGSUBarray($Lookup, $lul2, $scripttag, 2, $inputGlyphs[$seqIndex], $exB, $exL); |
||
2040 | } |
||
2041 | if (count($subRule['rules'])) $volt[] = $subRule; |
||
2042 | |||
2043 | } |
||
2044 | } |
||
2045 | |||
2046 | |||
2047 | |||
2048 | } |
||
2049 | // Format 3: Coverage-based Context Glyph Substitution p259 |
||
2050 | else if ($SubstFormat==3) { |
||
2051 | $html .= '<div class="lookuptypesub">Format 3: Coverage-based Context Glyph Substitution </div>'; |
||
2052 | // IgnoreMarks flag set on main Lookup table |
||
2053 | $inputGlyphs = $Lookup[$i]['Subtable'][$c]['CoverageInputGlyphs']; |
||
2054 | $CoverageInputGlyphs = implode('|', $inputGlyphs); |
||
2055 | $nInput = $Lookup[$i]['Subtable'][$c]['InputGlyphCount']; |
||
2056 | |||
2057 | $exampleI = array(); |
||
2058 | $html .= '<div class="context">CONTEXT: '; |
||
2059 | View Code Duplication | for ($ff=0;$ff<count($inputGlyphs);$ff++) { |
|
2060 | $html .= '<div>Input #'.$ff.': <span class="unchanged"> '.$this->formatEntityStr($inputGlyphs[$ff]).' </span></div>'; |
||
2061 | $exampleI[] = $this->formatEntityFirst($inputGlyphs[$ff]); |
||
2062 | } |
||
2063 | $html .= '</div>'; |
||
2064 | |||
2065 | |||
2066 | for ($b=0;$b<$Lookup[$i]['Subtable'][$c]['SubstCount'];$b++) { |
||
2067 | $lup = $Lookup[$i]['Subtable'][$c]['SubstLookupRecord'][$b]['LookupListIndex']; |
||
2068 | $seqIndex = $Lookup[$i]['Subtable'][$c]['SubstLookupRecord'][$b]['SequenceIndex']; |
||
2069 | // GENERATE exampleI[<seqIndex] .... exampleI[>seqIndex] |
||
2070 | $exB = ''; |
||
2071 | $exL = ''; |
||
2072 | View Code Duplication | if ($seqIndex>0) { |
|
2073 | $exB .= '<span class="inputother">'; |
||
2074 | for($ip=0;$ip<$seqIndex;$ip++) { |
||
2075 | $exB .= $exampleI[$ip].'‍'; |
||
2076 | } |
||
2077 | $exB .= '</span>'; |
||
2078 | } |
||
2079 | |||
2080 | View Code Duplication | if (count($inputGlyphs)>($seqIndex+1)) { |
|
2081 | $exL .= '<span class="inputother">'; |
||
2082 | for($ip=$seqIndex+1;$ip<count($inputGlyphs);$ip++) { |
||
2083 | $exL .= $exampleI[$ip].'‍'; |
||
2084 | } |
||
2085 | $exL .= '</span>'; |
||
2086 | } |
||
2087 | |||
2088 | $html .= '<div class="sequenceIndex">Substitution Position: '.$seqIndex.'</div>'; |
||
2089 | |||
2090 | $lul2 = array($lup=>$tag); |
||
2091 | |||
2092 | // Only apply if the (first) 'Replace' glyph from the |
||
2093 | // Lookup list is in the [inputGlyphs] at ['SequenceIndex'] |
||
2094 | // Pass $inputGlyphs[$seqIndex] e.g. 00636|00645|00656 |
||
2095 | // to level 2 and only apply if first Replace glyph is in this list |
||
2096 | $html .= $this->_getGSUBarray($Lookup, $lul2, $scripttag, 2, $inputGlyphs[$seqIndex], $exB, $exL); |
||
2097 | } |
||
2098 | if (count($subRule['rules'])) $volt[] = $subRule; |
||
2099 | } |
||
2100 | |||
2101 | //print_r($Lookup[$i]); |
||
2102 | //print_r($volt[(count($volt)-1)]); exit; |
||
2103 | } |
||
2104 | // LookupType 6: Chaining Contextual Substitution Subtable |
||
2105 | else if ($Lookup[$i]['Type'] == 6) { |
||
2106 | $html .= '<div class="lookuptype">LookupType 6: Chaining Contextual Substitution Subtable</div>'; |
||
2107 | // Format 1: Simple Chaining Context Glyph Substitution p255 |
||
2108 | if ($SubstFormat==1) { |
||
2109 | $html .= '<div class="lookuptypesub">Format 1: Simple Chaining Context Glyph Substitution </div>'; |
||
2110 | for($s=0;$s<$Lookup[$i]['Subtable'][$c]['ChainSubRuleSetCount'];$s++) { |
||
2111 | // ChainSubRuleSet |
||
2112 | $subRule = array(); |
||
2113 | $html .= '<div class="rule">Subrule Set: '.$s.'</div>'; |
||
2114 | $firstInputGlyph = $Lookup[$i]['Subtable'][$c]['CoverageGlyphs'][$s]; // First input gyyph |
||
2115 | foreach($Lookup[$i]['Subtable'][$c]['ChainSubRuleSet'][$s]['ChainSubRule'] AS $rctr=>$rule) { |
||
2116 | $html .= '<div class="rule">SubRule: '.$rctr.'</div>'; |
||
2117 | // ChainSubRule |
||
2118 | $inputGlyphs = array(); |
||
2119 | if ($rule['InputGlyphCount']>1) { |
||
2120 | $inputGlyphs = $rule['InputGlyphs']; |
||
2121 | } |
||
2122 | $inputGlyphs[0] = $firstInputGlyph; |
||
2123 | ksort($inputGlyphs); |
||
2124 | $nInput = count($inputGlyphs); |
||
2125 | |||
2126 | if ($rule['BacktrackGlyphCount']) { $backtrackGlyphs = $rule['BacktrackGlyphs']; } |
||
2127 | else { $backtrackGlyphs = array(); } |
||
2128 | |||
2129 | if ($rule['LookaheadGlyphCount']) { $lookaheadGlyphs = $rule['LookaheadGlyphs']; } |
||
2130 | else { $lookaheadGlyphs = array(); } |
||
2131 | |||
2132 | |||
2133 | $exampleB = array(); |
||
2134 | $exampleI = array(); |
||
2135 | $exampleL = array(); |
||
2136 | $html .= '<div class="context">CONTEXT: '; |
||
2137 | View Code Duplication | for ($ff=count($backtrackGlyphs)-1;$ff>=0;$ff--) { |
|
2138 | $html .= '<div>Backtrack #'.$ff.': <span class="unicode">'.$this->formatUniStr($backtrackGlyphs[$ff]).'</span></div>'; |
||
2139 | $exampleB[] = $this->formatEntityFirst($backtrackGlyphs[$ff]); |
||
2140 | } |
||
2141 | View Code Duplication | for ($ff=0;$ff<count($inputGlyphs);$ff++) { |
|
0 ignored issues
–
show
It seems like you are calling the size function
count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.
If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration: for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}
// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
|
|||
2142 | $html .= '<div>Input #'.$ff.': <span class="unchanged"> '.$this->formatEntityStr($inputGlyphs[$ff]).' </span></div>'; |
||
2143 | $exampleI[] = $this->formatEntityFirst($inputGlyphs[$ff]); |
||
2144 | } |
||
2145 | View Code Duplication | for ($ff=0;$ff<count($lookaheadGlyphs);$ff++) { |
|
2146 | $html .= '<div>Lookahead #'.$ff.': <span class="unicode">'.$this->formatUniStr($lookaheadGlyphs[$ff]).'</span></div>'; |
||
2147 | $exampleL[] = $this->formatEntityFirst($lookaheadGlyphs[$ff]); |
||
2148 | } |
||
2149 | $html .= '</div>'; |
||
2150 | |||
2151 | |||
2152 | for ($b=0;$b<$rule['SubstCount'];$b++) { |
||
2153 | $lup = $rule['LookupListIndex'][$b]; |
||
2154 | $seqIndex = $rule['SequenceIndex'][$b]; |
||
2155 | |||
2156 | // GENERATE exampleB[n] exampleI[<seqIndex] .... exampleI[>seqIndex] exampleL[n] |
||
2157 | $exB = ''; |
||
2158 | $exL = ''; |
||
2159 | if (count($exampleB)) { $exB .= '<span class="backtrack">'.implode('‍',$exampleB).'</span>'; } |
||
2160 | |||
2161 | View Code Duplication | if ($seqIndex>0) { |
|
2162 | $exB .= '<span class="inputother">'; |
||
2163 | for($ip=0;$ip<$seqIndex;$ip++) { |
||
2164 | $exB .= $this->formatEntity($inputGlyphs[$ip]).'‍'; |
||
2165 | } |
||
2166 | $exB .= '</span>'; |
||
2167 | } |
||
2168 | |||
2169 | View Code Duplication | if (count($inputGlyphs)>($seqIndex+1)) { |
|
2170 | $exL .= '<span class="inputother">'; |
||
2171 | for($ip=$seqIndex+1;$ip<count($inputGlyphs);$ip++) { |
||
2172 | $exL .= $this->formatEntity($inputGlyphs[$ip]).'‍'; |
||
2173 | } |
||
2174 | $exL .= '</span>'; |
||
2175 | } |
||
2176 | |||
2177 | if (count($exampleL)) { $exL .= '<span class="lookahead">'.implode('‍',$exampleL).'</span>'; } |
||
2178 | |||
2179 | $html .= '<div class="sequenceIndex">Substitution Position: '.$seqIndex.'</div>'; |
||
2180 | |||
2181 | $lul2 = array($lup=>$tag); |
||
2182 | |||
2183 | // Only apply if the (first) 'Replace' glyph from the |
||
2184 | // Lookup list is in the [inputGlyphs] at ['SequenceIndex'] |
||
2185 | // Pass $inputGlyphs[$seqIndex] e.g. 00636|00645|00656 |
||
2186 | // to level 2 and only apply if first Replace glyph is in this list |
||
2187 | $html .= $this->_getGSUBarray($Lookup, $lul2, $scripttag, 2, $inputGlyphs[$seqIndex], $exB, $exL); |
||
2188 | } |
||
2189 | |||
2190 | |||
2191 | if (count($subRule['rules'])) $volt[] = $subRule; |
||
2192 | |||
2193 | |||
2194 | |||
2195 | } |
||
2196 | } |
||
2197 | |||
2198 | } |
||
2199 | // Format 2: Class-based Chaining Context Glyph Substitution p257 |
||
2200 | else if ($SubstFormat==2) { |
||
2201 | $html .= '<div class="lookuptypesub">Format 2: Class-based Chaining Context Glyph Substitution </div>'; |
||
2202 | foreach($Lookup[$i]['Subtable'][$c]['ChainSubClassSet'] AS $inputClass=>$cscs) { |
||
2203 | $html .= '<div class="rule">Input Class: '.$inputClass.'</div>'; |
||
2204 | for($cscrule=0;$cscrule<$cscs['ChainSubClassRuleCnt'];$cscrule++) { |
||
2205 | $html .= '<div class="rule">Rule: '.$cscrule.'</div>'; |
||
2206 | $rule = $cscs['ChainSubClassRule'][$cscrule]; |
||
2207 | |||
2208 | // These contain classes of glyphs as strings |
||
2209 | // $Lookup[$i]['Subtable'][$c]['InputClasses'][(class)] e.g. 02E6|02E7|02E8 |
||
2210 | // $Lookup[$i]['Subtable'][$c]['LookaheadClasses'][(class)] |
||
2211 | // $Lookup[$i]['Subtable'][$c]['BacktrackClasses'][(class)] |
||
2212 | |||
2213 | // These contain arrays of classIndexes |
||
2214 | // [Backtrack] [Lookahead] and [Input] (Input is from the second position only) |
||
2215 | |||
2216 | $inputGlyphs = array(); |
||
2217 | |||
2218 | $inputGlyphs[0] = $Lookup[$i]['Subtable'][$c]['InputClasses'][$inputClass]; |
||
2219 | View Code Duplication | if ($rule['InputGlyphCount']>1) { |
|
2220 | // NB starts at 1 |
||
2221 | for ($gcl=1;$gcl<$rule['InputGlyphCount'];$gcl++) { |
||
2222 | $classindex = $rule['Input'][$gcl]; |
||
2223 | $inputGlyphs[$gcl] = $Lookup[$i]['Subtable'][$c]['InputClasses'][$classindex]; |
||
2224 | } |
||
2225 | } |
||
2226 | // Class 0 contains all the glyphs NOT in the other classes |
||
2227 | $class0excl = implode('|', $Lookup[$i]['Subtable'][$c]['InputClasses']); |
||
2228 | |||
2229 | $nInput = $rule['InputGlyphCount']; |
||
2230 | |||
2231 | View Code Duplication | if ($rule['BacktrackGlyphCount']) { |
|
2232 | for ($gcl=0;$gcl<$rule['BacktrackGlyphCount'];$gcl++) { |
||
2233 | $classindex = $rule['Backtrack'][$gcl]; |
||
2234 | $backtrackGlyphs[$gcl] = $Lookup[$i]['Subtable'][$c]['BacktrackClasses'][$classindex]; |
||
2235 | } |
||
2236 | } |
||
2237 | else { $backtrackGlyphs = array(); } |
||
2238 | |||
2239 | View Code Duplication | if ($rule['LookaheadGlyphCount']) { |
|
2240 | for ($gcl=0;$gcl<$rule['LookaheadGlyphCount'];$gcl++) { |
||
2241 | $classindex = $rule['Lookahead'][$gcl]; |
||
2242 | $lookaheadGlyphs[$gcl] = $Lookup[$i]['Subtable'][$c]['LookaheadClasses'][$classindex]; |
||
2243 | } |
||
2244 | } |
||
2245 | else { $lookaheadGlyphs = array(); } |
||
2246 | |||
2247 | |||
2248 | $exampleB = array(); |
||
2249 | $exampleI = array(); |
||
2250 | $exampleL = array(); |
||
2251 | $html .= '<div class="context">CONTEXT: '; |
||
2252 | View Code Duplication | for ($ff=count($backtrackGlyphs)-1;$ff>=0;$ff--) { |
|
2253 | if (!$backtrackGlyphs[$ff]) { |
||
2254 | $html .= '<div>Backtrack #'.$ff.': <span class="unchanged"> [NOT '.$this->formatEntityStr($class0excl).'] </span></div>'; |
||
2255 | $exampleB[] = '[NOT '.$this->formatEntityFirst($class0excl).']'; |
||
2256 | |||
2257 | } |
||
2258 | else { |
||
2259 | $html .= '<div>Backtrack #'.$ff.': <span class="unicode">'.$this->formatUniStr($backtrackGlyphs[$ff]).'</span></div>'; |
||
2260 | $exampleB[] = $this->formatEntityFirst($backtrackGlyphs[$ff]); |
||
2261 | } |
||
2262 | } |
||
2263 | View Code Duplication | for ($ff=0;$ff<count($inputGlyphs);$ff++) { |
|
2264 | if (!$inputGlyphs[$ff]) { |
||
2265 | $html .= '<div>Input #'.$ff.': <span class="unchanged"> [NOT '.$this->formatEntityStr($class0excl).'] </span></div>'; |
||
2266 | $exampleI[] = '[NOT '.$this->formatEntityFirst($class0excl).']'; |
||
2267 | } |
||
2268 | else { |
||
2269 | $html .= '<div>Input #'.$ff.': <span class="unchanged"> '.$this->formatEntityStr($inputGlyphs[$ff]).' </span></div>'; |
||
2270 | $exampleI[] = $this->formatEntityFirst($inputGlyphs[$ff]); |
||
2271 | } |
||
2272 | } |
||
2273 | View Code Duplication | for ($ff=0;$ff<count($lookaheadGlyphs);$ff++) { |
|
2274 | if (!$lookaheadGlyphs[$ff]) { |
||
2275 | $html .= '<div>Lookahead #'.$ff.': <span class="unchanged"> [NOT '.$this->formatEntityStr($class0excl).'] </span></div>'; |
||
2276 | $exampleL[] = '[NOT '.$this->formatEntityFirst($class0excl).']'; |
||
2277 | |||
2278 | } |
||
2279 | else { |
||
2280 | $html .= '<div>Lookahead #'.$ff.': <span class="unicode">'.$this->formatUniStr($lookaheadGlyphs[$ff]).'</span></div>'; |
||
2281 | $exampleL[] = $this->formatEntityFirst($lookaheadGlyphs[$ff]); |
||
2282 | } |
||
2283 | } |
||
2284 | $html .= '</div>'; |
||
2285 | |||
2286 | |||
2287 | for ($b=0;$b<$rule['SubstCount'];$b++) { |
||
2288 | $lup = $rule['LookupListIndex'][$b]; |
||
2289 | $seqIndex = $rule['SequenceIndex'][$b]; |
||
2290 | |||
2291 | // GENERATE exampleB[n] exampleI[<seqIndex] .... exampleI[>seqIndex] exampleL[n] |
||
2292 | $exB = ''; |
||
2293 | $exL = ''; |
||
2294 | if (count($exampleB)) { $exB .= '<span class="backtrack">'.implode('‍',$exampleB).'</span>'; } |
||
2295 | |||
2296 | View Code Duplication | if ($seqIndex>0) { |
|
2297 | $exB .= '<span class="inputother">'; |
||
2298 | for($ip=0;$ip<$seqIndex;$ip++) { |
||
2299 | if (!$inputGlyphs[$ip]) { |
||
2300 | $exB .= '[*]'; |
||
2301 | } |
||
2302 | else { |
||
2303 | $exB .= $this->formatEntityFirst($inputGlyphs[$ip]).'‍'; |
||
2304 | } |
||
2305 | } |
||
2306 | $exB .= '</span>'; |
||
2307 | } |
||
2308 | |||
2309 | View Code Duplication | if (count($inputGlyphs)>($seqIndex+1)) { |
|
2310 | $exL .= '<span class="inputother">'; |
||
2311 | for($ip=$seqIndex+1;$ip<count($inputGlyphs);$ip++) { |
||
2312 | if (!$inputGlyphs[$ip]) { |
||
2313 | $exL .= '[*]'; |
||
2314 | } |
||
2315 | else { |
||
2316 | $exL .= $this->formatEntityFirst($inputGlyphs[$ip]).'‍'; |
||
2317 | } |
||
2318 | } |
||
2319 | $exL .= '</span>'; |
||
2320 | } |
||
2321 | |||
2322 | if (count($exampleL)) { $exL .= '<span class="lookahead">'.implode('‍',$exampleL).'</span>'; } |
||
2323 | |||
2324 | $html .= '<div class="sequenceIndex">Substitution Position: '.$seqIndex.'</div>'; |
||
2325 | |||
2326 | $lul2 = array($lup=>$tag); |
||
2327 | |||
2328 | // Only apply if the (first) 'Replace' glyph from the |
||
2329 | // Lookup list is in the [inputGlyphs] at ['SequenceIndex'] |
||
2330 | // Pass $inputGlyphs[$seqIndex] e.g. 00636|00645|00656 |
||
2331 | // to level 2 and only apply if first Replace glyph is in this list |
||
2332 | $html .= $this->_getGSUBarray($Lookup, $lul2, $scripttag, 2, $inputGlyphs[$seqIndex], $exB, $exL); |
||
2333 | |||
2334 | } |
||
2335 | |||
2336 | } |
||
2337 | } |
||
2338 | |||
2339 | |||
2340 | //print_r($Lookup[$i]['Subtable'][$c]); exit; |
||
2341 | |||
2342 | } |
||
2343 | // Format 3: Coverage-based Chaining Context Glyph Substitution p259 |
||
2344 | else if ($SubstFormat==3) { |
||
2345 | $html .= '<div class="lookuptypesub">Format 3: Coverage-based Chaining Context Glyph Substitution </div>'; |
||
2346 | // IgnoreMarks flag set on main Lookup table |
||
2347 | $inputGlyphs = $Lookup[$i]['Subtable'][$c]['CoverageInputGlyphs']; |
||
2348 | $CoverageInputGlyphs = implode('|', $inputGlyphs); |
||
2349 | $nInput = $Lookup[$i]['Subtable'][$c]['InputGlyphCount']; |
||
2350 | |||
2351 | if ($Lookup[$i]['Subtable'][$c]['BacktrackGlyphCount']) { |
||
2352 | $backtrackGlyphs = $Lookup[$i]['Subtable'][$c]['CoverageBacktrackGlyphs']; |
||
2353 | } |
||
2354 | else { $backtrackGlyphs = array(); } |
||
2355 | |||
2356 | if ($Lookup[$i]['Subtable'][$c]['LookaheadGlyphCount']) { |
||
2357 | $lookaheadGlyphs = $Lookup[$i]['Subtable'][$c]['CoverageLookaheadGlyphs']; |
||
2358 | } |
||
2359 | else { $lookaheadGlyphs = array(); } |
||
2360 | |||
2361 | |||
2362 | $exampleB = array(); |
||
2363 | $exampleI = array(); |
||
2364 | $exampleL = array(); |
||
2365 | $html .= '<div class="context">CONTEXT: '; |
||
2366 | View Code Duplication | for ($ff=count($backtrackGlyphs)-1;$ff>=0;$ff--) { |
|
2367 | $html .= '<div>Backtrack #'.$ff.': <span class="unicode">'.$this->formatUniStr($backtrackGlyphs[$ff]).'</span></div>'; |
||
2368 | $exampleB[] = $this->formatEntityFirst($backtrackGlyphs[$ff]); |
||
2369 | } |
||
2370 | View Code Duplication | for ($ff=0;$ff<count($inputGlyphs);$ff++) { |
|
2371 | $html .= '<div>Input #'.$ff.': <span class="unchanged"> '.$this->formatEntityStr($inputGlyphs[$ff]).' </span></div>'; |
||
2372 | $exampleI[] = $this->formatEntityFirst($inputGlyphs[$ff]); |
||
2373 | } |
||
2374 | View Code Duplication | for ($ff=0;$ff<count($lookaheadGlyphs);$ff++) { |
|
2375 | $html .= '<div>Lookahead #'.$ff.': <span class="unicode">'.$this->formatUniStr($lookaheadGlyphs[$ff]).'</span></div>'; |
||
2376 | $exampleL[] = $this->formatEntityFirst($lookaheadGlyphs[$ff]); |
||
2377 | } |
||
2378 | $html .= '</div>'; |
||
2379 | |||
2380 | |||
2381 | for ($b=0;$b<$Lookup[$i]['Subtable'][$c]['SubstCount'];$b++) { |
||
2382 | $lup = $Lookup[$i]['Subtable'][$c]['SubstLookupRecord'][$b]['LookupListIndex']; |
||
2383 | $seqIndex = $Lookup[$i]['Subtable'][$c]['SubstLookupRecord'][$b]['SequenceIndex']; |
||
2384 | |||
2385 | |||
2386 | // GENERATE exampleB[n] exampleI[<seqIndex] .... exampleI[>seqIndex] exampleL[n] |
||
2387 | $exB = ''; |
||
2388 | $exL = ''; |
||
2389 | if (count($exampleB)) { $exB .= '<span class="backtrack">'.implode('‍',$exampleB).'</span>'; } |
||
2390 | |||
2391 | View Code Duplication | if ($seqIndex>0) { |
|
2392 | $exB .= '<span class="inputother">'; |
||
2393 | for($ip=0;$ip<$seqIndex;$ip++) { |
||
2394 | $exB .= $exampleI[$ip].'‍'; |
||
2395 | } |
||
2396 | $exB .= '</span>'; |
||
2397 | } |
||
2398 | |||
2399 | View Code Duplication | if (count($inputGlyphs)>($seqIndex+1)) { |
|
2400 | $exL .= '<span class="inputother">'; |
||
2401 | for($ip=$seqIndex+1;$ip<count($inputGlyphs);$ip++) { |
||
2402 | $exL .= $exampleI[$ip].'‍'; |
||
2403 | } |
||
2404 | $exL .= '</span>'; |
||
2405 | } |
||
2406 | |||
2407 | if (count($exampleL)) { $exL .= '<span class="lookahead">'.implode('‍',$exampleL).'</span>'; } |
||
2408 | |||
2409 | $html .= '<div class="sequenceIndex">Substitution Position: '.$seqIndex.'</div>'; |
||
2410 | |||
2411 | $lul2 = array($lup=>$tag); |
||
2412 | |||
2413 | // Only apply if the (first) 'Replace' glyph from the |
||
2414 | // Lookup list is in the [inputGlyphs] at ['SequenceIndex'] |
||
2415 | // Pass $inputGlyphs[$seqIndex] e.g. 00636|00645|00656 |
||
2416 | // to level 2 and only apply if first Replace glyph is in this list |
||
2417 | $html .= $this->_getGSUBarray($Lookup, $lul2, $scripttag, 2, $inputGlyphs[$seqIndex], $exB, $exL); |
||
2418 | |||
2419 | } |
||
2420 | } |
||
2421 | } |
||
2422 | } |
||
2423 | $html .= '</div>'; |
||
2424 | } |
||
2425 | if ($level ==1) { $this->mpdf->WriteHTML($html); } |
||
2426 | else { return $html; } |
||
2427 | //print_r($Lookup); exit; |
||
2428 | } |
||
2429 | //===================================================================================== |
||
2430 | //===================================================================================== |
||
2431 | // mPDF 5.7.1 |
||
2432 | View Code Duplication | function _checkGSUBignore($flag, $glyph, $MarkFilteringSet) { |
|
2433 | $ignore = false; |
||
2434 | // Flag & 0x0008 = Ignore Marks |
||
2435 | if ((($flag & 0x0008) == 0x0008) && strpos($this->GlyphClassMarks,$glyph)) { $ignore = true; } |
||
2436 | if ((($flag & 0x0004) == 0x0004) && strpos($this->GlyphClassLigatures,$glyph)) { $ignore = true; } |
||
2437 | if ((($flag & 0x0002) == 0x0002) && strpos($this->GlyphClassBases,$glyph)) { $ignore = true; } |
||
2438 | // Flag & 0xFF?? = MarkAttachmentType |
||
2439 | if (($flag & 0xFF00) && strpos($this->MarkAttachmentType[($flag >> 8)],$glyph)) { $ignore = true; } |
||
2440 | // Flag & 0x0010 = UseMarkFilteringSet |
||
2441 | if (($flag & 0x0010) && strpos($this->MarkGlyphSets[$MarkFilteringSet],$glyph)) { $ignore = true; } |
||
2442 | return $ignore; |
||
2443 | } |
||
2444 | |||
2445 | function _getGSUBignoreString($flag, $MarkFilteringSet) { |
||
2446 | // If ignoreFlag set, combine all ignore glyphs into -> "((?:(?: FBA1| FBA2| FBA3))*)" |
||
2447 | // else "()" |
||
2448 | // for Input - set on secondary Lookup table if in Context, and set Backtrack and Lookahead on Context Lookup |
||
2449 | $str = ""; |
||
2450 | $ignoreflag = 0; |
||
2451 | |||
2452 | // Flag & 0xFF?? = MarkAttachmentType |
||
2453 | if ($flag & 0xFF00) { |
||
2454 | $MarkAttachmentType = $flag >> 8; |
||
2455 | $ignoreflag = $flag; |
||
2456 | //$str = $this->MarkAttachmentType[$MarkAttachmentType]; |
||
2457 | $str = "MarkAttachmentType[".$MarkAttachmentType."] "; |
||
2458 | } |
||
2459 | |||
2460 | // Flag & 0x0010 = UseMarkFilteringSet |
||
2461 | View Code Duplication | if ($flag & 0x0010) { |
|
2462 | die("This font ".$this->fontkey." contains MarkGlyphSets"); |
||
2463 | $str = "Mark Glyph Set: "; |
||
2464 | $str .= $this->MarkGlyphSets[$MarkFilteringSet]; |
||
2465 | } |
||
2466 | |||
2467 | // If Ignore Marks set, supercedes any above |
||
2468 | // Flag & 0x0008 = Ignore Marks |
||
2469 | if (($flag & 0x0008) == 0x0008) { |
||
2470 | $ignoreflag = 8; |
||
2471 | //$str = $this->GlyphClassMarks; |
||
2472 | $str = "Mark Glyphs "; |
||
2473 | } |
||
2474 | |||
2475 | // Flag & 0x0004 = Ignore Ligatures |
||
2476 | View Code Duplication | if (($flag & 0x0004) == 0x0004) { |
|
2477 | $ignoreflag += 4; |
||
2478 | if ($str) { $str .= "|"; } |
||
2479 | //$str .= $this->GlyphClassLigatures; |
||
2480 | $str .= "Ligature Glyphs "; |
||
2481 | } |
||
2482 | // Flag & 0x0002 = Ignore BaseGlyphs |
||
2483 | View Code Duplication | if (($flag & 0x0002) == 0x0002) { |
|
2484 | $ignoreflag += 2; |
||
2485 | if ($str) { $str .= "|"; } |
||
2486 | //$str .= $this->GlyphClassBases; |
||
2487 | $str .= "Base Glyphs "; |
||
2488 | } |
||
2489 | if ($str) { |
||
2490 | return $str; |
||
2491 | } |
||
2492 | else return ""; |
||
2493 | } |
||
2494 | |||
2495 | // GSUB Patterns |
||
2496 | |||
2497 | /* |
||
2498 | BACKTRACK INPUT LOOKAHEAD |
||
2499 | ================================== ================== ================================== |
||
2500 | (FEEB|FEEC)(ign) �(FD12|FD13)(ign) �(0612)�(ign) (0613)�(ign) (FD12|FD13)�(ign) (FEEB|FEEC) |
||
2501 | ---------------- ---------------- ----- ------------ --------------- --------------- |
||
2502 | Backtrack 1 Backtrack 2 Input 1 Input 2 Lookahead 1 Lookahead 2 |
||
2503 | -------- --- --------- --- ---- --- ---- --- --------- --- ------- |
||
2504 | \${1} \${2} \${3} \${4} \${5+} \${6+} \${7+} \${8+} |
||
2505 | |||
2506 | nBacktrack = 2 nInput = 2 nLookahead = 2 |
||
2507 | |||
2508 | nBsubs = 2xnBack nIsubs = (nBsubs+) nLsubs = (nBsubs+nIsubs+) 2xnLookahead |
||
2509 | "\${1}\${2} " (nInput*2)-1 "\${5+} \${6+}" |
||
2510 | "REPL" |
||
2511 | |||
2512 | �\${1}\${2} �\${3}\${4} �REPL�\${5+} \${6+}�\${7+} \${8+}� |
||
2513 | |||
2514 | |||
2515 | INPUT nInput = 5 |
||
2516 | ============================================================ |
||
2517 | �(0612)�(ign) (0613)�(ign) (0614)�(ign) (0615)�(ign) (0615)� |
||
2518 | \${1} \${2} \${3} \${4} \${5} \${6} \${7} \${8} \${9} (All backreference numbers are + nBsubs) |
||
2519 | ----- ------------ ------------ ------------ ------------ |
||
2520 | Input 1 Input 2 Input 3 Input 4 Input 5 |
||
2521 | |||
2522 | A====== SequenceIndex=1 ; Lookup match nGlyphs=1 |
||
2523 | B=================== SequenceIndex=1 ; Lookup match nGlyphs=2 |
||
2524 | C=============================== SequenceIndex=1 ; Lookup match nGlyphs=3 |
||
2525 | D======================= SequenceIndex=2 ; Lookup match nGlyphs=2 |
||
2526 | E===================================== SequenceIndex=2 ; Lookup match nGlyphs=3 |
||
2527 | F====================== SequenceIndex=4 ; Lookup match nGlyphs=2 |
||
2528 | |||
2529 | All backreference numbers are + nBsubs |
||
2530 | A - "REPL\${2} \${3}\${4} \${5}\${6} \${7}\${8} \${9}" |
||
2531 | B - "REPL\${2}\${4} \${5}\${6} \${7}\${8} \${9}" |
||
2532 | C - "REPL\${2}\${4}\${6} \${7}\${8} \${9}" |
||
2533 | D - "\${1} REPL\${2}\${4}\${6} \${7}\${8} \${9}" |
||
2534 | E - "\${1} REPL\${2}\${4}\${6}\${8} \${9}" |
||
2535 | F - "\${1}\${2} \${3}\${4} \${5} REPL\${6}\${8}" |
||
2536 | */ |
||
2537 | |||
2538 | View Code Duplication | function _makeGSUBcontextInputMatch($inputGlyphs, $ignore, $lookupGlyphs, $seqIndex) { |
|
2539 | // $ignore = "((?:(?: FBA1| FBA2| FBA3))*)" or "()" |
||
2540 | // Returns e.g. �(0612)�(ignore) (0613)�(ignore) (0614)� |
||
2541 | // $inputGlyphs = array of glyphs(glyphstrings) making up Input sequence in Context |
||
2542 | // $lookupGlyphs = array of glyphs (single Glyphs) making up Lookup Input sequence |
||
2543 | $mLen = count($lookupGlyphs); // nGlyphs in the secondary Lookup match |
||
2544 | $nInput = count($inputGlyphs); // nGlyphs in the Primary Input sequence |
||
2545 | $str = ""; |
||
2546 | for($i=0;$i<$nInput;$i++) { |
||
2547 | if ($i>0) { $str .= $ignore." "; } |
||
2548 | if ($i>=$seqIndex && $i<($seqIndex+$mLen)) { $str .= "".$lookupGlyphs[($i-$seqIndex)].""; } |
||
2549 | else { $str .= "".$inputGlyphs[($i)].""; } |
||
2550 | } |
||
2551 | return $str; |
||
2552 | } |
||
2553 | |||
2554 | View Code Duplication | function _makeGSUBinputMatch($inputGlyphs, $ignore) { |
|
2555 | // $ignore = "((?:(?: FBA1| FBA2| FBA3))*)" or "()" |
||
2556 | // Returns e.g. �(0612)�(ignore) (0613)�(ignore) (0614)� |
||
2557 | // $inputGlyphs = array of glyphs(glyphstrings) making up Input sequence in Context |
||
2558 | // $lookupGlyphs = array of glyphs making up Lookup Input sequence - if applicable |
||
2559 | $str = ""; |
||
2560 | for($i=1;$i<=count($inputGlyphs);$i++) { |
||
2561 | if ($i>1) { $str .= $ignore." "; } |
||
2562 | $str .= "".$inputGlyphs[($i-1)].""; |
||
2563 | } |
||
2564 | return $str; |
||
2565 | } |
||
2566 | |||
2567 | View Code Duplication | function _makeGSUBbacktrackMatch($backtrackGlyphs, $ignore) { |
|
2568 | // $ignore = "((?:(?: FBA1| FBA2| FBA3))*)" or "()" |
||
2569 | // Returns e.g. �(FEEB|FEEC)(ignore) �(FD12|FD13)(ignore) � |
||
2570 | // $backtrackGlyphs = array of glyphstrings making up Backtrack sequence |
||
2571 | // 3 2 1 0 |
||
2572 | // each item being e.g. E0AD|E0AF|F1FD |
||
2573 | $str = ""; |
||
2574 | for($i=(count($backtrackGlyphs)-1);$i>=0;$i--) { |
||
2575 | $str .= "".$backtrackGlyphs[$i]." ".$ignore." "; |
||
2576 | } |
||
2577 | return $str; |
||
2578 | } |
||
2579 | |||
2580 | View Code Duplication | function _makeGSUBlookaheadMatch($lookaheadGlyphs, $ignore) { |
|
2581 | // $ignore = "((?:(?: FBA1| FBA2| FBA3))*)" or "()" |
||
2582 | // Returns e.g. �(ignore) (FD12|FD13)�(ignore) (FEEB|FEEC)� |
||
2583 | // $lookaheadGlyphs = array of glyphstrings making up Lookahead sequence |
||
2584 | // 0 1 2 3 |
||
2585 | // each item being e.g. E0AD|E0AF|F1FD |
||
2586 | $str = ""; |
||
2587 | for($i=0;$i<count($lookaheadGlyphs);$i++) { |
||
2588 | $str .= $ignore." ".$lookaheadGlyphs[$i].""; |
||
2589 | } |
||
2590 | return $str; |
||
2591 | } |
||
2592 | |||
2593 | |||
2594 | |||
2595 | View Code Duplication | function _makeGSUBinputReplacement($nInput, $REPL, $ignore, $nBsubs, $mLen, $seqIndex) { |
|
2596 | // Returns e.g. "REPL\${6}\${8}" or "\${1}\${2} \${3} REPL\${4}\${6}\${8} \${9}" |
||
2597 | // $nInput nGlyphs in the Primary Input sequence |
||
2598 | // $REPL replacement glyphs from secondary lookup |
||
2599 | // $ignore = "((?:(?: FBA1| FBA2| FBA3))*)" or "()" |
||
2600 | // $nBsubs Number of Backtrack substitutions (= 2x Number of Backtrack glyphs) |
||
2601 | // $mLen nGlyphs in the secondary Lookup match - if no secondary lookup, should=$nInput |
||
2602 | // $seqIndex Sequence Index to apply the secondary match |
||
2603 | if ($ignore=="()") { $ign = false; } |
||
2604 | else { $ign = true; } |
||
2605 | $str = ""; |
||
2606 | if ($nInput == 1) { $str = $REPL; } |
||
2607 | else if ($nInput>1) { |
||
2608 | if ($mLen==$nInput) { // whole string replaced |
||
2609 | $str = $REPL; |
||
2610 | if ($ign) { |
||
2611 | // for every nInput over 1, add another replacement backreference, to move IGNORES after replacement |
||
2612 | for($x=2;$x<=$nInput;$x++) { |
||
2613 | $str .= '\\'.($nBsubs+(2*($x-1))); |
||
2614 | } |
||
2615 | } |
||
2616 | } |
||
2617 | else { // if only part of string replaced: |
||
2618 | for($x=1;$x<($seqIndex+1);$x++) { |
||
2619 | if ($x==1) { $str .= '\\'.($nBsubs + 1); } |
||
2620 | else { |
||
2621 | if ($ign) { $str .= '\\'.($nBsubs+(2*($x-1))); } |
||
2622 | $str .= ' \\'.($nBsubs+1+(2*($x-1))); |
||
2623 | } |
||
2624 | } |
||
2625 | if ($seqIndex>0) { $str .= " "; } |
||
2626 | $str .= $REPL; |
||
2627 | if ($ign) { |
||
2628 | for($x=(max(($seqIndex+1),2));$x<($seqIndex+1+$mLen);$x++) { // move IGNORES after replacement |
||
2629 | $str .= '\\'.($nBsubs+(2*($x-1))); |
||
2630 | } |
||
2631 | } |
||
2632 | for($x=($seqIndex+1+$mLen);$x<=$nInput;$x++) { |
||
2633 | if ($ign) { $str .= '\\'.($nBsubs+(2*($x-1))); } |
||
2634 | $str .= ' \\'.($nBsubs+1+(2*($x-1))); |
||
2635 | } |
||
2636 | } |
||
2637 | } |
||
2638 | return $str; |
||
2639 | } |
||
2640 | |||
2641 | |||
2642 | |||
2643 | ////////////////////////////////////////////////////////////////////////////////// |
||
2644 | function _getCoverage($convert2hex=true) { |
||
2645 | $g = array(); |
||
2646 | $CoverageFormat= $this->read_ushort(); |
||
2647 | if ($CoverageFormat == 1) { |
||
2648 | $CoverageGlyphCount= $this->read_ushort(); |
||
2649 | View Code Duplication | for ($gid=0;$gid<$CoverageGlyphCount;$gid++) { |
|
2650 | $glyphID = $this->read_ushort(); |
||
2651 | if ($convert2hex) { $g[] = unicode_hex($this->glyphToChar[$glyphID][0]); } |
||
2652 | else { $g[] = $glyphID; } |
||
2653 | } |
||
2654 | } |
||
2655 | if ($CoverageFormat == 2) { |
||
2656 | $RangeCount= $this->read_ushort(); |
||
2657 | for ($r=0;$r<$RangeCount;$r++) { |
||
2658 | $start = $this->read_ushort(); |
||
2659 | $end = $this->read_ushort(); |
||
2660 | $StartCoverageIndex = $this->read_ushort(); // n/a |
||
2661 | for ($gid=$start;$gid<=$end;$gid++) { |
||
2662 | $glyphID = $gid; |
||
2663 | if ($convert2hex) { $g[] = unicode_hex($this->glyphToChar[$glyphID][0]); } |
||
2664 | else { $g[] = $glyphID; } |
||
2665 | } |
||
2666 | } |
||
2667 | } |
||
2668 | return $g; |
||
2669 | } |
||
2670 | |||
2671 | ////////////////////////////////////////////////////////////////////////////////// |
||
2672 | View Code Duplication | function _getClasses($offset) { |
|
2673 | $this->seek($offset); |
||
2674 | $ClassFormat = $this->read_ushort(); |
||
2675 | $GlyphByClass = array(); |
||
2676 | if ($ClassFormat == 1) { |
||
2677 | $StartGlyph = $this->read_ushort(); |
||
2678 | $GlyphCount = $this->read_ushort(); |
||
2679 | for ($i=0;$i<$GlyphCount;$i++) { |
||
2680 | $startGlyphID = $StartGlyph + $i; |
||
2681 | $endGlyphID = $StartGlyph + $i; |
||
2682 | $class = $this->read_ushort(); |
||
2683 | for($g=$startGlyphID;$g<=$endGlyphID;$g++) { |
||
2684 | if ($this->glyphToChar[$g][0]) { |
||
2685 | $GlyphByClass[$class][] = unicode_hex($this->glyphToChar[$g][0]); |
||
2686 | } |
||
2687 | } |
||
2688 | } |
||
2689 | } |
||
2690 | else if ($ClassFormat == 2) { |
||
2691 | $tableCount = $this->read_ushort(); |
||
2692 | for ($i=0;$i<$tableCount;$i++) { |
||
2693 | $startGlyphID = $this->read_ushort(); |
||
2694 | $endGlyphID = $this->read_ushort(); |
||
2695 | $class = $this->read_ushort(); |
||
2696 | for($g=$startGlyphID;$g<=$endGlyphID;$g++) { |
||
2697 | if ($this->glyphToChar[$g][0]) { |
||
2698 | $GlyphByClass[$class][] = unicode_hex($this->glyphToChar[$g][0]); |
||
2699 | } |
||
2700 | } |
||
2701 | } |
||
2702 | } |
||
2703 | $gbc = array(); |
||
2704 | foreach($GlyphByClass AS $class=>$garr) { $gbc[$class] = implode('|',$garr); } |
||
2705 | return $gbc; |
||
2706 | } |
||
2707 | ////////////////////////////////////////////////////////////////////////////////// |
||
2708 | ////////////////////////////////////////////////////////////////////////////////// |
||
2709 | ////////////////////////////////////////////////////////////////////////////////// |
||
2710 | ////////////////////////////////////////////////////////////////////////////////// |
||
2711 | ////////////////////////////////////////////////////////////////////////////////// |
||
2712 | function _getGPOStables() { |
||
2713 | /////////////////////////////////// |
||
2714 | // GPOS - Glyph Positioning |
||
2715 | /////////////////////////////////// |
||
2716 | if (isset($this->tables["GPOS"])) { |
||
2717 | $this->mpdf->WriteHTML('<h1>GPOS Tables</h1>'); |
||
2718 | $ffeats = array(); |
||
2719 | $gpos_offset = $this->seek_table("GPOS"); |
||
2720 | $this->skip(4); |
||
2721 | $ScriptList_offset = $gpos_offset + $this->read_ushort(); |
||
2722 | $FeatureList_offset = $gpos_offset + $this->read_ushort(); |
||
2723 | $LookupList_offset = $gpos_offset + $this->read_ushort(); |
||
2724 | |||
2725 | // ScriptList |
||
2726 | $this->seek($ScriptList_offset ); |
||
2727 | $ScriptCount = $this->read_ushort(); |
||
2728 | View Code Duplication | for ($i=0;$i<$ScriptCount;$i++) { |
|
2729 | $ScriptTag = $this->read_tag(); // = "beng", "deva" etc. |
||
2730 | $ScriptTableOffset = $this->read_ushort(); |
||
2731 | $ffeats[$ScriptTag] = $ScriptList_offset + $ScriptTableOffset; |
||
2732 | } |
||
2733 | |||
2734 | // Script Table |
||
2735 | View Code Duplication | foreach($ffeats AS $t=>$o) { |
|
2736 | $ls = array(); |
||
2737 | $this->seek($o); |
||
2738 | $DefLangSys_offset = $this->read_ushort(); |
||
2739 | if ($DefLangSys_offset > 0) { |
||
2740 | $ls['DFLT'] = $DefLangSys_offset + $o; |
||
2741 | } |
||
2742 | $LangSysCount = $this->read_ushort(); |
||
2743 | for ($i=0;$i<$LangSysCount;$i++) { |
||
2744 | $LangTag = $this->read_tag(); // = |
||
2745 | $LangTableOffset = $this->read_ushort(); |
||
2746 | $ls[$LangTag] = $o + $LangTableOffset; |
||
2747 | } |
||
2748 | $ffeats[$t] = $ls; |
||
2749 | } |
||
2750 | |||
2751 | |||
2752 | // Get FeatureIndexList |
||
2753 | // LangSys Table - from first listed langsys |
||
2754 | View Code Duplication | foreach($ffeats AS $st=>$scripts) { |
|
2755 | foreach($scripts AS $t=>$o) { |
||
2756 | $FeatureIndex = array(); |
||
2757 | $langsystable_offset = $o; |
||
2758 | $this->seek($langsystable_offset); |
||
2759 | $LookUpOrder = $this->read_ushort(); //==NULL |
||
2760 | $ReqFeatureIndex = $this->read_ushort(); |
||
2761 | if ($ReqFeatureIndex != 0xFFFF) { $FeatureIndex[] = $ReqFeatureIndex ; } |
||
2762 | $FeatureCount = $this->read_ushort(); |
||
2763 | for ($i=0;$i<$FeatureCount;$i++) { |
||
2764 | $FeatureIndex[] = $this->read_ushort(); // = index of feature |
||
2765 | } |
||
2766 | $ffeats[$st][$t] = $FeatureIndex; |
||
2767 | } |
||
2768 | } |
||
2769 | //print_r($ffeats); exit; |
||
2770 | |||
2771 | |||
2772 | // Feauture List => LookupListIndex es |
||
2773 | $this->seek($FeatureList_offset ); |
||
2774 | $FeatureCount = $this->read_ushort(); |
||
2775 | $Feature = array(); |
||
2776 | View Code Duplication | for ($i=0;$i<$FeatureCount;$i++) { |
|
2777 | $Feature[$i] = array('tag' => $this->read_tag() ); |
||
2778 | $Feature[$i]['offset'] = $FeatureList_offset + $this->read_ushort(); |
||
2779 | } |
||
2780 | View Code Duplication | for ($i=0;$i<$FeatureCount;$i++) { |
|
2781 | $this->seek($Feature[$i]['offset']); |
||
2782 | $this->read_ushort(); // null |
||
2783 | $Feature[$i]['LookupCount'] = $Lookupcount = $this->read_ushort(); |
||
2784 | $Feature[$i]['LookupListIndex'] = array(); |
||
2785 | for ($c=0;$c<$Lookupcount;$c++) { |
||
2786 | $Feature[$i]['LookupListIndex'][] = $this->read_ushort(); |
||
2787 | } |
||
2788 | } |
||
2789 | |||
2790 | |||
2791 | View Code Duplication | foreach($ffeats AS $st=>$scripts) { |
|
2792 | foreach($scripts AS $t=>$o) { |
||
2793 | $FeatureIndex = $ffeats[$st][$t]; |
||
2794 | foreach($FeatureIndex AS $k=>$fi) { |
||
2795 | $ffeats[$st][$t][$k] = $Feature[$fi]; |
||
2796 | } |
||
2797 | } |
||
2798 | } |
||
2799 | //print_r($ffeats); exit; |
||
2800 | //===================================================================================== |
||
2801 | $gpos = array(); |
||
2802 | $GPOSScriptLang = array(); |
||
2803 | View Code Duplication | foreach($ffeats AS $st=>$scripts) { |
|
2804 | foreach($scripts AS $t=>$langsys) { |
||
2805 | $lg = array(); |
||
2806 | foreach($langsys AS $ft) { |
||
2807 | $lg[$ft['LookupListIndex'][0]] = $ft; |
||
2808 | } |
||
2809 | // list of Lookups in order they need to be run i.e. order listed in Lookup table |
||
2810 | ksort($lg); |
||
2811 | foreach($lg AS $ft) { |
||
2812 | $gpos[$st][$t][$ft['tag']] = $ft['LookupListIndex']; |
||
2813 | } |
||
2814 | if (!isset($GPOSScriptLang[$st])) { $GPOSScriptLang[$st] = ''; } |
||
2815 | $GPOSScriptLang[$st] .= $t.' '; |
||
2816 | } |
||
2817 | } |
||
2818 | View Code Duplication | if ($this->mode == 'summary') { |
|
2819 | $this->mpdf->WriteHTML('<h3>GPOS Scripts & Languages</h3>'); |
||
2820 | $html = ''; |
||
2821 | if (count($gpos)) { |
||
2822 | foreach ($gpos AS $st=>$g) { |
||
2823 | $html .= '<h5>'.$st.'</h5>'; |
||
2824 | foreach ($g AS $l=>$t) { |
||
2825 | $html .= '<div><a href="font_dump_OTL.php?script='.$st.'&lang='.$l.'">'.$l.'</a></b>: '; |
||
2826 | foreach ($t AS $tag=>$o) { |
||
2827 | $html .= $tag.' '; |
||
2828 | } |
||
2829 | $html .= '</div>'; |
||
2830 | } |
||
2831 | } |
||
2832 | } |
||
2833 | else { |
||
2834 | $html .= '<div>No entries in GPOS table.</div>'; |
||
2835 | } |
||
2836 | $this->mpdf->WriteHTML($html); |
||
2837 | $this->mpdf->WriteHTML('</div>'); |
||
2838 | return 0; |
||
2839 | } |
||
2840 | |||
2841 | |||
2842 | |||
2843 | //===================================================================================== |
||
2844 | // Get metadata and offsets for whole Lookup List table |
||
2845 | $this->seek($LookupList_offset ); |
||
2846 | $LookupCount = $this->read_ushort(); |
||
2847 | $Lookup = array(); |
||
2848 | $Offsets = array(); |
||
2849 | $SubtableCount = array(); |
||
2850 | View Code Duplication | for ($i=0;$i<$LookupCount;$i++) { |
|
2851 | $Offsets[$i] = $LookupList_offset + $this->read_ushort(); |
||
2852 | } |
||
2853 | View Code Duplication | for ($i=0;$i<$LookupCount;$i++) { |
|
2854 | $this->seek($Offsets[$i]); |
||
2855 | $Lookup[$i]['Type'] = $this->read_ushort(); |
||
2856 | $Lookup[$i]['Flag'] = $flag = $this->read_ushort(); |
||
2857 | $Lookup[$i]['SubtableCount'] = $SubtableCount[$i] = $this->read_ushort(); |
||
2858 | for ($c=0;$c<$SubtableCount[$i] ;$c++) { |
||
2859 | $Lookup[$i]['Subtables'][$c] = $Offsets[$i] + $this->read_ushort(); |
||
2860 | |||
2861 | } |
||
2862 | // MarkFilteringSet = Index (base 0) into GDEF mark glyph sets structure |
||
2863 | if (($flag & 0x0010) == 0x0010) { |
||
2864 | $Lookup[$i]['MarkFilteringSet'] = $this->read_ushort(); |
||
2865 | } |
||
2866 | // else { $Lookup[$i]['MarkFilteringSet'] = ''; } |
||
2867 | |||
2868 | // Lookup Type 9: Extension |
||
2869 | if ($Lookup[$i]['Type'] == 9) { |
||
2870 | // Overwrites new offset (32-bit) for each subtable, and a new lookup Type |
||
2871 | for ($c=0;$c<$SubtableCount[$i] ;$c++) { |
||
2872 | $this->seek($Lookup[$i]['Subtables'][$c]); |
||
2873 | $ExtensionPosFormat = $this->read_ushort(); |
||
2874 | $type = $this->read_ushort(); |
||
2875 | $Lookup[$i]['Subtables'][$c] = $Lookup[$i]['Subtables'][$c] + $this->read_ulong(); |
||
2876 | } |
||
2877 | $Lookup[$i]['Type'] = $type; |
||
2878 | } |
||
2879 | |||
2880 | } |
||
2881 | |||
2882 | |||
2883 | //===================================================================================== |
||
2884 | |||
2885 | $st = $this->mpdf->OTLscript; |
||
2886 | $t = $this->mpdf->OTLlang; |
||
2887 | $langsys = $gpos[$st][$t]; |
||
2888 | |||
2889 | |||
2890 | $lul = array(); // array of LookupListIndexes |
||
2891 | $tags = array(); // corresponding array of feature tags e.g. 'ccmp' |
||
2892 | if (count($langsys)) { |
||
2893 | foreach($langsys AS $tag=>$ft) { |
||
2894 | foreach($ft AS $ll) { |
||
2895 | $lul[$ll] = $tag; |
||
2896 | } |
||
2897 | } |
||
2898 | } |
||
2899 | ksort($lul); // Order the Lookups in the order they are in the GUSB table, regardless of Feature order |
||
2900 | $this->_getGPOSarray($Lookup, $lul, $st); |
||
2901 | //print_r($lul); exit; |
||
2902 | |||
2903 | |||
2904 | return array($GPOSScriptLang, $gpos, $Lookup); |
||
2905 | |||
2906 | } // end if GPOS |
||
2907 | } |
||
2908 | |||
2909 | ////////////////////////////////////////////////////////////////////////////////// |
||
2910 | |||
2911 | //===================================================================================== |
||
2912 | //===================================================================================== |
||
2913 | //===================================================================================== |
||
2914 | ///////////////////////////////////////////////////////////////////////////////////////// |
||
2915 | // GPOS functions |
||
2916 | function _getGPOSarray(&$Lookup, $lul, $scripttag, $level=1, $lcoverage='', $exB='', $exL='') { |
||
2917 | // Process (3) LookupList for specific Script-LangSys |
||
2918 | $html = ''; |
||
2919 | if ($level==1) { $html .= '<bookmark level="0" content="GPOS features">'; } |
||
2920 | foreach($lul AS $luli=>$tag) { |
||
2921 | $html .= '<div class="level'.$level.'">'; |
||
2922 | $html .= '<h5 class="level'.$level.'">'; |
||
2923 | if ($level==1) { $html .= '<bookmark level="1" content="'.$tag.' [#'.$luli.']">'; } |
||
2924 | $html .= 'Lookup #'.$luli.' [tag: <span style="color:#000066;">'.$tag.'</span>]</h5>'; |
||
2925 | $ignore = $this->_getGSUBignoreString($Lookup[$luli]['Flag'], $Lookup[$luli]['MarkFilteringSet']); |
||
2926 | if ($ignore) { $html .= '<div class="ignore">Ignoring: '.$ignore.'</div> '; } |
||
2927 | |||
2928 | $Type = $Lookup[$luli]['Type']; |
||
2929 | $Flag = $Lookup[$luli]['Flag']; |
||
2930 | if (($Flag & 0x0001) == 1) { $dir = 'RTL'; } |
||
2931 | else { $dir = 'LTR'; } |
||
2932 | |||
2933 | for ($c=0;$c<$Lookup[$luli]['SubtableCount'] ;$c++) { |
||
2934 | $html .= '<div class="subtable">Subtable #'.$c; |
||
2935 | if ($level==1) { $html .= '<bookmark level="2" content="Subtable #'.$c.'">'; } |
||
2936 | $html .= '</div>'; |
||
2937 | |||
2938 | // Lets start |
||
2939 | $subtable_offset = $Lookup[$luli]['Subtables'][$c]; |
||
2940 | $this->seek($subtable_offset); |
||
2941 | $PosFormat = $this->read_ushort(); |
||
2942 | |||
2943 | //////////////////////////////////////////////////////////////////////////////// |
||
2944 | // LookupType 1: Single adjustment Adjust position of a single glyph (e.g. SmallCaps/Sups/Subs) |
||
2945 | //////////////////////////////////////////////////////////////////////////////// |
||
2946 | if ($Lookup[$luli]['Type'] == 1) { |
||
2947 | $html .= '<div class="lookuptype">LookupType 1: Single adjustment [Format '.$PosFormat.']</div>'; |
||
2948 | //=========== |
||
2949 | // Format 1: |
||
2950 | //=========== |
||
2951 | if ($PosFormat==1) { |
||
2952 | $Coverage = $subtable_offset + $this->read_ushort(); |
||
2953 | $ValueFormat = $this->read_ushort(); |
||
2954 | $Value = $this->_getValueRecord($ValueFormat); |
||
2955 | |||
2956 | $this->seek($Coverage); |
||
2957 | $glyphs = $this->_getCoverage(); // Array of Hex Glyphs |
||
2958 | for($g=0;$g<count($glyphs);$g++) { |
||
2959 | if ($level==2 && strpos($lcoverage, $glyphs[$g])===false) { continue; } |
||
2960 | |||
2961 | $html .= '<div class="substitution">'; |
||
2962 | $html .= '<span class="unicode">'.$this->formatUni($glyphs[$g]).' </span> '; |
||
2963 | if ($level==2 && $exB) { $html .= $exB; } |
||
2964 | $html .= '<span class="unchanged"> '.$this->formatEntity($glyphs[$g]).'</span>'; |
||
2965 | if ($level==2 && $exL) { $html .= $exL; } |
||
2966 | $html .= ' » » '; |
||
2967 | if ($level==2 && $exB) { $html .= $exB; } |
||
2968 | $html .= '<span class="changed" style="font-feature-settings:\''.$tag.'\' 1;"> '.$this->formatEntity($glyphs[$g]).'</span>'; |
||
2969 | if ($level==2 && $exL) { $html .= $exL; } |
||
2970 | $html .= ' <span class="unicode">'; |
||
2971 | if ($Value['XPlacement']) { $html .= ' Xpl: '.$Value['XPlacement'].';'; } |
||
2972 | if ($Value['YPlacement']) { $html .= ' YPl: '.$Value['YPlacement'].';'; } |
||
2973 | if ($Value['XAdvance']) { $html .= ' Xadv: '.$Value['XAdvance']; } |
||
2974 | $html .= '</span>'; |
||
2975 | $html .= '</div>'; |
||
2976 | } |
||
2977 | |||
2978 | } |
||
2979 | //=========== |
||
2980 | // Format 2: |
||
2981 | //=========== |
||
2982 | else if ($PosFormat==2) { |
||
2983 | $Coverage = $subtable_offset + $this->read_ushort(); |
||
2984 | $ValueFormat = $this->read_ushort(); |
||
2985 | $ValueCount = $this->read_ushort(); |
||
2986 | $Values = array(); |
||
2987 | for($v=0;$v<$ValueCount;$v++) { |
||
2988 | $Values[] = $this->_getValueRecord($ValueFormat); |
||
2989 | } |
||
2990 | |||
2991 | $this->seek($Coverage); |
||
2992 | $glyphs = $this->_getCoverage(); // Array of Hex Glyphs |
||
2993 | |||
2994 | for($g=0;$g<count($glyphs);$g++) { |
||
2995 | if ($level==2 && strpos($lcoverage, $glyphs[$g])===false) { continue; } |
||
2996 | $Value = $Values[$g]; |
||
2997 | |||
2998 | $html .= '<div class="substitution">'; |
||
2999 | $html .= '<span class="unicode">'.$this->formatUni($glyphs[$g]).' </span> '; |
||
3000 | if ($level==2 && $exB) { $html .= $exB; } |
||
3001 | $html .= '<span class="unchanged"> '.$this->formatEntity($glyphs[$g]).'</span>'; |
||
3002 | if ($level==2 && $exL) { $html .= $exL; } |
||
3003 | $html .= ' » » '; |
||
3004 | if ($level==2 && $exB) { $html .= $exB; } |
||
3005 | $html .= '<span class="changed" style="font-feature-settings:\''.$tag.'\' 1;"> '.$this->formatEntity($glyphs[$g]).'</span>'; |
||
3006 | if ($level==2 && $exL) { $html .= $exL; } |
||
3007 | $html .= ' <span class="unicode">'; |
||
3008 | if ($Value['XPlacement']) { $html .= ' Xpl: '.$Value['XPlacement'].';'; } |
||
3009 | if ($Value['YPlacement']) { $html .= ' YPl: '.$Value['YPlacement'].';'; } |
||
3010 | if ($Value['XAdvance']) { $html .= ' Xadv: '.$Value['XAdvance']; } |
||
3011 | $html .= '</span>'; |
||
3012 | $html .= '</div>'; |
||
3013 | } |
||
3014 | } |
||
3015 | |||
3016 | |||
3017 | |||
3018 | } |
||
3019 | //////////////////////////////////////////////////////////////////////////////// |
||
3020 | // LookupType 2: Pair adjustment Adjust position of a pair of glyphs (Kerning) |
||
3021 | //////////////////////////////////////////////////////////////////////////////// |
||
3022 | else if ($Lookup[$luli]['Type'] == 2) { |
||
3023 | $html .= '<div class="lookuptype">LookupType 2: Pair adjustment e.g. Kerning [Format '.$PosFormat.']</div>'; |
||
3024 | $Coverage = $subtable_offset + $this->read_ushort(); |
||
3025 | $ValueFormat1 = $this->read_ushort(); |
||
3026 | $ValueFormat2 = $this->read_ushort(); |
||
3027 | //=========== |
||
3028 | // Format 1: |
||
3029 | //=========== |
||
3030 | if ($PosFormat==1) { |
||
3031 | $PairSetCount = $this->read_ushort(); |
||
3032 | $PairSetOffset = array(); |
||
3033 | View Code Duplication | for($p=0;$p<$PairSetCount;$p++) { |
|
3034 | $PairSetOffset[] = $subtable_offset + $this->read_ushort(); |
||
3035 | } |
||
3036 | $this->seek($Coverage); |
||
3037 | $glyphs = $this->_getCoverage(); // Array of Hex Glyphs |
||
3038 | for($p=0;$p<$PairSetCount;$p++) { |
||
3039 | if ($level==2 && strpos($lcoverage, $glyphs[$p])===false) { continue; } |
||
3040 | $this->seek($PairSetOffset[$p]); |
||
3041 | // First Glyph = $glyphs[$p] |
||
3042 | |||
3043 | // Takes too long e.g. Calibri font - just list kerning pairs with this: |
||
3044 | $html .= '<div class="glyphs">'; |
||
3045 | $html .= '<span class="unchanged"> '.$this->formatEntity($glyphs[$p]).' </span>'; |
||
3046 | |||
3047 | //PairSet table |
||
3048 | $PairValueCount = $this->read_ushort(); |
||
3049 | for($pv=0;$pv<$PairValueCount;$pv++) { |
||
3050 | //PairValueRecord |
||
3051 | $gid = $this->read_ushort(); |
||
3052 | $SecondGlyph = unicode_hex($this->glyphToChar[$gid][0]); |
||
3053 | $Value1 = $this->_getValueRecord($ValueFormat1); |
||
3054 | $Value2 = $this->_getValueRecord($ValueFormat2); |
||
3055 | |||
3056 | // If RTL pairs, GPOS declares a XPlacement e.g. -180 for an XAdvance of -180 to take |
||
3057 | // account of direction. mPDF does not need the XPlacement adjustment |
||
3058 | if ($dir=='RTL' && $Value1['XPlacement']) { |
||
3059 | $Value1['XPlacement'] -= $Value1['XAdvance']; |
||
3060 | } |
||
3061 | |||
3062 | View Code Duplication | if($ValueFormat2) { |
|
3063 | // If RTL pairs, GPOS declares a XPlacement e.g. -180 for an XAdvance of -180 to take |
||
3064 | // account of direction. mPDF does not need the XPlacement adjustment |
||
3065 | if ($dir=='RTL' && $Value2['XPlacement'] && $Value2['XAdvance']) { |
||
3066 | $Value2['XPlacement'] -= $Value2['XAdvance']; |
||
3067 | } |
||
3068 | } |
||
3069 | |||
3070 | $html .= ' '.$this->formatEntity($SecondGlyph).' '; |
||
3071 | |||
3072 | /* |
||
3073 | $html .= '<div class="substitution">'; |
||
3074 | $html .= '<span class="unicode">'.$this->formatUni($glyphs[$p]).' </span> '; |
||
3075 | if ($level==2 && $exB) { $html .= $exB; } |
||
3076 | $html .= '<span class="unchanged"> '.$this->formatEntity($glyphs[$p]).$this->formatEntity($SecondGlyph).'</span>'; |
||
3077 | if ($level==2 && $exL) { $html .= $exL; } |
||
3078 | $html .= ' » » '; |
||
3079 | if ($level==2 && $exB) { $html .= $exB; } |
||
3080 | $html .= '<span class="changed" style="font-feature-settings:\''.$tag.'\' 1;"> '.$this->formatEntity($glyphs[$p]).$this->formatEntity($SecondGlyph).'</span>'; |
||
3081 | if ($level==2 && $exL) { $html .= $exL; } |
||
3082 | $html .= ' <span class="unicode">'; |
||
3083 | if ($Value1['XPlacement']) { $html .= ' Xpl[1]: '.$Value1['XPlacement'].';'; } |
||
3084 | if ($Value1['YPlacement']) { $html .= ' YPl[1]: '.$Value1['YPlacement'].';'; } |
||
3085 | if ($Value1['XAdvance']) { $html .= ' Xadv[1]: '.$Value1['XAdvance']; } |
||
3086 | if ($Value2['XPlacement']) { $html .= ' Xpl[2]: '.$Value2['XPlacement'].';'; } |
||
3087 | if ($Value2['YPlacement']) { $html .= ' YPl[2]: '.$Value2['YPlacement'].';'; } |
||
3088 | if ($Value2['XAdvance']) { $html .= ' Xadv[2]: '.$Value2['XAdvance']; } |
||
3089 | $html .= '</span>'; |
||
3090 | $html .= '</div>'; |
||
3091 | */ |
||
3092 | |||
3093 | } |
||
3094 | $html .= '</div>'; |
||
3095 | } |
||
3096 | } |
||
3097 | //=========== |
||
3098 | // Format 2: |
||
3099 | //=========== |
||
3100 | else if ($PosFormat==2) { |
||
3101 | $ClassDef1 = $subtable_offset + $this->read_ushort(); |
||
3102 | $ClassDef2 = $subtable_offset + $this->read_ushort(); |
||
3103 | $Class1Count = $this->read_ushort(); |
||
3104 | $Class2Count = $this->read_ushort(); |
||
3105 | |||
3106 | $sizeOfPair = ( 2*$this->count_bits($ValueFormat1) ) + ( 2*$this->count_bits($ValueFormat2) ); |
||
3107 | $sizeOfValueRecords = $Class1Count * $Class2Count * $sizeOfPair; |
||
3108 | |||
3109 | |||
3110 | // NB Class1Count includes Class 0 even though it is not defined by $ClassDef1 |
||
3111 | // i.e. Class1Count = 5; Class1 will contain array(indices 1-4); |
||
3112 | $Class1 = $this->_getClassDefinitionTable($ClassDef1); |
||
3113 | $Class2 = $this->_getClassDefinitionTable($ClassDef2); |
||
3114 | |||
3115 | $this->seek($subtable_offset + 16); |
||
3116 | |||
3117 | for($i=0;$i<$Class1Count;$i++) { |
||
3118 | for($j=0;$j<$Class2Count;$j++) { |
||
3119 | $Value1 = $this->_getValueRecord($ValueFormat1); |
||
3120 | $Value2 = $this->_getValueRecord($ValueFormat2); |
||
3121 | |||
3122 | // If RTL pairs, GPOS declares a XPlacement e.g. -180 for an XAdvance of -180 |
||
3123 | // of direction. mPDF does not need the XPlacement adjustment |
||
3124 | if ($dir=='RTL' && $Value1['XPlacement'] && $Value1['XAdvance']) { |
||
3125 | $Value1['XPlacement'] -= $Value1['XAdvance']; |
||
3126 | } |
||
3127 | View Code Duplication | if($ValueFormat2) { |
|
3128 | if ($dir=='RTL' && $Value2['XPlacement'] && $Value2['XAdvance']) { |
||
3129 | $Value2['XPlacement'] -= $Value2['XAdvance']; |
||
3130 | } |
||
3131 | } |
||
3132 | |||
3133 | |||
3134 | for($c1=0;$c1<count($Class1[$i]);$c1++) { |
||
3135 | |||
3136 | $FirstGlyph = $Class1[$i][$c1]; |
||
3137 | if ($level==2 && strpos($lcoverage, $FirstGlyph)===false) { |
||
3138 | continue; |
||
3139 | } |
||
3140 | |||
3141 | |||
3142 | for($c2=0;$c2<count($Class2[$j]);$c2++) { |
||
3143 | $SecondGlyph = $Class2[$j][$c2]; |
||
3144 | |||
3145 | |||
3146 | if (!$Value1['XPlacement'] && !$Value1['YPlacement'] && !$Value1['XAdvance'] && !$Value2['XPlacement'] && !$Value2['YPlacement'] && !$Value2['XAdvance']) { continue; } |
||
3147 | |||
3148 | |||
3149 | $html .= '<div class="substitution">'; |
||
3150 | $html .= '<span class="unicode">'.$this->formatUni($FirstGlyph).' </span> '; |
||
3151 | if ($level==2 && $exB) { $html .= $exB; } |
||
3152 | $html .= '<span class="unchanged"> '.$this->formatEntity($FirstGlyph).$this->formatEntity($SecondGlyph).'</span>'; |
||
3153 | if ($level==2 && $exL) { $html .= $exL; } |
||
3154 | $html .= ' » » '; |
||
3155 | if ($level==2 && $exB) { $html .= $exB; } |
||
3156 | $html .= '<span class="changed" style="font-feature-settings:\''.$tag.'\' 1;"> '.$this->formatEntity($FirstGlyph).$this->formatEntity($SecondGlyph).'</span>'; |
||
3157 | if ($level==2 && $exL) { $html .= $exL; } |
||
3158 | $html .= ' <span class="unicode">'; |
||
3159 | if ($Value1['XPlacement']) { $html .= ' Xpl[1]: '.$Value1['XPlacement'].';'; } |
||
3160 | if ($Value1['YPlacement']) { $html .= ' YPl[1]: '.$Value1['YPlacement'].';'; } |
||
3161 | if ($Value1['XAdvance']) { $html .= ' Xadv[1]: '.$Value1['XAdvance']; } |
||
3162 | if ($Value2['XPlacement']) { $html .= ' Xpl[2]: '.$Value2['XPlacement'].';'; } |
||
3163 | if ($Value2['YPlacement']) { $html .= ' YPl[2]: '.$Value2['YPlacement'].';'; } |
||
3164 | if ($Value2['XAdvance']) { $html .= ' Xadv[2]: '.$Value2['XAdvance']; } |
||
3165 | $html .= '</span>'; |
||
3166 | $html .= '</div>'; |
||
3167 | |||
3168 | } |
||
3169 | } |
||
3170 | } |
||
3171 | |||
3172 | } |
||
3173 | } |
||
3174 | } |
||
3175 | //////////////////////////////////////////////////////////////////////////////// |
||
3176 | // LookupType 3: Cursive attachment Attach cursive glyphs |
||
3177 | //////////////////////////////////////////////////////////////////////////////// |
||
3178 | else if ($Lookup[$luli]['Type'] == 3) { |
||
3179 | $html .= '<div class="lookuptype">LookupType 3: Cursive attachment </div>'; |
||
3180 | $Coverage = $subtable_offset + $this->read_ushort(); |
||
3181 | $EntryExitCount = $this->read_ushort(); |
||
3182 | $EntryAnchors = array(); |
||
3183 | $ExitAnchors = array(); |
||
3184 | for($i=0;$i<$EntryExitCount;$i++) { |
||
3185 | $EntryAnchors[$i] = $this->read_ushort(); |
||
3186 | $ExitAnchors[$i] = $this->read_ushort(); |
||
3187 | } |
||
3188 | |||
3189 | $this->seek($Coverage); |
||
3190 | $Glyphs = $this->_getCoverage(); |
||
3191 | for($i=0;$i<$EntryExitCount;$i++) { |
||
3192 | // Need default XAdvance for glyph |
||
3193 | $pdfWidth = $this->mpdf->_getCharWidth($this->mpdf->fonts[$this->fontkey]['cw'], hexdec($Glyphs[$i])); |
||
3194 | $EntryAnchor = $EntryAnchors[$i] ; |
||
3195 | $ExitAnchor = $ExitAnchors[$i] ; |
||
3196 | $html .= '<div class="glyphs">'; |
||
3197 | $html .= '<span class="unchanged">'.$this->formatEntity($Glyphs[$i]).' </span> '; |
||
3198 | $html .= '<span class="unicode"> '.$this->formatUni($Glyphs[$i]).' => '; |
||
3199 | |||
3200 | View Code Duplication | if ($EntryAnchor != 0) { |
|
3201 | $EntryAnchor += $subtable_offset; |
||
3202 | list($x,$y) = $this->_getAnchorTable($EntryAnchor); |
||
3203 | if ($dir == 'RTL') { |
||
3204 | if (round($pdfWidth) == round($x * 1000/ $this->mpdf->fonts[$this->fontkey]['desc']['unitsPerEm']) ) { |
||
3205 | $x = 0; |
||
3206 | } |
||
3207 | else { $x = $x - ($pdfWidth * $this->mpdf->fonts[$this->fontkey]['desc']['unitsPerEm']/1000); } |
||
3208 | } |
||
3209 | $html .= " Entry X: ".$x." Y: ".$y."; "; |
||
3210 | } |
||
3211 | View Code Duplication | if ($ExitAnchor != 0) { |
|
3212 | $ExitAnchor += $subtable_offset; |
||
3213 | list($x,$y) = $this->_getAnchorTable($ExitAnchor); |
||
3214 | if ($dir == 'LTR') { |
||
3215 | if (round($pdfWidth) == round($x * 1000/ $this->mpdf->fonts[$this->fontkey]['desc']['unitsPerEm']) ) { |
||
3216 | $x = 0; |
||
3217 | } |
||
3218 | else { $x = $x - ($pdfWidth * $this->mpdf->fonts[$this->fontkey]['desc']['unitsPerEm']/1000); } |
||
3219 | } |
||
3220 | $html .= " Exit X: ".$x." Y: ".$y."; "; |
||
3221 | } |
||
3222 | |||
3223 | |||
3224 | $html .= '</span></div>'; |
||
3225 | } |
||
3226 | |||
3227 | } |
||
3228 | //////////////////////////////////////////////////////////////////////////////// |
||
3229 | // LookupType 4: MarkToBase attachment Attach a combining mark to a base glyph |
||
3230 | //////////////////////////////////////////////////////////////////////////////// |
||
3231 | else if ($Lookup[$luli]['Type'] == 4) { |
||
3232 | $html .= '<div class="lookuptype">LookupType 4: MarkToBase attachment </div>'; |
||
3233 | $MarkCoverage = $subtable_offset + $this->read_ushort(); |
||
3234 | $BaseCoverage = $subtable_offset + $this->read_ushort(); |
||
3235 | |||
3236 | $this->seek($MarkCoverage); |
||
3237 | $MarkGlyphs = $this->_getCoverage(); |
||
3238 | |||
3239 | $this->seek($BaseCoverage); |
||
3240 | $BaseGlyphs = $this->_getCoverage(); |
||
3241 | |||
3242 | $firstMark = ''; |
||
3243 | $html .= '<div class="glyphs">Marks: '; |
||
3244 | View Code Duplication | for($i=0;$i<count($MarkGlyphs);$i++) { |
|
3245 | if ($level==2 && strpos($lcoverage, $MarkGlyphs[$i])===false) { continue; } |
||
3246 | else { |
||
3247 | if (!$firstMark) { $firstMark = $MarkGlyphs[$i]; } |
||
3248 | } |
||
3249 | $html .= ' '.$this->formatEntity($MarkGlyphs[$i]).' '; |
||
3250 | } |
||
3251 | $html .= '</div>'; |
||
3252 | if (!$firstMark) { return; } |
||
3253 | |||
3254 | $html .= '<div class="glyphs">Bases: '; |
||
3255 | View Code Duplication | for($j=0;$j<count($BaseGlyphs);$j++) { |
|
3256 | $html .= ' '.$this->formatEntity($BaseGlyphs[$j]).' '; |
||
3257 | } |
||
3258 | $html .= '</div>'; |
||
3259 | |||
3260 | // Example |
||
3261 | $html .= '<div class="glyphs" style="font-feature-settings:\''.$tag.'\' 1;">Example(s): '; |
||
3262 | View Code Duplication | for ($j=0;$j<min(count($BaseGlyphs),20);$j++) { |
|
3263 | $html .= ' '.$this->formatEntity($BaseGlyphs[$j]).$this->formatEntity($firstMark,true).' '; |
||
3264 | } |
||
3265 | $html .= '</div>'; |
||
3266 | |||
3267 | |||
3268 | } |
||
3269 | //////////////////////////////////////////////////////////////////////////////// |
||
3270 | // LookupType 5: MarkToLigature attachment Attach a combining mark to a ligature |
||
3271 | //////////////////////////////////////////////////////////////////////////////// |
||
3272 | else if ($Lookup[$luli]['Type'] == 5) { |
||
3273 | $html .= '<div class="lookuptype">LookupType 5: MarkToLigature attachment </div>'; |
||
3274 | $MarkCoverage = $subtable_offset + $this->read_ushort(); |
||
3275 | //$MarkCoverage is already set in $lcoverage 00065|00073 etc |
||
3276 | $LigatureCoverage = $subtable_offset + $this->read_ushort(); |
||
3277 | $ClassCount = $this->read_ushort(); // Number of classes defined for marks = Number of mark glyphs in the MarkCoverage table |
||
3278 | $MarkArray = $subtable_offset + $this->read_ushort(); // Offset to MarkArray table |
||
3279 | $LigatureArray = $subtable_offset + $this->read_ushort(); // Offset to LigatureArray table |
||
3280 | |||
3281 | $this->seek($MarkCoverage); |
||
3282 | $MarkGlyphs = $this->_getCoverage(); |
||
3283 | $this->seek($LigatureCoverage); |
||
3284 | $LigatureGlyphs = $this->_getCoverage(); |
||
3285 | |||
3286 | $firstMark = ''; |
||
3287 | $html .= '<div class="glyphs">Marks: <span class="unchanged">'; |
||
3288 | $MarkRecord = array(); |
||
3289 | for ($i=0;$i<count($MarkGlyphs);$i++) { |
||
3290 | if ($level==2 && strpos($lcoverage, $MarkGlyphs[$i])===false) { continue; } |
||
3291 | else { |
||
3292 | if (!$firstMark) { $firstMark = $MarkGlyphs[$i]; } |
||
3293 | } |
||
3294 | // Get the relevant MarkRecord |
||
3295 | $MarkRecord[$i] = $this->_getMarkRecord($MarkArray, $i); |
||
3296 | //Mark Class is = $MarkRecord[$i]['Class'] |
||
3297 | $html .= ' '.$this->formatEntity($MarkGlyphs[$i]).' '; |
||
3298 | } |
||
3299 | $html .= '</span></div>'; |
||
3300 | if (!$firstMark) { return; } |
||
3301 | |||
3302 | $this->seek($LigatureArray); |
||
3303 | $LigatureCount = $this->read_ushort(); |
||
3304 | $LigatureAttach = array(); |
||
3305 | $html .= '<div class="glyphs">Ligatures: <span class="unchanged">'; |
||
3306 | for ($j=0;$j<count($LigatureGlyphs);$j++) { |
||
3307 | // Get the relevant LigatureRecord |
||
3308 | $LigatureAttach[$j] = $LigatureArray + $this->read_ushort(); |
||
3309 | $html .= ' '.$this->formatEntity($LigatureGlyphs[$j]).' '; |
||
3310 | } |
||
3311 | $html .= '</span></div>'; |
||
3312 | |||
3313 | /* |
||
3314 | for ($i=0;$i<count($MarkGlyphs);$i++) { |
||
3315 | $html .= '<div class="glyphs">'; |
||
3316 | $html .= '<span class="unchanged">'.$this->formatEntity($MarkGlyphs[$i]).'</span>'; |
||
3317 | |||
3318 | for ($j=0;$j<count($LigatureGlyphs);$j++) { |
||
3319 | $this->seek($LigatureAttach[$j]); |
||
3320 | $ComponentCount = $this->read_ushort(); |
||
3321 | $html .= '<span class="unchanged">'.$this->formatEntity($LigatureGlyphs[$j]).'</span>'; |
||
3322 | $offsets = array(); |
||
3323 | for ($comp=0;$comp<$ComponentCount;$comp++) { |
||
3324 | // ComponentRecords |
||
3325 | for ($class=0;$class<$ClassCount;$class++) { |
||
3326 | $offset = $this->read_ushort(); |
||
3327 | if ($offset!= 0 && $class == $MarkRecord[$i]['Class']) { |
||
3328 | |||
3329 | $html .= ' ['.$comp.'] '; |
||
3330 | |||
3331 | } |
||
3332 | } |
||
3333 | } |
||
3334 | } |
||
3335 | $html .= '</span></div>'; |
||
3336 | } |
||
3337 | */ |
||
3338 | |||
3339 | |||
3340 | } |
||
3341 | //////////////////////////////////////////////////////////////////////////////// |
||
3342 | // LookupType 6: MarkToMark attachment Attach a combining mark to another mark |
||
3343 | //////////////////////////////////////////////////////////////////////////////// |
||
3344 | else if ($Lookup[$luli]['Type'] == 6) { |
||
3345 | $html .= '<div class="lookuptype">LookupType 6: MarkToMark attachment </div>'; |
||
3346 | $Mark1Coverage = $subtable_offset + $this->read_ushort(); // Combining Mark |
||
3347 | //$Mark1Coverage is already set in $LuCoverage 0065|0073 etc |
||
3348 | $Mark2Coverage = $subtable_offset + $this->read_ushort(); // Base Mark |
||
3349 | $ClassCount = $this->read_ushort(); // Number of classes defined for marks = No. of Combining mark1 glyphs in the MarkCoverage table |
||
3350 | $this->seek($Mark1Coverage); |
||
3351 | $Mark1Glyphs = $this->_getCoverage(); |
||
3352 | $this->seek($Mark2Coverage); |
||
3353 | $Mark2Glyphs = $this->_getCoverage(); |
||
3354 | |||
3355 | |||
3356 | $firstMark = ''; |
||
3357 | $html .= '<div class="glyphs">Marks: <span class="unchanged">'; |
||
3358 | View Code Duplication | for($i=0;$i<count($Mark1Glyphs);$i++) { |
|
3359 | if ($level==2 && strpos($lcoverage, $Mark1Glyphs[$i])===false) { continue; } |
||
3360 | else { |
||
3361 | if (!$firstMark) { $firstMark = $Mark1Glyphs[$i]; } |
||
3362 | } |
||
3363 | $html .= ' '.$this->formatEntity($Mark1Glyphs[$i]).' '; |
||
3364 | } |
||
3365 | $html .= '</span></div>'; |
||
3366 | |||
3367 | if ($firstMark) { |
||
3368 | |||
3369 | $html .= '<div class="glyphs">Bases: <span class="unchanged">'; |
||
3370 | View Code Duplication | for($j=0;$j<count($Mark2Glyphs);$j++) { |
|
3371 | $html .= ' '.$this->formatEntity($Mark2Glyphs[$j]).' '; |
||
3372 | } |
||
3373 | $html .= '</span></div>'; |
||
3374 | |||
3375 | // Example |
||
3376 | $html .= '<div class="glyphs" style="font-feature-settings:\''.$tag.'\' 1;">Example(s): <span class="changed">'; |
||
3377 | View Code Duplication | for ($j=0;$j<min(count($Mark2Glyphs),20);$j++) { |
|
3378 | $html .= ' '.$this->formatEntity($Mark2Glyphs[$j]).$this->formatEntity($firstMark,true).' '; |
||
3379 | } |
||
3380 | $html .= '</span></div>'; |
||
3381 | } |
||
3382 | |||
3383 | } |
||
3384 | //////////////////////////////////////////////////////////////////////////////// |
||
3385 | // LookupType 7: Context positioning Position one or more glyphs in context |
||
3386 | //////////////////////////////////////////////////////////////////////////////// |
||
3387 | else if ($Lookup[$luli]['Type'] == 7) { |
||
3388 | $html .= '<div class="lookuptype">LookupType 7: Context positioning [Format '.$PosFormat.']</div>'; |
||
3389 | //=========== |
||
3390 | // Format 1: |
||
3391 | //=========== |
||
3392 | if ($PosFormat==1) { |
||
3393 | die("GPOS Lookup Type ".$Type." Format ".$PosFormat." not YET TESTED."); |
||
3394 | } |
||
3395 | //=========== |
||
3396 | // Format 2: |
||
3397 | //=========== |
||
3398 | else if ($PosFormat==2) { |
||
3399 | die("GPOS Lookup Type ".$Type." Format ".$PosFormat." not YET TESTED."); |
||
3400 | } |
||
3401 | //=========== |
||
3402 | // Format 3: |
||
3403 | //=========== |
||
3404 | View Code Duplication | else if ($PosFormat==3) { |
|
3405 | die("GPOS Lookup Type ".$Type." Format ".$PosFormat." not YET TESTED."); |
||
3406 | } |
||
3407 | else { |
||
3408 | die("GPOS Lookup Type ".$Type.", Format ".$PosFormat." not supported."); |
||
3409 | } |
||
3410 | } |
||
3411 | //////////////////////////////////////////////////////////////////////////////// |
||
3412 | // LookupType 8: Chained Context positioning Position one or more glyphs in chained context |
||
3413 | //////////////////////////////////////////////////////////////////////////////// |
||
3414 | else if ($Lookup[$luli]['Type'] == 8) { |
||
3415 | $html .= '<div class="lookuptype">LookupType 8: Chained Context positioning [Format '.$PosFormat.']</div>'; |
||
3416 | //=========== |
||
3417 | // Format 1: |
||
3418 | //=========== |
||
3419 | if ($PosFormat==1) { |
||
3420 | die("GPOS Lookup Type ".$Type." Format ".$PosFormat." not TESTED YET."); |
||
3421 | } |
||
3422 | //=========== |
||
3423 | // Format 2: |
||
3424 | //=========== |
||
3425 | else if ($PosFormat==2) { |
||
3426 | $html .= '<div>GPOS Lookup Type 8: Format 2 not yet supported in OTL dump</div>'; |
||
3427 | continue; |
||
3428 | /* NB When developing - cf. GSUB 6.2 */ |
||
3429 | die("GPOS Lookup Type ".$Type." Format ".$PosFormat." not TESTED YET."); |
||
3430 | } |
||
3431 | //=========== |
||
3432 | // Format 3: |
||
3433 | //=========== |
||
3434 | else if ($PosFormat==3) { |
||
3435 | $BacktrackGlyphCount = $this->read_ushort(); |
||
3436 | $CoverageBacktrackOffset = array(); |
||
3437 | View Code Duplication | for ($b=0;$b<$BacktrackGlyphCount;$b++) { |
|
3438 | $CoverageBacktrackOffset[] = $subtable_offset + $this->read_ushort(); // in glyph sequence order |
||
3439 | } |
||
3440 | $InputGlyphCount = $this->read_ushort(); |
||
3441 | $CoverageInputOffset = array(); |
||
3442 | View Code Duplication | for ($b=0;$b<$InputGlyphCount;$b++) { |
|
3443 | $CoverageInputOffset[] = $subtable_offset + $this->read_ushort(); // in glyph sequence order |
||
3444 | } |
||
3445 | $LookaheadGlyphCount = $this->read_ushort(); |
||
3446 | $CoverageLookaheadOffset = array(); |
||
3447 | View Code Duplication | for ($b=0;$b<$LookaheadGlyphCount;$b++) { |
|
3448 | $CoverageLookaheadOffset[] = $subtable_offset + $this->read_ushort(); // in glyph sequence order |
||
3449 | } |
||
3450 | $PosCount = $this->read_ushort(); |
||
3451 | |||
3452 | $PosLookupRecord = array(); |
||
3453 | for ($p=0;$p<$PosCount;$p++) { |
||
3454 | // PosLookupRecord |
||
3455 | $PosLookupRecord[$p]['SequenceIndex'] = $this->read_ushort(); |
||
3456 | $PosLookupRecord[$p]['LookupListIndex'] = $this->read_ushort(); |
||
3457 | } |
||
3458 | |||
3459 | $backtrackGlyphs = array(); |
||
3460 | View Code Duplication | for ($b=0;$b<$BacktrackGlyphCount;$b++) { |
|
3461 | $this->seek($CoverageBacktrackOffset[$b]); |
||
3462 | $backtrackGlyphs[$b] = implode('|',$this->_getCoverage()); |
||
3463 | } |
||
3464 | $inputGlyphs = array(); |
||
3465 | View Code Duplication | for ($b=0;$b<$InputGlyphCount;$b++) { |
|
3466 | $this->seek($CoverageInputOffset[$b]); |
||
3467 | $inputGlyphs[$b] = implode('|',$this->_getCoverage()); |
||
3468 | } |
||
3469 | $lookaheadGlyphs = array(); |
||
3470 | View Code Duplication | for ($b=0;$b<$LookaheadGlyphCount;$b++) { |
|
3471 | $this->seek($CoverageLookaheadOffset[$b]); |
||
3472 | $lookaheadGlyphs[$b] = implode('|',$this->_getCoverage()); |
||
3473 | } |
||
3474 | |||
3475 | $exampleB = array(); |
||
3476 | $exampleI = array(); |
||
3477 | $exampleL = array(); |
||
3478 | $html .= '<div class="context">CONTEXT: '; |
||
3479 | View Code Duplication | for ($ff=count($backtrackGlyphs)-1;$ff>=0;$ff--) { |
|
3480 | $html .= '<div>Backtrack #'.$ff.': <span class="unicode">'.$this->formatUniStr($backtrackGlyphs[$ff]).'</span></div>'; |
||
3481 | $exampleB[] = $this->formatEntityFirst($backtrackGlyphs[$ff]); |
||
3482 | } |
||
3483 | View Code Duplication | for ($ff=0;$ff<count($inputGlyphs);$ff++) { |
|
0 ignored issues
–
show
It seems like you are calling the size function
count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.
If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration: for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}
// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
|
|||
3484 | $html .= '<div>Input #'.$ff.': <span class="unchanged"> '.$this->formatEntityStr($inputGlyphs[$ff]).' </span></div>'; |
||
3485 | $exampleI[] = $this->formatEntityFirst($inputGlyphs[$ff]); |
||
3486 | } |
||
3487 | View Code Duplication | for ($ff=0;$ff<count($lookaheadGlyphs);$ff++) { |
|
3488 | $html .= '<div>Lookahead #'.$ff.': <span class="unicode">'.$this->formatUniStr($lookaheadGlyphs[$ff]).'</span></div>'; |
||
3489 | $exampleL[] = $this->formatEntityFirst($lookaheadGlyphs[$ff]); |
||
3490 | } |
||
3491 | $html .= '</div>'; |
||
3492 | |||
3493 | |||
3494 | for ($p=0;$p<$PosCount;$p++) { |
||
3495 | $lup = $PosLookupRecord[$p]['LookupListIndex'] ; |
||
3496 | $seqIndex = $PosLookupRecord[$p]['SequenceIndex'] ; |
||
3497 | |||
3498 | // GENERATE exampleB[n] exampleI[<seqIndex] .... exampleI[>seqIndex] exampleL[n] |
||
3499 | $exB = ''; |
||
3500 | $exL = ''; |
||
3501 | if (count($exampleB)) { $exB .= '<span class="backtrack">'.implode('‍',$exampleB).'</span>'; } |
||
3502 | |||
3503 | View Code Duplication | if ($seqIndex>0) { |
|
3504 | $exB .= '<span class="inputother">'; |
||
3505 | for($ip=0;$ip<$seqIndex;$ip++) { |
||
3506 | $exB .= $exampleI[$ip].'‍'; |
||
3507 | } |
||
3508 | $exB .= '</span>'; |
||
3509 | } |
||
3510 | |||
3511 | View Code Duplication | if (count($inputGlyphs)>($seqIndex+1)) { |
|
3512 | $exL .= '<span class="inputother">'; |
||
3513 | for($ip=$seqIndex+1;$ip<count($inputGlyphs);$ip++) { |
||
3514 | $exL .= '‍'.$exampleI[$ip]; |
||
3515 | } |
||
3516 | $exL .= '</span>'; |
||
3517 | } |
||
3518 | |||
3519 | if (count($exampleL)) { $exL .= '<span class="lookahead">'.implode('‍',$exampleL).'</span>'; } |
||
3520 | |||
3521 | $html .= '<div class="sequenceIndex">Substitution Position: '.$seqIndex.'</div>'; |
||
3522 | |||
3523 | $lul2 = array($lup=>$tag); |
||
3524 | |||
3525 | // Only apply if the (first) 'Replace' glyph from the |
||
3526 | // Lookup list is in the [inputGlyphs] at ['SequenceIndex'] |
||
3527 | // Pass $inputGlyphs[$seqIndex] e.g. 00636|00645|00656 |
||
3528 | // to level 2 and only apply if first Replace glyph is in this list |
||
3529 | $html .= $this->_getGPOSarray($Lookup, $lul2, $scripttag, 2, $inputGlyphs[$seqIndex], $exB, $exL); |
||
3530 | |||
3531 | } |
||
3532 | } |
||
3533 | } |
||
3534 | |||
3535 | } |
||
3536 | $html .= '</div>'; |
||
3537 | } |
||
3538 | if ($level ==1) { $this->mpdf->WriteHTML($html); } |
||
3539 | else { return $html; } |
||
3540 | //print_r($Lookup); exit; |
||
3541 | } |
||
3542 | //===================================================================================== |
||
3543 | //===================================================================================== |
||
3544 | // GPOS FUNCTIONS |
||
3545 | //===================================================================================== |
||
3546 | |||
3547 | function count_bits($n) { |
||
3548 | for ($c=0; $n; $c++) { |
||
3549 | $n &= $n - 1; // clear the least significant bit set |
||
3550 | } |
||
3551 | return $c; |
||
3552 | } |
||
3553 | |||
3554 | View Code Duplication | function _getValueRecord($ValueFormat) { // Common ValueRecord for GPOS |
|
3555 | // Only returns 3 possible: $vra['XPlacement'] $vra['YPlacement'] $vra['XAdvance'] |
||
3556 | $vra = array(); |
||
3557 | // Horizontal adjustment for placement-in design units |
||
3558 | if (($ValueFormat & 0x0001) == 0x0001) { $vra['XPlacement'] = $this->read_short(); } |
||
3559 | // Vertical adjustment for placement-in design units |
||
3560 | if (($ValueFormat & 0x0002) == 0x0002) { $vra['YPlacement'] = $this->read_short(); } |
||
3561 | // Horizontal adjustment for advance-in design units (only used for horizontal writing) |
||
3562 | if (($ValueFormat & 0x0004) == 0x0004) { $vra['XAdvance'] = $this->read_short(); } |
||
3563 | // Vertical adjustment for advance-in design units (only used for vertical writing) |
||
3564 | if (($ValueFormat & 0x0008) == 0x0008) { $this->read_short(); } |
||
3565 | // Offset to Device table for horizontal placement-measured from beginning of PosTable (may be NULL) |
||
3566 | if (($ValueFormat & 0x0010) == 0x0010) { $this->read_ushort(); } |
||
3567 | // Offset to Device table for vertical placement-measured from beginning of PosTable (may be NULL) |
||
3568 | if (($ValueFormat & 0x0020) == 0x0020) { $this->read_ushort(); } |
||
3569 | // Offset to Device table for horizontal advance-measured from beginning of PosTable (may be NULL) |
||
3570 | if (($ValueFormat & 0x0040) == 0x0040) { $this->read_ushort(); } |
||
3571 | // Offset to Device table for vertical advance-measured from beginning of PosTable (may be NULL) |
||
3572 | if (($ValueFormat & 0x0080) == 0x0080) { $this->read_ushort(); } |
||
3573 | return $vra; |
||
3574 | } |
||
3575 | |||
3576 | View Code Duplication | function _getAnchorTable($offset=0) { |
|
3577 | if ($offset) { $this->seek($offset); } |
||
3578 | $AnchorFormat = $this->read_ushort(); |
||
3579 | $XCoordinate = $this->read_short(); |
||
3580 | $YCoordinate = $this->read_short(); |
||
3581 | // Format 2 specifies additional link to contour point; Format 3 additional Device table |
||
3582 | return array($XCoordinate, $YCoordinate); |
||
3583 | } |
||
3584 | |||
3585 | View Code Duplication | function _getMarkRecord($offset, $MarkPos) { |
|
3586 | $this->seek($offset); |
||
3587 | $MarkCount = $this->read_ushort(); |
||
3588 | $this->skip($MarkPos*4); |
||
3589 | $Class = $this->read_ushort(); |
||
3590 | $MarkAnchor = $offset + $this->read_ushort(); // = Offset to anchor table |
||
3591 | list($x,$y) = $this->_getAnchorTable($MarkAnchor ); |
||
3592 | $MarkRecord = array('Class'=>$Class, 'AnchorX'=>$x, 'AnchorY'=>$y); |
||
3593 | return $MarkRecord; |
||
3594 | } |
||
3595 | |||
3596 | |||
3597 | ////////////////////////////////////////////////////////////////////////////////// |
||
3598 | // Recursively get composite glyph data |
||
3599 | View Code Duplication | function getGlyphData($originalGlyphIdx, &$maxdepth, &$depth, &$points, &$contours) { |
|
3600 | $depth++; |
||
3601 | $maxdepth = max($maxdepth, $depth); |
||
3602 | if (count($this->glyphdata[$originalGlyphIdx]['compGlyphs'])) { |
||
3603 | foreach($this->glyphdata[$originalGlyphIdx]['compGlyphs'] AS $glyphIdx) { |
||
3604 | $this->getGlyphData($glyphIdx, $maxdepth, $depth, $points, $contours); |
||
3605 | } |
||
3606 | } |
||
3607 | else if (($this->glyphdata[$originalGlyphIdx]['nContours'] > 0) && $depth > 0) { // simple |
||
3608 | $contours += $this->glyphdata[$originalGlyphIdx]['nContours']; |
||
3609 | $points += $this->glyphdata[$originalGlyphIdx]['nPoints']; |
||
3610 | } |
||
3611 | $depth--; |
||
3612 | } |
||
3613 | |||
3614 | |||
3615 | ////////////////////////////////////////////////////////////////////////////////// |
||
3616 | // Recursively get composite glyphs |
||
3617 | View Code Duplication | function getGlyphs($originalGlyphIdx, &$start, &$glyphSet, &$subsetglyphs) { |
|
3618 | $glyphPos = $this->glyphPos[$originalGlyphIdx]; |
||
3619 | $glyphLen = $this->glyphPos[$originalGlyphIdx + 1] - $glyphPos; |
||
3620 | if (!$glyphLen) { |
||
3621 | return; |
||
3622 | } |
||
3623 | $this->seek($start + $glyphPos); |
||
3624 | $numberOfContours = $this->read_short(); |
||
3625 | if ($numberOfContours < 0) { |
||
3626 | $this->skip(8); |
||
3627 | $flags = GF_MORE; |
||
3628 | while ($flags & GF_MORE) { |
||
3629 | $flags = $this->read_ushort(); |
||
3630 | $glyphIdx = $this->read_ushort(); |
||
3631 | if (!isset($glyphSet[$glyphIdx])) { |
||
3632 | $glyphSet[$glyphIdx] = count($subsetglyphs); // old glyphID to new glyphID |
||
3633 | $subsetglyphs[$glyphIdx] = true; |
||
3634 | } |
||
3635 | $savepos = ftell($this->fh); |
||
3636 | $this->getGlyphs($glyphIdx, $start, $glyphSet, $subsetglyphs); |
||
3637 | $this->seek($savepos); |
||
3638 | if ($flags & GF_WORDS) |
||
3639 | $this->skip(4); |
||
3640 | else |
||
3641 | $this->skip(2); |
||
3642 | if ($flags & GF_SCALE) |
||
3643 | $this->skip(2); |
||
3644 | else if ($flags & GF_XYSCALE) |
||
3645 | $this->skip(4); |
||
3646 | else if ($flags & GF_TWOBYTWO) |
||
3647 | $this->skip(8); |
||
3648 | } |
||
3649 | } |
||
3650 | } |
||
3651 | |||
3652 | ////////////////////////////////////////////////////////////////////////////////// |
||
3653 | |||
3654 | View Code Duplication | function getHMTX($numberOfHMetrics, $numGlyphs, &$glyphToChar, $scale) { |
|
3655 | $start = $this->seek_table("hmtx"); |
||
3656 | $aw = 0; |
||
3657 | $this->charWidths = str_pad('', 256*256*2, "\x00"); |
||
3658 | if ($this->maxUniChar > 65536) { $this->charWidths .= str_pad('', 256*256*2, "\x00"); } // Plane 1 SMP |
||
3659 | if ($this->maxUniChar > 131072) { $this->charWidths .= str_pad('', 256*256*2, "\x00"); } // Plane 2 SMP |
||
3660 | $nCharWidths = 0; |
||
3661 | if (($numberOfHMetrics*4) < $this->maxStrLenRead) { |
||
3662 | $data = $this->get_chunk($start,($numberOfHMetrics*4)); |
||
3663 | $arr = unpack("n*", $data); |
||
3664 | } |
||
3665 | else { $this->seek($start); } |
||
3666 | for( $glyph=0; $glyph<$numberOfHMetrics; $glyph++) { |
||
3667 | if (($numberOfHMetrics*4) < $this->maxStrLenRead) { |
||
3668 | $aw = $arr[($glyph*2)+1]; |
||
3669 | } |
||
3670 | else { |
||
3671 | $aw = $this->read_ushort(); |
||
3672 | $lsb = $this->read_ushort(); |
||
3673 | } |
||
3674 | if (isset($glyphToChar[$glyph]) || $glyph == 0) { |
||
3675 | |||
3676 | if ($aw >= (1 << 15) ) { $aw = 0; } // 1.03 Some (arabic) fonts have -ve values for width |
||
3677 | // although should be unsigned value - comes out as e.g. 65108 (intended -50) |
||
3678 | if ($glyph == 0) { |
||
3679 | $this->defaultWidth = $scale*$aw; |
||
3680 | continue; |
||
3681 | } |
||
3682 | foreach($glyphToChar[$glyph] AS $char) { |
||
3683 | //$this->charWidths[$char] = intval(round($scale*$aw)); |
||
3684 | if ($char != 0 && $char != 65535) { |
||
3685 | $w = intval(round($scale*$aw)); |
||
3686 | if ($w == 0) { $w = 65535; } |
||
3687 | if ($char < 196608) { |
||
3688 | $this->charWidths[$char*2] = chr($w >> 8); |
||
3689 | $this->charWidths[$char*2 + 1] = chr($w & 0xFF); |
||
3690 | $nCharWidths++; |
||
3691 | } |
||
3692 | } |
||
3693 | } |
||
3694 | } |
||
3695 | } |
||
3696 | $data = $this->get_chunk(($start+$numberOfHMetrics*4),($numGlyphs*2)); |
||
3697 | $arr = unpack("n*", $data); |
||
3698 | $diff = $numGlyphs-$numberOfHMetrics; |
||
3699 | $w = intval(round($scale*$aw)); |
||
3700 | if ($w == 0) { $w = 65535; } |
||
3701 | for( $pos=0; $pos<$diff; $pos++) { |
||
3702 | $glyph = $pos + $numberOfHMetrics; |
||
3703 | if (isset($glyphToChar[$glyph])) { |
||
3704 | foreach($glyphToChar[$glyph] AS $char) { |
||
3705 | if ($char != 0 && $char != 65535) { |
||
3706 | if ($char < 196608) { |
||
3707 | $this->charWidths[$char*2] = chr($w >> 8); |
||
3708 | $this->charWidths[$char*2 + 1] = chr($w & 0xFF); |
||
3709 | $nCharWidths++; |
||
3710 | } |
||
3711 | } |
||
3712 | } |
||
3713 | } |
||
3714 | } |
||
3715 | // NB 65535 is a set width of 0 |
||
3716 | // First bytes define number of chars in font |
||
3717 | $this->charWidths[0] = chr($nCharWidths >> 8); |
||
3718 | $this->charWidths[1] = chr($nCharWidths & 0xFF); |
||
3719 | } |
||
3720 | |||
3721 | |||
3722 | |||
3723 | |||
3724 | |||
3725 | View Code Duplication | function getHMetric($numberOfHMetrics, $gid) { |
|
3726 | $start = $this->seek_table("hmtx"); |
||
3727 | if ($gid < $numberOfHMetrics) { |
||
3728 | $this->seek($start+($gid*4)); |
||
3729 | $hm = fread($this->fh,4); |
||
3730 | } |
||
3731 | else { |
||
3732 | $this->seek($start+(($numberOfHMetrics-1)*4)); |
||
3733 | $hm = fread($this->fh,2); |
||
3734 | $this->seek($start+($numberOfHMetrics*2)+($gid*2)); |
||
3735 | $hm .= fread($this->fh,2); |
||
3736 | } |
||
3737 | return $hm; |
||
3738 | } |
||
3739 | |||
3740 | View Code Duplication | function getLOCA($indexToLocFormat, $numGlyphs) { |
|
3741 | $start = $this->seek_table('loca'); |
||
3742 | $this->glyphPos = array(); |
||
3743 | if ($indexToLocFormat == 0) { |
||
3744 | $data = $this->get_chunk($start,($numGlyphs*2)+2); |
||
3745 | $arr = unpack("n*", $data); |
||
3746 | for ($n=0; $n<=$numGlyphs; $n++) { |
||
3747 | $this->glyphPos[] = ($arr[$n+1] * 2); |
||
3748 | } |
||
3749 | } |
||
3750 | else if ($indexToLocFormat == 1) { |
||
3751 | $data = $this->get_chunk($start,($numGlyphs*4)+4); |
||
3752 | $arr = unpack("N*", $data); |
||
3753 | for ($n=0; $n<=$numGlyphs; $n++) { |
||
3754 | $this->glyphPos[] = ($arr[$n+1]); |
||
3755 | } |
||
3756 | } |
||
3757 | else |
||
3758 | die('Unknown location table format '.$indexToLocFormat); |
||
3759 | } |
||
3760 | |||
3761 | |||
3762 | // CMAP Format 4 |
||
3763 | View Code Duplication | function getCMAP4($unicode_cmap_offset, &$glyphToChar, &$charToGlyph ) { |
|
3764 | $this->maxUniChar = 0; |
||
3765 | $this->seek($unicode_cmap_offset + 2); |
||
3766 | $length = $this->read_ushort(); |
||
3767 | $limit = $unicode_cmap_offset + $length; |
||
3768 | $this->skip(2); |
||
3769 | |||
3770 | $segCount = $this->read_ushort() / 2; |
||
3771 | $this->skip(6); |
||
3772 | $endCount = array(); |
||
3773 | for($i=0; $i<$segCount; $i++) { $endCount[] = $this->read_ushort(); } |
||
3774 | $this->skip(2); |
||
3775 | $startCount = array(); |
||
3776 | for($i=0; $i<$segCount; $i++) { $startCount[] = $this->read_ushort(); } |
||
3777 | $idDelta = array(); |
||
3778 | for($i=0; $i<$segCount; $i++) { $idDelta[] = $this->read_short(); } // ???? was unsigned short |
||
3779 | $idRangeOffset_start = $this->_pos; |
||
3780 | $idRangeOffset = array(); |
||
3781 | for($i=0; $i<$segCount; $i++) { $idRangeOffset[] = $this->read_ushort(); } |
||
3782 | |||
3783 | for ($n=0;$n<$segCount;$n++) { |
||
3784 | $endpoint = ($endCount[$n] + 1); |
||
3785 | for ($unichar=$startCount[$n];$unichar<$endpoint;$unichar++) { |
||
3786 | if ($idRangeOffset[$n] == 0) |
||
3787 | $glyph = ($unichar + $idDelta[$n]) & 0xFFFF; |
||
3788 | else { |
||
3789 | $offset = ($unichar - $startCount[$n]) * 2 + $idRangeOffset[$n]; |
||
3790 | $offset = $idRangeOffset_start + 2 * $n + $offset; |
||
3791 | if ($offset >= $limit) |
||
3792 | $glyph = 0; |
||
3793 | else { |
||
3794 | $glyph = $this->get_ushort($offset); |
||
3795 | if ($glyph != 0) |
||
3796 | $glyph = ($glyph + $idDelta[$n]) & 0xFFFF; |
||
3797 | } |
||
3798 | } |
||
3799 | $charToGlyph[$unichar] = $glyph; |
||
3800 | if ($unichar < 196608) { $this->maxUniChar = max($unichar,$this->maxUniChar); } |
||
3801 | $glyphToChar[$glyph][] = $unichar; |
||
3802 | } |
||
3803 | } |
||
3804 | |||
3805 | } |
||
3806 | |||
3807 | function formatUni($char) { |
||
3808 | $x = preg_replace('/^[0]*/','',$char); |
||
3809 | $x = str_pad($x, 4, '0', STR_PAD_LEFT); |
||
3810 | $d = hexdec($x); |
||
3811 | if (($d>57343 && $d<63744) || ($d>122879 && $d<126977)) { $id = 'M'; } // E000 - F8FF, 1E000-1F000 |
||
3812 | else { $id = 'U'; } |
||
3813 | return $id .'+'.$x; |
||
3814 | } |
||
3815 | function formatEntity($char, $allowjoining=false) { |
||
3816 | $char = preg_replace('/^[0]/','',$char); |
||
3817 | $x = '&#x'.$char.';'; |
||
3818 | if (strpos($this->GlyphClassMarks, $char)!==false) { |
||
3819 | if (!$allowjoining) { |
||
3820 | $x = '◌'.$x; |
||
3821 | } |
||
3822 | } |
||
3823 | return $x; |
||
3824 | } |
||
3825 | View Code Duplication | function formatUniArr($arr) { |
|
3826 | $s = array(); |
||
3827 | foreach($arr AS $c) { |
||
3828 | $x = preg_replace('/^[0]*/','',$c); |
||
3829 | $d = hexdec($x); |
||
3830 | if (($d>57343 && $d<63744) || ($d>122879 && $d<126977)) { $id = 'M'; } // E000 - F8FF, 1E000-1F000 |
||
3831 | else { $id = 'U'; } |
||
3832 | $s[] = $id .'+'.str_pad($x, 4, '0', STR_PAD_LEFT); |
||
3833 | } |
||
3834 | return implode(', ',$s); |
||
3835 | } |
||
3836 | View Code Duplication | function formatEntityArr($arr) { |
|
3837 | $s = array(); |
||
3838 | foreach($arr AS $c) { |
||
3839 | $c = preg_replace('/^[0]/','',$c); |
||
3840 | $x = '&#x'.$c.';'; |
||
3841 | if (strpos($this->GlyphClassMarks, $c)!==false) { |
||
3842 | $x = '◌'.$x; |
||
3843 | } |
||
3844 | $s[] = $x; |
||
3845 | } |
||
3846 | return implode(' ',$s); // ZWNJ? ‍ |
||
3847 | } |
||
3848 | View Code Duplication | function formatClassArr($arr) { |
|
3849 | $s = array(); |
||
3850 | foreach($arr AS $c) { |
||
3851 | $x = preg_replace('/^[0]*/','',$c); |
||
3852 | $d = hexdec($x); |
||
3853 | if (($d>57343 && $d<63744) || ($d>122879 && $d<126977)) { $id = 'M'; } // E000 - F8FF, 1E000-1F000 |
||
3854 | else { $id = 'U'; } |
||
3855 | $s[] = $id .'+'.str_pad($x, 4, '0', STR_PAD_LEFT); |
||
3856 | } |
||
3857 | return implode(', ',$s); |
||
3858 | } |
||
3859 | View Code Duplication | function formatUniStr($str) { |
|
3860 | $s = array(); |
||
3861 | $arr = explode('|',$str); |
||
3862 | foreach($arr AS $c) { |
||
3863 | $x = preg_replace('/^[0]*/','',$c); |
||
3864 | $d = hexdec($x); |
||
3865 | if (($d>57343 && $d<63744) || ($d>122879 && $d<126977)) { $id = 'M'; } // E000 - F8FF, 1E000-1F000 |
||
3866 | else { $id = 'U'; } |
||
3867 | $s[] = $id .'+'.str_pad($x, 4, '0', STR_PAD_LEFT); |
||
3868 | } |
||
3869 | return implode(', ',$s); |
||
3870 | } |
||
3871 | View Code Duplication | function formatEntityStr($str) { |
|
3872 | $s = array(); |
||
3873 | $arr = explode('|',$str); |
||
3874 | foreach($arr AS $c) { |
||
3875 | $c = preg_replace('/^[0]/','',$c); |
||
3876 | $x = '&#x'.$c.';'; |
||
3877 | if (strpos($this->GlyphClassMarks, $c)!==false) { |
||
3878 | $x = '◌'.$x; |
||
3879 | } |
||
3880 | $s[] = $x; |
||
3881 | } |
||
3882 | return implode(' ',$s); // ZWNJ? ‍ |
||
3883 | } |
||
3884 | function formatEntityFirst($str) { |
||
3885 | $arr = explode('|',$str); |
||
3886 | $char = preg_replace('/^[0]/','',$arr[0]); |
||
3887 | $x = '&#x'.$char.';'; |
||
3888 | if (strpos($this->GlyphClassMarks, $char)!==false) { |
||
3889 | $x = '◌'.$x; |
||
3890 | } |
||
3891 | return $x; |
||
3892 | } |
||
3893 | |||
3894 | } |
||
3895 | |||
3896 | |||
3897 | ?> |
The PSR-2 coding standard requires that all properties in a class have their visibility explicitly declared. If you declare a property using
the property is implicitly global.
To learn more about the PSR-2, please see the PHP-FIG site on the PSR-2.