PageRenderTime 39ms CodeModel.GetById 42ms RepoModel.GetById 1ms app.codeStats 1ms

/lib/tcpdf/include/tcpdf_fonts.php

https://bitbucket.org/moodle/moodle
PHP | 2656 lines | 1964 code | 104 blank | 588 comment | 554 complexity | c3c2d5f48c4a02a78013c071086a65d3 MD5 | raw file
Possible License(s): Apache-2.0, LGPL-2.1, BSD-3-Clause, MIT, GPL-3.0

Large files files are truncated, but you can click here to view the full file

  1. <?php
  2. //============================================================+
  3. // File name : tcpdf_fonts.php
  4. // Version : 1.1.0
  5. // Begin : 2008-01-01
  6. // Last Update : 2014-12-10
  7. // Author : Nicola Asuni - Tecnick.com LTD - www.tecnick.com - info@tecnick.com
  8. // License : GNU-LGPL v3 (http://www.gnu.org/copyleft/lesser.html)
  9. // -------------------------------------------------------------------
  10. // Copyright (C) 2008-2014 Nicola Asuni - Tecnick.com LTD
  11. //
  12. // This file is part of TCPDF software library.
  13. //
  14. // TCPDF is free software: you can redistribute it and/or modify it
  15. // under the terms of the GNU Lesser General Public License as
  16. // published by the Free Software Foundation, either version 3 of the
  17. // License, or (at your option) any later version.
  18. //
  19. // TCPDF is distributed in the hope that it will be useful, but
  20. // WITHOUT ANY WARRANTY; without even the implied warranty of
  21. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  22. // See the GNU Lesser General Public License for more details.
  23. //
  24. // You should have received a copy of the GNU Lesser General Public License
  25. // along with TCPDF. If not, see <http://www.gnu.org/licenses/>.
  26. //
  27. // See LICENSE.TXT file for more information.
  28. // -------------------------------------------------------------------
  29. //
  30. // Description :Font methods for TCPDF library.
  31. //
  32. //============================================================+
  33. /**
  34. * @file
  35. * Unicode data and font methods for TCPDF library.
  36. * @author Nicola Asuni
  37. * @package com.tecnick.tcpdf
  38. */
  39. /**
  40. * @class TCPDF_FONTS
  41. * Font methods for TCPDF library.
  42. * @package com.tecnick.tcpdf
  43. * @version 1.1.0
  44. * @author Nicola Asuni - info@tecnick.com
  45. */
  46. class TCPDF_FONTS {
  47. /**
  48. * Static cache used for speed up uniord performances
  49. * @protected
  50. */
  51. protected static $cache_uniord = array();
  52. /**
  53. * Convert and add the selected TrueType or Type1 font to the fonts folder (that must be writeable).
  54. * @param $fontfile (string) Font file (full path).
  55. * @param $fonttype (string) Font type. Leave empty for autodetect mode. Valid values are: TrueTypeUnicode, TrueType, Type1, CID0JP = CID-0 Japanese, CID0KR = CID-0 Korean, CID0CS = CID-0 Chinese Simplified, CID0CT = CID-0 Chinese Traditional.
  56. * @param $enc (string) Name of the encoding table to use. Leave empty for default mode. Omit this parameter for TrueType Unicode and symbolic fonts like Symbol or ZapfDingBats.
  57. * @param $flags (int) Unsigned 32-bit integer containing flags specifying various characteristics of the font (PDF32000:2008 - 9.8.2 Font Descriptor Flags): +1 for fixed font; +4 for symbol or +32 for non-symbol; +64 for italic. Fixed and Italic mode are generally autodetected so you have to set it to 32 = non-symbolic font (default) or 4 = symbolic font.
  58. * @param $outpath (string) Output path for generated font files (must be writeable by the web server). Leave empty for default font folder.
  59. * @param $platid (int) Platform ID for CMAP table to extract (when building a Unicode font for Windows this value should be 3, for Macintosh should be 1).
  60. * @param $encid (int) Encoding ID for CMAP table to extract (when building a Unicode font for Windows this value should be 1, for Macintosh should be 0). When Platform ID is 3, legal values for Encoding ID are: 0=Symbol, 1=Unicode, 2=ShiftJIS, 3=PRC, 4=Big5, 5=Wansung, 6=Johab, 7=Reserved, 8=Reserved, 9=Reserved, 10=UCS-4.
  61. * @param $addcbbox (boolean) If true includes the character bounding box information on the php font file.
  62. * @param $link (boolean) If true link to system font instead of copying the font data (not transportable) - Note: do not work with Type1 fonts.
  63. * @return (string) TCPDF font name or boolean false in case of error.
  64. * @author Nicola Asuni
  65. * @since 5.9.123 (2010-09-30)
  66. * @public static
  67. */
  68. public static function addTTFfont($fontfile, $fonttype='', $enc='', $flags=32, $outpath='', $platid=3, $encid=1, $addcbbox=false, $link=false) {
  69. if (!TCPDF_STATIC::file_exists($fontfile)) {
  70. // Could not find file
  71. return false;
  72. }
  73. // font metrics
  74. $fmetric = array();
  75. // build new font name for TCPDF compatibility
  76. $font_path_parts = pathinfo($fontfile);
  77. if (!isset($font_path_parts['filename'])) {
  78. $font_path_parts['filename'] = substr($font_path_parts['basename'], 0, -(strlen($font_path_parts['extension']) + 1));
  79. }
  80. $font_name = strtolower($font_path_parts['filename']);
  81. $font_name = preg_replace('/[^a-z0-9_]/', '', $font_name);
  82. $search = array('bold', 'oblique', 'italic', 'regular');
  83. $replace = array('b', 'i', 'i', '');
  84. $font_name = str_replace($search, $replace, $font_name);
  85. if (empty($font_name)) {
  86. // set generic name
  87. $font_name = 'tcpdffont';
  88. }
  89. // set output path
  90. if (empty($outpath)) {
  91. $outpath = self::_getfontpath();
  92. }
  93. // check if this font already exist
  94. if (@TCPDF_STATIC::file_exists($outpath.$font_name.'.php')) {
  95. // this font already exist (delete it from fonts folder to rebuild it)
  96. return $font_name;
  97. }
  98. $fmetric['file'] = $font_name;
  99. $fmetric['ctg'] = $font_name.'.ctg.z';
  100. // get font data
  101. $font = file_get_contents($fontfile);
  102. $fmetric['originalsize'] = strlen($font);
  103. // autodetect font type
  104. if (empty($fonttype)) {
  105. if (TCPDF_STATIC::_getULONG($font, 0) == 0x10000) {
  106. // True Type (Unicode or not)
  107. $fonttype = 'TrueTypeUnicode';
  108. } elseif (substr($font, 0, 4) == 'OTTO') {
  109. // Open Type (Unicode or not)
  110. //Unsupported font format: OpenType with CFF data
  111. return false;
  112. } else {
  113. // Type 1
  114. $fonttype = 'Type1';
  115. }
  116. }
  117. // set font type
  118. switch ($fonttype) {
  119. case 'CID0CT':
  120. case 'CID0CS':
  121. case 'CID0KR':
  122. case 'CID0JP': {
  123. $fmetric['type'] = 'cidfont0';
  124. break;
  125. }
  126. case 'Type1': {
  127. $fmetric['type'] = 'Type1';
  128. if (empty($enc) AND (($flags & 4) == 0)) {
  129. $enc = 'cp1252';
  130. }
  131. break;
  132. }
  133. case 'TrueType': {
  134. $fmetric['type'] = 'TrueType';
  135. break;
  136. }
  137. case 'TrueTypeUnicode':
  138. default: {
  139. $fmetric['type'] = 'TrueTypeUnicode';
  140. break;
  141. }
  142. }
  143. // set encoding maps (if any)
  144. $fmetric['enc'] = preg_replace('/[^A-Za-z0-9_\-]/', '', $enc);
  145. $fmetric['diff'] = '';
  146. if (($fmetric['type'] == 'TrueType') OR ($fmetric['type'] == 'Type1')) {
  147. if (!empty($enc) AND ($enc != 'cp1252') AND isset(TCPDF_FONT_DATA::$encmap[$enc])) {
  148. // build differences from reference encoding
  149. $enc_ref = TCPDF_FONT_DATA::$encmap['cp1252'];
  150. $enc_target = TCPDF_FONT_DATA::$encmap[$enc];
  151. $last = 0;
  152. for ($i = 32; $i <= 255; ++$i) {
  153. if ($enc_target[$i] != $enc_ref[$i]) {
  154. if ($i != ($last + 1)) {
  155. $fmetric['diff'] .= $i.' ';
  156. }
  157. $last = $i;
  158. $fmetric['diff'] .= '/'.$enc_target[$i].' ';
  159. }
  160. }
  161. }
  162. }
  163. // parse the font by type
  164. if ($fmetric['type'] == 'Type1') {
  165. // ---------- TYPE 1 ----------
  166. // read first segment
  167. $a = unpack('Cmarker/Ctype/Vsize', substr($font, 0, 6));
  168. if ($a['marker'] != 128) {
  169. // Font file is not a valid binary Type1
  170. return false;
  171. }
  172. $fmetric['size1'] = $a['size'];
  173. $data = substr($font, 6, $fmetric['size1']);
  174. // read second segment
  175. $a = unpack('Cmarker/Ctype/Vsize', substr($font, (6 + $fmetric['size1']), 6));
  176. if ($a['marker'] != 128) {
  177. // Font file is not a valid binary Type1
  178. return false;
  179. }
  180. $fmetric['size2'] = $a['size'];
  181. $encrypted = substr($font, (12 + $fmetric['size1']), $fmetric['size2']);
  182. $data .= $encrypted;
  183. // store compressed font
  184. $fmetric['file'] .= '.z';
  185. $fp = TCPDF_STATIC::fopenLocal($outpath.$fmetric['file'], 'wb');
  186. fwrite($fp, gzcompress($data));
  187. fclose($fp);
  188. // get font info
  189. $fmetric['Flags'] = $flags;
  190. preg_match ('#/FullName[\s]*\(([^\)]*)#', $font, $matches);
  191. $fmetric['name'] = preg_replace('/[^a-zA-Z0-9_\-]/', '', $matches[1]);
  192. preg_match('#/FontBBox[\s]*{([^}]*)#', $font, $matches);
  193. $fmetric['bbox'] = trim($matches[1]);
  194. $bv = explode(' ', $fmetric['bbox']);
  195. $fmetric['Ascent'] = intval($bv[3]);
  196. $fmetric['Descent'] = intval($bv[1]);
  197. preg_match('#/ItalicAngle[\s]*([0-9\+\-]*)#', $font, $matches);
  198. $fmetric['italicAngle'] = intval($matches[1]);
  199. if ($fmetric['italicAngle'] != 0) {
  200. $fmetric['Flags'] |= 64;
  201. }
  202. preg_match('#/UnderlinePosition[\s]*([0-9\+\-]*)#', $font, $matches);
  203. $fmetric['underlinePosition'] = intval($matches[1]);
  204. preg_match('#/UnderlineThickness[\s]*([0-9\+\-]*)#', $font, $matches);
  205. $fmetric['underlineThickness'] = intval($matches[1]);
  206. preg_match('#/isFixedPitch[\s]*([^\s]*)#', $font, $matches);
  207. if ($matches[1] == 'true') {
  208. $fmetric['Flags'] |= 1;
  209. }
  210. // get internal map
  211. $imap = array();
  212. if (preg_match_all('#dup[\s]([0-9]+)[\s]*/([^\s]*)[\s]put#sU', $font, $fmap, PREG_SET_ORDER) > 0) {
  213. foreach ($fmap as $v) {
  214. $imap[$v[2]] = $v[1];
  215. }
  216. }
  217. // decrypt eexec encrypted part
  218. $r = 55665; // eexec encryption constant
  219. $c1 = 52845;
  220. $c2 = 22719;
  221. $elen = strlen($encrypted);
  222. $eplain = '';
  223. for ($i = 0; $i < $elen; ++$i) {
  224. $chr = ord($encrypted[$i]);
  225. $eplain .= chr($chr ^ ($r >> 8));
  226. $r = ((($chr + $r) * $c1 + $c2) % 65536);
  227. }
  228. if (preg_match('#/ForceBold[\s]*([^\s]*)#', $eplain, $matches) > 0) {
  229. if ($matches[1] == 'true') {
  230. $fmetric['Flags'] |= 0x40000;
  231. }
  232. }
  233. if (preg_match('#/StdVW[\s]*\[([^\]]*)#', $eplain, $matches) > 0) {
  234. $fmetric['StemV'] = intval($matches[1]);
  235. } else {
  236. $fmetric['StemV'] = 70;
  237. }
  238. if (preg_match('#/StdHW[\s]*\[([^\]]*)#', $eplain, $matches) > 0) {
  239. $fmetric['StemH'] = intval($matches[1]);
  240. } else {
  241. $fmetric['StemH'] = 30;
  242. }
  243. if (preg_match('#/BlueValues[\s]*\[([^\]]*)#', $eplain, $matches) > 0) {
  244. $bv = explode(' ', $matches[1]);
  245. if (count($bv) >= 6) {
  246. $v1 = intval($bv[2]);
  247. $v2 = intval($bv[4]);
  248. if ($v1 <= $v2) {
  249. $fmetric['XHeight'] = $v1;
  250. $fmetric['CapHeight'] = $v2;
  251. } else {
  252. $fmetric['XHeight'] = $v2;
  253. $fmetric['CapHeight'] = $v1;
  254. }
  255. } else {
  256. $fmetric['XHeight'] = 450;
  257. $fmetric['CapHeight'] = 700;
  258. }
  259. } else {
  260. $fmetric['XHeight'] = 450;
  261. $fmetric['CapHeight'] = 700;
  262. }
  263. // get the number of random bytes at the beginning of charstrings
  264. if (preg_match('#/lenIV[\s]*([0-9]*)#', $eplain, $matches) > 0) {
  265. $lenIV = intval($matches[1]);
  266. } else {
  267. $lenIV = 4;
  268. }
  269. $fmetric['Leading'] = 0;
  270. // get charstring data
  271. $eplain = substr($eplain, (strpos($eplain, '/CharStrings') + 1));
  272. preg_match_all('#/([A-Za-z0-9\.]*)[\s][0-9]+[\s]RD[\s](.*)[\s]ND#sU', $eplain, $matches, PREG_SET_ORDER);
  273. if (!empty($enc) AND isset(TCPDF_FONT_DATA::$encmap[$enc])) {
  274. $enc_map = TCPDF_FONT_DATA::$encmap[$enc];
  275. } else {
  276. $enc_map = false;
  277. }
  278. $fmetric['cw'] = '';
  279. $fmetric['MaxWidth'] = 0;
  280. $cwidths = array();
  281. foreach ($matches as $k => $v) {
  282. $cid = 0;
  283. if (isset($imap[$v[1]])) {
  284. $cid = $imap[$v[1]];
  285. } elseif ($enc_map !== false) {
  286. $cid = array_search($v[1], $enc_map);
  287. if ($cid === false) {
  288. $cid = 0;
  289. } elseif ($cid > 1000) {
  290. $cid -= 1000;
  291. }
  292. }
  293. // decrypt charstring encrypted part
  294. $r = 4330; // charstring encryption constant
  295. $c1 = 52845;
  296. $c2 = 22719;
  297. $cd = $v[2];
  298. $clen = strlen($cd);
  299. $ccom = array();
  300. for ($i = 0; $i < $clen; ++$i) {
  301. $chr = ord($cd[$i]);
  302. $ccom[] = ($chr ^ ($r >> 8));
  303. $r = ((($chr + $r) * $c1 + $c2) % 65536);
  304. }
  305. // decode numbers
  306. $cdec = array();
  307. $ck = 0;
  308. $i = $lenIV;
  309. while ($i < $clen) {
  310. if ($ccom[$i] < 32) {
  311. $cdec[$ck] = $ccom[$i];
  312. if (($ck > 0) AND ($cdec[$ck] == 13)) {
  313. // hsbw command: update width
  314. $cwidths[$cid] = $cdec[($ck - 1)];
  315. }
  316. ++$i;
  317. } elseif (($ccom[$i] >= 32) AND ($ccom[$i] <= 246)) {
  318. $cdec[$ck] = ($ccom[$i] - 139);
  319. ++$i;
  320. } elseif (($ccom[$i] >= 247) AND ($ccom[$i] <= 250)) {
  321. $cdec[$ck] = ((($ccom[$i] - 247) * 256) + $ccom[($i + 1)] + 108);
  322. $i += 2;
  323. } elseif (($ccom[$i] >= 251) AND ($ccom[$i] <= 254)) {
  324. $cdec[$ck] = ((-($ccom[$i] - 251) * 256) - $ccom[($i + 1)] - 108);
  325. $i += 2;
  326. } elseif ($ccom[$i] == 255) {
  327. $sval = chr($ccom[($i + 1)]).chr($ccom[($i + 2)]).chr($ccom[($i + 3)]).chr($ccom[($i + 4)]);
  328. $vsval = unpack('li', $sval);
  329. $cdec[$ck] = $vsval['i'];
  330. $i += 5;
  331. }
  332. ++$ck;
  333. }
  334. } // end for each matches
  335. $fmetric['MissingWidth'] = $cwidths[0];
  336. $fmetric['MaxWidth'] = $fmetric['MissingWidth'];
  337. $fmetric['AvgWidth'] = 0;
  338. // set chars widths
  339. for ($cid = 0; $cid <= 255; ++$cid) {
  340. if (isset($cwidths[$cid])) {
  341. if ($cwidths[$cid] > $fmetric['MaxWidth']) {
  342. $fmetric['MaxWidth'] = $cwidths[$cid];
  343. }
  344. $fmetric['AvgWidth'] += $cwidths[$cid];
  345. $fmetric['cw'] .= ','.$cid.'=>'.$cwidths[$cid];
  346. } else {
  347. $fmetric['cw'] .= ','.$cid.'=>'.$fmetric['MissingWidth'];
  348. }
  349. }
  350. $fmetric['AvgWidth'] = round($fmetric['AvgWidth'] / count($cwidths));
  351. } else {
  352. // ---------- TRUE TYPE ----------
  353. $offset = 0; // offset position of the font data
  354. if (TCPDF_STATIC::_getULONG($font, $offset) != 0x10000) {
  355. // sfnt version must be 0x00010000 for TrueType version 1.0.
  356. return false;
  357. }
  358. if ($fmetric['type'] != 'cidfont0') {
  359. if ($link) {
  360. // creates a symbolic link to the existing font
  361. symlink($fontfile, $outpath.$fmetric['file']);
  362. } else {
  363. // store compressed font
  364. $fmetric['file'] .= '.z';
  365. $fp = TCPDF_STATIC::fopenLocal($outpath.$fmetric['file'], 'wb');
  366. fwrite($fp, gzcompress($font));
  367. fclose($fp);
  368. }
  369. }
  370. $offset += 4;
  371. // get number of tables
  372. $numTables = TCPDF_STATIC::_getUSHORT($font, $offset);
  373. $offset += 2;
  374. // skip searchRange, entrySelector and rangeShift
  375. $offset += 6;
  376. // tables array
  377. $table = array();
  378. // ---------- get tables ----------
  379. for ($i = 0; $i < $numTables; ++$i) {
  380. // get table info
  381. $tag = substr($font, $offset, 4);
  382. $offset += 4;
  383. $table[$tag] = array();
  384. $table[$tag]['checkSum'] = TCPDF_STATIC::_getULONG($font, $offset);
  385. $offset += 4;
  386. $table[$tag]['offset'] = TCPDF_STATIC::_getULONG($font, $offset);
  387. $offset += 4;
  388. $table[$tag]['length'] = TCPDF_STATIC::_getULONG($font, $offset);
  389. $offset += 4;
  390. }
  391. // check magicNumber
  392. $offset = $table['head']['offset'] + 12;
  393. if (TCPDF_STATIC::_getULONG($font, $offset) != 0x5F0F3CF5) {
  394. // magicNumber must be 0x5F0F3CF5
  395. return false;
  396. }
  397. $offset += 4;
  398. $offset += 2; // skip flags
  399. // get FUnits
  400. $fmetric['unitsPerEm'] = TCPDF_STATIC::_getUSHORT($font, $offset);
  401. $offset += 2;
  402. // units ratio constant
  403. $urk = (1000 / $fmetric['unitsPerEm']);
  404. $offset += 16; // skip created, modified
  405. $xMin = round(TCPDF_STATIC::_getFWORD($font, $offset) * $urk);
  406. $offset += 2;
  407. $yMin = round(TCPDF_STATIC::_getFWORD($font, $offset) * $urk);
  408. $offset += 2;
  409. $xMax = round(TCPDF_STATIC::_getFWORD($font, $offset) * $urk);
  410. $offset += 2;
  411. $yMax = round(TCPDF_STATIC::_getFWORD($font, $offset) * $urk);
  412. $offset += 2;
  413. $fmetric['bbox'] = ''.$xMin.' '.$yMin.' '.$xMax.' '.$yMax.'';
  414. $macStyle = TCPDF_STATIC::_getUSHORT($font, $offset);
  415. $offset += 2;
  416. // PDF font flags
  417. $fmetric['Flags'] = $flags;
  418. if (($macStyle & 2) == 2) {
  419. // italic flag
  420. $fmetric['Flags'] |= 64;
  421. }
  422. // get offset mode (indexToLocFormat : 0 = short, 1 = long)
  423. $offset = $table['head']['offset'] + 50;
  424. $short_offset = (TCPDF_STATIC::_getSHORT($font, $offset) == 0);
  425. $offset += 2;
  426. // get the offsets to the locations of the glyphs in the font, relative to the beginning of the glyphData table
  427. $indexToLoc = array();
  428. $offset = $table['loca']['offset'];
  429. if ($short_offset) {
  430. // short version
  431. $tot_num_glyphs = floor($table['loca']['length'] / 2); // numGlyphs + 1
  432. for ($i = 0; $i < $tot_num_glyphs; ++$i) {
  433. $indexToLoc[$i] = TCPDF_STATIC::_getUSHORT($font, $offset) * 2;
  434. if (isset($indexToLoc[($i - 1)]) && ($indexToLoc[$i] == $indexToLoc[($i - 1)])) {
  435. // the last glyph didn't have an outline
  436. unset($indexToLoc[($i - 1)]);
  437. }
  438. $offset += 2;
  439. }
  440. } else {
  441. // long version
  442. $tot_num_glyphs = floor($table['loca']['length'] / 4); // numGlyphs + 1
  443. for ($i = 0; $i < $tot_num_glyphs; ++$i) {
  444. $indexToLoc[$i] = TCPDF_STATIC::_getULONG($font, $offset);
  445. if (isset($indexToLoc[($i - 1)]) && ($indexToLoc[$i] == $indexToLoc[($i - 1)])) {
  446. // the last glyph didn't have an outline
  447. unset($indexToLoc[($i - 1)]);
  448. }
  449. $offset += 4;
  450. }
  451. }
  452. // get glyphs indexes of chars from cmap table
  453. $offset = $table['cmap']['offset'] + 2;
  454. $numEncodingTables = TCPDF_STATIC::_getUSHORT($font, $offset);
  455. $offset += 2;
  456. $encodingTables = array();
  457. for ($i = 0; $i < $numEncodingTables; ++$i) {
  458. $encodingTables[$i]['platformID'] = TCPDF_STATIC::_getUSHORT($font, $offset);
  459. $offset += 2;
  460. $encodingTables[$i]['encodingID'] = TCPDF_STATIC::_getUSHORT($font, $offset);
  461. $offset += 2;
  462. $encodingTables[$i]['offset'] = TCPDF_STATIC::_getULONG($font, $offset);
  463. $offset += 4;
  464. }
  465. // ---------- get os/2 metrics ----------
  466. $offset = $table['OS/2']['offset'];
  467. $offset += 2; // skip version
  468. // xAvgCharWidth
  469. $fmetric['AvgWidth'] = round(TCPDF_STATIC::_getFWORD($font, $offset) * $urk);
  470. $offset += 2;
  471. // usWeightClass
  472. $usWeightClass = round(TCPDF_STATIC::_getUFWORD($font, $offset) * $urk);
  473. // estimate StemV and StemH (400 = usWeightClass for Normal - Regular font)
  474. $fmetric['StemV'] = round((70 * $usWeightClass) / 400);
  475. $fmetric['StemH'] = round((30 * $usWeightClass) / 400);
  476. $offset += 2;
  477. $offset += 2; // usWidthClass
  478. $fsType = TCPDF_STATIC::_getSHORT($font, $offset);
  479. $offset += 2;
  480. if ($fsType == 2) {
  481. // This Font cannot be modified, embedded or exchanged in any manner without first obtaining permission of the legal owner.
  482. return false;
  483. }
  484. // ---------- get font name ----------
  485. $fmetric['name'] = '';
  486. $offset = $table['name']['offset'];
  487. $offset += 2; // skip Format selector (=0).
  488. // Number of NameRecords that follow n.
  489. $numNameRecords = TCPDF_STATIC::_getUSHORT($font, $offset);
  490. $offset += 2;
  491. // Offset to start of string storage (from start of table).
  492. $stringStorageOffset = TCPDF_STATIC::_getUSHORT($font, $offset);
  493. $offset += 2;
  494. for ($i = 0; $i < $numNameRecords; ++$i) {
  495. $offset += 6; // skip Platform ID, Platform-specific encoding ID, Language ID.
  496. // Name ID.
  497. $nameID = TCPDF_STATIC::_getUSHORT($font, $offset);
  498. $offset += 2;
  499. if ($nameID == 6) {
  500. // String length (in bytes).
  501. $stringLength = TCPDF_STATIC::_getUSHORT($font, $offset);
  502. $offset += 2;
  503. // String offset from start of storage area (in bytes).
  504. $stringOffset = TCPDF_STATIC::_getUSHORT($font, $offset);
  505. $offset += 2;
  506. $offset = ($table['name']['offset'] + $stringStorageOffset + $stringOffset);
  507. $fmetric['name'] = substr($font, $offset, $stringLength);
  508. $fmetric['name'] = preg_replace('/[^a-zA-Z0-9_\-]/', '', $fmetric['name']);
  509. break;
  510. } else {
  511. $offset += 4; // skip String length, String offset
  512. }
  513. }
  514. if (empty($fmetric['name'])) {
  515. $fmetric['name'] = $font_name;
  516. }
  517. // ---------- get post data ----------
  518. $offset = $table['post']['offset'];
  519. $offset += 4; // skip Format Type
  520. $fmetric['italicAngle'] = TCPDF_STATIC::_getFIXED($font, $offset);
  521. $offset += 4;
  522. $fmetric['underlinePosition'] = round(TCPDF_STATIC::_getFWORD($font, $offset) * $urk);
  523. $offset += 2;
  524. $fmetric['underlineThickness'] = round(TCPDF_STATIC::_getFWORD($font, $offset) * $urk);
  525. $offset += 2;
  526. $isFixedPitch = (TCPDF_STATIC::_getULONG($font, $offset) == 0) ? false : true;
  527. $offset += 2;
  528. if ($isFixedPitch) {
  529. $fmetric['Flags'] |= 1;
  530. }
  531. // ---------- get hhea data ----------
  532. $offset = $table['hhea']['offset'];
  533. $offset += 4; // skip Table version number
  534. // Ascender
  535. $fmetric['Ascent'] = round(TCPDF_STATIC::_getFWORD($font, $offset) * $urk);
  536. $offset += 2;
  537. // Descender
  538. $fmetric['Descent'] = round(TCPDF_STATIC::_getFWORD($font, $offset) * $urk);
  539. $offset += 2;
  540. // LineGap
  541. $fmetric['Leading'] = round(TCPDF_STATIC::_getFWORD($font, $offset) * $urk);
  542. $offset += 2;
  543. // advanceWidthMax
  544. $fmetric['MaxWidth'] = round(TCPDF_STATIC::_getUFWORD($font, $offset) * $urk);
  545. $offset += 2;
  546. $offset += 22; // skip some values
  547. // get the number of hMetric entries in hmtx table
  548. $numberOfHMetrics = TCPDF_STATIC::_getUSHORT($font, $offset);
  549. // ---------- get maxp data ----------
  550. $offset = $table['maxp']['offset'];
  551. $offset += 4; // skip Table version number
  552. // get the the number of glyphs in the font.
  553. $numGlyphs = TCPDF_STATIC::_getUSHORT($font, $offset);
  554. // ---------- get CIDToGIDMap ----------
  555. $ctg = array();
  556. $c = 0;
  557. foreach ($encodingTables as $enctable) {
  558. // get only specified Platform ID and Encoding ID
  559. if (($enctable['platformID'] == $platid) AND ($enctable['encodingID'] == $encid)) {
  560. $offset = $table['cmap']['offset'] + $enctable['offset'];
  561. $format = TCPDF_STATIC::_getUSHORT($font, $offset);
  562. $offset += 2;
  563. switch ($format) {
  564. case 0: { // Format 0: Byte encoding table
  565. $offset += 4; // skip length and version/language
  566. for ($c = 0; $c < 256; ++$c) {
  567. $g = TCPDF_STATIC::_getBYTE($font, $offset);
  568. $ctg[$c] = $g;
  569. ++$offset;
  570. }
  571. break;
  572. }
  573. case 2: { // Format 2: High-byte mapping through table
  574. $offset += 4; // skip length and version/language
  575. $numSubHeaders = 0;
  576. for ($i = 0; $i < 256; ++$i) {
  577. // Array that maps high bytes to subHeaders: value is subHeader index * 8.
  578. $subHeaderKeys[$i] = (TCPDF_STATIC::_getUSHORT($font, $offset) / 8);
  579. $offset += 2;
  580. if ($numSubHeaders < $subHeaderKeys[$i]) {
  581. $numSubHeaders = $subHeaderKeys[$i];
  582. }
  583. }
  584. // the number of subHeaders is equal to the max of subHeaderKeys + 1
  585. ++$numSubHeaders;
  586. // read subHeader structures
  587. $subHeaders = array();
  588. $numGlyphIndexArray = 0;
  589. for ($k = 0; $k < $numSubHeaders; ++$k) {
  590. $subHeaders[$k]['firstCode'] = TCPDF_STATIC::_getUSHORT($font, $offset);
  591. $offset += 2;
  592. $subHeaders[$k]['entryCount'] = TCPDF_STATIC::_getUSHORT($font, $offset);
  593. $offset += 2;
  594. $subHeaders[$k]['idDelta'] = TCPDF_STATIC::_getUSHORT($font, $offset);
  595. $offset += 2;
  596. $subHeaders[$k]['idRangeOffset'] = TCPDF_STATIC::_getUSHORT($font, $offset);
  597. $offset += 2;
  598. $subHeaders[$k]['idRangeOffset'] -= (2 + (($numSubHeaders - $k - 1) * 8));
  599. $subHeaders[$k]['idRangeOffset'] /= 2;
  600. $numGlyphIndexArray += $subHeaders[$k]['entryCount'];
  601. }
  602. for ($k = 0; $k < $numGlyphIndexArray; ++$k) {
  603. $glyphIndexArray[$k] = TCPDF_STATIC::_getUSHORT($font, $offset);
  604. $offset += 2;
  605. }
  606. for ($i = 0; $i < 256; ++$i) {
  607. $k = $subHeaderKeys[$i];
  608. if ($k == 0) {
  609. // one byte code
  610. $c = $i;
  611. $g = $glyphIndexArray[0];
  612. $ctg[$c] = $g;
  613. } else {
  614. // two bytes code
  615. $start_byte = $subHeaders[$k]['firstCode'];
  616. $end_byte = $start_byte + $subHeaders[$k]['entryCount'];
  617. for ($j = $start_byte; $j < $end_byte; ++$j) {
  618. // combine high and low bytes
  619. $c = (($i << 8) + $j);
  620. $idRangeOffset = ($subHeaders[$k]['idRangeOffset'] + $j - $subHeaders[$k]['firstCode']);
  621. $g = ($glyphIndexArray[$idRangeOffset] + $subHeaders[$k]['idDelta']) % 65536;
  622. if ($g < 0) {
  623. $g = 0;
  624. }
  625. $ctg[$c] = $g;
  626. }
  627. }
  628. }
  629. break;
  630. }
  631. case 4: { // Format 4: Segment mapping to delta values
  632. $length = TCPDF_STATIC::_getUSHORT($font, $offset);
  633. $offset += 2;
  634. $offset += 2; // skip version/language
  635. $segCount = floor(TCPDF_STATIC::_getUSHORT($font, $offset) / 2);
  636. $offset += 2;
  637. $offset += 6; // skip searchRange, entrySelector, rangeShift
  638. $endCount = array(); // array of end character codes for each segment
  639. for ($k = 0; $k < $segCount; ++$k) {
  640. $endCount[$k] = TCPDF_STATIC::_getUSHORT($font, $offset);
  641. $offset += 2;
  642. }
  643. $offset += 2; // skip reservedPad
  644. $startCount = array(); // array of start character codes for each segment
  645. for ($k = 0; $k < $segCount; ++$k) {
  646. $startCount[$k] = TCPDF_STATIC::_getUSHORT($font, $offset);
  647. $offset += 2;
  648. }
  649. $idDelta = array(); // delta for all character codes in segment
  650. for ($k = 0; $k < $segCount; ++$k) {
  651. $idDelta[$k] = TCPDF_STATIC::_getUSHORT($font, $offset);
  652. $offset += 2;
  653. }
  654. $idRangeOffset = array(); // Offsets into glyphIdArray or 0
  655. for ($k = 0; $k < $segCount; ++$k) {
  656. $idRangeOffset[$k] = TCPDF_STATIC::_getUSHORT($font, $offset);
  657. $offset += 2;
  658. }
  659. $gidlen = (floor($length / 2) - 8 - (4 * $segCount));
  660. $glyphIdArray = array(); // glyph index array
  661. for ($k = 0; $k < $gidlen; ++$k) {
  662. $glyphIdArray[$k] = TCPDF_STATIC::_getUSHORT($font, $offset);
  663. $offset += 2;
  664. }
  665. for ($k = 0; $k < $segCount - 1; ++$k) {
  666. for ($c = $startCount[$k]; $c <= $endCount[$k]; ++$c) {
  667. if ($idRangeOffset[$k] == 0) {
  668. $g = ($idDelta[$k] + $c) % 65536;
  669. } else {
  670. $gid = (floor($idRangeOffset[$k] / 2) + ($c - $startCount[$k]) - ($segCount - $k));
  671. $g = ($glyphIdArray[$gid] + $idDelta[$k]) % 65536;
  672. }
  673. if ($g < 0) {
  674. $g = 0;
  675. }
  676. $ctg[$c] = $g;
  677. }
  678. }
  679. break;
  680. }
  681. case 6: { // Format 6: Trimmed table mapping
  682. $offset += 4; // skip length and version/language
  683. $firstCode = TCPDF_STATIC::_getUSHORT($font, $offset);
  684. $offset += 2;
  685. $entryCount = TCPDF_STATIC::_getUSHORT($font, $offset);
  686. $offset += 2;
  687. for ($k = 0; $k < $entryCount; ++$k) {
  688. $c = ($k + $firstCode);
  689. $g = TCPDF_STATIC::_getUSHORT($font, $offset);
  690. $offset += 2;
  691. $ctg[$c] = $g;
  692. }
  693. break;
  694. }
  695. case 8: { // Format 8: Mixed 16-bit and 32-bit coverage
  696. $offset += 10; // skip reserved, length and version/language
  697. for ($k = 0; $k < 8192; ++$k) {
  698. $is32[$k] = TCPDF_STATIC::_getBYTE($font, $offset);
  699. ++$offset;
  700. }
  701. $nGroups = TCPDF_STATIC::_getULONG($font, $offset);
  702. $offset += 4;
  703. for ($i = 0; $i < $nGroups; ++$i) {
  704. $startCharCode = TCPDF_STATIC::_getULONG($font, $offset);
  705. $offset += 4;
  706. $endCharCode = TCPDF_STATIC::_getULONG($font, $offset);
  707. $offset += 4;
  708. $startGlyphID = TCPDF_STATIC::_getULONG($font, $offset);
  709. $offset += 4;
  710. for ($k = $startCharCode; $k <= $endCharCode; ++$k) {
  711. $is32idx = floor($c / 8);
  712. if ((isset($is32[$is32idx])) AND (($is32[$is32idx] & (1 << (7 - ($c % 8)))) == 0)) {
  713. $c = $k;
  714. } else {
  715. // 32 bit format
  716. // convert to decimal (http://www.unicode.org/faq//utf_bom.html#utf16-4)
  717. //LEAD_OFFSET = (0xD800 - (0x10000 >> 10)) = 55232
  718. //SURROGATE_OFFSET = (0x10000 - (0xD800 << 10) - 0xDC00) = -56613888
  719. $c = ((55232 + ($k >> 10)) << 10) + (0xDC00 + ($k & 0x3FF)) -56613888;
  720. }
  721. $ctg[$c] = 0;
  722. ++$startGlyphID;
  723. }
  724. }
  725. break;
  726. }
  727. case 10: { // Format 10: Trimmed array
  728. $offset += 10; // skip reserved, length and version/language
  729. $startCharCode = TCPDF_STATIC::_getULONG($font, $offset);
  730. $offset += 4;
  731. $numChars = TCPDF_STATIC::_getULONG($font, $offset);
  732. $offset += 4;
  733. for ($k = 0; $k < $numChars; ++$k) {
  734. $c = ($k + $startCharCode);
  735. $g = TCPDF_STATIC::_getUSHORT($font, $offset);
  736. $ctg[$c] = $g;
  737. $offset += 2;
  738. }
  739. break;
  740. }
  741. case 12: { // Format 12: Segmented coverage
  742. $offset += 10; // skip length and version/language
  743. $nGroups = TCPDF_STATIC::_getULONG($font, $offset);
  744. $offset += 4;
  745. for ($k = 0; $k < $nGroups; ++$k) {
  746. $startCharCode = TCPDF_STATIC::_getULONG($font, $offset);
  747. $offset += 4;
  748. $endCharCode = TCPDF_STATIC::_getULONG($font, $offset);
  749. $offset += 4;
  750. $startGlyphCode = TCPDF_STATIC::_getULONG($font, $offset);
  751. $offset += 4;
  752. for ($c = $startCharCode; $c <= $endCharCode; ++$c) {
  753. $ctg[$c] = $startGlyphCode;
  754. ++$startGlyphCode;
  755. }
  756. }
  757. break;
  758. }
  759. case 13: { // Format 13: Many-to-one range mappings
  760. // to be implemented ...
  761. break;
  762. }
  763. case 14: { // Format 14: Unicode Variation Sequences
  764. // to be implemented ...
  765. break;
  766. }
  767. }
  768. }
  769. }
  770. if (!isset($ctg[0])) {
  771. $ctg[0] = 0;
  772. }
  773. // get xHeight (height of x)
  774. $offset = ($table['glyf']['offset'] + $indexToLoc[$ctg[120]] + 4);
  775. $yMin = TCPDF_STATIC::_getFWORD($font, $offset);
  776. $offset += 4;
  777. $yMax = TCPDF_STATIC::_getFWORD($font, $offset);
  778. $offset += 2;
  779. $fmetric['XHeight'] = round(($yMax - $yMin) * $urk);
  780. // get CapHeight (height of H)
  781. $offset = ($table['glyf']['offset'] + $indexToLoc[$ctg[72]] + 4);
  782. $yMin = TCPDF_STATIC::_getFWORD($font, $offset);
  783. $offset += 4;
  784. $yMax = TCPDF_STATIC::_getFWORD($font, $offset);
  785. $offset += 2;
  786. $fmetric['CapHeight'] = round(($yMax - $yMin) * $urk);
  787. // ceate widths array
  788. $cw = array();
  789. $offset = $table['hmtx']['offset'];
  790. for ($i = 0 ; $i < $numberOfHMetrics; ++$i) {
  791. $cw[$i] = round(TCPDF_STATIC::_getUFWORD($font, $offset) * $urk);
  792. $offset += 4; // skip lsb
  793. }
  794. if ($numberOfHMetrics < $numGlyphs) {
  795. // fill missing widths with the last value
  796. $cw = array_pad($cw, $numGlyphs, $cw[($numberOfHMetrics - 1)]);
  797. }
  798. $fmetric['MissingWidth'] = $cw[0];
  799. $fmetric['cw'] = '';
  800. $fmetric['cbbox'] = '';
  801. for ($cid = 0; $cid <= 65535; ++$cid) {
  802. if (isset($ctg[$cid])) {
  803. if (isset($cw[$ctg[$cid]])) {
  804. $fmetric['cw'] .= ','.$cid.'=>'.$cw[$ctg[$cid]];
  805. }
  806. if ($addcbbox AND isset($indexToLoc[$ctg[$cid]])) {
  807. $offset = ($table['glyf']['offset'] + $indexToLoc[$ctg[$cid]]);
  808. $xMin = round(TCPDF_STATIC::_getFWORD($font, $offset + 2) * $urk);
  809. $yMin = round(TCPDF_STATIC::_getFWORD($font, $offset + 4) * $urk);
  810. $xMax = round(TCPDF_STATIC::_getFWORD($font, $offset + 6) * $urk);
  811. $yMax = round(TCPDF_STATIC::_getFWORD($font, $offset + 8) * $urk);
  812. $fmetric['cbbox'] .= ','.$cid.'=>array('.$xMin.','.$yMin.','.$xMax.','.$yMax.')';
  813. }
  814. }
  815. }
  816. } // end of true type
  817. if (($fmetric['type'] == 'TrueTypeUnicode') AND (count($ctg) == 256)) {
  818. $fmetric['type'] = 'TrueType';
  819. }
  820. // ---------- create php font file ----------
  821. $pfile = '<'.'?'.'php'."\n";
  822. $pfile .= '// TCPDF FONT FILE DESCRIPTION'."\n";
  823. $pfile .= '$type=\''.$fmetric['type'].'\';'."\n";
  824. $pfile .= '$name=\''.$fmetric['name'].'\';'."\n";
  825. $pfile .= '$up='.$fmetric['underlinePosition'].';'."\n";
  826. $pfile .= '$ut='.$fmetric['underlineThickness'].';'."\n";
  827. if ($fmetric['MissingWidth'] > 0) {
  828. $pfile .= '$dw='.$fmetric['MissingWidth'].';'."\n";
  829. } else {
  830. $pfile .= '$dw='.$fmetric['AvgWidth'].';'."\n";
  831. }
  832. $pfile .= '$diff=\''.$fmetric['diff'].'\';'."\n";
  833. if ($fmetric['type'] == 'Type1') {
  834. // Type 1
  835. $pfile .= '$enc=\''.$fmetric['enc'].'\';'."\n";
  836. $pfile .= '$file=\''.$fmetric['file'].'\';'."\n";
  837. $pfile .= '$size1='.$fmetric['size1'].';'."\n";
  838. $pfile .= '$size2='.$fmetric['size2'].';'."\n";
  839. } else {
  840. $pfile .= '$originalsize='.$fmetric['originalsize'].';'."\n";
  841. if ($fmetric['type'] == 'cidfont0') {
  842. // CID-0
  843. switch ($fonttype) {
  844. case 'CID0JP': {
  845. $pfile .= '// Japanese'."\n";
  846. $pfile .= '$enc=\'UniJIS-UTF16-H\';'."\n";
  847. $pfile .= '$cidinfo=array(\'Registry\'=>\'Adobe\', \'Ordering\'=>\'Japan1\',\'Supplement\'=>5);'."\n";
  848. $pfile .= 'include(dirname(__FILE__).\'/uni2cid_aj16.php\');'."\n";
  849. break;
  850. }
  851. case 'CID0KR': {
  852. $pfile .= '// Korean'."\n";
  853. $pfile .= '$enc=\'UniKS-UTF16-H\';'."\n";
  854. $pfile .= '$cidinfo=array(\'Registry\'=>\'Adobe\', \'Ordering\'=>\'Korea1\',\'Supplement\'=>0);'."\n";
  855. $pfile .= 'include(dirname(__FILE__).\'/uni2cid_ak12.php\');'."\n";
  856. break;
  857. }
  858. case 'CID0CS': {
  859. $pfile .= '// Chinese Simplified'."\n";
  860. $pfile .= '$enc=\'UniGB-UTF16-H\';'."\n";
  861. $pfile .= '$cidinfo=array(\'Registry\'=>\'Adobe\', \'Ordering\'=>\'GB1\',\'Supplement\'=>2);'."\n";
  862. $pfile .= 'include(dirname(__FILE__).\'/uni2cid_ag15.php\');'."\n";
  863. break;
  864. }
  865. case 'CID0CT':
  866. default: {
  867. $pfile .= '// Chinese Traditional'."\n";
  868. $pfile .= '$enc=\'UniCNS-UTF16-H\';'."\n";
  869. $pfile .= '$cidinfo=array(\'Registry\'=>\'Adobe\', \'Ordering\'=>\'CNS1\',\'Supplement\'=>0);'."\n";
  870. $pfile .= 'include(dirname(__FILE__).\'/uni2cid_aj16.php\');'."\n";
  871. break;
  872. }
  873. }
  874. } else {
  875. // TrueType
  876. $pfile .= '$enc=\''.$fmetric['enc'].'\';'."\n";
  877. $pfile .= '$file=\''.$fmetric['file'].'\';'."\n";
  878. $pfile .= '$ctg=\''.$fmetric['ctg'].'\';'."\n";
  879. // create CIDToGIDMap
  880. $cidtogidmap = str_pad('', 131072, "\x00"); // (256 * 256 * 2) = 131072
  881. foreach ($ctg as $cid => $gid) {
  882. $cidtogidmap = self::updateCIDtoGIDmap($cidtogidmap, $cid, $ctg[$cid]);
  883. }
  884. // store compressed CIDToGIDMap
  885. $fp = TCPDF_STATIC::fopenLocal($outpath.$fmetric['ctg'], 'wb');
  886. fwrite($fp, gzcompress($cidtogidmap));
  887. fclose($fp);
  888. }
  889. }
  890. $pfile .= '$desc=array(';
  891. $pfile .= '\'Flags\'=>'.$fmetric['Flags'].',';
  892. $pfile .= '\'FontBBox\'=>\'['.$fmetric['bbox'].']\',';
  893. $pfile .= '\'ItalicAngle\'=>'.$fmetric['italicAngle'].',';
  894. $pfile .= '\'Ascent\'=>'.$fmetric['Ascent'].',';
  895. $pfile .= '\'Descent\'=>'.$fmetric['Descent'].',';
  896. $pfile .= '\'Leading\'=>'.$fmetric['Leading'].',';
  897. $pfile .= '\'CapHeight\'=>'.$fmetric['CapHeight'].',';
  898. $pfile .= '\'XHeight\'=>'.$fmetric['XHeight'].',';
  899. $pfile .= '\'StemV\'=>'.$fmetric['StemV'].',';
  900. $pfile .= '\'StemH\'=>'.$fmetric['StemH'].',';
  901. $pfile .= '\'AvgWidth\'=>'.$fmetric['AvgWidth'].',';
  902. $pfile .= '\'MaxWidth\'=>'.$fmetric['MaxWidth'].',';
  903. $pfile .= '\'MissingWidth\'=>'.$fmetric['MissingWidth'].'';
  904. $pfile .= ');'."\n";
  905. if (!empty($fmetric['cbbox'])) {
  906. $pfile .= '$cbbox=array('.substr($fmetric['cbbox'], 1).');'."\n";
  907. }
  908. $pfile .= '$cw=array('.substr($fmetric['cw'], 1).');'."\n";
  909. $pfile .= '// --- EOF ---'."\n";
  910. // store file
  911. $fp = TCPDF_STATIC::fopenLocal($outpath.$font_name.'.php', 'w');
  912. fwrite($fp, $pfile);
  913. fclose($fp);
  914. // return TCPDF font name
  915. return $font_name;
  916. }
  917. /**
  918. * Returs the checksum of a TTF table.
  919. * @param $table (string) table to check
  920. * @param $length (int) length of table in bytes
  921. * @return int checksum
  922. * @author Nicola Asuni
  923. * @since 5.2.000 (2010-06-02)
  924. * @public static
  925. */
  926. public static function _getTTFtableChecksum($table, $length) {
  927. $sum = 0;
  928. $tlen = ($length / 4);
  929. $offset = 0;
  930. for ($i = 0; $i < $tlen; ++$i) {
  931. $v = unpack('Ni', substr($table, $offset, 4));
  932. $sum += $v['i'];
  933. $offset += 4;
  934. }
  935. $sum = unpack('Ni', pack('N', $sum));
  936. return $sum['i'];
  937. }
  938. /**
  939. * Returns a subset of the TrueType font data without the unused glyphs.
  940. * @param $font (string) TrueType font data.
  941. * @param $subsetchars (array) Array of used characters (the glyphs to keep).
  942. * @return (string) A subset of TrueType font data without the unused glyphs.
  943. * @author Nicola Asuni
  944. * @since 5.2.000 (2010-06-02)
  945. * @public static
  946. */
  947. public static function _getTrueTypeFontSubset($font, $subsetchars) {
  948. ksort($subsetchars);
  949. $offset = 0; // offset position of the font data
  950. if (TCPDF_STATIC::_getULONG($font, $offset) != 0x10000) {
  951. // sfnt version must be 0x00010000 for TrueType version 1.0.
  952. return $font;
  953. }
  954. $c = 0;
  955. $offset += 4;
  956. // get number of tables
  957. $numTables = TCPDF_STATIC::_getUSHORT($font, $offset);
  958. $offset += 2;
  959. // skip searchRange, entrySelector and rangeShift
  960. $offset += 6;
  961. // tables array
  962. $table = array();
  963. // for each table
  964. for ($i = 0; $i < $numTables; ++$i) {
  965. // get table info
  966. $tag = substr($font, $offset, 4);
  967. $offset += 4;
  968. $table[$tag] = array();
  969. $table[$tag]['checkSum'] = TCPDF_STATIC::_getULONG($font, $offset);
  970. $offset += 4;
  971. $table[$tag]['offset'] = TCPDF_STATIC::_getULONG($font, $offset);
  972. $offset += 4;
  973. $table[$tag]['length'] = TCPDF_STATIC::_getULONG($font, $offset);
  974. $offset += 4;
  975. }
  976. // check magicNumber
  977. $offset = $table['head']['offset'] + 12;
  978. if (TCPDF_STATIC::_getULONG($font, $offset) != 0x5F0F3CF5) {
  979. // magicNumber must be 0x5F0F3CF5
  980. return $font;
  981. }
  982. $offset += 4;
  983. // get offset mode (indexToLocFormat : 0 = short, 1 = long)
  984. $offset = $table['head']['offset'] + 50;
  985. $short_offset = (TCPDF_STATIC::_getSHORT($font, $offset) == 0);
  986. $offset += 2;
  987. // get the offsets to the locations of the glyphs in the font, relative to the beginning of the glyphData table
  988. $indexToLoc = array();
  989. $offset = $table['loca']['offset'];
  990. if ($short_offset) {
  991. // short version
  992. $tot_num_glyphs = floor($table['loca']['length'] / 2); // numGlyphs + 1
  993. for ($i = 0; $i < $tot_num_glyphs; ++$i) {
  994. $indexToLoc[$i] = TCPDF_STATIC::_getUSHORT($font, $offset) * 2;
  995. $offset += 2;
  996. }
  997. } else {
  998. // long version
  999. $tot_num_glyphs = ($table['loca']['length'] / 4); // numGlyphs + 1
  1000. for ($i = 0; $i < $tot_num_glyphs; ++$i) {
  1001. $indexToLoc[$i] = TCPDF_STATIC::_getULONG($font, $offset);
  1002. $offset += 4;
  1003. }
  1004. }
  1005. // get glyphs indexes of chars from cmap table
  1006. $subsetglyphs = array(); // glyph IDs on key
  1007. $subsetglyphs[0] = true; // character codes that do not correspond to any glyph in the font should be mapped to glyph index 0
  1008. $offset = $table['cmap']['offset'] + 2;
  1009. $numEncodingTables = TCPDF_STATIC::_getUSHORT($font, $offset);
  1010. $offset += 2;
  1011. $encodingTables = array();
  1012. for ($i = 0; $i < $numEncodingTables; ++$i) {
  1013. $encodingTables[$i]['platformID'] = TCPDF_STATIC::_getUSHORT($font, $offset);
  1014. $offset += 2;
  1015. $encodingTables[$i]['encodingID'] = TCPDF_STATIC::_getUSHORT($font, $offset);
  1016. $offset += 2;
  1017. $encodingTables[$i]['offset'] = TCPDF_STATIC::_getULONG($font, $offset);
  1018. $offset += 4;
  1019. }
  1020. foreach ($encodingTables as $enctable) {
  1021. // get all platforms and encodings
  1022. $offset = $table['cmap']['offset'] + $enctable['offset'];
  1023. $format = TCPDF_STATIC::_getUSHORT($font, $offset);
  1024. $offset += 2;
  1025. switch ($format) {
  1026. case 0: { // Format 0: Byte encoding table
  1027. $offset += 4; // skip length and version/language
  1028. for ($c = 0; $c < 256; ++$c) {
  1029. if (isset($subsetchars[$c])) {
  1030. $g = TCPDF_STATIC::_getBYTE($font, $offset);
  1031. $subsetglyphs[$g] = true;
  1032. }
  1033. ++$offset;
  1034. }
  1035. break;
  1036. }
  1037. case 2: { // Format 2: High-byte mapping through table
  1038. $offset += 4; // skip length and version/language
  1039. $numSubHeaders = 0;
  1040. for ($i = 0; $i < 256; ++$i) {
  1041. // Array that maps high bytes to subHeaders: value is subHeader index * 8.
  1042. $subHeaderKeys[$i] = (TCPDF_STATIC::_getUSHORT($font, $offset) / 8);
  1043. $offset += 2;
  1044. if ($numSubHeaders < $subHeaderKeys[$i]) {
  1045. $numSubHeaders = $subHeaderKeys[$i];
  1046. }
  1047. }
  1048. // the number of subHeaders is equal to the max of subHeaderKeys + 1
  1049. ++$numSubHeaders;
  1050. // read subHeader structures
  1051. $subHeaders = array();
  1052. $numGlyphIndexArray = 0;
  1053. for ($k = 0; $k < $numSubHeaders; ++$k) {
  1054. $subHeaders[$k]['firstCode'] = TCPDF_STATIC::_getUSHORT($font, $offset);
  1055. $offset += 2;
  1056. $subHeaders[$k]['entryCount'] = TCPDF_STATIC::_getUSHORT($font, $offset);
  1057. $offset += 2;
  1058. $subHeaders[$k]['idDelta'] = TCPDF_STATIC::_getUSHORT($font, $offset);
  1059. $offset += 2;
  1060. $subHeaders[$k]['idRangeOffset'] = TCPDF_STATIC::_getUSHORT($font, $offset);
  1061. $offset += 2;
  1062. $subHeaders[$k]['idRangeOffset'] -= (2 + (($numSubHeaders - $k - 1) * 8));
  1063. $subHeaders[$k]['idRangeOffset'] /= 2;
  1064. $numGlyphIndexArray += $subHeaders[$k]['entryCount'];
  1065. }
  1066. for ($k = 0; $k < $numGlyphIndexArray; ++$k) {
  1067. $glyphIndexArray[$k] = TCPDF_STATIC::_getUSHORT($font, $offset);
  1068. $offset += 2;
  1069. }
  1070. for ($i = 0; $i < 256; ++$i) {
  1071. $k = $subHeaderKeys[$i];
  1072. if ($k == 0) {
  1073. // one byte code
  1074. $c = $i;
  1075. if (isset($subsetchars[$c])) {
  1076. $g = $glyphIndexArray[0];
  1077. $subsetglyphs[$g] = true;
  1078. }
  1079. } else {
  1080. // two bytes code
  1081. $start_byte = $subHeaders[$k]['firstCode'];
  1082. $end_byte = $start_byte + $subHeaders[$k]['entryCount'];
  1083. for ($j = $start_byte; $j < $end_byte; ++$j) {
  1084. // combine high and low bytes
  1085. $c = (($i << 8) + $j);
  1086. if (isset($subsetchars[$c])) {
  1087. $idRangeOffset = ($subHeaders[$k]['idRangeOffset'] + $j - $subHeaders[$k]['firstCode']);
  1088. $g = ($glyphIndexArray[$idRangeOffset] + $subHeaders[$k]['idDelta']) % 65536;
  1089. if ($g < 0) {
  1090. $g = 0;
  1091. }
  1092. $subsetglyphs[$g] = true;
  1093. }
  1094. }
  1095. }
  1096. }
  1097. break;
  1098. }
  1099. case 4: { // Format 4: Segment mapping to delta values
  1100. $length = TCPDF_STATIC::_getUSHORT($font, $offset);
  1101. $offset += 2;
  1102. $offset += 2; // skip version/language
  1103. $segCount = floor(TCPDF_STATIC::_getUSHORT($font, $offset) / 2);
  1104. $offset += 2;
  1105. $offset += 6; // skip searchRange, entrySelector, rangeShift
  1106. $endCount = array(); // array of end character codes for each segment
  1107. for ($k = 0; $k < $segCount; ++$k) {
  1108. $endCount[$k] = TCPDF_STATIC::_getUSHORT($font, $offset);
  1109. $offset += 2;
  1110. }
  1111. $offset += 2; // skip reservedPad
  1112. $startCount = array(); // array of start character codes for each segment
  1113. for ($k = 0; $k < $segCount; ++$k) {
  1114. $startCount[$k] = TCPDF_STATIC::_getUSHORT($font, $offset);
  1115. $offset += 2;
  1116. }
  1117. $idDelta = array(); // delta for all character codes in segment
  1118. for ($k = 0; $k < $segCount; ++$k) {
  1119. $idDelta[$k] = TCPDF_STATIC::_getUSHORT($font, $offset);
  1120. $offset += 2;
  1121. }
  1122. $idRangeOffset = array(); // Offsets into glyphIdArray or 0
  1123. for ($k = 0; $k < $segCount; ++$k) {
  1124. $idRangeOffset[$k] = TCPDF_STATIC::_getUSHORT($font, $offset);
  1125. $offset += 2;
  1126. }
  1127. $gidlen = (floor($length / 2) - 8 - (4 * $segCount));
  1128. $glyphIdArray = array(); // glyph index array
  1129. for ($k = 0; $k < $gidlen; ++$k) {
  1130. $glyphIdArray[$k] = TCPDF_STATIC::_getUSHORT($font, $offset);
  1131. $offset += 2;
  1132. }
  1133. for ($k = 0; $k < $segCount; ++$k) {
  1134. for ($c = $startCount[$k]; $c <= $endCount[$k]; ++$c) {
  1135. if (isset($subsetchars[$c])) {
  1136. if ($idRangeOffset[$k] == 0) {
  1137. $g = ($idDelta[$k] + $c) % 65536;
  1138. } else {
  1139. $gid = (floor($idRangeOffset[$k] / 2) + ($c - $startCount[$k]) - ($segCount - $k));
  1140. $g = ($glyphIdArray[$gid] + $idDelta[$k]) % 65536;
  1141. }
  1142. if ($g < 0) {
  1143. $g = 0;
  1144. }
  1145. $subsetglyphs[$g] = true;
  1146. }
  1147. }
  1148. }
  1149. break;
  1150. }
  1151. case 6: { // Format 6: Trimmed table mapping
  1152. $offset += 4; // skip length and version/language
  1153. $firstCode = TCPDF_STATIC::_getUSHORT($font, $offset);
  1154. $offset += 2;
  1155. $entryCount = TCPDF_STATIC::_getUSHORT($font, $offset);
  1156. $offset += 2;
  1157. for ($k = 0; $k < $entryCount; ++$k) {
  1158. $c = ($k + $firstCode);
  1159. if (isset($subsetchars[$c])) {
  1160. $g = TCPDF_STATIC::_getUSHORT($font, $offset);
  1161. $subsetglyphs[$g] = true;
  1162. }
  1163. $offset += 2;
  1164. }
  1165. break;
  1166. }
  1167. case 8: { // Format 8: Mixed 16-bit and 32-bit coverage
  1168. $offset += 10; // skip reserved, length and version/language
  1169. for ($k = 0; $k < 8192; ++$k) {
  1170. $is32[$k] = TCPDF_STATIC::_getBYTE($font, $offset);
  1171. ++$offset;
  1172. }
  1173. $nGroups = TCPDF_STATIC::_getULONG($font, $offset);
  1174. $offset += 4;
  1175. for ($i = 0; $i < $nGroups; ++$i) {
  1176. $startCharCode = TCPDF_STATIC::_getULONG($font, $offset);
  1177. $offset += 4;
  1178. $endCharCode = TCPDF_STATIC::_getULONG($font, $offset);
  1179. $offset += 4;
  1180. $startGlyphID = TCPDF_STATIC::_getULONG($font, $offset);
  1181. $offset += 4;
  1182. for ($k = $startCharCode; $k <= $endCharCode; ++$k) {
  1183. $is32idx = floor($c / 8);
  1184. if ((isset($is32[$is32idx])) AND (($is32[$is32idx] & (1 << (7 - ($c % 8)))) == 0)) {
  1185. $c = $k;
  1186. } else {
  1187. // 32 bit format
  1188. // convert to decimal (http://www.unicode.org/faq//utf_bom.html#utf16-4)
  1189. //LEAD_OFFSET = (0xD800 - (0x10000 >> 10)) = 55232
  1190. //SURROGATE_OFFSET = (0x10000 - (0xD800 << 10) - 0xDC00) = -56613888
  1191. $c = ((55232 + ($k >> 10)) << 10) + (0xDC00 + ($k & 0x3FF)) -56613888;
  1192. }
  1193. if (isset($subsetchars[$c])) {
  1194. $subsetglyphs[$startGlyphID] = true;
  1195. }
  1196. ++$startGlyphID;
  1197. }
  1198. }
  1199. break;
  1200. }
  1201. case 10: { // Format 10: Trimmed array
  1202. $offset += 10; // skip reserved, length and version/language
  1203. $startCharCode = TCPDF_STATIC::_getULONG($font, $offset);
  1204. $offset += 4;
  1205. $numChars = TCPDF_STATIC::_getULONG($font, $offset);
  1206. $offset += 4;
  1207. for ($k = 0; $k < $numChars; ++$k) {
  1208. $c = ($k + $startCharCode);
  1209. if (isset($subsetchars[$c])) {
  1210. $g = TCPDF_STATIC::_getUSHORT($font, $offset);
  1211. $subsetglyphs[$g] = true;
  1212. }
  1213. $offset += 2;
  1214. }
  1215. break;
  1216. }
  1217. case 12: { // Format 12: Segmented coverage
  1218. $offset += 10; // skip length and version/language
  1219. $nGroups = TCPDF_STATIC::_getULONG($font, $offset);
  1220. $offset += 4;
  1221. for ($k = 0; $k < $nGroups; ++$k) {
  1222. $startCharCode = TCPDF_STATIC::_getULONG($font, $offset);
  1223. $offset += 4;
  1224. $endCharCode = TCPDF_STATIC::_getULONG($font, $offset);
  1225. $offset += 4;
  1226. $startGlyphCode = TCPDF_STATIC::_getULONG($font, $offset);
  1227. $offset += 4;
  1228. for ($c = $startCharCode; $c <= $endCharCode; ++$c) {
  1229. if (isset($subsetchars[$c])) {
  1230. $subsetglyphs[$startGlyphCode] = true;
  1231. }
  1232. ++$startGlyphCode;
  1233. }
  1234. }
  1235. break;
  1236. }
  1237. case 13: { // Format 13: Many-to-one range mappings
  1238. // to be implemented ...
  1239. break;
  1240. }
  1241. case 14: { // Format 14: Unicode Variation Sequences
  1242. // to be implemented ...
  1243. break;
  1244. }
  1245. }
  1246. }
  1247. // include all parts of composite glyphs
  1248. $new_sga = $subsetglyphs;
  1249. while (!empty($new_sga)) {
  1250. $sga = $new_sga;
  1251. $new_sga = array();
  1252. foreach ($sga as $key => $val) {
  1253. if (isset($indexToLoc[$key])) {
  1254. $offset = ($table['glyf']['offset'] + $indexToLoc[$key]);
  1255. $numberOfContours = TCPDF_STATIC::_getSHORT($font, $offset);
  1256. $offset += 2;
  1257. if ($numberOfContours < 0) { // composite glyph
  1258. $offset += 8; // skip xMin, yMin, xMax, yMax
  1259. do {
  1260. $flags = TCPDF_STATIC::_getUSHORT($font, $offset);
  1261. $offset += 2;
  1262. $glyphIndex = TCPDF_STATIC::_getUSHORT($font, $offset);
  1263. $offset += 2;
  1264. if (!isset($subsetglyphs[$glyphIndex])) {
  1265. // add missing glyphs
  1266. $new_sga[$glyphIndex] = true;
  1267. }
  1268. // skip some bytes by case
  1269. if ($flags & 1) {
  1270. $offset += 4;
  1271. } else {
  1272. $offset += 2;
  1273. }
  1274. if ($flags & 8) {
  1275. $offset += 2;
  1276. } elseif ($flags & 64) {
  1277. $offset += 4;
  1278. } elseif ($flags & 128) {
  1279. $offset += 8;
  1280. }
  1281. } while ($flags & 32);
  1282. }
  1283. }
  1284. }
  1285. $subsetglyphs += $new_sga;
  1286. }
  1287. // sort glyphs by key (and remove duplicates)
  1288. ksort($subsetglyphs);
  1289. // build new glyf and loca tables
  1290. $glyf = '';
  1291. $loca = '';
  1292. $offset = 0;
  1293. $glyf_offset = $table['glyf']['offset'];
  1294. for ($i = 0; $i < $tot_num_glyphs; ++$i) {
  1295. if (isset($subsetglyphs[$i])) {
  1296. $length = ($indexToLoc[($i + 1)] - $indexToLoc[$i]);
  1297. $glyf .= substr($font, ($glyf_offset + $indexToLoc[$i]), $length);
  1298. } else {
  1299. $length = 0;
  1300. }
  1301. if ($short_offset) {
  1302. $loca .= pack('n', floor($offset / 2));
  1303. } else {
  1304. $loca .= pack('N', $offset);
  1305. }
  1306. $offset += $length;
  1307. }
  1308. // array of table names to preserve (loca and glyf tables will be added later)
  1309. // the cmap table is not needed and shall not be present, since the mapping from character codes to glyph descriptions is provided separately
  1310. $table_names = array ('head', 'hhea', 'hmtx', 'maxp', 'cvt ', 'fpgm', 'prep'); // minimum required table names
  1311. // get the tables to preserve
  1312. $offset = 12;
  1313. foreach ($table as $tag => $val) {
  1314. if (in_array($tag, $table_names)) {
  1315. $table[$tag]['data'] = substr($font, $table[$tag]['offset'], $table[$tag]['length']);
  1316. if ($tag == 'head') {
  1317. // set the checkSumAdjustment to 0
  1318. $table[$tag]['data'] = substr($table[$tag]['data'], 0, 8)."\x0\x0\x0\x0".substr($table[$tag]['data'], 12);
  1319. }
  1320. $pad = 4 - ($table[$tag]['length'] % 4);
  1321. if ($pad != 4) {
  1322. // the length of a table must be a multiple of four bytes
  1323. $table[$tag]['length'] += $pad;
  1324. $table[$tag]['data'] .= str_repeat("\x0", $pad);
  1325. }
  1326. $table[$tag]['offset'] = $offset;
  1327. $offset += $table[$tag]['length'];
  1328. // check sum is not changed (so keep the following line commented)
  1329. //$table[$tag]['checkSum'] = self::_getTTFtableChecksum($table[$tag]['data'], $table[$tag]['length']);
  1330. } else {
  1331. unset($table[$tag]);
  1332. }
  1333. }
  1334. // add loca
  1335. $table['loca']['data'] = $loca;
  1336. $table['loca']['length'] = strlen($loca);
  1337. $pad = 4 - ($table['loca']['length'] % 4);
  1338. if ($pad != 4) {
  1339. // the length of a table must be a multiple of four bytes
  1340. $table['loca']['length'] += $pad;
  1341. $table['loca']['data'] .= str_repeat("\x0", $pad);
  1342. }

Large files files are truncated, but you can click here to view the full file