';
$html .= '
';
if ($level == 1) {
$html .= '';
}
$html .= 'Lookup #' . $luli . ' [tag: ' . $tag . ']
';
$ignore = $this->_getGSUBignoreString($Lookup[$luli]['Flag'], $Lookup[$luli]['MarkFilteringSet']);
if ($ignore) {
$html .= '
Ignoring: ' . $ignore . '
';
}
$Type = $Lookup[$luli]['Type'];
$Flag = $Lookup[$luli]['Flag'];
if (($Flag & 0x0001) == 1) {
$dir = 'RTL';
} else {
$dir = 'LTR';
}
for ($c = 0; $c < $Lookup[$luli]['SubtableCount']; $c++) {
$html .= '
Subtable #' . $c;
if ($level == 1) {
$html .= '';
}
$html .= '
';
// Lets start
$subtable_offset = $Lookup[$luli]['Subtables'][$c];
$this->seek($subtable_offset);
$PosFormat = $this->read_ushort();
////////////////////////////////////////////////////////////////////////////////
// LookupType 1: Single adjustment Adjust position of a single glyph (e.g. SmallCaps/Sups/Subs)
////////////////////////////////////////////////////////////////////////////////
if ($Lookup[$luli]['Type'] == 1) {
$html .= '
LookupType 1: Single adjustment [Format ' . $PosFormat . ']
';
//===========
// Format 1:
//===========
if ($PosFormat == 1) {
$Coverage = $subtable_offset + $this->read_ushort();
$ValueFormat = $this->read_ushort();
$Value = $this->_getValueRecord($ValueFormat);
$this->seek($Coverage);
$glyphs = $this->_getCoverage(); // Array of Hex Glyphs
for ($g = 0; $g < count($glyphs); $g++) {
if ($level == 2 && strpos($lcoverage, $glyphs[$g]) === false) {
continue;
}
$html .= '
';
$html .= '' . $this->formatUni($glyphs[$g]) . ' ';
if ($level == 2 && $exB) {
$html .= $exB;
}
$html .= ' ' . $this->formatEntity($glyphs[$g]) . '';
if ($level == 2 && $exL) {
$html .= $exL;
}
$html .= ' » » ';
if ($level == 2 && $exB) {
$html .= $exB;
}
$html .= ' ' . $this->formatEntity($glyphs[$g]) . '';
if ($level == 2 && $exL) {
$html .= $exL;
}
$html .= ' ';
if ($Value['XPlacement']) {
$html .= ' Xpl: ' . $Value['XPlacement'] . ';';
}
if ($Value['YPlacement']) {
$html .= ' YPl: ' . $Value['YPlacement'] . ';';
}
if ($Value['XAdvance']) {
$html .= ' Xadv: ' . $Value['XAdvance'];
}
$html .= '';
$html .= '
';
}
} //===========
// Format 2:
//===========
else {
if ($PosFormat == 2) {
$Coverage = $subtable_offset + $this->read_ushort();
$ValueFormat = $this->read_ushort();
$ValueCount = $this->read_ushort();
$Values = [];
for ($v = 0; $v < $ValueCount; $v++) {
$Values[] = $this->_getValueRecord($ValueFormat);
}
$this->seek($Coverage);
$glyphs = $this->_getCoverage(); // Array of Hex Glyphs
for ($g = 0; $g < count($glyphs); $g++) {
if ($level == 2 && strpos($lcoverage, $glyphs[$g]) === false) {
continue;
}
$Value = $Values[$g];
$html .= '
';
$html .= '' . $this->formatUni($glyphs[$g]) . ' ';
if ($level == 2 && $exB) {
$html .= $exB;
}
$html .= ' ' . $this->formatEntity($glyphs[$g]) . '';
if ($level == 2 && $exL) {
$html .= $exL;
}
$html .= ' » » ';
if ($level == 2 && $exB) {
$html .= $exB;
}
$html .= ' ' . $this->formatEntity($glyphs[$g]) . '';
if ($level == 2 && $exL) {
$html .= $exL;
}
$html .= ' ';
if ($Value['XPlacement']) {
$html .= ' Xpl: ' . $Value['XPlacement'] . ';';
}
if ($Value['YPlacement']) {
$html .= ' YPl: ' . $Value['YPlacement'] . ';';
}
if ($Value['XAdvance']) {
$html .= ' Xadv: ' . $Value['XAdvance'];
}
$html .= '';
$html .= '
';
}
}
}
} ////////////////////////////////////////////////////////////////////////////////
// LookupType 2: Pair adjustment Adjust position of a pair of glyphs (Kerning)
////////////////////////////////////////////////////////////////////////////////
else {
if ($Lookup[$luli]['Type'] == 2) {
$html .= '
LookupType 2: Pair adjustment e.g. Kerning [Format ' . $PosFormat . ']
';
$Coverage = $subtable_offset + $this->read_ushort();
$ValueFormat1 = $this->read_ushort();
$ValueFormat2 = $this->read_ushort();
//===========
// Format 1:
//===========
if ($PosFormat == 1) {
$PairSetCount = $this->read_ushort();
$PairSetOffset = [];
for ($p = 0; $p < $PairSetCount; $p++) {
$PairSetOffset[] = $subtable_offset + $this->read_ushort();
}
$this->seek($Coverage);
$glyphs = $this->_getCoverage(); // Array of Hex Glyphs
for ($p = 0; $p < $PairSetCount; $p++) {
if ($level == 2 && strpos($lcoverage, $glyphs[$p]) === false) {
continue;
}
$this->seek($PairSetOffset[$p]);
// First Glyph = $glyphs[$p]
// Takes too long e.g. Calibri font - just list kerning pairs with this:
$html .= '
';
$html .= '
' . $this->formatEntity($glyphs[$p]) . ' ';
//PairSet table
$PairValueCount = $this->read_ushort();
for ($pv = 0; $pv < $PairValueCount; $pv++) {
//PairValueRecord
$gid = $this->read_ushort();
$SecondGlyph = unicode_hex($this->glyphToChar[$gid][0]);
$Value1 = $this->_getValueRecord($ValueFormat1);
$Value2 = $this->_getValueRecord($ValueFormat2);
// If RTL pairs, GPOS declares a XPlacement e.g. -180 for an XAdvance of -180 to take
// account of direction. mPDF does not need the XPlacement adjustment
if ($dir == 'RTL' && $Value1['XPlacement']) {
$Value1['XPlacement'] -= $Value1['XAdvance'];
}
if ($ValueFormat2) {
// If RTL pairs, GPOS declares a XPlacement e.g. -180 for an XAdvance of -180 to take
// account of direction. mPDF does not need the XPlacement adjustment
if ($dir == 'RTL' && $Value2['XPlacement'] && $Value2['XAdvance']) {
$Value2['XPlacement'] -= $Value2['XAdvance'];
}
}
$html .= ' ' . $this->formatEntity($SecondGlyph) . ' ';
/*
$html .= '
';
$html .= ''.$this->formatUni($glyphs[$p]).' ';
if ($level==2 && $exB) { $html .= $exB; }
$html .= ' '.$this->formatEntity($glyphs[$p]).$this->formatEntity($SecondGlyph).'';
if ($level==2 && $exL) { $html .= $exL; }
$html .= ' » » ';
if ($level==2 && $exB) { $html .= $exB; }
$html .= ' '.$this->formatEntity($glyphs[$p]).$this->formatEntity($SecondGlyph).'';
if ($level==2 && $exL) { $html .= $exL; }
$html .= ' ';
if ($Value1['XPlacement']) { $html .= ' Xpl[1]: '.$Value1['XPlacement'].';'; }
if ($Value1['YPlacement']) { $html .= ' YPl[1]: '.$Value1['YPlacement'].';'; }
if ($Value1['XAdvance']) { $html .= ' Xadv[1]: '.$Value1['XAdvance']; }
if ($Value2['XPlacement']) { $html .= ' Xpl[2]: '.$Value2['XPlacement'].';'; }
if ($Value2['YPlacement']) { $html .= ' YPl[2]: '.$Value2['YPlacement'].';'; }
if ($Value2['XAdvance']) { $html .= ' Xadv[2]: '.$Value2['XAdvance']; }
$html .= '';
$html .= '
';
*/
}
$html .= '
';
}
} //===========
// Format 2:
//===========
else {
if ($PosFormat == 2) {
$ClassDef1 = $subtable_offset + $this->read_ushort();
$ClassDef2 = $subtable_offset + $this->read_ushort();
$Class1Count = $this->read_ushort();
$Class2Count = $this->read_ushort();
$sizeOfPair = (2 * $this->count_bits($ValueFormat1)) + (2 * $this->count_bits($ValueFormat2));
$sizeOfValueRecords = $Class1Count * $Class2Count * $sizeOfPair;
// NB Class1Count includes Class 0 even though it is not defined by $ClassDef1
// i.e. Class1Count = 5; Class1 will contain array(indices 1-4);
$Class1 = $this->_getClassDefinitionTable($ClassDef1);
$Class2 = $this->_getClassDefinitionTable($ClassDef2);
$this->seek($subtable_offset + 16);
for ($i = 0; $i < $Class1Count; $i++) {
for ($j = 0; $j < $Class2Count; $j++) {
$Value1 = $this->_getValueRecord($ValueFormat1);
$Value2 = $this->_getValueRecord($ValueFormat2);
// If RTL pairs, GPOS declares a XPlacement e.g. -180 for an XAdvance of -180
// of direction. mPDF does not need the XPlacement adjustment
if ($dir == 'RTL' && $Value1['XPlacement'] && $Value1['XAdvance']) {
$Value1['XPlacement'] -= $Value1['XAdvance'];
}
if ($ValueFormat2) {
if ($dir == 'RTL' && $Value2['XPlacement'] && $Value2['XAdvance']) {
$Value2['XPlacement'] -= $Value2['XAdvance'];
}
}
for ($c1 = 0; $c1 < count($Class1[$i]); $c1++) {
$FirstGlyph = $Class1[$i][$c1];
if ($level == 2 && strpos($lcoverage, $FirstGlyph) === false) {
continue;
}
for ($c2 = 0; $c2 < count($Class2[$j]); $c2++) {
$SecondGlyph = $Class2[$j][$c2];
if (!$Value1['XPlacement'] && !$Value1['YPlacement'] && !$Value1['XAdvance'] && !$Value2['XPlacement'] && !$Value2['YPlacement'] && !$Value2['XAdvance']) {
continue;
}
$html .= '
';
$html .= '' . $this->formatUni($FirstGlyph) . ' ';
if ($level == 2 && $exB) {
$html .= $exB;
}
$html .= ' ' . $this->formatEntity($FirstGlyph) . $this->formatEntity($SecondGlyph) . '';
if ($level == 2 && $exL) {
$html .= $exL;
}
$html .= ' » » ';
if ($level == 2 && $exB) {
$html .= $exB;
}
$html .= ' ' . $this->formatEntity($FirstGlyph) . $this->formatEntity($SecondGlyph) . '';
if ($level == 2 && $exL) {
$html .= $exL;
}
$html .= ' ';
if ($Value1['XPlacement']) {
$html .= ' Xpl[1]: ' . $Value1['XPlacement'] . ';';
}
if ($Value1['YPlacement']) {
$html .= ' YPl[1]: ' . $Value1['YPlacement'] . ';';
}
if ($Value1['XAdvance']) {
$html .= ' Xadv[1]: ' . $Value1['XAdvance'];
}
if ($Value2['XPlacement']) {
$html .= ' Xpl[2]: ' . $Value2['XPlacement'] . ';';
}
if ($Value2['YPlacement']) {
$html .= ' YPl[2]: ' . $Value2['YPlacement'] . ';';
}
if ($Value2['XAdvance']) {
$html .= ' Xadv[2]: ' . $Value2['XAdvance'];
}
$html .= '';
$html .= '
';
}
}
}
}
}
}
} ////////////////////////////////////////////////////////////////////////////////
// LookupType 3: Cursive attachment Attach cursive glyphs
////////////////////////////////////////////////////////////////////////////////
else {
if ($Lookup[$luli]['Type'] == 3) {
$html .= '
LookupType 3: Cursive attachment
';
$Coverage = $subtable_offset + $this->read_ushort();
$EntryExitCount = $this->read_ushort();
$EntryAnchors = [];
$ExitAnchors = [];
for ($i = 0; $i < $EntryExitCount; $i++) {
$EntryAnchors[$i] = $this->read_ushort();
$ExitAnchors[$i] = $this->read_ushort();
}
$this->seek($Coverage);
$Glyphs = $this->_getCoverage();
for ($i = 0; $i < $EntryExitCount; $i++) {
// Need default XAdvance for glyph
$pdfWidth = $this->mpdf->_getCharWidth($this->mpdf->fonts[$this->fontkey]['cw'], hexdec($Glyphs[$i]));
$EntryAnchor = $EntryAnchors[$i];
$ExitAnchor = $ExitAnchors[$i];
$html .= '
';
$html .= '' . $this->formatEntity($Glyphs[$i]) . ' ';
$html .= ' ' . $this->formatUni($Glyphs[$i]) . ' => ';
if ($EntryAnchor != 0) {
$EntryAnchor += $subtable_offset;
list($x, $y) = $this->_getAnchorTable($EntryAnchor);
if ($dir == 'RTL') {
if (round($pdfWidth) == round($x * 1000 / $this->mpdf->fonts[$this->fontkey]['desc']['unitsPerEm'])) {
$x = 0;
} else {
$x = $x - ($pdfWidth * $this->mpdf->fonts[$this->fontkey]['desc']['unitsPerEm'] / 1000);
}
}
$html .= " Entry X: " . $x . " Y: " . $y . "; ";
}
if ($ExitAnchor != 0) {
$ExitAnchor += $subtable_offset;
list($x, $y) = $this->_getAnchorTable($ExitAnchor);
if ($dir == 'LTR') {
if (round($pdfWidth) == round($x * 1000 / $this->mpdf->fonts[$this->fontkey]['desc']['unitsPerEm'])) {
$x = 0;
} else {
$x = $x - ($pdfWidth * $this->mpdf->fonts[$this->fontkey]['desc']['unitsPerEm'] / 1000);
}
}
$html .= " Exit X: " . $x . " Y: " . $y . "; ";
}
$html .= '
';
}
} ////////////////////////////////////////////////////////////////////////////////
// LookupType 4: MarkToBase attachment Attach a combining mark to a base glyph
////////////////////////////////////////////////////////////////////////////////
else {
if ($Lookup[$luli]['Type'] == 4) {
$html .= '
LookupType 4: MarkToBase attachment
';
$MarkCoverage = $subtable_offset + $this->read_ushort();
$BaseCoverage = $subtable_offset + $this->read_ushort();
$this->seek($MarkCoverage);
$MarkGlyphs = $this->_getCoverage();
$this->seek($BaseCoverage);
$BaseGlyphs = $this->_getCoverage();
$firstMark = '';
$html .= '
Marks: ';
for ($i = 0; $i < count($MarkGlyphs); $i++) {
if ($level == 2 && strpos($lcoverage, $MarkGlyphs[$i]) === false) {
continue;
} else {
if (!$firstMark) {
$firstMark = $MarkGlyphs[$i];
}
}
$html .= ' ' . $this->formatEntity($MarkGlyphs[$i]) . ' ';
}
$html .= '
';
if (!$firstMark) {
return;
}
$html .= '
Bases: ';
for ($j = 0; $j < count($BaseGlyphs); $j++) {
$html .= ' ' . $this->formatEntity($BaseGlyphs[$j]) . ' ';
}
$html .= '
';
// Example
$html .= '
Example(s): ';
for ($j = 0; $j < min(count($BaseGlyphs), 20); $j++) {
$html .= ' ' . $this->formatEntity($BaseGlyphs[$j]) . $this->formatEntity($firstMark, true) . ' ';
}
$html .= '
';
} ////////////////////////////////////////////////////////////////////////////////
// LookupType 5: MarkToLigature attachment Attach a combining mark to a ligature
////////////////////////////////////////////////////////////////////////////////
else {
if ($Lookup[$luli]['Type'] == 5) {
$html .= '
LookupType 5: MarkToLigature attachment
';
$MarkCoverage = $subtable_offset + $this->read_ushort();
//$MarkCoverage is already set in $lcoverage 00065|00073 etc
$LigatureCoverage = $subtable_offset + $this->read_ushort();
$ClassCount = $this->read_ushort(); // Number of classes defined for marks = Number of mark glyphs in the MarkCoverage table
$MarkArray = $subtable_offset + $this->read_ushort(); // Offset to MarkArray table
$LigatureArray = $subtable_offset + $this->read_ushort(); // Offset to LigatureArray table
$this->seek($MarkCoverage);
$MarkGlyphs = $this->_getCoverage();
$this->seek($LigatureCoverage);
$LigatureGlyphs = $this->_getCoverage();
$firstMark = '';
$html .= '
Marks: ';
$MarkRecord = [];
for ($i = 0; $i < count($MarkGlyphs); $i++) {
if ($level == 2 && strpos($lcoverage, $MarkGlyphs[$i]) === false) {
continue;
} else {
if (!$firstMark) {
$firstMark = $MarkGlyphs[$i];
}
}
// Get the relevant MarkRecord
$MarkRecord[$i] = $this->_getMarkRecord($MarkArray, $i);
//Mark Class is = $MarkRecord[$i]['Class']
$html .= ' ' . $this->formatEntity($MarkGlyphs[$i]) . ' ';
}
$html .= '
';
if (!$firstMark) {
return;
}
$this->seek($LigatureArray);
$LigatureCount = $this->read_ushort();
$LigatureAttach = [];
$html .= '
Ligatures: ';
for ($j = 0; $j < count($LigatureGlyphs); $j++) {
// Get the relevant LigatureRecord
$LigatureAttach[$j] = $LigatureArray + $this->read_ushort();
$html .= ' ' . $this->formatEntity($LigatureGlyphs[$j]) . ' ';
}
$html .= '
';
/*
for ($i=0;$i
formatEntity($MarkGlyphs[$i]).'';
for ($j=0;$jseek($LigatureAttach[$j]);
$ComponentCount = $this->read_ushort();
$html .= ''.$this->formatEntity($LigatureGlyphs[$j]).'';
$offsets = array();
for ($comp=0;$comp<$ComponentCount;$comp++) {
// ComponentRecords
for ($class=0;$class<$ClassCount;$class++) {
$offset = $this->read_ushort();
if ($offset!= 0 && $class == $MarkRecord[$i]['Class']) {
$html .= ' ['.$comp.'] ';
}
}
}
}
$html .= ' ';
}
*/
} ////////////////////////////////////////////////////////////////////////////////
// LookupType 6: MarkToMark attachment Attach a combining mark to another mark
////////////////////////////////////////////////////////////////////////////////
else {
if ($Lookup[$luli]['Type'] == 6) {
$html .= 'LookupType 7: Context positioning [Format ' . $PosFormat . ']
';
//===========
// Format 1:
//===========
if ($PosFormat == 1) {
throw new \Mpdf\Exception\FontException("GPOS Lookup Type " . $Type . " Format " . $PosFormat . " not YET TESTED.");
} //===========
// Format 2:
//===========
else {
if ($PosFormat == 2) {
throw new \Mpdf\Exception\FontException("GPOS Lookup Type " . $Type . " Format " . $PosFormat . " not YET TESTED.");
} //===========
// Format 3:
//===========
else {
if ($PosFormat == 3) {
throw new \Mpdf\Exception\FontException("GPOS Lookup Type " . $Type . " Format " . $PosFormat . " not YET TESTED.");
} else {
throw new \Mpdf\Exception\FontException("GPOS Lookup Type " . $Type . ", Format " . $PosFormat . " not supported.");
}
}
}
} ////////////////////////////////////////////////////////////////////////////////
// LookupType 8: Chained Context positioning Position one or more glyphs in chained context
////////////////////////////////////////////////////////////////////////////////
else {
if ($Lookup[$luli]['Type'] == 8) {
$html .= 'LookupType 8: Chained Context positioning [Format ' . $PosFormat . ']
';
//===========
// Format 1:
//===========
if ($PosFormat == 1) {
throw new \Mpdf\Exception\FontException("GPOS Lookup Type " . $Type . " Format " . $PosFormat . " not TESTED YET.");
} //===========
// Format 2:
//===========
else {
if ($PosFormat == 2) {
$html .= 'CONTEXT: ';
for ($ff = count($backtrackGlyphs) - 1; $ff >= 0; $ff--) {
$html .= '
Backtrack #' . $ff . ': ' . $this->formatUniStr($backtrackGlyphs[$ff]) . '
';
$exampleB[] = $this->formatEntityFirst($backtrackGlyphs[$ff]);
}
for ($ff = 0; $ff < count($inputGlyphs); $ff++) {
$html .= '
Input #' . $ff . ': ' . $this->formatEntityStr($inputGlyphs[$ff]) . '
';
$exampleI[] = $this->formatEntityFirst($inputGlyphs[$ff]);
}
for ($ff = 0; $ff < count($lookaheadGlyphs); $ff++) {
$html .= '
Lookahead #' . $ff . ': ' . $this->formatUniStr($lookaheadGlyphs[$ff]) . '
';
$exampleL[] = $this->formatEntityFirst($lookaheadGlyphs[$ff]);
}
$html .= '
';
for ($p = 0; $p < $PosCount; $p++) {
$lup = $PosLookupRecord[$p]['LookupListIndex'];
$seqIndex = $PosLookupRecord[$p]['SequenceIndex'];
// GENERATE exampleB[n] exampleI[