]> git.immae.eu Git - github/wallabag/wallabag.git/blob - inc/3rdparty/libraries/mpdf/classes/ttfontsuni_analysis.php
removed unnecessary fonts
[github/wallabag/wallabag.git] / inc / 3rdparty / libraries / mpdf / classes / ttfontsuni_analysis.php
1 <?php
2
3 require_once(_MPDF_PATH.'classes/ttfontsuni.php');
4
5 class TTFontFile_Analysis EXTENDS TTFontFile {
6
7 // Used to get font information from files in directory
8 function extractCoreInfo($file, $TTCfontID=0) {
9 $this->filename = $file;
10 $this->fh = fopen($file,'rb');
11 if (!$this->fh) { return ('ERROR - Can\'t open file ' . $file); }
12 $this->_pos = 0;
13 $this->charWidths = '';
14 $this->glyphPos = array();
15 $this->charToGlyph = array();
16 $this->tables = array();
17 $this->otables = array();
18 $this->ascent = 0;
19 $this->descent = 0;
20 $this->numTTCFonts = 0;
21 $this->TTCFonts = array();
22 $this->version = $version = $this->read_ulong();
23 $this->panose = array(); // mPDF 5.0
24 if ($version==0x4F54544F)
25 return("ERROR - NOT ADDED as Postscript outlines are not supported - " . $file);
26 if ($version==0x74746366) {
27 if ($TTCfontID > 0) {
28 $this->version = $version = $this->read_ulong(); // TTC Header version now
29 if (!in_array($version, array(0x00010000,0x00020000)))
30 return("ERROR - NOT ADDED as Error parsing TrueType Collection: version=".$version." - " . $file);
31 }
32 else return("ERROR - Error parsing TrueType Collection - " . $file);
33 $this->numTTCFonts = $this->read_ulong();
34 for ($i=1; $i<=$this->numTTCFonts; $i++) {
35 $this->TTCFonts[$i]['offset'] = $this->read_ulong();
36 }
37 $this->seek($this->TTCFonts[$TTCfontID]['offset']);
38 $this->version = $version = $this->read_ulong(); // TTFont version again now
39 $this->readTableDirectory(false);
40 }
41 else {
42 if (!in_array($version, array(0x00010000,0x74727565)))
43 return("ERROR - NOT ADDED as Not a TrueType font: version=".$version." - " . $file);
44 $this->readTableDirectory(false);
45 }
46
47 /* Included for testing...
48 $cmap_offset = $this->seek_table("cmap");
49 $this->skip(2);
50 $cmapTableCount = $this->read_ushort();
51 $unicode_cmap_offset = 0;
52 for ($i=0;$i<$cmapTableCount;$i++) {
53 $x[$i]['platformId'] = $this->read_ushort();
54 $x[$i]['encodingId'] = $this->read_ushort();
55 $x[$i]['offset'] = $this->read_ulong();
56 $save_pos = $this->_pos;
57 $x[$i]['format'] = $this->get_ushort($cmap_offset + $x[$i]['offset'] );
58 $this->seek($save_pos );
59 }
60 print_r($x); exit;
61 */
62 ///////////////////////////////////
63 // name - Naming table
64 ///////////////////////////////////
65
66 /* Test purposes - displays table of names
67 $name_offset = $this->seek_table("name");
68 $format = $this->read_ushort();
69 if ($format != 0 && $format != 1) // mPDF 5.3.73
70 die("Unknown name table format ".$format);
71 $numRecords = $this->read_ushort();
72 $string_data_offset = $name_offset + $this->read_ushort();
73 for ($i=0;$i<$numRecords; $i++) {
74 $x[$i]['platformId'] = $this->read_ushort();
75 $x[$i]['encodingId'] = $this->read_ushort();
76 $x[$i]['languageId'] = $this->read_ushort();
77 $x[$i]['nameId'] = $this->read_ushort();
78 $x[$i]['length'] = $this->read_ushort();
79 $x[$i]['offset'] = $this->read_ushort();
80
81 $N = '';
82 if ($x[$i]['platformId'] == 1 && $x[$i]['encodingId'] == 0 && $x[$i]['languageId'] == 0) { // Roman
83 $opos = $this->_pos;
84 $N = $this->get_chunk($string_data_offset + $x[$i]['offset'] , $x[$i]['length'] );
85 $this->_pos = $opos;
86 $this->seek($opos);
87 }
88 else { // Unicode
89 $opos = $this->_pos;
90 $this->seek($string_data_offset + $x[$i]['offset'] );
91 $length = $x[$i]['length'] ;
92 if ($length % 2 != 0)
93 $length -= 1;
94 // die("PostScript name is UTF-16BE string of odd length");
95 $length /= 2;
96 $N = '';
97 while ($length > 0) {
98 $char = $this->read_ushort();
99 $N .= (chr($char));
100 $length -= 1;
101 }
102 $this->_pos = $opos;
103 $this->seek($opos);
104 }
105 $x[$i]['names'][$nameId] = $N;
106 }
107 print_r($x); exit;
108 */
109
110 $name_offset = $this->seek_table("name");
111 $format = $this->read_ushort();
112 if ($format != 0 && $format != 1) // mPDF 5.3.73
113 return("ERROR - NOT ADDED as Unknown name table format ".$format." - " . $file);
114 $numRecords = $this->read_ushort();
115 $string_data_offset = $name_offset + $this->read_ushort();
116 $names = array(1=>'',2=>'',3=>'',4=>'',6=>'');
117 $K = array_keys($names);
118 $nameCount = count($names);
119 for ($i=0;$i<$numRecords; $i++) {
120 $platformId = $this->read_ushort();
121 $encodingId = $this->read_ushort();
122 $languageId = $this->read_ushort();
123 $nameId = $this->read_ushort();
124 $length = $this->read_ushort();
125 $offset = $this->read_ushort();
126 if (!in_array($nameId,$K)) continue;
127 $N = '';
128 if ($platformId == 3 && $encodingId == 1 && $languageId == 0x409) { // Microsoft, Unicode, US English, PS Name
129 $opos = $this->_pos;
130 $this->seek($string_data_offset + $offset);
131 if ($length % 2 != 0)
132 $length += 1;
133 $length /= 2;
134 $N = '';
135 while ($length > 0) {
136 $char = $this->read_ushort();
137 $N .= (chr($char));
138 $length -= 1;
139 }
140 $this->_pos = $opos;
141 $this->seek($opos);
142 }
143 else if ($platformId == 1 && $encodingId == 0 && $languageId == 0) { // Macintosh, Roman, English, PS Name
144 $opos = $this->_pos;
145 $N = $this->get_chunk($string_data_offset + $offset, $length);
146 $this->_pos = $opos;
147 $this->seek($opos);
148 }
149 if ($N && $names[$nameId]=='') {
150 $names[$nameId] = $N;
151 $nameCount -= 1;
152 if ($nameCount==0) break;
153 }
154 }
155 if ($names[6])
156 $psName = preg_replace('/ /','-',$names[6]);
157 else if ($names[4])
158 $psName = preg_replace('/ /','-',$names[4]);
159 else if ($names[1])
160 $psName = preg_replace('/ /','-',$names[1]);
161 else
162 $psName = '';
163 if (!$names[1] && !$psName)
164 return("ERROR - NOT ADDED as Could not find valid font name - " . $file);
165 $this->name = $psName;
166 if ($names[1]) { $this->familyName = $names[1]; } else { $this->familyName = $psName; }
167 if ($names[2]) { $this->styleName = $names[2]; } else { $this->styleName = 'Regular'; }
168
169 ///////////////////////////////////
170 // head - Font header table
171 ///////////////////////////////////
172 $this->seek_table("head");
173 $ver_maj = $this->read_ushort();
174 $ver_min = $this->read_ushort();
175 if ($ver_maj != 1)
176 return('ERROR - NOT ADDED as Unknown head table version '. $ver_maj .'.'. $ver_min." - " . $file);
177 $this->fontRevision = $this->read_ushort() . $this->read_ushort();
178 $this->skip(4);
179 $magic = $this->read_ulong();
180 if ($magic != 0x5F0F3CF5)
181 return('ERROR - NOT ADDED as Invalid head table magic ' .$magic." - " . $file);
182 $this->skip(2);
183 $this->unitsPerEm = $unitsPerEm = $this->read_ushort();
184 $scale = 1000 / $unitsPerEm;
185 $this->skip(24);
186 $macStyle = $this->read_short();
187 $this->skip(4);
188 $indexLocFormat = $this->read_short();
189
190 ///////////////////////////////////
191 // OS/2 - OS/2 and Windows metrics table
192 ///////////////////////////////////
193 $sFamily = '';
194 $panose = '';
195 $fsSelection = '';
196 if (isset($this->tables["OS/2"])) {
197 $this->seek_table("OS/2");
198 $this->skip(30);
199 $sF = $this->read_short();
200 $sFamily = ($sF >> 8);
201 $this->_pos += 10; //PANOSE = 10 byte length
202 $panose = fread($this->fh,10);
203 $this->panose = array();
204 for ($p=0;$p<strlen($panose);$p++) { $this->panose[] = ord($panose[$p]); }
205 $this->skip(20);
206 $fsSelection = $this->read_short();
207 }
208
209 ///////////////////////////////////
210 // post - PostScript table
211 ///////////////////////////////////
212 $this->seek_table("post");
213 $this->skip(4);
214 $this->italicAngle = $this->read_short() + $this->read_ushort() / 65536.0;
215 $this->skip(4);
216 $isFixedPitch = $this->read_ulong();
217
218
219
220 ///////////////////////////////////
221 // cmap - Character to glyph index mapping table
222 ///////////////////////////////////
223 $cmap_offset = $this->seek_table("cmap");
224 $this->skip(2);
225 $cmapTableCount = $this->read_ushort();
226 $unicode_cmap_offset = 0;
227 for ($i=0;$i<$cmapTableCount;$i++) {
228 $platformID = $this->read_ushort();
229 $encodingID = $this->read_ushort();
230 $offset = $this->read_ulong();
231 $save_pos = $this->_pos;
232 if (($platformID == 3 && $encodingID == 1) || $platformID == 0) { // Microsoft, Unicode
233 $format = $this->get_ushort($cmap_offset + $offset);
234 if ($format == 4) {
235 if (!$unicode_cmap_offset) $unicode_cmap_offset = $cmap_offset + $offset;
236 }
237 }
238 else if ((($platformID == 3 && $encodingID == 10) || $platformID == 0)) { // Microsoft, Unicode Format 12 table HKCS
239 $format = $this->get_ushort($cmap_offset + $offset);
240 if ($format == 12) {
241 $unicode_cmap_offset = $cmap_offset + $offset;
242 break;
243 }
244 }
245 $this->seek($save_pos );
246 }
247
248 if (!$unicode_cmap_offset)
249 return('ERROR - Font ('.$this->filename .') NOT ADDED as it is not Unicode encoded, and cannot be used by mPDF');
250
251 $rtl = false;
252 $indic = false;
253 $cjk = false;
254 $sip = false;
255 $smp = false;
256 $pua = false;
257 $puaag = false;
258 $glyphToChar = array();
259 $unAGlyphs = '';
260 // Format 12 CMAP does characters above Unicode BMP i.e. some HKCS characters U+20000 and above
261 if ($format == 12) {
262 $this->seek($unicode_cmap_offset + 4);
263 $length = $this->read_ulong();
264 $limit = $unicode_cmap_offset + $length;
265 $this->skip(4);
266 $nGroups = $this->read_ulong();
267 for($i=0; $i<$nGroups ; $i++) {
268 $startCharCode = $this->read_ulong();
269 $endCharCode = $this->read_ulong();
270 $startGlyphCode = $this->read_ulong();
271 if (($endCharCode > 0x20000 && $endCharCode < 0x2A6DF) || ($endCharCode > 0x2F800 && $endCharCode < 0x2FA1F)) {
272 $sip = true;
273 }
274 if ($endCharCode > 0x10000 && $endCharCode < 0x1FFFF) {
275 $smp = true;
276 }
277 if (($endCharCode > 0x0590 && $endCharCode < 0x077F) || ($endCharCode > 0xFE70 && $endCharCode < 0xFEFF) || ($endCharCode > 0xFB50 && $endCharCode < 0xFDFF)) {
278 $rtl = true;
279 }
280 if ($endCharCode > 0x0900 && $endCharCode < 0x0DFF) {
281 $indic = true;
282 }
283 if ($endCharCode > 0xE000 && $endCharCode < 0xF8FF) {
284 $pua = true;
285 if ($endCharCode > 0xF500 && $endCharCode < 0xF7FF) {
286 $puaag = true;
287 }
288 }
289 if (($endCharCode > 0x2E80 && $endCharCode < 0x4DC0) || ($endCharCode > 0x4E00 && $endCharCode < 0xA4CF) || ($endCharCode > 0xAC00 && $endCharCode < 0xD7AF) || ($endCharCode > 0xF900 && $endCharCode < 0xFAFF) || ($endCharCode > 0xFE30 && $endCharCode < 0xFE4F)) {
290 $cjk = true;
291 }
292
293 $offset = 0;
294 // Get each glyphToChar - only point if going to analyse un-mapped Arabic Glyphs
295 if (isset($this->tables['post'])) {
296 for ($unichar=$startCharCode;$unichar<=$endCharCode;$unichar++) {
297 $glyph = $startGlyphCode + $offset ;
298 $offset++;
299 $glyphToChar[$glyph][] = $unichar;
300 }
301 }
302
303
304 }
305 }
306
307 else { // Format 4 CMap
308 $this->seek($unicode_cmap_offset + 2);
309 $length = $this->read_ushort();
310 $limit = $unicode_cmap_offset + $length;
311 $this->skip(2);
312
313 $segCount = $this->read_ushort() / 2;
314 $this->skip(6);
315 $endCount = array();
316 for($i=0; $i<$segCount; $i++) { $endCount[] = $this->read_ushort(); }
317 $this->skip(2);
318 $startCount = array();
319 for($i=0; $i<$segCount; $i++) { $startCount[] = $this->read_ushort(); }
320 $idDelta = array();
321 for($i=0; $i<$segCount; $i++) { $idDelta[] = $this->read_short(); }
322 $idRangeOffset_start = $this->_pos;
323 $idRangeOffset = array();
324 for($i=0; $i<$segCount; $i++) { $idRangeOffset[] = $this->read_ushort(); }
325
326 for ($n=0;$n<$segCount;$n++) {
327 if (($endCount[$n] > 0x0590 && $endCount[$n] < 0x077F) || ($endCount[$n] > 0xFE70 && $endCount[$n] < 0xFEFF) || ($endCount[$n] > 0xFB50 && $endCount[$n] < 0xFDFF)) {
328 $rtl = true;
329 }
330 if ($endCount[$n] > 0x0900 && $endCount[$n] < 0x0DFF) {
331 $indic = true;
332 }
333 if (($endCount[$n] > 0x2E80 && $endCount[$n] < 0x4DC0) || ($endCount[$n] > 0x4E00 && $endCount[$n] < 0xA4CF) || ($endCount[$n] > 0xAC00 && $endCount[$n] < 0xD7AF) || ($endCount[$n] > 0xF900 && $endCount[$n] < 0xFAFF) || ($endCount[$n] > 0xFE30 && $endCount[$n] < 0xFE4F)) {
334 $cjk = true;
335 }
336 if ($endCount[$n] > 0xE000 && $endCount[$n] < 0xF8FF) {
337 $pua = true;
338 if ($endCount[$n] > 0xF500 && $endCount[$n] < 0xF7FF) {
339 $puaag = true;
340 }
341 }
342 // Get each glyphToChar - only point if going to analyse un-mapped Arabic Glyphs
343 if (isset($this->tables['post'])) {
344 $endpoint = ($endCount[$n] + 1);
345 for ($unichar=$startCount[$n];$unichar<$endpoint;$unichar++) {
346 if ($idRangeOffset[$n] == 0)
347 $glyph = ($unichar + $idDelta[$n]) & 0xFFFF;
348 else {
349 $offset = ($unichar - $startCount[$n]) * 2 + $idRangeOffset[$n];
350 $offset = $idRangeOffset_start + 2 * $n + $offset;
351 if ($offset >= $limit)
352 $glyph = 0;
353 else {
354 $glyph = $this->get_ushort($offset);
355 if ($glyph != 0)
356 $glyph = ($glyph + $idDelta[$n]) & 0xFFFF;
357 }
358 }
359 $glyphToChar[$glyph][] = $unichar;
360 }
361 }
362
363 }
364 }
365 // 'POST' table for un-mapped arabic glyphs
366 if (isset($this->tables['post'])) {
367 $this->seek_table("post");
368 // Only works on Format 2.0
369 $formata = $this->read_ushort();
370 $formatb = $this->read_ushort();
371 if ($formata == 2 && $formatb == 0) {
372 $this->skip(28);
373 $nGlyfs = $this->read_ushort();
374 $glyphNameIndex = array();
375 for ($i=0; $i<$nGlyfs; $i++) {
376 $glyphNameIndex[($this->read_ushort())] = $i;
377 }
378
379 $opost = $this->get_table('post');
380 $ptr = 34+($nGlyfs*2);
381 for ($i=0; $i<$nGlyfs; $i++) {
382 $len = ord(substr($opost,$ptr,1));
383 $ptr++;
384 $name = substr($opost,$ptr,$len);
385 $gid = $glyphNameIndex[$i+258];
386 // Select uni0600.xxx(x) - uni06FF.xxx(x)
387 if (preg_match('/^uni(06[0-9a-f]{2})\.(fina|medi|init|fin|med|ini)$/i',$name,$m)) {
388 if (!isset($glyphToChar[$gid]) || (isset($glyphToChar[$gid]) && is_array($glyphToChar[$gid]) && count($glyphToChar[$gid])==1 && $glyphToChar[$gid][0]>57343 && $glyphToChar[$gid][0]<63489)) { // if set in PUA private use area E000-F8FF, or NOT Unicode mapped
389 $uni = hexdec($m[1]);
390 $form = strtoupper(substr($m[2],0,1));
391 // Assign new PUA Unicode between F500 - F7FF
392 $bit = $uni & 0xFF;
393 if ($form == 'I') { $bit += 0xF600; }
394 else if ($form == 'M') { $bit += 0xF700; }
395 else { $bit += 0xF500; }
396 $unAGlyphs .= $gid;
397 $name = 'uni'.strtoupper($m[1]).'.'.strtolower($m[2]);
398 $unAGlyphs .= ' : '.$name;
399 $unihexstr = $m[1];
400 $unAGlyphs .= ' : '.$unihexstr;
401 $unAGlyphs .= ' : '.$uni;
402 $unAGlyphs .= ' : '.$form;
403 // if already set in PUA private use area E000-F8FF
404 if (isset($glyphToChar[$gid]) && $glyphToChar[$gid][0]>57343 && $glyphToChar[$gid][0]<63489) {
405 $unAGlyphs .= ' : '.$glyphToChar[$gid][0].' {'.dechex($glyphToChar[$gid][0]).'}';
406 }
407 //else $unAGlyphs .= ':';
408 $unAGlyphs .= ' : '.strtoupper(dechex($bit));
409 $unAGlyphs .= '<br />';
410 }
411 }
412 $ptr += $len;
413 }
414 if ($unAGlyphs) {
415 $unAGlyphs = 'GID:Name:Unicode base Hex:Dec:Form:PUA Unicode<br />'.$unAGlyphs ;
416 }
417 }
418 }
419
420
421
422 $bold = false;
423 $italic = false;
424 $ftype = '';
425 if ($macStyle & (1 << 0)) { $bold = true; } // bit 0 bold
426 else if ($fsSelection & (1 << 5)) { $bold = true; } // 5 BOLD Characters are emboldened
427
428 if ($macStyle & (1 << 1)) { $italic = true; } // bit 1 italic
429 else if ($fsSelection & (1 << 0)) { $italic = true; } // 0 ITALIC Font contains Italic characters, otherwise they are upright
430 else if ($this->italicAngle <> 0) { $italic = true; }
431
432 if ($isFixedPitch ) { $ftype = 'mono'; }
433 else if ($sFamily >0 && $sFamily <8) { $ftype = 'serif'; }
434 else if ($sFamily ==8) { $ftype = 'sans'; }
435 else if ($sFamily ==10) { $ftype = 'cursive'; }
436 // Use PANOSE
437 if ($panose) {
438 $bFamilyType=ord($panose[0]);
439 if ($bFamilyType==2) {
440 $bSerifStyle=ord($panose[1]);
441 if (!$ftype) {
442 if ($bSerifStyle>1 && $bSerifStyle<11) { $ftype = 'serif'; }
443 else if ($bSerifStyle>10) { $ftype = 'sans'; }
444 }
445 $bProportion=ord($panose[3]);
446 if ($bProportion==9 || $bProportion==1) { $ftype = 'mono'; } // ==1 i.e. No Fit needed for OCR-a and -b
447 }
448 else if ($bFamilyType==3) {
449 $ftype = 'cursive';
450 }
451 }
452
453 fclose($this->fh);
454 return array($this->familyName, $bold, $italic, $ftype, $TTCfontID, $rtl, $indic, $cjk, $sip, $smp, $puaag, $pua, $unAGlyphs);
455 }
456
457
458
459
460 }
461
462
463 ?>