PageRenderTime 64ms CodeModel.GetById 21ms RepoModel.GetById 1ms app.codeStats 1ms

/include/PHPExcel/Writer/PDF/lib/php-font-lib/classes/font_truetype.cls.php

https://bitbucket.org/ite/on-track-code-base
PHP | 487 lines | 409 code | 53 blank | 25 comment | 30 complexity | 3ba7d4583b16513ac3f3aa2eb87ca5a3 MD5 | raw file
  1. <?php
  2. /**
  3. * @package php-font-lib
  4. * @link http://php-font-lib.googlecode.com/
  5. * @author Fabien Ménager <fabien.menager@gmail.com>
  6. * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
  7. * @version $Id: font_truetype.cls.php 41 2012-02-04 18:01:38Z fabien.menager $
  8. */
  9. $dir = dirname(__FILE__);
  10. require_once "$dir/font_binary_stream.cls.php";
  11. require_once "$dir/font_truetype_table_directory_entry.cls.php";
  12. require_once "$dir/font_truetype_header.cls.php";
  13. require_once "$dir/font_table.cls.php";
  14. require_once "$dir/adobe_font_metrics.cls.php";
  15. /**
  16. * TrueType font file.
  17. *
  18. * @package php-font-lib
  19. */
  20. class Font_TrueType extends Font_Binary_Stream {
  21. /**
  22. * @var Font_TrueType_Header
  23. */
  24. public $header = array();
  25. private $tableOffset = 0; // Used for TTC
  26. private static $raw = false;
  27. protected $directory = array();
  28. protected $data = array();
  29. protected $glyph_subset = array();
  30. protected $glyph_all = array();
  31. public $compound_glyph_offsets = array();
  32. static $nameIdCodes = array(
  33. 0 => "Copyright",
  34. 1 => "FontName",
  35. 2 => "FontSubfamily",
  36. 3 => "UniqueID",
  37. 4 => "FullName",
  38. 5 => "Version",
  39. 6 => "PostScriptName",
  40. 7 => "Trademark",
  41. 8 => "Manufacturer",
  42. 9 => "Designer",
  43. 10 => "Description",
  44. 11 => "FontVendorURL",
  45. 12 => "FontDesignerURL",
  46. 13 => "LicenseDescription",
  47. 14 => "LicenseURL",
  48. // 15
  49. 16 => "PreferredFamily",
  50. 17 => "PreferredSubfamily",
  51. 18 => "CompatibleFullName",
  52. 19 => "SampleText",
  53. );
  54. static $platforms = array(
  55. 0 => "Unicode",
  56. 1 => "Macintosh",
  57. // 2 => Reserved
  58. 3 => "Microsoft",
  59. );
  60. static $plaformSpecific = array(
  61. // Unicode
  62. 0 => array(
  63. 0 => "Default semantics",
  64. 1 => "Version 1.1 semantics",
  65. 2 => "ISO 10646 1993 semantics (deprecated)",
  66. 3 => "Unicode 2.0 or later semantics",
  67. ),
  68. // Macintosh
  69. 1 => array(
  70. 0 => "Roman",
  71. 1 => "Japanese",
  72. 2 => "Traditional Chinese",
  73. 3 => "Korean",
  74. 4 => "Arabic",
  75. 5 => "Hebrew",
  76. 6 => "Greek",
  77. 7 => "Russian",
  78. 8 => "RSymbol",
  79. 9 => "Devanagari",
  80. 10 => "Gurmukhi",
  81. 11 => "Gujarati",
  82. 12 => "Oriya",
  83. 13 => "Bengali",
  84. 14 => "Tamil",
  85. 15 => "Telugu",
  86. 16 => "Kannada",
  87. 17 => "Malayalam",
  88. 18 => "Sinhalese",
  89. 19 => "Burmese",
  90. 20 => "Khmer",
  91. 21 => "Thai",
  92. 22 => "Laotian",
  93. 23 => "Georgian",
  94. 24 => "Armenian",
  95. 25 => "Simplified Chinese",
  96. 26 => "Tibetan",
  97. 27 => "Mongolian",
  98. 28 => "Geez",
  99. 29 => "Slavic",
  100. 30 => "Vietnamese",
  101. 31 => "Sindhi",
  102. ),
  103. // Microsoft
  104. 3 => array(
  105. 0 => "Symbol",
  106. 1 => "Unicode BMP (UCS-2)",
  107. 2 => "ShiftJIS",
  108. 3 => "PRC",
  109. 4 => "Big5",
  110. 5 => "Wansung",
  111. 6 => "Johab",
  112. // 7 => Reserved
  113. // 8 => Reserved
  114. // 9 => Reserved
  115. 10 => "Unicode UCS-4",
  116. ),
  117. );
  118. static $macCharNames = array(
  119. ".notdef", ".null", "CR",
  120. "space", "exclam", "quotedbl", "numbersign",
  121. "dollar", "percent", "ampersand", "quotesingle",
  122. "parenleft", "parenright", "asterisk", "plus",
  123. "comma", "hyphen", "period", "slash",
  124. "zero", "one", "two", "three",
  125. "four", "five", "six", "seven",
  126. "eight", "nine", "colon", "semicolon",
  127. "less", "equal", "greater", "question",
  128. "at", "A", "B", "C", "D", "E", "F", "G",
  129. "H", "I", "J", "K", "L", "M", "N", "O",
  130. "P", "Q", "R", "S", "T", "U", "V", "W",
  131. "X", "Y", "Z", "bracketleft",
  132. "backslash", "bracketright", "asciicircum", "underscore",
  133. "grave", "a", "b", "c", "d", "e", "f", "g",
  134. "h", "i", "j", "k", "l", "m", "n", "o",
  135. "p", "q", "r", "s", "t", "u", "v", "w",
  136. "x", "y", "z", "braceleft",
  137. "bar", "braceright", "asciitilde", "Adieresis",
  138. "Aring", "Ccedilla", "Eacute", "Ntilde",
  139. "Odieresis", "Udieresis", "aacute", "agrave",
  140. "acircumflex", "adieresis", "atilde", "aring",
  141. "ccedilla", "eacute", "egrave", "ecircumflex",
  142. "edieresis", "iacute", "igrave", "icircumflex",
  143. "idieresis", "ntilde", "oacute", "ograve",
  144. "ocircumflex", "odieresis", "otilde", "uacute",
  145. "ugrave", "ucircumflex", "udieresis", "dagger",
  146. "degree", "cent", "sterling", "section",
  147. "bullet", "paragraph", "germandbls", "registered",
  148. "copyright", "trademark", "acute", "dieresis",
  149. "notequal", "AE", "Oslash", "infinity",
  150. "plusminus", "lessequal", "greaterequal", "yen",
  151. "mu", "partialdiff", "summation", "product",
  152. "pi", "integral", "ordfeminine", "ordmasculine",
  153. "Omega", "ae", "oslash", "questiondown",
  154. "exclamdown", "logicalnot", "radical", "florin",
  155. "approxequal", "increment", "guillemotleft", "guillemotright",
  156. "ellipsis", "nbspace", "Agrave", "Atilde",
  157. "Otilde", "OE", "oe", "endash",
  158. "emdash", "quotedblleft", "quotedblright", "quoteleft",
  159. "quoteright", "divide", "lozenge", "ydieresis",
  160. "Ydieresis", "fraction", "currency", "guilsinglleft",
  161. "guilsinglright", "fi", "fl", "daggerdbl",
  162. "periodcentered", "quotesinglbase", "quotedblbase", "perthousand",
  163. "Acircumflex", "Ecircumflex", "Aacute", "Edieresis",
  164. "Egrave", "Iacute", "Icircumflex", "Idieresis",
  165. "Igrave", "Oacute", "Ocircumflex", "applelogo",
  166. "Ograve", "Uacute", "Ucircumflex", "Ugrave",
  167. "dotlessi", "circumflex", "tilde", "macron",
  168. "breve", "dotaccent", "ring", "cedilla",
  169. "hungarumlaut", "ogonek", "caron", "Lslash",
  170. "lslash", "Scaron", "scaron", "Zcaron",
  171. "zcaron", "brokenbar", "Eth", "eth",
  172. "Yacute", "yacute", "Thorn", "thorn",
  173. "minus", "multiply", "onesuperior", "twosuperior",
  174. "threesuperior", "onehalf", "onequarter", "threequarters",
  175. "franc", "Gbreve", "gbreve", "Idot",
  176. "Scedilla", "scedilla", "Cacute", "cacute",
  177. "Ccaron", "ccaron", "dmacron"
  178. );
  179. function getTable(){
  180. $this->parseTableEntries();
  181. return $this->directory;
  182. }
  183. function setTableOffset($offset) {
  184. $this->tableOffset = $offset;
  185. }
  186. function parse() {
  187. $this->parseTableEntries();
  188. $this->data = array();
  189. foreach($this->directory as $tag => $table) {
  190. if (empty($this->data[$tag])) {
  191. $this->readTable($tag);
  192. }
  193. }
  194. }
  195. function utf8toUnicode($str) {
  196. $len = strlen($str);
  197. $out = array();
  198. for ($i = 0; $i < $len; $i++) {
  199. $uni = -1;
  200. $h = ord($str[$i]);
  201. if ( $h <= 0x7F ) {
  202. $uni = $h;
  203. }
  204. elseif ( $h >= 0xC2 ) {
  205. if ( ($h <= 0xDF) && ($i < $len -1) )
  206. $uni = ($h & 0x1F) << 6 | (ord($str[++$i]) & 0x3F);
  207. elseif ( ($h <= 0xEF) && ($i < $len -2) )
  208. $uni = ($h & 0x0F) << 12 | (ord($str[++$i]) & 0x3F) << 6 | (ord($str[++$i]) & 0x3F);
  209. elseif ( ($h <= 0xF4) && ($i < $len -3) )
  210. $uni = ($h & 0x0F) << 18 | (ord($str[++$i]) & 0x3F) << 12 | (ord($str[++$i]) & 0x3F) << 6 | (ord($str[++$i]) & 0x3F);
  211. }
  212. if ($uni >= 0) {
  213. $out[] = $uni;
  214. }
  215. }
  216. return $out;
  217. }
  218. function lookupGlyph($gid, &$gids, &$newGlyphOffsets, $glyfOffset, $indexToLoc, $gidToCid) {
  219. $this->seek($glyfOffset + $indexToLoc[$gid]);
  220. $numberOfContours = $this->readInt16();
  221. if ($numberOfContours < 0) {
  222. $this->skip(8);
  223. $compoundOffset = 10; // 2 + 8
  224. do {
  225. $flags = $this->readUInt16();
  226. $compoundOffset += 2;
  227. $glyphIndex = $this->readUInt16();
  228. $newGlyphOffsets[$compoundOffset] = $glyphIndex;
  229. $compoundOffset += 2;
  230. if (!in_array($glyphIndex, $gids) && isset($indexToLoc[$glyphIndex])) {
  231. $code = $gidToCid[$glyphIndex];
  232. $gids[$code] = $glyphIndex;
  233. }
  234. $pos = $this->pos();
  235. $this->lookupGlyph($glyphIndex, $gids, $newGids, $glyfOffset, $indexToLoc, $gidToCid);
  236. $this->seek($pos);
  237. $offset = 0;
  238. // skip some bytes by case
  239. if ($flags & Font_Table_glyf::ARG_1_AND_2_ARE_WORDS) {
  240. $offset += 4;
  241. }
  242. else {
  243. $offset += 2;
  244. }
  245. if ($flags & Font_Table_glyf::WE_HAVE_A_SCALE) {
  246. $offset += 2;
  247. }
  248. elseif ($flags & Font_Table_glyf::WE_HAVE_AN_X_AND_Y_SCALE) {
  249. $offset += 4;
  250. }
  251. elseif ($flags & Font_Table_glyf::WE_HAVE_A_TWO_BY_TWO) {
  252. $offset += 8;
  253. }
  254. $this->skip($offset);
  255. $compoundOffset += $offset;
  256. } while ($flags & Font_Table_glyf::MORE_COMPONENTS);
  257. }
  258. }
  259. function setSubset($subset) {
  260. if ( !is_array($subset) ) {
  261. $subset = $this->utf8toUnicode($subset);
  262. }
  263. $subtable = null;
  264. foreach($this->getData("cmap", "subtables") as $_subtable) {
  265. if ($_subtable["platformID"] == 0 || $_subtable["platformID"] == 3 && $_subtable["platformSpecificID"] == 1) {
  266. $subtable = $_subtable;
  267. break;
  268. }
  269. }
  270. if (!$subtable) return;
  271. $gids = array();
  272. foreach($subset as $code) {
  273. if (!isset($subtable["glyphIndexArray"][$code])) {
  274. continue;
  275. }
  276. $gids[$code] = $subtable["glyphIndexArray"][$code];
  277. }
  278. // add compound glyphs
  279. $indexToLoc = $this->getData("loca");
  280. $glyfOffset = $this->directory["glyf"]->offset;
  281. $cidToGid = $subtable["glyphIndexArray"];
  282. $gidToCid = array_flip($cidToGid);
  283. $newGlyphOffsets = array();
  284. foreach($gids as $code => $gid) {
  285. if ($gid === null) {
  286. unset($gids[$code]);
  287. continue;
  288. }
  289. $_newGlyphOffsets = array();
  290. $this->lookupGlyph($gid, $gids, $_newGlyphOffsets, $glyfOffset, $indexToLoc, $gidToCid);
  291. if (count($_newGlyphOffsets)) {
  292. $newGlyphOffsets[$gid] = $_newGlyphOffsets;
  293. }
  294. }
  295. ksort($gids);
  296. foreach($newGlyphOffsets as $_gid => $compoundOffsets) {
  297. foreach($compoundOffsets as $offset => $gid) {
  298. $newGlyphOffsets[$_gid][$offset] = array_search($gid, array_values($gids));
  299. }
  300. }
  301. $this->compound_glyph_offsets = $newGlyphOffsets;
  302. $this->glyph_subset = $gids;
  303. $this->glyph_all = $subtable["glyphIndexArray"];
  304. }
  305. function getSubset() {
  306. if (empty($this->glyph_subset)) {
  307. return $this->glyph_all;
  308. }
  309. return $this->glyph_subset;
  310. }
  311. function encode($tags = array()){
  312. if (!self::$raw) {
  313. $tags = array_merge(array("head", "hhea", "cmap", "hmtx", "maxp", "glyf", "loca", "name", "post"), $tags);
  314. }
  315. else {
  316. $tags = array_keys($this->directory);
  317. }
  318. $num_tables = count($tags);
  319. $n = 16;// @todo
  320. Font::d("Tables : ".implode(", ", $tags));
  321. $entries = array();
  322. foreach($tags as $tag) {
  323. if (!isset($this->directory[$tag])) {
  324. Font::d(" >> '$tag' table doesn't exist");
  325. continue;
  326. }
  327. $entries[$tag] = $this->directory[$tag];
  328. }
  329. $this->header->data["numTables"] = $num_tables;
  330. $this->header->encode();
  331. $directory_offset = $this->pos();
  332. $offset = $directory_offset + $num_tables * $n;
  333. $this->seek($offset);
  334. $i = 0;
  335. foreach($entries as $tag => $entry) {
  336. $entry->encode($directory_offset + $i * $n);
  337. $i++;
  338. }
  339. }
  340. function parseHeader(){
  341. if (!empty($this->header)) {
  342. return;
  343. }
  344. $this->seek($this->tableOffset);
  345. $this->header = new Font_TrueType_Header($this);
  346. $this->header->parse();
  347. }
  348. function parseTableEntries(){
  349. $this->parseHeader();
  350. if (!empty($this->directory)) {
  351. return;
  352. }
  353. $class = get_class($this)."_Table_Directory_Entry";
  354. for($i = 0; $i < $this->header->data["numTables"]; $i++) {
  355. $entry = new $class($this);
  356. $this->directory[$entry->tag] = $entry;
  357. }
  358. }
  359. function normalizeFUnit($value, $base = 1000){
  360. return round($value * ($base / $this->getData("head", "unitsPerEm")));
  361. }
  362. protected function readTable($tag) {
  363. $this->parseTableEntries();
  364. if (!self::$raw) {
  365. $name_canon = preg_replace("/[^a-z0-9]/", "", strtolower($tag));
  366. $class_file = dirname(__FILE__)."/font_table_$name_canon.cls.php";
  367. if (!isset($this->directory[$tag]) || !file_exists($class_file)) {
  368. return;
  369. }
  370. require_once $class_file;
  371. $class = "Font_Table_$name_canon";
  372. }
  373. else {
  374. $class = "Font_Table";
  375. }
  376. $table = new $class($this->directory[$tag]);
  377. $table->parse();
  378. $this->data[$tag] = $table;
  379. }
  380. public function getTableObject($name) {
  381. return $this->data[$name];
  382. }
  383. public function getData($name, $key = null) {
  384. $this->parseTableEntries();
  385. if (empty($this->data[$name])) {
  386. $this->readTable($name);
  387. }
  388. if (!isset($this->data[$name])) {
  389. return null;
  390. }
  391. if (!$key) {
  392. return $this->data[$name]->data;
  393. }
  394. else {
  395. return $this->data[$name]->data[$key];
  396. }
  397. }
  398. function saveAdobeFontMetrics($file, $encoding = null) {
  399. $afm = new Adobe_Font_Metrics($this);
  400. $afm->write($file, $encoding);
  401. }
  402. function reduce(){
  403. $names_to_keep = array(0, 1, 2, 3, 4, 5, 6);
  404. foreach($this->data["name"]->data["records"] as $id => $rec) {
  405. if (in_array($id, $names_to_keep)) continue;
  406. unset($this->data["name"]->data["records"][$id]);
  407. }
  408. }
  409. }