PluginProbe
PDF & Print by BestWebSoft – WordPress Posts and Pages PDF Generator Plugin / 1.5
PDF & Print by BestWebSoft – WordPress Posts and Pages PDF Generator Plugin v1.5
trunk 1.5 1.6 1.7 1.7.1 1.7.2 1.7.3 1.7.4 1.7.5 1.7.6 1.7.7 1.7.8 1.7.9 1.8.0 1.8.1 1.8.2 1.8.3 1.8.4 1.8.5 1.8.6 1.8.7 1.8.8 1.8.9 1.9.0 1.9.1 All 71 releases
← All changes | mpdf/classes/ttfontsuni.php +480 -2836 1.8.1 → 1.5 View file →
@@ -2,10 +2,10 @@
2 2
3 3 /*******************************************************************************
4 4 * TTFontFile class *
5 5 * *
6 -* Version: 4.01 *
7 -* Date: 2014-10-25 *
6 +* Version: 1.05 *
7 +* Date: 2011-07-21 *
8 8 * Author: Ian Back <[email protected]> *
9 9 * License: LGPL *
10 10 * Copyright (c) Ian Back, 2010 *
11 11 * This class is based on The ReportLab Open Source PDF library *
@@ -15,14 +15,8 @@
15 15 * modification of the file. *
16 16 * *
17 17 *******************************************************************************/
18 18
19 -
20 -
21 -// NOTE*** If you change the defined constants below, be sure to delete all temporary font data files in /ttfontdata/
22 -// to force mPDF to regenerate cached font files.
23 -if (!defined('_OTL_OLD_SPEC_COMPAT_2')) define("_OTL_OLD_SPEC_COMPAT_2", true);
24 -
25 19 // Define the value used in the "head" table of a created TTF file
26 20 // 0x74727565 "true" for Mac
27 21 // 0x00010000 for Windows
28 22 // Either seems to work for a font embedded in a PDF file
@@ -39,32 +33,12 @@
39 33 define("GF_MORE",(1 << 5));
40 34 define("GF_XYSCALE",(1 << 6));
41 35 define("GF_TWOBYTWO",(1 << 7));
42 36
43 -// mPDF 5.7.1
44 -if(!function_exists('unicode_hex')){
45 - function unicode_hex($unicode_dec) {
46 - return (sprintf("%05s", strtoupper(dechex($unicode_dec))));
47 - }
48 -}
49 37
50 38
51 39 class TTFontFile {
52 40
53 -var $GPOSFeatures; // mPDF 5.7.1
54 -var $GPOSLookups; // mPDF 5.7.1
55 -var $GPOSScriptLang; // mPDF 5.7.1
56 -var $MarkAttachmentType; // mPDF 5.7.1
57 -var $MarkGlyphSets; // mPDF 7.5.1
58 -var $GlyphClassMarks; // mPDF 5.7.1
59 -var $GlyphClassLigatures; // mPDF 5.7.1
60 -var $GlyphClassBases; // mPDF 5.7.1
61 -var $GlyphClassComponents; // mPDF 5.7.1
62 -var $GSUBScriptLang; // mPDF 5.7.1
63 -var $rtlPUAstr; // mPDF 5.7.1
64 -//var $rtlPUAarr; // mPDF 5.7.1
65 -var $fontkey; // mPDF 5.7.1
66 -var $useOTL; // mPDF 5.7.1
67 41 var $panose;
68 42 var $maxUni;
69 43 var $sFamilyClass;
70 44 var $sFamilySubClass;
@@ -82,20 +56,8 @@
82 56 var $glyphPos;
83 57 var $charToGlyph;
84 58 var $ascent;
85 59 var $descent;
86 -var $lineGap; // mPDF 6
87 -var $hheaascent;
88 -var $hheadescent;
89 -var $hhealineGap; // mPDF 6
90 -var $advanceWidthMax; // mPDF 6
91 -var $typoAscender; // mPDF 6
92 -var $typoDescender; // mPDF 6
93 -var $typoLineGap; // mPDF 6
94 -var $usWinAscent; // mPDF 6
95 -var $usWinDescent; // mPDF 6
96 -var $strikeoutSize;
97 -var $strikeoutPosition;
98 60 var $name;
99 61 var $familyName;
100 62 var $styleName;
101 63 var $fullName;
@@ -102,9 +64,8 @@
102 64 var $uniqueFontID;
103 65 var $unitsPerEm;
104 66 var $bbox;
105 67 var $capHeight;
106 -var $xHeight; // mPDF 6
107 68 var $stemV;
108 69 var $italicAngle;
109 70 var $flags;
110 71 var $underlinePosition;
@@ -115,10 +76,8 @@
115 76 var $numTTCFonts;
116 77 var $TTCFonts;
117 78 var $maxUniChar;
118 79 var $kerninfo;
119 -var $haskernGPOS;
120 -var $hassmallcapsGSUB;
121 80
122 81 function TTFontFile() {
123 82 $this->maxStrLenRead = 200000; // Maximum size of glyf table to read in as string (otherwise reads each glyph from file)
124 83 }
@@ -123,11 +82,9 @@
123 82 $this->maxStrLenRead = 200000; // Maximum size of glyf table to read in as string (otherwise reads each glyph from file)
124 83 }
125 84
126 85
127 - function getMetrics($file, $fontkey, $TTCfontID=0, $debug=false, $BMPonly=false, $useOTL=0) { // mPDF 5.7.1
128 - $this->useOTL = $useOTL; // mPDF 5.7.1
129 - $this->fontkey = $fontkey; // mPDF 5.7.1
86 + function getMetrics($file, $TTCfontID=0, $debug=false, $BMPonly=false, $kerninfo=false) {
130 87 $this->filename = $file;
131 88 $this->fh = fopen($file,'rb') or die('Can\'t open file ' . $file);
132 89 $this->_pos = 0;
133 90 $this->charWidths = '';
@@ -135,29 +92,10 @@
135 92 $this->charToGlyph = array();
136 93 $this->tables = array();
137 94 $this->otables = array();
138 95 $this->kerninfo = array();
139 - $this->haskernGPOS = array();
140 - $this->hassmallcapsGSUB = array();
141 96 $this->ascent = 0;
142 97 $this->descent = 0;
143 - $this->lineGap = 0; // mPDF 6
144 - $this->hheaascent = 0; // mPDF 6
145 - $this->hheadescent = 0; // mPDF 6
146 - $this->hhealineGap = 0; // mPDF 6
147 - $this->xHeight = 0; // mPDF 6
148 - $this->capHeight = 0; // mPDF 6
149 - $this->panose = array();
150 - $this->sFamilyClass = 0;
151 - $this->sFamilySubClass = 0;
152 - $this->typoAscender = 0; // mPDF 6
153 - $this->typoDescender = 0; // mPDF 6
154 - $this->typoLineGap = 0; // mPDF 6
155 - $this->usWinAscent = 0; // mPDF 6
156 - $this->usWinDescent = 0; // mPDF 6
157 - $this->advanceWidthMax = 0; // mPDF 6
158 - $this->strikeoutSize = 0;
159 - $this->strikeoutPosition = 0;
160 98 $this->numTTCFonts = 0;
161 99 $this->TTCFonts = array();
162 100 $this->version = $version = $this->read_ulong();
163 101 $this->panose = array();
@@ -178,9 +116,9 @@
178 116 $this->seek($this->TTCFonts[$TTCfontID]['offset']);
179 117 $this->version = $version = $this->read_ulong(); // TTFont version again now
180 118 }
181 119 $this->readTableDirectory($debug);
182 - $this->extractInfo($debug, $BMPonly, $useOTL);
120 + $this->extractInfo($debug, $BMPonly, $kerninfo);
183 121 fclose($this->fh);
184 122 }
185 123
186 124
@@ -240,17 +178,15 @@
240 178 $lo=0x0000;
241 179 for($i=0;$i<$len;$i+=4) {
242 180 $hi += (ord($data[$i])<<8) + ord($data[$i+1]);
243 181 $lo += (ord($data[$i+2])<<8) + ord($data[$i+3]);
244 - $hi += ($lo >> 16) & 0xFFFF;
182 + $hi += ($lo >> 16) & 0xFFFF; // mPDF 5.3.07
245 183 $lo = $lo & 0xFFFF;
246 184 }
247 - $hi = $hi & 0xFFFF; // mPDF 5.7.1
248 185 return array($hi, $lo);
249 186 }
250 187
251 188 function get_table_pos($tag) {
252 - if (!isset($this->tables[$tag])) { return array(0,0); }
253 189 $offset = $this->tables[$tag]['offset'];
254 190 $length = $this->tables[$tag]['length'];
255 191 return array($offset, $length);
256 192 }
@@ -261,9 +197,10 @@
261 197 }
262 198
263 199 function skip($delta) {
264 200 $this->_pos = $this->_pos + $delta;
265 - fseek($this->fh,$delta,SEEK_CUR);
201 + // fseek($this->fh,$this->_pos);
202 + fseek($this->fh,$delta,SEEK_CUR); // mPDF 5.3.19
266 203 }
267 204
268 205 function seek_table($tag, $offset_in_table = 0) {
269 206 $tpos = $this->get_table_pos($tag);
@@ -338,8 +275,9 @@
338 275 $up = pack("n", $value);
339 276 return $this->splice($stream, $offset, $up);
340 277 }
341 278
279 + // mPDF 5.0
342 280 function _set_short($stream, $offset, $val) {
343 281 if ($val<0) {
344 282 $val = abs($val);
345 283 $val = ~$val;
@@ -371,11 +309,9 @@
371 309
372 310
373 311
374 312 /////////////////////////////////////////////////////////////////////////////////////////
375 - function getCTG($file, $TTCfontID=0, $debug=false, $useOTL=false) { // mPDF 5.7.1
376 - // Only called if font is not to be used as embedded subset i.e. NOT called for SIP/SMP fonts
377 - $this->useOTL = $useOTL; // mPDF 5.7.1
313 + function getCTG($file, $TTCfontID=0, $debug=false) {
378 314 $this->filename = $file;
379 315 $this->fh = fopen($file,'rb') or die('Can\'t open file ' . $file);
380 316 $this->_pos = 0;
381 317 $this->charWidths = '';
@@ -429,30 +365,8 @@
429 365 $glyphToChar = array();
430 366 $charToGlyph = array();
431 367 $this->getCMAP4($unicode_cmap_offset, $glyphToChar, $charToGlyph );
432 368
433 - ///////////////////////////////////
434 - // mPDF 5.7.1
435 - // Map Unmapped glyphs - from $numGlyphs
436 - if ($useOTL) {
437 - $this->seek_table("maxp");
438 - $this->skip(4);
439 - $numGlyphs = $this->read_ushort();
440 - $bctr = 0xE000;
441 - for ($gid=1; $gid<$numGlyphs; $gid++) {
442 - if (!isset($glyphToChar[$gid])) {
443 - while(isset($charToGlyph[$bctr])) { $bctr++; } // Avoid overwriting a glyph already mapped in PUA
444 - if ($bctr > 0xF8FF) {
445 - die($file." : WARNING - Font cannot map all included glyphs into Private Use Area U+E000 - U+F8FF; cannot use useOTL on this font");
446 - }
447 - $glyphToChar[$gid][] = $bctr;
448 - $charToGlyph[$bctr] = $gid;
449 - $bctr++;
450 - }
451 - }
452 - }
453 - ///////////////////////////////////
454 -
455 369 fclose($this->fh);
456 370 return ($charToGlyph);
457 371 }
458 372
@@ -478,24 +392,374 @@
478 392 }
479 393 }
480 394
481 395
396 + // Used to get font information from files in directory for mPDF config
397 + function extractCoreInfo($file, $TTCfontID=0) {
398 + $this->filename = $file;
399 + $this->fh = fopen($file,'rb');
400 + if (!$this->fh) { return ('ERROR - Can\'t open file ' . $file); }
401 + $this->_pos = 0;
402 + $this->charWidths = '';
403 + $this->glyphPos = array();
404 + $this->charToGlyph = array();
405 + $this->tables = array();
406 + $this->otables = array();
407 + $this->ascent = 0;
408 + $this->descent = 0;
409 + $this->numTTCFonts = 0;
410 + $this->TTCFonts = array();
411 + $this->version = $version = $this->read_ulong();
412 + $this->panose = array(); // mPDF 5.0
413 + if ($version==0x4F54544F)
414 + return("ERROR - NOT ADDED as Postscript outlines are not supported - " . $file);
415 + if ($version==0x74746366) {
416 + if ($TTCfontID > 0) {
417 + $this->version = $version = $this->read_ulong(); // TTC Header version now
418 + if (!in_array($version, array(0x00010000,0x00020000)))
419 + return("ERROR - NOT ADDED as Error parsing TrueType Collection: version=".$version." - " . $file);
420 + }
421 + else return("ERROR - Error parsing TrueType Collection - " . $file);
422 + $this->numTTCFonts = $this->read_ulong();
423 + for ($i=1; $i<=$this->numTTCFonts; $i++) {
424 + $this->TTCFonts[$i]['offset'] = $this->read_ulong();
425 + }
426 + $this->seek($this->TTCFonts[$TTCfontID]['offset']);
427 + $this->version = $version = $this->read_ulong(); // TTFont version again now
428 + $this->readTableDirectory(false);
429 + }
430 + else {
431 + if (!in_array($version, array(0x00010000,0x74727565)))
432 + return("ERROR - NOT ADDED as Not a TrueType font: version=".$version." - " . $file);
433 + $this->readTableDirectory(false);
434 + }
482 435
436 +/* Included for testing...
437 + $cmap_offset = $this->seek_table("cmap");
438 + $this->skip(2);
439 + $cmapTableCount = $this->read_ushort();
440 + $unicode_cmap_offset = 0;
441 + for ($i=0;$i<$cmapTableCount;$i++) {
442 + $x[$i]['platformId'] = $this->read_ushort();
443 + $x[$i]['encodingId'] = $this->read_ushort();
444 + $x[$i]['offset'] = $this->read_ulong();
445 + $save_pos = $this->_pos;
446 + $x[$i]['format'] = $this->get_ushort($cmap_offset + $x[$i]['offset'] );
447 + $this->seek($save_pos );
448 + }
449 + print_r($x); exit;
450 +*/
451 + ///////////////////////////////////
452 + // name - Naming table
453 + ///////////////////////////////////
454 + $name_offset = $this->seek_table("name");
455 + $format = $this->read_ushort();
456 + if ($format != 0 && $format != 1) // mPDF 5.3.73
457 + return("ERROR - NOT ADDED as Unknown name table format ".$format." - " . $file);
458 + $numRecords = $this->read_ushort();
459 + $string_data_offset = $name_offset + $this->read_ushort();
460 + $names = array(1=>'',2=>'',3=>'',4=>'',6=>'');
461 + $K = array_keys($names);
462 + $nameCount = count($names);
463 + for ($i=0;$i<$numRecords; $i++) {
464 + $platformId = $this->read_ushort();
465 + $encodingId = $this->read_ushort();
466 + $languageId = $this->read_ushort();
467 + $nameId = $this->read_ushort();
468 + $length = $this->read_ushort();
469 + $offset = $this->read_ushort();
470 + if (!in_array($nameId,$K)) continue;
471 + $N = '';
472 + if ($platformId == 3 && $encodingId == 1 && $languageId == 0x409) { // Microsoft, Unicode, US English, PS Name
473 + $opos = $this->_pos;
474 + $this->seek($string_data_offset + $offset);
475 + if ($length % 2 != 0)
476 + $length += 1;
477 + $length /= 2;
478 + $N = '';
479 + while ($length > 0) {
480 + $char = $this->read_ushort();
481 + $N .= (chr($char));
482 + $length -= 1;
483 + }
484 + $this->_pos = $opos;
485 + $this->seek($opos);
486 + }
487 + else if ($platformId == 1 && $encodingId == 0 && $languageId == 0) { // Macintosh, Roman, English, PS Name
488 + $opos = $this->_pos;
489 + $N = $this->get_chunk($string_data_offset + $offset, $length);
490 + $this->_pos = $opos;
491 + $this->seek($opos);
492 + }
493 + if ($N && $names[$nameId]=='') {
494 + $names[$nameId] = $N;
495 + $nameCount -= 1;
496 + if ($nameCount==0) break;
497 + }
498 + }
499 + if ($names[6])
500 + $psName = preg_replace('/ /','-',$names[6]);
501 + else if ($names[4])
502 + $psName = preg_replace('/ /','-',$names[4]);
503 + else if ($names[1])
504 + $psName = preg_replace('/ /','-',$names[1]);
505 + else
506 + $psName = '';
507 + if (!$names[1] && !$psName)
508 + return("ERROR - NOT ADDED as Could not find valid font name - " . $file);
509 + $this->name = $psName;
510 + if ($names[1]) { $this->familyName = $names[1]; } else { $this->familyName = $psName; }
511 + if ($names[2]) { $this->styleName = $names[2]; } else { $this->styleName = 'Regular'; }
512 +
513 + ///////////////////////////////////
514 + // head - Font header table
515 + ///////////////////////////////////
516 + $this->seek_table("head");
517 + $ver_maj = $this->read_ushort();
518 + $ver_min = $this->read_ushort();
519 + if ($ver_maj != 1)
520 + return('ERROR - NOT ADDED as Unknown head table version '. $ver_maj .'.'. $ver_min." - " . $file);
521 + $this->fontRevision = $this->read_ushort() . $this->read_ushort();
522 + $this->skip(4);
523 + $magic = $this->read_ulong();
524 + if ($magic != 0x5F0F3CF5)
525 + return('ERROR - NOT ADDED as Invalid head table magic ' .$magic." - " . $file);
526 + $this->skip(2);
527 + $this->unitsPerEm = $unitsPerEm = $this->read_ushort();
528 + $scale = 1000 / $unitsPerEm;
529 + $this->skip(24);
530 + $macStyle = $this->read_short();
531 + $this->skip(4);
532 + $indexLocFormat = $this->read_short();
533 +
534 + ///////////////////////////////////
535 + // OS/2 - OS/2 and Windows metrics table
536 + ///////////////////////////////////
537 + $sFamily = '';
538 + $panose = '';
539 + $fsSelection = '';
540 + if (isset($this->tables["OS/2"])) {
541 + $this->seek_table("OS/2");
542 + $this->skip(30);
543 + $sF = $this->read_short();
544 + $sFamily = ($sF >> 8);
545 + $this->_pos += 10; //PANOSE = 10 byte length
546 + $panose = fread($this->fh,10);
547 + $this->panose = array();
548 + for ($p=0;$p<strlen($panose);$p++) { $this->panose[] = ord($panose[$p]); }
549 + $this->skip(20);
550 + $fsSelection = $this->read_short();
551 + }
552 +
553 + ///////////////////////////////////
554 + // post - PostScript table
555 + ///////////////////////////////////
556 + $this->seek_table("post");
557 + $this->skip(4);
558 + $this->italicAngle = $this->read_short() + $this->read_ushort() / 65536.0;
559 + $this->skip(4);
560 + $isFixedPitch = $this->read_ulong();
561 +
562 +
563 +
564 + ///////////////////////////////////
565 + // cmap - Character to glyph index mapping table
566 + ///////////////////////////////////
567 + $cmap_offset = $this->seek_table("cmap");
568 + $this->skip(2);
569 + $cmapTableCount = $this->read_ushort();
570 + $unicode_cmap_offset = 0;
571 + for ($i=0;$i<$cmapTableCount;$i++) {
572 + $platformID = $this->read_ushort();
573 + $encodingID = $this->read_ushort();
574 + $offset = $this->read_ulong();
575 + $save_pos = $this->_pos;
576 + if (($platformID == 3 && $encodingID == 1) || $platformID == 0) { // Microsoft, Unicode
577 + $format = $this->get_ushort($cmap_offset + $offset);
578 + if ($format == 4) {
579 + if (!$unicode_cmap_offset) $unicode_cmap_offset = $cmap_offset + $offset;
580 + }
581 + }
582 + else if ((($platformID == 3 && $encodingID == 10) || $platformID == 0)) { // Microsoft, Unicode Format 12 table HKCS
583 + $format = $this->get_ushort($cmap_offset + $offset);
584 + if ($format == 12) {
585 + $unicode_cmap_offset = $cmap_offset + $offset;
586 + break;
587 + }
588 + }
589 + $this->seek($save_pos );
590 + }
591 +
592 + if (!$unicode_cmap_offset)
593 + return('ERROR - Font ('.$this->filename .') NOT ADDED as it is not Unicode encoded, and cannot be used by mPDF');
594 +
595 + $rtl = false;
596 + $indic = false;
597 + $cjk = false;
598 + $sip = false;
599 + $smp = false;
600 +
601 + // Format 12 CMAP does characters above Unicode BMP i.e. some HKCS characters U+20000 and above
602 + if ($format == 12) {
603 + $this->seek($unicode_cmap_offset + 4);
604 + $length = $this->read_ulong();
605 + $limit = $unicode_cmap_offset + $length;
606 + $this->skip(4);
607 + $nGroups = $this->read_ulong();
608 + for($i=0; $i<$nGroups ; $i++) {
609 + $startCharCode = $this->read_ulong();
610 + $endCharCode = $this->read_ulong();
611 + $startGlyphCode = $this->read_ulong();
612 + if (($endCharCode > 0x20000 && $endCharCode < 0x2A6DF) || ($endCharCode > 0x2F800 && $endCharCode < 0x2FA1F)) {
613 + $sip = true;
614 + }
615 + else if ($endCharCode > 0x10000 && $endCharCode < 0x1FFFF) {
616 + $smp = true;
617 + }
618 + else if (($endCharCode > 0x0590 && $endCharCode < 0x077F) || ($endCharCode > 0xFE70 && $endCharCode < 0xFEFF) || ($endCharCode > 0xFB50 && $endCharCode < 0xFDFF)) {
619 + $rtl = true;
620 + }
621 + else if ($endCharCode > 0x0900 && $endCharCode < 0x0DFF) {
622 + $indic = true;
623 + }
624 + else if (($endCharCode > 0x2E80 && $endCharCode < 0x4DC0) || ($endCharCode > 0x4E00 && $endCharCode < 0xA4CF) || ($endCharCode > 0xAC00 && $endCharCode < 0xD7AF) || ($endCharCode > 0xF900 && $endCharCode < 0xFAFF) || ($endCharCode > 0xFE30 && $endCharCode < 0xFE4F)) {
625 + $cjk = true;
626 + }
627 + }
628 + }
629 +
630 + else { // Format 4 CMap
631 + $this->seek($unicode_cmap_offset + 2);
632 + $length = $this->read_ushort();
633 + $limit = $unicode_cmap_offset + $length;
634 + $this->skip(2);
635 +
636 + $segCount = $this->read_ushort() / 2;
637 + $this->skip(6);
638 + $endCount = array();
639 + for($i=0; $i<$segCount; $i++) { $endCount[] = $this->read_ushort(); }
640 + $this->skip(2);
641 + $startCount = array();
642 + for($i=0; $i<$segCount; $i++) { $startCount[] = $this->read_ushort(); }
643 + $idDelta = array();
644 + for($i=0; $i<$segCount; $i++) { $idDelta[] = $this->read_short(); } // ???? was unsigned short
645 + $idRangeOffset_start = $this->_pos;
646 + $idRangeOffset = array();
647 + for($i=0; $i<$segCount; $i++) { $idRangeOffset[] = $this->read_ushort(); }
648 +
649 + for ($n=0;$n<$segCount;$n++) {
650 + if (($endCount[$n] > 0x0590 && $endCount[$n] < 0x077F) || ($endCount[$n] > 0xFE70 && $endCount[$n] < 0xFEFF) || ($endCount[$n] > 0xFB50 && $endCount[$n] < 0xFDFF)) {
651 + $rtl = true;
652 + }
653 + else if ($endCount[$n] > 0x0900 && $endCount[$n] < 0x0DFF) {
654 + $indic = true;
655 + }
656 + else if (($endCount[$n] > 0x2E80 && $endCount[$n] < 0x4DC0) || ($endCount[$n] > 0x4E00 && $endCount[$n] < 0xA4CF) || ($endCount[$n] > 0xAC00 && $endCount[$n] < 0xD7AF) || ($endCount[$n] > 0xF900 && $endCount[$n] < 0xFAFF) || ($endCount[$n] > 0xFE30 && $endCount[$n] < 0xFE4F)) {
657 + $cjk = true;
658 + }
659 + }
660 + }
661 +
662 +
663 +
664 + $bold = false;
665 + $italic = false;
666 + $ftype = '';
667 + if ($macStyle & (1 << 0)) { $bold = true; } // bit 0 bold
668 + else if ($fsSelection & (1 << 5)) { $bold = true; } // 5 BOLD Characters are emboldened
669 +
670 + if ($macStyle & (1 << 1)) { $italic = true; } // bit 1 italic
671 + else if ($fsSelection & (1 << 0)) { $italic = true; } // 0 ITALIC Font contains Italic characters, otherwise they are upright
672 + else if ($this->italicAngle <> 0) { $italic = true; }
673 +
674 + if ($isFixedPitch ) { $ftype = 'mono'; }
675 + else if ($sFamily >0 && $sFamily <8) { $ftype = 'serif'; }
676 + else if ($sFamily ==8) { $ftype = 'sans'; }
677 + else if ($sFamily ==10) { $ftype = 'cursive'; }
678 + // Use PANOSE
679 + if ($panose) {
680 + $bFamilyType=ord($panose[0]);
681 + if ($bFamilyType==2) {
682 + $bSerifStyle=ord($panose[1]);
683 + if (!$ftype) {
684 + if ($bSerifStyle>1 && $bSerifStyle<11) { $ftype = 'serif'; }
685 + else if ($bSerifStyle>10) { $ftype = 'sans'; }
686 + }
687 + $bProportion=ord($panose[3]);
688 + if ($bProportion==9 || $bProportion==1) { $ftype = 'mono'; } // ==1 i.e. No Fit needed for OCR-a and -b
689 + }
690 + else if ($bFamilyType==3) {
691 + $ftype = 'cursive';
692 + }
693 + }
694 +
695 + fclose($this->fh);
696 + return array($this->familyName, $bold, $italic, $ftype, $TTCfontID, $rtl, $indic, $cjk, $sip, $smp);
697 + }
698 +
699 +
483 700 /////////////////////////////////////////////////////////////////////////////////////////
484 701
485 702 /////////////////////////////////////////////////////////////////////////////////////////
486 703
487 - function extractInfo($debug=false, $BMPonly=false, $useOTL=0) {
488 - // Values are all set to 0 or blank at start of getMetrics
704 + function extractInfo($debug=false, $BMPonly=false, $kerninfo=false) {
489 705 ///////////////////////////////////
490 706 // name - Naming table
491 707 ///////////////////////////////////
708 + $this->panose = array();
709 + $this->sFamilyClass = 0;
710 + $this->sFamilySubClass = 0;
711 +
712 +/* Test purposes - displays table of names
492 713 $name_offset = $this->seek_table("name");
493 714 $format = $this->read_ushort();
494 - if ($format != 0 && $format != 1)
715 + if ($format != 0 && $format != 1) // mPDF 5.3.73
495 716 die("Unknown name table format ".$format);
496 717 $numRecords = $this->read_ushort();
497 718 $string_data_offset = $name_offset + $this->read_ushort();
719 + for ($i=0;$i<$numRecords; $i++) {
720 + $x[$i]['platformId'] = $this->read_ushort();
721 + $x[$i]['encodingId'] = $this->read_ushort();
722 + $x[$i]['languageId'] = $this->read_ushort();
723 + $x[$i]['nameId'] = $this->read_ushort();
724 + $x[$i]['length'] = $this->read_ushort();
725 + $x[$i]['offset'] = $this->read_ushort();
726 +
727 + $N = '';
728 + if ($x[$i]['platformId'] == 1 && $x[$i]['encodingId'] == 0 && $x[$i]['languageId'] == 0) { // Roman
729 + $opos = $this->_pos;
730 + $N = $this->get_chunk($string_data_offset + $x[$i]['offset'] , $x[$i]['length'] );
731 + $this->_pos = $opos;
732 + $this->seek($opos);
733 + }
734 + else { // Unicode
735 + $opos = $this->_pos;
736 + $this->seek($string_data_offset + $x[$i]['offset'] );
737 + $length = $x[$i]['length'] ;
738 + if ($length % 2 != 0)
739 + $length -= 1;
740 + // die("PostScript name is UTF-16BE string of odd length");
741 + $length /= 2;
742 + $N = '';
743 + while ($length > 0) {
744 + $char = $this->read_ushort();
745 + $N .= (chr($char));
746 + $length -= 1;
747 + }
748 + $this->_pos = $opos;
749 + $this->seek($opos);
750 + }
751 + $x[$i]['names'][$nameId] = $N;
752 + }
753 + print_r($x); exit;
754 +*/
755 +
756 + $name_offset = $this->seek_table("name");
757 + $format = $this->read_ushort();
758 + if ($format != 0 && $format != 1) // mPDF 5.3.73
759 + die("Unknown name table format ".$format);
760 + $numRecords = $this->read_ushort();
761 + $string_data_offset = $name_offset + $this->read_ushort();
498 762 $names = array(1=>'',2=>'',3=>'',4=>'',6=>'');
499 763 $K = array_keys($names);
500 764 $nameCount = count($names);
501 765 for ($i=0;$i<$numRecords; $i++) {
@@ -534,8 +798,9 @@
534 798 if ($nameCount==0) break;
535 799 }
536 800 }
537 801 if ($names[6])
802 + // $psName = preg_replace('/ /','-',$names[6]); // mPDF 5.2.03
538 803 $psName = $names[6];
539 804 else if ($names[4])
540 805 $psName = preg_replace('/ /','-',$names[4]);
541 806 else if ($names[1])
@@ -543,24 +808,16 @@
543 808 else
544 809 $psName = '';
545 810 if (!$psName)
546 811 die("Could not find PostScript font name: ".$this->filename);
547 - // CHECK IF psName valid (PadaukBook contains illegal characters in Name ID 6 i.e. Postscript Name)
548 - $psNameInvalid = false;
549 - for ($i=0;$i<count($psName);$i++) {
812 + if ($debug) {
813 + for ($i=0;$i<count($psName);$i++) { // mPDF 5.3.07
550 814 $c = $psName[$i];
551 815 $oc = ord($c);
552 - if ($oc>126 || strpos(' [](){}<>/%',$c)!==false) {
553 - //die("psName=".$psName." contains invalid character ".$c." ie U+".ord(c));
554 - $psNameInvalid = true;
555 - break;
556 - }
816 + if ($oc>126 || strpos(' [](){}<>/%',$c)!==false)
817 + die("psName=".$psName." contains invalid character ".$c." ie U+".ord(c));
818 + }
557 819 }
558 -
559 - if ($psNameInvalid && $names[4])
560 - $psName = preg_replace('/ /','-',$names[4]);
561 -
562 -
563 820 $this->name = $psName;
564 821 if ($names[1]) { $this->familyName = $names[1]; } else { $this->familyName = $psName; }
565 822 if ($names[2]) { $this->styleName = $names[2]; } else { $this->styleName = 'Regular'; }
566 823 if ($names[4]) { $this->fullName = $names[4]; } else { $this->fullName = $psName; }
@@ -565,9 +822,9 @@
565 822 if ($names[2]) { $this->styleName = $names[2]; } else { $this->styleName = 'Regular'; }
566 823 if ($names[4]) { $this->fullName = $names[4]; } else { $this->fullName = $psName; }
567 824 if ($names[3]) { $this->uniqueFontID = $names[3]; } else { $this->uniqueFontID = $psName; }
568 825
569 - if (!$psNameInvalid && $names[6]) { $this->fullName = $names[6]; }
826 + if ($names[6]) { $this->fullName = $names[6]; } // mPDF 5.2.03
570 827
571 828 ///////////////////////////////////
572 829 // head - Font header table
573 830 ///////////////////////////////////
@@ -595,9 +852,8 @@
595 852 $yMin = $this->read_short();
596 853 $xMax = $this->read_short();
597 854 $yMax = $this->read_short();
598 855 $this->bbox = array(($xMin*$scale), ($yMin*$scale), ($xMax*$scale), ($yMax*$scale));
599 -
600 856 $this->skip(3*2);
601 857 $indexToLocFormat = $this->read_ushort();
602 858 $glyphDataFormat = $this->read_ushort();
603 859 if ($glyphDataFormat != 0)
@@ -605,25 +861,21 @@
605 861
606 862 ///////////////////////////////////
607 863 // hhea metrics table
608 864 ///////////////////////////////////
865 + // ttf2t1 seems to use this value rather than the one in OS/2 - so put in for compatibility
609 866 if (isset($this->tables["hhea"])) {
610 867 $this->seek_table("hhea");
611 868 $this->skip(4);
612 869 $hheaAscender = $this->read_short();
613 870 $hheaDescender = $this->read_short();
614 - $hheaLineGap = $this->read_short(); // mPDF 6
615 - $hheaAdvanceWidthMax = $this->read_ushort(); // mPDF 6
616 - $this->hheaascent = ($hheaAscender *$scale);
617 - $this->hheadescent = ($hheaDescender *$scale);
618 - $this->hhealineGap = ($hheaLineGap * $scale); // mPDF 6
619 - $this->advanceWidthMax = ($hheaAdvanceWidthMax * $scale); // mPDF 6
871 + $this->ascent = ($hheaAscender *$scale);
872 + $this->descent = ($hheaDescender *$scale);
620 873 }
621 874
622 875 ///////////////////////////////////
623 876 // OS/2 - OS/2 and Windows metrics table
624 877 ///////////////////////////////////
625 - $use_typo_metrics = false;
626 878 if (isset($this->tables["OS/2"])) {
627 879 $this->seek_table("OS/2");
628 880 $version = $this->read_ushort();
629 881 $this->skip(2);
@@ -630,18 +882,13 @@
630 882 $usWeightClass = $this->read_ushort();
631 883 $this->skip(2);
632 884 $fsType = $this->read_ushort();
633 885 if ($fsType == 0x0002 || ($fsType & 0x0300) != 0) {
886 + global $overrideTTFFontRestriction;
887 + if (!$overrideTTFFontRestriction) die('ERROR - Font file '.$this->filename.' cannot be embedded due to copyright restrictions.');
634 888 $this->restrictedUse = true;
635 889 }
636 -
637 - // mPDF 6
638 - $this->skip(16);
639 - $yStrikeoutSize = $this->read_short();
640 - $yStrikeoutPosition = $this->read_short();
641 - $this->strikeoutSize = ($yStrikeoutSize*$scale);
642 - $this->strikeoutPosition = ($yStrikeoutPosition*$scale);
643 -
890 + $this->skip(20);
644 891 $sF = $this->read_short();
645 892 $this->sFamilyClass = ($sF >> 8);
646 893 $this->sFamilySubClass = ($sF & 0xFF);
647 894 $this->_pos += 10; //PANOSE = 10 byte length
@@ -647,81 +894,30 @@
647 894 $this->_pos += 10; //PANOSE = 10 byte length
648 895 $panose = fread($this->fh,10);
649 896 $this->panose = array();
650 897 for ($p=0;$p<strlen($panose);$p++) { $this->panose[] = ord($panose[$p]); }
651 - //$this->skip(26);
652 - // mPDF 6
653 - $this->skip(20);
654 - $fsSelection = $this->read_ushort();
655 - $use_typo_metrics = (($fsSelection & 0x80) == 0x80); // bit#7 = USE_TYPO_METRICS
656 - $this->skip(4);
657 -
898 + $this->skip(26);
658 899 $sTypoAscender = $this->read_short();
659 900 $sTypoDescender = $this->read_short();
660 - $sTypoLineGap = $this->read_short(); // mPDF 6
661 - if ($sTypoAscender) $this->typoAscender = ($sTypoAscender*$scale); // mPDF 6
662 - if ($sTypoDescender) $this->typoDescender = ($sTypoDescender*$scale); // mPDF 6
663 - if ($sTypoLineGap ) $this->typoLineGap = ($sTypoLineGap * $scale); // mPDF 6
664 -
665 - $usWinAscent = $this->read_ushort(); // mPDF 6
666 - $usWinDescent = $this->read_ushort(); // mPDF 6
667 - if ($usWinAscent) $this->usWinAscent = ($usWinAscent*$scale); // mPDF 6
668 - if ($usWinDescent) $this->usWinDescent = ($usWinDescent*$scale); // mPDF 6
669 -
901 + if (!$this->ascent) $this->ascent = ($sTypoAscender*$scale);
902 + if (!$this->descent) $this->descent = ($sTypoDescender*$scale);
670 903 if ($version > 1) {
671 - $this->skip(8); // mPDF 6
672 - $sxHeight = $this->read_short();
673 - $this->xHeight = ($sxHeight*$scale);
904 + $this->skip(16);
674 905 $sCapHeight = $this->read_short();
675 906 $this->capHeight = ($sCapHeight*$scale);
676 907 }
908 + else {
909 + $this->capHeight = $this->ascent;
910 + }
677 911 }
678 912 else {
679 - $usWeightClass = 400;
913 + $usWeightClass = 500;
914 + if (!$this->ascent) $this->ascent = ($yMax*$scale);
915 + if (!$this->descent) $this->descent = ($yMin*$scale);
916 + $this->capHeight = $this->ascent;
680 917 }
681 918 $this->stemV = 50 + intval(pow(($usWeightClass / 65.0),2));
682 919
683 -
684 - // FONT DESCRIPTOR METRICS
685 - if (_FONT_DESCRIPTOR == 'winTypo') {
686 - $this->ascent = $this->typoAscender;
687 - $this->descent = $this->typoDescender;
688 - $this->lineGap = $this->typoLineGap;
689 - }
690 - else if (_FONT_DESCRIPTOR == 'mac') {
691 - $this->ascent = $this->hheaascent;
692 - $this->descent = $this->hheadescent;
693 - $this->lineGap = $this->hhealineGap;
694 - }
695 - else { // if (_FONT_DESCRIPTOR == 'win') { // default
696 - $this->ascent = $this->usWinAscent;
697 - $this->descent = -$this->usWinDescent;
698 - $this->lineGap = 0;
699 -
700 - /* Special case - if either the winAscent or winDescent are greater than the
701 - font bounding box yMin yMax, then reduce them accordingly.
702 - This works with Myanmar Text (Windows 8 version) to give a
703 - line-height normal that is equivalent to that produced in browsers.
704 - Also Khmer OS = compatible with MSWord, Wordpad and browser. */
705 - if ($this->ascent > $this->bbox[3]) { $this->ascent = $this->bbox[3]; }
706 - if ($this->descent < $this->bbox[1]) { $this->descent = $this->bbox[1]; }
707 -
708 -
709 - /* Override case - if the USE_TYPO_METRICS bit is set on OS/2 fsSelection
710 - this is telling the font to use the sTypo values and not the usWinAscent values.
711 - This works as a fix with Cambria Math to give a normal line-height;
712 - at present, this is the only font I have found with this bit set;
713 - although note that MS WordPad and windows FF browser uses the big line-height from winAscent
714 - but Word 2007 get it right . */
715 - if ($use_typo_metrics && $this->typoAscender) {
716 - $this->ascent = $this->typoAscender;
717 - $this->descent = $this->typoDescender;
718 - $this->lineGap = $this->typoLineGap;
719 - }
720 -
721 - }
722 -
723 -
724 920 ///////////////////////////////////
725 921 // post - PostScript table
726 922 ///////////////////////////////////
727 923 $this->seek_table("post");
@@ -787,13 +983,29 @@
787 983
788 984 ///////////////////////////////////
789 985 // cmap - Character to glyph index mapping table
790 986 ///////////////////////////////////
987 +/* Test purposes - displays list of cmaps
791 988 $cmap_offset = $this->seek_table("cmap");
792 989 $this->skip(2);
793 990 $cmapTableCount = $this->read_ushort();
794 991 $unicode_cmap_offset = 0;
795 992 for ($i=0;$i<$cmapTableCount;$i++) {
993 + $x[$i]['platformId'] = $this->read_ushort();
994 + $x[$i]['encodingId'] = $this->read_ushort();
995 + $x[$i]['offset'] = $this->read_ulong();
996 + $save_pos = $this->_pos;
997 + $x[$i]['format'] = $this->get_ushort($cmap_offset + $x[$i]['offset'] );
998 + $this->seek($save_pos );
999 + }
1000 + print_r($x); exit;
1001 +*/
1002 +
1003 + $cmap_offset = $this->seek_table("cmap");
1004 + $this->skip(2);
1005 + $cmapTableCount = $this->read_ushort();
1006 + $unicode_cmap_offset = 0;
1007 + for ($i=0;$i<$cmapTableCount;$i++) {
796 1008 $platformID = $this->read_ushort();
797 1009 $encodingID = $this->read_ushort();
798 1010 $offset = $this->read_ulong();
799 1011 $save_pos = $this->_pos;
@@ -800,9 +1012,9 @@
800 1012 if (($platformID == 3 && $encodingID == 1) || $platformID == 0) { // Microsoft, Unicode
801 1013 $format = $this->get_ushort($cmap_offset + $offset);
802 1014 if ($format == 4) {
803 1015 if (!$unicode_cmap_offset) $unicode_cmap_offset = $cmap_offset + $offset;
804 - if ($BMPonly) break;
1016 + if ($BMPonly) break; // mPDF 5.0
805 1017 }
806 1018 }
807 1019 // Microsoft, Unicode Format 12 table HKCS
808 1020 else if ((($platformID == 3 && $encodingID == 10) || $platformID == 0) && !$BMPonly) {
@@ -813,9 +1025,8 @@
813 1025 }
814 1026 }
815 1027 $this->seek($save_pos );
816 1028 }
817 -
818 1029 if (!$unicode_cmap_offset)
819 1030 die('Font ('.$this->filename .') does not have cmap for Unicode (platform 3, encoding 1, format 4, or platform 0, any encoding, format 4)');
820 1031
821 1032
@@ -820,23 +1031,11 @@
820 1031
821 1032
822 1033 $sipset = false;
823 1034 $smpset = false;
824 -
825 - // mPDF 5.7.1
826 - $this->rtlPUAstr = '';
827 - //$this->rtlPUAarr = array();
828 - $this->GSUBScriptLang = array();
829 - $this->GSUBFeatures = array();
830 - $this->GSUBLookups = array();
831 - $this->GPOSScriptLang = array();
832 - $this->GPOSFeatures = array();
833 - $this->GPOSLookups = array();
834 - $this->glyphIDtoUni = '';
835 -
836 1035 // Format 12 CMAP does characters above Unicode BMP i.e. some HKCS characters U+20000 and above
837 1036 if ($format == 12 && !$BMPonly) {
838 - $this->maxUniChar = 0;
1037 + $this->maxUniChar = 0; // mPDF 5.0
839 1038 $this->seek($unicode_cmap_offset + 4);
840 1039 $length = $this->read_ulong();
841 1040 $limit = $unicode_cmap_offset + $length;
842 1041 $this->skip(4);
@@ -848,11 +1047,10 @@
848 1047 for($i=0; $i<$nGroups ; $i++) {
849 1048 $startCharCode = $this->read_ulong();
850 1049 $endCharCode = $this->read_ulong();
851 1050 $startGlyphCode = $this->read_ulong();
852 - // ZZZ98
853 - if ($endCharCode > 0x20000 && $endCharCode < 0x2FFFF) {
854 - $sipset = true;
1051 + if (($endCharCode > 0x20000 && $endCharCode < 0x2A6DF) || ($endCharCode > 0x2F800 && $endCharCode < 0x2FA1F)) {
1052 + $sipset = true;
855 1053 }
856 1054 else if ($endCharCode > 0x10000 && $endCharCode < 0x1FFFF) {
857 1055 $smpset = true;
858 1056 }
@@ -859,14 +1057,11 @@
859 1057 $offset = 0;
860 1058 for ($unichar=$startCharCode;$unichar<=$endCharCode;$unichar++) {
861 1059 $glyph = $startGlyphCode + $offset ;
862 1060 $offset++;
863 - // ZZZ98
864 - if ($unichar < 0x30000) {
865 - $charToGlyph[$unichar] = $glyph;
866 - $this->maxUniChar = max($unichar,$this->maxUniChar);
867 - $glyphToChar[$glyph][] = $unichar;
868 - }
1061 + $charToGlyph[$unichar] = $glyph;
1062 + if ($unichar < 196608) { $this->maxUniChar = max($unichar,$this->maxUniChar); }
1063 + $glyphToChar[$glyph][] = $unichar;
869 1064 }
870 1065 }
871 1066 }
872 1067 else {
@@ -877,2403 +1072,50 @@
877 1072
878 1073 }
879 1074 $this->sipset = $sipset ;
880 1075 $this->smpset = $smpset ;
881 - ///////////////////////////////////
882 - // mPDF 5.7.1
883 - // Map Unmapped glyphs (or glyphs mapped to upper PUA U+F00000 onwards i.e. > U+2FFFF) - from $numGlyphs
884 - if ($this->useOTL) {
885 - $bctr = 0xE000;
886 - for ($gid=1; $gid<$numGlyphs; $gid++) {
887 - if (!isset($glyphToChar[$gid])) {
888 - while(isset($charToGlyph[$bctr])) { $bctr++; } // Avoid overwriting a glyph already mapped in PUA
889 - // ZZZ98
890 - if (($bctr > 0xF8FF) && ($bctr < 0x2CEB0)) {
891 - if (!$BMPonly) {
892 - $bctr = 0x2CEB0; // Use unassigned area 0x2CEB0 to 0x2F7FF (space for 10,000 characters)
893 - $this->sipset = $sipset = true; // forces subsetting; also ensure charwidths are saved
894 - while(isset($charToGlyph[$bctr])) { $bctr++; }
895 - }
896 - 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"); }
897 - }
898 - $glyphToChar[$gid][] = $bctr;
899 - $charToGlyph[$bctr] = $gid;
900 - $this->maxUniChar = max($bctr,$this->maxUniChar);
901 - $bctr++;
902 - }
903 - }
904 - }
905 1076
906 - $this->glyphToChar = $glyphToChar;
907 1077 ///////////////////////////////////
908 - // mPDF 5.7.1 OpenType Layout tables
909 - $this->GSUBScriptLang=array();
910 - $this->rtlPUAstr = '';
911 - //$this->rtlPUAarr = array();
912 - if ($useOTL) {
913 - $this->_getGDEFtables();
914 - list($this->GSUBScriptLang, $this->GSUBFeatures, $this->GSUBLookups, $this->rtlPUAstr) = $this->_getGSUBtables();
915 - // , $this->rtlPUAarr not needed
916 - list($this->GPOSScriptLang, $this->GPOSFeatures, $this->GPOSLookups) = $this->_getGPOStables();
917 - $this->glyphIDtoUni = str_pad('', 256*256*3, "\x00");
918 - foreach($glyphToChar AS $gid=>$arr) {
919 - if (isset($glyphToChar[$gid][0])) {
920 - $char = $glyphToChar[$gid][0];
921 -
922 - if ($char != 0 && $char != 65535) {
923 - $this->glyphIDtoUni[$gid*3] = chr($char >> 16);
924 - $this->glyphIDtoUni[$gid*3 + 1] = chr(($char >> 8) & 0xFF);
925 - $this->glyphIDtoUni[$gid*3 + 2] = chr($char & 0xFF);
926 - }
927 - }
928 - }
929 - }
930 - ///////////////////////////////////
931 -
932 -
933 - // if xHeight and/or CapHeight are not available from OS/2 (e.g. eraly versions)
934 - // Calculate from yMax of 'x' or 'H' Glyphs...
935 - if ($this->xHeight == 0) {
936 - if (isset($charToGlyph[0x78])) {
937 - $gidx = $charToGlyph[0x78]; // U+0078 (LATIN SMALL LETTER X)
938 - $start = $this->seek_table('loca');
939 - if ($indexToLocFormat == 0) {
940 - $this->skip($gidx*2);
941 - $locax = $this->read_ushort() * 2;
942 - }
943 - else if ($indexToLocFormat == 1) {
944 - $this->skip($gidx*4);
945 - $locax = $this->read_ulong();
946 - }
947 - $start = $this->seek_table('glyf');
948 - $this->skip($locax);
949 - $this->skip(8);
950 - $yMaxx = $this->read_short();
951 - $this->xHeight = $yMaxx * $scale;
952 - }
953 - }
954 - if ($this->capHeight == 0) {
955 - if (isset($charToGlyph[0x48])) {
956 - $gidH = $charToGlyph[0x48]; // U+0048 (LATIN CAPITAL LETTER H)
957 - $start = $this->seek_table('loca');
958 - if ($indexToLocFormat == 0) {
959 - $this->skip($gidH*2);
960 - $locaH = $this->read_ushort() * 2;
961 - }
962 - else if ($indexToLocFormat == 1) {
963 - $this->skip($gidH*4);
964 - $locaH = $this->read_ulong();
965 - }
966 - $start = $this->seek_table('glyf');
967 - $this->skip($locaH);
968 - $this->skip(8);
969 - $yMaxH = $this->read_short();
970 - $this->capHeight = $yMaxH * $scale;
971 - }
972 - else { $this->capHeight = $this->ascent; } // final default is to set it = to Ascent
973 - }
974 -
975 -
976 -
977 -
978 - ///////////////////////////////////
979 1078 // hmtx - Horizontal metrics table
980 1079 ///////////////////////////////////
981 1080 $this->getHMTX($numberOfHMetrics, $numGlyphs, $glyphToChar, $scale);
982 1081
983 1082 ///////////////////////////////////
984 - // kern - Kerning pair table
1083 + // kern - Kerning pair table mPDF 5.1
985 1084 ///////////////////////////////////
986 - // Recognises old form of Kerning table - as required by Windows - Format 0 only
987 - $kern_offset = $this->seek_table("kern");
988 - $version = $this->read_ushort();
989 - $nTables = $this->read_ushort();
990 - // subtable header
991 - $sversion = $this->read_ushort();
992 - $slength = $this->read_ushort();
993 - $scoverage = $this->read_ushort();
994 - $format = $scoverage >> 8;
995 - if ($kern_offset && $version==0 && $format==0) {
996 - // Format 0
997 - $nPairs = $this->read_ushort();
998 - $this->skip(6);
999 - for ($i=0; $i<$nPairs; $i++) {
1000 - $left = $this->read_ushort();
1001 - $right = $this->read_ushort();
1002 - $val = $this->read_short();
1003 - if (count($glyphToChar[$left])==1 && count($glyphToChar[$right])==1) {
1004 - if ($left != 32 && $right != 32) {
1005 - $this->kerninfo[$glyphToChar[$left][0]][$glyphToChar[$right][0]] = intval($val*$scale);
1006 - }
1007 - }
1008 - }
1009 - }
1010 - }
1011 -
1012 -
1013 -/////////////////////////////////////////////////////////////////////////////////////////
1014 - function _getGDEFtables() {
1015 - ///////////////////////////////////
1016 - // GDEF - Glyph Definition
1017 - ///////////////////////////////////
1018 - // http://www.microsoft.com/typography/otspec/gdef.htm
1019 - if (isset($this->tables["GDEF"])) {
1020 - $gdef_offset = $this->seek_table("GDEF");
1021 - // ULONG Version of the GDEF table-currently 0x00010000
1022 - $ver_maj = $this->read_ushort();
1023 - $ver_min = $this->read_ushort();
1024 - $GlyphClassDef_offset = $this->read_ushort();
1025 - $AttachList_offset = $this->read_ushort();
1026 - $LigCaretList_offset = $this->read_ushort();
1027 - $MarkAttachClassDef_offset = $this->read_ushort();
1028 - // Version 0x00010002 of GDEF header contains additional Offset to a list defining mark glyph set definitions (MarkGlyphSetDef)
1029 - if ($ver_min == 2) {
1030 - $MarkGlyphSetsDef_offset = $this->read_ushort();
1031 - }
1032 -
1033 - // GlyphClassDef
1034 - if ($GlyphClassDef_offset) {
1035 - $this->seek($gdef_offset+$GlyphClassDef_offset );
1036 - /*
1037 - 1 Base glyph (single character, spacing glyph)
1038 - 2 Ligature glyph (multiple character, spacing glyph)
1039 - 3 Mark glyph (non-spacing combining glyph)
1040 - 4 Component glyph (part of single character, spacing glyph)
1041 - */
1042 - $GlyphByClass = $this->_getClassDefinitionTable();
1043 - }
1044 - else { $GlyphByClass = array(); }
1045 -
1046 - if (isset($GlyphByClass[1]) && count($GlyphByClass[1])>0) { $this->GlyphClassBases = ' '.implode('| ',$GlyphByClass[1]); }
1047 - else { $this->GlyphClassBases = ''; }
1048 - if (isset($GlyphByClass[2]) && count($GlyphByClass[2])>0) { $this->GlyphClassLigatures = ' '.implode('| ',$GlyphByClass[2]); }
1049 - else { $this->GlyphClassLigatures = ''; }
1050 - if (isset($GlyphByClass[3]) && count($GlyphByClass[3])>0) { $this->GlyphClassMarks = ' '.implode('| ',$GlyphByClass[3]); }
1051 - else { $this->GlyphClassMarks = ''; }
1052 - if (isset($GlyphByClass[4]) && count($GlyphByClass[4])>0) { $this->GlyphClassComponents = ' '.implode('| ',$GlyphByClass[4]); }
1053 - else { $this->GlyphClassComponents = ''; }
1054 -
1055 - if (isset($GlyphByClass[3]) && count($GlyphByClass[3])>0) { $Marks = $GlyphByClass[3]; } // to use for MarkAttachmentType
1056 - else { $Marks = array(); }
1057 -
1058 -
1059 -
1060 -/* Required for GPOS
1061 - // Attachment List
1062 - if ($AttachList_offset) {
1063 - $this->seek($gdef_offset+$AttachList_offset );
1064 - }
1065 -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.
1066 -
1067 -The Attachment List table (AttachList) may be used to cache attachment point coordinates along with glyph bitmaps.
1068 -
1069 -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.
1070 -AttachList table
1071 -Type Name Description
1072 -Offset Coverage Offset to Coverage table - from beginning of AttachList table
1073 -uint16 GlyphCount Number of glyphs with attachment points
1074 -Offset AttachPoint[GlyphCount] Array of offsets to AttachPoint tables-from beginning of AttachList table-in Coverage Index order
1075 -
1076 -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.
1077 -
1078 -AttachPoint table
1079 -Type Name Description
1080 -uint16 PointCount Number of attachment points on this glyph
1081 -uint16 PointIndex[PointCount] Array of contour point indices -in increasing numerical order
1082 -
1083 -See Example 3 - http://www.microsoft.com/typography/otspec/gdef.htm
1084 -*/
1085 -
1086 -
1087 - // Ligature Caret List
1088 - // The Ligature Caret List table (LigCaretList) defines caret positions for all the ligatures in a font.
1089 - // Not required for mDPF
1090 -
1091 -
1092 - // MarkAttachmentType
1093 - if ($MarkAttachClassDef_offset) {
1094 - $this->seek($gdef_offset+$MarkAttachClassDef_offset);
1095 - $MarkAttachmentTypes = $this->_getClassDefinitionTable();
1096 - foreach($MarkAttachmentTypes AS $class=>$glyphs) {
1097 -
1098 - if (is_array($Marks) && count($Marks)) {
1099 - $mat = array_diff($Marks, $MarkAttachmentTypes[$class]);
1100 - sort($mat, SORT_STRING);
1085 + if ($kerninfo) {
1086 + // Recognises old form of Kerning table - as required by Windows - Format 0 only
1087 + $kern_offset = $this->seek_table("kern");
1088 + $version = $this->read_ushort();
1089 + $nTables = $this->read_ushort();
1090 + // subtable header
1091 + $sversion = $this->read_ushort();
1092 + $slength = $this->read_ushort();
1093 + $scoverage = $this->read_ushort();
1094 + $format = $scoverage >> 8;
1095 + if ($kern_offset && $version==0 && $format==0) {
1096 + // Format 0
1097 + $nPairs = $this->read_ushort();
1098 + $this->skip(6);
1099 + for ($i=0; $i<$nPairs; $i++) {
1100 + $left = $this->read_ushort();
1101 + $right = $this->read_ushort();
1102 + $val = $this->read_short();
1103 + if (count($glyphToChar[$left])==1 && count($glyphToChar[$right])==1) {
1104 + if ($left != 32 && $right != 32) {
1105 + $this->kerninfo[$glyphToChar[$left][0]][$glyphToChar[$right][0]] = intval($val*$scale);
1106 + }
1101 1107 }
1102 - else { $mat = array(); }
1103 -
1104 - $this->MarkAttachmentType[$class] = ' '.implode('| ',$mat);
1105 1108 }
1106 1109 }
1107 - else { $this->MarkAttachmentType = array(); }
1108 -
1109 -
1110 - // MarkGlyphSets only in Version 0x00010002 of GDEF
1111 - if ($ver_min == 2 && $MarkGlyphSetsDef_offset) {
1112 - $this->seek($gdef_offset+$MarkGlyphSetsDef_offset);
1113 - $MarkSetTableFormat = $this->read_ushort();
1114 - $MarkSetCount = $this->read_ushort();
1115 - $MarkSetOffset = array();
1116 - for ($i=0;$i<$MarkSetCount;$i++) {
1117 - $MarkSetOffset[] = $this->read_ulong();
1118 - }
1119 - for ($i=0;$i<$MarkSetCount;$i++) {
1120 - $this->seek($MarkSetOffset[$i]);
1121 - $glyphs = $this->_getCoverage();
1122 - $this->MarkGlyphSets[$i] = ' '.implode('| ',$glyphs);
1123 - }
1124 - }
1125 - else { $this->MarkGlyphSets = array(); }
1126 1110 }
1127 - else { die('Warning - You cannot set this font ('.$this->filename.') to use OTL, as it does not include OTL tables (or at least, not a GDEF table).'); }
1128 -
1129 - //=====================================================================================
1130 - //=====================================================================================
1131 - //=====================================================================================
1132 - $GSUB_offset = 0;
1133 - $GPOS_offset = 0;
1134 - $GSUB_length = 0;
1135 - $s = '';
1136 - if (isset($this->tables["GSUB"])) {
1137 - $GSUB_offset = $this->seek_table("GSUB");
1138 - $GSUB_length = $this->tables["GSUB"]['length'];
1139 - $s .= fread($this->fh,$this->tables["GSUB"]['length']);
1140 - }
1141 - if (isset($this->tables["GPOS"])) {
1142 - $GPOS_offset = $this->seek_table("GPOS");
1143 - $s .= fread($this->fh,$this->tables["GPOS"]['length']);
1144 - }
1145 - if ($s) file_put_contents(_MPDF_TTFONTDATAPATH.$this->fontkey.'.GSUBGPOStables.dat', $s);
1146 -
1147 - //=====================================================================================
1148 - //=====================================================================================
1149 -
1150 - $s = '<?php
1151 -$GSUB_offset = '.$GSUB_offset.';
1152 -$GPOS_offset = '.$GPOS_offset.';
1153 -$GSUB_length = '.$GSUB_length.';
1154 -$GlyphClassBases = \''.$this->GlyphClassBases.'\';
1155 -$GlyphClassMarks = \''.$this->GlyphClassMarks.'\';
1156 -$GlyphClassLigatures = \''.$this->GlyphClassLigatures.'\';
1157 -$GlyphClassComponents = \''.$this->GlyphClassComponents.'\';
1158 -$MarkGlyphSets = '.var_export($this->MarkGlyphSets , true).';
1159 -$MarkAttachmentType = '.var_export($this->MarkAttachmentType , true).';
1160 -?>';
1161 -
1162 -
1163 - file_put_contents(_MPDF_TTFONTDATAPATH.$this->fontkey.'.GDEFdata.php', $s);
1164 -
1165 - //=====================================================================================
1166 -
1167 -
1168 -//echo $this->GlyphClassMarks ; exit;
1169 -//print_r($GlyphClass); exit;
1170 -//print_r($GlyphByClass); exit;
1171 1111 }
1172 1112
1173 - function _getClassDefinitionTable() {
1174 1113
1175 - // NB Any glyph not included in the range of covered GlyphIDs automatically belongs to Class 0. This is not returned by this function
1176 - $ClassFormat = $this->read_ushort();
1177 - $GlyphByClass = array();
1178 - if ($ClassFormat == 1) {
1179 - $StartGlyph = $this->read_ushort();
1180 - $GlyphCount = $this->read_ushort();
1181 - for ($i=0;$i<$GlyphCount;$i++) {
1182 - $gid = $StartGlyph + $i;
1183 - $class = $this->read_ushort();
1184 - // Several fonts (mainly dejavu.../Freeserif etc) have a MarkAttachClassDef Format 1, where StartGlyph is 0 and GlyphCount is 1
1185 - // This doesn't seem to do anything useful?
1186 - // Freeserif does not have $this->glyphToChar[0] allocated and would throw an error, so check if isset:
1187 - if (isset($this->glyphToChar[$gid][0])) {
1188 - $GlyphByClass[$class][] = unicode_hex($this->glyphToChar[$gid][0]);
1189 - }
1190 - }
1191 - }
1192 - else if ($ClassFormat == 2) {
1193 - $tableCount = $this->read_ushort();
1194 - for ($i=0;$i<$tableCount;$i++) {
1195 - $startGlyphID = $this->read_ushort();
1196 - $endGlyphID = $this->read_ushort();
1197 - $class = $this->read_ushort();
1198 - for($gid=$startGlyphID;$gid<=$endGlyphID;$gid++) {
1199 - if (isset($this->glyphToChar[$gid][0])) {
1200 - $GlyphByClass[$class][] = unicode_hex($this->glyphToChar[$gid][0]);
1201 - }
1202 - }
1203 - }
1204 - }
1205 - foreach($GlyphByClass AS $class=>$glyphs) {
1206 - sort($GlyphByClass[$class], SORT_STRING); // SORT makes it easier to read in development ? order not important ???
1207 - }
1208 - ksort($GlyphByClass);
1209 - return $GlyphByClass;
1210 - }
1211 -
1212 - function _getGSUBtables() {
1213 - ///////////////////////////////////
1214 - // GSUB - Glyph Substitution
1215 - ///////////////////////////////////
1216 - if (isset($this->tables["GSUB"])) {
1217 - $ffeats = array();
1218 - $gsub_offset = $this->seek_table("GSUB");
1219 - $this->skip(4);
1220 - $ScriptList_offset = $gsub_offset + $this->read_ushort();
1221 - $FeatureList_offset = $gsub_offset + $this->read_ushort();
1222 - $LookupList_offset = $gsub_offset + $this->read_ushort();
1223 -
1224 - // ScriptList
1225 - $this->seek($ScriptList_offset );
1226 - $ScriptCount = $this->read_ushort();
1227 - for ($i=0;$i<$ScriptCount;$i++) {
1228 - $ScriptTag = $this->read_tag(); // = "beng", "deva" etc.
1229 - $ScriptTableOffset = $this->read_ushort();
1230 - $ffeats[$ScriptTag] = $ScriptList_offset + $ScriptTableOffset;
1231 - }
1232 -
1233 - // Script Table
1234 - foreach($ffeats AS $t=>$o) {
1235 - $ls = array();
1236 - $this->seek($o);
1237 - $DefLangSys_offset = $this->read_ushort();
1238 - if ($DefLangSys_offset > 0) {
1239 - $ls['DFLT'] = $DefLangSys_offset + $o;
1240 - }
1241 - $LangSysCount = $this->read_ushort();
1242 - for ($i=0;$i<$LangSysCount;$i++) {
1243 - $LangTag = $this->read_tag(); // =
1244 - $LangTableOffset = $this->read_ushort();
1245 - $ls[$LangTag] = $o + $LangTableOffset;
1246 - }
1247 - $ffeats[$t] = $ls;
1248 - }
1249 -//print_r($ffeats); exit;
1250 -
1251 -
1252 - // Get FeatureIndexList
1253 - // LangSys Table - from first listed langsys
1254 - foreach($ffeats AS $st=>$scripts) {
1255 - foreach($scripts AS $t=>$o) {
1256 - $FeatureIndex = array();
1257 - $langsystable_offset = $o;
1258 - $this->seek($langsystable_offset);
1259 - $LookUpOrder = $this->read_ushort(); //==NULL
1260 - $ReqFeatureIndex = $this->read_ushort();
1261 - if ($ReqFeatureIndex != 0xFFFF) { $FeatureIndex[] = $ReqFeatureIndex; }
1262 - $FeatureCount = $this->read_ushort();
1263 - for ($i=0;$i<$FeatureCount;$i++) {
1264 - $FeatureIndex[] = $this->read_ushort(); // = index of feature
1265 - }
1266 - $ffeats[$st][$t] = $FeatureIndex;
1267 - }
1268 - }
1269 -//print_r($ffeats); exit;
1270 -
1271 -
1272 - // Feauture List => LookupListIndex es
1273 - $this->seek($FeatureList_offset );
1274 - $FeatureCount = $this->read_ushort();
1275 - $Feature = array();
1276 - for ($i=0;$i<$FeatureCount;$i++) {
1277 - $tag = $this->read_tag() ;
1278 - if ($tag == 'smcp') { $this->hassmallcapsGSUB = true; }
1279 - $Feature[$i] = array('tag' => $tag);
1280 - $Feature[$i]['offset'] = $FeatureList_offset + $this->read_ushort();
1281 - }
1282 - for ($i=0;$i<$FeatureCount;$i++) {
1283 - $this->seek($Feature[$i]['offset']);
1284 - $this->read_ushort(); // null [FeatureParams]
1285 - $Feature[$i]['LookupCount'] = $Lookupcount = $this->read_ushort();
1286 - $Feature[$i]['LookupListIndex'] = array();
1287 - for ($c=0;$c<$Lookupcount;$c++) {
1288 - $Feature[$i]['LookupListIndex'][] = $this->read_ushort();
1289 - }
1290 - }
1291 -
1292 -//print_r($Feature); exit;
1293 -
1294 - foreach($ffeats AS $st=>$scripts) {
1295 - foreach($scripts AS $t=>$o) {
1296 - $FeatureIndex = $ffeats[$st][$t];
1297 - foreach($FeatureIndex AS $k=>$fi) {
1298 - $ffeats[$st][$t][$k] = $Feature[$fi];
1299 - }
1300 - }
1301 - }
1302 - //=====================================================================================
1303 - $gsub = array();
1304 - $GSUBScriptLang = array();
1305 - foreach($ffeats AS $st=>$scripts) {
1306 - foreach($scripts AS $t=>$langsys) {
1307 - $lg = array();
1308 - foreach($langsys AS $ft) {
1309 - $lg[$ft['LookupListIndex'][0]] = $ft;
1310 - }
1311 - // list of Lookups in order they need to be run i.e. order listed in Lookup table
1312 - ksort($lg);
1313 - foreach($lg AS $ft) {
1314 - $gsub[$st][$t][$ft['tag']] = $ft['LookupListIndex'];
1315 - }
1316 - if (!isset($GSUBScriptLang[$st])) { $GSUBScriptLang[$st] = ''; }
1317 - $GSUBScriptLang[$st] .= $t.' ';
1318 - }
1319 - }
1320 -
1321 -//print_r($gsub); exit;
1322 -
1323 -
1324 -
1325 - //=====================================================================================
1326 - // Get metadata and offsets for whole Lookup List table
1327 - $this->seek($LookupList_offset );
1328 - $LookupCount = $this->read_ushort();
1329 - $GSLookup = array();
1330 - $Offsets = array();
1331 - $SubtableCount = array();
1332 - for ($i=0;$i<$LookupCount;$i++) {
1333 - $Offsets[$i] = $LookupList_offset + $this->read_ushort();
1334 - }
1335 - for ($i=0;$i<$LookupCount;$i++) {
1336 - $this->seek($Offsets[$i]);
1337 - $GSLookup[$i]['Type'] = $this->read_ushort();
1338 - $GSLookup[$i]['Flag'] = $flag = $this->read_ushort();
1339 - $GSLookup[$i]['SubtableCount'] = $SubtableCount[$i] = $this->read_ushort();
1340 - for ($c=0;$c<$SubtableCount[$i] ;$c++) {
1341 - $GSLookup[$i]['Subtables'][$c] = $Offsets[$i] + $this->read_ushort();
1342 -
1343 - }
1344 - // MarkFilteringSet = Index (base 0) into GDEF mark glyph sets structure
1345 - if (($flag & 0x0010) == 0x0010) {
1346 - $GSLookup[$i]['MarkFilteringSet'] = $this->read_ushort();
1347 - }
1348 - else { $GSLookup[$i]['MarkFilteringSet'] = ''; }
1349 -
1350 - // Lookup Type 7: Extension
1351 - if ($GSLookup[$i]['Type'] == 7) {
1352 - // Overwrites new offset (32-bit) for each subtable, and a new lookup Type
1353 - for ($c=0;$c<$SubtableCount[$i] ;$c++) {
1354 - $this->seek($GSLookup[$i]['Subtables'][$c]);
1355 - $ExtensionPosFormat = $this->read_ushort();
1356 - $type = $this->read_ushort();
1357 - $ext_offset = $this->read_ulong();
1358 - $GSLookup[$i]['Subtables'][$c] = $GSLookup[$i]['Subtables'][$c] + $ext_offset;
1359 - }
1360 - $GSLookup[$i]['Type'] = $type;
1361 - }
1362 - }
1363 -
1364 -//print_r($GSLookup); exit;
1365 - //=====================================================================================
1366 - // Process Whole LookupList - Get LuCoverage = Lookup coverage just for first glyph
1367 - $this->GSLuCoverage = array();
1368 - for ($i=0;$i<$LookupCount;$i++) {
1369 - for ($c=0;$c<$GSLookup[$i]['SubtableCount'] ;$c++) {
1370 -
1371 - $this->seek($GSLookup[$i]['Subtables'][$c]);
1372 - $PosFormat= $this->read_ushort();
1373 -
1374 - if ($GSLookup[$i]['Type']==5 && $PosFormat==3) { $this->skip(4); }
1375 - else if ($GSLookup[$i]['Type']==6 && $PosFormat==3) {
1376 - $BacktrackGlyphCount= $this->read_ushort();
1377 - $this->skip(2*$BacktrackGlyphCount + 2);
1378 - }
1379 - // NB Coverage only looks at glyphs for position 1 (i.e. 5.3 and 6.3) // NEEDS TO READ ALL ********************
1380 - $Coverage = $GSLookup[$i]['Subtables'][$c] + $this->read_ushort();
1381 - $this->seek($Coverage);
1382 - $glyphs = $this->_getCoverage(false,2);
1383 - $this->GSLuCoverage[$i][$c] = $glyphs;
1384 - }
1385 - }
1386 -
1387 -// $this->GSLuCoverage and $GSLookup
1388 -
1389 - //=====================================================================================
1390 - //=====================================================================================
1391 - $s = '<?php
1392 -$GSLuCoverage = '.var_export($this->GSLuCoverage , true).';
1393 -?>';
1394 -
1395 - file_put_contents(_MPDF_TTFONTDATAPATH.$this->fontkey.'.GSUBdata.php', $s);
1396 -
1397 - //=====================================================================================
1398 - //=====================================================================================
1399 - //=====================================================================================
1400 - //=====================================================================================
1401 -// Now repeats as original to get Substitution rules
1402 - //=====================================================================================
1403 - //=====================================================================================
1404 - //=====================================================================================
1405 - // Get metadata and offsets for whole Lookup List table
1406 - $this->seek($LookupList_offset );
1407 - $LookupCount = $this->read_ushort();
1408 - $Lookup = array();
1409 - for ($i=0;$i<$LookupCount;$i++) {
1410 - $Lookup[$i]['offset'] = $LookupList_offset + $this->read_ushort();
1411 - }
1412 - for ($i=0;$i<$LookupCount;$i++) {
1413 - $this->seek($Lookup[$i]['offset']);
1414 - $Lookup[$i]['Type'] = $this->read_ushort();
1415 - $Lookup[$i]['Flag'] = $flag = $this->read_ushort();
1416 - $Lookup[$i]['SubtableCount'] = $this->read_ushort();
1417 - for ($c=0;$c<$Lookup[$i]['SubtableCount'] ;$c++) {
1418 - $Lookup[$i]['Subtable'][$c]['Offset'] = $Lookup[$i]['offset'] + $this->read_ushort();
1419 -
1420 - }
1421 - // MarkFilteringSet = Index (base 0) into GDEF mark glyph sets structure
1422 - if (($flag & 0x0010) == 0x0010) {
1423 - $Lookup[$i]['MarkFilteringSet'] = $this->read_ushort();
1424 - }
1425 - else { $Lookup[$i]['MarkFilteringSet'] = ''; }
1426 -
1427 - // Lookup Type 7: Extension
1428 - if ($Lookup[$i]['Type'] == 7) {
1429 - // Overwrites new offset (32-bit) for each subtable, and a new lookup Type
1430 - for ($c=0;$c<$Lookup[$i]['SubtableCount'] ;$c++) {
1431 - $this->seek($Lookup[$i]['Subtable'][$c]['Offset']);
1432 - $ExtensionPosFormat = $this->read_ushort();
1433 - $type = $this->read_ushort();
1434 - $Lookup[$i]['Subtable'][$c]['Offset'] = $Lookup[$i]['Subtable'][$c]['Offset'] + $this->read_ulong();
1435 - }
1436 - $Lookup[$i]['Type'] = $type;
1437 - }
1438 -
1439 - }
1440 -
1441 -//print_r($Lookup); exit;
1442 - //=====================================================================================
1443 - // Process (1) Whole LookupList
1444 - for ($i=0;$i<$LookupCount;$i++) {
1445 - for ($c=0;$c<$Lookup[$i]['SubtableCount'] ;$c++) {
1446 -
1447 - $this->seek($Lookup[$i]['Subtable'][$c]['Offset']);
1448 - $SubstFormat= $this->read_ushort();
1449 - $Lookup[$i]['Subtable'][$c]['Format'] = $SubstFormat;
1450 -
1451 -/*
1452 -Lookup['Type'] Enumeration table for glyph substitution
1453 -Value Type Description
1454 -1 Single Replace one glyph with one glyph
1455 -2 Multiple Replace one glyph with more than one glyph
1456 -3 Alternate Replace one glyph with one of many glyphs
1457 -4 Ligature Replace multiple glyphs with one glyph
1458 -5 Context Replace one or more glyphs in context
1459 -6 Chaining Context Replace one or more glyphs in chained context
1460 -7 Extension Substitution Extension mechanism for other substitutions (i.e. this excludes the Extension type substitution itself)
1461 -8 Reverse chaining context single Applied in reverse order, replace single glyph in chaining context
1462 -*/
1463 -
1464 - // LookupType 1: Single Substitution Subtable
1465 - if ($Lookup[$i]['Type'] == 1) {
1466 - $Lookup[$i]['Subtable'][$c]['CoverageTableOffset'] = $Lookup[$i]['Subtable'][$c]['Offset'] + $this->read_ushort();
1467 - if ($SubstFormat==1) { // Calculated output glyph indices
1468 - $Lookup[$i]['Subtable'][$c]['DeltaGlyphID'] = $this->read_short();
1469 - }
1470 - else if ($SubstFormat==2) { // Specified output glyph indices
1471 - $GlyphCount = $this->read_ushort();
1472 - for ($g=0;$g<$GlyphCount;$g++) {
1473 - $Lookup[$i]['Subtable'][$c]['Glyphs'][] = $this->read_ushort();
1474 - }
1475 - }
1476 - }
1477 - // LookupType 2: Multiple Substitution Subtable
1478 - else if ($Lookup[$i]['Type'] == 2) {
1479 - $Lookup[$i]['Subtable'][$c]['CoverageTableOffset'] = $Lookup[$i]['Subtable'][$c]['Offset'] + $this->read_ushort();
1480 - $Lookup[$i]['Subtable'][$c]['SequenceCount'] = $SequenceCount = $this->read_short();
1481 - for($s=0;$s<$SequenceCount;$s++) {
1482 - $Lookup[$i]['Subtable'][$c]['Sequences'][$s]['Offset'] = $Lookup[$i]['Subtable'][$c]['Offset'] + $this->read_short();
1483 - }
1484 - for($s=0;$s<$SequenceCount;$s++) {
1485 - // Sequence Tables
1486 - $this->seek($Lookup[$i]['Subtable'][$c]['Sequences'][$s]['Offset']);
1487 - $Lookup[$i]['Subtable'][$c]['Sequences'][$s]['GlyphCount'] = $this->read_short();
1488 - for ($g=0;$g<$Lookup[$i]['Subtable'][$c]['Sequences'][$s]['GlyphCount'];$g++) {
1489 - $Lookup[$i]['Subtable'][$c]['Sequences'][$s]['SubstituteGlyphID'][] = $this->read_ushort();
1490 - }
1491 - }
1492 - }
1493 - // LookupType 3: Alternate Forms
1494 - else if ($Lookup[$i]['Type'] == 3) {
1495 - $Lookup[$i]['Subtable'][$c]['CoverageTableOffset'] = $Lookup[$i]['Subtable'][$c]['Offset'] + $this->read_ushort();
1496 - $Lookup[$i]['Subtable'][$c]['AlternateSetCount'] = $AlternateSetCount = $this->read_short();
1497 - for($s=0;$s<$AlternateSetCount;$s++) {
1498 - $Lookup[$i]['Subtable'][$c]['AlternateSets'][$s]['Offset'] = $Lookup[$i]['Subtable'][$c]['Offset'] + $this->read_short();
1499 - }
1500 -
1501 - for($s=0;$s<$AlternateSetCount;$s++) {
1502 - // AlternateSet Tables
1503 - $this->seek($Lookup[$i]['Subtable'][$c]['AlternateSets'][$s]['Offset']);
1504 - $Lookup[$i]['Subtable'][$c]['AlternateSets'][$s]['GlyphCount'] = $this->read_short();
1505 - for ($g=0;$g<$Lookup[$i]['Subtable'][$c]['AlternateSets'][$s]['GlyphCount'];$g++) {
1506 - $Lookup[$i]['Subtable'][$c]['AlternateSets'][$s]['SubstituteGlyphID'][] = $this->read_ushort();
1507 - }
1508 - }
1509 - }
1510 - // LookupType 4: Ligature Substitution Subtable
1511 - else if ($Lookup[$i]['Type'] == 4) {
1512 - $Lookup[$i]['Subtable'][$c]['CoverageTableOffset'] = $Lookup[$i]['Subtable'][$c]['Offset'] + $this->read_ushort();
1513 - $Lookup[$i]['Subtable'][$c]['LigSetCount'] = $LigSetCount = $this->read_short();
1514 - for($s=0;$s<$LigSetCount;$s++) {
1515 - $Lookup[$i]['Subtable'][$c]['LigSet'][$s]['Offset'] = $Lookup[$i]['Subtable'][$c]['Offset'] + $this->read_short();
1516 - }
1517 - for($s=0;$s<$LigSetCount;$s++) {
1518 - // LigatureSet Tables
1519 - $this->seek($Lookup[$i]['Subtable'][$c]['LigSet'][$s]['Offset']);
1520 - $Lookup[$i]['Subtable'][$c]['LigSet'][$s]['LigCount'] = $this->read_short();
1521 - for ($g=0;$g<$Lookup[$i]['Subtable'][$c]['LigSet'][$s]['LigCount'];$g++) {
1522 - $Lookup[$i]['Subtable'][$c]['LigSet'][$s]['LigatureOffset'][$g] = $Lookup[$i]['Subtable'][$c]['LigSet'][$s]['Offset'] + $this->read_ushort();
1523 - }
1524 - }
1525 - for($s=0;$s<$LigSetCount;$s++) {
1526 - for ($g=0;$g<$Lookup[$i]['Subtable'][$c]['LigSet'][$s]['LigCount'];$g++) {
1527 - // Ligature tables
1528 - $this->seek($Lookup[$i]['Subtable'][$c]['LigSet'][$s]['LigatureOffset'][$g]);
1529 - $Lookup[$i]['Subtable'][$c]['LigSet'][$s]['Ligature'][$g]['LigGlyph'] = $this->read_ushort();
1530 - $Lookup[$i]['Subtable'][$c]['LigSet'][$s]['Ligature'][$g]['CompCount'] = $this->read_ushort();
1531 - for ($l=1;$l<$Lookup[$i]['Subtable'][$c]['LigSet'][$s]['Ligature'][$g]['CompCount'];$l++) {
1532 - $Lookup[$i]['Subtable'][$c]['LigSet'][$s]['Ligature'][$g]['GlyphID'][$l] = $this->read_ushort();
1533 - }
1534 - }
1535 - }
1536 - }
1537 -
1538 - // LookupType 5: Contextual Substitution Subtable
1539 - else if ($Lookup[$i]['Type'] == 5) {
1540 - // Format 1: Context Substitution
1541 - if ($SubstFormat==1) {
1542 - $Lookup[$i]['Subtable'][$c]['CoverageTableOffset'] = $Lookup[$i]['Subtable'][$c]['Offset'] + $this->read_ushort();
1543 - $Lookup[$i]['Subtable'][$c]['SubRuleSetCount'] = $SubRuleSetCount = $this->read_short();
1544 - for($s=0;$s<$SubRuleSetCount;$s++) {
1545 - $Lookup[$i]['Subtable'][$c]['SubRuleSet'][$s]['Offset'] = $Lookup[$i]['Subtable'][$c]['Offset'] + $this->read_short();
1546 - }
1547 - for($s=0;$s<$SubRuleSetCount;$s++) {
1548 - // SubRuleSet Tables
1549 - $this->seek($Lookup[$i]['Subtable'][$c]['SubRuleSet'][$s]['Offset']);
1550 - $Lookup[$i]['Subtable'][$c]['SubRuleSet'][$s]['SubRuleCount'] = $this->read_short();
1551 - for ($g=0;$g<$Lookup[$i]['Subtable'][$c]['SubRuleSet'][$s]['SubRuleCount'];$g++) {
1552 - $Lookup[$i]['Subtable'][$c]['SubRuleSet'][$s]['SubRuleOffset'][$g] = $Lookup[$i]['Subtable'][$c]['SubRuleSet'][$s]['Offset'] + $this->read_ushort();
1553 - }
1554 - }
1555 - for($s=0;$s<$SubRuleSetCount;$s++) {
1556 - // SubRule Tables
1557 - for ($g=0;$g<$Lookup[$i]['Subtable'][$c]['SubRuleSet'][$s]['SubRuleCount'];$g++) {
1558 - // Ligature tables
1559 - $this->seek($Lookup[$i]['Subtable'][$c]['SubRuleSet'][$s]['SubRuleOffset'][$g]);
1560 -
1561 - $Lookup[$i]['Subtable'][$c]['SubRuleSet'][$s]['SubRule'][$g]['GlyphCount'] = $this->read_ushort();
1562 - $Lookup[$i]['Subtable'][$c]['SubRuleSet'][$s]['SubRule'][$g]['SubstCount'] = $this->read_ushort();
1563 - // "Input"::[GlyphCount - 1]::Array of input GlyphIDs-start with second glyph
1564 - for ($l=1;$l<$Lookup[$i]['Subtable'][$c]['SubRuleSet'][$s]['SubRule'][$g]['GlyphCount'];$l++) {
1565 - $Lookup[$i]['Subtable'][$c]['SubRuleSet'][$s]['SubRule'][$g]['Input'][$l] = $this->read_ushort();
1566 - }
1567 - // "SubstLookupRecord"::[SubstCount]::Array of SubstLookupRecords-in design order
1568 - for ($l=0;$l<$Lookup[$i]['Subtable'][$c]['SubRuleSet'][$s]['SubRule'][$g]['SubstCount'];$l++) {
1569 - $Lookup[$i]['Subtable'][$c]['SubRuleSet'][$s]['SubRule'][$g]['SubstLookupRecord'][$l]['SequenceIndex'] = $this->read_ushort();
1570 - $Lookup[$i]['Subtable'][$c]['SubRuleSet'][$s]['SubRule'][$g]['SubstLookupRecord'][$l]['LookupListIndex'] = $this->read_ushort();
1571 - }
1572 -
1573 - }
1574 - }
1575 -
1576 - }
1577 - // Format 2: Class-based Context Glyph Substitution
1578 - else if ($SubstFormat==2) {
1579 - $Lookup[$i]['Subtable'][$c]['CoverageTableOffset'] = $Lookup[$i]['Subtable'][$c]['Offset'] + $this->read_ushort();
1580 - $Lookup[$i]['Subtable'][$c]['ClassDefOffset'] = $Lookup[$i]['Subtable'][$c]['Offset'] + $this->read_ushort();
1581 - $Lookup[$i]['Subtable'][$c]['SubClassSetCnt'] = $this->read_ushort();
1582 - for ($b=0;$b<$Lookup[$i]['Subtable'][$c]['SubClassSetCnt'];$b++) {
1583 - $offset = $this->read_ushort();
1584 - if ($offset==0x0000) {
1585 - $Lookup[$i]['Subtable'][$c]['SubClassSetOffset'][] = 0;
1586 - }
1587 - else {
1588 - $Lookup[$i]['Subtable'][$c]['SubClassSetOffset'][] = $Lookup[$i]['Subtable'][$c]['Offset'] + $offset;
1589 - }
1590 - }
1591 - }
1592 - else { die("GPOS Lookup Type ".$Lookup[$i]['Type'].", Format ".$SubstFormat." not supported (ttfontsuni.php)."); }
1593 - }
1594 -
1595 - // LookupType 6: Chaining Contextual Substitution Subtable
1596 - else if ($Lookup[$i]['Type'] == 6) {
1597 - // Format 1: Simple Chaining Context Glyph Substitution p255
1598 - if ($SubstFormat==1) {
1599 - $Lookup[$i]['Subtable'][$c]['CoverageTableOffset'] = $Lookup[$i]['Subtable'][$c]['Offset'] + $this->read_ushort();
1600 - $Lookup[$i]['Subtable'][$c]['ChainSubRuleSetCount'] = $this->read_ushort();
1601 - for ($b=0;$b<$Lookup[$i]['Subtable'][$c]['ChainSubRuleSetCount'];$b++) {
1602 - $Lookup[$i]['Subtable'][$c]['ChainSubRuleSetOffset'][] = $Lookup[$i]['Subtable'][$c]['Offset'] + $this->read_ushort();
1603 - }
1604 - }
1605 - // Format 2: Class-based Chaining Context Glyph Substitution p257
1606 - else if ($SubstFormat==2) {
1607 - $Lookup[$i]['Subtable'][$c]['CoverageTableOffset'] = $Lookup[$i]['Subtable'][$c]['Offset'] + $this->read_ushort();
1608 - $Lookup[$i]['Subtable'][$c]['BacktrackClassDefOffset'] = $Lookup[$i]['Subtable'][$c]['Offset'] + $this->read_ushort();
1609 - $Lookup[$i]['Subtable'][$c]['InputClassDefOffset'] = $Lookup[$i]['Subtable'][$c]['Offset'] + $this->read_ushort();
1610 - $Lookup[$i]['Subtable'][$c]['LookaheadClassDefOffset'] = $Lookup[$i]['Subtable'][$c]['Offset'] + $this->read_ushort();
1611 - $Lookup[$i]['Subtable'][$c]['ChainSubClassSetCnt'] = $this->read_ushort();
1612 - for ($b=0;$b<$Lookup[$i]['Subtable'][$c]['ChainSubClassSetCnt'];$b++) {
1613 - $offset = $this->read_ushort();
1614 - if ($offset==0x0000) {
1615 - $Lookup[$i]['Subtable'][$c]['ChainSubClassSetOffset'][] = $offset;
1616 - }
1617 - else {
1618 - $Lookup[$i]['Subtable'][$c]['ChainSubClassSetOffset'][] = $Lookup[$i]['Subtable'][$c]['Offset'] + $offset;
1619 - }
1620 - }
1621 - }
1622 - // Format 3: Coverage-based Chaining Context Glyph Substitution p259
1623 - else if ($SubstFormat==3) {
1624 - $Lookup[$i]['Subtable'][$c]['BacktrackGlyphCount'] = $this->read_ushort();
1625 - for ($b=0;$b<$Lookup[$i]['Subtable'][$c]['BacktrackGlyphCount'];$b++) {
1626 - $Lookup[$i]['Subtable'][$c]['CoverageBacktrack'][] = $Lookup[$i]['Subtable'][$c]['Offset'] + $this->read_ushort();
1627 - }
1628 - $Lookup[$i]['Subtable'][$c]['InputGlyphCount'] = $this->read_ushort();
1629 - for ($b=0;$b<$Lookup[$i]['Subtable'][$c]['InputGlyphCount'];$b++) {
1630 - $Lookup[$i]['Subtable'][$c]['CoverageInput'][] = $Lookup[$i]['Subtable'][$c]['Offset'] + $this->read_ushort();
1631 - }
1632 - $Lookup[$i]['Subtable'][$c]['LookaheadGlyphCount'] = $this->read_ushort();
1633 - for ($b=0;$b<$Lookup[$i]['Subtable'][$c]['LookaheadGlyphCount'];$b++) {
1634 - $Lookup[$i]['Subtable'][$c]['CoverageLookahead'][] = $Lookup[$i]['Subtable'][$c]['Offset'] + $this->read_ushort();
1635 - }
1636 - $Lookup[$i]['Subtable'][$c]['SubstCount'] = $this->read_ushort();
1637 - for ($b=0;$b<$Lookup[$i]['Subtable'][$c]['SubstCount'];$b++) {
1638 - $Lookup[$i]['Subtable'][$c]['SubstLookupRecord'][$b]['SequenceIndex'] = $this->read_ushort();
1639 - $Lookup[$i]['Subtable'][$c]['SubstLookupRecord'][$b]['LookupListIndex'] = $this->read_ushort();
1640 -/*
1641 -Substitution Lookup Record
1642 -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.
1643 -*/
1644 -
1645 - }
1646 - }
1647 - }
1648 - else { die("Lookup Type ".$Lookup[$i]['Type']." not supported."); }
1649 - }
1650 - }
1651 -//print_r($Lookup); exit;
1652 -
1653 -
1654 -
1655 -
1656 - //=====================================================================================
1657 - // Process (2) Whole LookupList
1658 - // Get Coverage tables and prepare preg_replace
1659 - for ($i=0;$i<$LookupCount;$i++) {
1660 - for ($c=0;$c<$Lookup[$i]['SubtableCount'] ;$c++) {
1661 - $SubstFormat= $Lookup[$i]['Subtable'][$c]['Format'] ;
1662 -
1663 - // LookupType 1: Single Substitution Subtable 1 => 1
1664 - if ($Lookup[$i]['Type'] == 1) {
1665 - $this->seek($Lookup[$i]['Subtable'][$c]['CoverageTableOffset']);
1666 - $glyphs = $this->_getCoverage(false);
1667 - for ($g=0;$g<count($glyphs);$g++) {
1668 - $replace = array();
1669 - $substitute = array();
1670 - $replace[] = unicode_hex($this->glyphToChar[$glyphs[$g]][0]);
1671 - // Flag = Ignore
1672 - if ($this->_checkGSUBignore($Lookup[$i]['Flag'], $replace[0], $Lookup[$i]['MarkFilteringSet'])) { continue; }
1673 - if (isset($Lookup[$i]['Subtable'][$c]['DeltaGlyphID'])) { // Format 1
1674 - $substitute[] = unicode_hex($this->glyphToChar[($glyphs[$g]+$Lookup[$i]['Subtable'][$c]['DeltaGlyphID'])][0]);
1675 - }
1676 - else { // Format 2
1677 - $substitute[] = unicode_hex($this->glyphToChar[($Lookup[$i]['Subtable'][$c]['Glyphs'][$g])][0]);
1678 - }
1679 - $Lookup[$i]['Subtable'][$c]['subs'][] = array('Replace'=>$replace, 'substitute'=>$substitute);
1680 - }
1681 - }
1682 -
1683 - // LookupType 2: Multiple Substitution Subtable 1 => n
1684 - else if ($Lookup[$i]['Type'] == 2) {
1685 - $this->seek($Lookup[$i]['Subtable'][$c]['CoverageTableOffset']);
1686 - $glyphs = $this->_getCoverage();
1687 - for ($g=0;$g<count($glyphs);$g++) {
1688 - $replace = array();
1689 - $substitute = array();
1690 - $replace[] = $glyphs[$g];
1691 - // Flag = Ignore
1692 - if ($this->_checkGSUBignore($Lookup[$i]['Flag'], $replace[0], $Lookup[$i]['MarkFilteringSet'])) { continue; }
1693 -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!
1694 - foreach($Lookup[$i]['Subtable'][$c]['Sequences'][$g]['SubstituteGlyphID'] AS $sub) {
1695 - $substitute[] = unicode_hex($this->glyphToChar[$sub][0]);
1696 - }
1697 - $Lookup[$i]['Subtable'][$c]['subs'][] = array('Replace'=>$replace, 'substitute'=>$substitute);
1698 - }
1699 - }
1700 - // LookupType 3: Alternate Forms 1 => 1 (only first alternate form is used)
1701 - else if ($Lookup[$i]['Type'] == 3) {
1702 - $this->seek($Lookup[$i]['Subtable'][$c]['CoverageTableOffset']);
1703 - $glyphs = $this->_getCoverage();
1704 - for ($g=0;$g<count($glyphs);$g++) {
1705 - $replace = array();
1706 - $substitute = array();
1707 - $replace[] = $glyphs[$g];
1708 - // Flag = Ignore
1709 - if ($this->_checkGSUBignore($Lookup[$i]['Flag'], $replace[0], $Lookup[$i]['MarkFilteringSet'])) { continue; }
1710 - $gid = $Lookup[$i]['Subtable'][$c]['AlternateSets'][$g]['SubstituteGlyphID'][0];
1711 - if (!isset($this->glyphToChar[$gid][0])) { continue; }
1712 - $substitute[] = unicode_hex($this->glyphToChar[$gid][0]);
1713 - $Lookup[$i]['Subtable'][$c]['subs'][] = array('Replace'=>$replace, 'substitute'=>$substitute);
1714 - }
1715 - }
1716 - // LookupType 4: Ligature Substitution Subtable n => 1
1717 - else if ($Lookup[$i]['Type'] == 4) {
1718 - $this->seek($Lookup[$i]['Subtable'][$c]['CoverageTableOffset']);
1719 - $glyphs = $this->_getCoverage();
1720 - $LigSetCount = $Lookup[$i]['Subtable'][$c]['LigSetCount'];
1721 - for($s=0;$s<$LigSetCount;$s++) {
1722 - for ($g=0;$g<$Lookup[$i]['Subtable'][$c]['LigSet'][$s]['LigCount'];$g++) {
1723 - $replace = array();
1724 - $substitute = array();
1725 - $replace[] = $glyphs[$s];
1726 - // Flag = Ignore
1727 - if ($this->_checkGSUBignore($Lookup[$i]['Flag'], $replace[0], $Lookup[$i]['MarkFilteringSet'])) { continue; }
1728 - for ($l=1;$l<$Lookup[$i]['Subtable'][$c]['LigSet'][$s]['Ligature'][$g]['CompCount'];$l++) {
1729 - $gid = $Lookup[$i]['Subtable'][$c]['LigSet'][$s]['Ligature'][$g]['GlyphID'][$l];
1730 - $rpl = unicode_hex($this->glyphToChar[$gid][0]);
1731 - // Flag = Ignore
1732 - if ($this->_checkGSUBignore($Lookup[$i]['Flag'], $rpl, $Lookup[$i]['MarkFilteringSet'])) { continue 2; }
1733 - $replace[] = $rpl;
1734 - }
1735 - $gid = $Lookup[$i]['Subtable'][$c]['LigSet'][$s]['Ligature'][$g]['LigGlyph'];
1736 - if (!isset($this->glyphToChar[$gid][0])) { continue; }
1737 - $substitute[] = unicode_hex($this->glyphToChar[$gid][0]);
1738 - $Lookup[$i]['Subtable'][$c]['subs'][] = array('Replace'=>$replace, 'substitute'=>$substitute, 'CompCount' => $Lookup[$i]['Subtable'][$c]['LigSet'][$s]['Ligature'][$g]['CompCount']);
1739 - }
1740 - }
1741 - }
1742 -
1743 - // LookupType 5: Contextual Substitution Subtable
1744 - else if ($Lookup[$i]['Type'] == 5) {
1745 - // Format 1: Context Substitution
1746 - if ($SubstFormat==1) {
1747 - $this->seek($Lookup[$i]['Subtable'][$c]['CoverageTableOffset']);
1748 - $Lookup[$i]['Subtable'][$c]['CoverageGlyphs'] = $CoverageGlyphs = $this->_getCoverage();
1749 -
1750 - for ($s=0;$s<$Lookup[$i]['Subtable'][$c]['SubRuleSetCount'];$s++) {
1751 - $SubRuleSet = $Lookup[$i]['Subtable'][$c]['SubRuleSet'][$s];
1752 - $Lookup[$i]['Subtable'][$c]['SubRuleSet'][$s]['FirstGlyph'] = $CoverageGlyphs[$s];
1753 - for ($r=0;$r<$Lookup[$i]['Subtable'][$c]['SubRuleSet'][$s]['SubRuleCount'];$r++) {
1754 - $GlyphCount = $Lookup[$i]['Subtable'][$c]['SubRuleSet'][$s]['SubRule'][$r]['GlyphCount'];
1755 - for ($g=1;$g<$GlyphCount;$g++) {
1756 - $glyphID = $Lookup[$i]['Subtable'][$c]['SubRuleSet'][$s]['SubRule'][$r]['Input'][$g];
1757 - $Lookup[$i]['Subtable'][$c]['SubRuleSet'][$s]['SubRule'][$r]['InputGlyphs'][$g] = unicode_hex($this->glyphToChar[$glyphID][0]);
1758 - }
1759 -
1760 - }
1761 - }
1762 - }
1763 - // Format 2: Class-based Context Glyph Substitution
1764 - else if ($SubstFormat==2) {
1765 - $this->seek($Lookup[$i]['Subtable'][$c]['CoverageTableOffset']);
1766 - $Lookup[$i]['Subtable'][$c]['CoverageGlyphs'] = $CoverageGlyphs = $this->_getCoverage();
1767 -
1768 - $InputClasses = $this->_getClasses($Lookup[$i]['Subtable'][$c]['ClassDefOffset']);
1769 - $Lookup[$i]['Subtable'][$c]['InputClasses'] = $InputClasses;
1770 - for ($s=0;$s<$Lookup[$i]['Subtable'][$c]['SubClassSetCnt'];$s++) {
1771 - if ($Lookup[$i]['Subtable'][$c]['SubClassSetOffset'][$s]>0) {
1772 - $this->seek($Lookup[$i]['Subtable'][$c]['SubClassSetOffset'][$s]);
1773 - $Lookup[$i]['Subtable'][$c]['SubClassSet'][$s]['SubClassRuleCnt'] = $SubClassRuleCnt = $this->read_ushort();
1774 - $SubClassRule = array();
1775 - for($b=0;$b<$SubClassRuleCnt;$b++) {
1776 - $SubClassRule[$b] = $Lookup[$i]['Subtable'][$c]['SubClassSetOffset'][$s]+$this->read_ushort();
1777 - $Lookup[$i]['Subtable'][$c]['SubClassSet'][$s]['SubClassRule'][$b] = $SubClassRule[$b];
1778 - }
1779 - }
1780 - }
1781 -
1782 - for ($s=0;$s<$Lookup[$i]['Subtable'][$c]['SubClassSetCnt'];$s++) {
1783 - if ($Lookup[$i]['Subtable'][$c]['SubClassSetOffset'][$s]>0) {
1784 - $SubClassRuleCnt = $Lookup[$i]['Subtable'][$c]['SubClassSet'][$s]['SubClassRuleCnt'];
1785 - for($b=0;$b<$SubClassRuleCnt;$b++) {
1786 - $this->seek($Lookup[$i]['Subtable'][$c]['SubClassSet'][$s]['SubClassRule'][$b]);
1787 - $Rule = array();
1788 - $Rule['InputGlyphCount'] = $this->read_ushort();
1789 - $Rule['SubstCount'] = $this->read_ushort();
1790 - for ($r=1;$r<$Rule['InputGlyphCount'];$r++) {
1791 - $Rule['Input'][$r] = $this->read_ushort();
1792 - }
1793 - for ($r=0;$r<$Rule['SubstCount'];$r++) {
1794 - $Rule['SequenceIndex'][$r] = $this->read_ushort();
1795 - $Rule['LookupListIndex'][$r] = $this->read_ushort();
1796 - }
1797 -
1798 - $Lookup[$i]['Subtable'][$c]['SubClassSet'][$s]['SubClassRule'][$b] = $Rule;
1799 - }
1800 - }
1801 - }
1802 - }
1803 - // Format 3: Coverage-based Context Glyph Substitution
1804 - else if ($SubstFormat==3) {
1805 - for ($b=0;$b<$Lookup[$i]['Subtable'][$c]['InputGlyphCount'];$b++) {
1806 - $this->seek($Lookup[$i]['Subtable'][$c]['CoverageInput'][$b]);
1807 - $glyphs = $this->_getCoverage();
1808 - $Lookup[$i]['Subtable'][$c]['CoverageInputGlyphs'][] = implode("|",$glyphs);
1809 - }
1810 - die("Lookup Type 5, SubstFormat 3 not tested. Please report this with the name of font used - ".$this->fontkey);
1811 - }
1812 -
1813 - }
1814 -
1815 - // LookupType 6: Chaining Contextual Substitution Subtable
1816 - else if ($Lookup[$i]['Type'] == 6) {
1817 - // Format 1: Simple Chaining Context Glyph Substitution p255
1818 - if ($SubstFormat==1) {
1819 - $this->seek($Lookup[$i]['Subtable'][$c]['CoverageTableOffset']);
1820 - $Lookup[$i]['Subtable'][$c]['CoverageGlyphs'] = $CoverageGlyphs = $this->_getCoverage();
1821 -
1822 - $ChainSubRuleSetCnt = $Lookup[$i]['Subtable'][$c]['ChainSubRuleSetCount'];
1823 -
1824 - for ($s=0;$s<$ChainSubRuleSetCnt;$s++) {
1825 - $this->seek($Lookup[$i]['Subtable'][$c]['ChainSubRuleSetOffset'][$s]);
1826 - $ChainSubRuleCnt = $Lookup[$i]['Subtable'][$c]['ChainSubRuleSet'][$s]['ChainSubRuleCount'] = $this->read_ushort();
1827 - for ($r=0;$r<$ChainSubRuleCnt;$r++) {
1828 - $Lookup[$i]['Subtable'][$c]['ChainSubRuleSet'][$s]['ChainSubRuleOffset'][$r] = $Lookup[$i]['Subtable'][$c]['ChainSubRuleSetOffset'][$s] + $this->read_ushort();
1829 -
1830 - }
1831 - }
1832 - for ($s=0;$s<$ChainSubRuleSetCnt;$s++) {
1833 - $ChainSubRuleCnt = $Lookup[$i]['Subtable'][$c]['ChainSubRuleSet'][$s]['ChainSubRuleCount'];
1834 - for ($r=0;$r<$ChainSubRuleCnt;$r++) {
1835 - // ChainSubRule
1836 - $this->seek($Lookup[$i]['Subtable'][$c]['ChainSubRuleSet'][$s]['ChainSubRuleOffset'][$r]);
1837 -
1838 - $BacktrackGlyphCount = $Lookup[$i]['Subtable'][$c]['ChainSubRuleSet'][$s]['ChainSubRule'][$r]['BacktrackGlyphCount'] = $this->read_ushort();
1839 - for ($g=0;$g<$BacktrackGlyphCount;$g++) {
1840 - $glyphID = $this->read_ushort();
1841 - $Lookup[$i]['Subtable'][$c]['ChainSubRuleSet'][$s]['ChainSubRule'][$r]['BacktrackGlyphs'][$g] = unicode_hex($this->glyphToChar[$glyphID][0]);
1842 - }
1843 -
1844 - $InputGlyphCount = $Lookup[$i]['Subtable'][$c]['ChainSubRuleSet'][$s]['ChainSubRule'][$r]['InputGlyphCount'] = $this->read_ushort();
1845 - for ($g=1;$g<$InputGlyphCount;$g++) {
1846 - $glyphID = $this->read_ushort();
1847 - $Lookup[$i]['Subtable'][$c]['ChainSubRuleSet'][$s]['ChainSubRule'][$r]['InputGlyphs'][$g] = unicode_hex($this->glyphToChar[$glyphID][0]);
1848 - }
1849 -
1850 -
1851 - $LookaheadGlyphCount = $Lookup[$i]['Subtable'][$c]['ChainSubRuleSet'][$s]['ChainSubRule'][$r]['LookaheadGlyphCount'] = $this->read_ushort();
1852 - for ($g=0;$g<$LookaheadGlyphCount;$g++) {
1853 - $glyphID = $this->read_ushort();
1854 - $Lookup[$i]['Subtable'][$c]['ChainSubRuleSet'][$s]['ChainSubRule'][$r]['LookaheadGlyphs'][$g] = unicode_hex($this->glyphToChar[$glyphID][0]);
1855 - }
1856 -
1857 - $SubstCount = $Lookup[$i]['Subtable'][$c]['ChainSubRuleSet'][$s]['ChainSubRule'][$r]['SubstCount'] = $this->read_ushort();
1858 - for ($lu=0;$lu<$SubstCount;$lu++) {
1859 - $Lookup[$i]['Subtable'][$c]['ChainSubRuleSet'][$s]['ChainSubRule'][$r]['SequenceIndex'][$lu] = $this->read_ushort();
1860 - $Lookup[$i]['Subtable'][$c]['ChainSubRuleSet'][$s]['ChainSubRule'][$r]['LookupListIndex'][$lu] = $this->read_ushort();
1861 - }
1862 - }
1863 - }
1864 -
1865 - }
1866 - // Format 2: Class-based Chaining Context Glyph Substitution p257
1867 - else if ($SubstFormat==2) {
1868 - $this->seek($Lookup[$i]['Subtable'][$c]['CoverageTableOffset']);
1869 - $Lookup[$i]['Subtable'][$c]['CoverageGlyphs'] = $CoverageGlyphs = $this->_getCoverage();
1870 -
1871 - $BacktrackClasses = $this->_getClasses($Lookup[$i]['Subtable'][$c]['BacktrackClassDefOffset']);
1872 - $Lookup[$i]['Subtable'][$c]['BacktrackClasses'] = $BacktrackClasses;
1873 -
1874 - $InputClasses = $this->_getClasses($Lookup[$i]['Subtable'][$c]['InputClassDefOffset']);
1875 - $Lookup[$i]['Subtable'][$c]['InputClasses'] = $InputClasses;
1876 -
1877 - $LookaheadClasses = $this->_getClasses($Lookup[$i]['Subtable'][$c]['LookaheadClassDefOffset']);
1878 - $Lookup[$i]['Subtable'][$c]['LookaheadClasses'] = $LookaheadClasses;
1879 -
1880 - for ($s=0;$s<$Lookup[$i]['Subtable'][$c]['ChainSubClassSetCnt'];$s++) {
1881 - if ($Lookup[$i]['Subtable'][$c]['ChainSubClassSetOffset'][$s]>0) {
1882 - $this->seek($Lookup[$i]['Subtable'][$c]['ChainSubClassSetOffset'][$s]);
1883 - $Lookup[$i]['Subtable'][$c]['ChainSubClassSet'][$s]['ChainSubClassRuleCnt'] = $ChainSubClassRuleCnt = $this->read_ushort();
1884 - $ChainSubClassRule = array();
1885 - for($b=0;$b<$ChainSubClassRuleCnt;$b++) {
1886 - $ChainSubClassRule[$b] = $Lookup[$i]['Subtable'][$c]['ChainSubClassSetOffset'][$s]+$this->read_ushort();
1887 - $Lookup[$i]['Subtable'][$c]['ChainSubClassSet'][$s]['ChainSubClassRule'][$b] = $ChainSubClassRule[$b];
1888 - }
1889 - }
1890 - }
1891 -
1892 - for ($s=0;$s<$Lookup[$i]['Subtable'][$c]['ChainSubClassSetCnt'];$s++) {
1893 - if (isset($Lookup[$i]['Subtable'][$c]['ChainSubClassSet'][$s]['ChainSubClassRuleCnt'])) {
1894 - $ChainSubClassRuleCnt = $Lookup[$i]['Subtable'][$c]['ChainSubClassSet'][$s]['ChainSubClassRuleCnt'];
1895 - }
1896 - else { $ChainSubClassRuleCnt = 0; }
1897 - for($b=0;$b<$ChainSubClassRuleCnt;$b++) {
1898 - if ($Lookup[$i]['Subtable'][$c]['ChainSubClassSetOffset'][$s]>0) {
1899 - $this->seek($Lookup[$i]['Subtable'][$c]['ChainSubClassSet'][$s]['ChainSubClassRule'][$b]);
1900 - $Rule = array();
1901 - $Rule['BacktrackGlyphCount'] = $this->read_ushort();
1902 - for ($r=0;$r<$Rule['BacktrackGlyphCount'];$r++) {
1903 - $Rule['Backtrack'][$r] = $this->read_ushort();
1904 - }
1905 - $Rule['InputGlyphCount'] = $this->read_ushort();
1906 - for ($r=1;$r<$Rule['InputGlyphCount'];$r++) {
1907 - $Rule['Input'][$r] = $this->read_ushort();
1908 - }
1909 - $Rule['LookaheadGlyphCount'] = $this->read_ushort();
1910 - for ($r=0;$r<$Rule['LookaheadGlyphCount'];$r++) {
1911 - $Rule['Lookahead'][$r] = $this->read_ushort();
1912 - }
1913 - $Rule['SubstCount'] = $this->read_ushort();
1914 - for ($r=0;$r<$Rule['SubstCount'];$r++) {
1915 - $Rule['SequenceIndex'][$r] = $this->read_ushort();
1916 - $Rule['LookupListIndex'][$r] = $this->read_ushort();
1917 - }
1918 -
1919 - $Lookup[$i]['Subtable'][$c]['ChainSubClassSet'][$s]['ChainSubClassRule'][$b] = $Rule;
1920 - }
1921 - }
1922 - }
1923 - }
1924 - // Format 3: Coverage-based Chaining Context Glyph Substitution p259
1925 - else if ($SubstFormat==3) {
1926 - for ($b=0;$b<$Lookup[$i]['Subtable'][$c]['BacktrackGlyphCount'];$b++) {
1927 - $this->seek($Lookup[$i]['Subtable'][$c]['CoverageBacktrack'][$b]);
1928 - $glyphs = $this->_getCoverage();
1929 - $Lookup[$i]['Subtable'][$c]['CoverageBacktrackGlyphs'][] = implode("|",$glyphs);
1930 - }
1931 - for ($b=0;$b<$Lookup[$i]['Subtable'][$c]['InputGlyphCount'];$b++) {
1932 - $this->seek($Lookup[$i]['Subtable'][$c]['CoverageInput'][$b]);
1933 - $glyphs = $this->_getCoverage();
1934 - $Lookup[$i]['Subtable'][$c]['CoverageInputGlyphs'][] = implode("|",$glyphs);
1935 - // Don't use above value as these are ordered numerically not as need to process
1936 - }
1937 - for ($b=0;$b<$Lookup[$i]['Subtable'][$c]['LookaheadGlyphCount'];$b++) {
1938 - $this->seek($Lookup[$i]['Subtable'][$c]['CoverageLookahead'][$b]);
1939 - $glyphs = $this->_getCoverage();
1940 - $Lookup[$i]['Subtable'][$c]['CoverageLookaheadGlyphs'][] = implode("|",$glyphs);
1941 - }
1942 -
1943 - }
1944 - }
1945 - }
1946 - }
1947 -
1948 -
1949 - //=====================================================================================
1950 - //=====================================================================================
1951 - //=====================================================================================
1952 - //=====================================================================================
1953 -
1954 - $GSUBScriptLang = array();
1955 - $rtlpua = array(); // All glyphs added to PUA [for magic_reverse]
1956 - foreach($gsub AS $st=>$scripts) {
1957 - foreach($scripts AS $t=>$langsys) {
1958 - $lul = array(); // array of LookupListIndexes
1959 - $tags = array(); // corresponding array of feature tags e.g. 'ccmp'
1960 -//print_r($langsys ); exit;
1961 - foreach($langsys AS $tag=>$ft) {
1962 - foreach($ft AS $ll) {
1963 - $lul[$ll] = $tag;
1964 - }
1965 - }
1966 - ksort($lul); // Order the Lookups in the order they are in the GUSB table, regardless of Feature order
1967 - $volt = $this->_getGSUBarray($Lookup, $lul, $st);
1968 -//print_r($lul); exit;
1969 -
1970 - //=====================================================================================
1971 - //=====================================================================================
1972 - // Interrogate $volt
1973 - // isol, fin, medi, init(arab syrc) into $rtlSUB for use in ArabJoin
1974 - // but also identify all RTL chars in PUA for magic_reverse (arab syrc hebr thaa nko samr)
1975 - // identify reph, matras, vatu, half forms etc for Indic for final re-ordering
1976 - //=====================================================================================
1977 - //=====================================================================================
1978 - $rtl = array();
1979 - $rtlSUB = "array()";
1980 - $finals = '';
1981 - if (strpos('arab syrc hebr thaa nko samr', $st)!==false) { // all RTL scripts [any/all languages] ? Mandaic
1982 -//print_r($volt); exit;
1983 - foreach($volt AS $v) {
1984 - // isol fina fin2 fin3 medi med2 for Syriac
1985 - // ISOLATED FORM :: FINAL :: INITIAL :: MEDIAL :: MED2 :: FIN2 :: FIN3
1986 - if (strpos('isol fina init medi fin2 fin3 med2',$v['tag'])!==false) {
1987 - $key = $v['match'];
1988 - $key = preg_replace('/[\(\)]*/','',$key);
1989 - $sub = $v['replace'];
1990 - if ($v['tag'] == 'isol') $kk = 0;
1991 - else if ($v['tag'] == 'fina') $kk = 1;
1992 - else if ($v['tag'] == 'init') $kk = 2;
1993 - else if ($v['tag'] == 'medi') $kk = 3;
1994 - else if ($v['tag'] == 'med2') $kk = 4;
1995 - else if ($v['tag'] == 'fin2') $kk = 5;
1996 - else if ($v['tag'] == 'fin3') $kk = 6;
1997 - $rtl[$key][$kk] = $sub;
1998 - if (isset($v['prel']) && count($v['prel'])) $rtl[$key]['prel'][$kk] = $v['prel'];
1999 - if (isset($v['postl']) && count($v['postl'])) $rtl[$key]['postl'][$kk] = $v['postl'];
2000 - if (isset($v['ignore']) && $v['ignore']) {
2001 - $rtl[$key]['ignore'][$kk] = $v['ignore'];
2002 - }
2003 - $rtlpua[] = $sub;
2004 - }
2005 - // Add any other glyphs which are in PUA
2006 - else {
2007 - if (isset($v['context']) && $v['context']) {
2008 - foreach($v['rules'] AS $vs) {
2009 - for($i=0;$i<count($vs['match']);$i++) {
2010 - if (isset($vs['replace'][$i]) && preg_match('/^0[A-F0-9]{4}$/', $vs['match'][$i])) {
2011 - if (preg_match('/^0[EF][A-F0-9]{3}$/', $vs['replace'][$i])) {
2012 - $rtlpua[] = $vs['replace'][$i];
2013 - }
2014 - }
2015 - }
2016 - }
2017 - }
2018 - else {
2019 - preg_match_all('/\((0[A-F0-9]{4})\)/', $v['match'], $m);
2020 - for($i=0;$i<count($m[0]);$i++) {
2021 - $sb = explode(' ',$v['replace']);
2022 - foreach($sb AS $sbg) {
2023 - if (preg_match('/(0[EF][A-F0-9]{3})/', $sbg, $mr)) {
2024 - $rtlpua[] = $mr[1];
2025 - }
2026 - }
2027 - }
2028 - }
2029 - }
2030 - }
2031 -//print_r($rtl); exit;
2032 - // For kashida, need to determine all final forms except ones already identified by kashida
2033 - // priority rules (see otl.php)
2034 - foreach($rtl AS $base=>$variants) {
2035 - if (isset($variants[1])) { // i.e. final form
2036 - if (strpos('0FE8E 0FE94 0FEA2 0FEAA 0FEAE 0FEC2 0FEDA 0FEDE 0FB93 0FECA 0FED2 0FED6 0FEEE 0FEF0 0FEF2', $variants[1])===false) { // not already included
2037 -
2038 - // This version does not exclude RA (0631) FEAE; Ya (064A) FEF2; Alef Maqsurah (0649) FEF0 which
2039 - // are selected in priority if connected to a medial Bah
2040 - //if (strpos('0FE8E 0FE94 0FEA2 0FEAA 0FEC2 0FEDA 0FEDE 0FB93 0FECA 0FED2 0FED6 0FEEE', $variants[1])===false) { // not already included
2041 - $finals .= $variants[1].' ';
2042 - }
2043 - }
2044 - }
2045 -//echo $finals ; exit;
2046 -//print_r($rtlpua); exit;
2047 - ksort($rtl);
2048 - $a = var_export($rtl, true);
2049 - $a = preg_replace('/\\\\\\\\/',"\\",$a);
2050 - $a = preg_replace('/\'/','"',$a);
2051 - $a = preg_replace('/\r/','',$a);
2052 - $a = preg_replace('/> \n/','>',$a);
2053 - $a = preg_replace('/\n \)/',')',$a);
2054 - $a= preg_replace('/\n /',' ',$a);
2055 - $a = preg_replace('/\[IGNORE(\d+)\]/','".$ignore[\\1]."',$a);
2056 - $rtlSUB = preg_replace('/[ ]+/',' ',$a);
2057 -
2058 - }
2059 - //=====================================================================================
2060 - // INDIC - Dynamic properties
2061 - //=====================================================================================
2062 - $rphf = array();
2063 - $half = array();
2064 - $pref = array();
2065 - $blwf = array();
2066 - $pstf = array();
2067 - if (strpos('dev2 bng2 gur2 gjr2 ory2 tml2 tel2 knd2 mlm2 deva beng guru gujr orya taml telu knda mlym', $st)!==false) { // all INDIC scripts [any/all languages]
2068 -
2069 - if (strpos('deva beng guru gujr orya taml telu knda mlym', $st)!==false) { $is_old_spec = true; }
2070 - else { $is_old_spec = false; }
2071 -
2072 - // First get 'locl' substitutions (reversed!)
2073 - $loclsubs = array();
2074 - foreach($volt AS $v) {
2075 - if (strpos('locl',$v['tag'])!==false) {
2076 - $key = $v['match'];
2077 - $key = preg_replace('/[\(\)]*/','',$key);
2078 - $sub = $v['replace'];
2079 - if ($key && strlen(trim($key))==5 && $sub) { $loclsubs[$sub] = $key; }
2080 - }
2081 - }
2082 -//if (count($loclsubs)) { print_r($loclsubs); exit; }
2083 -
2084 - foreach($volt AS $v) {
2085 - // <rphf> <half> <pref> <blwf> <pstf>
2086 - // defines consonant types:
2087 - // Reph <rphf>
2088 - // Half forms <half>
2089 - // Pre-base-reordering forms of Ra/Rra <pref>
2090 - // Below-base forms <blwf>
2091 - // Post-base forms <pstf>
2092 -
2093 - // applied together with <locl> feature to input sequences consisting of two characters
2094 - // This is done for each consonant
2095 - // for <rphf> and <half>, features are applied to Consonant + Halant combinations
2096 - // for <pref>, <blwf> and <pstf>, features are applied to Halant + Consonant combinations
2097 - // Old version eg 'deva' <pref>, <blwf> and <pstf>, features are applied to Consonant + Halant
2098 - // Some malformed fonts still do Consonant + Halant for these - so match both??
2099 - // If these two glyphs form a ligature, with no additional glyphs in context
2100 - // this means the consonant has the corresponding form
2101 -
2102 - // Currently set to cope with both
2103 - // See also classes/otl.php
2104 -
2105 - if (strpos('rphf half pref blwf pstf',$v['tag'])!==false) {
2106 - if (isset($v['context']) && $v['context'] && $v['nBacktrack']==0 && $v['nLookahead']==0) {
2107 - foreach($v['rules'] AS $vs) {
2108 - if (count($vs['match'])==2 && count($vs['replace'])==1) {
2109 - $sub = $vs['replace'][0];
2110 - // If Halant Cons <pref>, <blwf> and <pstf> in New version only
2111 - if (strpos('0094D 009CD 00A4D 00ACD 00B4D 00BCD 00C4D 00CCD 00D4D',$vs['match'][0])!==false && strpos('pref blwf pstf',$v['tag'])!==false && !$is_old_spec ) {
2112 - $key = $vs['match'][1];
2113 - $tag = $v['tag'];
2114 - if (isset($loclsubs[$key])) {
2115 - $$tag[$loclsubs[$key]] = $sub;
2116 - }
2117 - $tmp = &$$tag;
2118 - $tmp[hexdec($key)] = hexdec($sub);
2119 - }
2120 - // If Cons Halant <rphf> and <half> always
2121 - // and <pref>, <blwf> and <pstf> in Old version
2122 - else if (strpos('0094D 009CD 00A4D 00ACD 00B4D 00BCD 00C4D 00CCD 00D4D',$vs['match'][1])!==false && (strpos('rphf half',$v['tag'])!==false || (strpos('pref blwf pstf',$v['tag'])!==false && ($is_old_spec || _OTL_OLD_SPEC_COMPAT_2)))) {
2123 - $key = $vs['match'][0];
2124 - $tag = $v['tag'];
2125 - if (isset($loclsubs[$key])) {
2126 - $$tag[$loclsubs[$key]] = $sub;
2127 - }
2128 - $tmp = &$$tag;
2129 - $tmp[hexdec($key)] = hexdec($sub);
2130 - }
2131 - }
2132 - }
2133 - }
2134 - else if (!isset($v['context'])) {
2135 - $key = $v['match'];
2136 - $key = preg_replace('/[\(\)]*/','',$key);
2137 - $sub = $v['replace'];
2138 - if ($key && strlen(trim($key))==11 && $sub) {
2139 - // If Cons Halant <rphf> and <half> always
2140 - // and <pref>, <blwf> and <pstf> in Old version
2141 - // If Halant Cons <pref>, <blwf> and <pstf> in New version only
2142 - if (strpos('0094D 009CD 00A4D 00ACD 00B4D 00BCD 00C4D 00CCD 00D4D',substr($key, 0, 5))!==false && strpos('pref blwf pstf',$v['tag'])!==false && !$is_old_spec ) {
2143 - $key = substr($key, 6, 5);
2144 - $tag = $v['tag'];
2145 - if (isset($loclsubs[$key])) {
2146 - $$tag[$loclsubs[$key]] = $sub;
2147 - }
2148 - $tmp = &$$tag;
2149 - $tmp[hexdec($key)] = hexdec($sub);
2150 - }
2151 - else if (strpos('0094D 009CD 00A4D 00ACD 00B4D 00BCD 00C4D 00CCD 00D4D',substr($key, 6, 5))!==false && (strpos('rphf half',$v['tag'])!==false || (strpos('pref blwf pstf',$v['tag'])!==false && ($is_old_spec || _OTL_OLD_SPEC_COMPAT_2)))) {
2152 - $key = substr($key, 0, 5);
2153 - $tag = $v['tag'];
2154 - if (isset($loclsubs[$key])) {
2155 - $$tag[$loclsubs[$key]] = $sub;
2156 - }
2157 - $tmp= &$$tag;
2158 - $tmp[hexdec($key)] = hexdec($sub);
2159 - }
2160 - }
2161 - }
2162 - }
2163 - }
2164 -/*
2165 -print_r($rphf );
2166 -print_r($half );
2167 -print_r($pref );
2168 -print_r($blwf );
2169 -print_r($pstf ); exit;
2170 -*/
2171 -
2172 - }
2173 -//print_r($rtlpua); exit;
2174 - //=====================================================================================
2175 - //=====================================================================================
2176 - //=====================================================================================
2177 - //=====================================================================================
2178 - if (count($rtl) || count($rphf ) || count($half ) || count($pref ) || count($blwf ) || count($pstf ) || $finals) {
2179 - // SAVE LOOKUPS TO FILE fontname.GSUB.scripttag.langtag.php
2180 -
2181 -
2182 - $s = '<?php
2183 -
2184 -$rtlSUB = '.$rtlSUB.';
2185 -$finals = \''.$finals.'\';
2186 -$rphf = '.var_export($rphf , true).';
2187 -$half = '.var_export($half , true).';
2188 -$pref = '.var_export($pref , true).';
2189 -$blwf = '.var_export($blwf , true).';
2190 -$pstf = '.var_export($pstf , true).';
2191 -
2192 - '."\n".'?>';
2193 -
2194 -
2195 - file_put_contents(_MPDF_TTFONTDATAPATH.$this->fontkey.'.GSUB.'.$st.'.'.$t.'.php', $s);
2196 -
2197 - }
2198 - //=====================================================================================
2199 - if (!isset($GSUBScriptLang[$st])) { $GSUBScriptLang[$st] = ''; }
2200 - $GSUBScriptLang[$st] .= $t.' ';
2201 - //=====================================================================================
2202 -
2203 - }
2204 - }
2205 - //print_r($rtlpua); exit;
2206 -
2207 - // All RTL glyphs from font added to (or already in) PUA [reqd for magic_reverse]
2208 - $rtlPUAstr = "";
2209 - if (count($rtlpua)) {
2210 - $rtlpua = array_unique($rtlpua);
2211 - sort($rtlpua);
2212 - $n = count($rtlpua);
2213 - for($i=0;$i<$n;$i++) {
2214 - if (hexdec($rtlpua[$i])<hexdec('E000') || hexdec($rtlpua[$i])>hexdec('F8FF')) {
2215 - unset($rtlpua[$i]);
2216 - }
2217 - }
2218 - sort($rtlpua, SORT_STRING);
2219 -
2220 - $rangeid = -1;
2221 - $range = array();
2222 - $prevgid = -2;
2223 - // for each character
2224 - foreach ($rtlpua as $gidhex) {
2225 - $gid = hexdec($gidhex);
2226 - if ($gid == ($prevgid + 1)) {
2227 - $range[$rangeid]['end'] = $gidhex;
2228 - $range[$rangeid]['count']++;
2229 - } else {
2230 - // new range
2231 - $rangeid++;
2232 - $range[$rangeid] = array();
2233 - $range[$rangeid]['start'] = $gidhex;
2234 - $range[$rangeid]['end'] = $gidhex;
2235 - $range[$rangeid]['count'] = 1;
2236 - }
2237 - $prevgid = $gid;
2238 - }
2239 - foreach($range AS $rg) {
2240 - if ($rg['count'] == 1) { $rtlPUAstr .= "\x{".$rg['start']."}"; }
2241 - else if ($rg['count'] == 2) { $rtlPUAstr .= "\x{".$rg['start']."}\x{".$rg['end']."}"; }
2242 - else { $rtlPUAstr .= "\x{".$rg['start']."}-\x{".$rg['end']."}"; }
2243 -
2244 - }
2245 - }
2246 -
2247 - //print_r($rtlPUAstr ); exit;
2248 -
2249 -
2250 - //=====================================================================================
2251 - //=====================================================================================
2252 - //=====================================================================================
2253 - //=====================================================================================
2254 - //print_r($rtlpua); exit;
2255 - //print_r($GSUBScriptLang); exit;
2256 -
2257 -
2258 -
2259 - }
2260 -//print_r($Lookup); exit;
2261 -
2262 - return array($GSUBScriptLang, $gsub, $GSLookup, $rtlPUAstr); // , $rtlPUAarr Not needed
2263 -
2264 - }
2265 1114 /////////////////////////////////////////////////////////////////////////////////////////
2266 - // GSUB functions
2267 - function _getGSUBarray(&$Lookup, &$lul, $scripttag) {
2268 - // Process (3) LookupList for specific Script-LangSys
2269 - // Generate preg_replace
2270 - $volt = array();
2271 - $reph = '';
2272 - $matraE = '';
2273 - $vatu = '';
2274 - foreach($lul AS $i=>$tag) {
2275 - for ($c=0;$c<$Lookup[$i]['SubtableCount'] ;$c++) {
2276 1115
2277 - $SubstFormat= $Lookup[$i]['Subtable'][$c]['Format'] ;
2278 1116
2279 - // LookupType 1: Single Substitution Subtable
2280 - if ($Lookup[$i]['Type'] == 1) {
2281 - for ($s=0;$s<count($Lookup[$i]['Subtable'][$c]['subs']);$s++) {
2282 - $inputGlyphs = $Lookup[$i]['Subtable'][$c]['subs'][$s]['Replace'];
2283 - $substitute = $Lookup[$i]['Subtable'][$c]['subs'][$s]['substitute'][0];
2284 - // Ignore has already been applied earlier on
2285 - $repl = $this->_makeGSUBinputMatch($inputGlyphs, "()");
2286 - $subs = $this->_makeGSUBinputReplacement(1, $substitute, "()", 0, 1, 0);
2287 - $volt[] = array('match'=>$repl, 'replace'=>$subs, 'tag'=>$tag, 'key'=>$inputGlyphs[0], 'type' => 1);
2288 - }
2289 - }
2290 - // LookupType 2: Multiple Substitution Subtable
2291 - else if ($Lookup[$i]['Type'] == 2) {
2292 - for ($s=0;$s<count($Lookup[$i]['Subtable'][$c]['subs']);$s++) {
2293 - $inputGlyphs = $Lookup[$i]['Subtable'][$c]['subs'][$s]['Replace'];
2294 - $substitute = implode(" ", $Lookup[$i]['Subtable'][$c]['subs'][$s]['substitute']);
2295 - // Ignore has already been applied earlier on
2296 - $repl = $this->_makeGSUBinputMatch($inputGlyphs,"()");
2297 - $subs = $this->_makeGSUBinputReplacement(1, $substitute, "()", 0, 1, 0);
2298 - $volt[] = array('match'=>$repl, 'replace'=>$subs, 'tag'=>$tag, 'key'=>$inputGlyphs[0], 'type' => 2);
2299 - }
2300 - }
2301 - // LookupType 3: Alternate Forms
2302 - else if ($Lookup[$i]['Type'] == 3) {
2303 - for ($s=0;$s<count($Lookup[$i]['Subtable'][$c]['subs']);$s++) {
2304 - $inputGlyphs = $Lookup[$i]['Subtable'][$c]['subs'][$s]['Replace'];
2305 - $substitute = $Lookup[$i]['Subtable'][$c]['subs'][$s]['substitute'][0];
2306 - // Ignore has already been applied earlier on
2307 - $repl = $this->_makeGSUBinputMatch($inputGlyphs, "()");
2308 - $subs = $this->_makeGSUBinputReplacement(1, $substitute, "()", 0, 1, 0);
2309 - $volt[] = array('match'=>$repl, 'replace'=>$subs, 'tag'=>$tag, 'key'=>$inputGlyphs[0], 'type' => 3);
2310 - }
2311 - }
2312 - // LookupType 4: Ligature Substitution Subtable
2313 - else if ($Lookup[$i]['Type'] == 4) {
2314 - for ($s=0;$s<count($Lookup[$i]['Subtable'][$c]['subs']);$s++) {
2315 - $inputGlyphs = $Lookup[$i]['Subtable'][$c]['subs'][$s]['Replace'];
2316 - $substitute = $Lookup[$i]['Subtable'][$c]['subs'][$s]['substitute'][0];
2317 - // Ignore has already been applied earlier on
2318 - $ignore = $this->_getGSUBignoreString($Lookup[$i]['Flag'], $Lookup[$i]['MarkFilteringSet']);
2319 - $repl = $this->_makeGSUBinputMatch($inputGlyphs, $ignore);
2320 - $subs = $this->_makeGSUBinputReplacement(count($inputGlyphs), $substitute, $ignore, 0, count($inputGlyphs), 0);
2321 - $volt[] = array('match'=>$repl, 'replace'=>$subs, 'tag'=>$tag, 'key'=>$inputGlyphs[0], 'type' => 4, 'CompCount' => $Lookup[$i]['Subtable'][$c]['subs'][$s]['CompCount'], 'Lig' => $substitute);
2322 - }
2323 - }
2324 -
2325 - // LookupType 5: Chaining Contextual Substitution Subtable
2326 - else if ($Lookup[$i]['Type'] == 5) {
2327 - // Format 1: Context Substitution
2328 - if ($SubstFormat==1) {
2329 - $ignore = $this->_getGSUBignoreString($Lookup[$i]['Flag'], $Lookup[$i]['MarkFilteringSet']);
2330 - for($s=0;$s<$Lookup[$i]['Subtable'][$c]['SubRuleSetCount'];$s++) {
2331 - // SubRuleSet
2332 -$subRule = array();
2333 - foreach($Lookup[$i]['Subtable'][$c]['SubRuleSet'][$s]['SubRule'] AS $rule) {
2334 - // SubRule
2335 - $inputGlyphs = array();
2336 - if ($rule['GlyphCount']>1) {
2337 - $inputGlyphs = $rule['InputGlyphs'];
2338 - }
2339 - $inputGlyphs[0] = $Lookup[$i]['Subtable'][$c]['SubRuleSet'][$s]['FirstGlyph'];
2340 - ksort($inputGlyphs);
2341 - $nInput = count($inputGlyphs);
2342 -
2343 - $contextInputMatch = $this->_makeGSUBcontextInputMatch($inputGlyphs, $ignore, array(), 0);
2344 - $subRule = array('context' => 1, 'tag' => $tag, 'matchback' => '', 'match' => $contextInputMatch, 'nBacktrack' => 0, 'nInput' => $nInput, 'nLookahead' => 0, 'rules' => array(), );
2345 -
2346 - for ($b=0;$b<$rule['SubstCount'];$b++) {
2347 - $lup = $rule['SubstLookupRecord'][$b]['LookupListIndex'];
2348 - $seqIndex = $rule['SubstLookupRecord'][$b]['SequenceIndex'];
2349 -
2350 - // $Lookup[$lup] = secondary Lookup
2351 - for($lus=0;$lus<$Lookup[$lup]['SubtableCount'];$lus++) {
2352 - if (count($Lookup[$lup]['Subtable'][$lus]['subs'])) {
2353 - foreach($Lookup[$lup]['Subtable'][$lus]['subs'] AS $luss) {
2354 -
2355 - $lookupGlyphs = $luss['Replace'];
2356 - $mLen = count($lookupGlyphs);
2357 -
2358 - // Only apply if the (first) 'Replace' glyph from the
2359 - // Lookup list is in the [inputGlyphs] at ['SequenceIndex']
2360 - // then apply the substitution
2361 - if (strpos($inputGlyphs[$seqIndex],$lookupGlyphs[0])===false) { continue; }
2362 - $REPL = implode(" ",$luss['substitute']);
2363 - if (strpos("isol fina fin2 fin3 medi med2 init ",$tag)!==false && $scripttag=='arab') {
2364 - $volt[] = array('match'=>$lookupGlyphs[0], 'replace'=>$REPL, 'tag'=>$tag, 'prel'=>$backtrackGlyphs, 'postl'=>$lookaheadGlyphs, 'ignore'=>$ignore);
2365 - }
2366 - else {
2367 - $subRule['rules'][] = array('type' => $Lookup[$lup]['Type'], 'match' => $lookupGlyphs, 'replace' => $luss['substitute'], 'seqIndex' => $seqIndex, 'key' => $lookupGlyphs[0], );
2368 - }
2369 - }
2370 - }
2371 - }
2372 - }
2373 -
2374 -
2375 - if (count($subRule['rules'])) $volt[] = $subRule;
2376 -
2377 - }
2378 - }
2379 - }
2380 - // Format 2: Class-based Context Glyph Substitution
2381 - else if ($SubstFormat==2) {
2382 - $ignore = $this->_getGSUBignoreString($Lookup[$i]['Flag'], $Lookup[$i]['MarkFilteringSet']);
2383 - foreach($Lookup[$i]['Subtable'][$c]['SubClassSet'] AS $inputClass=>$cscs) {
2384 - for($cscrule=0;$cscrule<$cscs['SubClassRuleCnt'];$cscrule++) {
2385 - $rule = $cscs['SubClassRule'][$cscrule];
2386 -
2387 - $inputGlyphs = array();
2388 -
2389 - $inputGlyphs[0] = $Lookup[$i]['Subtable'][$c]['InputClasses'][$inputClass];
2390 - if ($rule['InputGlyphCount']>1) {
2391 - // NB starts at 1
2392 - for ($gcl=1;$gcl<$rule['InputGlyphCount'];$gcl++) {
2393 - $classindex = $rule['Input'][$gcl];
2394 - if (isset($Lookup[$i]['Subtable'][$c]['InputClasses'][$classindex])) {
2395 - $inputGlyphs[$gcl] = $Lookup[$i]['Subtable'][$c]['InputClasses'][$classindex];
2396 - }
2397 - // if class[0] = all glyphs excluding those specified in all other classes
2398 - // set to blank '' for now
2399 - else { $inputGlyphs[$gcl] = ''; }
2400 - }
2401 - }
2402 -
2403 - $nInput = $rule['InputGlyphCount'];
2404 -
2405 - $nIsubs = (2*$nInput)-1;
2406 -
2407 - $contextInputMatch = $this->_makeGSUBcontextInputMatch($inputGlyphs, $ignore, array(), 0);
2408 - $subRule = array('context' => 1, 'tag' => $tag, 'matchback' => '', 'match' => $contextInputMatch, 'nBacktrack' => 0, 'nInput' => $nInput, 'nLookahead' => 0, 'rules' => array(), );
2409 -
2410 - for ($b=0;$b<$rule['SubstCount'];$b++) {
2411 - $lup = $rule['LookupListIndex'][$b];
2412 - $seqIndex = $rule['SequenceIndex'][$b];
2413 -
2414 - // $Lookup[$lup] = secondary Lookup
2415 - for($lus=0;$lus<$Lookup[$lup]['SubtableCount'];$lus++) {
2416 - if (isset($Lookup[$lup]['Subtable'][$lus]['subs']) && count($Lookup[$lup]['Subtable'][$lus]['subs'])) {
2417 - foreach($Lookup[$lup]['Subtable'][$lus]['subs'] AS $luss) {
2418 -
2419 - $lookupGlyphs = $luss['Replace'];
2420 - $mLen = count($lookupGlyphs);
2421 -
2422 - // Only apply if the (first) 'Replace' glyph from the
2423 - // Lookup list is in the [inputGlyphs] at ['SequenceIndex']
2424 - // then apply the substitution
2425 - if (strpos($inputGlyphs[$seqIndex],$lookupGlyphs[0])===false) { continue; }
2426 -
2427 - // Returns e.g. �(0612)�(ignore) (0613)�(ignore) (0614)�
2428 - $contextInputMatch = $this->_makeGSUBcontextInputMatch($inputGlyphs, $ignore, $lookupGlyphs, $seqIndex);
2429 - $REPL = implode(" ",$luss['substitute']);
2430 - // Returns e.g. "REPL\${6}\${8}" or "\${1}\${2} \${3} REPL\${4}\${6}\${8} \${9}"
2431 -
2432 - if (strpos("isol fina fin2 fin3 medi med2 init ",$tag)!==false && $scripttag=='arab') {
2433 - $volt[] = array('match'=>$lookupGlyphs[0], 'replace'=>$REPL, 'tag'=>$tag, 'prel'=>$backtrackGlyphs, 'postl'=>$lookaheadGlyphs, 'ignore'=>$ignore);
2434 - }
2435 - else {
2436 - $subRule['rules'][] = array('type' => $Lookup[$lup]['Type'], 'match' => $lookupGlyphs, 'replace' => $luss['substitute'], 'seqIndex' => $seqIndex, 'key' => $lookupGlyphs[0], );
2437 - }
2438 - }
2439 - }
2440 - }
2441 - }
2442 - if (count($subRule['rules'])) $volt[] = $subRule;
2443 -
2444 - }
2445 - }
2446 -
2447 -
2448 -
2449 - }
2450 - // Format 3: Coverage-based Context Glyph Substitution p259
2451 - else if ($SubstFormat==3) {
2452 - // IgnoreMarks flag set on main Lookup table
2453 - $ignore = $this->_getGSUBignoreString($Lookup[$i]['Flag'], $Lookup[$i]['MarkFilteringSet']);
2454 - $inputGlyphs = $Lookup[$i]['Subtable'][$c]['CoverageInputGlyphs'];
2455 - $CoverageInputGlyphs = implode('|', $inputGlyphs);
2456 - $nInput = $Lookup[$i]['Subtable'][$c]['InputGlyphCount'];
2457 -
2458 - if ($Lookup[$i]['Subtable'][$c]['BacktrackGlyphCount']) {
2459 - $backtrackGlyphs = $Lookup[$i]['Subtable'][$c]['CoverageBacktrackGlyphs'];
2460 - }
2461 - else { $backtrackGlyphs = array(); }
2462 - // Returns e.g. �(FEEB|FEEC)(ignore) �(FD12|FD13)(ignore) �
2463 - $backtrackMatch = $this->_makeGSUBbacktrackMatch($backtrackGlyphs, $ignore);
2464 -
2465 - if ($Lookup[$i]['Subtable'][$c]['LookaheadGlyphCount']) {
2466 - $lookaheadGlyphs = $Lookup[$i]['Subtable'][$c]['CoverageLookaheadGlyphs'];
2467 - }
2468 - else { $lookaheadGlyphs = array(); }
2469 - // Returns e.g. �(ignore) (FD12|FD13)�(ignore) (FEEB|FEEC)�
2470 - $lookaheadMatch = $this->_makeGSUBlookaheadMatch($lookaheadGlyphs, $ignore);
2471 -
2472 - $nBsubs = 2*count($backtrackGlyphs);
2473 - $nIsubs = (2*$nInput)-1;
2474 - $contextInputMatch = $this->_makeGSUBcontextInputMatch($inputGlyphs, $ignore, array(), 0);
2475 - $subRule = array('context' => 1, 'tag' => $tag, 'matchback' => $backtrackMatch, 'match' => ($contextInputMatch . $lookaheadMatch), 'nBacktrack' => count($backtrackGlyphs), 'nInput' => $nInput, 'nLookahead' => count($lookaheadGlyphs), 'rules' => array(), );
2476 -
2477 - for ($b=0;$b<$Lookup[$i]['Subtable'][$c]['SubstCount'];$b++) {
2478 - $lup = $Lookup[$i]['Subtable'][$c]['SubstLookupRecord'][$b]['LookupListIndex'];
2479 - $seqIndex = $Lookup[$i]['Subtable'][$c]['SubstLookupRecord'][$b]['SequenceIndex'];
2480 - for($lus=0;$lus<$Lookup[$lup]['SubtableCount'];$lus++) {
2481 - if (count($Lookup[$lup]['Subtable'][$lus]['subs'])) {
2482 - foreach($Lookup[$lup]['Subtable'][$lus]['subs'] AS $luss) {
2483 - $lookupGlyphs = $luss['Replace'];
2484 - $mLen = count($lookupGlyphs);
2485 -
2486 - // Only apply if the (first) 'Replace' glyph from the
2487 - // Lookup list is in the [inputGlyphs] at ['SequenceIndex']
2488 - // then apply the substitution
2489 - if (strpos($inputGlyphs[$seqIndex],$lookupGlyphs[0])===false) { continue; }
2490 -
2491 - // Returns e.g. �(0612)�(ignore) (0613)�(ignore) (0614)�
2492 - $contextInputMatch = $this->_makeGSUBcontextInputMatch($inputGlyphs, $ignore, $lookupGlyphs, $seqIndex);
2493 - $REPL = implode(" ",$luss['substitute']);
2494 -
2495 - if (strpos("isol fina fin2 fin3 medi med2 init ",$tag)!==false && $scripttag=='arab') {
2496 - $volt[] = array('match'=>$lookupGlyphs[0], 'replace'=>$REPL, 'tag'=>$tag, 'prel'=>$backtrackGlyphs, 'postl'=>$lookaheadGlyphs, 'ignore'=>$ignore);
2497 - }
2498 - else {
2499 - $subRule['rules'][] = array('type' => $Lookup[$lup]['Type'], 'match' => $lookupGlyphs, 'replace' => $luss['substitute'], 'seqIndex' => $seqIndex, 'key' => $lookupGlyphs[0], );
2500 - }
2501 - }
2502 - }
2503 - }
2504 - }
2505 - if (count($subRule['rules'])) $volt[] = $subRule;
2506 - }
2507 -
2508 -//print_r($Lookup[$i]);
2509 -//print_r($volt[(count($volt)-1)]); exit;
2510 - }
2511 - // LookupType 6: ing Contextual Substitution Subtable
2512 - else if ($Lookup[$i]['Type'] == 6) {
2513 - // Format 1: Simple Chaining Context Glyph Substitution p255
2514 - if ($SubstFormat==1) {
2515 - $ignore = $this->_getGSUBignoreString($Lookup[$i]['Flag'], $Lookup[$i]['MarkFilteringSet']);
2516 - for($s=0;$s<$Lookup[$i]['Subtable'][$c]['ChainSubRuleSetCount'];$s++) {
2517 - // ChainSubRuleSet
2518 - $subRule = array();
2519 - $firstInputGlyph = $Lookup[$i]['Subtable'][$c]['CoverageGlyphs'][$s]; // First input gyyph
2520 - foreach($Lookup[$i]['Subtable'][$c]['ChainSubRuleSet'][$s]['ChainSubRule'] AS $rule) {
2521 - // ChainSubRule
2522 - $inputGlyphs = array();
2523 - if ($rule['InputGlyphCount']>1) {
2524 - $inputGlyphs = $rule['InputGlyphs'];
2525 - }
2526 - $inputGlyphs[0] = $firstInputGlyph;
2527 - ksort($inputGlyphs);
2528 - $nInput = count($inputGlyphs);
2529 -
2530 - if ($rule['BacktrackGlyphCount']) { $backtrackGlyphs = $rule['BacktrackGlyphs']; }
2531 - else { $backtrackGlyphs = array(); }
2532 - $backtrackMatch = $this->_makeGSUBbacktrackMatch($backtrackGlyphs, $ignore);
2533 -
2534 - if ($rule['LookaheadGlyphCount']) { $lookaheadGlyphs = $rule['LookaheadGlyphs']; }
2535 - else { $lookaheadGlyphs = array(); }
2536 -
2537 - $lookaheadMatch = $this->_makeGSUBlookaheadMatch($lookaheadGlyphs, $ignore);
2538 -
2539 - $nBsubs = 2*count($backtrackGlyphs);
2540 - $nIsubs = (2*$nInput)-1;
2541 -
2542 - $contextInputMatch = $this->_makeGSUBcontextInputMatch($inputGlyphs, $ignore, array(), 0);
2543 - $subRule = array('context' => 1, 'tag' => $tag, 'matchback' => $backtrackMatch, 'match' => ($contextInputMatch . $lookaheadMatch), 'nBacktrack' => count($backtrackGlyphs), 'nInput' => $nInput, 'nLookahead' => count($lookaheadGlyphs), 'rules' => array(), );
2544 -
2545 -
2546 - for ($b=0;$b<$rule['SubstCount'];$b++) {
2547 - $lup = $rule['LookupListIndex'][$b];
2548 - $seqIndex = $rule['SequenceIndex'][$b];
2549 -
2550 - // $Lookup[$lup] = secondary Lookup
2551 - for($lus=0;$lus<$Lookup[$lup]['SubtableCount'];$lus++) {
2552 - if (count($Lookup[$lup]['Subtable'][$lus]['subs'])) {
2553 - foreach($Lookup[$lup]['Subtable'][$lus]['subs'] AS $luss) {
2554 -
2555 - $lookupGlyphs = $luss['Replace'];
2556 - $mLen = count($lookupGlyphs);
2557 -
2558 - // Only apply if the (first) 'Replace' glyph from the
2559 - // Lookup list is in the [inputGlyphs] at ['SequenceIndex']
2560 - // then apply the substitution
2561 - if (strpos($inputGlyphs[$seqIndex],$lookupGlyphs[0])===false) { continue; }
2562 -
2563 - // Returns e.g. �(0612)�(ignore) (0613)�(ignore) (0614)�
2564 - $contextInputMatch = $this->_makeGSUBcontextInputMatch($inputGlyphs, $ignore, $lookupGlyphs, $seqIndex);
2565 -
2566 - $REPL = implode(" ",$luss['substitute']);
2567 -
2568 - if (strpos("isol fina fin2 fin3 medi med2 init ",$tag)!==false && $scripttag=='arab') {
2569 - $volt[] = array('match'=>$lookupGlyphs[0], 'replace'=>$REPL, 'tag'=>$tag, 'prel'=>$backtrackGlyphs, 'postl'=>$lookaheadGlyphs, 'ignore'=>$ignore);
2570 - }
2571 - else {
2572 - $subRule['rules'][] = array('type' => $Lookup[$lup]['Type'], 'match' => $lookupGlyphs, 'replace' => $luss['substitute'], 'seqIndex' => $seqIndex, 'key' => $lookupGlyphs[0], );
2573 - }
2574 - }
2575 - }
2576 - }
2577 - }
2578 -
2579 -
2580 - if (count($subRule['rules'])) $volt[] = $subRule;
2581 -
2582 -
2583 -
2584 - }
2585 - }
2586 -
2587 - }
2588 - // Format 2: Class-based Chaining Context Glyph Substitution p257
2589 - else if ($SubstFormat==2) {
2590 - $ignore = $this->_getGSUBignoreString($Lookup[$i]['Flag'], $Lookup[$i]['MarkFilteringSet']);
2591 - foreach($Lookup[$i]['Subtable'][$c]['ChainSubClassSet'] AS $inputClass=>$cscs) {
2592 - for($cscrule=0;$cscrule<$cscs['ChainSubClassRuleCnt'];$cscrule++) {
2593 - $rule = $cscs['ChainSubClassRule'][$cscrule];
2594 -
2595 - // These contain classes of glyphs as strings
2596 - // $Lookup[$i]['Subtable'][$c]['InputClasses'][(class)] e.g. 02E6|02E7|02E8
2597 - // $Lookup[$i]['Subtable'][$c]['LookaheadClasses'][(class)]
2598 - // $Lookup[$i]['Subtable'][$c]['BacktrackClasses'][(class)]
2599 -
2600 - // These contain arrays of classIndexes
2601 - // [Backtrack] [Lookahead] and [Input] (Input is from the second position only)
2602 -
2603 - $inputGlyphs = array();
2604 -
2605 - if (isset($Lookup[$i]['Subtable'][$c]['InputClasses'][$inputClass])) {
2606 - $inputGlyphs[0] = $Lookup[$i]['Subtable'][$c]['InputClasses'][$inputClass];
2607 - }
2608 - else { $inputGlyphs[0] = ''; }
2609 - if ($rule['InputGlyphCount']>1) {
2610 - // NB starts at 1
2611 - for ($gcl=1;$gcl<$rule['InputGlyphCount'];$gcl++) {
2612 - $classindex = $rule['Input'][$gcl];
2613 - if (isset($Lookup[$i]['Subtable'][$c]['InputClasses'][$classindex])) {
2614 - $inputGlyphs[$gcl] = $Lookup[$i]['Subtable'][$c]['InputClasses'][$classindex];
2615 - }
2616 - // if class[0] = all glyphs excluding those specified in all other classes
2617 - // set to blank '' for now
2618 - else { $inputGlyphs[$gcl] = ''; }
2619 - }
2620 - }
2621 -
2622 - $nInput = $rule['InputGlyphCount'];
2623 -
2624 - if ($rule['BacktrackGlyphCount']) {
2625 - for ($gcl=0;$gcl<$rule['BacktrackGlyphCount'];$gcl++) {
2626 - $classindex = $rule['Backtrack'][$gcl];
2627 - if (isset($Lookup[$i]['Subtable'][$c]['BacktrackClasses'][$classindex])) {
2628 - $backtrackGlyphs[$gcl] = $Lookup[$i]['Subtable'][$c]['BacktrackClasses'][$classindex];
2629 - }
2630 - // if class[0] = all glyphs excluding those specified in all other classes
2631 - // set to blank '' for now
2632 - else { $backtrackGlyphs[$gcl] = ''; }
2633 - }
2634 - }
2635 - else { $backtrackGlyphs = array(); }
2636 - // Returns e.g. �(FEEB|FEEC)(ignore) �(FD12|FD13)(ignore) �
2637 - $backtrackMatch = $this->_makeGSUBbacktrackMatch($backtrackGlyphs, $ignore);
2638 -
2639 - if ($rule['LookaheadGlyphCount']) {
2640 - for ($gcl=0;$gcl<$rule['LookaheadGlyphCount'];$gcl++) {
2641 - $classindex = $rule['Lookahead'][$gcl];
2642 - if (isset($Lookup[$i]['Subtable'][$c]['LookaheadClasses'][$classindex])) {
2643 - $lookaheadGlyphs[$gcl] = $Lookup[$i]['Subtable'][$c]['LookaheadClasses'][$classindex];
2644 - }
2645 - // if class[0] = all glyphs excluding those specified in all other classes
2646 - // set to blank '' for now
2647 - else { $lookaheadGlyphs[$gcl] = ''; }
2648 - }
2649 - }
2650 - else { $lookaheadGlyphs = array(); }
2651 - // Returns e.g. �(ignore) (FD12|FD13)�(ignore) (FEEB|FEEC)�
2652 - $lookaheadMatch = $this->_makeGSUBlookaheadMatch($lookaheadGlyphs, $ignore);
2653 -
2654 - $nBsubs = 2*count($backtrackGlyphs);
2655 - $nIsubs = (2*$nInput)-1;
2656 -
2657 - $contextInputMatch = $this->_makeGSUBcontextInputMatch($inputGlyphs, $ignore, array(), 0);
2658 - $subRule = array('context' => 1, 'tag' => $tag, 'matchback' => $backtrackMatch, 'match' => ($contextInputMatch . $lookaheadMatch), 'nBacktrack' => count($backtrackGlyphs), 'nInput' => $nInput, 'nLookahead' => count($lookaheadGlyphs), 'rules' => array(), );
2659 -
2660 - for ($b=0;$b<$rule['SubstCount'];$b++) {
2661 - $lup = $rule['LookupListIndex'][$b];
2662 - $seqIndex = $rule['SequenceIndex'][$b];
2663 -
2664 - // $Lookup[$lup] = secondary Lookup
2665 - for($lus=0;$lus<$Lookup[$lup]['SubtableCount'];$lus++) {
2666 - if (count($Lookup[$lup]['Subtable'][$lus]['subs'])) {
2667 - foreach($Lookup[$lup]['Subtable'][$lus]['subs'] AS $luss) {
2668 -
2669 - $lookupGlyphs = $luss['Replace'];
2670 - $mLen = count($lookupGlyphs);
2671 -
2672 - // Only apply if the (first) 'Replace' glyph from the
2673 - // Lookup list is in the [inputGlyphs] at ['SequenceIndex']
2674 - // then apply the substitution
2675 - if (strpos($inputGlyphs[$seqIndex],$lookupGlyphs[0])===false) { continue; }
2676 -
2677 - // Returns e.g. �(0612)�(ignore) (0613)�(ignore) (0614)�
2678 - $contextInputMatch = $this->_makeGSUBcontextInputMatch($inputGlyphs, $ignore, $lookupGlyphs, $seqIndex);
2679 - $REPL = implode(" ",$luss['substitute']);
2680 - // Returns e.g. "REPL\${6}\${8}" or "\${1}\${2} \${3} REPL\${4}\${6}\${8} \${9}"
2681 -
2682 - if (strpos("isol fina fin2 fin3 medi med2 init ",$tag)!==false && $scripttag=='arab') {
2683 - $volt[] = array('match'=>$lookupGlyphs[0], 'replace'=>$REPL, 'tag'=>$tag, 'prel'=>$backtrackGlyphs, 'postl'=>$lookaheadGlyphs, 'ignore'=>$ignore);
2684 - }
2685 - else {
2686 - $subRule['rules'][] = array('type' => $Lookup[$lup]['Type'], 'match' => $lookupGlyphs, 'replace' => $luss['substitute'], 'seqIndex' => $seqIndex, 'key' => $lookupGlyphs[0], );
2687 - }
2688 - }
2689 - }
2690 - }
2691 - }
2692 - if (count($subRule['rules'])) $volt[] = $subRule;
2693 -
2694 - }
2695 - }
2696 -
2697 -
2698 -//print_r($Lookup[$i]['Subtable'][$c]); exit;
2699 -
2700 - }
2701 - // Format 3: Coverage-based Chaining Context Glyph Substitution p259
2702 - else if ($SubstFormat==3) {
2703 - // IgnoreMarks flag set on main Lookup table
2704 - $ignore = $this->_getGSUBignoreString($Lookup[$i]['Flag'], $Lookup[$i]['MarkFilteringSet']);
2705 - $inputGlyphs = $Lookup[$i]['Subtable'][$c]['CoverageInputGlyphs'];
2706 - $CoverageInputGlyphs = implode('|', $inputGlyphs);
2707 - $nInput = $Lookup[$i]['Subtable'][$c]['InputGlyphCount'];
2708 -
2709 - if ($Lookup[$i]['Subtable'][$c]['BacktrackGlyphCount']) {
2710 - $backtrackGlyphs = $Lookup[$i]['Subtable'][$c]['CoverageBacktrackGlyphs'];
2711 - }
2712 - else { $backtrackGlyphs = array(); }
2713 - // Returns e.g. �(FEEB|FEEC)(ignore) �(FD12|FD13)(ignore) �
2714 - $backtrackMatch = $this->_makeGSUBbacktrackMatch($backtrackGlyphs, $ignore);
2715 -
2716 - if ($Lookup[$i]['Subtable'][$c]['LookaheadGlyphCount']) {
2717 - $lookaheadGlyphs = $Lookup[$i]['Subtable'][$c]['CoverageLookaheadGlyphs'];
2718 - }
2719 - else { $lookaheadGlyphs = array(); }
2720 - // Returns e.g. �(ignore) (FD12|FD13)�(ignore) (FEEB|FEEC)�
2721 - $lookaheadMatch = $this->_makeGSUBlookaheadMatch($lookaheadGlyphs, $ignore);
2722 -
2723 - $nBsubs = 2*count($backtrackGlyphs);
2724 - $nIsubs = (2*$nInput)-1;
2725 - $contextInputMatch = $this->_makeGSUBcontextInputMatch($inputGlyphs, $ignore, array(), 0);
2726 - $subRule = array('context' => 1, 'tag' => $tag, 'matchback' => $backtrackMatch, 'match' => ($contextInputMatch . $lookaheadMatch), 'nBacktrack' => count($backtrackGlyphs), 'nInput' => $nInput, 'nLookahead' => count($lookaheadGlyphs), 'rules' => array(), );
2727 -
2728 - for ($b=0;$b<$Lookup[$i]['Subtable'][$c]['SubstCount'];$b++) {
2729 - $lup = $Lookup[$i]['Subtable'][$c]['SubstLookupRecord'][$b]['LookupListIndex'];
2730 - $seqIndex = $Lookup[$i]['Subtable'][$c]['SubstLookupRecord'][$b]['SequenceIndex'];
2731 - for($lus=0;$lus<$Lookup[$lup]['SubtableCount'];$lus++) {
2732 - if (count($Lookup[$lup]['Subtable'][$lus]['subs'])) {
2733 - foreach($Lookup[$lup]['Subtable'][$lus]['subs'] AS $luss) {
2734 - $lookupGlyphs = $luss['Replace'];
2735 - $mLen = count($lookupGlyphs);
2736 -
2737 - // Only apply if the (first) 'Replace' glyph from the
2738 - // Lookup list is in the [inputGlyphs] at ['SequenceIndex']
2739 - // then apply the substitution
2740 - if (strpos($inputGlyphs[$seqIndex],$lookupGlyphs[0])===false) { continue; }
2741 -
2742 - // Returns e.g. �(0612)�(ignore) (0613)�(ignore) (0614)�
2743 - $contextInputMatch = $this->_makeGSUBcontextInputMatch($inputGlyphs, $ignore, $lookupGlyphs, $seqIndex);
2744 - $REPL = implode(" ",$luss['substitute']);
2745 -
2746 - if (strpos("isol fina fin2 fin3 medi med2 init ",$tag)!==false && $scripttag=='arab') {
2747 - $volt[] = array('match'=>$lookupGlyphs[0], 'replace'=>$REPL, 'tag'=>$tag, 'prel'=>$backtrackGlyphs, 'postl'=>$lookaheadGlyphs, 'ignore'=>$ignore);
2748 - }
2749 - else {
2750 - $subRule['rules'][] = array('type' => $Lookup[$lup]['Type'], 'match' => $lookupGlyphs, 'replace' => $luss['substitute'], 'seqIndex' => $seqIndex, 'key' => $lookupGlyphs[0], );
2751 - }
2752 - }
2753 - }
2754 - }
2755 - }
2756 - if (count($subRule['rules'])) $volt[] = $subRule;
2757 - }
2758 - }
2759 - }
2760 - }
2761 -//print_r($Lookup); exit;
2762 - return $volt;
2763 - }
2764 - //=====================================================================================
2765 - //=====================================================================================
2766 - // mPDF 5.7.1
2767 - function _checkGSUBignore($flag, $glyph, $MarkFilteringSet) {
2768 - $ignore = false;
2769 - // Flag & 0x0008 = Ignore Marks - (unless already done with MarkAttachmentType)
2770 - if ((($flag & 0x0008) == 0x0008 && ($flag & 0xFF00) == 0) && strpos($this->GlyphClassMarks,$glyph)) { $ignore = true; }
2771 - if ((($flag & 0x0004) == 0x0004) && strpos($this->GlyphClassLigatures,$glyph)) { $ignore = true; }
2772 - if ((($flag & 0x0002) == 0x0002) && strpos($this->GlyphClassBases,$glyph)) { $ignore = true; }
2773 - // Flag & 0xFF?? = MarkAttachmentType
2774 - if ($flag & 0xFF00) {
2775 - // "a lookup must ignore any mark glyphs that are not in the specified mark attachment class"
2776 - // $this->MarkAttachmentType is already adjusted for this i.e. contains all Marks except those in the MarkAttachmentClassDef table
2777 - if (strpos($this->MarkAttachmentType[($flag >> 8)],$glyph)) { $ignore = true; }
2778 - }
2779 - // Flag & 0x0010 = UseMarkFilteringSet
2780 - if (($flag & 0x0010) && strpos($this->MarkGlyphSets[$MarkFilteringSet],$glyph)) { $ignore = true; }
2781 - return $ignore;
2782 - }
2783 -
2784 - function _getGSUBignoreString($flag, $MarkFilteringSet) {
2785 - // If ignoreFlag set, combine all ignore glyphs into -> "((?:(?: FBA1| FBA2| FBA3))*)"
2786 - // else "()"
2787 - // for Input - set on secondary Lookup table if in Context, and set Backtrack and Lookahead on Context Lookup
2788 - $str = "";
2789 - $ignoreflag = 0;
2790 -
2791 - // Flag & 0xFF?? = MarkAttachmentType
2792 - if ($flag & 0xFF00) {
2793 - // "a lookup must ignore any mark glyphs that are not in the specified mark attachment class"
2794 - // $this->MarkAttachmentType is already adjusted for this i.e. contains all Marks except those in the MarkAttachmentClassDef table
2795 - $MarkAttachmentType = $flag >> 8;
2796 - $ignoreflag = $flag;
2797 - $str = $this->MarkAttachmentType[$MarkAttachmentType];
2798 - }
2799 -
2800 - // Flag & 0x0010 = UseMarkFilteringSet
2801 - if ($flag & 0x0010) {
2802 - die("This font ".$this->fontkey." contains MarkGlyphSets - Not tested yet");
2803 - $str = $this->MarkGlyphSets[$MarkFilteringSet];
2804 - }
2805 -
2806 - // If Ignore Marks set, supercedes any above
2807 - // Flag & 0x0008 = Ignore Marks - (unless already done with MarkAttachmentType)
2808 - if (($flag & 0x0008) == 0x0008 && ($flag & 0xFF00) == 0) {
2809 - $ignoreflag = 8;
2810 - $str = $this->GlyphClassMarks;
2811 - }
2812 -
2813 - // Flag & 0x0004 = Ignore Ligatures
2814 - if (($flag & 0x0004) == 0x0004) {
2815 - $ignoreflag += 4;
2816 - if ($str) { $str .= "|"; }
2817 - $str .= $this->GlyphClassLigatures;
2818 - }
2819 - // Flag & 0x0002 = Ignore BaseGlyphs
2820 - if (($flag & 0x0002) == 0x0002) {
2821 - $ignoreflag += 2;
2822 - if ($str) { $str .= "|"; }
2823 - $str .= $this->GlyphClassBases;
2824 - }
2825 - if ($str) {
2826 - // This originally returned e.g. ((?:(?:[IGNORE8]))*) when NOT specific to a Lookup e.g. rtlSub in
2827 - // arabictypesetting.GSUB.arab.DFLT.php
2828 - // This would save repeatedly saving long text strings if used multiple times
2829 - // When writing e.g. arabictypesetting.GSUB.arab.DFLT.php to file, included as $ignore[8]
2830 - // Would need to also write the $ignore array to that file
2831 - // // If UseMarkFilteringSet (specific to the Lookup) return the string
2832 - // if (($flag & 0x0010) && ($flag & 0x0008) != 0x0008) {
2833 - // return "((?:(?:" . $str . "))*)";
2834 - // }
2835 - // else { return "((?:(?:" . "[IGNORE".$ignoreflag."]" . "))*)"; }
2836 - // // e.g. ((?:(?: 0031| 0032| 0033| 0034| 0045))*)
2837 -
2838 - // But never finished coding it to add the $ignore array to the file, and it doesn't seem to occur often enough to be worth
2839 - // writing. So just output it as a string:
2840 - return "((?:(?:" . $str . "))*)";
2841 -
2842 - }
2843 - else return "()";
2844 - }
2845 -
2846 - // GSUB Patterns
2847 -
2848 -/*
2849 - BACKTRACK INPUT LOOKAHEAD
2850 -================================== ================== ==================================
2851 -(FEEB|FEEC)(ign) �(FD12|FD13)(ign) �(0612)�(ign) (0613)�(ign) (FD12|FD13)�(ign) (FEEB|FEEC)
2852 - Backtrack 1 Backtrack 2 Input 1 Input 2 Lookahead 1 Lookahead 2
2853 - \${1} \${2} \${3} \${4} \${5+} \${6+} \${7+} \${8+}
2854 -
2855 - nBacktrack = 2 nInput = 2 nLookahead = 2
2856 -
2857 - nBsubs = 2xnBack nIsubs = (nBsubs+) nLsubs = (nBsubs+nIsubs+) 2xnLookahead
2858 - "\${1}\${2} " (nInput*2)-1 "\${5+} \${6+}"
2859 - "REPL"
2860 -
2861 -�\${1}\${2} �\${3}\${4} �REPL�\${5+} \${6+}�\${7+} \${8+}�
2862 -
2863 -
2864 - INPUT nInput = 5
2865 -============================================================
2866 -�(0612)�(ign) (0613)�(ign) (0614)�(ign) (0615)�(ign) (0615)�
2867 -\${1} \${2} \${3} \${4} \${5} \${6} \${7} \${8} \${9} (All backreference numbers are + nBsubs)
2868 -Input 1 Input 2 Input 3 Input 4 Input 5
2869 -
2870 -A====== SequenceIndex=1 ; Lookup match nGlyphs=1
2871 -B=================== SequenceIndex=1 ; Lookup match nGlyphs=2
2872 -C=============================== SequenceIndex=1 ; Lookup match nGlyphs=3
2873 - D======================= SequenceIndex=2 ; Lookup match nGlyphs=2
2874 - E===================================== SequenceIndex=2 ; Lookup match nGlyphs=3
2875 - F====================== SequenceIndex=4 ; Lookup match nGlyphs=2
2876 -
2877 -All backreference numbers are + nBsubs
2878 -A - "REPL\${2} \${3}\${4} \${5}\${6} \${7}\${8} \${9}"
2879 -B - "REPL\${2}\${4} \${5}\${6} \${7}\${8} \${9}"
2880 -C - "REPL\${2}\${4}\${6} \${7}\${8} \${9}"
2881 -D - "\${1} REPL\${2}\${4}\${6} \${7}\${8} \${9}"
2882 -E - "\${1} REPL\${2}\${4}\${6}\${8} \${9}"
2883 -F - "\${1}\${2} \${3}\${4} \${5} REPL\${6}\${8}"
2884 -*/
2885 -
2886 - function _makeGSUBcontextInputMatch($inputGlyphs, $ignore, $lookupGlyphs, $seqIndex) {
2887 - // $ignore = "((?:(?: FBA1| FBA2| FBA3))*)" or "()"
2888 - // Returns e.g. �(0612)�(ignore) (0613)�(ignore) (0614)�
2889 - // $inputGlyphs = array of glyphs(glyphstrings) making up Input sequence in Context
2890 - // $lookupGlyphs = array of glyphs (single Glyphs) making up Lookup Input sequence
2891 - $mLen = count($lookupGlyphs); // nGlyphs in the secondary Lookup match
2892 - $nInput = count($inputGlyphs); // nGlyphs in the Primary Input sequence
2893 - $str = "";
2894 - for($i=0;$i<$nInput;$i++) {
2895 - if ($i>0) { $str .= $ignore." "; }
2896 - if ($i>=$seqIndex && $i<($seqIndex+$mLen)) { $str .= "(".$lookupGlyphs[($i-$seqIndex)].")"; }
2897 - else { $str .= "(".$inputGlyphs[($i)].")"; }
2898 - }
2899 - return $str;
2900 - }
2901 -
2902 - function _makeGSUBinputMatch($inputGlyphs, $ignore) {
2903 - // $ignore = "((?:(?: FBA1| FBA2| FBA3))*)" or "()"
2904 - // Returns e.g. �(0612)�(ignore) (0613)�(ignore) (0614)�
2905 - // $inputGlyphs = array of glyphs(glyphstrings) making up Input sequence in Context
2906 - // $lookupGlyphs = array of glyphs making up Lookup Input sequence - if applicable
2907 - $str = "";
2908 - for($i=1;$i<=count($inputGlyphs);$i++) {
2909 - if ($i>1) { $str .= $ignore." "; }
2910 - $str .= "(".$inputGlyphs[($i-1)].")";
2911 - }
2912 - return $str;
2913 - }
2914 -
2915 - function _makeGSUBbacktrackMatch($backtrackGlyphs, $ignore) {
2916 - // $ignore = "((?:(?: FBA1| FBA2| FBA3))*)" or "()"
2917 - // Returns e.g. �(FEEB|FEEC)(ignore) �(FD12|FD13)(ignore) �
2918 - // $backtrackGlyphs = array of glyphstrings making up Backtrack sequence
2919 - // 3 2 1 0
2920 - // each item being e.g. E0AD|E0AF|F1FD
2921 - $str = "";
2922 - for($i=(count($backtrackGlyphs)-1);$i>=0;$i--) {
2923 - $str .= "(".$backtrackGlyphs[$i].")".$ignore." ";
2924 - }
2925 - return $str;
2926 - }
2927 -
2928 - function _makeGSUBlookaheadMatch($lookaheadGlyphs, $ignore) {
2929 - // $ignore = "((?:(?: FBA1| FBA2| FBA3))*)" or "()"
2930 - // Returns e.g. �(ignore) (FD12|FD13)�(ignore) (FEEB|FEEC)�
2931 - // $lookaheadGlyphs = array of glyphstrings making up Lookahead sequence
2932 - // 0 1 2 3
2933 - // each item being e.g. E0AD|E0AF|F1FD
2934 - $str = "";
2935 - for($i=0;$i<count($lookaheadGlyphs);$i++) {
2936 - $str .= $ignore." (".$lookaheadGlyphs[$i].")";
2937 - }
2938 - return $str;
2939 - }
2940 -
2941 -
2942 -
2943 - function _makeGSUBinputReplacement($nInput, $REPL, $ignore, $nBsubs, $mLen, $seqIndex) {
2944 - // Returns e.g. "REPL\${6}\${8}" or "\${1}\${2} \${3} REPL\${4}\${6}\${8} \${9}"
2945 - // $nInput nGlyphs in the Primary Input sequence
2946 - // $REPL replacement glyphs from secondary lookup
2947 - // $ignore = "((?:(?: FBA1| FBA2| FBA3))*)" or "()"
2948 - // $nBsubs Number of Backtrack substitutions (= 2x Number of Backtrack glyphs)
2949 - // $mLen nGlyphs in the secondary Lookup match - if no secondary lookup, should=$nInput
2950 - // $seqIndex Sequence Index to apply the secondary match
2951 - if ($ignore=="()") { $ign = false; }
2952 - else { $ign = true; }
2953 - $str = "";
2954 - if ($nInput == 1) { $str = $REPL; }
2955 - else if ($nInput>1) {
2956 - if ($mLen==$nInput) { // whole string replaced
2957 - $str = $REPL;
2958 - if ($ign) {
2959 - // for every nInput over 1, add another replacement backreference, to move IGNORES after replacement
2960 - for($x=2;$x<=$nInput;$x++) {
2961 - $str .= '\\'.($nBsubs+(2*($x-1)));
2962 - }
2963 - }
2964 - }
2965 - else { // if only part of string replaced:
2966 - for($x=1;$x<($seqIndex+1);$x++) {
2967 - if ($x==1) { $str .= '\\'.($nBsubs + 1); }
2968 - else {
2969 - if ($ign) { $str .= '\\'.($nBsubs+(2*($x-1))); }
2970 - $str .= ' \\'.($nBsubs+1+(2*($x-1)));
2971 - }
2972 - }
2973 - if ($seqIndex>0) { $str .= " "; }
2974 - $str .= $REPL;
2975 - if ($ign) {
2976 - for($x=(max(($seqIndex+1),2));$x<($seqIndex+1+$mLen);$x++) { // move IGNORES after replacement
2977 - $str .= '\\'.($nBsubs+(2*($x-1)));
2978 - }
2979 - }
2980 - for($x=($seqIndex+1+$mLen);$x<=$nInput;$x++) {
2981 - if ($ign) { $str .= '\\'.($nBsubs+(2*($x-1))); }
2982 - $str .= ' \\'.($nBsubs+1+(2*($x-1)));
2983 - }
2984 - }
2985 - }
2986 - return $str;
2987 - }
2988 -
2989 -
2990 -
2991 - //////////////////////////////////////////////////////////////////////////////////
2992 - function _getCoverage($convert2hex=true, $mode=1) {
2993 - $g = array();
2994 - $ctr = 0;
2995 - $CoverageFormat= $this->read_ushort();
2996 - if ($CoverageFormat == 1) {
2997 - $CoverageGlyphCount= $this->read_ushort();
2998 - for ($gid=0;$gid<$CoverageGlyphCount;$gid++) {
2999 - $glyphID = $this->read_ushort();
3000 - $uni = $this->glyphToChar[$glyphID][0];
3001 - if ($convert2hex) { $g[] = unicode_hex($uni); }
3002 - else if ($mode==2) { $g[$uni] = $ctr; $ctr++; }
3003 - else { $g[] = $glyphID; }
3004 - }
3005 - }
3006 - if ($CoverageFormat == 2) {
3007 - $RangeCount= $this->read_ushort();
3008 - for ($r=0;$r<$RangeCount;$r++) {
3009 - $start = $this->read_ushort();
3010 - $end = $this->read_ushort();
3011 - $StartCoverageIndex = $this->read_ushort(); // n/a
3012 - for ($glyphID=$start;$glyphID<=$end;$glyphID++) {
3013 - $uni = $this->glyphToChar[$glyphID][0];
3014 - if ($convert2hex) { $g[] = unicode_hex($uni); }
3015 - else if ($mode==2) { $uni = $g[$uni] = $ctr; $ctr++; }
3016 - else { $g[] = $glyphID; }
3017 - }
3018 - }
3019 - }
3020 - return $g;
3021 - }
3022 -
3023 - //////////////////////////////////////////////////////////////////////////////////
3024 - function _getClasses($offset) {
3025 - $this->seek($offset);
3026 - $ClassFormat = $this->read_ushort();
3027 - $GlyphByClass = array();
3028 - if ($ClassFormat == 1) {
3029 - $StartGlyph = $this->read_ushort();
3030 - $GlyphCount = $this->read_ushort();
3031 - for ($i=0;$i<$GlyphCount;$i++) {
3032 - $startGlyphID = $StartGlyph + $i;
3033 - $endGlyphID = $StartGlyph + $i;
3034 - $class = $this->read_ushort();
3035 - for($g=$startGlyphID;$g<=$endGlyphID;$g++) {
3036 - if (isset($this->glyphToChar[$g][0])) {
3037 - $GlyphByClass[$class][] = unicode_hex($this->glyphToChar[$g][0]);
3038 - }
3039 - }
3040 - }
3041 - }
3042 - else if ($ClassFormat == 2) {
3043 - $tableCount = $this->read_ushort();
3044 - for ($i=0;$i<$tableCount;$i++) {
3045 - $startGlyphID = $this->read_ushort();
3046 - $endGlyphID = $this->read_ushort();
3047 - $class = $this->read_ushort();
3048 - for($g=$startGlyphID;$g<=$endGlyphID;$g++) {
3049 - if ($this->glyphToChar[$g][0]) {
3050 - $GlyphByClass[$class][] = unicode_hex($this->glyphToChar[$g][0]);
3051 - }
3052 - }
3053 - }
3054 - }
3055 - $gbc = array();
3056 - foreach($GlyphByClass AS $class=>$garr) { $gbc[$class] = implode('|',$garr); }
3057 - return $gbc;
3058 - }
3059 - //////////////////////////////////////////////////////////////////////////////////
3060 - //////////////////////////////////////////////////////////////////////////////////
3061 - //////////////////////////////////////////////////////////////////////////////////
3062 - //////////////////////////////////////////////////////////////////////////////////
3063 - //////////////////////////////////////////////////////////////////////////////////
3064 - function _getGPOStables() {
3065 - ///////////////////////////////////
3066 - // GPOS - Glyph Positioning
3067 - ///////////////////////////////////
3068 - if (isset($this->tables["GPOS"])) {
3069 - $ffeats = array();
3070 - $gpos_offset = $this->seek_table("GPOS");
3071 - $this->skip(4);
3072 - $ScriptList_offset = $gpos_offset + $this->read_ushort();
3073 - $FeatureList_offset = $gpos_offset + $this->read_ushort();
3074 - $LookupList_offset = $gpos_offset + $this->read_ushort();
3075 -
3076 - // ScriptList
3077 - $this->seek($ScriptList_offset );
3078 - $ScriptCount = $this->read_ushort();
3079 - for ($i=0;$i<$ScriptCount;$i++) {
3080 - $ScriptTag = $this->read_tag(); // = "beng", "deva" etc.
3081 - $ScriptTableOffset = $this->read_ushort();
3082 - $ffeats[$ScriptTag] = $ScriptList_offset + $ScriptTableOffset;
3083 - }
3084 -
3085 - // Script Table
3086 - foreach($ffeats AS $t=>$o) {
3087 - $ls = array();
3088 - $this->seek($o);
3089 - $DefLangSys_offset = $this->read_ushort();
3090 - if ($DefLangSys_offset > 0) {
3091 - $ls['DFLT'] = $DefLangSys_offset + $o;
3092 - }
3093 - $LangSysCount = $this->read_ushort();
3094 - for ($i=0;$i<$LangSysCount;$i++) {
3095 - $LangTag = $this->read_tag(); // =
3096 - $LangTableOffset = $this->read_ushort();
3097 - $ls[$LangTag] = $o + $LangTableOffset;
3098 - }
3099 - $ffeats[$t] = $ls;
3100 - }
3101 -
3102 -
3103 - // Get FeatureIndexList
3104 - // LangSys Table - from first listed langsys
3105 - foreach($ffeats AS $st=>$scripts) {
3106 - foreach($scripts AS $t=>$o) {
3107 - $FeatureIndex = array();
3108 - $langsystable_offset = $o;
3109 - $this->seek($langsystable_offset);
3110 - $LookUpOrder = $this->read_ushort(); //==NULL
3111 - $ReqFeatureIndex = $this->read_ushort();
3112 - if ($ReqFeatureIndex != 0xFFFF) { $FeatureIndex[] = $ReqFeatureIndex ; }
3113 - $FeatureCount = $this->read_ushort();
3114 - for ($i=0;$i<$FeatureCount;$i++) {
3115 - $FeatureIndex[] = $this->read_ushort(); // = index of feature
3116 - }
3117 - $ffeats[$st][$t] = $FeatureIndex;
3118 - }
3119 - }
3120 -//print_r($ffeats); exit;
3121 -
3122 -
3123 - // Feauture List => LookupListIndex es
3124 - $this->seek($FeatureList_offset );
3125 - $FeatureCount = $this->read_ushort();
3126 - $Feature = array();
3127 - for ($i=0;$i<$FeatureCount;$i++) {
3128 - $tag = $this->read_tag() ;
3129 - if ($tag == 'kern') { $this->haskernGPOS = true; }
3130 - $Feature[$i] = array('tag' => $tag);
3131 - $Feature[$i]['offset'] = $FeatureList_offset + $this->read_ushort();
3132 - }
3133 - for ($i=0;$i<$FeatureCount;$i++) {
3134 - $this->seek($Feature[$i]['offset']);
3135 - $this->read_ushort(); // null
3136 - $Feature[$i]['LookupCount'] = $Lookupcount = $this->read_ushort();
3137 - $Feature[$i]['LookupListIndex'] = array();
3138 - for ($c=0;$c<$Lookupcount;$c++) {
3139 - $Feature[$i]['LookupListIndex'][] = $this->read_ushort();
3140 - }
3141 - }
3142 -
3143 -
3144 - foreach($ffeats AS $st=>$scripts) {
3145 - foreach($scripts AS $t=>$o) {
3146 - $FeatureIndex = $ffeats[$st][$t];
3147 - foreach($FeatureIndex AS $k=>$fi) {
3148 - $ffeats[$st][$t][$k] = $Feature[$fi];
3149 - }
3150 - }
3151 - }
3152 -//print_r($ffeats); exit;
3153 - //=====================================================================================
3154 - $gpos = array();
3155 - $GPOSScriptLang = array();
3156 - foreach($ffeats AS $st=>$scripts) {
3157 - foreach($scripts AS $t=>$langsys) {
3158 - $lg = array();
3159 - foreach($langsys AS $ft) {
3160 - $lg[$ft['LookupListIndex'][0]] = $ft;
3161 - }
3162 - // list of Lookups in order they need to be run i.e. order listed in Lookup table
3163 - ksort($lg);
3164 - foreach($lg AS $ft) {
3165 - $gpos[$st][$t][$ft['tag']] = $ft['LookupListIndex'];
3166 - }
3167 - if (!isset($GPOSScriptLang[$st])) { $GPOSScriptLang[$st] = ''; }
3168 - $GPOSScriptLang[$st] .= $t.' ';
3169 - }
3170 - }
3171 -
3172 - //=====================================================================================
3173 - // Get metadata and offsets for whole Lookup List table
3174 - $this->seek($LookupList_offset );
3175 - $LookupCount = $this->read_ushort();
3176 - $Lookup = array();
3177 - $Offsets = array();
3178 - $SubtableCount = array();
3179 - for ($i=0;$i<$LookupCount;$i++) {
3180 - $Offsets[$i] = $LookupList_offset + $this->read_ushort();
3181 - }
3182 - for ($i=0;$i<$LookupCount;$i++) {
3183 - $this->seek($Offsets[$i]);
3184 - $Lookup[$i]['Type'] = $this->read_ushort();
3185 - $Lookup[$i]['Flag'] = $flag = $this->read_ushort();
3186 - $Lookup[$i]['SubtableCount'] = $SubtableCount[$i] = $this->read_ushort();
3187 - for ($c=0;$c<$SubtableCount[$i] ;$c++) {
3188 - $Lookup[$i]['Subtables'][$c] = $Offsets[$i] + $this->read_ushort();
3189 -
3190 - }
3191 - // MarkFilteringSet = Index (base 0) into GDEF mark glyph sets structure
3192 - if (($flag & 0x0010) == 0x0010) {
3193 - $Lookup[$i]['MarkFilteringSet'] = $this->read_ushort();
3194 - }
3195 - else { $Lookup[$i]['MarkFilteringSet'] = ''; }
3196 -
3197 - // Lookup Type 9: Extension
3198 - if ($Lookup[$i]['Type'] == 9) {
3199 - // Overwrites new offset (32-bit) for each subtable, and a new lookup Type
3200 - for ($c=0;$c<$SubtableCount[$i] ;$c++) {
3201 - $this->seek($Lookup[$i]['Subtables'][$c]);
3202 - $ExtensionPosFormat = $this->read_ushort();
3203 - $type = $this->read_ushort();
3204 - $Lookup[$i]['Subtables'][$c] = $Lookup[$i]['Subtables'][$c] + $this->read_ulong();
3205 - }
3206 - $Lookup[$i]['Type'] = $type;
3207 - }
3208 -
3209 - }
3210 -
3211 -
3212 - //=====================================================================================
3213 - // Process Whole LookupList - Get LuCoverage = Lookup coverage just for first glyph
3214 - $this->LuCoverage = array();
3215 - for ($i=0;$i<$LookupCount;$i++) {
3216 - for ($c=0;$c<$Lookup[$i]['SubtableCount'] ;$c++) {
3217 -
3218 - $this->seek($Lookup[$i]['Subtables'][$c]);
3219 - $PosFormat= $this->read_ushort();
3220 -
3221 - if ($Lookup[$i]['Type']==7 && $PosFormat==3) { $this->skip(4); }
3222 - else if ($Lookup[$i]['Type']==8 && $PosFormat==3) {
3223 - $BacktrackGlyphCount= $this->read_ushort();
3224 - $this->skip(2*$BacktrackGlyphCount + 2);
3225 - }
3226 - // NB Coverage only looks at glyphs for position 1 (i.e. 7.3 and 8.3) // NEEDS TO READ ALL ********************
3227 - // NB For e.g. Type 4, this may be the Coverage for the Mark
3228 - $Coverage = $Lookup[$i]['Subtables'][$c] + $this->read_ushort();
3229 - $this->seek($Coverage);
3230 - $glyphs = $this->_getCoverage(false,2);
3231 - $this->LuCoverage[$i][$c] = $glyphs;
3232 - }
3233 - }
3234 -
3235 -
3236 -
3237 - //=====================================================================================
3238 -
3239 -
3240 -
3241 -//print_r($GPOSScriptLang); exit;
3242 -//print_r($gpos); exit;
3243 -//print_r($Lookup); exit;
3244 -
3245 -
3246 -
3247 -
3248 - $s = '<?php
3249 -$LuCoverage = '.var_export($this->LuCoverage , true).';
3250 -?>';
3251 -
3252 -
3253 - file_put_contents(_MPDF_TTFONTDATAPATH.$this->fontkey.'.GPOSdata.php', $s);
3254 -
3255 -
3256 -
3257 - return array($GPOSScriptLang, $gpos, $Lookup);
3258 -
3259 - } // end if GPOS
3260 - }
3261 -
3262 - //////////////////////////////////////////////////////////////////////////////////
3263 -
3264 - //=====================================================================================
3265 - //=====================================================================================
3266 - //=====================================================================================
3267 - //=====================================================================================
3268 - //=====================================================================================
3269 - //=====================================================================================
3270 -
3271 - function makeSubset($file, &$subset, $TTCfontID=0, $debug=false, $useOTL=false) { // mPDF 5.7.1
3272 - $this->useOTL = $useOTL; // mPDF 5.7.1
1117 + function makeSubset($file, &$subset, $TTCfontID=0, $debug=false) {
3273 1118 $this->filename = $file;
3274 1119 $this->fh = fopen($file ,'rb') or die('Can\'t open file ' . $file);
3275 1120 $this->_pos = 0;
3276 1121 $this->charWidths = '';
@@ -3282,10 +1124,8 @@
3282 1124 $this->tables = array();
3283 1125 $this->otables = array();
3284 1126 $this->ascent = 0;
3285 1127 $this->descent = 0;
3286 - $this->strikeoutSize = 0;
3287 - $this->strikeoutPosition = 0;
3288 1128 $this->numTTCFonts = 0;
3289 1129 $this->TTCFonts = array();
3290 1130 $this->skip(4);
3291 1131 $this->maxUni = 0;
@@ -3301,8 +1141,9 @@
3301 1141 $this->version = $version = $this->read_ulong(); // TTFont version again now
3302 1142 }
3303 1143 $this->readTableDirectory($debug);
3304 1144
1145 +
3305 1146 ///////////////////////////////////
3306 1147 // head - Font header table
3307 1148 ///////////////////////////////////
3308 1149 $this->seek_table("head");
@@ -3355,31 +1196,10 @@
3355 1196 $glyphToChar = array();
3356 1197 $charToGlyph = array();
3357 1198 $this->getCMAP4($unicode_cmap_offset, $glyphToChar, $charToGlyph );
3358 1199
3359 - ///////////////////////////////////
3360 - // mPDF 5.7.1
3361 - // Map Unmapped glyphs - from $numGlyphs
3362 - if ($useOTL) {
3363 - $bctr = 0xE000;
3364 - for ($gid=1; $gid<$numGlyphs; $gid++) {
3365 - if (!isset($glyphToChar[$gid])) {
3366 - while(isset($charToGlyph[$bctr])) { $bctr++; } // Avoid overwriting a glyph already mapped in PUA
3367 - if ($bctr > 0xF8FF) {
3368 - die($file." : WARNING - Font cannot map all included glyphs into Private Use Area U+E000 - U+F8FF; cannot use useOTL on this font");
3369 - }
3370 - $glyphToChar[$gid][] = $bctr;
3371 - $charToGlyph[$bctr] = $gid;
3372 - $bctr++;
3373 - }
3374 - }
3375 - }
3376 - ///////////////////////////////////
3377 -
3378 1200 $this->charToGlyph = $charToGlyph;
3379 - $this->glyphToChar = $glyphToChar;
3380 1201
3381 -
3382 1202 ///////////////////////////////////
3383 1203 // hmtx - Horizontal metrics table
3384 1204 ///////////////////////////////////
3385 1205 $scale = 1; // not used
@@ -3389,9 +1209,9 @@
3389 1209 // loca - Index to location
3390 1210 ///////////////////////////////////
3391 1211 $this->getLOCA($indexToLocFormat, $numGlyphs);
3392 1212
3393 - $subsetglyphs = array(0=>0, 1=>1, 2=>2);
1213 + $subsetglyphs = array(0=>0, 1=>1, 2=>2); // mPDF 5.3.31
3394 1214 $subsetCharToGlyph = array();
3395 1215 foreach($subset AS $code) {
3396 1216 if (isset($this->charToGlyph[$code])) {
3397 1217 $subsetglyphs[$this->charToGlyph[$code]] = $code; // Old Glyph ID => Unicode
@@ -3405,11 +1225,12 @@
3405 1225
3406 1226 $glyphSet = array();
3407 1227 ksort($subsetglyphs);
3408 1228 $n = 0;
1229 + // mPDF 5.0
3409 1230 $fsLastCharIndex = 0; // maximum Unicode index (character code) in this font, according to the cmap subtable for platform ID 3 and platform- specific encoding ID 0 or 1.
3410 1231 foreach($subsetglyphs AS $originalGlyphIdx => $uni) {
3411 - $fsLastCharIndex = max($fsLastCharIndex , $uni);
1232 + $fsLastCharIndex = max($fsLastCharIndex , $uni); // mPDF 5.0
3412 1233 $glyphSet[$originalGlyphIdx] = $n; // old glyphID to new glyphID
3413 1234 $n++;
3414 1235 }
3415 1236
@@ -3428,17 +1249,14 @@
3428 1249
3429 1250 ///////////////////////////////////
3430 1251 // name - table copied from the original
3431 1252 ///////////////////////////////////
3432 - // MS spec says that "Platform and encoding ID's in the name table should be consistent with those in the cmap table.
3433 - // If they are not, the font will not load in Windows"
3434 - // Doesn't seem to be a problem?
3435 - ///////////////////////////////////
3436 1253 $this->add('name', $this->get_table('name'));
3437 1254
3438 1255 ///////////////////////////////////
3439 1256 //tables copied from the original
3440 1257 ///////////////////////////////////
1258 + // mPDF 5.0.067 // 5.1.020
3441 1259 $tags = array ('cvt ', 'fpgm', 'prep', 'gasp');
3442 1260 foreach($tags AS $tag) {
3443 1261 if (isset($this->tables[$tag])) { $this->add($tag, $this->get_table($tag)); }
3444 1262 }
@@ -3491,8 +1309,9 @@
3491 1309 }
3492 1310 $searchRange = $searchRange * 2;
3493 1311 $rangeShift = $segCount * 2 - $searchRange;
3494 1312 $length = 16 + (8*$segCount ) + ($numGlyphs+1);
1313 + // mPDF 5.3.30
3495 1314 $cmap = array(0, 3, // Index : version, number of encoding subtables
3496 1315 0, 0, // Encoding Subtable : platform (UNI=0), encoding 0
3497 1316 0, 28, // Encoding Subtable : offset (hi,lo)
3498 1317 0, 3, // Encoding Subtable : platform (UNI=0), encoding 3
@@ -3585,8 +1404,9 @@
3585 1404 if ($glyphLen > 0) $data = $this->get_chunk($glyfOffset+$glyphPos,$glyphLen);
3586 1405 else $data = '';
3587 1406 }
3588 1407
1408 + // mPDF 5.0
3589 1409 if ($glyphLen > 0) {
3590 1410 if (_RECALC_PROFILE) {
3591 1411 $xMin = $this->unpack_short(substr($data,2,2));
3592 1412 $yMin = $this->unpack_short(substr($data,4,2));
@@ -3625,9 +1445,9 @@
3625 1445 else if ($flags & GF_TWOBYTWO) { $pos_in_glyph += 8; }
3626 1446 }
3627 1447 $maxComponentElements = max($maxComponentElements, $nComponentElements);
3628 1448 }
3629 - // Simple Glyph
1449 + // mPDF 5.0 - Simple Glyph
3630 1450 else if (_RECALC_PROFILE && $glyphLen > 2 && $up[1] < (1 << 15) && $up[1] > 0) { // Number of contours > 0 simple glyph
3631 1451 $nContours = $up[1];
3632 1452 $this->glyphdata[$originalGlyphIdx]['nContours'] = $nContours;
3633 1453 $maxContours = max($maxContours, $nContours);
@@ -3694,8 +1514,9 @@
3694 1514 $head = $this->_set_short($head, 36, $xMinT); // for all glyph bounding boxes
3695 1515 $head = $this->_set_short($head, 38, $yMinT); // for all glyph bounding boxes
3696 1516 $head = $this->_set_short($head, 40, $xMaxT); // for all glyph bounding boxes
3697 1517 $head = $this->_set_short($head, 42, $yMaxT); // for all glyph bounding boxes
1518 + // mPDF 5.3.33
3698 1519 $head[17] = chr($head[17] & ~(1 << 4)); // Unset Bit 4 (as hdmx/LTSH tables not included)
3699 1520 }
3700 1521 $this->add('head', $head);
3701 1522
@@ -3732,9 +1553,11 @@
3732 1553 ///////////////////////////////////
3733 1554 // OS/2 - OS/2
3734 1555 ///////////////////////////////////
3735 1556 if (isset($this->tables['OS/2'])) {
1557 + // mPDF 5.2.03
3736 1558 $os2_offset = $this->seek_table("OS/2");
1559 + // mPDF 5.0
3737 1560 if (_RECALC_PROFILE) {
3738 1561 $fsSelection = $this->get_ushort($os2_offset+62);
3739 1562 $fsSelection = ($fsSelection & ~(1 << 6)); // 2-byte bit field containing information concerning the nature of the font patterns
3740 1563 // bit#0 = Italic; bit#5=Bold
@@ -3749,8 +1572,9 @@
3749 1572
3750 1573 $os2 = $this->get_table('OS/2');
3751 1574 if (_RECALC_PROFILE) {
3752 1575 $os2 = $this->_set_ushort($os2, 62, $fsSelection);
1576 + // mPDF 5.2.03
3753 1577 $os2 = $this->_set_ushort($os2, 66, $fsLastCharIndex);
3754 1578 $os2 = $this->_set_ushort($os2, 42, 0x0000); // ulCharRange (ulUnicodeRange) bits 24-31 | 16-23
3755 1579 $os2 = $this->_set_ushort($os2, 44, 0x0000); // ulCharRange (Unicode ranges) bits 8-15 | 0-7
3756 1580 $os2 = $this->_set_ushort($os2, 46, $nonBMP); // ulCharRange (Unicode ranges) bits 56-63 | 48-55
@@ -3775,13 +1599,12 @@
3775 1599
3776 1600 //================================================================================
3777 1601
3778 1602 // Also does SMP
3779 - function makeSubsetSIP($file, &$subset, $TTCfontID=0, $debug=false, $useOTL=0) { // mPDF 5.7.1
1603 + function makeSubsetSIP($file, &$subset, $TTCfontID=0, $debug=false) {
3780 1604 $this->fh = fopen($file ,'rb') or die('Can\'t open file ' . $file);
3781 1605 $this->filename = $file;
3782 1606 $this->_pos = 0;
3783 - $this->useOTL = $useOTL; // mPDF 5.7.1
3784 1607 $this->charWidths = '';
3785 1608 $this->glyphPos = array();
3786 1609 $this->charToGlyph = array();
3787 1610 $this->tables = array();
@@ -3787,10 +1610,8 @@
3787 1610 $this->tables = array();
3788 1611 $this->otables = array();
3789 1612 $this->ascent = 0;
3790 1613 $this->descent = 0;
3791 - $this->strikeoutSize = 0;
3792 - $this->strikeoutPosition = 0;
3793 1614 $this->numTTCFonts = 0;
3794 1615 $this->TTCFonts = array();
3795 1616 $this->skip(4);
3796 1617 if ($TTCfontID > 0) {
@@ -3806,8 +1627,9 @@
3806 1627 }
3807 1628 $this->readTableDirectory($debug);
3808 1629
3809 1630
1631 +
3810 1632 ///////////////////////////////////
3811 1633 // head - Font header table
3812 1634 ///////////////////////////////////
3813 1635 $this->seek_table("head");
@@ -3843,8 +1665,9 @@
3843 1665 $platformID = $this->read_ushort();
3844 1666 $encodingID = $this->read_ushort();
3845 1667 $offset = $this->read_ulong();
3846 1668 $save_pos = $this->_pos;
1669 + // mPDF 5.3.30
3847 1670 if (($platformID == 3 && $encodingID == 10) || $platformID == 0) { // Microsoft, Unicode Format 12 table HKCS
3848 1671 $format = $this->get_ushort($cmap_offset + $offset);
3849 1672 if ($format == 12) {
3850 1673 $unicode_cmap_offset = $cmap_offset + $offset;
@@ -3850,22 +1673,13 @@
3850 1673 $unicode_cmap_offset = $cmap_offset + $offset;
3851 1674 break;
3852 1675 }
3853 1676 }
3854 - // mPDF 5.7.1
3855 - if (($platformID == 3 && $encodingID == 1) || $platformID == 0) { // Microsoft, Unicode
3856 - $format = $this->get_ushort($cmap_offset + $offset);
3857 - if ($format == 4) {
3858 - $unicode_cmap_offset = $cmap_offset + $offset;
3859 - }
3860 - }
3861 1677 $this->seek($save_pos );
3862 1678 }
3863 1679
3864 1680 if (!$unicode_cmap_offset)
3865 1681 die('Font does not have cmap for Unicode (platform 3, encoding 1, format 4, or platform 0, any encoding, format 4)');
3866 -
3867 -
3868 1682 // Format 12 CMAP does characters above Unicode BMP i.e. some HKCS characters U+20000 and above
3869 1683 if ($format == 12) {
3870 1684 $this->maxUniChar = 0;
3871 1685 $this->seek($unicode_cmap_offset + 4);
@@ -3884,45 +1698,17 @@
3884 1698 $offset = 0;
3885 1699 for ($unichar=$startCharCode;$unichar<=$endCharCode;$unichar++) {
3886 1700 $glyph = $startGlyphCode + $offset ;
3887 1701 $offset++;
3888 - // ZZZ98
3889 - if ($unichar < 0x30000) {
3890 - $charToGlyph[$unichar] = $glyph;
3891 - $this->maxUniChar = max($unichar,$this->maxUniChar);
3892 - $glyphToChar[$glyph][] = $unichar;
3893 - }
1702 + $charToGlyph[$unichar] = $glyph;
1703 + if ($unichar < 196608) { $this->maxUniChar = max($unichar,$this->maxUniChar); }
1704 + $glyphToChar[$glyph][] = $unichar;
3894 1705 }
3895 1706 }
3896 1707 }
3897 - // mPDF 5.7.1
3898 - else {
3899 - $glyphToChar = array();
3900 - $charToGlyph = array();
3901 - $this->getCMAP4($unicode_cmap_offset, $glyphToChar, $charToGlyph );
3902 - }
1708 + else
1709 + die('Font does not have cmap for Unicode (format 12)');
3903 1710
3904 - ///////////////////////////////////
3905 - // mPDF 5.7.1
3906 - // Map Unmapped glyphs - from $numGlyphs
3907 - if ($useOTL) {
3908 - $bctr = 0xE000;
3909 - for ($gid=1; $gid<$numGlyphs; $gid++) {
3910 - if (!isset($glyphToChar[$gid])) {
3911 - while(isset($charToGlyph[$bctr])) { $bctr++; } // Avoid overwriting a glyph already mapped in PUA
3912 - // ZZZ98
3913 - if ($bctr > 0xF8FF && $bctr < 0x2CEB0) {
3914 - $bctr = 0x2CEB0;
3915 - while(isset($charToGlyph[$bctr])) { $bctr++; }
3916 - }
3917 - $glyphToChar[$gid][] = $bctr;
3918 - $charToGlyph[$bctr] = $gid;
3919 - $this->maxUniChar = max($bctr,$this->maxUniChar);
3920 - $bctr++;
3921 - }
3922 - }
3923 - }
3924 - ///////////////////////////////////
3925 1711
3926 1712 ///////////////////////////////////
3927 1713 // hmtx - Horizontal metrics table
3928 1714 ///////////////////////////////////
@@ -3938,15 +1724,15 @@
3938 1724
3939 1725 $glyphMap = array(0=>0);
3940 1726 $glyphSet = array(0=>0);
3941 1727 $codeToGlyph = array();
3942 - // Set a substitute if ASCII characters do not have glyphs
1728 + // mPDF 5.0.067 Set a substitute if ASCII characters do not have glyphs
3943 1729 if (isset($charToGlyph[0x3F])) { $subs = $charToGlyph[0x3F]; } // Question mark
3944 1730 else { $subs = $charToGlyph[32]; }
3945 1731 foreach($subset AS $code) {
3946 1732 if (isset($charToGlyph[$code]))
3947 1733 $originalGlyphIdx = $charToGlyph[$code];
3948 - else if ($code<128) {
1734 + else if ($code<128) { // mPDF 5.0.067
3949 1735 $originalGlyphIdx = $subs;
3950 1736 }
3951 1737 else { $originalGlyphIdx = 0; }
3952 1738 if (!isset($glyphSet[$originalGlyphIdx])) {
@@ -3996,12 +1782,8 @@
3996 1782
3997 1783 ///////////////////////////////////
3998 1784 // name
3999 1785 ///////////////////////////////////
4000 - // MS spec says that "Platform and encoding ID's in the name table should be consistent with those in the cmap table.
4001 - // If they are not, the font will not load in Windows"
4002 - // Doesn't seem to be a problem?
4003 - ///////////////////////////////////
4004 1786 // Needs to have a name entry in 3,0 (e.g. symbol) - original font will be 3,1 (i.e. Unicode)
4005 1787 $name = $this->get_table('name');
4006 1788 $name_offset = $this->seek_table("name");
4007 1789 $format = $this->read_ushort();
@@ -4039,9 +1821,9 @@
4039 1821 $os2 = $this->_set_ushort($os2, 84, 0x0000); // ulCodePageRange = Bit #32- Symbol **** 84 = Bits 32-47
4040 1822
4041 1823 $os2 = $this->_set_ushort($os2, 64, 0x01); // FirstCharIndex
4042 1824 $os2 = $this->_set_ushort($os2, 66, count($subset)); // LastCharIndex
4043 - // Set PANOSE first bit to 5 for Symbol
1825 + // Set PANOSE first bit to 5 for Symbol mPDF 5.0.017
4044 1826 $os2 = $this->splice($os2, 32, chr(5).chr(0).chr(1).chr(0).chr(1).chr(0).chr(0).chr(0).chr(0).chr(0));
4045 1827 $this->add('OS/2', $os2 );
4046 1828 }
4047 1829
@@ -4048,8 +1830,9 @@
4048 1830
4049 1831 ///////////////////////////////////
4050 1832 //tables copied from the original
4051 1833 ///////////////////////////////////
1834 + // mPDF 5.1.020 // mPDF 5.1.022
4052 1835 $tags = array ('cvt ', 'fpgm', 'prep', 'gasp');
4053 1836 foreach($tags AS $tag) { // 1.02
4054 1837 if (isset($this->tables[$tag])) { $this->add($tag, $this->get_table($tag)); }
4055 1838 }
@@ -4167,9 +1950,9 @@
4167 1950
4168 1951 $cmap = array(0, 2, // Index : version, number of subtables
4169 1952 1, 0, // Subtable : platform, encoding
4170 1953 0, 20, // offset (hi,lo)
4171 - 3, 0, // Subtable : platform, encoding // See note above for 'name'
1954 + 3, 0, // Subtable : platform, encoding
4172 1955 $hoff, $loff, // offset (hi,lo)
4173 1956 6, $length, // Format 6 Mapping table: format, length
4174 1957 0, 1, // language, First char code
4175 1958 $entryCount
@@ -4350,8 +2133,9 @@
4350 2133 $aw = $this->read_ushort();
4351 2134 $lsb = $this->read_ushort();
4352 2135 }
4353 2136 if (isset($glyphToChar[$glyph]) || $glyph == 0) {
2137 +
4354 2138 if ($aw >= (1 << 15) ) { $aw = 0; } // 1.03 Some (arabic) fonts have -ve values for width
4355 2139 // although should be unsigned value - comes out as e.g. 65108 (intended -50)
4356 2140 if ($glyph == 0) {
4357 2141 $this->defaultWidth = $scale*$aw;
@@ -4357,8 +2141,9 @@
4357 2141 $this->defaultWidth = $scale*$aw;
4358 2142 continue;
4359 2143 }
4360 2144 foreach($glyphToChar[$glyph] AS $char) {
2145 + //$this->charWidths[$char] = intval(round($scale*$aw));
4361 2146 if ($char != 0 && $char != 65535) {
4362 2147 $w = intval(round($scale*$aw));
4363 2148 if ($w == 0) { $w = 65535; }
4364 2149 if ($char < 196608) {
@@ -4364,20 +2149,18 @@
4364 2149 if ($char < 196608) {
4365 2150 $this->charWidths[$char*2] = chr($w >> 8);
4366 2151 $this->charWidths[$char*2 + 1] = chr($w & 0xFF);
4367 2152 $nCharWidths++;
4368 -
4369 2153 }
4370 2154 }
4371 2155 }
4372 2156 }
4373 2157 }
4374 -
4375 2158 $data = $this->get_chunk(($start+$numberOfHMetrics*4),($numGlyphs*2));
4376 2159 $arr = unpack("n*", $data);
4377 2160 $diff = $numGlyphs-$numberOfHMetrics;
4378 - $w = intval(round($scale*$aw));
4379 - if ($w == 0) { $w = 65535; }
2161 + $w = intval(round($scale*$aw)); // mPDF 5.3.32
2162 + if ($w == 0) { $w = 65535; } // mPDF 5.3.32
4380 2163 for( $pos=0; $pos<$diff; $pos++) {
4381 2164 $glyph = $pos + $numberOfHMetrics;
4382 2165 if (isset($glyphToChar[$glyph])) {
4383 2166 foreach($glyphToChar[$glyph] AS $char) {
@@ -4396,12 +2179,8 @@
4396 2179 $this->charWidths[0] = chr($nCharWidths >> 8);
4397 2180 $this->charWidths[1] = chr($nCharWidths & 0xFF);
4398 2181 }
4399 2182
4400 -
4401 -
4402 -
4403 -
4404 2183 function getHMetric($numberOfHMetrics, $gid) {
4405 2184 $start = $this->seek_table("hmtx");
4406 2185 if ($gid < $numberOfHMetrics) {
4407 2186 $this->seek($start+($gid*4));
@@ -4479,9 +2258,8 @@
4479 2258 if ($unichar < 196608) { $this->maxUniChar = max($unichar,$this->maxUniChar); }
4480 2259 $glyphToChar[$glyph][] = $unichar;
4481 2260 }
4482 2261 }
4483 -
4484 2262 }
4485 2263
4486 2264
4487 2265 // Put the TTF file together
@@ -4532,11 +2310,9 @@
4532 2310 return $stm ;
4533 2311 }
4534 2312
4535 2313
4536 - function repackageTTF($file, $TTCfontID=0, $debug=false, $useOTL=false) { // mPDF 5.7.1
4537 - // (Does not called for subsets)
4538 - $this->useOTL = $useOTL; // mPDF 5.7.1
2314 + function repackageTTF($file, $TTCfontID=0, $debug=false) {
4539 2315 $this->filename = $file;
4540 2316 $this->fh = fopen($file ,'rb') or die('Can\'t open file ' . $file);
4541 2317 $this->_pos = 0;
4542 2318 $this->charWidths = '';
@@ -4545,10 +2321,8 @@
4545 2321 $this->tables = array();
4546 2322 $this->otables = array();
4547 2323 $this->ascent = 0;
4548 2324 $this->descent = 0;
4549 - $this->strikeoutSize = 0;
4550 - $this->strikeoutPosition = 0;
4551 2325 $this->numTTCFonts = 0;
4552 2326 $this->TTCFonts = array();
4553 2327 $this->skip(4);
4554 2328 $this->maxUni = 0;
@@ -4563,163 +2337,30 @@
4563 2337 $this->seek($this->TTCFonts[$TTCfontID]['offset']);
4564 2338 $this->version = $version = $this->read_ulong(); // TTFont version again now
4565 2339 }
4566 2340 $this->readTableDirectory($debug);
4567 - $tags = array ('OS/2', 'glyf', 'head', 'hhea', 'hmtx', 'loca', 'maxp', 'name', 'post', 'cvt ', 'fpgm', 'gasp', 'prep');
2341 + $tags = array ('OS/2', 'cmap', 'glyf', 'head', 'hhea', 'hmtx', 'loca', 'maxp', 'name', 'post', 'cvt ', 'fpgm', 'gasp', 'prep');
2342 +/*
2343 +Tables which require glyphIndex
2344 +hdmx
2345 +kern
2346 +LTSH
4568 2347
4569 - foreach($tags AS $tag) {
4570 - if (isset($this->tables[$tag])) { $this->add($tag, $this->get_table($tag)); }
4571 - }
2348 +Tables which do NOT require glyphIndex
2349 +VDMX
4572 2350
4573 - // mPDF 5.7.1
4574 - if ($useOTL) {
4575 - ///////////////////////////////////
4576 - // maxp - Maximum profile table
4577 - ///////////////////////////////////
4578 - $this->seek_table("maxp");
4579 - $this->skip(4);
4580 - $numGlyphs = $this->read_ushort();
2351 +GDEF
2352 +GPOS
2353 +GSUB
2354 +JSTF
4581 2355
2356 +DSIG
2357 +PCLT - not recommended
2358 +*/
4582 2359
4583 - ///////////////////////////////////
4584 - // cmap - Character to glyph index mapping table
4585 - ///////////////////////////////////
4586 - $cmap_offset = $this->seek_table("cmap");
4587 - $this->skip(2);
4588 - $cmapTableCount = $this->read_ushort();
4589 - $unicode_cmap_offset = 0;
4590 - for ($i=0;$i<$cmapTableCount;$i++) {
4591 - $platformID = $this->read_ushort();
4592 - $encodingID = $this->read_ushort();
4593 - $offset = $this->read_ulong();
4594 - $save_pos = $this->_pos;
4595 - if (($platformID == 3 && $encodingID == 1) || $platformID == 0) { // Microsoft, Unicode
4596 - $format = $this->get_ushort($cmap_offset + $offset);
4597 - if ($format == 4) {
4598 - $unicode_cmap_offset = $cmap_offset + $offset;
4599 - break;
4600 - }
4601 - }
4602 - $this->seek($save_pos );
4603 - }
4604 -
4605 - if (!$unicode_cmap_offset)
4606 - die('Font ('.$this->filename .') does not have cmap for Unicode (platform 3, encoding 1, format 4, or platform 0, any encoding, format 4)');
4607 -
4608 -
4609 - $glyphToChar = array();
4610 - $charToGlyph = array();
4611 - $this->getCMAP4($unicode_cmap_offset, $glyphToChar, $charToGlyph );
4612 -
4613 -
4614 - ///////////////////////////////////
4615 - // Map Unmapped glyphs - from $numGlyphs
4616 - $bctr = 0xE000;
4617 - for ($gid=1; $gid<$numGlyphs; $gid++) {
4618 - if (!isset($glyphToChar[$gid])) {
4619 - while(isset($charToGlyph[$bctr])) { $bctr++; } // Avoid overwriting a glyph already mapped in PUA (6,400)
4620 - if ($bctr > 0xF8FF) {
4621 - die("Problem. Trying to repackage TF file; not enough space for unmapped glyphs");
4622 - }
4623 - $glyphToChar[$gid][] = $bctr;
4624 - $charToGlyph[$bctr] = $gid;
4625 - $bctr++;
4626 - }
4627 - }
4628 - ///////////////////////////////////
4629 -
4630 - ///////////////////////////////////
4631 - // Sort CID2GID map into segments of contiguous codes
4632 - ///////////////////////////////////
4633 - unset($charToGlyph[65535]);
4634 - unset($charToGlyph[0]);
4635 - ksort($charToGlyph);
4636 - $rangeid = 0;
4637 - $range = array();
4638 - $prevcid = -2;
4639 - $prevglidx = -1;
4640 - // for each character
4641 - foreach ($charToGlyph as $cid => $glidx) {
4642 - if ($cid == ($prevcid + 1) && $glidx == ($prevglidx + 1)) {
4643 - $range[$rangeid][] = $glidx;
4644 - } else {
4645 - // new range
4646 - $rangeid = $cid;
4647 - $range[$rangeid] = array();
4648 - $range[$rangeid][] = $glidx;
4649 - }
4650 - $prevcid = $cid;
4651 - $prevglidx = $glidx;
4652 - }
4653 -
4654 -
4655 - ///////////////////////////////////
4656 - // CMap table
4657 - ///////////////////////////////////
4658 - // cmap - Character to glyph mapping
4659 - $segCount = count($range) + 1; // + 1 Last segment has missing character 0xFFFF
4660 - $searchRange = 1;
4661 - $entrySelector = 0;
4662 - while ($searchRange * 2 <= $segCount ) {
4663 - $searchRange = $searchRange * 2;
4664 - $entrySelector = $entrySelector + 1;
4665 - }
4666 - $searchRange = $searchRange * 2;
4667 - $rangeShift = $segCount * 2 - $searchRange;
4668 - $length = 16 + (8*$segCount ) + ($numGlyphs+1);
4669 - $cmap = array(0, 3, // Index : version, number of encoding subtables
4670 - 0, 0, // Encoding Subtable : platform (UNI=0), encoding 0
4671 - 0, 28, // Encoding Subtable : offset (hi,lo)
4672 - 0, 3, // Encoding Subtable : platform (UNI=0), encoding 3
4673 - 0, 28, // Encoding Subtable : offset (hi,lo)
4674 - 3, 1, // Encoding Subtable : platform (MS=3), encoding 1
4675 - 0, 28, // Encoding Subtable : offset (hi,lo)
4676 - 4, $length, 0, // Format 4 Mapping subtable: format, length, language
4677 - $segCount*2,
4678 - $searchRange,
4679 - $entrySelector,
4680 - $rangeShift);
4681 -
4682 - // endCode(s)
4683 - foreach($range AS $start=>$subrange) {
4684 - $endCode = $start + (count($subrange)-1);
4685 - $cmap[] = $endCode; // endCode(s)
4686 - }
4687 - $cmap[] = 0xFFFF; // endCode of last Segment
4688 - $cmap[] = 0; // reservedPad
4689 -
4690 - // startCode(s)
4691 - foreach($range AS $start=>$subrange) {
4692 - $cmap[] = $start; // startCode(s)
4693 - }
4694 - $cmap[] = 0xFFFF; // startCode of last Segment
4695 - // idDelta(s)
4696 - foreach($range AS $start=>$subrange) {
4697 - $idDelta = -($start-$subrange[0]);
4698 - //$n += count($subrange); // ?? Line not required
4699 - $cmap[] = $idDelta; // idDelta(s)
4700 - }
4701 - $cmap[] = 1; // idDelta of last Segment
4702 - // idRangeOffset(s)
4703 - foreach($range AS $subrange) {
4704 - $cmap[] = 0; // idRangeOffset[segCount] Offset in bytes to glyph indexArray, or 0
4705 - }
4706 - $cmap[] = 0; // idRangeOffset of last Segment
4707 - foreach($range AS $subrange) {
4708 - foreach($subrange AS $glidx) {
4709 - $cmap[] = $glidx;
4710 - }
4711 - }
4712 - $cmap[] = 0; // Mapping for last character
4713 - $cmapstr = '';
4714 - foreach($cmap AS $cm) { $cmapstr .= pack("n",$cm); }
4715 - $this->add('cmap', $cmapstr);
4716 -
4717 -
2360 + foreach($tags AS $tag) {
2361 + if (isset($this->tables[$tag])) { $this->add($tag, $this->get_table($tag)); }
4718 2362 }
4719 - else { $this->add('cmap', $this->get_table('cmap')); }
4720 -
4721 -
4722 2363 fclose($this->fh);
4723 2364 $stm = '';
4724 2365 $this->endTTFile($stm);
4725 2366 return $stm ;