Skip to content

Commit

Permalink
Merge pull request #64 from chumanfu/master
Browse files Browse the repository at this point in the history
Implement form 12 cmap. Issue #40
  • Loading branch information
PhenX authored Sep 13, 2017
2 parents e7df150 + 01bbab8 commit 7601488
Show file tree
Hide file tree
Showing 3 changed files with 105 additions and 40 deletions.
Binary file added sample-fonts/NotoSansShavian-Regular.ttf
Binary file not shown.
120 changes: 80 additions & 40 deletions src/FontLib/Table/Type/cmap.php
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand All @@ -46,74 +52,108 @@ 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) {
$font->seek($cmap_offset + $subtable["offset"]);

$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;
Expand Down
25 changes: 25 additions & 0 deletions tests/FontLib/FontTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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']));
}

}

0 comments on commit 7601488

Please sign in to comment.