commiting uncommited changes on live site
[weblabels.fsf.org.git] / crm.fsf.org / 20131203 / files / sites / all / modules-old / civicrm / vendor / phenx / php-font-lib / src / FontLib / TrueType / File.php
1 <?php
2 /**
3 * @package php-font-lib
4 * @link https://github.com/PhenX/php-font-lib
5 * @author Fabien Ménager <fabien.menager@gmail.com>
6 * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
7 */
8
9 namespace FontLib\TrueType;
10
11 use FontLib\AdobeFontMetrics;
12 use FontLib\Font;
13 use FontLib\BinaryStream;
14 use FontLib\Table\Table;
15 use FontLib\Table\DirectoryEntry;
16 use FontLib\Table\Type\glyf;
17 use FontLib\Table\Type\name;
18 use FontLib\Table\Type\nameRecord;
19
20 /**
21 * TrueType font file.
22 *
23 * @package php-font-lib
24 */
25 class File extends BinaryStream {
26 /**
27 * @var Header
28 */
29 public $header = array();
30
31 private $tableOffset = 0; // Used for TTC
32
33 private static $raw = false;
34
35 protected $directory = array();
36 protected $data = array();
37
38 protected $glyph_subset = array();
39
40 public $glyph_all = array();
41
42 static $macCharNames = array(
43 ".notdef", ".null", "CR",
44 "space", "exclam", "quotedbl", "numbersign",
45 "dollar", "percent", "ampersand", "quotesingle",
46 "parenleft", "parenright", "asterisk", "plus",
47 "comma", "hyphen", "period", "slash",
48 "zero", "one", "two", "three",
49 "four", "five", "six", "seven",
50 "eight", "nine", "colon", "semicolon",
51 "less", "equal", "greater", "question",
52 "at", "A", "B", "C", "D", "E", "F", "G",
53 "H", "I", "J", "K", "L", "M", "N", "O",
54 "P", "Q", "R", "S", "T", "U", "V", "W",
55 "X", "Y", "Z", "bracketleft",
56 "backslash", "bracketright", "asciicircum", "underscore",
57 "grave", "a", "b", "c", "d", "e", "f", "g",
58 "h", "i", "j", "k", "l", "m", "n", "o",
59 "p", "q", "r", "s", "t", "u", "v", "w",
60 "x", "y", "z", "braceleft",
61 "bar", "braceright", "asciitilde", "Adieresis",
62 "Aring", "Ccedilla", "Eacute", "Ntilde",
63 "Odieresis", "Udieresis", "aacute", "agrave",
64 "acircumflex", "adieresis", "atilde", "aring",
65 "ccedilla", "eacute", "egrave", "ecircumflex",
66 "edieresis", "iacute", "igrave", "icircumflex",
67 "idieresis", "ntilde", "oacute", "ograve",
68 "ocircumflex", "odieresis", "otilde", "uacute",
69 "ugrave", "ucircumflex", "udieresis", "dagger",
70 "degree", "cent", "sterling", "section",
71 "bullet", "paragraph", "germandbls", "registered",
72 "copyright", "trademark", "acute", "dieresis",
73 "notequal", "AE", "Oslash", "infinity",
74 "plusminus", "lessequal", "greaterequal", "yen",
75 "mu", "partialdiff", "summation", "product",
76 "pi", "integral", "ordfeminine", "ordmasculine",
77 "Omega", "ae", "oslash", "questiondown",
78 "exclamdown", "logicalnot", "radical", "florin",
79 "approxequal", "increment", "guillemotleft", "guillemotright",
80 "ellipsis", "nbspace", "Agrave", "Atilde",
81 "Otilde", "OE", "oe", "endash",
82 "emdash", "quotedblleft", "quotedblright", "quoteleft",
83 "quoteright", "divide", "lozenge", "ydieresis",
84 "Ydieresis", "fraction", "currency", "guilsinglleft",
85 "guilsinglright", "fi", "fl", "daggerdbl",
86 "periodcentered", "quotesinglbase", "quotedblbase", "perthousand",
87 "Acircumflex", "Ecircumflex", "Aacute", "Edieresis",
88 "Egrave", "Iacute", "Icircumflex", "Idieresis",
89 "Igrave", "Oacute", "Ocircumflex", "applelogo",
90 "Ograve", "Uacute", "Ucircumflex", "Ugrave",
91 "dotlessi", "circumflex", "tilde", "macron",
92 "breve", "dotaccent", "ring", "cedilla",
93 "hungarumlaut", "ogonek", "caron", "Lslash",
94 "lslash", "Scaron", "scaron", "Zcaron",
95 "zcaron", "brokenbar", "Eth", "eth",
96 "Yacute", "yacute", "Thorn", "thorn",
97 "minus", "multiply", "onesuperior", "twosuperior",
98 "threesuperior", "onehalf", "onequarter", "threequarters",
99 "franc", "Gbreve", "gbreve", "Idot",
100 "Scedilla", "scedilla", "Cacute", "cacute",
101 "Ccaron", "ccaron", "dmacron"
102 );
103
104 function getTable() {
105 $this->parseTableEntries();
106
107 return $this->directory;
108 }
109
110 function setTableOffset($offset) {
111 $this->tableOffset = $offset;
112 }
113
114 function parse() {
115 $this->parseTableEntries();
116
117 $this->data = array();
118
119 foreach ($this->directory as $tag => $table) {
120 if (empty($this->data[$tag])) {
121 $this->readTable($tag);
122 }
123 }
124 }
125
126 function utf8toUnicode($str) {
127 $len = strlen($str);
128 $out = array();
129
130 for ($i = 0; $i < $len; $i++) {
131 $uni = -1;
132 $h = ord($str[$i]);
133
134 if ($h <= 0x7F) {
135 $uni = $h;
136 }
137 elseif ($h >= 0xC2) {
138 if (($h <= 0xDF) && ($i < $len - 1)) {
139 $uni = ($h & 0x1F) << 6 | (ord($str[++$i]) & 0x3F);
140 }
141 elseif (($h <= 0xEF) && ($i < $len - 2)) {
142 $uni = ($h & 0x0F) << 12 | (ord($str[++$i]) & 0x3F) << 6 | (ord($str[++$i]) & 0x3F);
143 }
144 elseif (($h <= 0xF4) && ($i < $len - 3)) {
145 $uni = ($h & 0x0F) << 18 | (ord($str[++$i]) & 0x3F) << 12 | (ord($str[++$i]) & 0x3F) << 6 | (ord($str[++$i]) & 0x3F);
146 }
147 }
148
149 if ($uni >= 0) {
150 $out[] = $uni;
151 }
152 }
153
154 return $out;
155 }
156
157 function getUnicodeCharMap() {
158 $subtable = null;
159 foreach ($this->getData("cmap", "subtables") as $_subtable) {
160 if ($_subtable["platformID"] == 0 || $_subtable["platformID"] == 3 && $_subtable["platformSpecificID"] == 1) {
161 $subtable = $_subtable;
162 break;
163 }
164 }
165
166 if ($subtable) {
167 return $subtable["glyphIndexArray"];
168 }
169
170 return null;
171 }
172
173 function setSubset($subset) {
174 if (!is_array($subset)) {
175 $subset = $this->utf8toUnicode($subset);
176 }
177
178 $subset = array_unique($subset);
179
180 $glyphIndexArray = $this->getUnicodeCharMap();
181
182 if (!$glyphIndexArray) {
183 return;
184 }
185
186 $gids = array(
187 0, // .notdef
188 1, // .null
189 );
190
191 foreach ($subset as $code) {
192 if (!isset($glyphIndexArray[$code])) {
193 continue;
194 }
195
196 $gid = $glyphIndexArray[$code];
197 $gids[$gid] = $gid;
198 }
199
200 /** @var glyf $glyf */
201 $glyf = $this->getTableObject("glyf");
202 $gids = $glyf->getGlyphIDs($gids);
203
204 sort($gids);
205
206 $this->glyph_subset = $gids;
207 $this->glyph_all = array_values($glyphIndexArray); // FIXME
208 }
209
210 function getSubset() {
211 if (empty($this->glyph_subset)) {
212 return $this->glyph_all;
213 }
214
215 return $this->glyph_subset;
216 }
217
218 function encode($tags = array()) {
219 if (!self::$raw) {
220 $tags = array_merge(array("head", "hhea", "cmap", "hmtx", "maxp", "glyf", "loca", "name", "post"), $tags);
221 }
222 else {
223 $tags = array_keys($this->directory);
224 }
225
226 $num_tables = count($tags);
227 $n = 16; // @todo
228
229 Font::d("Tables : " . implode(", ", $tags));
230
231 /** @var DirectoryEntry[] $entries */
232 $entries = array();
233 foreach ($tags as $tag) {
234 if (!isset($this->directory[$tag])) {
235 Font::d(" >> '$tag' table doesn't exist");
236 continue;
237 }
238
239 $entries[$tag] = $this->directory[$tag];
240 }
241
242 $this->header->data["numTables"] = $num_tables;
243 $this->header->encode();
244
245 $directory_offset = $this->pos();
246 $offset = $directory_offset + $num_tables * $n;
247 $this->seek($offset);
248
249 $i = 0;
250 foreach ($entries as $entry) {
251 $entry->encode($directory_offset + $i * $n);
252 $i++;
253 }
254 }
255
256 function parseHeader() {
257 if (!empty($this->header)) {
258 return;
259 }
260
261 $this->seek($this->tableOffset);
262
263 $this->header = new Header($this);
264 $this->header->parse();
265 }
266
267 function getFontType(){
268 $class_parts = explode("\\", get_class($this));
269 return $class_parts[1];
270 }
271
272 function parseTableEntries() {
273 $this->parseHeader();
274
275 if (!empty($this->directory)) {
276 return;
277 }
278
279 if (empty($this->header->data["numTables"])) {
280 return;
281 }
282
283
284 $type = $this->getFontType();
285 $class = "FontLib\\$type\\TableDirectoryEntry";
286
287 for ($i = 0; $i < $this->header->data["numTables"]; $i++) {
288 /** @var TableDirectoryEntry $entry */
289 $entry = new $class($this);
290 $entry->parse();
291
292 $this->directory[$entry->tag] = $entry;
293 }
294 }
295
296 function normalizeFUnit($value, $base = 1000) {
297 return round($value * ($base / $this->getData("head", "unitsPerEm")));
298 }
299
300 protected function readTable($tag) {
301 $this->parseTableEntries();
302
303 if (!self::$raw) {
304 $name_canon = preg_replace("/[^a-z0-9]/", "", strtolower($tag));
305
306 $class = "FontLib\\Table\\Type\\$name_canon";
307
308 if (!isset($this->directory[$tag]) || !class_exists($class)) {
309 return;
310 }
311 }
312 else {
313 $class = "FontLib\\Table\\Table";
314 }
315
316 /** @var Table $table */
317 $table = new $class($this->directory[$tag]);
318 $table->parse();
319
320 $this->data[$tag] = $table;
321 }
322
323 /**
324 * @param $name
325 *
326 * @return Table
327 */
328 public function getTableObject($name) {
329 return $this->data[$name];
330 }
331
332 public function setTableObject($name, Table $data) {
333 $this->data[$name] = $data;
334 }
335
336 public function getData($name, $key = null) {
337 $this->parseTableEntries();
338
339 if (empty($this->data[$name])) {
340 $this->readTable($name);
341 }
342
343 if (!isset($this->data[$name])) {
344 return null;
345 }
346
347 if (!$key) {
348 return $this->data[$name]->data;
349 }
350 else {
351 return $this->data[$name]->data[$key];
352 }
353 }
354
355 function addDirectoryEntry(DirectoryEntry $entry) {
356 $this->directory[$entry->tag] = $entry;
357 }
358
359 function saveAdobeFontMetrics($file, $encoding = null) {
360 $afm = new AdobeFontMetrics($this);
361 $afm->write($file, $encoding);
362 }
363
364 /**
365 * Get a specific name table string value from its ID
366 *
367 * @param int $nameID The name ID
368 *
369 * @return string|null
370 */
371 function getNameTableString($nameID) {
372 /** @var nameRecord[] $records */
373 $records = $this->getData("name", "records");
374
375 if (!isset($records[$nameID])) {
376 return null;
377 }
378
379 return $records[$nameID]->string;
380 }
381
382 /**
383 * Get font copyright
384 *
385 * @return string|null
386 */
387 function getFontCopyright() {
388 return $this->getNameTableString(name::NAME_COPYRIGHT);
389 }
390
391 /**
392 * Get font name
393 *
394 * @return string|null
395 */
396 function getFontName() {
397 return $this->getNameTableString(name::NAME_NAME);
398 }
399
400 /**
401 * Get font subfamily
402 *
403 * @return string|null
404 */
405 function getFontSubfamily() {
406 return $this->getNameTableString(name::NAME_SUBFAMILY);
407 }
408
409 /**
410 * Get font subfamily ID
411 *
412 * @return string|null
413 */
414 function getFontSubfamilyID() {
415 return $this->getNameTableString(name::NAME_SUBFAMILY_ID);
416 }
417
418 /**
419 * Get font full name
420 *
421 * @return string|null
422 */
423 function getFontFullName() {
424 return $this->getNameTableString(name::NAME_FULL_NAME);
425 }
426
427 /**
428 * Get font version
429 *
430 * @return string|null
431 */
432 function getFontVersion() {
433 return $this->getNameTableString(name::NAME_VERSION);
434 }
435
436 /**
437 * Get font weight
438 *
439 * @return string|null
440 */
441 function getFontWeight() {
442 return $this->getTableObject("OS/2")->data["usWeightClass"];
443 }
444
445 /**
446 * Get font Postscript name
447 *
448 * @return string|null
449 */
450 function getFontPostscriptName() {
451 return $this->getNameTableString(name::NAME_POSTSCRIPT_NAME);
452 }
453
454 function reduce() {
455 $names_to_keep = array(
456 name::NAME_COPYRIGHT,
457 name::NAME_NAME,
458 name::NAME_SUBFAMILY,
459 name::NAME_SUBFAMILY_ID,
460 name::NAME_FULL_NAME,
461 name::NAME_VERSION,
462 name::NAME_POSTSCRIPT_NAME,
463 );
464
465 foreach ($this->data["name"]->data["records"] as $id => $rec) {
466 if (!in_array($id, $names_to_keep)) {
467 unset($this->data["name"]->data["records"][$id]);
468 }
469 }
470 }
471 }