Skip to content
26 changes: 11 additions & 15 deletions src/FontLib/Table/DirectoryEntry.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,20 +49,8 @@ static function computeChecksum($data) {
$data = str_pad($data, $len + (4 - $mod), "\0");
}

$len = mb_strlen($data, '8bit');

$hi = 0x0000;
$lo = 0x0000;

for ($i = 0; $i < $len; $i += 4) {
$hi += (ord($data[$i]) << 8) + ord($data[$i + 1]);
$lo += (ord($data[$i + 2]) << 8) + ord($data[$i + 3]);
$hi += $lo >> 16;
$lo = $lo & 0xFFFF;
$hi = $hi & 0xFFFF;
}

return ($hi << 8) + $lo;
$table = unpack("N*", $data);
return array_sum($table);
}

function __construct(File $font) {
Expand Down Expand Up @@ -93,6 +81,14 @@ function encode($entry_offset) {
$this->offset = $table_offset;
$table_length = $data->encode();

$font->seek($table_offset + $table_length);
$pad = 0;
$mod = $table_length % 4;
if ($mod != 0) {
$pad = 4 - $mod;
$font->write(str_pad("", $pad, "\0"), $pad);
}

$font->seek($table_offset);
$table_data = $font->read($table_length);

Expand All @@ -105,7 +101,7 @@ function encode($entry_offset) {

Font::d("Bytes written = $table_length");

$font->seek($table_offset + $table_length);
$font->seek($table_offset + $table_length + $pad);
}

/**
Expand Down
259 changes: 168 additions & 91 deletions src/FontLib/Table/Type/cmap.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,18 @@ class cmap extends Table {
"offset" => self::uint32,
);

private static $subtable_v2_format = array(
"length" => self::uint16,
"language" => self::uint16
);

private static $subtable_v2_format_subheader = array(
"firstCode" => self::uint16,
"entryCount" => self::uint16,
"idDelta" => self::int16,
"idRangeOffset" => self::uint16
);

private static $subtable_v4_format = array(
"length" => self::uint16,
"language" => self::uint16,
Expand All @@ -38,7 +50,7 @@ class cmap extends Table {
private static $subtable_v12_format = array(
"length" => self::uint32,
"language" => self::uint32,
"ngroups" => self::uint32
"ngroups" => self::uint32
);

protected function _parse() {
Expand All @@ -60,105 +72,170 @@ protected function _parse() {

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

// @todo Only CMAP version 4 and 12
if (($subtable["format"] != 4) && ($subtable["format"] != 12)) {
unset($data["subtables"][$i]);
$data["numberSubtables"]--;
continue;
}

if ($subtable["format"] == 12) {

$font->readUInt16();

$subtable += $font->unpack(self::$subtable_v12_format);

$glyphIndexArray = array();
$endCodes = array();
$startCodes = array();

for ($p = 0; $p < $subtable['ngroups']; $p++) {

$startCode = $startCodes[] = $font->readUInt32();
$endCode = $endCodes[] = $font->readUInt32();
$startGlyphCode = $font->readUInt32();

for ($c = $startCode; $c <= $endCode; $c++) {
$glyphIndexArray[$c] = $startGlyphCode;
$startGlyphCode++;
}
}

$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);
switch ($subtable["format"]) {
case 0:
case 6:
case 8:
case 10:
case 13:
case 14:
unset($data["subtables"][$i]);
$data["numberSubtables"]--;
continue 2;

case 2:
$subtable += $font->unpack(self::$subtable_v2_format);

$subHeaderKeys = array_map(function($val) { return $val / 8; }, $font->readUInt16Many(256));
$subHeaders = array();

$glyphIdArray = array();
$maxSubHeaderIndex = max($subHeaderKeys);
for ($i = 0; $i <= $maxSubHeaderIndex; $i++) {
$subHeader = $font->unpack(self::$subtable_v2_format_subheader);
$offset = $font->pos();
$subHeader["glyphIdArrayOffset"] = $offset + $subHeader["idRangeOffset"] - 2;
$subHeaders[$i] = $subHeader;

if (!\array_key_exists($subHeader["glyphIdArrayOffset"], $glyphIdArray) || count($glyphIdArray[$subHeader["glyphIdArrayOffset"]]) < $subHeader["entryCount"]) {
$font->seek($subHeader["glyphIdArrayOffset"]);
$glyphIdArray[$subHeader["glyphIdArrayOffset"]] = $font->readUInt16Many($subHeader["entryCount"]);
$font->seek($offset);
}
}

for ($c = $c1; $c <= $c2; $c++) {
if ($c === 0xFFFF) {
continue;
$glyphIndexArray = array();
foreach ($subHeaderKeys as $highByte => $subHeaderKey) {
$subHeader = $subHeaders[$subHeaderKey];
if ($subHeaderKey === 0) {
$c = $highByte;
if ($c < $subHeader["firstCode"] || $c >= ($subHeader["firstCode"] + $subHeader["entryCount"])) {
$glyphIndexArray[$c] = 0;
continue;
}
$c = $highByte;
$index = $c - $subHeader["firstCode"];
$glyphId = $glyphIdArray[$subHeader["glyphIdArrayOffset"]][$index];
if ($glyphId === 0) {
$glyphIndexArray[$c] = 0;
} else {
$glyphIndexArray[$c] = ($glyphId + $subHeader["idDelta"]) & 0xFFFF;
}
} else {
for ($index = 0; $index < $subHeader["entryCount"]; $index++) {
$c = null;
$lowByte = $subHeader["firstCode"] + $index;
$c = (($highByte & 0xFF) << 8) | ($lowByte & 0xFF);
$glyphId = $glyphIdArray[$subHeader["glyphIdArrayOffset"]][$index];
if ($glyphId === 0) {
$glyphIndexArray[$c] = 0;
} else {
$glyphIndexArray[$c] = ($glyphId + $subHeader["idDelta"]) & 0xFFFF;
}
}
}
}

if ($ro == 0) {
$gid = ($c + $d) & 0xFFFF;
$subtable += array(
"subHeaderKeys" => $subHeaderKeys,
"subHeaders" => $subHeaders,
"glyphIdArray" => $glyphIdArray,
"glyphIndexArray" => $glyphIndexArray
);

break;

case 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;

$gid = 0;
if ($font->seek($offset) === true) {
$gid = $font->readUInt16();

for ($c = $c1; $c <= $c2; $c++) {
if ($c === 0xFFFF) {
continue;
}

if ($gid != 0) {
$gid = ($gid + $d) & 0xFFFF;

if ($ro == 0) {
$gid = ($c + $d) & 0xFFFF;
}
else {
$offset = ($c - $c1) * 2 + $ro;
$offset = $ro_start + 2 * $i + $offset;

$gid = 0;
if ($font->seek($offset) === true) {
$gid = $font->readUInt16();
}

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
);
break;

case 12:
$font->readUInt16();

$subtable += $font->unpack(self::$subtable_v12_format);

$glyphIndexArray = array();
$endCodes = array();
$startCodes = array();

for ($p = 0; $p < $subtable['ngroups']; $p++) {

$startCode = $startCodes[] = $font->readUInt32();
$endCode = $endCodes[] = $font->readUInt32();
$startGlyphCode = $font->readUInt32();

for ($c = $startCode; $c <= $endCode; $c++) {
$glyphIndexArray[$c] = $startGlyphCode;
$startGlyphCode++;
}
}
}

$subtable += array(
"endCode" => $endCode,
"startCode" => $startCode,
"idDelta" => $idDelta,
"idRangeOffset" => $idRangeOffset,
"glyphIndexArray" => $glyphIndexArray,
);

$subtable += array(
"startCode" => $startCodes,
"endCode" => $endCodes,
"glyphIndexArray" => $glyphIndexArray,
);
break;
}
}

Expand Down Expand Up @@ -202,7 +279,7 @@ function _encode() {
$prevGid = $gid;
}

$segments[][] = array(0xFFFF, 0xFFFF);
$segments[][] = array(0xFFFF, null);

$startCode = array();
$endCode = array();
Expand Down
11 changes: 10 additions & 1 deletion src/FontLib/Table/Type/glyf.php
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,16 @@ protected function _encode() {
$length = 0;
foreach ($subset as $gid) {
$loca[] = $length;
$length += $data[$gid]->encode();

$bytes = $data[$gid]->encode();

$pad = 0;
$mod = $bytes % 4;
if ($mod != 0) {
$pad = 4 - $mod;
$font->write(str_pad("", $pad, "\0"), $pad);
}
$length += $bytes + $pad;
}

$loca[] = $length; // dummy loca
Expand Down
5 changes: 5 additions & 0 deletions src/FontLib/Table/Type/head.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,9 @@ protected function _parse() {
throw new Exception("Incorrect magic number (" . dechex($this->data["magicNumber"]) . ")");
}
}

function _encode() {
$this->data["checkSumAdjustment"] = 0;
return parent::_encode();
}
}
Loading