Total Complexity | 539 |
Total Lines | 2604 |
Duplicated Lines | 0 % |
Changes | 1 | ||
Bugs | 0 | Features | 0 |
Complex classes like TCPDF_FONTS often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use TCPDF_FONTS, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
48 | class TCPDF_FONTS { |
||
49 | |||
50 | /** |
||
51 | * Static cache used for speed up uniord performances |
||
52 | * @protected |
||
53 | */ |
||
54 | protected static $cache_uniord = array(); |
||
55 | |||
56 | /** |
||
57 | * Convert and add the selected TrueType or Type1 font to the fonts folder (that must be writeable). |
||
58 | * @param string $fontfile Font file (full path). |
||
59 | * @param string $fonttype 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. |
||
60 | * @param string $enc 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. |
||
61 | * @param int $flags 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. |
||
62 | * @param string $outpath Output path for generated font files (must be writeable by the web server). Leave empty for default font folder. |
||
63 | * @param int $platid Platform ID for CMAP table to extract (when building a Unicode font for Windows this value should be 3, for Macintosh should be 1). |
||
64 | * @param int $encid 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. |
||
65 | * @param boolean $addcbbox If true includes the character bounding box information on the php font file. |
||
66 | * @param boolean $link If true link to system font instead of copying the font data (not transportable) - Note: do not work with Type1 fonts. |
||
67 | * @return string|false TCPDF font name or boolean false in case of error. |
||
68 | * @author Nicola Asuni |
||
69 | * @since 5.9.123 (2010-09-30) |
||
70 | * @public static |
||
71 | */ |
||
72 | public static function addTTFfont($fontfile, $fonttype='', $enc='', $flags=32, $outpath='', $platid=3, $encid=1, $addcbbox=false, $link=false) { |
||
73 | if (!TCPDF_STATIC::file_exists($fontfile)) { |
||
74 | // Could not find file |
||
75 | return false; |
||
76 | } |
||
77 | // font metrics |
||
78 | $fmetric = array(); |
||
79 | // build new font name for TCPDF compatibility |
||
80 | $font_path_parts = pathinfo($fontfile); |
||
81 | if (!isset($font_path_parts['filename'])) { |
||
82 | $font_path_parts['filename'] = substr($font_path_parts['basename'], 0, -(strlen($font_path_parts['extension']) + 1)); |
||
83 | } |
||
84 | $font_name = strtolower($font_path_parts['filename']); |
||
85 | $font_name = preg_replace('/[^a-z0-9_]/', '', $font_name); |
||
86 | $search = array('bold', 'oblique', 'italic', 'regular'); |
||
87 | $replace = array('b', 'i', 'i', ''); |
||
88 | $font_name = str_replace($search, $replace, $font_name); |
||
89 | if (empty($font_name)) { |
||
90 | // set generic name |
||
91 | $font_name = 'tcpdffont'; |
||
92 | } |
||
93 | // set output path |
||
94 | if (empty($outpath)) { |
||
95 | $outpath = self::_getfontpath(); |
||
96 | } |
||
97 | // check if this font already exist |
||
98 | if (@TCPDF_STATIC::file_exists($outpath.$font_name.'.php')) { |
||
99 | // this font already exist (delete it from fonts folder to rebuild it) |
||
100 | return $font_name; |
||
101 | } |
||
102 | $fmetric['file'] = $font_name; |
||
103 | $fmetric['ctg'] = $font_name.'.ctg.z'; |
||
104 | // get font data |
||
105 | $font = file_get_contents($fontfile); |
||
106 | $fmetric['originalsize'] = strlen($font); |
||
107 | // autodetect font type |
||
108 | if (empty($fonttype)) { |
||
109 | if (TCPDF_STATIC::_getULONG($font, 0) == 0x10000) { |
||
110 | // True Type (Unicode or not) |
||
111 | $fonttype = 'TrueTypeUnicode'; |
||
112 | } elseif (substr($font, 0, 4) == 'OTTO') { |
||
113 | // Open Type (Unicode or not) |
||
114 | //Unsupported font format: OpenType with CFF data |
||
115 | return false; |
||
116 | } else { |
||
117 | // Type 1 |
||
118 | $fonttype = 'Type1'; |
||
119 | } |
||
120 | } |
||
121 | // set font type |
||
122 | switch ($fonttype) { |
||
123 | case 'CID0CT': |
||
124 | case 'CID0CS': |
||
125 | case 'CID0KR': |
||
126 | case 'CID0JP': { |
||
127 | $fmetric['type'] = 'cidfont0'; |
||
128 | break; |
||
129 | } |
||
130 | case 'Type1': { |
||
131 | $fmetric['type'] = 'Type1'; |
||
132 | if (empty($enc) AND (($flags & 4) == 0)) { |
||
133 | $enc = 'cp1252'; |
||
134 | } |
||
135 | break; |
||
136 | } |
||
137 | case 'TrueType': { |
||
138 | $fmetric['type'] = 'TrueType'; |
||
139 | break; |
||
140 | } |
||
141 | case 'TrueTypeUnicode': |
||
142 | default: { |
||
143 | $fmetric['type'] = 'TrueTypeUnicode'; |
||
144 | break; |
||
145 | } |
||
146 | } |
||
147 | // set encoding maps (if any) |
||
148 | $fmetric['enc'] = preg_replace('/[^A-Za-z0-9_\-]/', '', $enc); |
||
149 | $fmetric['diff'] = ''; |
||
150 | if (($fmetric['type'] == 'TrueType') OR ($fmetric['type'] == 'Type1')) { |
||
151 | if (!empty($enc) AND ($enc != 'cp1252') AND isset(TCPDF_FONT_DATA::$encmap[$enc])) { |
||
152 | // build differences from reference encoding |
||
153 | $enc_ref = TCPDF_FONT_DATA::$encmap['cp1252']; |
||
154 | $enc_target = TCPDF_FONT_DATA::$encmap[$enc]; |
||
155 | $last = 0; |
||
156 | for ($i = 32; $i <= 255; ++$i) { |
||
157 | if ($enc_target[$i] != $enc_ref[$i]) { |
||
158 | if ($i != ($last + 1)) { |
||
159 | $fmetric['diff'] .= $i.' '; |
||
160 | } |
||
161 | $last = $i; |
||
162 | $fmetric['diff'] .= '/'.$enc_target[$i].' '; |
||
163 | } |
||
164 | } |
||
165 | } |
||
166 | } |
||
167 | // parse the font by type |
||
168 | if ($fmetric['type'] == 'Type1') { |
||
169 | // ---------- TYPE 1 ---------- |
||
170 | // read first segment |
||
171 | $a = unpack('Cmarker/Ctype/Vsize', substr($font, 0, 6)); |
||
172 | if ($a['marker'] != 128) { |
||
173 | // Font file is not a valid binary Type1 |
||
174 | return false; |
||
175 | } |
||
176 | $fmetric['size1'] = $a['size']; |
||
177 | $data = substr($font, 6, $fmetric['size1']); |
||
178 | // read second segment |
||
179 | $a = unpack('Cmarker/Ctype/Vsize', substr($font, (6 + $fmetric['size1']), 6)); |
||
180 | if ($a['marker'] != 128) { |
||
181 | // Font file is not a valid binary Type1 |
||
182 | return false; |
||
183 | } |
||
184 | $fmetric['size2'] = $a['size']; |
||
185 | $encrypted = substr($font, (12 + $fmetric['size1']), $fmetric['size2']); |
||
186 | $data .= $encrypted; |
||
187 | // store compressed font |
||
188 | $fmetric['file'] .= '.z'; |
||
189 | $fp = TCPDF_STATIC::fopenLocal($outpath.$fmetric['file'], 'wb'); |
||
190 | fwrite($fp, gzcompress($data)); |
||
|
|||
191 | fclose($fp); |
||
192 | // get font info |
||
193 | $fmetric['Flags'] = $flags; |
||
194 | preg_match ('#/FullName[\s]*+\(([^\)]*+)#', $font, $matches); |
||
195 | $fmetric['name'] = preg_replace('/[^a-zA-Z0-9_\-]/', '', $matches[1]); |
||
196 | preg_match('#/FontBBox[\s]*+{([^}]*+)#', $font, $matches); |
||
197 | $rawbvl = explode(' ', trim($matches[1])); |
||
198 | $bvl = [(int) $rawbvl[0], (int) $rawbvl[1], (int) $rawbvl[2], (int) $rawbvl[3]]; |
||
199 | $fmetric['bbox'] = implode(' ', $bvl); |
||
200 | $fmetric['Ascent'] = $bvl[3]; |
||
201 | $fmetric['Descent'] = $bvl[1]; |
||
202 | preg_match('#/ItalicAngle[\s]*+([0-9\+\-]*+)#', $font, $matches); |
||
203 | $fmetric['italicAngle'] = intval($matches[1]); |
||
204 | if ($fmetric['italicAngle'] != 0) { |
||
205 | $fmetric['Flags'] |= 64; |
||
206 | } |
||
207 | preg_match('#/UnderlinePosition[\s]*+([0-9\+\-]*+)#', $font, $matches); |
||
208 | $fmetric['underlinePosition'] = intval($matches[1]); |
||
209 | preg_match('#/UnderlineThickness[\s]*+([0-9\+\-]*+)#', $font, $matches); |
||
210 | $fmetric['underlineThickness'] = intval($matches[1]); |
||
211 | preg_match('#/isFixedPitch[\s]*+([^\s]*+)#', $font, $matches); |
||
212 | if ($matches[1] == 'true') { |
||
213 | $fmetric['Flags'] |= 1; |
||
214 | } |
||
215 | // get internal map |
||
216 | $imap = array(); |
||
217 | if (preg_match_all('#dup[\s]([0-9]+)[\s]*+/([^\s]*+)[\s]put#sU', $font, $fmap, PREG_SET_ORDER) > 0) { |
||
218 | foreach ($fmap as $v) { |
||
219 | $imap[$v[2]] = $v[1]; |
||
220 | } |
||
221 | } |
||
222 | // decrypt eexec encrypted part |
||
223 | $r = 55665; // eexec encryption constant |
||
224 | $c1 = 52845; |
||
225 | $c2 = 22719; |
||
226 | $elen = strlen($encrypted); |
||
227 | $eplain = ''; |
||
228 | for ($i = 0; $i < $elen; ++$i) { |
||
229 | $chr = ord($encrypted[$i]); |
||
230 | $eplain .= chr($chr ^ ($r >> 8)); |
||
231 | $r = ((($chr + $r) * $c1 + $c2) % 65536); |
||
232 | } |
||
233 | if (preg_match('#/ForceBold[\s]*+([^\s]*+)#', $eplain, $matches) > 0) { |
||
234 | if ($matches[1] == 'true') { |
||
235 | $fmetric['Flags'] |= 0x40000; |
||
236 | } |
||
237 | } |
||
238 | if (preg_match('#/StdVW[\s]*+\[([^\]]*+)#', $eplain, $matches) > 0) { |
||
239 | $fmetric['StemV'] = intval($matches[1]); |
||
240 | } else { |
||
241 | $fmetric['StemV'] = 70; |
||
242 | } |
||
243 | if (preg_match('#/StdHW[\s]*+\[([^\]]*+)#', $eplain, $matches) > 0) { |
||
244 | $fmetric['StemH'] = intval($matches[1]); |
||
245 | } else { |
||
246 | $fmetric['StemH'] = 30; |
||
247 | } |
||
248 | if (preg_match('#/BlueValues[\s]*+\[([^\]]*+)#', $eplain, $matches) > 0) { |
||
249 | $bv = explode(' ', $matches[1]); |
||
250 | if (count($bv) >= 6) { |
||
251 | $v1 = intval($bv[2]); |
||
252 | $v2 = intval($bv[4]); |
||
253 | if ($v1 <= $v2) { |
||
254 | $fmetric['XHeight'] = $v1; |
||
255 | $fmetric['CapHeight'] = $v2; |
||
256 | } else { |
||
257 | $fmetric['XHeight'] = $v2; |
||
258 | $fmetric['CapHeight'] = $v1; |
||
259 | } |
||
260 | } else { |
||
261 | $fmetric['XHeight'] = 450; |
||
262 | $fmetric['CapHeight'] = 700; |
||
263 | } |
||
264 | } else { |
||
265 | $fmetric['XHeight'] = 450; |
||
266 | $fmetric['CapHeight'] = 700; |
||
267 | } |
||
268 | // get the number of random bytes at the beginning of charstrings |
||
269 | if (preg_match('#/lenIV[\s]*+([\d]*+)#', $eplain, $matches) > 0) { |
||
270 | $lenIV = intval($matches[1]); |
||
271 | } else { |
||
272 | $lenIV = 4; |
||
273 | } |
||
274 | $fmetric['Leading'] = 0; |
||
275 | // get charstring data |
||
276 | $eplain = substr($eplain, (strpos($eplain, '/CharStrings') + 1)); |
||
277 | preg_match_all('#/([A-Za-z0-9\.]*+)[\s][0-9]+[\s]RD[\s](.*)[\s]ND#sU', $eplain, $matches, PREG_SET_ORDER); |
||
278 | if (!empty($enc) AND isset(TCPDF_FONT_DATA::$encmap[$enc])) { |
||
279 | $enc_map = TCPDF_FONT_DATA::$encmap[$enc]; |
||
280 | } else { |
||
281 | $enc_map = false; |
||
282 | } |
||
283 | $fmetric['cw'] = ''; |
||
284 | $fmetric['MaxWidth'] = 0; |
||
285 | $cwidths = array(); |
||
286 | foreach ($matches as $k => $v) { |
||
287 | $cid = 0; |
||
288 | if (isset($imap[$v[1]])) { |
||
289 | $cid = $imap[$v[1]]; |
||
290 | } elseif ($enc_map !== false) { |
||
291 | $cid = array_search($v[1], $enc_map); |
||
292 | if ($cid === false) { |
||
293 | $cid = 0; |
||
294 | } elseif ($cid > 1000) { |
||
295 | $cid -= 1000; |
||
296 | } |
||
297 | } |
||
298 | // decrypt charstring encrypted part |
||
299 | $r = 4330; // charstring encryption constant |
||
300 | $c1 = 52845; |
||
301 | $c2 = 22719; |
||
302 | $cd = $v[2]; |
||
303 | $clen = strlen($cd); |
||
304 | $ccom = array(); |
||
305 | for ($i = 0; $i < $clen; ++$i) { |
||
306 | $chr = ord($cd[$i]); |
||
307 | $ccom[] = ($chr ^ ($r >> 8)); |
||
308 | $r = ((($chr + $r) * $c1 + $c2) % 65536); |
||
309 | } |
||
310 | // decode numbers |
||
311 | $cdec = array(); |
||
312 | $ck = 0; |
||
313 | $i = $lenIV; |
||
314 | while ($i < $clen) { |
||
315 | if ($ccom[$i] < 32) { |
||
316 | $cdec[$ck] = $ccom[$i]; |
||
317 | if (($ck > 0) AND ($cdec[$ck] == 13)) { |
||
318 | // hsbw command: update width |
||
319 | $cwidths[$cid] = $cdec[($ck - 1)]; |
||
320 | } |
||
321 | ++$i; |
||
322 | } elseif (($ccom[$i] >= 32) AND ($ccom[$i] <= 246)) { |
||
323 | $cdec[$ck] = ($ccom[$i] - 139); |
||
324 | ++$i; |
||
325 | } elseif (($ccom[$i] >= 247) AND ($ccom[$i] <= 250)) { |
||
326 | $cdec[$ck] = ((($ccom[$i] - 247) * 256) + $ccom[($i + 1)] + 108); |
||
327 | $i += 2; |
||
328 | } elseif (($ccom[$i] >= 251) AND ($ccom[$i] <= 254)) { |
||
329 | $cdec[$ck] = ((-($ccom[$i] - 251) * 256) - $ccom[($i + 1)] - 108); |
||
330 | $i += 2; |
||
331 | } elseif ($ccom[$i] == 255) { |
||
332 | $sval = chr($ccom[($i + 1)]).chr($ccom[($i + 2)]).chr($ccom[($i + 3)]).chr($ccom[($i + 4)]); |
||
333 | $vsval = unpack('li', $sval); |
||
334 | $cdec[$ck] = $vsval['i']; |
||
335 | $i += 5; |
||
336 | } |
||
337 | ++$ck; |
||
338 | } |
||
339 | } // end for each matches |
||
340 | $fmetric['MissingWidth'] = $cwidths[0]; |
||
341 | $fmetric['MaxWidth'] = $fmetric['MissingWidth']; |
||
342 | $fmetric['AvgWidth'] = 0; |
||
343 | // set chars widths |
||
344 | for ($cid = 0; $cid <= 255; ++$cid) { |
||
345 | if (isset($cwidths[$cid])) { |
||
346 | if ($cwidths[$cid] > $fmetric['MaxWidth']) { |
||
347 | $fmetric['MaxWidth'] = $cwidths[$cid]; |
||
348 | } |
||
349 | $fmetric['AvgWidth'] += $cwidths[$cid]; |
||
350 | $fmetric['cw'] .= ','.$cid.'=>'.$cwidths[$cid]; |
||
351 | } else { |
||
352 | $fmetric['cw'] .= ','.$cid.'=>'.$fmetric['MissingWidth']; |
||
353 | } |
||
354 | } |
||
355 | $fmetric['AvgWidth'] = round($fmetric['AvgWidth'] / count($cwidths)); |
||
356 | } else { |
||
357 | // ---------- TRUE TYPE ---------- |
||
358 | $offset = 0; // offset position of the font data |
||
359 | if (TCPDF_STATIC::_getULONG($font, $offset) != 0x10000) { |
||
360 | // sfnt version must be 0x00010000 for TrueType version 1.0. |
||
361 | return false; |
||
362 | } |
||
363 | if ($fmetric['type'] != 'cidfont0') { |
||
364 | if ($link) { |
||
365 | // creates a symbolic link to the existing font |
||
366 | symlink($fontfile, $outpath.$fmetric['file']); |
||
367 | } else { |
||
368 | // store compressed font |
||
369 | $fmetric['file'] .= '.z'; |
||
370 | $fp = TCPDF_STATIC::fopenLocal($outpath.$fmetric['file'], 'wb'); |
||
371 | fwrite($fp, gzcompress($font)); |
||
372 | fclose($fp); |
||
373 | } |
||
374 | } |
||
375 | $offset += 4; |
||
376 | // get number of tables |
||
377 | $numTables = TCPDF_STATIC::_getUSHORT($font, $offset); |
||
378 | $offset += 2; |
||
379 | // skip searchRange, entrySelector and rangeShift |
||
380 | $offset += 6; |
||
381 | // tables array |
||
382 | $table = array(); |
||
383 | // ---------- get tables ---------- |
||
384 | for ($i = 0; $i < $numTables; ++$i) { |
||
385 | // get table info |
||
386 | $tag = substr($font, $offset, 4); |
||
387 | $offset += 4; |
||
388 | $table[$tag] = array(); |
||
389 | $table[$tag]['checkSum'] = TCPDF_STATIC::_getULONG($font, $offset); |
||
390 | $offset += 4; |
||
391 | $table[$tag]['offset'] = TCPDF_STATIC::_getULONG($font, $offset); |
||
392 | $offset += 4; |
||
393 | $table[$tag]['length'] = TCPDF_STATIC::_getULONG($font, $offset); |
||
394 | $offset += 4; |
||
395 | } |
||
396 | // check magicNumber |
||
397 | $offset = $table['head']['offset'] + 12; |
||
398 | if (TCPDF_STATIC::_getULONG($font, $offset) != 0x5F0F3CF5) { |
||
399 | // magicNumber must be 0x5F0F3CF5 |
||
400 | return false; |
||
401 | } |
||
402 | $offset += 4; |
||
403 | $offset += 2; // skip flags |
||
404 | // get FUnits |
||
405 | $fmetric['unitsPerEm'] = TCPDF_STATIC::_getUSHORT($font, $offset); |
||
406 | $offset += 2; |
||
407 | // units ratio constant |
||
408 | $urk = (1000 / $fmetric['unitsPerEm']); |
||
409 | $offset += 16; // skip created, modified |
||
410 | $xMin = round(TCPDF_STATIC::_getFWORD($font, $offset) * $urk); |
||
411 | $offset += 2; |
||
412 | $yMin = round(TCPDF_STATIC::_getFWORD($font, $offset) * $urk); |
||
413 | $offset += 2; |
||
414 | $xMax = round(TCPDF_STATIC::_getFWORD($font, $offset) * $urk); |
||
415 | $offset += 2; |
||
416 | $yMax = round(TCPDF_STATIC::_getFWORD($font, $offset) * $urk); |
||
417 | $offset += 2; |
||
418 | $fmetric['bbox'] = ''.$xMin.' '.$yMin.' '.$xMax.' '.$yMax.''; |
||
419 | $macStyle = TCPDF_STATIC::_getUSHORT($font, $offset); |
||
420 | $offset += 2; |
||
421 | // PDF font flags |
||
422 | $fmetric['Flags'] = $flags; |
||
423 | if (($macStyle & 2) == 2) { |
||
424 | // italic flag |
||
425 | $fmetric['Flags'] |= 64; |
||
426 | } |
||
427 | // get offset mode (indexToLocFormat : 0 = short, 1 = long) |
||
428 | $offset = $table['head']['offset'] + 50; |
||
429 | $short_offset = (TCPDF_STATIC::_getSHORT($font, $offset) == 0); |
||
430 | $offset += 2; |
||
431 | // get the offsets to the locations of the glyphs in the font, relative to the beginning of the glyphData table |
||
432 | $indexToLoc = array(); |
||
433 | $offset = $table['loca']['offset']; |
||
434 | if ($short_offset) { |
||
435 | // short version |
||
436 | $tot_num_glyphs = floor($table['loca']['length'] / 2); // numGlyphs + 1 |
||
437 | for ($i = 0; $i < $tot_num_glyphs; ++$i) { |
||
438 | $indexToLoc[$i] = TCPDF_STATIC::_getUSHORT($font, $offset) * 2; |
||
439 | if (isset($indexToLoc[($i - 1)]) && ($indexToLoc[$i] == $indexToLoc[($i - 1)])) { |
||
440 | // the last glyph didn't have an outline |
||
441 | unset($indexToLoc[($i - 1)]); |
||
442 | } |
||
443 | $offset += 2; |
||
444 | } |
||
445 | } else { |
||
446 | // long version |
||
447 | $tot_num_glyphs = floor($table['loca']['length'] / 4); // numGlyphs + 1 |
||
448 | for ($i = 0; $i < $tot_num_glyphs; ++$i) { |
||
449 | $indexToLoc[$i] = TCPDF_STATIC::_getULONG($font, $offset); |
||
450 | if (isset($indexToLoc[($i - 1)]) && ($indexToLoc[$i] == $indexToLoc[($i - 1)])) { |
||
451 | // the last glyph didn't have an outline |
||
452 | unset($indexToLoc[($i - 1)]); |
||
453 | } |
||
454 | $offset += 4; |
||
455 | } |
||
456 | } |
||
457 | // get glyphs indexes of chars from cmap table |
||
458 | $offset = $table['cmap']['offset'] + 2; |
||
459 | $numEncodingTables = TCPDF_STATIC::_getUSHORT($font, $offset); |
||
460 | $offset += 2; |
||
461 | $encodingTables = array(); |
||
462 | for ($i = 0; $i < $numEncodingTables; ++$i) { |
||
463 | $encodingTables[$i]['platformID'] = TCPDF_STATIC::_getUSHORT($font, $offset); |
||
464 | $offset += 2; |
||
465 | $encodingTables[$i]['encodingID'] = TCPDF_STATIC::_getUSHORT($font, $offset); |
||
466 | $offset += 2; |
||
467 | $encodingTables[$i]['offset'] = TCPDF_STATIC::_getULONG($font, $offset); |
||
468 | $offset += 4; |
||
469 | } |
||
470 | // ---------- get os/2 metrics ---------- |
||
471 | $offset = $table['OS/2']['offset']; |
||
472 | $offset += 2; // skip version |
||
473 | // xAvgCharWidth |
||
474 | $fmetric['AvgWidth'] = round(TCPDF_STATIC::_getFWORD($font, $offset) * $urk); |
||
475 | $offset += 2; |
||
476 | // usWeightClass |
||
477 | $usWeightClass = round(TCPDF_STATIC::_getUFWORD($font, $offset) * $urk); |
||
478 | // estimate StemV and StemH (400 = usWeightClass for Normal - Regular font) |
||
479 | $fmetric['StemV'] = round((70 * $usWeightClass) / 400); |
||
480 | $fmetric['StemH'] = round((30 * $usWeightClass) / 400); |
||
481 | $offset += 2; |
||
482 | $offset += 2; // usWidthClass |
||
483 | $fsType = TCPDF_STATIC::_getSHORT($font, $offset); |
||
484 | $offset += 2; |
||
485 | if ($fsType == 2) { |
||
486 | // This Font cannot be modified, embedded or exchanged in any manner without first obtaining permission of the legal owner. |
||
487 | return false; |
||
488 | } |
||
489 | // ---------- get font name ---------- |
||
490 | $fmetric['name'] = ''; |
||
491 | $offset = $table['name']['offset']; |
||
492 | $offset += 2; // skip Format selector (=0). |
||
493 | // Number of NameRecords that follow n. |
||
494 | $numNameRecords = TCPDF_STATIC::_getUSHORT($font, $offset); |
||
495 | $offset += 2; |
||
496 | // Offset to start of string storage (from start of table). |
||
497 | $stringStorageOffset = TCPDF_STATIC::_getUSHORT($font, $offset); |
||
498 | $offset += 2; |
||
499 | for ($i = 0; $i < $numNameRecords; ++$i) { |
||
500 | $offset += 6; // skip Platform ID, Platform-specific encoding ID, Language ID. |
||
501 | // Name ID. |
||
502 | $nameID = TCPDF_STATIC::_getUSHORT($font, $offset); |
||
503 | $offset += 2; |
||
504 | if ($nameID == 6) { |
||
505 | // String length (in bytes). |
||
506 | $stringLength = TCPDF_STATIC::_getUSHORT($font, $offset); |
||
507 | $offset += 2; |
||
508 | // String offset from start of storage area (in bytes). |
||
509 | $stringOffset = TCPDF_STATIC::_getUSHORT($font, $offset); |
||
510 | $offset += 2; |
||
511 | $offset = ($table['name']['offset'] + $stringStorageOffset + $stringOffset); |
||
512 | $fmetric['name'] = substr($font, $offset, $stringLength); |
||
513 | $fmetric['name'] = preg_replace('/[^a-zA-Z0-9_\-]/', '', $fmetric['name']); |
||
514 | break; |
||
515 | } else { |
||
516 | $offset += 4; // skip String length, String offset |
||
517 | } |
||
518 | } |
||
519 | if (empty($fmetric['name'])) { |
||
520 | $fmetric['name'] = $font_name; |
||
521 | } |
||
522 | // ---------- get post data ---------- |
||
523 | $offset = $table['post']['offset']; |
||
524 | $offset += 4; // skip Format Type |
||
525 | $fmetric['italicAngle'] = TCPDF_STATIC::_getFIXED($font, $offset); |
||
526 | $offset += 4; |
||
527 | $fmetric['underlinePosition'] = round(TCPDF_STATIC::_getFWORD($font, $offset) * $urk); |
||
528 | $offset += 2; |
||
529 | $fmetric['underlineThickness'] = round(TCPDF_STATIC::_getFWORD($font, $offset) * $urk); |
||
530 | $offset += 2; |
||
531 | $isFixedPitch = (TCPDF_STATIC::_getULONG($font, $offset) == 0) ? false : true; |
||
532 | $offset += 2; |
||
533 | if ($isFixedPitch) { |
||
534 | $fmetric['Flags'] |= 1; |
||
535 | } |
||
536 | // ---------- get hhea data ---------- |
||
537 | $offset = $table['hhea']['offset']; |
||
538 | $offset += 4; // skip Table version number |
||
539 | // Ascender |
||
540 | $fmetric['Ascent'] = round(TCPDF_STATIC::_getFWORD($font, $offset) * $urk); |
||
541 | $offset += 2; |
||
542 | // Descender |
||
543 | $fmetric['Descent'] = round(TCPDF_STATIC::_getFWORD($font, $offset) * $urk); |
||
544 | $offset += 2; |
||
545 | // LineGap |
||
546 | $fmetric['Leading'] = round(TCPDF_STATIC::_getFWORD($font, $offset) * $urk); |
||
547 | $offset += 2; |
||
548 | // advanceWidthMax |
||
549 | $fmetric['MaxWidth'] = round(TCPDF_STATIC::_getUFWORD($font, $offset) * $urk); |
||
550 | $offset += 2; |
||
551 | $offset += 22; // skip some values |
||
552 | // get the number of hMetric entries in hmtx table |
||
553 | $numberOfHMetrics = TCPDF_STATIC::_getUSHORT($font, $offset); |
||
554 | // ---------- get maxp data ---------- |
||
555 | $offset = $table['maxp']['offset']; |
||
556 | $offset += 4; // skip Table version number |
||
557 | // get the the number of glyphs in the font. |
||
558 | $numGlyphs = TCPDF_STATIC::_getUSHORT($font, $offset); |
||
559 | // ---------- get CIDToGIDMap ---------- |
||
560 | $ctg = array(); |
||
561 | $c = 0; |
||
562 | foreach ($encodingTables as $enctable) { |
||
563 | // get only specified Platform ID and Encoding ID |
||
564 | if (($enctable['platformID'] == $platid) AND ($enctable['encodingID'] == $encid)) { |
||
565 | $offset = $table['cmap']['offset'] + $enctable['offset']; |
||
566 | $format = TCPDF_STATIC::_getUSHORT($font, $offset); |
||
567 | $offset += 2; |
||
568 | switch ($format) { |
||
569 | case 0: { // Format 0: Byte encoding table |
||
570 | $offset += 4; // skip length and version/language |
||
571 | for ($c = 0; $c < 256; ++$c) { |
||
572 | $g = TCPDF_STATIC::_getBYTE($font, $offset); |
||
573 | $ctg[$c] = $g; |
||
574 | ++$offset; |
||
575 | } |
||
576 | break; |
||
577 | } |
||
578 | case 2: { // Format 2: High-byte mapping through table |
||
579 | $offset += 4; // skip length and version/language |
||
580 | $numSubHeaders = 0; |
||
581 | for ($i = 0; $i < 256; ++$i) { |
||
582 | // Array that maps high bytes to subHeaders: value is subHeader index * 8. |
||
583 | $subHeaderKeys[$i] = (TCPDF_STATIC::_getUSHORT($font, $offset) / 8); |
||
584 | $offset += 2; |
||
585 | if ($numSubHeaders < $subHeaderKeys[$i]) { |
||
586 | $numSubHeaders = $subHeaderKeys[$i]; |
||
587 | } |
||
588 | } |
||
589 | // the number of subHeaders is equal to the max of subHeaderKeys + 1 |
||
590 | ++$numSubHeaders; |
||
591 | // read subHeader structures |
||
592 | $subHeaders = array(); |
||
593 | $numGlyphIndexArray = 0; |
||
594 | for ($k = 0; $k < $numSubHeaders; ++$k) { |
||
595 | $subHeaders[$k]['firstCode'] = TCPDF_STATIC::_getUSHORT($font, $offset); |
||
596 | $offset += 2; |
||
597 | $subHeaders[$k]['entryCount'] = TCPDF_STATIC::_getUSHORT($font, $offset); |
||
598 | $offset += 2; |
||
599 | $subHeaders[$k]['idDelta'] = TCPDF_STATIC::_getUSHORT($font, $offset); |
||
600 | $offset += 2; |
||
601 | $subHeaders[$k]['idRangeOffset'] = TCPDF_STATIC::_getUSHORT($font, $offset); |
||
602 | $offset += 2; |
||
603 | $subHeaders[$k]['idRangeOffset'] -= (2 + (($numSubHeaders - $k - 1) * 8)); |
||
604 | $subHeaders[$k]['idRangeOffset'] /= 2; |
||
605 | $numGlyphIndexArray += $subHeaders[$k]['entryCount']; |
||
606 | } |
||
607 | for ($k = 0; $k < $numGlyphIndexArray; ++$k) { |
||
608 | $glyphIndexArray[$k] = TCPDF_STATIC::_getUSHORT($font, $offset); |
||
609 | $offset += 2; |
||
610 | } |
||
611 | for ($i = 0; $i < 256; ++$i) { |
||
612 | $k = $subHeaderKeys[$i]; |
||
613 | if ($k == 0) { |
||
614 | // one byte code |
||
615 | $c = $i; |
||
616 | $g = $glyphIndexArray[0]; |
||
617 | $ctg[$c] = $g; |
||
618 | } else { |
||
619 | // two bytes code |
||
620 | $start_byte = $subHeaders[$k]['firstCode']; |
||
621 | $end_byte = $start_byte + $subHeaders[$k]['entryCount']; |
||
622 | for ($j = $start_byte; $j < $end_byte; ++$j) { |
||
623 | // combine high and low bytes |
||
624 | $c = (($i << 8) + $j); |
||
625 | $idRangeOffset = ($subHeaders[$k]['idRangeOffset'] + $j - $subHeaders[$k]['firstCode']); |
||
626 | $g = ($glyphIndexArray[$idRangeOffset] + $subHeaders[$k]['idDelta']) % 65536; |
||
627 | if ($g < 0) { |
||
628 | $g = 0; |
||
629 | } |
||
630 | $ctg[$c] = $g; |
||
631 | } |
||
632 | } |
||
633 | } |
||
634 | break; |
||
635 | } |
||
636 | case 4: { // Format 4: Segment mapping to delta values |
||
637 | $length = TCPDF_STATIC::_getUSHORT($font, $offset); |
||
638 | $offset += 2; |
||
639 | $offset += 2; // skip version/language |
||
640 | $segCount = floor(TCPDF_STATIC::_getUSHORT($font, $offset) / 2); |
||
641 | $offset += 2; |
||
642 | $offset += 6; // skip searchRange, entrySelector, rangeShift |
||
643 | $endCount = array(); // array of end character codes for each segment |
||
644 | for ($k = 0; $k < $segCount; ++$k) { |
||
645 | $endCount[$k] = TCPDF_STATIC::_getUSHORT($font, $offset); |
||
646 | $offset += 2; |
||
647 | } |
||
648 | $offset += 2; // skip reservedPad |
||
649 | $startCount = array(); // array of start character codes for each segment |
||
650 | for ($k = 0; $k < $segCount; ++$k) { |
||
651 | $startCount[$k] = TCPDF_STATIC::_getUSHORT($font, $offset); |
||
652 | $offset += 2; |
||
653 | } |
||
654 | $idDelta = array(); // delta for all character codes in segment |
||
655 | for ($k = 0; $k < $segCount; ++$k) { |
||
656 | $idDelta[$k] = TCPDF_STATIC::_getUSHORT($font, $offset); |
||
657 | $offset += 2; |
||
658 | } |
||
659 | $idRangeOffset = array(); // Offsets into glyphIdArray or 0 |
||
660 | for ($k = 0; $k < $segCount; ++$k) { |
||
661 | $idRangeOffset[$k] = TCPDF_STATIC::_getUSHORT($font, $offset); |
||
662 | $offset += 2; |
||
663 | } |
||
664 | $gidlen = (floor($length / 2) - 8 - (4 * $segCount)); |
||
665 | $glyphIdArray = array(); // glyph index array |
||
666 | for ($k = 0; $k < $gidlen; ++$k) { |
||
667 | $glyphIdArray[$k] = TCPDF_STATIC::_getUSHORT($font, $offset); |
||
668 | $offset += 2; |
||
669 | } |
||
670 | for ($k = 0; $k < $segCount - 1; ++$k) { |
||
671 | for ($c = $startCount[$k]; $c <= $endCount[$k]; ++$c) { |
||
672 | if ($idRangeOffset[$k] == 0) { |
||
673 | $g = ($idDelta[$k] + $c) % 65536; |
||
674 | } else { |
||
675 | $gid = (floor($idRangeOffset[$k] / 2) + ($c - $startCount[$k]) - ($segCount - $k)); |
||
676 | $g = ($glyphIdArray[$gid] + $idDelta[$k]) % 65536; |
||
677 | } |
||
678 | if ($g < 0) { |
||
679 | $g = 0; |
||
680 | } |
||
681 | $ctg[$c] = $g; |
||
682 | } |
||
683 | } |
||
684 | break; |
||
685 | } |
||
686 | case 6: { // Format 6: Trimmed table mapping |
||
687 | $offset += 4; // skip length and version/language |
||
688 | $firstCode = TCPDF_STATIC::_getUSHORT($font, $offset); |
||
689 | $offset += 2; |
||
690 | $entryCount = TCPDF_STATIC::_getUSHORT($font, $offset); |
||
691 | $offset += 2; |
||
692 | for ($k = 0; $k < $entryCount; ++$k) { |
||
693 | $c = ($k + $firstCode); |
||
694 | $g = TCPDF_STATIC::_getUSHORT($font, $offset); |
||
695 | $offset += 2; |
||
696 | $ctg[$c] = $g; |
||
697 | } |
||
698 | break; |
||
699 | } |
||
700 | case 8: { // Format 8: Mixed 16-bit and 32-bit coverage |
||
701 | $offset += 10; // skip reserved, length and version/language |
||
702 | for ($k = 0; $k < 8192; ++$k) { |
||
703 | $is32[$k] = TCPDF_STATIC::_getBYTE($font, $offset); |
||
704 | ++$offset; |
||
705 | } |
||
706 | $nGroups = TCPDF_STATIC::_getULONG($font, $offset); |
||
707 | $offset += 4; |
||
708 | for ($i = 0; $i < $nGroups; ++$i) { |
||
709 | $startCharCode = TCPDF_STATIC::_getULONG($font, $offset); |
||
710 | $offset += 4; |
||
711 | $endCharCode = TCPDF_STATIC::_getULONG($font, $offset); |
||
712 | $offset += 4; |
||
713 | $startGlyphID = TCPDF_STATIC::_getULONG($font, $offset); |
||
714 | $offset += 4; |
||
715 | for ($k = $startCharCode; $k <= $endCharCode; ++$k) { |
||
716 | $is32idx = floor($c / 8); |
||
717 | if ((isset($is32[$is32idx])) AND (($is32[$is32idx] & (1 << (7 - ($c % 8)))) == 0)) { |
||
718 | $c = $k; |
||
719 | } else { |
||
720 | // 32 bit format |
||
721 | // convert to decimal (http://www.unicode.org/faq//utf_bom.html#utf16-4) |
||
722 | //LEAD_OFFSET = (0xD800 - (0x10000 >> 10)) = 55232 |
||
723 | //SURROGATE_OFFSET = (0x10000 - (0xD800 << 10) - 0xDC00) = -56613888 |
||
724 | $c = ((55232 + ($k >> 10)) << 10) + (0xDC00 + ($k & 0x3FF)) -56613888; |
||
725 | } |
||
726 | $ctg[$c] = 0; |
||
727 | ++$startGlyphID; |
||
728 | } |
||
729 | } |
||
730 | break; |
||
731 | } |
||
732 | case 10: { // Format 10: Trimmed array |
||
733 | $offset += 10; // skip reserved, length and version/language |
||
734 | $startCharCode = TCPDF_STATIC::_getULONG($font, $offset); |
||
735 | $offset += 4; |
||
736 | $numChars = TCPDF_STATIC::_getULONG($font, $offset); |
||
737 | $offset += 4; |
||
738 | for ($k = 0; $k < $numChars; ++$k) { |
||
739 | $c = ($k + $startCharCode); |
||
740 | $g = TCPDF_STATIC::_getUSHORT($font, $offset); |
||
741 | $ctg[$c] = $g; |
||
742 | $offset += 2; |
||
743 | } |
||
744 | break; |
||
745 | } |
||
746 | case 12: { // Format 12: Segmented coverage |
||
747 | $offset += 10; // skip length and version/language |
||
748 | $nGroups = TCPDF_STATIC::_getULONG($font, $offset); |
||
749 | $offset += 4; |
||
750 | for ($k = 0; $k < $nGroups; ++$k) { |
||
751 | $startCharCode = TCPDF_STATIC::_getULONG($font, $offset); |
||
752 | $offset += 4; |
||
753 | $endCharCode = TCPDF_STATIC::_getULONG($font, $offset); |
||
754 | $offset += 4; |
||
755 | $startGlyphCode = TCPDF_STATIC::_getULONG($font, $offset); |
||
756 | $offset += 4; |
||
757 | for ($c = $startCharCode; $c <= $endCharCode; ++$c) { |
||
758 | $ctg[$c] = $startGlyphCode; |
||
759 | ++$startGlyphCode; |
||
760 | } |
||
761 | } |
||
762 | break; |
||
763 | } |
||
764 | case 13: { // Format 13: Many-to-one range mappings |
||
765 | // to be implemented ... |
||
766 | break; |
||
767 | } |
||
768 | case 14: { // Format 14: Unicode Variation Sequences |
||
769 | // to be implemented ... |
||
770 | break; |
||
771 | } |
||
772 | } |
||
773 | } |
||
774 | } |
||
775 | if (!isset($ctg[0])) { |
||
776 | $ctg[0] = 0; |
||
777 | } |
||
778 | // get xHeight (height of x) |
||
779 | $offset = ($table['glyf']['offset'] + $indexToLoc[$ctg[120]] + 4); |
||
780 | $yMin = TCPDF_STATIC::_getFWORD($font, $offset); |
||
781 | $offset += 4; |
||
782 | $yMax = TCPDF_STATIC::_getFWORD($font, $offset); |
||
783 | $offset += 2; |
||
784 | $fmetric['XHeight'] = round(($yMax - $yMin) * $urk); |
||
785 | // get CapHeight (height of H) |
||
786 | $offset = ($table['glyf']['offset'] + $indexToLoc[$ctg[72]] + 4); |
||
787 | $yMin = TCPDF_STATIC::_getFWORD($font, $offset); |
||
788 | $offset += 4; |
||
789 | $yMax = TCPDF_STATIC::_getFWORD($font, $offset); |
||
790 | $offset += 2; |
||
791 | $fmetric['CapHeight'] = round(($yMax - $yMin) * $urk); |
||
792 | // ceate widths array |
||
793 | $cw = array(); |
||
794 | $offset = $table['hmtx']['offset']; |
||
795 | for ($i = 0 ; $i < $numberOfHMetrics; ++$i) { |
||
796 | $cw[$i] = round(TCPDF_STATIC::_getUFWORD($font, $offset) * $urk); |
||
797 | $offset += 4; // skip lsb |
||
798 | } |
||
799 | if ($numberOfHMetrics < $numGlyphs) { |
||
800 | // fill missing widths with the last value |
||
801 | $cw = array_pad($cw, $numGlyphs, $cw[($numberOfHMetrics - 1)]); |
||
802 | } |
||
803 | $fmetric['MissingWidth'] = $cw[0]; |
||
804 | $fmetric['cw'] = ''; |
||
805 | $fmetric['cbbox'] = ''; |
||
806 | for ($cid = 0; $cid <= 65535; ++$cid) { |
||
807 | if (isset($ctg[$cid])) { |
||
808 | if (isset($cw[$ctg[$cid]])) { |
||
809 | $fmetric['cw'] .= ','.$cid.'=>'.$cw[$ctg[$cid]]; |
||
810 | } |
||
811 | if ($addcbbox AND isset($indexToLoc[$ctg[$cid]])) { |
||
812 | $offset = ($table['glyf']['offset'] + $indexToLoc[$ctg[$cid]]); |
||
813 | $xMin = round(TCPDF_STATIC::_getFWORD($font, $offset + 2) * $urk); |
||
814 | $yMin = round(TCPDF_STATIC::_getFWORD($font, $offset + 4) * $urk); |
||
815 | $xMax = round(TCPDF_STATIC::_getFWORD($font, $offset + 6) * $urk); |
||
816 | $yMax = round(TCPDF_STATIC::_getFWORD($font, $offset + 8) * $urk); |
||
817 | $fmetric['cbbox'] .= ','.$cid.'=>array('.$xMin.','.$yMin.','.$xMax.','.$yMax.')'; |
||
818 | } |
||
819 | } |
||
820 | } |
||
821 | } // end of true type |
||
822 | if (($fmetric['type'] == 'TrueTypeUnicode') AND (count($ctg) == 256)) { |
||
823 | $fmetric['type'] = 'TrueType'; |
||
824 | } |
||
825 | // ---------- create php font file ---------- |
||
826 | $pfile = '<'.'?'.'php'."\n"; |
||
827 | $pfile .= '// TCPDF FONT FILE DESCRIPTION'."\n"; |
||
828 | $pfile .= '$type=\''.$fmetric['type'].'\';'."\n"; |
||
829 | $pfile .= '$name=\''.$fmetric['name'].'\';'."\n"; |
||
830 | $pfile .= '$up='.$fmetric['underlinePosition'].';'."\n"; |
||
831 | $pfile .= '$ut='.$fmetric['underlineThickness'].';'."\n"; |
||
832 | if ($fmetric['MissingWidth'] > 0) { |
||
833 | $pfile .= '$dw='.$fmetric['MissingWidth'].';'."\n"; |
||
834 | } else { |
||
835 | $pfile .= '$dw='.$fmetric['AvgWidth'].';'."\n"; |
||
836 | } |
||
837 | $pfile .= '$diff=\''.$fmetric['diff'].'\';'."\n"; |
||
838 | if ($fmetric['type'] == 'Type1') { |
||
839 | // Type 1 |
||
840 | $pfile .= '$enc=\''.$fmetric['enc'].'\';'."\n"; |
||
841 | $pfile .= '$file=\''.$fmetric['file'].'\';'."\n"; |
||
842 | $pfile .= '$size1='.$fmetric['size1'].';'."\n"; |
||
843 | $pfile .= '$size2='.$fmetric['size2'].';'."\n"; |
||
844 | } else { |
||
845 | $pfile .= '$originalsize='.$fmetric['originalsize'].';'."\n"; |
||
846 | if ($fmetric['type'] == 'cidfont0') { |
||
847 | // CID-0 |
||
848 | switch ($fonttype) { |
||
849 | case 'CID0JP': { |
||
850 | $pfile .= '// Japanese'."\n"; |
||
851 | $pfile .= '$enc=\'UniJIS-UTF16-H\';'."\n"; |
||
852 | $pfile .= '$cidinfo=array(\'Registry\'=>\'Adobe\', \'Ordering\'=>\'Japan1\',\'Supplement\'=>5);'."\n"; |
||
853 | $pfile .= 'include(dirname(__FILE__).\'/uni2cid_aj16.php\');'."\n"; |
||
854 | break; |
||
855 | } |
||
856 | case 'CID0KR': { |
||
857 | $pfile .= '// Korean'."\n"; |
||
858 | $pfile .= '$enc=\'UniKS-UTF16-H\';'."\n"; |
||
859 | $pfile .= '$cidinfo=array(\'Registry\'=>\'Adobe\', \'Ordering\'=>\'Korea1\',\'Supplement\'=>0);'."\n"; |
||
860 | $pfile .= 'include(dirname(__FILE__).\'/uni2cid_ak12.php\');'."\n"; |
||
861 | break; |
||
862 | } |
||
863 | case 'CID0CS': { |
||
864 | $pfile .= '// Chinese Simplified'."\n"; |
||
865 | $pfile .= '$enc=\'UniGB-UTF16-H\';'."\n"; |
||
866 | $pfile .= '$cidinfo=array(\'Registry\'=>\'Adobe\', \'Ordering\'=>\'GB1\',\'Supplement\'=>2);'."\n"; |
||
867 | $pfile .= 'include(dirname(__FILE__).\'/uni2cid_ag15.php\');'."\n"; |
||
868 | break; |
||
869 | } |
||
870 | case 'CID0CT': |
||
871 | default: { |
||
872 | $pfile .= '// Chinese Traditional'."\n"; |
||
873 | $pfile .= '$enc=\'UniCNS-UTF16-H\';'."\n"; |
||
874 | $pfile .= '$cidinfo=array(\'Registry\'=>\'Adobe\', \'Ordering\'=>\'CNS1\',\'Supplement\'=>0);'."\n"; |
||
875 | $pfile .= 'include(dirname(__FILE__).\'/uni2cid_aj16.php\');'."\n"; |
||
876 | break; |
||
877 | } |
||
878 | } |
||
879 | } else { |
||
880 | // TrueType |
||
881 | $pfile .= '$enc=\''.$fmetric['enc'].'\';'."\n"; |
||
882 | $pfile .= '$file=\''.$fmetric['file'].'\';'."\n"; |
||
883 | $pfile .= '$ctg=\''.$fmetric['ctg'].'\';'."\n"; |
||
884 | // create CIDToGIDMap |
||
885 | $cidtogidmap = str_pad('', 131072, "\x00"); // (256 * 256 * 2) = 131072 |
||
886 | foreach ($ctg as $cid => $gid) { |
||
887 | $cidtogidmap = self::updateCIDtoGIDmap($cidtogidmap, $cid, $ctg[$cid]); |
||
888 | } |
||
889 | // store compressed CIDToGIDMap |
||
890 | $fp = TCPDF_STATIC::fopenLocal($outpath.$fmetric['ctg'], 'wb'); |
||
891 | fwrite($fp, gzcompress($cidtogidmap)); |
||
892 | fclose($fp); |
||
893 | } |
||
894 | } |
||
895 | $pfile .= '$desc=array('; |
||
896 | $pfile .= '\'Flags\'=>'.$fmetric['Flags'].','; |
||
897 | $pfile .= '\'FontBBox\'=>\'['.$fmetric['bbox'].']\','; |
||
898 | $pfile .= '\'ItalicAngle\'=>'.$fmetric['italicAngle'].','; |
||
899 | $pfile .= '\'Ascent\'=>'.$fmetric['Ascent'].','; |
||
900 | $pfile .= '\'Descent\'=>'.$fmetric['Descent'].','; |
||
901 | $pfile .= '\'Leading\'=>'.$fmetric['Leading'].','; |
||
902 | $pfile .= '\'CapHeight\'=>'.$fmetric['CapHeight'].','; |
||
903 | $pfile .= '\'XHeight\'=>'.$fmetric['XHeight'].','; |
||
904 | $pfile .= '\'StemV\'=>'.$fmetric['StemV'].','; |
||
905 | $pfile .= '\'StemH\'=>'.$fmetric['StemH'].','; |
||
906 | $pfile .= '\'AvgWidth\'=>'.$fmetric['AvgWidth'].','; |
||
907 | $pfile .= '\'MaxWidth\'=>'.$fmetric['MaxWidth'].','; |
||
908 | $pfile .= '\'MissingWidth\'=>'.$fmetric['MissingWidth'].''; |
||
909 | $pfile .= ');'."\n"; |
||
910 | if (!empty($fmetric['cbbox'])) { |
||
911 | $pfile .= '$cbbox=array('.substr($fmetric['cbbox'], 1).');'."\n"; |
||
912 | } |
||
913 | $pfile .= '$cw=array('.substr($fmetric['cw'], 1).');'."\n"; |
||
914 | $pfile .= '// --- EOF ---'."\n"; |
||
915 | // store file |
||
916 | $fp = TCPDF_STATIC::fopenLocal($outpath.$font_name.'.php', 'w'); |
||
917 | fwrite($fp, $pfile); |
||
918 | fclose($fp); |
||
919 | // return TCPDF font name |
||
920 | return $font_name; |
||
921 | } |
||
922 | |||
923 | /** |
||
924 | * Returs the checksum of a TTF table. |
||
925 | * @param string $table table to check |
||
926 | * @param int $length length of table in bytes |
||
927 | * @return int checksum |
||
928 | * @author Nicola Asuni |
||
929 | * @since 5.2.000 (2010-06-02) |
||
930 | * @public static |
||
931 | */ |
||
932 | public static function _getTTFtableChecksum($table, $length) { |
||
933 | $sum = 0; |
||
934 | $tlen = ($length / 4); |
||
935 | $offset = 0; |
||
936 | for ($i = 0; $i < $tlen; ++$i) { |
||
937 | $v = unpack('Ni', substr($table, $offset, 4)); |
||
938 | $sum += $v['i']; |
||
939 | $offset += 4; |
||
940 | } |
||
941 | $sum = unpack('Ni', pack('N', $sum)); |
||
942 | return $sum['i']; |
||
943 | } |
||
944 | |||
945 | /** |
||
946 | * Returns a subset of the TrueType font data without the unused glyphs. |
||
947 | * @param string $font TrueType font data. |
||
948 | * @param array $subsetchars Array of used characters (the glyphs to keep). |
||
949 | * @return string A subset of TrueType font data without the unused glyphs. |
||
950 | * @author Nicola Asuni |
||
951 | * @since 5.2.000 (2010-06-02) |
||
952 | * @public static |
||
953 | */ |
||
954 | public static function _getTrueTypeFontSubset($font, $subsetchars) { |
||
955 | ksort($subsetchars); |
||
956 | $offset = 0; // offset position of the font data |
||
957 | if (TCPDF_STATIC::_getULONG($font, $offset) != 0x10000) { |
||
958 | // sfnt version must be 0x00010000 for TrueType version 1.0. |
||
959 | return $font; |
||
960 | } |
||
961 | $c = 0; |
||
962 | $offset += 4; |
||
963 | // get number of tables |
||
964 | $numTables = TCPDF_STATIC::_getUSHORT($font, $offset); |
||
965 | $offset += 2; |
||
966 | // skip searchRange, entrySelector and rangeShift |
||
967 | $offset += 6; |
||
968 | // tables array |
||
969 | $table = array(); |
||
970 | // for each table |
||
971 | for ($i = 0; $i < $numTables; ++$i) { |
||
972 | // get table info |
||
973 | $tag = substr($font, $offset, 4); |
||
974 | $offset += 4; |
||
975 | $table[$tag] = array(); |
||
976 | $table[$tag]['checkSum'] = TCPDF_STATIC::_getULONG($font, $offset); |
||
977 | $offset += 4; |
||
978 | $table[$tag]['offset'] = TCPDF_STATIC::_getULONG($font, $offset); |
||
979 | $offset += 4; |
||
980 | $table[$tag]['length'] = TCPDF_STATIC::_getULONG($font, $offset); |
||
981 | $offset += 4; |
||
982 | } |
||
983 | // check magicNumber |
||
984 | $offset = $table['head']['offset'] + 12; |
||
985 | if (TCPDF_STATIC::_getULONG($font, $offset) != 0x5F0F3CF5) { |
||
986 | // magicNumber must be 0x5F0F3CF5 |
||
987 | return $font; |
||
988 | } |
||
989 | $offset += 4; |
||
990 | // get offset mode (indexToLocFormat : 0 = short, 1 = long) |
||
991 | $offset = $table['head']['offset'] + 50; |
||
992 | $short_offset = (TCPDF_STATIC::_getSHORT($font, $offset) == 0); |
||
993 | $offset += 2; |
||
994 | // get the offsets to the locations of the glyphs in the font, relative to the beginning of the glyphData table |
||
995 | $indexToLoc = array(); |
||
996 | $offset = $table['loca']['offset']; |
||
997 | if ($short_offset) { |
||
998 | // short version |
||
999 | $tot_num_glyphs = floor($table['loca']['length'] / 2); // numGlyphs + 1 |
||
1000 | for ($i = 0; $i < $tot_num_glyphs; ++$i) { |
||
1001 | $indexToLoc[$i] = TCPDF_STATIC::_getUSHORT($font, $offset) * 2; |
||
1002 | $offset += 2; |
||
1003 | } |
||
1004 | } else { |
||
1005 | // long version |
||
1006 | $tot_num_glyphs = ($table['loca']['length'] / 4); // numGlyphs + 1 |
||
1007 | for ($i = 0; $i < $tot_num_glyphs; ++$i) { |
||
1008 | $indexToLoc[$i] = TCPDF_STATIC::_getULONG($font, $offset); |
||
1009 | $offset += 4; |
||
1010 | } |
||
1011 | } |
||
1012 | // get glyphs indexes of chars from cmap table |
||
1013 | $subsetglyphs = array(); // glyph IDs on key |
||
1014 | $subsetglyphs[0] = true; // character codes that do not correspond to any glyph in the font should be mapped to glyph index 0 |
||
1015 | $offset = $table['cmap']['offset'] + 2; |
||
1016 | $numEncodingTables = TCPDF_STATIC::_getUSHORT($font, $offset); |
||
1017 | $offset += 2; |
||
1018 | $encodingTables = array(); |
||
1019 | for ($i = 0; $i < $numEncodingTables; ++$i) { |
||
1020 | $encodingTables[$i]['platformID'] = TCPDF_STATIC::_getUSHORT($font, $offset); |
||
1021 | $offset += 2; |
||
1022 | $encodingTables[$i]['encodingID'] = TCPDF_STATIC::_getUSHORT($font, $offset); |
||
1023 | $offset += 2; |
||
1024 | $encodingTables[$i]['offset'] = TCPDF_STATIC::_getULONG($font, $offset); |
||
1025 | $offset += 4; |
||
1026 | } |
||
1027 | foreach ($encodingTables as $enctable) { |
||
1028 | // get all platforms and encodings |
||
1029 | $offset = $table['cmap']['offset'] + $enctable['offset']; |
||
1030 | $format = TCPDF_STATIC::_getUSHORT($font, $offset); |
||
1031 | $offset += 2; |
||
1032 | switch ($format) { |
||
1033 | case 0: { // Format 0: Byte encoding table |
||
1034 | $offset += 4; // skip length and version/language |
||
1035 | for ($c = 0; $c < 256; ++$c) { |
||
1036 | if (isset($subsetchars[$c])) { |
||
1037 | $g = TCPDF_STATIC::_getBYTE($font, $offset); |
||
1038 | $subsetglyphs[$g] = true; |
||
1039 | } |
||
1040 | ++$offset; |
||
1041 | } |
||
1042 | break; |
||
1043 | } |
||
1044 | case 2: { // Format 2: High-byte mapping through table |
||
1045 | $offset += 4; // skip length and version/language |
||
1046 | $numSubHeaders = 0; |
||
1047 | for ($i = 0; $i < 256; ++$i) { |
||
1048 | // Array that maps high bytes to subHeaders: value is subHeader index * 8. |
||
1049 | $subHeaderKeys[$i] = (TCPDF_STATIC::_getUSHORT($font, $offset) / 8); |
||
1050 | $offset += 2; |
||
1051 | if ($numSubHeaders < $subHeaderKeys[$i]) { |
||
1052 | $numSubHeaders = $subHeaderKeys[$i]; |
||
1053 | } |
||
1054 | } |
||
1055 | // the number of subHeaders is equal to the max of subHeaderKeys + 1 |
||
1056 | ++$numSubHeaders; |
||
1057 | // read subHeader structures |
||
1058 | $subHeaders = array(); |
||
1059 | $numGlyphIndexArray = 0; |
||
1060 | for ($k = 0; $k < $numSubHeaders; ++$k) { |
||
1061 | $subHeaders[$k]['firstCode'] = TCPDF_STATIC::_getUSHORT($font, $offset); |
||
1062 | $offset += 2; |
||
1063 | $subHeaders[$k]['entryCount'] = TCPDF_STATIC::_getUSHORT($font, $offset); |
||
1064 | $offset += 2; |
||
1065 | $subHeaders[$k]['idDelta'] = TCPDF_STATIC::_getUSHORT($font, $offset); |
||
1066 | $offset += 2; |
||
1067 | $subHeaders[$k]['idRangeOffset'] = TCPDF_STATIC::_getUSHORT($font, $offset); |
||
1068 | $offset += 2; |
||
1069 | $subHeaders[$k]['idRangeOffset'] -= (2 + (($numSubHeaders - $k - 1) * 8)); |
||
1070 | $subHeaders[$k]['idRangeOffset'] /= 2; |
||
1071 | $numGlyphIndexArray += $subHeaders[$k]['entryCount']; |
||
1072 | } |
||
1073 | for ($k = 0; $k < $numGlyphIndexArray; ++$k) { |
||
1074 | $glyphIndexArray[$k] = TCPDF_STATIC::_getUSHORT($font, $offset); |
||
1075 | $offset += 2; |
||
1076 | } |
||
1077 | for ($i = 0; $i < 256; ++$i) { |
||
1078 | $k = $subHeaderKeys[$i]; |
||
1079 | if ($k == 0) { |
||
1080 | // one byte code |
||
1081 | $c = $i; |
||
1082 | if (isset($subsetchars[$c])) { |
||
1083 | $g = $glyphIndexArray[0]; |
||
1084 | $subsetglyphs[$g] = true; |
||
1085 | } |
||
1086 | } else { |
||
1087 | // two bytes code |
||
1088 | $start_byte = $subHeaders[$k]['firstCode']; |
||
1089 | $end_byte = $start_byte + $subHeaders[$k]['entryCount']; |
||
1090 | for ($j = $start_byte; $j < $end_byte; ++$j) { |
||
1091 | // combine high and low bytes |
||
1092 | $c = (($i << 8) + $j); |
||
1093 | if (isset($subsetchars[$c])) { |
||
1094 | $idRangeOffset = ($subHeaders[$k]['idRangeOffset'] + $j - $subHeaders[$k]['firstCode']); |
||
1095 | $g = ($glyphIndexArray[$idRangeOffset] + $subHeaders[$k]['idDelta']) % 65536; |
||
1096 | if ($g < 0) { |
||
1097 | $g = 0; |
||
1098 | } |
||
1099 | $subsetglyphs[$g] = true; |
||
1100 | } |
||
1101 | } |
||
1102 | } |
||
1103 | } |
||
1104 | break; |
||
1105 | } |
||
1106 | case 4: { // Format 4: Segment mapping to delta values |
||
1107 | $length = TCPDF_STATIC::_getUSHORT($font, $offset); |
||
1108 | $offset += 2; |
||
1109 | $offset += 2; // skip version/language |
||
1110 | $segCount = floor(TCPDF_STATIC::_getUSHORT($font, $offset) / 2); |
||
1111 | $offset += 2; |
||
1112 | $offset += 6; // skip searchRange, entrySelector, rangeShift |
||
1113 | $endCount = array(); // array of end character codes for each segment |
||
1114 | for ($k = 0; $k < $segCount; ++$k) { |
||
1115 | $endCount[$k] = TCPDF_STATIC::_getUSHORT($font, $offset); |
||
1116 | $offset += 2; |
||
1117 | } |
||
1118 | $offset += 2; // skip reservedPad |
||
1119 | $startCount = array(); // array of start character codes for each segment |
||
1120 | for ($k = 0; $k < $segCount; ++$k) { |
||
1121 | $startCount[$k] = TCPDF_STATIC::_getUSHORT($font, $offset); |
||
1122 | $offset += 2; |
||
1123 | } |
||
1124 | $idDelta = array(); // delta for all character codes in segment |
||
1125 | for ($k = 0; $k < $segCount; ++$k) { |
||
1126 | $idDelta[$k] = TCPDF_STATIC::_getUSHORT($font, $offset); |
||
1127 | $offset += 2; |
||
1128 | } |
||
1129 | $idRangeOffset = array(); // Offsets into glyphIdArray or 0 |
||
1130 | for ($k = 0; $k < $segCount; ++$k) { |
||
1131 | $idRangeOffset[$k] = TCPDF_STATIC::_getUSHORT($font, $offset); |
||
1132 | $offset += 2; |
||
1133 | } |
||
1134 | $gidlen = (floor($length / 2) - 8 - (4 * $segCount)); |
||
1135 | $glyphIdArray = array(); // glyph index array |
||
1136 | for ($k = 0; $k < $gidlen; ++$k) { |
||
1137 | $glyphIdArray[$k] = TCPDF_STATIC::_getUSHORT($font, $offset); |
||
1138 | $offset += 2; |
||
1139 | } |
||
1140 | for ($k = 0; $k < $segCount; ++$k) { |
||
1141 | for ($c = $startCount[$k]; $c <= $endCount[$k]; ++$c) { |
||
1142 | if (isset($subsetchars[$c])) { |
||
1143 | if ($idRangeOffset[$k] == 0) { |
||
1144 | $g = ($idDelta[$k] + $c) % 65536; |
||
1145 | } else { |
||
1146 | $gid = (floor($idRangeOffset[$k] / 2) + ($c - $startCount[$k]) - ($segCount - $k)); |
||
1147 | $g = ($glyphIdArray[$gid] + $idDelta[$k]) % 65536; |
||
1148 | } |
||
1149 | if ($g < 0) { |
||
1150 | $g = 0; |
||
1151 | } |
||
1152 | $subsetglyphs[$g] = true; |
||
1153 | } |
||
1154 | } |
||
1155 | } |
||
1156 | break; |
||
1157 | } |
||
1158 | case 6: { // Format 6: Trimmed table mapping |
||
1159 | $offset += 4; // skip length and version/language |
||
1160 | $firstCode = TCPDF_STATIC::_getUSHORT($font, $offset); |
||
1161 | $offset += 2; |
||
1162 | $entryCount = TCPDF_STATIC::_getUSHORT($font, $offset); |
||
1163 | $offset += 2; |
||
1164 | for ($k = 0; $k < $entryCount; ++$k) { |
||
1165 | $c = ($k + $firstCode); |
||
1166 | if (isset($subsetchars[$c])) { |
||
1167 | $g = TCPDF_STATIC::_getUSHORT($font, $offset); |
||
1168 | $subsetglyphs[$g] = true; |
||
1169 | } |
||
1170 | $offset += 2; |
||
1171 | } |
||
1172 | break; |
||
1173 | } |
||
1174 | case 8: { // Format 8: Mixed 16-bit and 32-bit coverage |
||
1175 | $offset += 10; // skip reserved, length and version/language |
||
1176 | for ($k = 0; $k < 8192; ++$k) { |
||
1177 | $is32[$k] = TCPDF_STATIC::_getBYTE($font, $offset); |
||
1178 | ++$offset; |
||
1179 | } |
||
1180 | $nGroups = TCPDF_STATIC::_getULONG($font, $offset); |
||
1181 | $offset += 4; |
||
1182 | for ($i = 0; $i < $nGroups; ++$i) { |
||
1183 | $startCharCode = TCPDF_STATIC::_getULONG($font, $offset); |
||
1184 | $offset += 4; |
||
1185 | $endCharCode = TCPDF_STATIC::_getULONG($font, $offset); |
||
1186 | $offset += 4; |
||
1187 | $startGlyphID = TCPDF_STATIC::_getULONG($font, $offset); |
||
1188 | $offset += 4; |
||
1189 | for ($k = $startCharCode; $k <= $endCharCode; ++$k) { |
||
1190 | $is32idx = floor($c / 8); |
||
1191 | if ((isset($is32[$is32idx])) AND (($is32[$is32idx] & (1 << (7 - ($c % 8)))) == 0)) { |
||
1192 | $c = $k; |
||
1193 | } else { |
||
1194 | // 32 bit format |
||
1195 | // convert to decimal (http://www.unicode.org/faq//utf_bom.html#utf16-4) |
||
1196 | //LEAD_OFFSET = (0xD800 - (0x10000 >> 10)) = 55232 |
||
1197 | //SURROGATE_OFFSET = (0x10000 - (0xD800 << 10) - 0xDC00) = -56613888 |
||
1198 | $c = ((55232 + ($k >> 10)) << 10) + (0xDC00 + ($k & 0x3FF)) -56613888; |
||
1199 | } |
||
1200 | if (isset($subsetchars[$c])) { |
||
1201 | $subsetglyphs[$startGlyphID] = true; |
||
1202 | } |
||
1203 | ++$startGlyphID; |
||
1204 | } |
||
1205 | } |
||
1206 | break; |
||
1207 | } |
||
1208 | case 10: { // Format 10: Trimmed array |
||
1209 | $offset += 10; // skip reserved, length and version/language |
||
1210 | $startCharCode = TCPDF_STATIC::_getULONG($font, $offset); |
||
1211 | $offset += 4; |
||
1212 | $numChars = TCPDF_STATIC::_getULONG($font, $offset); |
||
1213 | $offset += 4; |
||
1214 | for ($k = 0; $k < $numChars; ++$k) { |
||
1215 | $c = ($k + $startCharCode); |
||
1216 | if (isset($subsetchars[$c])) { |
||
1217 | $g = TCPDF_STATIC::_getUSHORT($font, $offset); |
||
1218 | $subsetglyphs[$g] = true; |
||
1219 | } |
||
1220 | $offset += 2; |
||
1221 | } |
||
1222 | break; |
||
1223 | } |
||
1224 | case 12: { // Format 12: Segmented coverage |
||
1225 | $offset += 10; // skip length and version/language |
||
1226 | $nGroups = TCPDF_STATIC::_getULONG($font, $offset); |
||
1227 | $offset += 4; |
||
1228 | for ($k = 0; $k < $nGroups; ++$k) { |
||
1229 | $startCharCode = TCPDF_STATIC::_getULONG($font, $offset); |
||
1230 | $offset += 4; |
||
1231 | $endCharCode = TCPDF_STATIC::_getULONG($font, $offset); |
||
1232 | $offset += 4; |
||
1233 | $startGlyphCode = TCPDF_STATIC::_getULONG($font, $offset); |
||
1234 | $offset += 4; |
||
1235 | for ($c = $startCharCode; $c <= $endCharCode; ++$c) { |
||
1236 | if (isset($subsetchars[$c])) { |
||
1237 | $subsetglyphs[$startGlyphCode] = true; |
||
1238 | } |
||
1239 | ++$startGlyphCode; |
||
1240 | } |
||
1241 | } |
||
1242 | break; |
||
1243 | } |
||
1244 | case 13: { // Format 13: Many-to-one range mappings |
||
1245 | // to be implemented ... |
||
1246 | break; |
||
1247 | } |
||
1248 | case 14: { // Format 14: Unicode Variation Sequences |
||
1249 | // to be implemented ... |
||
1250 | break; |
||
1251 | } |
||
1252 | } |
||
1253 | } |
||
1254 | // include all parts of composite glyphs |
||
1255 | $new_sga = $subsetglyphs; |
||
1256 | while (!empty($new_sga)) { |
||
1257 | $sga = $new_sga; |
||
1258 | $new_sga = array(); |
||
1259 | foreach ($sga as $key => $val) { |
||
1260 | if (isset($indexToLoc[$key])) { |
||
1261 | $offset = ($table['glyf']['offset'] + $indexToLoc[$key]); |
||
1262 | $numberOfContours = TCPDF_STATIC::_getSHORT($font, $offset); |
||
1263 | $offset += 2; |
||
1264 | if ($numberOfContours < 0) { // composite glyph |
||
1265 | $offset += 8; // skip xMin, yMin, xMax, yMax |
||
1266 | do { |
||
1267 | $flags = TCPDF_STATIC::_getUSHORT($font, $offset); |
||
1268 | $offset += 2; |
||
1269 | $glyphIndex = TCPDF_STATIC::_getUSHORT($font, $offset); |
||
1270 | $offset += 2; |
||
1271 | if (!isset($subsetglyphs[$glyphIndex])) { |
||
1272 | // add missing glyphs |
||
1273 | $new_sga[$glyphIndex] = true; |
||
1274 | } |
||
1275 | // skip some bytes by case |
||
1276 | if ($flags & 1) { |
||
1277 | $offset += 4; |
||
1278 | } else { |
||
1279 | $offset += 2; |
||
1280 | } |
||
1281 | if ($flags & 8) { |
||
1282 | $offset += 2; |
||
1283 | } elseif ($flags & 64) { |
||
1284 | $offset += 4; |
||
1285 | } elseif ($flags & 128) { |
||
1286 | $offset += 8; |
||
1287 | } |
||
1288 | } while ($flags & 32); |
||
1289 | } |
||
1290 | } |
||
1291 | } |
||
1292 | $subsetglyphs += $new_sga; |
||
1293 | } |
||
1294 | // sort glyphs by key (and remove duplicates) |
||
1295 | ksort($subsetglyphs); |
||
1296 | // build new glyf and loca tables |
||
1297 | $glyf = ''; |
||
1298 | $loca = ''; |
||
1299 | $offset = 0; |
||
1300 | $glyf_offset = $table['glyf']['offset']; |
||
1301 | for ($i = 0; $i < $tot_num_glyphs; ++$i) { |
||
1302 | if (isset($subsetglyphs[$i])) { |
||
1303 | $length = ($indexToLoc[($i + 1)] - $indexToLoc[$i]); |
||
1304 | $glyf .= substr($font, ($glyf_offset + $indexToLoc[$i]), $length); |
||
1305 | } else { |
||
1306 | $length = 0; |
||
1307 | } |
||
1308 | if ($short_offset) { |
||
1309 | $loca .= pack('n', floor($offset / 2)); |
||
1310 | } else { |
||
1311 | $loca .= pack('N', $offset); |
||
1312 | } |
||
1313 | $offset += $length; |
||
1314 | } |
||
1315 | // array of table names to preserve (loca and glyf tables will be added later) |
||
1316 | // the cmap table is not needed and shall not be present, since the mapping from character codes to glyph descriptions is provided separately |
||
1317 | $table_names = array ('head', 'hhea', 'hmtx', 'maxp', 'cvt ', 'fpgm', 'prep'); // minimum required table names |
||
1318 | // get the tables to preserve |
||
1319 | $offset = 12; |
||
1320 | foreach ($table as $tag => $val) { |
||
1321 | if (in_array($tag, $table_names)) { |
||
1322 | $table[$tag]['data'] = substr($font, $table[$tag]['offset'], $table[$tag]['length']); |
||
1323 | if ($tag == 'head') { |
||
1324 | // set the checkSumAdjustment to 0 |
||
1325 | $table[$tag]['data'] = substr($table[$tag]['data'], 0, 8)."\x0\x0\x0\x0".substr($table[$tag]['data'], 12); |
||
1326 | } |
||
1327 | $table[$tag]['offset'] = $offset; |
||
1328 | $offset += $table[$tag]['length']; |
||
1329 | $numPad = ($offset + 3 & ~3) - $offset; |
||
1330 | if($numPad > 0) { |
||
1331 | $table[$tag]['data'] .= str_repeat("\x0", $numPad); |
||
1332 | $offset += $numPad; |
||
1333 | } |
||
1334 | // check sum is not changed (so keep the following line commented) |
||
1335 | //$table[$tag]['checkSum'] = self::_getTTFtableChecksum($table[$tag]['data'], $table[$tag]['length'] + $numPad); |
||
1336 | } else { |
||
1337 | unset($table[$tag]); |
||
1338 | } |
||
1339 | } |
||
1340 | // add loca |
||
1341 | $table['loca'] = array(); |
||
1342 | $table['loca']['data'] = $loca; |
||
1343 | $table['loca']['length'] = strlen($loca); |
||
1344 | $table['loca']['offset'] = $offset; |
||
1345 | $offset += $table['loca']['length']; |
||
1346 | $numPad = ($offset + 3 & ~3) - $offset; |
||
1347 | if($numPad > 0) { |
||
1348 | $table['loca']['data'] .= str_repeat("\x0", $numPad); |
||
1349 | $offset += $numPad; |
||
1350 | } |
||
1351 | $table['loca']['checkSum'] = self::_getTTFtableChecksum($table['loca']['data'], $table['loca']['length'] + $numPad); |
||
1352 | // add glyf |
||
1353 | $table['glyf'] = array(); |
||
1354 | $table['glyf']['data'] = $glyf; |
||
1355 | $table['glyf']['length'] = strlen($glyf); |
||
1356 | $table['glyf']['offset'] = $offset; |
||
1357 | $offset += $table['glyf']['length']; |
||
1358 | $numPad = ($offset + 3 & ~3) - $offset; |
||
1359 | if($numPad > 0) { |
||
1360 | $table['glyf']['data'] .= str_repeat("\x0", $numPad); |
||
1361 | $offset += $numPad; |
||
1362 | } |
||
1363 | $table['glyf']['checkSum'] = self::_getTTFtableChecksum($table['glyf']['data'], $table['glyf']['length'] + $numPad); |
||
1364 | // rebuild font |
||
1365 | $font = ''; |
||
1366 | $font .= pack('N', 0x10000); // sfnt version |
||
1367 | $numTables = count($table); |
||
1368 | $font .= pack('n', $numTables); // numTables |
||
1369 | $entrySelector = floor(log($numTables, 2)); |
||
1370 | $searchRange = pow(2, $entrySelector) * 16; |
||
1371 | $rangeShift = ($numTables * 16) - $searchRange; |
||
1372 | $font .= pack('n', $searchRange); // searchRange |
||
1373 | $font .= pack('n', $entrySelector); // entrySelector |
||
1374 | $font .= pack('n', $rangeShift); // rangeShift |
||
1375 | $offset = ($numTables * 16); |
||
1376 | foreach ($table as $tag => $data) { |
||
1377 | $font .= $tag; // tag |
||
1378 | $font .= pack('N', $data['checkSum']); // checkSum |
||
1379 | $font .= pack('N', ($data['offset'] + $offset)); // offset |
||
1380 | $font .= pack('N', $data['length']); // length |
||
1381 | } |
||
1382 | foreach ($table as $data) { |
||
1383 | $font .= $data['data']; |
||
1384 | } |
||
1385 | // set checkSumAdjustment on head table |
||
1386 | $checkSumAdjustment = 0xB1B0AFBA - self::_getTTFtableChecksum($font, strlen($font)); |
||
1387 | $font = substr($font, 0, $table['head']['offset'] + $offset + 8).pack('N', $checkSumAdjustment).substr($font, $table['head']['offset'] + $offset + 12); |
||
1388 | return $font; |
||
1389 | } |
||
1390 | |||
1391 | /** |
||
1392 | * Outputs font widths |
||
1393 | * @param array $font font data |
||
1394 | * @param int $cidoffset offset for CID values |
||
1395 | * @return string PDF command string for font widths |
||
1396 | * @author Nicola Asuni |
||
1397 | * @since 4.4.000 (2008-12-07) |
||
1398 | * @public static |
||
1399 | */ |
||
1400 | public static function _putfontwidths($font, $cidoffset=0) { |
||
1401 | ksort($font['cw']); |
||
1402 | $rangeid = 0; |
||
1403 | $range = array(); |
||
1404 | $prevcid = -2; |
||
1405 | $prevwidth = -1; |
||
1406 | $interval = false; |
||
1407 | // for each character |
||
1408 | foreach ($font['cw'] as $cid => $width) { |
||
1409 | $cid -= $cidoffset; |
||
1410 | if ($font['subset'] AND (!isset($font['subsetchars'][$cid]))) { |
||
1411 | // ignore the unused characters (font subsetting) |
||
1412 | continue; |
||
1413 | } |
||
1414 | if ($width != $font['dw']) { |
||
1415 | if ($cid == ($prevcid + 1)) { |
||
1416 | // consecutive CID |
||
1417 | if ($width == $prevwidth) { |
||
1418 | if ($width == $range[$rangeid][0]) { |
||
1419 | $range[$rangeid][] = $width; |
||
1420 | } else { |
||
1421 | array_pop($range[$rangeid]); |
||
1422 | // new range |
||
1423 | $rangeid = $prevcid; |
||
1424 | $range[$rangeid] = array(); |
||
1425 | $range[$rangeid][] = $prevwidth; |
||
1426 | $range[$rangeid][] = $width; |
||
1427 | } |
||
1428 | $interval = true; |
||
1429 | $range[$rangeid]['interval'] = true; |
||
1430 | } else { |
||
1431 | if ($interval) { |
||
1432 | // new range |
||
1433 | $rangeid = $cid; |
||
1434 | $range[$rangeid] = array(); |
||
1435 | $range[$rangeid][] = $width; |
||
1436 | } else { |
||
1437 | $range[$rangeid][] = $width; |
||
1438 | } |
||
1439 | $interval = false; |
||
1440 | } |
||
1441 | } else { |
||
1442 | // new range |
||
1443 | $rangeid = $cid; |
||
1444 | $range[$rangeid] = array(); |
||
1445 | $range[$rangeid][] = $width; |
||
1446 | $interval = false; |
||
1447 | } |
||
1448 | $prevcid = $cid; |
||
1449 | $prevwidth = $width; |
||
1450 | } |
||
1451 | } |
||
1452 | // optimize ranges |
||
1453 | $prevk = -1; |
||
1454 | $nextk = -1; |
||
1455 | $prevint = false; |
||
1456 | foreach ($range as $k => $ws) { |
||
1457 | $cws = count($ws); |
||
1458 | if (($k == $nextk) AND (!$prevint) AND ((!isset($ws['interval'])) OR ($cws < 4))) { |
||
1459 | if (isset($range[$k]['interval'])) { |
||
1460 | unset($range[$k]['interval']); |
||
1461 | } |
||
1462 | $range[$prevk] = array_merge($range[$prevk], $range[$k]); |
||
1463 | unset($range[$k]); |
||
1464 | } else { |
||
1465 | $prevk = $k; |
||
1466 | } |
||
1467 | $nextk = $k + $cws; |
||
1468 | if (isset($ws['interval'])) { |
||
1469 | if ($cws > 3) { |
||
1470 | $prevint = true; |
||
1471 | } else { |
||
1472 | $prevint = false; |
||
1473 | } |
||
1474 | if (isset($range[$k]['interval'])) { |
||
1475 | unset($range[$k]['interval']); |
||
1476 | } |
||
1477 | --$nextk; |
||
1478 | } else { |
||
1479 | $prevint = false; |
||
1480 | } |
||
1481 | } |
||
1482 | // output data |
||
1483 | $w = ''; |
||
1484 | foreach ($range as $k => $ws) { |
||
1485 | if (count(array_count_values($ws)) == 1) { |
||
1486 | // interval mode is more compact |
||
1487 | $w .= ' '.$k.' '.($k + count($ws) - 1).' '.$ws[0]; |
||
1488 | } else { |
||
1489 | // range mode |
||
1490 | $w .= ' '.$k.' [ '.implode(' ', $ws).' ]'; |
||
1491 | } |
||
1492 | } |
||
1493 | return '/W ['.$w.' ]'; |
||
1494 | } |
||
1495 | |||
1496 | |||
1497 | |||
1498 | |||
1499 | /** |
||
1500 | * Update the CIDToGIDMap string with a new value. |
||
1501 | * @param string $map CIDToGIDMap. |
||
1502 | * @param int $cid CID value. |
||
1503 | * @param int $gid GID value. |
||
1504 | * @return string CIDToGIDMap. |
||
1505 | * @author Nicola Asuni |
||
1506 | * @since 5.9.123 (2011-09-29) |
||
1507 | * @public static |
||
1508 | */ |
||
1509 | public static function updateCIDtoGIDmap($map, $cid, $gid) { |
||
1510 | if (($cid >= 0) AND ($cid <= 0xFFFF) AND ($gid >= 0)) { |
||
1511 | if ($gid > 0xFFFF) { |
||
1512 | $gid -= 0x10000; |
||
1513 | } |
||
1514 | $map[($cid * 2)] = chr($gid >> 8); |
||
1515 | $map[(($cid * 2) + 1)] = chr($gid & 0xFF); |
||
1516 | } |
||
1517 | return $map; |
||
1518 | } |
||
1519 | |||
1520 | /** |
||
1521 | * Return fonts path |
||
1522 | * @return string |
||
1523 | * @public static |
||
1524 | */ |
||
1525 | public static function _getfontpath() { |
||
1526 | if (!defined('K_PATH_FONTS') AND is_dir($fdir = realpath(dirname(__FILE__).'/../fonts'))) { |
||
1527 | if (substr($fdir, -1) != '/') { |
||
1528 | $fdir .= '/'; |
||
1529 | } |
||
1530 | define('K_PATH_FONTS', $fdir); |
||
1531 | } |
||
1532 | return defined('K_PATH_FONTS') ? K_PATH_FONTS : ''; |
||
1533 | } |
||
1534 | |||
1535 | |||
1536 | |||
1537 | /** |
||
1538 | * Return font full path |
||
1539 | * @param string $file Font file name. |
||
1540 | * @param string $fontdir Font directory (set to false fto search on default directories) |
||
1541 | * @return string Font full path or empty string |
||
1542 | * @author Nicola Asuni |
||
1543 | * @since 6.0.025 |
||
1544 | * @public static |
||
1545 | */ |
||
1546 | public static function getFontFullPath($file, $fontdir=false) { |
||
1547 | $fontfile = ''; |
||
1548 | // search files on various directories |
||
1549 | if (($fontdir !== false) AND @TCPDF_STATIC::file_exists($fontdir.$file)) { |
||
1550 | $fontfile = $fontdir.$file; |
||
1551 | } elseif (@TCPDF_STATIC::file_exists(self::_getfontpath().$file)) { |
||
1552 | $fontfile = self::_getfontpath().$file; |
||
1553 | } elseif (@TCPDF_STATIC::file_exists($file)) { |
||
1554 | $fontfile = $file; |
||
1555 | } |
||
1556 | return $fontfile; |
||
1557 | } |
||
1558 | |||
1559 | |||
1560 | |||
1561 | |||
1562 | /** |
||
1563 | * Get a reference font size. |
||
1564 | * @param string $size String containing font size value. |
||
1565 | * @param float $refsize Reference font size in points. |
||
1566 | * @return float value in points |
||
1567 | * @public static |
||
1568 | */ |
||
1569 | public static function getFontRefSize($size, $refsize=12) { |
||
1570 | switch ($size) { |
||
1571 | case 'xx-small': { |
||
1572 | $size = ($refsize - 4); |
||
1573 | break; |
||
1574 | } |
||
1575 | case 'x-small': { |
||
1576 | $size = ($refsize - 3); |
||
1577 | break; |
||
1578 | } |
||
1579 | case 'small': { |
||
1580 | $size = ($refsize - 2); |
||
1581 | break; |
||
1582 | } |
||
1583 | case 'medium': { |
||
1584 | $size = $refsize; |
||
1585 | break; |
||
1586 | } |
||
1587 | case 'large': { |
||
1588 | $size = ($refsize + 2); |
||
1589 | break; |
||
1590 | } |
||
1591 | case 'x-large': { |
||
1592 | $size = ($refsize + 4); |
||
1593 | break; |
||
1594 | } |
||
1595 | case 'xx-large': { |
||
1596 | $size = ($refsize + 6); |
||
1597 | break; |
||
1598 | } |
||
1599 | case 'smaller': { |
||
1600 | $size = ($refsize - 3); |
||
1601 | break; |
||
1602 | } |
||
1603 | case 'larger': { |
||
1604 | $size = ($refsize + 3); |
||
1605 | break; |
||
1606 | } |
||
1607 | } |
||
1608 | return $size; |
||
1609 | } |
||
1610 | |||
1611 | |||
1612 | |||
1613 | |||
1614 | |||
1615 | |||
1616 | |||
1617 | |||
1618 | |||
1619 | |||
1620 | |||
1621 | |||
1622 | |||
1623 | |||
1624 | |||
1625 | |||
1626 | |||
1627 | |||
1628 | |||
1629 | |||
1630 | |||
1631 | |||
1632 | |||
1633 | |||
1634 | |||
1635 | |||
1636 | |||
1637 | |||
1638 | |||
1639 | |||
1640 | |||
1641 | |||
1642 | |||
1643 | |||
1644 | |||
1645 | |||
1646 | |||
1647 | |||
1648 | |||
1649 | |||
1650 | // ==================================================================================================================== |
||
1651 | // REIMPLEMENTED |
||
1652 | // ==================================================================================================================== |
||
1653 | |||
1654 | |||
1655 | |||
1656 | |||
1657 | |||
1658 | |||
1659 | |||
1660 | |||
1661 | /** |
||
1662 | * Returns the unicode caracter specified by the value |
||
1663 | * @param int $c UTF-8 value |
||
1664 | * @param boolean $unicode True if we are in unicode mode, false otherwise. |
||
1665 | * @return string Returns the specified character. |
||
1666 | * @since 2.3.000 (2008-03-05) |
||
1667 | * @public static |
||
1668 | */ |
||
1669 | public static function unichr($c, $unicode=true) { |
||
1670 | $c = intval($c); |
||
1671 | if (!$unicode) { |
||
1672 | return chr($c); |
||
1673 | } elseif ($c <= 0x7F) { |
||
1674 | // one byte |
||
1675 | return chr($c); |
||
1676 | } elseif ($c <= 0x7FF) { |
||
1677 | // two bytes |
||
1678 | return chr(0xC0 | $c >> 6).chr(0x80 | $c & 0x3F); |
||
1679 | } elseif ($c <= 0xFFFF) { |
||
1680 | // three bytes |
||
1681 | return chr(0xE0 | $c >> 12).chr(0x80 | $c >> 6 & 0x3F).chr(0x80 | $c & 0x3F); |
||
1682 | } elseif ($c <= 0x10FFFF) { |
||
1683 | // four bytes |
||
1684 | return chr(0xF0 | $c >> 18).chr(0x80 | $c >> 12 & 0x3F).chr(0x80 | $c >> 6 & 0x3F).chr(0x80 | $c & 0x3F); |
||
1685 | } else { |
||
1686 | return ''; |
||
1687 | } |
||
1688 | } |
||
1689 | |||
1690 | /** |
||
1691 | * Returns the unicode caracter specified by UTF-8 value |
||
1692 | * @param int $c UTF-8 value |
||
1693 | * @return string Returns the specified character. |
||
1694 | * @public static |
||
1695 | */ |
||
1696 | public static function unichrUnicode($c) { |
||
1697 | return self::unichr($c, true); |
||
1698 | } |
||
1699 | |||
1700 | /** |
||
1701 | * Returns the unicode caracter specified by ASCII value |
||
1702 | * @param int $c UTF-8 value |
||
1703 | * @return string Returns the specified character. |
||
1704 | * @public static |
||
1705 | */ |
||
1706 | public static function unichrASCII($c) { |
||
1707 | return self::unichr($c, false); |
||
1708 | } |
||
1709 | |||
1710 | /** |
||
1711 | * Converts array of UTF-8 characters to UTF16-BE string.<br> |
||
1712 | * Based on: http://www.faqs.org/rfcs/rfc2781.html |
||
1713 | * <pre> |
||
1714 | * Encoding UTF-16: |
||
1715 | * |
||
1716 | * Encoding of a single character from an ISO 10646 character value to |
||
1717 | * UTF-16 proceeds as follows. Let U be the character number, no greater |
||
1718 | * than 0x10FFFF. |
||
1719 | * |
||
1720 | * 1) If U < 0x10000, encode U as a 16-bit unsigned integer and |
||
1721 | * terminate. |
||
1722 | * |
||
1723 | * 2) Let U' = U - 0x10000. Because U is less than or equal to 0x10FFFF, |
||
1724 | * U' must be less than or equal to 0xFFFFF. That is, U' can be |
||
1725 | * represented in 20 bits. |
||
1726 | * |
||
1727 | * 3) Initialize two 16-bit unsigned integers, W1 and W2, to 0xD800 and |
||
1728 | * 0xDC00, respectively. These integers each have 10 bits free to |
||
1729 | * encode the character value, for a total of 20 bits. |
||
1730 | * |
||
1731 | * 4) Assign the 10 high-order bits of the 20-bit U' to the 10 low-order |
||
1732 | * bits of W1 and the 10 low-order bits of U' to the 10 low-order |
||
1733 | * bits of W2. Terminate. |
||
1734 | * |
||
1735 | * Graphically, steps 2 through 4 look like: |
||
1736 | * U' = yyyyyyyyyyxxxxxxxxxx |
||
1737 | * W1 = 110110yyyyyyyyyy |
||
1738 | * W2 = 110111xxxxxxxxxx |
||
1739 | * </pre> |
||
1740 | * @param array $unicode array containing UTF-8 unicode values |
||
1741 | * @param boolean $setbom if true set the Byte Order Mark (BOM = 0xFEFF) |
||
1742 | * @return string |
||
1743 | * @protected |
||
1744 | * @author Nicola Asuni |
||
1745 | * @since 2.1.000 (2008-01-08) |
||
1746 | * @public static |
||
1747 | */ |
||
1748 | public static function arrUTF8ToUTF16BE($unicode, $setbom=false) { |
||
1749 | $outstr = ''; // string to be returned |
||
1750 | if ($setbom) { |
||
1751 | $outstr .= "\xFE\xFF"; // Byte Order Mark (BOM) |
||
1752 | } |
||
1753 | foreach ($unicode as $char) { |
||
1754 | if ($char == 0x200b) { |
||
1755 | // skip Unicode Character 'ZERO WIDTH SPACE' (DEC:8203, U+200B) |
||
1756 | } elseif ($char == 0xFFFD) { |
||
1757 | $outstr .= "\xFF\xFD"; // replacement character |
||
1758 | } elseif ($char < 0x10000) { |
||
1759 | $outstr .= chr($char >> 0x08); |
||
1760 | $outstr .= chr($char & 0xFF); |
||
1761 | } else { |
||
1762 | $char -= 0x10000; |
||
1763 | $w1 = 0xD800 | ($char >> 0x0a); |
||
1764 | $w2 = 0xDC00 | ($char & 0x3FF); |
||
1765 | $outstr .= chr($w1 >> 0x08); |
||
1766 | $outstr .= chr($w1 & 0xFF); |
||
1767 | $outstr .= chr($w2 >> 0x08); |
||
1768 | $outstr .= chr($w2 & 0xFF); |
||
1769 | } |
||
1770 | } |
||
1771 | return $outstr; |
||
1772 | } |
||
1773 | |||
1774 | /** |
||
1775 | * Convert an array of UTF8 values to array of unicode characters |
||
1776 | * @param array $ta The input array of UTF8 values. |
||
1777 | * @param boolean $isunicode True for Unicode mode, false otherwise. |
||
1778 | * @return array Return array of unicode characters |
||
1779 | * @since 4.5.037 (2009-04-07) |
||
1780 | * @public static |
||
1781 | */ |
||
1782 | public static function UTF8ArrayToUniArray($ta, $isunicode=true) { |
||
1783 | if ($isunicode) { |
||
1784 | return array_map(static::class.'::unichrUnicode', $ta); |
||
1785 | } |
||
1786 | return array_map(static::class.'::unichrASCII', $ta); |
||
1787 | } |
||
1788 | |||
1789 | /** |
||
1790 | * Extract a slice of the $strarr array and return it as string. |
||
1791 | * @param string[] $strarr The input array of characters. |
||
1792 | * @param int $start the starting element of $strarr. |
||
1793 | * @param int $end first element that will not be returned. |
||
1794 | * @param boolean $unicode True if we are in unicode mode, false otherwise. |
||
1795 | * @return string Return part of a string |
||
1796 | * @public static |
||
1797 | */ |
||
1798 | public static function UTF8ArrSubString($strarr, $start='', $end='', $unicode=true) { |
||
1799 | if (strlen($start) == 0) { |
||
1800 | $start = 0; |
||
1801 | } |
||
1802 | if (strlen($end) == 0) { |
||
1803 | $end = count($strarr); |
||
1804 | } |
||
1805 | $string = ''; |
||
1806 | for ($i = $start; $i < $end; ++$i) { |
||
1807 | $string .= self::unichr($strarr[$i], $unicode); |
||
1808 | } |
||
1809 | return $string; |
||
1810 | } |
||
1811 | |||
1812 | /** |
||
1813 | * Extract a slice of the $uniarr array and return it as string. |
||
1814 | * @param string[] $uniarr The input array of characters. |
||
1815 | * @param int $start the starting element of $strarr. |
||
1816 | * @param int $end first element that will not be returned. |
||
1817 | * @return string Return part of a string |
||
1818 | * @since 4.5.037 (2009-04-07) |
||
1819 | * @public static |
||
1820 | */ |
||
1821 | public static function UniArrSubString($uniarr, $start='', $end='') { |
||
1822 | if (strlen($start) == 0) { |
||
1823 | $start = 0; |
||
1824 | } |
||
1825 | if (strlen($end) == 0) { |
||
1826 | $end = count($uniarr); |
||
1827 | } |
||
1828 | $string = ''; |
||
1829 | for ($i=$start; $i < $end; ++$i) { |
||
1830 | $string .= $uniarr[$i]; |
||
1831 | } |
||
1832 | return $string; |
||
1833 | } |
||
1834 | |||
1835 | /** |
||
1836 | * Converts UTF-8 characters array to array of Latin1 characters array<br> |
||
1837 | * @param array $unicode array containing UTF-8 unicode values |
||
1838 | * @return array |
||
1839 | * @author Nicola Asuni |
||
1840 | * @since 4.8.023 (2010-01-15) |
||
1841 | * @public static |
||
1842 | */ |
||
1843 | public static function UTF8ArrToLatin1Arr($unicode) { |
||
1844 | $outarr = array(); // array to be returned |
||
1845 | foreach ($unicode as $char) { |
||
1846 | if ($char < 256) { |
||
1847 | $outarr[] = $char; |
||
1848 | } elseif (array_key_exists($char, TCPDF_FONT_DATA::$uni_utf8tolatin)) { |
||
1849 | // map from UTF-8 |
||
1850 | $outarr[] = TCPDF_FONT_DATA::$uni_utf8tolatin[$char]; |
||
1851 | } elseif ($char == 0xFFFD) { |
||
1852 | // skip |
||
1853 | } else { |
||
1854 | $outarr[] = 63; // '?' character |
||
1855 | } |
||
1856 | } |
||
1857 | return $outarr; |
||
1858 | } |
||
1859 | |||
1860 | /** |
||
1861 | * Converts UTF-8 characters array to Latin1 string<br> |
||
1862 | * @param array $unicode array containing UTF-8 unicode values |
||
1863 | * @return string |
||
1864 | * @author Nicola Asuni |
||
1865 | * @since 4.8.023 (2010-01-15) |
||
1866 | * @public static |
||
1867 | */ |
||
1868 | public static function UTF8ArrToLatin1($unicode) { |
||
1869 | $outstr = ''; // string to be returned |
||
1870 | foreach ($unicode as $char) { |
||
1871 | if ($char < 256) { |
||
1872 | $outstr .= chr($char); |
||
1873 | } elseif (array_key_exists($char, TCPDF_FONT_DATA::$uni_utf8tolatin)) { |
||
1874 | // map from UTF-8 |
||
1875 | $outstr .= chr(TCPDF_FONT_DATA::$uni_utf8tolatin[$char]); |
||
1876 | } elseif ($char == 0xFFFD) { |
||
1877 | // skip |
||
1878 | } else { |
||
1879 | $outstr .= '?'; |
||
1880 | } |
||
1881 | } |
||
1882 | return $outstr; |
||
1883 | } |
||
1884 | |||
1885 | /** |
||
1886 | * Converts UTF-8 character to integer value.<br> |
||
1887 | * Uses the getUniord() method if the value is not cached. |
||
1888 | * @param string $uch character string to process. |
||
1889 | * @return int Unicode value |
||
1890 | * @public static |
||
1891 | */ |
||
1892 | public static function uniord($uch) { |
||
1893 | if (!isset(self::$cache_uniord[$uch])) { |
||
1894 | self::$cache_uniord[$uch] = self::getUniord($uch); |
||
1895 | } |
||
1896 | return self::$cache_uniord[$uch]; |
||
1897 | } |
||
1898 | |||
1899 | /** |
||
1900 | * Converts UTF-8 character to integer value.<br> |
||
1901 | * Invalid byte sequences will be replaced with 0xFFFD (replacement character)<br> |
||
1902 | * Based on: http://www.faqs.org/rfcs/rfc3629.html |
||
1903 | * <pre> |
||
1904 | * Char. number range | UTF-8 octet sequence |
||
1905 | * (hexadecimal) | (binary) |
||
1906 | * --------------------+----------------------------------------------- |
||
1907 | * 0000 0000-0000 007F | 0xxxxxxx |
||
1908 | * 0000 0080-0000 07FF | 110xxxxx 10xxxxxx |
||
1909 | * 0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx |
||
1910 | * 0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx |
||
1911 | * --------------------------------------------------------------------- |
||
1912 | * |
||
1913 | * ABFN notation: |
||
1914 | * --------------------------------------------------------------------- |
||
1915 | * UTF8-octets = *( UTF8-char ) |
||
1916 | * UTF8-char = UTF8-1 / UTF8-2 / UTF8-3 / UTF8-4 |
||
1917 | * UTF8-1 = %x00-7F |
||
1918 | * UTF8-2 = %xC2-DF UTF8-tail |
||
1919 | * |
||
1920 | * UTF8-3 = %xE0 %xA0-BF UTF8-tail / %xE1-EC 2( UTF8-tail ) / |
||
1921 | * %xED %x80-9F UTF8-tail / %xEE-EF 2( UTF8-tail ) |
||
1922 | * UTF8-4 = %xF0 %x90-BF 2( UTF8-tail ) / %xF1-F3 3( UTF8-tail ) / |
||
1923 | * %xF4 %x80-8F 2( UTF8-tail ) |
||
1924 | * UTF8-tail = %x80-BF |
||
1925 | * --------------------------------------------------------------------- |
||
1926 | * </pre> |
||
1927 | * @param string $uch character string to process. |
||
1928 | * @return int Unicode value |
||
1929 | * @author Nicola Asuni |
||
1930 | * @public static |
||
1931 | */ |
||
1932 | public static function getUniord($uch) { |
||
1933 | if (function_exists('mb_convert_encoding')) { |
||
1934 | list(, $char) = @unpack('N', mb_convert_encoding($uch, 'UCS-4BE', 'UTF-8')); |
||
1935 | if ($char >= 0) { |
||
1936 | return $char; |
||
1937 | } |
||
1938 | } |
||
1939 | $bytes = array(); // array containing single character byte sequences |
||
1940 | $countbytes = 0; |
||
1941 | $numbytes = 1; // number of octetc needed to represent the UTF-8 character |
||
1942 | $length = strlen($uch); |
||
1943 | for ($i = 0; $i < $length; ++$i) { |
||
1944 | $char = ord($uch[$i]); // get one string character at time |
||
1945 | if ($countbytes == 0) { // get starting octect |
||
1946 | if ($char <= 0x7F) { |
||
1947 | return $char; // use the character "as is" because is ASCII |
||
1948 | } elseif (($char >> 0x05) == 0x06) { // 2 bytes character (0x06 = 110 BIN) |
||
1949 | $bytes[] = ($char - 0xC0) << 0x06; |
||
1950 | ++$countbytes; |
||
1951 | $numbytes = 2; |
||
1952 | } elseif (($char >> 0x04) == 0x0E) { // 3 bytes character (0x0E = 1110 BIN) |
||
1953 | $bytes[] = ($char - 0xE0) << 0x0C; |
||
1954 | ++$countbytes; |
||
1955 | $numbytes = 3; |
||
1956 | } elseif (($char >> 0x03) == 0x1E) { // 4 bytes character (0x1E = 11110 BIN) |
||
1957 | $bytes[] = ($char - 0xF0) << 0x12; |
||
1958 | ++$countbytes; |
||
1959 | $numbytes = 4; |
||
1960 | } else { |
||
1961 | // use replacement character for other invalid sequences |
||
1962 | return 0xFFFD; |
||
1963 | } |
||
1964 | } elseif (($char >> 0x06) == 0x02) { // bytes 2, 3 and 4 must start with 0x02 = 10 BIN |
||
1965 | $bytes[] = $char - 0x80; |
||
1966 | ++$countbytes; |
||
1967 | if ($countbytes == $numbytes) { |
||
1968 | // compose UTF-8 bytes to a single unicode value |
||
1969 | $char = $bytes[0]; |
||
1970 | for ($j = 1; $j < $numbytes; ++$j) { |
||
1971 | $char += ($bytes[$j] << (($numbytes - $j - 1) * 0x06)); |
||
1972 | } |
||
1973 | if ((($char >= 0xD800) AND ($char <= 0xDFFF)) OR ($char >= 0x10FFFF)) { |
||
1974 | // The definition of UTF-8 prohibits encoding character numbers between |
||
1975 | // U+D800 and U+DFFF, which are reserved for use with the UTF-16 |
||
1976 | // encoding form (as surrogate pairs) and do not directly represent |
||
1977 | // characters. |
||
1978 | return 0xFFFD; // use replacement character |
||
1979 | } else { |
||
1980 | return $char; |
||
1981 | } |
||
1982 | } |
||
1983 | } else { |
||
1984 | // use replacement character for other invalid sequences |
||
1985 | return 0xFFFD; |
||
1986 | } |
||
1987 | } |
||
1988 | return 0xFFFD; |
||
1989 | } |
||
1990 | |||
1991 | /** |
||
1992 | * Converts UTF-8 strings to codepoints array.<br> |
||
1993 | * Invalid byte sequences will be replaced with 0xFFFD (replacement character)<br> |
||
1994 | * @param string $str string to process. |
||
1995 | * @param boolean $isunicode True when the documetn is in Unicode mode, false otherwise. |
||
1996 | * @param array $currentfont Reference to current font array. |
||
1997 | * @return array containing codepoints (UTF-8 characters values) |
||
1998 | * @author Nicola Asuni |
||
1999 | * @public static |
||
2000 | */ |
||
2001 | public static function UTF8StringToArray($str, $isunicode, &$currentfont) { |
||
2002 | $str = is_null($str) ? '' : $str; |
||
2003 | if ($isunicode) { |
||
2004 | // requires PCRE unicode support turned on |
||
2005 | $chars = TCPDF_STATIC::pregSplit('//','u', $str, -1, PREG_SPLIT_NO_EMPTY); |
||
2006 | $carr = array_map(static::class.'::uniord', $chars); |
||
2007 | } else { |
||
2008 | $chars = str_split($str); |
||
2009 | $carr = array_map('ord', $chars); |
||
2010 | } |
||
2011 | if (is_array($currentfont['subsetchars']) && is_array($carr)) { |
||
2012 | $currentfont['subsetchars'] += array_fill_keys($carr, true); |
||
2013 | } else { |
||
2014 | $currentfont['subsetchars'] = array_merge($currentfont['subsetchars'], $carr); |
||
2015 | } |
||
2016 | return $carr; |
||
2017 | } |
||
2018 | |||
2019 | /** |
||
2020 | * Converts UTF-8 strings to Latin1 when using the standard 14 core fonts.<br> |
||
2021 | * @param string $str string to process. |
||
2022 | * @param boolean $isunicode True when the documetn is in Unicode mode, false otherwise. |
||
2023 | * @param array $currentfont Reference to current font array. |
||
2024 | * @return string |
||
2025 | * @since 3.2.000 (2008-06-23) |
||
2026 | * @public static |
||
2027 | */ |
||
2028 | public static function UTF8ToLatin1($str, $isunicode, &$currentfont) { |
||
2029 | $unicode = self::UTF8StringToArray($str, $isunicode, $currentfont); // array containing UTF-8 unicode values |
||
2030 | return self::UTF8ArrToLatin1($unicode); |
||
2031 | } |
||
2032 | |||
2033 | /** |
||
2034 | * Converts UTF-8 strings to UTF16-BE.<br> |
||
2035 | * @param string $str string to process. |
||
2036 | * @param boolean $setbom if true set the Byte Order Mark (BOM = 0xFEFF) |
||
2037 | * @param boolean $isunicode True when the documetn is in Unicode mode, false otherwise. |
||
2038 | * @param array $currentfont Reference to current font array. |
||
2039 | * @return string |
||
2040 | * @author Nicola Asuni |
||
2041 | * @since 1.53.0.TC005 (2005-01-05) |
||
2042 | * @public static |
||
2043 | */ |
||
2044 | public static function UTF8ToUTF16BE($str, $setbom, $isunicode, &$currentfont) { |
||
2045 | if (!$isunicode) { |
||
2046 | return $str; // string is not in unicode |
||
2047 | } |
||
2048 | $unicode = self::UTF8StringToArray($str, $isunicode, $currentfont); // array containing UTF-8 unicode values |
||
2049 | return self::arrUTF8ToUTF16BE($unicode, $setbom); |
||
2050 | } |
||
2051 | |||
2052 | /** |
||
2053 | * Reverse the RLT substrings using the Bidirectional Algorithm (http://unicode.org/reports/tr9/). |
||
2054 | * @param string $str string to manipulate. |
||
2055 | * @param bool $setbom if true set the Byte Order Mark (BOM = 0xFEFF) |
||
2056 | * @param bool $forcertl if true forces RTL text direction |
||
2057 | * @param boolean $isunicode True if the document is in Unicode mode, false otherwise. |
||
2058 | * @param array $currentfont Reference to current font array. |
||
2059 | * @return string |
||
2060 | * @author Nicola Asuni |
||
2061 | * @since 2.1.000 (2008-01-08) |
||
2062 | * @public static |
||
2063 | */ |
||
2064 | public static function utf8StrRev($str, $setbom, $forcertl, $isunicode, &$currentfont) { |
||
2065 | return self::utf8StrArrRev(self::UTF8StringToArray($str, $isunicode, $currentfont), $str, $setbom, $forcertl, $isunicode, $currentfont); |
||
2066 | } |
||
2067 | |||
2068 | /** |
||
2069 | * Reverse the RLT substrings array using the Bidirectional Algorithm (http://unicode.org/reports/tr9/). |
||
2070 | * @param array $arr array of unicode values. |
||
2071 | * @param string $str string to manipulate (or empty value). |
||
2072 | * @param bool $setbom if true set the Byte Order Mark (BOM = 0xFEFF) |
||
2073 | * @param bool $forcertl if true forces RTL text direction |
||
2074 | * @param boolean $isunicode True if the document is in Unicode mode, false otherwise. |
||
2075 | * @param array $currentfont Reference to current font array. |
||
2076 | * @return string |
||
2077 | * @author Nicola Asuni |
||
2078 | * @since 4.9.000 (2010-03-27) |
||
2079 | * @public static |
||
2080 | */ |
||
2081 | public static function utf8StrArrRev($arr, $str, $setbom, $forcertl, $isunicode, &$currentfont) { |
||
2083 | } |
||
2084 | |||
2085 | /** |
||
2086 | * Reverse the RLT substrings using the Bidirectional Algorithm (http://unicode.org/reports/tr9/). |
||
2087 | * @param array $ta array of characters composing the string. |
||
2088 | * @param string $str string to process |
||
2089 | * @param bool $forcertl if 'R' forces RTL, if 'L' forces LTR |
||
2090 | * @param boolean $isunicode True if the document is in Unicode mode, false otherwise. |
||
2091 | * @param array $currentfont Reference to current font array. |
||
2092 | * @return array of unicode chars |
||
2093 | * @author Nicola Asuni |
||
2094 | * @since 2.4.000 (2008-03-06) |
||
2095 | * @public static |
||
2096 | */ |
||
2097 | public static function utf8Bidi($ta, $str, $forcertl, $isunicode, &$currentfont) { |
||
2098 | // paragraph embedding level |
||
2099 | $pel = 0; |
||
2100 | // max level |
||
2101 | $maxlevel = 0; |
||
2102 | if (TCPDF_STATIC::empty_string($str)) { |
||
2103 | // create string from array |
||
2652 | } |
||
2653 | |||
2654 | } // END OF TCPDF_FONTS CLASS |
||
2655 | |||
2659 |