Я пытался добиться этого и подошел к его созданию. Тем не менее, в конце я решил использовать Python вместо этого при разборе данных. Во всяком случае, если кто-то решит, что он хочет идти по пути PHP, вот что я имею до этого момента. Это один шаг близко к получению карты глифа. Для документации о том, как читать данные, обратитесь к http://developer.apple.com/fonts/ttrefman/RM06/Chap6.html.
<?php
$ttr = new TrueTypeReader;
$ttr->open('font.otf');
class TrueTypeReader
{
private $position = 0;
private $offset = 0;
private $file;
public function open($file)
{
$this->file = file_get_contents($file);
// http://developer.apple.com/fonts/ttrefman/RM06/Chap6.html
// The offset subtable
$number_of_tables = $this->getUint16(4);
$this->tables = array();
for($i = 0; $i < $number_of_tables; $i++)
{
// http://developer.apple.com/fonts/ttrefman/RM06/Chap6.html
// The table directory
$table = array();
$tag = $this->getTag(12 + $i * 16);
$table = array
(
'tag' => $tag,
'check_sum' => $this->getUint32(12 + $i * 16 + 4),
'offset' => $this->getUint32(12 + $i * 16 + 8),
'length' => $this->getUint32(12 + $i * 16 + 12),
);
if($tag == 'cmap')
{
$this->parseCmapTable($table);
}
$this->tables[] = $table;
}
}
private function getTag($pt = FALSE)
{
if($pt === FALSE)
{
$pt = $this->position;
$this->position += 4;
}
return substr($this->file, $pt, 4);
}
private function getUint32($pt = FALSE)
{
if($pt === FALSE)
{
$pt = $this->position;
$this->position += 4;
}
$r = unpack("N", substr($this->file, $pt, 4) );
return $r[1];
}
private function getUint16($pt = FALSE)
{
if ($pt === FALSE)
{
$pt = $this->position;
$this->position += 2;
}
$r = unpack("n", substr($this->file, $pt, 2) );
return $r[1];
}
private function parseCmapTable($table)
{
$this->position = $table['offset'];
// http://developer.apple.com/fonts/ttrefman/RM06/Chap6cmap.html
// General table information
$data = array
(
'version' => $this->getUint16(),
'number_subtables' => $this->getUint16(),
);
$sub_tables = array();
for($i = 0; $i < $data['number_subtables']; $i++)
{
// http://developer.apple.com/fonts/ttrefman/RM06/Chap6cmap.html
// The 'cmap' encoding subtables
$sub_tables[] = array
(
'platform_id' => $this->getUint16(),
'specific_id' => $this->getUint16(),
'offset' => $this->getUint32(),
);
}
// http://developer.apple.com/fonts/ttrefman/RM06/Chap6cmap.html
// The 'cmap' formats
$formats = array();
foreach($sub_tables as $t)
{
// /5049814/tablitsa-sopostavleniya-simvolov-i-simvolov#5049828
$this->position = $table['offset'] + $t['offset'];
$format = array
(
'format' => $this->getUint16(),
'length' => $this->getUint16(),
'language' => $this->getUint16(),
);
if($format['format'] == 4)
{
$format += array
(
'seg_count_X2' => $this->getUint16(),
'search_range' => $this->getUint16(),
'entry_selector' => $this->getUint16(),
'range_shift' => $this->getUint16(),
'end_code[segCount]' => $this->getUint16(),
'reserved_pad' => $this->getUint16(),
'start_code[segCount]' => $this->getUint16(),
'id_delta[segCount]' => $this->getUint16(),
'id_range_offset[segCount]' => $this->getUint16(),
'glyph_index_array[variable]' => $this->getUint16(),
);
$backup = $format;
$format['seg_count_X2'] = $backup['seg_count_X2']*2;
$format['search_range'] = 2 * (2 * floor(log($backup['seg_count_X2'], 2)));
$format['entry_selector'] = log($backup['search_range']/2, 2);
$format['range_shift'] = (2 * $backup['seg_count_X2']) - $backup['search_range'];
}
$formats[$t['offset']] = $format;
}
die(var_dump( $sub_tables, $formats ));
die(var_dump( $this->getUint16(), $this->getUint16(), $this->getUint16(), $this->getUint16(), $this->getUint16() ));
die(var_dump( $sub_tables[0] ));
$cmap = array
(
'format' => $this->getUint16(),
#'length' => $this->getUint16(),
#'language' => $this->getUint16(),
);
die(var_dump( $cmap ));
die(var_dump( $table, $data, $table, $cmap ));
}
private function parseNameTable($table)
{
// http://developer.apple.com/fonts/ttrefman/RM06/Chap6name.html
// Name Table Format
$data = array
(
'format' => $this->getUint16($table['offset']),
'count' => $this->getUint16($table['offset'] + 2), // $num_of_name_tables
'string_offset' => $this->getUint16($table['offset'] + 4),
);
$offset = $table['offset'] + $data['string_offset']; // $name_tables_offset
$name_tables = array();
for($i = 0; $i < $data['count']; $i ++)
{
$this->position = $table['offset'] + 6 + $i * 12;
$d = array
(
'platform_id' => $this->getUint16(),
'specific_id' => $this->getUint16(),
'lang_id' => $this->getUint16(),
'name_id' => $this->getUint16(),
'length' => $this->getUint16(),
'offset' => $this->getUint16() + $offset,
);
$key = "{$d['platform_id']}::{$d['specific_id']}::{$d['lang_id']}";
if(isset($d['name_id']) && empty($name_tables[$key][$d['name_id']]))
{
$text = substr($this->file, $d['offset'], $d['length']);
$name_tables[$key][$d['name_id']] = str_replace(chr(0), '', $text);
}
}
die(var_dump( $name_tables ));
}
}