Skip to content

Commit fa027b2

Browse files
committed
Add support for parsing cmap subtable format 2
A font with a format 2 cmap table will still re-encode with a format 4 cmap table. relates to #123
1 parent cb5f432 commit fa027b2

File tree

1 file changed

+168
-85
lines changed

1 file changed

+168
-85
lines changed

src/FontLib/Table/Type/cmap.php

Lines changed: 168 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,18 @@ class cmap extends Table {
2626
"offset" => self::uint32,
2727
);
2828

29+
private static $subtable_v2_format = array(
30+
"length" => self::uint16,
31+
"language" => self::uint16
32+
);
33+
34+
private static $subtable_v2_format_subheader = array(
35+
"firstCode" => self::uint16,
36+
"entryCount" => self::uint16,
37+
"idDelta" => self::int16,
38+
"idRangeOffset" => self::uint16
39+
);
40+
2941
private static $subtable_v4_format = array(
3042
"length" => self::uint16,
3143
"language" => self::uint16,
@@ -38,7 +50,7 @@ class cmap extends Table {
3850
private static $subtable_v12_format = array(
3951
"length" => self::uint32,
4052
"language" => self::uint32,
41-
"ngroups" => self::uint32
53+
"ngroups" => self::uint32
4254
);
4355

4456
protected function _parse() {
@@ -60,99 +72,170 @@ protected function _parse() {
6072

6173
$subtable["format"] = $font->readUInt16();
6274

63-
// @todo Only CMAP version 4 and 12
64-
if (($subtable["format"] != 4) && ($subtable["format"] != 12)) {
65-
unset($data["subtables"][$i]);
66-
$data["numberSubtables"]--;
67-
continue;
68-
}
69-
70-
if ($subtable["format"] == 12) {
71-
72-
$font->readUInt16();
73-
74-
$subtable += $font->unpack(self::$subtable_v12_format);
75-
76-
$glyphIndexArray = array();
77-
$endCodes = array();
78-
$startCodes = array();
79-
80-
for ($p = 0; $p < $subtable['ngroups']; $p++) {
81-
82-
$startCode = $startCodes[] = $font->readUInt32();
83-
$endCode = $endCodes[] = $font->readUInt32();
84-
$startGlyphCode = $font->readUInt32();
85-
86-
for ($c = $startCode; $c <= $endCode; $c++) {
87-
$glyphIndexArray[$c] = $startGlyphCode;
88-
$startGlyphCode++;
75+
switch ($subtable["format"]) {
76+
case 0:
77+
case 6:
78+
case 8:
79+
case 10:
80+
case 13:
81+
case 14:
82+
unset($data["subtables"][$i]);
83+
$data["numberSubtables"]--;
84+
continue;
85+
86+
case 2:
87+
$subtable += $font->unpack(self::$subtable_v2_format);
88+
89+
$subHeaderKeys = array_map(function($val) { return $val / 8; }, $font->readUInt16Many(256));
90+
$subHeaders = array();
91+
92+
$glyphIdArray = array();
93+
$maxSubHeaderIndex = max($subHeaderKeys);
94+
for ($i = 0; $i <= $maxSubHeaderIndex; $i++) {
95+
$subHeader = $font->unpack(self::$subtable_v2_format_subheader);
96+
$offset = $font->pos();
97+
$subHeader["glyphIdArrayOffset"] = $offset + $subHeader["idRangeOffset"] - 2;
98+
$subHeaders[$i] = $subHeader;
99+
100+
if (!\array_key_exists($subHeader["glyphIdArrayOffset"], $glyphIdArray) || count($glyphIdArray[$subHeader["glyphIdArrayOffset"]]) < $subHeader["entryCount"]) {
101+
$font->seek($subHeader["glyphIdArrayOffset"]);
102+
$glyphIdArray[$subHeader["glyphIdArrayOffset"]] = $font->readUInt16Many($subHeader["entryCount"]);
103+
$font->seek($offset);
104+
}
89105
}
90-
}
91-
92-
$subtable += array(
93-
"startCode" => $startCodes,
94-
"endCode" => $endCodes,
95-
"glyphIndexArray" => $glyphIndexArray,
96-
);
97-
98-
}
99-
else if ($subtable["format"] == 4) {
100-
101-
$subtable += $font->unpack(self::$subtable_v4_format);
102106

103-
$segCount = $subtable["segCountX2"] / 2;
104-
$subtable["segCount"] = $segCount;
105-
106-
$endCode = $font->readUInt16Many($segCount);
107-
108-
$font->readUInt16(); // reservedPad
109-
110-
$startCode = $font->readUInt16Many($segCount);
111-
$idDelta = $font->readInt16Many($segCount);
112-
113-
$ro_start = $font->pos();
114-
$idRangeOffset = $font->readUInt16Many($segCount);
115-
116-
$glyphIndexArray = array();
117-
for ($i = 0; $i < $segCount; $i++) {
118-
$c1 = $startCode[$i];
119-
$c2 = $endCode[$i];
120-
$d = $idDelta[$i];
121-
$ro = $idRangeOffset[$i];
122-
123-
if ($ro > 0) {
124-
$font->seek($subtable["offset"] + 2 * $i + $ro);
107+
$glyphIndexArray = array();
108+
foreach ($subHeaderKeys as $highByte => $subHeaderKey) {
109+
$subHeader = $subHeaders[$subHeaderKey];
110+
if ($subHeaderKey === 0) {
111+
$c = $highByte;
112+
if ($c < $subHeader["firstCode"] || $c >= ($subHeader["firstCode"] + $subHeader["entryCount"])) {
113+
$glyphIndexArray[$c] = 0;
114+
continue;
115+
}
116+
$c = $highByte;
117+
$index = $c - $subHeader["firstCode"];
118+
$glyphId = $glyphIdArray[$subHeader["glyphIdArrayOffset"]][$index];
119+
if ($glyphId === 0) {
120+
$glyphIndexArray[$c] = 0;
121+
} else {
122+
$glyphIndexArray[$c] = ($glyphId + $subHeader["idDelta"]) & 0xFFFF;
123+
}
124+
} else {
125+
for ($index = 0; $index < $subHeader["entryCount"]; $index++) {
126+
$c = null;
127+
$lowByte = $subHeader["firstCode"] + $index;
128+
$c = (($highByte & 0xFF) << 8) | ($lowByte & 0xFF);
129+
$glyphId = $glyphIdArray[$subHeader["glyphIdArrayOffset"]][$index];
130+
if ($glyphId === 0) {
131+
$glyphIndexArray[$c] = 0;
132+
} else {
133+
$glyphIndexArray[$c] = ($glyphId + $subHeader["idDelta"]) & 0xFFFF;
134+
}
135+
}
136+
}
125137
}
126138

127-
for ($c = $c1; $c <= $c2; $c++) {
128-
if ($ro == 0) {
129-
$gid = ($c + $d) & 0xFFFF;
139+
$subtable += array(
140+
"subHeaderKeys" => $subHeaderKeys,
141+
"subHeaders" => $subHeaders,
142+
"glyphIdArray" => $glyphIdArray,
143+
"glyphIndexArray" => $glyphIndexArray
144+
);
145+
146+
break;
147+
148+
case 4:
149+
$subtable += $font->unpack(self::$subtable_v4_format);
150+
151+
$segCount = $subtable["segCountX2"] / 2;
152+
$subtable["segCount"] = $segCount;
153+
154+
$endCode = $font->readUInt16Many($segCount);
155+
156+
$font->readUInt16(); // reservedPad
157+
158+
$startCode = $font->readUInt16Many($segCount);
159+
$idDelta = $font->readInt16Many($segCount);
160+
161+
$ro_start = $font->pos();
162+
$idRangeOffset = $font->readUInt16Many($segCount);
163+
164+
$glyphIndexArray = array();
165+
for ($i = 0; $i < $segCount; $i++) {
166+
$c1 = $startCode[$i];
167+
$c2 = $endCode[$i];
168+
$d = $idDelta[$i];
169+
$ro = $idRangeOffset[$i];
170+
171+
if ($ro > 0) {
172+
$font->seek($subtable["offset"] + 2 * $i + $ro);
130173
}
131-
else {
132-
$offset = ($c - $c1) * 2 + $ro;
133-
$offset = $ro_start + 2 * $i + $offset;
134-
135-
$font->seek($offset);
136-
$gid = $font->readUInt16();
137-
138-
if ($gid != 0) {
139-
$gid = ($gid + $d) & 0xFFFF;
174+
175+
for ($c = $c1; $c <= $c2; $c++) {
176+
if ($c === 0xFFFF) {
177+
continue;
178+
}
179+
180+
if ($ro == 0) {
181+
$gid = ($c + $d) & 0xFFFF;
182+
}
183+
else {
184+
$offset = ($c - $c1) * 2 + $ro;
185+
$offset = $ro_start + 2 * $i + $offset;
186+
187+
$gid = 0;
188+
if ($font->seek($offset) === true) {
189+
$gid = $font->readUInt16();
190+
}
191+
192+
if ($gid != 0) {
193+
$gid = ($gid + $d) & 0xFFFF;
194+
}
195+
}
196+
197+
if ($gid >= 0) {
198+
$glyphIndexArray[$c] = $gid;
140199
}
141200
}
142-
143-
if ($gid > 0) {
144-
$glyphIndexArray[$c] = $gid;
201+
}
202+
203+
$subtable += array(
204+
"endCode" => $endCode,
205+
"startCode" => $startCode,
206+
"idDelta" => $idDelta,
207+
"idRangeOffset" => $idRangeOffset,
208+
"glyphIndexArray" => $glyphIndexArray
209+
);
210+
break;
211+
212+
case 12:
213+
$font->readUInt16();
214+
215+
$subtable += $font->unpack(self::$subtable_v12_format);
216+
217+
$glyphIndexArray = array();
218+
$endCodes = array();
219+
$startCodes = array();
220+
221+
for ($p = 0; $p < $subtable['ngroups']; $p++) {
222+
223+
$startCode = $startCodes[] = $font->readUInt32();
224+
$endCode = $endCodes[] = $font->readUInt32();
225+
$startGlyphCode = $font->readUInt32();
226+
227+
for ($c = $startCode; $c <= $endCode; $c++) {
228+
$glyphIndexArray[$c] = $startGlyphCode;
229+
$startGlyphCode++;
145230
}
146231
}
147-
}
148-
149-
$subtable += array(
150-
"endCode" => $endCode,
151-
"startCode" => $startCode,
152-
"idDelta" => $idDelta,
153-
"idRangeOffset" => $idRangeOffset,
154-
"glyphIndexArray" => $glyphIndexArray,
155-
);
232+
233+
$subtable += array(
234+
"startCode" => $startCodes,
235+
"endCode" => $endCodes,
236+
"glyphIndexArray" => $glyphIndexArray,
237+
);
238+
break;
156239
}
157240
}
158241

0 commit comments

Comments
 (0)