FINAL suepr merge step : added all modules to this super repos

This commit is contained in:
Bachir Soussi Chiadmi
2015-04-19 16:46:59 +02:00
7585 changed files with 1723356 additions and 18 deletions

View File

@@ -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");
}
}

View File

@@ -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;
}
}

View File

@@ -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");
}
}

View File

@@ -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);
}
}

View File

@@ -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'];
}
}

View File

@@ -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));
}
}

View File

@@ -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();
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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 {
//
}

View File

@@ -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 {
}

View File

@@ -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();
}
}

View File

@@ -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;
}
}

View File

@@ -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() {
//
}
}

View File

@@ -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;
}
}

View File

@@ -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"]).")");
}
}
}

View File

@@ -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();
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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();
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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,
);
}

View File

@@ -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;
}
}

View File

@@ -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]);
}
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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();
}
}

View File

@@ -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,
);
}

View File

@@ -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();
}
}