PageRenderTime 70ms CodeModel.GetById 28ms RepoModel.GetById 1ms app.codeStats 0ms

/lib/tcpdf/include/tcpdf_fonts.php

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

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