Completed
Push — master ( ac5f5b...19a64a )
by
unknown
27:06 queued 12:25
created

system/mpdf/classes/otl_dump.php (3 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
/*******************************************************************************
4
* otl_dump class                                                             *
5
*******************************************************************************/
6
7
// Define the value used in the "head" table of a created TTF file
8
// 0x74727565 "true" for Mac
9
// 0x00010000 for Windows
10
// Either seems to work for a font embedded in a PDF file
11
// when read by Adobe Reader on a Windows PC(!)
12
if (!defined('_TTF_MAC_HEADER')) define("_TTF_MAC_HEADER", false);
13
14
// Recalculate correct metadata/profiles when making subset fonts (not SIP/SMP)
15
// e.g. xMin, xMax, maxNContours
16
if (!defined('_RECALC_PROFILE')) define("_RECALC_PROFILE", false);
17
18
// TrueType Font Glyph operators
19
define("GF_WORDS",(1 << 0));
20
define("GF_SCALE",(1 << 3));
21
define("GF_MORE",(1 << 5));
22
define("GF_XYSCALE",(1 << 6));
23
define("GF_TWOBYTWO",(1 << 7));
24
25
// mPDF 5.7.1
26
if(!function_exists('unicode_hex')){ 
27
	function unicode_hex($unicode_dec) {
28
		return (sprintf("%05s", strtoupper(dechex($unicode_dec))));
29
	}
30
}
31
class OTLdump {
32
33
var $GPOSFeatures;	// mPDF 5.7.1
34
var $GPOSLookups;		// mPDF 5.7.1
35
var $GPOSScriptLang;	// mPDF 5.7.1
36
var $ignoreStrings;	// mPDF 5.7.1
37
var $MarkAttachmentType; // mPDF 5.7.1
38
var $MarkGlyphSets;	// mPDF 7.5.1
39
var $GlyphClassMarks;	// mPDF 5.7.1
40
var $GlyphClassLigatures;	// mPDF 5.7.1
41
var $GlyphClassBases;	// mPDF 5.7.1
42
var $GlyphClassComponents;	// mPDF 5.7.1
43
var $GSUBScriptLang;	// mPDF 5.7.1
44
var $rtlPUAstr;	// mPDF 5.7.1
45
var $rtlPUAarr;	// mPDF 5.7.1
46
var $fontkey;	// mPDF 5.7.1
47
var $useOTL;	// mPDF 5.7.1
48
var $panose;
49
var $maxUni;
50
var $sFamilyClass;
51
var $sFamilySubClass;
52
var $sipset;
53
var $smpset;
54
var $_pos;
55
var $numTables;
56
var $searchRange;
0 ignored issues
show
The visibility should be declared for property $searchRange.

The PSR-2 coding standard requires that all properties in a class have their visibility explicitly declared. If you declare a property using

class A {
    var $property;
}

the property is implicitly global.

To learn more about the PSR-2, please see the PHP-FIG site on the PSR-2.

Loading history...
57
var $entrySelector;
58
var $rangeShift;
59
var $tables;
60
var $otables;
61
var $filename;
62
var $fh;
63
var $glyphPos;
64
var $charToGlyph;
65
var $ascent;
66
var $descent;
67
var $name;
68
var $familyName;
69
var $styleName;
70
var $fullName;
71
var $uniqueFontID;
72
var $unitsPerEm;
73
var $bbox;
74
var $capHeight;
75
var $stemV;
76
var $italicAngle;
77
var $flags;
78
var $underlinePosition;
79
var $underlineThickness;
80
var $charWidths;
81
var $defaultWidth;
82
var $maxStrLenRead;
83
var $numTTCFonts;
84
var $TTCFonts;
85
var $maxUniChar;
86
var $kerninfo;
87
88
	function OTLdump(&$mpdf) {
89
		$this->mpdf = $mpdf;
90
		$this->maxStrLenRead = 200000;	// Maximum size of glyf table to read in as string (otherwise reads each glyph from file)
91
	}
92
93
94
	function getMetrics($file, $fontkey, $TTCfontID=0, $debug=false, $BMPonly=false, $kerninfo=false, $useOTL=0, $mode) {	// mPDF 5.7.1
95
		$this->mode = $mode;
96
		$this->useOTL = $useOTL;	// mPDF 5.7.1
97
		$this->fontkey = $fontkey;	// mPDF 5.7.1
98
		$this->filename = $file;
99
		$this->fh = fopen($file,'rb') or die('Can\'t open file ' . $file);
100
		$this->_pos = 0;
101
		$this->charWidths = '';
102
		$this->glyphPos = array();
103
		$this->charToGlyph = array();
104
		$this->tables = array();
105
		$this->otables = array();
106
		$this->kerninfo = array();
107
		$this->ascent = 0;
108
		$this->descent = 0;
109
		$this->numTTCFonts = 0;
110
		$this->TTCFonts = array();
111
		$this->version = $version = $this->read_ulong();
112
		$this->panose = array();
113
		if ($version==0x4F54544F) 
114
			die("Postscript outlines are not supported");
115
		if ($version==0x74746366 && !$TTCfontID) 
116
			die("ERROR - You must define the TTCfontID for a TrueType Collection in config_fonts.php (". $file.")");
117
		if (!in_array($version, array(0x00010000,0x74727565)) && !$TTCfontID)
118
			die("Not a TrueType font: version=".$version);
119 View Code Duplication
		if ($TTCfontID > 0) {
120
			$this->version = $version = $this->read_ulong();	// TTC Header version now
121
			if (!in_array($version, array(0x00010000,0x00020000)))
122
				die("ERROR - Error parsing TrueType Collection: version=".$version." - " . $file);
123
			$this->numTTCFonts = $this->read_ulong();
124
			for ($i=1; $i<=$this->numTTCFonts; $i++) {
125
	      	      $this->TTCFonts[$i]['offset'] = $this->read_ulong();
126
			}
127
			$this->seek($this->TTCFonts[$TTCfontID]['offset']);
128
			$this->version = $version = $this->read_ulong();	// TTFont version again now
129
		}
130
		$this->readTableDirectory($debug);
131
		$this->extractInfo($debug, $BMPonly, $kerninfo, $useOTL); 
132
		fclose($this->fh);
133
	}
134
135
136 View Code Duplication
	function readTableDirectory($debug=false) {
137
	    $this->numTables = $this->read_ushort();
138
            $this->searchRange = $this->read_ushort();
139
            $this->entrySelector = $this->read_ushort();
140
            $this->rangeShift = $this->read_ushort();
141
            $this->tables = array();	
142
            for ($i=0;$i<$this->numTables;$i++) {
143
                $record = array();
144
                $record['tag'] = $this->read_tag();
145
                $record['checksum'] = array($this->read_ushort(),$this->read_ushort());
146
                $record['offset'] = $this->read_ulong();
147
                $record['length'] = $this->read_ulong();
148
                $this->tables[$record['tag']] = $record;
149
		}
150
		if ($debug) $this->checksumTables();
151
	}
152
153 View Code Duplication
	function checksumTables() {
154
		// Check the checksums for all tables
155
		foreach($this->tables AS $t) {
156
		  if ($t['length'] > 0 && $t['length'] < $this->maxStrLenRead) {	// 1.02
157
            	$table = $this->get_chunk($t['offset'], $t['length']);
158
            	$checksum = $this->calcChecksum($table);
159
            	if ($t['tag'] == 'head') {
160
				$up = unpack('n*', substr($table,8,4));
161
				$adjustment[0] = $up[1];
162
				$adjustment[1] = $up[2];
163
            		$checksum = $this->sub32($checksum, $adjustment);
164
			}
165
            	$xchecksum = $t['checksum'];
166
            	if ($xchecksum != $checksum) 
167
            	    die(sprintf('TTF file "%s": invalid checksum %s table: %s (expected %s)', $this->filename,dechex($checksum[0]).dechex($checksum[1]),$t['tag'],dechex($xchecksum[0]).dechex($xchecksum[1])));
168
		  }
169
		}
170
	}
171
172 View Code Duplication
	function sub32($x, $y) {
173
		$xlo = $x[1];
174
		$xhi = $x[0];
175
		$ylo = $y[1];
176
		$yhi = $y[0];
177
		if ($ylo > $xlo) { $xlo += 1 << 16; $yhi += 1; }
178
		$reslo = $xlo-$ylo;
179
		if ($yhi > $xhi) { $xhi += 1 << 16;  }
180
		$reshi = $xhi-$yhi;
181
		$reshi = $reshi & 0xFFFF;
182
		return array($reshi, $reslo);
183
	}
184
185 View Code Duplication
	function calcChecksum($data)  {
186
		if (strlen($data) % 4) { $data .= str_repeat("\0",(4-(strlen($data) % 4))); }
187
		$len = strlen($data);
188
		$hi=0x0000;
189
		$lo=0x0000;
190
		for($i=0;$i<$len;$i+=4) {
191
			$hi += (ord($data[$i])<<8) + ord($data[$i+1]);
192
			$lo += (ord($data[$i+2])<<8) + ord($data[$i+3]);
193
			$hi += ($lo >> 16) & 0xFFFF;
194
			$lo = $lo & 0xFFFF;
195
		}
196
		return array($hi, $lo);
197
	}
198
199
	function get_table_pos($tag) {
200
		$offset = $this->tables[$tag]['offset'];
201
		$length = $this->tables[$tag]['length'];
202
		return array($offset, $length);
203
	}
204
205
	function seek($pos) {
206
		$this->_pos = $pos;
207
		fseek($this->fh,$this->_pos);
208
	}
209
210
	function skip($delta) {
211
		$this->_pos = $this->_pos + $delta;
212
		fseek($this->fh,$delta,SEEK_CUR);
213
	}
214
215 View Code Duplication
	function seek_table($tag, $offset_in_table = 0) {
216
		$tpos = $this->get_table_pos($tag);
217
		$this->_pos = $tpos[0] + $offset_in_table;
218
		fseek($this->fh, $this->_pos);
219
		return $this->_pos;
220
	}
221
222
	function read_tag() {
223
		$this->_pos += 4;
224
		return fread($this->fh,4);
225
	}
226
227 View Code Duplication
	function read_short() {
228
		$this->_pos += 2;
229
		$s = fread($this->fh,2);
230
		$a = (ord($s[0])<<8) + ord($s[1]);
231
		if ($a & (1 << 15) ) { 
232
			$a = ($a - (1 << 16)); 
233
		}
234
		return $a;
235
	}
236
237 View Code Duplication
	function unpack_short($s) {
238
		$a = (ord($s[0])<<8) + ord($s[1]);
239
		if ($a & (1 << 15) ) { 
240
			$a = ($a - (1 << 16)); 
241
		}
242
		return $a;
243
	}
244
245
	function read_ushort() {
246
		$this->_pos += 2;
247
		$s = fread($this->fh,2);
248
		return (ord($s[0])<<8) + ord($s[1]);
249
	}
250
251
	function read_ulong() {
252
		$this->_pos += 4;
253
		$s = fread($this->fh,4);
254
		// if large uInt32 as an integer, PHP converts it to -ve
255
		return (ord($s[0])*16777216) + (ord($s[1])<<16) + (ord($s[2])<<8) + ord($s[3]); // 	16777216  = 1<<24
256
	}
257
258
	function get_ushort($pos) {
259
		fseek($this->fh,$pos);
260
		$s = fread($this->fh,2);
261
		return (ord($s[0])<<8) + ord($s[1]);
262
	}
263
264
	function get_ulong($pos) {
265
		fseek($this->fh,$pos);
266
		$s = fread($this->fh,4);
267
		// iF large uInt32 as an integer, PHP converts it to -ve
268
		return (ord($s[0])*16777216) + (ord($s[1])<<16) + (ord($s[2])<<8) + ord($s[3]); // 	16777216  = 1<<24
269
	}
270
271 View Code Duplication
	function pack_short($val) {
272
		if ($val<0) { 
273
			$val = abs($val);
274
			$val = ~$val;
275
			$val += 1;
276
		}
277
		return pack("n",$val); 
278
	}
279
280
	function splice($stream, $offset, $value) {
281
		return substr($stream,0,$offset) . $value . substr($stream,$offset+strlen($value));
282
	}
283
284
	function _set_ushort($stream, $offset, $value) {
285
		$up = pack("n", $value);
286
		return $this->splice($stream, $offset, $up);
287
	}
288
289 View Code Duplication
	function _set_short($stream, $offset, $val) {
290
		if ($val<0) { 
291
			$val = abs($val);
292
			$val = ~$val;
293
			$val += 1;
294
		}
295
		$up = pack("n",$val); 
296
		return $this->splice($stream, $offset, $up);
297
	}
298
299
	function get_chunk($pos, $length) {
300
		fseek($this->fh,$pos);
301
		if ($length <1) { return ''; }
302
		return (fread($this->fh,$length));
303
	}
304
305 View Code Duplication
	function get_table($tag) {
306
		list($pos, $length) = $this->get_table_pos($tag);
307
		if ($length == 0) { return ''; }
308
		fseek($this->fh,$pos);
309
		return (fread($this->fh,$length));
310
	}
311
312
	function add($tag, $data) {
313
		if ($tag == 'head') {
314
			$data = $this->splice($data, 8, "\0\0\0\0");
315
		}
316
		$this->otables[$tag] = $data;
317
	}
318
319
320
321
322
/////////////////////////////////////////////////////////////////////////////////////////
323
324
/////////////////////////////////////////////////////////////////////////////////////////
325
326
	function extractInfo($debug=false, $BMPonly=false, $kerninfo=false, $useOTL=0) {
327
		$this->panose = array();
328
		$this->sFamilyClass = 0;
329
		$this->sFamilySubClass = 0;
330
		///////////////////////////////////
331
		// name - Naming table
332
		///////////////////////////////////
333
			$name_offset = $this->seek_table("name");
334
			$format = $this->read_ushort();
335
			if ($format != 0 && $format != 1)
336
				die("Unknown name table format ".$format);
337
			$numRecords = $this->read_ushort();
338
			$string_data_offset = $name_offset + $this->read_ushort();
339
			$names = array(1=>'',2=>'',3=>'',4=>'',6=>'');
340
			$K = array_keys($names);
341
			$nameCount = count($names);
342 View Code Duplication
			for ($i=0;$i<$numRecords; $i++) {
343
				$platformId = $this->read_ushort();
344
				$encodingId = $this->read_ushort();
345
				$languageId = $this->read_ushort();
346
				$nameId = $this->read_ushort();
347
				$length = $this->read_ushort();
348
				$offset = $this->read_ushort();
349
				if (!in_array($nameId,$K)) continue;
350
				$N = '';
351
				if ($platformId == 3 && $encodingId == 1 && $languageId == 0x409) { // Microsoft, Unicode, US English, PS Name
352
					$opos = $this->_pos;
353
					$this->seek($string_data_offset + $offset);
354
					if ($length % 2 != 0)
355
						die("PostScript name is UTF-16BE string of odd length");
356
					$length /= 2;
357
					$N = '';
358
					while ($length > 0) {
359
						$char = $this->read_ushort();
360
						$N .= (chr($char));
361
						$length -= 1;
362
					}
363
					$this->_pos = $opos;
364
					$this->seek($opos);
365
				}
366
				else if ($platformId == 1 && $encodingId == 0 && $languageId == 0) { // Macintosh, Roman, English, PS Name
367
					$opos = $this->_pos;
368
					$N = $this->get_chunk($string_data_offset + $offset, $length);
369
					$this->_pos = $opos;
370
					$this->seek($opos);
371
				}
372
				if ($N && $names[$nameId]=='') {
373
					$names[$nameId] = $N;
374
					$nameCount -= 1;
375
					if ($nameCount==0) break;
376
				}
377
			}
378 View Code Duplication
			if ($names[6])
379
				$psName = $names[6];
380
			else if ($names[4])
381
				$psName = preg_replace('/ /','-',$names[4]);
382
			else if ($names[1])
383
				$psName = preg_replace('/ /','-',$names[1]);
384
			else
385
				$psName = '';
386
			if (!$psName)
387
				die("Could not find PostScript font name: ".$this->filename);
388
			if ($debug) {
389
			   for ($i=0;$i<count($psName);$i++) {
390
				$c = $psName[$i];
391
				$oc = ord($c);
392
				if ($oc>126 || strpos(' [](){}<>/%',$c)!==false)
393
					die("psName=".$psName." contains invalid character ".$c." ie U+".ord(c));
394
			   }
395
			}
396
			$this->name = $psName;
397
			if ($names[1]) { $this->familyName = $names[1]; } else { $this->familyName = $psName; }
398
			if ($names[2]) { $this->styleName = $names[2]; } else { $this->styleName = 'Regular'; }
399
			if ($names[4]) { $this->fullName = $names[4]; } else { $this->fullName = $psName; }
400
			if ($names[3]) { $this->uniqueFontID = $names[3]; } else { $this->uniqueFontID = $psName; }
401
402
			if ($names[6]) { $this->fullName = $names[6]; }
403
404
		///////////////////////////////////
405
		// head - Font header table
406
		///////////////////////////////////
407
		$this->seek_table("head");
408 View Code Duplication
		if ($debug) { 
409
			$ver_maj = $this->read_ushort();
410
			$ver_min = $this->read_ushort();
411
			if ($ver_maj != 1)
412
				die('Unknown head table version '. $ver_maj .'.'. $ver_min);
413
			$this->fontRevision = $this->read_ushort() . $this->read_ushort();
414
415
			$this->skip(4);
416
			$magic = $this->read_ulong();
417
			if ($magic != 0x5F0F3CF5) 
418
				die('Invalid head table magic ' .$magic);
419
			$this->skip(2);
420
		}
421
		else {
422
			$this->skip(18); 
423
		}
424
		$this->unitsPerEm = $unitsPerEm = $this->read_ushort();
425
		$scale = 1000 / $unitsPerEm;
426
		$this->skip(16);
427
		$xMin = $this->read_short();
428
		$yMin = $this->read_short();
429
		$xMax = $this->read_short();
430
		$yMax = $this->read_short();
431
		$this->bbox = array(($xMin*$scale), ($yMin*$scale), ($xMax*$scale), ($yMax*$scale));
432
		$this->skip(3*2);
433
		$indexToLocFormat = $this->read_ushort();
434
		$glyphDataFormat = $this->read_ushort();
435
		if ($glyphDataFormat != 0)
436
			die('Unknown glyph data format '.$glyphDataFormat);
437
438
		///////////////////////////////////
439
		// hhea metrics table
440
		///////////////////////////////////
441
		// ttf2t1 seems to use this value rather than the one in OS/2 - so put in for compatibility
442
		if (isset($this->tables["hhea"])) {
443
			$this->seek_table("hhea");
444
			$this->skip(4);
445
			$hheaAscender = $this->read_short();
446
			$hheaDescender = $this->read_short();
447
			$this->ascent = ($hheaAscender *$scale);
448
			$this->descent = ($hheaDescender *$scale);
449
		}
450
451
		///////////////////////////////////
452
		// OS/2 - OS/2 and Windows metrics table
453
		///////////////////////////////////
454
		if (isset($this->tables["OS/2"])) {
455
			$this->seek_table("OS/2");
456
			$version = $this->read_ushort();
457
			$this->skip(2);
458
			$usWeightClass = $this->read_ushort();
459
			$this->skip(2);
460
			$fsType = $this->read_ushort();
461
			if ($fsType == 0x0002 || ($fsType & 0x0300) != 0) {
462
				global $overrideTTFFontRestriction;
463
				if (!$overrideTTFFontRestriction) die('ERROR - Font file '.$this->filename.' cannot be embedded due to copyright restrictions.');
464
				$this->restrictedUse = true;
465
			}
466
			$this->skip(20);
467
			$sF = $this->read_short();
468
			$this->sFamilyClass = ($sF >> 8);
469
			$this->sFamilySubClass = ($sF & 0xFF);
470
			$this->_pos += 10;  //PANOSE = 10 byte length
471
			$panose = fread($this->fh,10);
472
			$this->panose = array();
473 View Code Duplication
			for ($p=0;$p<strlen($panose);$p++) { $this->panose[] = ord($panose[$p]); }
474
			$this->skip(26);
475
			$sTypoAscender = $this->read_short();
476
			$sTypoDescender = $this->read_short();
477
			if (!$this->ascent) $this->ascent = ($sTypoAscender*$scale);
478
			if (!$this->descent) $this->descent = ($sTypoDescender*$scale);
479
			if ($version > 1) {
480
				$this->skip(16);
481
				$sCapHeight = $this->read_short();
482
				$this->capHeight = ($sCapHeight*$scale);
483
			}
484
			else {
485
				$this->capHeight = $this->ascent;
486
			}
487
		}
488
		else {
489
			$usWeightClass = 500;
490
			if (!$this->ascent) $this->ascent = ($yMax*$scale);
491
			if (!$this->descent) $this->descent = ($yMin*$scale);
492
			$this->capHeight = $this->ascent;
493
		}
494
		$this->stemV = 50 + intval(pow(($usWeightClass / 65.0),2));
495
496
		///////////////////////////////////
497
		// post - PostScript table
498
		///////////////////////////////////
499
		$this->seek_table("post");
500 View Code Duplication
		if ($debug) { 
501
			$ver_maj = $this->read_ushort();
502
			$ver_min = $this->read_ushort();
503
			if ($ver_maj <1 || $ver_maj >4) 
504
				die('Unknown post table version '.$ver_maj);
505
		}
506
		else {
507
			$this->skip(4); 
508
		}
509
		$this->italicAngle = $this->read_short() + $this->read_ushort() / 65536.0;
510
		$this->underlinePosition = $this->read_short() * $scale;
511
		$this->underlineThickness = $this->read_short() * $scale;
512
		$isFixedPitch = $this->read_ulong();
513
514
		$this->flags = 4;
515
516
		if ($this->italicAngle!= 0) 
517
			$this->flags = $this->flags | 64;
518
		if ($usWeightClass >= 600)
519
			$this->flags = $this->flags | 262144;
520
		if ($isFixedPitch)
521
			$this->flags = $this->flags | 1;
522
523
		///////////////////////////////////
524
		// hhea - Horizontal header table
525
		///////////////////////////////////
526
		$this->seek_table("hhea");
527 View Code Duplication
		if ($debug) { 
528
			$ver_maj = $this->read_ushort();
529
			$ver_min = $this->read_ushort();
530
			if ($ver_maj != 1)
531
				die('Unknown hhea table version '.$ver_maj);
532
			$this->skip(28);
533
		}
534
		else {
535
			$this->skip(32); 
536
		}
537
		$metricDataFormat = $this->read_ushort();
538
		if ($metricDataFormat != 0)
539
			die('Unknown horizontal metric data format '.$metricDataFormat);
540
		$numberOfHMetrics = $this->read_ushort();
541
		if ($numberOfHMetrics == 0) 
542
			die('Number of horizontal metrics is 0');
543
544
		///////////////////////////////////
545
		// maxp - Maximum profile table
546
		///////////////////////////////////
547
		$this->seek_table("maxp");
548 View Code Duplication
		if ($debug) { 
549
			$ver_maj = $this->read_ushort();
550
			$ver_min = $this->read_ushort();
551
			if ($ver_maj != 1)
552
				die('Unknown maxp table version '.$ver_maj);
553
		}
554
		else {
555
			$this->skip(4); 
556
		}
557
		$numGlyphs = $this->read_ushort();
558
559
560
		///////////////////////////////////
561
		// cmap - Character to glyph index mapping table
562
		///////////////////////////////////
563
		$cmap_offset = $this->seek_table("cmap");
564
		$this->skip(2);
565
		$cmapTableCount = $this->read_ushort();
566
		$unicode_cmap_offset = 0;
567
		for ($i=0;$i<$cmapTableCount;$i++) {
568
			$platformID = $this->read_ushort();
569
			$encodingID = $this->read_ushort();
570
			$offset = $this->read_ulong();
571
			$save_pos = $this->_pos;
572
			if (($platformID == 3 && $encodingID == 1) || $platformID == 0) { // Microsoft, Unicode
573
				$format = $this->get_ushort($cmap_offset + $offset);
574
				if ($format == 4) {
575
					if (!$unicode_cmap_offset) $unicode_cmap_offset = $cmap_offset + $offset;
576
					if ($BMPonly) break;
577
				}
578
			}
579
			// Microsoft, Unicode Format 12 table HKCS
580
			else if ((($platformID == 3 && $encodingID == 10) || $platformID == 0) && !$BMPonly) {
581
				$format = $this->get_ushort($cmap_offset + $offset);
582
				if ($format == 12) {
583
					$unicode_cmap_offset = $cmap_offset + $offset;
584
					break;
585
				}
586
			}
587
			$this->seek($save_pos );
588
		}
589
590
		if (!$unicode_cmap_offset)
591
			die('Font ('.$this->filename .') does not have cmap for Unicode (platform 3, encoding 1, format 4, or platform 0, any encoding, format 4)');
592
593
594
		$sipset = false;
595
		$smpset = false;
596
597
		// mPDF 5.7.1
598
		$this->GSUBScriptLang = array();
599
		$this->rtlPUAstr = '';
600
		$this->rtlPUAarr = array();
601
		$this->GSUBFeatures = array();
602
		$this->GSUBLookups = array();
603
		$this->GPOSScriptLang = array();
604
		$this->GPOSFeatures = array();
605
		$this->GPOSLookups = array();
606
		$this->glyphIDtoUni = '';
607
608
		// Format 12 CMAP does characters above Unicode BMP i.e. some HKCS characters U+20000 and above
609 View Code Duplication
		if ($format == 12 && !$BMPonly) {
610
			$this->maxUniChar = 0;
611
			$this->seek($unicode_cmap_offset + 4);
612
			$length = $this->read_ulong();
613
			$limit = $unicode_cmap_offset + $length;
614
			$this->skip(4);
615
616
			$nGroups = $this->read_ulong();
617
618
			$glyphToChar = array();
619
			$charToGlyph = array();
620
			for($i=0; $i<$nGroups ; $i++) { 
621
				$startCharCode = $this->read_ulong(); 
622
				$endCharCode = $this->read_ulong(); 
623
				$startGlyphCode = $this->read_ulong(); 
624
				if ($endCharCode > 0x20000 && $endCharCode < 0x2FFFF) {
625
					$sipset = true; 
626
				}
627
				else if ($endCharCode > 0x10000 && $endCharCode < 0x1FFFF) {
628
					$smpset = true; 
629
				}
630
				$offset = 0;
631
				for ($unichar=$startCharCode;$unichar<=$endCharCode;$unichar++) {
632
					$glyph = $startGlyphCode + $offset ;
633
					$offset++;
634
					if ($unichar < 0x30000) {
635
						$charToGlyph[$unichar] = $glyph;
636
						$this->maxUniChar = max($unichar,$this->maxUniChar); 
637
						$glyphToChar[$glyph][] = $unichar;
638
					}
639
				}
640
			}
641
		}
642
		else {
643
644
			$glyphToChar = array();
645
			$charToGlyph = array();
646
			$this->getCMAP4($unicode_cmap_offset, $glyphToChar, $charToGlyph );
647
648
		}
649
		$this->sipset = $sipset ;
650
		$this->smpset = $smpset ;
651
652
653
		///////////////////////////////////
654
		// mPDF 5.7.1
655
		// Map Unmapped glyphs - from $numGlyphs
656 View Code Duplication
		if ($this->useOTL) {
657
			$bctr = 0xE000;
658
			for ($gid=1; $gid<$numGlyphs; $gid++) {
659
				if (!isset($glyphToChar[$gid])) {
660
					while(isset($charToGlyph[$bctr])) { $bctr++; }	// Avoid overwriting a glyph already mapped in PUA
661
					if (($bctr > 0xF8FF) && ($bctr < 0x2CEB0)) {
662
						if (!$BMPonly) {
663
							$bctr = 0x2CEB0; 	// Use unassigned area 0x2CEB0 to 0x2F7FF (space for 10,000 characters)
664
							$this->sipset = $sipset = true; // forces subsetting; also ensure charwidths are saved
665
							while(isset($charToGlyph[$bctr])) { $bctr++; }
666
						}
667
						else { die($names[1]." : WARNING - The font does not have enough space to map all (unmapped) included glyphs into Private Use Area U+E000 - U+F8FF"); }
668
					}
669
					$glyphToChar[$gid][] = $bctr;
670
					$charToGlyph[$bctr] = $gid;
671
					$this->maxUniChar = max($bctr,$this->maxUniChar); 
672
					$bctr++;
673
				}
674
			}
675
		}
676
		$this->glyphToChar = $glyphToChar;
677
		$this->charToGlyph = $charToGlyph;
678
		///////////////////////////////////
679
		// mPDF 5.7.1	OpenType Layout tables
680
		$this->GSUBScriptLang=array(); $this->rtlPUAstr = ''; $this->rtlPUAarr = array();
681 View Code Duplication
		if ($useOTL) {
682
			$this->_getGDEFtables();
683
			list($this->GSUBScriptLang, $this->GSUBFeatures, $this->GSUBLookups, $this->rtlPUAstr, $this->rtlPUAarr) = $this->_getGSUBtables();
684
			list($this->GPOSScriptLang, $this->GPOSFeatures, $this->GPOSLookups) = $this->_getGPOStables();
685
			$this->glyphIDtoUni = str_pad('', 256*256*3, "\x00");
686
			foreach($glyphToChar AS $gid=>$arr) {
687
				if (isset($glyphToChar[$gid][0])) {
688
					$char = $glyphToChar[$gid][0];
689
					if ($char != 0 && $char != 65535) {
690
						$this->glyphIDtoUni[$gid*3] = chr($char >> 16);
691
						$this->glyphIDtoUni[$gid*3 + 1] = chr(($char >> 8) & 0xFF);
692
						$this->glyphIDtoUni[$gid*3 + 2] = chr($char & 0xFF);
693
					}
694
				}
695
			}
696
		}
697
		///////////////////////////////////
698
699
		///////////////////////////////////
700
		// hmtx - Horizontal metrics table
701
		///////////////////////////////////
702
		$this->getHMTX($numberOfHMetrics, $numGlyphs, $glyphToChar, $scale);
703
704
		///////////////////////////////////
705
		// kern - Kerning pair table
706
		///////////////////////////////////
707
		if ($kerninfo) {
708
			// Recognises old form of Kerning table - as required by Windows - Format 0 only
709
			$kern_offset = $this->seek_table("kern");
710
			$version = $this->read_ushort();
711
			$nTables = $this->read_ushort();
712
			// subtable header
713
			$sversion = $this->read_ushort();
714
			$slength = $this->read_ushort();
715
			$scoverage = $this->read_ushort();
716
			$format = $scoverage >> 8;
717 View Code Duplication
 			if ($kern_offset && $version==0 && $format==0) {
718
				// Format 0
719
				$nPairs = $this->read_ushort();
720
				$this->skip(6);
721
				for ($i=0; $i<$nPairs; $i++) {
722
					$left = $this->read_ushort();
723
					$right = $this->read_ushort();
724
					$val = $this->read_short();
725
					if (count($glyphToChar[$left])==1 && count($glyphToChar[$right])==1) {
726
					  if ($left != 32 && $right != 32) {
727
						$this->kerninfo[$glyphToChar[$left][0]][$glyphToChar[$right][0]] = intval($val*$scale);
728
					  }
729
					}
730
				}
731
			}
732
		}
733
	}
734
735
736
/////////////////////////////////////////////////////////////////////////////////////////
737
	function _getGDEFtables() {
738
		///////////////////////////////////
739
		// GDEF - Glyph Definition
740
		///////////////////////////////////
741
		// http://www.microsoft.com/typography/otspec/gdef.htm
742
		if (isset($this->tables["GDEF"])) {
743
			if ($this->mode == 'summary') { $this->mpdf->WriteHTML('<h1>GDEF table</h1>');  }
744
			$gdef_offset = $this->seek_table("GDEF");
745
			// ULONG Version of the GDEF table-currently 0x00010000
746
			$ver_maj = $this->read_ushort();
747
			$ver_min = $this->read_ushort();
748
			// Version 0x00010002 of GDEF header contains additional Offset to a list defining mark glyph set definitions (MarkGlyphSetDef)
749
			$GlyphClassDef_offset = $this->read_ushort();
750
			$AttachList_offset = $this->read_ushort();
751
			$LigCaretList_offset = $this->read_ushort();
752
			$MarkAttachClassDef_offset = $this->read_ushort();
753
			if ($ver_min == 2) {
754
				$MarkGlyphSetsDef_offset = $this->read_ushort();
755
			}
756
757
			// GlyphClassDef
758
			$this->seek($gdef_offset+$GlyphClassDef_offset );
759
			/*
760
			1	Base glyph (single character, spacing glyph)
761
			2	Ligature glyph (multiple character, spacing glyph)
762
			3	Mark glyph (non-spacing combining glyph)
763
			4	Component glyph (part of single character, spacing glyph)
764
			*/
765
			$GlyphByClass = $this->_getClassDefinitionTable();
766
767
			if ($this->mode == 'summary') {
768
				$this->mpdf->WriteHTML('<h2>Glyph classes</h2>'); 
769
			}
770
771 View Code Duplication
			if (isset($GlyphByClass[1]) && count($GlyphByClass[1])>0) { 
772
				$this->GlyphClassBases = $this->formatClassArr($GlyphByClass[1]); 
773
				if ($this->mode == 'summary') {
774
					$this->mpdf->WriteHTML('<h3>Glyph class 1</h3>'); 
775
					$this->mpdf->WriteHTML('<h5>Base glyph (single character, spacing glyph)</h5>'); 
776
					$html = '';
777
					$html .= '<div class="glyphs">'; 
778
					foreach ($GlyphByClass[1] AS $g) {
779
						$html .= '&#x'.$g.'; '; 
780
					}
781
					$html .= '</div>';
782
					$this->mpdf->WriteHTML($html); 
783
				}
784
			}
785
			else { $this->GlyphClassBases = ''; }
786 View Code Duplication
			if (isset($GlyphByClass[2]) && count($GlyphByClass[2])>0) {
787
				$this->GlyphClassLigatures = $this->formatClassArr($GlyphByClass[2]); 
788
				if ($this->mode == 'summary') {
789
					$this->mpdf->WriteHTML('<h3>Glyph class 2</h3>'); 
790
					$this->mpdf->WriteHTML('<h5>Ligature glyph (multiple character, spacing glyph)</h5>'); 
791
					$html = '';
792
					$html .= '<div class="glyphs">'; 
793
					foreach ($GlyphByClass[2] AS $g) {
794
						$html .= '&#x'.$g.'; '; 
795
					}
796
					$html .= '</div>';
797
					$this->mpdf->WriteHTML($html); 
798
				}
799
			}
800
			else { $this->GlyphClassLigatures = ''; }
801 View Code Duplication
			if (isset($GlyphByClass[3]) && count($GlyphByClass[3])>0) {
802
				$this->GlyphClassMarks = $this->formatClassArr($GlyphByClass[3]); 
803
				if ($this->mode == 'summary') {
804
					$this->mpdf->WriteHTML('<h3>Glyph class 3</h3>'); 
805
					$this->mpdf->WriteHTML('<h5>Mark glyph (non-spacing combining glyph)</h5>'); 
806
					$html = '';
807
					$html .= '<div class="glyphs">'; 
808
					foreach ($GlyphByClass[3] AS $g) {
809
						$html .= '&#x25cc;&#x'.$g.'; '; 
810
					}
811
					$html .= '</div>';
812
					$this->mpdf->WriteHTML($html); 
813
				}
814
			}
815
			else { $this->GlyphClassMarks = ''; }
816 View Code Duplication
			if (isset($GlyphByClass[4]) && count($GlyphByClass[4])>0) {
817
				$this->GlyphClassComponents = $this->formatClassArr($GlyphByClass[4]); 
818
				if ($this->mode == 'summary') {
819
					$this->mpdf->WriteHTML('<h3>Glyph class 4</h3>'); 
820
					$this->mpdf->WriteHTML('<h5>Component glyph (part of single character, spacing glyph)</h5>'); 
821
					$html = '';
822
					$html .= '<div class="glyphs">'; 
823
					foreach ($GlyphByClass[4] AS $g) {
824
						$html .= '&#x'.$g.'; '; 
825
					}
826
					$html .= '</div>';
827
					$this->mpdf->WriteHTML($html); 
828
				}
829
			}
830
			else { $this->GlyphClassComponents = ''; }
831
832
			$Marks = $GlyphByClass[3]; // to use for MarkAttachmentType
833
834
835
/* Required for GPOS
836
			// Attachment List
837
			if ($AttachList_offset) {
838
				$this->seek($gdef_offset+$AttachList_offset );
839
			}
840
The Attachment Point List table (AttachmentList) identifies all the attachment points defined in the GPOS table and their associated glyphs so a client can quickly access coordinates for each glyph's attachment points. As a result, the client can cache coordinates for attachment points along with glyph bitmaps and avoid recalculating the attachment points each time it displays a glyph. Without this table, processing speed would be slower because the client would have to decode the GPOS lookups that define attachment points and compile the points in a list.
841
842
The Attachment List table (AttachList) may be used to cache attachment point coordinates along with glyph bitmaps.
843
844
The table consists of an offset to a Coverage table (Coverage) listing all glyphs that define attachment points in the GPOS table, a count of the glyphs with attachment points (GlyphCount), and an array of offsets to AttachPoint tables (AttachPoint). The array lists the AttachPoint tables, one for each glyph in the Coverage table, in the same order as the Coverage Index.
845
AttachList table
846
Type 	Name 	Description
847
Offset 	Coverage 	Offset to Coverage table - from beginning of AttachList table
848
uint16 	GlyphCount 	Number of glyphs with attachment points
849
Offset 	AttachPoint[GlyphCount] 	Array of offsets to AttachPoint tables-from beginning of AttachList table-in Coverage Index order
850
851
An AttachPoint table consists of a count of the attachment points on a single glyph (PointCount) and an array of contour indices of those points (PointIndex), listed in increasing numerical order.
852
853
AttachPoint table
854
Type 	Name 	Description
855
uint16 	PointCount 	Number of attachment points on this glyph
856
uint16 	PointIndex[PointCount] 	Array of contour point indices -in increasing numerical order
857
858
See Example 3 - http://www.microsoft.com/typography/otspec/gdef.htm
859
*/
860
861
862
			// Ligature Caret List 
863
			// The Ligature Caret List table (LigCaretList) defines caret positions for all the ligatures in a font. 
864
			// Not required for mDPF
865
866
867
			// MarkAttachmentType
868
			if ($MarkAttachClassDef_offset) {
869
				if ($this->mode == 'summary') { $this->mpdf->WriteHTML('<h1>Mark Attachment Types</h1>');  }
870
				$this->seek($gdef_offset+$MarkAttachClassDef_offset );
871
				$MarkAttachmentTypes = $this->_getClassDefinitionTable();
872
				foreach($MarkAttachmentTypes AS $class=>$glyphs) {
873
874 View Code Duplication
					if (is_array($Marks) && count($Marks)) {
875
						$mat = array_diff($Marks, $MarkAttachmentTypes[$class]);
876
						sort($mat, SORT_STRING);
877
					}
878
					else { $mat = array(); }
879
880
					$this->MarkAttachmentType[$class] = $this->formatClassArr($mat);
881
882 View Code Duplication
					if ($this->mode == 'summary') {
883
						$this->mpdf->WriteHTML('<h3>Mark Attachment Type: '.$class.'</h3>'); 
884
						$html = '';
885
						$html .= '<div class="glyphs">'; 
886
						foreach ($glyphs AS $g) {
887
							$html .= '&#x25cc;&#x'.$g.'; '; 
888
						}
889
						$html .= '</div>';
890
						$this->mpdf->WriteHTML($html); 
891
					}
892
				}
893
			}
894
			else  { $this->MarkAttachmentType = array(); }
895
896
897
			// MarkGlyphSets only in Version 0x00010002 of GDEF
898
			if ($ver_min == 2 && $MarkGlyphSetsDef_offset) {
899
				if ($this->mode == 'summary') { $this->mpdf->WriteHTML('<h1>Mark Glyph Sets</h1>');  }
900
				$this->seek($gdef_offset+$MarkGlyphSetsDef_offset);
901
				$MarkSetTableFormat = $this->read_ushort();
902
				$MarkSetCount = $this->read_ushort();
903
				$MarkSetOffset = array();
904
				for ($i=0;$i<$MarkSetCount;$i++) {
905
					$MarkSetOffset[] = $this->read_ulong();
906
				}
907
				for ($i=0;$i<$MarkSetCount;$i++) {
908
					$this->seek($MarkSetOffset[$i]);
909
					$glyphs = $this->_getCoverage();
910
					$this->MarkGlyphSets[$i] = $this->formatClassArr($glyphs);
911 View Code Duplication
					if ($this->mode == 'summary') {
912
						$this->mpdf->WriteHTML('<h3>Mark Glyph Set class: '.$i.'</h3>'); 
913
						$html = '';
914
						$html .= '<div class="glyphs">'; 
915
						foreach ($glyphs AS $g) {
916
							$html .= '&#x25cc;&#x'.$g.'; '; 
917
						}
918
						$html .= '</div>';
919
						$this->mpdf->WriteHTML($html); 
920
					}
921
				}
922
			}
923
			else { $this->MarkGlyphSets = array(); }
924
		}
925
		else { $this->mpdf->WriteHTML('<div>GDEF table not defined</div>'); }
926
927
928
//echo $this->GlyphClassMarks ; exit;
929
//print_r($GlyphClass); exit;
930
//print_r($GlyphByClass); exit;
931
	}
932
933
	function _getClassDefinitionTable($offset=0) {
934
935
	if ($offset>0) {
936
			$this->seek($offset);
937
	}
938
939
	// NB Any glyph not included in the range of covered GlyphIDs automatically belongs to Class 0. This is not returned by this function
940
		$ClassFormat = $this->read_ushort();
941
		$GlyphByClass = array();
942
		if ($ClassFormat == 1) {
943
			$StartGlyph = $this->read_ushort();
944
			$GlyphCount = $this->read_ushort();
945 View Code Duplication
			for ($i=0;$i<$GlyphCount;$i++) {
946
				$gid = $StartGlyph + $i;
947
				$class = $this->read_ushort();
948
				$GlyphByClass[$class][] = unicode_hex($this->glyphToChar[$gid][0]);
949
			}
950
		}
951
		else if ($ClassFormat == 2) {
952
			$tableCount = $this->read_ushort();
953
			for ($i=0;$i<$tableCount;$i++) {
954
				$startGlyphID = $this->read_ushort();
955
				$endGlyphID = $this->read_ushort();
956
				$class = $this->read_ushort();
957
				for($gid=$startGlyphID;$gid<=$endGlyphID;$gid++) {
958
					$GlyphByClass[$class][] = unicode_hex($this->glyphToChar[$gid][0]);
959
				}
960
			}
961
		}
962
		ksort($GlyphByClass);
963
		return $GlyphByClass;
964
	}
965
966
	function _getGSUBtables() {
967
		///////////////////////////////////
968
		// GSUB - Glyph Substitution
969
		///////////////////////////////////
970
		if (isset($this->tables["GSUB"])) { 
971
			$this->mpdf->WriteHTML('<h1>GSUB Tables</h1>'); 
972
			$ffeats = array();
973
			$gsub_offset = $this->seek_table("GSUB");
974
			$this->skip(4);
975
			$ScriptList_offset = $gsub_offset + $this->read_ushort();
976
			$FeatureList_offset = $gsub_offset + $this->read_ushort();
977
			$LookupList_offset = $gsub_offset + $this->read_ushort();
978
979
			// ScriptList
980
			$this->seek($ScriptList_offset );
981
			$ScriptCount = $this->read_ushort();
982 View Code Duplication
			for ($i=0;$i<$ScriptCount;$i++) {
983
					$ScriptTag = $this->read_tag();	// = "beng", "deva" etc.
984
					$ScriptTableOffset = $this->read_ushort();
985
					$ffeats[$ScriptTag] = $ScriptList_offset + $ScriptTableOffset;
986
			}
987
988
			// Script Table
989 View Code Duplication
			foreach($ffeats AS $t=>$o) {
990
				$ls = array();
991
				$this->seek($o);
992
				$DefLangSys_offset = $this->read_ushort();
993
				if ($DefLangSys_offset > 0) {
994
					$ls['DFLT'] = $DefLangSys_offset + $o;
995
				}
996
				$LangSysCount = $this->read_ushort();
997
				for ($i=0;$i<$LangSysCount;$i++) {
998
					$LangTag = $this->read_tag();	// = 
999
					$LangTableOffset = $this->read_ushort();
1000
					$ls[$LangTag] = $o + $LangTableOffset;
1001
				}
1002
				$ffeats[$t] = $ls;
1003
			}
1004
//print_r($ffeats); exit;
1005
1006
1007
			// Get FeatureIndexList
1008
			// LangSys Table - from first listed langsys
1009 View Code Duplication
			foreach($ffeats AS $st=>$scripts) {
1010
				foreach($scripts AS $t=>$o) {
1011
					$FeatureIndex = array();
1012
					$langsystable_offset = $o;
1013
					$this->seek($langsystable_offset);
1014
					$LookUpOrder = $this->read_ushort();	//==NULL
1015
					$ReqFeatureIndex = $this->read_ushort();
1016
					if ($ReqFeatureIndex != 0xFFFF) { $FeatureIndex[] = $ReqFeatureIndex; }
1017
					$FeatureCount = $this->read_ushort();
1018
					for ($i=0;$i<$FeatureCount;$i++) {
1019
							$FeatureIndex[] = $this->read_ushort();	// = index of feature
1020
					}
1021
					$ffeats[$st][$t] = $FeatureIndex; 
1022
				}
1023
			}
1024
//print_r($ffeats); exit;
1025
1026
1027
			// Feauture List => LookupListIndex es
1028
			$this->seek($FeatureList_offset );
1029
			$FeatureCount = $this->read_ushort();
1030
			$Feature = array();
1031 View Code Duplication
			for ($i=0;$i<$FeatureCount;$i++) {
1032
				$Feature[$i] = array('tag' => $this->read_tag() );
1033
				$Feature[$i]['offset'] = $FeatureList_offset + $this->read_ushort();
1034
			}
1035 View Code Duplication
			for ($i=0;$i<$FeatureCount;$i++) {
1036
				$this->seek($Feature[$i]['offset']);
1037
				$this->read_ushort(); // null
1038
				$Feature[$i]['LookupCount'] = $Lookupcount = $this->read_ushort();
1039
				$Feature[$i]['LookupListIndex'] = array();
1040
				for ($c=0;$c<$Lookupcount;$c++) {
1041
					$Feature[$i]['LookupListIndex'][] = $this->read_ushort();
1042
				}
1043
			}
1044
1045
1046 View Code Duplication
			foreach($ffeats AS $st=>$scripts) {
1047
				foreach($scripts AS $t=>$o) {
1048
					$FeatureIndex = $ffeats[$st][$t];
1049
					foreach($FeatureIndex AS $k=>$fi) {
1050
						$ffeats[$st][$t][$k] = $Feature[$fi];
1051
					}
1052
				}
1053
			}
1054
			//=====================================================================================
1055
			$gsub = array();
1056
			$GSUBScriptLang = array();
1057 View Code Duplication
			foreach($ffeats AS $st=>$scripts) {
1058
				foreach($scripts AS $t=>$langsys) {
1059
					$lg = array();
1060
					foreach($langsys AS $ft) {
1061
						$lg[$ft['LookupListIndex'][0]] = $ft;
1062
					}
1063
					// list of Lookups in order they need to be run i.e. order listed in Lookup table
1064
					ksort($lg);
1065
					foreach($lg AS $ft) {
1066
						$gsub[$st][$t][$ft['tag']] = $ft['LookupListIndex'];
1067
					}
1068
					if (!isset($GSUBScriptLang[$st])) { $GSUBScriptLang[$st] = ''; }
1069
					$GSUBScriptLang[$st] .= $t.' ';
1070
				}
1071
			}
1072
1073
//print_r($gsub); exit;
1074
1075 View Code Duplication
			if ($this->mode == 'summary') {
1076
				$this->mpdf->WriteHTML('<h3>GSUB Scripts &amp; Languages</h3>'); 
1077
				$this->mpdf->WriteHTML('<div class="glyphs">'); 
1078
				$html = '';
1079
				if (count($gsub)) {
1080
					foreach ($gsub AS $st=>$g) {
1081
						$html .= '<h5>'.$st.'</h5>'; 
1082
						foreach ($g AS $l=>$t) {
1083
							$html .= '<div><a href="font_dump_OTL.php?script='.$st.'&lang='.$l.'">'.$l.'</a></b>: '; 
1084
							foreach ($t AS $tag=>$o) {
1085
								$html .= $tag.' '; 
1086
							}
1087
							$html .= '</div>'; 
1088
						}
1089
					}
1090
				}
1091
				else {
1092
					$html .= '<div>No entries in GSUB table.</div>';
1093
				}
1094
				$this->mpdf->WriteHTML($html); 
1095
				$this->mpdf->WriteHTML('</div>'); 
1096
				return 0;
1097
			}
1098
1099
1100
1101
			//=====================================================================================
1102
			// Get metadata and offsets for whole Lookup List table
1103
			$this->seek($LookupList_offset );
1104
			$LookupCount = $this->read_ushort();
1105
			$GSLookup = array();
1106
			$Offsets = array();
1107
			$SubtableCount = array();
1108 View Code Duplication
			for ($i=0;$i<$LookupCount;$i++) {
1109
				$Offsets[$i] = $LookupList_offset + $this->read_ushort();
1110
			}
1111 View Code Duplication
			for ($i=0;$i<$LookupCount;$i++) {
1112
				$this->seek($Offsets[$i]);
1113
				$GSLookup[$i]['Type'] = $this->read_ushort();
1114
				$GSLookup[$i]['Flag'] = $flag = $this->read_ushort();
1115
				$GSLookup[$i]['SubtableCount'] = $SubtableCount[$i] = $this->read_ushort();
1116
				for ($c=0;$c<$SubtableCount[$i] ;$c++) {
1117
					$GSLookup[$i]['Subtables'][$c] = $Offsets[$i] + $this->read_ushort();
1118
1119
				}
1120
				// MarkFilteringSet = Index (base 0) into GDEF mark glyph sets structure
1121
				if (($flag & 0x0010) == 0x0010) {
1122
					$GSLookup[$i]['MarkFilteringSet'] = $this->read_ushort();
1123
				}
1124
				// else { $GSLookup[$i]['MarkFilteringSet'] = ''; }
1125
1126
				// Lookup Type 7: Extension
1127
				if ($GSLookup[$i]['Type'] == 7) {
1128
					// Overwrites new offset (32-bit) for each subtable, and a new lookup Type
1129
					for ($c=0;$c<$SubtableCount[$i] ;$c++) {
1130
						$this->seek($GSLookup[$i]['Subtables'][$c]);
1131
						$ExtensionPosFormat = $this->read_ushort();
1132
						$type = $this->read_ushort();
1133
						$GSLookup[$i]['Subtables'][$c] = $GSLookup[$i]['Subtables'][$c] + $this->read_ulong();
1134
					}
1135
					$GSLookup[$i]['Type'] = $type;
1136
				}
1137
1138
			}
1139
1140
//print_r($GSLookup); exit;
1141
			//=====================================================================================
1142
			// Process Whole LookupList - Get LuCoverage = Lookup coverage just for first glyph
1143
			$this->GSLuCoverage = array();
1144 View Code Duplication
			for ($i=0;$i<$LookupCount;$i++) {
1145
				for ($c=0;$c<$GSLookup[$i]['SubtableCount'] ;$c++) {
1146
1147
					$this->seek($GSLookup[$i]['Subtables'][$c]);
1148
					$PosFormat= $this->read_ushort();
1149
1150
					if ($GSLookup[$i]['Type']==5 && $PosFormat==3) { $this->skip(4); }
1151
					else if ($GSLookup[$i]['Type']==6 && $PosFormat==3) {
1152
						$BacktrackGlyphCount= $this->read_ushort();
1153
						$this->skip(2*$BacktrackGlyphCount + 2); 
1154
					}
1155
					// NB Coverage only looks at glyphs for position 1 (i.e. 5.3 and 6.3)	// NEEDS TO READ ALL ********************
1156
					$Coverage = $GSLookup[$i]['Subtables'][$c] + $this->read_ushort();
1157
					$this->seek($Coverage);
1158
					$glyphs = $this->_getCoverage();
1159
					$this->GSLuCoverage[$i][$c] = implode('|',$glyphs);
1160
				}
1161
			}
1162
1163
// $this->GSLuCoverage and $GSLookup
1164
1165
			//=====================================================================================
1166
			$s = '<?php
1167
$GSLuCoverage = '.var_export($this->GSLuCoverage , true).';
1168
?>';
1169
1170
1171
			//=====================================================================================
1172
			$s = '<?php
1173
$GlyphClassBases = \''.$this->GlyphClassBases.'\';
1174
$GlyphClassMarks = \''.$this->GlyphClassMarks.'\';
1175
$GlyphClassLigatures = \''.$this->GlyphClassLigatures.'\';
1176
$GlyphClassComponents = \''.$this->GlyphClassComponents.'\';
1177
$MarkGlyphSets = '.var_export($this->MarkGlyphSets , true).';
1178
$MarkAttachmentType = '.var_export($this->MarkAttachmentType , true).';
1179
?>';
1180
1181
1182
			//=====================================================================================
1183
			//=====================================================================================
1184
			//=====================================================================================
1185
// Now repeats as original to get Substitution rules
1186
			//=====================================================================================
1187
			//=====================================================================================
1188
			//=====================================================================================
1189
			// Get metadata and offsets for whole Lookup List table
1190
			$this->seek($LookupList_offset );
1191
			$LookupCount = $this->read_ushort();
1192
			$Lookup = array();
1193 View Code Duplication
			for ($i=0;$i<$LookupCount;$i++) {
1194
				$Lookup[$i]['offset'] = $LookupList_offset + $this->read_ushort();
1195
			}
1196 View Code Duplication
			for ($i=0;$i<$LookupCount;$i++) {
1197
				$this->seek($Lookup[$i]['offset']);
1198
				$Lookup[$i]['Type'] = $this->read_ushort();
1199
				$Lookup[$i]['Flag'] = $flag = $this->read_ushort();
1200
				$Lookup[$i]['SubtableCount'] = $this->read_ushort();
1201
				for ($c=0;$c<$Lookup[$i]['SubtableCount'] ;$c++) {
1202
					$Lookup[$i]['Subtable'][$c]['Offset'] = $Lookup[$i]['offset'] + $this->read_ushort();
1203
1204
				}
1205
				// MarkFilteringSet = Index (base 0) into GDEF mark glyph sets structure
1206
				if (($flag & 0x0010) == 0x0010) {
1207
					$Lookup[$i]['MarkFilteringSet'] = $this->read_ushort();
1208
				}
1209
				else { $Lookup[$i]['MarkFilteringSet'] = ''; }
1210
1211
				// Lookup Type 7: Extension
1212
				if ($Lookup[$i]['Type'] == 7) {
1213
					// Overwrites new offset (32-bit) for each subtable, and a new lookup Type
1214
					for ($c=0;$c<$Lookup[$i]['SubtableCount'] ;$c++) {
1215
						$this->seek($Lookup[$i]['Subtable'][$c]['Offset']);
1216
						$ExtensionPosFormat = $this->read_ushort();
1217
						$type = $this->read_ushort();
1218
						$Lookup[$i]['Subtable'][$c]['Offset'] = $Lookup[$i]['Subtable'][$c]['Offset'] + $this->read_ulong();
1219
					}
1220
					$Lookup[$i]['Type'] = $type;
1221
				}
1222
1223
			}
1224
1225
//print_r($Lookup); exit;
1226
			//=====================================================================================
1227
			// Process (1) Whole LookupList
1228 View Code Duplication
			for ($i=0;$i<$LookupCount;$i++) {
1229
				for ($c=0;$c<$Lookup[$i]['SubtableCount'] ;$c++) {
1230
1231
					$this->seek($Lookup[$i]['Subtable'][$c]['Offset']);
1232
					$SubstFormat= $this->read_ushort();
1233
					$Lookup[$i]['Subtable'][$c]['Format'] = $SubstFormat;
1234
1235
/*
1236
Lookup['Type'] Enumeration table for glyph substitution 
1237
Value	Type	Description
1238
1	Single	Replace one glyph with one glyph
1239
2	Multiple	Replace one glyph with more than one glyph
1240
3	Alternate	Replace one glyph with one of many glyphs
1241
4	Ligature	Replace multiple glyphs with one glyph
1242
5	Context	Replace one or more glyphs in context
1243
6	Chaining Context	Replace one or more glyphs in chained context
1244
7	Extension Substitution	Extension mechanism for other substitutions (i.e. this excludes the Extension type substitution itself)
1245
8	Reverse chaining context single 	Applied in reverse order, replace single glyph in chaining context
1246
*/
1247
1248
					// LookupType 1: Single Substitution Subtable
1249
					if ($Lookup[$i]['Type'] == 1) {
1250
						$Lookup[$i]['Subtable'][$c]['CoverageTableOffset'] = $Lookup[$i]['Subtable'][$c]['Offset'] + $this->read_ushort();
1251
						if ($SubstFormat==1) {	// Calculated output glyph indices
1252
							$Lookup[$i]['Subtable'][$c]['DeltaGlyphID'] = $this->read_short();
1253
						}
1254
						else if ($SubstFormat==2) {	// Specified output glyph indices
1255
							$GlyphCount = $this->read_ushort();
1256
							for ($g=0;$g<$GlyphCount;$g++) { 
1257
								$Lookup[$i]['Subtable'][$c]['Glyphs'][] = $this->read_ushort();
1258
							}
1259
						}
1260
					}
1261
					// LookupType 2: Multiple Substitution Subtable
1262
					else if ($Lookup[$i]['Type'] == 2) {
1263
						$Lookup[$i]['Subtable'][$c]['CoverageTableOffset'] = $Lookup[$i]['Subtable'][$c]['Offset'] + $this->read_ushort();
1264
						$Lookup[$i]['Subtable'][$c]['SequenceCount'] = $SequenceCount = $this->read_short();
1265
						for($s=0;$s<$SequenceCount;$s++) {
1266
							$Lookup[$i]['Subtable'][$c]['Sequences'][$s]['Offset'] = $Lookup[$i]['Subtable'][$c]['Offset'] + $this->read_short();
1267
						}
1268
						for($s=0;$s<$SequenceCount;$s++) {
1269
							// Sequence Tables
1270
							$this->seek($Lookup[$i]['Subtable'][$c]['Sequences'][$s]['Offset']);
1271
							$Lookup[$i]['Subtable'][$c]['Sequences'][$s]['GlyphCount'] = $this->read_short();
1272
							for ($g=0;$g<$Lookup[$i]['Subtable'][$c]['Sequences'][$s]['GlyphCount'];$g++) { 
1273
								$Lookup[$i]['Subtable'][$c]['Sequences'][$s]['SubstituteGlyphID'][] = $this->read_ushort();
1274
							}
1275
						}
1276
					}
1277
					// LookupType 3: Alternate Forms
1278
					else if ($Lookup[$i]['Type'] == 3) {
1279
						$Lookup[$i]['Subtable'][$c]['CoverageTableOffset'] = $Lookup[$i]['Subtable'][$c]['Offset'] + $this->read_ushort();
1280
						$Lookup[$i]['Subtable'][$c]['AlternateSetCount'] = $AlternateSetCount = $this->read_short();
1281
						for($s=0;$s<$AlternateSetCount;$s++) {
1282
							$Lookup[$i]['Subtable'][$c]['AlternateSets'][$s]['Offset'] = $Lookup[$i]['Subtable'][$c]['Offset'] + $this->read_short();
1283
						}
1284
1285
						for($s=0;$s<$AlternateSetCount;$s++) {
1286
							// AlternateSet Tables
1287
							$this->seek($Lookup[$i]['Subtable'][$c]['AlternateSets'][$s]['Offset']);
1288
							$Lookup[$i]['Subtable'][$c]['AlternateSets'][$s]['GlyphCount'] = $this->read_short();
1289
							for ($g=0;$g<$Lookup[$i]['Subtable'][$c]['AlternateSets'][$s]['GlyphCount'];$g++) { 
1290
								$Lookup[$i]['Subtable'][$c]['AlternateSets'][$s]['SubstituteGlyphID'][] = $this->read_ushort();
1291
							}
1292
						}
1293
					}
1294
					// LookupType 4: Ligature Substitution Subtable
1295
					else if ($Lookup[$i]['Type'] == 4) {
1296
						$Lookup[$i]['Subtable'][$c]['CoverageTableOffset'] = $Lookup[$i]['Subtable'][$c]['Offset'] + $this->read_ushort();
1297
						$Lookup[$i]['Subtable'][$c]['LigSetCount'] = $LigSetCount = $this->read_short();
1298
						for($s=0;$s<$LigSetCount;$s++) {
1299
							$Lookup[$i]['Subtable'][$c]['LigSet'][$s]['Offset'] = $Lookup[$i]['Subtable'][$c]['Offset'] + $this->read_short();
1300
						}
1301
						for($s=0;$s<$LigSetCount;$s++) {
1302
							// LigatureSet Tables
1303
							$this->seek($Lookup[$i]['Subtable'][$c]['LigSet'][$s]['Offset']);
1304
							$Lookup[$i]['Subtable'][$c]['LigSet'][$s]['LigCount'] = $this->read_short();
1305
							for ($g=0;$g<$Lookup[$i]['Subtable'][$c]['LigSet'][$s]['LigCount'];$g++) { 
1306
								$Lookup[$i]['Subtable'][$c]['LigSet'][$s]['LigatureOffset'][$g] = $Lookup[$i]['Subtable'][$c]['LigSet'][$s]['Offset'] + $this->read_ushort();
1307
							}
1308
						}
1309
						for($s=0;$s<$LigSetCount;$s++) {
1310
							for ($g=0;$g<$Lookup[$i]['Subtable'][$c]['LigSet'][$s]['LigCount'];$g++) { 
1311
								// Ligature tables
1312
								$this->seek($Lookup[$i]['Subtable'][$c]['LigSet'][$s]['LigatureOffset'][$g]);
1313
								$Lookup[$i]['Subtable'][$c]['LigSet'][$s]['Ligature'][$g]['LigGlyph'] = $this->read_ushort();
1314
								$Lookup[$i]['Subtable'][$c]['LigSet'][$s]['Ligature'][$g]['CompCount'] = $this->read_ushort();
1315
								for ($l=1;$l<$Lookup[$i]['Subtable'][$c]['LigSet'][$s]['Ligature'][$g]['CompCount'];$l++) { 
1316
									$Lookup[$i]['Subtable'][$c]['LigSet'][$s]['Ligature'][$g]['GlyphID'][$l] = $this->read_ushort();
1317
								}
1318
							}
1319
						}
1320
					}
1321
1322
					// LookupType 5: Contextual Substitution Subtable
1323
					else if ($Lookup[$i]['Type'] == 5) {
1324
						// Format 1: Context Substitution
1325
						if ($SubstFormat==1) {	
1326
							$Lookup[$i]['Subtable'][$c]['CoverageTableOffset'] = $Lookup[$i]['Subtable'][$c]['Offset'] + $this->read_ushort();
1327
							$Lookup[$i]['Subtable'][$c]['SubRuleSetCount'] = $SubRuleSetCount = $this->read_short();
1328
							for($s=0;$s<$SubRuleSetCount;$s++) {
1329
								$Lookup[$i]['Subtable'][$c]['SubRuleSet'][$s]['Offset'] = $Lookup[$i]['Subtable'][$c]['Offset'] + $this->read_short();
1330
							}
1331
							for($s=0;$s<$SubRuleSetCount;$s++) {
1332
								// SubRuleSet Tables
1333
								$this->seek($Lookup[$i]['Subtable'][$c]['SubRuleSet'][$s]['Offset']);
1334
								$Lookup[$i]['Subtable'][$c]['SubRuleSet'][$s]['SubRuleCount'] = $this->read_short();
1335
								for ($g=0;$g<$Lookup[$i]['Subtable'][$c]['SubRuleSet'][$s]['SubRuleCount'];$g++) { 
1336
									$Lookup[$i]['Subtable'][$c]['SubRuleSet'][$s]['SubRuleOffset'][$g] = $Lookup[$i]['Subtable'][$c]['SubRuleSet'][$s]['Offset'] + $this->read_ushort();
1337
								}
1338
							}
1339
							for($s=0;$s<$SubRuleSetCount;$s++) {
1340
								// SubRule Tables
1341
								for ($g=0;$g<$Lookup[$i]['Subtable'][$c]['SubRuleSet'][$s]['SubRuleCount'];$g++) { 
1342
									// Ligature tables
1343
									$this->seek($Lookup[$i]['Subtable'][$c]['SubRuleSet'][$s]['SubRuleOffset'][$g]);
1344
1345
									$Lookup[$i]['Subtable'][$c]['SubRuleSet'][$s]['SubRule'][$g]['GlyphCount'] = $this->read_ushort();
1346
									$Lookup[$i]['Subtable'][$c]['SubRuleSet'][$s]['SubRule'][$g]['SubstCount'] = $this->read_ushort();
1347
									// "Input"::[GlyphCount - 1]::Array of input GlyphIDs-start with second glyph
1348
									for ($l=1;$l<$Lookup[$i]['Subtable'][$c]['SubRuleSet'][$s]['SubRule'][$g]['GlyphCount'];$l++) { 
1349
										$Lookup[$i]['Subtable'][$c]['SubRuleSet'][$s]['SubRule'][$g]['Input'][$l] = $this->read_ushort();
1350
									}
1351
									// "SubstLookupRecord"::[SubstCount]::Array of SubstLookupRecords-in design order
1352
									for ($l=0;$l<$Lookup[$i]['Subtable'][$c]['SubRuleSet'][$s]['SubRule'][$g]['SubstCount'];$l++) { 
1353
										$Lookup[$i]['Subtable'][$c]['SubRuleSet'][$s]['SubRule'][$g]['SubstLookupRecord'][$l]['SequenceIndex'] = $this->read_ushort();
1354
										$Lookup[$i]['Subtable'][$c]['SubRuleSet'][$s]['SubRule'][$g]['SubstLookupRecord'][$l]['LookupListIndex'] = $this->read_ushort();
1355
									}
1356
1357
								}
1358
							}
1359
1360
						}
1361
						// Format 2: Class-based Context Glyph Substitution
1362
						else if ($SubstFormat==2) {	
1363
							$Lookup[$i]['Subtable'][$c]['CoverageTableOffset'] = $Lookup[$i]['Subtable'][$c]['Offset'] + $this->read_ushort();
1364
							$Lookup[$i]['Subtable'][$c]['ClassDefOffset'] = $Lookup[$i]['Subtable'][$c]['Offset'] + $this->read_ushort();
1365
							$Lookup[$i]['Subtable'][$c]['SubClassSetCnt'] = $this->read_ushort();
1366
							for ($b=0;$b<$Lookup[$i]['Subtable'][$c]['SubClassSetCnt'];$b++) {
1367
								$offset = $this->read_ushort();
1368
								if ($offset==0x0000) {
1369
									$Lookup[$i]['Subtable'][$c]['SubClassSetOffset'][] = 0;
1370
								}
1371
								else {
1372
									$Lookup[$i]['Subtable'][$c]['SubClassSetOffset'][] = $Lookup[$i]['Subtable'][$c]['Offset'] + $offset;
1373
								}
1374
							}
1375
						}
1376
						else { die("GPOS Lookup Type ".$Lookup[$i]['Type'].", Format ".$SubstFormat." not supported (ttfontsuni.php)."); }
1377
					}
1378
1379
					// LookupType 6: Chaining Contextual Substitution Subtable
1380
					else if ($Lookup[$i]['Type'] == 6) {
1381
						// Format 1: Simple Chaining Context Glyph Substitution  p255
1382
						if ($SubstFormat==1) {	
1383
							$Lookup[$i]['Subtable'][$c]['CoverageTableOffset'] = $Lookup[$i]['Subtable'][$c]['Offset'] + $this->read_ushort();
1384
							$Lookup[$i]['Subtable'][$c]['ChainSubRuleSetCount'] = $this->read_ushort();
1385
							for ($b=0;$b<$Lookup[$i]['Subtable'][$c]['ChainSubRuleSetCount'];$b++) {
1386
								$Lookup[$i]['Subtable'][$c]['ChainSubRuleSetOffset'][] = $Lookup[$i]['Subtable'][$c]['Offset'] + $this->read_ushort();
1387
							}
1388
						}
1389
						// Format 2: Class-based Chaining Context Glyph Substitution  p257
1390
						else if ($SubstFormat==2) {	
1391
							$Lookup[$i]['Subtable'][$c]['CoverageTableOffset'] = $Lookup[$i]['Subtable'][$c]['Offset'] + $this->read_ushort();
1392
							$Lookup[$i]['Subtable'][$c]['BacktrackClassDefOffset'] = $Lookup[$i]['Subtable'][$c]['Offset'] + $this->read_ushort();
1393
							$Lookup[$i]['Subtable'][$c]['InputClassDefOffset'] = $Lookup[$i]['Subtable'][$c]['Offset'] + $this->read_ushort();
1394
							$Lookup[$i]['Subtable'][$c]['LookaheadClassDefOffset'] = $Lookup[$i]['Subtable'][$c]['Offset'] + $this->read_ushort();
1395
							$Lookup[$i]['Subtable'][$c]['ChainSubClassSetCnt'] = $this->read_ushort();
1396
							for ($b=0;$b<$Lookup[$i]['Subtable'][$c]['ChainSubClassSetCnt'];$b++) {
1397
								$offset = $this->read_ushort();
1398
								if ($offset==0x0000) {
1399
									$Lookup[$i]['Subtable'][$c]['ChainSubClassSetOffset'][] = $offset;
1400
								}
1401
								else {
1402
									$Lookup[$i]['Subtable'][$c]['ChainSubClassSetOffset'][] = $Lookup[$i]['Subtable'][$c]['Offset'] + $offset;
1403
								}
1404
							}
1405
						}
1406
						// Format 3: Coverage-based Chaining Context Glyph Substitution  p259
1407
						else if ($SubstFormat==3) {	
1408
							$Lookup[$i]['Subtable'][$c]['BacktrackGlyphCount'] = $this->read_ushort();
1409
							for ($b=0;$b<$Lookup[$i]['Subtable'][$c]['BacktrackGlyphCount'];$b++) {
1410
								$Lookup[$i]['Subtable'][$c]['CoverageBacktrack'][] = $Lookup[$i]['Subtable'][$c]['Offset'] + $this->read_ushort();
1411
							}
1412
							$Lookup[$i]['Subtable'][$c]['InputGlyphCount'] = $this->read_ushort();
1413
							for ($b=0;$b<$Lookup[$i]['Subtable'][$c]['InputGlyphCount'];$b++) {
1414
								$Lookup[$i]['Subtable'][$c]['CoverageInput'][] = $Lookup[$i]['Subtable'][$c]['Offset'] + $this->read_ushort();
1415
							}
1416
							$Lookup[$i]['Subtable'][$c]['LookaheadGlyphCount'] = $this->read_ushort();
1417
							for ($b=0;$b<$Lookup[$i]['Subtable'][$c]['LookaheadGlyphCount'];$b++) {
1418
								$Lookup[$i]['Subtable'][$c]['CoverageLookahead'][] = $Lookup[$i]['Subtable'][$c]['Offset'] + $this->read_ushort();
1419
							}
1420
							$Lookup[$i]['Subtable'][$c]['SubstCount'] = $this->read_ushort();
1421
							for ($b=0;$b<$Lookup[$i]['Subtable'][$c]['SubstCount'];$b++) {
1422
								$Lookup[$i]['Subtable'][$c]['SubstLookupRecord'][$b]['SequenceIndex'] = $this->read_ushort();
1423
								$Lookup[$i]['Subtable'][$c]['SubstLookupRecord'][$b]['LookupListIndex'] = $this->read_ushort();
1424
/*
1425
Substitution Lookup Record 
1426
All contextual substitution subtables specify the substitution data in a Substitution Lookup Record (SubstLookupRecord). Each record contains a SequenceIndex, which indicates the position where the substitution will occur in the glyph sequence. In addition, a LookupListIndex identifies the lookup to be applied at the glyph position specified by the SequenceIndex. 
1427
*/
1428
1429
							}
1430
						}
1431
					}
1432
					else { die("Lookup Type ".$Lookup[$i]['Type']." not supported."); }
1433
				}
1434
			}
1435
//print_r($Lookup); exit;
1436
1437
1438
1439
1440
			//=====================================================================================
1441
			// Process (2) Whole LookupList
1442
			// Get Coverage tables and prepare preg_replace
1443
			for ($i=0;$i<$LookupCount;$i++) {
1444
				for ($c=0;$c<$Lookup[$i]['SubtableCount'] ;$c++) {
1445
					$SubstFormat= $Lookup[$i]['Subtable'][$c]['Format'] ;
1446
1447
					// LookupType 1: Single Substitution Subtable 1 => 1
1448
					if ($Lookup[$i]['Type'] == 1) {
1449
						$this->seek($Lookup[$i]['Subtable'][$c]['CoverageTableOffset']);
1450
						$glyphs = $this->_getCoverage(false);
1451 View Code Duplication
						for ($g=0;$g<count($glyphs);$g++) {
1452
							$replace = array();
1453
							$substitute = array();
1454
							$replace[] = unicode_hex($this->glyphToChar[$glyphs[$g]][0]);
1455
							// Flag = Ignore
1456
							if ($this->_checkGSUBignore($Lookup[$i]['Flag'], $replace[0], $Lookup[$i]['MarkFilteringSet'])) { continue; }
1457
							if (isset($Lookup[$i]['Subtable'][$c]['DeltaGlyphID'])) {	// Format 1
1458
								$substitute[] = unicode_hex($this->glyphToChar[($glyphs[$g]+$Lookup[$i]['Subtable'][$c]['DeltaGlyphID'])][0]);
1459
							}
1460
							else {	// Format 2
1461
								$substitute[] = unicode_hex($this->glyphToChar[($Lookup[$i]['Subtable'][$c]['Glyphs'][$g])][0]);
1462
							}
1463
							$Lookup[$i]['Subtable'][$c]['subs'][] = array('Replace'=>$replace, 'substitute'=>$substitute);
1464
						}
1465
					}
1466
1467
					// LookupType 2: Multiple Substitution Subtable 1 => n
1468
					else if ($Lookup[$i]['Type'] == 2) {
1469
						$this->seek($Lookup[$i]['Subtable'][$c]['CoverageTableOffset']);
1470
						$glyphs = $this->_getCoverage();
1471 View Code Duplication
						for ($g=0;$g<count($glyphs);$g++) {
1472
							$replace = array();
1473
							$substitute = array();
1474
							$replace[] = $glyphs[$g];
1475
							// Flag = Ignore
1476
							if ($this->_checkGSUBignore($Lookup[$i]['Flag'], $replace[0], $Lookup[$i]['MarkFilteringSet'])) { continue; }
1477
if (!isset($Lookup[$i]['Subtable'][$c]['Sequences'][$g]['SubstituteGlyphID']) || count($Lookup[$i]['Subtable'][$c]['Sequences'][$g]['SubstituteGlyphID'])==0) { continue; }	// Illegal for GlyphCount to be 0; either error in font, or something has gone wrong - lets carry on for now!
1478
							foreach($Lookup[$i]['Subtable'][$c]['Sequences'][$g]['SubstituteGlyphID'] AS $sub) {
1479
								$substitute[] = unicode_hex($this->glyphToChar[$sub][0]);
1480
							}
1481
							$Lookup[$i]['Subtable'][$c]['subs'][] = array('Replace'=>$replace, 'substitute'=>$substitute);
1482
						}
1483
					}
1484
					// LookupType 3: Alternate Forms 1 => 1 (only first alternate form is used)
1485
					else if ($Lookup[$i]['Type'] == 3) {
1486
						$this->seek($Lookup[$i]['Subtable'][$c]['CoverageTableOffset']);
1487
						$glyphs = $this->_getCoverage();
1488
						for ($g=0;$g<count($glyphs);$g++) {
1489
							$replace = array();
1490
							$substitute = array();
1491
							$replace[] = $glyphs[$g];
1492
							// Flag = Ignore
1493
							if ($this->_checkGSUBignore($Lookup[$i]['Flag'], $replace[0], $Lookup[$i]['MarkFilteringSet'])) { continue; }
1494
1495
							for ($gl=0;$gl<$Lookup[$i]['Subtable'][$c]['AlternateSets'][$g]['GlyphCount'];$gl++) { 
1496
								$gid = $Lookup[$i]['Subtable'][$c]['AlternateSets'][$g]['SubstituteGlyphID'][$gl];
1497
								$substitute[] = unicode_hex($this->glyphToChar[$gid][0]);
1498
							}
1499
1500
							//$gid = $Lookup[$i]['Subtable'][$c]['AlternateSets'][$g]['SubstituteGlyphID'][0];
1501
							//$substitute[] = unicode_hex($this->glyphToChar[$gid][0]);
1502
1503
							$Lookup[$i]['Subtable'][$c]['subs'][] = array('Replace'=>$replace, 'substitute'=>$substitute);
1504
						}
1505
if ($i==166) {
1506
	print_r($Lookup[$i]['Subtable']);
1507
	exit;
1508
}
1509
					}
1510
					// LookupType 4: Ligature Substitution Subtable n => 1
1511
					else if ($Lookup[$i]['Type'] == 4) {
1512
						$this->seek($Lookup[$i]['Subtable'][$c]['CoverageTableOffset']);
1513
						$glyphs = $this->_getCoverage();
1514
						$LigSetCount = $Lookup[$i]['Subtable'][$c]['LigSetCount'];
1515
						for($s=0;$s<$LigSetCount;$s++) {
1516
							for ($g=0;$g<$Lookup[$i]['Subtable'][$c]['LigSet'][$s]['LigCount'];$g++) { 
1517
								$replace = array();
1518
								$substitute = array();
1519
								$replace[] = $glyphs[$s];
1520
								// Flag = Ignore
1521
								if ($this->_checkGSUBignore($Lookup[$i]['Flag'], $replace[0], $Lookup[$i]['MarkFilteringSet'])) { continue; }
1522 View Code Duplication
								for ($l=1;$l<$Lookup[$i]['Subtable'][$c]['LigSet'][$s]['Ligature'][$g]['CompCount'];$l++) { 
1523
									$gid = $Lookup[$i]['Subtable'][$c]['LigSet'][$s]['Ligature'][$g]['GlyphID'][$l];
1524
									$rpl = unicode_hex($this->glyphToChar[$gid][0]);
1525
									// Flag = Ignore
1526
									if ($this->_checkGSUBignore($Lookup[$i]['Flag'], $rpl, $Lookup[$i]['MarkFilteringSet'])) { continue 2; }
1527
									$replace[] = $rpl;
1528
								}
1529
								$gid = $Lookup[$i]['Subtable'][$c]['LigSet'][$s]['Ligature'][$g]['LigGlyph'];
1530
								$substitute[] = unicode_hex($this->glyphToChar[$gid][0]);
1531
								$Lookup[$i]['Subtable'][$c]['subs'][] = array('Replace'=>$replace, 'substitute'=>$substitute, 'CompCount' => $Lookup[$i]['Subtable'][$c]['LigSet'][$s]['Ligature'][$g]['CompCount']);
1532
							}
1533
						}
1534
					}
1535
1536
					// LookupType 5: Contextual Substitution Subtable
1537
					else if ($Lookup[$i]['Type'] == 5) {
1538
						// Format 1: Context Substitution
1539 View Code Duplication
						if ($SubstFormat==1) {
1540
							$this->seek($Lookup[$i]['Subtable'][$c]['CoverageTableOffset']);
1541
							$Lookup[$i]['Subtable'][$c]['CoverageGlyphs'] = $CoverageGlyphs = $this->_getCoverage();
1542
1543
							for ($s=0;$s<$Lookup[$i]['Subtable'][$c]['SubRuleSetCount'];$s++) {
1544
								$SubRuleSet = $Lookup[$i]['Subtable'][$c]['SubRuleSet'][$s];
1545
								$Lookup[$i]['Subtable'][$c]['SubRuleSet'][$s]['FirstGlyph'] = $CoverageGlyphs[$s];
1546
								for ($r=0;$r<$Lookup[$i]['Subtable'][$c]['SubRuleSet'][$s]['SubRuleCount'];$r++) {
1547
									$GlyphCount = $Lookup[$i]['Subtable'][$c]['SubRuleSet'][$s]['SubRule'][$r]['GlyphCount'];
1548
									for ($g=1;$g<$GlyphCount;$g++) {
1549
										$glyphID = $Lookup[$i]['Subtable'][$c]['SubRuleSet'][$s]['SubRule'][$r]['Input'][$g];
1550
										$Lookup[$i]['Subtable'][$c]['SubRuleSet'][$s]['SubRule'][$r]['InputGlyphs'][$g] = unicode_hex($this->glyphToChar[$glyphID][0]);
1551
									}
1552
1553
								}
1554
							}
1555
						}
1556
						// Format 2: Class-based Context Glyph Substitution
1557
						else if ($SubstFormat==2) {	
1558
							$this->seek($Lookup[$i]['Subtable'][$c]['CoverageTableOffset']);
1559
							$Lookup[$i]['Subtable'][$c]['CoverageGlyphs'] = $CoverageGlyphs = $this->_getCoverage();
1560
1561
							$InputClasses = $this->_getClasses($Lookup[$i]['Subtable'][$c]['ClassDefOffset']);
1562
							$Lookup[$i]['Subtable'][$c]['InputClasses'] = $InputClasses;
1563
1564
							for ($s=0;$s<$Lookup[$i]['Subtable'][$c]['SubClassSetCnt'];$s++) {
1565
								if ($Lookup[$i]['Subtable'][$c]['SubClassSetOffset'][$s]>0) {
1566
									$this->seek($Lookup[$i]['Subtable'][$c]['SubClassSetOffset'][$s]);
1567
									$Lookup[$i]['Subtable'][$c]['SubClassSet'][$s]['SubClassRuleCnt'] = $SubClassRuleCnt = $this->read_ushort();
1568
									$SubClassRule = array();
1569
									for($b=0;$b<$SubClassRuleCnt;$b++) {
1570
										$SubClassRule[$b] = $Lookup[$i]['Subtable'][$c]['SubClassSetOffset'][$s]+$this->read_ushort();
1571
										$Lookup[$i]['Subtable'][$c]['SubClassSet'][$s]['SubClassRule'][$b] = $SubClassRule[$b];
1572
									}
1573
								}
1574
							}
1575
1576
							for ($s=0;$s<$Lookup[$i]['Subtable'][$c]['SubClassSetCnt'];$s++) {
1577
								$SubClassRuleCnt = $Lookup[$i]['Subtable'][$c]['SubClassSet'][$s]['SubClassRuleCnt'];
1578
								for($b=0;$b<$SubClassRuleCnt;$b++) {
1579
								   if ($Lookup[$i]['Subtable'][$c]['SubClassSetOffset'][$s]>0) {
1580
									$this->seek($Lookup[$i]['Subtable'][$c]['SubClassSet'][$s]['SubClassRule'][$b]);
1581
									$Rule = array();
1582
									$Rule['InputGlyphCount'] = $this->read_ushort();
1583
									$Rule['SubstCount'] = $this->read_ushort();
1584
									for ($r=1;$r<$Rule['InputGlyphCount'];$r++) {
1585
										$Rule['Input'][$r] = $this->read_ushort();
1586
									}
1587
									for ($r=0;$r<$Rule['SubstCount'];$r++) {
1588
										$Rule['SequenceIndex'][$r] = $this->read_ushort();
1589
										$Rule['LookupListIndex'][$r] = $this->read_ushort();
1590
									}
1591
1592
									$Lookup[$i]['Subtable'][$c]['SubClassSet'][$s]['SubClassRule'][$b] = $Rule;
1593
								   }
1594
								}
1595
							}
1596
						}
1597
						// Format 3: Coverage-based Context Glyph Substitution
1598
						else if ($SubstFormat==3) {
1599
							for ($b=0;$b<$Lookup[$i]['Subtable'][$c]['InputGlyphCount'];$b++) {
1600
								$this->seek($Lookup[$i]['Subtable'][$c]['CoverageInput'][$b]);
1601
								$glyphs = $this->_getCoverage();
1602
								$Lookup[$i]['Subtable'][$c]['CoverageInputGlyphs'][] = implode("|",$glyphs);
1603
							}
1604
							die("Lookup Type 5, SubstFormat 3 not tested. Please report this with the name of font used - ".$this->fontkey); 
1605
						}
1606
1607
					}
1608
1609
					// LookupType 6: Chaining Contextual Substitution Subtable
1610
					else if ($Lookup[$i]['Type'] == 6) {
1611
						// Format 1: Simple Chaining Context Glyph Substitution  p255
1612
						if ($SubstFormat==1) {	
1613
							$this->seek($Lookup[$i]['Subtable'][$c]['CoverageTableOffset']);
1614
							$Lookup[$i]['Subtable'][$c]['CoverageGlyphs'] = $CoverageGlyphs = $this->_getCoverage();
1615
1616
							$ChainSubRuleSetCnt = $Lookup[$i]['Subtable'][$c]['ChainSubRuleSetCount'];
1617
1618 View Code Duplication
							for ($s=0;$s<$ChainSubRuleSetCnt;$s++) {
1619
								$this->seek($Lookup[$i]['Subtable'][$c]['ChainSubRuleSetOffset'][$s]);
1620
								$ChainSubRuleCnt = $Lookup[$i]['Subtable'][$c]['ChainSubRuleSet'][$s]['ChainSubRuleCount'] = $this->read_ushort();
1621
								for ($r=0;$r<$ChainSubRuleCnt;$r++) {
1622
									$Lookup[$i]['Subtable'][$c]['ChainSubRuleSet'][$s]['ChainSubRuleOffset'][$r] = $Lookup[$i]['Subtable'][$c]['ChainSubRuleSetOffset'][$s] + $this->read_ushort();
1623
1624
								}
1625
							}
1626 View Code Duplication
							for ($s=0;$s<$ChainSubRuleSetCnt;$s++) {
1627
								$ChainSubRuleCnt = $Lookup[$i]['Subtable'][$c]['ChainSubRuleSet'][$s]['ChainSubRuleCount'];
1628
								for ($r=0;$r<$ChainSubRuleCnt;$r++) {
1629
									// ChainSubRule
1630
									$this->seek($Lookup[$i]['Subtable'][$c]['ChainSubRuleSet'][$s]['ChainSubRuleOffset'][$r]);
1631
1632
									$BacktrackGlyphCount = $Lookup[$i]['Subtable'][$c]['ChainSubRuleSet'][$s]['ChainSubRule'][$r]['BacktrackGlyphCount'] = $this->read_ushort();
1633
									for ($g=0;$g<$BacktrackGlyphCount;$g++) {
1634
										$glyphID = $this->read_ushort();
1635
										$Lookup[$i]['Subtable'][$c]['ChainSubRuleSet'][$s]['ChainSubRule'][$r]['BacktrackGlyphs'][$g] = unicode_hex($this->glyphToChar[$glyphID][0]); 
1636
									}
1637
1638
									$InputGlyphCount = $Lookup[$i]['Subtable'][$c]['ChainSubRuleSet'][$s]['ChainSubRule'][$r]['InputGlyphCount'] = $this->read_ushort();
1639
									for ($g=1;$g<$InputGlyphCount;$g++) {
1640
										$glyphID = $this->read_ushort();
1641
										$Lookup[$i]['Subtable'][$c]['ChainSubRuleSet'][$s]['ChainSubRule'][$r]['InputGlyphs'][$g] = unicode_hex($this->glyphToChar[$glyphID][0]);
1642
									}
1643
1644
1645
									$LookaheadGlyphCount = $Lookup[$i]['Subtable'][$c]['ChainSubRuleSet'][$s]['ChainSubRule'][$r]['LookaheadGlyphCount'] = $this->read_ushort();
1646
									for ($g=0;$g<$LookaheadGlyphCount;$g++) {
1647
										$glyphID = $this->read_ushort();
1648
										$Lookup[$i]['Subtable'][$c]['ChainSubRuleSet'][$s]['ChainSubRule'][$r]['LookaheadGlyphs'][$g] = unicode_hex($this->glyphToChar[$glyphID][0]);
1649
									}
1650
1651
									$SubstCount = $Lookup[$i]['Subtable'][$c]['ChainSubRuleSet'][$s]['ChainSubRule'][$r]['SubstCount'] = $this->read_ushort();
1652
									for ($lu=0;$lu<$SubstCount;$lu++) {
1653
										$Lookup[$i]['Subtable'][$c]['ChainSubRuleSet'][$s]['ChainSubRule'][$r]['SequenceIndex'][$lu] = $this->read_ushort();
1654
										$Lookup[$i]['Subtable'][$c]['ChainSubRuleSet'][$s]['ChainSubRule'][$r]['LookupListIndex'][$lu] = $this->read_ushort();
1655
									}
1656
								}
1657
							}
1658
1659
						}
1660
						// Format 2: Class-based Chaining Context Glyph Substitution  p257
1661
						else if ($SubstFormat==2) {	
1662
							$this->seek($Lookup[$i]['Subtable'][$c]['CoverageTableOffset']);
1663
							$Lookup[$i]['Subtable'][$c]['CoverageGlyphs'] = $CoverageGlyphs = $this->_getCoverage();
1664
1665
							$BacktrackClasses = $this->_getClasses($Lookup[$i]['Subtable'][$c]['BacktrackClassDefOffset']);
1666
							$Lookup[$i]['Subtable'][$c]['BacktrackClasses'] = $BacktrackClasses;
1667
1668
							$InputClasses = $this->_getClasses($Lookup[$i]['Subtable'][$c]['InputClassDefOffset']);
1669
							$Lookup[$i]['Subtable'][$c]['InputClasses'] = $InputClasses;
1670
1671
							$LookaheadClasses = $this->_getClasses($Lookup[$i]['Subtable'][$c]['LookaheadClassDefOffset']);
1672
							$Lookup[$i]['Subtable'][$c]['LookaheadClasses'] = $LookaheadClasses;
1673
1674 View Code Duplication
							for ($s=0;$s<$Lookup[$i]['Subtable'][$c]['ChainSubClassSetCnt'];$s++) {
1675
								if ($Lookup[$i]['Subtable'][$c]['ChainSubClassSetOffset'][$s]>0) {
1676
									$this->seek($Lookup[$i]['Subtable'][$c]['ChainSubClassSetOffset'][$s]);
1677
									$Lookup[$i]['Subtable'][$c]['ChainSubClassSet'][$s]['ChainSubClassRuleCnt'] = $ChainSubClassRuleCnt = $this->read_ushort();
1678
									$ChainSubClassRule = array();
1679
									for($b=0;$b<$ChainSubClassRuleCnt;$b++) {
1680
										$ChainSubClassRule[$b] = $Lookup[$i]['Subtable'][$c]['ChainSubClassSetOffset'][$s]+$this->read_ushort();
1681
										$Lookup[$i]['Subtable'][$c]['ChainSubClassSet'][$s]['ChainSubClassRule'][$b] = $ChainSubClassRule[$b];
1682
									}
1683
								}
1684
							}
1685
1686
							for ($s=0;$s<$Lookup[$i]['Subtable'][$c]['ChainSubClassSetCnt'];$s++) {
1687
								$ChainSubClassRuleCnt = $Lookup[$i]['Subtable'][$c]['ChainSubClassSet'][$s]['ChainSubClassRuleCnt'];
1688 View Code Duplication
								for($b=0;$b<$ChainSubClassRuleCnt;$b++) {
1689
								   if ($Lookup[$i]['Subtable'][$c]['ChainSubClassSetOffset'][$s]>0) {
1690
									$this->seek($Lookup[$i]['Subtable'][$c]['ChainSubClassSet'][$s]['ChainSubClassRule'][$b]);
1691
									$Rule = array();
1692
									$Rule['BacktrackGlyphCount'] = $this->read_ushort();
1693
									for ($r=0;$r<$Rule['BacktrackGlyphCount'];$r++) {
1694
										$Rule['Backtrack'][$r] = $this->read_ushort();
1695
									}
1696
									$Rule['InputGlyphCount'] = $this->read_ushort();
1697
									for ($r=1;$r<$Rule['InputGlyphCount'];$r++) {
1698
										$Rule['Input'][$r] = $this->read_ushort();
1699
									}
1700
									$Rule['LookaheadGlyphCount'] = $this->read_ushort();
1701
									for ($r=0;$r<$Rule['LookaheadGlyphCount'];$r++) {
1702
										$Rule['Lookahead'][$r] = $this->read_ushort();
1703
									}
1704
									$Rule['SubstCount'] = $this->read_ushort();
1705
									for ($r=0;$r<$Rule['SubstCount'];$r++) {
1706
										$Rule['SequenceIndex'][$r] = $this->read_ushort();
1707
										$Rule['LookupListIndex'][$r] = $this->read_ushort();
1708
									}
1709
1710
									$Lookup[$i]['Subtable'][$c]['ChainSubClassSet'][$s]['ChainSubClassRule'][$b] = $Rule;
1711
								   }
1712
								}
1713
							}
1714
						}
1715
						// Format 3: Coverage-based Chaining Context Glyph Substitution  p259
1716 View Code Duplication
						else if ($SubstFormat==3) {
1717
							for ($b=0;$b<$Lookup[$i]['Subtable'][$c]['BacktrackGlyphCount'];$b++) {
1718
								$this->seek($Lookup[$i]['Subtable'][$c]['CoverageBacktrack'][$b]);
1719
								$glyphs = $this->_getCoverage();
1720
								$Lookup[$i]['Subtable'][$c]['CoverageBacktrackGlyphs'][] = implode("|",$glyphs);
1721
							}
1722
							for ($b=0;$b<$Lookup[$i]['Subtable'][$c]['InputGlyphCount'];$b++) {
1723
								$this->seek($Lookup[$i]['Subtable'][$c]['CoverageInput'][$b]);
1724
								$glyphs = $this->_getCoverage();
1725
								$Lookup[$i]['Subtable'][$c]['CoverageInputGlyphs'][] = implode("|",$glyphs);
1726
								// Don't use above value as these are ordered numerically not as need to process
1727
							}
1728
							for ($b=0;$b<$Lookup[$i]['Subtable'][$c]['LookaheadGlyphCount'];$b++) {
1729
								$this->seek($Lookup[$i]['Subtable'][$c]['CoverageLookahead'][$b]);
1730
								$glyphs = $this->_getCoverage();
1731
								$Lookup[$i]['Subtable'][$c]['CoverageLookaheadGlyphs'][] = implode("|",$glyphs);
1732
							}
1733
1734
						}
1735
					}
1736
				}
1737
			}
1738
1739
1740
			//=====================================================================================
1741
			//=====================================================================================
1742
			//=====================================================================================
1743
1744
1745
1746
			$st = $this->mpdf->OTLscript;
1747
			$t = $this->mpdf->OTLlang;
1748
			$langsys = $gsub[$st][$t];
1749
1750
1751
			$lul = array();	// array of LookupListIndexes
1752
			$tags = array();	// corresponding array of feature tags e.g. 'ccmp'
1753
			foreach($langsys AS $tag=>$ft) {
1754
				foreach($ft AS $ll) { 
1755
					$lul[$ll] = $tag; 
1756
				}
1757
			}
1758
			ksort($lul);	// Order the Lookups in the order they are in the GUSB table, regardless of Feature order
1759
			$this->_getGSUBarray($Lookup, $lul, $st);
1760
//print_r($lul); exit;
1761
1762
1763
1764
1765
		}
1766
//print_r($Lookup); exit;
1767
1768
		return array($GSUBScriptLang, $gsub, $GSLookup, $rtlPUAstr, $rtlPUAarr);
1769
1770
	}
1771
/////////////////////////////////////////////////////////////////////////////////////////
1772
	// GSUB functions
1773
	function _getGSUBarray(&$Lookup, &$lul, $scripttag, $level=1, $coverage='', $exB='', $exL='') {
1774
			// Process (3) LookupList for specific Script-LangSys
1775
			// Generate preg_replace
1776
			$html = '';
1777
			if ($level==1) { $html .= '<bookmark level="0" content="GSUB features">'; }
1778
			foreach($lul AS $i=>$tag) {
1779
				$html .= '<div class="level'.$level.'">'; 
1780
				$html .= '<h5 class="level'.$level.'">';
1781
				if ($level==1) { $html .= '<bookmark level="1" content="'.$tag.' [#'.$i.']">'; }
1782
				$html .= 'Lookup #'.$i.' [tag: <span style="color:#000066;">'.$tag.'</span>]</h5>'; 
1783
				$ignore = $this->_getGSUBignoreString($Lookup[$i]['Flag'], $Lookup[$i]['MarkFilteringSet']);
1784
				if ($ignore) { $html .= '<div class="ignore">Ignoring: '.$ignore.'</div> '; }
1785
1786
				$Type = $Lookup[$i]['Type'];
1787
				$Flag = $Lookup[$i]['Flag'];
1788
				if (($Flag  & 0x0001) == 1) { $dir = 'RTL'; }
1789
				else { $dir = 'LTR'; }
1790
1791
				for ($c=0;$c<$Lookup[$i]['SubtableCount'] ;$c++) {
1792
					$html .= '<div class="subtable">Subtable #'.$c;
1793
					if ($level==1) { $html .= '<bookmark level="2" content="Subtable #'.$c.'">'; }
1794
					$html .= '</div>'; 
1795
1796
					$SubstFormat= $Lookup[$i]['Subtable'][$c]['Format'] ;
1797
1798
					// LookupType 1: Single Substitution Subtable
1799
					if ($Lookup[$i]['Type'] == 1) {
1800
						$html .= '<div class="lookuptype">LookupType 1: Single Substitution Subtable</div>'; 
1801 View Code Duplication
						for ($s=0;$s<count($Lookup[$i]['Subtable'][$c]['subs']);$s++) {
1802
							$inputGlyphs = $Lookup[$i]['Subtable'][$c]['subs'][$s]['Replace'];
1803
							$substitute = $Lookup[$i]['Subtable'][$c]['subs'][$s]['substitute'][0];
1804
							if ($level==2 && strpos($coverage, $inputGlyphs[0])===false) { continue; }
1805
							$html .= '<div class="substitution">';
1806
							$html .= '<span class="unicode">'.$this->formatUni($inputGlyphs[0]).'&nbsp;</span> ';
1807
							if ($level==2 && $exB) { $html .= $exB; }
1808
							$html .= '<span class="unchanged">&nbsp;'.$this->formatEntity($inputGlyphs[0]).'</span>';
1809
							if ($level==2 && $exL) { $html .= $exL; }
1810
							$html .= '&nbsp; &raquo; &raquo; &nbsp;';
1811
							if ($level==2 && $exB) { $html .= $exB; }
1812
							$html .= '<span class="changed">&nbsp;'.$this->formatEntity($substitute).'</span>';
1813
							if ($level==2 && $exL) { $html .= $exL; }
1814
							$html .= '&nbsp; <span class="unicode">'.$this->formatUni($substitute).'</span> ';
1815
							$html .= '</div>'; 
1816
						}
1817
					}
1818
					// LookupType 2: Multiple Substitution Subtable
1819
					else if ($Lookup[$i]['Type'] == 2) {
1820
						$html .= '<div class="lookuptype">LookupType 2: Multiple Substitution Subtable</div>'; 
1821 View Code Duplication
						for ($s=0;$s<count($Lookup[$i]['Subtable'][$c]['subs']);$s++) {
1822
							$inputGlyphs = $Lookup[$i]['Subtable'][$c]['subs'][$s]['Replace'];
1823
							$substitute = $Lookup[$i]['Subtable'][$c]['subs'][$s]['substitute'];
1824
							if ($level==2 && strpos($coverage, $inputGlyphs[0])===false) { continue; }
1825
							$html .= '<div class="substitution">';
1826
							$html .= '<span class="unicode">'.$this->formatUni($inputGlyphs[0]).'&nbsp;</span> ';
1827
							if ($level==2 && $exB) { $html .= $exB; }
1828
							$html .= '<span class="unchanged">&nbsp;'.$this->formatEntity($inputGlyphs[0]).'</span>';
1829
							if ($level==2 && $exL) { $html .= $exL; }
1830
							$html .= '&nbsp; &raquo; &raquo; &nbsp;';
1831
							if ($level==2 && $exB) { $html .= $exB; }
1832
							$html .= '<span class="changed">&nbsp;'.$this->formatEntityArr($substitute).'</span>';
1833
							if ($level==2 && $exL) { $html .= $exL; }
1834
							$html .= '&nbsp; <span class="unicode">'.$this->formatUniArr($substitute).'</span> ';
1835
							$html .= '</div>'; 
1836
						}
1837
					}
1838
					// LookupType 3: Alternate Forms
1839
					else if ($Lookup[$i]['Type'] == 3) {
1840
						$html .= '<div class="lookuptype">LookupType 3: Alternate Forms</div>'; 
1841
						for ($s=0;$s<count($Lookup[$i]['Subtable'][$c]['subs']);$s++) {
1842
							$inputGlyphs = $Lookup[$i]['Subtable'][$c]['subs'][$s]['Replace'];
1843
							$substitute = $Lookup[$i]['Subtable'][$c]['subs'][$s]['substitute'][0];
1844
							if ($level==2 && strpos($coverage, $inputGlyphs[0])===false) { continue; }
1845
							$html .= '<div class="substitution">';
1846
							$html .= '<span class="unicode">'.$this->formatUni($inputGlyphs[0]).'&nbsp;</span> ';
1847
							if ($level==2 && $exB) { $html .= $exB; }
1848
							$html .= '<span class="unchanged">&nbsp;'.$this->formatEntity($inputGlyphs[0]).'</span>';
1849
							if ($level==2 && $exL) { $html .= $exL; }
1850
							$html .= '&nbsp; &raquo; &raquo; &nbsp;';
1851
							if ($level==2 && $exB) { $html .= $exB; }
1852
							$html .= '<span class="changed">&nbsp;'.$this->formatEntity($substitute).'</span>';
1853
							if ($level==2 && $exL) { $html .= $exL; }
1854
							$html .= '&nbsp; <span class="unicode">'.$this->formatUni($substitute).'</span> ';
1855
							if (count($Lookup[$i]['Subtable'][$c]['subs'][$s]['substitute'])>1) {
1856
								for ($alt=1;$alt<count($Lookup[$i]['Subtable'][$c]['subs'][$s]['substitute']);$alt++) {
1857
									$substitute = $Lookup[$i]['Subtable'][$c]['subs'][$s]['substitute'][$alt];
1858
									$html .= '&nbsp; | &nbsp; ALT #'.$alt.' &nbsp; ';
1859
									$html .= '<span class="changed">&nbsp;'.$this->formatEntity($substitute).'</span>';
1860
									$html .= '&nbsp; <span class="unicode">'.$this->formatUni($substitute).'</span> ';
1861
								}
1862
							}
1863
							$html .= '</div>'; 
1864
						}
1865
					}
1866
					// LookupType 4: Ligature Substitution Subtable
1867
					else if ($Lookup[$i]['Type'] == 4) {
1868
						$html .= '<div class="lookuptype">LookupType 4: Ligature Substitution Subtable</div>'; 
1869 View Code Duplication
						for ($s=0;$s<count($Lookup[$i]['Subtable'][$c]['subs']);$s++) {
1870
							$inputGlyphs = $Lookup[$i]['Subtable'][$c]['subs'][$s]['Replace'];
1871
							$substitute = $Lookup[$i]['Subtable'][$c]['subs'][$s]['substitute'][0];
1872
							if ($level==2 && strpos($coverage, $inputGlyphs[0])===false) { continue; }
1873
							$html .= '<div class="substitution">';
1874
							$html .= '<span class="unicode">'.$this->formatUniArr($inputGlyphs).'&nbsp;</span> ';
1875
							if ($level==2 && $exB) { $html .= $exB; }
1876
							$html .= '<span class="unchanged">&nbsp;'.$this->formatEntityArr($inputGlyphs).'</span>';
1877
							if ($level==2 && $exL) { $html .= $exL; }
1878
							$html .= '&nbsp; &raquo; &raquo; &nbsp;';
1879
							if ($level==2 && $exB) { $html .= $exB; }
1880
							$html .= '<span class="changed">&nbsp;'.$this->formatEntity($substitute).'</span>';
1881
							if ($level==2 && $exL) { $html .= $exL; }
1882
							$html .= '&nbsp; <span class="unicode">'.$this->formatUni($substitute).'</span> ';
1883
							$html .= '</div>'; 
1884
						}
1885
					}
1886
1887
					// LookupType 5: Contextual Substitution Subtable
1888
					else if ($Lookup[$i]['Type'] == 5) {
1889
						$html .= '<div class="lookuptype">LookupType 5: Contextual Substitution Subtable</div>'; 
1890
						// Format 1: Context Substitution
1891
						if ($SubstFormat==1) {	
1892
							$html .= '<div class="lookuptypesub">Format 1: Context Substitution</div>'; 
1893
							for($s=0;$s<$Lookup[$i]['Subtable'][$c]['SubRuleSetCount'];$s++) {
1894
								// SubRuleSet
1895
$subRule = array();
1896
								$html .= '<div class="rule">Subrule Set: '.$s.'</div>'; 
1897
								foreach($Lookup[$i]['Subtable'][$c]['SubRuleSet'][$s]['SubRule'] AS $rctr=>$rule) {
1898
									// SubRule
1899
									$html .= '<div class="rule">SubRule: '.$rctr.'</div>'; 
1900
									$inputGlyphs = array(); 
1901
									if ($rule['GlyphCount']>1) { 
1902
										$inputGlyphs = $rule['InputGlyphs']; 
1903
									}
1904
									$inputGlyphs[0] = $Lookup[$i]['Subtable'][$c]['SubRuleSet'][$s]['FirstGlyph'];
1905
									ksort($inputGlyphs);
1906
									$nInput = count($inputGlyphs);
1907
1908
1909
									$exampleI = array();
1910
									$html .= '<div class="context">CONTEXT: ';
1911 View Code Duplication
									for ($ff=0;$ff<count($inputGlyphs);$ff++) {
1912
										$html .= '<div>Input #'.$ff.': <span class="unchanged">&nbsp;'.$this->formatEntityStr($inputGlyphs[$ff]).'&nbsp;</span></div>';
1913
										$exampleI[] = $this->formatEntityFirst($inputGlyphs[$ff]);
1914
									}
1915
									$html .= '</div>'; 
1916
1917
1918
									for ($b=0;$b<$rule['SubstCount'];$b++) {
1919
										$lup = $rule['SubstLookupRecord'][$b]['LookupListIndex'];
1920
										$seqIndex = $rule['SubstLookupRecord'][$b]['SequenceIndex'];
1921
1922
										// GENERATE exampleI[<seqIndex] .... exampleI[>seqIndex] 
1923
										$exB = '';
1924
										$exL = '';
1925 View Code Duplication
										if ($seqIndex>0) {
1926
											$exB .= '<span class="inputother">';
1927
											for($ip=0;$ip<$seqIndex;$ip++) {
1928
												$exB .=  $this->formatEntity($inputGlyphs[$ip]).'&#x200d;';
1929
											}
1930
											$exB .= '</span>';
1931
										}
1932 View Code Duplication
										if (count($inputGlyphs)>($seqIndex+1)) {
1933
											$exL .= '<span class="inputother">';
1934
											for($ip=$seqIndex+1;$ip<count($inputGlyphs);$ip++) {
1935
												$exL .=  $this->formatEntity($inputGlyphs[$ip]).'&#x200d;';
1936
											}
1937
											$exL .= '</span>';
1938
										}
1939
										$html .= '<div class="sequenceIndex">Substitution Position: '.$seqIndex.'</div>'; 
1940
1941
										$lul2 = array($lup=>$tag);
1942
1943
										// Only apply if the (first) 'Replace' glyph from the 
1944
										// Lookup list is in the [inputGlyphs] at ['SequenceIndex']
1945
										// Pass $inputGlyphs[$seqIndex] e.g. 00636|00645|00656
1946
										// to level 2 and only apply if first Replace glyph is in this list
1947
										$html .= $this->_getGSUBarray($Lookup, $lul2, $scripttag, 2, $inputGlyphs[$seqIndex], $exB, $exL);
1948
									}
1949
1950
1951
									if (count($subRule['rules'])) $volt[] = $subRule;
1952
1953
								}
1954
							}
1955
						}
1956
						// Format 2: Class-based Context Glyph Substitution
1957
						else if ($SubstFormat==2) {	
1958
							$html .= '<div class="lookuptypesub">Format 2: Class-based Context Glyph Substitution</div>'; 
1959
							foreach($Lookup[$i]['Subtable'][$c]['SubClassSet'] AS $inputClass=>$cscs) {
1960
								$html .= '<div class="rule">Input Class: '.$inputClass.'</div>'; 
1961
								for($cscrule=0;$cscrule<$cscs['SubClassRuleCnt'];$cscrule++) {
1962
									$html .= '<div class="rule">Rule: '.$cscrule.'</div>'; 
1963
									$rule = $cscs['SubClassRule'][$cscrule];
1964
1965
									$inputGlyphs = array();
1966
1967
									$inputGlyphs[0] = $Lookup[$i]['Subtable'][$c]['InputClasses'][$inputClass];
1968
1969 View Code Duplication
									if ($rule['InputGlyphCount']>1) {
1970
										//  NB starts at 1 
1971
										for ($gcl=1;$gcl<$rule['InputGlyphCount'];$gcl++) {
1972
											$classindex = $rule['Input'][$gcl];
1973
											$inputGlyphs[$gcl] = $Lookup[$i]['Subtable'][$c]['InputClasses'][$classindex];
1974
										}
1975
									}
1976
1977
									// Class 0 contains all the glyphs NOT in the other classes
1978
									$class0excl = implode('|', $Lookup[$i]['Subtable'][$c]['InputClasses']);
1979
1980
									$exampleI = array();
1981
									$html .= '<div class="context">CONTEXT: ';
1982 View Code Duplication
									for ($ff=0;$ff<count($inputGlyphs);$ff++) {
1983
1984
										if (!$inputGlyphs[$ff]) { 
1985
1986
											$html .= '<div>Input #'.$ff.': <span class="unchanged">&nbsp;[NOT '.$this->formatEntityStr($class0excl).']&nbsp;</span></div>';
1987
											$exampleI[] = '[NOT '.$this->formatEntityFirst($class0excl).']';
1988
										}
1989
										else {
1990
											$html .= '<div>Input #'.$ff.': <span class="unchanged">&nbsp;'.$this->formatEntityStr($inputGlyphs[$ff]).'&nbsp;</span></div>';
1991
											$exampleI[] = $this->formatEntityFirst($inputGlyphs[$ff]);
1992
										}
1993
									}
1994
									$html .= '</div>'; 
1995
1996
1997
									for ($b=0;$b<$rule['SubstCount'];$b++) {
1998
										$lup = $rule['LookupListIndex'][$b];
1999
										$seqIndex = $rule['SequenceIndex'][$b];
2000
2001
										// GENERATE exampleI[<seqIndex] .... exampleI[>seqIndex] 
2002
										$exB = '';
2003
										$exL = '';
2004
2005 View Code Duplication
										if ($seqIndex>0) {
2006
											$exB .= '<span class="inputother">';
2007
											for($ip=0;$ip<$seqIndex;$ip++) {
2008
												if (!$inputGlyphs[$ip]) { 
2009
													$exB .=  '[*]';
2010
												}
2011
												else {
2012
													$exB .=  $this->formatEntityFirst($inputGlyphs[$ip]).'&#x200d;';
2013
												}
2014
											}
2015
											$exB .= '</span>';
2016
										}
2017
2018 View Code Duplication
										if (count($inputGlyphs)>($seqIndex+1)) {
2019
											$exL .= '<span class="inputother">';
2020
											for($ip=$seqIndex+1;$ip<count($inputGlyphs);$ip++) {
2021
												if (!$inputGlyphs[$ip]) { 
2022
													$exL .=  '[*]';
2023
												}
2024
												else {
2025
													$exL .=  $this->formatEntityFirst($inputGlyphs[$ip]).'&#x200d;';
2026
												}
2027
											}
2028
											$exL .= '</span>';
2029
										}
2030
2031
										$html .= '<div class="sequenceIndex">Substitution Position: '.$seqIndex.'</div>'; 
2032
2033
										$lul2 = array($lup=>$tag);
2034
2035
										// Only apply if the (first) 'Replace' glyph from the 
2036
										// Lookup list is in the [inputGlyphs] at ['SequenceIndex']
2037
										// Pass $inputGlyphs[$seqIndex] e.g. 00636|00645|00656
2038
										// to level 2 and only apply if first Replace glyph is in this list
2039
										$html .= $this->_getGSUBarray($Lookup, $lul2, $scripttag, 2, $inputGlyphs[$seqIndex], $exB, $exL);
2040
									}
2041
									if (count($subRule['rules'])) $volt[] = $subRule;
2042
2043
								}
2044
							}
2045
2046
2047
2048
						}
2049
						// Format 3: Coverage-based Context Glyph Substitution  p259
2050
						else if ($SubstFormat==3) {
2051
							$html .= '<div class="lookuptypesub">Format 3: Coverage-based Context Glyph Substitution  </div>'; 
2052
							// IgnoreMarks flag set on main Lookup table
2053
							$inputGlyphs = $Lookup[$i]['Subtable'][$c]['CoverageInputGlyphs'];
2054
							$CoverageInputGlyphs = implode('|', $inputGlyphs);
2055
							$nInput = $Lookup[$i]['Subtable'][$c]['InputGlyphCount'];
2056
2057
							$exampleI = array();
2058
							$html .= '<div class="context">CONTEXT: ';
2059 View Code Duplication
							for ($ff=0;$ff<count($inputGlyphs);$ff++) {
2060
								$html .= '<div>Input #'.$ff.': <span class="unchanged">&nbsp;'.$this->formatEntityStr($inputGlyphs[$ff]).'&nbsp;</span></div>';
2061
								$exampleI[] = $this->formatEntityFirst($inputGlyphs[$ff]);
2062
							}
2063
							$html .= '</div>'; 
2064
2065
2066
							for ($b=0;$b<$Lookup[$i]['Subtable'][$c]['SubstCount'];$b++) {
2067
								$lup = $Lookup[$i]['Subtable'][$c]['SubstLookupRecord'][$b]['LookupListIndex'];
2068
								$seqIndex = $Lookup[$i]['Subtable'][$c]['SubstLookupRecord'][$b]['SequenceIndex'];
2069
										// GENERATE exampleI[<seqIndex] .... exampleI[>seqIndex] 
2070
										$exB = '';
2071
										$exL = '';
2072 View Code Duplication
										if ($seqIndex>0) {
2073
											$exB .= '<span class="inputother">';
2074
											for($ip=0;$ip<$seqIndex;$ip++) {
2075
												$exB .=  $exampleI[$ip].'&#x200d;';
2076
											}
2077
											$exB .= '</span>';
2078
										}
2079
2080 View Code Duplication
										if (count($inputGlyphs)>($seqIndex+1)) {
2081
											$exL .= '<span class="inputother">';
2082
											for($ip=$seqIndex+1;$ip<count($inputGlyphs);$ip++) {
2083
												$exL .=  $exampleI[$ip].'&#x200d;';
2084
											}
2085
											$exL .= '</span>';
2086
										}
2087
2088
										$html .= '<div class="sequenceIndex">Substitution Position: '.$seqIndex.'</div>'; 
2089
2090
										$lul2 = array($lup=>$tag);
2091
2092
										// Only apply if the (first) 'Replace' glyph from the 
2093
										// Lookup list is in the [inputGlyphs] at ['SequenceIndex']
2094
										// Pass $inputGlyphs[$seqIndex] e.g. 00636|00645|00656
2095
										// to level 2 and only apply if first Replace glyph is in this list
2096
										$html .= $this->_getGSUBarray($Lookup, $lul2, $scripttag, 2, $inputGlyphs[$seqIndex], $exB, $exL);
2097
							}
2098
							if (count($subRule['rules'])) $volt[] = $subRule;
2099
						}
2100
2101
//print_r($Lookup[$i]);
2102
//print_r($volt[(count($volt)-1)]); exit;
2103
					}
2104
					// LookupType 6: Chaining Contextual Substitution Subtable
2105
					else if ($Lookup[$i]['Type'] == 6) {
2106
						$html .= '<div class="lookuptype">LookupType 6: Chaining Contextual Substitution Subtable</div>'; 
2107
						// Format 1: Simple Chaining Context Glyph Substitution  p255
2108
						if ($SubstFormat==1) {	
2109
							$html .= '<div class="lookuptypesub">Format 1: Simple Chaining Context Glyph Substitution  </div>'; 
2110
							for($s=0;$s<$Lookup[$i]['Subtable'][$c]['ChainSubRuleSetCount'];$s++) {
2111
								// ChainSubRuleSet
2112
$subRule = array();
2113
								$html .= '<div class="rule">Subrule Set: '.$s.'</div>'; 
2114
								$firstInputGlyph = $Lookup[$i]['Subtable'][$c]['CoverageGlyphs'][$s];	// First input gyyph
2115
								foreach($Lookup[$i]['Subtable'][$c]['ChainSubRuleSet'][$s]['ChainSubRule'] AS $rctr=>$rule) {
2116
									$html .= '<div class="rule">SubRule: '.$rctr.'</div>'; 
2117
									// ChainSubRule
2118
									$inputGlyphs = array(); 
2119
									if ($rule['InputGlyphCount']>1) { 
2120
										$inputGlyphs = $rule['InputGlyphs']; 
2121
									}
2122
									$inputGlyphs[0] = $firstInputGlyph;
2123
									ksort($inputGlyphs);
2124
									$nInput = count($inputGlyphs);
2125
2126
									if ($rule['BacktrackGlyphCount']) { $backtrackGlyphs = $rule['BacktrackGlyphs']; }
2127
									else { $backtrackGlyphs = array(); }
2128
2129
									if ($rule['LookaheadGlyphCount']) { $lookaheadGlyphs = $rule['LookaheadGlyphs']; }
2130
									else { $lookaheadGlyphs = array(); }
2131
2132
2133
									$exampleB = array();
2134
									$exampleI = array();
2135
									$exampleL = array();
2136
									$html .= '<div class="context">CONTEXT: ';
2137 View Code Duplication
									for ($ff=count($backtrackGlyphs)-1;$ff>=0;$ff--) {
2138
										$html .= '<div>Backtrack #'.$ff.': <span class="unicode">'.$this->formatUniStr($backtrackGlyphs[$ff]).'</span></div>';
2139
										$exampleB[] = $this->formatEntityFirst($backtrackGlyphs[$ff]);
2140
									}
2141 View Code Duplication
									for ($ff=0;$ff<count($inputGlyphs);$ff++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
2142
										$html .= '<div>Input #'.$ff.': <span class="unchanged">&nbsp;'.$this->formatEntityStr($inputGlyphs[$ff]).'&nbsp;</span></div>';
2143
										$exampleI[] = $this->formatEntityFirst($inputGlyphs[$ff]);
2144
									}
2145 View Code Duplication
									for ($ff=0;$ff<count($lookaheadGlyphs);$ff++) {
2146
										$html .= '<div>Lookahead #'.$ff.': <span class="unicode">'.$this->formatUniStr($lookaheadGlyphs[$ff]).'</span></div>';
2147
										$exampleL[] = $this->formatEntityFirst($lookaheadGlyphs[$ff]);
2148
									}
2149
									$html .= '</div>'; 
2150
2151
2152
									for ($b=0;$b<$rule['SubstCount'];$b++) {
2153
										$lup = $rule['LookupListIndex'][$b];
2154
										$seqIndex = $rule['SequenceIndex'][$b];
2155
2156
										// GENERATE exampleB[n] exampleI[<seqIndex] .... exampleI[>seqIndex] exampleL[n]
2157
										$exB = '';
2158
										$exL = '';
2159
										if (count($exampleB)) { $exB .= '<span class="backtrack">'.implode('&#x200d;',$exampleB).'</span>'; }
2160
2161 View Code Duplication
										if ($seqIndex>0) {
2162
											$exB .= '<span class="inputother">';
2163
											for($ip=0;$ip<$seqIndex;$ip++) {
2164
												$exB .=  $this->formatEntity($inputGlyphs[$ip]).'&#x200d;';
2165
											}
2166
											$exB .= '</span>';
2167
										}
2168
2169 View Code Duplication
										if (count($inputGlyphs)>($seqIndex+1)) {
2170
											$exL .= '<span class="inputother">';
2171
											for($ip=$seqIndex+1;$ip<count($inputGlyphs);$ip++) {
2172
												$exL .=  $this->formatEntity($inputGlyphs[$ip]).'&#x200d;';
2173
											}
2174
											$exL .= '</span>';
2175
										}
2176
2177
										if (count($exampleL)) { $exL .= '<span class="lookahead">'.implode('&#x200d;',$exampleL).'</span>'; }
2178
2179
										$html .= '<div class="sequenceIndex">Substitution Position: '.$seqIndex.'</div>'; 
2180
2181
										$lul2 = array($lup=>$tag);
2182
2183
										// Only apply if the (first) 'Replace' glyph from the 
2184
										// Lookup list is in the [inputGlyphs] at ['SequenceIndex']
2185
										// Pass $inputGlyphs[$seqIndex] e.g. 00636|00645|00656
2186
										// to level 2 and only apply if first Replace glyph is in this list
2187
										$html .= $this->_getGSUBarray($Lookup, $lul2, $scripttag, 2, $inputGlyphs[$seqIndex], $exB, $exL);
2188
									}
2189
2190
2191
									if (count($subRule['rules'])) $volt[] = $subRule;
2192
2193
2194
2195
								}
2196
							}
2197
2198
						}
2199
						// Format 2: Class-based Chaining Context Glyph Substitution  p257
2200
						else if ($SubstFormat==2) {
2201
							$html .= '<div class="lookuptypesub">Format 2: Class-based Chaining Context Glyph Substitution  </div>'; 
2202
							foreach($Lookup[$i]['Subtable'][$c]['ChainSubClassSet'] AS $inputClass=>$cscs) {
2203
								$html .= '<div class="rule">Input Class: '.$inputClass.'</div>'; 
2204
								for($cscrule=0;$cscrule<$cscs['ChainSubClassRuleCnt'];$cscrule++) {
2205
									$html .= '<div class="rule">Rule: '.$cscrule.'</div>'; 
2206
									$rule = $cscs['ChainSubClassRule'][$cscrule];
2207
2208
									// These contain classes of glyphs as strings
2209
									// $Lookup[$i]['Subtable'][$c]['InputClasses'][(class)] e.g. 02E6|02E7|02E8
2210
									// $Lookup[$i]['Subtable'][$c]['LookaheadClasses'][(class)]
2211
									// $Lookup[$i]['Subtable'][$c]['BacktrackClasses'][(class)]
2212
2213
									// These contain arrays of classIndexes
2214
									// [Backtrack] [Lookahead] and [Input] (Input is from the second position only)
2215
2216
									$inputGlyphs = array();
2217
2218
									$inputGlyphs[0] = $Lookup[$i]['Subtable'][$c]['InputClasses'][$inputClass];
2219 View Code Duplication
									if ($rule['InputGlyphCount']>1) {
2220
										//  NB starts at 1 
2221
										for ($gcl=1;$gcl<$rule['InputGlyphCount'];$gcl++) {
2222
											$classindex = $rule['Input'][$gcl];
2223
											$inputGlyphs[$gcl] = $Lookup[$i]['Subtable'][$c]['InputClasses'][$classindex];
2224
										}
2225
									}
2226
									// Class 0 contains all the glyphs NOT in the other classes
2227
									$class0excl = implode('|', $Lookup[$i]['Subtable'][$c]['InputClasses']);
2228
2229
									$nInput = $rule['InputGlyphCount'];
2230
2231 View Code Duplication
									if ($rule['BacktrackGlyphCount']) {
2232
										for ($gcl=0;$gcl<$rule['BacktrackGlyphCount'];$gcl++) {
2233
											$classindex = $rule['Backtrack'][$gcl];
2234
											$backtrackGlyphs[$gcl] = $Lookup[$i]['Subtable'][$c]['BacktrackClasses'][$classindex];
2235
										}
2236
									}
2237
									else { $backtrackGlyphs = array(); }
2238
2239 View Code Duplication
									if ($rule['LookaheadGlyphCount']) {
2240
										for ($gcl=0;$gcl<$rule['LookaheadGlyphCount'];$gcl++) {
2241
											$classindex = $rule['Lookahead'][$gcl];
2242
											$lookaheadGlyphs[$gcl] = $Lookup[$i]['Subtable'][$c]['LookaheadClasses'][$classindex];
2243
										}
2244
									}
2245
									else { $lookaheadGlyphs = array(); }
2246
2247
2248
									$exampleB = array();
2249
									$exampleI = array();
2250
									$exampleL = array();
2251
									$html .= '<div class="context">CONTEXT: ';
2252 View Code Duplication
									for ($ff=count($backtrackGlyphs)-1;$ff>=0;$ff--) {
2253
										if (!$backtrackGlyphs[$ff]) { 
2254
											$html .= '<div>Backtrack #'.$ff.': <span class="unchanged">&nbsp;[NOT '.$this->formatEntityStr($class0excl).']&nbsp;</span></div>';
2255
											$exampleB[] = '[NOT '.$this->formatEntityFirst($class0excl).']';
2256
2257
										}
2258
										else {
2259
											$html .= '<div>Backtrack #'.$ff.': <span class="unicode">'.$this->formatUniStr($backtrackGlyphs[$ff]).'</span></div>';
2260
											$exampleB[] = $this->formatEntityFirst($backtrackGlyphs[$ff]);
2261
										}
2262
									}
2263 View Code Duplication
									for ($ff=0;$ff<count($inputGlyphs);$ff++) {
2264
										if (!$inputGlyphs[$ff]) { 
2265
											$html .= '<div>Input #'.$ff.': <span class="unchanged">&nbsp;[NOT '.$this->formatEntityStr($class0excl).']&nbsp;</span></div>';
2266
											$exampleI[] = '[NOT '.$this->formatEntityFirst($class0excl).']';
2267
										}
2268
										else {
2269
											$html .= '<div>Input #'.$ff.': <span class="unchanged">&nbsp;'.$this->formatEntityStr($inputGlyphs[$ff]).'&nbsp;</span></div>';
2270
											$exampleI[] = $this->formatEntityFirst($inputGlyphs[$ff]);
2271
										}
2272
									}
2273 View Code Duplication
									for ($ff=0;$ff<count($lookaheadGlyphs);$ff++) {
2274
										if (!$lookaheadGlyphs[$ff]) { 
2275
											$html .= '<div>Lookahead #'.$ff.': <span class="unchanged">&nbsp;[NOT '.$this->formatEntityStr($class0excl).']&nbsp;</span></div>';
2276
											$exampleL[] = '[NOT '.$this->formatEntityFirst($class0excl).']';
2277
2278
										}
2279
										else {
2280
											$html .= '<div>Lookahead #'.$ff.': <span class="unicode">'.$this->formatUniStr($lookaheadGlyphs[$ff]).'</span></div>';
2281
											$exampleL[] = $this->formatEntityFirst($lookaheadGlyphs[$ff]);
2282
										}
2283
									}
2284
									$html .= '</div>'; 
2285
2286
2287
									for ($b=0;$b<$rule['SubstCount'];$b++) {
2288
										$lup = $rule['LookupListIndex'][$b];
2289
										$seqIndex = $rule['SequenceIndex'][$b];
2290
2291
										// GENERATE exampleB[n] exampleI[<seqIndex] .... exampleI[>seqIndex] exampleL[n]
2292
										$exB = '';
2293
										$exL = '';
2294
										if (count($exampleB)) { $exB .= '<span class="backtrack">'.implode('&#x200d;',$exampleB).'</span>'; }
2295
2296 View Code Duplication
										if ($seqIndex>0) {
2297
											$exB .= '<span class="inputother">';
2298
											for($ip=0;$ip<$seqIndex;$ip++) {
2299
												if (!$inputGlyphs[$ip]) { 
2300
													$exB .=  '[*]';
2301
												}
2302
												else {
2303
													$exB .=  $this->formatEntityFirst($inputGlyphs[$ip]).'&#x200d;';
2304
												}
2305
											}
2306
											$exB .= '</span>';
2307
										}
2308
2309 View Code Duplication
										if (count($inputGlyphs)>($seqIndex+1)) {
2310
											$exL .= '<span class="inputother">';
2311
											for($ip=$seqIndex+1;$ip<count($inputGlyphs);$ip++) {
2312
												if (!$inputGlyphs[$ip]) { 
2313
													$exL .=  '[*]';
2314
												}
2315
												else {
2316
													$exL .=  $this->formatEntityFirst($inputGlyphs[$ip]).'&#x200d;';
2317
												}
2318
											}
2319
											$exL .= '</span>';
2320
										}
2321
2322
										if (count($exampleL)) { $exL .= '<span class="lookahead">'.implode('&#x200d;',$exampleL).'</span>'; }
2323
2324
										$html .= '<div class="sequenceIndex">Substitution Position: '.$seqIndex.'</div>'; 
2325
2326
										$lul2 = array($lup=>$tag);
2327
2328
										// Only apply if the (first) 'Replace' glyph from the 
2329
										// Lookup list is in the [inputGlyphs] at ['SequenceIndex']
2330
										// Pass $inputGlyphs[$seqIndex] e.g. 00636|00645|00656
2331
										// to level 2 and only apply if first Replace glyph is in this list
2332
										$html .= $this->_getGSUBarray($Lookup, $lul2, $scripttag, 2, $inputGlyphs[$seqIndex], $exB, $exL);
2333
2334
									}
2335
2336
								}
2337
							}
2338
2339
2340
//print_r($Lookup[$i]['Subtable'][$c]); exit;	
2341
2342
						}
2343
						// Format 3: Coverage-based Chaining Context Glyph Substitution  p259
2344
						else if ($SubstFormat==3) {
2345
							$html .= '<div class="lookuptypesub">Format 3: Coverage-based Chaining Context Glyph Substitution  </div>'; 
2346
							// IgnoreMarks flag set on main Lookup table
2347
							$inputGlyphs = $Lookup[$i]['Subtable'][$c]['CoverageInputGlyphs'];
2348
							$CoverageInputGlyphs = implode('|', $inputGlyphs);
2349
							$nInput = $Lookup[$i]['Subtable'][$c]['InputGlyphCount'];
2350
2351
							if ($Lookup[$i]['Subtable'][$c]['BacktrackGlyphCount']) {
2352
								$backtrackGlyphs = $Lookup[$i]['Subtable'][$c]['CoverageBacktrackGlyphs'];
2353
							}
2354
							else { $backtrackGlyphs = array(); }
2355
2356
							if ($Lookup[$i]['Subtable'][$c]['LookaheadGlyphCount']) {
2357
								$lookaheadGlyphs = $Lookup[$i]['Subtable'][$c]['CoverageLookaheadGlyphs'];
2358
							}
2359
							else { $lookaheadGlyphs = array(); }
2360
2361
2362
							$exampleB = array();
2363
							$exampleI = array();
2364
							$exampleL = array();
2365
							$html .= '<div class="context">CONTEXT: ';
2366 View Code Duplication
							for ($ff=count($backtrackGlyphs)-1;$ff>=0;$ff--) {
2367
								$html .= '<div>Backtrack #'.$ff.': <span class="unicode">'.$this->formatUniStr($backtrackGlyphs[$ff]).'</span></div>';
2368
								$exampleB[] = $this->formatEntityFirst($backtrackGlyphs[$ff]);
2369
							}
2370 View Code Duplication
							for ($ff=0;$ff<count($inputGlyphs);$ff++) {
2371
								$html .= '<div>Input #'.$ff.': <span class="unchanged">&nbsp;'.$this->formatEntityStr($inputGlyphs[$ff]).'&nbsp;</span></div>';
2372
								$exampleI[] = $this->formatEntityFirst($inputGlyphs[$ff]);
2373
							}
2374 View Code Duplication
							for ($ff=0;$ff<count($lookaheadGlyphs);$ff++) {
2375
								$html .= '<div>Lookahead #'.$ff.': <span class="unicode">'.$this->formatUniStr($lookaheadGlyphs[$ff]).'</span></div>';
2376
								$exampleL[] = $this->formatEntityFirst($lookaheadGlyphs[$ff]);
2377
							}
2378
							$html .= '</div>'; 
2379
2380
2381
							for ($b=0;$b<$Lookup[$i]['Subtable'][$c]['SubstCount'];$b++) {
2382
								$lup = $Lookup[$i]['Subtable'][$c]['SubstLookupRecord'][$b]['LookupListIndex'];
2383
								$seqIndex = $Lookup[$i]['Subtable'][$c]['SubstLookupRecord'][$b]['SequenceIndex'];
2384
2385
2386
										// GENERATE exampleB[n] exampleI[<seqIndex] .... exampleI[>seqIndex] exampleL[n]
2387
										$exB = '';
2388
										$exL = '';
2389
										if (count($exampleB)) { $exB .= '<span class="backtrack">'.implode('&#x200d;',$exampleB).'</span>'; }
2390
2391 View Code Duplication
										if ($seqIndex>0) {
2392
											$exB .= '<span class="inputother">';
2393
											for($ip=0;$ip<$seqIndex;$ip++) {
2394
												$exB .=  $exampleI[$ip].'&#x200d;';
2395
											}
2396
											$exB .= '</span>';
2397
										}
2398
2399 View Code Duplication
										if (count($inputGlyphs)>($seqIndex+1)) {
2400
											$exL .= '<span class="inputother">';
2401
											for($ip=$seqIndex+1;$ip<count($inputGlyphs);$ip++) {
2402
												$exL .=  $exampleI[$ip].'&#x200d;';
2403
											}
2404
											$exL .= '</span>';
2405
										}
2406
2407
										if (count($exampleL)) { $exL .= '<span class="lookahead">'.implode('&#x200d;',$exampleL).'</span>'; }
2408
2409
										$html .= '<div class="sequenceIndex">Substitution Position: '.$seqIndex.'</div>'; 
2410
2411
										$lul2 = array($lup=>$tag);
2412
2413
										// Only apply if the (first) 'Replace' glyph from the 
2414
										// Lookup list is in the [inputGlyphs] at ['SequenceIndex']
2415
										// Pass $inputGlyphs[$seqIndex] e.g. 00636|00645|00656
2416
										// to level 2 and only apply if first Replace glyph is in this list
2417
										$html .= $this->_getGSUBarray($Lookup, $lul2, $scripttag, 2, $inputGlyphs[$seqIndex], $exB, $exL);
2418
2419
							}
2420
						}
2421
					}
2422
				}
2423
				$html .= '</div>'; 
2424
			}
2425
			if ($level ==1) { $this->mpdf->WriteHTML($html); }
2426
			else  { return $html; }
2427
//print_r($Lookup); exit;
2428
	}
2429
	//=====================================================================================
2430
	//=====================================================================================
2431
	// mPDF 5.7.1
2432 View Code Duplication
	function _checkGSUBignore($flag, $glyph, $MarkFilteringSet) {
2433
		$ignore = false;
2434
		// Flag & 0x0008 = Ignore Marks
2435
		if ((($flag & 0x0008) == 0x0008) && strpos($this->GlyphClassMarks,$glyph)) { $ignore = true; }
2436
		if ((($flag & 0x0004) == 0x0004) && strpos($this->GlyphClassLigatures,$glyph)) { $ignore = true; }
2437
		if ((($flag & 0x0002) == 0x0002) && strpos($this->GlyphClassBases,$glyph)) { $ignore = true; }
2438
		// Flag & 0xFF?? = MarkAttachmentType
2439
		if (($flag & 0xFF00) && strpos($this->MarkAttachmentType[($flag >> 8)],$glyph)) { $ignore = true; }
2440
		// Flag & 0x0010 = UseMarkFilteringSet
2441
		if (($flag & 0x0010) && strpos($this->MarkGlyphSets[$MarkFilteringSet],$glyph)) { $ignore = true; }
2442
		return $ignore;
2443
	}
2444
2445
	function _getGSUBignoreString($flag, $MarkFilteringSet) {
2446
		// If ignoreFlag set, combine all ignore glyphs into -> "((?:(?: FBA1| FBA2| FBA3))*)"
2447
		// else "()"
2448
		// for Input - set on secondary Lookup table if in Context, and set Backtrack and Lookahead on Context Lookup
2449
		$str = "";
2450
		$ignoreflag = 0;
2451
2452
		// Flag & 0xFF?? = MarkAttachmentType
2453
		if ($flag & 0xFF00) {
2454
			$MarkAttachmentType = $flag >> 8;
2455
			$ignoreflag = $flag; 
2456
			//$str = $this->MarkAttachmentType[$MarkAttachmentType]; 
2457
			$str = "MarkAttachmentType[".$MarkAttachmentType."] "; 
2458
		}
2459
2460
		// Flag & 0x0010 = UseMarkFilteringSet
2461 View Code Duplication
		if ($flag & 0x0010) {
2462
			die("This font ".$this->fontkey." contains MarkGlyphSets"); 
2463
			$str = "Mark Glyph Set: "; 
2464
			$str .= $this->MarkGlyphSets[$MarkFilteringSet]; 
2465
		}
2466
2467
		// If Ignore Marks set, supercedes any above
2468
		// Flag & 0x0008 = Ignore Marks
2469
		if (($flag & 0x0008) == 0x0008) { 
2470
			$ignoreflag = 8; 
2471
			//$str = $this->GlyphClassMarks; 
2472
			$str = "Mark Glyphs "; 
2473
		}
2474
2475
		// Flag & 0x0004 = Ignore Ligatures  
2476 View Code Duplication
		if (($flag & 0x0004) == 0x0004) {
2477
			$ignoreflag += 4; 
2478
			if ($str) { $str .= "|"; }
2479
			//$str .= $this->GlyphClassLigatures; 
2480
			$str .= "Ligature Glyphs "; 
2481
		}
2482
		// Flag & 0x0002 = Ignore BaseGlyphs  
2483 View Code Duplication
		if (($flag & 0x0002) == 0x0002) {
2484
			$ignoreflag += 2; 
2485
			if ($str) { $str .= "|"; }
2486
			//$str .= $this->GlyphClassBases; 
2487
			$str .= "Base Glyphs "; 
2488
		}
2489
		if ($str) { 
2490
			return $str;
2491
		}
2492
		else return "";
2493
	}
2494
2495
	// GSUB Patterns
2496
2497
/*
2498
       BACKTRACK                        INPUT                   LOOKAHEAD
2499
==================================  ==================  ==================================
2500
(FEEB|FEEC)(ign) �(FD12|FD13)(ign) �(0612)�(ign) (0613)�(ign) (FD12|FD13)�(ign) (FEEB|FEEC)
2501
----------------  ----------------  -----  ------------  ---------------   ---------------
2502
  Backtrack 1       Backtrack 2     Input 1   Input 2       Lookahead 1      Lookahead 2
2503
--------   ---    ---------  ---    ----   ---   ----   ---   ---------   ---    -------
2504
    \${1}  \${2}     \${3}   \${4}                      \${5+}  \${6+}    \${7+}  \${8+}
2505
2506
          nBacktrack = 2               nInput = 2                 nLookahead = 2
2507
2508
        nBsubs = 2xnBack          nIsubs = (nBsubs+)    nLsubs = (nBsubs+nIsubs+) 2xnLookahead
2509
        "\${1}\${2} "                 (nInput*2)-1               "\${5+} \${6+}"
2510
                                        "REPL"
2511
2512
�\${1}\${2} �\${3}\${4} �REPL�\${5+} \${6+}�\${7+} \${8+}�
2513
2514
2515
                      INPUT nInput = 5
2516
============================================================  
2517
�(0612)�(ign) (0613)�(ign) (0614)�(ign) (0615)�(ign) (0615)�
2518
\${1}  \${2}  \${3}  \${4} \${5} \${6}  \${7} \${8}  \${9} (All backreference numbers are + nBsubs)
2519
-----  ------------ ------------ ------------ ------------
2520
Input 1   Input 2      Input 3      Input 4      Input 5
2521
2522
A======  SequenceIndex=1 ; Lookup match nGlyphs=1
2523
B===================  SequenceIndex=1 ; Lookup match nGlyphs=2
2524
C===============================  SequenceIndex=1 ; Lookup match nGlyphs=3
2525
        D=======================  SequenceIndex=2 ; Lookup match nGlyphs=2
2526
        E=====================================  SequenceIndex=2 ; Lookup match nGlyphs=3
2527
                                   F======================  SequenceIndex=4 ; Lookup match nGlyphs=2
2528
2529
All backreference numbers are + nBsubs
2530
A - "REPL\${2} \${3}\${4} \${5}\${6} \${7}\${8} \${9}"
2531
B - "REPL\${2}\${4} \${5}\${6} \${7}\${8} \${9}"
2532
C - "REPL\${2}\${4}\${6} \${7}\${8} \${9}"
2533
D - "\${1} REPL\${2}\${4}\${6} \${7}\${8} \${9}"
2534
E - "\${1} REPL\${2}\${4}\${6}\${8} \${9}"
2535
F - "\${1}\${2} \${3}\${4} \${5} REPL\${6}\${8}"
2536
*/
2537
2538 View Code Duplication
	function _makeGSUBcontextInputMatch($inputGlyphs, $ignore, $lookupGlyphs, $seqIndex) {
2539
		// $ignore = "((?:(?: FBA1| FBA2| FBA3))*)" or "()"
2540
		// Returns e.g. �(0612)�(ignore) (0613)�(ignore) (0614)�
2541
		// $inputGlyphs = array of glyphs(glyphstrings) making up Input sequence in Context
2542
		// $lookupGlyphs = array of glyphs (single Glyphs) making up Lookup Input sequence
2543
		$mLen = count($lookupGlyphs);		// nGlyphs in the secondary Lookup match 
2544
		$nInput = count($inputGlyphs);	// nGlyphs in the Primary Input sequence
2545
		$str = "";
2546
		for($i=0;$i<$nInput;$i++) {
2547
			if ($i>0) { $str .= $ignore." "; }
2548
			if ($i>=$seqIndex && $i<($seqIndex+$mLen)) { $str .= "".$lookupGlyphs[($i-$seqIndex)].""; }
2549
			else { $str .= "".$inputGlyphs[($i)].""; }
2550
		}
2551
		return $str;
2552
	}
2553
2554 View Code Duplication
	function _makeGSUBinputMatch($inputGlyphs, $ignore) {
2555
		// $ignore = "((?:(?: FBA1| FBA2| FBA3))*)" or "()"
2556
		// Returns e.g. �(0612)�(ignore) (0613)�(ignore) (0614)�
2557
		// $inputGlyphs = array of glyphs(glyphstrings) making up Input sequence in Context
2558
		// $lookupGlyphs = array of glyphs making up Lookup Input sequence - if applicable
2559
		$str = "";
2560
		for($i=1;$i<=count($inputGlyphs);$i++) {
2561
			if ($i>1) { $str .= $ignore." "; }
2562
			$str .= "".$inputGlyphs[($i-1)]."";
2563
		}
2564
		return $str;
2565
	}
2566
2567 View Code Duplication
	function _makeGSUBbacktrackMatch($backtrackGlyphs, $ignore) {
2568
		// $ignore = "((?:(?: FBA1| FBA2| FBA3))*)" or "()"
2569
		// Returns e.g. �(FEEB|FEEC)(ignore) �(FD12|FD13)(ignore) �
2570
		// $backtrackGlyphs = array of glyphstrings making up Backtrack sequence
2571
		// 3  2  1  0
2572
		// each item being e.g. E0AD|E0AF|F1FD
2573
		$str = "";
2574
		for($i=(count($backtrackGlyphs)-1);$i>=0;$i--) {
2575
			$str .= "".$backtrackGlyphs[$i]." ".$ignore." ";
2576
		}
2577
		return $str;
2578
	}
2579
2580 View Code Duplication
	function _makeGSUBlookaheadMatch($lookaheadGlyphs, $ignore) {
2581
		// $ignore = "((?:(?: FBA1| FBA2| FBA3))*)" or "()"
2582
		// Returns e.g. �(ignore) (FD12|FD13)�(ignore) (FEEB|FEEC)�
2583
		// $lookaheadGlyphs = array of glyphstrings making up Lookahead sequence
2584
		// 0  1  2  3
2585
		// each item being e.g. E0AD|E0AF|F1FD
2586
		$str = "";
2587
		for($i=0;$i<count($lookaheadGlyphs);$i++) {
2588
			$str .= $ignore." ".$lookaheadGlyphs[$i]."";
2589
		}
2590
		return $str;
2591
	}
2592
2593
2594
2595 View Code Duplication
	function _makeGSUBinputReplacement($nInput, $REPL, $ignore, $nBsubs, $mLen, $seqIndex) {
2596
		// Returns e.g. "REPL\${6}\${8}" or "\${1}\${2} \${3} REPL\${4}\${6}\${8} \${9}"
2597
		// $nInput	nGlyphs in the Primary Input sequence
2598
		// $REPL 	replacement glyphs from secondary lookup
2599
		// $ignore = "((?:(?: FBA1| FBA2| FBA3))*)" or "()"
2600
		// $nBsubs	Number of Backtrack substitutions (= 2x Number of Backtrack glyphs)
2601
		// $mLen 	nGlyphs in the secondary Lookup match - if no secondary lookup, should=$nInput
2602
		// $seqIndex	Sequence Index to apply the secondary match
2603
		if ($ignore=="()") { $ign = false; }
2604
		else { $ign = true; }
2605
		$str = "";
2606
		if ($nInput == 1) { $str = $REPL; }
2607
		else if ($nInput>1) {
2608
			if ($mLen==$nInput) {	// whole string replaced
2609
				$str = $REPL; 
2610
				if ($ign) {
2611
					// for every nInput over 1, add another replacement backreference, to move IGNORES after replacement
2612
					for($x=2;$x<=$nInput;$x++) {
2613
						$str .= '\\'.($nBsubs+(2*($x-1)));
2614
					}
2615
				}
2616
			}
2617
			else {	// if only part of string replaced:
2618
				for($x=1;$x<($seqIndex+1);$x++) {
2619
				      if ($x==1) { $str .= '\\'.($nBsubs + 1); }
2620
				      else { 
2621
						if ($ign) { $str .= '\\'.($nBsubs+(2*($x-1))); }
2622
						$str .= ' \\'.($nBsubs+1+(2*($x-1))); 
2623
					}
2624
				}
2625
				if ($seqIndex>0) { $str .= " "; }
2626
				$str .= $REPL;
2627
				if ($ign) {
2628
					for($x=(max(($seqIndex+1),2));$x<($seqIndex+1+$mLen);$x++) {	//  move IGNORES after replacement
2629
					      $str .= '\\'.($nBsubs+(2*($x-1)));
2630
					}
2631
				}
2632
				for($x=($seqIndex+1+$mLen);$x<=$nInput;$x++) {
2633
				      if ($ign) { $str .= '\\'.($nBsubs+(2*($x-1))); }
2634
				      $str .= ' \\'.($nBsubs+1+(2*($x-1)));
2635
				}
2636
			}
2637
		}
2638
		return $str;
2639
	}
2640
2641
2642
2643
	//////////////////////////////////////////////////////////////////////////////////
2644
	function _getCoverage($convert2hex=true) {
2645
		$g = array();
2646
		$CoverageFormat= $this->read_ushort();
2647
		if ($CoverageFormat == 1) {
2648
			$CoverageGlyphCount= $this->read_ushort();
2649 View Code Duplication
			for ($gid=0;$gid<$CoverageGlyphCount;$gid++) {
2650
				$glyphID = $this->read_ushort();
2651
				if ($convert2hex) { $g[] = unicode_hex($this->glyphToChar[$glyphID][0]); }
2652
				else { $g[] = $glyphID; }
2653
			}
2654
		}						
2655
		if ($CoverageFormat == 2) {
2656
			$RangeCount= $this->read_ushort();
2657
			for ($r=0;$r<$RangeCount;$r++) {
2658
				$start = $this->read_ushort();
2659
				$end = $this->read_ushort();
2660
				$StartCoverageIndex = $this->read_ushort(); // n/a
2661
				for ($gid=$start;$gid<=$end;$gid++) {
2662
					$glyphID = $gid;
2663
					if ($convert2hex) { $g[] = unicode_hex($this->glyphToChar[$glyphID][0]); }
2664
					else { $g[] = $glyphID; }
2665
				}
2666
			}
2667
		}
2668
		return $g;						
2669
	}
2670
2671
	//////////////////////////////////////////////////////////////////////////////////
2672 View Code Duplication
	function _getClasses($offset) {
2673
		$this->seek($offset);
2674
		$ClassFormat = $this->read_ushort();
2675
		$GlyphByClass = array();
2676
		if ($ClassFormat == 1) {
2677
			$StartGlyph = $this->read_ushort();
2678
			$GlyphCount = $this->read_ushort();
2679
			for ($i=0;$i<$GlyphCount;$i++) {
2680
				$startGlyphID = $StartGlyph + $i;
2681
				$endGlyphID = $StartGlyph + $i;
2682
				$class = $this->read_ushort();
2683
				for($g=$startGlyphID;$g<=$endGlyphID;$g++) {
2684
					if ($this->glyphToChar[$g][0]) {
2685
						$GlyphByClass[$class][] = unicode_hex($this->glyphToChar[$g][0]);
2686
					}
2687
				}
2688
			}
2689
		}
2690
		else if ($ClassFormat == 2) {
2691
			$tableCount = $this->read_ushort();
2692
			for ($i=0;$i<$tableCount;$i++) {
2693
				$startGlyphID = $this->read_ushort();
2694
				$endGlyphID = $this->read_ushort();
2695
				$class = $this->read_ushort();
2696
				for($g=$startGlyphID;$g<=$endGlyphID;$g++) {
2697
					if ($this->glyphToChar[$g][0]) {
2698
						$GlyphByClass[$class][] = unicode_hex($this->glyphToChar[$g][0]);
2699
					}
2700
				}
2701
			}
2702
		}
2703
		$gbc = array();
2704
		foreach($GlyphByClass AS $class=>$garr) { $gbc[$class] = implode('|',$garr); }
2705
		return $gbc;
2706
	}
2707
	//////////////////////////////////////////////////////////////////////////////////
2708
	//////////////////////////////////////////////////////////////////////////////////
2709
	//////////////////////////////////////////////////////////////////////////////////
2710
	//////////////////////////////////////////////////////////////////////////////////
2711
	//////////////////////////////////////////////////////////////////////////////////
2712
	function _getGPOStables() {
2713
		///////////////////////////////////
2714
		// GPOS - Glyph Positioning
2715
		///////////////////////////////////
2716
		if (isset($this->tables["GPOS"])) { 
2717
			$this->mpdf->WriteHTML('<h1>GPOS Tables</h1>'); 
2718
			$ffeats = array();
2719
			$gpos_offset = $this->seek_table("GPOS");
2720
			$this->skip(4);
2721
			$ScriptList_offset = $gpos_offset + $this->read_ushort();
2722
			$FeatureList_offset = $gpos_offset + $this->read_ushort();
2723
			$LookupList_offset = $gpos_offset + $this->read_ushort();
2724
2725
			// ScriptList
2726
			$this->seek($ScriptList_offset );
2727
			$ScriptCount = $this->read_ushort();
2728 View Code Duplication
			for ($i=0;$i<$ScriptCount;$i++) {
2729
					$ScriptTag = $this->read_tag();	// = "beng", "deva" etc.
2730
					$ScriptTableOffset = $this->read_ushort();
2731
					$ffeats[$ScriptTag] = $ScriptList_offset + $ScriptTableOffset;
2732
			}
2733
2734
			// Script Table
2735 View Code Duplication
			foreach($ffeats AS $t=>$o) {
2736
				$ls = array();
2737
				$this->seek($o);
2738
				$DefLangSys_offset = $this->read_ushort();
2739
				if ($DefLangSys_offset > 0) {
2740
					$ls['DFLT'] = $DefLangSys_offset + $o;
2741
				}
2742
				$LangSysCount = $this->read_ushort();
2743
				for ($i=0;$i<$LangSysCount;$i++) {
2744
					$LangTag = $this->read_tag();	// = 
2745
					$LangTableOffset = $this->read_ushort();
2746
					$ls[$LangTag] = $o + $LangTableOffset;
2747
				}
2748
				$ffeats[$t] = $ls;
2749
			}
2750
2751
2752
			// Get FeatureIndexList
2753
			// LangSys Table - from first listed langsys
2754 View Code Duplication
			foreach($ffeats AS $st=>$scripts) {
2755
				foreach($scripts AS $t=>$o) {
2756
					$FeatureIndex = array();
2757
					$langsystable_offset = $o;
2758
					$this->seek($langsystable_offset);
2759
					$LookUpOrder = $this->read_ushort();	//==NULL
2760
					$ReqFeatureIndex = $this->read_ushort();
2761
					if ($ReqFeatureIndex != 0xFFFF) { $FeatureIndex[] = $ReqFeatureIndex ; }
2762
					$FeatureCount = $this->read_ushort();
2763
					for ($i=0;$i<$FeatureCount;$i++) {
2764
							$FeatureIndex[] = $this->read_ushort();	// = index of feature
2765
					}
2766
					$ffeats[$st][$t] = $FeatureIndex; 
2767
				}
2768
			}
2769
//print_r($ffeats); exit;
2770
2771
2772
			// Feauture List => LookupListIndex es
2773
			$this->seek($FeatureList_offset );
2774
			$FeatureCount = $this->read_ushort();
2775
			$Feature = array();
2776 View Code Duplication
			for ($i=0;$i<$FeatureCount;$i++) {
2777
				$Feature[$i] = array('tag' => $this->read_tag() );
2778
				$Feature[$i]['offset'] = $FeatureList_offset + $this->read_ushort();
2779
			}
2780 View Code Duplication
			for ($i=0;$i<$FeatureCount;$i++) {
2781
				$this->seek($Feature[$i]['offset']);
2782
				$this->read_ushort(); // null
2783
				$Feature[$i]['LookupCount'] = $Lookupcount = $this->read_ushort();
2784
				$Feature[$i]['LookupListIndex'] = array();
2785
				for ($c=0;$c<$Lookupcount;$c++) {
2786
					$Feature[$i]['LookupListIndex'][] = $this->read_ushort();
2787
				}
2788
			}
2789
2790
2791 View Code Duplication
			foreach($ffeats AS $st=>$scripts) {
2792
				foreach($scripts AS $t=>$o) {
2793
					$FeatureIndex = $ffeats[$st][$t];
2794
					foreach($FeatureIndex AS $k=>$fi) {
2795
						$ffeats[$st][$t][$k] = $Feature[$fi];
2796
					}
2797
				}
2798
			}
2799
//print_r($ffeats); exit;
2800
			//=====================================================================================
2801
			$gpos = array();
2802
			$GPOSScriptLang = array();
2803 View Code Duplication
			foreach($ffeats AS $st=>$scripts) {
2804
				foreach($scripts AS $t=>$langsys) {
2805
					$lg = array();
2806
					foreach($langsys AS $ft) {
2807
						$lg[$ft['LookupListIndex'][0]] = $ft;
2808
					}
2809
					// list of Lookups in order they need to be run i.e. order listed in Lookup table
2810
					ksort($lg);
2811
					foreach($lg AS $ft) {
2812
						$gpos[$st][$t][$ft['tag']] = $ft['LookupListIndex'];
2813
					}
2814
					if (!isset($GPOSScriptLang[$st])) { $GPOSScriptLang[$st] = ''; }
2815
					$GPOSScriptLang[$st] .= $t.' ';
2816
				}
2817
			}
2818 View Code Duplication
			if ($this->mode == 'summary') {
2819
				$this->mpdf->WriteHTML('<h3>GPOS Scripts &amp; Languages</h3>'); 
2820
				$html = '';
2821
				if (count($gpos)) {
2822
					foreach ($gpos AS $st=>$g) {
2823
						$html .= '<h5>'.$st.'</h5>'; 
2824
						foreach ($g AS $l=>$t) {
2825
							$html .= '<div><a href="font_dump_OTL.php?script='.$st.'&lang='.$l.'">'.$l.'</a></b>: '; 
2826
							foreach ($t AS $tag=>$o) {
2827
								$html .= $tag.' '; 
2828
							}
2829
							$html .= '</div>'; 
2830
						}
2831
					}
2832
				}
2833
				else {
2834
					$html .= '<div>No entries in GPOS table.</div>';
2835
				}
2836
				$this->mpdf->WriteHTML($html); 
2837
				$this->mpdf->WriteHTML('</div>'); 
2838
				return 0;
2839
			}
2840
2841
2842
2843
			//=====================================================================================
2844
			// Get metadata and offsets for whole Lookup List table
2845
			$this->seek($LookupList_offset );
2846
			$LookupCount = $this->read_ushort();
2847
			$Lookup = array();
2848
			$Offsets = array();
2849
			$SubtableCount = array();
2850 View Code Duplication
			for ($i=0;$i<$LookupCount;$i++) {
2851
				$Offsets[$i] = $LookupList_offset + $this->read_ushort();
2852
			}
2853 View Code Duplication
			for ($i=0;$i<$LookupCount;$i++) {
2854
				$this->seek($Offsets[$i]);
2855
				$Lookup[$i]['Type'] = $this->read_ushort();
2856
				$Lookup[$i]['Flag'] = $flag = $this->read_ushort();
2857
				$Lookup[$i]['SubtableCount'] = $SubtableCount[$i] = $this->read_ushort();
2858
				for ($c=0;$c<$SubtableCount[$i] ;$c++) {
2859
					$Lookup[$i]['Subtables'][$c] = $Offsets[$i] + $this->read_ushort();
2860
2861
				}
2862
				// MarkFilteringSet = Index (base 0) into GDEF mark glyph sets structure
2863
				if (($flag & 0x0010) == 0x0010) {
2864
					$Lookup[$i]['MarkFilteringSet'] = $this->read_ushort();
2865
				}
2866
				// else { $Lookup[$i]['MarkFilteringSet'] = ''; }
2867
2868
				// Lookup Type 9: Extension
2869
				if ($Lookup[$i]['Type'] == 9) {
2870
					// Overwrites new offset (32-bit) for each subtable, and a new lookup Type
2871
					for ($c=0;$c<$SubtableCount[$i] ;$c++) {
2872
						$this->seek($Lookup[$i]['Subtables'][$c]);
2873
						$ExtensionPosFormat = $this->read_ushort();
2874
						$type = $this->read_ushort();
2875
						$Lookup[$i]['Subtables'][$c] = $Lookup[$i]['Subtables'][$c] + $this->read_ulong();
2876
					}
2877
					$Lookup[$i]['Type'] = $type;
2878
				}
2879
2880
			}
2881
2882
2883
			//=====================================================================================
2884
2885
			$st = $this->mpdf->OTLscript;
2886
			$t = $this->mpdf->OTLlang;
2887
			$langsys = $gpos[$st][$t];
2888
2889
2890
			$lul = array();	// array of LookupListIndexes
2891
			$tags = array();	// corresponding array of feature tags e.g. 'ccmp'
2892
			if (count($langsys)) {
2893
				foreach($langsys AS $tag=>$ft) {
2894
					foreach($ft AS $ll) { 
2895
						$lul[$ll] = $tag; 
2896
					}
2897
				}
2898
			}
2899
			ksort($lul);	// Order the Lookups in the order they are in the GUSB table, regardless of Feature order
2900
			$this->_getGPOSarray($Lookup, $lul, $st);
2901
//print_r($lul); exit;
2902
2903
2904
			return array($GPOSScriptLang, $gpos, $Lookup);
2905
2906
		}	// end if GPOS
2907
	}
2908
2909
	//////////////////////////////////////////////////////////////////////////////////
2910
2911
			//=====================================================================================
2912
			//=====================================================================================
2913
			//=====================================================================================
2914
/////////////////////////////////////////////////////////////////////////////////////////
2915
	// GPOS functions
2916
	function _getGPOSarray(&$Lookup, $lul, $scripttag, $level=1, $lcoverage='', $exB='', $exL='') {
2917
			// Process (3) LookupList for specific Script-LangSys
2918
			$html = '';
2919
			if ($level==1) { $html .= '<bookmark level="0" content="GPOS features">'; }
2920
			foreach($lul AS $luli=>$tag) {
2921
				$html .= '<div class="level'.$level.'">'; 
2922
				$html .= '<h5 class="level'.$level.'">';
2923
				if ($level==1) { $html .= '<bookmark level="1" content="'.$tag.' [#'.$luli.']">'; }
2924
				$html .= 'Lookup #'.$luli.' [tag: <span style="color:#000066;">'.$tag.'</span>]</h5>'; 
2925
				$ignore = $this->_getGSUBignoreString($Lookup[$luli]['Flag'], $Lookup[$luli]['MarkFilteringSet']);
2926
				if ($ignore) { $html .= '<div class="ignore">Ignoring: '.$ignore.'</div> '; }
2927
2928
				$Type = $Lookup[$luli]['Type'];
2929
				$Flag = $Lookup[$luli]['Flag'];
2930
				if (($Flag  & 0x0001) == 1) { $dir = 'RTL'; }
2931
				else { $dir = 'LTR'; }
2932
2933
				for ($c=0;$c<$Lookup[$luli]['SubtableCount'] ;$c++) {
2934
					$html .= '<div class="subtable">Subtable #'.$c;
2935
					if ($level==1) { $html .= '<bookmark level="2" content="Subtable #'.$c.'">'; }
2936
					$html .= '</div>'; 
2937
2938
					// Lets start
2939
					$subtable_offset = $Lookup[$luli]['Subtables'][$c];
2940
					$this->seek($subtable_offset);
2941
					$PosFormat = $this->read_ushort();
2942
2943
					////////////////////////////////////////////////////////////////////////////////
2944
					// LookupType 1: Single adjustment 	Adjust position of a single glyph (e.g. SmallCaps/Sups/Subs)
2945
					////////////////////////////////////////////////////////////////////////////////
2946
					if ($Lookup[$luli]['Type'] == 1) {
2947
						$html .= '<div class="lookuptype">LookupType 1: Single adjustment [Format '.$PosFormat.']</div>'; 
2948
						//===========
2949
						// Format 1: 
2950
						//===========
2951
						if ($PosFormat==1) {
2952
							$Coverage = $subtable_offset + $this->read_ushort();
2953
							$ValueFormat = $this->read_ushort();
2954
							$Value = $this->_getValueRecord($ValueFormat);
2955
2956
							$this->seek($Coverage);
2957
							$glyphs = $this->_getCoverage();	// Array of Hex Glyphs
2958
							for($g=0;$g<count($glyphs);$g++) {
2959
								if ($level==2 && strpos($lcoverage, $glyphs[$g])===false) { continue; }
2960
2961
								$html .= '<div class="substitution">';
2962
								$html .= '<span class="unicode">'.$this->formatUni($glyphs[$g]).'&nbsp;</span> ';
2963
								if ($level==2 && $exB) { $html .= $exB; }
2964
								$html .= '<span class="unchanged">&nbsp;'.$this->formatEntity($glyphs[$g]).'</span>';
2965
								if ($level==2 && $exL) { $html .= $exL; }
2966
								$html .= '&nbsp; &raquo; &raquo; &nbsp;';
2967
								if ($level==2 && $exB) { $html .= $exB; }
2968
								$html .= '<span class="changed" style="font-feature-settings:\''.$tag.'\' 1;">&nbsp;'.$this->formatEntity($glyphs[$g]).'</span>';
2969
								if ($level==2 && $exL) { $html .= $exL; }
2970
								$html .= ' <span class="unicode">';
2971
								if ($Value['XPlacement']) { $html .= ' Xpl: '.$Value['XPlacement'].';'; }
2972
								if ($Value['YPlacement']) { $html .= ' YPl: '.$Value['YPlacement'].';'; }
2973
								if ($Value['XAdvance']) { $html .= ' Xadv: '.$Value['XAdvance']; }
2974
								$html .= '</span>';
2975
								$html .= '</div>'; 
2976
							}
2977
2978
						}
2979
						//===========
2980
						// Format 2: 
2981
						//===========
2982
						else if ($PosFormat==2) {	
2983
							$Coverage = $subtable_offset + $this->read_ushort();
2984
							$ValueFormat = $this->read_ushort();
2985
							$ValueCount = $this->read_ushort();
2986
							$Values = array();
2987
							for($v=0;$v<$ValueCount;$v++) {
2988
								$Values[] = $this->_getValueRecord($ValueFormat);
2989
							}
2990
2991
							$this->seek($Coverage);
2992
							$glyphs = $this->_getCoverage();	// Array of Hex Glyphs
2993
2994
							for($g=0;$g<count($glyphs);$g++) {
2995
								if ($level==2 && strpos($lcoverage, $glyphs[$g])===false) { continue; }
2996
								$Value = $Values[$g];
2997
2998
								$html .= '<div class="substitution">';
2999
								$html .= '<span class="unicode">'.$this->formatUni($glyphs[$g]).'&nbsp;</span> ';
3000
								if ($level==2 && $exB) { $html .= $exB; }
3001
								$html .= '<span class="unchanged">&nbsp;'.$this->formatEntity($glyphs[$g]).'</span>';
3002
								if ($level==2 && $exL) { $html .= $exL; }
3003
								$html .= '&nbsp; &raquo; &raquo; &nbsp;';
3004
								if ($level==2 && $exB) { $html .= $exB; }
3005
								$html .= '<span class="changed" style="font-feature-settings:\''.$tag.'\' 1;">&nbsp;'.$this->formatEntity($glyphs[$g]).'</span>';
3006
								if ($level==2 && $exL) { $html .= $exL; }
3007
								$html .= ' <span class="unicode">';
3008
								if ($Value['XPlacement']) { $html .= ' Xpl: '.$Value['XPlacement'].';'; }
3009
								if ($Value['YPlacement']) { $html .= ' YPl: '.$Value['YPlacement'].';'; }
3010
								if ($Value['XAdvance']) { $html .= ' Xadv: '.$Value['XAdvance']; }
3011
								$html .= '</span>';
3012
								$html .= '</div>'; 
3013
							}
3014
						}
3015
3016
3017
3018
					}
3019
					////////////////////////////////////////////////////////////////////////////////
3020
					// LookupType 2: Pair adjustment 	Adjust position of a pair of glyphs (Kerning)
3021
					////////////////////////////////////////////////////////////////////////////////
3022
					else if ($Lookup[$luli]['Type'] == 2) {
3023
						$html .= '<div class="lookuptype">LookupType 2: Pair adjustment e.g. Kerning [Format '.$PosFormat.']</div>'; 
3024
						$Coverage = $subtable_offset + $this->read_ushort();
3025
						$ValueFormat1 = $this->read_ushort();
3026
						$ValueFormat2 = $this->read_ushort();
3027
						//===========
3028
						// Format 1: 
3029
						//===========
3030
						if ($PosFormat==1) {
3031
							$PairSetCount = $this->read_ushort();
3032
							$PairSetOffset = array();
3033 View Code Duplication
							for($p=0;$p<$PairSetCount;$p++) {
3034
								$PairSetOffset[] = $subtable_offset + $this->read_ushort();
3035
							}
3036
							$this->seek($Coverage);
3037
							$glyphs = $this->_getCoverage();	// Array of Hex Glyphs
3038
							for($p=0;$p<$PairSetCount;$p++) {
3039
								if ($level==2 && strpos($lcoverage, $glyphs[$p])===false) { continue; }
3040
								$this->seek($PairSetOffset[$p]);
3041
								// First Glyph = $glyphs[$p]
3042
3043
// Takes too long e.g. Calibri font - just list kerning pairs with this:
3044
$html .= '<div class="glyphs">';
3045
$html .= '<span class="unchanged">&nbsp;'.$this->formatEntity($glyphs[$p]).' </span>';
3046
3047
								//PairSet table
3048
								$PairValueCount = $this->read_ushort();
3049
								for($pv=0;$pv<$PairValueCount;$pv++) {
3050
									//PairValueRecord
3051
									$gid = $this->read_ushort();
3052
									$SecondGlyph = unicode_hex($this->glyphToChar[$gid][0]); 
3053
									$Value1 = $this->_getValueRecord($ValueFormat1);
3054
									$Value2 = $this->_getValueRecord($ValueFormat2);
3055
3056
									// If RTL pairs, GPOS declares a XPlacement e.g. -180 for an XAdvance of -180 to take 
3057
									// account of direction. mPDF does not need the XPlacement adjustment
3058
									if ($dir=='RTL' && $Value1['XPlacement']) {
3059
										$Value1['XPlacement'] -= $Value1['XAdvance'];
3060
									}
3061
3062 View Code Duplication
									if($ValueFormat2) { 
3063
										// If RTL pairs, GPOS declares a XPlacement e.g. -180 for an XAdvance of -180 to take 
3064
										// account of direction. mPDF does not need the XPlacement adjustment
3065
										if ($dir=='RTL' && $Value2['XPlacement'] && $Value2['XAdvance']) {
3066
											$Value2['XPlacement'] -= $Value2['XAdvance'];
3067
										}
3068
									}
3069
3070
$html .= ' '.$this->formatEntity($SecondGlyph).' ';
3071
3072
/*
3073
									$html .= '<div class="substitution">';
3074
									$html .= '<span class="unicode">'.$this->formatUni($glyphs[$p]).'&nbsp;</span> ';
3075
									if ($level==2 && $exB) { $html .= $exB; }
3076
									$html .= '<span class="unchanged">&nbsp;'.$this->formatEntity($glyphs[$p]).$this->formatEntity($SecondGlyph).'</span>';
3077
									if ($level==2 && $exL) { $html .= $exL; }
3078
									$html .= '&nbsp; &raquo; &raquo; &nbsp;';
3079
									if ($level==2 && $exB) { $html .= $exB; }
3080
									$html .= '<span class="changed" style="font-feature-settings:\''.$tag.'\' 1;">&nbsp;'.$this->formatEntity($glyphs[$p]).$this->formatEntity($SecondGlyph).'</span>';
3081
									if ($level==2 && $exL) { $html .= $exL; }
3082
									$html .= ' <span class="unicode">';
3083
									if ($Value1['XPlacement']) { $html .= ' Xpl[1]: '.$Value1['XPlacement'].';'; }
3084
									if ($Value1['YPlacement']) { $html .= ' YPl[1]: '.$Value1['YPlacement'].';'; }
3085
									if ($Value1['XAdvance']) { $html .= ' Xadv[1]: '.$Value1['XAdvance']; }
3086
									if ($Value2['XPlacement']) { $html .= ' Xpl[2]: '.$Value2['XPlacement'].';'; }
3087
									if ($Value2['YPlacement']) { $html .= ' YPl[2]: '.$Value2['YPlacement'].';'; }
3088
									if ($Value2['XAdvance']) { $html .= ' Xadv[2]: '.$Value2['XAdvance']; }
3089
									$html .= '</span>';
3090
									$html .= '</div>'; 
3091
*/
3092
3093
								}
3094
$html .= '</div>';
3095
							}
3096
						}
3097
						//===========
3098
						// Format 2: 
3099
						//===========
3100
						else if ($PosFormat==2) {	
3101
							$ClassDef1 = $subtable_offset + $this->read_ushort();
3102
							$ClassDef2 = $subtable_offset + $this->read_ushort();
3103
							$Class1Count = $this->read_ushort();
3104
							$Class2Count = $this->read_ushort();
3105
3106
							$sizeOfPair = ( 2*$this->count_bits($ValueFormat1) ) + ( 2*$this->count_bits($ValueFormat2) );
3107
							$sizeOfValueRecords = $Class1Count * $Class2Count * $sizeOfPair;
3108
3109
3110
							// NB Class1Count includes Class 0 even though it is not defined by $ClassDef1
3111
							// i.e. Class1Count = 5; Class1 will contain array(indices 1-4);
3112
							$Class1 = $this->_getClassDefinitionTable($ClassDef1);
3113
							$Class2 = $this->_getClassDefinitionTable($ClassDef2);
3114
3115
							$this->seek($subtable_offset + 16);
3116
3117
							for($i=0;$i<$Class1Count;$i++) {
3118
									for($j=0;$j<$Class2Count;$j++) {
3119
											$Value1 = $this->_getValueRecord($ValueFormat1);
3120
											$Value2 = $this->_getValueRecord($ValueFormat2);
3121
3122
											// If RTL pairs, GPOS declares a XPlacement e.g. -180 for an XAdvance of -180 
3123
											// of direction. mPDF does not need the XPlacement adjustment
3124
											if ($dir=='RTL' && $Value1['XPlacement'] && $Value1['XAdvance']) {
3125
												$Value1['XPlacement'] -= $Value1['XAdvance'];
3126
											}
3127 View Code Duplication
											if($ValueFormat2) { 
3128
												if ($dir=='RTL' && $Value2['XPlacement'] && $Value2['XAdvance']) {
3129
													$Value2['XPlacement'] -= $Value2['XAdvance'];
3130
												}
3131
											}
3132
3133
3134
											for($c1=0;$c1<count($Class1[$i]);$c1++) {
3135
3136
												$FirstGlyph = $Class1[$i][$c1];
3137
												if ($level==2 && strpos($lcoverage, $FirstGlyph)===false) { 
3138
													continue; 
3139
												}
3140
3141
3142
												for($c2=0;$c2<count($Class2[$j]);$c2++) {
3143
													$SecondGlyph = $Class2[$j][$c2];
3144
3145
3146
													if (!$Value1['XPlacement'] && !$Value1['YPlacement'] && !$Value1['XAdvance'] && !$Value2['XPlacement'] && !$Value2['YPlacement'] && !$Value2['XAdvance']) { continue; }
3147
3148
3149
													$html .= '<div class="substitution">';
3150
													$html .= '<span class="unicode">'.$this->formatUni($FirstGlyph).'&nbsp;</span> ';
3151
													if ($level==2 && $exB) { $html .= $exB; }
3152
													$html .= '<span class="unchanged">&nbsp;'.$this->formatEntity($FirstGlyph).$this->formatEntity($SecondGlyph).'</span>';
3153
													if ($level==2 && $exL) { $html .= $exL; }
3154
													$html .= '&nbsp; &raquo; &raquo; &nbsp;';
3155
													if ($level==2 && $exB) { $html .= $exB; }
3156
													$html .= '<span class="changed" style="font-feature-settings:\''.$tag.'\' 1;">&nbsp;'.$this->formatEntity($FirstGlyph).$this->formatEntity($SecondGlyph).'</span>';
3157
													if ($level==2 && $exL) { $html .= $exL; }
3158
													$html .= ' <span class="unicode">';
3159
													if ($Value1['XPlacement']) { $html .= ' Xpl[1]: '.$Value1['XPlacement'].';'; }
3160
													if ($Value1['YPlacement']) { $html .= ' YPl[1]: '.$Value1['YPlacement'].';'; }
3161
													if ($Value1['XAdvance']) { $html .= ' Xadv[1]: '.$Value1['XAdvance']; }
3162
													if ($Value2['XPlacement']) { $html .= ' Xpl[2]: '.$Value2['XPlacement'].';'; }
3163
													if ($Value2['YPlacement']) { $html .= ' YPl[2]: '.$Value2['YPlacement'].';'; }
3164
													if ($Value2['XAdvance']) { $html .= ' Xadv[2]: '.$Value2['XAdvance']; }
3165
													$html .= '</span>';
3166
													$html .= '</div>'; 
3167
3168
												}
3169
											}
3170
									}
3171
3172
							}
3173
						}
3174
					}
3175
					////////////////////////////////////////////////////////////////////////////////
3176
					// LookupType 3: Cursive attachment 	Attach cursive glyphs
3177
					////////////////////////////////////////////////////////////////////////////////
3178
					else if ($Lookup[$luli]['Type'] == 3) {
3179
						$html .= '<div class="lookuptype">LookupType 3: Cursive attachment </div>'; 
3180
						$Coverage = $subtable_offset + $this->read_ushort();
3181
						$EntryExitCount = $this->read_ushort();
3182
						$EntryAnchors = array();
3183
						$ExitAnchors = array();
3184
						for($i=0;$i<$EntryExitCount;$i++) {
3185
							$EntryAnchors[$i] = $this->read_ushort();
3186
							$ExitAnchors[$i] = $this->read_ushort();
3187
						}
3188
3189
						$this->seek($Coverage);
3190
						$Glyphs = $this->_getCoverage();
3191
						for($i=0;$i<$EntryExitCount;$i++) {
3192
							// Need default XAdvance for glyph
3193
							$pdfWidth = $this->mpdf->_getCharWidth($this->mpdf->fonts[$this->fontkey]['cw'], hexdec($Glyphs[$i]));
3194
							$EntryAnchor = $EntryAnchors[$i] ;
3195
							$ExitAnchor = $ExitAnchors[$i] ;
3196
							$html .= '<div class="glyphs">';
3197
							$html .= '<span class="unchanged">'.$this->formatEntity($Glyphs[$i]).' </span> ';
3198
							$html .= '<span class="unicode"> '.$this->formatUni($Glyphs[$i]).' => '; 
3199
3200 View Code Duplication
							if ($EntryAnchor != 0) {
3201
								$EntryAnchor += $subtable_offset;
3202
								list($x,$y) = $this->_getAnchorTable($EntryAnchor);
3203
								if ($dir == 'RTL') {
3204
									if (round($pdfWidth) == round($x * 1000/ $this->mpdf->fonts[$this->fontkey]['desc']['unitsPerEm']) ) {
3205
										$x = 0;
3206
									}
3207
									else { $x = $x - ($pdfWidth * $this->mpdf->fonts[$this->fontkey]['desc']['unitsPerEm']/1000); }
3208
								}
3209
								$html .= " Entry X: ".$x." Y: ".$y."; "; 
3210
							}
3211 View Code Duplication
							if ($ExitAnchor != 0) {
3212
								$ExitAnchor += $subtable_offset;
3213
								list($x,$y) = $this->_getAnchorTable($ExitAnchor);
3214
								if ($dir == 'LTR') {
3215
									if (round($pdfWidth) == round($x * 1000/ $this->mpdf->fonts[$this->fontkey]['desc']['unitsPerEm']) ) {
3216
										$x = 0;
3217
									}
3218
									else { $x = $x - ($pdfWidth * $this->mpdf->fonts[$this->fontkey]['desc']['unitsPerEm']/1000); }
3219
								}
3220
								$html .= " Exit X: ".$x." Y: ".$y."; "; 
3221
							}
3222
3223
3224
							$html .= '</span></div>';
3225
						}
3226
3227
					}
3228
					////////////////////////////////////////////////////////////////////////////////
3229
					// LookupType 4: MarkToBase attachment 	Attach a combining mark to a base glyph
3230
					////////////////////////////////////////////////////////////////////////////////
3231
					else if ($Lookup[$luli]['Type'] == 4) {
3232
						$html .= '<div class="lookuptype">LookupType 4: MarkToBase attachment </div>'; 
3233
						$MarkCoverage = $subtable_offset + $this->read_ushort();
3234
						$BaseCoverage = $subtable_offset + $this->read_ushort();
3235
3236
						$this->seek($MarkCoverage);
3237
						$MarkGlyphs = $this->_getCoverage();
3238
3239
						$this->seek($BaseCoverage);
3240
						$BaseGlyphs = $this->_getCoverage();
3241
3242
						$firstMark = '';
3243
						$html .= '<div class="glyphs">Marks: ';
3244 View Code Duplication
						for($i=0;$i<count($MarkGlyphs);$i++) {
3245
							if ($level==2 && strpos($lcoverage, $MarkGlyphs[$i])===false) { continue; }
3246
							else { 
3247
								if (!$firstMark) { $firstMark = $MarkGlyphs[$i]; }
3248
							}
3249
							$html .= ' '.$this->formatEntity($MarkGlyphs[$i]).' ';
3250
						}
3251
						$html .= '</div>';
3252
						if (!$firstMark) { return; }
3253
3254
						$html .= '<div class="glyphs">Bases: ';
3255 View Code Duplication
						for($j=0;$j<count($BaseGlyphs);$j++) {
3256
							$html .= ' '.$this->formatEntity($BaseGlyphs[$j]).' ';
3257
						}
3258
						$html .= '</div>';
3259
3260
						// Example
3261
						$html .= '<div class="glyphs" style="font-feature-settings:\''.$tag.'\' 1;">Example(s): ';
3262 View Code Duplication
						for ($j=0;$j<min(count($BaseGlyphs),20);$j++) {
3263
							$html .= ' '.$this->formatEntity($BaseGlyphs[$j]).$this->formatEntity($firstMark,true).' &nbsp; ';
3264
						}
3265
						$html .= '</div>';
3266
3267
3268
					}
3269
					////////////////////////////////////////////////////////////////////////////////
3270
					// LookupType 5: MarkToLigature attachment 	Attach a combining mark to a ligature
3271
					////////////////////////////////////////////////////////////////////////////////
3272
					else if ($Lookup[$luli]['Type'] == 5) {
3273
						$html .= '<div class="lookuptype">LookupType 5: MarkToLigature attachment </div>'; 
3274
						$MarkCoverage = $subtable_offset + $this->read_ushort();
3275
						//$MarkCoverage is already set in $lcoverage 00065|00073 etc
3276
						$LigatureCoverage = $subtable_offset + $this->read_ushort();
3277
						$ClassCount = $this->read_ushort(); // Number of classes defined for marks = Number of mark glyphs in the MarkCoverage table
3278
						$MarkArray = $subtable_offset + $this->read_ushort();	// Offset to MarkArray table
3279
						$LigatureArray = $subtable_offset + $this->read_ushort(); // Offset to LigatureArray table
3280
3281
						$this->seek($MarkCoverage);
3282
						$MarkGlyphs = $this->_getCoverage();
3283
						$this->seek($LigatureCoverage);
3284
						$LigatureGlyphs = $this->_getCoverage();
3285
3286
						$firstMark = '';
3287
						$html .= '<div class="glyphs">Marks: <span class="unchanged">';
3288
						$MarkRecord = array();
3289
						for ($i=0;$i<count($MarkGlyphs);$i++) {
3290
							if ($level==2 && strpos($lcoverage, $MarkGlyphs[$i])===false) { continue; }
3291
							else { 
3292
								if (!$firstMark) { $firstMark = $MarkGlyphs[$i]; }
3293
							}
3294
							// Get the relevant MarkRecord
3295
							$MarkRecord[$i] = $this->_getMarkRecord($MarkArray, $i);
3296
							//Mark Class is = $MarkRecord[$i]['Class']
3297
							$html .= ' '.$this->formatEntity($MarkGlyphs[$i]).' ';
3298
						}
3299
						$html .= '</span></div>';
3300
						if (!$firstMark) { return; }
3301
3302
						$this->seek($LigatureArray);
3303
						$LigatureCount = $this->read_ushort();
3304
						$LigatureAttach = array();
3305
						$html .= '<div class="glyphs">Ligatures: <span class="unchanged">';
3306
						for ($j=0;$j<count($LigatureGlyphs);$j++) {
3307
							// Get the relevant LigatureRecord
3308
							$LigatureAttach[$j] = $LigatureArray + $this->read_ushort();
3309
							$html .= ' '.$this->formatEntity($LigatureGlyphs[$j]).' ';
3310
						}
3311
						$html .= '</span></div>';
3312
3313
/*
3314
						for ($i=0;$i<count($MarkGlyphs);$i++) {
3315
							$html .= '<div class="glyphs">';
3316
							$html .= '<span class="unchanged">'.$this->formatEntity($MarkGlyphs[$i]).'</span>';
3317
3318
							for ($j=0;$j<count($LigatureGlyphs);$j++) {
3319
								$this->seek($LigatureAttach[$j]);
3320
								$ComponentCount = $this->read_ushort();
3321
								$html .= '<span class="unchanged">'.$this->formatEntity($LigatureGlyphs[$j]).'</span>';
3322
								$offsets = array();
3323
								for ($comp=0;$comp<$ComponentCount;$comp++) {
3324
									// ComponentRecords
3325
									for ($class=0;$class<$ClassCount;$class++) {
3326
										$offset = $this->read_ushort();
3327
										if ($offset!= 0 && $class == $MarkRecord[$i]['Class']) {
3328
3329
											$html .= ' ['.$comp.'] ';
3330
3331
										}
3332
									}
3333
								}
3334
							}
3335
							$html .= '</span></div>';
3336
						}
3337
*/
3338
3339
3340
					}
3341
					////////////////////////////////////////////////////////////////////////////////
3342
					// LookupType 6: MarkToMark attachment 	Attach a combining mark to another mark
3343
					////////////////////////////////////////////////////////////////////////////////
3344
					else if ($Lookup[$luli]['Type'] == 6) {
3345
						$html .= '<div class="lookuptype">LookupType 6: MarkToMark attachment </div>'; 
3346
						$Mark1Coverage = $subtable_offset + $this->read_ushort();	// Combining Mark
3347
						//$Mark1Coverage is already set in $LuCoverage 0065|0073 etc
3348
						$Mark2Coverage = $subtable_offset + $this->read_ushort();	// Base Mark
3349
						$ClassCount = $this->read_ushort(); // Number of classes defined for marks = No. of Combining mark1 glyphs in the MarkCoverage table
3350
						$this->seek($Mark1Coverage);
3351
						$Mark1Glyphs = $this->_getCoverage();
3352
						$this->seek($Mark2Coverage);
3353
						$Mark2Glyphs = $this->_getCoverage();
3354
3355
3356
						$firstMark = '';
3357
						$html .= '<div class="glyphs">Marks: <span class="unchanged">';
3358 View Code Duplication
						for($i=0;$i<count($Mark1Glyphs);$i++) {
3359
							if ($level==2 && strpos($lcoverage, $Mark1Glyphs[$i])===false) { continue; }
3360
							else { 
3361
								if (!$firstMark) { $firstMark = $Mark1Glyphs[$i]; }
3362
							}
3363
							$html .= ' '.$this->formatEntity($Mark1Glyphs[$i]).' ';
3364
						}
3365
						$html .= '</span></div>';
3366
3367
						if ($firstMark) { 
3368
3369
							$html .= '<div class="glyphs">Bases: <span class="unchanged">';
3370 View Code Duplication
							for($j=0;$j<count($Mark2Glyphs);$j++) {
3371
								$html .= ' '.$this->formatEntity($Mark2Glyphs[$j]).' ';
3372
							}
3373
							$html .= '</span></div>';
3374
3375
							// Example
3376
							$html .= '<div class="glyphs" style="font-feature-settings:\''.$tag.'\' 1;">Example(s): <span class="changed">';
3377 View Code Duplication
							for ($j=0;$j<min(count($Mark2Glyphs),20);$j++) {
3378
								$html .= ' '.$this->formatEntity($Mark2Glyphs[$j]).$this->formatEntity($firstMark,true).' &nbsp; ';
3379
							}
3380
							$html .= '</span></div>';
3381
						}
3382
3383
					}
3384
					////////////////////////////////////////////////////////////////////////////////
3385
					// LookupType 7: Context positioning 	Position one or more glyphs in context
3386
					////////////////////////////////////////////////////////////////////////////////
3387
					else if ($Lookup[$luli]['Type'] == 7) {
3388
						$html .= '<div class="lookuptype">LookupType 7: Context positioning [Format '.$PosFormat.']</div>'; 
3389
						//===========
3390
						// Format 1: 
3391
						//===========
3392
						if ($PosFormat==1) {	
3393
							die("GPOS Lookup Type ".$Type." Format ".$PosFormat." not YET TESTED."); 
3394
						}
3395
						//===========
3396
						// Format 2: 
3397
						//===========
3398
						else if ($PosFormat==2) {	
3399
							die("GPOS Lookup Type ".$Type." Format ".$PosFormat." not YET TESTED."); 
3400
						}
3401
						//===========
3402
						// Format 3: 
3403
						//===========
3404 View Code Duplication
						else if ($PosFormat==3) {	
3405
							die("GPOS Lookup Type ".$Type." Format ".$PosFormat." not YET TESTED."); 
3406
						}
3407
						else { 
3408
							die("GPOS Lookup Type ".$Type.", Format ".$PosFormat." not supported."); 
3409
						}
3410
					}
3411
					////////////////////////////////////////////////////////////////////////////////
3412
					// LookupType 8: Chained Context positioning 	Position one or more glyphs in chained context
3413
					////////////////////////////////////////////////////////////////////////////////
3414
					else if ($Lookup[$luli]['Type'] == 8) {
3415
						$html .= '<div class="lookuptype">LookupType 8: Chained Context positioning [Format '.$PosFormat.']</div>'; 
3416
						//===========
3417
						// Format 1: 
3418
						//===========
3419
						if ($PosFormat==1) {
3420
							die("GPOS Lookup Type ".$Type." Format ".$PosFormat." not TESTED YET."); 
3421
						}
3422
						//===========
3423
						// Format 2: 
3424
						//===========
3425
						else if ($PosFormat==2) {
3426
							$html .= '<div>GPOS Lookup Type 8: Format 2 not yet supported in OTL dump</div>';
3427
							continue;
3428
							/* NB When developing - cf. GSUB 6.2 */
3429
							die("GPOS Lookup Type ".$Type." Format ".$PosFormat." not TESTED YET."); 
3430
						}
3431
						//===========
3432
						// Format 3: 
3433
						//===========
3434
						else if ($PosFormat==3) {
3435
							$BacktrackGlyphCount = $this->read_ushort();
3436
							$CoverageBacktrackOffset = array();
3437 View Code Duplication
							for ($b=0;$b<$BacktrackGlyphCount;$b++) {
3438
								$CoverageBacktrackOffset[] = $subtable_offset + $this->read_ushort();	// in glyph sequence order
3439
							}
3440
							$InputGlyphCount = $this->read_ushort();
3441
							$CoverageInputOffset = array();
3442 View Code Duplication
							for ($b=0;$b<$InputGlyphCount;$b++) {
3443
								$CoverageInputOffset[] = $subtable_offset + $this->read_ushort();	// in glyph sequence order
3444
							}
3445
							$LookaheadGlyphCount = $this->read_ushort();
3446
							$CoverageLookaheadOffset = array();
3447 View Code Duplication
							for ($b=0;$b<$LookaheadGlyphCount;$b++) {
3448
								$CoverageLookaheadOffset[] = $subtable_offset + $this->read_ushort();	// in glyph sequence order
3449
							}
3450
							$PosCount = $this->read_ushort();
3451
3452
							$PosLookupRecord = array();
3453
							for ($p=0;$p<$PosCount;$p++) {
3454
								// PosLookupRecord
3455
								$PosLookupRecord[$p]['SequenceIndex'] = $this->read_ushort();
3456
								$PosLookupRecord[$p]['LookupListIndex'] = $this->read_ushort();
3457
							}
3458
3459
							$backtrackGlyphs = array();
3460 View Code Duplication
							for ($b=0;$b<$BacktrackGlyphCount;$b++) {
3461
								$this->seek($CoverageBacktrackOffset[$b]);
3462
								$backtrackGlyphs[$b] = implode('|',$this->_getCoverage());
3463
							}
3464
							$inputGlyphs = array();
3465 View Code Duplication
							for ($b=0;$b<$InputGlyphCount;$b++) {
3466
								$this->seek($CoverageInputOffset[$b]);
3467
								$inputGlyphs[$b] = implode('|',$this->_getCoverage());
3468
							}
3469
							$lookaheadGlyphs = array();
3470 View Code Duplication
							for ($b=0;$b<$LookaheadGlyphCount;$b++) {
3471
								$this->seek($CoverageLookaheadOffset[$b]);
3472
								$lookaheadGlyphs[$b] = implode('|',$this->_getCoverage());
3473
							}
3474
3475
							$exampleB = array();
3476
							$exampleI = array();
3477
							$exampleL = array();
3478
							$html .= '<div class="context">CONTEXT: ';
3479 View Code Duplication
							for ($ff=count($backtrackGlyphs)-1;$ff>=0;$ff--) {
3480
								$html .= '<div>Backtrack #'.$ff.': <span class="unicode">'.$this->formatUniStr($backtrackGlyphs[$ff]).'</span></div>';
3481
								$exampleB[] = $this->formatEntityFirst($backtrackGlyphs[$ff]);
3482
							}
3483 View Code Duplication
							for ($ff=0;$ff<count($inputGlyphs);$ff++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
3484
								$html .= '<div>Input #'.$ff.': <span class="unchanged">&nbsp;'.$this->formatEntityStr($inputGlyphs[$ff]).'&nbsp;</span></div>';
3485
								$exampleI[] = $this->formatEntityFirst($inputGlyphs[$ff]);
3486
							}
3487 View Code Duplication
							for ($ff=0;$ff<count($lookaheadGlyphs);$ff++) {
3488
								$html .= '<div>Lookahead #'.$ff.': <span class="unicode">'.$this->formatUniStr($lookaheadGlyphs[$ff]).'</span></div>';
3489
								$exampleL[] = $this->formatEntityFirst($lookaheadGlyphs[$ff]);
3490
							}
3491
							$html .= '</div>'; 
3492
3493
3494
							for ($p=0;$p<$PosCount;$p++) {
3495
								$lup = $PosLookupRecord[$p]['LookupListIndex'] ;
3496
								$seqIndex = $PosLookupRecord[$p]['SequenceIndex'] ;
3497
3498
								// GENERATE exampleB[n] exampleI[<seqIndex] .... exampleI[>seqIndex] exampleL[n]
3499
								$exB = '';
3500
								$exL = '';
3501
								if (count($exampleB)) { $exB .= '<span class="backtrack">'.implode('&#x200d;',$exampleB).'</span>'; }
3502
3503 View Code Duplication
								if ($seqIndex>0) {
3504
									$exB .= '<span class="inputother">';
3505
									for($ip=0;$ip<$seqIndex;$ip++) {
3506
										$exB .=  $exampleI[$ip].'&#x200d;';
3507
									}
3508
									$exB .= '</span>';
3509
								}
3510
3511 View Code Duplication
								if (count($inputGlyphs)>($seqIndex+1)) {
3512
									$exL .= '<span class="inputother">';
3513
									for($ip=$seqIndex+1;$ip<count($inputGlyphs);$ip++) {
3514
										$exL .=  '&#x200d;'.$exampleI[$ip];
3515
									}
3516
									$exL .= '</span>';
3517
								}
3518
3519
								if (count($exampleL)) { $exL .= '<span class="lookahead">'.implode('&#x200d;',$exampleL).'</span>'; }
3520
3521
								$html .= '<div class="sequenceIndex">Substitution Position: '.$seqIndex.'</div>'; 
3522
3523
								$lul2 = array($lup=>$tag);
3524
3525
								// Only apply if the (first) 'Replace' glyph from the 
3526
								// Lookup list is in the [inputGlyphs] at ['SequenceIndex']
3527
								// Pass $inputGlyphs[$seqIndex] e.g. 00636|00645|00656
3528
								// to level 2 and only apply if first Replace glyph is in this list
3529
								$html .= $this->_getGPOSarray($Lookup, $lul2, $scripttag, 2, $inputGlyphs[$seqIndex], $exB, $exL);
3530
3531
							}
3532
						}
3533
					}
3534
3535
				}
3536
				$html .= '</div>'; 
3537
			}
3538
			if ($level ==1) { $this->mpdf->WriteHTML($html); }
3539
			else  { return $html; }
3540
//print_r($Lookup); exit;
3541
	}
3542
			//=====================================================================================
3543
			//=====================================================================================
3544
			// GPOS FUNCTIONS
3545
			//=====================================================================================
3546
3547
	function count_bits($n) {     
3548
		for ($c=0; $n; $c++) {
3549
			$n &= $n - 1; // clear the least significant bit set
3550
		}
3551
		return $c;
3552
	}
3553
3554 View Code Duplication
	function _getValueRecord($ValueFormat) {	// Common ValueRecord for GPOS
3555
		// Only returns 3 possible: $vra['XPlacement'] $vra['YPlacement'] $vra['XAdvance'] 
3556
		$vra = array();
3557
		// Horizontal adjustment for placement-in design units
3558
		if (($ValueFormat & 0x0001) == 0x0001) { $vra['XPlacement'] = $this->read_short(); }
3559
		// Vertical adjustment for placement-in design units
3560
		if (($ValueFormat & 0x0002) == 0x0002) { $vra['YPlacement'] = $this->read_short(); }
3561
		// Horizontal adjustment for advance-in design units (only used for horizontal writing)
3562
		if (($ValueFormat & 0x0004) == 0x0004) { $vra['XAdvance'] = $this->read_short(); }
3563
		// Vertical adjustment for advance-in design units (only used for vertical writing)
3564
		if (($ValueFormat & 0x0008) == 0x0008) { $this->read_short(); }
3565
		// Offset to Device table for horizontal placement-measured from beginning of PosTable (may be NULL)
3566
		if (($ValueFormat & 0x0010) == 0x0010) { $this->read_ushort(); }
3567
		// Offset to Device table for vertical placement-measured from beginning of PosTable (may be NULL)
3568
		if (($ValueFormat & 0x0020) == 0x0020) { $this->read_ushort(); }
3569
		// Offset to Device table for horizontal advance-measured from beginning of PosTable (may be NULL)
3570
		if (($ValueFormat & 0x0040) == 0x0040) { $this->read_ushort(); }
3571
		// Offset to Device table for vertical advance-measured from beginning of PosTable (may be NULL)
3572
		if (($ValueFormat & 0x0080) == 0x0080) { $this->read_ushort(); }
3573
		return $vra;
3574
	}
3575
3576 View Code Duplication
	function _getAnchorTable($offset=0) {
3577
		if ($offset) { $this->seek($offset); }
3578
		$AnchorFormat = $this->read_ushort();
3579
		$XCoordinate = $this->read_short(); 
3580
		$YCoordinate = $this->read_short(); 
3581
		// Format 2 specifies additional link to contour point; Format 3 additional Device table
3582
		return array($XCoordinate, $YCoordinate);
3583
	}
3584
3585 View Code Duplication
	function _getMarkRecord($offset, $MarkPos) {
3586
		$this->seek($offset);
3587
		$MarkCount = $this->read_ushort();
3588
		$this->skip($MarkPos*4);
3589
		$Class = $this->read_ushort(); 
3590
		$MarkAnchor = $offset + $this->read_ushort(); 	// = Offset to anchor table
3591
		list($x,$y) = $this->_getAnchorTable($MarkAnchor );
3592
		$MarkRecord = array('Class'=>$Class, 'AnchorX'=>$x, 'AnchorY'=>$y);
3593
		return $MarkRecord;
3594
	}
3595
3596
3597
	//////////////////////////////////////////////////////////////////////////////////
3598
	// Recursively get composite glyph data
3599 View Code Duplication
	function getGlyphData($originalGlyphIdx, &$maxdepth, &$depth, &$points, &$contours) {
3600
		$depth++;
3601
		$maxdepth = max($maxdepth, $depth);
3602
		if (count($this->glyphdata[$originalGlyphIdx]['compGlyphs'])) {
3603
			foreach($this->glyphdata[$originalGlyphIdx]['compGlyphs'] AS $glyphIdx) {
3604
				$this->getGlyphData($glyphIdx, $maxdepth, $depth, $points, $contours);
3605
			}
3606
		}
3607
		else if (($this->glyphdata[$originalGlyphIdx]['nContours'] > 0) && $depth > 0) {	// simple
3608
			$contours += $this->glyphdata[$originalGlyphIdx]['nContours'];
3609
			$points += $this->glyphdata[$originalGlyphIdx]['nPoints'];
3610
		}
3611
		$depth--;
3612
	}
3613
3614
3615
	//////////////////////////////////////////////////////////////////////////////////
3616
	// Recursively get composite glyphs
3617 View Code Duplication
	function getGlyphs($originalGlyphIdx, &$start, &$glyphSet, &$subsetglyphs) {
3618
		$glyphPos = $this->glyphPos[$originalGlyphIdx];
3619
		$glyphLen = $this->glyphPos[$originalGlyphIdx + 1] - $glyphPos;
3620
		if (!$glyphLen) { 
3621
			return;
3622
		}
3623
		$this->seek($start + $glyphPos);
3624
		$numberOfContours = $this->read_short();
3625
		if ($numberOfContours < 0) {
3626
			$this->skip(8);
3627
			$flags = GF_MORE;
3628
			while ($flags & GF_MORE) {
3629
				$flags = $this->read_ushort();
3630
				$glyphIdx = $this->read_ushort();
3631
				if (!isset($glyphSet[$glyphIdx])) {
3632
					$glyphSet[$glyphIdx] = count($subsetglyphs);	// old glyphID to new glyphID
3633
					$subsetglyphs[$glyphIdx] = true;
3634
				}
3635
				$savepos = ftell($this->fh);
3636
				$this->getGlyphs($glyphIdx, $start, $glyphSet, $subsetglyphs);
3637
				$this->seek($savepos);
3638
				if ($flags & GF_WORDS)
3639
					$this->skip(4);
3640
				else
3641
					$this->skip(2);
3642
				if ($flags & GF_SCALE)
3643
					$this->skip(2);
3644
				else if ($flags & GF_XYSCALE)
3645
					$this->skip(4);
3646
				else if ($flags & GF_TWOBYTWO)
3647
					$this->skip(8);
3648
			}
3649
		}
3650
	}
3651
3652
	//////////////////////////////////////////////////////////////////////////////////
3653
3654 View Code Duplication
	function getHMTX($numberOfHMetrics, $numGlyphs, &$glyphToChar, $scale) {
3655
		$start = $this->seek_table("hmtx");
3656
		$aw = 0;
3657
		$this->charWidths = str_pad('', 256*256*2, "\x00");
3658
		if ($this->maxUniChar > 65536) { $this->charWidths .= str_pad('', 256*256*2, "\x00"); }	// Plane 1 SMP
3659
		if ($this->maxUniChar > 131072) { $this->charWidths .= str_pad('', 256*256*2, "\x00"); }	// Plane 2 SMP
3660
		$nCharWidths = 0;
3661
		if (($numberOfHMetrics*4) < $this->maxStrLenRead) {
3662
			$data = $this->get_chunk($start,($numberOfHMetrics*4));
3663
			$arr = unpack("n*", $data);
3664
		}
3665
		else { $this->seek($start); }
3666
		for( $glyph=0; $glyph<$numberOfHMetrics; $glyph++) {
3667
			if (($numberOfHMetrics*4) < $this->maxStrLenRead) {
3668
				$aw = $arr[($glyph*2)+1];
3669
			}
3670
			else {
3671
				$aw = $this->read_ushort();
3672
				$lsb = $this->read_ushort();
3673
			}
3674
			if (isset($glyphToChar[$glyph]) || $glyph == 0) {
3675
3676
				if ($aw >= (1 << 15) ) { $aw = 0; }	// 1.03 Some (arabic) fonts have -ve values for width
3677
					// although should be unsigned value - comes out as e.g. 65108 (intended -50)
3678
				if ($glyph == 0) {
3679
					$this->defaultWidth = $scale*$aw;
3680
					continue;
3681
				}
3682
				foreach($glyphToChar[$glyph] AS $char) {
3683
					//$this->charWidths[$char] = intval(round($scale*$aw));
3684
					if ($char != 0 && $char != 65535) {
3685
 						$w = intval(round($scale*$aw));
3686
						if ($w == 0) { $w = 65535; }
3687
						if ($char < 196608) {
3688
							$this->charWidths[$char*2] = chr($w >> 8);
3689
							$this->charWidths[$char*2 + 1] = chr($w & 0xFF);
3690
							$nCharWidths++;
3691
						}
3692
					}
3693
				}
3694
			}
3695
		}
3696
		$data = $this->get_chunk(($start+$numberOfHMetrics*4),($numGlyphs*2));
3697
		$arr = unpack("n*", $data);
3698
		$diff = $numGlyphs-$numberOfHMetrics;
3699
		$w = intval(round($scale*$aw));
3700
		if ($w == 0) { $w = 65535; }
3701
		for( $pos=0; $pos<$diff; $pos++) {
3702
			$glyph = $pos + $numberOfHMetrics;
3703
			if (isset($glyphToChar[$glyph])) {
3704
				foreach($glyphToChar[$glyph] AS $char) {
3705
					if ($char != 0 && $char != 65535) {
3706
						if ($char < 196608) { 
3707
							$this->charWidths[$char*2] = chr($w >> 8);
3708
							$this->charWidths[$char*2 + 1] = chr($w & 0xFF);
3709
							$nCharWidths++;
3710
						}
3711
					}
3712
				}
3713
			}
3714
		}
3715
		// NB 65535 is a set width of 0
3716
		// First bytes define number of chars in font
3717
		$this->charWidths[0] = chr($nCharWidths >> 8);
3718
		$this->charWidths[1] = chr($nCharWidths & 0xFF);
3719
	}
3720
3721
3722
3723
3724
3725 View Code Duplication
	function getHMetric($numberOfHMetrics, $gid) {
3726
		$start = $this->seek_table("hmtx");
3727
		if ($gid < $numberOfHMetrics) {
3728
			$this->seek($start+($gid*4));
3729
			$hm = fread($this->fh,4);
3730
		}
3731
		else {
3732
			$this->seek($start+(($numberOfHMetrics-1)*4));
3733
			$hm = fread($this->fh,2);
3734
			$this->seek($start+($numberOfHMetrics*2)+($gid*2));
3735
			$hm .= fread($this->fh,2);
3736
		}
3737
		return $hm;
3738
	}
3739
3740 View Code Duplication
	function getLOCA($indexToLocFormat, $numGlyphs) {
3741
		$start = $this->seek_table('loca');
3742
		$this->glyphPos = array();
3743
		if ($indexToLocFormat == 0) {
3744
			$data = $this->get_chunk($start,($numGlyphs*2)+2);
3745
			$arr = unpack("n*", $data);
3746
			for ($n=0; $n<=$numGlyphs; $n++) {
3747
				$this->glyphPos[] = ($arr[$n+1] * 2);
3748
			}
3749
		}
3750
		else if ($indexToLocFormat == 1) {
3751
			$data = $this->get_chunk($start,($numGlyphs*4)+4);
3752
			$arr = unpack("N*", $data);
3753
			for ($n=0; $n<=$numGlyphs; $n++) {
3754
				$this->glyphPos[] = ($arr[$n+1]);
3755
			}
3756
		}
3757
		else 
3758
			die('Unknown location table format '.$indexToLocFormat);
3759
	}
3760
3761
3762
	// CMAP Format 4
3763 View Code Duplication
	function getCMAP4($unicode_cmap_offset, &$glyphToChar, &$charToGlyph ) {
3764
		$this->maxUniChar = 0;	
3765
		$this->seek($unicode_cmap_offset + 2);
3766
		$length = $this->read_ushort();
3767
		$limit = $unicode_cmap_offset + $length;
3768
		$this->skip(2);
3769
3770
		$segCount = $this->read_ushort() / 2;
3771
		$this->skip(6);
3772
		$endCount = array();
3773
		for($i=0; $i<$segCount; $i++) { $endCount[] = $this->read_ushort(); }
3774
		$this->skip(2);
3775
		$startCount = array();
3776
		for($i=0; $i<$segCount; $i++) { $startCount[] = $this->read_ushort(); }
3777
		$idDelta = array();
3778
		for($i=0; $i<$segCount; $i++) { $idDelta[] = $this->read_short(); }		// ???? was unsigned short
3779
		$idRangeOffset_start = $this->_pos;
3780
		$idRangeOffset = array();
3781
		for($i=0; $i<$segCount; $i++) { $idRangeOffset[] = $this->read_ushort(); }
3782
3783
		for ($n=0;$n<$segCount;$n++) {
3784
			$endpoint = ($endCount[$n] + 1);
3785
			for ($unichar=$startCount[$n];$unichar<$endpoint;$unichar++) {
3786
				if ($idRangeOffset[$n] == 0)
3787
					$glyph = ($unichar + $idDelta[$n]) & 0xFFFF;
3788
				else {
3789
					$offset = ($unichar - $startCount[$n]) * 2 + $idRangeOffset[$n];
3790
					$offset = $idRangeOffset_start + 2 * $n + $offset;
3791
					if ($offset >= $limit)
3792
						$glyph = 0;
3793
					else {
3794
						$glyph = $this->get_ushort($offset);
3795
						if ($glyph != 0)
3796
						   $glyph = ($glyph + $idDelta[$n]) & 0xFFFF;
3797
					}
3798
				}
3799
				$charToGlyph[$unichar] = $glyph;
3800
				if ($unichar < 196608) { $this->maxUniChar = max($unichar,$this->maxUniChar); }
3801
				$glyphToChar[$glyph][] = $unichar;
3802
			}
3803
		}
3804
3805
	}
3806
3807
function formatUni($char) {
3808
	$x = preg_replace('/^[0]*/','',$char);
3809
	$x = str_pad($x, 4, '0', STR_PAD_LEFT);
3810
	$d = hexdec($x);
3811
	if (($d>57343 && $d<63744) || ($d>122879 && $d<126977)) { $id = 'M'; }	// E000 - F8FF, 1E000-1F000
3812
	else { $id = 'U'; }
3813
	return $id .'+'.$x;
3814
}
3815
function formatEntity($char, $allowjoining=false) {
3816
	$char = preg_replace('/^[0]/','',$char);
3817
	$x = '&#x'.$char.';';
3818
	if (strpos($this->GlyphClassMarks, $char)!==false) { 
3819
		if (!$allowjoining) { 
3820
			$x = '&#x25cc;'.$x; 
3821
		}
3822
	}
3823
	return $x;
3824
}
3825 View Code Duplication
function formatUniArr($arr) {
3826
	$s = array();
3827
	foreach($arr AS $c) {
3828
		$x = preg_replace('/^[0]*/','',$c);
3829
		$d = hexdec($x);
3830
		if (($d>57343 && $d<63744) || ($d>122879 && $d<126977)) { $id = 'M'; }	// E000 - F8FF, 1E000-1F000
3831
		else { $id = 'U'; }
3832
		$s[] = $id .'+'.str_pad($x, 4, '0', STR_PAD_LEFT);
3833
	}
3834
	return implode(', ',$s);
3835
}
3836 View Code Duplication
function formatEntityArr($arr) {
3837
	$s = array();
3838
	foreach($arr AS $c) {
3839
		$c = preg_replace('/^[0]/','',$c);
3840
		$x = '&#x'.$c.';';
3841
		if (strpos($this->GlyphClassMarks, $c)!==false) { 
3842
			$x = '&#x25cc;'.$x; 
3843
		}
3844
		$s[] = $x;
3845
	}
3846
	return implode(' ',$s);	// ZWNJ? &#x200d;
3847
}
3848 View Code Duplication
function formatClassArr($arr) {
3849
	$s = array();
3850
	foreach($arr AS $c) {
3851
		$x = preg_replace('/^[0]*/','',$c);
3852
		$d = hexdec($x);
3853
		if (($d>57343 && $d<63744) || ($d>122879 && $d<126977)) { $id = 'M'; }	// E000 - F8FF, 1E000-1F000
3854
		else { $id = 'U'; }
3855
		$s[] = $id .'+'.str_pad($x, 4, '0', STR_PAD_LEFT);
3856
	}
3857
	return implode(', ',$s);
3858
}
3859 View Code Duplication
function formatUniStr($str) {
3860
	$s = array();
3861
	$arr = explode('|',$str);
3862
	foreach($arr AS $c) {
3863
		$x = preg_replace('/^[0]*/','',$c);
3864
		$d = hexdec($x);
3865
		if (($d>57343 && $d<63744) || ($d>122879 && $d<126977)) { $id = 'M'; }	// E000 - F8FF, 1E000-1F000
3866
		else { $id = 'U'; }
3867
		$s[] = $id .'+'.str_pad($x, 4, '0', STR_PAD_LEFT);
3868
	}
3869
	return implode(', ',$s);
3870
}
3871 View Code Duplication
function formatEntityStr($str) {
3872
	$s = array();
3873
	$arr = explode('|',$str);
3874
	foreach($arr AS $c) {
3875
		$c = preg_replace('/^[0]/','',$c);
3876
		$x = '&#x'.$c.';';
3877
		if (strpos($this->GlyphClassMarks, $c)!==false) { 
3878
			$x = '&#x25cc;'.$x; 
3879
		}
3880
		$s[] = $x;
3881
	}
3882
	return implode(' ',$s);	// ZWNJ? &#x200d;
3883
}
3884
function formatEntityFirst($str) {
3885
	$arr = explode('|',$str);
3886
	$char = preg_replace('/^[0]/','',$arr[0]);
3887
	$x = '&#x'.$char.';';
3888
	if (strpos($this->GlyphClassMarks, $char)!==false) { 
3889
		$x = '&#x25cc;'.$x; 
3890
	}
3891
	return $x;
3892
}
3893
3894
}
3895
3896
3897
?>