diff --git a/sample-fonts/NotoSansShavian-Regular.ttf b/sample-fonts/NotoSansShavian-Regular.ttf new file mode 100644 index 0000000..29ebdb5 Binary files /dev/null and b/sample-fonts/NotoSansShavian-Regular.ttf differ diff --git a/src/FontLib/Table/Type/cmap.php b/src/FontLib/Table/Type/cmap.php index 562f51e..7db77e1 100644 --- a/src/FontLib/Table/Type/cmap.php +++ b/src/FontLib/Table/Type/cmap.php @@ -35,6 +35,12 @@ class cmap extends Table { "rangeShift" => self::uint16, ); + private static $subtable_v12_format = array( + "length" => self::uint32, + "language" => self::uint32, + "ngroups" => self::uint32 + ); + protected function _parse() { $font = $this->getFont(); @@ -46,6 +52,7 @@ protected function _parse() { for ($i = 0; $i < $data["numberSubtables"]; $i++) { $subtables[] = $font->unpack(self::$subtable_header_format); } + $data["subtables"] = $subtables; foreach ($data["subtables"] as $i => &$subtable) { @@ -53,67 +60,100 @@ protected function _parse() { $subtable["format"] = $font->readUInt16(); - // @todo Only CMAP version 4 - if ($subtable["format"] != 4) { + // @todo Only CMAP version 4 and 12 + if (($subtable["format"] != 4) && ($subtable["format"] != 12)) { unset($data["subtables"][$i]); $data["numberSubtables"]--; continue; } - $subtable += $font->unpack(self::$subtable_v4_format); - $segCount = $subtable["segCountX2"] / 2; - $subtable["segCount"] = $segCount; + if ($subtable["format"] == 12) { - $endCode = $font->readUInt16Many($segCount); + $font->readUInt16(); - $font->readUInt16(); // reservedPad + $subtable += $font->unpack(self::$subtable_v12_format); - $startCode = $font->readUInt16Many($segCount); - $idDelta = $font->readInt16Many($segCount); + $glyphIndexArray = array(); + $endCodes = array(); + $startCodes = array(); - $ro_start = $font->pos(); - $idRangeOffset = $font->readUInt16Many($segCount); + for ($p = 0; $p < $subtable['ngroups']; $p++) { - $glyphIndexArray = array(); - for ($i = 0; $i < $segCount; $i++) { - $c1 = $startCode[$i]; - $c2 = $endCode[$i]; - $d = $idDelta[$i]; - $ro = $idRangeOffset[$i]; + $startCode = $startCodes[] = $font->readUInt32(); + $endCode = $endCodes[] = $font->readUInt32(); + $startGlyphCode = $font->readUInt32(); - if ($ro > 0) { - $font->seek($subtable["offset"] + 2 * $i + $ro); + for ($c = $startCode; $c <= $endCode; $c++) { + $glyphIndexArray[$c] = $startGlyphCode; + $startGlyphCode++; + } } - for ($c = $c1; $c <= $c2; $c++) { - if ($ro == 0) { - $gid = ($c + $d) & 0xFFFF; + $subtable += array( + "startCode" => $startCodes, + "endCode" => $endCodes, + "glyphIndexArray" => $glyphIndexArray, + ); + + } + else if ($subtable["format"] == 4) { + + $subtable += $font->unpack(self::$subtable_v4_format); + + $segCount = $subtable["segCountX2"] / 2; + $subtable["segCount"] = $segCount; + + $endCode = $font->readUInt16Many($segCount); + + $font->readUInt16(); // reservedPad + + $startCode = $font->readUInt16Many($segCount); + $idDelta = $font->readInt16Many($segCount); + + $ro_start = $font->pos(); + $idRangeOffset = $font->readUInt16Many($segCount); + + $glyphIndexArray = array(); + for ($i = 0; $i < $segCount; $i++) { + $c1 = $startCode[$i]; + $c2 = $endCode[$i]; + $d = $idDelta[$i]; + $ro = $idRangeOffset[$i]; + + if ($ro > 0) { + $font->seek($subtable["offset"] + 2 * $i + $ro); } - else { - $offset = ($c - $c1) * 2 + $ro; - $offset = $ro_start + 2 * $i + $offset; - $font->seek($offset); - $gid = $font->readUInt16(); + for ($c = $c1; $c <= $c2; $c++) { + if ($ro == 0) { + $gid = ($c + $d) & 0xFFFF; + } + else { + $offset = ($c - $c1) * 2 + $ro; + $offset = $ro_start + 2 * $i + $offset; + + $font->seek($offset); + $gid = $font->readUInt16(); - if ($gid != 0) { - $gid = ($gid + $d) & 0xFFFF; + if ($gid != 0) { + $gid = ($gid + $d) & 0xFFFF; + } } - } - if ($gid > 0) { - $glyphIndexArray[$c] = $gid; + if ($gid > 0) { + $glyphIndexArray[$c] = $gid; + } } } - } - $subtable += array( - "endCode" => $endCode, - "startCode" => $startCode, - "idDelta" => $idDelta, - "idRangeOffset" => $idRangeOffset, - "glyphIndexArray" => $glyphIndexArray, - ); + $subtable += array( + "endCode" => $endCode, + "startCode" => $startCode, + "idDelta" => $idDelta, + "idRangeOffset" => $idRangeOffset, + "glyphIndexArray" => $glyphIndexArray, + ); + } } $this->data = $data; diff --git a/tests/FontLib/FontTest.php b/tests/FontLib/FontTest.php index 08cf415..754a555 100644 --- a/tests/FontLib/FontTest.php +++ b/tests/FontLib/FontTest.php @@ -20,4 +20,29 @@ public function testLoadTTFFontSuccessfully() $this->assertInstanceOf('FontLib\TrueType\File', $trueTypeFont); } + + public function test12CmapFormat() + { + $trueTypeFont = Font::load('sample-fonts/NotoSansShavian-Regular.ttf'); + + $trueTypeFont->parse(); + + $cmapTable = $trueTypeFont->getData("cmap", "subtables"); + + $cmapFormat4Table = $cmapTable[0]; + + $this->assertEquals(4, $cmapFormat4Table['format']); + $this->assertEquals(6, $cmapFormat4Table['segCount']); + $this->assertEquals($cmapFormat4Table['segCount'], count($cmapFormat4Table['startCode'])); + $this->assertEquals($cmapFormat4Table['segCount'], count($cmapFormat4Table['endCode'])); + + $cmapFormat12Table = $cmapTable[1]; + + $this->assertEquals(12, $cmapFormat12Table['format']); + $this->assertEquals(6, $cmapFormat12Table['ngroups']); + $this->assertEquals(6, count($cmapFormat12Table['startCode'])); + $this->assertEquals(6, count($cmapFormat12Table['endCode'])); + $this->assertEquals(53, count($cmapFormat12Table['glyphIndexArray'])); + } + } \ No newline at end of file