PluginProbe
PDF & Print by BestWebSoft – WordPress Posts and Pages PDF Generator Plugin / 1.7.1
PDF & Print by BestWebSoft – WordPress Posts and Pages PDF Generator Plugin v1.7.1
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 +156 -2820 1.9.1 → 1.7.1 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: 2.01 *
7 +* Date: 2012-02-25 *
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,13 @@
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
41 +var $unAGlyphs; // mPDF 5.4.05
67 42 var $panose;
68 43 var $maxUni;
69 44 var $sFamilyClass;
70 45 var $sFamilySubClass;
@@ -82,20 +57,8 @@
82 57 var $glyphPos;
83 58 var $charToGlyph;
84 59 var $ascent;
85 60 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 61 var $name;
99 62 var $familyName;
100 63 var $styleName;
101 64 var $fullName;
@@ -102,9 +65,8 @@
102 65 var $uniqueFontID;
103 66 var $unitsPerEm;
104 67 var $bbox;
105 68 var $capHeight;
106 -var $xHeight; // mPDF 6
107 69 var $stemV;
108 70 var $italicAngle;
109 71 var $flags;
110 72 var $underlinePosition;
@@ -115,10 +77,8 @@
115 77 var $numTTCFonts;
116 78 var $TTCFonts;
117 79 var $maxUniChar;
118 80 var $kerninfo;
119 -var $haskernGPOS;
120 -var $hassmallcapsGSUB;
121 81
122 82 function TTFontFile() {
123 83 $this->maxStrLenRead = 200000; // Maximum size of glyf table to read in as string (otherwise reads each glyph from file)
124 84 }
@@ -123,11 +83,10 @@
123 83 $this->maxStrLenRead = 200000; // Maximum size of glyf table to read in as string (otherwise reads each glyph from file)
124 84 }
125 85
126 86
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
87 + function getMetrics($file, $TTCfontID=0, $debug=false, $BMPonly=false, $kerninfo=false, $unAGlyphs=false) { // mPDF 5.4.05
88 + $this->unAGlyphs = $unAGlyphs; // mPDF 5.4.05
130 89 $this->filename = $file;
131 90 $this->fh = fopen($file,'rb') or die('Can\'t open file ' . $file);
132 91 $this->_pos = 0;
133 92 $this->charWidths = '';
@@ -135,29 +94,10 @@
135 94 $this->charToGlyph = array();
136 95 $this->tables = array();
137 96 $this->otables = array();
138 97 $this->kerninfo = array();
139 - $this->haskernGPOS = array();
140 - $this->hassmallcapsGSUB = array();
141 98 $this->ascent = 0;
142 99 $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 100 $this->numTTCFonts = 0;
161 101 $this->TTCFonts = array();
162 102 $this->version = $version = $this->read_ulong();
163 103 $this->panose = array();
@@ -178,9 +118,9 @@
178 118 $this->seek($this->TTCFonts[$TTCfontID]['offset']);
179 119 $this->version = $version = $this->read_ulong(); // TTFont version again now
180 120 }
181 121 $this->readTableDirectory($debug);
182 - $this->extractInfo($debug, $BMPonly, $useOTL);
122 + $this->extractInfo($debug, $BMPonly, $kerninfo);
183 123 fclose($this->fh);
184 124 }
185 125
186 126
@@ -243,14 +183,12 @@
243 183 $lo += (ord($data[$i+2])<<8) + ord($data[$i+3]);
244 184 $hi += ($lo >> 16) & 0xFFFF;
245 185 $lo = $lo & 0xFFFF;
246 186 }
247 - $hi = $hi & 0xFFFF; // mPDF 5.7.1
248 187 return array($hi, $lo);
249 188 }
250 189
251 190 function get_table_pos($tag) {
252 - if (!isset($this->tables[$tag])) { return array(0,0); }
253 191 $offset = $this->tables[$tag]['offset'];
254 192 $length = $this->tables[$tag]['length'];
255 193 return array($offset, $length);
256 194 }
@@ -371,11 +309,10 @@
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, $unAGlyphs=false) { // mPDF 5.4.05
314 + $this->unAGlyphs = $unAGlyphs; // mPDF 5.4.05
378 315 $this->filename = $file;
379 316 $this->fh = fopen($file,'rb') or die('Can\'t open file ' . $file);
380 317 $this->_pos = 0;
381 318 $this->charWidths = '';
@@ -429,30 +366,8 @@
429 366 $glyphToChar = array();
430 367 $charToGlyph = array();
431 368 $this->getCMAP4($unicode_cmap_offset, $glyphToChar, $charToGlyph );
432 369
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 370 fclose($this->fh);
456 371 return ($charToGlyph);
457 372 }
458 373
@@ -483,10 +398,12 @@
483 398 /////////////////////////////////////////////////////////////////////////////////////////
484 399
485 400 /////////////////////////////////////////////////////////////////////////////////////////
486 401
487 - function extractInfo($debug=false, $BMPonly=false, $useOTL=0) {
488 - // Values are all set to 0 or blank at start of getMetrics
402 + function extractInfo($debug=false, $BMPonly=false, $kerninfo=false) {
403 + $this->panose = array();
404 + $this->sFamilyClass = 0;
405 + $this->sFamilySubClass = 0;
489 406 ///////////////////////////////////
490 407 // name - Naming table
491 408 ///////////////////////////////////
492 409 $name_offset = $this->seek_table("name");
@@ -543,24 +460,16 @@
543 460 else
544 461 $psName = '';
545 462 if (!$psName)
546 463 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++) {
464 + if ($debug) {
465 + for ($i=0;$i<count($psName);$i++) {
550 466 $c = $psName[$i];
551 467 $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 - }
468 + if ($oc>126 || strpos(' [](){}<>/%',$c)!==false)
469 + die("psName=".$psName." contains invalid character ".$c." ie U+".ord(c));
470 + }
557 471 }
558 -
559 - if ($psNameInvalid && $names[4])
560 - $psName = preg_replace('/ /','-',$names[4]);
561 -
562 -
563 472 $this->name = $psName;
564 473 if ($names[1]) { $this->familyName = $names[1]; } else { $this->familyName = $psName; }
565 474 if ($names[2]) { $this->styleName = $names[2]; } else { $this->styleName = 'Regular'; }
566 475 if ($names[4]) { $this->fullName = $names[4]; } else { $this->fullName = $psName; }
@@ -565,9 +474,9 @@
565 474 if ($names[2]) { $this->styleName = $names[2]; } else { $this->styleName = 'Regular'; }
566 475 if ($names[4]) { $this->fullName = $names[4]; } else { $this->fullName = $psName; }
567 476 if ($names[3]) { $this->uniqueFontID = $names[3]; } else { $this->uniqueFontID = $psName; }
568 477
569 - if (!$psNameInvalid && $names[6]) { $this->fullName = $names[6]; }
478 + if ($names[6]) { $this->fullName = $names[6]; }
570 479
571 480 ///////////////////////////////////
572 481 // head - Font header table
573 482 ///////////////////////////////////
@@ -595,9 +504,8 @@
595 504 $yMin = $this->read_short();
596 505 $xMax = $this->read_short();
597 506 $yMax = $this->read_short();
598 507 $this->bbox = array(($xMin*$scale), ($yMin*$scale), ($xMax*$scale), ($yMax*$scale));
599 -
600 508 $this->skip(3*2);
601 509 $indexToLocFormat = $this->read_ushort();
602 510 $glyphDataFormat = $this->read_ushort();
603 511 if ($glyphDataFormat != 0)
@@ -605,25 +513,21 @@
605 513
606 514 ///////////////////////////////////
607 515 // hhea metrics table
608 516 ///////////////////////////////////
517 + // ttf2t1 seems to use this value rather than the one in OS/2 - so put in for compatibility
609 518 if (isset($this->tables["hhea"])) {
610 519 $this->seek_table("hhea");
611 520 $this->skip(4);
612 521 $hheaAscender = $this->read_short();
613 522 $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
523 + $this->ascent = ($hheaAscender *$scale);
524 + $this->descent = ($hheaDescender *$scale);
620 525 }
621 526
622 527 ///////////////////////////////////
623 528 // OS/2 - OS/2 and Windows metrics table
624 529 ///////////////////////////////////
625 - $use_typo_metrics = false;
626 530 if (isset($this->tables["OS/2"])) {
627 531 $this->seek_table("OS/2");
628 532 $version = $this->read_ushort();
629 533 $this->skip(2);
@@ -630,18 +534,13 @@
630 534 $usWeightClass = $this->read_ushort();
631 535 $this->skip(2);
632 536 $fsType = $this->read_ushort();
633 537 if ($fsType == 0x0002 || ($fsType & 0x0300) != 0) {
538 + global $overrideTTFFontRestriction;
539 + if (!$overrideTTFFontRestriction) die('ERROR - Font file '.$this->filename.' cannot be embedded due to copyright restrictions.');
634 540 $this->restrictedUse = true;
635 541 }
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 -
542 + $this->skip(20);
644 543 $sF = $this->read_short();
645 544 $this->sFamilyClass = ($sF >> 8);
646 545 $this->sFamilySubClass = ($sF & 0xFF);
647 546 $this->_pos += 10; //PANOSE = 10 byte length
@@ -647,81 +546,30 @@
647 546 $this->_pos += 10; //PANOSE = 10 byte length
648 547 $panose = fread($this->fh,10);
649 548 $this->panose = array();
650 549 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 -
550 + $this->skip(26);
658 551 $sTypoAscender = $this->read_short();
659 552 $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 -
553 + if (!$this->ascent) $this->ascent = ($sTypoAscender*$scale);
554 + if (!$this->descent) $this->descent = ($sTypoDescender*$scale);
670 555 if ($version > 1) {
671 - $this->skip(8); // mPDF 6
672 - $sxHeight = $this->read_short();
673 - $this->xHeight = ($sxHeight*$scale);
556 + $this->skip(16);
674 557 $sCapHeight = $this->read_short();
675 558 $this->capHeight = ($sCapHeight*$scale);
676 559 }
560 + else {
561 + $this->capHeight = $this->ascent;
562 + }
677 563 }
678 564 else {
679 - $usWeightClass = 400;
565 + $usWeightClass = 500;
566 + if (!$this->ascent) $this->ascent = ($yMax*$scale);
567 + if (!$this->descent) $this->descent = ($yMin*$scale);
568 + $this->capHeight = $this->ascent;
680 569 }
681 570 $this->stemV = 50 + intval(pow(($usWeightClass / 65.0),2));
682 571
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 572 ///////////////////////////////////
725 573 // post - PostScript table
726 574 ///////////////////////////////////
727 575 $this->seek_table("post");
@@ -813,9 +661,8 @@
813 661 }
814 662 }
815 663 $this->seek($save_pos );
816 664 }
817 -
818 665 if (!$unicode_cmap_offset)
819 666 die('Font ('.$this->filename .') does not have cmap for Unicode (platform 3, encoding 1, format 4, or platform 0, any encoding, format 4)');
820 667
821 668
@@ -820,20 +667,8 @@
820 667
821 668
822 669 $sipset = false;
823 670 $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 671 // Format 12 CMAP does characters above Unicode BMP i.e. some HKCS characters U+20000 and above
837 672 if ($format == 12 && !$BMPonly) {
838 673 $this->maxUniChar = 0;
839 674 $this->seek($unicode_cmap_offset + 4);
@@ -848,11 +683,10 @@
848 683 for($i=0; $i<$nGroups ; $i++) {
849 684 $startCharCode = $this->read_ulong();
850 685 $endCharCode = $this->read_ulong();
851 686 $startGlyphCode = $this->read_ulong();
852 - // ZZZ98
853 - if ($endCharCode > 0x20000 && $endCharCode < 0x2FFFF) {
854 - $sipset = true;
687 + if (($endCharCode > 0x20000 && $endCharCode < 0x2A6DF) || ($endCharCode > 0x2F800 && $endCharCode < 0x2FA1F)) {
688 + $sipset = true;
855 689 }
856 690 else if ($endCharCode > 0x10000 && $endCharCode < 0x1FFFF) {
857 691 $smpset = true;
858 692 }
@@ -859,14 +693,11 @@
859 693 $offset = 0;
860 694 for ($unichar=$startCharCode;$unichar<=$endCharCode;$unichar++) {
861 695 $glyph = $startGlyphCode + $offset ;
862 696 $offset++;
863 - // ZZZ98
864 - if ($unichar < 0x30000) {
865 - $charToGlyph[$unichar] = $glyph;
866 - $this->maxUniChar = max($unichar,$this->maxUniChar);
867 - $glyphToChar[$glyph][] = $unichar;
868 - }
697 + $charToGlyph[$unichar] = $glyph;
698 + if ($unichar < 196608) { $this->maxUniChar = max($unichar,$this->maxUniChar); }
699 + $glyphToChar[$glyph][] = $unichar;
869 700 }
870 701 }
871 702 }
872 703 else {
@@ -877,106 +708,10 @@
877 708
878 709 }
879 710 $this->sipset = $sipset ;
880 711 $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 712
906 - $this->glyphToChar = $glyphToChar;
907 713 ///////////////////////////////////
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 714 // hmtx - Horizontal metrics table
980 715 ///////////////////////////////////
981 716 $this->getHMTX($numberOfHMetrics, $numGlyphs, $glyphToChar, $scale);
982 717
@@ -982,2298 +717,42 @@
982 717
983 718 ///////////////////////////////////
984 719 // kern - Kerning pair table
985 720 ///////////////////////////////////
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);
721 + if ($kerninfo) {
722 + // Recognises old form of Kerning table - as required by Windows - Format 0 only
723 + $kern_offset = $this->seek_table("kern");
724 + $version = $this->read_ushort();
725 + $nTables = $this->read_ushort();
726 + // subtable header
727 + $sversion = $this->read_ushort();
728 + $slength = $this->read_ushort();
729 + $scoverage = $this->read_ushort();
730 + $format = $scoverage >> 8;
731 + if ($kern_offset && $version==0 && $format==0) {
732 + // Format 0
733 + $nPairs = $this->read_ushort();
734 + $this->skip(6);
735 + for ($i=0; $i<$nPairs; $i++) {
736 + $left = $this->read_ushort();
737 + $right = $this->read_ushort();
738 + $val = $this->read_short();
739 + if (count($glyphToChar[$left])==1 && count($glyphToChar[$right])==1) {
740 + if ($left != 32 && $right != 32) {
741 + $this->kerninfo[$glyphToChar[$left][0]][$glyphToChar[$right][0]] = intval($val*$scale);
742 + }
1101 743 }
1102 - else { $mat = array(); }
1103 -
1104 - $this->MarkAttachmentType[$class] = ' '.implode('| ',$mat);
1105 744 }
1106 745 }
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 746 }
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 747 }
1172 748
1173 - function _getClassDefinitionTable() {
1174 749
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 750 /////////////////////////////////////////////////////////////////////////////////////////
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 751
2277 - $SubstFormat= $Lookup[$i]['Subtable'][$c]['Format'] ;
2278 752
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
753 + function makeSubset($file, &$subset, $TTCfontID=0, $debug=false, $unAGlyphs=false) { // mPDF 5.4.05
754 + $this->unAGlyphs = $unAGlyphs; // mPDF 5.4.05
3273 755 $this->filename = $file;
3274 756 $this->fh = fopen($file ,'rb') or die('Can\'t open file ' . $file);
3275 757 $this->_pos = 0;
3276 758 $this->charWidths = '';
@@ -3282,10 +761,8 @@
3282 761 $this->tables = array();
3283 762 $this->otables = array();
3284 763 $this->ascent = 0;
3285 764 $this->descent = 0;
3286 - $this->strikeoutSize = 0;
3287 - $this->strikeoutPosition = 0;
3288 765 $this->numTTCFonts = 0;
3289 766 $this->TTCFonts = array();
3290 767 $this->skip(4);
3291 768 $this->maxUni = 0;
@@ -3355,29 +832,9 @@
3355 832 $glyphToChar = array();
3356 833 $charToGlyph = array();
3357 834 $this->getCMAP4($unicode_cmap_offset, $glyphToChar, $charToGlyph );
3358 835
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 836 $this->charToGlyph = $charToGlyph;
3379 - $this->glyphToChar = $glyphToChar;
3380 837
3381 838
3382 839 ///////////////////////////////////
3383 840 // hmtx - Horizontal metrics table
@@ -3428,12 +885,8 @@
3428 885
3429 886 ///////////////////////////////////
3430 887 // name - table copied from the original
3431 888 ///////////////////////////////////
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 889 $this->add('name', $this->get_table('name'));
3437 890
3438 891 ///////////////////////////////////
3439 892 //tables copied from the original
@@ -3775,13 +1228,13 @@
3775 1228
3776 1229 //================================================================================
3777 1230
3778 1231 // Also does SMP
3779 - function makeSubsetSIP($file, &$subset, $TTCfontID=0, $debug=false, $useOTL=0) { // mPDF 5.7.1
1232 + function makeSubsetSIP($file, &$subset, $TTCfontID=0, $debug=false) {
3780 1233 $this->fh = fopen($file ,'rb') or die('Can\'t open file ' . $file);
3781 1234 $this->filename = $file;
3782 1235 $this->_pos = 0;
3783 - $this->useOTL = $useOTL; // mPDF 5.7.1
1236 + $this->unAGlyphs = false; // mPDF 5.4.05
3784 1237 $this->charWidths = '';
3785 1238 $this->glyphPos = array();
3786 1239 $this->charToGlyph = array();
3787 1240 $this->tables = array();
@@ -3787,10 +1240,8 @@
3787 1240 $this->tables = array();
3788 1241 $this->otables = array();
3789 1242 $this->ascent = 0;
3790 1243 $this->descent = 0;
3791 - $this->strikeoutSize = 0;
3792 - $this->strikeoutPosition = 0;
3793 1244 $this->numTTCFonts = 0;
3794 1245 $this->TTCFonts = array();
3795 1246 $this->skip(4);
3796 1247 if ($TTCfontID > 0) {
@@ -3806,8 +1257,9 @@
3806 1257 }
3807 1258 $this->readTableDirectory($debug);
3808 1259
3809 1260
1261 +
3810 1262 ///////////////////////////////////
3811 1263 // head - Font header table
3812 1264 ///////////////////////////////////
3813 1265 $this->seek_table("head");
@@ -3850,22 +1302,13 @@
3850 1302 $unicode_cmap_offset = $cmap_offset + $offset;
3851 1303 break;
3852 1304 }
3853 1305 }
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 1306 $this->seek($save_pos );
3862 1307 }
3863 1308
3864 1309 if (!$unicode_cmap_offset)
3865 1310 die('Font does not have cmap for Unicode (platform 3, encoding 1, format 4, or platform 0, any encoding, format 4)');
3866 -
3867 -
3868 1311 // Format 12 CMAP does characters above Unicode BMP i.e. some HKCS characters U+20000 and above
3869 1312 if ($format == 12) {
3870 1313 $this->maxUniChar = 0;
3871 1314 $this->seek($unicode_cmap_offset + 4);
@@ -3884,45 +1327,17 @@
3884 1327 $offset = 0;
3885 1328 for ($unichar=$startCharCode;$unichar<=$endCharCode;$unichar++) {
3886 1329 $glyph = $startGlyphCode + $offset ;
3887 1330 $offset++;
3888 - // ZZZ98
3889 - if ($unichar < 0x30000) {
3890 - $charToGlyph[$unichar] = $glyph;
3891 - $this->maxUniChar = max($unichar,$this->maxUniChar);
3892 - $glyphToChar[$glyph][] = $unichar;
3893 - }
1331 + $charToGlyph[$unichar] = $glyph;
1332 + if ($unichar < 196608) { $this->maxUniChar = max($unichar,$this->maxUniChar); }
1333 + $glyphToChar[$glyph][] = $unichar;
3894 1334 }
3895 1335 }
3896 1336 }
3897 - // mPDF 5.7.1
3898 - else {
3899 - $glyphToChar = array();
3900 - $charToGlyph = array();
3901 - $this->getCMAP4($unicode_cmap_offset, $glyphToChar, $charToGlyph );
3902 - }
1337 + else
1338 + die('Font does not have cmap for Unicode (format 12)');
3903 1339
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 1340
3926 1341 ///////////////////////////////////
3927 1342 // hmtx - Horizontal metrics table
3928 1343 ///////////////////////////////////
@@ -3996,12 +1411,8 @@
3996 1411
3997 1412 ///////////////////////////////////
3998 1413 // name
3999 1414 ///////////////////////////////////
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 1415 // Needs to have a name entry in 3,0 (e.g. symbol) - original font will be 3,1 (i.e. Unicode)
4005 1416 $name = $this->get_table('name');
4006 1417 $name_offset = $this->seek_table("name");
4007 1418 $format = $this->read_ushort();
@@ -4167,9 +1578,9 @@
4167 1578
4168 1579 $cmap = array(0, 2, // Index : version, number of subtables
4169 1580 1, 0, // Subtable : platform, encoding
4170 1581 0, 20, // offset (hi,lo)
4171 - 3, 0, // Subtable : platform, encoding // See note above for 'name'
1582 + 3, 0, // Subtable : platform, encoding
4172 1583 $hoff, $loff, // offset (hi,lo)
4173 1584 6, $length, // Format 6 Mapping table: format, length
4174 1585 0, 1, // language, First char code
4175 1586 $entryCount
@@ -4350,8 +1761,9 @@
4350 1761 $aw = $this->read_ushort();
4351 1762 $lsb = $this->read_ushort();
4352 1763 }
4353 1764 if (isset($glyphToChar[$glyph]) || $glyph == 0) {
1765 +
4354 1766 if ($aw >= (1 << 15) ) { $aw = 0; } // 1.03 Some (arabic) fonts have -ve values for width
4355 1767 // although should be unsigned value - comes out as e.g. 65108 (intended -50)
4356 1768 if ($glyph == 0) {
4357 1769 $this->defaultWidth = $scale*$aw;
@@ -4357,8 +1769,9 @@
4357 1769 $this->defaultWidth = $scale*$aw;
4358 1770 continue;
4359 1771 }
4360 1772 foreach($glyphToChar[$glyph] AS $char) {
1773 + //$this->charWidths[$char] = intval(round($scale*$aw));
4361 1774 if ($char != 0 && $char != 65535) {
4362 1775 $w = intval(round($scale*$aw));
4363 1776 if ($w == 0) { $w = 65535; }
4364 1777 if ($char < 196608) {
@@ -4364,15 +1777,13 @@
4364 1777 if ($char < 196608) {
4365 1778 $this->charWidths[$char*2] = chr($w >> 8);
4366 1779 $this->charWidths[$char*2 + 1] = chr($w & 0xFF);
4367 1780 $nCharWidths++;
4368 -
4369 1781 }
4370 1782 }
4371 1783 }
4372 1784 }
4373 1785 }
4374 -
4375 1786 $data = $this->get_chunk(($start+$numberOfHMetrics*4),($numGlyphs*2));
4376 1787 $arr = unpack("n*", $data);
4377 1788 $diff = $numGlyphs-$numberOfHMetrics;
4378 1789 $w = intval(round($scale*$aw));
@@ -4396,12 +1807,8 @@
4396 1807 $this->charWidths[0] = chr($nCharWidths >> 8);
4397 1808 $this->charWidths[1] = chr($nCharWidths & 0xFF);
4398 1809 }
4399 1810
4400 -
4401 -
4402 -
4403 -
4404 1811 function getHMetric($numberOfHMetrics, $gid) {
4405 1812 $start = $this->seek_table("hmtx");
4406 1813 if ($gid < $numberOfHMetrics) {
4407 1814 $this->seek($start+($gid*4));
@@ -4480,8 +1887,70 @@
4480 1887 $glyphToChar[$glyph][] = $unichar;
4481 1888 }
4482 1889 }
4483 1890
1891 + // mPDF 5.4.05
1892 + if ($this->unAGlyphs) {
1893 + if (isset($this->tables['post'])) {
1894 + $this->seek_table("post");
1895 + $formata = $this->read_ushort();
1896 + $formatb = $this->read_ushort();
1897 + // Only works on Format 2.0
1898 + if ($formata != 2 || $formatb != 0) { die("Cannot set unAGlyphs for this font (".$file."). POST table must be in Format 2."); }
1899 + $this->skip(28);
1900 + $nGlyfs = $this->read_ushort();
1901 + $glyphNameIndex = array();
1902 + for ($i=0; $i<$nGlyfs; $i++) {
1903 + $glyphNameIndex[($this->read_ushort())] = $i;
1904 + }
1905 +
1906 + $opost = $this->get_table('post');
1907 + $ptr = 34+($nGlyfs*2);
1908 + for ($i=0; $i<$nGlyfs; $i++) {
1909 + $len = ord(substr($opost,$ptr,1));
1910 + $ptr++;
1911 + $name = substr($opost,$ptr,$len);
1912 + $gid = $glyphNameIndex[$i+258];
1913 + // Select uni0600.xxx(x) - uni06FF.xxx(x)
1914 + if (preg_match('/^uni(06[0-9a-f]{2})\.(fina|medi|init|fin|med|ini)$/i',$name,$m)) {
1915 + if (!isset($glyphToChar[$gid]) || (isset($glyphToChar[$gid]) && is_array($glyphToChar[$gid]) && count($glyphToChar[$gid])==1 && $glyphToChar[$gid][0]>57343 && $glyphToChar[$gid][0]<63489)) { // if set in PUA private use area E000-F8FF, or NOT Unicode mapped
1916 + $uni = hexdec($m[1]);
1917 + $form = strtoupper(substr($m[2],0,1));
1918 + // Assign new PUA Unicode between F500 - F7FF
1919 + $bit = $uni & 0xFF;
1920 + if ($form == 'I') { $bit += 0xF600; }
1921 + else if ($form == 'M') { $bit += 0xF700; }
1922 + else { $bit += 0xF500; }
1923 + // ADD TO CMAP
1924 + $glyphToChar[$gid][] = $bit;
1925 + $charToGlyph[$bit] = $gid;
1926 + }
1927 + }
1928 + // LAM with ALEF ligatures (Mandatory ligatures)
1929 + else if (preg_match('/^uni064406(22|23|25|27)(\.fina|\.fin){0,1}$/i',$name,$m)) {
1930 + if ($m[1]=='22') {
1931 + if ($m[2]) { $uni = hexdec('FEF6'); } else { $uni = hexdec('FEF5'); }
1932 + }
1933 + else if ($m[1]=='23') {
1934 + if ($m[2]) { $uni = hexdec('FEF8'); } else { $uni = hexdec('FEF7'); }
1935 + }
1936 + else if ($m[1]=='25') {
1937 + if ($m[2]) { $uni = hexdec('FEFA'); } else { $uni = hexdec('FEF9'); }
1938 + }
1939 + else if ($m[1]=='27') {
1940 + if ($m[2]) { $uni = hexdec('FEFC'); } else { $uni = hexdec('FEFB'); }
1941 + }
1942 + if (!isset($glyphToChar[$gid]) || (isset($glyphToChar[$gid]) && is_array($glyphToChar[$gid]) && count($glyphToChar[$gid])==1 && $glyphToChar[$gid][0]>57343 && $glyphToChar[$gid][0]<63489)) { // if set in PUA private use area E000-F8FF, or NOT Unicode mapped
1943 + // ADD TO CMAP
1944 + $glyphToChar[$gid][] = $uni;
1945 + $charToGlyph[$uni] = $gid;
1946 + }
1947 + }
1948 + $ptr += $len;
1949 + }
1950 + }
1951 + }
1952 +
4484 1953 }
4485 1954
4486 1955
4487 1956 // Put the TTF file together
@@ -4532,11 +2001,10 @@
4532 2001 return $stm ;
4533 2002 }
4534 2003
4535 2004
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
2005 + function repackageTTF($file, $TTCfontID=0, $debug=false, $unAGlyphs=false) { // mPDF 5.4.05
2006 + $this->unAGlyphs = $unAGlyphs; // mPDF 5.4.05
4539 2007 $this->filename = $file;
4540 2008 $this->fh = fopen($file ,'rb') or die('Can\'t open file ' . $file);
4541 2009 $this->_pos = 0;
4542 2010 $this->charWidths = '';
@@ -4545,10 +2013,8 @@
4545 2013 $this->tables = array();
4546 2014 $this->otables = array();
4547 2015 $this->ascent = 0;
4548 2016 $this->descent = 0;
4549 - $this->strikeoutSize = 0;
4550 - $this->strikeoutPosition = 0;
4551 2017 $this->numTTCFonts = 0;
4552 2018 $this->TTCFonts = array();
4553 2019 $this->skip(4);
4554 2020 $this->maxUni = 0;
@@ -4563,163 +2029,30 @@
4563 2029 $this->seek($this->TTCFonts[$TTCfontID]['offset']);
4564 2030 $this->version = $version = $this->read_ulong(); // TTFont version again now
4565 2031 }
4566 2032 $this->readTableDirectory($debug);
4567 - $tags = array ('OS/2', 'glyf', 'head', 'hhea', 'hmtx', 'loca', 'maxp', 'name', 'post', 'cvt ', 'fpgm', 'gasp', 'prep');
2033 + $tags = array ('OS/2', 'cmap', 'glyf', 'head', 'hhea', 'hmtx', 'loca', 'maxp', 'name', 'post', 'cvt ', 'fpgm', 'gasp', 'prep');
2034 +/*
2035 +Tables which require glyphIndex
2036 +hdmx
2037 +kern
2038 +LTSH
4568 2039
4569 - foreach($tags AS $tag) {
4570 - if (isset($this->tables[$tag])) { $this->add($tag, $this->get_table($tag)); }
4571 - }
2040 +Tables which do NOT require glyphIndex
2041 +VDMX
4572 2042
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();
2043 +GDEF
2044 +GPOS
2045 +GSUB
2046 +JSTF
4581 2047
2048 +DSIG
2049 +PCLT - not recommended
2050 +*/
4582 2051
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 -
2052 + foreach($tags AS $tag) {
2053 + if (isset($this->tables[$tag])) { $this->add($tag, $this->get_table($tag)); }
4718 2054 }
4719 - else { $this->add('cmap', $this->get_table('cmap')); }
4720 -
4721 -
4722 2055 fclose($this->fh);
4723 2056 $stm = '';
4724 2057 $this->endTTFile($stm);
4725 2058 return $stm ;