diff --git a/src/OtlDump.php b/src/OtlDump.php index 177cb9a88..f5246ce6a 100644 --- a/src/OtlDump.php +++ b/src/OtlDump.php @@ -433,14 +433,6 @@ protected function gsubReverseChainRule(array $Lookup, $i, $c, $tag, $scripttag, return []; } - /** - * The dump reads each class-based rule's positions fresh, where the parser does not (#170). - */ - protected function keepsEarlierRulePositions() - { - return false; - } - /** * One substitution, as a row of the report. * diff --git a/src/TTFontFile.php b/src/TTFontFile.php index 86ff29b43..9b9444b78 100644 --- a/src/TTFontFile.php +++ b/src/TTFontFile.php @@ -2412,8 +2412,8 @@ function _getGSUBarray(array $Lookup, $lul, $scripttag) { $volt = []; - // Kept across rules and lookups rather than read fresh for each: see keepsEarlierRulePositions(). - // A Type 5 Format 1 or 2 rule hands whatever these last held to the Arabic shaper (#189). + // A Type 5 Format 1 or 2 rule reads these without setting them, so hands whatever the last + // chained rule left in them to the Arabic shaper (#189). $backtrackGlyphs = []; $lookaheadGlyphs = []; @@ -2544,19 +2544,7 @@ function _getGSUBarray(array $Lookup, $lul, $scripttag) for ($cscrule = 0; $cscrule < $cscs['ChainSubClassRuleCnt']; $cscrule++) { $rule = $cscs['ChainSubClassRule'][$cscrule]; - if (!$rule['BacktrackGlyphCount'] || !$this->keepsEarlierRulePositions()) { - $backtrackGlyphs = []; - } - for ($gcl = 0; $gcl < $rule['BacktrackGlyphCount']; $gcl++) { - $backtrackGlyphs[$gcl] = $this->classGlyphs($subtable['BacktrackClasses'], $rule['Backtrack'][$gcl]); - } - - if (!$rule['LookaheadGlyphCount'] || !$this->keepsEarlierRulePositions()) { - $lookaheadGlyphs = []; - } - for ($gcl = 0; $gcl < $rule['LookaheadGlyphCount']; $gcl++) { - $lookaheadGlyphs[$gcl] = $this->classGlyphs($subtable['LookaheadClasses'], $rule['Lookahead'][$gcl]); - } + list($backtrackGlyphs, $lookaheadGlyphs) = $this->classSequences($subtable, $rule); $this->addTo($volt, $this->gsubContextRule($Lookup, $i, $c, $tag, $scripttag, $ignore, $this->contextRule( $cscrule, @@ -2638,6 +2626,25 @@ private function coverageSequences(array $subtable) ]; } + /** + * @return array [backtrack, lookahead] of one class-based chained rule (Type 6 Format 2), one + * "|"-joined glyph string per position + */ + private function classSequences(array $subtable, array $rule) + { + $backtrack = []; + for ($gcl = 0; $gcl < $rule['BacktrackGlyphCount']; $gcl++) { + $backtrack[$gcl] = $this->classGlyphs($subtable['BacktrackClasses'], $rule['Backtrack'][$gcl]); + } + + $lookahead = []; + for ($gcl = 0; $gcl < $rule['LookaheadGlyphCount']; $gcl++) { + $lookahead[$gcl] = $this->classGlyphs($subtable['LookaheadClasses'], $rule['Lookahead'][$gcl]); + } + + return [$backtrack, $lookahead]; + } + /** * The input sequence of a class-based rule: the class its rule set is for, then the class of each * position after the first. @@ -2679,18 +2686,6 @@ private function addTo(array &$volt, array $entries) } } - /** - * Whether a class-based chained rule (Type 6 Format 2) that names fewer backtrack or lookahead - * positions than the rule read before it keeps that rule's extra positions. - * - * The parser does, which is #170: the extra positions end up in the rule's matchback and counts, - * and in what an Arabic joining form hands the shaper. Fixing it is returning false here. - */ - protected function keepsEarlierRulePositions() - { - return true; - } - /** * The rules of a Single, Multiple, Alternate or Ligature substitution subtable (Types 1 to 4). * diff --git a/tests/Mpdf/ChainedClassRulePositionsTest.php b/tests/Mpdf/ChainedClassRulePositionsTest.php new file mode 100644 index 000000000..888e14f27 --- /dev/null +++ b/tests/Mpdf/ChainedClassRulePositionsTest.php @@ -0,0 +1,82 @@ + 'utf-8', + 'fontDir' => [__DIR__ . '/../data/ttf'], + 'fontdata' => ['notosansarabicgsub62positionssynthetic' => [ + 'R' => 'NotoSansArabic-GSUB62Positions-Synthetic.ttf', + 'useOTL' => 0xFF, + ]], + 'default_font' => 'notosansarabicgsub62positionssynthetic', + ]); + $mpdf->WriteHTML('

' . $html . '

'); + + return array_values(unpack('N*', mb_convert_encoding($mpdf->drawnText[0], 'UTF-32BE', 'UTF-8'))); + } + + public function dataRuns() + { + return [ + 'the first rule, with two positions either side' => [ + [self::ALEF, self::BEH, self::ALEF, self::LOW_ALEF, self::LOW_ALEF], + [self::LOW_ALEF, self::LOW_ALEF, self::ALEF_FINAL, self::BEH, self::ALEF], + ], + 'the second rule, where the first rule\'s second backtrack position does not hold' => [ + [self::BEH, self::BEH, self::LOW_ALEF, self::LOW_ALEF], + [self::LOW_ALEF, self::LOW_ALEF, self::DOTLESS_BEH_FINAL, self::BEH], + ], + 'the second rule, where the first rule\'s second lookahead position does not hold' => [ + [self::ALEF, self::BEH, self::BEH, self::LOW_ALEF], + [self::LOW_ALEF, self::DOTLESS_BEH_FINAL, self::BEH, self::ALEF], + ], + ]; + } + + /** + * @dataProvider dataRuns + */ + public function testEachRuleMatchesItsOwnPositions($codepoints, $expected) + { + $this->assertSame($expected, $this->drawn($codepoints)); + } + +} diff --git a/tests/Mpdf/Fonts/GsubArrayTest.php b/tests/Mpdf/Fonts/GsubArrayTest.php index 860d1b06d..8fbaaf8d2 100644 --- a/tests/Mpdf/Fonts/GsubArrayTest.php +++ b/tests/Mpdf/Fonts/GsubArrayTest.php @@ -9,8 +9,7 @@ use Mpdf\TTFontFile; /** - * The GSUB walk the parser and the dump share, where the two part ways on purpose and no font in - * tests/data/ttf shows it. + * The GSUB walk the parser and the dump share, over rule sets no font in tests/data/ttf has. * * The lookups are built by hand, in the shape readGSUBrules() leaves them: lookup 0 is a class-based * chained context (Type 6 Format 2) whose two rules name first two backtrack positions, then one, and @@ -20,21 +19,21 @@ class GsubArrayTest extends \Yoast\PHPUnitPolyfills\TestCases\TestCase { /** - * The parser keeps the first rule's second backtrack position into the second rule (#170). + * The second rule matches the one backtrack position it names, not the first rule's second as + * well (#170). */ - public function testTheParserKeepsAnEarlierClassRulesExtraPositions() + public function testTheParserReadsEachClassRuleWithItsOwnPositions() { $parser = $this->withGdef(new TTFontFile($this->cache(), 'win')); $volt = $parser->_getGSUBarray($this->chainedClassRules(), [0 => 'ccmp'], 'latn'); - $this->assertSame([2, 2], [$volt[0]['nBacktrack'], $volt[1]['nBacktrack']]); - $this->assertSame('(00043)() (00043)() ', $volt[1]['matchback']); + $this->assertSame([2, 1], [$volt[0]['nBacktrack'], $volt[1]['nBacktrack']]); + $this->assertSame('(00043)() ', $volt[1]['matchback']); } /** - * The dump reads each rule's positions fresh, so it reports the second rule with the one - * backtrack position it names. + * So does the dump, which reports the second rule with the one backtrack position it names. */ public function testTheDumpReportsEachClassRuleWithItsOwnPositions() { @@ -56,8 +55,7 @@ public function testTheDumpReportsEachClassRuleWithItsOwnPositions() /** * A Type 5 rule has no backtrack or lookahead, yet where it belongs to an Arabic joining form the - * parser hands the shaper the sequences of the chained rule it read last. Kept as it was by the - * walk the two share; see #189. + * parser hands the shaper the sequences of the chained rule it read last (#189). */ public function testAPlainContextRuleOfAnArabicFormCarriesTheLastChainedRulesSequences() { @@ -80,7 +78,7 @@ public function testAPlainContextRuleOfAnArabicFormCarriesTheLastChainedRulesSeq $volt = $parser->_getGSUBarray($Lookup, [0 => 'init', 2 => 'init'], 'arab'); - $this->assertSame(['match' => '00041', 'replace' => '00044', 'tag' => 'init', 'prel' => ['00043', '00043'], 'postl' => [], 'ignore' => '()'], end($volt)); + $this->assertSame(['match' => '00041', 'replace' => '00044', 'tag' => 'init', 'prel' => ['00043'], 'postl' => [], 'ignore' => '()'], end($volt)); } private function chainedClassRules() diff --git a/tests/data/fontcache/NotoSansArabic-GSUB62Positions-Synthetic.json b/tests/data/fontcache/NotoSansArabic-GSUB62Positions-Synthetic.json new file mode 100644 index 000000000..a3bac07e4 --- /dev/null +++ b/tests/data/fontcache/NotoSansArabic-GSUB62Positions-Synthetic.json @@ -0,0 +1,226 @@ +{ + "_": "Generated by composer fontcache:update. See tests/Mpdf/Fonts/ParserGoldenMaster.php.", + "fullName": "NotoSansArabicGSUB62PositionsSynthetic-Regular", + "mtx": { + "GSUBScriptLang": { + "DFLT": "DFLT ", + "arab": "DFLT " + }, + "GSUBFeatures": { + "DFLT": { + "DFLT": { + "fina": [ + 0 + ] + } + }, + "arab": { + "DFLT": { + "fina": [ + 0 + ] + } + } + }, + "GSUBLookups": [ + { + "Type": 6, + "Flag": 0, + "SubtableCount": 1, + "Subtables": [ + 64 + ], + "MarkFilteringSet": "" + }, + { + "Type": 1, + "Flag": 0, + "SubtableCount": 1, + "Subtables": [ + 174 + ], + "MarkFilteringSet": "" + } + ], + "GPOSScriptLang": { + "DFLT": "DFLT ", + "arab": "DFLT ", + "cyrl": "DFLT ", + "dev2": "DFLT ", + "grek": "DFLT ", + "latn": "DFLT " + }, + "GPOSFeatures": { + "DFLT": { + "DFLT": { + "mark": [ + 0 + ], + "mkmk": [ + 1 + ] + } + }, + "arab": { + "DFLT": { + "mark": [ + 0 + ], + "mkmk": [ + 1 + ] + } + }, + "cyrl": { + "DFLT": { + "mark": [ + 0 + ], + "mkmk": [ + 1 + ] + } + }, + "dev2": { + "DFLT": { + "mark": [ + 0 + ], + "mkmk": [ + 1 + ] + } + }, + "grek": { + "DFLT": { + "mark": [ + 0 + ], + "mkmk": [ + 1 + ] + } + }, + "latn": { + "DFLT": { + "mark": [ + 0 + ], + "mkmk": [ + 1 + ] + } + } + }, + "GPOSLookups": [ + { + "Type": 4, + "Flag": 0, + "SubtableCount": 1, + "Subtables": [ + 102 + ], + "MarkFilteringSet": "" + }, + { + "Type": 6, + "Flag": 16, + "SubtableCount": 1, + "Subtables": [ + 194 + ], + "MarkFilteringSet": 0 + } + ], + "MarkGlyphSets": [ + " 0E005" + ], + "rtlPUAstr": "\\x{0E000}\\x{0E002}", + "haskernGPOS": false, + "hassmallcapsGSUB": false + }, + "cache": { + "GDEFdata.json": { + "GlyphClassBases": " 00020| 00627| 008AD| 0E000| 0E001| 0E002| 0E003| 0E004", + "GlyphClassMarks": " 0E005", + "GlyphClassLigatures": "", + "GlyphClassComponents": "", + "MarkGlyphSets": [ + " 0E005" + ], + "MarkAttachmentType": [] + }, + "GPOS.dat": "234 bytes, sha256 0f363351c5ea276d72ca75127977045024304d1f2b19b04e8cfded715c737a13", + "GPOSdata.json": [ + [ + { + "57349": 0 + } + ], + [ + { + "57349": 0 + } + ] + ], + "GSUB.arab.DFLT.json": { + "rtlSUB": { + "00627": { + "1": "0E000", + "prel": { + "1": [ + "00628", + "00627" + ] + }, + "postl": { + "1": [ + "008AD", + "008AD" + ] + }, + "ignore": { + "1": "()" + } + }, + "00628": { + "1": "0E002", + "prel": { + "1": [ + "00628" + ] + }, + "postl": { + "1": [ + "008AD" + ] + }, + "ignore": { + "1": "()" + } + } + }, + "finals": "0E000 0E002 ", + "rphf": [], + "half": [], + "pref": [], + "blwf": [], + "pstf": [] + }, + "GSUB.dat": "192 bytes, sha256 7da285b0e28dc3dc7c919987056d9b0e39edcf78f337ef45ec3b10f9d8fc07ac", + "GSUBdata.json": [ + [ + { + "1575": 0, + "1576": 1 + } + ], + [ + { + "1575": 0, + "1576": 1 + } + ] + ] + } +} diff --git a/tests/data/otldump/NotoSansArabic-GSUB62Positions-Synthetic.txt b/tests/data/otldump/NotoSansArabic-GSUB62Positions-Synthetic.txt new file mode 100644 index 000000000..cd09e743a --- /dev/null +++ b/tests/data/otldump/NotoSansArabic-GSUB62Positions-Synthetic.txt @@ -0,0 +1,27 @@ +

GDEF table

+

Glyph classes

+

Glyph class 1

+
Base glyph (single character, spacing glyph)
+
ا ࢭ     
+

Glyph class 3

+
Mark glyph (non-spacing combining glyph)
+
◌
+

Mark Glyph Sets

+

Mark Glyph Set class: 0

+
◌
+

GSUB Tables

+

GSUB Scripts & Languages

+
+
DFLT
DFLT: fina
arab
DFLT: fina
+
+

GPOS Tables

+

GPOS Scripts & Languages

+
+
DFLT
DFLT: mark mkmk
arab
DFLT: mark mkmk
cyrl
DFLT: mark mkmk
dev2
DFLT: mark mkmk
grek
DFLT: mark mkmk
latn
DFLT: mark mkmk
+
+ +=== detail: script arab language DFLT === +

GSUB Tables

+
Lookup #0 [tag: fina]
Subtable #0
LookupType 6: Chaining Contextual Substitution Subtable
Format 2: Class-based Chaining Context Glyph Substitution
Input Class: 1
Rule: 0
CONTEXT:
Backtrack #1: U+0627
Backtrack #0: U+0628
Input #0:  ا 
Lookahead #0: U+08AD
Lookahead #1: U+08AD
Substitution Position: 0
Lookup #1 [tag: fina]
Subtable #0
LookupType 1: Single Substitution Subtable
U+0627  ا‍ب اࢭ‍ࢭ  » »  ا‍ب ࢭ‍ࢭ  M+E000
Input Class: 2
Rule: 0
CONTEXT:
Backtrack #0: U+0628
Input #0:  ب 
Lookahead #0: U+08AD
Substitution Position: 0
Lookup #1 [tag: fina]
Subtable #0
LookupType 1: Single Substitution Subtable
U+0628  ب ب  » »  ب   M+E002
+

GPOS Tables

+
Lookup #0 [tag: mark]
Subtable #0
LookupType 4: MarkToBase attachment
Marks: ◌
Bases: ا      ࢭ
Example(s):    ا                  ࢭ  
Lookup #1 [tag: mkmk]
Ignoring: Marks outside Mark Glyph Set[0]
Subtable #0
LookupType 6: MarkToMark attachment
Marks: ◌
Bases: ◌
Example(s): ◌  
diff --git a/tests/data/shaping/NotoSansArabic-GSUB62Positions-Synthetic.txt b/tests/data/shaping/NotoSansArabic-GSUB62Positions-Synthetic.txt new file mode 100644 index 000000000..3320dea0b --- /dev/null +++ b/tests/data/shaping/NotoSansArabic-GSUB62Positions-Synthetic.txt @@ -0,0 +1,96 @@ +=== latin === +0xFF 0041 0056 0041 0054 0061 0072 => 0041 0056 0041 0054 0061 0072 group=CCCCCC +0x80 0041 0056 0041 0054 0061 0072 => 0041 0056 0041 0054 0061 0072 group=CCCCCC +0xFF +kern -liga 0041 0056 0041 0054 0061 0072 => 0041 0056 0041 0054 0061 0072 group=CCCCCC +=== cyrillic === +0xFF 0416 0430 0439 => 0416 0430 0439 group=CCC +0x80 0416 0430 0439 => 0416 0430 0439 group=CCC +0xFF +kern -liga 0416 0430 0439 => 0416 0430 0439 group=CCC +=== greek === +0xFF 03B1 03B2 03C2 => 03B1 03B2 03C2 group=CCC +0x80 03B1 03B2 03C2 => 03B1 03B2 03C2 group=CCC +0xFF +kern -liga 03B1 03B2 03C2 => 03B1 03B2 03C2 group=CCC +=== hiragana === +0xFF 3042 3043 3044 => 3042 3043 3044 group=CCC +0x80 3042 3043 3044 => 3042 3043 3044 group=CCC +0xFF +kern -liga 3042 3043 3044 => 3042 3043 3044 group=CCC +=== arabic === +0xFF 0628 0640 0645 0644 0627 => 0628 0640 0645 0644 0627 group=CCCCC gpos={"1":{"kashida":8}} +0x80 0628 0640 0645 0644 0627 => 0628 0640 0645 0644 0627 group=CCCCC gpos={"1":{"kashida":8}} +0xFF +kern -liga 0628 0640 0645 0644 0627 => 0628 0640 0645 0644 0627 group=CCCCC gpos={"1":{"kashida":8}} +=== syriac === +0xFF 0710 0712 0713 0715 => 0710 0712 0713 0715 group=CCCC +0x80 0710 0712 0713 0715 => 0710 0712 0713 0715 group=CCCC +0xFF +kern -liga 0710 0712 0713 0715 => 0710 0712 0713 0715 group=CCCC +=== nko === +0xFF 07CA 07CB 07CC => 07CA 07CB 07CC group=CCC +0x80 07CA 07CB 07CC => 07CA 07CB 07CC group=CCC +0xFF +kern -liga 07CA 07CB 07CC => 07CA 07CB 07CC group=CCC +=== devanagari === +0xFF 0915 094D 0937 093F => 0915 094D 093F 0937 group=CCCC +0x80 0915 094D 0937 093F => 0915 094D 093F 0937 group=CCCC +0xFF +kern -liga 0915 094D 0937 093F => 0915 094D 093F 0937 group=CCCC +=== bengali === +0xFF 0995 09CD 09B7 09BF => 0995 09CD 09BF 09B7 group=CCCC +0x80 0995 09CD 09B7 09BF => 0995 09CD 09BF 09B7 group=CCCC +0xFF +kern -liga 0995 09CD 09B7 09BF => 0995 09CD 09BF 09B7 group=CCCC +=== gurmukhi === +0xFF 0A15 0A4D 0A38 0A3F => 0A15 0A4D 0A3F 0A38 group=CCCC +0x80 0A15 0A4D 0A38 0A3F => 0A15 0A4D 0A3F 0A38 group=CCCC +0xFF +kern -liga 0A15 0A4D 0A38 0A3F => 0A15 0A4D 0A3F 0A38 group=CCCC +=== tamil === +0xFF 0B95 0BCD 0BB7 0BBF => 0B95 0BCD 0BB7 0BBF group=CCCC +0x80 0B95 0BCD 0BB7 0BBF => 0B95 0BCD 0BB7 0BBF group=CCCC +0xFF +kern -liga 0B95 0BCD 0BB7 0BBF => 0B95 0BCD 0BB7 0BBF group=CCCC +=== malayalam === +0xFF 0D15 0D4D 0D37 0D3F => 0D15 0D4D 0D37 0D3F group=CCCC +0x80 0D15 0D4D 0D37 0D3F => 0D15 0D4D 0D37 0D3F group=CCCC +0xFF +kern -liga 0D15 0D4D 0D37 0D3F => 0D15 0D4D 0D37 0D3F group=CCCC +=== sinhala === +0xFF 0D9A 0DCA 0DBB 0DBB => 0D9A 0DCA 0DBB 0DBB group=CCCC +0x80 0D9A 0DCA 0DBB 0DBB => 0D9A 0DCA 0DBB 0DBB group=CCCC +0xFF +kern -liga 0D9A 0DCA 0DBB 0DBB => 0D9A 0DCA 0DBB 0DBB group=CCCC +=== khmer === +0xFF 1780 17D2 1781 17C1 => 17C1 1780 17D2 1781 group=CCCC +0x80 1780 17D2 1781 17C1 => 17C1 1780 17D2 1781 group=CCCC +0xFF +kern -liga 1780 17D2 1781 17C1 => 17C1 1780 17D2 1781 group=CCCC +=== thai === +0xFF 0E01 0E34 0E48 0E23 => 0E01 0E34 0E48 0E23 group=CCCC +0x80 0E01 0E34 0E48 0E23 => 0E01 0E34 0E48 0E23 group=CCCC +0xFF +kern -liga 0E01 0E34 0E48 0E23 => 0E01 0E34 0E48 0E23 group=CCCC +=== lao === +0xFF 0E81 0EB4 0E8D => 0E81 0EB4 0E8D group=CCC +0x80 0E81 0EB4 0E8D => 0E81 0EB4 0E8D group=CCC +0xFF +kern -liga 0E81 0EB4 0E8D => 0E81 0EB4 0E8D group=CCC +=== myanmar === +0xFF 1000 103A 1039 1001 => 1000 103A 1039 1001 group=CCCC +0x80 1000 103A 1039 1001 => 1000 103A 1039 1001 group=CCCC +0xFF +kern -liga 1000 103A 1039 1001 => 1000 103A 1039 1001 group=CCCC +=== new tai lue === +0xFF 1980 19B0 1981 => 1980 19B0 1981 group=CCC +0x80 1980 19B0 1981 => 1980 19B0 1981 group=CCC +0xFF +kern -liga 1980 19B0 1981 => 1980 19B0 1981 group=CCC +=== cham === +0xFF AA00 AA33 AA01 => AA00 AA33 AA01 group=CCC +0x80 AA00 AA33 AA01 => AA00 AA33 AA01 group=CCC +0xFF +kern -liga AA00 AA33 AA01 => AA00 AA33 AA01 group=CCC +=== tai tham === +0xFF 1A20 1A60 1A21 => 1A20 1A60 1A21 group=CCC +0x80 1A20 1A60 1A21 => 1A20 1A60 1A21 group=CCC +0xFF +kern -liga 1A20 1A60 1A21 => 1A20 1A60 1A21 group=CCC +=== mixed scripts === +0xFF 0041 0628 0915 0042 => 0041 0628 0915 0042 group=CCCC +0x80 0041 0628 0915 0042 => 0041 0628 0915 0042 group=CCCC +0xFF +kern -liga 0041 0628 0915 0042 => 0041 0628 0915 0042 group=CCCC +=== spaced === +0xFF 0041 0020 0628 0020 0042 => 0041 0020 0628 0020 0042 group=CSCSC +0x80 0041 0020 0628 0020 0042 => 0041 0020 0628 0020 0042 group=CSCSC +0xFF +kern -liga 0041 0020 0628 0020 0042 => 0041 0020 0628 0020 0042 group=CSCSC +=== font characters 0 === +0xFF 0020 0627 0628 08AD E000 E001 E002 E003 => 0020 0627 0628 08AD E000 E001 E002 E003 group=SCCCCCCC gpos={"4":{"kashida":2},"6":{"kashida":2}} +0x80 0020 0627 0628 08AD E000 E001 E002 E003 => 0020 0627 0628 08AD E000 E001 E002 E003 group=SCCCCCCC gpos={"4":{"kashida":2},"6":{"kashida":2}} +0xFF +kern -liga 0020 0627 0628 08AD E000 E001 E002 E003 => 0020 0627 0628 08AD E000 E001 E002 E003 group=SCCCCCCC gpos={"4":{"kashida":2},"6":{"kashida":2}} +=== font characters 1 === +0xFF E004 E005 => E004 E005 group=CM gpos={"1":{"BaseWidth":269,"XPlacement":31,"YPlacement":-3}} +0x80 E004 E005 => E004 E005 group=CM gpos={"1":{"BaseWidth":269,"XPlacement":31,"YPlacement":-3}} +0xFF +kern -liga E004 E005 => E004 E005 group=CM gpos={"1":{"BaseWidth":269,"XPlacement":31,"YPlacement":-3}} diff --git a/tests/data/subset/NotoSansArabic-GSUB62Positions-Synthetic.json b/tests/data/subset/NotoSansArabic-GSUB62Positions-Synthetic.json new file mode 100644 index 000000000..ac95dfa96 --- /dev/null +++ b/tests/data/subset/NotoSansArabic-GSUB62Positions-Synthetic.json @@ -0,0 +1,129 @@ +{ + "_": "Generated by composer subset:update. See tests/Mpdf/Fonts/SubsetGoldenMaster.php.", + "useOTL=0x00": { + "characters": 5, + "makeSubset": { + "bytes": 1540, + "sha256": "e77f3591d38195a8ca52bb36cf48ec32528a34cfb10492a8506e9804d7151ae3", + "tables": { + "OS/2": "96 bytes, checksum 0x83C35E30, sha256 c8d44e1e3e21744fb6447d75613e7d094658598ae11cd4ecd6ea51953585ed86", + "cmap": "92 bytes, checksum 0x0EEF06F5, sha256 ae6fb5dc97aecd1fc550113941bb8dd17c1e315a921037dd742091775397192d", + "gasp": "8 bytes, checksum 0x00000010, sha256 4ca731f86ad506ac0e320283dc9461926346de3c4d91d539ea7f6620d3826940", + "glyf": "304 bytes, checksum 0x7C5B4089, sha256 dda0aee857654d74b832394e016fc1e1e7710819c3dd061b666da107b0c39414", + "head": "54 bytes, checksum 0x46EA9EA2, sha256 dbb762ff579e225ee9b32a99c331cd90fff0f542dd6fe48ad92283744ad83d65", + "hhea": "36 bytes, checksum 0x29871F54, sha256 b6790f08d7b49267a846ddaa6976fa1ae4f2c482adfd5ca104166fe4ec48ca93", + "hmtx": "28 bytes, checksum 0x0CF90144, sha256 5a0c3e3815967f5179a662960145cd59d1377b1065498bb99d512c035752a67d", + "loca": "16 bytes, checksum 0x00D00114, sha256 82987de0caadd775301bb7f92dc6663a42f1351074a37aaa9e8f43ddd2629de3", + "maxp": "32 bytes, checksum 0x0058052C, sha256 712fd6ee38e7ed3e4f3271a00a1ea02f9588b526df499378ee6c2db8aeed859e", + "name": "626 bytes, checksum 0x36A04F5C, sha256 4ac8e50cdaf871921a459fd22a3d72cd1e297ec807b51608557d7d1e2145d1ba", + "post": "32 bytes, checksum 0xFF9F0032, sha256 4fdc83d5e42fe44650fe86f15a645d5cc926d07355ba945e1ede42737607bf83", + "prep": "7 bytes, checksum 0x68068C85, sha256 907c4106ff9989c1805827d5581bdf107a253b098cb71eb374f5e47c35604452" + }, + "maxUni": 65535, + "defaultWidth": 600, + "codeToGlyph": "5 entries, sha256 e330c81f30e51088fd0070ff21260ad702c20dd4487cd3b50ac48068aee467ca" + }, + "makeSubsetSIP": { + "bytes": 1528, + "sha256": "61015d0b42002421bfd8ab49d509ce9eec1f338b500b701f440dd4cd479fd00a", + "tables": { + "OS/2": "96 bytes, checksum 0x69A2D688, sha256 69a32b780b72e95ba9dfa002a1b8a2d0019efbe42ba74209c65095bc15e06732", + "cmap": "80 bytes, checksum 0x002C0083, sha256 3d0e58f1892397e1e94f3d0f75c6038f1212ed4d46a99eef47bc722bc366ee41", + "gasp": "8 bytes, checksum 0x00000010, sha256 4ca731f86ad506ac0e320283dc9461926346de3c4d91d539ea7f6620d3826940", + "glyf": "304 bytes, checksum 0x7C5B4089, sha256 184dd6f6301fa823051edcb9ea0c09851b5c5fd96ef911f898c134000990f13b", + "head": "54 bytes, checksum 0x46EA9EA2, sha256 16d82b9b235e72a1e7f02b2948e351696a40866e8ce9bbc4ec0681bdd5dad2fc", + "hhea": "36 bytes, checksum 0x29871F54, sha256 b6790f08d7b49267a846ddaa6976fa1ae4f2c482adfd5ca104166fe4ec48ca93", + "hmtx": "28 bytes, checksum 0x0CF90144, sha256 cd581a635bc9a3a20a5f498d3842038b3b5acfb25d1187221fab16a8cf9430ee", + "loca": "16 bytes, checksum 0x00C40114, sha256 b012200316381b41dc293cfa1f58f92eb79c195dbc958907b7309a3fcab6d76e", + "maxp": "32 bytes, checksum 0x0058052C, sha256 712fd6ee38e7ed3e4f3271a00a1ea02f9588b526df499378ee6c2db8aeed859e", + "name": "626 bytes, checksum 0x36994F5C, sha256 154baee591bcc5b123fb15fe83ce360d42974c8abfb09816a6f085e524001800", + "post": "32 bytes, checksum 0xFF9F0032, sha256 4fdc83d5e42fe44650fe86f15a645d5cc926d07355ba945e1ede42737607bf83", + "prep": "7 bytes, checksum 0x68068C85, sha256 907c4106ff9989c1805827d5581bdf107a253b098cb71eb374f5e47c35604452" + }, + "maxUniChar": 65535, + "defaultWidth": 600 + }, + "repackageTTF": { + "bytes": 2028, + "sha256": "258c43c914b092e79f1b834433f86e189324544bab685b8b48760f6d33626cb7", + "tables": { + "OS/2": "96 bytes, checksum 0x83C35E30, sha256 c8d44e1e3e21744fb6447d75613e7d094658598ae11cd4ecd6ea51953585ed86", + "cmap": "72 bytes, checksum 0x06420F70, sha256 e29b8cf3f1e636faa27f210c98884c21505fdd6b6b8acf965ef0b86eafa0c480", + "gasp": "8 bytes, checksum 0x00000010, sha256 4ca731f86ad506ac0e320283dc9461926346de3c4d91d539ea7f6620d3826940", + "glyf": "668 bytes, checksum 0xD2CBF85C, sha256 64df708dd07ef438ab78c2fa6ec037cb9d2ec39a026eeecb018460dbe43ab527", + "head": "54 bytes, checksum 0x46EA9EA2, sha256 680287d4e4cd4f6c7b3231566d790ba1780f4c1ea85a3dc8950599725a24a9cf", + "hhea": "36 bytes, checksum 0x29871F58, sha256 d5c8aa00ac210ae41f8cae1f3201846d77c0ac04d04e11627ee19851bdfdcd08", + "hmtx": "44 bytes, checksum 0x14C501AA, sha256 af3326b0ee31557a2ee01c72ee59dac76903c6a84f8a077b74b99271f2ba42f5", + "loca": "24 bytes, checksum 0x036E041D, sha256 0a4cc922874d893e9926bbbf0eece0202d5cab7ec6724963cafca63946eec457", + "maxp": "32 bytes, checksum 0x005C052C, sha256 26689100d5138bb752af4293a4bbb556b534aa9d94134a46edd960ec269019ec", + "name": "626 bytes, checksum 0x36A04F5C, sha256 4ac8e50cdaf871921a459fd22a3d72cd1e297ec807b51608557d7d1e2145d1ba", + "post": "151 bytes, checksum 0xB924F86C, sha256 4baf6477b5a1ef33145e61b8c7bb9c828d6b17629e10af34eaf47d20e2978446", + "prep": "7 bytes, checksum 0x68068C85, sha256 907c4106ff9989c1805827d5581bdf107a253b098cb71eb374f5e47c35604452" + }, + "maxUni": 0 + } + }, + "useOTL=0xFF": { + "characters": 11, + "makeSubset": { + "bytes": 1956, + "sha256": "8ad5f665839d42f00390e52aab3caa0bb3bdc35647e94d6f53a5a67d8c80a602", + "tables": { + "OS/2": "96 bytes, checksum 0x83C35E30, sha256 c8d44e1e3e21744fb6447d75613e7d094658598ae11cd4ecd6ea51953585ed86", + "cmap": "108 bytes, checksum 0xEF02E71C, sha256 78a92931bee3fd46f5a9c5415eedaa0f295efa03625ca4afa41c4480a898243b", + "gasp": "8 bytes, checksum 0x00000010, sha256 4ca731f86ad506ac0e320283dc9461926346de3c4d91d539ea7f6620d3826940", + "glyf": "680 bytes, checksum 0x41A9897B, sha256 56ca9bb7742204cbdc96a05b2385d3fa81b1179034b69c093b9aa272a3e64d52", + "head": "54 bytes, checksum 0x46EA9EA2, sha256 f6d6b03dc841e641f44185e8da3ef71a41da8c233c6f6193aebd1e38023cb360", + "hhea": "36 bytes, checksum 0x29871F58, sha256 d5c8aa00ac210ae41f8cae1f3201846d77c0ac04d04e11627ee19851bdfdcd08", + "hmtx": "44 bytes, checksum 0x14C501AA, sha256 af3326b0ee31557a2ee01c72ee59dac76903c6a84f8a077b74b99271f2ba42f5", + "loca": "24 bytes, checksum 0x03800432, sha256 2cb2965a2d09fea2eb65e980cd64ac413ab3d2cfcd850e65997ad5fef232b95b", + "maxp": "32 bytes, checksum 0x005C052C, sha256 26689100d5138bb752af4293a4bbb556b534aa9d94134a46edd960ec269019ec", + "name": "626 bytes, checksum 0x36A04F5C, sha256 4ac8e50cdaf871921a459fd22a3d72cd1e297ec807b51608557d7d1e2145d1ba", + "post": "32 bytes, checksum 0xFF9F0032, sha256 4fdc83d5e42fe44650fe86f15a645d5cc926d07355ba945e1ede42737607bf83", + "prep": "7 bytes, checksum 0x68068C85, sha256 907c4106ff9989c1805827d5581bdf107a253b098cb71eb374f5e47c35604452" + }, + "maxUni": 65535, + "defaultWidth": 600, + "codeToGlyph": "11 entries, sha256 4976a91e70c6d83b58a35c5bf0afec8d29c446258274744a3317930df09fa365" + }, + "makeSubsetSIP": { + "bytes": 1940, + "sha256": "19079b8cc29a85b04b96564eb2180ec12bbbdfeb126a45b9b22a3a481635d059", + "tables": { + "OS/2": "96 bytes, checksum 0x69A2D68E, sha256 d3e6c0234f8db691dbb73991a5141f8184691d245eda464e73fcfd0645daf3a7", + "cmap": "92 bytes, checksum 0x005600B0, sha256 5febc775230d1d4c2ff1203cc3ba03258d79c52651c9eb23a521082d323ea414", + "gasp": "8 bytes, checksum 0x00000010, sha256 4ca731f86ad506ac0e320283dc9461926346de3c4d91d539ea7f6620d3826940", + "glyf": "680 bytes, checksum 0x41AB897C, sha256 78d00159157d7c704efacfa95bba99beebb11d2b1c4313852a6f55f06936c1af", + "head": "54 bytes, checksum 0x46EA9EA2, sha256 dccea7ea3b78b86d002a614d3117eb3b51ccbb49378cf37a18622638cc09cebc", + "hhea": "36 bytes, checksum 0x29871F58, sha256 d5c8aa00ac210ae41f8cae1f3201846d77c0ac04d04e11627ee19851bdfdcd08", + "hmtx": "44 bytes, checksum 0x14C501AA, sha256 1bcad69d16f9d4b7cdb1a2e30f1e1b5172902317c43c2383364b125bc9287e90", + "loca": "24 bytes, checksum 0x02D2038E, sha256 276b53cc6f655d4a16fd4168880b6fba91e36f6b119e3f1b5905dd0725dba35c", + "maxp": "32 bytes, checksum 0x005C052C, sha256 26689100d5138bb752af4293a4bbb556b534aa9d94134a46edd960ec269019ec", + "name": "626 bytes, checksum 0x36994F5C, sha256 154baee591bcc5b123fb15fe83ce360d42974c8abfb09816a6f085e524001800", + "post": "32 bytes, checksum 0xFF9F0032, sha256 4fdc83d5e42fe44650fe86f15a645d5cc926d07355ba945e1ede42737607bf83", + "prep": "7 bytes, checksum 0x68068C85, sha256 907c4106ff9989c1805827d5581bdf107a253b098cb71eb374f5e47c35604452" + }, + "maxUniChar": 65535, + "defaultWidth": 600 + }, + "repackageTTF": { + "bytes": 2056, + "sha256": "9ce11cff4be42fecbcbe0ff2bbba52059205ccb6957c9406e42ae78adba255a7", + "tables": { + "OS/2": "96 bytes, checksum 0x83C35E30, sha256 c8d44e1e3e21744fb6447d75613e7d094658598ae11cd4ecd6ea51953585ed86", + "cmap": "100 bytes, checksum 0xF793DE7F, sha256 0dbe9edb138b44d6281c2fb3bff43096c4966deba0e380e4bed0d7e8887d0cba", + "gasp": "8 bytes, checksum 0x00000010, sha256 4ca731f86ad506ac0e320283dc9461926346de3c4d91d539ea7f6620d3826940", + "glyf": "668 bytes, checksum 0xD2CBF85C, sha256 64df708dd07ef438ab78c2fa6ec037cb9d2ec39a026eeecb018460dbe43ab527", + "head": "54 bytes, checksum 0x46EA9EA2, sha256 0b8c3ca999a2b17fbf2b3d563eb50321fe89783a5c2ca8f58cb91d6d0db34387", + "hhea": "36 bytes, checksum 0x29871F58, sha256 d5c8aa00ac210ae41f8cae1f3201846d77c0ac04d04e11627ee19851bdfdcd08", + "hmtx": "44 bytes, checksum 0x14C501AA, sha256 af3326b0ee31557a2ee01c72ee59dac76903c6a84f8a077b74b99271f2ba42f5", + "loca": "24 bytes, checksum 0x036E041D, sha256 0a4cc922874d893e9926bbbf0eece0202d5cab7ec6724963cafca63946eec457", + "maxp": "32 bytes, checksum 0x005C052C, sha256 26689100d5138bb752af4293a4bbb556b534aa9d94134a46edd960ec269019ec", + "name": "626 bytes, checksum 0x36A04F5C, sha256 4ac8e50cdaf871921a459fd22a3d72cd1e297ec807b51608557d7d1e2145d1ba", + "post": "151 bytes, checksum 0xB924F86C, sha256 4baf6477b5a1ef33145e61b8c7bb9c828d6b17629e10af34eaf47d20e2978446", + "prep": "7 bytes, checksum 0x68068C85, sha256 907c4106ff9989c1805827d5581bdf107a253b098cb71eb374f5e47c35604452" + }, + "maxUni": 0 + } + } +} diff --git a/tests/data/ttf/NotoSansArabic-GSUB62Positions-Synthetic.ttf b/tests/data/ttf/NotoSansArabic-GSUB62Positions-Synthetic.ttf new file mode 100644 index 000000000..7638d11e4 Binary files /dev/null and b/tests/data/ttf/NotoSansArabic-GSUB62Positions-Synthetic.ttf differ