Files
faveo/vendor/phenx/php-font-lib/classes/Font_Table_cmap.php
2015-05-01 13:13:01 +05:30

253 lines
7.1 KiB
PHP

<?php
/**
* @package php-font-lib
* @link https://github.com/PhenX/php-font-lib
* @author Fabien Ménager <fabien.menager@gmail.com>
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
/**
* `cmap` font table.
*
* @package php-font-lib
*/
class Font_Table_cmap extends Font_Table {
private static $header_format = array(
"version" => self::uint16,
"numberSubtables" => self::uint16,
);
private static $subtable_header_format = array(
"platformID" => self::uint16,
"platformSpecificID" => self::uint16,
"offset" => self::uint32,
);
private static $subtable_v4_format = array(
"length" => self::uint16,
"language" => self::uint16,
"segCountX2" => self::uint16,
"searchRange" => self::uint16,
"entrySelector" => self::uint16,
"rangeShift" => self::uint16,
);
protected function _parse(){
$font = $this->getFont();
$cmap_offset = $font->pos();
$data = $font->unpack(self::$header_format);
$subtables = array();
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) {
unset($data["subtables"][$i]);
$data["numberSubtables"]--;
continue;
}
$subtable += $font->unpack(self::$subtable_v4_format);
$segCount = $subtable["segCountX2"] / 2;
$subtable["segCount"] = $segCount;
$endCode = $font->r(array(self::uint16, $segCount));
$font->readUInt16(); // reservedPad
$startCode = $font->r(array(self::uint16, $segCount));
$idDelta = $font->r(array(self::int16, $segCount));
$ro_start = $font->pos();
$idRangeOffset = $font->r(array(self::uint16, $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);
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) {
$glyphIndexArray[$c] = $gid;
}
}
}
$subtable += array(
"endCode" => $endCode,
"startCode" => $startCode,
"idDelta" => $idDelta,
"idRangeOffset" => $idRangeOffset,
"glyphIndexArray" => $glyphIndexArray,
);
}
$this->data = $data;
}
function _encode(){
$font = $this->getFont();
$subset = $font->getSubset();
$glyphIndexArray = $font->getUnicodeCharMap();
$newGlyphIndexArray = array();
foreach ($glyphIndexArray as $code => $gid) {
$new_gid = array_search($gid, $subset);
if ($new_gid !== false) {
$newGlyphIndexArray[$code] = $new_gid;
}
}
ksort($newGlyphIndexArray); // Sort by char code
$segments = array();
$i = -1;
$prevCode = 0xFFFF;
$prevGid = 0xFFFF;
foreach($newGlyphIndexArray as $code => $gid) {
if (
$prevCode + 1 != $code ||
$prevGid + 1 != $gid
) {
$i++;
$segments[$i] = array();
}
$segments[$i][] = array($code, $gid);
$prevCode = $code;
$prevGid = $gid;
}
$segments[][] = array(0xFFFF, 0xFFFF);
$startCode = array();
$endCode = array();
$idDelta = array();
foreach($segments as $codes){
$start = reset($codes);
$end = end($codes);
$startCode[] = $start[0];
$endCode[] = $end[0];
$idDelta[] = $start[1] - $start[0];
}
$segCount = count($startCode);
$idRangeOffset = array_fill(0, $segCount, 0);
$searchRange = 1;
$entrySelector = 0;
while ($searchRange * 2 <= $segCount) {
$searchRange *= 2;
$entrySelector++;
}
$searchRange *= 2;
$rangeShift = $segCount * 2 - $searchRange;
$subtables = array(
array(
// header
"platformID" => 3, // Unicode
"platformSpecificID" => 1,
"offset" => null,
// subtable
"format" => 4,
"length" => null,
"language" => 0,
"segCount" => $segCount,
"segCountX2" => $segCount * 2,
"searchRange" => $searchRange,
"entrySelector" => $entrySelector,
"rangeShift" => $rangeShift,
"startCode" => $startCode,
"endCode" => $endCode,
"idDelta" => $idDelta,
"idRangeOffset" => $idRangeOffset,
"glyphIndexArray" => $newGlyphIndexArray,
)
);
$data = array(
"version" => 0,
"numberSubtables" => count($subtables),
"subtables" => $subtables,
);
$length = $font->pack(self::$header_format, $data);
$subtable_headers_size = $data["numberSubtables"] * 8; // size of self::$subtable_header_format
$subtable_headers_offset = $font->pos();
$length += $font->write(str_repeat("\0", $subtable_headers_size), $subtable_headers_size);
// write subtables data
foreach($data["subtables"] as $i => $subtable) {
$length_before = $length;
$data["subtables"][$i]["offset"] = $length;
$length += $font->writeUInt16($subtable["format"]);
$before_subheader = $font->pos();
$length += $font->pack(self::$subtable_v4_format, $subtable);
$segCount = $subtable["segCount"];
$length += $font->w(array(self::uint16, $segCount), $subtable["endCode"]);
$length += $font->writeUInt16(0); // reservedPad
$length += $font->w(array(self::uint16, $segCount), $subtable["startCode"]);
$length += $font->w(array(self::int16, $segCount), $subtable["idDelta"]);
$length += $font->w(array(self::uint16, $segCount), $subtable["idRangeOffset"]);
$length += $font->w(array(self::uint16, $segCount), array_values($subtable["glyphIndexArray"]));
$after_subtable = $font->pos();
$subtable["length"] = $length - $length_before;
$font->seek($before_subheader);
$length += $font->pack(self::$subtable_v4_format, $subtable);
$font->seek($after_subtable);
}
// write subtables headers
$font->seek($subtable_headers_offset);
foreach($data["subtables"] as $subtable) {
$font->pack(self::$subtable_header_format, $subtable);
}
return $length;
}
}