FINAL suepr merge step : added all modules to this super repos
This commit is contained in:
@@ -0,0 +1,201 @@
|
||||
<?php
|
||||
/**
|
||||
* @package php-font-lib
|
||||
* @link http://php-font-lib.googlecode.com/
|
||||
* @author Fabien Ménager <fabien.menager@gmail.com>
|
||||
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
|
||||
* @version $Id$
|
||||
*/
|
||||
|
||||
require_once dirname(__FILE__)."/encoding_map.cls.php";
|
||||
|
||||
/**
|
||||
* Adobe Font Metrics file creation utility class.
|
||||
*
|
||||
* @package php-font-lib
|
||||
*/
|
||||
class Adobe_Font_Metrics {
|
||||
private $f;
|
||||
|
||||
/**
|
||||
* @var Font_TrueType
|
||||
*/
|
||||
private $font;
|
||||
|
||||
function __construct(Font_TrueType $font) {
|
||||
$this->font = $font;
|
||||
}
|
||||
|
||||
function write($file, $encoding = null){
|
||||
if ($encoding) {
|
||||
$encoding = preg_replace("/[^a-z0-9-_]/", "", $encoding);
|
||||
$map_file = dirname(__FILE__)."/../maps/$encoding.map";
|
||||
if (!file_exists($map_file)) {
|
||||
throw new Exception("Unkown encoding ($encoding)");
|
||||
}
|
||||
|
||||
$map = new Encoding_Map($map_file);
|
||||
$map_data = $map->parse();
|
||||
}
|
||||
|
||||
$this->f = fopen($file, "w+");
|
||||
|
||||
$font = $this->font;
|
||||
|
||||
$this->startSection("FontMetrics", 4.1);
|
||||
$this->addPair("Notice", "Converted by PHP-font-lib");
|
||||
$this->addPair("Comment", "http://php-font-lib.googlecode.com/");
|
||||
|
||||
$encoding_scheme = ($encoding ? $encoding : "FontSpecific");
|
||||
$this->addPair("EncodingScheme", $encoding_scheme);
|
||||
|
||||
$records = $font->getData("name", "records");
|
||||
foreach($records as $id => $record) {
|
||||
if (!isset(Font_TrueType::$nameIdCodes[$id]) || preg_match("/[\r\n]/", $record->string)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->addPair(Font_TrueType::$nameIdCodes[$id], $record->string);
|
||||
}
|
||||
|
||||
$os2 = $font->getData("OS/2");
|
||||
$this->addPair("Weight", ($os2["usWeightClass"] > 400 ? "Bold" : "Medium"));
|
||||
|
||||
$post = $font->getData("post");
|
||||
$this->addPair("ItalicAngle", $post["italicAngle"]);
|
||||
$this->addPair("IsFixedPitch", ($post["isFixedPitch"] ? "true" : "false"));
|
||||
$this->addPair("UnderlineThickness", $font->normalizeFUnit($post["underlineThickness"]));
|
||||
$this->addPair("UnderlinePosition", $font->normalizeFUnit($post["underlinePosition"]));
|
||||
|
||||
$hhea = $font->getData("hhea");
|
||||
|
||||
if (isset($hhea["ascent"])) {
|
||||
$this->addPair("FontHeightOffset", $font->normalizeFUnit($hhea["lineGap"]));
|
||||
$this->addPair("Ascender", $font->normalizeFUnit($hhea["ascent"]));
|
||||
$this->addPair("Descender", $font->normalizeFUnit($hhea["descent"]));
|
||||
}
|
||||
else {
|
||||
$this->addPair("FontHeightOffset", $font->normalizeFUnit($os2["typoLineGap"]));
|
||||
$this->addPair("Ascender", $font->normalizeFUnit($os2["typoAscender"]));
|
||||
$this->addPair("Descender", -abs($font->normalizeFUnit($os2["typoDescender"])));
|
||||
}
|
||||
|
||||
$head = $font->getData("head");
|
||||
$this->addArray("FontBBox", array(
|
||||
$font->normalizeFUnit($head["xMin"]),
|
||||
$font->normalizeFUnit($head["yMin"]),
|
||||
$font->normalizeFUnit($head["xMax"]),
|
||||
$font->normalizeFUnit($head["yMax"]),
|
||||
));
|
||||
|
||||
$glyphIndexArray = $font->getUnicodeCharMap();
|
||||
|
||||
if ($glyphIndexArray) {
|
||||
$hmtx = $font->getData("hmtx");
|
||||
$names = $font->getData("post", "names");
|
||||
|
||||
$this->startSection("CharMetrics", count($hmtx));
|
||||
|
||||
if ($encoding) {
|
||||
foreach($map_data as $code => $value) {
|
||||
list($c, $name) = $value;
|
||||
|
||||
if (!isset($glyphIndexArray[$c])) continue;
|
||||
|
||||
$g = $glyphIndexArray[$c];
|
||||
|
||||
if (!isset($hmtx[$g])) {
|
||||
$hmtx[$g] = $hmtx[0];
|
||||
}
|
||||
|
||||
$this->addMetric(array(
|
||||
"C" => ($code > 255 ? -1 : $code),
|
||||
"WX" => $font->normalizeFUnit($hmtx[$g][0]),
|
||||
"N" => $name,
|
||||
));
|
||||
}
|
||||
}
|
||||
else {
|
||||
foreach($glyphIndexArray as $c => $g) {
|
||||
if (!isset($hmtx[$g])) {
|
||||
$hmtx[$g] = $hmtx[0];
|
||||
}
|
||||
|
||||
$this->addMetric(array(
|
||||
"U" => $c,
|
||||
"WX" => $font->normalizeFUnit($hmtx[$g][0]),
|
||||
"N" => (isset($names[$g]) ? $names[$g] : sprintf("uni%04x", $c)),
|
||||
"G" => $g,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
$this->endSection("CharMetrics");
|
||||
|
||||
$kern = $font->getData("kern", "subtable");
|
||||
$tree = $kern["tree"];
|
||||
|
||||
if (!$encoding && is_array($tree)) {
|
||||
$this->startSection("KernData");
|
||||
$this->startSection("KernPairs", count($tree, COUNT_RECURSIVE) - count($tree));
|
||||
|
||||
foreach($tree as $left => $values) {
|
||||
if (!is_array($values)) continue;
|
||||
if (!isset($glyphIndexArray[$left])) continue;
|
||||
|
||||
$left_gid = $glyphIndexArray[$left];
|
||||
|
||||
if (!isset($names[$left_gid])) continue;
|
||||
|
||||
$left_name = $names[$left_gid];
|
||||
|
||||
$this->addLine("");
|
||||
|
||||
foreach($values as $right => $value) {
|
||||
if (!isset($glyphIndexArray[$right])) continue;
|
||||
|
||||
$right_gid = $glyphIndexArray[$right];
|
||||
|
||||
if (!isset($names[$right_gid])) continue;
|
||||
|
||||
$right_name = $names[$right_gid];
|
||||
$this->addPair("KPX", "$left_name $right_name $value");
|
||||
}
|
||||
}
|
||||
|
||||
$this->endSection("KernPairs");
|
||||
$this->endSection("KernData");
|
||||
}
|
||||
}
|
||||
|
||||
$this->endSection("FontMetrics");
|
||||
}
|
||||
|
||||
function addLine($line) {
|
||||
fwrite($this->f, "$line\n");
|
||||
}
|
||||
|
||||
function addPair($key, $value) {
|
||||
$this->addLine("$key $value");
|
||||
}
|
||||
|
||||
function addArray($key, $array) {
|
||||
$this->addLine("$key ".implode(" ", $array));
|
||||
}
|
||||
|
||||
function addMetric($data) {
|
||||
$array = array();
|
||||
foreach($data as $key => $value) {
|
||||
$array[] = "$key $value";
|
||||
}
|
||||
$this->addLine(implode(" ; ", $array));
|
||||
}
|
||||
|
||||
function startSection($name, $value = "") {
|
||||
$this->addLine("Start$name $value");
|
||||
}
|
||||
|
||||
function endSection($name) {
|
||||
$this->addLine("End$name");
|
||||
}
|
||||
}
|
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
/**
|
||||
* @package php-font-lib
|
||||
* @link http://php-font-lib.googlecode.com/
|
||||
* @author Fabien Ménager <fabien.menager@gmail.com>
|
||||
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
|
||||
* @version $Id$
|
||||
*/
|
||||
|
||||
/**
|
||||
* Encoding map used to map a code point to a Unicode char.
|
||||
*
|
||||
* @package php-font-lib
|
||||
*/
|
||||
class Encoding_Map {
|
||||
private $f;
|
||||
|
||||
function __construct($file) {
|
||||
$this->f = fopen($file, "r");
|
||||
}
|
||||
|
||||
function parse(){
|
||||
$map = array();
|
||||
|
||||
while($line = fgets($this->f)) {
|
||||
if (preg_match("/^[\!\=]([0-9A-F]{2,})\s+U\+([0-9A-F]{2})([0-9A-F]{2})\s+([^\s]+)/", $line, $matches)) {
|
||||
$unicode = (hexdec($matches[2]) << 8) + hexdec($matches[3]);
|
||||
$map[hexdec($matches[1])] = array($unicode, $matches[4]);
|
||||
}
|
||||
}
|
||||
|
||||
ksort($map);
|
||||
|
||||
return $map;
|
||||
}
|
||||
}
|
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
/**
|
||||
* @package php-font-lib
|
||||
* @link http://php-font-lib.googlecode.com/
|
||||
* @author Fabien Ménager <fabien.menager@gmail.com>
|
||||
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
|
||||
* @version $Id$
|
||||
*/
|
||||
|
||||
/**
|
||||
* Generic font file.
|
||||
*
|
||||
* @package php-font-lib
|
||||
*/
|
||||
class Font {
|
||||
static $debug = false;
|
||||
|
||||
/**
|
||||
* @param string $file The font file
|
||||
* @return Font_TrueType $file
|
||||
*/
|
||||
public static function load($file) {
|
||||
$header = file_get_contents($file, false, null, null, 4);
|
||||
$class = null;
|
||||
|
||||
switch($header) {
|
||||
case "\x00\x01\x00\x00":
|
||||
case "true":
|
||||
case "typ1":
|
||||
$class = "Font_TrueType"; break;
|
||||
|
||||
case "OTTO":
|
||||
$class = "Font_OpenType"; break;
|
||||
|
||||
case "wOFF":
|
||||
$class = "Font_WOFF"; break;
|
||||
|
||||
case "ttcf":
|
||||
$class = "Font_TrueType_Collection"; break;
|
||||
|
||||
// Unknown type or EOT
|
||||
default:
|
||||
$magicNumber = file_get_contents($file, false, null, 34, 2);
|
||||
|
||||
if ($magicNumber === "LP") {
|
||||
$class = "Font_EOT";
|
||||
}
|
||||
}
|
||||
|
||||
if ($class) {
|
||||
require_once dirname(__FILE__)."/".strtolower($class).".cls.php";
|
||||
|
||||
$obj = new $class;
|
||||
$obj->load($file);
|
||||
|
||||
return $obj;
|
||||
}
|
||||
}
|
||||
|
||||
static function d($str) {
|
||||
if (!self::$debug) return;
|
||||
echo "$str\n";
|
||||
}
|
||||
|
||||
static function UTF16ToUTF8($str) {
|
||||
return mb_convert_encoding($str, "utf-8", "utf-16");
|
||||
}
|
||||
|
||||
static function UTF8ToUTF16($str) {
|
||||
return mb_convert_encoding($str, "utf-16", "utf-8");
|
||||
}
|
||||
}
|
@@ -0,0 +1,358 @@
|
||||
<?php
|
||||
/**
|
||||
* @package php-font-lib
|
||||
* @link http://php-font-lib.googlecode.com/
|
||||
* @author Fabien Ménager <fabien.menager@gmail.com>
|
||||
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
|
||||
* @version $Id$
|
||||
*/
|
||||
|
||||
/**
|
||||
* Generic font file binary stream.
|
||||
*
|
||||
* @package php-font-lib
|
||||
*/
|
||||
class Font_Binary_Stream {
|
||||
/**
|
||||
* @var resource The file pointer
|
||||
*/
|
||||
protected $f;
|
||||
|
||||
const uint8 = 1;
|
||||
const int8 = 2;
|
||||
const uint16 = 3;
|
||||
const int16 = 4;
|
||||
const uint32 = 5;
|
||||
const int32 = 6;
|
||||
const shortFrac = 7;
|
||||
const Fixed = 8;
|
||||
const FWord = 9;
|
||||
const uFWord = 10;
|
||||
const F2Dot14 = 11;
|
||||
const longDateTime = 12;
|
||||
const char = 13;
|
||||
|
||||
private static $sizes = array(
|
||||
self::uint8 => 1,
|
||||
self::int8 => 1,
|
||||
self::uint16 => 2,
|
||||
self::int16 => 2,
|
||||
self::uint32 => 4,
|
||||
self::int32 => 4,
|
||||
self::shortFrac => 4,
|
||||
self::Fixed => 4,
|
||||
self::FWord => 2,
|
||||
self::uFWord => 2,
|
||||
self::F2Dot14 => 2,
|
||||
self::longDateTime => 8,
|
||||
self::char => 1,
|
||||
);
|
||||
|
||||
const modeRead = "rb";
|
||||
const modeWrite = "wb";
|
||||
const modeReadWrite = "rb+";
|
||||
|
||||
static function backtrace(){
|
||||
var_dump(debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS));
|
||||
}
|
||||
|
||||
/**
|
||||
* Open a font file in read mode
|
||||
*
|
||||
* @param string $filename The file name of the font to open
|
||||
*/
|
||||
public function load($filename) {
|
||||
return $this->open($filename, self::modeRead);
|
||||
}
|
||||
|
||||
/**
|
||||
* Open a font file in a chosen mode
|
||||
*
|
||||
* @param string $filename The file name of the font to open
|
||||
* @param string $mode The opening mode
|
||||
*/
|
||||
public function open($filename, $mode = self::modeRead) {
|
||||
if (!in_array($mode, array(self::modeRead, self::modeWrite, self::modeReadWrite))) {
|
||||
throw new Exception("Unkown file open mode");
|
||||
}
|
||||
|
||||
$this->f = fopen($filename, $mode);
|
||||
return $this->f != false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the internal file pointer
|
||||
*/
|
||||
public function close() {
|
||||
return fclose($this->f) != false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the internal file pointer
|
||||
*
|
||||
* @param resource $fp
|
||||
*/
|
||||
public function setFile($fp) {
|
||||
if (!is_resource($fp)) {
|
||||
throw new Exception('$fp is not a valid resource');
|
||||
}
|
||||
|
||||
$this->f = $fp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a temporary file in write mode
|
||||
*
|
||||
* @return resource the temporary file pointer resource
|
||||
*/
|
||||
public static function getTempFile($allow_memory = true) {
|
||||
$f = null;
|
||||
|
||||
if ($allow_memory) {
|
||||
// PHP 5.1+
|
||||
@fopen("php://temp", "rb+");
|
||||
}
|
||||
|
||||
if (!$f) {
|
||||
$f = fopen(tempnam(sys_get_temp_dir(), "fnt"), "rb+");
|
||||
}
|
||||
|
||||
return $f;
|
||||
}
|
||||
|
||||
/**
|
||||
* Move the internal file pinter to $offset bytes
|
||||
*
|
||||
* @param int $offset
|
||||
* @return bool True if the $offset position exists in the file
|
||||
*/
|
||||
public function seek($offset) {
|
||||
return fseek($this->f, $offset, SEEK_SET) == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gives the current position in the file
|
||||
*
|
||||
* @return int The current position
|
||||
*/
|
||||
public function pos() {
|
||||
return ftell($this->f);
|
||||
}
|
||||
|
||||
public function skip($n) {
|
||||
fseek($this->f, $n, SEEK_CUR);
|
||||
}
|
||||
|
||||
public function read($n) {
|
||||
if ($n < 1) return "";
|
||||
return fread($this->f, $n);
|
||||
}
|
||||
|
||||
public function write($data, $length = null) {
|
||||
if ($data === null || $data === "") return;
|
||||
return fwrite($this->f, $data, $length);
|
||||
}
|
||||
|
||||
public function readUInt8() {
|
||||
return ord($this->read(1));
|
||||
}
|
||||
|
||||
public function writeUInt8($data) {
|
||||
return $this->write(chr($data), 1);
|
||||
}
|
||||
|
||||
public function readInt8() {
|
||||
$v = $this->readUInt8();
|
||||
|
||||
if ($v >= 0x80) {
|
||||
$v -= 0x100;
|
||||
}
|
||||
|
||||
return $v;
|
||||
}
|
||||
|
||||
public function writeInt8($data) {
|
||||
if ($data < 0) {
|
||||
$data += 0x100;
|
||||
}
|
||||
|
||||
return $this->writeUInt8($data);
|
||||
}
|
||||
|
||||
public function readUInt16() {
|
||||
$a = unpack("nn", $this->read(2));
|
||||
return $a["n"];
|
||||
}
|
||||
|
||||
public function readUFWord(){
|
||||
return $this->readUInt16();
|
||||
}
|
||||
|
||||
public function writeUInt16($data) {
|
||||
return $this->write(pack("n", $data), 2);
|
||||
}
|
||||
|
||||
public function writeUFWord($data){
|
||||
return $this->writeUInt16($data);
|
||||
}
|
||||
|
||||
public function readInt16() {
|
||||
$v = $this->readUInt16();
|
||||
|
||||
if ($v >= 0x8000) {
|
||||
$v -= 0x10000;
|
||||
}
|
||||
|
||||
return $v;
|
||||
}
|
||||
|
||||
public function readFWord(){
|
||||
return $this->readInt16();
|
||||
}
|
||||
|
||||
public function writeInt16($data) {
|
||||
if ($data < 0) {
|
||||
$data += 0x10000;
|
||||
}
|
||||
|
||||
return $this->writeUInt16($data);
|
||||
}
|
||||
|
||||
public function writeFWord($data){
|
||||
return $this->writeInt16($data);
|
||||
}
|
||||
|
||||
public function readUInt32() {
|
||||
$a = unpack("NN", $this->read(4));
|
||||
return $a["N"];
|
||||
}
|
||||
|
||||
public function writeUInt32($data) {
|
||||
return $this->write(pack("N", $data), 4);
|
||||
}
|
||||
|
||||
public function readFixed() {
|
||||
$d = $this->readInt16();
|
||||
$d2 = $this->readUInt16();
|
||||
return round($d + $d2 / 0x10000, 4);
|
||||
}
|
||||
|
||||
public function writeFixed($data) {
|
||||
$left = floor($data);
|
||||
$right = ($data - $left) * 0x10000;
|
||||
return $this->writeInt16($left) + $this->writeUInt16($right);
|
||||
}
|
||||
|
||||
public function readLongDateTime() {
|
||||
$this->readUInt32(); // ignored
|
||||
$date = $this->readUInt32() - 2082844800;
|
||||
|
||||
return strftime("%Y-%m-%d %H:%M:%S", $date);
|
||||
}
|
||||
|
||||
public function writeLongDateTime($data) {
|
||||
$date = strtotime($data);
|
||||
$date += 2082844800;
|
||||
|
||||
return $this->writeUInt32(0) + $this->writeUInt32($date);
|
||||
}
|
||||
|
||||
public function unpack($def) {
|
||||
$d = array();
|
||||
foreach($def as $name => $type) {
|
||||
$d[$name] = $this->r($type);
|
||||
}
|
||||
return $d;
|
||||
}
|
||||
|
||||
public function pack($def, $data) {
|
||||
$bytes = 0;
|
||||
foreach($def as $name => $type) {
|
||||
$bytes += $this->w($type, $data[$name]);
|
||||
}
|
||||
return $bytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a data of type $type in the file from the current position
|
||||
*
|
||||
* @param mixed $type The data type to read
|
||||
* @return mixed The data that was read
|
||||
*/
|
||||
public function r($type) {
|
||||
switch($type) {
|
||||
case self::uint8: return $this->readUInt8();
|
||||
case self::int8: return $this->readInt8();
|
||||
case self::uint16: return $this->readUInt16();
|
||||
case self::int16: return $this->readInt16();
|
||||
case self::uint32: return $this->readUInt32();
|
||||
case self::int32: return $this->readUInt32();
|
||||
case self::shortFrac: return $this->readFixed();
|
||||
case self::Fixed: return $this->readFixed();
|
||||
case self::FWord: return $this->readInt16();
|
||||
case self::uFWord: return $this->readUInt16();
|
||||
case self::F2Dot14: return $this->readInt16();
|
||||
case self::longDateTime: return $this->readLongDateTime();
|
||||
case self::char: return $this->read(1);
|
||||
default:
|
||||
if ( is_array($type) ) {
|
||||
if ($type[0] == self::char) {
|
||||
return $this->read($type[1]);
|
||||
}
|
||||
|
||||
$ret = array();
|
||||
for($i = 0; $i < $type[1]; $i++) {
|
||||
$ret[] = $this->r($type[0]);
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write $data of type $type in the file from the current position
|
||||
*
|
||||
* @param mixed $type The data type to write
|
||||
* @param mixed $data The data to write
|
||||
* @return int The number of bytes read
|
||||
*/
|
||||
public function w($type, $data) {
|
||||
switch($type) {
|
||||
case self::uint8: return $this->writeUInt8($data);
|
||||
case self::int8: return $this->writeInt8($data);
|
||||
case self::uint16: return $this->writeUInt16($data);
|
||||
case self::int16: return $this->writeInt16($data);
|
||||
case self::uint32: return $this->writeUInt32($data);
|
||||
case self::int32: return $this->writeUInt32($data);
|
||||
case self::shortFrac: return $this->writeFixed($data);
|
||||
case self::Fixed: return $this->writeFixed($data);
|
||||
case self::FWord: return $this->writeInt16($data);
|
||||
case self::uFWord: return $this->writeUInt16($data);
|
||||
case self::F2Dot14: return $this->writeInt16($data);
|
||||
case self::longDateTime: return $this->writeLongDateTime($data);
|
||||
case self::char: return $this->write($data, 1);
|
||||
default:
|
||||
if ( is_array($type) ) {
|
||||
if ($type[0] == self::char) {
|
||||
return $this->write($data, $type[1]);
|
||||
}
|
||||
|
||||
$ret = 0;
|
||||
for($i = 0; $i < $type[1]; $i++) {
|
||||
$ret += $this->w($type[0], $data[$i]);
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a Uint32 value to string
|
||||
*
|
||||
* @param int $uint32
|
||||
* @return string The string
|
||||
*/
|
||||
public function convertUInt32ToStr($uint32) {
|
||||
return chr(($uint32 >> 24) & 0xFF).chr(($uint32 >> 16) & 0xFF).chr(($uint32 >> 8) & 0xFF).chr($uint32 & 0xFF);
|
||||
}
|
||||
}
|
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
/**
|
||||
* @package php-font-lib
|
||||
* @link http://php-font-lib.googlecode.com/
|
||||
* @author Fabien Ménager <fabien.menager@gmail.com>
|
||||
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
|
||||
* @version $Id$
|
||||
*/
|
||||
|
||||
require_once dirname(__FILE__)."/font_truetype.cls.php";
|
||||
|
||||
/**
|
||||
* EOT font file.
|
||||
*
|
||||
* @package php-font-lib
|
||||
*/
|
||||
class Font_EOT extends Font_TrueType {
|
||||
private $origF;
|
||||
private $fileOffset = 0;
|
||||
|
||||
public $header;
|
||||
|
||||
function parseHeader(){
|
||||
$this->header = $this->unpack(array(
|
||||
"EOTSize" => self::uint32,
|
||||
"FontDataSize" => self::uint32,
|
||||
"Version" => self::uint32,
|
||||
"Flags" => self::uint32,
|
||||
));
|
||||
|
||||
$this->header["FontPANOSE"] = $this->read(10);
|
||||
|
||||
$this->header += $this->unpack(array(
|
||||
"Charset" => self::uint8,
|
||||
"Italic" => self::uint8,
|
||||
"Weight" => self::uint32,
|
||||
"fsType" => self::uint16,
|
||||
"MagicNumber" => self::uint16,
|
||||
"UnicodeRange1" => self::uint32,
|
||||
"UnicodeRange2" => self::uint32,
|
||||
"UnicodeRange3" => self::uint32,
|
||||
"UnicodeRange4" => self::uint32,
|
||||
"CodePageRange1" => self::uint32,
|
||||
"CodePageRange2" => self::uint32,
|
||||
"CheckSumAdjustment" => self::uint32,
|
||||
"Reserved1" => self::uint32,
|
||||
"Reserved2" => self::uint32,
|
||||
"Reserved3" => self::uint32,
|
||||
"Reserved4" => self::uint32,
|
||||
"Padding1" => self::uint16,
|
||||
"FamilyNameSize" => self::uint16,
|
||||
));
|
||||
}
|
||||
|
||||
function parse() {
|
||||
exit("EOT not supported yet");
|
||||
}
|
||||
|
||||
public function readUInt16() {
|
||||
$a = unpack('vv', $this->read(2));
|
||||
return $a['v'];
|
||||
}
|
||||
|
||||
public function readUInt32() {
|
||||
$a = unpack('VV', $this->read(4));
|
||||
return $a['V'];
|
||||
}
|
||||
}
|
@@ -0,0 +1,109 @@
|
||||
<?php
|
||||
/**
|
||||
* @package php-font-lib
|
||||
* @link http://php-font-lib.googlecode.com/
|
||||
* @author Fabien Ménager <fabien.menager@gmail.com>
|
||||
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
|
||||
* @version $Id: font_table_glyf.cls.php 46 2012-04-02 20:22:38Z fabien.menager $
|
||||
*/
|
||||
|
||||
require_once dirname(__FILE__)."/font_glyph_outline_simple.cls.php";
|
||||
require_once dirname(__FILE__)."/font_glyph_outline_composite.cls.php";
|
||||
|
||||
/**
|
||||
* `glyf` font table.
|
||||
*
|
||||
* @package php-font-lib
|
||||
*/
|
||||
class Font_Glyph_Outline extends Font_Binary_Stream {
|
||||
const ARG_1_AND_2_ARE_WORDS = 0x0001;
|
||||
const ARGS_ARE_XY_VALUES = 0x0002;
|
||||
const ROUND_XY_TO_GRID = 0x0004;
|
||||
const WE_HAVE_A_SCALE = 0x0008;
|
||||
const MORE_COMPONENTS = 0x0020;
|
||||
const WE_HAVE_AN_X_AND_Y_SCALE = 0x0040;
|
||||
const WE_HAVE_A_TWO_BY_TWO = 0x0080;
|
||||
const WE_HAVE_INSTRUCTIONS = 0x0100;
|
||||
const USE_MY_METRICS = 0x0200;
|
||||
const OVERLAP_COMPOUND = 0x0400;
|
||||
|
||||
/**
|
||||
* @var Font_Table_glyf
|
||||
*/
|
||||
protected $table;
|
||||
|
||||
protected $offset;
|
||||
protected $size;
|
||||
|
||||
// Data
|
||||
public $numberOfContours;
|
||||
public $xMin;
|
||||
public $yMin;
|
||||
public $xMax;
|
||||
public $yMax;
|
||||
|
||||
public $raw;
|
||||
|
||||
/**
|
||||
* @return Font_Glyph_Outline
|
||||
*/
|
||||
static function init(Font_Table_glyf $table, $offset, $size) {
|
||||
$font = $table->getFont();
|
||||
$font->seek($offset);
|
||||
|
||||
/**
|
||||
* @var Font_Glyph_Outline
|
||||
*/
|
||||
$glyph;
|
||||
|
||||
if ($font->readInt16() > -1) {
|
||||
$glyph = new Font_Glyph_Outline_Simple($table, $offset, $size);
|
||||
}
|
||||
else {
|
||||
$glyph = new Font_Glyph_Outline_Composite($table, $offset, $size);
|
||||
}
|
||||
|
||||
$glyph->parse();
|
||||
return $glyph;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Font_TrueType
|
||||
*/
|
||||
function getFont() {
|
||||
return $this->table->getFont();
|
||||
}
|
||||
|
||||
function __construct(Font_Table_glyf $table, $offset = null, $size = null) {
|
||||
$this->table = $table;
|
||||
$this->offset = $offset;
|
||||
$this->size = $size;
|
||||
}
|
||||
|
||||
function parse() {
|
||||
$font = $this->getFont();
|
||||
$font->seek($this->offset);
|
||||
|
||||
if (!$this->size) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->raw = $font->read($this->size);
|
||||
}
|
||||
|
||||
function parseData(){
|
||||
$font = $this->getFont();
|
||||
$font->seek($this->offset);
|
||||
|
||||
$this->numberOfContours = $font->readInt16();
|
||||
$this->xMin = $font->readFWord();
|
||||
$this->yMin = $font->readFWord();
|
||||
$this->xMax = $font->readFWord();
|
||||
$this->yMax = $font->readFWord();
|
||||
}
|
||||
|
||||
function encode(){
|
||||
$font = $this->getFont();
|
||||
return $font->write($this->raw, strlen($this->raw));
|
||||
}
|
||||
}
|
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
/**
|
||||
* @package php-font-lib
|
||||
* @link http://php-font-lib.googlecode.com/
|
||||
* @author Fabien Ménager <fabien.menager@gmail.com>
|
||||
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
|
||||
* @version $Id: font_table_glyf.cls.php 46 2012-04-02 20:22:38Z fabien.menager $
|
||||
*/
|
||||
|
||||
/**
|
||||
* `glyf` font table.
|
||||
*
|
||||
* @package php-font-lib
|
||||
*/
|
||||
class Font_Glyph_Outline_Composite extends Font_Glyph_Outline {
|
||||
public $flags;
|
||||
public $glyphIndex;
|
||||
|
||||
function parseData(){
|
||||
parent::parseData();
|
||||
|
||||
$font = $this->getFont();
|
||||
|
||||
$this->flags = $font->readUInt16();
|
||||
$this->glyphIndex = $font->readUInt16();
|
||||
}
|
||||
}
|
@@ -0,0 +1,327 @@
|
||||
<?php
|
||||
/**
|
||||
* @package php-font-lib
|
||||
* @link http://php-font-lib.googlecode.com/
|
||||
* @author Fabien Ménager <fabien.menager@gmail.com>
|
||||
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
|
||||
* @version $Id: font_table_glyf.cls.php 46 2012-04-02 20:22:38Z fabien.menager $
|
||||
*/
|
||||
|
||||
/**
|
||||
* `glyf` font table.
|
||||
*
|
||||
* @package php-font-lib
|
||||
*/
|
||||
class Font_Glyph_Outline_Simple extends Font_Glyph_Outline {
|
||||
const ON_CURVE = 0x01;
|
||||
const X_SHORT_VECTOR = 0x02;
|
||||
const Y_SHORT_VECTOR = 0x04;
|
||||
const REPEAT = 0x08;
|
||||
const THIS_X_IS_SAME = 0x10;
|
||||
const THIS_Y_IS_SAME = 0x20;
|
||||
|
||||
public $instructions;
|
||||
public $points;
|
||||
|
||||
function parseData(){
|
||||
parent::parseData();
|
||||
|
||||
if (!$this->size) {
|
||||
return;
|
||||
}
|
||||
|
||||
$font = $this->getFont();
|
||||
|
||||
$noc = $this->numberOfContours;
|
||||
|
||||
if ($noc == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
$endPtsOfContours = $font->r(array(self::uint16, $noc));
|
||||
|
||||
$instructionLength = $font->readUInt16();
|
||||
$this->instructions = $font->r(array(self::uint8, $instructionLength));
|
||||
|
||||
$count = $endPtsOfContours[$noc-1] + 1;
|
||||
|
||||
// Flags
|
||||
$flags = array();
|
||||
for ($index = 0; $index < $count; $index++) {
|
||||
$flags[$index] = $font->readUInt8();
|
||||
|
||||
if ($flags[$index] & self::REPEAT) {
|
||||
$repeats = $font->readUInt8();
|
||||
|
||||
for ($i = 1; $i <= $repeats; $i++) {
|
||||
$flags[$index+$i] = $flags[$index];
|
||||
}
|
||||
|
||||
$index += $repeats;
|
||||
}
|
||||
}
|
||||
|
||||
$points = array();
|
||||
foreach ($flags as $i => $flag) {
|
||||
$points[$i]["onCurve"] = $flag & self::ON_CURVE;
|
||||
$points[$i]["endOfContour"] = in_array($i, $endPtsOfContours);
|
||||
}
|
||||
|
||||
// X Coords
|
||||
$x = 0;
|
||||
for($i = 0; $i < $count; $i++) {
|
||||
$flag = $flags[$i];
|
||||
|
||||
if ($flag & self::THIS_X_IS_SAME) {
|
||||
if ($flag & self::X_SHORT_VECTOR) {
|
||||
$x += $font->readUInt8();
|
||||
}
|
||||
}
|
||||
else {
|
||||
if ($flag & self::X_SHORT_VECTOR) {
|
||||
$x -= $font->readUInt8();
|
||||
}
|
||||
else {
|
||||
$x += $font->readInt16();
|
||||
}
|
||||
}
|
||||
|
||||
$points[$i]["x"] = $x;
|
||||
}
|
||||
|
||||
// Y Coords
|
||||
$y = 0;
|
||||
for($i = 0; $i < $count; $i++) {
|
||||
$flag = $flags[$i];
|
||||
|
||||
if ($flag & self::THIS_Y_IS_SAME) {
|
||||
if ($flag & self::Y_SHORT_VECTOR) {
|
||||
$y += $font->readUInt8();
|
||||
}
|
||||
}
|
||||
else {
|
||||
if ($flag & self::Y_SHORT_VECTOR) {
|
||||
$y -= $font->readUInt8();
|
||||
}
|
||||
else {
|
||||
$y += $font->readInt16();
|
||||
}
|
||||
}
|
||||
|
||||
$points[$i]["y"] = $y;
|
||||
}
|
||||
|
||||
$this->points = $points;
|
||||
}
|
||||
|
||||
public function splitSVGPath($path) {
|
||||
preg_match_all('/([a-z])|(-?\d+(?:\.\d+)?)/i', $path, $matches, PREG_PATTERN_ORDER);
|
||||
return $matches[0];
|
||||
}
|
||||
|
||||
public function makePoints($path) {
|
||||
$path = $this->splitSVGPath($path);
|
||||
$l = count($path);
|
||||
$i = 0;
|
||||
|
||||
$points = array();
|
||||
|
||||
while($i < $l) {
|
||||
switch($path[$i]) {
|
||||
// moveTo
|
||||
case "M":
|
||||
$points[] = array(
|
||||
"onCurve" => true,
|
||||
"x" => $path[++$i],
|
||||
"y" => $path[++$i],
|
||||
"endOfContour" => false,
|
||||
);
|
||||
break;
|
||||
|
||||
// lineTo
|
||||
case "L":
|
||||
$points[] = array(
|
||||
"onCurve" => true,
|
||||
"x" => $path[++$i],
|
||||
"y" => $path[++$i],
|
||||
"endOfContour" => false,
|
||||
);
|
||||
break;
|
||||
|
||||
// quadraticCurveTo
|
||||
case "Q":
|
||||
$points[] = array(
|
||||
"onCurve" => false,
|
||||
"x" => $path[++$i],
|
||||
"y" => $path[++$i],
|
||||
"endOfContour" => false,
|
||||
);
|
||||
$points[] = array(
|
||||
"onCurve" => true,
|
||||
"x" => $path[++$i],
|
||||
"y" => $path[++$i],
|
||||
"endOfContour" => false,
|
||||
);
|
||||
break;
|
||||
|
||||
// closePath
|
||||
case "z":
|
||||
$points[count($points)-1]["endOfContour"] = true;
|
||||
|
||||
default:
|
||||
$i++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $points;
|
||||
}
|
||||
|
||||
function encode(){
|
||||
if (empty($this->points)) {
|
||||
return parent::encode();
|
||||
}
|
||||
|
||||
return $this->size = $this->encodePoints($this->points);
|
||||
}
|
||||
|
||||
public function encodePoints($points) {
|
||||
$endPtsOfContours = array();
|
||||
$flags = array();
|
||||
$coords_x = array();
|
||||
$coords_y = array();
|
||||
|
||||
$last_x = 0;
|
||||
$last_y = 0;
|
||||
$xMin = $yMin = 0xFFFF;
|
||||
$xMax = $yMax = -0xFFFF;
|
||||
foreach($points as $i => $point) {
|
||||
$flag = 0;
|
||||
if ($point["onCurve"]) {
|
||||
$flag |= self::ON_CURVE;
|
||||
}
|
||||
|
||||
if ($point["endOfContour"]) {
|
||||
$endPtsOfContours[] = $i;
|
||||
}
|
||||
|
||||
// Simplified, we could do some optimizations
|
||||
if ($point["x"] == $last_x) {
|
||||
$flag |= self::THIS_X_IS_SAME;
|
||||
}
|
||||
else {
|
||||
$x = intval($point["x"]);
|
||||
$xMin = min($x, $xMin);
|
||||
$xMax = max($x, $xMax);
|
||||
$coords_x[] = $x-$last_x; // int16
|
||||
}
|
||||
|
||||
// Simplified, we could do some optimizations
|
||||
if ($point["y"] == $last_y) {
|
||||
$flag |= self::THIS_Y_IS_SAME;
|
||||
}
|
||||
else {
|
||||
$y = intval($point["y"]);
|
||||
$yMin = min($y, $yMin);
|
||||
$yMax = max($y, $yMax);
|
||||
$coords_y[] = $y-$last_y; // int16
|
||||
}
|
||||
|
||||
$flags[] = $flag;
|
||||
$last_x = $point["x"];
|
||||
$last_y = $point["y"];
|
||||
}
|
||||
|
||||
$font = $this->getFont();
|
||||
|
||||
$l = 0;
|
||||
$l += $font->writeInt16(count($endPtsOfContours)); // endPtsOfContours
|
||||
$l += $font->writeFWord(isset($this->xMin) ? $this->xMin : $xMin); // xMin
|
||||
$l += $font->writeFWord(isset($this->yMin) ? $this->yMin : $yMin); // yMin
|
||||
$l += $font->writeFWord(isset($this->xMax) ? $this->xMax : $xMax); // xMax
|
||||
$l += $font->writeFWord(isset($this->yMax) ? $this->yMax : $yMax); // yMax
|
||||
|
||||
// Simple glyf
|
||||
$l += $font->w(array(self::uint16, count($endPtsOfContours)), $endPtsOfContours); // endPtsOfContours
|
||||
$l += $font->writeUInt16(0); // instructionLength
|
||||
$l += $font->w(array(self::uint8, count($flags)), $flags); // flags
|
||||
$l += $font->w(array(self::int16, count($coords_x)), $coords_x); // xCoordinates
|
||||
$l += $font->w(array(self::int16, count($coords_y)), $coords_y); // yCoordinates
|
||||
return $l;
|
||||
}
|
||||
|
||||
public function getSVGContours($points = null){
|
||||
$path = "";
|
||||
|
||||
if (!$points) {
|
||||
$points = $this->points;
|
||||
}
|
||||
|
||||
$length = count($points);
|
||||
$firstIndex = 0;
|
||||
$count = 0;
|
||||
|
||||
for($i = 0; $i < $length; $i++) {
|
||||
$count++;
|
||||
|
||||
if ($points[$i]["endOfContour"]) {
|
||||
$path .= $this->getSVGPath($points, $firstIndex, $count);
|
||||
$firstIndex = $i + 1;
|
||||
$count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return $path;
|
||||
}
|
||||
|
||||
protected function getSVGPath($points, $startIndex, $count) {
|
||||
$offset = 0;
|
||||
$path = "";
|
||||
|
||||
while($offset < $count) {
|
||||
$point = $points[ $startIndex + $offset %$count ];
|
||||
$point_p1 = $points[ $startIndex + ($offset+1)%$count ];
|
||||
|
||||
if($offset == 0) {
|
||||
$path .= "M{$point['x']},{$point['y']} ";
|
||||
}
|
||||
|
||||
if ($point["onCurve"]) {
|
||||
if ($point_p1["onCurve"]) {
|
||||
$path .= "L{$point_p1['x']},{$point_p1['y']} ";
|
||||
$offset++;
|
||||
}
|
||||
else {
|
||||
$point_p2 = $points[ $startIndex + ($offset+2)%$count ];
|
||||
|
||||
if ($point_p2["onCurve"]){
|
||||
$path .= "Q{$point_p1['x']},{$point_p1['y']},{$point_p2['x']},{$point_p2['y']} ";
|
||||
}
|
||||
else {
|
||||
$path .= "Q{$point_p1['x']},{$point_p1['y']},".$this->midValue($point_p1['x'], $point_p2['x']).",".$this->midValue($point_p1['y'], $point_p2['y'])." ";
|
||||
}
|
||||
|
||||
$offset += 2;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if ($point_p1["onCurve"]) {
|
||||
$path .= "Q{$point['x']},{$point['y']},{$point_p1['x']},{$point_p1['y']} ";
|
||||
}
|
||||
else {
|
||||
$path .= "Q{$point['x']},{$point['y']},".$this->midValue($point['x'], $point_p1['x']).",".$this->midValue($point['y'], $point_p1['y'])." ";
|
||||
}
|
||||
|
||||
$offset++;
|
||||
}
|
||||
}
|
||||
|
||||
$path .= "z ";
|
||||
|
||||
return $path;
|
||||
}
|
||||
|
||||
function midValue($a, $b){
|
||||
return $a + ($b - $a)/2;
|
||||
}
|
||||
}
|
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
/**
|
||||
* @package php-font-lib
|
||||
* @link http://php-font-lib.googlecode.com/
|
||||
* @author Fabien Ménager <fabien.menager@gmail.com>
|
||||
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
|
||||
* @version $Id$
|
||||
*/
|
||||
|
||||
/**
|
||||
* Font header container.
|
||||
*
|
||||
* @package php-font-lib
|
||||
*/
|
||||
abstract class Font_Header extends Font_Binary_Stream {
|
||||
/**
|
||||
* @var Font_TrueType
|
||||
*/
|
||||
protected $font;
|
||||
protected $def = array();
|
||||
|
||||
public $data;
|
||||
|
||||
public function __construct(Font_TrueType $font) {
|
||||
$this->font = $font;
|
||||
}
|
||||
|
||||
public function encode(){
|
||||
return $this->font->pack($this->def, $this->data);
|
||||
}
|
||||
|
||||
public function parse(){
|
||||
$this->data = $this->font->unpack($this->def);
|
||||
}
|
||||
}
|
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
/**
|
||||
* @package php-font-lib
|
||||
* @link http://php-font-lib.googlecode.com/
|
||||
* @author Fabien Ménager <fabien.menager@gmail.com>
|
||||
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
|
||||
* @version $Id$
|
||||
*/
|
||||
|
||||
require_once dirname(__FILE__)."/font_truetype.cls.php";
|
||||
require_once dirname(__FILE__)."/font_opentype_table_directory_entry.cls.php";
|
||||
|
||||
/**
|
||||
* Open Type font, the same as a TrueType one.
|
||||
*
|
||||
* @package php-font-lib
|
||||
*/
|
||||
class Font_OpenType extends Font_TrueType {
|
||||
//
|
||||
}
|
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
/**
|
||||
* @package php-font-lib
|
||||
* @link http://php-font-lib.googlecode.com/
|
||||
* @author Fabien Ménager <fabien.menager@gmail.com>
|
||||
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
|
||||
* @version $Id$
|
||||
*/
|
||||
|
||||
require_once dirname(__FILE__)."/font_truetype_table_directory_entry.cls.php";
|
||||
|
||||
/**
|
||||
* Open Type Table directory entry, the same as a TrueType one.
|
||||
*
|
||||
* @package php-font-lib
|
||||
*/
|
||||
class Font_OpenType_Table_Directory_Entry extends Font_TrueType_Table_Directory_Entry {
|
||||
|
||||
}
|
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
/**
|
||||
* @package php-font-lib
|
||||
* @link http://php-font-lib.googlecode.com/
|
||||
* @author Fabien Ménager <fabien.menager@gmail.com>
|
||||
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
|
||||
* @version $Id$
|
||||
*/
|
||||
|
||||
/**
|
||||
* Generic font table.
|
||||
*
|
||||
* @package php-font-lib
|
||||
*/
|
||||
class Font_Table extends Font_Binary_Stream {
|
||||
/**
|
||||
* @var Font_Table_Directory_Entry
|
||||
*/
|
||||
protected $entry;
|
||||
protected $def = array();
|
||||
|
||||
public $data;
|
||||
|
||||
final public function __construct(Font_Table_Directory_Entry $entry) {
|
||||
$this->entry = $entry;
|
||||
$entry->setTable($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Font_TrueType
|
||||
*/
|
||||
public function getFont(){
|
||||
return $this->entry->getFont();
|
||||
}
|
||||
|
||||
protected function _encode(){
|
||||
if (empty($this->data)) {
|
||||
Font::d(" >> Table is empty");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return $this->getFont()->pack($this->def, $this->data);
|
||||
}
|
||||
|
||||
protected function _parse(){
|
||||
$this->data = $this->getFont()->unpack($this->def);
|
||||
}
|
||||
|
||||
protected function _parseRaw(){
|
||||
$this->data = $this->getFont()->read($this->entry->length);
|
||||
}
|
||||
|
||||
protected function _encodeRaw(){
|
||||
return $this->getFont()->write($this->data, $this->entry->length);
|
||||
}
|
||||
|
||||
public function toHTML(){
|
||||
return "<pre>".var_export($this->data, true)."</pre>";
|
||||
}
|
||||
|
||||
final public function encode(){
|
||||
$this->entry->startWrite();
|
||||
|
||||
if (false && empty($this->def)) {
|
||||
$length = $this->_encodeRaw();
|
||||
}
|
||||
else {
|
||||
$length = $this->_encode();
|
||||
}
|
||||
|
||||
$this->entry->endWrite();
|
||||
|
||||
return $length;
|
||||
}
|
||||
|
||||
final public function parse(){
|
||||
$this->entry->startRead();
|
||||
|
||||
if (false && empty($this->def)) {
|
||||
$this->_parseRaw();
|
||||
}
|
||||
else {
|
||||
$this->_parse();
|
||||
}
|
||||
|
||||
$this->entry->endRead();
|
||||
}
|
||||
}
|
@@ -0,0 +1,240 @@
|
||||
<?php
|
||||
/**
|
||||
* @package php-font-lib
|
||||
* @link http://php-font-lib.googlecode.com/
|
||||
* @author Fabien Ménager <fabien.menager@gmail.com>
|
||||
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
|
||||
* @version $Id$
|
||||
*/
|
||||
|
||||
/**
|
||||
* `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();
|
||||
|
||||
$segments = array();
|
||||
|
||||
$i = count($segments)-1;
|
||||
$j = $i+1;
|
||||
$prevCode = 0;
|
||||
$glyphIndexArray = array();
|
||||
|
||||
foreach($subset as $code => $gid) {
|
||||
if ($prevCode + 1 != $code) {
|
||||
$i++;
|
||||
$segments[$i] = array();
|
||||
}
|
||||
|
||||
$segments[$i][] = array($code, $j);
|
||||
|
||||
$glyphIndexArray[] = $code;
|
||||
$j++;
|
||||
$prevCode = $code;
|
||||
}
|
||||
|
||||
$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" => $glyphIndexArray,
|
||||
)
|
||||
);
|
||||
|
||||
$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), $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;
|
||||
}
|
||||
}
|
@@ -0,0 +1,125 @@
|
||||
<?php
|
||||
/**
|
||||
* @package php-font-lib
|
||||
* @link http://php-font-lib.googlecode.com/
|
||||
* @author Fabien Ménager <fabien.menager@gmail.com>
|
||||
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
|
||||
* @version $Id$
|
||||
*/
|
||||
|
||||
/**
|
||||
* Generic Font table directory entry.
|
||||
*
|
||||
* @package php-font-lib
|
||||
*/
|
||||
class Font_Table_Directory_Entry extends Font_Binary_Stream {
|
||||
/**
|
||||
* @var Font_TrueType
|
||||
*/
|
||||
protected $font;
|
||||
|
||||
/**
|
||||
* @var Font_Table
|
||||
*/
|
||||
protected $font_table;
|
||||
|
||||
public $entryLength = 4;
|
||||
|
||||
var $tag;
|
||||
var $checksum;
|
||||
var $offset;
|
||||
var $length;
|
||||
|
||||
protected $origF;
|
||||
|
||||
static function computeChecksum($data){
|
||||
$len = strlen($data);
|
||||
$mod = $len % 4;
|
||||
|
||||
if ($mod) {
|
||||
$data = str_pad($data, $len + (4 - $mod), "\0");
|
||||
}
|
||||
|
||||
$len = strlen($data);
|
||||
|
||||
$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;
|
||||
}
|
||||
|
||||
function __construct(Font_TrueType $font) {
|
||||
$this->font = $font;
|
||||
$this->f = $font->f;
|
||||
}
|
||||
|
||||
function parse() {
|
||||
$this->tag = $this->font->read(4);
|
||||
}
|
||||
|
||||
function open($filename, $mode = self::modeRead) {
|
||||
// void
|
||||
}
|
||||
|
||||
function setTable(Font_Table $font_table) {
|
||||
$this->font_table = $font_table;
|
||||
}
|
||||
|
||||
function encode($entry_offset){
|
||||
Font::d("\n==== $this->tag ====");
|
||||
//Font::d("Entry offset = $entry_offset");
|
||||
|
||||
$data = $this->font_table;
|
||||
$font = $this->font;
|
||||
|
||||
$table_offset = $font->pos();
|
||||
$this->offset = $table_offset;
|
||||
$table_length = $data->encode();
|
||||
|
||||
$font->seek($table_offset);
|
||||
$table_data = $font->read($table_length);
|
||||
|
||||
$font->seek($entry_offset);
|
||||
|
||||
$font->write($this->tag, 4);
|
||||
$font->writeUInt32(self::computeChecksum($table_data));
|
||||
$font->writeUInt32($table_offset);
|
||||
$font->writeUInt32($table_length);
|
||||
|
||||
Font::d("Bytes written = $table_length");
|
||||
|
||||
$font->seek($table_offset + $table_length);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Font_TrueType
|
||||
*/
|
||||
function getFont() {
|
||||
return $this->font;
|
||||
}
|
||||
|
||||
function startRead() {
|
||||
$this->font->seek($this->offset);
|
||||
}
|
||||
|
||||
function endRead() {
|
||||
//
|
||||
}
|
||||
|
||||
function startWrite() {
|
||||
$this->font->seek($this->offset);
|
||||
}
|
||||
|
||||
function endWrite() {
|
||||
//
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,138 @@
|
||||
<?php
|
||||
/**
|
||||
* @package php-font-lib
|
||||
* @link http://php-font-lib.googlecode.com/
|
||||
* @author Fabien Ménager <fabien.menager@gmail.com>
|
||||
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
|
||||
* @version $Id$
|
||||
*/
|
||||
|
||||
require_once dirname(__FILE__)."/font_glyph_outline.cls.php";
|
||||
|
||||
/**
|
||||
* `glyf` font table.
|
||||
*
|
||||
* @package php-font-lib
|
||||
*/
|
||||
class Font_Table_glyf extends Font_Table {
|
||||
protected function _parse(){
|
||||
$font = $this->getFont();
|
||||
$offset = $font->pos();
|
||||
|
||||
$loca = $font->getData("loca");
|
||||
$real_loca = array_slice($loca, 0, -1); // Not the last dummy loca entry
|
||||
|
||||
$data = array();
|
||||
|
||||
foreach($real_loca as $gid => $location) {
|
||||
$_offset = $offset + $loca[$gid];
|
||||
$_size = $loca[$gid+1] - $loca[$gid];
|
||||
$data[$gid] = Font_Glyph_Outline::init($this, $_offset, $_size);
|
||||
}
|
||||
|
||||
$this->data = $data;
|
||||
}
|
||||
|
||||
public function toHTML(){
|
||||
$max = 160;
|
||||
$font = $this->getFont();
|
||||
|
||||
$head = $font->getData("head");
|
||||
$head_json = json_encode($head);
|
||||
|
||||
$os2 = $font->getData("OS/2");
|
||||
$os2_json = json_encode($os2);
|
||||
|
||||
$hmtx = $font->getData("hmtx");
|
||||
$hmtx_json = json_encode($hmtx);
|
||||
|
||||
$names = $font->getData("post", "names");
|
||||
$glyphIndexArray = array_flip($font->getUnicodeCharMap());
|
||||
|
||||
$width = (abs($head["xMin"]) + $head["xMax"]);
|
||||
$height = (abs($head["yMin"]) + $head["yMax"]);
|
||||
|
||||
$ratio = 1;
|
||||
if ($width > $max || $height > $max) {
|
||||
$ratio = max($width, $height) / $max;
|
||||
$width = round($width/$ratio);
|
||||
$height = round($height/$ratio);
|
||||
}
|
||||
|
||||
$n = 100;
|
||||
|
||||
$s = "<h3>Only the first $n simple glyphs are shown</h3>
|
||||
<script>
|
||||
Glyph.ratio = $ratio;
|
||||
Glyph.head = $head_json;
|
||||
Glyph.os2 = $os2_json;
|
||||
Glyph.hmtx = $hmtx_json;
|
||||
</script>";
|
||||
|
||||
foreach($this->data as $g => $glyph) {
|
||||
if (!$glyph instanceof Font_Glyph_Outline_Simple) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($n-- <= 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
$glyph->parseData();
|
||||
|
||||
$shape = array(
|
||||
"SVGContours" => $glyph->getSVGContours(),
|
||||
"xMin" => $glyph->xMin,
|
||||
"yMin" => $glyph->yMin,
|
||||
"xMax" => $glyph->xMax,
|
||||
"yMax" => $glyph->yMax,
|
||||
);
|
||||
$shape_json = json_encode($shape);
|
||||
|
||||
$char = isset($glyphIndexArray[$g]) ? $glyphIndexArray[$g] : 0;
|
||||
$name = isset($names[$g]) ? $names[$g] : sprintf("uni%04x", $char);
|
||||
$char = $char ? "&#{$glyphIndexArray[$g]};" : "";
|
||||
|
||||
$s .= "<div class='glyph-view'>
|
||||
<span class='glyph-id'>$g</span>
|
||||
<span class='char'>$char</span>
|
||||
<span class='char-name'>$name</span>
|
||||
<br />
|
||||
<canvas width='$width' height='$height' id='glyph-$g'></canvas>
|
||||
</div>
|
||||
<script>Glyph.draw(\$('#glyph-$g'), $shape_json, $g)</script>";
|
||||
}
|
||||
|
||||
return $s;
|
||||
}
|
||||
|
||||
|
||||
protected function _encode() {
|
||||
$font = $this->getFont();
|
||||
$subset = $font->getSubset();
|
||||
$compoundGlyphOffsets = $font->compound_glyph_offsets;
|
||||
$data = $this->data;
|
||||
|
||||
$loca = array();
|
||||
|
||||
$length = 0;
|
||||
foreach($subset as $gid) {
|
||||
$loca[] = $length;
|
||||
$glyph = $data[$gid];
|
||||
|
||||
if ($glyph instanceof Font_Glyph_Outline_Composite && isset($compoundGlyphOffsets[$gid])) {
|
||||
$offsets = $compoundGlyphOffsets[$gid];
|
||||
foreach($offsets as $offset => $newGid) {
|
||||
list($glyph->raw[$offset], $glyph->raw[$offset+1]) = pack("n", $newGid);
|
||||
}
|
||||
}
|
||||
|
||||
$length += $glyph->encode();
|
||||
}
|
||||
|
||||
$loca[] = $length; // dummy loca
|
||||
$font->getTableObject("loca")->data = $loca;
|
||||
|
||||
return $length;
|
||||
}
|
||||
}
|
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
/**
|
||||
* @package php-font-lib
|
||||
* @link http://php-font-lib.googlecode.com/
|
||||
* @author Fabien Ménager <fabien.menager@gmail.com>
|
||||
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
|
||||
* @version $Id$
|
||||
*/
|
||||
|
||||
/**
|
||||
* `head` font table.
|
||||
*
|
||||
* @package php-font-lib
|
||||
*/
|
||||
class Font_Table_head extends Font_Table {
|
||||
protected $def = array(
|
||||
"tableVersion" => self::Fixed,
|
||||
"fontRevision" => self::Fixed,
|
||||
"checkSumAdjustment" => self::uint32,
|
||||
"magicNumber" => self::uint32,
|
||||
"flags" => self::uint16,
|
||||
"unitsPerEm" => self::uint16,
|
||||
"created" => self::longDateTime,
|
||||
"modified" => self::longDateTime,
|
||||
"xMin" => self::FWord,
|
||||
"yMin" => self::FWord,
|
||||
"xMax" => self::FWord,
|
||||
"yMax" => self::FWord,
|
||||
"macStyle" => self::uint16,
|
||||
"lowestRecPPEM" => self::uint16,
|
||||
"fontDirectionHint" => self::int16,
|
||||
"indexToLocFormat" => self::int16,
|
||||
"glyphDataFormat" => self::int16,
|
||||
);
|
||||
|
||||
protected function _parse(){
|
||||
parent::_parse();
|
||||
|
||||
if($this->data["magicNumber"] != 0x5F0F3CF5) {
|
||||
throw new Exception("Incorrect magic number (".dechex($this->data["magicNumber"]).")");
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
/**
|
||||
* @package php-font-lib
|
||||
* @link http://php-font-lib.googlecode.com/
|
||||
* @author Fabien Ménager <fabien.menager@gmail.com>
|
||||
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
|
||||
* @version $Id$
|
||||
*/
|
||||
|
||||
/**
|
||||
* `hhea` font table.
|
||||
*
|
||||
* @package php-font-lib
|
||||
*/
|
||||
class Font_Table_hhea extends Font_Table {
|
||||
protected $def = array(
|
||||
"version" => self::Fixed,
|
||||
"ascent" => self::FWord,
|
||||
"descent" => self::FWord,
|
||||
"lineGap" => self::FWord,
|
||||
"advanceWidthMax" => self::uFWord,
|
||||
"minLeftSideBearing" => self::FWord,
|
||||
"minRightSideBearing" => self::FWord,
|
||||
"xMaxExtent" => self::FWord,
|
||||
"caretSlopeRise" => self::int16,
|
||||
"caretSlopeRun" => self::int16,
|
||||
"caretOffset" => self::FWord,
|
||||
self::int16,
|
||||
self::int16,
|
||||
self::int16,
|
||||
self::int16,
|
||||
"metricDataFormat" => self::int16,
|
||||
"numOfLongHorMetrics" => self::uint16,
|
||||
);
|
||||
|
||||
function _encode(){
|
||||
$font = $this->getFont();
|
||||
$this->data["numOfLongHorMetrics"] = count($font->getSubset());
|
||||
|
||||
return parent::_encode();
|
||||
}
|
||||
}
|
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
/**
|
||||
* @package php-font-lib
|
||||
* @link http://php-font-lib.googlecode.com/
|
||||
* @author Fabien Ménager <fabien.menager@gmail.com>
|
||||
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
|
||||
* @version $Id$
|
||||
*/
|
||||
|
||||
/**
|
||||
* `hmtx` font table.
|
||||
*
|
||||
* @package php-font-lib
|
||||
*/
|
||||
class Font_Table_hmtx extends Font_Table {
|
||||
protected function _parse(){
|
||||
$font = $this->getFont();
|
||||
$offset = $font->pos();
|
||||
|
||||
$numOfLongHorMetrics = $font->getData("hhea", "numOfLongHorMetrics");
|
||||
$numGlyphs = $font->getData("maxp", "numGlyphs");
|
||||
|
||||
$font->seek($offset);
|
||||
|
||||
$data = array();
|
||||
for($gid = 0; $gid < $numOfLongHorMetrics; $gid++) {
|
||||
$advanceWidth = $font->readUInt16();
|
||||
$leftSideBearing = $font->readUInt16();
|
||||
$data[$gid] = array($advanceWidth, $leftSideBearing);
|
||||
}
|
||||
|
||||
if($numOfLongHorMetrics < $numGlyphs){
|
||||
$lastWidth = end($data);
|
||||
$data = array_pad($data, $numGlyphs, $lastWidth);
|
||||
}
|
||||
|
||||
$this->data = $data;
|
||||
}
|
||||
|
||||
protected function _encode() {
|
||||
$font = $this->getFont();
|
||||
$subset = $font->getSubset();
|
||||
$data = $this->data;
|
||||
|
||||
$length = 0;
|
||||
|
||||
foreach($subset as $gid) {
|
||||
$length += $font->writeUInt16($data[$gid][0]);
|
||||
$length += $font->writeUInt16($data[$gid][1]);
|
||||
}
|
||||
|
||||
return $length;
|
||||
}
|
||||
}
|
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
/**
|
||||
* @package php-font-lib
|
||||
* @link http://php-font-lib.googlecode.com/
|
||||
* @author Fabien Ménager <fabien.menager@gmail.com>
|
||||
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
|
||||
* @version $Id$
|
||||
*/
|
||||
|
||||
/**
|
||||
* `kern` font table.
|
||||
*
|
||||
* @package php-font-lib
|
||||
*/
|
||||
class Font_Table_kern extends Font_Table {
|
||||
protected function _parse(){
|
||||
$font = $this->getFont();
|
||||
|
||||
$data = $font->unpack(array(
|
||||
"version" => self::uint16,
|
||||
"nTables" => self::uint16,
|
||||
|
||||
// only the first subtable will be parsed
|
||||
"subtableVersion" => self::uint16,
|
||||
"length" => self::uint16,
|
||||
"coverage" => self::uint16,
|
||||
));
|
||||
|
||||
$data["format"] = ($data["coverage"] >> 8);
|
||||
|
||||
$subtable = array();
|
||||
|
||||
switch($data["format"]) {
|
||||
case 0:
|
||||
$subtable = $font->unpack(array(
|
||||
"nPairs" => self::uint16,
|
||||
"searchRange" => self::uint16,
|
||||
"entrySelector" => self::uint16,
|
||||
"rangeShift" => self::uint16,
|
||||
));
|
||||
|
||||
$pairs = array();
|
||||
$tree = array();
|
||||
|
||||
for ($i = 0; $i < $subtable["nPairs"]; $i++) {
|
||||
$left = $font->readUInt16();
|
||||
$right = $font->readUInt16();
|
||||
$value = $font->readInt16();
|
||||
|
||||
$pairs[] = array(
|
||||
"left" => $left,
|
||||
"right" => $right,
|
||||
"value" => $value,
|
||||
);
|
||||
|
||||
$tree[$left][$right] = $value;
|
||||
}
|
||||
|
||||
//$subtable["pairs"] = $pairs;
|
||||
$subtable["tree"] = $tree;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
break;
|
||||
}
|
||||
|
||||
$data["subtable"] = $subtable;
|
||||
|
||||
$this->data = $data;
|
||||
}
|
||||
}
|
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
/**
|
||||
* @package php-font-lib
|
||||
* @link http://php-font-lib.googlecode.com/
|
||||
* @author Fabien Ménager <fabien.menager@gmail.com>
|
||||
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
|
||||
* @version $Id$
|
||||
*/
|
||||
|
||||
/**
|
||||
* `loca` font table.
|
||||
*
|
||||
* @package php-font-lib
|
||||
*/
|
||||
class Font_Table_loca extends Font_Table {
|
||||
protected function _parse(){
|
||||
$font = $this->getFont();
|
||||
$offset = $font->pos();
|
||||
|
||||
$indexToLocFormat = $font->getData("head", "indexToLocFormat");
|
||||
$numGlyphs = $font->getData("maxp", "numGlyphs");
|
||||
|
||||
$font->seek($offset);
|
||||
|
||||
$data = array();
|
||||
|
||||
// 2 bytes
|
||||
if ($indexToLocFormat == 0) {
|
||||
$d = $font->read(($numGlyphs + 1) * 2);
|
||||
$loc = unpack("n*", $d);
|
||||
|
||||
for ($i = 0; $i <= $numGlyphs; $i++) {
|
||||
$data[] = $loc[$i+1] * 2;
|
||||
}
|
||||
}
|
||||
|
||||
// 4 bytes
|
||||
else if ($indexToLocFormat == 1) {
|
||||
$d = $font->read(($numGlyphs + 1) * 4);
|
||||
$loc = unpack("N*", $d);
|
||||
|
||||
for ($i = 0; $i <= $numGlyphs; $i++) {
|
||||
$data[] = $loc[$i+1];
|
||||
}
|
||||
}
|
||||
|
||||
$this->data = $data;
|
||||
}
|
||||
|
||||
function _encode(){
|
||||
$font = $this->getFont();
|
||||
$data = $this->data;
|
||||
|
||||
$indexToLocFormat = $font->getData("head", "indexToLocFormat");
|
||||
$numGlyphs = $font->getData("maxp", "numGlyphs");
|
||||
$length = 0;
|
||||
|
||||
// 2 bytes
|
||||
if ($indexToLocFormat == 0) {
|
||||
for ($i = 0; $i <= $numGlyphs; $i++) {
|
||||
$length += $font->writeUInt16($data[$i] / 2);
|
||||
}
|
||||
}
|
||||
|
||||
// 4 bytes
|
||||
else if ($indexToLocFormat == 1) {
|
||||
for ($i = 0; $i <= $numGlyphs; $i++) {
|
||||
$length += $font->writeUInt32($data[$i]);
|
||||
}
|
||||
}
|
||||
|
||||
return $length;
|
||||
}
|
||||
}
|
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
/**
|
||||
* @package php-font-lib
|
||||
* @link http://php-font-lib.googlecode.com/
|
||||
* @author Fabien Ménager <fabien.menager@gmail.com>
|
||||
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
|
||||
* @version $Id$
|
||||
*/
|
||||
|
||||
/**
|
||||
* `maxp` font table.
|
||||
*
|
||||
* @package php-font-lib
|
||||
*/
|
||||
class Font_Table_maxp extends Font_Table {
|
||||
protected $def = array(
|
||||
"version" => self::Fixed,
|
||||
"numGlyphs" => self::uint16,
|
||||
"maxPoints" => self::uint16,
|
||||
"maxContours" => self::uint16,
|
||||
"maxComponentPoints" => self::uint16,
|
||||
"maxComponentContours" => self::uint16,
|
||||
"maxZones" => self::uint16,
|
||||
"maxTwilightPoints" => self::uint16,
|
||||
"maxStorage" => self::uint16,
|
||||
"maxFunctionDefs" => self::uint16,
|
||||
"maxInstructionDefs" => self::uint16,
|
||||
"maxStackElements" => self::uint16,
|
||||
"maxSizeOfInstructions" => self::uint16,
|
||||
"maxComponentElements" => self::uint16,
|
||||
"maxComponentDepth" => self::uint16,
|
||||
);
|
||||
|
||||
function _encode(){
|
||||
$font = $this->getFont();
|
||||
$this->data["numGlyphs"] = count($font->getSubset());
|
||||
|
||||
return parent::_encode();
|
||||
}
|
||||
}
|
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
/**
|
||||
* @package php-font-lib
|
||||
* @link http://php-font-lib.googlecode.com/
|
||||
* @author Fabien Ménager <fabien.menager@gmail.com>
|
||||
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
|
||||
* @version $Id$
|
||||
*/
|
||||
|
||||
require_once dirname(__FILE__)."/font_table_name_record.cls.php";
|
||||
|
||||
/**
|
||||
* `name` font table.
|
||||
*
|
||||
* @package php-font-lib
|
||||
*/
|
||||
class Font_Table_name extends Font_Table {
|
||||
private static $header_format = array(
|
||||
"format" => self::uint16,
|
||||
"count" => self::uint16,
|
||||
"stringOffset" => self::uint16,
|
||||
);
|
||||
|
||||
protected function _parse(){
|
||||
$font = $this->getFont();
|
||||
|
||||
$tableOffset = $font->pos();
|
||||
|
||||
$data = $font->unpack(self::$header_format);
|
||||
|
||||
$records = array();
|
||||
for($i = 0; $i < $data["count"]; $i++) {
|
||||
$record = new Font_Table_name_Record();
|
||||
$record_data = $font->unpack(Font_Table_name_Record::$format);
|
||||
$record->map($record_data);
|
||||
|
||||
$records[] = $record;
|
||||
}
|
||||
|
||||
$names = array();
|
||||
foreach($records as $record) {
|
||||
$font->seek($tableOffset + $data["stringOffset"] + $record->offset);
|
||||
$s = $font->read($record->length);
|
||||
$record->string = Font::UTF16ToUTF8($s);
|
||||
$names[$record->nameID] = $record;
|
||||
}
|
||||
|
||||
$data["records"] = $names;
|
||||
|
||||
$this->data = $data;
|
||||
}
|
||||
|
||||
protected function _encode(){
|
||||
$font = $this->getFont();
|
||||
|
||||
$records = $this->data["records"];
|
||||
$count_records = count($records);
|
||||
|
||||
$this->data["count"] = $count_records;
|
||||
$this->data["stringOffset"] = 6 + $count_records * 12; // 6 => uint16 * 3, 12 => sizeof self::$record_format
|
||||
|
||||
$length = $font->pack(self::$header_format, $this->data);
|
||||
|
||||
$offset = 0;
|
||||
foreach($records as $record) {
|
||||
$record->length = mb_strlen($record->getUTF16(), "8bit");
|
||||
$record->offset = $offset;
|
||||
$offset += $record->length;
|
||||
$length += $font->pack(Font_Table_name_Record::$format, (array)$record);
|
||||
}
|
||||
|
||||
foreach($records as $record) {
|
||||
$str = $record->getUTF16();
|
||||
$length += $font->write($str, mb_strlen($str, "8bit"));
|
||||
}
|
||||
|
||||
return $length;
|
||||
}
|
||||
}
|
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
/**
|
||||
* @package php-font-lib
|
||||
* @link http://php-font-lib.googlecode.com/
|
||||
* @author Fabien Ménager <fabien.menager@gmail.com>
|
||||
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
|
||||
* @version $Id$
|
||||
*/
|
||||
|
||||
/**
|
||||
* Font table name record.
|
||||
*
|
||||
* @package php-font-lib
|
||||
*/
|
||||
class Font_Table_name_Record extends Font_Binary_Stream {
|
||||
public $platformID;
|
||||
public $platformSpecificID;
|
||||
public $languageID;
|
||||
public $nameID;
|
||||
public $length;
|
||||
public $offset;
|
||||
public $string;
|
||||
|
||||
public static $format = array(
|
||||
"platformID" => self::uint16,
|
||||
"platformSpecificID" => self::uint16,
|
||||
"languageID" => self::uint16,
|
||||
"nameID" => self::uint16,
|
||||
"length" => self::uint16,
|
||||
"offset" => self::uint16,
|
||||
);
|
||||
|
||||
public function map($data) {
|
||||
foreach($data as $key => $value) {
|
||||
$this->$key = $value;
|
||||
}
|
||||
}
|
||||
|
||||
public function getUTF8() {
|
||||
return $this->string;
|
||||
}
|
||||
|
||||
public function getUTF16() {
|
||||
return Font::UTF8ToUTF16($this->string);
|
||||
}
|
||||
|
||||
function __toString(){
|
||||
return $this->string;
|
||||
}
|
||||
}
|
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
/**
|
||||
* @package php-font-lib
|
||||
* @link http://php-font-lib.googlecode.com/
|
||||
* @author Fabien Ménager <fabien.menager@gmail.com>
|
||||
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
|
||||
* @version $Id$
|
||||
*/
|
||||
|
||||
/**
|
||||
* `OS/2` font table.
|
||||
*
|
||||
* @package php-font-lib
|
||||
*/
|
||||
class Font_Table_os2 extends Font_Table {
|
||||
protected $def = array(
|
||||
"version" => self::uint16,
|
||||
"xAvgCharWidth" => self::int16,
|
||||
"usWeightClass" => self::uint16,
|
||||
"usWidthClass" => self::uint16,
|
||||
"fsType" => self::int16,
|
||||
"ySubscriptXSize" => self::int16,
|
||||
"ySubscriptYSize" => self::int16,
|
||||
"ySubscriptXOffset" => self::int16,
|
||||
"ySubscriptYOffset" => self::int16,
|
||||
"ySuperscriptXSize" => self::int16,
|
||||
"ySuperscriptYSize" => self::int16,
|
||||
"ySuperscriptXOffset" => self::int16,
|
||||
"ySuperscriptYOffset" => self::int16,
|
||||
"yStrikeoutSize" => self::int16,
|
||||
"yStrikeoutPosition" => self::int16,
|
||||
"sFamilyClass" => self::int16,
|
||||
"panose" => array(self::uint8, 10),
|
||||
"ulCharRange" => array(self::uint32, 4),
|
||||
"achVendID" => array(self::char, 4),
|
||||
"fsSelection" => self::uint16,
|
||||
"fsFirstCharIndex" => self::uint16,
|
||||
"fsLastCharIndex" => self::uint16,
|
||||
"typoAscender" => self::int16,
|
||||
"typoDescender" => self::int16,
|
||||
"typoLineGap" => self::int16,
|
||||
"winAscent" => self::int16,
|
||||
"winDescent" => self::int16,
|
||||
);
|
||||
}
|
@@ -0,0 +1,141 @@
|
||||
<?php
|
||||
/**
|
||||
* @package php-font-lib
|
||||
* @link http://php-font-lib.googlecode.com/
|
||||
* @author Fabien Ménager <fabien.menager@gmail.com>
|
||||
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
|
||||
* @version $Id$
|
||||
*/
|
||||
|
||||
/**
|
||||
* `post` font table.
|
||||
*
|
||||
* @package php-font-lib
|
||||
*/
|
||||
class Font_Table_post extends Font_Table {
|
||||
protected $def = array(
|
||||
"format" => self::Fixed,
|
||||
"italicAngle" => self::Fixed,
|
||||
"underlinePosition" => self::FWord,
|
||||
"underlineThickness" => self::FWord,
|
||||
"isFixedPitch" => self::uint32,
|
||||
"minMemType42" => self::uint32,
|
||||
"maxMemType42" => self::uint32,
|
||||
"minMemType1" => self::uint32,
|
||||
"maxMemType1" => self::uint32,
|
||||
);
|
||||
|
||||
protected function _parse(){
|
||||
$font = $this->getFont();
|
||||
$data = $font->unpack($this->def);
|
||||
|
||||
$names = array();
|
||||
|
||||
switch($data["format"]) {
|
||||
case 1:
|
||||
$names = Font_TrueType::$macCharNames;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
$data["numberOfGlyphs"] = $font->readUInt16();
|
||||
|
||||
$glyphNameIndex = array();
|
||||
for($i = 0; $i < $data["numberOfGlyphs"]; $i++) {
|
||||
$glyphNameIndex[] = $font->readUInt16();
|
||||
}
|
||||
|
||||
$data["glyphNameIndex"] = $glyphNameIndex;
|
||||
|
||||
$namesPascal = array();
|
||||
for($i = 0; $i < $data["numberOfGlyphs"]; $i++) {
|
||||
$len = $font->readUInt8();
|
||||
$namesPascal[] = $font->read($len);
|
||||
}
|
||||
|
||||
foreach($glyphNameIndex as $g => $index) {
|
||||
if ($index < 258) {
|
||||
$names[$g] = Font_TrueType::$macCharNames[$index];
|
||||
}
|
||||
else {
|
||||
$names[$g] = $namesPascal[$index - 258];
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 2.5:
|
||||
// TODO
|
||||
break;
|
||||
|
||||
case 3:
|
||||
// nothing
|
||||
break;
|
||||
|
||||
case 4:
|
||||
// TODO
|
||||
break;
|
||||
}
|
||||
|
||||
$data["names"] = $names;
|
||||
|
||||
$this->data = $data;
|
||||
}
|
||||
|
||||
function _encode(){
|
||||
$font = $this->getFont();
|
||||
$data = $this->data;
|
||||
$data["format"] = 3;
|
||||
|
||||
$length = $font->pack($this->def, $data);
|
||||
|
||||
return $length;
|
||||
|
||||
$subset = $font->getSubset();
|
||||
|
||||
switch($data["format"]) {
|
||||
case 1:
|
||||
// nothing to do
|
||||
break;
|
||||
|
||||
case 2:
|
||||
$old_names = $data["names"];
|
||||
|
||||
$glyphNameIndex = range(0, count($subset));
|
||||
|
||||
$names = array();
|
||||
foreach($subset as $gid) {
|
||||
$names[] = $data["names"][$data["glyphNameIndex"][$gid]];
|
||||
}
|
||||
|
||||
$numberOfGlyphs = count($names);
|
||||
$length += $font->writeUInt16($numberOfGlyphs);
|
||||
|
||||
foreach($glyphNameIndex as $gni) {
|
||||
$length += $font->writeUInt16($gni);
|
||||
}
|
||||
|
||||
//$names = array_slice($names, 257);
|
||||
foreach($names as $name) {
|
||||
$len = strlen($name);
|
||||
$length += $font->writeUInt8($len);
|
||||
$length += $font->write($name, $len);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 2.5:
|
||||
// TODO
|
||||
break;
|
||||
|
||||
case 3:
|
||||
// nothing
|
||||
break;
|
||||
|
||||
case 4:
|
||||
// TODO
|
||||
break;
|
||||
}
|
||||
|
||||
return $length;
|
||||
}
|
||||
}
|
@@ -0,0 +1,514 @@
|
||||
<?php
|
||||
/**
|
||||
* @package php-font-lib
|
||||
* @link http://php-font-lib.googlecode.com/
|
||||
* @author Fabien Ménager <fabien.menager@gmail.com>
|
||||
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
|
||||
* @version $Id$
|
||||
*/
|
||||
|
||||
$dir = dirname(__FILE__);
|
||||
require_once "$dir/font_binary_stream.cls.php";
|
||||
require_once "$dir/font_truetype_table_directory_entry.cls.php";
|
||||
require_once "$dir/font_truetype_header.cls.php";
|
||||
require_once "$dir/font_table.cls.php";
|
||||
require_once "$dir/adobe_font_metrics.cls.php";
|
||||
|
||||
/**
|
||||
* TrueType font file.
|
||||
*
|
||||
* @package php-font-lib
|
||||
*/
|
||||
class Font_TrueType extends Font_Binary_Stream {
|
||||
/**
|
||||
* @var Font_TrueType_Header
|
||||
*/
|
||||
public $header = array();
|
||||
|
||||
private $tableOffset = 0; // Used for TTC
|
||||
|
||||
private static $raw = false;
|
||||
|
||||
protected $directory = array();
|
||||
protected $data = array();
|
||||
|
||||
protected $glyph_subset = array();
|
||||
|
||||
public $glyph_all = array();
|
||||
public $compound_glyph_offsets = array();
|
||||
|
||||
static $nameIdCodes = array(
|
||||
0 => "Copyright",
|
||||
1 => "FontName",
|
||||
2 => "FontSubfamily",
|
||||
3 => "UniqueID",
|
||||
4 => "FullName",
|
||||
5 => "Version",
|
||||
6 => "PostScriptName",
|
||||
7 => "Trademark",
|
||||
8 => "Manufacturer",
|
||||
9 => "Designer",
|
||||
10 => "Description",
|
||||
11 => "FontVendorURL",
|
||||
12 => "FontDesignerURL",
|
||||
13 => "LicenseDescription",
|
||||
14 => "LicenseURL",
|
||||
// 15
|
||||
16 => "PreferredFamily",
|
||||
17 => "PreferredSubfamily",
|
||||
18 => "CompatibleFullName",
|
||||
19 => "SampleText",
|
||||
);
|
||||
|
||||
static $platforms = array(
|
||||
0 => "Unicode",
|
||||
1 => "Macintosh",
|
||||
// 2 => Reserved
|
||||
3 => "Microsoft",
|
||||
);
|
||||
|
||||
static $plaformSpecific = array(
|
||||
// Unicode
|
||||
0 => array(
|
||||
0 => "Default semantics",
|
||||
1 => "Version 1.1 semantics",
|
||||
2 => "ISO 10646 1993 semantics (deprecated)",
|
||||
3 => "Unicode 2.0 or later semantics",
|
||||
),
|
||||
|
||||
// Macintosh
|
||||
1 => array(
|
||||
0 => "Roman",
|
||||
1 => "Japanese",
|
||||
2 => "Traditional Chinese",
|
||||
3 => "Korean",
|
||||
4 => "Arabic",
|
||||
5 => "Hebrew",
|
||||
6 => "Greek",
|
||||
7 => "Russian",
|
||||
8 => "RSymbol",
|
||||
9 => "Devanagari",
|
||||
10 => "Gurmukhi",
|
||||
11 => "Gujarati",
|
||||
12 => "Oriya",
|
||||
13 => "Bengali",
|
||||
14 => "Tamil",
|
||||
15 => "Telugu",
|
||||
16 => "Kannada",
|
||||
17 => "Malayalam",
|
||||
18 => "Sinhalese",
|
||||
19 => "Burmese",
|
||||
20 => "Khmer",
|
||||
21 => "Thai",
|
||||
22 => "Laotian",
|
||||
23 => "Georgian",
|
||||
24 => "Armenian",
|
||||
25 => "Simplified Chinese",
|
||||
26 => "Tibetan",
|
||||
27 => "Mongolian",
|
||||
28 => "Geez",
|
||||
29 => "Slavic",
|
||||
30 => "Vietnamese",
|
||||
31 => "Sindhi",
|
||||
),
|
||||
|
||||
// Microsoft
|
||||
3 => array(
|
||||
0 => "Symbol",
|
||||
1 => "Unicode BMP (UCS-2)",
|
||||
2 => "ShiftJIS",
|
||||
3 => "PRC",
|
||||
4 => "Big5",
|
||||
5 => "Wansung",
|
||||
6 => "Johab",
|
||||
// 7 => Reserved
|
||||
// 8 => Reserved
|
||||
// 9 => Reserved
|
||||
10 => "Unicode UCS-4",
|
||||
),
|
||||
);
|
||||
|
||||
static $macCharNames = array(
|
||||
".notdef", ".null", "CR",
|
||||
"space", "exclam", "quotedbl", "numbersign",
|
||||
"dollar", "percent", "ampersand", "quotesingle",
|
||||
"parenleft", "parenright", "asterisk", "plus",
|
||||
"comma", "hyphen", "period", "slash",
|
||||
"zero", "one", "two", "three",
|
||||
"four", "five", "six", "seven",
|
||||
"eight", "nine", "colon", "semicolon",
|
||||
"less", "equal", "greater", "question",
|
||||
"at", "A", "B", "C", "D", "E", "F", "G",
|
||||
"H", "I", "J", "K", "L", "M", "N", "O",
|
||||
"P", "Q", "R", "S", "T", "U", "V", "W",
|
||||
"X", "Y", "Z", "bracketleft",
|
||||
"backslash", "bracketright", "asciicircum", "underscore",
|
||||
"grave", "a", "b", "c", "d", "e", "f", "g",
|
||||
"h", "i", "j", "k", "l", "m", "n", "o",
|
||||
"p", "q", "r", "s", "t", "u", "v", "w",
|
||||
"x", "y", "z", "braceleft",
|
||||
"bar", "braceright", "asciitilde", "Adieresis",
|
||||
"Aring", "Ccedilla", "Eacute", "Ntilde",
|
||||
"Odieresis", "Udieresis", "aacute", "agrave",
|
||||
"acircumflex", "adieresis", "atilde", "aring",
|
||||
"ccedilla", "eacute", "egrave", "ecircumflex",
|
||||
"edieresis", "iacute", "igrave", "icircumflex",
|
||||
"idieresis", "ntilde", "oacute", "ograve",
|
||||
"ocircumflex", "odieresis", "otilde", "uacute",
|
||||
"ugrave", "ucircumflex", "udieresis", "dagger",
|
||||
"degree", "cent", "sterling", "section",
|
||||
"bullet", "paragraph", "germandbls", "registered",
|
||||
"copyright", "trademark", "acute", "dieresis",
|
||||
"notequal", "AE", "Oslash", "infinity",
|
||||
"plusminus", "lessequal", "greaterequal", "yen",
|
||||
"mu", "partialdiff", "summation", "product",
|
||||
"pi", "integral", "ordfeminine", "ordmasculine",
|
||||
"Omega", "ae", "oslash", "questiondown",
|
||||
"exclamdown", "logicalnot", "radical", "florin",
|
||||
"approxequal", "increment", "guillemotleft", "guillemotright",
|
||||
"ellipsis", "nbspace", "Agrave", "Atilde",
|
||||
"Otilde", "OE", "oe", "endash",
|
||||
"emdash", "quotedblleft", "quotedblright", "quoteleft",
|
||||
"quoteright", "divide", "lozenge", "ydieresis",
|
||||
"Ydieresis", "fraction", "currency", "guilsinglleft",
|
||||
"guilsinglright", "fi", "fl", "daggerdbl",
|
||||
"periodcentered", "quotesinglbase", "quotedblbase", "perthousand",
|
||||
"Acircumflex", "Ecircumflex", "Aacute", "Edieresis",
|
||||
"Egrave", "Iacute", "Icircumflex", "Idieresis",
|
||||
"Igrave", "Oacute", "Ocircumflex", "applelogo",
|
||||
"Ograve", "Uacute", "Ucircumflex", "Ugrave",
|
||||
"dotlessi", "circumflex", "tilde", "macron",
|
||||
"breve", "dotaccent", "ring", "cedilla",
|
||||
"hungarumlaut", "ogonek", "caron", "Lslash",
|
||||
"lslash", "Scaron", "scaron", "Zcaron",
|
||||
"zcaron", "brokenbar", "Eth", "eth",
|
||||
"Yacute", "yacute", "Thorn", "thorn",
|
||||
"minus", "multiply", "onesuperior", "twosuperior",
|
||||
"threesuperior", "onehalf", "onequarter", "threequarters",
|
||||
"franc", "Gbreve", "gbreve", "Idot",
|
||||
"Scedilla", "scedilla", "Cacute", "cacute",
|
||||
"Ccaron", "ccaron", "dmacron"
|
||||
);
|
||||
|
||||
function getTable(){
|
||||
$this->parseTableEntries();
|
||||
return $this->directory;
|
||||
}
|
||||
|
||||
function setTableOffset($offset) {
|
||||
$this->tableOffset = $offset;
|
||||
}
|
||||
|
||||
function parse() {
|
||||
$this->parseTableEntries();
|
||||
|
||||
$this->data = array();
|
||||
|
||||
foreach($this->directory as $tag => $table) {
|
||||
if (empty($this->data[$tag])) {
|
||||
$this->readTable($tag);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function utf8toUnicode($str) {
|
||||
$len = strlen($str);
|
||||
$out = array();
|
||||
|
||||
for ($i = 0; $i < $len; $i++) {
|
||||
$uni = -1;
|
||||
$h = ord($str[$i]);
|
||||
|
||||
if ( $h <= 0x7F ) {
|
||||
$uni = $h;
|
||||
}
|
||||
elseif ( $h >= 0xC2 ) {
|
||||
if ( ($h <= 0xDF) && ($i < $len -1) )
|
||||
$uni = ($h & 0x1F) << 6 | (ord($str[++$i]) & 0x3F);
|
||||
elseif ( ($h <= 0xEF) && ($i < $len -2) )
|
||||
$uni = ($h & 0x0F) << 12 | (ord($str[++$i]) & 0x3F) << 6 | (ord($str[++$i]) & 0x3F);
|
||||
elseif ( ($h <= 0xF4) && ($i < $len -3) )
|
||||
$uni = ($h & 0x0F) << 18 | (ord($str[++$i]) & 0x3F) << 12 | (ord($str[++$i]) & 0x3F) << 6 | (ord($str[++$i]) & 0x3F);
|
||||
}
|
||||
|
||||
if ($uni >= 0) {
|
||||
$out[] = $uni;
|
||||
}
|
||||
}
|
||||
|
||||
return $out;
|
||||
}
|
||||
|
||||
function getUnicodeCharMap() {
|
||||
$subtable = null;
|
||||
foreach($this->getData("cmap", "subtables") as $_subtable) {
|
||||
if ($_subtable["platformID"] == 0 || $_subtable["platformID"] == 3 && $_subtable["platformSpecificID"] == 1) {
|
||||
$subtable = $_subtable;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($subtable) {
|
||||
return $subtable["glyphIndexArray"];
|
||||
}
|
||||
}
|
||||
|
||||
function lookupGlyph($gid, &$gids, &$newGlyphOffsets, $glyfOffset, $indexToLoc, $gidToCid) {
|
||||
$this->seek($glyfOffset + $indexToLoc[$gid]);
|
||||
|
||||
$numberOfContours = $this->readInt16();
|
||||
|
||||
if ($numberOfContours < 0) {
|
||||
$this->skip(8);
|
||||
$compoundOffset = 10; // 2 + 8
|
||||
|
||||
do {
|
||||
$flags = $this->readUInt16();
|
||||
$compoundOffset += 2;
|
||||
|
||||
$glyphIndex = $this->readUInt16();
|
||||
$newGlyphOffsets[$compoundOffset] = $glyphIndex;
|
||||
|
||||
$compoundOffset += 2;
|
||||
|
||||
if (!in_array($glyphIndex, $gids) && isset($indexToLoc[$glyphIndex])) {
|
||||
$code = $gidToCid[$glyphIndex];
|
||||
$gids[$code] = $glyphIndex;
|
||||
}
|
||||
|
||||
$pos = $this->pos();
|
||||
$this->lookupGlyph($glyphIndex, $gids, $newGids, $glyfOffset, $indexToLoc, $gidToCid);
|
||||
$this->seek($pos);
|
||||
|
||||
$offset = 0;
|
||||
|
||||
// skip some bytes by case
|
||||
if ($flags & Font_Glyph_Outline::ARG_1_AND_2_ARE_WORDS) {
|
||||
$offset += 4;
|
||||
}
|
||||
else {
|
||||
$offset += 2;
|
||||
}
|
||||
|
||||
if ($flags & Font_Glyph_Outline::WE_HAVE_A_SCALE) {
|
||||
$offset += 2;
|
||||
}
|
||||
elseif ($flags & Font_Glyph_Outline::WE_HAVE_AN_X_AND_Y_SCALE) {
|
||||
$offset += 4;
|
||||
}
|
||||
elseif ($flags & Font_Glyph_Outline::WE_HAVE_A_TWO_BY_TWO) {
|
||||
$offset += 8;
|
||||
}
|
||||
|
||||
$this->skip($offset);
|
||||
$compoundOffset += $offset;
|
||||
|
||||
} while ($flags & Font_Glyph_Outline::MORE_COMPONENTS);
|
||||
}
|
||||
}
|
||||
|
||||
function setSubset($subset) {
|
||||
if ( !is_array($subset) ) {
|
||||
$subset = $this->utf8toUnicode($subset);
|
||||
}
|
||||
|
||||
$subtable = null;
|
||||
foreach($this->getData("cmap", "subtables") as $_subtable) {
|
||||
if ($_subtable["platformID"] == 0 || $_subtable["platformID"] == 3 && $_subtable["platformSpecificID"] == 1) {
|
||||
$subtable = $_subtable;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$subtable) return;
|
||||
|
||||
$gids = array(
|
||||
0 => 0 // Required glyph
|
||||
);
|
||||
foreach($subset as $code) {
|
||||
if (!isset($subtable["glyphIndexArray"][$code])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$gids[$code] = $subtable["glyphIndexArray"][$code];
|
||||
}
|
||||
|
||||
// add compound glyphs
|
||||
$indexToLoc = $this->getData("loca");
|
||||
$glyfOffset = $this->directory["glyf"]->offset;
|
||||
$cidToGid = $subtable["glyphIndexArray"];
|
||||
$gidToCid = array_flip($cidToGid);
|
||||
$newGlyphOffsets = array();
|
||||
|
||||
foreach($gids as $code => $gid) {
|
||||
if ($gid === null) {
|
||||
unset($gids[$code]);
|
||||
continue;
|
||||
}
|
||||
|
||||
$_newGlyphOffsets = array();
|
||||
$this->lookupGlyph($gid, $gids, $_newGlyphOffsets, $glyfOffset, $indexToLoc, $gidToCid);
|
||||
|
||||
if (count($_newGlyphOffsets)) {
|
||||
$newGlyphOffsets[$gid] = $_newGlyphOffsets;
|
||||
}
|
||||
}
|
||||
|
||||
ksort($gids);
|
||||
|
||||
foreach($newGlyphOffsets as $_gid => $compoundOffsets) {
|
||||
foreach($compoundOffsets as $offset => $gid) {
|
||||
$newGlyphOffsets[$_gid][$offset] = array_search($gid, array_values($gids));
|
||||
}
|
||||
}
|
||||
|
||||
$this->compound_glyph_offsets = $newGlyphOffsets;
|
||||
$this->glyph_subset = $gids;
|
||||
$this->glyph_all = $subtable["glyphIndexArray"];
|
||||
}
|
||||
|
||||
function getSubset() {
|
||||
if (empty($this->glyph_subset)) {
|
||||
return $this->glyph_all;
|
||||
}
|
||||
|
||||
return $this->glyph_subset;
|
||||
}
|
||||
|
||||
function encode($tags = array()){
|
||||
if (!self::$raw) {
|
||||
$tags = array_merge(array("head", "hhea", "cmap", "hmtx", "maxp", "glyf", "loca", "name", "post"), $tags);
|
||||
}
|
||||
else {
|
||||
$tags = array_keys($this->directory);
|
||||
}
|
||||
|
||||
$num_tables = count($tags);
|
||||
$n = 16;// @todo
|
||||
|
||||
Font::d("Tables : ".implode(", ", $tags));
|
||||
|
||||
$entries = array();
|
||||
foreach($tags as $tag) {
|
||||
if (!isset($this->directory[$tag])) {
|
||||
Font::d(" >> '$tag' table doesn't exist");
|
||||
continue;
|
||||
}
|
||||
|
||||
$entries[$tag] = $this->directory[$tag];
|
||||
}
|
||||
|
||||
$this->header->data["numTables"] = $num_tables;
|
||||
$this->header->encode();
|
||||
|
||||
$directory_offset = $this->pos();
|
||||
$offset = $directory_offset + $num_tables * $n;
|
||||
$this->seek($offset);
|
||||
|
||||
$i = 0;
|
||||
foreach($entries as $entry) {
|
||||
$entry->encode($directory_offset + $i * $n);
|
||||
$i++;
|
||||
}
|
||||
}
|
||||
|
||||
function parseHeader(){
|
||||
if (!empty($this->header)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->seek($this->tableOffset);
|
||||
|
||||
$this->header = new Font_TrueType_Header($this);
|
||||
$this->header->parse();
|
||||
}
|
||||
|
||||
function parseTableEntries(){
|
||||
$this->parseHeader();
|
||||
|
||||
if (!empty($this->directory)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$class = get_class($this)."_Table_Directory_Entry";
|
||||
|
||||
for($i = 0; $i < $this->header->data["numTables"]; $i++) {
|
||||
$entry = new $class($this);
|
||||
$entry->parse();
|
||||
|
||||
$this->directory[$entry->tag] = $entry;
|
||||
}
|
||||
}
|
||||
|
||||
function normalizeFUnit($value, $base = 1000){
|
||||
return round($value * ($base / $this->getData("head", "unitsPerEm")));
|
||||
}
|
||||
|
||||
protected function readTable($tag) {
|
||||
$this->parseTableEntries();
|
||||
|
||||
if (!self::$raw) {
|
||||
$name_canon = preg_replace("/[^a-z0-9]/", "", strtolower($tag));
|
||||
$class_file = dirname(__FILE__)."/font_table_$name_canon.cls.php";
|
||||
|
||||
if (!isset($this->directory[$tag]) || !file_exists($class_file)) {
|
||||
return;
|
||||
}
|
||||
|
||||
require_once $class_file;
|
||||
$class = "Font_Table_$name_canon";
|
||||
}
|
||||
else {
|
||||
$class = "Font_Table";
|
||||
}
|
||||
|
||||
$table = new $class($this->directory[$tag]);
|
||||
$table->parse();
|
||||
|
||||
$this->data[$tag] = $table;
|
||||
}
|
||||
|
||||
public function getTableObject($name) {
|
||||
return $this->data[$name];
|
||||
}
|
||||
|
||||
public function setTableObject($name, Font_Table $data) {
|
||||
$this->data[$name] = $data;
|
||||
}
|
||||
|
||||
public function getData($name, $key = null) {
|
||||
$this->parseTableEntries();
|
||||
|
||||
if (empty($this->data[$name])) {
|
||||
$this->readTable($name);
|
||||
}
|
||||
|
||||
if (!isset($this->data[$name])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!$key) {
|
||||
return $this->data[$name]->data;
|
||||
}
|
||||
else {
|
||||
return $this->data[$name]->data[$key];
|
||||
}
|
||||
}
|
||||
|
||||
function addDirectoryEntry(Font_Table_Directory_Entry $entry) {
|
||||
$this->directory[$entry->tag] = $entry;
|
||||
}
|
||||
|
||||
function saveAdobeFontMetrics($file, $encoding = null) {
|
||||
$afm = new Adobe_Font_Metrics($this);
|
||||
$afm->write($file, $encoding);
|
||||
}
|
||||
|
||||
function reduce(){
|
||||
$names_to_keep = array(0, 1, 2, 3, 4, 5, 6);
|
||||
foreach($this->data["name"]->data["records"] as $id => $rec) {
|
||||
if (in_array($id, $names_to_keep)) continue;
|
||||
unset($this->data["name"]->data["records"][$id]);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,93 @@
|
||||
<?php
|
||||
/**
|
||||
* @package php-font-lib
|
||||
* @link http://php-font-lib.googlecode.com/
|
||||
* @author Fabien Ménager <fabien.menager@gmail.com>
|
||||
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
|
||||
* @version $Id$
|
||||
*/
|
||||
|
||||
require_once dirname(__FILE__)."/font_binary_stream.cls.php";
|
||||
require_once dirname(__FILE__)."/font_truetype.cls.php";
|
||||
|
||||
/**
|
||||
* TrueType collection font file.
|
||||
*
|
||||
* @package php-font-lib
|
||||
*/
|
||||
class Font_TrueType_Collection extends Font_Binary_Stream implements Iterator, Countable {
|
||||
/**
|
||||
* Current iterator position.
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
private $position = 0;
|
||||
|
||||
protected $collectionOffsets = array();
|
||||
protected $collection = array();
|
||||
protected $version;
|
||||
protected $numFonts;
|
||||
|
||||
function parse(){
|
||||
if (isset($this->numFonts)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->read(4); // tag name
|
||||
|
||||
$this->version = $this->readFixed();
|
||||
$this->numFonts = $this->readUInt32();
|
||||
|
||||
for ($i = 0; $i < $this->numFonts; $i++) {
|
||||
$this->collectionOffsets[] = $this->readUInt32();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $fontId
|
||||
* @return Font_TrueType
|
||||
*/
|
||||
function getFont($fontId) {
|
||||
$this->parse();
|
||||
|
||||
if (!isset($this->collectionOffsets[$fontId])) {
|
||||
throw new OutOfBoundsException();
|
||||
}
|
||||
|
||||
if (isset($this->collection[$fontId])) {
|
||||
return $this->collection[$fontId];
|
||||
}
|
||||
|
||||
$font = new Font_TrueType();
|
||||
$font->f = $this->f;
|
||||
$font->setTableOffset($this->collectionOffsets[$fontId]);
|
||||
|
||||
return $this->collection[$fontId] = $font;
|
||||
}
|
||||
|
||||
function current() {
|
||||
return $this->getFont($this->position);
|
||||
}
|
||||
|
||||
function key() {
|
||||
return $this->position;
|
||||
}
|
||||
|
||||
function next() {
|
||||
return ++$this->position;
|
||||
}
|
||||
|
||||
function rewind() {
|
||||
$this->position = 0;
|
||||
}
|
||||
|
||||
function valid() {
|
||||
$this->parse();
|
||||
return isset($this->collectionOffsets[$this->position]);
|
||||
}
|
||||
|
||||
function count() {
|
||||
$this->parse();
|
||||
return $this->numFonts;
|
||||
}
|
||||
}
|
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
/**
|
||||
* @package php-font-lib
|
||||
* @link http://php-font-lib.googlecode.com/
|
||||
* @author Fabien Ménager <fabien.menager@gmail.com>
|
||||
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
|
||||
* @version $Id$
|
||||
*/
|
||||
|
||||
require_once dirname(__FILE__)."/font_header.cls.php";
|
||||
|
||||
/**
|
||||
* TrueType font file header.
|
||||
*
|
||||
* @package php-font-lib
|
||||
*/
|
||||
class Font_TrueType_Header extends Font_Header {
|
||||
protected $def = array(
|
||||
"format" => self::uint32,
|
||||
"numTables" => self::uint16,
|
||||
"searchRange" => self::uint16,
|
||||
"entrySelector" => self::uint16,
|
||||
"rangeShift" => self::uint16,
|
||||
);
|
||||
|
||||
public function parse(){
|
||||
parent::parse();
|
||||
|
||||
$format = $this->data["format"];
|
||||
$this->data["formatText"] = $this->convertUInt32ToStr($format);
|
||||
}
|
||||
}
|
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
/**
|
||||
* @package php-font-lib
|
||||
* @link http://php-font-lib.googlecode.com/
|
||||
* @author Fabien Ménager <fabien.menager@gmail.com>
|
||||
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
|
||||
* @version $Id$
|
||||
*/
|
||||
|
||||
require_once dirname(__FILE__)."/font_table_directory_entry.cls.php";
|
||||
|
||||
/**
|
||||
* TrueType table directory entry.
|
||||
*
|
||||
* @package php-font-lib
|
||||
*/
|
||||
class Font_TrueType_Table_Directory_Entry extends Font_Table_Directory_Entry {
|
||||
function __construct(Font_TrueType $font) {
|
||||
parent::__construct($font);
|
||||
}
|
||||
|
||||
function parse(){
|
||||
parent::parse();
|
||||
|
||||
$font = $this->font;
|
||||
$this->checksum = $font->readUInt32();
|
||||
$this->offset = $font->readUInt32();
|
||||
$this->length = $font->readUInt32();
|
||||
$this->entryLength += 12;
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
/**
|
||||
* @package php-font-lib
|
||||
* @link http://php-font-lib.googlecode.com/
|
||||
* @author Fabien Ménager <fabien.menager@gmail.com>
|
||||
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
|
||||
* @version $Id$
|
||||
*/
|
||||
|
||||
require_once dirname(__FILE__)."/font_truetype.cls.php";
|
||||
require_once dirname(__FILE__)."/font_woff_table_directory_entry.cls.php";
|
||||
require_once dirname(__FILE__)."/font_woff_header.cls.php";
|
||||
|
||||
/**
|
||||
* WOFF font file.
|
||||
*
|
||||
* @package php-font-lib
|
||||
*/
|
||||
class Font_WOFF extends Font_TrueType {
|
||||
function parseHeader(){
|
||||
if (!empty($this->header)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->header = new Font_WOFF_Header($this);
|
||||
$this->header->parse();
|
||||
}
|
||||
|
||||
public function load($file) {
|
||||
parent::load($file);
|
||||
|
||||
$this->parseTableEntries();
|
||||
$dataOffset = $this->pos() + count($this->directory) * 20;
|
||||
|
||||
$fw = $this->getTempFile(false);
|
||||
$fr = $this->f;
|
||||
|
||||
$this->f = $fw;
|
||||
$offset = $this->header->encode();
|
||||
|
||||
foreach($this->directory as $entry) {
|
||||
// Read ...
|
||||
$this->f = $fr;
|
||||
$this->seek($entry->offset);
|
||||
$data = $this->read($entry->length);
|
||||
|
||||
if ($entry->length < $entry->origLength) {
|
||||
$data = gzuncompress($data);
|
||||
}
|
||||
|
||||
// Prepare data ...
|
||||
$length = strlen($data);
|
||||
$entry->length = $entry->origLength = $length;
|
||||
$entry->offset = $dataOffset;
|
||||
|
||||
// Write ...
|
||||
$this->f = $fw;
|
||||
|
||||
// Woff Entry
|
||||
$this->seek($offset);
|
||||
$offset += $this->write($entry->tag, 4); // tag
|
||||
$offset += $this->writeUInt32($dataOffset); // offset
|
||||
$offset += $this->writeUInt32($length); // length
|
||||
$offset += $this->writeUInt32($length); // origLength
|
||||
$offset += $this->writeUInt32(Font_Table_Directory_Entry::computeChecksum($data)); // checksum
|
||||
|
||||
// Data
|
||||
$this->seek($dataOffset);
|
||||
$dataOffset += $this->write($data, $length);
|
||||
}
|
||||
|
||||
$this->f = $fw;
|
||||
$this->seek(0);
|
||||
|
||||
// Need to re-parse this, don't know why
|
||||
$this->header = null;
|
||||
$this->directory = array();
|
||||
$this->parseTableEntries();
|
||||
}
|
||||
}
|
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
/**
|
||||
* @package php-font-lib
|
||||
* @link http://php-font-lib.googlecode.com/
|
||||
* @author Fabien Ménager <fabien.menager@gmail.com>
|
||||
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
|
||||
* @version $Id$
|
||||
*/
|
||||
|
||||
require_once dirname(__FILE__)."/font_truetype_header.cls.php";
|
||||
|
||||
/**
|
||||
* WOFF font file header.
|
||||
*
|
||||
* @package php-font-lib
|
||||
*/
|
||||
class Font_WOFF_Header extends Font_TrueType_Header {
|
||||
protected $def = array(
|
||||
"format" => self::uint32,
|
||||
"flavor" => self::uint32,
|
||||
"length" => self::uint32,
|
||||
"numTables" => self::uint16,
|
||||
self::uint16,
|
||||
"totalSfntSize" => self::uint32,
|
||||
"majorVersion" => self::uint16,
|
||||
"minorVersion" => self::uint16,
|
||||
"metaOffset" => self::uint32,
|
||||
"metaLength" => self::uint32,
|
||||
"metaOrigLength" => self::uint32,
|
||||
"privOffset" => self::uint32,
|
||||
"privLength" => self::uint32,
|
||||
);
|
||||
}
|
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
/**
|
||||
* @package php-font-lib
|
||||
* @link http://php-font-lib.googlecode.com/
|
||||
* @author Fabien Ménager <fabien.menager@gmail.com>
|
||||
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
|
||||
* @version $Id$
|
||||
*/
|
||||
|
||||
require_once dirname(__FILE__)."/font_table_directory_entry.cls.php";
|
||||
|
||||
/**
|
||||
* WOFF font file table directory entry.
|
||||
*
|
||||
* @package php-font-lib
|
||||
*/
|
||||
class Font_WOFF_Table_Directory_Entry extends Font_Table_Directory_Entry {
|
||||
protected $origLength;
|
||||
|
||||
function __construct(Font_WOFF $font) {
|
||||
parent::__construct($font);
|
||||
}
|
||||
|
||||
function parse(){
|
||||
parent::parse();
|
||||
|
||||
$font = $this->font;
|
||||
$this->offset = $font->readUInt32();
|
||||
$this->length = $font->readUInt32();
|
||||
$this->origLength = $font->readUInt32();
|
||||
$this->checksum = $font->readUInt32();
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user