3 * @package php-font-lib
4 * @link https://github.com/PhenX/php-font-lib
5 * @author Fabien Ménager <fabien.menager@gmail.com>
6 * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
9 namespace FontLib\Table\Type
;
10 use FontLib\Table\Table
;
15 * @package php-font-lib
17 class cmap
extends Table
{
18 private static $header_format = array(
19 "version" => self
::uint16
,
20 "numberSubtables" => self
::uint16
,
23 private static $subtable_header_format = array(
24 "platformID" => self
::uint16
,
25 "platformSpecificID" => self
::uint16
,
26 "offset" => self
::uint32
,
29 private static $subtable_v4_format = array(
30 "length" => self
::uint16
,
31 "language" => self
::uint16
,
32 "segCountX2" => self
::uint16
,
33 "searchRange" => self
::uint16
,
34 "entrySelector" => self
::uint16
,
35 "rangeShift" => self
::uint16
,
38 protected function _parse() {
39 $font = $this->getFont();
41 $cmap_offset = $font->pos();
43 $data = $font->unpack(self
::$header_format);
46 for ($i = 0; $i < $data["numberSubtables"]; $i++
) {
47 $subtables[] = $font->unpack(self
::$subtable_header_format);
49 $data["subtables"] = $subtables;
51 foreach ($data["subtables"] as $i => &$subtable) {
52 $font->seek($cmap_offset +
$subtable["offset"]);
54 $subtable["format"] = $font->readUInt16();
56 // @todo Only CMAP version 4
57 if ($subtable["format"] != 4) {
58 unset($data["subtables"][$i]);
59 $data["numberSubtables"]--;
63 $subtable +
= $font->unpack(self
::$subtable_v4_format);
64 $segCount = $subtable["segCountX2"] / 2;
65 $subtable["segCount"] = $segCount;
67 $endCode = $font->r(array(self
::uint16
, $segCount));
69 $font->readUInt16(); // reservedPad
71 $startCode = $font->r(array(self
::uint16
, $segCount));
72 $idDelta = $font->r(array(self
::int16
, $segCount));
74 $ro_start = $font->pos();
75 $idRangeOffset = $font->r(array(self
::uint16
, $segCount));
77 $glyphIndexArray = array();
78 for ($i = 0; $i < $segCount; $i++
) {
82 $ro = $idRangeOffset[$i];
85 $font->seek($subtable["offset"] +
2 * $i +
$ro);
88 for ($c = $c1; $c <= $c2; $c++
) {
90 $gid = ($c +
$d) & 0xFFFF;
93 $offset = ($c - $c1) * 2 +
$ro;
94 $offset = $ro_start +
2 * $i +
$offset;
97 $gid = $font->readUInt16();
100 $gid = ($gid +
$d) & 0xFFFF;
105 $glyphIndexArray[$c] = $gid;
111 "endCode" => $endCode,
112 "startCode" => $startCode,
113 "idDelta" => $idDelta,
114 "idRangeOffset" => $idRangeOffset,
115 "glyphIndexArray" => $glyphIndexArray,
123 $font = $this->getFont();
125 $subset = $font->getSubset();
126 $glyphIndexArray = $font->getUnicodeCharMap();
128 $newGlyphIndexArray = array();
129 foreach ($glyphIndexArray as $code => $gid) {
130 $new_gid = array_search($gid, $subset);
131 if ($new_gid !== false) {
132 $newGlyphIndexArray[$code] = $new_gid;
136 ksort($newGlyphIndexArray); // Sort by char code
144 foreach ($newGlyphIndexArray as $code => $gid) {
146 $prevCode +
1 != $code ||
150 $segments[$i] = array();
153 $segments[$i][] = array($code, $gid);
159 $segments[][] = array(0xFFFF, 0xFFFF);
161 $startCode = array();
165 foreach ($segments as $codes) {
166 $start = reset($codes);
169 $startCode[] = $start[0];
170 $endCode[] = $end[0];
171 $idDelta[] = $start[1] - $start[0];
174 $segCount = count($startCode);
175 $idRangeOffset = array_fill(0, $segCount, 0);
179 while ($searchRange * 2 <= $segCount) {
184 $rangeShift = $segCount * 2 - $searchRange;
189 "platformID" => 3, // Unicode
190 "platformSpecificID" => 1,
197 "segCount" => $segCount,
198 "segCountX2" => $segCount * 2,
199 "searchRange" => $searchRange,
200 "entrySelector" => $entrySelector,
201 "rangeShift" => $rangeShift,
202 "startCode" => $startCode,
203 "endCode" => $endCode,
204 "idDelta" => $idDelta,
205 "idRangeOffset" => $idRangeOffset,
206 "glyphIndexArray" => $newGlyphIndexArray,
212 "numberSubtables" => count($subtables),
213 "subtables" => $subtables,
216 $length = $font->pack(self
::$header_format, $data);
218 $subtable_headers_size = $data["numberSubtables"] * 8; // size of self::$subtable_header_format
219 $subtable_headers_offset = $font->pos();
221 $length +
= $font->write(str_repeat("\0", $subtable_headers_size), $subtable_headers_size);
223 // write subtables data
224 foreach ($data["subtables"] as $i => $subtable) {
225 $length_before = $length;
226 $data["subtables"][$i]["offset"] = $length;
228 $length +
= $font->writeUInt16($subtable["format"]);
230 $before_subheader = $font->pos();
231 $length +
= $font->pack(self
::$subtable_v4_format, $subtable);
233 $segCount = $subtable["segCount"];
234 $length +
= $font->w(array(self
::uint16
, $segCount), $subtable["endCode"]);
235 $length +
= $font->writeUInt16(0); // reservedPad
236 $length +
= $font->w(array(self
::uint16
, $segCount), $subtable["startCode"]);
237 $length +
= $font->w(array(self
::int16
, $segCount), $subtable["idDelta"]);
238 $length +
= $font->w(array(self
::uint16
, $segCount), $subtable["idRangeOffset"]);
239 $length +
= $font->w(array(self
::uint16
, $segCount), array_values($subtable["glyphIndexArray"]));
241 $after_subtable = $font->pos();
243 $subtable["length"] = $length - $length_before;
244 $font->seek($before_subheader);
245 $length +
= $font->pack(self
::$subtable_v4_format, $subtable);
247 $font->seek($after_subtable);
250 // write subtables headers
251 $font->seek($subtable_headers_offset);
252 foreach ($data["subtables"] as $subtable) {
253 $font->pack(self
::$subtable_header_format, $subtable);