aboutsummaryrefslogtreecommitdiffhomepage
path: root/inc/3rdparty/libraries/mpdf/classes
diff options
context:
space:
mode:
Diffstat (limited to 'inc/3rdparty/libraries/mpdf/classes')
-rw-r--r--inc/3rdparty/libraries/mpdf/classes/barcode.php1965
-rw-r--r--inc/3rdparty/libraries/mpdf/classes/bmp.php248
-rw-r--r--inc/3rdparty/libraries/mpdf/classes/cssmgr.php1566
-rw-r--r--inc/3rdparty/libraries/mpdf/classes/directw.php408
-rw-r--r--inc/3rdparty/libraries/mpdf/classes/form.php1498
-rw-r--r--inc/3rdparty/libraries/mpdf/classes/gif.php700
-rw-r--r--inc/3rdparty/libraries/mpdf/classes/grad.php723
-rw-r--r--inc/3rdparty/libraries/mpdf/classes/indic.php433
-rw-r--r--inc/3rdparty/libraries/mpdf/classes/meter.php224
-rw-r--r--inc/3rdparty/libraries/mpdf/classes/svg.php2600
-rw-r--r--inc/3rdparty/libraries/mpdf/classes/tocontents.php468
-rw-r--r--inc/3rdparty/libraries/mpdf/classes/ttfontsuni.php2065
-rw-r--r--inc/3rdparty/libraries/mpdf/classes/ttfontsuni_analysis.php463
-rw-r--r--inc/3rdparty/libraries/mpdf/classes/wmf.php236
14 files changed, 13597 insertions, 0 deletions
diff --git a/inc/3rdparty/libraries/mpdf/classes/barcode.php b/inc/3rdparty/libraries/mpdf/classes/barcode.php
new file mode 100644
index 00000000..2a002693
--- /dev/null
+++ b/inc/3rdparty/libraries/mpdf/classes/barcode.php
@@ -0,0 +1,1965 @@
1<?php
2
3// Adapted for mPDF from TCPDF barcode. Original Details left below.
4
5//============================================================+
6// File name : barcodes.php
7// Begin : 2008-06-09
8// Last Update : 2009-04-15
9// Version : 1.0.008
10// License : GNU LGPL (http://www.gnu.org/copyleft/lesser.html)
11// ----------------------------------------------------------------------------
12// Copyright (C) 2008-2009 Nicola Asuni - Tecnick.com S.r.l.
13//
14// This program is free software: you can redistribute it and/or modify
15// it under the terms of the GNU Lesser General Public License as published by
16// the Free Software Foundation, either version 2.1 of the License, or
17// (at your option) any later version.
18//
19// This program is distributed in the hope that it will be useful,
20// but WITHOUT ANY WARRANTY; without even the implied warranty of
21// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22// GNU Lesser General Public License for more details.
23//
24// You should have received a copy of the GNU Lesser General Public License
25// along with this program. If not, see <http://www.gnu.org/licenses/>.
26//
27// See LICENSE.TXT file for more information.
28// ----------------------------------------------------------------------------
29//
30// Description : PHP class to creates array representations for
31// common 1D barcodes to be used with TCPDF.
32//
33// Author: Nicola Asuni
34//
35// (c) Copyright:
36// Nicola Asuni
37// Tecnick.com S.r.l.
38// Via della Pace, 11
39// 09044 Quartucciu (CA)
40// ITALY
41// www.tecnick.com
42// info@tecnick.com
43//============================================================+
44
45class PDFBarcode {
46
47 protected $barcode_array;
48 protected $gapwidth;
49 protected $print_ratio;
50 protected $daft;
51
52 public function __construct() {
53
54 }
55
56 public function getBarcodeArray($code, $type, $pr='') {
57 $this->setBarcode($code, $type, $pr);
58 return $this->barcode_array;
59 }
60 public function getChecksum($code, $type) {
61 $this->setBarcode($code, $type);
62 if (!$this->barcode_array) { return ''; }
63 else { return $this->barcode_array['checkdigit']; }
64 }
65
66 public function setBarcode($code, $type, $pr='') {
67 $this->print_ratio = 1;
68 switch (strtoupper($type)) {
69 case 'ISBN':
70 case 'ISSN':
71 case 'EAN13': { // EAN 13
72 $arrcode = $this->barcode_eanupc($code, 13);
73 $arrcode['lightmL'] = 11; // LEFT light margin = x X-dim (http://www.gs1uk.org)
74 $arrcode['lightmR'] = 7; // RIGHT light margin = x X-dim (http://www.gs1uk.org)
75 $arrcode['nom-X'] = 0.33; // Nominal value for X-dim in mm (http://www.gs1uk.org)
76 $arrcode['nom-H'] = 25.93; // Nominal bar height in mm incl. numerals (http://www.gs1uk.org)
77 break;
78 }
79 case 'UPCA': { // UPC-A
80 $arrcode = $this->barcode_eanupc($code, 12);
81 $arrcode['lightmL'] = 9; // LEFT light margin = x X-dim (http://www.gs1uk.org)
82 $arrcode['lightmR'] = 9; // RIGHT light margin = x X-dim (http://www.gs1uk.org)
83 $arrcode['nom-X'] = 0.33; // Nominal value for X-dim in mm (http://www.gs1uk.org)
84 $arrcode['nom-H'] = 25.91; // Nominal bar height in mm incl. numerals (http://www.gs1uk.org)
85 break;
86 }
87 case 'UPCE': { // UPC-E
88 $arrcode = $this->barcode_eanupc($code, 6);
89 $arrcode['lightmL'] = 9; // LEFT light margin = x X-dim (http://www.gs1uk.org)
90 $arrcode['lightmR'] = 7; // RIGHT light margin = x X-dim (http://www.gs1uk.org)
91 $arrcode['nom-X'] = 0.33; // Nominal value for X-dim in mm (http://www.gs1uk.org)
92 $arrcode['nom-H'] = 25.93; // Nominal bar height in mm incl. numerals (http://www.gs1uk.org)
93 break;
94 }
95 case 'EAN8': { // EAN 8
96 $arrcode = $this->barcode_eanupc($code, 8);
97 $arrcode['lightmL'] = 7; // LEFT light margin = x X-dim (http://www.gs1uk.org)
98 $arrcode['lightmR'] = 7; // RIGHT light margin = x X-dim (http://www.gs1uk.org)
99 $arrcode['nom-X'] = 0.33; // Nominal value for X-dim in mm (http://www.gs1uk.org)
100 $arrcode['nom-H'] = 21.64; // Nominal bar height in mm incl. numerals (http://www.gs1uk.org)
101 break;
102 }
103 case 'EAN2': { // 2-Digits UPC-Based Extention
104 $arrcode = $this->barcode_eanext($code, 2);
105 $arrcode['lightmL'] = 7; // LEFT light margin = x X-dim (estimated)
106 $arrcode['lightmR'] = 7; // RIGHT light margin = x X-dim (estimated)
107 $arrcode['sepM'] = 9; // SEPARATION margin = x X-dim (http://web.archive.org/web/19990501035133/http://www.uc-council.org/d36-d.htm)
108 $arrcode['nom-X'] = 0.33; // Nominal value for X-dim in mm (http://www.gs1uk.org)
109 $arrcode['nom-H'] = 20; // Nominal bar height in mm incl. numerals (estimated) not used when combined
110 break;
111 }
112 case 'EAN5': { // 5-Digits UPC-Based Extention
113 $arrcode = $this->barcode_eanext($code, 5);
114 $arrcode['lightmL'] = 7; // LEFT light margin = x X-dim (estimated)
115 $arrcode['lightmR'] = 7; // RIGHT light margin = x X-dim (estimated)
116 $arrcode['sepM'] = 9; // SEPARATION margin = x X-dim (http://web.archive.org/web/19990501035133/http://www.uc-council.org/d36-d.htm)
117 $arrcode['nom-X'] = 0.33; // Nominal value for X-dim in mm (http://www.gs1uk.org)
118 $arrcode['nom-H'] = 20; // Nominal bar height in mm incl. numerals (estimated) not used when combined
119 break;
120 }
121
122 case 'IMB': { // IMB - Intelligent Mail Barcode - Onecode - USPS-B-3200
123 $xdim = 0.508; // Nominal value for X-dim (bar width) in mm (spec.)
124 $bpi = 22; // Bars per inch
125 // Ratio of Nominal value for width of spaces in mm / Nominal value for X-dim (bar width) in mm based on bars per inch
126 $this->gapwidth = ((25.4/$bpi) - $xdim)/$xdim;
127 $this->daft = array('D'=>2, 'A'=>2, 'F'=>3, 'T'=>1); // Descender; Ascender; Full; Tracker bar heights
128 $arrcode = $this->barcode_imb($code);
129 $arrcode['nom-X'] = $xdim ;
130 $arrcode['nom-H'] = 3.68; // Nominal value for Height of Full bar in mm (spec.)
131 // USPS-B-3200 Revision C = 4.623
132 // USPS-B-3200 Revision E = 3.68
133 $arrcode['quietL'] = 3.175; // LEFT Quiet margin = mm (spec.)
134 $arrcode['quietR'] = 3.175; // RIGHT Quiet margin = mm (spec.)
135 $arrcode['quietTB'] = 0.711; // TOP/BOTTOM Quiet margin = mm (spec.)
136 break;
137 }
138 case 'RM4SCC': { // RM4SCC (Royal Mail 4-state Customer Code) - CBC (Customer Bar Code)
139 $xdim = 0.508; // Nominal value for X-dim (bar width) in mm (spec.)
140 $bpi = 22; // Bars per inch
141 // Ratio of Nominal value for width of spaces in mm / Nominal value for X-dim (bar width) in mm based on bars per inch
142 $this->gapwidth = ((25.4/$bpi) - $xdim)/$xdim;
143 $this->daft = array('D'=>5, 'A'=>5, 'F'=>8, 'T'=>2); // Descender; Ascender; Full; Tracker bar heights
144 $arrcode = $this->barcode_rm4scc($code, false);
145 $arrcode['nom-X'] = $xdim ;
146 $arrcode['nom-H'] = 5.0; // Nominal value for Height of Full bar in mm (spec.)
147 $arrcode['quietL'] = 2; // LEFT Quiet margin = mm (spec.)
148 $arrcode['quietR'] = 2; // RIGHT Quiet margin = mm (spec.)
149 $arrcode['quietTB'] = 2; // TOP/BOTTOM Quiet margin = mm (spec?)
150 break;
151 }
152 case 'KIX': { // KIX (Klant index - Customer index)
153 $xdim = 0.508; // Nominal value for X-dim (bar width) in mm (spec.)
154 $bpi = 22; // Bars per inch
155 // Ratio of Nominal value for width of spaces in mm / Nominal value for X-dim (bar width) in mm based on bars per inch
156 $this->gapwidth = ((25.4/$bpi) - $xdim)/$xdim;
157 $this->daft = array('D'=>5, 'A'=>5, 'F'=>8, 'T'=>2); // Descender; Ascender; Full; Tracker bar heights
158 $arrcode = $this->barcode_rm4scc($code, true);
159 $arrcode['nom-X'] = $xdim ;
160 $arrcode['nom-H'] = 5.0; // Nominal value for Height of Full bar in mm (? spec.)
161 $arrcode['quietL'] = 2; // LEFT Quiet margin = mm (spec.)
162 $arrcode['quietR'] = 2; // RIGHT Quiet margin = mm (spec.)
163 $arrcode['quietTB'] = 2; // TOP/BOTTOM Quiet margin = mm (spec.)
164 break;
165 }
166 case 'POSTNET': { // POSTNET
167 $xdim = 0.508; // Nominal value for X-dim (bar width) in mm (spec.)
168 $bpi = 22; // Bars per inch
169 // Ratio of Nominal value for width of spaces in mm / Nominal value for X-dim (bar width) in mm based on bars per inch
170 $this->gapwidth = ((25.4/$bpi) - $xdim)/$xdim;
171 $arrcode = $this->barcode_postnet($code, false);
172 $arrcode['nom-X'] = $xdim ;
173 $arrcode['nom-H'] = 3.175; // Nominal value for Height of Full bar in mm (spec.)
174 $arrcode['quietL'] = 3.175; // LEFT Quiet margin = mm (?spec.)
175 $arrcode['quietR'] = 3.175; // RIGHT Quiet margin = mm (?spec.)
176 $arrcode['quietTB'] = 1.016; // TOP/BOTTOM Quiet margin = mm (?spec.)
177 break;
178 }
179 case 'PLANET': { // PLANET
180 $xdim = 0.508; // Nominal value for X-dim (bar width) in mm (spec.)
181 $bpi = 22; // Bars per inch
182 // Ratio of Nominal value for width of spaces in mm / Nominal value for X-dim (bar width) in mm based on bars per inch
183 $this->gapwidth = ((25.4/$bpi) - $xdim)/$xdim;
184 $arrcode = $this->barcode_postnet($code, true);
185 $arrcode['nom-X'] = $xdim ;
186 $arrcode['nom-H'] = 3.175; // Nominal value for Height of Full bar in mm (spec.)
187 $arrcode['quietL'] = 3.175; // LEFT Quiet margin = mm (?spec.)
188 $arrcode['quietR'] = 3.175; // RIGHT Quiet margin = mm (?spec.)
189 $arrcode['quietTB'] = 1.016; // TOP/BOTTOM Quiet margin = mm (?spec.)
190 break;
191 }
192
193 case 'C93': { // CODE 93 - USS-93
194 $arrcode = $this->barcode_code93($code);
195 if ($arrcode == false) { break; }
196 $arrcode['nom-X'] = 0.381; // Nominal value for X-dim (bar width) in mm (2 X min. spec.)
197 $arrcode['nom-H'] = 10; // Nominal value for Height of Full bar in mm (non-spec.)
198 $arrcode['lightmL'] = 10; // LEFT light margin = x X-dim (spec.)
199 $arrcode['lightmR'] = 10; // RIGHT light margin = x X-dim (spec.)
200 $arrcode['lightTB'] = 0; // TOP/BOTTOM light margin = x X-dim (non-spec.)
201 break;
202 }
203 case 'CODE11': { // CODE 11
204 if ($pr > 0) { $this->print_ratio = $pr; }
205 else { $this->print_ratio = 3; } // spec: Pr= 1:2.24 - 1:3.5
206 $arrcode = $this->barcode_code11($code);
207 if ($arrcode == false) { break; }
208 $arrcode['nom-X'] = 0.381; // Nominal value for X-dim (bar width) in mm (2 X min. spec.)
209 $arrcode['nom-H'] = 10; // Nominal value for Height of Full bar in mm (non-spec.)
210 $arrcode['lightmL'] = 10; // LEFT light margin = x X-dim (spec.)
211 $arrcode['lightmR'] = 10; // RIGHT light margin = x X-dim (spec.)
212 $arrcode['lightTB'] = 0; // TOP/BOTTOM light margin = x X-dim (non-spec.)
213 break;
214 }
215 case 'MSI': // MSI (Variation of Plessey code)
216 case 'MSI+': { // MSI + CHECKSUM (modulo 11)
217 if (strtoupper($type)=='MSI') { $arrcode = $this->barcode_msi($code, false); }
218 if (strtoupper($type)=='MSI+') { $arrcode = $this->barcode_msi($code, true); }
219 if ($arrcode == false) { break; }
220 $arrcode['nom-X'] = 0.381; // Nominal value for X-dim (bar width) in mm (2 X min. spec.)
221 $arrcode['nom-H'] = 10; // Nominal value for Height of Full bar in mm (non-spec.)
222 $arrcode['lightmL'] = 12; // LEFT light margin = x X-dim (spec.)
223 $arrcode['lightmR'] = 12; // RIGHT light margin = x X-dim (spec.)
224 $arrcode['lightTB'] = 0; // TOP/BOTTOM light margin = x X-dim (non-spec.)
225 break;
226 }
227 case 'CODABAR': { // CODABAR
228 if ($pr > 0) { $this->print_ratio = $pr; }
229 else { $this->print_ratio = 2.5; } // spec: Pr= 1:2 - 1:3 (>2.2 if X<0.50)
230 if (strtoupper($type)=='CODABAR') { $arrcode = $this->barcode_codabar($code); }
231 if ($arrcode == false) { break; }
232 $arrcode['nom-X'] = 0.381; // Nominal value for X-dim (bar width) in mm (2 X min. spec.)
233 $arrcode['nom-H'] = 10; // Nominal value for Height of Full bar in mm (non-spec.)
234 $arrcode['lightmL'] = 10; // LEFT light margin = x X-dim (spec.)
235 $arrcode['lightmR'] = 10; // RIGHT light margin = x X-dim (spec.)
236 $arrcode['lightTB'] = 0; // TOP/BOTTOM light margin = x X-dim (non-spec.)
237 break;
238 }
239 case 'C128A': // CODE 128 A
240 case 'C128B': // CODE 128 B
241 case 'C128C': // CODE 128 C
242 case 'EAN128A': // EAN 128 A
243 case 'EAN128B': // EAN 128 B
244 case 'EAN128C': { // EAN 128 C
245 if (strtoupper($type)=='C128A') { $arrcode = $this->barcode_c128($code, 'A'); }
246 if (strtoupper($type)=='C128B') { $arrcode = $this->barcode_c128($code, 'B'); }
247 if (strtoupper($type)=='C128C') { $arrcode = $this->barcode_c128($code, 'C'); }
248 if (strtoupper($type)=='EAN128A') { $arrcode = $this->barcode_c128($code, 'A', true); }
249 if (strtoupper($type)=='EAN128B') { $arrcode = $this->barcode_c128($code, 'B', true); }
250 if (strtoupper($type)=='EAN128C') { $arrcode = $this->barcode_c128($code, 'C', true); }
251 if ($arrcode == false) { break; }
252 $arrcode['nom-X'] = 0.381; // Nominal value for X-dim (bar width) in mm (2 X min. spec.)
253 $arrcode['nom-H'] = 10; // Nominal value for Height of Full bar in mm (non-spec.)
254 $arrcode['lightmL'] = 10; // LEFT light margin = x X-dim (spec.)
255 $arrcode['lightmR'] = 10; // RIGHT light margin = x X-dim (spec.)
256 $arrcode['lightTB'] = 0; // TOP/BOTTOM light margin = x X-dim (non-spec.)
257 break;
258 }
259 case 'C39': // CODE 39 - ANSI MH10.8M-1983 - USD-3 - 3 of 9.
260 case 'C39+': // CODE 39 with checksum
261 case 'C39E': // CODE 39 EXTENDED
262 case 'C39E+': { // CODE 39 EXTENDED + CHECKSUM
263 if ($pr > 0) { $this->print_ratio = $pr; }
264 else { $this->print_ratio = 2.5; } // spec: Pr= 1:2 - 1:3 (>2.2 if X<0.50)
265 $code = str_replace(chr(194).chr(160), ' ', $code); // mPDF 5.3.95 (for utf-8 encoded)
266 $code = str_replace(chr(160), ' ', $code); // mPDF 5.3.95 (for win-1252)
267 if (strtoupper($type)=='C39') { $arrcode = $this->barcode_code39($code, false, false); }
268 if (strtoupper($type)=='C39+') { $arrcode = $this->barcode_code39($code, false, true); }
269 if (strtoupper($type)=='C39E') { $arrcode = $this->barcode_code39($code, true, false); }
270 if (strtoupper($type)=='C39E+') { $arrcode = $this->barcode_code39($code, true, true); }
271 if ($arrcode == false) { break; }
272 $arrcode['nom-X'] = 0.381; // Nominal value for X-dim (bar width) in mm (2 X min. spec.)
273 $arrcode['nom-H'] = 10; // Nominal value for Height of Full bar in mm (non-spec.)
274 $arrcode['lightmL'] = 10; // LEFT light margin = x X-dim (spec.)
275 $arrcode['lightmR'] = 10; // RIGHT light margin = x X-dim (spec.)
276 $arrcode['lightTB'] = 0; // TOP/BOTTOM light margin = x X-dim (non-spec.)
277 break;
278 }
279 case 'S25': // Standard 2 of 5
280 case 'S25+': { // Standard 2 of 5 + CHECKSUM
281 if ($pr > 0) { $this->print_ratio = $pr; }
282 else { $this->print_ratio = 3; } // spec: Pr=1:3/1:4.5
283 if (strtoupper($type)=='S25') { $arrcode = $this->barcode_s25($code, false); }
284 if (strtoupper($type)=='S25+') { $arrcode = $this->barcode_s25($code, true); }
285 if ($arrcode == false) { break; }
286 $arrcode['nom-X'] = 0.381; // Nominal value for X-dim (bar width) in mm (2 X min. spec.)
287 $arrcode['nom-H'] = 10; // Nominal value for Height of Full bar in mm (non-spec.)
288 $arrcode['lightmL'] = 10; // LEFT light margin = x X-dim (spec.)
289 $arrcode['lightmR'] = 10; // RIGHT light margin = x X-dim (spec.)
290 $arrcode['lightTB'] = 0; // TOP/BOTTOM light margin = x X-dim (non-spec.)
291 break;
292 }
293 case 'I25': // Interleaved 2 of 5
294 case 'I25+': { // Interleaved 2 of 5 + CHECKSUM
295 if ($pr > 0) { $this->print_ratio = $pr; }
296 else { $this->print_ratio = 2.5; } // spec: Pr= 1:2 - 1:3 (>2.2 if X<0.50)
297 if (strtoupper($type)=='I25') { $arrcode = $this->barcode_i25($code, false); }
298 if (strtoupper($type)=='I25+') { $arrcode = $this->barcode_i25($code, true); }
299 if ($arrcode == false) { break; }
300 $arrcode['nom-X'] = 0.381; // Nominal value for X-dim (bar width) in mm (2 X min. spec.)
301 $arrcode['nom-H'] = 10; // Nominal value for Height of Full bar in mm (non-spec.)
302 $arrcode['lightmL'] = 10; // LEFT light margin = x X-dim (spec.)
303 $arrcode['lightmR'] = 10; // RIGHT light margin = x X-dim (spec.)
304 $arrcode['lightTB'] = 0; // TOP/BOTTOM light margin = x X-dim (non-spec.)
305 break;
306 }
307 case 'I25B': // Interleaved 2 of 5 + Bearer bars
308 case 'I25B+': { // Interleaved 2 of 5 + CHECKSUM + Bearer bars
309 if ($pr > 0) { $this->print_ratio = $pr; }
310 else { $this->print_ratio = 2.5; } // spec: Pr= 1:2 - 1:3 (>2.2 if X<0.50)
311 if (strtoupper($type)=='I25B') { $arrcode = $this->barcode_i25($code, false); }
312 if (strtoupper($type)=='I25B+') { $arrcode = $this->barcode_i25($code, true); }
313 if ($arrcode == false) { break; }
314 $arrcode['nom-X'] = 0.381; // Nominal value for X-dim (bar width) in mm (2 X min. spec.)
315 $arrcode['nom-H'] = 10; // Nominal value for Height of Full bar in mm (non-spec.)
316 $arrcode['lightmL'] = 10; // LEFT light margin = x X-dim (spec.)
317 $arrcode['lightmR'] = 10; // RIGHT light margin = x X-dim (spec.)
318 $arrcode['lightTB'] = 2; // TOP/BOTTOM light margin = x X-dim (non-spec.) - used for bearer bars
319 break;
320 }
321 default: {
322 $this->barcode_array = false;
323 }
324 }
325 $this->barcode_array = $arrcode;
326 }
327
328 /**
329 * CODE 39 - ANSI MH10.8M-1983 - USD-3 - 3 of 9.
330 */
331 protected function barcode_code39($code, $extended=false, $checksum=false) {
332 $chr['0'] = '111221211';
333 $chr['1'] = '211211112';
334 $chr['2'] = '112211112';
335 $chr['3'] = '212211111';
336 $chr['4'] = '111221112';
337 $chr['5'] = '211221111';
338 $chr['6'] = '112221111';
339 $chr['7'] = '111211212';
340 $chr['8'] = '211211211';
341 $chr['9'] = '112211211';
342 $chr['A'] = '211112112';
343 $chr['B'] = '112112112';
344 $chr['C'] = '212112111';
345 $chr['D'] = '111122112';
346 $chr['E'] = '211122111';
347 $chr['F'] = '112122111';
348 $chr['G'] = '111112212';
349 $chr['H'] = '211112211';
350 $chr['I'] = '112112211';
351 $chr['J'] = '111122211';
352 $chr['K'] = '211111122';
353 $chr['L'] = '112111122';
354 $chr['M'] = '212111121';
355 $chr['N'] = '111121122';
356 $chr['O'] = '211121121';
357 $chr['P'] = '112121121';
358 $chr['Q'] = '111111222';
359 $chr['R'] = '211111221';
360 $chr['S'] = '112111221';
361 $chr['T'] = '111121221';
362 $chr['U'] = '221111112';
363 $chr['V'] = '122111112';
364 $chr['W'] = '222111111';
365 $chr['X'] = '121121112';
366 $chr['Y'] = '221121111';
367 $chr['Z'] = '122121111';
368 $chr['-'] = '121111212';
369 $chr['.'] = '221111211';
370 $chr[' '] = '122111211';
371 $chr['$'] = '121212111';
372 $chr['/'] = '121211121';
373 $chr['+'] = '121112121';
374 $chr['%'] = '111212121';
375 $chr['*'] = '121121211';
376
377 $code = strtoupper($code);
378 if ($extended) {
379 // extended mode
380 $code = $this->encode_code39_ext($code);
381 }
382 if ($code === false) {
383 return false;
384 }
385 if ($checksum) {
386 // checksum
387 $checkdigit = $this->checksum_code39($code);
388 $code .= $checkdigit ;
389 }
390 // add start and stop codes
391 $code = '*'.$code.'*';
392
393 $bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 1, 'bcode' => array());
394 $k = 0;
395 $clen = strlen($code);
396 for ($i = 0; $i < $clen; ++$i) {
397 $char = $code[$i];
398 if(!isset($chr[$char])) {
399 // invalid character
400 return false;
401 }
402 for ($j = 0; $j < 9; ++$j) {
403 if (($j % 2) == 0) {
404 $t = true; // bar
405 } else {
406 $t = false; // space
407 }
408 $x = $chr[$char][$j];
409 if ($x == 2) { $w = $this->print_ratio; }
410 else { $w = 1; }
411
412 $bararray['bcode'][$k] = array('t' => $t, 'w' => $w, 'h' => 1, 'p' => 0);
413 $bararray['maxw'] += $w;
414 ++$k;
415 }
416 $bararray['bcode'][$k] = array('t' => false, 'w' => 1, 'h' => 1, 'p' => 0);
417 $bararray['maxw'] += 1;
418 ++$k;
419 }
420 $bararray['checkdigit'] = $checkdigit;
421 return $bararray;
422 }
423
424 /**
425 * Encode a string to be used for CODE 39 Extended mode.
426 */
427 protected function encode_code39_ext($code) {
428 $encode = array(
429 chr(0) => '%U', chr(1) => '$A', chr(2) => '$B', chr(3) => '$C',
430 chr(4) => '$D', chr(5) => '$E', chr(6) => '$F', chr(7) => '$G',
431 chr(8) => '$H', chr(9) => '$I', chr(10) => '$J', chr(11) => '£K',
432 chr(12) => '$L', chr(13) => '$M', chr(14) => '$N', chr(15) => '$O',
433 chr(16) => '$P', chr(17) => '$Q', chr(18) => '$R', chr(19) => '$S',
434 chr(20) => '$T', chr(21) => '$U', chr(22) => '$V', chr(23) => '$W',
435 chr(24) => '$X', chr(25) => '$Y', chr(26) => '$Z', chr(27) => '%A',
436 chr(28) => '%B', chr(29) => '%C', chr(30) => '%D', chr(31) => '%E',
437 chr(32) => ' ', chr(33) => '/A', chr(34) => '/B', chr(35) => '/C',
438 chr(36) => '/D', chr(37) => '/E', chr(38) => '/F', chr(39) => '/G',
439 chr(40) => '/H', chr(41) => '/I', chr(42) => '/J', chr(43) => '/K',
440 chr(44) => '/L', chr(45) => '-', chr(46) => '.', chr(47) => '/O',
441 chr(48) => '0', chr(49) => '1', chr(50) => '2', chr(51) => '3',
442 chr(52) => '4', chr(53) => '5', chr(54) => '6', chr(55) => '7',
443 chr(56) => '8', chr(57) => '9', chr(58) => '/Z', chr(59) => '%F',
444 chr(60) => '%G', chr(61) => '%H', chr(62) => '%I', chr(63) => '%J',
445 chr(64) => '%V', chr(65) => 'A', chr(66) => 'B', chr(67) => 'C',
446 chr(68) => 'D', chr(69) => 'E', chr(70) => 'F', chr(71) => 'G',
447 chr(72) => 'H', chr(73) => 'I', chr(74) => 'J', chr(75) => 'K',
448 chr(76) => 'L', chr(77) => 'M', chr(78) => 'N', chr(79) => 'O',
449 chr(80) => 'P', chr(81) => 'Q', chr(82) => 'R', chr(83) => 'S',
450 chr(84) => 'T', chr(85) => 'U', chr(86) => 'V', chr(87) => 'W',
451 chr(88) => 'X', chr(89) => 'Y', chr(90) => 'Z', chr(91) => '%K',
452 chr(92) => '%L', chr(93) => '%M', chr(94) => '%N', chr(95) => '%O',
453 chr(96) => '%W', chr(97) => '+A', chr(98) => '+B', chr(99) => '+C',
454 chr(100) => '+D', chr(101) => '+E', chr(102) => '+F', chr(103) => '+G',
455 chr(104) => '+H', chr(105) => '+I', chr(106) => '+J', chr(107) => '+K',
456 chr(108) => '+L', chr(109) => '+M', chr(110) => '+N', chr(111) => '+O',
457 chr(112) => '+P', chr(113) => '+Q', chr(114) => '+R', chr(115) => '+S',
458 chr(116) => '+T', chr(117) => '+U', chr(118) => '+V', chr(119) => '+W',
459 chr(120) => '+X', chr(121) => '+Y', chr(122) => '+Z', chr(123) => '%P',
460 chr(124) => '%Q', chr(125) => '%R', chr(126) => '%S', chr(127) => '%T');
461 $code_ext = '';
462 $clen = strlen($code);
463 for ($i = 0 ; $i < $clen; ++$i) {
464 if (ord($code[$i]) > 127) {
465 return false;
466 }
467 $code_ext .= $encode[$code[$i]];
468 }
469 return $code_ext;
470 }
471
472 /**
473 * Calculate CODE 39 checksum (modulo 43).
474 */
475 protected function checksum_code39($code) {
476 $chars = array(
477 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
478 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
479 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
480 'W', 'X', 'Y', 'Z', '-', '.', ' ', '$', '/', '+', '%');
481 $sum = 0;
482 $clen = strlen($code);
483 for ($i = 0 ; $i < $clen; ++$i) {
484 $k = array_keys($chars, $code[$i]);
485 $sum += $k[0];
486 }
487 $j = ($sum % 43);
488 return $chars[$j];
489 }
490
491 /**
492 * CODE 93 - USS-93
493 * Compact code similar to Code 39
494 */
495 protected function barcode_code93($code) {
496 $chr['0'] = '131112';
497 $chr['1'] = '111213';
498 $chr['2'] = '111312';
499 $chr['3'] = '111411';
500 $chr['4'] = '121113';
501 $chr['5'] = '121212';
502 $chr['6'] = '121311';
503 $chr['7'] = '111114';
504 $chr['8'] = '131211';
505 $chr['9'] = '141111';
506 $chr['A'] = '211113';
507 $chr['B'] = '211212';
508 $chr['C'] = '211311';
509 $chr['D'] = '221112';
510 $chr['E'] = '221211';
511 $chr['F'] = '231111';
512 $chr['G'] = '112113';
513 $chr['H'] = '112212';
514 $chr['I'] = '112311';
515 $chr['J'] = '122112';
516 $chr['K'] = '132111';
517 $chr['L'] = '111123';
518 $chr['M'] = '111222';
519 $chr['N'] = '111321';
520 $chr['O'] = '121122';
521 $chr['P'] = '131121';
522 $chr['Q'] = '212112';
523 $chr['R'] = '212211';
524 $chr['S'] = '211122';
525 $chr['T'] = '211221';
526 $chr['U'] = '221121';
527 $chr['V'] = '222111';
528 $chr['W'] = '112122';
529 $chr['X'] = '112221';
530 $chr['Y'] = '122121';
531 $chr['Z'] = '123111';
532 $chr['-'] = '121131';
533 $chr['.'] = '311112';
534 $chr[' '] = '311211';
535 $chr['$'] = '321111';
536 $chr['/'] = '112131';
537 $chr['+'] = '113121';
538 $chr['%'] = '211131';
539 $chr[128] = '121221'; // ($)
540 $chr[129] = '311121'; // (/)
541 $chr[130] = '122211'; // (+)
542 $chr[131] = '312111'; // (%)
543 $chr['*'] = '111141';
544 $code = strtoupper($code);
545 $encode = array(
546 chr(0) => chr(131).'U', chr(1) => chr(128).'A', chr(2) => chr(128).'B', chr(3) => chr(128).'C',
547 chr(4) => chr(128).'D', chr(5) => chr(128).'E', chr(6) => chr(128).'F', chr(7) => chr(128).'G',
548 chr(8) => chr(128).'H', chr(9) => chr(128).'I', chr(10) => chr(128).'J', chr(11) => '£K',
549 chr(12) => chr(128).'L', chr(13) => chr(128).'M', chr(14) => chr(128).'N', chr(15) => chr(128).'O',
550 chr(16) => chr(128).'P', chr(17) => chr(128).'Q', chr(18) => chr(128).'R', chr(19) => chr(128).'S',
551 chr(20) => chr(128).'T', chr(21) => chr(128).'U', chr(22) => chr(128).'V', chr(23) => chr(128).'W',
552 chr(24) => chr(128).'X', chr(25) => chr(128).'Y', chr(26) => chr(128).'Z', chr(27) => chr(131).'A',
553 chr(28) => chr(131).'B', chr(29) => chr(131).'C', chr(30) => chr(131).'D', chr(31) => chr(131).'E',
554 chr(32) => ' ', chr(33) => chr(129).'A', chr(34) => chr(129).'B', chr(35) => chr(129).'C',
555 chr(36) => chr(129).'D', chr(37) => chr(129).'E', chr(38) => chr(129).'F', chr(39) => chr(129).'G',
556 chr(40) => chr(129).'H', chr(41) => chr(129).'I', chr(42) => chr(129).'J', chr(43) => chr(129).'K',
557 chr(44) => chr(129).'L', chr(45) => '-', chr(46) => '.', chr(47) => chr(129).'O',
558 chr(48) => '0', chr(49) => '1', chr(50) => '2', chr(51) => '3',
559 chr(52) => '4', chr(53) => '5', chr(54) => '6', chr(55) => '7',
560 chr(56) => '8', chr(57) => '9', chr(58) => chr(129).'Z', chr(59) => chr(131).'F',
561 chr(60) => chr(131).'G', chr(61) => chr(131).'H', chr(62) => chr(131).'I', chr(63) => chr(131).'J',
562 chr(64) => chr(131).'V', chr(65) => 'A', chr(66) => 'B', chr(67) => 'C',
563 chr(68) => 'D', chr(69) => 'E', chr(70) => 'F', chr(71) => 'G',
564 chr(72) => 'H', chr(73) => 'I', chr(74) => 'J', chr(75) => 'K',
565 chr(76) => 'L', chr(77) => 'M', chr(78) => 'N', chr(79) => 'O',
566 chr(80) => 'P', chr(81) => 'Q', chr(82) => 'R', chr(83) => 'S',
567 chr(84) => 'T', chr(85) => 'U', chr(86) => 'V', chr(87) => 'W',
568 chr(88) => 'X', chr(89) => 'Y', chr(90) => 'Z', chr(91) => chr(131).'K',
569 chr(92) => chr(131).'L', chr(93) => chr(131).'M', chr(94) => chr(131).'N', chr(95) => chr(131).'O',
570 chr(96) => chr(131).'W', chr(97) => chr(130).'A', chr(98) => chr(130).'B', chr(99) => chr(130).'C',
571 chr(100) => chr(130).'D', chr(101) => chr(130).'E', chr(102) => chr(130).'F', chr(103) => chr(130).'G',
572 chr(104) => chr(130).'H', chr(105) => chr(130).'I', chr(106) => chr(130).'J', chr(107) => chr(130).'K',
573 chr(108) => chr(130).'L', chr(109) => chr(130).'M', chr(110) => chr(130).'N', chr(111) => chr(130).'O',
574 chr(112) => chr(130).'P', chr(113) => chr(130).'Q', chr(114) => chr(130).'R', chr(115) => chr(130).'S',
575 chr(116) => chr(130).'T', chr(117) => chr(130).'U', chr(118) => chr(130).'V', chr(119) => chr(130).'W',
576 chr(120) => chr(130).'X', chr(121) => chr(130).'Y', chr(122) => chr(130).'Z', chr(123) => chr(131).'P',
577 chr(124) => chr(131).'Q', chr(125) => chr(131).'R', chr(126) => chr(131).'S', chr(127) => chr(131).'T');
578 $code_ext = '';
579 $clen = strlen($code);
580 for ($i = 0 ; $i < $clen; ++$i) {
581 if (ord($code[$i]) > 127) {
582 return false;
583 }
584 $code_ext .= $encode[$code[$i]];
585 }
586 // checksum
587 $checkdigit = $this->checksum_code93($code);
588 $code .= $checkdigit ;
589 // add start and stop codes
590 $code = '*'.$code.'*';
591 $bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 1, 'bcode' => array());
592 $k = 0;
593 $clen = strlen($code);
594 for ($i = 0; $i < $clen; ++$i) {
595 $char = $code[$i];
596 if(!isset($chr[$char])) {
597 // invalid character
598 return false;
599 }
600 for ($j = 0; $j < 6; ++$j) {
601 if (($j % 2) == 0) {
602 $t = true; // bar
603 } else {
604 $t = false; // space
605 }
606 $w = $chr[$char][$j];
607 $bararray['bcode'][$k] = array('t' => $t, 'w' => $w, 'h' => 1, 'p' => 0);
608 $bararray['maxw'] += $w;
609 ++$k;
610 }
611 }
612 $bararray['bcode'][$k] = array('t' => true, 'w' => 1, 'h' => 1, 'p' => 0);
613 $bararray['maxw'] += 1;
614 ++$k;
615 $bararray['checkdigit'] = $checkdigit;
616 return $bararray;
617 }
618
619 /**
620 * Calculate CODE 93 checksum (modulo 47).
621 */
622 protected function checksum_code93($code) {
623 $chars = array(
624 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
625 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
626 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
627 'W', 'X', 'Y', 'Z', '-', '.', ' ', '$', '/', '+', '%');
628 // translate special characters
629 $code = strtr($code, chr(128).chr(129).chr(130).chr(131), '$/+%');
630 $len = strlen($code);
631 // calculate check digit C
632 $p = 1;
633 $check = 0;
634 for ($i = ($len - 1); $i >= 0; --$i) {
635 $k = array_keys($chars, $code[$i]);
636 $check += ($k[0] * $p);
637 ++$p;
638 if ($p > 20) {
639 $p = 1;
640 }
641 }
642 $check %= 47;
643 $c = $chars[$check];
644 $code .= $c;
645 // calculate check digit K
646 $p = 1;
647 $check = 0;
648 for ($i = $len; $i >= 0; --$i) {
649 $k = array_keys($chars, $code[$i]);
650 $check += ($k[0] * $p);
651 ++$p;
652 if ($p > 15) {
653 $p = 1;
654 }
655 }
656 $check %= 47;
657 $k = $chars[$check];
658 return $c.$k;
659 }
660
661 /**
662 * Checksum for standard 2 of 5 barcodes.
663 */
664 protected function checksum_s25($code) {
665 $len = strlen($code);
666 $sum = 0;
667 for ($i = 0; $i < $len; $i+=2) {
668 $sum += $code[$i];
669 }
670 $sum *= 3;
671 for ($i = 1; $i < $len; $i+=2) {
672 $sum += ($code[$i]);
673 }
674 $r = $sum % 10;
675 if($r > 0) {
676 $r = (10 - $r);
677 }
678 return $r;
679 }
680
681 /**
682 * MSI.
683 * Variation of Plessey code, with similar applications
684 * Contains digits (0 to 9) and encodes the data only in the width of bars.
685 */
686 protected function barcode_msi($code, $checksum=false) {
687 $chr['0'] = '100100100100';
688 $chr['1'] = '100100100110';
689 $chr['2'] = '100100110100';
690 $chr['3'] = '100100110110';
691 $chr['4'] = '100110100100';
692 $chr['5'] = '100110100110';
693 $chr['6'] = '100110110100';
694 $chr['7'] = '100110110110';
695 $chr['8'] = '110100100100';
696 $chr['9'] = '110100100110';
697 $chr['A'] = '110100110100';
698 $chr['B'] = '110100110110';
699 $chr['C'] = '110110100100';
700 $chr['D'] = '110110100110';
701 $chr['E'] = '110110110100';
702 $chr['F'] = '110110110110';
703 if ($checksum) {
704 // add checksum
705 $clen = strlen($code);
706 $p = 2;
707 $check = 0;
708 for ($i = ($clen - 1); $i >= 0; --$i) {
709 $check += (hexdec($code[$i]) * $p);
710 ++$p;
711 if ($p > 7) {
712 $p = 2;
713 }
714 }
715 $check %= 11;
716 if ($check > 0) {
717 $check = 11 - $check;
718 }
719 $code .= $check;
720 $checkdigit = $check;
721 }
722 $seq = '110'; // left guard
723 $clen = strlen($code);
724 for ($i = 0; $i < $clen; ++$i) {
725 $digit = $code[$i];
726 if (!isset($chr[$digit])) {
727 // invalid character
728 return false;
729 }
730 $seq .= $chr[$digit];
731 }
732 $seq .= '1001'; // right guard
733 $bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 1, 'bcode' => array());
734 $bararray['checkdigit'] = $checkdigit;
735 return $this->binseq_to_array($seq, $bararray);
736 }
737
738 /**
739 * Standard 2 of 5 barcodes.
740 * Used in airline ticket marking, photofinishing
741 * Contains digits (0 to 9) and encodes the data only in the width of bars.
742 */
743 protected function barcode_s25($code, $checksum=false) {
744 $chr['0'] = '10101110111010';
745 $chr['1'] = '11101010101110';
746 $chr['2'] = '10111010101110';
747 $chr['3'] = '11101110101010';
748 $chr['4'] = '10101110101110';
749 $chr['5'] = '11101011101010';
750 $chr['6'] = '10111011101010';
751 $chr['7'] = '10101011101110';
752 $chr['8'] = '10101110111010';
753 $chr['9'] = '10111010111010';
754 if ($checksum) {
755 // add checksum
756 $checkdigit = $this->checksum_s25($code);
757 $code .= $checkdigit ;
758 }
759 if((strlen($code) % 2) != 0) {
760 // add leading zero if code-length is odd
761 $code = '0'.$code;
762 }
763 $seq = '11011010';
764 $clen = strlen($code);
765 for ($i = 0; $i < $clen; ++$i) {
766 $digit = $code[$i];
767 if (!isset($chr[$digit])) {
768 // invalid character
769 return false;
770 }
771 $seq .= $chr[$digit];
772 }
773 $seq .= '1101011';
774 $bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 1, 'bcode' => array());
775 $bararray['checkdigit'] = $checkdigit;
776 return $this->binseq_to_array($seq, $bararray);
777 }
778
779 /**
780 * Convert binary barcode sequence to barcode array
781 */
782 protected function binseq_to_array($seq, $bararray) {
783 $len = strlen($seq);
784 $w = 0;
785 $k = 0;
786 for ($i = 0; $i < $len; ++$i) {
787 $w += 1;
788 if (($i == ($len - 1)) OR (($i < ($len - 1)) AND ($seq[$i] != $seq[($i+1)]))) {
789 if ($seq[$i] == '1') {
790 $t = true; // bar
791 } else {
792 $t = false; // space
793 }
794 $bararray['bcode'][$k] = array('t' => $t, 'w' => $w, 'h' => 1, 'p' => 0);
795 $bararray['maxw'] += $w;
796 ++$k;
797 $w = 0;
798 }
799 }
800 return $bararray;
801 }
802
803 /**
804 * Interleaved 2 of 5 barcodes.
805 * Compact numeric code, widely used in industry, air cargo
806 * Contains digits (0 to 9) and encodes the data in the width of both bars and spaces.
807 */
808 protected function barcode_i25($code, $checksum=false) {
809 $chr['0'] = '11221';
810 $chr['1'] = '21112';
811 $chr['2'] = '12112';
812 $chr['3'] = '22111';
813 $chr['4'] = '11212';
814 $chr['5'] = '21211';
815 $chr['6'] = '12211';
816 $chr['7'] = '11122';
817 $chr['8'] = '21121';
818 $chr['9'] = '12121';
819 $chr['A'] = '11';
820 $chr['Z'] = '21';
821 if ($checksum) {
822 // add checksum
823 $checkdigit = $this->checksum_s25($code);
824 $code .= $checkdigit ;
825 }
826 if((strlen($code) % 2) != 0) {
827 // add leading zero if code-length is odd
828 $code = '0'.$code;
829 }
830 // add start and stop codes
831 $code = 'AA'.strtolower($code).'ZA';
832
833 $bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 1, 'bcode' => array());
834 $k = 0;
835 $clen = strlen($code);
836 for ($i = 0; $i < $clen; $i = ($i + 2)) {
837 $char_bar = $code[$i];
838 $char_space = $code[$i+1];
839 if((!isset($chr[$char_bar])) OR (!isset($chr[$char_space]))) {
840 // invalid character
841 return false;
842 }
843 // create a bar-space sequence
844 $seq = '';
845 $chrlen = strlen($chr[$char_bar]);
846 for ($s = 0; $s < $chrlen; $s++){
847 $seq .= $chr[$char_bar][$s] . $chr[$char_space][$s];
848 }
849 $seqlen = strlen($seq);
850 for ($j = 0; $j < $seqlen; ++$j) {
851 if (($j % 2) == 0) {
852 $t = true; // bar
853 } else {
854 $t = false; // space
855 }
856 $x = $seq[$j];
857 if ($x == 2) { $w = $this->print_ratio; }
858 else { $w = 1; }
859
860 $bararray['bcode'][$k] = array('t' => $t, 'w' => $w, 'h' => 1, 'p' => 0);
861 $bararray['maxw'] += $w;
862 ++$k;
863 }
864 }
865 $bararray['checkdigit'] = $checkdigit;
866 return $bararray;
867 }
868
869 /**
870 * C128 barcodes.
871 * Very capable code, excellent density, high reliability; in very wide use world-wide
872 */
873 protected function barcode_c128($code, $type='B', $ean=false) {
874 $chr = array(
875 '212222', /* 00 */
876 '222122', /* 01 */
877 '222221', /* 02 */
878 '121223', /* 03 */
879 '121322', /* 04 */
880 '131222', /* 05 */
881 '122213', /* 06 */
882 '122312', /* 07 */
883 '132212', /* 08 */
884 '221213', /* 09 */
885 '221312', /* 10 */
886 '231212', /* 11 */
887 '112232', /* 12 */
888 '122132', /* 13 */
889 '122231', /* 14 */
890 '113222', /* 15 */
891 '123122', /* 16 */
892 '123221', /* 17 */
893 '223211', /* 18 */
894 '221132', /* 19 */
895 '221231', /* 20 */
896 '213212', /* 21 */
897 '223112', /* 22 */
898 '312131', /* 23 */
899 '311222', /* 24 */
900 '321122', /* 25 */
901 '321221', /* 26 */
902 '312212', /* 27 */
903 '322112', /* 28 */
904 '322211', /* 29 */
905 '212123', /* 30 */
906 '212321', /* 31 */
907 '232121', /* 32 */
908 '111323', /* 33 */
909 '131123', /* 34 */
910 '131321', /* 35 */
911 '112313', /* 36 */
912 '132113', /* 37 */
913 '132311', /* 38 */
914 '211313', /* 39 */
915 '231113', /* 40 */
916 '231311', /* 41 */
917 '112133', /* 42 */
918 '112331', /* 43 */
919 '132131', /* 44 */
920 '113123', /* 45 */
921 '113321', /* 46 */
922 '133121', /* 47 */
923 '313121', /* 48 */
924 '211331', /* 49 */
925 '231131', /* 50 */
926 '213113', /* 51 */
927 '213311', /* 52 */
928 '213131', /* 53 */
929 '311123', /* 54 */
930 '311321', /* 55 */
931 '331121', /* 56 */
932 '312113', /* 57 */
933 '312311', /* 58 */
934 '332111', /* 59 */
935 '314111', /* 60 */
936 '221411', /* 61 */
937 '431111', /* 62 */
938 '111224', /* 63 */
939 '111422', /* 64 */
940 '121124', /* 65 */
941 '121421', /* 66 */
942 '141122', /* 67 */
943 '141221', /* 68 */
944 '112214', /* 69 */
945 '112412', /* 70 */
946 '122114', /* 71 */
947 '122411', /* 72 */
948 '142112', /* 73 */
949 '142211', /* 74 */
950 '241211', /* 75 */
951 '221114', /* 76 */
952 '413111', /* 77 */
953 '241112', /* 78 */
954 '134111', /* 79 */
955 '111242', /* 80 */
956 '121142', /* 81 */
957 '121241', /* 82 */
958 '114212', /* 83 */
959 '124112', /* 84 */
960 '124211', /* 85 */
961 '411212', /* 86 */
962 '421112', /* 87 */
963 '421211', /* 88 */
964 '212141', /* 89 */
965 '214121', /* 90 */
966 '412121', /* 91 */
967 '111143', /* 92 */
968 '111341', /* 93 */
969 '131141', /* 94 */
970 '114113', /* 95 */
971 '114311', /* 96 */
972 '411113', /* 97 */
973 '411311', /* 98 */
974 '113141', /* 99 */
975 '114131', /* 100 */
976 '311141', /* 101 */
977 '411131', /* 102 */
978 '211412', /* 103 START A */
979 '211214', /* 104 START B */
980 '211232', /* 105 START C */
981 '233111', /* STOP */
982 '200000' /* END */
983 );
984 $keys = '';
985 switch(strtoupper($type)) {
986 case 'A': {
987 $startid = 103;
988 $keys = ' !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_';
989 for ($i = 0; $i < 32; ++$i) {
990 $keys .= chr($i);
991 }
992 break;
993 }
994 case 'B': {
995 $startid = 104;
996 $keys = ' !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~'.chr(127);
997 break;
998 }
999 case 'C': {
1000 $startid = 105;
1001 $keys = '';
1002 if ((strlen($code) % 2) != 0) {
1003 // The length of barcode value must be even ($code). You must pad the number with zeros
1004 return false;
1005 }
1006 for ($i = 0; $i <= 99; ++$i) {
1007 $keys .= chr($i);
1008 }
1009 $new_code = '';
1010 $hclen = (strlen($code) / 2);
1011 for ($i = 0; $i < $hclen; ++$i) {
1012 $new_code .= chr(intval($code{(2 * $i)}.$code{(2 * $i + 1)}));
1013 }
1014 $code = $new_code;
1015 break;
1016 }
1017 default: {
1018 return false;
1019 }
1020 }
1021
1022 // calculate check character
1023 $sum = $startid;
1024 if ($ean) { $code = chr(102) . $code; } // Add FNC 1 - which identifies it as EAN-128
1025 $clen = strlen($code);
1026 for ($i = 0; $i < $clen; ++$i) {
1027 if ($ean && $i==0) { $sum += 102; }
1028 else { $sum += (strpos($keys, $code[$i]) * ($i+1)); }
1029 }
1030 $check = ($sum % 103);
1031 $checkdigit = $check ;
1032 // add start, check and stop codes
1033 $code = chr($startid).$code.chr($check).chr(106).chr(107);
1034 $bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 1, 'bcode' => array());
1035 $k = 0;
1036 $len = strlen($code);
1037 for ($i = 0; $i < $len; ++$i) {
1038 $ck = strpos($keys, $code[$i]);
1039 if (($i == 0) || ($ean && $i==1) | ($i > ($len-4))) {
1040 $char_num = ord($code[$i]);
1041 $seq = $chr[$char_num];
1042 } elseif(($ck >= 0) AND isset($chr[$ck])) {
1043 $seq = $chr[$ck];
1044 } else {
1045 // invalid character
1046 return false;
1047 }
1048 for ($j = 0; $j < 6; ++$j) {
1049 if (($j % 2) == 0) {
1050 $t = true; // bar
1051 } else {
1052 $t = false; // space
1053 }
1054 $w = $seq[$j];
1055 $bararray['bcode'][$k] = array('t' => $t, 'w' => $w, 'h' => 1, 'p' => 0);
1056 $bararray['maxw'] += $w;
1057 ++$k;
1058 }
1059 }
1060 $bararray['checkdigit'] = $checkdigit;
1061 return $bararray;
1062 }
1063
1064 /**
1065 * EAN13 and UPC-A barcodes.
1066 * EAN13: European Article Numbering international retail product code
1067 * UPC-A: Universal product code seen on almost all retail products in the USA and Canada
1068 * UPC-E: Short version of UPC symbol
1069 */
1070 protected function barcode_eanupc($code, $len=13) {
1071 $upce = false;
1072 $checkdigit = false;
1073 if ($len == 6) {
1074 $len = 12; // UPC-A
1075 $upce = true; // UPC-E mode
1076 }
1077 $data_len = $len - 1;
1078 //Padding
1079 $code = str_pad($code, $data_len, '0', STR_PAD_LEFT);
1080 $code_len = strlen($code);
1081 // calculate check digit
1082 $sum_a = 0;
1083 for ($i = 1; $i < $data_len; $i+=2) {
1084 $sum_a += $code[$i];
1085 }
1086 if ($len > 12) {
1087 $sum_a *= 3;
1088 }
1089 $sum_b = 0;
1090 for ($i = 0; $i < $data_len; $i+=2) {
1091 $sum_b += ($code[$i]);
1092 }
1093 if ($len < 13) {
1094 $sum_b *= 3;
1095 }
1096 $r = ($sum_a + $sum_b) % 10;
1097 if($r > 0) {
1098 $r = (10 - $r);
1099 }
1100 if ($code_len == $data_len) {
1101 // add check digit
1102 $code .= $r;
1103 $checkdigit = $r;
1104 } elseif ($r !== intval($code[$data_len])) {
1105 // wrong checkdigit
1106 return false;
1107 }
1108 if ($len == 12) {
1109 // UPC-A
1110 $code = '0'.$code;
1111 ++$len;
1112 }
1113 if ($upce) {
1114 // convert UPC-A to UPC-E
1115 $tmp = substr($code, 4, 3);
1116 $prod_code = intval(substr($code,7,5)); // product code
1117 $invalid_upce = false;
1118 if (($tmp == '000') OR ($tmp == '100') OR ($tmp == '200')) {
1119 // manufacturer code ends in 000, 100, or 200
1120 $upce_code = substr($code, 2, 2).substr($code, 9, 3).substr($code, 4, 1);
1121 if ($prod_code > 999) { $invalid_upce = true; }
1122 } else {
1123 $tmp = substr($code, 5, 2);
1124 if ($tmp == '00') {
1125 // manufacturer code ends in 00
1126 $upce_code = substr($code, 2, 3).substr($code, 10, 2).'3';
1127 if ($prod_code > 99) { $invalid_upce = true; }
1128 } else {
1129 $tmp = substr($code, 6, 1);
1130 if ($tmp == '0') {
1131 // manufacturer code ends in 0
1132 $upce_code = substr($code, 2, 4).substr($code, 11, 1).'4';
1133 if ($prod_code > 9) { $invalid_upce = true; }
1134 } else {
1135 // manufacturer code does not end in zero
1136 $upce_code = substr($code, 2, 5).substr($code, 11, 1);
1137 if ($prod_code > 9) { $invalid_upce = true; }
1138 }
1139 }
1140 }
1141 if ($invalid_upce) { die("Error - UPC-A cannot produce a valid UPC-E barcode"); } // Error generating a UPCE code
1142 }
1143 //Convert digits to bars
1144 $codes = array(
1145 'A'=>array( // left odd parity
1146 '0'=>'0001101',
1147 '1'=>'0011001',
1148 '2'=>'0010011',
1149 '3'=>'0111101',
1150 '4'=>'0100011',
1151 '5'=>'0110001',
1152 '6'=>'0101111',
1153 '7'=>'0111011',
1154 '8'=>'0110111',
1155 '9'=>'0001011'),
1156 'B'=>array( // left even parity
1157 '0'=>'0100111',
1158 '1'=>'0110011',
1159 '2'=>'0011011',
1160 '3'=>'0100001',
1161 '4'=>'0011101',
1162 '5'=>'0111001',
1163 '6'=>'0000101',
1164 '7'=>'0010001',
1165 '8'=>'0001001',
1166 '9'=>'0010111'),
1167 'C'=>array( // right
1168 '0'=>'1110010',
1169 '1'=>'1100110',
1170 '2'=>'1101100',
1171 '3'=>'1000010',
1172 '4'=>'1011100',
1173 '5'=>'1001110',
1174 '6'=>'1010000',
1175 '7'=>'1000100',
1176 '8'=>'1001000',
1177 '9'=>'1110100')
1178 );
1179 $parities = array(
1180 '0'=>array('A','A','A','A','A','A'),
1181 '1'=>array('A','A','B','A','B','B'),
1182 '2'=>array('A','A','B','B','A','B'),
1183 '3'=>array('A','A','B','B','B','A'),
1184 '4'=>array('A','B','A','A','B','B'),
1185 '5'=>array('A','B','B','A','A','B'),
1186 '6'=>array('A','B','B','B','A','A'),
1187 '7'=>array('A','B','A','B','A','B'),
1188 '8'=>array('A','B','A','B','B','A'),
1189 '9'=>array('A','B','B','A','B','A')
1190 );
1191 $upce_parities = array();
1192 $upce_parities[0] = array(
1193 '0'=>array('B','B','B','A','A','A'),
1194 '1'=>array('B','B','A','B','A','A'),
1195 '2'=>array('B','B','A','A','B','A'),
1196 '3'=>array('B','B','A','A','A','B'),
1197 '4'=>array('B','A','B','B','A','A'),
1198 '5'=>array('B','A','A','B','B','A'),
1199 '6'=>array('B','A','A','A','B','B'),
1200 '7'=>array('B','A','B','A','B','A'),
1201 '8'=>array('B','A','B','A','A','B'),
1202 '9'=>array('B','A','A','B','A','B')
1203 );
1204 $upce_parities[1] = array(
1205 '0'=>array('A','A','A','B','B','B'),
1206 '1'=>array('A','A','B','A','B','B'),
1207 '2'=>array('A','A','B','B','A','B'),
1208 '3'=>array('A','A','B','B','B','A'),
1209 '4'=>array('A','B','A','A','B','B'),
1210 '5'=>array('A','B','B','A','A','B'),
1211 '6'=>array('A','B','B','B','A','A'),
1212 '7'=>array('A','B','A','B','A','B'),
1213 '8'=>array('A','B','A','B','B','A'),
1214 '9'=>array('A','B','B','A','B','A')
1215 );
1216 $k = 0;
1217 $seq = '101'; // left guard bar
1218 if ($upce) {
1219 $bararray = array('code' => $upce_code, 'maxw' => 0, 'maxh' => 1, 'bcode' => array());
1220 $p = $upce_parities[$code{1}][$r];
1221 for ($i = 0; $i < 6; ++$i) {
1222 $seq .= $codes[$p[$i]][$upce_code[$i]];
1223 }
1224 $seq .= '010101'; // right guard bar
1225 } else {
1226 $bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 1, 'bcode' => array());
1227 $half_len = ceil($len / 2);
1228 if ($len == 8) {
1229 for ($i = 0; $i < $half_len; ++$i) {
1230 $seq .= $codes['A'][$code[$i]];
1231 }
1232 } else {
1233 $p = $parities[$code{0}];
1234 for ($i = 1; $i < $half_len; ++$i) {
1235 $seq .= $codes[$p[$i-1]][$code[$i]];
1236 }
1237 }
1238 $seq .= '01010'; // center guard bar
1239 for ($i = $half_len; $i < $len; ++$i) {
1240 $seq .= $codes['C'][$code[$i]];
1241 }
1242 $seq .= '101'; // right guard bar
1243 }
1244 $clen = strlen($seq);
1245 $w = 0;
1246 for ($i = 0; $i < $clen; ++$i) {
1247 $w += 1;
1248 if (($i == ($clen - 1)) OR (($i < ($clen - 1)) AND ($seq[$i] != $seq[($i+1)]))) {
1249 if ($seq[$i] == '1') {
1250 $t = true; // bar
1251 } else {
1252 $t = false; // space
1253 }
1254 $bararray['bcode'][$k] = array('t' => $t, 'w' => $w, 'h' => 1, 'p' => 0);
1255 $bararray['maxw'] += $w;
1256 ++$k;
1257 $w = 0;
1258 }
1259 }
1260 $bararray['checkdigit'] = $checkdigit;
1261 return $bararray;
1262 }
1263
1264 /**
1265 * UPC-Based Extentions
1266 * 2-Digit Ext.: Used to indicate magazines and newspaper issue numbers
1267 * 5-Digit Ext.: Used to mark suggested retail price of books
1268 */
1269 protected function barcode_eanext($code, $len=5) {
1270 //Padding
1271 $code = str_pad($code, $len, '0', STR_PAD_LEFT);
1272 // calculate check digit
1273 if ($len == 2) {
1274 $r = $code % 4;
1275 } elseif ($len == 5) {
1276 $r = (3 * ($code{0} + $code{2} + $code{4})) + (9 * ($code{1} + $code{3}));
1277 $r %= 10;
1278 } else {
1279 return false;
1280 }
1281 //Convert digits to bars
1282 $codes = array(
1283 'A'=>array( // left odd parity
1284 '0'=>'0001101',
1285 '1'=>'0011001',
1286 '2'=>'0010011',
1287 '3'=>'0111101',
1288 '4'=>'0100011',
1289 '5'=>'0110001',
1290 '6'=>'0101111',
1291 '7'=>'0111011',
1292 '8'=>'0110111',
1293 '9'=>'0001011'),
1294 'B'=>array( // left even parity
1295 '0'=>'0100111',
1296 '1'=>'0110011',
1297 '2'=>'0011011',
1298 '3'=>'0100001',
1299 '4'=>'0011101',
1300 '5'=>'0111001',
1301 '6'=>'0000101',
1302 '7'=>'0010001',
1303 '8'=>'0001001',
1304 '9'=>'0010111')
1305 );
1306 $parities = array();
1307 $parities[2] = array(
1308 '0'=>array('A','A'),
1309 '1'=>array('A','B'),
1310 '2'=>array('B','A'),
1311 '3'=>array('B','B')
1312 );
1313 $parities[5] = array(
1314 '0'=>array('B','B','A','A','A'),
1315 '1'=>array('B','A','B','A','A'),
1316 '2'=>array('B','A','A','B','A'),
1317 '3'=>array('B','A','A','A','B'),
1318 '4'=>array('A','B','B','A','A'),
1319 '5'=>array('A','A','B','B','A'),
1320 '6'=>array('A','A','A','B','B'),
1321 '7'=>array('A','B','A','B','A'),
1322 '8'=>array('A','B','A','A','B'),
1323 '9'=>array('A','A','B','A','B')
1324 );
1325 $p = $parities[$len][$r];
1326 $seq = '1011'; // left guard bar
1327 $seq .= $codes[$p[0]][$code{0}];
1328 for ($i = 1; $i < $len; ++$i) {
1329 $seq .= '01'; // separator
1330 $seq .= $codes[$p[$i]][$code[$i]];
1331 }
1332 $bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 1, 'bcode' => array());
1333 return $this->binseq_to_array($seq, $bararray);
1334 }
1335
1336 /**
1337 * POSTNET and PLANET barcodes.
1338 * Used by U.S. Postal Service for automated mail sorting
1339 */
1340 protected function barcode_postnet($code, $planet=false) {
1341 // bar lenght
1342 if ($planet) {
1343 $barlen = Array(
1344 0 => Array(1,1,2,2,2),
1345 1 => Array(2,2,2,1,1),
1346 2 => Array(2,2,1,2,1),
1347 3 => Array(2,2,1,1,2),
1348 4 => Array(2,1,2,2,1),
1349 5 => Array(2,1,2,1,2),
1350 6 => Array(2,1,1,2,2),
1351 7 => Array(1,2,2,2,1),
1352 8 => Array(1,2,2,1,2),
1353 9 => Array(1,2,1,2,2)
1354 );
1355 } else {
1356 $barlen = Array(
1357 0 => Array(2,2,1,1,1),
1358 1 => Array(1,1,1,2,2),
1359 2 => Array(1,1,2,1,2),
1360 3 => Array(1,1,2,2,1),
1361 4 => Array(1,2,1,1,2),
1362 5 => Array(1,2,1,2,1),
1363 6 => Array(1,2,2,1,1),
1364 7 => Array(2,1,1,1,2),
1365 8 => Array(2,1,1,2,1),
1366 9 => Array(2,1,2,1,1)
1367 );
1368 }
1369 $bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 5, 'bcode' => array());
1370 $k = 0;
1371 $code = str_replace('-', '', $code);
1372 $code = str_replace(' ', '', $code);
1373 $len = strlen($code);
1374 // calculate checksum
1375 $sum = 0;
1376 for ($i = 0; $i < $len; ++$i) {
1377 $sum += intval($code[$i]);
1378 }
1379 $chkd = ($sum % 10);
1380 if($chkd > 0) {
1381 $chkd = (10 - $chkd);
1382 }
1383 $code .= $chkd;
1384 $checkdigit = $chkd;
1385 $len = strlen($code);
1386 // start bar
1387 $bararray['bcode'][$k++] = array('t' => 1, 'w' => 1, 'h' => 5, 'p' => 0);
1388 $bararray['bcode'][$k++] = array('t' => 0, 'w' => $this->gapwidth , 'h' => 5, 'p' => 0);
1389 $bararray['maxw'] += (1 + $this->gapwidth );
1390 for ($i = 0; $i < $len; ++$i) {
1391 for ($j = 0; $j < 5; ++$j) {
1392 $bh = $barlen[$code[$i]][$j];
1393 if ($bh == 2) {
1394 $h = 5;
1395 $p = 0;
1396 }
1397 else {
1398 $h = 2;
1399 $p = 3;
1400 }
1401 $bararray['bcode'][$k++] = array('t' => 1, 'w' => 1, 'h' => $h, 'p' => $p);
1402 $bararray['bcode'][$k++] = array('t' => 0, 'w' => $this->gapwidth , 'h' => 2, 'p' => 0);
1403 $bararray['maxw'] += (1 + $this->gapwidth );
1404 }
1405 }
1406 // end bar
1407 $bararray['bcode'][$k++] = array('t' => 1, 'w' => 1, 'h' => 5, 'p' => 0);
1408 $bararray['maxw'] += 1;
1409 $bararray['checkdigit'] = $checkdigit;
1410 return $bararray;
1411 }
1412
1413 /**
1414 * RM4SCC - CBC - KIX
1415 * RM4SCC (Royal Mail 4-state Customer Code) - CBC (Customer Bar Code) - KIX (Klant index - Customer index)
1416 * RM4SCC is the name of the barcode symbology used by the Royal Mail for its Cleanmail service.
1417 */
1418 protected function barcode_rm4scc($code, $kix=false) {
1419 $notkix = !$kix;
1420 // bar mode
1421 // 1 = pos 1, length 2
1422 // 2 = pos 1, length 3
1423 // 3 = pos 2, length 1
1424 // 4 = pos 2, length 2
1425 $barmode = array(
1426 '0' => array(3,3,2,2),
1427 '1' => array(3,4,1,2),
1428 '2' => array(3,4,2,1),
1429 '3' => array(4,3,1,2),
1430 '4' => array(4,3,2,1),
1431 '5' => array(4,4,1,1),
1432 '6' => array(3,1,4,2),
1433 '7' => array(3,2,3,2),
1434 '8' => array(3,2,4,1),
1435 '9' => array(4,1,3,2),
1436 'A' => array(4,1,4,1),
1437 'B' => array(4,2,3,1),
1438 'C' => array(3,1,2,4),
1439 'D' => array(3,2,1,4),
1440 'E' => array(3,2,2,3),
1441 'F' => array(4,1,1,4),
1442 'G' => array(4,1,2,3),
1443 'H' => array(4,2,1,3),
1444 'I' => array(1,3,4,2),
1445 'J' => array(1,4,3,2),
1446 'K' => array(1,4,4,1),
1447 'L' => array(2,3,3,2),
1448 'M' => array(2,3,4,1),
1449 'N' => array(2,4,3,1),
1450 'O' => array(1,3,2,4),
1451 'P' => array(1,4,1,4),
1452 'Q' => array(1,4,2,3),
1453 'R' => array(2,3,1,4),
1454 'S' => array(2,3,2,3),
1455 'T' => array(2,4,1,3),
1456 'U' => array(1,1,4,4),
1457 'V' => array(1,2,3,4),
1458 'W' => array(1,2,4,3),
1459 'X' => array(2,1,3,4),
1460 'Y' => array(2,1,4,3),
1461 'Z' => array(2,2,3,3)
1462 );
1463 $code = strtoupper($code);
1464 $len = strlen($code);
1465 $bararray = array('code' => $code, 'maxw' => 0, 'maxh' => $this->daft['F'], 'bcode' => array());
1466 if ($notkix) {
1467 // table for checksum calculation (row,col)
1468 $checktable = array(
1469 '0' => array(1,1),
1470 '1' => array(1,2),
1471 '2' => array(1,3),
1472 '3' => array(1,4),
1473 '4' => array(1,5),
1474 '5' => array(1,0),
1475 '6' => array(2,1),
1476 '7' => array(2,2),
1477 '8' => array(2,3),
1478 '9' => array(2,4),
1479 'A' => array(2,5),
1480 'B' => array(2,0),
1481 'C' => array(3,1),
1482 'D' => array(3,2),
1483 'E' => array(3,3),
1484 'F' => array(3,4),
1485 'G' => array(3,5),
1486 'H' => array(3,0),
1487 'I' => array(4,1),
1488 'J' => array(4,2),
1489 'K' => array(4,3),
1490 'L' => array(4,4),
1491 'M' => array(4,5),
1492 'N' => array(4,0),
1493 'O' => array(5,1),
1494 'P' => array(5,2),
1495 'Q' => array(5,3),
1496 'R' => array(5,4),
1497 'S' => array(5,5),
1498 'T' => array(5,0),
1499 'U' => array(0,1),
1500 'V' => array(0,2),
1501 'W' => array(0,3),
1502 'X' => array(0,4),
1503 'Y' => array(0,5),
1504 'Z' => array(0,0)
1505 );
1506 $row = 0;
1507 $col = 0;
1508 for ($i = 0; $i < $len; ++$i) {
1509 $row += $checktable[$code[$i]][0];
1510 $col += $checktable[$code[$i]][1];
1511 }
1512 $row %= 6;
1513 $col %= 6;
1514 $chk = array_keys($checktable, array($row,$col));
1515 $code .= $chk[0];
1516 $bararray['checkdigit'] = $chk[0];
1517 ++$len;
1518 }
1519 $k = 0;
1520 if ($notkix) {
1521 // start bar
1522 $bararray['bcode'][$k++] = array('t' => 1, 'w' => 1, 'h' => $this->daft['A'] , 'p' => 0);
1523 $bararray['bcode'][$k++] = array('t' => 0, 'w' => $this->gapwidth , 'h' => $this->daft['A'] , 'p' => 0);
1524 $bararray['maxw'] += (1 + $this->gapwidth) ;
1525 }
1526 for ($i = 0; $i < $len; ++$i) {
1527 for ($j = 0; $j < 4; ++$j) {
1528 switch ($barmode[$code[$i]][$j]) {
1529 case 1: {
1530 // ascender (A)
1531 $p = 0;
1532 $h = $this->daft['A'];
1533 break;
1534 }
1535 case 2: {
1536 // full bar (F)
1537 $p = 0;
1538 $h = $this->daft['F'];
1539 break;
1540 }
1541 case 3: {
1542 // tracker (T)
1543 $p = ($this->daft['F'] - $this->daft['T'])/2;
1544 $h = $this->daft['T'];
1545 break;
1546 }
1547 case 4: {
1548 // descender (D)
1549 $p = $this->daft['F'] - $this->daft['D'];
1550 $h = $this->daft['D'];
1551 break;
1552 }
1553 }
1554
1555 $bararray['bcode'][$k++] = array('t' => 1, 'w' => 1, 'h' => $h, 'p' => $p);
1556 $bararray['bcode'][$k++] = array('t' => 0, 'w' => $this->gapwidth, 'h' => 2, 'p' => 0);
1557 $bararray['maxw'] += (1 + $this->gapwidth) ;
1558 }
1559 }
1560 if ($notkix) {
1561 // stop bar
1562 $bararray['bcode'][$k++] = array('t' => 1, 'w' => 1, 'h' => $this->daft['F'], 'p' => 0);
1563 $bararray['maxw'] += 1;
1564 }
1565 return $bararray;
1566 }
1567
1568 /**
1569 * CODABAR barcodes.
1570 * Older code often used in library systems, sometimes in blood banks
1571 */
1572 protected function barcode_codabar($code) {
1573 $chr = array(
1574 '0' => '11111221',
1575 '1' => '11112211',
1576 '2' => '11121121',
1577 '3' => '22111111',
1578 '4' => '11211211',
1579 '5' => '21111211',
1580 '6' => '12111121',
1581 '7' => '12112111',
1582 '8' => '12211111',
1583 '9' => '21121111',
1584 '-' => '11122111',
1585 '$' => '11221111',
1586 ':' => '21112121',
1587 '/' => '21211121',
1588 '.' => '21212111',
1589 '+' => '11222221',
1590 'A' => '11221211',
1591 'B' => '12121121',
1592 'C' => '11121221',
1593 'D' => '11122211'
1594 );
1595 $bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 1, 'bcode' => array());
1596 $k = 0;
1597 $w = 0;
1598 $seq = '';
1599 $code = strtoupper($code);
1600 $len = strlen($code);
1601 for ($i = 0; $i < $len; ++$i) {
1602 if (!isset($chr[$code[$i]])) {
1603 return false;
1604 }
1605 $seq = $chr[$code[$i]];
1606 for ($j = 0; $j < 8; ++$j) {
1607 if (($j % 2) == 0) {
1608 $t = true; // bar
1609 } else {
1610 $t = false; // space
1611 }
1612 $x = $seq[$j];
1613 if ($x == 2) { $w = $this->print_ratio; }
1614 else { $w = 1; }
1615 $bararray['bcode'][$k] = array('t' => $t, 'w' => $w, 'h' => 1, 'p' => 0);
1616 $bararray['maxw'] += $w;
1617 ++$k;
1618 }
1619 }
1620 return $bararray;
1621 }
1622
1623 /**
1624 * CODE11 barcodes.
1625 * Used primarily for labeling telecommunications equipment
1626 */
1627 protected function barcode_code11($code) {
1628 $chr = array(
1629 '0' => '111121',
1630 '1' => '211121',
1631 '2' => '121121',
1632 '3' => '221111',
1633 '4' => '112121',
1634 '5' => '212111',
1635 '6' => '122111',
1636 '7' => '111221',
1637 '8' => '211211',
1638 '9' => '211111',
1639 '-' => '112111',
1640 'S' => '112211'
1641 );
1642
1643 $bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 1, 'bcode' => array());
1644 $k = 0;
1645 $w = 0;
1646 $seq = '';
1647 $len = strlen($code);
1648 // calculate check digit C
1649 $p = 1;
1650 $check = 0;
1651 for ($i = ($len - 1); $i >= 0; --$i) {
1652 $digit = $code[$i];
1653 if ($digit == '-') {
1654 $dval = 10;
1655 } else {
1656 $dval = intval($digit);
1657 }
1658 $check += ($dval * $p);
1659 ++$p;
1660 if ($p > 10) {
1661 $p = 1;
1662 }
1663 }
1664 $check %= 11;
1665 if ($check == 10) {
1666 $check = '-';
1667 }
1668 $code .= $check;
1669 $checkdigit = $check;
1670 if ($len > 10) {
1671 // calculate check digit K
1672 $p = 1;
1673 $check = 0;
1674 for ($i = $len; $i >= 0; --$i) {
1675 $digit = $code[$i];
1676 if ($digit == '-') {
1677 $dval = 10;
1678 } else {
1679 $dval = intval($digit);
1680 }
1681 $check += ($dval * $p);
1682 ++$p;
1683 if ($p > 9) {
1684 $p = 1;
1685 }
1686 }
1687 $check %= 11;
1688 $code .= $check;
1689 $checkdigit .= $check;
1690 ++$len;
1691 }
1692 $code = 'S'.$code.'S';
1693 $len += 3;
1694 for ($i = 0; $i < $len; ++$i) {
1695 if (!isset($chr[$code[$i]])) {
1696 return false;
1697 }
1698 $seq = $chr[$code[$i]];
1699 for ($j = 0; $j < 6; ++$j) {
1700 if (($j % 2) == 0) {
1701 $t = true; // bar
1702 } else {
1703 $t = false; // space
1704 }
1705 $x = $seq[$j];
1706 if ($x == 2) { $w = $this->print_ratio; }
1707 else { $w = 1; }
1708 $bararray['bcode'][$k] = array('t' => $t, 'w' => $w, 'h' => 1, 'p' => 0);
1709 $bararray['maxw'] += $w;
1710 ++$k;
1711 }
1712 }
1713 $bararray['checkdigit'] = $checkdigit;
1714 return $bararray;
1715 }
1716
1717
1718 /**
1719 * IMB - Intelligent Mail Barcode - Onecode - USPS-B-3200
1720 * (requires PHP bcmath extension)
1721 * Intelligent Mail barcode is a 65-bar code for use on mail in the United States.
1722 * The fields are described as follows:<ul><li>The Barcode Identifier shall be assigned by USPS to encode the presort identification that is currently printed in human readable form on the optional endorsement line (OEL) as well as for future USPS use. This shall be two digits, with the second digit in the range of 0-4. The allowable encoding ranges shall be 00-04, 10-14, 20-24, 30-34, 40-44, 50-54, 60-64, 70-74, 80-84, and 90-94.</li><li>The Service Type Identifier shall be assigned by USPS for any combination of services requested on the mailpiece. The allowable encoding range shall be 000-999. Each 3-digit value shall correspond to a particular mail class with a particular combination of service(s). Each service program, such as OneCode Confirm and OneCode ACS, shall provide the list of Service Type Identifier values.</li><li>The Mailer or Customer Identifier shall be assigned by USPS as a unique, 6 or 9 digit number that identifies a business entity. The allowable encoding range for the 6 digit Mailer ID shall be 000000- 899999, while the allowable encoding range for the 9 digit Mailer ID shall be 900000000-999999999.</li><li>The Serial or Sequence Number shall be assigned by the mailer for uniquely identifying and tracking mailpieces. The allowable encoding range shall be 000000000-999999999 when used with a 6 digit Mailer ID and 000000-999999 when used with a 9 digit Mailer ID. e. The Delivery Point ZIP Code shall be assigned by the mailer for routing the mailpiece. This shall replace POSTNET for routing the mailpiece to its final delivery point. The length may be 0, 5, 9, or 11 digits. The allowable encoding ranges shall be no ZIP Code, 00000-99999, 000000000-999999999, and 00000000000-99999999999.</li></ul>
1723 */
1724 protected function barcode_imb($code) {
1725 $asc_chr = array(4,0,2,6,3,5,1,9,8,7,1,2,0,6,4,8,2,9,5,3,0,1,3,7,4,6,8,9,2,0,5,1,9,4,3,8,6,7,1,2,4,3,9,5,7,8,3,0,2,1,4,0,9,1,7,0,2,4,6,3,7,1,9,5,8);
1726 $dsc_chr = array(7,1,9,5,8,0,2,4,6,3,5,8,9,7,3,0,6,1,7,4,6,8,9,2,5,1,7,5,4,3,8,7,6,0,2,5,4,9,3,0,1,6,8,2,0,4,5,9,6,7,5,2,6,3,8,5,1,9,8,7,4,0,2,6,3);
1727 $asc_pos = array(3,0,8,11,1,12,8,11,10,6,4,12,2,7,9,6,7,9,2,8,4,0,12,7,10,9,0,7,10,5,7,9,6,8,2,12,1,4,2,0,1,5,4,6,12,1,0,9,4,7,5,10,2,6,9,11,2,12,6,7,5,11,0,3,2);
1728 $dsc_pos = array(2,10,12,5,9,1,5,4,3,9,11,5,10,1,6,3,4,1,10,0,2,11,8,6,1,12,3,8,6,4,4,11,0,6,1,9,11,5,3,7,3,10,7,11,8,2,10,3,5,8,0,3,12,11,8,4,5,1,3,0,7,12,9,8,10);
1729 $code_arr = explode('-', $code);
1730 $tracking_number = $code_arr[0];
1731 if (isset($code_arr[1])) {
1732 $routing_code = $code_arr[1];
1733 } else {
1734 $routing_code = '';
1735 }
1736 // Conversion of Routing Code
1737 switch (strlen($routing_code)) {
1738 case 0: {
1739 $binary_code = 0;
1740 break;
1741 }
1742 case 5: {
1743 $binary_code = bcadd($routing_code, '1');
1744 break;
1745 }
1746 case 9: {
1747 $binary_code = bcadd($routing_code, '100001');
1748 break;
1749 }
1750 case 11: {
1751 $binary_code = bcadd($routing_code, '1000100001');
1752 break;
1753 }
1754 default: {
1755 return false;
1756 break;
1757 }
1758 }
1759 $binary_code = bcmul($binary_code, 10);
1760 $binary_code = bcadd($binary_code, $tracking_number{0});
1761 $binary_code = bcmul($binary_code, 5);
1762 $binary_code = bcadd($binary_code, $tracking_number{1});
1763 $binary_code .= substr($tracking_number, 2, 18);
1764 // convert to hexadecimal
1765 $binary_code = $this->dec_to_hex($binary_code);
1766 // pad to get 13 bytes
1767 $binary_code = str_pad($binary_code, 26, '0', STR_PAD_LEFT);
1768 // convert string to array of bytes
1769 $binary_code_arr = chunk_split($binary_code, 2, "\r");
1770 $binary_code_arr = substr($binary_code_arr, 0, -1);
1771 $binary_code_arr = explode("\r", $binary_code_arr);
1772 // calculate frame check sequence
1773 $fcs = $this->imb_crc11fcs($binary_code_arr);
1774 // exclude first 2 bits from first byte
1775 $first_byte = sprintf('%2s', dechex((hexdec($binary_code_arr[0]) << 2) >> 2));
1776 $binary_code_102bit = $first_byte.substr($binary_code, 2);
1777 // convert binary data to codewords
1778 $codewords = array();
1779 $data = $this->hex_to_dec($binary_code_102bit);
1780 $codewords[0] = bcmod($data, 636) * 2;
1781 $data = bcdiv($data, 636);
1782 for ($i = 1; $i < 9; ++$i) {
1783 $codewords[$i] = bcmod($data, 1365);
1784 $data = bcdiv($data, 1365);
1785 }
1786 $codewords[9] = $data;
1787 if (($fcs >> 10) == 1) {
1788 $codewords[9] += 659;
1789 }
1790 // generate lookup tables
1791 $table2of13 = $this->imb_tables(2, 78);
1792 $table5of13 = $this->imb_tables(5, 1287);
1793 // convert codewords to characters
1794 $characters = array();
1795 $bitmask = 512;
1796 foreach($codewords as $k => $val) {
1797 if ($val <= 1286) {
1798 $chrcode = $table5of13[$val];
1799 } else {
1800 $chrcode = $table2of13[($val - 1287)];
1801 }
1802 if (($fcs & $bitmask) > 0) {
1803 // bitwise invert
1804 $chrcode = ((~$chrcode) & 8191);
1805 }
1806 $characters[] = $chrcode;
1807 $bitmask /= 2;
1808 }
1809 $characters = array_reverse($characters);
1810 // build bars
1811 $k = 0;
1812 $bararray = array('code' => $code, 'maxw' => 0, 'maxh' => $this->daft['F'], 'bcode' => array());
1813 for ($i = 0; $i < 65; ++$i) {
1814 $asc = (($characters[$asc_chr[$i]] & pow(2, $asc_pos[$i])) > 0);
1815 $dsc = (($characters[$dsc_chr[$i]] & pow(2, $dsc_pos[$i])) > 0);
1816 if ($asc AND $dsc) {
1817 // full bar (F)
1818 $p = 0;
1819 $h = $this->daft['F'];
1820 } elseif ($asc) {
1821 // ascender (A)
1822 $p = 0;
1823 $h = $this->daft['A'];
1824 } elseif ($dsc) {
1825 // descender (D)
1826 $p = $this->daft['F'] - $this->daft['D'];
1827 $h = $this->daft['D'];
1828 } else {
1829 // tracker (T)
1830 $p = ($this->daft['F'] - $this->daft['T'])/2;
1831 $h = $this->daft['T'];
1832 }
1833 $bararray['bcode'][$k++] = array('t' => 1, 'w' => 1, 'h' => $h, 'p' => $p);
1834 // Gap
1835 $bararray['bcode'][$k++] = array('t' => 0, 'w' => $this->gapwidth , 'h' => 1, 'p' => 0);
1836 $bararray['maxw'] += (1 + $this->gapwidth );
1837 }
1838 unset($bararray['bcode'][($k - 1)]);
1839 $bararray['maxw'] -= $this->gapwidth ;
1840 return $bararray;
1841 }
1842
1843 /**
1844 * Convert large integer number to hexadecimal representation.
1845 * (requires PHP bcmath extension)
1846 */
1847 public function dec_to_hex($number) {
1848 $i = 0;
1849 $hex = array();
1850 if($number == 0) {
1851 return '00';
1852 }
1853 while($number > 0) {
1854 if($number == 0) {
1855 array_push($hex, '0');
1856 } else {
1857 array_push($hex, strtoupper(dechex(bcmod($number, '16'))));
1858 $number = bcdiv($number, '16', 0);
1859 }
1860 }
1861 $hex = array_reverse($hex);
1862 return implode($hex);
1863 }
1864
1865 /**
1866 * Convert large hexadecimal number to decimal representation (string).
1867 * (requires PHP bcmath extension)
1868 */
1869 public function hex_to_dec($hex) {
1870 $dec = 0;
1871 $bitval = 1;
1872 $len = strlen($hex);
1873 for($pos = ($len - 1); $pos >= 0; --$pos) {
1874 $dec = bcadd($dec, bcmul(hexdec($hex[$pos]), $bitval));
1875 $bitval = bcmul($bitval, 16);
1876 }
1877 return $dec;
1878 }
1879
1880 /**
1881 * Intelligent Mail Barcode calculation of Frame Check Sequence
1882 */
1883 protected function imb_crc11fcs($code_arr) {
1884 $genpoly = 0x0F35; // generator polynomial
1885 $fcs = 0x07FF; // Frame Check Sequence
1886 // do most significant byte skipping the 2 most significant bits
1887 $data = hexdec($code_arr[0]) << 5;
1888 for ($bit = 2; $bit < 8; ++$bit) {
1889 if (($fcs ^ $data) & 0x400) {
1890 $fcs = ($fcs << 1) ^ $genpoly;
1891 } else {
1892 $fcs = ($fcs << 1);
1893 }
1894 $fcs &= 0x7FF;
1895 $data <<= 1;
1896 }
1897 // do rest of bytes
1898 for ($byte = 1; $byte < 13; ++$byte) {
1899 $data = hexdec($code_arr[$byte]) << 3;
1900 for ($bit = 0; $bit < 8; ++$bit) {
1901 if (($fcs ^ $data) & 0x400) {
1902 $fcs = ($fcs << 1) ^ $genpoly;
1903 } else {
1904 $fcs = ($fcs << 1);
1905 }
1906 $fcs &= 0x7FF;
1907 $data <<= 1;
1908 }
1909 }
1910 return $fcs;
1911 }
1912
1913 /**
1914 * Reverse unsigned short value
1915 */
1916 protected function imb_reverse_us($num) {
1917 $rev = 0;
1918 for ($i = 0; $i < 16; ++$i) {
1919 $rev <<= 1;
1920 $rev |= ($num & 1);
1921 $num >>= 1;
1922 }
1923 return $rev;
1924 }
1925
1926 /**
1927 * generate Nof13 tables used for Intelligent Mail Barcode
1928 */
1929 protected function imb_tables($n, $size) {
1930 $table = array();
1931 $lli = 0; // LUT lower index
1932 $lui = $size - 1; // LUT upper index
1933 for ($count = 0; $count < 8192; ++$count) {
1934 $bit_count = 0;
1935 for ($bit_index = 0; $bit_index < 13; ++$bit_index) {
1936 $bit_count += intval(($count & (1 << $bit_index)) != 0);
1937 }
1938 // if we don't have the right number of bits on, go on to the next value
1939 if ($bit_count == $n) {
1940 $reverse = ($this->imb_reverse_us($count) >> 3);
1941 // if the reverse is less than count, we have already visited this pair before
1942 if ($reverse >= $count) {
1943 // If count is symmetric, place it at the first free slot from the end of the list.
1944 // Otherwise, place it at the first free slot from the beginning of the list AND place $reverse ath the next free slot from the beginning of the list
1945 if ($reverse == $count) {
1946 $table[$lui] = $count;
1947 --$lui;
1948 } else {
1949 $table[$lli] = $count;
1950 ++$lli;
1951 $table[$lli] = $reverse;
1952 ++$lli;
1953 }
1954 }
1955 }
1956 }
1957 return $table;
1958 }
1959
1960} // end of class
1961
1962//============================================================+
1963// END OF FILE
1964//============================================================+
1965?> \ No newline at end of file
diff --git a/inc/3rdparty/libraries/mpdf/classes/bmp.php b/inc/3rdparty/libraries/mpdf/classes/bmp.php
new file mode 100644
index 00000000..896ced89
--- /dev/null
+++ b/inc/3rdparty/libraries/mpdf/classes/bmp.php
@@ -0,0 +1,248 @@
1<?php
2
3class bmp {
4
5var $mpdf = null;
6
7function bmp(&$mpdf) {
8 $this->mpdf = $mpdf;
9}
10
11
12function _getBMPimage($data, $file) {
13 $info = array();
14 // Adapted from script by Valentin Schmidt
15 // http://staff.dasdeck.de/valentin/fpdf/fpdf_bmp/
16 $bfOffBits=$this->_fourbytes2int_le(substr($data,10,4));
17 $width=$this->_fourbytes2int_le(substr($data,18,4));
18 $height=$this->_fourbytes2int_le(substr($data,22,4));
19 $flip = ($height<0);
20 if ($flip) $height =-$height;
21 $biBitCount=$this->_twobytes2int_le(substr($data,28,2));
22 $biCompression=$this->_fourbytes2int_le(substr($data,30,4));
23 $info = array('w'=>$width, 'h'=>$height);
24 if ($biBitCount<16){
25 $info['cs'] = 'Indexed';
26 $info['bpc'] = $biBitCount;
27 $palStr = substr($data,54,($bfOffBits-54));
28 $pal = '';
29 $cnt = strlen($palStr)/4;
30 for ($i=0;$i<$cnt;$i++){
31 $n = 4*$i;
32 $pal .= $palStr[$n+2].$palStr[$n+1].$palStr[$n];
33 }
34 $info['pal'] = $pal;
35 }
36 else{
37 $info['cs'] = 'DeviceRGB';
38 $info['bpc'] = 8;
39 }
40
41 if ($this->mpdf->restrictColorSpace==1 || $this->mpdf->PDFX || $this->mpdf->restrictColorSpace==3) {
42 if (($this->mpdf->PDFA && !$this->mpdf->PDFAauto) || ($this->mpdf->PDFX && !$this->mpdf->PDFXauto)) { $this->mpdf->PDFAXwarnings[] = "Image cannot be converted to suitable colour space for PDFA or PDFX file - ".$file." - (Image replaced by 'no-image'.)"; }
43 return array('error' => "BMP Image cannot be converted to suitable colour space - ".$file." - (Image replaced by 'no-image'.)");
44 }
45
46 $biXPelsPerMeter=$this->_fourbytes2int_le(substr($data,38,4)); // horizontal pixels per meter, usually set to zero
47 //$biYPelsPerMeter=$this->_fourbytes2int_le(substr($data,42,4)); // vertical pixels per meter, usually set to zero
48 $biXPelsPerMeter=round($biXPelsPerMeter/1000 *25.4);
49 //$biYPelsPerMeter=round($biYPelsPerMeter/1000 *25.4);
50 $info['set-dpi'] = $biXPelsPerMeter;
51
52 switch ($biCompression){
53 case 0:
54 $str = substr($data,$bfOffBits);
55 break;
56 case 1: # BI_RLE8
57 $str = $this->rle8_decode(substr($data,$bfOffBits), $width);
58 break;
59 case 2: # BI_RLE4
60 $str = $this->rle4_decode(substr($data,$bfOffBits), $width);
61 break;
62 }
63 $bmpdata = '';
64 $padCnt = (4-ceil(($width/(8/$biBitCount)))%4)%4;
65 switch ($biBitCount){
66 case 1:
67 case 4:
68 case 8:
69 $w = floor($width/(8/$biBitCount)) + ($width%(8/$biBitCount)?1:0);
70 $w_row = $w + $padCnt;
71 if ($flip){
72 for ($y=0;$y<$height;$y++){
73 $y0 = $y*$w_row;
74 for ($x=0;$x<$w;$x++)
75 $bmpdata .= $str[$y0+$x];
76 }
77 }else{
78 for ($y=$height-1;$y>=0;$y--){
79 $y0 = $y*$w_row;
80 for ($x=0;$x<$w;$x++)
81 $bmpdata .= $str[$y0+$x];
82 }
83 }
84 break;
85
86 case 16:
87 $w_row = $width*2 + $padCnt;
88 if ($flip){
89 for ($y=0;$y<$height;$y++){
90 $y0 = $y*$w_row;
91 for ($x=0;$x<$width;$x++){
92 $n = (ord( $str[$y0 + 2*$x + 1])*256 + ord( $str[$y0 + 2*$x]));
93 $b = ($n & 31)<<3; $g = ($n & 992)>>2; $r = ($n & 31744)>>7128;
94 $bmpdata .= chr($r) . chr($g) . chr($b);
95 }
96 }
97 }else{
98 for ($y=$height-1;$y>=0;$y--){
99 $y0 = $y*$w_row;
100 for ($x=0;$x<$width;$x++){
101 $n = (ord( $str[$y0 + 2*$x + 1])*256 + ord( $str[$y0 + 2*$x]));
102 $b = ($n & 31)<<3; $g = ($n & 992)>>2; $r = ($n & 31744)>>7;
103 $bmpdata .= chr($r) . chr($g) . chr($b);
104 }
105 }
106 }
107 break;
108
109 case 24:
110 case 32:
111 $byteCnt = $biBitCount/8;
112 $w_row = $width*$byteCnt + $padCnt;
113
114 if ($flip){
115 for ($y=0;$y<$height;$y++){
116 $y0 = $y*$w_row;
117 for ($x=0;$x<$width;$x++){
118 $i = $y0 + $x*$byteCnt ; # + 1
119 $bmpdata .= $str[$i+2].$str[$i+1].$str[$i];
120 }
121 }
122 }else{
123 for ($y=$height-1;$y>=0;$y--){
124 $y0 = $y*$w_row;
125 for ($x=0;$x<$width;$x++){
126 $i = $y0 + $x*$byteCnt ; # + 1
127 $bmpdata .= $str[$i+2].$str[$i+1].$str[$i];
128 }
129 }
130 }
131 break;
132
133 default:
134 return array('error' => 'Error parsing BMP image - Unsupported image biBitCount');
135 }
136 if ($this->mpdf->compress) {
137 $bmpdata=gzcompress($bmpdata);
138 $info['f']='FlateDecode';
139 }
140 $info['data']=$bmpdata;
141 $info['type']='bmp';
142 return $info;
143}
144
145function _fourbytes2int_le($s) {
146 //Read a 4-byte integer from string
147 return (ord($s[3])<<24) + (ord($s[2])<<16) + (ord($s[1])<<8) + ord($s[0]);
148}
149
150function _twobytes2int_le($s) {
151 //Read a 2-byte integer from string
152 return (ord(substr($s, 1, 1))<<8) + ord(substr($s, 0, 1));
153}
154
155
156# Decoder for RLE8 compression in windows bitmaps
157# see http://msdn.microsoft.com/library/default.asp?url=/library/en-us/gdi/bitmaps_6x0u.asp
158function rle8_decode ($str, $width){
159 $lineWidth = $width + (3 - ($width-1) % 4);
160 $out = '';
161 $cnt = strlen($str);
162 for ($i=0;$i<$cnt;$i++){
163 $o = ord($str[$i]);
164 switch ($o){
165 case 0: # ESCAPE
166 $i++;
167 switch (ord($str[$i])){
168 case 0: # NEW LINE
169 $padCnt = $lineWidth - strlen($out)%$lineWidth;
170 if ($padCnt<$lineWidth) $out .= str_repeat(chr(0), $padCnt); # pad line
171 break;
172 case 1: # END OF FILE
173 $padCnt = $lineWidth - strlen($out)%$lineWidth;
174 if ($padCnt<$lineWidth) $out .= str_repeat(chr(0), $padCnt); # pad line
175 break 3;
176 case 2: # DELTA
177 $i += 2;
178 break;
179 default: # ABSOLUTE MODE
180 $num = ord($str[$i]);
181 for ($j=0;$j<$num;$j++)
182 $out .= $str[++$i];
183 if ($num % 2) $i++;
184 }
185 break;
186 default:
187 $out .= str_repeat($str[++$i], $o);
188 }
189 }
190 return $out;
191}
192
193# Decoder for RLE4 compression in windows bitmaps
194# see http://msdn.microsoft.com/library/default.asp?url=/library/en-us/gdi/bitmaps_6x0u.asp
195function rle4_decode ($str, $width){
196 $w = floor($width/2) + ($width % 2);
197 $lineWidth = $w + (3 - ( ($width-1) / 2) % 4);
198 $pixels = array();
199 $cnt = strlen($str);
200 for ($i=0;$i<$cnt;$i++){
201 $o = ord($str[$i]);
202 switch ($o){
203 case 0: # ESCAPE
204 $i++;
205 switch (ord($str[$i])){
206 case 0: # NEW LINE
207 while (count($pixels)%$lineWidth!=0)
208 $pixels[]=0;
209 break;
210 case 1: # END OF FILE
211 while (count($pixels)%$lineWidth!=0)
212 $pixels[]=0;
213 break 3;
214 case 2: # DELTA
215 $i += 2;
216 break;
217 default: # ABSOLUTE MODE
218 $num = ord($str[$i]);
219 for ($j=0;$j<$num;$j++){
220 if ($j%2==0){
221 $c = ord($str[++$i]);
222 $pixels[] = ($c & 240)>>4;
223 } else
224 $pixels[] = $c & 15;
225 }
226 if ($num % 2) $i++;
227 }
228 break;
229 default:
230 $c = ord($str[++$i]);
231 for ($j=0;$j<$o;$j++)
232 $pixels[] = ($j%2==0 ? ($c & 240)>>4 : $c & 15);
233 }
234 }
235
236 $out = '';
237 if (count($pixels)%2) $pixels[]=0;
238 $cnt = count($pixels)/2;
239 for ($i=0;$i<$cnt;$i++)
240 $out .= chr(16*$pixels[2*$i] + $pixels[2*$i+1]);
241 return $out;
242}
243
244
245
246}
247
248?> \ No newline at end of file
diff --git a/inc/3rdparty/libraries/mpdf/classes/cssmgr.php b/inc/3rdparty/libraries/mpdf/classes/cssmgr.php
new file mode 100644
index 00000000..9bbbed9c
--- /dev/null
+++ b/inc/3rdparty/libraries/mpdf/classes/cssmgr.php
@@ -0,0 +1,1566 @@
1<?php
2
3class cssmgr {
4
5var $mpdf = null;
6
7var $tablecascadeCSS;
8var $listcascadeCSS;
9var $cascadeCSS;
10var $CSS;
11var $tbCSSlvl;
12var $listCSSlvl;
13
14
15function cssmgr(&$mpdf) {
16 $this->mpdf = $mpdf;
17 $this->tablecascadeCSS = array();
18 $this->listcascadeCSS = array();
19 $this->CSS=array();
20 $this->cascadeCSS = array();
21 $this->tbCSSlvl = 0;
22 $this->listCSSlvl = 0;
23}
24
25
26function ReadDefaultCSS($CSSstr) {
27 $CSS = array();
28 $CSSstr = preg_replace('|/\*.*?\*/|s',' ',$CSSstr);
29 $CSSstr = preg_replace('/[\s\n\r\t\f]/s',' ',$CSSstr);
30 $CSSstr = preg_replace('/(<\!\-\-|\-\->)/s',' ',$CSSstr);
31 if ($CSSstr ) {
32 preg_match_all('/(.*?)\{(.*?)\}/',$CSSstr,$styles);
33 for($i=0; $i < count($styles[1]) ; $i++) {
34 $stylestr= trim($styles[2][$i]);
35 $stylearr = explode(';',$stylestr);
36 foreach($stylearr AS $sta) {
37 if (trim($sta)) {
38 // Changed to allow style="background: url('http://www.bpm1.com/bg.jpg')"
39 list($property,$value) = explode(':',$sta,2);
40 $property = trim($property);
41 $value = preg_replace('/\s*!important/i','',$value);
42 $value = trim($value);
43 if ($property && ($value || $value==='0')) {
44 $classproperties[strtoupper($property)] = $value;
45 }
46 }
47 }
48 $classproperties = $this->fixCSS($classproperties);
49 $tagstr = strtoupper(trim($styles[1][$i]));
50 $tagarr = explode(',',$tagstr);
51 foreach($tagarr AS $tg) {
52 $tags = preg_split('/\s+/',trim($tg));
53 $level = count($tags);
54 if ($level == 1) { // e.g. p or .class or #id or p.class or p#id
55 $t = trim($tags[0]);
56 if ($t) {
57 $tag = '';
58 if (preg_match('/^('.$this->mpdf->allowedCSStags.')$/',$t)) { $tag= $t; }
59 if ($this->CSS[$tag] && $tag) { $CSS[$tag] = $this->array_merge_recursive_unique($CSS[$tag], $classproperties); }
60 else if ($tag) { $CSS[$tag] = $classproperties; }
61 }
62 }
63 }
64 $properties = array();
65 $values = array();
66 $classproperties = array();
67 }
68
69 } // end of if
70 return $CSS;
71}
72
73
74
75function ReadCSS($html) {
76 preg_match_all('/<style[^>]*media=["\']([^"\'>]*)["\'].*?<\/style>/is',$html,$m);
77 for($i=0; $i<count($m[0]); $i++) {
78 if ($this->mpdf->CSSselectMedia && !preg_match('/('.trim($this->mpdf->CSSselectMedia).'|all)/i',$m[1][$i])) {
79 $html = preg_replace('/'.preg_quote($m[0][$i],'/').'/','',$html);
80 }
81 }
82 preg_match_all('/<link[^>]*media=["\']([^"\'>]*)["\'].*?>/is',$html,$m);
83 for($i=0; $i<count($m[0]); $i++) {
84 if ($this->mpdf->CSSselectMedia && !preg_match('/('.trim($this->mpdf->CSSselectMedia).'|all)/i',$m[1][$i])) {
85 $html = preg_replace('/'.preg_quote($m[0][$i],'/').'/','',$html);
86 }
87 }
88
89 // mPDF 5.5.02
90 // Remove Comment tags <!-- ... --> inside CSS as <style> in HTML document
91 // Remove Comment tags /* ... */ inside CSS as <style> in HTML document
92 // But first, we replace upper and mixed case closing style tag with lower
93 // case so we can use str_replace later.
94 preg_replace('/<\/style>/i', '</style>', $html);
95 preg_match_all('/<style.*?>(.*?)<\/style>/si',$html,$m);
96 if (count($m[1])) {
97 for($i=0;$i<count($m[1]);$i++) {
98 // Remove comment tags
99 $sub = preg_replace('/(<\!\-\-|\-\->)/s',' ',$m[1][$i]);
100 $sub = '>'.preg_replace('|/\*.*?\*/|s',' ',$sub).'</style>';
101 $html = str_replace('>'.$m[1][$i].'</style>', $sub, $html);
102 }
103 }
104
105
106 $html = preg_replace('/<!--mpdf/i','',$html);
107 $html = preg_replace('/mpdf-->/i','',$html);
108 $html = preg_replace('/<\!\-\-.*?\-\->/s',' ',$html);
109
110 $match = 0; // no match for instance
111 $regexp = ''; // This helps debugging: showing what is the REAL string being processed
112 $CSSext = array();
113
114 //CSS inside external files
115 $regexp = '/<link[^>]*rel=["\']stylesheet["\'][^>]*href=["\']([^>"\']*)["\'].*?>/si';
116 $x = preg_match_all($regexp,$html,$cxt);
117 if ($x) {
118 $match += $x;
119 $CSSext = $cxt[1];
120 }
121
122 $regexp = '/<link[^>]*href=["\']([^>"\']*)["\'][^>]*?rel=["\']stylesheet["\'].*?>/si';
123 $x = preg_match_all($regexp,$html,$cxt);
124 if ($x) {
125 $match += $x;
126 $CSSext = array_merge($CSSext,$cxt[1]);
127 }
128
129 // look for @import stylesheets
130 //$regexp = '/@import url\([\'\"]{0,1}([^\)]*?\.css)[\'\"]{0,1}\)/si';
131 $regexp = '/@import url\([\'\"]{0,1}([^\)]*?\.css(\?\S+)?)[\'\"]{0,1}\)/si';
132 $x = preg_match_all($regexp,$html,$cxt);
133 if ($x) {
134 $match += $x;
135 $CSSext = array_merge($CSSext,$cxt[1]);
136 }
137
138 // look for @import without the url()
139 //$regexp = '/@import [\'\"]{0,1}([^;]*?\.css)[\'\"]{0,1}/si';
140 $regexp = '/@import [\'\"]{0,1}([^;]*?\.css(\?\S+)?)[\'\"]{0,1}/si';
141 $x = preg_match_all($regexp,$html,$cxt);
142 if ($x) {
143 $match += $x;
144 $CSSext = array_merge($CSSext,$cxt[1]);
145 }
146
147 $ind = 0;
148 $CSSstr = '';
149
150 if (!is_array($this->cascadeCSS)) $this->cascadeCSS = array();
151
152 while($match){
153 $path = $CSSext[$ind];
154 $this->mpdf->GetFullPath($path);
155 $CSSextblock = $this->mpdf->_get_file($path);
156 if ($CSSextblock) {
157 // look for embedded @import stylesheets in other stylesheets
158 // and fix url paths (including background-images) relative to stylesheet
159 //$regexpem = '/@import url\([\'\"]{0,1}(.*?\.css)[\'\"]{0,1}\)/si';
160 $regexpem = '/@import url\([\'\"]{0,1}(.*?\.css(\?\S+)?)[\'\"]{0,1}\)/si';
161 $xem = preg_match_all($regexpem,$CSSextblock,$cxtem);
162 $cssBasePath = preg_replace('/\/[^\/]*$/','',$path) . '/';
163 if ($xem) {
164 foreach($cxtem[1] AS $cxtembedded) {
165 // path is relative to original stlyesheet!!
166 $this->mpdf->GetFullPath($cxtembedded, $cssBasePath );
167 $match++;
168 $CSSext[] = $cxtembedded;
169 }
170 }
171 $regexpem = '/(background[^;]*url\s*\(\s*[\'\"]{0,1})([^\)\'\"]*)([\'\"]{0,1}\s*\))/si';
172 $xem = preg_match_all($regexpem,$CSSextblock,$cxtem);
173 if ($xem) {
174 for ($i=0;$i<count($cxtem[0]);$i++) {
175 // path is relative to original stlyesheet!!
176 $embedded = $cxtem[2][$i];
177 if (!preg_match('/^data:image/i', $embedded)) { // mPDF 5.5.13
178 $this->mpdf->GetFullPath($embedded, $cssBasePath );
179 $CSSextblock = preg_replace('/'.preg_quote($cxtem[0][$i],'/').'/', ($cxtem[1][$i].$embedded.$cxtem[3][$i]), $CSSextblock);
180 }
181 }
182 }
183 $CSSstr .= ' '.$CSSextblock;
184 }
185 $match--;
186 $ind++;
187 } //end of match
188
189 $match = 0; // reset value, if needed
190 // CSS as <style> in HTML document
191 $regexp = '/<style.*?>(.*?)<\/style>/si';
192 $match = preg_match_all($regexp,$html,$CSSblock);
193 if ($match) {
194 $tmpCSSstr = implode(' ',$CSSblock[1]);
195 $regexpem = '/(background[^;]*url\s*\(\s*[\'\"]{0,1})([^\)\'\"]*)([\'\"]{0,1}\s*\))/si';
196 $xem = preg_match_all($regexpem,$tmpCSSstr ,$cxtem);
197 if ($xem) {
198 for ($i=0;$i<count($cxtem[0]);$i++) {
199 $embedded = $cxtem[2][$i];
200 if (!preg_match('/^data:image/i', $embedded)) { // mPDF 5.5.13
201 $this->mpdf->GetFullPath($embedded);
202 $tmpCSSstr = preg_replace('/'.preg_quote($cxtem[0][$i],'/').'/', ($cxtem[1][$i].$embedded.$cxtem[3][$i]), $tmpCSSstr );
203 }
204 }
205 }
206 $CSSstr .= ' '.$tmpCSSstr;
207 }
208 // Remove comments
209 $CSSstr = preg_replace('|/\*.*?\*/|s',' ',$CSSstr);
210 $CSSstr = preg_replace('/[\s\n\r\t\f]/s',' ',$CSSstr);
211
212 if (preg_match('/@media/',$CSSstr)) {
213 preg_match_all('/@media(.*?)\{(([^\{\}]*\{[^\{\}]*\})+)\s*\}/is',$CSSstr,$m);
214 for($i=0; $i<count($m[0]); $i++) {
215 if ($this->mpdf->CSSselectMedia && !preg_match('/('.trim($this->mpdf->CSSselectMedia).'|all)/i',$m[1][$i])) {
216 $CSSstr = preg_replace('/'.preg_quote($m[0][$i],'/').'/','',$CSSstr);
217 }
218 else {
219 $CSSstr = preg_replace('/'.preg_quote($m[0][$i],'/').'/',' '.$m[2][$i].' ',$CSSstr);
220 }
221 }
222 }
223
224 // mPDF 5.5.13
225 // Replace any background: url(data:image... with temporary image file reference
226 preg_match_all("/(url\(data:image\/(jpeg|gif|png);base64,(.*)\))/si", $CSSstr, $idata);
227 if (count($idata[0])) {
228 for($i=0;$i<count($idata[0]);$i++) {
229 $file = _MPDF_TEMP_PATH.'_tempCSSidata'.RAND(1,10000).'_'.$i.'.'.$idata[2][$i];
230 //Save to local file
231 file_put_contents($file, base64_decode($idata[3][$i]));
232 // $this->mpdf->GetFullPath($file); // ? is this needed - NO mPDF 5.6.03
233 $CSSstr = str_replace($idata[0][$i], 'url("'.$file.'")', $CSSstr); // mPDF 5.5.17
234 }
235 }
236
237 $CSSstr = preg_replace('/(<\!\-\-|\-\->)/s',' ',$CSSstr);
238 if ($CSSstr ) {
239 preg_match_all('/(.*?)\{(.*?)\}/',$CSSstr,$styles);
240 for($i=0; $i < count($styles[1]) ; $i++) {
241 // SET array e.g. $classproperties['COLOR'] = '#ffffff';
242 $stylestr= trim($styles[2][$i]);
243 $stylearr = explode(';',$stylestr);
244 foreach($stylearr AS $sta) {
245 if (trim($sta)) {
246 // Changed to allow style="background: url('http://www.bpm1.com/bg.jpg')"
247 list($property,$value) = explode(':',$sta,2);
248 $property = trim($property);
249 $value = preg_replace('/\s*!important/i','',$value);
250 $value = trim($value);
251 if ($property && ($value || $value==='0')) {
252 // Ignores -webkit-gradient so doesn't override -moz-
253 if ((strtoupper($property)=='BACKGROUND-IMAGE' || strtoupper($property)=='BACKGROUND') && preg_match('/-webkit-gradient/i',$value)) {
254 continue;
255 }
256 $classproperties[strtoupper($property)] = $value;
257 }
258 }
259 }
260 $classproperties = $this->fixCSS($classproperties);
261 $tagstr = strtoupper(trim($styles[1][$i]));
262 $tagarr = explode(',',$tagstr);
263 $pageselectors = false; // used to turn on $this->mpdf->mirrorMargins
264 foreach($tagarr AS $tg) {
265 $tags = preg_split('/\s+/',trim($tg));
266 $level = count($tags);
267 $t = '';
268 $t2 = '';
269 $t3 = '';
270 if (trim($tags[0])=='@PAGE') {
271 if (isset($tags[0])) { $t = trim($tags[0]); }
272 if (isset($tags[1])) { $t2 = trim($tags[1]); }
273 if (isset($tags[2])) { $t3 = trim($tags[2]); }
274 $tag = '';
275 if ($level==1) { $tag = $t; }
276 else if ($level==2 && preg_match('/^[:](.*)$/',$t2,$m)) {
277 $tag = $t.'>>PSEUDO>>'.$m[1];
278 if ($m[1]=='LEFT' || $m[1]=='RIGHT') { $pageselectors = true; } // used to turn on $this->mpdf->mirrorMargins
279 }
280 else if ($level==2) { $tag = $t.'>>NAMED>>'.$t2; }
281 else if ($level==3 && preg_match('/^[:](.*)$/',$t3,$m)) {
282 $tag = $t.'>>NAMED>>'.$t2.'>>PSEUDO>>'.$m[1];
283 if ($m[1]=='LEFT' || $m[1]=='RIGHT') { $pageselectors = true; } // used to turn on $this->mpdf->mirrorMargins
284 }
285 if (isset($this->CSS[$tag]) && $tag) { $this->CSS[$tag] = $this->array_merge_recursive_unique($this->CSS[$tag], $classproperties); }
286 else if ($tag) { $this->CSS[$tag] = $classproperties; }
287 }
288
289 else if ($level == 1) { // e.g. p or .class or #id or p.class or p#id
290 if (isset($tags[0])) { $t = trim($tags[0]); }
291 if ($t) {
292 $tag = '';
293 if (preg_match('/^[.](.*)$/',$t,$m)) { $tag = 'CLASS>>'.$m[1]; }
294 else if (preg_match('/^[#](.*)$/',$t,$m)) { $tag = 'ID>>'.$m[1]; }
295 else if (preg_match('/^('.$this->mpdf->allowedCSStags.')[.](.*)$/',$t,$m)) { $tag = $m[1].'>>CLASS>>'.$m[2]; }
296 else if (preg_match('/^('.$this->mpdf->allowedCSStags.')\s*:NTH-CHILD\((.*)\)$/',$t,$m)) { $tag = $m[1].'>>SELECTORNTHCHILD>>'.$m[2]; }
297 else if (preg_match('/^('.$this->mpdf->allowedCSStags.')[#](.*)$/',$t,$m)) { $tag = $m[1].'>>ID>>'.$m[2]; }
298 else if (preg_match('/^('.$this->mpdf->allowedCSStags.')$/',$t)) { $tag= $t; }
299 if (isset($this->CSS[$tag]) && $tag) { $this->CSS[$tag] = $this->array_merge_recursive_unique($this->CSS[$tag], $classproperties); }
300 else if ($tag) { $this->CSS[$tag] = $classproperties; }
301 }
302 }
303 else {
304 $tmp = array();
305 for($n=0;$n<$level;$n++) {
306 if (isset($tags[$n])) { $t = trim($tags[$n]); }
307 else { $t = ''; }
308 if ($t) {
309 $tag = '';
310 if (preg_match('/^[.](.*)$/',$t,$m)) { $tag = 'CLASS>>'.$m[1]; }
311 else if (preg_match('/^[#](.*)$/',$t,$m)) { $tag = 'ID>>'.$m[1]; }
312 else if (preg_match('/^('.$this->mpdf->allowedCSStags.')[.](.*)$/',$t,$m)) { $tag = $m[1].'>>CLASS>>'.$m[2]; }
313 else if (preg_match('/^('.$this->mpdf->allowedCSStags.')\s*:NTH-CHILD\((.*)\)$/',$t,$m)) { $tag = $m[1].'>>SELECTORNTHCHILD>>'.$m[2]; }
314 else if (preg_match('/^('.$this->mpdf->allowedCSStags.')[#](.*)$/',$t,$m)) { $tag = $m[1].'>>ID>>'.$m[2]; }
315 else if (preg_match('/^('.$this->mpdf->allowedCSStags.')$/',$t)) { $tag= $t; }
316
317 if ($tag) $tmp[] = $tag;
318 else { break; }
319 }
320 }
321
322 if ($tag) {
323 $x = &$this->cascadeCSS;
324 foreach($tmp AS $tp) { $x = &$x[$tp]; }
325 $x = $this->array_merge_recursive_unique($x, $classproperties);
326 $x['depth'] = $level;
327 }
328 }
329 }
330 if ($pageselectors) { $this->mpdf->mirrorMargins = true; }
331 $properties = array();
332 $values = array();
333 $classproperties = array();
334 }
335 } // end of if
336 //Remove CSS (tags and content), if any
337 $regexp = '/<style.*?>(.*?)<\/style>/si'; // it can be <style> or <style type="txt/css">
338 $html = preg_replace($regexp,'',$html);
339//print_r($this->CSS); exit;
340//print_r($this->cascadeCSS); exit;
341 return $html;
342}
343
344
345
346function readInlineCSS($html) {
347 //Fix incomplete CSS code
348 $size = strlen($html)-1;
349 if (substr($html,$size,1) != ';') $html .= ';';
350 //Make CSS[Name-of-the-class] = array(key => value)
351 $regexp = '|\\s*?(\\S+?):(.+?);|i';
352 preg_match_all( $regexp, $html, $styleinfo);
353 $properties = $styleinfo[1];
354 $values = $styleinfo[2];
355 //Array-properties and Array-values must have the SAME SIZE!
356 $classproperties = array();
357 for($i = 0; $i < count($properties) ; $i++) {
358 // Ignores -webkit-gradient so doesn't override -moz-
359 if ((strtoupper($properties[$i])=='BACKGROUND-IMAGE' || strtoupper($properties[$i])=='BACKGROUND') && preg_match('/-webkit-gradient/i',$values[$i])) {
360 continue;
361 }
362 $classproperties[strtoupper($properties[$i])] = trim($values[$i]);
363 }
364 return $this->fixCSS($classproperties);
365}
366
367
368
369function _fix_borderStr($bd) {
370 preg_match_all("/\((.*?)\)/", $bd, $m);
371 if (count($m[1])) {
372 for($i=0;$i<count($m[1]);$i++) {
373 $sub = preg_replace("/ /", "", $m[1][$i]);
374 $bd = preg_replace('/'.preg_quote($m[1][$i], '/').'/si', $sub, $bd);
375 }
376 }
377
378 $prop = preg_split('/\s+/',trim($bd));
379 $w = 'medium';
380 $c = '#000000';
381 $s = 'none';
382
383 if ( count($prop) == 1 ) {
384 // solid
385 if (in_array($prop[0],$this->mpdf->borderstyles) || $prop[0] == 'none' || $prop[0] == 'hidden' ) { $s = $prop[0]; }
386 // #000000
387 else if (is_array($this->mpdf->ConvertColor($prop[0]))) { $c = $prop[0]; }
388 // 1px
389 else { $w = $prop[0]; }
390 }
391 else if (count($prop) == 2 ) {
392 // 1px solid
393 if (in_array($prop[1],$this->mpdf->borderstyles) || $prop[1] == 'none' || $prop[1] == 'hidden' ) { $w = $prop[0]; $s = $prop[1]; }
394 // solid #000000
395 else if (in_array($prop[0],$this->mpdf->borderstyles) || $prop[0] == 'none' || $prop[0] == 'hidden' ) { $s = $prop[0]; $c = $prop[1]; }
396 // 1px #000000
397 else { $w = $prop[0]; $c = $prop[1]; }
398 }
399 else if ( count($prop) == 3 ) {
400 // Change #000000 1px solid to 1px solid #000000 (proper)
401 if (substr($prop[0],0,1) == '#') { $c = $prop[0]; $w = $prop[1]; $s = $prop[2]; }
402 // Change solid #000000 1px to 1px solid #000000 (proper)
403 else if (substr($prop[0],1,1) == '#') { $s = $prop[0]; $c = $prop[1]; $w = $prop[2]; }
404 // Change solid 1px #000000 to 1px solid #000000 (proper)
405 else if (in_array($prop[0],$this->mpdf->borderstyles) || $prop[0] == 'none' || $prop[0] == 'hidden' ) {
406 $s = $prop[0]; $w = $prop[1]; $c = $prop[2];
407 }
408 else { $w = $prop[0]; $s = $prop[1]; $c = $prop[2]; }
409 }
410 else { return ''; }
411 $s = strtolower($s);
412 return $w.' '.$s.' '.$c;
413}
414
415
416
417function fixCSS($prop) {
418 if (!is_array($prop) || (count($prop)==0)) return array();
419 $newprop = array();
420 foreach($prop AS $k => $v) {
421 if ($k != 'BACKGROUND-IMAGE' && $k != 'BACKGROUND' && $k != 'ODD-HEADER-NAME' && $k != 'EVEN-HEADER-NAME' && $k != 'ODD-FOOTER-NAME' && $k != 'EVEN-FOOTER-NAME' && $k != 'HEADER' && $k != 'FOOTER') {
422 $v = strtolower($v);
423 }
424
425 if ($k == 'FONT') {
426 $s = trim($v);
427 preg_match_all('/\"(.*?)\"/',$s,$ff);
428 if (count($ff[1])) {
429 foreach($ff[1] AS $ffp) {
430 $w = preg_split('/\s+/',$ffp);
431 $s = preg_replace('/\"'.$ffp.'\"/',$w[0],$s);
432 }
433 }
434 preg_match_all('/\'(.*?)\'/',$s,$ff);
435 if (count($ff[1])) {
436 foreach($ff[1] AS $ffp) {
437 $w = preg_split('/\s+/',$ffp);
438 $s = preg_replace('/\''.$ffp.'\'/',$w[0],$s);
439 }
440 }
441 $s = preg_replace('/\s*,\s*/',',',$s);
442 $bits = preg_split('/\s+/',$s);
443 if (count($bits)>1) {
444 $k = 'FONT-FAMILY'; $v = $bits[(count($bits)-1)];
445 $fs = $bits[(count($bits)-2)];
446 if (preg_match('/(.*?)\/(.*)/',$fs, $fsp)) {
447 $newprop['FONT-SIZE'] = $fsp[1];
448 $newprop['LINE-HEIGHT'] = $fsp[2];
449 }
450 else { $newprop['FONT-SIZE'] = $fs; }
451 if (preg_match('/(italic|oblique)/i',$s)) { $newprop['FONT-STYLE'] = 'italic'; }
452 else { $newprop['FONT-STYLE'] = 'normal'; }
453 if (preg_match('/bold/i',$s)) { $newprop['FONT-WEIGHT'] = 'bold'; }
454 else { $newprop['FONT-WEIGHT'] = 'normal'; }
455 if (preg_match('/small-caps/i',$s)) { $newprop['TEXT-TRANSFORM'] = 'uppercase'; }
456 }
457 }
458 if ($k == 'FONT-FAMILY') {
459 $aux_fontlist = explode(",",$v);
460 $found = 0;
461 foreach($aux_fontlist AS $f) {
462 $fonttype = trim($f);
463 $fonttype = preg_replace('/["\']*(.*?)["\']*/','\\1',$fonttype);
464 $fonttype = preg_replace('/ /','',$fonttype);
465 $v = strtolower(trim($fonttype));
466 if (isset($this->mpdf->fonttrans[$v]) && $this->mpdf->fonttrans[$v]) { $v = $this->mpdf->fonttrans[$v]; }
467 if ((!$this->mpdf->onlyCoreFonts && in_array($v,$this->mpdf->available_unifonts)) ||
468 in_array($v,array('ccourier','ctimes','chelvetica')) ||
469 ($this->mpdf->onlyCoreFonts && in_array($v,array('courier','times','helvetica','arial'))) ||
470 in_array($v, array('sjis','uhc','big5','gb'))) {
471 $newprop[$k] = $v;
472 $found = 1;
473 break;
474 }
475 }
476 if (!$found) {
477 foreach($aux_fontlist AS $f) {
478 $fonttype = trim($f);
479 $fonttype = preg_replace('/["\']*(.*?)["\']*/','\\1',$fonttype);
480 $fonttype = preg_replace('/ /','',$fonttype);
481 $v = strtolower(trim($fonttype));
482 if (isset($this->mpdf->fonttrans[$v]) && $this->mpdf->fonttrans[$v]) { $v = $this->mpdf->fonttrans[$v]; }
483 if (in_array($v,$this->mpdf->sans_fonts) || in_array($v,$this->mpdf->serif_fonts) || in_array($v,$this->mpdf->mono_fonts) ) {
484 $newprop[$k] = $v;
485 break;
486 }
487 }
488 }
489 }
490 else if ($k == 'MARGIN') {
491 $tmp = $this->expand24($v);
492 $newprop['MARGIN-TOP'] = $tmp['T'];
493 $newprop['MARGIN-RIGHT'] = $tmp['R'];
494 $newprop['MARGIN-BOTTOM'] = $tmp['B'];
495 $newprop['MARGIN-LEFT'] = $tmp['L'];
496 }
497/*-- BORDER-RADIUS --*/
498 else if ($k == 'BORDER-RADIUS' || $k == 'BORDER-TOP-LEFT-RADIUS' || $k == 'BORDER-TOP-RIGHT-RADIUS' || $k == 'BORDER-BOTTOM-LEFT-RADIUS' || $k == 'BORDER-BOTTOM-RIGHT-RADIUS') {
499 $tmp = $this->border_radius_expand($v,$k);
500 if (isset($tmp['TL-H'])) $newprop['BORDER-TOP-LEFT-RADIUS-H'] = $tmp['TL-H'];
501 if (isset($tmp['TL-V'])) $newprop['BORDER-TOP-LEFT-RADIUS-V'] = $tmp['TL-V'];
502 if (isset($tmp['TR-H'])) $newprop['BORDER-TOP-RIGHT-RADIUS-H'] = $tmp['TR-H'];
503 if (isset($tmp['TR-V'])) $newprop['BORDER-TOP-RIGHT-RADIUS-V'] = $tmp['TR-V'];
504 if (isset($tmp['BL-H'])) $newprop['BORDER-BOTTOM-LEFT-RADIUS-H'] = $tmp['BL-H'];
505 if (isset($tmp['BL-V'])) $newprop['BORDER-BOTTOM-LEFT-RADIUS-V'] = $tmp['BL-V'];
506 if (isset($tmp['BR-H'])) $newprop['BORDER-BOTTOM-RIGHT-RADIUS-H'] = $tmp['BR-H'];
507 if (isset($tmp['BR-V'])) $newprop['BORDER-BOTTOM-RIGHT-RADIUS-V'] = $tmp['BR-V'];
508 }
509/*-- END BORDER-RADIUS --*/
510 else if ($k == 'PADDING') {
511 $tmp = $this->expand24($v);
512 $newprop['PADDING-TOP'] = $tmp['T'];
513 $newprop['PADDING-RIGHT'] = $tmp['R'];
514 $newprop['PADDING-BOTTOM'] = $tmp['B'];
515 $newprop['PADDING-LEFT'] = $tmp['L'];
516 }
517 else if ($k == 'BORDER') {
518 if ($v == '1') { $v = '1px solid #000000'; }
519 else { $v = $this->_fix_borderStr($v); }
520 $newprop['BORDER-TOP'] = $v;
521 $newprop['BORDER-RIGHT'] = $v;
522 $newprop['BORDER-BOTTOM'] = $v;
523 $newprop['BORDER-LEFT'] = $v;
524 }
525 else if ($k == 'BORDER-TOP') {
526 $newprop['BORDER-TOP'] = $this->_fix_borderStr($v);
527 }
528 else if ($k == 'BORDER-RIGHT') {
529 $newprop['BORDER-RIGHT'] = $this->_fix_borderStr($v);
530 }
531 else if ($k == 'BORDER-BOTTOM') {
532 $newprop['BORDER-BOTTOM'] = $this->_fix_borderStr($v);
533 }
534 else if ($k == 'BORDER-LEFT') {
535 $newprop['BORDER-LEFT'] = $this->_fix_borderStr($v);
536 }
537 else if ($k == 'BORDER-STYLE') {
538 $e = $this->expand24($v);
539 $newprop['BORDER-TOP-STYLE'] = $e['T'];
540 $newprop['BORDER-RIGHT-STYLE'] = $e['R'];
541 $newprop['BORDER-BOTTOM-STYLE'] = $e['B'];
542 $newprop['BORDER-LEFT-STYLE'] = $e['L'];
543 }
544 else if ($k == 'BORDER-WIDTH') {
545 $e = $this->expand24($v);
546 $newprop['BORDER-TOP-WIDTH'] = $e['T'];
547 $newprop['BORDER-RIGHT-WIDTH'] = $e['R'];
548 $newprop['BORDER-BOTTOM-WIDTH'] = $e['B'];
549 $newprop['BORDER-LEFT-WIDTH'] = $e['L'];
550 }
551 else if ($k == 'BORDER-COLOR') {
552 $e = $this->expand24($v);
553 $newprop['BORDER-TOP-COLOR'] = $e['T'];
554 $newprop['BORDER-RIGHT-COLOR'] = $e['R'];
555 $newprop['BORDER-BOTTOM-COLOR'] = $e['B'];
556 $newprop['BORDER-LEFT-COLOR'] = $e['L'];
557 }
558
559 else if ($k == 'BORDER-SPACING') {
560 $prop = preg_split('/\s+/',trim($v));
561 if (count($prop) == 1 ) {
562 $newprop['BORDER-SPACING-H'] = $prop[0];
563 $newprop['BORDER-SPACING-V'] = $prop[0];
564 }
565 else if (count($prop) == 2 ) {
566 $newprop['BORDER-SPACING-H'] = $prop[0];
567 $newprop['BORDER-SPACING-V'] = $prop[1];
568 }
569 }
570 else if ($k == 'TEXT-OUTLINE') { // mPDF 5.6.07
571 $prop = preg_split('/\s+/',trim($v));
572 if (trim(strtolower($v)) == 'none' ) {
573 $newprop['TEXT-OUTLINE'] = 'none';
574 }
575 else if (count($prop) == 2 ) {
576 $newprop['TEXT-OUTLINE-WIDTH'] = $prop[0];
577 $newprop['TEXT-OUTLINE-COLOR'] = $prop[1];
578 }
579 else if (count($prop) == 3 ) {
580 $newprop['TEXT-OUTLINE-WIDTH'] = $prop[0];
581 $newprop['TEXT-OUTLINE-COLOR'] = $prop[2];
582 }
583 }
584 else if ($k == 'SIZE') {
585 $prop = preg_split('/\s+/',trim($v));
586 if (preg_match('/(auto|portrait|landscape)/',$prop[0])) {
587 $newprop['SIZE'] = strtoupper($prop[0]);
588 }
589 else if (count($prop) == 1 ) {
590 $newprop['SIZE']['W'] = $this->mpdf->ConvertSize($prop[0]);
591 $newprop['SIZE']['H'] = $this->mpdf->ConvertSize($prop[0]);
592 }
593 else if (count($prop) == 2 ) {
594 $newprop['SIZE']['W'] = $this->mpdf->ConvertSize($prop[0]);
595 $newprop['SIZE']['H'] = $this->mpdf->ConvertSize($prop[1]);
596 }
597 }
598 else if ($k == 'SHEET-SIZE') {
599 $prop = preg_split('/\s+/',trim($v));
600 if (count($prop) == 2 ) {
601 $newprop['SHEET-SIZE'] = array($this->mpdf->ConvertSize($prop[0]), $this->mpdf->ConvertSize($prop[1]));
602 }
603 else {
604 if(preg_match('/([0-9a-zA-Z]*)-L/i',$v,$m)) { // e.g. A4-L = A$ landscape
605 $ft = $this->mpdf->_getPageFormat($m[1]);
606 $format = array($ft[1],$ft[0]);
607 }
608 else { $format = $this->mpdf->_getPageFormat($v); }
609 if ($format) { $newprop['SHEET-SIZE'] = array($format[0]/_MPDFK, $format[1]/_MPDFK); }
610 }
611 }
612 else if ($k == 'BACKGROUND') {
613 $bg = $this->parseCSSbackground($v);
614 if ($bg['c']) { $newprop['BACKGROUND-COLOR'] = $bg['c']; }
615 else { $newprop['BACKGROUND-COLOR'] = 'transparent'; }
616/*-- BACKGROUNDS --*/
617 if ($bg['i']) {
618 $newprop['BACKGROUND-IMAGE'] = $bg['i'];
619 if ($bg['r']) { $newprop['BACKGROUND-REPEAT'] = $bg['r']; }
620 if ($bg['p']) { $newprop['BACKGROUND-POSITION'] = $bg['p']; }
621 }
622 else { $newprop['BACKGROUND-IMAGE'] = ''; }
623/*-- END BACKGROUNDS --*/
624 }
625/*-- BACKGROUNDS --*/
626 else if ($k == 'BACKGROUND-IMAGE') {
627 if (preg_match('/(-moz-)*(repeating-)*(linear|radial)-gradient\(.*\)/i',$v,$m)) {
628 $newprop['BACKGROUND-IMAGE'] = $m[0];
629 continue;
630 }
631 if (preg_match('/url\([\'\"]{0,1}(.*?)[\'\"]{0,1}\)/i',$v,$m)) {
632 $newprop['BACKGROUND-IMAGE'] = $m[1];
633 }
634
635 else if (strtolower($v)=='none') { $newprop['BACKGROUND-IMAGE'] = ''; }
636
637 }
638 else if ($k == 'BACKGROUND-REPEAT') {
639 if (preg_match('/(repeat-x|repeat-y|no-repeat|repeat)/i',$v,$m)) {
640 $newprop['BACKGROUND-REPEAT'] = strtolower($m[1]);
641 }
642 }
643 else if ($k == 'BACKGROUND-POSITION') {
644 $s = $v;
645 $bits = preg_split('/\s+/',trim($s));
646 // These should be Position x1 or x2
647 if (count($bits)==1) {
648 if (preg_match('/bottom/',$bits[0])) { $bg['p'] = '50% 100%'; }
649 else if (preg_match('/top/',$bits[0])) { $bg['p'] = '50% 0%'; }
650 else { $bg['p'] = $bits[0] . ' 50%'; }
651 }
652 else if (count($bits)==2) {
653 // Can be either right center or center right
654 if (preg_match('/(top|bottom)/',$bits[0]) || preg_match('/(left|right)/',$bits[1])) {
655 $bg['p'] = $bits[1] . ' '.$bits[0];
656 }
657 else {
658 $bg['p'] = $bits[0] . ' '.$bits[1];
659 }
660 }
661 if ($bg['p']) {
662 $bg['p'] = preg_replace('/(left|top)/','0%',$bg['p']);
663 $bg['p'] = preg_replace('/(right|bottom)/','100%',$bg['p']);
664 $bg['p'] = preg_replace('/(center)/','50%',$bg['p']);
665 if (!preg_match('/[\-]{0,1}\d+(in|cm|mm|pt|pc|em|ex|px|%)* [\-]{0,1}\d+(in|cm|mm|pt|pc|em|ex|px|%)*/',$bg['p'])) {
666 $bg['p'] = false;
667 }
668 }
669 if ($bg['p']) { $newprop['BACKGROUND-POSITION'] = $bg['p']; }
670 }
671/*-- END BACKGROUNDS --*/
672 else if ($k == 'IMAGE-ORIENTATION') {
673 if (preg_match('/([\-]*[0-9\.]+)(deg|grad|rad)/i',$v,$m)) {
674 $angle = $m[1] + 0;
675 if (strtolower($m[2])=='deg') { $angle = $angle; }
676 else if (strtolower($m[2])=='grad') { $angle *= (360/400); }
677 else if (strtolower($m[2])=='rad') { $angle = rad2deg($angle); }
678 while($angle < 0) { $angle += 360; }
679 $angle = ($angle % 360);
680 $angle /= 90;
681 $angle = round($angle) * 90;
682 $newprop['IMAGE-ORIENTATION'] = $angle;
683 }
684 }
685 // mPDF 5.6.13
686 else if ($k == 'TEXT-ALIGN') {
687 if (preg_match('/["\'](.){1}["\']/i',$v,$m)) {
688 $d = array_search($m[1],$this->mpdf->decimal_align);
689 if ($d !== false) { $newprop['TEXT-ALIGN'] = $d; }
690 if (preg_match('/(center|left|right)/i',$v,$m)) { $newprop['TEXT-ALIGN'] .= strtoupper(substr($m[1],0,1)); }
691 else { $newprop['TEXT-ALIGN'] .= 'R'; } // default = R
692 }
693 else if (preg_match('/["\'](\\\[a-fA-F0-9]{1,6})["\']/i',$v,$m)) {
694 $utf8 = codeHex2utf(substr($m[1],1,6));
695 $d = array_search($utf8,$this->mpdf->decimal_align);
696 if ($d !== false) { $newprop['TEXT-ALIGN'] = $d; }
697 if (preg_match('/(center|left|right)/i',$v,$m)) { $newprop['TEXT-ALIGN'] .= strtoupper(substr($m[1],0,1)); }
698 else { $newprop['TEXT-ALIGN'] .= 'R'; } // default = R
699 }
700 else { $newprop[$k] = $v; }
701 }
702
703 else {
704 $newprop[$k] = $v;
705 }
706 }
707
708 return $newprop;
709}
710
711function setCSSboxshadow($v) {
712 $sh = array();
713 $c = preg_match_all('/(rgba|rgb|device-cmyka|cmyka|device-cmyk|cmyk|hsla|hsl)\(.*?\)/',$v,$x); // mPDF 5.6.05
714 for($i=0; $i<$c; $i++) {
715 $col = preg_replace('/,/','*',$x[0][$i]);
716 $v = preg_replace('/'.preg_quote($x[0][$i],'/').'/',$col,$v);
717 }
718 $ss = explode(',',$v);
719 foreach ($ss AS $s) {
720 $new = array('inset'=>false, 'blur'=>0, 'spread'=>0);
721 if (preg_match('/inset/i',$s)) { $new['inset'] = true; $s = preg_replace('/\s*inset\s*/','',$s); }
722 $p = explode(' ',trim($s));
723 if (isset($p[0])) { $new['x'] = $this->mpdf->ConvertSize(trim($p[0]),$this->mpdf->blk[$this->mpdf->blklvl-1]['inner_width'],$this->mpdf->FontSize,false); }
724 if (isset($p[1])) { $new['y'] = $this->mpdf->ConvertSize(trim($p[1]),$this->mpdf->blk[$this->mpdf->blklvl-1]['inner_width'],$this->mpdf->FontSize,false); }
725 if (isset($p[2])) {
726 if (preg_match('/^\s*[\.\-0-9]/',$p[2])) {
727 $new['blur'] = $this->mpdf->ConvertSize(trim($p[2]),$this->mpdf->blk[$this->mpdf->blklvl-1]['inner_width'],$this->mpdf->FontSize,false);
728 }
729 else { $new['col'] = $this->mpdf->ConvertColor(preg_replace('/\*/',',',$p[2])); }
730 if (isset($p[3])) {
731 if (preg_match('/^\s*[\.\-0-9]/',$p[3])) {
732 $new['spread'] = $this->mpdf->ConvertSize(trim($p[3]),$this->mpdf->blk[$this->mpdf->blklvl-1]['inner_width'],$this->mpdf->FontSize,false);
733 }
734 else { $new['col'] = $this->mpdf->ConvertColor(preg_replace('/\*/',',',$p[3])); }
735 if (isset($p[4])) {
736 $new['col'] = $this->mpdf->ConvertColor(preg_replace('/\*/',',',$p[4]));
737 }
738 }
739 }
740 if (!$new['col']) { $new['col'] = $this->mpdf->ConvertColor('#888888'); }
741 if (isset($new['y'])) { array_unshift($sh, $new); }
742 }
743 return $sh;
744}
745
746function setCSStextshadow($v) {
747 $sh = array();
748 $c = preg_match_all('/(rgba|rgb|device-cmyka|cmyka|device-cmyk|cmyk|hsla|hsl)\(.*?\)/',$v,$x); // mPDF 5.6.05
749 for($i=0; $i<$c; $i++) {
750 $col = preg_replace('/,/','*',$x[0][$i]);
751 $v = preg_replace('/'.preg_quote($x[0][$i],'/').'/',$col,$v);
752 }
753 $ss = explode(',',$v);
754 foreach ($ss AS $s) {
755 $new = array('blur'=>0);
756 $p = explode(' ',trim($s));
757 if (isset($p[0])) { $new['x'] = $this->mpdf->ConvertSize(trim($p[0]),$this->mpdf->blk[$this->mpdf->blklvl-1]['inner_width'],$this->mpdf->FontSize,false); }
758 if (isset($p[1])) { $new['y'] = $this->mpdf->ConvertSize(trim($p[1]),$this->mpdf->blk[$this->mpdf->blklvl-1]['inner_width'],$this->mpdf->FontSize,false); }
759 if (isset($p[2])) {
760 if (preg_match('/^\s*[\.\-0-9]/',$p[2])) {
761 $new['blur'] = $this->mpdf->ConvertSize(trim($p[2]),$this->mpdf->blk[$this->mpdf->blklvl-1]['inner_width'],$this->mpdf->FontSize,false);
762 }
763 else { $new['col'] = $this->mpdf->ConvertColor(preg_replace('/\*/',',',$p[2])); }
764 if (isset($p[3])) {
765 $new['col'] = $this->mpdf->ConvertColor(preg_replace('/\*/',',',$p[3]));
766 }
767 }
768 if (!$new['col']) { $new['col'] = $this->mpdf->ConvertColor('#888888'); }
769 if (isset($new['y'])) { array_unshift($sh, $new); }
770 }
771 return $sh;
772}
773
774function parseCSSbackground($s) {
775 $bg = array('c'=>false, 'i'=>false, 'r'=>false, 'p'=>false, );
776/*-- BACKGROUNDS --*/
777 if (preg_match('/(-moz-)*(repeating-)*(linear|radial)-gradient\(.*\)/i',$s,$m)) {
778 $bg['i'] = $m[0];
779 }
780 else
781/*-- END BACKGROUNDS --*/
782 if (preg_match('/url\(/i',$s)) {
783 // If color, set and strip it off
784 // mPDF 5.6.05
785 if (preg_match('/^\s*(#[0-9a-fA-F]{3,6}|(rgba|rgb|device-cmyka|cmyka|device-cmyk|cmyk|hsla|hsl|spot)\(.*?\)|[a-zA-Z]{3,})\s+(url\(.*)/i',$s,$m)) {
786 $bg['c'] = strtolower($m[1]);
787 $s = $m[3];
788 }
789/*-- BACKGROUNDS --*/
790 if (preg_match('/url\([\'\"]{0,1}(.*?)[\'\"]{0,1}\)\s*(.*)/i',$s,$m)) {
791 $bg['i'] = $m[1];
792 $s = strtolower($m[2]);
793 if (preg_match('/(repeat-x|repeat-y|no-repeat|repeat)/',$s,$m)) {
794 $bg['r'] = $m[1];
795 }
796 // Remove repeat, attachment (discarded) and also any inherit
797 $s = preg_replace('/(repeat-x|repeat-y|no-repeat|repeat|scroll|fixed|inherit)/','',$s);
798 $bits = preg_split('/\s+/',trim($s));
799 // These should be Position x1 or x2
800 if (count($bits)==1) {
801 if (preg_match('/bottom/',$bits[0])) { $bg['p'] = '50% 100%'; }
802 else if (preg_match('/top/',$bits[0])) { $bg['p'] = '50% 0%'; }
803 else { $bg['p'] = $bits[0] . ' 50%'; }
804 }
805 else if (count($bits)==2) {
806 // Can be either right center or center right
807 if (preg_match('/(top|bottom)/',$bits[0]) || preg_match('/(left|right)/',$bits[1])) {
808 $bg['p'] = $bits[1] . ' '.$bits[0];
809 }
810 else {
811 $bg['p'] = $bits[0] . ' '.$bits[1];
812 }
813 }
814 if ($bg['p']) {
815 $bg['p'] = preg_replace('/(left|top)/','0%',$bg['p']);
816 $bg['p'] = preg_replace('/(right|bottom)/','100%',$bg['p']);
817 $bg['p'] = preg_replace('/(center)/','50%',$bg['p']);
818 if (!preg_match('/[\-]{0,1}\d+(in|cm|mm|pt|pc|em|ex|px|%)* [\-]{0,1}\d+(in|cm|mm|pt|pc|em|ex|px|%)*/',$bg['p'])) {
819 $bg['p'] = false;
820 }
821 }
822 }
823/*-- END BACKGROUNDS --*/
824 }
825 else if (preg_match('/^\s*(#[0-9a-fA-F]{3,6}|(rgba|rgb|device-cmyka|cmyka|device-cmyk|cmyk|hsla|hsl|spot)\(.*?\)|[a-zA-Z]{3,})/i',$s,$m)) { $bg['c'] = strtolower($m[1]); } // mPDF 5.6.05
826 return ($bg);
827}
828
829
830function expand24($mp) {
831 $prop = preg_split('/\s+/',trim($mp));
832 if (count($prop) == 1 ) {
833 return array('T' => $prop[0], 'R' => $prop[0], 'B' => $prop[0], 'L'=> $prop[0]);
834 }
835 if (count($prop) == 2 ) {
836 return array('T' => $prop[0], 'R' => $prop[1], 'B' => $prop[0], 'L'=> $prop[1]);
837 }
838
839 if (count($prop) == 3 ) {
840 return array('T' => $prop[0], 'R' => $prop[1], 'B' => $prop[2], 'L'=> $prop[1]);
841 }
842 if (count($prop) == 4 ) {
843 return array('T' => $prop[0], 'R' => $prop[1], 'B' => $prop[2], 'L'=> $prop[3]);
844 }
845 return array();
846}
847
848/*-- BORDER-RADIUS --*/
849function border_radius_expand($val,$k) {
850 $b = array();
851 if ($k == 'BORDER-RADIUS') {
852 $hv = explode('/',trim($val));
853 $prop = preg_split('/\s+/',trim($hv[0]));
854 if (count($prop)==1) {
855 $b['TL-H'] = $b['TR-H'] = $b['BR-H'] = $b['BL-H'] = $prop[0];
856 }
857 else if (count($prop)==2) {
858 $b['TL-H'] = $b['BR-H'] = $prop[0];
859 $b['TR-H'] = $b['BL-H'] = $prop[1];
860 }
861 else if (count($prop)==3) {
862 $b['TL-H'] = $prop[0];
863 $b['TR-H'] = $b['BL-H'] = $prop[1];
864 $b['BR-H'] = $prop[2];
865 }
866 else if (count($prop)==4) {
867 $b['TL-H'] = $prop[0];
868 $b['TR-H'] = $prop[1];
869 $b['BR-H'] = $prop[2];
870 $b['BL-H'] = $prop[3];
871 }
872 if (count($hv)==2) {
873 $prop = preg_split('/\s+/',trim($hv[1]));
874 if (count($prop)==1) {
875 $b['TL-V'] = $b['TR-V'] = $b['BR-V'] = $b['BL-V'] = $prop[0];
876 }
877 else if (count($prop)==2) {
878 $b['TL-V'] = $b['BR-V'] = $prop[0];
879 $b['TR-V'] = $b['BL-V'] = $prop[1];
880 }
881 else if (count($prop)==3) {
882 $b['TL-V'] = $prop[0];
883 $b['TR-V'] = $b['BL-V'] = $prop[1];
884 $b['BR-V'] = $prop[2];
885 }
886 else if (count($prop)==4) {
887 $b['TL-V'] = $prop[0];
888 $b['TR-V'] = $prop[1];
889 $b['BR-V'] = $prop[2];
890 $b['BL-V'] = $prop[3];
891 }
892 }
893 else {
894 $b['TL-V'] = $b['TL-H'];
895 $b['TR-V'] = $b['TR-H'];
896 $b['BL-V'] = $b['BL-H'];
897 $b['BR-V'] = $b['BR-H'];
898 }
899 return $b;
900 }
901
902 // Parse 2
903 $h = 0;
904 $v = 0;
905 $prop = preg_split('/\s+/',trim($val));
906 if (count($prop)==1) { $h = $v = $val; }
907 else { $h = $prop[0]; $v = $prop[1]; }
908 if ($h==0 || $v==0) { $h = $v = 0; }
909 if ($k == 'BORDER-TOP-LEFT-RADIUS') {
910 $b['TL-H'] = $h;
911 $b['TL-V'] = $v;
912 }
913 else if ($k == 'BORDER-TOP-RIGHT-RADIUS') {
914 $b['TR-H'] = $h;
915 $b['TR-V'] = $v;
916 }
917 else if ($k == 'BORDER-BOTTOM-LEFT-RADIUS') {
918 $b['BL-H'] = $h;
919 $b['BL-V'] = $v;
920 }
921 else if ($k == 'BORDER-BOTTOM-RIGHT-RADIUS') {
922 $b['BR-H'] = $h;
923 $b['BR-V'] = $v;
924 }
925 return $b;
926
927}
928/*-- END BORDER-RADIUS --*/
929
930function _mergeCSS($p, &$t) {
931 // Save Cascading CSS e.g. "div.topic p" at this block level
932 if (isset($p) && $p) {
933 if ($t) {
934 $t = $this->array_merge_recursive_unique($t, $p);
935 }
936 else { $t = $p; }
937 }
938}
939
940// for CSS handling
941function array_merge_recursive_unique($array1, $array2) {
942 $arrays = func_get_args();
943 $narrays = count($arrays);
944 $ret = $arrays[0];
945 for ($i = 1; $i < $narrays; $i ++) {
946 foreach ($arrays[$i] as $key => $value) {
947 if (((string) $key) === ((string) intval($key))) { // integer or string as integer key - append
948 $ret[] = $value;
949 }
950 else { // string key - merge
951 if (is_array($value) && isset($ret[$key])) {
952 $ret[$key] = $this->array_merge_recursive_unique($ret[$key], $value);
953 }
954 else {
955 $ret[$key] = $value;
956 }
957 }
958 }
959 }
960 return $ret;
961}
962
963
964
965function _mergeFullCSS($p, &$t, $tag, $classes, $id) {
966 $this->_mergeCSS($p[$tag], $t);
967 // STYLESHEET CLASS e.g. .smallone{} .redletter{}
968 foreach($classes AS $class) {
969 $this->_mergeCSS($p['CLASS>>'.$class], $t);
970 }
971 // STYLESHEET nth-child SELECTOR e.g. tr:nth-child(odd) td:nth-child(2n+1)
972 if ($tag=='TR' && isset($p) && $p) {
973 foreach($p AS $k=>$val) {
974 if (preg_match('/'.$tag.'>>SELECTORNTHCHILD>>(.*)/',$k, $m)) {
975 $select = false;
976 if ($tag=='TR') {
977 $row = $this->mpdf->row;
978 $thnr = (isset($this->mpdf->table[$this->mpdf->tableLevel][$this->mpdf->tbctr[$this->mpdf->tableLevel]]['is_thead']) ? count($this->mpdf->table[$this->mpdf->tableLevel][$this->mpdf->tbctr[$this->mpdf->tableLevel]]['is_thead']) : 0);
979 $tfnr = (isset($this->mpdf->table[$this->mpdf->tableLevel][$this->mpdf->tbctr[$this->mpdf->tableLevel]]['is_tfoot']) ? count($this->mpdf->table[$this->mpdf->tableLevel][$this->mpdf->tbctr[$this->mpdf->tableLevel]]['is_tfoot']) : 0);
980 if ($this->mpdf->tabletfoot) { $row -= $thnr; }
981 else if (!$this->mpdf->tablethead) { $row -= ($thnr + $tfnr); }
982 if ($m[1]=='ODD' && ($row % 2) == 0) { $select = true; }
983 else if ($m[1]=='EVEN' && ($row % 2) == 1) { $select = true; }
984 else if (preg_match('/(\d+)N\+(\d+)/',$m[1],$a)) {
985 if ((($row + 1) % $a[1]) == $a[2]) { $select = true; }
986 }
987 }
988 else if ($tag=='TD' || $tag=='TH') {
989 if ($m[1]=='ODD' && ($this->mpdf->col % 2) == 0) { $select = true; }
990 else if ($m[1]=='EVEN' && ($this->mpdf->col % 2) == 1) { $select = true; }
991 else if (preg_match('/(\d+)N\+(\d+)/',$m[1],$a)) {
992 if ((($this->mpdf->col + 1) % $a[1]) == $a[2]) { $select = true; }
993 }
994 }
995 if ($select) {
996 $this->_mergeCSS($p[$tag.'>>SELECTORNTHCHILD>>'.$m[1]], $t);
997 }
998 }
999 }
1000 }
1001 // STYLESHEET CLASS e.g. #smallone{} #redletter{}
1002 if (isset($id) && $id) {
1003 $this->_mergeCSS($p['ID>>'.$id], $t);
1004 }
1005 // STYLESHEET CLASS e.g. .smallone{} .redletter{}
1006 foreach($classes AS $class) {
1007 $this->_mergeCSS($p[$tag.'>>CLASS>>'.$class], $t);
1008 }
1009 // STYLESHEET CLASS e.g. #smallone{} #redletter{}
1010 if (isset($id)) {
1011 $this->_mergeCSS($p[$tag.'>>ID>>'.$id], $t);
1012 }
1013}
1014
1015function setBorderDominance($prop, $val) {
1016 if (isset($prop['BORDER-LEFT']) && $prop['BORDER-LEFT']) { $this->cell_border_dominance_L = $val; }
1017 if (isset($prop['BORDER-RIGHT']) && $prop['BORDER-RIGHT']) { $this->cell_border_dominance_R = $val; }
1018 if (isset($prop['BORDER-TOP']) && $prop['BORDER-TOP']) { $this->cell_border_dominance_T = $val; }
1019 if (isset($prop['BORDER-BOTTOM']) && $prop['BORDER-BOTTOM']) { $this->cell_border_dominance_B = $val; }
1020}
1021
1022function _set_mergedCSS(&$m, &$p, $d=true, $bd=false) {
1023 if (isset($m)) {
1024 if ((isset($m['depth']) && $m['depth']>1) || $d==false) { // include check for 'depth'
1025 if ($bd) { $this->setBorderDominance($m, $bd); } // *TABLES*
1026 if (is_array($m)) {
1027 $p = array_merge($p,$m);
1028 $this->_mergeBorders($p,$m);
1029 }
1030 }
1031 }
1032}
1033
1034
1035function _mergeBorders(&$b, &$a) { // Merges $a['BORDER-TOP-STYLE'] to $b['BORDER-TOP'] etc.
1036 foreach(array('TOP','RIGHT','BOTTOM','LEFT') AS $side) {
1037 foreach(array('STYLE','WIDTH','COLOR') AS $el) {
1038 if (isset($a['BORDER-'.$side.'-'.$el])) { // e.g. $b['BORDER-TOP-STYLE']
1039 $s = trim($a['BORDER-'.$side.'-'.$el]);
1040 if (isset($b['BORDER-'.$side])) { // e.g. $b['BORDER-TOP']
1041 $p = trim($b['BORDER-'.$side]);
1042 }
1043 else { $p = ''; }
1044 if ($el=='STYLE') {
1045 if ($p) { $b['BORDER-'.$side] = preg_replace('/(\S+)\s+(\S+)\s+(\S+)/', '\\1 '.$s.' \\3', $p); }
1046 else { $b['BORDER-'.$side] = '0px '.$s.' #000000'; }
1047 }
1048 else if ($el=='WIDTH') {
1049 if ($p) { $b['BORDER-'.$side] = preg_replace('/(\S+)\s+(\S+)\s+(\S+)/', $s.' \\2 \\3', $p); }
1050 else { $b['BORDER-'.$side] = $s.' none #000000'; }
1051 }
1052 else if ($el=='COLOR') {
1053 if ($p) { $b['BORDER-'.$side] = preg_replace('/(\S+)\s+(\S+)\s+(\S+)/', '\\1 \\2 '.$s, $p); }
1054 else { $b['BORDER-'.$side] = '0px none '.$s; }
1055 }
1056 }
1057 }
1058 }
1059}
1060
1061
1062function MergeCSS($inherit,$tag,$attr) {
1063 $p = array();
1064 $zp = array();
1065
1066 $classes = array();
1067 if (isset($attr['CLASS'])) {
1068 $classes = preg_split('/\s+/',$attr['CLASS']);
1069 }
1070 if (!isset($attr['ID'])) { $attr['ID']=''; }
1071 //===============================================
1072/*-- TABLES --*/
1073 // Set Inherited properties
1074 if ($inherit == 'TOPTABLE') { // $tag = TABLE
1075 //===============================================
1076 // Save Cascading CSS e.g. "div.topic p" at this block level
1077
1078 if (isset($this->mpdf->blk[$this->mpdf->blklvl]['cascadeCSS'])) {
1079 $this->tablecascadeCSS[0] = $this->mpdf->blk[$this->mpdf->blklvl]['cascadeCSS'];
1080 }
1081 else {
1082 $this->tablecascadeCSS[0] = $this->cascadeCSS;
1083 }
1084 }
1085 //===============================================
1086 // Set Inherited properties
1087 if ($inherit == 'TOPTABLE' || $inherit == 'TABLE') {
1088 //Cascade everything from last level that is not an actual property, or defined by current tag/attributes
1089 if (isset($this->tablecascadeCSS[$this->tbCSSlvl-1]) && is_array($this->tablecascadeCSS[$this->tbCSSlvl-1])) {
1090 foreach($this->tablecascadeCSS[$this->tbCSSlvl-1] AS $k=>$v) {
1091 $this->tablecascadeCSS[$this->tbCSSlvl][$k] = $v;
1092 }
1093 }
1094 $this->_mergeFullCSS($this->cascadeCSS, $this->tablecascadeCSS[$this->tbCSSlvl], $tag, $classes, $attr['ID']);
1095 //===============================================
1096 // Cascading forward CSS e.g. "table.topic td" for this table in $this->tablecascadeCSS
1097 //===============================================
1098 // STYLESHEET TAG e.g. table
1099 $this->_mergeFullCSS($this->tablecascadeCSS[$this->tbCSSlvl-1], $this->tablecascadeCSS[$this->tbCSSlvl], $tag, $classes, $attr['ID']);
1100 //===============================================
1101 }
1102/*-- END TABLES --*/
1103 //===============================================
1104/*-- LISTS --*/
1105 // Set Inherited properties
1106 if ($inherit == 'TOPLIST') { // $tag = UL,OL
1107 //===============================================
1108 // Save Cascading CSS e.g. "div.topic p" at this block level
1109 if (isset($this->mpdf->blk[$this->mpdf->blklvl]['cascadeCSS'])) {
1110 $this->listcascadeCSS[0] = $this->mpdf->blk[$this->mpdf->blklvl]['cascadeCSS'];
1111 }
1112 else {
1113 $this->listcascadeCSS[0] = $this->cascadeCSS;
1114 }
1115 }
1116 //===============================================
1117 // Set Inherited properties
1118 if ($inherit == 'TOPLIST' || $inherit == 'LIST') {
1119 //Cascade everything from last level that is not an actual property, or defined by current tag/attributes
1120 if (isset($this->listcascadeCSS[$this->listCSSlvl-1]) && is_array($this->listcascadeCSS[$this->listCSSlvl-1])) {
1121 foreach($this->listcascadeCSS[$this->listCSSlvl-1] AS $k=>$v) {
1122 $this->listcascadeCSS[$this->listCSSlvl][$k] = $v;
1123 }
1124 }
1125 $this->_mergeFullCSS($this->cascadeCSS, $this->listcascadeCSS[$this->listCSSlvl], $tag, $classes, $attr['ID']);
1126 //===============================================
1127 // Cascading forward CSS e.g. "table.topic td" for this list in $this->listcascadeCSS
1128 //===============================================
1129 // STYLESHEET TAG e.g. table
1130 $this->_mergeFullCSS($this->listcascadeCSS[$this->listCSSlvl-1], $this->listcascadeCSS[$this->listCSSlvl], $tag, $classes, $attr['ID']);
1131 //===============================================
1132 }
1133/*-- END LISTS --*/
1134 //===============================================
1135 // Set Inherited properties
1136 if ($inherit == 'BLOCK') {
1137 if (isset($this->mpdf->blk[$this->mpdf->blklvl-1]['cascadeCSS']) && is_array($this->mpdf->blk[$this->mpdf->blklvl-1]['cascadeCSS'])) {
1138 foreach($this->mpdf->blk[$this->mpdf->blklvl-1]['cascadeCSS'] AS $k=>$v) {
1139 $this->mpdf->blk[$this->mpdf->blklvl]['cascadeCSS'][$k] = $v;
1140
1141 }
1142 }
1143
1144 //===============================================
1145 // Save Cascading CSS e.g. "div.topic p" at this block level
1146 $this->_mergeFullCSS($this->cascadeCSS, $this->mpdf->blk[$this->mpdf->blklvl]['cascadeCSS'], $tag, $classes, $attr['ID']);
1147 //===============================================
1148 // Cascading forward CSS
1149 //===============================================
1150 $this->_mergeFullCSS($this->mpdf->blk[$this->mpdf->blklvl-1]['cascadeCSS'], $this->mpdf->blk[$this->mpdf->blklvl]['cascadeCSS'], $tag, $classes, $attr['ID']);
1151 //===============================================
1152 // Block properties
1153 if (isset($this->mpdf->blk[$this->mpdf->blklvl-1]['margin_collapse']) && $this->mpdf->blk[$this->mpdf->blklvl-1]['margin_collapse']) { $p['MARGIN-COLLAPSE'] = 'COLLAPSE'; } // custom tag, but follows CSS principle that border-collapse is inherited
1154 if (isset($this->mpdf->blk[$this->mpdf->blklvl-1]['line_height']) && $this->mpdf->blk[$this->mpdf->blklvl-1]['line_height']) { $p['LINE-HEIGHT'] = $this->mpdf->blk[$this->mpdf->blklvl-1]['line_height']; }
1155
1156 if (isset($this->mpdf->blk[$this->mpdf->blklvl-1]['direction']) && $this->mpdf->blk[$this->mpdf->blklvl-1]['direction']) { $p['DIRECTION'] = $this->mpdf->blk[$this->mpdf->blklvl-1]['direction']; }
1157
1158 if (isset($this->mpdf->blk[$this->mpdf->blklvl-1]['align']) && $this->mpdf->blk[$this->mpdf->blklvl-1]['align']) {
1159 if ($this->mpdf->blk[$this->mpdf->blklvl-1]['align'] == 'L') { $p['TEXT-ALIGN'] = 'left'; }
1160 else if ($this->mpdf->blk[$this->mpdf->blklvl-1]['align'] == 'J') { $p['TEXT-ALIGN'] = 'justify'; }
1161 else if ($this->mpdf->blk[$this->mpdf->blklvl-1]['align'] == 'R') { $p['TEXT-ALIGN'] = 'right'; }
1162 else if ($this->mpdf->blk[$this->mpdf->blklvl-1]['align'] == 'C') { $p['TEXT-ALIGN'] = 'center'; }
1163 }
1164 if ($this->mpdf->ColActive || $this->mpdf->keep_block_together) {
1165 if (isset($this->mpdf->blk[$this->mpdf->blklvl-1]['bgcolor']) && $this->mpdf->blk[$this->mpdf->blklvl-1]['bgcolor']) { // Doesn't officially inherit, but default value is transparent (?=inherited)
1166 $cor = $this->mpdf->blk[$this->mpdf->blklvl-1]['bgcolorarray' ];
1167 $p['BACKGROUND-COLOR'] = $this->mpdf->_colAtoString($cor);
1168 }
1169 }
1170
1171 if (isset($this->mpdf->blk[$this->mpdf->blklvl-1]['text_indent']) && ($this->mpdf->blk[$this->mpdf->blklvl-1]['text_indent'] || $this->mpdf->blk[$this->mpdf->blklvl-1]['text_indent']===0)) { $p['TEXT-INDENT'] = $this->mpdf->blk[$this->mpdf->blklvl-1]['text_indent']; }
1172 if (isset($this->mpdf->blk[$this->mpdf->blklvl-1]['InlineProperties'])) {
1173 $biilp = $this->mpdf->blk[$this->mpdf->blklvl-1]['InlineProperties'];
1174 }
1175 else { $biilp = null; }
1176 if (isset($biilp[ 'family' ]) && $biilp[ 'family' ]) { $p['FONT-FAMILY'] = $biilp[ 'family' ]; }
1177 if (isset($biilp[ 'I' ]) && $biilp[ 'I' ]) { $p['FONT-STYLE'] = 'italic'; }
1178 if (isset($biilp[ 'sizePt' ]) && $biilp[ 'sizePt' ]) { $p['FONT-SIZE'] = $biilp[ 'sizePt' ] . 'pt'; }
1179 if (isset($biilp[ 'B' ]) && $biilp[ 'B' ]) { $p['FONT-WEIGHT'] = 'bold'; }
1180 if (isset($biilp[ 'colorarray' ]) && $biilp[ 'colorarray' ]) {
1181 $cor = $biilp[ 'colorarray' ];
1182 $p['COLOR'] = $this->mpdf->_colAtoString($cor);
1183 }
1184 if (isset($biilp[ 'fontkerning' ])) {
1185 if ($biilp[ 'fontkerning' ]) { $p['FONT-KERNING'] = 'normal'; }
1186 else { $p['FONT-KERNING'] = 'none'; }
1187 }
1188 if (isset($biilp[ 'lSpacingCSS' ]) && $biilp[ 'lSpacingCSS' ]) { $p['LETTER-SPACING'] = $biilp[ 'lSpacingCSS' ]; }
1189 if (isset($biilp[ 'wSpacingCSS' ]) && $biilp[ 'wSpacingCSS' ]) { $p['WORD-SPACING'] = $biilp[ 'wSpacingCSS' ]; }
1190 if (isset($biilp[ 'toupper' ]) && $biilp[ 'toupper' ]) { $p['TEXT-TRANSFORM'] = 'uppercase'; }
1191 else if (isset($biilp[ 'tolower' ]) && $biilp[ 'tolower' ]) { $p['TEXT-TRANSFORM'] = 'lowercase'; }
1192 else if (isset($biilp[ 'capitalize' ]) && $biilp[ 'capitalize' ]) { $p['TEXT-TRANSFORM'] = 'capitalize'; }
1193 // CSS says text-decoration is not inherited, but IE7 does??
1194 if (isset($biilp[ 'underline' ]) && $biilp[ 'underline' ]) { $p['TEXT-DECORATION'] = 'underline'; }
1195 if (isset($biilp[ 'smCaps' ]) && $biilp[ 'smCaps' ]) { $p['FONT-VARIANT'] = 'small-caps'; }
1196
1197 }
1198 //===============================================
1199 //===============================================
1200/*-- LISTS --*/
1201 // Set Inherited properties
1202 if ($inherit == 'TOPLIST') {
1203 if ($this->listCSSlvl == 1) {
1204 $bilp = $this->mpdf->blk[$this->mpdf->blklvl]['InlineProperties'];
1205 if (isset($bilp[ 'family' ]) && $bilp[ 'family' ]) { $p['FONT-FAMILY'] = $bilp[ 'family' ]; }
1206 if (isset($bilp[ 'I' ]) && $bilp[ 'I' ]) { $p['FONT-STYLE'] = 'italic'; }
1207 if (isset($bilp[ 'sizePt' ]) && $bilp[ 'sizePt' ]) { $p['FONT-SIZE'] = $bilp[ 'sizePt' ] . 'pt'; }
1208 if (isset($bilp[ 'B' ]) && $bilp[ 'B' ]) { $p['FONT-WEIGHT'] = 'bold'; }
1209 if (isset($bilp[ 'colorarray' ]) && $bilp[ 'colorarray' ]) {
1210 $cor = $bilp[ 'colorarray' ];
1211 $p['COLOR'] = $this->mpdf->_colAtoString($cor);
1212 }
1213 if (isset($bilp[ 'toupper' ]) && $bilp[ 'toupper' ]) { $p['TEXT-TRANSFORM'] = 'uppercase'; }
1214 else if (isset($bilp[ 'tolower' ]) && $bilp[ 'tolower' ]) { $p['TEXT-TRANSFORM'] = 'lowercase'; }
1215 else if (isset($bilp[ 'capitalize' ]) && $bilp[ 'capitalize' ]) { $p['TEXT-TRANSFORM'] = 'capitalize'; }
1216 if (isset($bilp[ 'fontkerning' ])) {
1217 if ($bilp[ 'fontkerning' ]) { $p['FONT-KERNING'] = 'normal'; }
1218 else { $p['FONT-KERNING'] = 'none'; }
1219 }
1220 if (isset($bilp[ 'lSpacingCSS' ]) && $bilp[ 'lSpacingCSS' ]) { $p['LETTER-SPACING'] = $bilp[ 'lSpacingCSS' ]; }
1221 if (isset($bilp[ 'wSpacingCSS' ]) && $bilp[ 'wSpacingCSS' ]) { $p['WORD-SPACING'] = $bilp[ 'wSpacingCSS' ]; }
1222 // CSS says text-decoration is not inherited, but IE7 does??
1223 if (isset($bilp[ 'underline' ]) && $bilp[ 'underline' ]) { $p['TEXT-DECORATION'] = 'underline'; }
1224 if (isset($bilp[ 'smCaps' ]) && $bilp[ 'smCaps' ]) { $p['FONT-VARIANT'] = 'small-caps'; }
1225 if ($tag=='LI') {
1226 // Note to self - this should never work, as TOPLIST is not called when LI (see code removed in v5.3)
1227 $this->mpdf->Error("If you see this message, please report this as a bug to the mPDF Forum.");
1228 }
1229 }
1230 }
1231/*-- END LISTS --*/
1232 //===============================================
1233 //===============================================
1234 // DEFAULT for this TAG set in DefaultCSS
1235 if (isset($this->mpdf->defaultCSS[$tag])) {
1236 $zp = $this->fixCSS($this->mpdf->defaultCSS[$tag]);
1237 if (is_array($zp)) { // Default overwrites Inherited
1238 $p = array_merge($p,$zp); // !! Note other way round !!
1239 $this->_mergeBorders($p,$zp);
1240 }
1241 }
1242 //===============================================
1243/*-- TABLES --*/
1244 // cellPadding overwrites TD/TH default but not specific CSS set on cell
1245 if (($tag=='TD' || $tag=='TH') && isset($this->mpdf->table[$this->mpdf->tableLevel][$this->mpdf->tbctr[$this->mpdf->tableLevel]]['cell_padding']) && ($this->mpdf->table[$this->mpdf->tableLevel][$this->mpdf->tbctr[$this->mpdf->tableLevel]]['cell_padding'] || $this->mpdf->table[$this->mpdf->tableLevel][$this->mpdf->tbctr[$this->mpdf->tableLevel]]['cell_padding']===0)) {
1246 $p['PADDING-LEFT'] = $this->mpdf->table[$this->mpdf->tableLevel][$this->mpdf->tbctr[$this->mpdf->tableLevel]]['cell_padding'];
1247 $p['PADDING-RIGHT'] = $this->mpdf->table[$this->mpdf->tableLevel][$this->mpdf->tbctr[$this->mpdf->tableLevel]]['cell_padding'];
1248 $p['PADDING-TOP'] = $this->mpdf->table[$this->mpdf->tableLevel][$this->mpdf->tbctr[$this->mpdf->tableLevel]]['cell_padding'];
1249 $p['PADDING-BOTTOM'] = $this->mpdf->table[$this->mpdf->tableLevel][$this->mpdf->tbctr[$this->mpdf->tableLevel]]['cell_padding'];
1250 }
1251/*-- END TABLES --*/
1252 //===============================================
1253 // STYLESHEET TAG e.g. h1 p div table
1254 if (isset($this->CSS[$tag]) && $this->CSS[$tag]) {
1255 $zp = $this->CSS[$tag];
1256 if ($tag=='TD' || $tag=='TH') { $this->setBorderDominance($zp, 9); } // *TABLES* // *TABLES-ADVANCED-BORDERS*
1257 if (is_array($zp)) {
1258 $p = array_merge($p,$zp);
1259 $this->_mergeBorders($p,$zp);
1260 }
1261 }
1262 //===============================================
1263 // STYLESHEET CLASS e.g. .smallone{} .redletter{}
1264 foreach($classes AS $class) {
1265 $zp = array();
1266 if (isset($this->CSS['CLASS>>'.$class]) && $this->CSS['CLASS>>'.$class]) { $zp = $this->CSS['CLASS>>'.$class]; }
1267 if ($tag=='TD' || $tag=='TH') { $this->setBorderDominance($zp, 9); } // *TABLES* // *TABLES-ADVANCED-BORDERS*
1268 if (is_array($zp)) {
1269 $p = array_merge($p,$zp);
1270 $this->_mergeBorders($p,$zp);
1271 }
1272 }
1273 //===============================================
1274/*-- TABLES --*/
1275 // STYLESHEET nth-child SELECTOR e.g. tr:nth-child(odd) td:nth-child(2n+1)
1276 if ($tag=='TR' || $tag=='TD' || $tag=='TH') {
1277 foreach($this->CSS AS $k=>$val) {
1278 if (preg_match('/'.$tag.'>>SELECTORNTHCHILD>>(.*)/',$k, $m)) {
1279 $select = false;
1280 if ($tag=='TR') {
1281 $row = $this->mpdf->row;
1282 $thnr = (isset($this->mpdf->table[$this->mpdf->tableLevel][$this->mpdf->tbctr[$this->mpdf->tableLevel]]['is_thead']) ? count($this->mpdf->table[$this->mpdf->tableLevel][$this->mpdf->tbctr[$this->mpdf->tableLevel]]['is_thead']) : 0);
1283 $tfnr = (isset($this->mpdf->table[$this->mpdf->tableLevel][$this->mpdf->tbctr[$this->mpdf->tableLevel]]['is_tfoot']) ? count($this->mpdf->table[$this->mpdf->tableLevel][$this->mpdf->tbctr[$this->mpdf->tableLevel]]['is_tfoot']) : 0);
1284 if ($this->mpdf->tabletfoot) { $row -= $thnr; }
1285 else if (!$this->mpdf->tablethead) { $row -= ($thnr + $tfnr); }
1286 if ($m[1]=='ODD' && ($row % 2) == 0) { $select = true; }
1287 else if ($m[1]=='EVEN' && ($row % 2) == 1) { $select = true; }
1288 else if (preg_match('/(\d+)N\+(\d+)/',$m[1],$a)) {
1289 if ((($row + 1) % $a[1]) == $a[2]) { $select = true; }
1290 }
1291 }
1292 else if ($tag=='TD' || $tag=='TH') {
1293 if ($m[1]=='ODD' && ($this->mpdf->col % 2) == 0) { $select = true; }
1294 else if ($m[1]=='EVEN' && ($this->mpdf->col % 2) == 1) { $select = true; }
1295 else if (preg_match('/(\d+)N\+(\d+)/',$m[1],$a)) {
1296 if ((($this->mpdf->col+1) % $a[1]) == $a[2]) { $select = true; }
1297 }
1298 }
1299 if ($select) {
1300 $zp = $this->CSS[$tag.'>>SELECTORNTHCHILD>>'.$m[1]];
1301 if ($tag=='TD' || $tag=='TH') { $this->setBorderDominance($zp, 9); }
1302 if (is_array($zp)) {
1303 $p = array_merge($p,$zp);
1304 $this->_mergeBorders($p,$zp);
1305 }
1306 }
1307 }
1308 }
1309 }
1310/*-- END TABLES --*/
1311 //===============================================
1312 // STYLESHEET ID e.g. #smallone{} #redletter{}
1313 if (isset($attr['ID']) && isset($this->CSS['ID>>'.$attr['ID']]) && $this->CSS['ID>>'.$attr['ID']]) {
1314 $zp = $this->CSS['ID>>'.$attr['ID']];
1315 if ($tag=='TD' || $tag=='TH') { $this->setBorderDominance($zp, 9); } // *TABLES* // *TABLES-ADVANCED-BORDERS*
1316 if (is_array($zp)) {
1317 $p = array_merge($p,$zp);
1318 $this->_mergeBorders($p,$zp);
1319 }
1320 }
1321 //===============================================
1322 // STYLESHEET CLASS e.g. p.smallone{} div.redletter{}
1323 foreach($classes AS $class) {
1324 $zp = array();
1325 if (isset($this->CSS[$tag.'>>CLASS>>'.$class]) && $this->CSS[$tag.'>>CLASS>>'.$class]) { $zp = $this->CSS[$tag.'>>CLASS>>'.$class]; }
1326 if ($tag=='TD' || $tag=='TH') { $this->setBorderDominance($zp, 9); } // *TABLES* // *TABLES-ADVANCED-BORDERS*
1327 if (is_array($zp)) {
1328 $p = array_merge($p,$zp);
1329 $this->_mergeBorders($p,$zp);
1330 }
1331 }
1332 //===============================================
1333 // STYLESHEET CLASS e.g. p#smallone{} div#redletter{}
1334 if (isset($attr['ID']) && isset($this->CSS[$tag.'>>ID>>'.$attr['ID']]) && $this->CSS[$tag.'>>ID>>'.$attr['ID']]) {
1335 $zp = $this->CSS[$tag.'>>ID>>'.$attr['ID']];
1336 if ($tag=='TD' || $tag=='TH') { $this->setBorderDominance($zp, 9); } // *TABLES* // *TABLES-ADVANCED-BORDERS*
1337 if (is_array($zp)) {
1338 $p = array_merge($p,$zp);
1339 $this->_mergeBorders($p,$zp);
1340 }
1341 }
1342 //===============================================
1343 // Cascaded e.g. div.class p only works for block level
1344 if ($inherit == 'BLOCK') {
1345 $this->_set_mergedCSS($this->mpdf->blk[$this->mpdf->blklvl-1]['cascadeCSS'][$tag], $p);
1346 foreach($classes AS $class) {
1347 $this->_set_mergedCSS($this->mpdf->blk[$this->mpdf->blklvl-1]['cascadeCSS']['CLASS>>'.$class], $p);
1348 }
1349 $this->_set_mergedCSS($this->mpdf->blk[$this->mpdf->blklvl-1]['cascadeCSS']['ID>>'.$attr['ID']], $p);
1350 foreach($classes AS $class) {
1351 $this->_set_mergedCSS($this->mpdf->blk[$this->mpdf->blklvl-1]['cascadeCSS'][$tag.'>>CLASS>>'.$class], $p);
1352 }
1353 $this->_set_mergedCSS($this->mpdf->blk[$this->mpdf->blklvl-1]['cascadeCSS'][$tag.'>>ID>>'.$attr['ID']], $p);
1354 }
1355 else if ($inherit == 'INLINE') {
1356 $this->_set_mergedCSS($this->mpdf->blk[$this->mpdf->blklvl]['cascadeCSS'][$tag], $p);
1357 foreach($classes AS $class) {
1358 $this->_set_mergedCSS($this->mpdf->blk[$this->mpdf->blklvl]['cascadeCSS']['CLASS>>'.$class], $p);
1359 }
1360 $this->_set_mergedCSS($this->mpdf->blk[$this->mpdf->blklvl]['cascadeCSS']['ID>>'.$attr['ID']], $p);
1361 foreach($classes AS $class) {
1362 $this->_set_mergedCSS($this->mpdf->blk[$this->mpdf->blklvl]['cascadeCSS'][$tag.'>>CLASS>>'.$class], $p);
1363 }
1364 $this->_set_mergedCSS($this->mpdf->blk[$this->mpdf->blklvl]['cascadeCSS'][$tag.'>>ID>>'.$attr['ID']], $p);
1365 }
1366/*-- TABLES --*/
1367 else if ($inherit == 'TOPTABLE' || $inherit == 'TABLE') { // NB looks at $this->tablecascadeCSS-1 for cascading CSS
1368 // false, 9 = don't check for 'depth' and do set border dominance
1369 $this->_set_mergedCSS($this->tablecascadeCSS[$this->tbCSSlvl-1][$tag], $p, false, 9);
1370 foreach($classes AS $class) {
1371 $this->_set_mergedCSS($this->tablecascadeCSS[$this->tbCSSlvl-1]['CLASS>>'.$class], $p, false, 9);
1372 }
1373 // STYLESHEET nth-child SELECTOR e.g. tr:nth-child(odd) td:nth-child(2n+1)
1374 if ($tag=='TR' || $tag=='TD' || $tag=='TH') {
1375 foreach($this->tablecascadeCSS[$this->tbCSSlvl-1] AS $k=>$val) {
1376 if (preg_match('/'.$tag.'>>SELECTORNTHCHILD>>(.*)/',$k, $m)) {
1377 $select = false;
1378 if ($tag=='TR') {
1379 $row = $this->mpdf->row;
1380 $thnr = (isset($this->mpdf->table[$this->mpdf->tableLevel][$this->mpdf->tbctr[$this->mpdf->tableLevel]]['is_thead']) ? count($this->mpdf->table[$this->mpdf->tableLevel][$this->mpdf->tbctr[$this->mpdf->tableLevel]]['is_thead']) : 0);
1381 $tfnr = (isset($this->mpdf->table[$this->mpdf->tableLevel][$this->mpdf->tbctr[$this->mpdf->tableLevel]]['is_tfoot']) ? count($this->mpdf->table[$this->mpdf->tableLevel][$this->mpdf->tbctr[$this->mpdf->tableLevel]]['is_tfoot']) : 0);
1382 if ($this->mpdf->tabletfoot) { $row -= $thnr; }
1383 else if (!$this->mpdf->tablethead) { $row -= ($thnr + $tfnr); }
1384 if ($m[1]=='ODD' && ($row % 2) == 0) { $select = true; }
1385 else if ($m[1]=='EVEN' && ($row % 2) == 1) { $select = true; }
1386 else if (preg_match('/(\d+)N\+(\d+)/',$m[1],$a)) {
1387 if ((($row + 1) % $a[1]) == $a[2]) { $select = true; }
1388 }
1389 }
1390 else if ($tag=='TD' || $tag=='TH') {
1391 if ($m[1]=='ODD' && ($this->mpdf->col % 2) == 0) { $select = true; }
1392 else if ($m[1]=='EVEN' && ($this->mpdf->col % 2) == 1) { $select = true; }
1393 else if (preg_match('/(\d+)N\+(\d+)/',$m[1],$a)) {
1394 if ((($this->mpdf->col + 1) % $a[1]) == $a[2]) { $select = true; }
1395 }
1396 }
1397 if ($select) {
1398 $this->_set_mergedCSS($this->tablecascadeCSS[$this->tbCSSlvl-1][$tag.'>>SELECTORNTHCHILD>>'.$m[1]], $p, false, 9);
1399 }
1400 }
1401 }
1402 }
1403 $this->_set_mergedCSS($this->tablecascadeCSS[$this->tbCSSlvl-1]['ID>>'.$attr['ID']], $p, false, 9);
1404 foreach($classes AS $class) {
1405 $this->_set_mergedCSS($this->tablecascadeCSS[$this->tbCSSlvl-1][$tag.'>>CLASS>>'.$class], $p, false, 9);
1406 }
1407 $this->_set_mergedCSS($this->tablecascadeCSS[$this->tbCSSlvl-1][$tag.'>>ID>>'.$attr['ID']], $p, false, 9);
1408 }
1409/*-- END TABLES --*/
1410 //===============================================
1411/*-- LISTS --*/
1412 else if ($inherit == 'TOPLIST' || $inherit == 'LIST') { // NB looks at $this->listcascadeCSS-1 for cascading CSS
1413 // false = don't check for 'depth'
1414 $this->_set_mergedCSS($this->listcascadeCSS[$this->listCSSlvl-1][$tag], $p, false);
1415 foreach($classes AS $class) {
1416 $this->_set_mergedCSS($this->listcascadeCSS[$this->listCSSlvl-1]['CLASS>>'.$class], $p, false);
1417 }
1418 $this->_set_mergedCSS($this->listcascadeCSS[$this->listCSSlvl-1]['ID>>'.$attr['ID']], $p, false);
1419 foreach($classes AS $class) {
1420 $this->_set_mergedCSS($this->listcascadeCSS[$this->listCSSlvl-1][$tag.'>>CLASS>>'.$class], $p, false);
1421 }
1422 $this->_set_mergedCSS($this->listcascadeCSS[$this->listCSSlvl-1][$tag.'>>ID>>'.$attr['ID']], $p, false);
1423 }
1424/*-- END LISTS --*/
1425 //===============================================
1426 //===============================================
1427 // INLINE STYLE e.g. style="CSS:property"
1428 if (isset($attr['STYLE'])) {
1429 $zp = $this->readInlineCSS($attr['STYLE']);
1430 if ($tag=='TD' || $tag=='TH') { $this->setBorderDominance($zp, 9); } // *TABLES* // *TABLES-ADVANCED-BORDERS*
1431 if (is_array($zp)) {
1432 $p = array_merge($p,$zp);
1433 $this->_mergeBorders($p,$zp);
1434 }
1435 }
1436 //===============================================
1437 //===============================================
1438 // INLINE ATTRIBUTES e.g. .. ALIGN="CENTER">
1439 if (isset($attr['LANG']) and $attr['LANG']!='') {
1440 $p['LANG'] = $attr['LANG'];
1441 }
1442 if (isset($attr['COLOR']) and $attr['COLOR']!='') {
1443 $p['COLOR'] = $attr['COLOR'];
1444 }
1445 if ($tag != 'INPUT') {
1446 if (isset($attr['WIDTH']) and $attr['WIDTH']!='') {
1447 $p['WIDTH'] = $attr['WIDTH'];
1448 }
1449 if (isset($attr['HEIGHT']) and $attr['HEIGHT']!='') {
1450 $p['HEIGHT'] = $attr['HEIGHT'];
1451 }
1452 }
1453 if ($tag == 'FONT') {
1454 if (isset($attr['FACE'])) {
1455 $p['FONT-FAMILY'] = $attr['FACE'];
1456 }
1457 if (isset($attr['SIZE']) and $attr['SIZE']!='') {
1458 $s = '';
1459 if ($attr['SIZE'] === '+1') { $s = '120%'; }
1460 else if ($attr['SIZE'] === '-1') { $s = '86%'; }
1461 else if ($attr['SIZE'] === '1') { $s = 'XX-SMALL'; }
1462 else if ($attr['SIZE'] == '2') { $s = 'X-SMALL'; }
1463 else if ($attr['SIZE'] == '3') { $s = 'SMALL'; }
1464 else if ($attr['SIZE'] == '4') { $s = 'MEDIUM'; }
1465 else if ($attr['SIZE'] == '5') { $s = 'LARGE'; }
1466 else if ($attr['SIZE'] == '6') { $s = 'X-LARGE'; }
1467 else if ($attr['SIZE'] == '7') { $s = 'XX-LARGE'; }
1468 if ($s) $p['FONT-SIZE'] = $s;
1469 }
1470 }
1471 if (isset($attr['VALIGN']) and $attr['VALIGN']!='') {
1472 $p['VERTICAL-ALIGN'] = $attr['VALIGN'];
1473 }
1474 if (isset($attr['VSPACE']) and $attr['VSPACE']!='') {
1475 $p['MARGIN-TOP'] = $attr['VSPACE'];
1476 $p['MARGIN-BOTTOM'] = $attr['VSPACE'];
1477 }
1478 if (isset($attr['HSPACE']) and $attr['HSPACE']!='') {
1479 $p['MARGIN-LEFT'] = $attr['HSPACE'];
1480 $p['MARGIN-RIGHT'] = $attr['HSPACE'];
1481 }
1482 //===============================================
1483 return $p;
1484}
1485
1486function PreviewBlockCSS($tag,$attr) {
1487 // Looks ahead from current block level to a new level
1488 $p = array();
1489 $zp = array();
1490 $oldcascadeCSS = $this->mpdf->blk[$this->mpdf->blklvl]['cascadeCSS'];
1491 $classes = array();
1492 if (isset($attr['CLASS'])) { $classes = preg_split('/\s+/',$attr['CLASS']); }
1493 //===============================================
1494 // DEFAULT for this TAG set in DefaultCSS
1495 if (isset($this->mpdf->defaultCSS[$tag])) {
1496 $zp = $this->fixCSS($this->mpdf->defaultCSS[$tag]);
1497 if (is_array($zp)) { $p = array_merge($zp,$p); } // Inherited overwrites default
1498 }
1499 // STYLESHEET TAG e.g. h1 p div table
1500 if (isset($this->CSS[$tag])) {
1501 $zp = $this->CSS[$tag];
1502 if (is_array($zp)) { $p = array_merge($p,$zp); }
1503 }
1504 // STYLESHEET CLASS e.g. .smallone{} .redletter{}
1505 foreach($classes AS $class) {
1506 $zp = array();
1507 if (isset($this->CSS['CLASS>>'.$class])) { $zp = $this->CSS['CLASS>>'.$class]; }
1508 if (is_array($zp)) { $p = array_merge($p,$zp); }
1509 }
1510 // STYLESHEET ID e.g. #smallone{} #redletter{}
1511 if (isset($attr['ID']) && isset($this->CSS['ID>>'.$attr['ID']])) {
1512 $zp = $this->CSS['ID>>'.$attr['ID']];
1513 if (is_array($zp)) { $p = array_merge($p,$zp); }
1514 }
1515 // STYLESHEET CLASS e.g. p.smallone{} div.redletter{}
1516 foreach($classes AS $class) {
1517 $zp = array();
1518 if (isset($this->CSS[$tag.'>>CLASS>>'.$class])) { $zp = $this->CSS[$tag.'>>CLASS>>'.$class]; }
1519 if (is_array($zp)) { $p = array_merge($p,$zp); }
1520 }
1521 // STYLESHEET CLASS e.g. p#smallone{} div#redletter{}
1522 if (isset($attr['ID']) && isset($this->CSS[$tag.'>>ID>>'.$attr['ID']])) {
1523 $zp = $this->CSS[$tag.'>>ID>>'.$attr['ID']];
1524 if (is_array($zp)) { $p = array_merge($p,$zp); }
1525 }
1526 //===============================================
1527 // STYLESHEET TAG e.g. div h1 div p
1528
1529 $this->_set_mergedCSS($oldcascadeCSS[$tag], $p);
1530 // STYLESHEET CLASS e.g. .smallone{} .redletter{}
1531 foreach($classes AS $class) {
1532
1533 $this->_set_mergedCSS($oldcascadeCSS['CLASS>>'.$class], $p);
1534 }
1535 // STYLESHEET CLASS e.g. #smallone{} #redletter{}
1536 if (isset($attr['ID'])) {
1537
1538 $this->_set_mergedCSS($oldcascadeCSS['ID>>'.$attr['ID']], $p);
1539 }
1540 // STYLESHEET CLASS e.g. div.smallone{} p.redletter{}
1541 foreach($classes AS $class) {
1542
1543 $this->_set_mergedCSS($oldcascadeCSS[$tag.'>>CLASS>>'.$class], $p);
1544 }
1545 // STYLESHEET CLASS e.g. div#smallone{} p#redletter{}
1546 if (isset($attr['ID'])) {
1547
1548 $this->_set_mergedCSS($oldcascadeCSS[$tag.'>>ID>>'.$attr['ID']], $p);
1549 }
1550 //===============================================
1551 // INLINE STYLE e.g. style="CSS:property"
1552 if (isset($attr['STYLE'])) {
1553 $zp = $this->readInlineCSS($attr['STYLE']);
1554 if (is_array($zp)) { $p = array_merge($p,$zp); }
1555 }
1556 //===============================================
1557 return $p;
1558}
1559
1560
1561
1562
1563
1564} // end of class
1565
1566?> \ No newline at end of file
diff --git a/inc/3rdparty/libraries/mpdf/classes/directw.php b/inc/3rdparty/libraries/mpdf/classes/directw.php
new file mode 100644
index 00000000..dc317d26
--- /dev/null
+++ b/inc/3rdparty/libraries/mpdf/classes/directw.php
@@ -0,0 +1,408 @@
1<?php
2
3class directw {
4
5var $mpdf = null;
6
7function directw(&$mpdf) {
8 $this->mpdf = $mpdf;
9}
10
11
12function Write($h,$txt,$currentx=0,$link='',$directionality='ltr',$align='') {
13 if (!$align) {
14 if ($directionality=='rtl') { $align = 'R'; }
15 else { $align = 'L'; }
16 }
17 if ($h == 0) { $this->mpdf->SetLineHeight(); $h = $this->mpdf->lineheight; }
18 //Output text in flowing mode
19 $w = $this->mpdf->w - $this->mpdf->rMargin - $this->mpdf->x;
20
21 $wmax = ($w - ($this->mpdf->cMarginL+$this->mpdf->cMarginR));
22 $s=str_replace("\r",'',$txt);
23 if ($this->mpdf->usingCoreFont) { $nb=strlen($s); }
24 else {
25 $nb=mb_strlen($s, $this->mpdf->mb_enc );
26 // handle single space character
27 if(($nb==1) && $s == " ") {
28 $this->mpdf->x += $this->mpdf->GetStringWidth($s);
29 return;
30 }
31 }
32 $sep=-1;
33 $i=0;
34 $j=0;
35 $l=0;
36 $nl=1;
37 if (!$this->mpdf->usingCoreFont) {
38 if (preg_match("/([".$this->mpdf->pregRTLchars."])/u", $txt)) { $this->mpdf->biDirectional = true; } // *RTL*
39 $checkCursive=false;
40 if ($this->mpdf->biDirectional) { $checkCursive=true; } // *RTL*
41 else if (isset($this->mpdf->CurrentFont['indic']) && $this->mpdf->CurrentFont['indic']) { $checkCursive=true; } // *INDIC*
42 while($i<$nb) {
43 //Get next character
44 $c = mb_substr($s,$i,1,$this->mpdf->mb_enc );
45 if($c == "\n") {
46 // WORD SPACING
47 $this->mpdf->ResetSpacing();
48 //Explicit line break
49 $tmp = rtrim(mb_substr($s,$j,$i-$j,$this->mpdf->mb_enc));
50 if ($directionality == 'rtl' && $align == 'J') { $align = 'R'; } // *RTL*
51 $this->mpdf->magic_reverse_dir($tmp, true, $directionality); // *RTL*
52 $this->mpdf->Cell($w, $h, $tmp, 0, 2, $align, $fill, $link);
53 $i++;
54 $sep = -1;
55 $j = $i;
56 $l = 0;
57 if($nl == 1) {
58 if ($currentx != 0) $this->mpdf->x=$currentx;
59 else $this->mpdf->x=$this->mpdf->lMargin;
60 $w = $this->mpdf->w - $this->mpdf->rMargin - $this->mpdf->x;
61 $wmax = ($w - ($this->mpdf->cMarginL+$this->mpdf->cMarginR));
62 }
63 $nl++;
64 continue;
65 }
66 if($c == " ") { $sep= $i; }
67 $l += $this->mpdf->GetCharWidthNonCore($c); // mPDF 5.3.04
68 if($l > $wmax) {
69 //Automatic line break (word wrapping)
70 if($sep == -1) {
71 // WORD SPACING
72 $this->mpdf->ResetSpacing();
73 if($this->mpdf->x > $this->mpdf->lMargin) {
74 //Move to next line
75 if ($currentx != 0) $this->mpdf->x=$currentx;
76 else $this->mpdf->x=$this->mpdf->lMargin;
77 $this->mpdf->y+=$h;
78 $w=$this->mpdf->w-$this->mpdf->rMargin-$this->mpdf->x;
79 $wmax = ($w - ($this->mpdf->cMarginL+$this->mpdf->cMarginR));
80 $i++;
81 $nl++;
82 continue;
83 }
84 if($i==$j) { $i++; }
85 $tmp = rtrim(mb_substr($s,$j,$i-$j,$this->mpdf->mb_enc));
86 if ($directionality == 'rtl' && $align == 'J') { $align = 'R'; } // *RTL*
87 $this->mpdf->magic_reverse_dir($tmp, true, $directionality); // *RTL*
88 $this->mpdf->Cell($w, $h, $tmp, 0, 2, $align, $fill, $link);
89 }
90 else {
91 $tmp = rtrim(mb_substr($s,$j,$sep-$j,$this->mpdf->mb_enc));
92 if ($directionality == 'rtl' && $align == 'J') { $align = 'R'; } // *RTL*
93 $this->mpdf->magic_reverse_dir($tmp, true, $directionality); // *RTL*
94
95 if($align=='J') {
96 //////////////////////////////////////////
97 // JUSTIFY J using Unicode fonts (Word spacing doesn't work)
98 // WORD SPACING
99 // Change NON_BREAKING SPACE to spaces so they are 'spaced' properly
100 $tmp = str_replace(chr(194).chr(160),chr(32),$tmp );
101 $len_ligne = $this->mpdf->GetStringWidth($tmp );
102 $nb_carac = mb_strlen( $tmp , $this->mpdf->mb_enc ) ;
103 $nb_spaces = mb_substr_count( $tmp ,' ', $this->mpdf->mb_enc ) ;
104 $inclCursive=false;
105 if ($checkCursive) {
106 if (preg_match("/([".$this->mpdf->pregRTLchars."])/u", $tmp)) { $inclCursive = true; } // *RTL*
107 if (preg_match("/([".$this->mpdf->pregHIchars.$this->mpdf->pregBNchars.$this->mpdf->pregPAchars."])/u", $tmp)) { $inclCursive = true; } // *INDIC*
108 }
109 list($charspacing,$ws) = $this->mpdf->GetJspacing($nb_carac,$nb_spaces,((($w-2) - $len_ligne) * _MPDFK),$inclCursive);
110 $this->mpdf->SetSpacing($charspacing,$ws);
111 //////////////////////////////////////////
112 }
113 $this->mpdf->Cell($w, $h, $tmp, 0, 2, $align, $fill, $link);
114 $i=$sep+1;
115 }
116 $sep = -1;
117 $j = $i;
118 $l = 0;
119 if($nl==1) {
120 if ($currentx != 0) $this->mpdf->x=$currentx;
121 else $this->mpdf->x=$this->mpdf->lMargin;
122 $w=$this->mpdf->w-$this->mpdf->rMargin-$this->mpdf->x;
123 $wmax = ($w - ($this->mpdf->cMarginL+$this->mpdf->cMarginR));
124 }
125 $nl++;
126 }
127 else { $i++; }
128 }
129 //Last chunk
130 // WORD SPACING
131 $this->mpdf->ResetSpacing();
132 }
133 else {
134 while($i<$nb) {
135 //Get next character
136 $c=$s[$i];
137 if($c == "\n") {
138 //Explicit line break
139 // WORD SPACING
140 $this->mpdf->ResetSpacing();
141 $this->mpdf->Cell($w, $h, substr($s, $j, $i-$j), 0, 2, $align, $fill, $link);
142 $i++;
143 $sep = -1;
144 $j = $i;
145 $l = 0;
146 if($nl == 1) {
147 if ($currentx != 0) $this->mpdf->x=$currentx;
148 else $this->mpdf->x=$this->mpdf->lMargin;
149 $w = $this->mpdf->w - $this->mpdf->rMargin - $this->mpdf->x;
150 $wmax=$w-($this->mpdf->cMarginL+$this->mpdf->cMarginR);
151 }
152 $nl++;
153 continue;
154 }
155 if($c == " ") { $sep= $i; }
156 $l += $this->mpdf->GetCharWidthCore($c); // mPDF 5.3.04
157 if($l > $wmax) {
158 //Automatic line break (word wrapping)
159 if($sep == -1) {
160 // WORD SPACING
161 $this->mpdf->ResetSpacing();
162 if($this->mpdf->x > $this->mpdf->lMargin) {
163 //Move to next line
164 if ($currentx != 0) $this->mpdf->x=$currentx;
165 else $this->mpdf->x=$this->mpdf->lMargin;
166 $this->mpdf->y+=$h;
167 $w=$this->mpdf->w-$this->mpdf->rMargin-$this->mpdf->x;
168 $wmax=$w-($this->mpdf->cMarginL+$this->mpdf->cMarginR);
169 $i++;
170 $nl++;
171 continue;
172 }
173 if($i==$j) { $i++; }
174 $this->mpdf->Cell($w, $h, substr($s, $j, $i-$j), 0, 2, $align, $fill, $link);
175 }
176 else {
177 $tmp = substr($s, $j, $sep-$j);
178 if($align=='J') {
179 //////////////////////////////////////////
180 // JUSTIFY J using Unicode fonts (Word spacing doesn't work)
181 // WORD SPACING
182 // Change NON_BREAKING SPACE to spaces so they are 'spaced' properly
183 $tmp = str_replace(chr(160),chr(32),$tmp );
184 $len_ligne = $this->mpdf->GetStringWidth($tmp );
185 $nb_carac = strlen( $tmp ) ;
186 $nb_spaces = substr_count( $tmp ,' ' ) ;
187 list($charspacing,$ws) = $this->mpdf->GetJspacing($nb_carac,$nb_spaces,((($w-2) - $len_ligne) * _MPDFK),$false);
188 $this->mpdf->SetSpacing($charspacing,$ws);
189 //////////////////////////////////////////
190 }
191 $this->mpdf->Cell($w, $h, $tmp, 0, 2, $align, $fill, $link);
192 $i=$sep+1;
193 }
194 $sep = -1;
195 $j = $i;
196 $l = 0;
197 if($nl==1) {
198 if ($currentx != 0) $this->mpdf->x=$currentx;
199 else $this->mpdf->x=$this->mpdf->lMargin;
200 $w=$this->mpdf->w-$this->mpdf->rMargin-$this->mpdf->x;
201 $wmax=$w-($this->mpdf->cMarginL+$this->mpdf->cMarginR);
202 }
203 $nl++;
204 }
205 else {
206 $i++;
207 }
208 }
209 // WORD SPACING
210 $this->mpdf->ResetSpacing();
211 }
212 //Last chunk
213 if($i!=$j) {
214 if ($currentx != 0) $this->mpdf->x=$currentx;
215 else $this->mpdf->x=$this->mpdf->lMargin;
216 if ($this->mpdf->usingCoreFont) { $tmp = substr($s,$j,$i-$j); }
217 else {
218 $tmp = mb_substr($s,$j,$i-$j,$this->mpdf->mb_enc);
219 if ($directionality == 'rtl' && $align == 'J') { $align = 'R'; } // *RTL*
220 $this->mpdf->magic_reverse_dir($tmp, true, $directionality); // *RTL*
221 }
222 $this->mpdf->Cell($w,$h,$tmp,0,0,$align,$fill,$link);
223 }
224}
225
226
227function CircularText($x, $y, $r, $text, $align='top', $fontfamily='', $fontsizePt=0, $fontstyle='', $kerning=120, $fontwidth=100, $divider='') { // mPDF 5.5.23
228 if ($font || $fontstyle || $fontsizePt) $this->mpdf->SetFont($fontfamily,$fontstyle,$fontsizePt);
229 $kerning/=100;
230 $fontwidth/=100;
231 if($kerning==0) $this->mpdf->Error('Please use values unequal to zero for kerning (CircularText)');
232 if($fontwidth==0) $this->mpdf->Error('Please use values unequal to zero for font width (CircularText)');
233 $text=str_replace("\r",'',$text);
234 //circumference
235 $u=($r*2)*M_PI;
236 // mPDF 5.5.23
237 $checking = true;
238 $autoset = false;
239 while($checking) {
240 $t=0;
241 $w = array();
242 if ($this->mpdf->usingCoreFont) {
243 $nb=strlen($text);
244 for($i=0; $i<$nb; $i++){
245 $w[$i]=$this->mpdf->GetStringWidth($text[$i]);
246 $w[$i]*=$kerning*$fontwidth;
247 $t+=$w[$i];
248 }
249 }
250 else {
251 $nb=mb_strlen($text, $this->mpdf->mb_enc );
252 $lastchar = '';
253 $unicode = $this->mpdf->UTF8StringToArray($text);
254 for($i=0; $i<$nb; $i++){
255 $c = mb_substr($text,$i,1,$this->mpdf->mb_enc );
256 $w[$i]=$this->mpdf->GetStringWidth($c);
257 $w[$i]*=$kerning*$fontwidth;
258 $char = $unicode[$i];
259 if ($this->mpdf->useKerning && $lastchar) {
260 if (isset($this->mpdf->CurrentFont['kerninfo'][$lastchar][$char])) {
261 $tk = $this->mpdf->CurrentFont['kerninfo'][$lastchar][$char] * ($this->mpdf->FontSize/ 1000) * $kerning * $fontwidth;
262 $w[$i] += $tk/2;
263 $w[$i-1] += $tk/2;
264 $t+=$tk;
265 }
266 }
267 $lastchar = $char;
268 $t+=$w[$i];
269 }
270 }
271 if ($fontsizePt>=0 || $autoset) { $checking = false; }
272 else {
273 $t+=$this->mpdf->GetStringWidth(' ');
274 if ($divider)
275 $t+=$this->mpdf->GetStringWidth(' ');
276 if ($fontsizePt==-2)
277 $fontsizePt = $this->mpdf->FontSizePt * 0.5 * $u/$t;
278 else
279 $fontsizePt = $this->mpdf->FontSizePt * $u/$t;
280 $this->mpdf->SetFontSize($fontsizePt);
281 $autoset = true;
282 }
283 }
284
285 //total width of string in degrees
286 $d=($t/$u)*360;
287
288 $this->mpdf->StartTransform();
289 // rotate matrix for the first letter to center the text
290 // (half of total degrees)
291 if($align=='top'){
292 $this->mpdf->transformRotate(-$d/2, $x, $y);
293 }
294 else{
295 $this->mpdf->transformRotate($d/2, $x, $y);
296 }
297 //run through the string
298 for($i=0; $i<$nb; $i++){
299 if($align=='top'){
300 //rotate matrix half of the width of current letter + half of the width of preceding letter
301 if($i==0){
302 $this->mpdf->transformRotate((($w[$i]/2)/$u)*360, $x, $y);
303 }
304 else{
305 $this->mpdf->transformRotate((($w[$i]/2+$w[$i-1]/2)/$u)*360, $x, $y);
306 }
307 if($fontwidth!=1){
308 $this->mpdf->StartTransform();
309 $this->mpdf->transformScale($fontwidth*100, 100, $x, $y);
310 }
311 $this->mpdf->SetXY($x-$w[$i]/2, $y-$r);
312 }
313 else{
314 //rotate matrix half of the width of current letter + half of the width of preceding letter
315 if($i==0){
316 $this->mpdf->transformRotate(-(($w[$i]/2)/$u)*360, $x, $y);
317 }
318 else{
319 $this->mpdf->transformRotate(-(($w[$i]/2+$w[$i-1]/2)/$u)*360, $x, $y);
320 }
321 if($fontwidth!=1){
322 $this->mpdf->StartTransform();
323 $this->mpdf->transformScale($fontwidth*100, 100, $x, $y);
324 }
325 $this->mpdf->SetXY($x-$w[$i]/2, $y+$r-($this->mpdf->FontSize));
326 }
327 if ($this->mpdf->usingCoreFont) { $c=$text[$i]; }
328 else { $c = mb_substr($text,$i,1,$this->mpdf->mb_enc ); }
329 $this->mpdf->Cell(($w[$i]),$this->mpdf->FontSize,$c,0,0,'C'); // mPDF 5.3.53
330 if($fontwidth!=1){
331 $this->mpdf->StopTransform();
332 }
333 }
334 $this->mpdf->StopTransform();
335
336 // mPDF 5.5.23
337 if($align=='top' && $divider!=''){
338 $wc=$this->mpdf->GetStringWidth($divider);
339 $wc*=$kerning*$fontwidth;
340
341 $this->mpdf->StartTransform();
342 $this->mpdf->transformRotate(90, $x, $y);
343 $this->mpdf->SetXY($x-$wc/2, $y-$r);
344 $this->mpdf->Cell(($wc),$this->mpdf->FontSize,$divider,0,0,'C');
345 $this->mpdf->StopTransform();
346
347 $this->mpdf->StartTransform();
348 $this->mpdf->transformRotate(-90, $x, $y);
349 $this->mpdf->SetXY($x-$wc/2, $y-$r);
350 $this->mpdf->Cell(($wc),$this->mpdf->FontSize,$divider,0,0,'C');
351 $this->mpdf->StopTransform();
352 }
353}
354
355function Shaded_box( $text,$font='',$fontstyle='B',$szfont='',$width='70%',$style='DF',$radius=2.5,$fill='#FFFFFF',$color='#000000',$pad=2 )
356{
357// F (shading - no line),S (line, no shading),DF (both)
358 if (!$font) { $font= $this->mpdf->default_font; }
359 if (!$szfont) { $szfont = ($this->mpdf->default_font_size * 1.8); }
360
361 $text = $this->mpdf->purify_utf8_text($text);
362 if ($this->mpdf->text_input_as_HTML) {
363 $text = $this->mpdf->all_entities_to_utf8($text);
364 }
365 if ($this->mpdf->usingCoreFont) { $text = mb_convert_encoding($text,$this->mpdf->mb_enc,'UTF-8'); }
366 // DIRECTIONALITY
367 $this->mpdf->magic_reverse_dir($text, true, $this->mpdf->directionality); // *RTL*
368 // Font-specific ligature substitution for Indic fonts
369 if (isset($this->mpdf->CurrentFont['indic']) && $this->mpdf->CurrentFont['indic']) $this->mpdf->ConvertIndic($text); // *INDIC*
370 $text = ' '.$text.' ';
371 if (!$width) { $width = $this->mpdf->pgwidth; } else { $width=$this->mpdf->ConvertSize($width,$this->mpdf->pgwidth); }
372 $midpt = $this->mpdf->lMargin+($this->mpdf->pgwidth/2);
373 $r1 = $midpt-($width/2); //($this->mpdf->w / 2) - 40;
374 $r2 = $r1 + $width; //$r1 + 80;
375 $y1 = $this->mpdf->y;
376
377
378 $mid = ($r1 + $r2 ) / 2;
379 $loop = 0;
380
381 while ( $loop == 0 )
382 {
383 $this->mpdf->SetFont( $font, $fontstyle, $szfont );
384 $sz = $this->mpdf->GetStringWidth( $text );
385 if ( ($r1+$sz) > $r2 )
386 $szfont --;
387 else
388 $loop ++;
389 }
390
391 $y2 = $this->mpdf->FontSize+($pad*2);
392
393 $this->mpdf->SetLineWidth(0.1);
394 $fc = $this->mpdf->ConvertColor($fill);
395 $tc = $this->mpdf->ConvertColor($color);
396 $this->mpdf->SetFColor($fc);
397 $this->mpdf->SetTColor($tc);
398 $this->mpdf->RoundedRect($r1, $y1, ($r2 - $r1), $y2, $radius, $style);
399 $this->mpdf->SetX( $r1);
400 $this->mpdf->Cell($r2-$r1, $y2, $text, 0, 1, "C" );
401 $this->mpdf->SetY($y1+$y2+2); // +2 = mm margin below shaded box
402 $this->mpdf->Reset();
403}
404
405
406}
407
408?> \ No newline at end of file
diff --git a/inc/3rdparty/libraries/mpdf/classes/form.php b/inc/3rdparty/libraries/mpdf/classes/form.php
new file mode 100644
index 00000000..9367e717
--- /dev/null
+++ b/inc/3rdparty/libraries/mpdf/classes/form.php
@@ -0,0 +1,1498 @@
1<?php
2
3class form {
4
5var $mpdf = null;
6
7var $forms;
8var $formn;
9
10//Active Forms
11var $formSubmitNoValueFields;
12var $formExportType;
13var $formSelectDefaultOption;
14var $formUseZapD;
15/* Form Styles */
16var $form_border_color;
17var $form_background_color;
18var $form_border_width;
19var $form_border_style;
20var $form_button_border_color;
21var $form_button_background_color;
22var $form_button_border_width;
23var $form_button_border_style;
24var $form_radio_color;
25var $form_radio_background_color;
26
27var $form_element_spacing;
28
29// Active forms
30var $formMethod;
31var $formAction;
32var $form_fonts;
33var $form_radio_groups;
34var $form_checkboxes;
35var $pdf_acro_array;
36
37var $pdf_array_co;
38var $array_form_button_js;
39var $array_form_choice_js;
40var $array_form_text_js;
41
42/* Button Text */
43var $form_button_text;
44var $form_button_text_over;
45var $form_button_text_click;
46var $form_button_icon;
47
48
49// FORMS
50var $textarea_lineheight;
51
52function form(&$mpdf) {
53 $this->mpdf = $mpdf;
54
55 // ACTIVE FORMS
56 $this->formExportType = 'xfdf'; // 'xfdf' or 'html'
57 $this->formSubmitNoValueFields = true; // Whether to include blank fields when submitting data
58 $this->formSelectDefaultOption = true; // for Select drop down box; if no option is explicitly maked as selected,
59 // this determines whether to select 1st option (as per browser)
60 // - affects whether "required" attribute is relevant
61 $this->formUseZapD = true; // Determine whether to use ZapfDingbat icons for radio/checkboxes
62
63 // FORM STYLES
64 // These can alternatively use a 4 number string to represent CMYK colours
65 $this->form_border_color = '0.6 0.6 0.72'; // RGB
66 $this->form_background_color = '0.975 0.975 0.975'; // RGB
67 $this->form_border_width = '1'; // 0 doesn't seem to work as it should
68 $this->form_border_style = 'S'; // B - Bevelled; D - Double
69 $this->form_button_border_color = '0.2 0.2 0.55';
70 $this->form_button_background_color = '0.941 0.941 0.941';
71 $this->form_button_border_width = '1';
72 $this->form_button_border_style = 'S';
73 $this->form_radio_color = '0.0 0.0 0.4'; // radio and checkbox
74 $this->form_radio_background_color = '0.9 0.9 0.9';
75
76 // FORMS
77 $this->textarea_lineheight = 1.25;
78
79 // FORM ELEMENT SPACING
80 $this->form_element_spacing['select']['outer']['h'] = 0.5; // Horizontal spacing around SELECT
81 $this->form_element_spacing['select']['outer']['v'] = 0.5; // Vertical spacing around SELECT
82 $this->form_element_spacing['select']['inner']['h'] = 0.7; // Horizontal padding around SELECT
83 $this->form_element_spacing['select']['inner']['v'] = 0.7; // Vertical padding around SELECT
84 $this->form_element_spacing['input']['outer']['h'] = 0.5;
85 $this->form_element_spacing['input']['outer']['v'] = 0.5;
86 $this->form_element_spacing['input']['inner']['h'] = 0.7;
87 $this->form_element_spacing['input']['inner']['v'] = 0.7;
88 $this->form_element_spacing['textarea']['outer']['h'] = 0.5;
89 $this->form_element_spacing['textarea']['outer']['v'] = 0.5;
90 $this->form_element_spacing['textarea']['inner']['h'] = 1;
91 $this->form_element_spacing['textarea']['inner']['v'] = 0.5;
92 $this->form_element_spacing['button']['outer']['h'] = 0.5;
93 $this->form_element_spacing['button']['outer']['v'] = 0.5;
94 $this->form_element_spacing['button']['inner']['h'] = 2;
95 $this->form_element_spacing['button']['inner']['v'] = 1;
96
97 // INITIALISE non-configurable
98 $this->formMethod = 'POST';
99 $this->formAction = '';
100 $this->form_fonts = array();
101 $this->form_radio_groups = array();
102 $this->form_checkboxes = false;
103 $this->forms = array();
104 $this->pdf_array_co = '';
105
106
107}
108
109
110function print_ob_text($objattr,$w,$h,$texto,$rtlalign,$k,$blockdir) {
111 // TEXT/PASSWORD INPUT
112 if ($this->mpdf->useActiveForms) {
113 // Flags: 1 - Readonly; 2 - Required; 3 - No export; 13 - textarea; 14 - Password
114 $flags = array();
115 if ($objattr['disabled'] || $objattr['readonly']) { $flags[] = 1; } // readonly
116 if ($objattr['disabled']) { $flags[] = 3; } // no export
117 if ($objattr['disabled']) { $objattr['color'] = array(3,128,128,128); } // gray out disabled
118 if ($objattr['required']) { $flags[] = 2; } // required
119 if (!$objattr['spellcheck']) { $flags[] = 23; } // DoNotSpellCheck
120 if ($objattr['subtype']=='PASSWORD') { $flags[] = 14; }
121 $this->mpdf->SetTColor($objattr['color']);
122 $fieldalign = $rtlalign;
123 if ($objattr['text_align']) { $fieldalign = $objattr['text_align']; }
124 if ($objattr['subtype']=='PASSWORD') { $val = $objattr['value']; }
125 else { $val = $objattr['text']; }
126 // mPDF 5.3.25
127 $js = array();
128 if ($objattr['onCalculate']) { $js[] = array('C', $objattr['onCalculate']); }
129 if ($objattr['onValidate']) { $js[] = array('V', $objattr['onValidate']); }
130 if ($objattr['onFormat']) { $js[] = array('F', $objattr['onFormat']); }
131 if ($objattr['onKeystroke']) { $js[] = array('K', $objattr['onKeystroke']); }
132 $this->SetFormText( $w, $h, $objattr['fieldname'], $val, $val, $objattr['title'], $flags, $fieldalign, false, $objattr['maxlength'], $js, $objattr['background-col'], $objattr['border-col'] );
133 $this->mpdf->SetTColor($this->mpdf->ConvertColor(0));
134 }
135 else {
136 $w -= $this->form_element_spacing['input']['outer']['h']*2 /$k;
137 $h -= $this->form_element_spacing['input']['outer']['v']*2 /$k;
138 $this->mpdf->x += $this->form_element_spacing['input']['outer']['h'] /$k;
139 $this->mpdf->y += $this->form_element_spacing['input']['outer']['v'] /$k;
140 // Chop texto to max length $w-inner-padding
141 while ($this->mpdf->GetStringWidth($texto) > $w-($this->form_element_spacing['input']['inner']['h']*2)) {
142 $texto = mb_substr($texto,0,mb_strlen($texto,$this->mpdf->mb_enc)-1,$this->mpdf->mb_enc);
143 }
144 $save_linemaxfontsize = $this->mpdf->linemaxfontsize;
145 $this->mpdf->linemaxfontsize = $this->mpdf->FontSize;
146 $this->mpdf->SetLineWidth(0.2 /$k );
147 $this->mpdf->magic_reverse_dir($texto, false, $blockdir); // *RTL*
148 if (isset($objattr['disabled']) && $objattr['disabled']) {
149 $this->mpdf->SetFColor($this->mpdf->ConvertColor(225));
150 $this->mpdf->SetTColor($this->mpdf->ConvertColor(127));
151 }
152 else if (isset($objattr['readonly']) && $objattr['readonly']) {
153 $this->mpdf->SetFColor($this->mpdf->ConvertColor(225));
154 $this->mpdf->SetTColor($this->mpdf->ConvertColor(0));
155 }
156 else {
157 $this->mpdf->SetFColor($this->mpdf->ConvertColor(250));
158 $this->mpdf->SetTColor($this->mpdf->ConvertColor(0));
159 }
160 $this->mpdf->Cell($w,$h,$texto,1,0,$rtlalign,1,'',0,$this->form_element_spacing['input']['inner']['h'] /$k ,$this->form_element_spacing['input']['inner']['h'] /$k , 'M');
161 $this->mpdf->SetFColor($this->mpdf->ConvertColor(255));
162 $this->mpdf->SetTColor($this->mpdf->ConvertColor(0));
163 $this->mpdf->linemaxfontsize = $save_linemaxfontsize;
164 }
165}
166
167function print_ob_textarea($objattr,$w,$h,$texto,$rtlalign,$k,$blockdir) {
168 // TEXTAREA
169 if ($this->mpdf->useActiveForms) {
170 // Flags: 1 - Readonly; 2 - Required; 3 - No export; 13 - textarea; 14 - Password
171 $flags = array();
172 $flags = array(13); // textarea
173 if ($objattr['disabled'] || $objattr['readonly']) { $flags[] = 1; } // readonly
174 if ($objattr['disabled']) { $flags[] = 3; } // no export
175 if ($objattr['disabled']) { $objattr['color'] = array(3,128,128,128); } // gray out disabled
176 if ($objattr['required']) { $flags[] = 2; } // required
177 if (!$objattr['spellcheck']) { $flags[] = 23; } // DoNotSpellCheck
178 if ($objattr['donotscroll']) { $flags[] = 24; } // DoNotScroll
179 $this->mpdf->SetTColor($objattr['color']);
180 $fieldalign = $rtlalign;
181 if ($texto == ' ') { $texto = ''; } // mPDF 5.3.24
182 if ($objattr['text_align']) { $fieldalign = $objattr['text_align']; }
183 // mPDF 5.3.25
184 $js = array();
185 if ($objattr['onCalculate']) { $js[] = array('C', $objattr['onCalculate']); }
186 if ($objattr['onValidate']) { $js[] = array('V', $objattr['onValidate']); }
187 if ($objattr['onFormat']) { $js[] = array('F', $objattr['onFormat']); }
188 if ($objattr['onKeystroke']) { $js[] = array('K', $objattr['onKeystroke']); }
189 $this->SetFormText( $w, $h, $objattr['fieldname'], $texto, $texto, $objattr['title'], $flags, $fieldalign , false, -1, $js, $objattr['background-col'], $objattr['border-col'] );
190 $this->mpdf->SetTColor($this->mpdf->ConvertColor(0));
191 }
192 else {
193 $w -= $this->form_element_spacing['textarea']['outer']['h']*2 /$k ;
194 $h -= $this->form_element_spacing['textarea']['outer']['v']*2 /$k ;
195 $this->mpdf->x += $this->form_element_spacing['textarea']['outer']['h'] /$k ;
196 $this->mpdf->y += $this->form_element_spacing['textarea']['outer']['v'] /$k ;
197 $this->mpdf->SetLineWidth(0.2 /$k );
198 if (isset($objattr['disabled']) && $objattr['disabled']) {
199 $this->mpdf->SetFColor($this->mpdf->ConvertColor(225));
200 $this->mpdf->SetTColor($this->mpdf->ConvertColor(127));
201 }
202 else if (isset($objattr['readonly']) && $objattr['readonly']) {
203 $this->mpdf->SetFColor($this->mpdf->ConvertColor(225));
204 $this->mpdf->SetTColor($this->mpdf->ConvertColor(0));
205 }
206 else {
207 $this->mpdf->SetFColor($this->mpdf->ConvertColor(250));
208 $this->mpdf->SetTColor($this->mpdf->ConvertColor(0));
209 }
210 $this->mpdf->Rect($this->mpdf->x,$this->mpdf->y,$w,$h,'DF');
211 $w -= $this->form_element_spacing['textarea']['inner']['h']*2 /$k ;
212 $this->mpdf->x += $this->form_element_spacing['textarea']['inner']['h'] /$k ;
213 $this->mpdf->y += $this->form_element_spacing['textarea']['inner']['v'] /$k ;
214 $linesneeded = $this->mpdf->WordWrap($texto,$w);
215 if ($linesneeded > $objattr['rows']) { //Too many words inside textarea
216 $textoaux = explode("\n",$texto);
217 $texto = '';
218 for($i=0;$i<$objattr['rows'];$i++) {
219 if ($i == ($objattr['rows']-1)) $texto .= $textoaux[$i];
220 else $texto .= $textoaux[$i] . "\n";
221 }
222 $texto = mb_substr($texto,0,mb_strlen($texto,$this->mpdf->mb_enc)-4,$this->mpdf->mb_enc) . "...";
223 }
224 if ($texto != '') $this->mpdf->MultiCell($w,$this->mpdf->FontSize*$this->textarea_lineheight,$texto,0,'',0,'',$blockdir,true);
225 $this->mpdf->SetFColor($this->mpdf->ConvertColor(255));
226 $this->mpdf->SetTColor($this->mpdf->ConvertColor(0));
227 }
228}
229
230function print_ob_select($objattr,$w,$h,$texto,$rtlalign,$k,$blockdir) {
231 // SELECT
232 if ($this->mpdf->useActiveForms) {
233 // Flags: 1 - Readonly; 2 - Required; 3 - No export; 19 - edit (only if combo)
234 $flags = array();
235 if ($objattr['disabled']) { $flags[] = 1; } // readonly
236 if ($objattr['disabled']) { $flags[] = 3; } // no export
237 if ($objattr['disabled']) { $objattr['color'] = array(3,128,128,128); } // gray out disabled
238 if ($objattr['required']) { $flags[] = 2; } // required
239 if ($objattr['multiple'] && $objattr['size']>1) { $flags[] = 22; } //flag 22 = multiselect (listbox)
240 if ($objattr['size']<2) {
241 $flags[] = 18; //flag 18 = combobox (else a listbox)
242 if ($objattr['editable']) { $flags[] = 19; } // editable
243 }
244 // only spellcheck if combo and editable
245 if (!$objattr['spellcheck'] || $objattr['size']>1 || !$objattr['editable']) { $flags[] = 23; } // DoNotSpellCheck
246 if ($objattr['subtype']=='PASSWORD') { $flags[] = 14; }
247 if ($objattr['onChange']) { $js = $objattr['onChange']; }
248 else { $js = ''; } // mPDF 5.3.37
249 $data = array('VAL' => array(), 'OPT' => array(), 'SEL' => array(), );
250 for($i=0; $i<count($objattr['items']); $i++) {
251 $item = $objattr['items'][$i];
252 $data['VAL'][] = $item['exportValue'];
253 $data['OPT'][] = $item['content'];
254 if ($item['selected']) { $data['SEL'][] = $i; }
255 }
256 if (count($data['SEL'])==0 && $this->formSelectDefaultOption) {$data['SEL'][] = 0; }
257 $this->mpdf->SetTColor($objattr['color']);
258 $this->SetFormChoice( $w, $h, $objattr['fieldname'], $flags, $data, $rtlalign, $js );
259 $this->mpdf->SetTColor($this->mpdf->ConvertColor(0));
260 }
261 else {
262 $save_linemaxfontsize = $this->mpdf->linemaxfontsize;
263 $this->mpdf->linemaxfontsize = $this->mpdf->FontSize;
264 $this->mpdf->magic_reverse_dir($texto, false, $blockdir); // *RTL*
265 $this->mpdf->SetLineWidth(0.2 /$k );
266 if (isset($objattr['disabled']) && $objattr['disabled']) {
267 $this->mpdf->SetFColor($this->mpdf->ConvertColor(225));
268 $this->mpdf->SetTColor($this->mpdf->ConvertColor(127));
269 }
270 else {
271 $this->mpdf->SetFColor($this->mpdf->ConvertColor(250));
272 $this->mpdf->SetTColor($this->mpdf->ConvertColor(0));
273 }
274 $w -= $this->form_element_spacing['select']['outer']['h']*2 /$k ;
275 $h -= $this->form_element_spacing['select']['outer']['v']*2 /$k ;
276 $this->mpdf->x += $this->form_element_spacing['select']['outer']['h'] /$k ;
277 $this->mpdf->y += $this->form_element_spacing['select']['outer']['v'] /$k ;
278 $this->mpdf->Cell($w-($this->mpdf->FontSize*1.4),$h,$texto,1,0,$rtlalign,1,'',0,$this->form_element_spacing['select']['inner']['h'] /$k,$this->form_element_spacing['select']['inner']['h'] /$k , 'M') ;
279 $this->mpdf->SetFColor($this->mpdf->ConvertColor(190));
280 $save_font = $this->mpdf->FontFamily;
281 $save_currentfont = $this->mpdf->currentfontfamily;
282 if ($this->mpdf->PDFA || $this->mpdf->PDFX) {
283 if (($this->mpdf->PDFA && !$this->mpdf->PDFAauto) || ($this->mpdf->PDFX && !$this->mpdf->PDFXauto)) { $this->mpdf->PDFAXwarnings[] = "Core Adobe font Zapfdingbats cannot be embedded in mPDF - used in Form element: Select - which is required for PDFA1-b or PDFX/1-a. (Different character/font will be substituted.)"; }
284 $this->mpdf->SetFont('sans');
285 if ($this->mpdf->_charDefined($this->mpdf->CurrentFont['cw'], 9660)) { $down = "\xe2\x96\xbc"; }
286 else { $down = '='; }
287 $this->mpdf->Cell(($this->mpdf->FontSize*1.4),$h,$down,1,0,'C',1,'',0,0,0, 'M') ;
288 }
289 else {
290 $this->mpdf->SetFont('czapfdingbats','',0);
291 $this->mpdf->Cell(($this->mpdf->FontSize*1.4),$h,chr(116),1,0,'C',1,'',0,0,0, 'M') ;
292 }
293 $this->mpdf->SetFont($save_font,'',0);
294 $this->mpdf->currentfontfamily = $save_currentfont;
295 $this->mpdf->linemaxfontsize = $save_linemaxfontsize;
296 $this->mpdf->SetFColor($this->mpdf->ConvertColor(255));
297 $this->mpdf->SetTColor($this->mpdf->ConvertColor(0));
298 }
299}
300
301function print_ob_imageinput($objattr,$w,$h,$texto,$rtlalign,$k,$blockdir) {
302 // INPUT/BUTTON as IMAGE
303 if ($this->mpdf->useActiveForms) {
304 // Flags: 1 - Readonly; 3 - No export;
305 $flags = array();
306 if ($objattr['disabled']) { $flags[] = 1; } // readonly
307 if ($objattr['disabled']) { $flags[] = 3; } // no export
308 if ($objattr['onClick']) { $js = $objattr['onClick']; }
309 else { $js = ''; }
310 $this->SetJSButton( $w, $h, $objattr['fieldname'], $objattr['value'], $js, $objattr['ID'], $objattr['title'], $flags, $objattr['Indexed']);
311 }
312 else {
313 $this->mpdf->y = $objattr['INNER-Y'];
314 $this->mpdf->_out( sprintf("q %.3F 0 0 %.3F %.3F %.3F cm /I%d Do Q",$objattr['INNER-WIDTH'] *_MPDFK,$objattr['INNER-HEIGHT'] *_MPDFK,$objattr['INNER-X'] *_MPDFK,($this->mpdf->h-($objattr['INNER-Y'] +$objattr['INNER-HEIGHT'] ))*_MPDFK,$objattr['ID'] ) );
315 if (isset($objattr['BORDER-WIDTH']) && $objattr['BORDER-WIDTH']) { $this->mpdf->PaintImgBorder($objattr,$is_table); }
316 }
317}
318
319function print_ob_button($objattr,$w,$h,$texto,$rtlalign,$k,$blockdir) {
320 // BUTTON
321 if ($this->mpdf->useActiveForms) {
322 // Flags: 1 - Readonly; 3 - No export;
323 $flags = array();
324 if ($objattr['disabled']) { $flags[] = 1; } // readonly
325 if ($objattr['disabled']) { $flags[] = 3; } // no export
326 if ($objattr['disabled']) { $objattr['color'] = array(3,128,128,128); }
327 $this->mpdf->SetTColor($objattr['color']);
328 if ($objattr['subtype'] == 'RESET') {
329 $this->SetFormButtonText( $objattr['value'] );
330 $this->SetFormReset( $w, $h, $objattr['fieldname'], $objattr['value'], $objattr['title'], $flags, $objattr['background-col'], $objattr['border-col'], $objattr['noprint'] );
331 }
332 else if ($objattr['subtype'] == 'SUBMIT') {
333 $url = $this->formAction;
334 $type = $this->formExportType;
335 $method = $this->formMethod;
336 $this->SetFormButtonText( $objattr['value'] );
337 $this->SetFormSubmit( $w, $h, $objattr['fieldname'], $objattr['value'], $url, $objattr['title'], $type, $method, $flags, $objattr['background-col'], $objattr['border-col'], $objattr['noprint'] );
338 }
339 else if ($objattr['subtype'] == 'BUTTON') {
340 $this->SetFormButtonText( $objattr['value'] );
341 if ($objattr['onClick']) { $js = $objattr['onClick']; }
342 else { $js = ''; }
343 $this->SetJSButton( $w, $h, $objattr['fieldname'], $objattr['value'], $js, 0, $objattr['title'], $flags, false, $objattr['background-col'], $objattr['border-col'], $objattr['noprint'] );
344 }
345 $this->mpdf->SetTColor($this->mpdf->ConvertColor(0));
346 }
347 else {
348 $this->mpdf->SetLineWidth(0.2 /$k );
349 $this->mpdf->SetFColor($this->mpdf->ConvertColor(190));
350 $w -= $this->form_element_spacing['button']['outer']['h']*2 /$k ;
351 $h -= $this->form_element_spacing['button']['outer']['v']*2 /$k ;
352 $this->mpdf->x += $this->form_element_spacing['button']['outer']['h'] /$k ;
353 $this->mpdf->y += $this->form_element_spacing['button']['outer']['v'] /$k ;
354 $this->mpdf->RoundedRect($this->mpdf->x, $this->mpdf->y, $w, $h, 0.5 /$k , 'DF');
355 $w -= $this->form_element_spacing['button']['inner']['h']*2 /$k ;
356 $h -= $this->form_element_spacing['button']['inner']['v']*2 /$k ;
357 $this->mpdf->x += $this->form_element_spacing['button']['inner']['h'] /$k ;
358 $this->mpdf->y += $this->form_element_spacing['button']['inner']['v'] /$k ;
359 $save_linemaxfontsize = $this->mpdf->linemaxfontsize;
360 $this->mpdf->linemaxfontsize = $this->mpdf->FontSize;
361 $this->mpdf->magic_reverse_dir($texto, false, $blockdir); // *RTL*
362 $this->mpdf->Cell($w,$h,$texto,'',0,'C',0,'',0,0,0, 'M') ;
363 $this->mpdf->SetFColor($this->mpdf->ConvertColor(0));
364 $this->mpdf->linemaxfontsize = $save_linemaxfontsize;
365 }
366}
367
368function print_ob_checkbox($objattr,$w,$h,$texto,$rtlalign,$k,$blockdir,$x,$y) {
369 // CHECKBOX
370 if ($this->mpdf->useActiveForms) {
371 // Flags: 1 - Readonly; 2 - Required; 3 - No export;
372 $flags = array();
373 if ($objattr['disabled']) { $flags[] = 1; } // readonly
374 if ($objattr['disabled']) { $flags[] = 3; } // no export
375 $checked = false;
376 if ($objattr['checked']) { $checked = true; }
377 if ($this->formUseZapD) {
378 $save_font = $this->mpdf->FontFamily;
379 $save_currentfont = $this->mpdf->currentfontfamily;
380 $this->mpdf->SetFont('czapfdingbats','',0);
381 }
382 $this->SetCheckBox( $w, $h, $objattr['fieldname'], $objattr['value'], $objattr['title'], $checked, $flags, $objattr['disabled'] );
383 if ($this->formUseZapD) {
384 $this->mpdf->SetFont($save_font,'',0);
385 $this->mpdf->currentfontfamily = $save_currentfont;
386 }
387 }
388 else {
389 $iw = $w * 0.7;
390 $ih = $h * 0.7;
391 $lx = $x + (($w-$iw)/2);
392 $ty = $y + (($h-$ih)/2);
393 $rx = $lx + $iw;
394 $by = $ty + $ih;
395 $this->mpdf->SetLineWidth(0.2 /$k );
396 if (isset($objattr['disabled']) && $objattr['disabled']) {
397 $this->mpdf->SetFColor($this->mpdf->ConvertColor(225));
398 $this->mpdf->SetDColor($this->mpdf->ConvertColor(127));
399 }
400 else {
401 $this->mpdf->SetFColor($this->mpdf->ConvertColor(250));
402 $this->mpdf->SetDColor($this->mpdf->ConvertColor(0));
403 }
404 $this->mpdf->Rect($lx,$ty,$iw,$ih,'DF');
405 if (isset($objattr['checked']) && $objattr['checked']) {
406 //Round join and cap
407 $this->mpdf->SetLineCap(1);
408 $this->mpdf->Line($lx,$ty,$rx,$by);
409 $this->mpdf->Line($lx,$by,$rx,$ty);
410 //Set line cap style back to square
411 $this->mpdf->SetLineCap(2);
412 }
413 $this->mpdf->SetFColor($this->mpdf->ConvertColor(255));
414 $this->mpdf->SetDColor($this->mpdf->ConvertColor(0));
415 }
416}
417
418function print_ob_radio($objattr,$w,$h,$texto,$rtlalign,$k,$blockdir,$x,$y) {
419 // RADIO
420 if ($this->mpdf->useActiveForms) {
421 // Flags: 1 - Readonly; 2 - Required; 3 - No export;
422 $flags = array();
423 if ($objattr['disabled']) { $flags[] = 1; } // readonly
424 if ($objattr['disabled']) { $flags[] = 3; } // no export
425 $checked = false;
426 if ($objattr['checked']) { $checked = true; }
427 if ($this->formUseZapD) {
428 $save_font = $this->mpdf->FontFamily;
429 $save_currentfont = $this->mpdf->currentfontfamily;
430 $this->mpdf->SetFont('czapfdingbats','',0);
431 }
432 $this->SetRadio( $w, $h, $objattr['fieldname'], $objattr['value'], $objattr['title'], $checked, $flags, $objattr['disabled'] );
433 if ($this->formUseZapD) {
434 $this->mpdf->SetFont($save_font,'',0);
435 $this->mpdf->currentfontfamily = $save_currentfont;
436 }
437 }
438 else {
439 $this->mpdf->SetLineWidth(0.2 /$k );
440 $radius = $this->mpdf->FontSize *0.35;
441 $cx = $x + ($w/2);
442 $cy = $y + ($h/2);
443 if (isset($objattr['disabled']) && $objattr['disabled']) {
444 $this->mpdf->SetFColor($this->mpdf->ConvertColor(127));
445 $this->mpdf->SetDColor($this->mpdf->ConvertColor(127));
446 }
447 else {
448 $this->mpdf->SetFColor($this->mpdf->ConvertColor(0));
449 $this->mpdf->SetDColor($this->mpdf->ConvertColor(0));
450 }
451 $this->mpdf->Circle($cx,$cy,$radius,'D');
452 if (isset($objattr['checked']) && $objattr['checked']) {
453 $this->mpdf->Circle($cx,$cy,$radius*0.4,'DF');
454 }
455 $this->mpdf->SetFColor($this->mpdf->ConvertColor(255));
456 $this->mpdf->SetDColor($this->mpdf->ConvertColor(0));
457
458 }
459}
460
461
462// In _putpages
463function countPageForms($n, &$totaladdnum) {
464 foreach( $this->forms as $form ) {
465 if ( $form['page'] == $n ) {
466 $totaladdnum++;
467 if ( $form['typ'] == 'Tx' ) {
468 if ( isset($this->array_form_text_js[$form['T']]) ) {
469 if ( isset($this->array_form_text_js[$form['T']]['F']) ) { $totaladdnum++; }
470 if ( isset($this->array_form_text_js[$form['T']]['K']) ) { $totaladdnum++; }
471 if ( isset($this->array_form_text_js[$form['T']]['V']) ) { $totaladdnum++; }
472 if ( isset($this->array_form_text_js[$form['T']]['C']) ) { $totaladdnum++; }
473 }
474 }
475 if ( $form['typ'] == 'Bt' ) {
476 if ( isset($this->array_form_button_js[$form['T']]) ) { $totaladdnum++; }
477 if ( isset($this->form_button_icon[$form['T']]) ) {
478 $totaladdnum++;
479 if ( $this->form_button_icon[$form['T']]['Indexed'] ) { $totaladdnum++; }
480 }
481 if ( $form['subtype'] == 'radio' ) { $totaladdnum+=2; }
482 else if ( $form['subtype'] == 'checkbox' && $this->formUseZapD ) { $totaladdnum++; }
483 else if ( $form['subtype'] == 'checkbox' && !$this->formUseZapD ) { $totaladdnum+=2; }
484 }
485 if ( $form['typ'] == 'Ch' ) {
486 if ( isset($this->array_form_choice_js[$form['T']]) ) { $totaladdnum++; }
487 }
488 }
489 }
490}
491
492// In _putpages
493function addFormIds($n, &$s, &$annotid) {
494 foreach( $this->forms as $form ) {
495 if ( $form['page'] == $n ) {
496 $s .= ($annotid) . ' 0 R ';
497 $annotid++;
498 if ( $form['typ'] == 'Tx' ) {
499 if ( isset($this->array_form_text_js[$form['T']]) ) {
500 if ( isset($this->array_form_text_js[$form['T']]['F']) ) { $annotid++; }
501 if ( isset($this->array_form_text_js[$form['T']]['K']) ) { $annotid++; }
502 if ( isset($this->array_form_text_js[$form['T']]['V']) ) { $annotid++; }
503 if ( isset($this->array_form_text_js[$form['T']]['C']) ) { $annotid++; }
504 }
505 }
506 if ( $form['typ'] == 'Bt' ) {
507 if ( isset($this->array_form_button_js[$form['T']]) ) { $annotid++; }
508 if ( isset($this->form_button_icon[$form['T']]) ) {
509 $annotid++;
510 if ( $this->form_button_icon[$form['T']]['Indexed'] ) { $annotid++; }
511 }
512 if ( $form['subtype'] == 'radio' ) { $annotid+=2; }
513 else if ( $form['subtype'] == 'checkbox' && $this->formUseZapD ) { $annotid++; }
514 else if ( $form['subtype'] == 'checkbox' && !$this->formUseZapD ) { $annotid+=2; }
515 }
516 if ( $form['typ'] == 'Ch' ) {
517 if ( isset($this->array_form_choice_js[$form['T']]) ) { $annotid++; }
518 }
519 }
520 }
521}
522
523// In _putannots
524function _putFormItems($n, $hPt) {
525 foreach( $this->forms as $val) {
526 if ( $val['page'] == $n ) {
527 if ( $val['typ'] == 'Tx' ) $this->_putform_tx( $val, $hPt );
528 if ( $val['typ'] == 'Ch' ) $this->_putform_ch( $val, $hPt );
529 if ( $val['typ'] == 'Bt' ) $this->_putform_bt( $val, $hPt );
530 }
531 }
532}
533
534// In _putannots
535function _putRadioItems($n) {
536 // Output Radio Groups
537 $key = 1;
538 foreach($this->form_radio_groups AS $name=>$frg) {
539 $this->mpdf->_newobj();
540 $this->pdf_acro_array .= $this->mpdf->n.' 0 R ';
541 $this->mpdf->_out('<<');
542 $this->mpdf->_out('/Type /Annot ');
543 $this->mpdf->_out('/Subtype /Widget');
544 $this->mpdf->_out('/NM '.$this->mpdf->_textstring(sprintf('%04u-%04u', $n, (3000 + $key++))));
545 $this->mpdf->_out('/M '.$this->mpdf->_textstring('D:'.date('YmdHis')));
546 $this->mpdf->_out('/Rect [0 0 0 0] ');
547 $this->mpdf->_out('/FT /Btn ');
548 if ($frg['disabled']) { $flags=array(1,3,15,16); } // NoExport and readonly
549 else { $flags=array(15,16); } // Flags for Radiobutton, and NoToggleToOff
550 $this->mpdf->_out('/Ff '.$this->_setflag($flags) );
551 $kstr = '';
552 $optstr = '';
553 foreach($frg['kids'] AS $kid) {
554 $kstr .= $this->forms[$kid['n']]['obj'].' 0 R ';
555 // $optstr .= ' '.$this->mpdf->_textstring($kid['OPT']).' ';
556 }
557 $this->mpdf->_out('/Kids [ '.$kstr.' ] '); // 11 0 R 12 0 R etc.
558 // $this->mpdf->_out('/Opt [ '.$optstr.' ] ');
559
560 //V entry holds index corresponding to the appearance state of
561 //whichever child field is currently in the on state = or Off
562 if (isset($frg['on'])) { $state = $frg['on']; }
563 else { $state = 'Off'; }
564 $this->mpdf->_out('/V /'.$state.' ');
565 $this->mpdf->_out('/DV /'.$state.' ');
566 $this->mpdf->_out('/T '.$this->mpdf->_textstring($name).' ');
567 $this->mpdf->_out('>>');
568 $this->mpdf->_out('endobj');
569 }
570}
571
572function _putFormsCatalog() {
573 if (isset($this->pdf_acro_array) ) {
574 $this->mpdf->_out('/AcroForm << /DA (/F1 0 Tf 0 g )');
575 $this->mpdf->_out('/Q 0');
576 $this->mpdf->_out('/Fields ['.$this->pdf_acro_array.']');
577 $f = '';
578 foreach($this->form_fonts AS $fn) {
579 if (is_array($this->mpdf->fonts[$fn]['n'])) { $this->mpdf->Error("Cannot use fonts with SMP or SIP characters for interactive Form elements"); }
580 $f .= '/F'.$this->mpdf->fonts[$fn]['i'].' '.$this->mpdf->fonts[$fn]['n'].' 0 R ';
581 }
582 $this->mpdf->_out('/DR << /Font << '.$f.' >> >>');
583 // CO Calculation Order
584 if ( $this->pdf_array_co ) {
585 $this->mpdf->_out('/CO ['.$this->pdf_array_co.']');
586 }
587 $this->mpdf->_out('/NeedAppearances true');
588 $this->mpdf->_out('>>');
589 }
590}
591
592
593
594function SetFormButtonJS( $name, $js ) {
595 $js = str_replace("\t",' ', trim($js) );
596 if ( isset($name) && isset($js) ) {
597 $this->array_form_button_js[$this->mpdf->_escape($name)] = array(
598 'js' => $js
599 );
600 }
601}
602
603function SetFormChoiceJS( $name, $js ) {
604 $js = str_replace("\t",' ', trim($js) );
605 if ( isset($name) && isset($js) ) {
606 $this->array_form_choice_js[$this->mpdf->_escape($name)] = array(
607 'js' => $js
608 );
609 }
610}
611
612function SetFormTextJS( $name, $js) {
613 for ($i=0; $i<count($js); $i++) {
614 $j = str_replace("\t",' ', trim($js[$i][1]) );
615 $format = $js[$i][0];
616 if ($name) {
617 $this->array_form_text_js[$this->mpdf->_escape($name)][$format] = array('js' => $j);
618 }
619 }
620}
621
622
623function Win1252ToPDFDocEncoding($txt) {
624 $Win1252ToPDFDocEncoding = array(
625 chr(0200) => chr(0240), chr(0214) => chr(0226), chr(0212) => chr(0227), chr(0237) => chr(0230),
626 chr(0225) => chr(0200), chr(0210) => chr(0032), chr(0206) => chr(0201), chr(0207) => chr(0202),
627 chr(0205) => chr(0203), chr(0227) => chr(0204), chr(0226) => chr(0205), chr(0203) => chr(0206),
628 chr(0213) => chr(0210), chr(0233) => chr(0211), chr(0211) => chr(0213), chr(0204) => chr(0214),
629 chr(0223) => chr(0215), chr(0224) => chr(0216), chr(0221) => chr(0217), chr(0222) => chr(0220),
630 chr(0202) => chr(0221), chr(0232) => chr(0235), chr(0230) => chr(0037), chr(0231) => chr(0222),
631 chr(0216) => chr(0231), chr(0240) => chr(0040)
632 ); // mPDF 5.3.46
633 return strtr($txt, $Win1252ToPDFDocEncoding );
634}
635
636
637function SetFormText( $w, $h, $name, $value = '', $default = '', $title = '', $flags = array(), $align='L', $hidden = false, $maxlen=-1, $js='', $background_col=false, $border_col=false ) {
638 // Flags: 1 - Readonly; 2 - Required; 3 - No export; 13 - textarea; 14 - Password
639 $this->formn++;
640 if( $align == 'C' ) { $align = '1'; }
641 else if( $align == 'R' ) { $align = '2'; }
642 else { $align = '0'; }
643 if ($maxlen < 1) { $maxlen = false; }
644 if (!preg_match('/^[a-zA-Z0-9_:\-]+$/', $name)) {
645 $this->mpdf->Error("Field [".$name."] must have a name attribute, which can only contain letters, numbers, colon(:), undersore(_) or hyphen(-)");
646 }
647 if ($this->mpdf->onlyCoreFonts) {
648 $value = $this->Win1252ToPDFDocEncoding($value);
649 $default = $this->Win1252ToPDFDocEncoding($default);
650 $title = $this->Win1252ToPDFDocEncoding($title);
651 }
652 else {
653 if (isset($this->mpdf->CurrentFont['subset'])) {
654 $this->mpdf->UTF8StringToArray($value, true); // Add characters to font subset
655 $this->mpdf->UTF8StringToArray($default, true); // Add characters to font subset
656 $this->mpdf->UTF8StringToArray($title, true); // Add characters to font subset
657 }
658 if ($value) $value = $this->mpdf->UTF8ToUTF16BE($value, true);
659 if ($default ) $default = $this->mpdf->UTF8ToUTF16BE($default, true);
660 $title = $this->mpdf->UTF8ToUTF16BE($title, true);
661 }
662 if ($background_col) { $bg_c = $this->mpdf->SetColor($background_col, 'CodeOnly'); }
663 else { $bg_c = $this->form_background_color; }
664 if ($border_col) { $bc_c = $this->mpdf->SetColor($border_col, 'CodeOnly'); }
665 else { $bc_c = $this->form_border_color; }
666 $f = array( 'n' => $this->formn,
667 'typ' => 'Tx',
668 'page' => $this->mpdf->page,
669 'x' => $this->mpdf->x,
670 'y' => $this->mpdf->y,
671 'w' => $w,
672 'h' => $h,
673 'T' => $name,
674 'FF' => $flags,
675 'V' => $value,
676 'DV' => $default,
677 'TU' => $title,
678 'hidden' => $hidden,
679 'Q' => $align,
680 'maxlen' => $maxlen,
681 'BS_W' => $this->form_border_width,
682 'BS_S' => $this->form_border_style,
683 'BC_C' => $bc_c,
684 'BG_C' => $bg_c,
685 'style' => array(
686 'font' => $this->mpdf->FontFamily,
687 'fontsize' => $this->mpdf->FontSizePt,
688 'fontcolor' => $this->mpdf->TextColor,
689 )
690 );
691 if (is_array($js) && count($js)>0) { $this->SetFormTextJS( $name, $js); } // mPDF 5.3.25
692 if ($this->mpdf->keep_block_together) { $this->mpdf->ktForms[]= $f; }
693 else if ($this->mpdf->writingHTMLheader || $this->mpdf->writingHTMLfooter) { $this->mpdf->HTMLheaderPageForms[]= $f; }
694 else {
695 if ($this->mpdf->ColActive) {
696 $this->mpdf->columnbuffer[] = array('s' => 'ACROFORM', 'col' => $this->mpdf->CurrCol, 'x' => $this->mpdf->x, 'y' => $this->mpdf->y,
697 'h' => $h);
698 $this->mpdf->columnForms[$this->mpdf->CurrCol][INTVAL($this->mpdf->x)][INTVAL($this->mpdf->y)] = $this->formn;
699 }
700 $this->forms[$this->formn] = $f;
701 }
702 if (!in_array($this->mpdf->FontFamily, $this->form_fonts)) {
703 $this->form_fonts[] = $this->mpdf->FontFamily;
704 $this->mpdf->fonts[$this->mpdf->FontFamily]['used'] = true;
705 }
706 if ( !$hidden ) $this->mpdf->x += $w;
707
708}
709
710
711 function SetFormChoice( $w, $h, $name, $flags, $array, $align='L', $js = '' ) {
712 $this->formn++;
713 if( $this->mpdf->blk[$this->mpdf->blklvl]['direction'] == 'rtl' ) { $align = '2'; }
714 else { $align = '0'; }
715 if (!preg_match('/^[a-zA-Z0-9_:\-]+$/', $name)) {
716 $this->mpdf->Error("Field [".$name."] must have a name attribute, which can only contain letters, numbers, colon(:), undersore(_) or hyphen(-)");
717 }
718 if ($this->mpdf->onlyCoreFonts) {
719 for($i=0;$i<count($array['VAL']);$i++) {
720 $array['VAL'][$i] = $this->Win1252ToPDFDocEncoding($array['VAL'][$i]);
721 $array['OPT'][$i] = $this->Win1252ToPDFDocEncoding($array['OPT'][$i]);
722 }
723 }
724 else {
725 for($i=0;$i<count($array['VAL']);$i++) {
726 if (isset($this->mpdf->CurrentFont['subset'])) {
727 $this->mpdf->UTF8StringToArray($array['VAL'][$i], true); // Add characters to font subset
728 $this->mpdf->UTF8StringToArray($array['OPT'][$i], true); // Add characters to font subset
729 }
730 if ($array['VAL'][$i] ) $array['VAL'][$i] = $this->mpdf->UTF8ToUTF16BE($array['VAL'][$i], true);
731 if ($array['OPT'][$i] ) $array['OPT'][$i] = $this->mpdf->UTF8ToUTF16BE($array['OPT'][$i], true);
732 }
733 }
734 $f = array( 'n' => $this->formn,
735 'typ' => 'Ch',
736 'page' => $this->mpdf->page,
737 'x' => $this->mpdf->x,
738 'y' => $this->mpdf->y,
739 'w' => $w,
740 'h' => $h,
741 'T' => $name,
742 'OPT' => $array,
743 'FF' => $flags,
744 'Q' => $align,
745 'BS_W' => $this->form_border_width,
746 'BS_S' => $this->form_border_style,
747 'BC_C' => $this->form_border_color,
748 'BG_C' => $this->form_background_color,
749 'style' => array(
750 'font' => $this->mpdf->FontFamily,
751 'fontsize' => $this->mpdf->FontSizePt,
752 'fontcolor' => $this->mpdf->TextColor,
753 )
754 );
755 if ($js) { $this->SetFormChoiceJS( $name, $js ); }
756 if ($this->mpdf->keep_block_together) { $this->mpdf->ktForms[]= $f; }
757 else if ($this->mpdf->writingHTMLheader || $this->mpdf->writingHTMLfooter) { $this->mpdf->HTMLheaderPageForms[]= $f; }
758 else {
759 if ($this->mpdf->ColActive) {
760 $this->mpdf->columnbuffer[] = array('s' => 'ACROFORM', 'col' => $this->mpdf->CurrCol, 'x' => $this->mpdf->x, 'y' => $this->mpdf->y,
761 'h' => $h);
762 $this->mpdf->columnForms[$this->mpdf->CurrCol][INTVAL($this->mpdf->x)][INTVAL($this->mpdf->y)] = $this->formn;
763 }
764 $this->forms[$this->formn] = $f;
765 }
766 if (!in_array($this->mpdf->FontFamily, $this->form_fonts)) {
767 $this->form_fonts[] = $this->mpdf->FontFamily;
768 $this->mpdf->fonts[$this->mpdf->FontFamily]['used'] = true;
769 }
770 $this->mpdf->x += $w;
771 }
772
773 // CHECKBOX
774 function SetCheckBox( $w, $h, $name, $value, $title = '', $checked = false, $flags = array(), $disabled=false ) {
775 $this->SetFormButton( $w, $h, $name, $value, 'checkbox', $title, $flags, $checked, $disabled );
776 $this->mpdf->x += $w;
777 }
778
779
780 // RADIO
781 function SetRadio( $w, $h, $name, $value, $title = '', $checked = false, $flags = array(), $disabled=false ) {
782 $this->SetFormButton( $w, $h, $name, $value, 'radio', $title, $flags, $checked, $disabled );
783 $this->mpdf->x += $w;
784 }
785
786
787 function SetFormReset( $w, $h, $name, $value = 'Reset', $title = '', $flags = array(), $background_col=false, $border_col=false, $noprint=false ) {
788 if (!$name) { $name = 'Reset'; }
789 $this->SetFormButton( $w, $h, $name, $value, 'reset', $title, $flags, false, false, $background_col, $border_col, $noprint);
790 $this->mpdf->x += $w;
791 }
792
793
794 function SetJSButton( $w, $h, $name, $value, $js, $image_id = 0, $title = '', $flags = array(), $indexed=false , $background_col=false, $border_col=false, $noprint=false ) {
795 $this->SetFormButton( $w, $h, $name, $value, 'js_button', $title, $flags, false, false, $background_col, $border_col, $noprint);
796 // pos => 1 = no caption, icon only; 0 = caption only
797 if ($image_id) {
798 $this->form_button_icon[$this->mpdf->_escape($name)] = array(
799 'pos' => 1,
800 'image_id' => $image_id,
801 'Indexed' => $indexed,
802 );
803 }
804 if ($js) { $this->SetFormButtonJS( $name, $js ); }
805 $this->mpdf->x += $w;
806 }
807
808
809 function SetFormSubmit( $w, $h, $name, $value = 'Submit', $url, $title = '', $typ = 'html', $method = 'POST', $flags = array(), $background_col=false, $border_col=false, $noprint=false) {
810 if (!$name) { $name = 'Submit'; }
811 $this->SetFormButton( $w, $h, $name, $value, 'submit', $title, $flags, false, false, $background_col, $border_col, $noprint);
812 $this->forms[$this->formn]['URL'] = $url;
813 $this->forms[$this->formn]['method'] = $method;
814 $this->forms[$this->formn]['exporttype'] = $typ;
815 $this->mpdf->x += $w;
816 }
817
818
819 function SetFormButtonText( $ca, $rc = '', $ac = '' ) {
820 if ($this->mpdf->onlyCoreFonts) {
821 $ca = $this->Win1252ToPDFDocEncoding($ca);
822 if ($rc) $rc = $this->Win1252ToPDFDocEncoding($rc);
823 if ($ac) $ac = $this->Win1252ToPDFDocEncoding($ac);
824 }
825 else {
826 if (isset($this->mpdf->CurrentFont['subset'])) {
827 $this->mpdf->UTF8StringToArray($ca, true); // Add characters to font subset
828 }
829 $ca = $this->mpdf->UTF8ToUTF16BE($ca, true);
830 if ($rc) {
831 if (isset($this->mpdf->CurrentFont['subset'])) { $this->mpdf->UTF8StringToArray($rc, true); }
832 $rc = $this->mpdf->UTF8ToUTF16BE($rc, true);
833 }
834 if ($ac) {
835 if (isset($this->mpdf->CurrentFont['subset'])) { $this->mpdf->UTF8StringToArray($ac, true); }
836 $ac = $this->mpdf->UTF8ToUTF16BE($ac, true);
837 }
838 }
839 $this->form_button_text = $ca;
840 $this->form_button_text_over = $rc ? $rc : $ca;
841 $this->form_button_text_click = $ac ? $ac : $ca;
842 }
843
844
845 function SetFormButton( $bb, $hh, $name, $value, $type, $title = '', $flags = array(), $checked=false, $disabled=false, $background_col=false, $border_col=false, $noprint=false ) {
846 $this->formn++;
847 if (!preg_match('/^[a-zA-Z0-9_:\-]+$/', $name)) {
848 $this->mpdf->Error("Field [".$name."] must have a name attribute, which can only contain letters, numbers, colon(:), undersore(_) or hyphen(-)");
849 }
850 if (!$this->mpdf->onlyCoreFonts) {
851 if (isset($this->mpdf->CurrentFont['subset'])) {
852 $this->mpdf->UTF8StringToArray($title, true); // Add characters to font subset
853 $this->mpdf->UTF8StringToArray($value, true); // Add characters to font subset
854 }
855 $title = $this->mpdf->UTF8ToUTF16BE($title, true);
856 if ($type == 'checkbox') {
857 $uvalue = $this->mpdf->UTF8ToUTF16BE($value, true);
858 }
859 else if ($type == 'radio') {
860 $uvalue = $this->mpdf->UTF8ToUTF16BE($value, true);
861 $value = mb_convert_encoding($value, 'Windows-1252', 'UTF-8');
862 }
863 else {
864 $value = $this->mpdf->UTF8ToUTF16BE($value, true);
865 $uvalue = $value;
866 }
867 }
868 else {
869 $title = $this->Win1252ToPDFDocEncoding($title);
870 $value = $this->Win1252ToPDFDocEncoding($value); //// ??? not needed
871 $uvalue = mb_convert_encoding($value, 'UTF-8', 'Windows-1252');
872 $uvalue = $this->mpdf->UTF8ToUTF16BE($uvalue, true);
873 }
874 if ($type == 'radio' || $type == 'checkbox') {
875 if (!preg_match('/^[a-zA-Z0-9_:\-\.]+$/', $value)) {
876 $this->mpdf->Error("Field '".$name."' must have a value, which can only contain letters, numbers, colon(:), undersore(_), hyphen(-) or period(.)");
877 }
878 }
879 if ($type == 'radio') {
880 if (!isset($this->form_radio_groups[$name])) {
881 $this->form_radio_groups[$name] = array(
882 'page' => $this->mpdf->page,
883 'kids' => array(),
884 );
885 }
886 $this->form_radio_groups[$name]['kids'][] = array(
887 'n' => $this->formn, 'V'=> $value, 'OPT'=>$uvalue, 'disabled'=>$disabled
888 );
889 if ( $checked ) { $this->form_radio_groups[$name]['on'] = $value; }
890 // Disable the whole radio group if one is disabled, because of inconsistency in PDF readers
891 if ( $disabled ) { $this->form_radio_groups[$name]['disabled'] = true; }
892 }
893 if ($type == 'checkbox') {
894 $this->form_checkboxes = true;
895 }
896 if ( $checked ) { $activ = 1; }
897 else { $activ = 0; }
898 if ($background_col) { $bg_c = $this->mpdf->SetColor($background_col, 'CodeOnly'); }
899 else { $bg_c = $this->form_button_background_color; }
900 if ($border_col) { $bc_c = $this->mpdf->SetColor($border_col, 'CodeOnly'); }
901 else { $bc_c = $this->form_button_border_color; }
902 $f = array( 'n' => $this->formn,
903 'typ' => 'Bt',
904 'page' => $this->mpdf->page,
905 'subtype' => $type,
906 'x' => $this->mpdf->x,
907 'y' => $this->mpdf->y,
908 'w' => $bb,
909 'h' => $hh,
910 'T' => $name,
911 'V' => $value,
912 'OPT' => $uvalue,
913 'TU' => $title,
914 'FF' => $flags,
915 'CA' => $this->form_button_text,
916 'RC' => $this->form_button_text_over,
917 'AC' => $this->form_button_text_click,
918 'BS_W' => $this->form_button_border_width,
919 'BS_S' => $this->form_button_border_style,
920 'BC_C' => $bc_c,
921 'BG_C' => $bg_c,
922 'activ' => $activ,
923 'disabled' => $disabled,
924 'noprint' => $noprint,
925 'style' => array(
926 'font' => $this->mpdf->FontFamily,
927 'fontsize' => $this->mpdf->FontSizePt,
928 'fontcolor' => $this->mpdf->TextColor,
929 )
930 );
931 if ($this->mpdf->keep_block_together) { $this->mpdf->ktForms[]= $f; }
932 else if ($this->mpdf->writingHTMLheader || $this->mpdf->writingHTMLfooter) { $this->mpdf->HTMLheaderPageForms[]= $f; }
933 else {
934 if ($this->mpdf->ColActive) {
935 $this->mpdf->columnbuffer[] = array('s' => 'ACROFORM', 'col' => $this->mpdf->CurrCol, 'x' => $this->mpdf->x, 'y' => $this->mpdf->y,
936 'h' => $hh);
937 $this->mpdf->columnForms[$this->mpdf->CurrCol][INTVAL($this->mpdf->x)][INTVAL($this->mpdf->y)] = $this->formn;
938 }
939 $this->forms[$this->formn] = $f;
940 }
941 if (!in_array($this->mpdf->FontFamily, $this->form_fonts)) {
942 $this->form_fonts[] = $this->mpdf->FontFamily;
943 $this->mpdf->fonts[$this->mpdf->FontFamily]['used'] = true;
944 }
945
946 $this->form_button_text = NULL;
947 $this->form_button_text_over = NULL;
948 $this->form_button_text_click = NULL;
949 }
950
951
952
953function SetFormBorderWidth ( $string ) {
954 switch( $string ) {
955 case 'S': $this->form_border_width = '1';
956 break;
957 case 'M': $this->form_border_width = '2';
958 break;
959 case 'B': $this->form_border_width = '3';
960 break;
961 case '0': $this->form_border_width = '0';
962 break;
963 default: $this->form_border_width = '0';
964 break;
965 }
966}
967
968
969function SetFormBorderStyle ( $string ) {
970 switch( $string ) {
971 case 'S': $this->form_border_style = 'S';
972 break;
973 case 'D': $this->form_border_style = 'D /D [3]';
974 break;
975 case 'B': $this->form_border_style = 'B';
976 break;
977 case 'I': $this->form_border_style = 'I';
978 break;
979 case 'U': $this->form_border_style = 'U';
980 break;
981 default: $this->form_border_style = 'B';
982 break;
983 }
984}
985
986function SetFormBorderColor ( $r, $g=-1, $b=-1 ) {
987 if ( ($r==0 and $g==0 and $b==0) || $g==-1 )
988 $this->form_border_color = sprintf('%.3F', $r/255);
989 else
990 $this->form_border_color = sprintf('%.3F %.3F %.3F', $r/255, $g/255, $b/255);
991}
992
993function SetFormBackgroundColor ( $r, $g=-1, $b=-1 ) {
994 if ( ($r==0 and $g==0 and $b==0) || $g==-1 )
995 $this->form_background_color = sprintf('%.3F', $r/255);
996 else
997 $this->form_background_color = sprintf('%.3F %.3F %.3F', $r/255, $g/255, $b/255);
998}
999
1000function SetFormD ( $W, $S, $BC, $BG ) {
1001 $this->SetFormBorderWidth ( $W );
1002 $this->SetFormBorderStyle ( $S );
1003 $this->SetFormBorderColor ( $BC );
1004 $this->SetFormBackgroundColor ( $BG );
1005}
1006
1007function _setflag( $array ) {
1008 $flag = 0;
1009 foreach($array as $val) { $flag += 1 << ($val-1); }
1010 return $flag;
1011}
1012
1013function _form_rect( $x, $y, $w, $h, $hPt ) {
1014 $x = $x * _MPDFK;
1015 $y = $hPt - ($y * _MPDFK);
1016 $x2 = $x + ($w * _MPDFK);
1017 $y2 = $y - ($h * _MPDFK);
1018 $rect = sprintf('%.3F %.3F %.3F %.3F', $x, $y2, $x2, $y );
1019 return $rect;
1020}
1021
1022
1023function _put_button_icon( $array , $w, $h ) {
1024 if (isset($array['image_id'])) {
1025 $info = false;
1026 foreach($this->mpdf->images AS $iid=>$img) {
1027 if ($img['i'] == $array['image_id']) {
1028 $info = $this->mpdf->images[$iid];
1029 break;
1030 }
1031 }
1032 }
1033 if (!$info) { die("Cannot find Button image"); }
1034 $this->mpdf->_newobj();
1035 $this->mpdf->_out('<<');
1036 $this->mpdf->_out('/Type /XObject');
1037 $this->mpdf->_out('/Subtype /Image');
1038 $this->mpdf->_out('/BBox [0 0 1 1]');
1039 $this->mpdf->_out('/Length '.strlen($info['data']));
1040 $this->mpdf->_out('/BitsPerComponent '.$info['bpc']);
1041 if ($info['cs']=='Indexed') {
1042 $this->mpdf->_out('/ColorSpace [/Indexed /DeviceRGB '.(strlen($info['pal'])/3-1).' '.($this->mpdf->n+1).' 0 R]');
1043 }
1044 else {
1045 $this->mpdf->_out('/ColorSpace /'.$info['cs']);
1046 if ($info['cs']=='DeviceCMYK')
1047 if($info['type']=='jpg') { $this->mpdf->_out('/Decode [1 0 1 0 1 0 1 0]'); }
1048 }
1049 if ( isset($info['f']) )
1050 $this->mpdf->_out('/Filter /'.$info['f']);
1051 if ( isset($info['parms']) )
1052 $this->mpdf->_out($info['parms']);
1053 $this->mpdf->_out('/Width '.$info['w']);
1054 $this->mpdf->_out('/Height '.$info['h']);
1055 $this->mpdf->_out('>>');
1056 $this->mpdf->_putstream($info['data']);
1057 $this->mpdf->_out('endobj');
1058 unset($array);
1059 //Palette
1060 if($info['cs']=='Indexed') {
1061 $filter=($this->mpdf->compress) ? '/Filter /FlateDecode ' : '';
1062 $this->mpdf->_newobj();
1063 $pal=($this->mpdf->compress) ? gzcompress($info['pal']) : $info['pal'];
1064 $this->mpdf->_out('<<'.$filter.'/Length '.strlen($pal).'>>');
1065 $this->mpdf->_putstream($pal);
1066 $this->mpdf->_out('endobj');
1067 }
1068
1069}
1070
1071
1072function _putform_bt( $form, $hPt ) {
1073 $cc = 0;
1074 $put_xobject = 0;
1075 $put_js = 0;
1076 $put_icon = 0;
1077 $this->mpdf->_newobj();
1078 $n = $this->mpdf->n;
1079 if ($form['subtype'] != 'radio') $this->pdf_acro_array .= $n.' 0 R '; // Add to /Field element
1080 $this->forms[ $form['n'] ]['obj'] = $n;
1081 $this->mpdf->_out('<<');
1082 $this->mpdf->_out('/Type /Annot ');
1083 $this->mpdf->_out('/Subtype /Widget');
1084 $this->mpdf->_out('/NM '.$this->mpdf->_textstring(sprintf('%04u-%04u', $n, (7000 + $form['n']))));
1085 $this->mpdf->_out('/M '.$this->mpdf->_textstring('D:'.date('YmdHis')));
1086 $this->mpdf->_out('/Rect [ '.$this->_form_rect($form['x'],$form['y'],$form['w'],$form['h'], $hPt).' ]');
1087 $form['noprint'] ? $this->mpdf->_out('/F 0 ') : $this->mpdf->_out('/F 4 ');
1088 $this->mpdf->_out('/FT /Btn ');
1089 $this->mpdf->_out('/H /P ');
1090 if ( $form['subtype'] != 'radio' ) // mPDF 5.3.23
1091 $this->mpdf->_out('/T '.$this->mpdf->_textstring($form['T']) );
1092 $this->mpdf->_out('/TU '.$this->mpdf->_textstring($form['TU']) );
1093 if ( isset( $this->form_button_icon[ $form['T'] ] ) ) { $form['BS_W'] = 0; }
1094 if ($form['BS_W'] == 0) { $form['BC_C'] = $form['BG_C']; }
1095 $bstemp = '';
1096 $bstemp .= '/W '.$form['BS_W'].' ';
1097 $bstemp .= '/S /'.$form['BS_S'].' ';
1098 $temp = '';
1099 $temp .= '/BC [ '.$form['BC_C']." ] ";
1100 $temp .= '/BG [ '.$form['BG_C']." ] ";
1101 if ( $form['subtype'] == 'checkbox' ) {
1102 if ($form['disabled']) {
1103 $radio_color = '0.5 0.5 0.5';
1104 $radio_background_color = '0.9 0.9 0.9';
1105 }
1106 else {
1107 $radio_color = $this->form_radio_color;
1108 $radio_background_color = $this->form_radio_background_color;
1109 }
1110 $temp = '';
1111 $temp .= '/BC [ '.$radio_color." ] ";
1112 $temp .= '/BG [ '.$radio_background_color." ] ";
1113 $this->mpdf->_out("/BS << /W 1 /S /S >>");
1114 $this->mpdf->_out("/MK << $temp >>");
1115 $this->mpdf->_out('/Ff '.$this->_setflag($form['FF']) );
1116 if ( $form['activ'] ) {
1117 $this->mpdf->_out('/V /'.$this->mpdf->_escape($form['V']).' ');
1118 $this->mpdf->_out('/DV /'.$this->mpdf->_escape($form['V']).' ');
1119 $this->mpdf->_out('/AS /'.$this->mpdf->_escape($form['V']).' ');
1120 } else {
1121 $this->mpdf->_out('/AS /Off ');
1122 }
1123 if ($this->formUseZapD) {
1124 $this->mpdf->_out('/DA (/F'.$this->mpdf->fonts['czapfdingbats']['i'].' 0 Tf '.$radio_color.' rg)');
1125 $this->mpdf->_out("/AP << /N << /".$this->mpdf->_escape($form['V'])." ".($this->mpdf->n+1)." 0 R /Off /Off >> >>");
1126 }
1127 else {
1128 $this->mpdf->_out('/DA (/F'.$this->mpdf->fonts[$this->mpdf->CurrentFont['fontkey']]['i'].' 0 Tf '.$radio_color.' rg)');
1129 $this->mpdf->_out("/AP << /N << /".$this->mpdf->_escape($form['V'])." ".($this->mpdf->n+1)." 0 R /Off ".($this->mpdf->n+2)." 0 R >> >>");
1130 }
1131 $this->mpdf->_out('/Opt [ '.$this->mpdf->_textstring($form['OPT']).' '.$this->mpdf->_textstring($form['OPT']).' ]');
1132 }
1133
1134
1135 if ( $form['subtype'] == 'radio' ) {
1136 if ($form['disabled'] || $this->form_radio_groups[$form['T']]['disabled']) {
1137 $radio_color = '0.5 0.5 0.5';
1138 $radio_background_color = '0.9 0.9 0.9';
1139 }
1140 else {
1141 $radio_color = $this->form_radio_color;
1142 $radio_background_color = $this->form_radio_background_color;
1143 }
1144 $this->mpdf->_out('/Parent '.$this->form_radio_groups[$form['T']]['obj_id'].' 0 R ');
1145 $temp = '';
1146 $temp .= '/BC [ '.$radio_color." ] ";
1147 $temp .= '/BG [ '.$radio_background_color." ] ";
1148 $this->mpdf->_out("/BS << /W 1 /S /S >>");
1149 $this->mpdf->_out('/MK << '.$temp.' >> ');
1150 $form['FF'][] = 16; // Radiobutton
1151 $form['FF'][] = 15; // NoToggleOff - must be same as radio button group setting?
1152 $this->mpdf->_out('/Ff '.$this->_setflag($form['FF']) );
1153 if ($this->formUseZapD)
1154 $this->mpdf->_out('/DA (/F'.$this->mpdf->fonts['czapfdingbats']['i'].' 0 Tf '.$radio_color.' rg)');
1155 else
1156 $this->mpdf->_out('/DA (/F'.$this->mpdf->fonts[$this->mpdf->CurrentFont['fontkey']]['i'].' 0 Tf '.$radio_color.' rg)');
1157 $this->mpdf->_out("/AP << /N << /".$this->mpdf->_escape($form['V'])." ".($this->mpdf->n+1)." 0 R /Off ".($this->mpdf->n+2)." 0 R >> >>");
1158 if ( $form['activ'] ) {
1159 $this->mpdf->_out('/V /'.$this->mpdf->_escape($form['V']).' ');
1160 $this->mpdf->_out('/DV /'.$this->mpdf->_escape($form['V']).' ');
1161 $this->mpdf->_out('/AS /'.$this->mpdf->_escape($form['V']).' ');
1162 }
1163 else {
1164 $this->mpdf->_out('/AS /Off ');
1165 }
1166 $this->mpdf->_out("/AP << /N << /".$this->mpdf->_escape($form['V'])." ".($this->mpdf->n+1)." 0 R /Off ".($this->mpdf->n+2)." 0 R >> >>");
1167 // $this->mpdf->_out('/Opt [ '.$this->mpdf->_textstring($form['OPT']).' '.$this->mpdf->_textstring($form['OPT']).' ]');
1168 }
1169
1170 if ( $form['subtype'] == 'reset' ) {
1171 $temp .= $form['CA'] ? '/CA '.$this->mpdf->_textstring($form['CA']).' ' : '/CA '.$this->mpdf->_textstring($form['T']).' ';
1172 $temp .= $form['RC'] ? '/RC '.$this->mpdf->_textstring($form['RC']).' ' : '/RC '.$this->mpdf->_textstring($form['T']).' ';
1173 $temp .= $form['AC'] ? '/AC '.$this->mpdf->_textstring($form['AC']).' ' : '/AC '.$this->mpdf->_textstring($form['T']).' ';
1174 $this->mpdf->_out("/BS << $bstemp >>");
1175 $this->mpdf->_out('/MK << '.$temp.' >>');
1176 $this->mpdf->_out('/DA (/F'.$this->mpdf->fonts[$form['style']['font']]['i'].' '.$form['style']['fontsize'].' Tf '.$form['style']['fontcolor'].')');
1177 $this->mpdf->_out('/AA << /D << /S /ResetForm /Flags 1 >> >>');
1178 $form['FF'][] = 17;
1179 $this->mpdf->_out('/Ff '.$this->_setflag($form['FF']) );
1180 }
1181
1182
1183 if ( $form['subtype'] == 'submit' ) {
1184 $temp .= $form['CA'] ? '/CA '.$this->mpdf->_textstring($form['CA']).' ' : '/CA '.$this->mpdf->_textstring($form['T']).' ';
1185 $temp .= $form['RC'] ? '/RC '.$this->mpdf->_textstring($form['RC']).' ' : '/RC '.$this->mpdf->_textstring($form['T']).' ';
1186 $temp .= $form['AC'] ? '/AC '.$this->mpdf->_textstring($form['AC']).' ' : '/AC '.$this->mpdf->_textstring($form['T']).' ';
1187 $this->mpdf->_out("/BS << $bstemp >>");
1188 $this->mpdf->_out("/MK << $temp >>");
1189 $this->mpdf->_out('/DA (/F'.$this->mpdf->fonts[$form['style']['font']]['i'].' '.$form['style']['fontsize'].' Tf '.$form['style']['fontcolor'].')');
1190 // Bit 4 (8) = useGETmethod else use POST
1191 // Bit 3 (4) = HTML export format (charset chosen by Adobe)--- OR ---
1192 // Bit 6 (32) = XFDF export format (form of XML in UTF-8)
1193 if ($form['exporttype'] == 'xfdf') { $flag = 32; } // 'xfdf' or 'html'
1194 else {
1195 if ($form['method'] == 'GET') { $flag = 12; }
1196 else { $flag = 4; }
1197 }
1198 // Bit 2 (2) = IncludeNoValueFields
1199 if ($this->formSubmitNoValueFields) $flag += 2;
1200 // To submit a value, needs to be in /AP dictionary, AND this object must contain a /Fields entry
1201 // listing all fields to output
1202 $this->mpdf->_out('/AA << /D << /S /SubmitForm /F ('.$form['URL'].') /Flags '.$flag.' >> >>');
1203 $form['FF'][] = 17;
1204 $this->mpdf->_out('/Ff '.$this->_setflag($form['FF']) );
1205 }
1206
1207 if ( $form['subtype'] == 'js_button' ) {
1208 // Icon / image
1209 if ( isset( $this->form_button_icon[ $form['T'] ] ) ) {
1210 $cc++;
1211 $temp .= '/TP '.$this->form_button_icon[$form['T']]['pos'].' ';
1212 $temp .= '/I '.($cc + $this->mpdf->n).' 0 R '; // Normal icon
1213 $temp .= '/RI '.($cc + $this->mpdf->n).' 0 R '; // onMouseOver
1214 $temp .= '/IX '.($cc + $this->mpdf->n).' 0 R '; // onClick / onMouseDown
1215 $temp .= '/IF << /SW /A /S /A /A [0.0 0.0] >> '; // Icon fit dictionary
1216 if ($this->form_button_icon[ $form['T'] ]['Indexed']) { $cc++; }
1217 $put_icon = 1;
1218 }
1219 $temp .= $form['CA'] ? '/CA '.$this->mpdf->_textstring($form['CA']).' ' : '/CA '.$this->mpdf->_textstring($form['T']).' ';
1220 $temp .= $form['RC'] ? '/RC '.$this->mpdf->_textstring($form['RC']).' ' : '/RC '.$this->mpdf->_textstring($form['T']).' ';
1221 $temp .= $form['AC'] ? '/AC '.$this->mpdf->_textstring($form['AC']).' ' : '/AC '.$this->mpdf->_textstring($form['T']).' ';
1222 $this->mpdf->_out("/BS << $bstemp >>");
1223 $this->mpdf->_out("/MK << $temp >>");
1224 $this->mpdf->_out('/DA (/F'.$this->mpdf->fonts[$form['style']['font']]['i'].' '.$form['style']['fontsize'].' Tf '.$form['style']['fontcolor'].')');
1225 $form['FF'][] = 17;
1226 $this->mpdf->_out('/Ff '.$this->_setflag($form['FF']) );
1227 // Javascript
1228 if ( isset($this->array_form_button_js[$form['T']]) ) {
1229 $cc++;
1230 $this->mpdf->_out("/AA << /D ".($cc + $this->mpdf->n)." 0 R >>");
1231 $put_js = 1;
1232 }
1233 }
1234
1235 $this->mpdf->_out('>>');
1236 $this->mpdf->_out('endobj');
1237
1238 // additional objects
1239 // obj icon
1240 if ( $put_icon == 1 ) {
1241 $this->_put_button_icon( $this->form_button_icon[ $form['T'] ], $form['w'], $form['h'] );
1242 $put_icon = NULL;
1243 }
1244 // obj + 1
1245 if ( $put_js == 1 ) {
1246 $this->mpdf->_set_object_javascript( $this->array_form_button_js[$form['T']]['js'] );
1247 unset( $this->array_form_button_js[$form['T']] );
1248 $put_js = NULL;
1249 }
1250
1251 // RADIO and CHECK BOX appearance streams
1252 $filter=($this->mpdf->compress) ? '/Filter /FlateDecode ' : '';
1253 if ( $form['subtype'] == 'radio' ) {
1254 // output 2 appearance streams for radio buttons on/off
1255 if ($this->formUseZapD) {
1256 $fs = sprintf('%.3F', $form['style']['fontsize']*1.25);
1257 $fi = 'czapfdingbats';
1258 $r_on = 'q '.$radio_color .' rg BT /F'.$this->mpdf->fonts[$fi]['i'].' '.$fs.' Tf 0 0 Td (4) Tj ET Q';
1259 $r_off = 'q '.$radio_color .' rg BT /F'.$this->mpdf->fonts[$fi]['i'].' '.$fs.' Tf 0 0 Td (8) Tj ET Q';
1260 }
1261 else {
1262 $matrix = sprintf('%.3F 0 0 %.3F 0 %.3F', $form['style']['fontsize']*1.33/10, $form['style']['fontsize']*1.25/10, $form['style']['fontsize']);
1263 $fill = $radio_background_color.' rg 3.778 -7.410 m 2.800 -7.410 1.947 -7.047 1.225 -6.322 c 0.500 -5.600 0.138 -4.747 0.138 -3.769 c 0.138 -2.788 0.500 -1.938 1.225 -1.213 c 1.947 -0.491 2.800 -0.128 3.778 -0.128 c 4.757 -0.128 5.610 -0.491 6.334 -1.213 c 7.056 -1.938 7.419 -2.788 7.419 -3.769 c 7.419 -4.747 7.056 -5.600 6.334 -6.322 c 5.610 -7.047 4.757 -7.410 3.778 -7.410 c h f ';
1264 $circle = '3.778 -6.963 m 4.631 -6.963 5.375 -6.641 6.013 -6.004 c 6.653 -5.366 6.972 -4.619 6.972 -3.769 c 6.972 -2.916 6.653 -2.172 6.013 -1.532 c 5.375 -0.894 4.631 -0.576 3.778 -0.576 c 2.928 -0.576 2.182 -0.894 1.544 -1.532 c 0.904 -2.172 0.585 -2.916 0.585 -3.769 c 0.585 -4.619 0.904 -5.366 1.544 -6.004 c 2.182 -6.641 2.928 -6.963 3.778 -6.963 c h 3.778 -7.410 m 2.800 -7.410 1.947 -7.047 1.225 -6.322 c 0.500 -5.600 0.138 -4.747 0.138 -3.769 c 0.138 -2.788 0.500 -1.938 1.225 -1.213 c 1.947 -0.491 2.800 -0.128 3.778 -0.128 c 4.757 -0.128 5.610 -0.491 6.334 -1.213 c 7.056 -1.938 7.419 -2.788 7.419 -3.769 c 7.419 -4.747 7.056 -5.600 6.334 -6.322 c 5.610 -7.047 4.757 -7.410 3.778 -7.410 c h f ';
1265 $r_on = 'q '.$matrix.' cm '.$fill .$radio_color.' rg '.$circle.' '.$radio_color.' rg
12665.184 -5.110 m 4.800 -5.494 4.354 -5.685 3.841 -5.685 c 3.331 -5.685 2.885 -5.494 2.501 -5.110 c 2.119 -4.725 1.925 -4.279 1.925 -3.769 c 1.925 -3.257 2.119 -2.810 2.501 -2.429 c 2.885 -2.044 3.331 -1.853 3.841 -1.853 c 4.354 -1.853 4.800 -2.044 5.184 -2.429 c 5.566 -2.810 5.760 -3.257 5.760 -3.769 c 5.760 -4.279 5.566 -4.725 5.184 -5.110 c h
1267f Q ';
1268 $r_off = 'q '.$matrix.' cm '.$fill .$radio_color.' rg '.$circle.' Q ';
1269 }
1270
1271 $this->mpdf->_newobj();
1272 $p=($this->mpdf->compress) ? gzcompress($r_on) : $r_on;
1273 $this->mpdf->_out('<<'.$filter.'/Length '.strlen($p).' /Resources 2 0 R>>');
1274 $this->mpdf->_putstream($p);
1275 $this->mpdf->_out('endobj');
1276
1277 $this->mpdf->_newobj();
1278 $p=($this->mpdf->compress) ? gzcompress($r_off) : $r_off;
1279 $this->mpdf->_out('<<'.$filter.'/Length '.strlen($p).' /Resources 2 0 R>>');
1280 $this->mpdf->_putstream($p);
1281 $this->mpdf->_out('endobj');
1282 }
1283 if ( $form['subtype'] == 'checkbox' ) {
1284 // First output appearance stream for check box on
1285 if ($this->formUseZapD) {
1286 $fs = sprintf('%.3F', $form['style']['fontsize']*1.25);
1287 $fi = 'czapfdingbats';
1288 $cb_on = 'q '.$radio_color .' rg BT /F'.$this->mpdf->fonts[$fi]['i'].' '.$fs.' Tf 0 0 Td (4) Tj ET Q';
1289 $cb_off = 'q '.$radio_color .' rg BT /F'.$this->mpdf->fonts[$fi]['i'].' '.$fs.' Tf 0 0 Td (8) Tj ET Q';
1290 }
1291 else {
1292 $matrix = sprintf('%.3F 0 0 %.3F 0 %.3F', $form['style']['fontsize']*1.33/10, $form['style']['fontsize']*1.25/10, $form['style']['fontsize']);
1293 $fill = $radio_background_color.' rg 7.395 -0.070 m 7.395 -7.344 l 0.121 -7.344 l 0.121 -0.070 l 7.395 -0.070 l h f ';
1294 $square = '0.508 -6.880 m 6.969 -6.880 l 6.969 -0.534 l 0.508 -0.534 l 0.508 -6.880 l h 7.395 -0.070 m 7.395 -7.344 l 0.121 -7.344 l 0.121 -0.070 l 7.395 -0.070 l h ';
1295 $cb_on = 'q '.$matrix.' cm '.$fill. $radio_color.' rg '.$square.' f '.$radio_color.' rg
12966.321 -1.352 m 5.669 -2.075 5.070 -2.801 4.525 -3.532 c 3.979 -4.262 3.508 -4.967 3.112 -5.649 c 3.080 -5.706 3.039 -5.779 2.993 -5.868 c 2.858 -6.118 2.638 -6.243 2.334 -6.243 c 2.194 -6.243 2.100 -6.231 2.052 -6.205 c 2.003 -6.180 1.954 -6.118 1.904 -6.020 c 1.787 -5.788 1.688 -5.523 1.604 -5.226 c 1.521 -4.930 1.480 -4.721 1.480 -4.600 c 1.480 -4.535 1.491 -4.484 1.512 -4.447 c 1.535 -4.410 1.579 -4.367 1.647 -4.319 c 1.733 -4.259 1.828 -4.210 1.935 -4.172 c 2.040 -4.134 2.131 -4.115 2.205 -4.115 c 2.267 -4.115 2.341 -4.232 2.429 -4.469 c 2.437 -4.494 2.444 -4.511 2.448 -4.522 c 2.451 -4.531 2.456 -4.546 2.465 -4.568 c 2.546 -4.795 2.614 -4.910 2.668 -4.910 c 2.714 -4.910 2.898 -4.652 3.219 -4.136 c 3.539 -3.620 3.866 -3.136 4.197 -2.683 c 4.426 -2.367 4.633 -2.103 4.816 -1.889 c 4.998 -1.676 5.131 -1.544 5.211 -1.493 c 5.329 -1.426 5.483 -1.368 5.670 -1.319 c 5.856 -1.271 6.066 -1.238 6.296 -1.217 c 6.321 -1.352 l h f Q ';
1297 $cb_off = 'q '.$matrix.' cm '.$fill. $radio_color.' rg '.$square.' f Q ';
1298
1299 }
1300 $this->mpdf->_newobj();
1301 $p=($this->mpdf->compress) ? gzcompress($cb_on) : $cb_on;
1302 $this->mpdf->_out('<<'.$filter.'/Length '.strlen($p).' /Resources 2 0 R>>');
1303 $this->mpdf->_putstream($p);
1304 $this->mpdf->_out('endobj');
1305
1306 // output appearance stream for check box off (only if not using ZapfDingbats)
1307 if (!$this->formUseZapD) {
1308 $this->mpdf->_newobj();
1309 $p=($this->mpdf->compress) ? gzcompress($cb_off) : $cb_off;
1310 $this->mpdf->_out('<<'.$filter.'/Length '.strlen($p).' /Resources 2 0 R>>');
1311 $this->mpdf->_putstream($p);
1312 $this->mpdf->_out('endobj');
1313 }
1314
1315 }
1316 return $n;
1317}
1318
1319
1320function _putform_ch( $form, $hPt ) {
1321 $put_js = 0;
1322 $this->mpdf->_newobj();
1323 $n = $this->mpdf->n;
1324 $this->pdf_acro_array .= $n.' 0 R ';
1325 $this->forms[ $form['n'] ]['obj'] = $n;
1326
1327 $this->mpdf->_out('<<');
1328 $this->mpdf->_out('/Type /Annot ');
1329 $this->mpdf->_out('/Subtype /Widget');
1330 $this->mpdf->_out('/Rect [ '.$this->_form_rect($form['x'],$form['y'],$form['w'],$form['h'], $hPt).' ]');
1331 $this->mpdf->_out('/F 4');
1332 $this->mpdf->_out('/FT /Ch');
1333 if ($form['Q']) $this->mpdf->_out('/Q '.$form['Q'].'');
1334 $temp = '';
1335 $temp .= '/W '.$form['BS_W'].' ';
1336 $temp .= '/S /'.$form['BS_S'].' ';
1337 $this->mpdf->_out("/BS << $temp >>");
1338
1339 $temp = '';
1340 $temp .= '/BC [ '.$form['BC_C']." ] ";
1341 $temp .= '/BG [ '.$form['BG_C']." ] ";
1342 $this->mpdf->_out('/MK << '.$temp.' >>');
1343
1344 $this->mpdf->_out('/NM '.$this->mpdf->_textstring(sprintf('%04u-%04u', $n, (6000 + $form['n']))));
1345 $this->mpdf->_out('/M '.$this->mpdf->_textstring('D:'.date('YmdHis')));
1346
1347 $this->mpdf->_out('/T '.$this->mpdf->_textstring($form['T']) );
1348 $this->mpdf->_out('/DA (/F'.$this->mpdf->fonts[$form['style']['font']]['i'].' '.$form['style']['fontsize'].' Tf '.$form['style']['fontcolor'].')');
1349
1350 $opt = '';
1351 for( $i = 0; $i < count($form['OPT']['VAL']) ; $i++ ) {
1352 $opt .= '[ '.$this->mpdf->_textstring($form['OPT']['VAL'][$i]).' '.$this->mpdf->_textstring($form['OPT']['OPT'][$i]).' ] ';
1353 }
1354 $this->mpdf->_out('/Opt [ '.$opt.']');
1355
1356 // selected
1357 $selectItem = false;
1358 $selectIndex = false;
1359 foreach ( $form['OPT']['SEL'] as $selectKey => $selectVal ) {
1360 $selectName = $this->mpdf->_textstring($form['OPT']['VAL'][$selectVal]);
1361 $selectItem .= ' '.$selectName.' ';
1362 $selectIndex .= ' '.$selectVal.' ';
1363 }
1364 if ( $selectItem ) {
1365 if (count($form['OPT']['SEL']) < 2) {
1366 $this->mpdf->_out('/V '.$selectItem.' ');
1367 $this->mpdf->_out('/DV '.$selectItem.' ');
1368 }
1369 else {
1370 $this->mpdf->_out('/V ['.$selectItem.'] ');
1371 $this->mpdf->_out('/DV ['.$selectItem.'] ');
1372 }
1373 $this->mpdf->_out('/I ['.$selectIndex.'] ');
1374 }
1375
1376 if ( is_array($form['FF']) && count($form['FF'])>0 ) {
1377 $this->mpdf->_out('/Ff '.$this->_setflag($form['FF']).' ');
1378 }
1379 // Javascript
1380 if ( isset($this->array_form_choice_js[$form['T']]) ) {
1381 $this->mpdf->_out("/AA << /V ".($this->mpdf->n+1)." 0 R >>");
1382 $put_js = 1;
1383 }
1384
1385 $this->mpdf->_out('>>');
1386 $this->mpdf->_out('endobj');
1387 // obj + 1
1388 if ( $put_js == 1 ) {
1389 $this->mpdf->_set_object_javascript( $this->array_form_choice_js[$form['T']]['js'] );
1390 unset( $this->array_form_choice_js[$form['T']] );
1391 $put_js = NULL;
1392 }
1393
1394 return $n;
1395}
1396
1397
1398function _putform_tx( $form, $hPt ) {
1399 $put_js = 0;
1400 $this->mpdf->_newobj();
1401 $n = $this->mpdf->n;
1402 $this->pdf_acro_array .= $n.' 0 R ';
1403 $this->forms[ $form['n'] ]['obj'] = $n;
1404
1405 $this->mpdf->_out('<<');
1406 $this->mpdf->_out('/Type /Annot ');
1407 $this->mpdf->_out('/Subtype /Widget ');
1408
1409 $this->mpdf->_out('/Rect [ '.$this->_form_rect($form['x'],$form['y'],$form['w'],$form['h'], $hPt).' ] ');
1410 $form['hidden'] ? $this->mpdf->_out('/F 2 ') : $this->mpdf->_out('/F 4 ');
1411 $this->mpdf->_out('/FT /Tx ');
1412
1413 $this->mpdf->_out('/H /N ');
1414 $this->mpdf->_out('/R 0 ');
1415
1416 if ( is_array($form['FF']) && count($form['FF'])>0 ) {
1417 $this->mpdf->_out('/Ff '.$this->_setflag($form['FF']).' ');
1418 }
1419 if ( isset($form['maxlen']) && $form['maxlen']>0 ) {
1420 $this->mpdf->_out('/MaxLen '.$form['maxlen']);
1421 }
1422
1423 $temp = '';
1424 $temp .= '/W '.$form['BS_W'].' ';
1425 $temp .= '/S /'.$form['BS_S'].' ';
1426 $this->mpdf->_out("/BS << $temp >>");
1427
1428 $temp = '';
1429 $temp .= '/BC [ '.$form['BC_C']." ] ";
1430 $temp .= '/BG [ '.$form['BG_C']." ] ";
1431 $this->mpdf->_out('/MK <<'.$temp.' >>');
1432
1433 $this->mpdf->_out('/T '.$this->mpdf->_textstring($form['T']) );
1434 $this->mpdf->_out('/TU '.$this->mpdf->_textstring($form['TU']) );
1435 if ($form['V'] || $form['V']==='0')
1436 $this->mpdf->_out('/V '.$this->mpdf->_textstring($form['V']) );
1437 $this->mpdf->_out('/DV '.$this->mpdf->_textstring($form['DV']) );
1438 $this->mpdf->_out('/DA (/F'.$this->mpdf->fonts[$form['style']['font']]['i'].' '.$form['style']['fontsize'].' Tf '.$form['style']['fontcolor'].')');
1439 if ( $form['Q'] ) $this->mpdf->_out('/Q '.$form['Q'].'');
1440
1441 $this->mpdf->_out('/NM '.$this->mpdf->_textstring(sprintf('%04u-%04u', $n, (5000 + $form['n']))));
1442 $this->mpdf->_out('/M '.$this->mpdf->_textstring('D:'.date('YmdHis')));
1443
1444
1445 if ( isset($this->array_form_text_js[$form['T']]) ) {
1446 $put_js = 1;
1447 $cc = 0;
1448 $js_str = '';
1449
1450 if ( isset($this->array_form_text_js[$form['T']]['F']) ) {
1451 $cc++;
1452 $js_str .= '/F '.($cc + $this->mpdf->n).' 0 R ';
1453 }
1454 if ( isset($this->array_form_text_js[$form['T']]['K']) ) {
1455 $cc++;
1456 $js_str .= '/K '.($cc + $this->mpdf->n).' 0 R ';
1457 }
1458 if ( isset($this->array_form_text_js[$form['T']]['V']) ) {
1459 $cc++;
1460 $js_str .= '/V '.($cc + $this->mpdf->n).' 0 R ';
1461 }
1462 if ( isset($this->array_form_text_js[$form['T']]['C']) ) {
1463 $cc++;
1464 $js_str .= '/C '.($cc + $this->mpdf->n).' 0 R ';
1465 $this->pdf_array_co .= $this->mpdf->n.' 0 R ';
1466 }
1467 $this->mpdf->_out('/AA << '.$js_str.' >>');
1468 }
1469
1470 $this->mpdf->_out('>>');
1471 $this->mpdf->_out('endobj');
1472
1473 if ( $put_js == 1 ) {
1474 if ( isset($this->array_form_text_js[$form['T']]['F']) ) {
1475 $this->mpdf->_set_object_javascript( $this->array_form_text_js[$form['T']]['F']['js'] );
1476 unset( $this->array_form_text_js[$form['T']]['F'] );
1477 }
1478 if ( isset($this->array_form_text_js[$form['T']]['K']) ) {
1479 $this->mpdf->_set_object_javascript( $this->array_form_text_js[$form['T']]['K']['js'] );
1480 unset( $this->array_form_text_js[$form['T']]['K'] );
1481 }
1482 if ( isset($this->array_form_text_js[$form['T']]['V']) ) {
1483 $this->mpdf->_set_object_javascript( $this->array_form_text_js[$form['T']]['V']['js'] );
1484 unset( $this->array_form_text_js[$form['T']]['V'] );
1485 }
1486 if ( isset($this->array_form_text_js[$form['T']]['C']) ) {
1487 $this->mpdf->_set_object_javascript( $this->array_form_text_js[$form['T']]['C']['js'] );
1488 unset( $this->array_form_text_js[$form['T']]['C'] );
1489 }
1490 }
1491 return $n;
1492}
1493
1494
1495
1496}
1497
1498?> \ No newline at end of file
diff --git a/inc/3rdparty/libraries/mpdf/classes/gif.php b/inc/3rdparty/libraries/mpdf/classes/gif.php
new file mode 100644
index 00000000..263513e2
--- /dev/null
+++ b/inc/3rdparty/libraries/mpdf/classes/gif.php
@@ -0,0 +1,700 @@
1<?php
2///////////////////////////////////////////////////////////////////////////////////////////////////
3// 2009-12-22 Adapted for mPDF 4.2
4///////////////////////////////////////////////////////////////////////////////////////////////////
5// GIF Util - (C) 2003 Yamasoft (S/C)
6// http://www.yamasoft.com
7// All Rights Reserved
8// This file can be freely copied, distributed, modified, updated by anyone under the only
9// condition to leave the original address (Yamasoft, http://www.yamasoft.com) and this header.
10///////////////////////////////////////////////////////////////////////////////////////////////////
11///////////////////////////////////////////////////////////////////////////////////////////////////
12// 2009-12-22 Adapted INB
13// Functions calling functionname($x, $len = 0) were not working on PHP5.1.5 as pass by reference
14// All edited to $len = 0; then call function.
15///////////////////////////////////////////////////////////////////////////////////////////////////
16
17
18///////////////////////////////////////////////////////////////////////////////////////////////////
19
20class CGIFLZW
21{
22 var $MAX_LZW_BITS;
23 var $Fresh, $CodeSize, $SetCodeSize, $MaxCode, $MaxCodeSize, $FirstCode, $OldCode;
24 var $ClearCode, $EndCode, $Next, $Vals, $Stack, $sp, $Buf, $CurBit, $LastBit, $Done, $LastByte;
25
26 ///////////////////////////////////////////////////////////////////////////
27
28 // CONSTRUCTOR
29 function CGIFLZW()
30 {
31 $this->MAX_LZW_BITS = 12;
32 unSet($this->Next);
33 unSet($this->Vals);
34 unSet($this->Stack);
35 unSet($this->Buf);
36
37 $this->Next = range(0, (1 << $this->MAX_LZW_BITS) - 1);
38 $this->Vals = range(0, (1 << $this->MAX_LZW_BITS) - 1);
39 $this->Stack = range(0, (1 << ($this->MAX_LZW_BITS + 1)) - 1);
40 $this->Buf = range(0, 279);
41 }
42
43 ///////////////////////////////////////////////////////////////////////////
44
45 function deCompress($data, &$datLen)
46 {
47 $stLen = strlen($data);
48 $datLen = 0;
49 $ret = "";
50 $dp = 0; // data pointer
51
52 // INITIALIZATION
53 $this->LZWCommandInit($data, $dp);
54
55 while(($iIndex = $this->LZWCommand($data, $dp)) >= 0) {
56 $ret .= chr($iIndex);
57 }
58
59 $datLen = $dp;
60
61 if($iIndex != -2) {
62 return false;
63 }
64
65 return $ret;
66 }
67
68 ///////////////////////////////////////////////////////////////////////////
69 function LZWCommandInit(&$data, &$dp)
70 {
71 $this->SetCodeSize = ord($data[0]);
72 $dp += 1;
73
74 $this->CodeSize = $this->SetCodeSize + 1;
75 $this->ClearCode = 1 << $this->SetCodeSize;
76 $this->EndCode = $this->ClearCode + 1;
77 $this->MaxCode = $this->ClearCode + 2;
78 $this->MaxCodeSize = $this->ClearCode << 1;
79
80 $this->GetCodeInit($data, $dp);
81
82 $this->Fresh = 1;
83 for($i = 0; $i < $this->ClearCode; $i++) {
84 $this->Next[$i] = 0;
85 $this->Vals[$i] = $i;
86 }
87
88 for(; $i < (1 << $this->MAX_LZW_BITS); $i++) {
89 $this->Next[$i] = 0;
90 $this->Vals[$i] = 0;
91 }
92
93 $this->sp = 0;
94 return 1;
95 }
96
97 function LZWCommand(&$data, &$dp)
98 {
99 if($this->Fresh) {
100 $this->Fresh = 0;
101 do {
102 $this->FirstCode = $this->GetCode($data, $dp);
103 $this->OldCode = $this->FirstCode;
104 }
105 while($this->FirstCode == $this->ClearCode);
106
107 return $this->FirstCode;
108 }
109
110 if($this->sp > 0) {
111 $this->sp--;
112 return $this->Stack[$this->sp];
113 }
114
115 while(($Code = $this->GetCode($data, $dp)) >= 0) {
116 if($Code == $this->ClearCode) {
117 for($i = 0; $i < $this->ClearCode; $i++) {
118 $this->Next[$i] = 0;
119 $this->Vals[$i] = $i;
120 }
121
122 for(; $i < (1 << $this->MAX_LZW_BITS); $i++) {
123 $this->Next[$i] = 0;
124 $this->Vals[$i] = 0;
125 }
126
127 $this->CodeSize = $this->SetCodeSize + 1;
128 $this->MaxCodeSize = $this->ClearCode << 1;
129 $this->MaxCode = $this->ClearCode + 2;
130 $this->sp = 0;
131 $this->FirstCode = $this->GetCode($data, $dp);
132 $this->OldCode = $this->FirstCode;
133
134 return $this->FirstCode;
135 }
136
137 if($Code == $this->EndCode) {
138 return -2;
139 }
140
141 $InCode = $Code;
142 if($Code >= $this->MaxCode) {
143 $this->Stack[$this->sp++] = $this->FirstCode;
144 $Code = $this->OldCode;
145 }
146
147 while($Code >= $this->ClearCode) {
148 $this->Stack[$this->sp++] = $this->Vals[$Code];
149
150 if($Code == $this->Next[$Code]) // Circular table entry, big GIF Error!
151 return -1;
152
153 $Code = $this->Next[$Code];
154 }
155
156 $this->FirstCode = $this->Vals[$Code];
157 $this->Stack[$this->sp++] = $this->FirstCode;
158
159 if(($Code = $this->MaxCode) < (1 << $this->MAX_LZW_BITS)) {
160 $this->Next[$Code] = $this->OldCode;
161 $this->Vals[$Code] = $this->FirstCode;
162 $this->MaxCode++;
163
164 if(($this->MaxCode >= $this->MaxCodeSize) && ($this->MaxCodeSize < (1 << $this->MAX_LZW_BITS))) {
165 $this->MaxCodeSize *= 2;
166 $this->CodeSize++;
167 }
168 }
169
170 $this->OldCode = $InCode;
171 if($this->sp > 0) {
172 $this->sp--;
173 return $this->Stack[$this->sp];
174 }
175 }
176
177 return $Code;
178 }
179
180 ///////////////////////////////////////////////////////////////////////////
181
182 function GetCodeInit(&$data, &$dp)
183 {
184 $this->CurBit = 0;
185 $this->LastBit = 0;
186 $this->Done = 0;
187 $this->LastByte = 2;
188 return 1;
189 }
190
191 function GetCode(&$data, &$dp)
192 {
193 if(($this->CurBit + $this->CodeSize) >= $this->LastBit) {
194 if($this->Done) {
195 if($this->CurBit >= $this->LastBit) {
196 // Ran off the end of my bits
197 return 0;
198 }
199 return -1;
200 }
201
202 $this->Buf[0] = $this->Buf[$this->LastByte - 2];
203 $this->Buf[1] = $this->Buf[$this->LastByte - 1];
204
205 $Count = ord($data[$dp]);
206 $dp += 1;
207
208 if($Count) {
209 for($i = 0; $i < $Count; $i++) {
210 $this->Buf[2 + $i] = ord($data[$dp+$i]);
211 }
212 $dp += $Count;
213 }
214 else {
215 $this->Done = 1;
216 }
217
218 $this->LastByte = 2 + $Count;
219 $this->CurBit = ($this->CurBit - $this->LastBit) + 16;
220 $this->LastBit = (2 + $Count) << 3;
221 }
222
223 $iRet = 0;
224 for($i = $this->CurBit, $j = 0; $j < $this->CodeSize; $i++, $j++) {
225 $iRet |= (($this->Buf[intval($i / 8)] & (1 << ($i % 8))) != 0) << $j;
226 }
227
228 $this->CurBit += $this->CodeSize;
229 return $iRet;
230 }
231}
232
233///////////////////////////////////////////////////////////////////////////////////////////////////
234
235class CGIFCOLORTABLE
236{
237 var $m_nColors;
238 var $m_arColors;
239
240 ///////////////////////////////////////////////////////////////////////////
241
242 // CONSTRUCTOR
243 function CGIFCOLORTABLE()
244 {
245 unSet($this->m_nColors);
246 unSet($this->m_arColors);
247 }
248
249 ///////////////////////////////////////////////////////////////////////////
250
251 function load($lpData, $num)
252 {
253 $this->m_nColors = 0;
254 $this->m_arColors = array();
255
256 for($i = 0; $i < $num; $i++) {
257 $rgb = substr($lpData, $i * 3, 3);
258 if(strlen($rgb) < 3) {
259 return false;
260 }
261
262 $this->m_arColors[] = (ord($rgb[2]) << 16) + (ord($rgb[1]) << 8) + ord($rgb[0]);
263 $this->m_nColors++;
264 }
265
266 return true;
267 }
268
269 ///////////////////////////////////////////////////////////////////////////
270
271 function toString()
272 {
273 $ret = "";
274
275 for($i = 0; $i < $this->m_nColors; $i++) {
276 $ret .=
277 chr(($this->m_arColors[$i] & 0x000000FF)) . // R
278 chr(($this->m_arColors[$i] & 0x0000FF00) >> 8) . // G
279 chr(($this->m_arColors[$i] & 0x00FF0000) >> 16); // B
280 }
281
282 return $ret;
283 }
284
285
286 ///////////////////////////////////////////////////////////////////////////
287
288 function colorIndex($rgb)
289 {
290 $rgb = intval($rgb) & 0xFFFFFF;
291 $r1 = ($rgb & 0x0000FF);
292 $g1 = ($rgb & 0x00FF00) >> 8;
293 $b1 = ($rgb & 0xFF0000) >> 16;
294 $idx = -1;
295
296 for($i = 0; $i < $this->m_nColors; $i++) {
297 $r2 = ($this->m_arColors[$i] & 0x000000FF);
298 $g2 = ($this->m_arColors[$i] & 0x0000FF00) >> 8;
299 $b2 = ($this->m_arColors[$i] & 0x00FF0000) >> 16;
300 $d = abs($r2 - $r1) + abs($g2 - $g1) + abs($b2 - $b1);
301
302 if(($idx == -1) || ($d < $dif)) {
303 $idx = $i;
304 $dif = $d;
305 }
306 }
307
308 return $idx;
309 }
310}
311
312///////////////////////////////////////////////////////////////////////////////////////////////////
313
314class CGIFFILEHEADER
315{
316 var $m_lpVer;
317 var $m_nWidth;
318 var $m_nHeight;
319 var $m_bGlobalClr;
320 var $m_nColorRes;
321 var $m_bSorted;
322 var $m_nTableSize;
323 var $m_nBgColor;
324 var $m_nPixelRatio;
325 var $m_colorTable;
326
327 ///////////////////////////////////////////////////////////////////////////
328
329 // CONSTRUCTOR
330 function CGIFFILEHEADER()
331 {
332 unSet($this->m_lpVer);
333 unSet($this->m_nWidth);
334 unSet($this->m_nHeight);
335 unSet($this->m_bGlobalClr);
336 unSet($this->m_nColorRes);
337 unSet($this->m_bSorted);
338 unSet($this->m_nTableSize);
339 unSet($this->m_nBgColor);
340 unSet($this->m_nPixelRatio);
341 unSet($this->m_colorTable);
342 }
343
344 ///////////////////////////////////////////////////////////////////////////
345
346 function load($lpData, &$hdrLen)
347 {
348 $hdrLen = 0;
349
350 $this->m_lpVer = substr($lpData, 0, 6);
351 if(($this->m_lpVer <> "GIF87a") && ($this->m_lpVer <> "GIF89a")) {
352 return false;
353 }
354
355 $this->m_nWidth = $this->w2i(substr($lpData, 6, 2));
356 $this->m_nHeight = $this->w2i(substr($lpData, 8, 2));
357 if(!$this->m_nWidth || !$this->m_nHeight) {
358 return false;
359 }
360
361 $b = ord(substr($lpData, 10, 1));
362 $this->m_bGlobalClr = ($b & 0x80) ? true : false;
363 $this->m_nColorRes = ($b & 0x70) >> 4;
364 $this->m_bSorted = ($b & 0x08) ? true : false;
365 $this->m_nTableSize = 2 << ($b & 0x07);
366 $this->m_nBgColor = ord(substr($lpData, 11, 1));
367 $this->m_nPixelRatio = ord(substr($lpData, 12, 1));
368 $hdrLen = 13;
369
370 if($this->m_bGlobalClr) {
371 $this->m_colorTable = new CGIFCOLORTABLE();
372 if(!$this->m_colorTable->load(substr($lpData, $hdrLen), $this->m_nTableSize)) {
373 return false;
374 }
375 $hdrLen += 3 * $this->m_nTableSize;
376 }
377
378 return true;
379 }
380
381 ///////////////////////////////////////////////////////////////////////////
382
383 function w2i($str)
384 {
385 return ord(substr($str, 0, 1)) + (ord(substr($str, 1, 1)) << 8);
386 }
387}
388
389///////////////////////////////////////////////////////////////////////////////////////////////////
390
391class CGIFIMAGEHEADER
392{
393 var $m_nLeft;
394 var $m_nTop;
395 var $m_nWidth;
396 var $m_nHeight;
397 var $m_bLocalClr;
398 var $m_bInterlace;
399 var $m_bSorted;
400 var $m_nTableSize;
401 var $m_colorTable;
402
403 ///////////////////////////////////////////////////////////////////////////
404
405 // CONSTRUCTOR
406 function CGIFIMAGEHEADER()
407 {
408 unSet($this->m_nLeft);
409 unSet($this->m_nTop);
410 unSet($this->m_nWidth);
411 unSet($this->m_nHeight);
412 unSet($this->m_bLocalClr);
413 unSet($this->m_bInterlace);
414 unSet($this->m_bSorted);
415 unSet($this->m_nTableSize);
416 unSet($this->m_colorTable);
417 }
418
419 ///////////////////////////////////////////////////////////////////////////
420
421 function load($lpData, &$hdrLen)
422 {
423 $hdrLen = 0;
424
425 $this->m_nLeft = $this->w2i(substr($lpData, 0, 2));
426 $this->m_nTop = $this->w2i(substr($lpData, 2, 2));
427 $this->m_nWidth = $this->w2i(substr($lpData, 4, 2));
428 $this->m_nHeight = $this->w2i(substr($lpData, 6, 2));
429
430 if(!$this->m_nWidth || !$this->m_nHeight) {
431 return false;
432 }
433
434 $b = ord($lpData{8});
435 $this->m_bLocalClr = ($b & 0x80) ? true : false;
436 $this->m_bInterlace = ($b & 0x40) ? true : false;
437 $this->m_bSorted = ($b & 0x20) ? true : false;
438 $this->m_nTableSize = 2 << ($b & 0x07);
439 $hdrLen = 9;
440
441 if($this->m_bLocalClr) {
442 $this->m_colorTable = new CGIFCOLORTABLE();
443 if(!$this->m_colorTable->load(substr($lpData, $hdrLen), $this->m_nTableSize)) {
444 return false;
445 }
446 $hdrLen += 3 * $this->m_nTableSize;
447 }
448
449 return true;
450 }
451
452 ///////////////////////////////////////////////////////////////////////////
453
454 function w2i($str)
455 {
456 return ord(substr($str, 0, 1)) + (ord(substr($str, 1, 1)) << 8);
457 }
458}
459
460///////////////////////////////////////////////////////////////////////////////////////////////////
461
462class CGIFIMAGE
463{
464 var $m_disp;
465 var $m_bUser;
466 var $m_bTrans;
467 var $m_nDelay;
468 var $m_nTrans;
469 var $m_lpComm;
470 var $m_gih;
471 var $m_data;
472 var $m_lzw;
473
474 ///////////////////////////////////////////////////////////////////////////
475
476 function CGIFIMAGE()
477 {
478 unSet($this->m_disp);
479 unSet($this->m_bUser);
480 unSet($this->m_bTrans);
481 unSet($this->m_nDelay);
482 unSet($this->m_nTrans);
483 unSet($this->m_lpComm);
484 unSet($this->m_data);
485 $this->m_gih = new CGIFIMAGEHEADER();
486 $this->m_lzw = new CGIFLZW();
487 }
488
489 ///////////////////////////////////////////////////////////////////////////
490
491 function load($data, &$datLen)
492 {
493 $datLen = 0;
494
495 while(true) {
496 $b = ord($data[0]);
497 $data = substr($data, 1);
498 $datLen++;
499
500 switch($b) {
501 case 0x21: // Extension
502 $len = 0;
503 if(!$this->skipExt($data, $len)) {
504 return false;
505 }
506 $datLen += $len;
507 break;
508
509 case 0x2C: // Image
510 // LOAD HEADER & COLOR TABLE
511 $len = 0;
512 if(!$this->m_gih->load($data, $len)) {
513 return false;
514 }
515 $data = substr($data, $len);
516 $datLen += $len;
517
518 // ALLOC BUFFER
519 $len = 0;
520
521 if(!($this->m_data = $this->m_lzw->deCompress($data, $len))) {
522 return false;
523 }
524
525 $data = substr($data, $len);
526 $datLen += $len;
527
528 if($this->m_gih->m_bInterlace) {
529 $this->deInterlace();
530 }
531
532 return true;
533
534 case 0x3B: // EOF
535 default:
536 return false;
537 }
538 }
539 return false;
540 }
541
542 ///////////////////////////////////////////////////////////////////////////
543
544 function skipExt(&$data, &$extLen)
545 {
546 $extLen = 0;
547
548 $b = ord($data[0]);
549 $data = substr($data, 1);
550 $extLen++;
551
552 switch($b) {
553 case 0xF9: // Graphic Control
554 $b = ord($data[1]);
555 $this->m_disp = ($b & 0x1C) >> 2;
556 $this->m_bUser = ($b & 0x02) ? true : false;
557 $this->m_bTrans = ($b & 0x01) ? true : false;
558 $this->m_nDelay = $this->w2i(substr($data, 2, 2));
559 $this->m_nTrans = ord($data[4]);
560 break;
561
562 case 0xFE: // Comment
563 $this->m_lpComm = substr($data, 1, ord($data[0]));
564 break;
565
566 case 0x01: // Plain text
567 break;
568
569 case 0xFF: // Application
570 break;
571 }
572
573 // SKIP DEFAULT AS DEFS MAY CHANGE
574 $b = ord($data[0]);
575 $data = substr($data, 1);
576 $extLen++;
577 while($b > 0) {
578 $data = substr($data, $b);
579 $extLen += $b;
580 $b = ord($data[0]);
581 $data = substr($data, 1);
582 $extLen++;
583 }
584 return true;
585 }
586
587 ///////////////////////////////////////////////////////////////////////////
588
589 function w2i($str)
590 {
591 return ord(substr($str, 0, 1)) + (ord(substr($str, 1, 1)) << 8);
592 }
593
594 ///////////////////////////////////////////////////////////////////////////
595
596 function deInterlace()
597 {
598 $data = $this->m_data;
599
600 for($i = 0; $i < 4; $i++) {
601 switch($i) {
602 case 0:
603 $s = 8;
604 $y = 0;
605 break;
606
607 case 1:
608 $s = 8;
609 $y = 4;
610 break;
611
612 case 2:
613 $s = 4;
614 $y = 2;
615 break;
616
617 case 3:
618 $s = 2;
619 $y = 1;
620 break;
621 }
622
623 for(; $y < $this->m_gih->m_nHeight; $y += $s) {
624 $lne = substr($this->m_data, 0, $this->m_gih->m_nWidth);
625 $this->m_data = substr($this->m_data, $this->m_gih->m_nWidth);
626
627 $data =
628 substr($data, 0, $y * $this->m_gih->m_nWidth) .
629 $lne .
630 substr($data, ($y + 1) * $this->m_gih->m_nWidth);
631 }
632 }
633
634 $this->m_data = $data;
635 }
636}
637
638///////////////////////////////////////////////////////////////////////////////////////////////////
639
640class CGIF
641{
642 var $m_gfh;
643 var $m_lpData;
644 var $m_img;
645 var $m_bLoaded;
646
647 ///////////////////////////////////////////////////////////////////////////
648
649 // CONSTRUCTOR
650 function CGIF()
651 {
652 $this->m_gfh = new CGIFFILEHEADER();
653 $this->m_img = new CGIFIMAGE();
654 $this->m_lpData = "";
655 $this->m_bLoaded = false;
656 }
657
658 ///////////////////////////////////////////////////////////////////////////
659 function ClearData() {
660 $this->m_lpData = '';
661 unSet($this->m_img->m_data);
662 unSet($this->m_img->m_lzw->Next);
663 unSet($this->m_img->m_lzw->Vals);
664 unSet($this->m_img->m_lzw->Stack);
665 unSet($this->m_img->m_lzw->Buf);
666 }
667
668 function loadFile(&$data, $iIndex)
669 {
670 if($iIndex < 0) {
671 return false;
672 }
673 $this->m_lpData = $data;
674
675 // GET FILE HEADER
676 $len = 0;
677 if(!$this->m_gfh->load($this->m_lpData, $len)) {
678 return false;
679 }
680
681 $this->m_lpData = substr($this->m_lpData, $len);
682
683 do {
684 $imgLen = 0;
685 if(!$this->m_img->load($this->m_lpData, $imgLen)) {
686 return false;
687 }
688 $this->m_lpData = substr($this->m_lpData, $imgLen);
689 }
690 while($iIndex-- > 0);
691
692 $this->m_bLoaded = true;
693 return true;
694 }
695
696}
697
698///////////////////////////////////////////////////////////////////////////////////////////////////
699
700?> \ No newline at end of file
diff --git a/inc/3rdparty/libraries/mpdf/classes/grad.php b/inc/3rdparty/libraries/mpdf/classes/grad.php
new file mode 100644
index 00000000..2691bf5d
--- /dev/null
+++ b/inc/3rdparty/libraries/mpdf/classes/grad.php
@@ -0,0 +1,723 @@
1<?php
2
3class grad {
4
5var $mpdf = null;
6
7function grad(&$mpdf) {
8 $this->mpdf = $mpdf;
9}
10
11// mPDF 5.3.A1
12function CoonsPatchMesh($x, $y, $w, $h, $patch_array=array(), $x_min=0, $x_max=1, $y_min=0, $y_max=1, $colspace='RGB', $return=false){
13 $s=' q ';
14 $s.=sprintf(' %.3F %.3F %.3F %.3F re W n ', $x*_MPDFK, ($this->mpdf->h-$y)*_MPDFK, $w*_MPDFK, -$h*_MPDFK);
15 $s.=sprintf(' %.3F 0 0 %.3F %.3F %.3F cm ', $w*_MPDFK, $h*_MPDFK, $x*_MPDFK, ($this->mpdf->h-($y+$h))*_MPDFK);
16 $n = count($this->mpdf->gradients)+1;
17 $this->mpdf->gradients[$n]['type'] = 6; //coons patch mesh
18 $this->mpdf->gradients[$n]['colorspace'] = $colspace; //coons patch mesh
19 $bpcd=65535; //16 BitsPerCoordinate
20 $trans = false;
21 $this->mpdf->gradients[$n]['stream']='';
22 for($i=0;$i<count($patch_array);$i++){
23 $this->mpdf->gradients[$n]['stream'].=chr($patch_array[$i]['f']); //start with the edge flag as 8 bit
24 for($j=0;$j<count($patch_array[$i]['points']);$j++){
25 //each point as 16 bit
26 if (($j % 2) == 1) { // Y coordinate (adjusted as input is From top left)
27 $patch_array[$i]['points'][$j]=(($patch_array[$i]['points'][$j]-$y_min)/($y_max-$y_min))*$bpcd;
28 $patch_array[$i]['points'][$j]=$bpcd-$patch_array[$i]['points'][$j];
29 }
30 else {
31 $patch_array[$i]['points'][$j]=(($patch_array[$i]['points'][$j]-$x_min)/($x_max-$x_min))*$bpcd;
32 }
33 if($patch_array[$i]['points'][$j]<0) $patch_array[$i]['points'][$j]=0;
34 if($patch_array[$i]['points'][$j]>$bpcd) $patch_array[$i]['points'][$j]=$bpcd;
35 $this->mpdf->gradients[$n]['stream'].=chr(floor($patch_array[$i]['points'][$j]/256));
36 $this->mpdf->gradients[$n]['stream'].=chr(floor($patch_array[$i]['points'][$j]%256));
37 }
38 for($j=0;$j<count($patch_array[$i]['colors']);$j++){
39 //each color component as 8 bit
40 if ($colspace=='RGB') {
41 $this->mpdf->gradients[$n]['stream'].=($patch_array[$i]['colors'][$j][1]);
42 $this->mpdf->gradients[$n]['stream'].=($patch_array[$i]['colors'][$j][2]);
43 $this->mpdf->gradients[$n]['stream'].=($patch_array[$i]['colors'][$j][3]);
44 if (isset($patch_array[$i]['colors'][$j][4]) && ord($patch_array[$i]['colors'][$j][4])<100) { $trans = true; }
45 }
46 else if ($colspace=='CMYK') {
47 $this->mpdf->gradients[$n]['stream'].=chr(ord($patch_array[$i]['colors'][$j][1])*2.55);
48 $this->mpdf->gradients[$n]['stream'].=chr(ord($patch_array[$i]['colors'][$j][2])*2.55);
49 $this->mpdf->gradients[$n]['stream'].=chr(ord($patch_array[$i]['colors'][$j][3])*2.55);
50 $this->mpdf->gradients[$n]['stream'].=chr(ord($patch_array[$i]['colors'][$j][4])*2.55);
51 if (isset($patch_array[$i]['colors'][$j][5]) && ord($patch_array[$i]['colors'][$j][5])<100) { $trans = true; }
52 }
53 else if ($colspace=='Gray') {
54 $this->mpdf->gradients[$n]['stream'].=($patch_array[$i]['colors'][$j][1]);
55 if ($patch_array[$i]['colors'][$j][2]==1) { $trans = true; } // transparency converted from rgba or cmyka()
56 }
57 }
58 }
59 // TRANSPARENCY
60 if ($trans) {
61 $this->mpdf->gradients[$n]['stream_trans']='';
62 for($i=0;$i<count($patch_array);$i++){
63 $this->mpdf->gradients[$n]['stream_trans'].=chr($patch_array[$i]['f']);
64 for($j=0;$j<count($patch_array[$i]['points']);$j++){
65 //each point as 16 bit
66 $this->mpdf->gradients[$n]['stream_trans'].=chr(floor($patch_array[$i]['points'][$j]/256));
67 $this->mpdf->gradients[$n]['stream_trans'].=chr(floor($patch_array[$i]['points'][$j]%256));
68 }
69 for($j=0;$j<count($patch_array[$i]['colors']);$j++){
70 //each color component as 8 bit // OPACITY
71 if ($colspace=='RGB') {
72 $this->mpdf->gradients[$n]['stream_trans'].=chr(intval(ord($patch_array[$i]['colors'][$j][4])*2.55));
73 }
74 else if ($colspace=='CMYK') {
75 $this->mpdf->gradients[$n]['stream_trans'].=chr(intval(ord($patch_array[$i]['colors'][$j][5])*2.55));
76 }
77 else if ($colspace=='Gray') {
78 $this->mpdf->gradients[$n]['stream_trans'].=chr(intval(ord($patch_array[$i]['colors'][$j][3])*2.55));
79 }
80 }
81 }
82 $this->mpdf->gradients[$n]['trans'] = true;
83 $s .= ' /TGS'.$n.' gs ';
84 }
85 //paint the gradient
86 $s .= '/Sh'.$n.' sh'."\n";
87 //restore previous Graphic State
88 $s .= 'Q'."\n";
89 if ($return) { return $s; }
90 else { $this->mpdf->_out($s); }
91}
92
93
94// type = linear:2; radial: 3;
95// Linear: $coords - array of the form (x1, y1, x2, y2) which defines the gradient vector (see linear_gradient_coords.jpg).
96// The default value is from left to right (x1=0, y1=0, x2=1, y2=0).
97// Radial: $coords - array of the form (fx, fy, cx, cy, r) where (fx, fy) is the starting point of the gradient with color1,
98// (cx, cy) is the center of the circle with color2, and r is the radius of the circle (see radial_gradient_coords.jpg).
99// (fx, fy) should be inside the circle, otherwise some areas will not be defined
100// $col = array(R,G,B/255); or array(G/255); or array(C,M,Y,K/100)
101// $stops = array('col'=>$col [, 'opacity'=>0-1] [, 'offset'=>0-1])
102function Gradient($x, $y, $w, $h, $type, $stops=array(), $colorspace='RGB', $coords='', $extend='', $return=false, $is_mask=false) {
103 if (strtoupper(substr($type,0,1)) == 'L') { $type = 2; } // linear
104 else if (strtoupper(substr($type,0,1)) == 'R') { $type = 3; } // radial
105 if ($colorspace != 'CMYK' && $colorspace != 'Gray') {
106 $colorspace = 'RGB';
107 }
108 $bboxw = $w;
109 $bboxh = $h;
110 $usex = $x;
111 $usey = $y;
112 $usew = $bboxw;
113 $useh = $bboxh;
114 if ($type < 1) { $type = 2; }
115 if ($coords[0]!==false && preg_match('/([0-9.]+(px|em|ex|pc|pt|cm|mm|in))/i',$coords[0],$m)) {
116 $tmp = $this->mpdf->ConvertSize($m[1],$this->mpdf->w,$this->mpdf->FontSize,false);
117 if ($tmp) { $coords[0] = $tmp/$w; }
118 }
119 if ($coords[1]!==false && preg_match('/([0-9.]+(px|em|ex|pc|pt|cm|mm|in))/i',$coords[1],$m)) {
120 $tmp = $this->mpdf->ConvertSize($m[1],$this->mpdf->w,$this->mpdf->FontSize,false);
121 if ($tmp) { $coords[1] = 1-($tmp/$h); }
122 }
123 // LINEAR
124 if ($type == 2) {
125 $angle = $coords[4];
126 $repeat = $coords[5];
127 // ALL POINTS SET (default for custom mPDF linear gradient) - no -moz
128 if ($coords[0]!==false && $coords[1]!==false && $coords[2]!==false && $coords[3]!==false) {
129 // do nothing - coords used as they are
130 }
131
132 // If both a <point> and <angle> are defined, the gradient axis starts from the point and runs along the angle. The end point is
133 // defined as before - in this case start points may not be in corners, and axis may not correctly fall in the right quadrant.
134 // NO end points (Angle defined & Start points)
135 else if ($angle!==false && $coords[0]!==false && $coords[1]!==false && $coords[2]===false && $coords[3]===false) {
136 if ($angle==0 || $angle==360) { $coords[3]=$coords[1]; if ($coords[0]==1) $coords[2]=2; else $coords[2]=1; }
137 else if ($angle==90) { $coords[2]=$coords[0]; $coords[3]=1; if ($coords[1]==1) $coords[3]=2; else $coords[3]=1; }
138 else if ($angle==180) { if ($coords[4]==0) $coords[2]=-1; else $coords[2]=0; $coords[3]=$coords[1]; }
139 else if ($angle==270) { $coords[2]=$coords[0]; if ($coords[1]==0) $coords[3]=-1; else $coords[3]=0; }
140 else {
141 $endx=1; $endy=1;
142 if ($angle <=90) {
143 if ($angle <=45) { $endy=tan(deg2rad($angle)); }
144 else { $endx=tan(deg2rad(90-$angle)); }
145 $b = atan2(($endy*$bboxh), ($endx*$bboxw));
146 $ny = 1 - $coords[1] - (tan($b) * (1-$coords[0]));
147 $tx = sin($b) * cos($b) * $ny;
148 $ty = cos($b) * cos($b) * $ny;
149 $coords[2] = 1+$tx; $coords[3] = 1-$ty;
150 }
151 else if ($angle <=180) {
152 if ($angle <=135) { $endx=tan(deg2rad($angle-90)); }
153 else { $endy=tan(deg2rad(180-$angle)); }
154 $b = atan2(($endy*$bboxh), ($endx*$bboxw));
155 $ny = 1 - $coords[1] - (tan($b) * ($coords[0]));
156 $tx = sin($b) * cos($b) * $ny;
157 $ty = cos($b) * cos($b) * $ny;
158 $coords[2] = -$tx; $coords[3] = 1-$ty;
159 }
160 else if ($angle <=270) {
161 if ($angle <=225) { $endy=tan(deg2rad($angle-180)); }
162 else { $endx=tan(deg2rad(270-$angle)); }
163 $b = atan2(($endy*$bboxh), ($endx*$bboxw));
164 $ny = $coords[1] - (tan($b) * ($coords[0]));
165 $tx = sin($b) * cos($b) * $ny;
166 $ty = cos($b) * cos($b) * $ny;
167 $coords[2] = -$tx; $coords[3] = $ty;
168 }
169 else {
170 if ($angle <=315) { $endx=tan(deg2rad($angle-270)); }
171 else { $endy=tan(deg2rad(360-$angle)); }
172 $b = atan2(($endy*$bboxh), ($endx*$bboxw));
173 $ny = $coords[1] - (tan($b) * (1-$coords[0]));
174 $tx = sin($b) * cos($b) * $ny;
175 $ty = cos($b) * cos($b) * $ny;
176 $coords[2] = 1+$tx; $coords[3] = $ty;
177
178 }
179 }
180 }
181
182 // -moz If the first parameter is only an <angle>, the gradient axis starts from the box's corner that would ensure the
183 // axis goes through the box. The axis runs along the specified angle. The end point of the axis is defined such that the
184 // farthest corner of the box from the starting point is perpendicular to the gradient axis at that point.
185 // NO end points or Start points (Angle defined)
186 else if ($angle!==false && $coords[0]===false && $coords[1]===false) {
187 if ($angle==0 || $angle==360) { $coords[0]=0; $coords[1]=0; $coords[2]=1; $coords[3]=0; }
188 else if ($angle==90) { $coords[0]=0; $coords[1]=0; $coords[2]=0; $coords[3]=1; }
189 else if ($angle==180) { $coords[0]=1; $coords[1]=0; $coords[2]=0; $coords[3]=0; }
190 else if ($angle==270) { $coords[0]=0; $coords[1]=1; $coords[2]=0; $coords[3]=0; }
191 else {
192 if ($angle <=90) {
193 $coords[0]=0; $coords[1]=0;
194 if ($angle <=45) { $endx=1; $endy=tan(deg2rad($angle)); }
195 else { $endx=tan(deg2rad(90-$angle)); $endy=1; }
196 }
197 else if ($angle <=180) {
198 $coords[0]=1; $coords[1]=0;
199 if ($angle <=135) { $endx=tan(deg2rad($angle-90)); $endy=1; }
200 else { $endx=1; $endy=tan(deg2rad(180-$angle)); }
201 }
202 else if ($angle <=270) {
203 $coords[0]=1; $coords[1]=1;
204 if ($angle <=225) { $endx=1; $endy=tan(deg2rad($angle-180)); }
205 else { $endx=tan(deg2rad(270-$angle)); $endy=1; }
206 }
207 else {
208 $coords[0]=0; $coords[1]=1;
209 if ($angle <=315) { $endx=tan(deg2rad($angle-270)); $endy=1; }
210 else { $endx=1; $endy=tan(deg2rad(360-$angle)); }
211 }
212 $b = atan2(($endy*$bboxh), ($endx*$bboxw));
213 $h2 = $bboxh - ($bboxh * tan($b));
214 $px = $bboxh + ($h2 * sin($b) * cos($b));
215 $py = ($bboxh * tan($b)) + ($h2 * sin($b) * sin($b));
216 $x1 = $px / $bboxh;
217 $y1 = $py / $bboxh;
218 if ($angle <=90) { $coords[2] = $x1; $coords[3] = $y1; }
219 else if ($angle <=180) { $coords[2] = 1-$x1; $coords[3] = $y1; }
220 else if ($angle <=270) { $coords[2] = 1-$x1; $coords[3] = 1-$y1; }
221 else { $coords[2] = $x1; $coords[3] = 1-$y1; }
222 }
223 }
224 // -moz If the first parameter to the gradient function is only a <point>, the gradient axis starts from the specified point,
225 // and ends at the point you would get if you rotated the starting point by 180 degrees about the center of the box that the
226 // gradient is to be applied to.
227 // NO angle and NO end points (Start points defined)
228 else if ((!isset($angle) || $angle===false) && $coords[0]!==false && $coords[1]!==false) { // should have start and end defined
229 $coords[2] = 1-$coords[0]; $coords[3] = 1-$coords[1];
230 $angle = rad2deg(atan2($coords[3]-$coords[1],$coords[2]-$coords[0]));
231 if ($angle < 0) { $angle += 360; }
232 else if ($angle > 360) { $angle -= 360; }
233 if ($angle!=0 && $angle!=360 && $angle!=90 && $angle!=180 && $angle!=270) {
234 if ($w >= $h) {
235 $coords[1] *= $h/$w ;
236 $coords[3] *= $h/$w ;
237 $usew = $useh = $bboxw;
238 $usey -= ($w-$h);
239 }
240 else {
241 $coords[0] *= $w/$h ;
242 $coords[2] *= $w/$h ;
243 $usew = $useh = $bboxh;
244 }
245 }
246 }
247
248 // -moz If neither a <point> or <angle> is specified, i.e. the entire function consists of only <stop> values, the gradient
249 // axis starts from the top of the box and runs vertically downwards, ending at the bottom of the box.
250 else { // default values T2B
251 // All values are set in parseMozGradient - so won't appear here
252 $coords = array(0,0,1,0); // default for original linear gradient (L2R)
253 }
254 $s = ' q';
255 $s .= sprintf(' %.3F %.3F %.3F %.3F re W n', $x*_MPDFK, ($this->mpdf->h-$y)*_MPDFK, $w*_MPDFK, -$h*_MPDFK)."\n";
256 $s .= sprintf(' %.3F 0 0 %.3F %.3F %.3F cm', $usew*_MPDFK, $useh*_MPDFK, $usex*_MPDFK, ($this->mpdf->h-($usey+$useh))*_MPDFK)."\n";
257 }
258
259 // RADIAL
260 else if ($type == 3) {
261 $radius = $coords[4];
262 $angle = $coords[5]; // ?? no effect
263 $shape = $coords[6];
264 $size = $coords[7];
265 $repeat = $coords[8];
266 // ALL POINTS AND RADIUS SET (default for custom mPDF radial gradient) - no -moz
267 if ($coords[0]!==false && $coords[1]!==false && $coords[2]!==false && $coords[3]!==false && $coords[4]!==false) {
268 // do nothing - coords used as they are
269 }
270 // If a <point> is defined
271 else if ($shape!==false && $size!==false) {
272 if ($coords[2]==false) { $coords[2] = $coords[0]; }
273 if ($coords[3]==false) { $coords[3] = $coords[1]; }
274 // ELLIPSE
275 if ($shape=='ellipse') {
276 $corner1 = sqrt(pow($coords[0],2) + pow($coords[1],2));
277 $corner2 = sqrt(pow($coords[0],2) + pow((1-$coords[1]),2));
278 $corner3 = sqrt(pow((1-$coords[0]),2) + pow($coords[1],2));
279 $corner4 = sqrt(pow((1-$coords[0]),2) + pow((1-$coords[1]),2));
280 if ($size=='closest-side') { $radius = min($coords[0], $coords[1], (1-$coords[0]), (1-$coords[1])); }
281 else if ($size=='closest-corner') { $radius = min($corner1, $corner2, $corner3, $corner4); }
282 else if ($size=='farthest-side') { $radius = max($coords[0], $coords[1], (1-$coords[0]), (1-$coords[1])); }
283 else { $radius = max($corner1, $corner2, $corner3, $corner4); } // farthest corner (default)
284 }
285 // CIRCLE
286 else if ($shape=='circle') {
287 if ($w >= $h) {
288 $coords[1] = $coords[3] = ($coords[1] * $h/$w) ;
289 $corner1 = sqrt(pow($coords[0],2) + pow($coords[1],2));
290 $corner2 = sqrt(pow($coords[0],2) + pow((($h/$w)-$coords[1]),2));
291 $corner3 = sqrt(pow((1-$coords[0]),2) + pow($coords[1],2));
292 $corner4 = sqrt(pow((1-$coords[0]),2) + pow((($h/$w)-$coords[1]),2));
293 if ($size=='closest-side') { $radius = min($coords[0], $coords[1], (1-$coords[0]), (($h/$w)-$coords[1])); }
294 else if ($size=='closest-corner') { $radius = min($corner1, $corner2, $corner3, $corner4); }
295 else if ($size=='farthest-side') { $radius = max($coords[0], $coords[1], (1-$coords[0]), (($h/$w)-$coords[1])); }
296 else if ($size=='farthest-corner') { $radius = max($corner1, $corner2, $corner3, $corner4); } // farthest corner (default)
297 $usew = $useh = $bboxw;
298 $usey -= ($w-$h);
299 }
300 else {
301 $coords[0] = $coords[2] = ($coords[0] * $w/$h) ;
302 $corner1 = sqrt(pow($coords[0],2) + pow($coords[1],2));
303 $corner2 = sqrt(pow($coords[0],2) + pow((1-$coords[1]),2));
304 $corner3 = sqrt(pow((($w/$h)-$coords[0]),2) + pow($coords[1],2));
305 $corner4 = sqrt(pow((($w/$h)-$coords[0]),2) + pow((1-$coords[1]),2));
306 if ($size=='closest-side') { $radius = min($coords[0], $coords[1], (($w/$h)-$coords[0]), (1-$coords[1])); }
307 else if ($size=='closest-corner') { $radius = min($corner1, $corner2, $corner3, $corner4); }
308 else if ($size=='farthest-side') { $radius = max($coords[0], $coords[1], (($w/$h)-$coords[0]), (1-$coords[1])); }
309 else if ($size=='farthest-corner') { $radius = max($corner1, $corner2, $corner3, $corner4); } // farthest corner (default)
310 $usew = $useh = $bboxh;
311 }
312 }
313 if ($radius==0) { $radius=0.001; } // to prevent error
314 $coords[4] = $radius;
315 }
316
317 // -moz If entire function consists of only <stop> values
318 else { // default values
319 // All values are set in parseMozGradient - so won't appear here
320 $coords = array(0.5,0.5,0.5,0.5); // default for radial gradient (centred)
321 }
322 $s = ' q';
323 $s .= sprintf(' %.3F %.3F %.3F %.3F re W n', $x*_MPDFK, ($this->mpdf->h-$y)*_MPDFK, $w*_MPDFK, -$h*_MPDFK)."\n";
324 $s .= sprintf(' %.3F 0 0 %.3F %.3F %.3F cm', $usew*_MPDFK, $useh*_MPDFK, $usex*_MPDFK, ($this->mpdf->h-($usey+$useh))*_MPDFK)."\n";
325 }
326
327 $n = count($this->mpdf->gradients) + 1;
328 $this->mpdf->gradients[$n]['type'] = $type;
329 $this->mpdf->gradients[$n]['colorspace'] = $colorspace;
330 $trans = false;
331 $this->mpdf->gradients[$n]['is_mask'] = $is_mask;
332 if ($is_mask) { $trans = true; }
333 if (count($stops) == 1) { $stops[1] = $stops[0]; }
334 if (!isset($stops[0]['offset'])) { $stops[0]['offset'] = 0; }
335 if (!isset($stops[(count($stops)-1)]['offset'])) { $stops[(count($stops)-1)]['offset'] = 1; }
336
337 // Fix stop-offsets set as absolute lengths
338 if ($type==2) {
339 $axisx = ($coords[2]-$coords[0])*$usew;
340 $axisy = ($coords[3]-$coords[1])*$useh;
341 $axis_length = sqrt(pow($axisx,2) + pow($axisy,2));
342 }
343 else { $axis_length = $coords[4]*$usew; } // Absolute lengths are meaningless for an ellipse - Firefox uses Width as reference
344
345 for($i=0;$i<count($stops);$i++) {
346 if (isset($stops[$i]['offset']) && preg_match('/([0-9.]+(px|em|ex|pc|pt|cm|mm|in))/i',$stops[$i]['offset'],$m)) {
347 $tmp = $this->mpdf->ConvertSize($m[1],$this->mpdf->w,$this->mpdf->FontSize,false);
348 $stops[$i]['offset'] = $tmp/$axis_length;
349 }
350 }
351
352
353 if (isset($stops[0]['offset']) && $stops[0]['offset']>0) {
354 $firststop = $stops[0];
355 $firststop['offset'] = 0;
356 array_unshift($stops, $firststop);
357 }
358 if (!$repeat && isset($stops[(count($stops)-1)]['offset']) && $stops[(count($stops)-1)]['offset']<1) {
359 $endstop = $stops[(count($stops)-1)];
360 $endstop['offset'] = 1;
361 $stops[] = $endstop;
362 }
363 if ($stops[0]['offset'] > $stops[(count($stops)-1)]['offset']) {
364 $stops[0]['offset'] = 0;
365 $stops[(count($stops)-1)]['offset'] = 1;
366 }
367
368 for($i=0;$i<count($stops);$i++) {
369 // mPDF 5.3.74
370 if ($colorspace == 'CMYK') {
371 $this->mpdf->gradients[$n]['stops'][$i]['col'] = sprintf('%.3F %.3F %.3F %.3F', (ord($stops[$i]['col']{1})/100), (ord($stops[$i]['col']{2})/100), (ord($stops[$i]['col']{3})/100), (ord($stops[$i]['col']{4})/100));
372 }
373 else if ($colorspace == 'Gray') {
374 $this->mpdf->gradients[$n]['stops'][$i]['col'] = sprintf('%.3F', (ord($stops[$i]['col']{1})/255));
375 }
376 else {
377 $this->mpdf->gradients[$n]['stops'][$i]['col'] = sprintf('%.3F %.3F %.3F', (ord($stops[$i]['col']{1})/255), (ord($stops[$i]['col']{2})/255), (ord($stops[$i]['col']{3})/255));
378 }
379 if (!isset($stops[$i]['opacity'])) { $stops[$i]['opacity'] = 1; }
380 else if ($stops[$i]['opacity'] > 1 || $stops[$i]['opacity'] < 0) { $stops[$i]['opacity'] = 1; }
381 else if ($stops[$i]['opacity'] < 1) {
382 $trans = true;
383 }
384 $this->mpdf->gradients[$n]['stops'][$i]['opacity'] = $stops[$i]['opacity'];
385 // OFFSET
386 if ($i>0 && $i<(count($stops)-1)) {
387 if (!isset($stops[$i]['offset']) || (isset($stops[$i+1]['offset']) && $stops[$i]['offset']>$stops[$i+1]['offset']) || $stops[$i]['offset']<$stops[$i-1]['offset']) {
388 if (isset($stops[$i-1]['offset']) && isset($stops[$i+1]['offset'])) {
389 $stops[$i]['offset'] = ($stops[$i-1]['offset']+$stops[$i+1]['offset'])/2;
390 }
391 else {
392 for($j=($i+1);$j<count($stops);$j++) {
393 if(isset($stops[$j]['offset'])) { break; }
394 }
395 $int = ($stops[$j]['offset'] - $stops[($i-1)]['offset'])/($j-$i+1);
396 for($f=0;$f<($j-$i-1);$f++) {
397 $stops[($i+$f)]['offset'] = $stops[($i+$f-1)]['offset'] + ($int);
398 }
399 }
400 }
401 }
402 $this->mpdf->gradients[$n]['stops'][$i]['offset'] = $stops[$i]['offset'];
403 $this->mpdf->gradients[$n]['stops'][$i]['offset'] = $stops[$i]['offset'];
404 }
405
406 if ($repeat) {
407 $ns = count($this->mpdf->gradients[$n]['stops']);
408 $offs = array();
409 for($i=0;$i<$ns;$i++) {
410 $offs[$i] = $this->mpdf->gradients[$n]['stops'][$i]['offset'];
411 }
412 $gp = 0;
413 $inside=true;
414 while($inside) {
415 $gp++;
416 for($i=0;$i<$ns;$i++) {
417 $this->mpdf->gradients[$n]['stops'][(($ns*$gp)+$i)] = $this->mpdf->gradients[$n]['stops'][(($ns*($gp-1))+$i)];
418 $tmp = $this->mpdf->gradients[$n]['stops'][(($ns*($gp-1))+($ns-1))]['offset']+$offs[$i] ;
419 if ($tmp < 1) { $this->mpdf->gradients[$n]['stops'][(($ns*$gp)+$i)]['offset'] = $tmp; }
420 else {
421 $this->mpdf->gradients[$n]['stops'][(($ns*$gp)+$i)]['offset'] = 1;
422 $inside = false;
423 break(2);
424 }
425 }
426 }
427 }
428
429 if ($trans) {
430 $this->mpdf->gradients[$n]['trans'] = true;
431 $s .= ' /TGS'.$n.' gs ';
432 }
433 if (!is_array($extend) || count($extend) <1) {
434 $extend=array('true', 'true'); // These are supposed to be quoted - appear in PDF file as text
435 }
436 $this->mpdf->gradients[$n]['coords'] = $coords;
437 $this->mpdf->gradients[$n]['extend'] = $extend;
438 //paint the gradient
439 $s .= '/Sh'.$n.' sh '."\n";
440 //restore previous Graphic State
441 $s .= ' Q '."\n";
442 if ($return) { return $s; }
443 else { $this->mpdf->_out($s); }
444}
445
446
447function parseMozGradient($bg) {
448 // background[-image]: -moz-linear-gradient(left, #c7Fdde 20%, #FF0000 );
449 // background[-image]: linear-gradient(left, #c7Fdde 20%, #FF0000 ); // CSS3
450 if (preg_match('/repeating-/',$bg)) { $repeat = true; }
451 else { $repeat = false; }
452 if (preg_match('/linear-gradient\((.*)\)/',$bg,$m)) {
453 $g = array();
454 $g['type'] = 2;
455 $g['colorspace'] = 'RGB';
456 $g['extend'] = array('true','true');
457 $v = trim($m[1]);
458 // Change commas inside e.g. rgb(x,x,x)
459 while(preg_match('/(\([^\)]*?),/',$v)) { $v = preg_replace('/(\([^\)]*?),/','\\1@',$v); }
460 // Remove spaces inside e.g. rgb(x, x, x)
461 while(preg_match('/(\([^\)]*?)[ ]/',$v)) { $v = preg_replace('/(\([^\)]*?)[ ]/','\\1',$v); }
462 $bgr = preg_split('/\s*,\s*/',$v);
463 for($i=0;$i<count($bgr);$i++) { $bgr[$i] = preg_replace('/@/', ',', $bgr[$i]); }
464 // Is first part $bgr[0] a valid point/angle?
465 $first = preg_split('/\s+/',trim($bgr[0]));
466 if (preg_match('/(left|center|right|bottom|top|deg|grad|rad)/i',$bgr[0]) && !preg_match('/(<#|rgb|rgba|hsl|hsla)/i',$bgr[0])) {
467 $startStops = 1;
468 }
469 else if (trim($first[(count($first)-1)]) === "0") {
470 $startStops = 1;
471 }
472 else {
473 $check = $this->mpdf->ConvertColor($first[0]);
474 if ($check) $startStops = 0;
475 else $startStops = 1;
476 }
477 // first part a valid point/angle?
478 if ($startStops == 1) { // default values
479 // [<point> || <angle>,] = [<% em px left center right bottom top> || <deg grad rad 0>,]
480 if (preg_match('/([\-]*[0-9\.]+)(deg|grad|rad)/i',$bgr[0],$m)) {
481 $angle = $m[1] + 0;
482 if (strtolower($m[2])=='deg') { $angle = $angle; }
483 else if (strtolower($m[2])=='grad') { $angle *= (360/400); }
484 else if (strtolower($m[2])=='rad') { $angle = rad2deg($angle); }
485 while($angle < 0) { $angle += 360; }
486 $angle = ($angle % 360);
487 }
488 else if (trim($first[(count($first)-1)]) === "0") { $angle = 0; }
489 if (preg_match('/left/i',$bgr[0])) { $startx = 0; }
490 else if (preg_match('/right/i',$bgr[0])) { $startx = 1; }
491 if (preg_match('/top/i',$bgr[0])) { $starty = 1; }
492 else if (preg_match('/bottom/i',$bgr[0])) { $starty = 0; }
493 // Check for %? ?% or %%
494 if (preg_match('/(\d+)[%]/i',$first[0],$m)) { $startx = $m[1]/100; }
495 else if (!isset($startx) && preg_match('/([0-9.]+(px|em|ex|pc|pt|cm|mm|in))/i',$first[0],$m)) {
496 $tmp = $this->mpdf->ConvertSize($m[1],$this->mpdf->w,$this->mpdf->FontSize,false);
497 if ($tmp) { $startx = $m[1]; }
498 }
499 if (isset($first[1]) && preg_match('/(\d+)[%]/i',$first[1],$m)) { $starty = 1 - ($m[1]/100); }
500 else if (!isset($starty) && preg_match('/([0-9.]+(px|em|ex|pc|pt|cm|mm|in))/i',$first[1],$m)) {
501 $tmp = $this->mpdf->ConvertSize($m[1],$this->mpdf->w,$this->mpdf->FontSize,false);
502 if ($tmp) { $starty = $m[1]; }
503 }
504 if (isset($startx) && !isset($starty)) { $starty = 0.5; }
505 if (!isset($startx) && isset($starty)) { $startx = 0.5; }
506
507 }
508 // If neither a <point> or <angle> is specified, i.e. the entire function consists of only <stop> values, the gradient axis starts from the top of the box and runs vertically downwards, ending at the bottom of the box.
509 else { // default values T2B
510 $starty = 1; $startx = 0.5;
511 $endy = 0; $endx = 0.5;
512 }
513 $coords = array();
514 if (!isset($startx)) { $startx = false; }
515 if (!isset($starty)) { $starty = false; }
516 if (!isset($endx)) { $endx = false; }
517 if (!isset($endy)) { $endy = false; }
518 if (!isset($angle)) { $angle = false; }
519 $g['coords'] = array($startx ,$starty ,$endx ,$endy, $angle, $repeat );
520 $g['stops'] = array();
521 for($i=$startStops;$i<count($bgr);$i++) {
522 $stop = array();
523 // parse stops
524 $el = preg_split('/\s+/',trim($bgr[$i]));
525 // mPDF 5.3.74
526 $col = $this->mpdf->ConvertColor($el[0]);
527 if ($col) { $stop['col'] = $col; }
528 else { $stop['col'] = $col = $this->mpdf->ConvertColor(255); }
529 if ($col{0}==1) $g['colorspace'] = 'Gray';
530 else if ($col{0}==4 || $col{0}==6) $g['colorspace'] = 'CMYK';
531 if ($col{0}==5) { $stop['opacity'] = ord($col{4})/100; } // transparency from rgba()
532 else if ($col{0}==6) { $stop['opacity'] = ord($col{5})/100; } // transparency from cmyka()
533 else if ($col{0}==1 && $col{2}==1) { $stop['opacity'] = ord($col{3})/100; } // transparency converted from rgba or cmyka()
534
535 if (isset($el[1]) && preg_match('/(\d+)[%]/',$el[1],$m)) {
536 $stop['offset'] = $m[1]/100;
537 if ($stop['offset']>1) { unset($stop['offset']); }
538 }
539 else if (isset($el[1]) && preg_match('/([0-9.]+(px|em|ex|pc|pt|cm|mm|in))/i',$el[1],$m)) {
540 $tmp = $this->mpdf->ConvertSize($m[1],$this->mpdf->w,$this->mpdf->FontSize,false);
541 if ($tmp) { $stop['offset'] = $m[1]; }
542 }
543 $g['stops'][] = $stop;
544 }
545 if (count($g['stops'] )) { return $g; }
546 }
547 else if (preg_match('/radial-gradient\((.*)\)/',$bg,$m)) {
548 $g = array();
549 $g['type'] = 3;
550 $g['colorspace'] = 'RGB';
551 $g['extend'] = array('true','true');
552 $v = trim($m[1]);
553 // Change commas inside e.g. rgb(x,x,x)
554 while(preg_match('/(\([^\)]*?),/',$v)) { $v = preg_replace('/(\([^\)]*?),/','\\1@',$v); }
555 // Remove spaces inside e.g. rgb(x, x, x)
556 while(preg_match('/(\([^\)]*?)[ ]/',$v)) { $v = preg_replace('/(\([^\)]*?)[ ]/','\\1',$v); }
557 $bgr = preg_split('/\s*,\s*/',$v);
558 for($i=0;$i<count($bgr);$i++) { $bgr[$i] = preg_replace('/@/', ',', $bgr[$i]); }
559
560 // Is first part $bgr[0] a valid point/angle?
561 $startStops = 0;
562 $pos_angle = false;
563 $shape_size = false;
564 $first = preg_split('/\s+/',trim($bgr[0]));
565 $checkCol = $this->mpdf->ConvertColor($first[0]);
566 if (preg_match('/(left|center|right|bottom|top|deg|grad|rad)/i',$bgr[0]) && !preg_match('/(<#|rgb|rgba|hsl|hsla)/i',$bgr[0])) {
567 $startStops=1;
568 $pos_angle = $bgr[0];
569 }
570 else if (trim($first[(count($first)-1)]) === "0") {
571 $startStops=1;
572 $pos_angle = $bgr[0];
573 }
574 else if (preg_match('/(circle|ellipse|closest-side|closest-corner|farthest-side|farthest-corner|contain|cover)/i',$bgr[0])) {
575 $startStops=1;
576 $shape_size = $bgr[0];
577 }
578 else if (!$checkCol) {
579 $startStops=1;
580 $pos_angle = $bgr[0];
581 }
582 if (preg_match('/(circle|ellipse|closest-side|closest-corner|farthest-side|farthest-corner|contain|cover)/i',$bgr[1])) {
583 $startStops=2;
584 $shape_size = $bgr[1];
585 }
586
587 // If valid point/angle?
588 if ($pos_angle) { // default values
589 // [<point> || <angle>,] = [<% em px left center right bottom top> || <deg grad rad 0>,]
590 if (preg_match('/left/i',$pos_angle)) { $startx = 0; }
591 else if (preg_match('/right/i',$pos_angle)) { $startx = 1; }
592 if (preg_match('/top/i',$pos_angle)) { $starty = 1; }
593 else if (preg_match('/bottom/i',$pos_angle)) { $starty = 0; }
594 // Check for %? ?% or %%
595 if (preg_match('/(\d+)[%]/i',$first[0],$m)) { $startx = $m[1]/100; }
596 else if (!isset($startx) && preg_match('/([0-9.]+(px|em|ex|pc|pt|cm|mm|in))/i',$first[0],$m)) {
597 $tmp = $this->mpdf->ConvertSize($m[1],$this->mpdf->w,$this->mpdf->FontSize,false);
598 if ($tmp) { $startx = $m[1]; }
599 }
600 if (isset($first[1]) && preg_match('/(\d+)[%]/i',$first[1],$m)) { $starty = 1 - ($m[1]/100); }
601 else if (!isset($starty) && preg_match('/([0-9.]+(px|em|ex|pc|pt|cm|mm|in))/i',$first[1],$m)) {
602 $tmp = $this->mpdf->ConvertSize($m[1],$this->mpdf->w,$this->mpdf->FontSize,false);
603 if ($tmp) { $starty = $m[1]; }
604 }
605
606/*
607 // ?? Angle has no effect in radial gradient (does not exist in CSS3 spec.)
608 if (preg_match('/([\-]*[0-9\.]+)(deg|grad|rad)/i',$pos_angle,$m)) {
609 $angle = $m[1] + 0;
610 if (strtolower($m[2])=='deg') { $angle = $angle; }
611 else if (strtolower($m[2])=='grad') { $angle *= (360/400); }
612 else if (strtolower($m[2])=='rad') { $angle = rad2deg($angle); }
613 while($angle < 0) { $angle += 360; }
614 $angle = ($angle % 360);
615 }
616*/
617 if (!isset($starty)) { $starty = 0.5; }
618 if (!isset($startx)) { $startx = 0.5; }
619
620 }
621 // If neither a <point> or <angle> is specified, i.e. the entire function consists of only <stop> values, the gradient axis starts from the top of the box and runs vertically downwards, ending at the bottom of the box.
622 else { // default values Center
623 $starty = 0.5; $startx = 0.5;
624 $endy = 0.5; $endx = 0.5;
625 }
626
627 // If valid shape/size?
628 $shape = 'ellipse'; // default
629 $size = 'farthest-corner'; // default
630 if ($shape_size) { // default values
631 if (preg_match('/(circle|ellipse)/i',$shape_size, $m)) {
632 $shape = $m[1];
633 }
634 if (preg_match('/(closest-side|closest-corner|farthest-side|farthest-corner|contain|cover)/i',$shape_size, $m)) {
635 $size = $m[1];
636 if ($size=='contain') { $size = 'closest-side'; }
637 else if ($size=='cover') { $size = 'farthest-corner'; }
638 }
639 }
640
641 $coords = array();
642 if (!isset($startx)) { $startx = false; }
643 if (!isset($starty)) { $starty = false; }
644 if (!isset($endx)) { $endx = false; }
645 if (!isset($endy)) { $endy = false; }
646 if (!isset($radius)) { $radius = false; }
647 if (!isset($angle)) { $angle = 0; }
648 $g['coords'] = array($startx ,$starty ,$endx ,$endy, $radius, $angle, $shape, $size, $repeat );
649
650 $g['stops'] = array();
651 for($i=$startStops;$i<count($bgr);$i++) {
652 $stop = array();
653 // parse stops
654 $el = preg_split('/\s+/',trim($bgr[$i]));
655 // mPDF 5.3.74
656 $col = $this->mpdf->ConvertColor($el[0]);
657 if ($col) { $stop['col'] = $col; }
658 else { $stop['col'] = $col = $this->mpdf->ConvertColor(255); }
659 if ($col{0}==1) $g['colorspace'] = 'Gray';
660 else if ($col{0}==4 || $col{0}==6) $g['colorspace'] = 'CMYK';
661 if ($col{0}==5) { $stop['opacity'] = ord($col{4})/100; } // transparency from rgba()
662 else if ($col{0}==6) { $stop['opacity'] = ord($col{5})/100; } // transparency from cmyka()
663 else if ($col{0}==1 && $col{2}==1) { $stop['opacity'] = ord($col{3})/100; } // transparency converted from rgba or cmyka()
664
665 if (isset($el[1]) && preg_match('/(\d+)[%]/',$el[1],$m)) {
666 $stop['offset'] = $m[1]/100;
667 if ($stop['offset']>1) { unset($stop['offset']); }
668 }
669 else if (isset($el[1]) && preg_match('/([0-9.]+(px|em|ex|pc|pt|cm|mm|in))/i',$el[1],$m)) {
670 $tmp = $this->mpdf->ConvertSize($m[1],$this->mpdf->w,$this->mpdf->FontSize,false);
671 $stop['offset'] = $el[1];
672 }
673 $g['stops'][] = $stop;
674 }
675 if (count($g['stops'] )) { return $g; }
676 }
677 return array();
678}
679
680function parseBackgroundGradient($bg) {
681 // background-gradient: linear #00FFFF #FFFF00 0 0.5 1 0.5; or
682 // background-gradient: radial #00FFFF #FFFF00 0.5 0.5 1 1 1.2;
683
684 $v = trim($bg);
685 $bgr = preg_split('/\s+/',$v);
686 $g = array();
687 if (count($bgr)> 6) {
688 if (strtoupper(substr($bgr[0],0,1)) == 'L' && count($bgr)==7) { // linear
689 $g['type'] = 2;
690 //$coords = array(0,0,1,1 ); // 0 0 1 0 or 0 1 1 1 is L 2 R; 1,1,0,1 is R2L; 1,1,1,0 is T2B; 1,0,1,1 is B2T
691 // Linear: $coords - array of the form (x1, y1, x2, y2) which defines the gradient vector (see linear_gradient_coords.jpg).
692 // The default value is from left to right (x1=0, y1=0, x2=1, y2=0).
693 $g['coords'] = array($bgr[3], $bgr[4], $bgr[5], $bgr[6]);
694 }
695 else if (count($bgr)==8) { // radial
696 $g['type'] = 3;
697 // Radial: $coords - array of the form (fx, fy, cx, cy, r) where (fx, fy) is the starting point of the gradient with color1,
698 // (cx, cy) is the center of the circle with color2, and r is the radius of the circle (see radial_gradient_coords.jpg).
699 // (fx, fy) should be inside the circle, otherwise some areas will not be defined
700 $g['coords'] = array($bgr[3], $bgr[4], $bgr[5], $bgr[6], $bgr[7]);
701 }
702 $g['colorspace'] = 'RGB';
703 // mPDF 5.3.74
704 $cor = $this->mpdf->ConvertColor($bgr[1]);
705 if ($cor{0}==1) $g['colorspace'] = 'Gray';
706 else if ($cor{0}==4 || $cor{0}==6) $g['colorspace'] = 'CMYK';
707 if ($cor) { $g['col'] = $cor; }
708 else { $g['col'] = $this->mpdf->ConvertColor(255); }
709 $cor = $this->mpdf->ConvertColor($bgr[2]);
710 if ($cor) { $g['col2'] = $cor; }
711 else { $g['col2'] = $this->mpdf->ConvertColor(255); }
712 $g['extend'] = array('true','true');
713 $g['stops'] = array(array('col'=>$g['col'], 'opacity'=>1, 'offset'=>0), array('col'=>$g['col2'], 'opacity'=>1, 'offset'=>1));
714 return $g;
715 }
716 return false;
717}
718
719
720
721}
722
723?> \ No newline at end of file
diff --git a/inc/3rdparty/libraries/mpdf/classes/indic.php b/inc/3rdparty/libraries/mpdf/classes/indic.php
new file mode 100644
index 00000000..0573053b
--- /dev/null
+++ b/inc/3rdparty/libraries/mpdf/classes/indic.php
@@ -0,0 +1,433 @@
1<?php
2
3class indic {
4
5function indic() {
6
7}
8
9
10function substituteIndic($earr, $lang, $font) {
11 global $voltdata;
12
13 if (!isset($voltdata[$font])) {
14 include_once(_MPDF_PATH.'includes/'.$font.'.volt.php');
15 $voltdata[$font] = $volt;
16 }
17
18 foreach($earr as $eid=>$char) {
19 $earr[$eid] = sprintf("%04s", strtoupper(dechex($char)));
20 }
21 $vstr = "0020 ".implode(" ",$earr)." 0020";
22 //============================
23 // Common Indic Punctuation marks
24 // If NOT devanagari
25 if ($lang!='hi') {
26 $vstr = str_replace('0964','007C', $vstr); // U+0964 replace with "|"
27 $vstr = str_replace('0965','007C 007C', $vstr); // U+0964 replace with "|"
28 }
29 //============================
30 // Tamil numeral for Zero missing Added mPDF 4.2
31 if ($lang=='ta') {
32 $vstr = str_replace('0BE6','0030', $vstr); // U+0BEB replace with "0"
33 }
34
35 //============================
36 // Re-order vowels
37
38 // DEVANAGARI vowel sign matraI[093F] before consonant
39 if ($lang=='hi') {
40 $prebasedvowels = "(093F)";
41 $nukta = "093C";
42 $halant = "094D";
43 $vstr = preg_replace('/([A-F0-9]{4}) '.$prebasedvowels.'/','\\2 \\1', $vstr); // vowel sign pre-based shift left
44 $vstr = preg_replace('/([A-F0-9]{4}) '.$prebasedvowels.' '.$nukta.'/','\\2 \\1 '.$nukta, $vstr); // before NUKTA
45 $vstr = preg_replace('/([A-F0-9]{4}) '.$halant.' '.$prebasedvowels.'/','\\2 \\1 '.$halant, $vstr); // before CHAR HALANT == VIRAMA
46 }
47
48 // BENGALI vowels [09BF 09C7 09C8]
49 else if ($lang=='bn') {
50
51 // Khanda Ta 09CE not in font -> replace with 09A4|09CD
52 $vstr = preg_replace('/09CE/','09A4 09CD 200D', $vstr); // mPDF 5.3.09
53
54 // BENGALI double-part vowels [09CB 09C7 09BE][09CC 09C7 09D7]
55 $vstr = str_replace('09CB','09C7 09BE', $vstr); // convert to 2 parts
56 $vstr = str_replace('09CC','09C7 09D7', $vstr); // 09C7 pre-based is then shifted below
57 $prebasedvowels = "(09BF|09C7|09C8)";
58 $nukta = "09BC";
59 $halant = "09CD";
60 // mPDF 5.0.044
61 $bnfullcons = "0995|0996|0997|0998|0999|099A|099B|099C|099D|099F|09A0|09A1|09A2|09A3|09A4|09A5|09A6|09A7|09A8|09AA|09AB|09AC|09AD|09AE|09AF|09B0|09B2|09B6|09B7|09B8|09B9|09DC|09DD|09DF";
62 $vstr = preg_replace('/([A-F0-9]{4}) '.$prebasedvowels.'/','\\2 \\1', $vstr); // vowel sign pre-based shift left
63 $vstr = preg_replace('/([A-F0-9]{4}) '.$prebasedvowels.' '.$nukta.'/','\\2 \\1 '.$nukta, $vstr); // before NUKTA
64 $vstr = preg_replace('/([A-F0-9]{4}) '.$halant.' '.$prebasedvowels.'/','\\2 \\1 '.$halant, $vstr); // before CHAR HALANT
65 // mPDF 5.0.044
66 // .. and shifting left-based vowel further to the left in case 3 consonants together.
67 $vstr = preg_replace('/('.$bnfullcons.') '.$halant.' '.$prebasedvowels.'/','\\2 \\1 '.$halant, $vstr);
68
69 // mPDF 5.0.044
70 // If left-based vowel has now been shifted to left of RA/Halant (09B0/09CD)
71 // Convert here to above-line form (E068) as it would get missed later
72 // e.g. 09B0 09CD 09AD 09C7 would be changed above =>
73 // e.g. 09C7 09B0 09CD 09AD. The 09B0 09CD should => E068
74 // ??? need to add 09BF as well (09BF|09C7|09C8)
75 $vstr = preg_replace('/(09C7|09C8) 09B0 09CD/', '\\1 E068', $vstr);
76
77 }
78
79 // GUJARATI pre-based vowel [0ABF]
80 else if ($lang=='gu') {
81 $prebasedvowels = "(0ABF)";
82 $nukta = "0ABC";
83 $halant = "0ACD";
84 $vstr = preg_replace('/([A-F0-9]{4}) '.$prebasedvowels.'/','\\2 \\1', $vstr); // vowel sign pre-based shift left
85 $vstr = preg_replace('/([A-F0-9]{4}) '.$prebasedvowels.' '.$nukta.'/','\\2 \\1 '.$nukta, $vstr); // before NUKTA
86 $vstr = preg_replace('/([A-F0-9]{4}) '.$halant.' '.$prebasedvowels.'/','\\2 \\1 '.$halant, $vstr); // before CHAR HALANT
87 }
88
89 // GURMUKHI/PUNJABI pre-based vowel [0ABF]
90 else if ($lang=='pa') {
91 $prebasedvowels = "(0A3F)";
92 $nukta = "0A3C";
93 $halant = "0A4D";
94 $vstr = preg_replace('/([A-F0-9]{4}) '.$prebasedvowels.'/','\\2 \\1', $vstr); // vowel sign pre-based shift left
95 $vstr = preg_replace('/([A-F0-9]{4}) '.$prebasedvowels.' '.$nukta.'/','\\2 \\1 '.$nukta, $vstr); // before NUKTA
96 $vstr = preg_replace('/([A-F0-9]{4}) '.$halant.' '.$prebasedvowels.'/','\\2 \\1 '.$halant, $vstr); // before CHAR HALANT
97 }
98
99 // TAMIL pre-based vowel [0ABF]
100 else if ($lang=='ta') {
101 // Shrii (Shree)
102 $vstr = preg_replace('/0BB6 0BCD 0BB0 0BC0/','E04B', $vstr);
103
104 // TAMIL double-part vowels [0BCA 0BC6 0BBE][0BCB 0BC7 0BBE][0BCC 0BC6 0BD7]
105 $vstr = preg_replace('/0BCA/','0BC6 0BBE', $vstr); // convert to 2 parts
106 $vstr = preg_replace('/0BCB/','0BC7 0BBE', $vstr); // pre-based is then shifted below
107 $vstr = preg_replace('/0BCC/','0BC6 0BD7', $vstr);
108 $prebasedvowels = "(0BC6|0BC7|0BC8)";
109 // No nukta
110 $halant = "0BCD"; // Doesn't seem to move most in front of halanted consonants
111 $vstr = preg_replace('/([A-F0-9]{4}) '.$prebasedvowels.'/','\\2 \\1', $vstr); // vowel sign pre-based shift left
112 // ? Only for special case KSS (already moved to left of 0BB7)
113 $vstr = preg_replace('/0B95 '.$halant.' '.$prebasedvowels.' 0BB7/','\\1 0B95 '.$halant.' 0BB7', $vstr);
114 }
115
116 // ORIYA
117 else if ($lang=='or') {
118 // ORIYA double-part vowels []
119 $vstr = str_replace('0B48','0B47 0B56', $vstr); // 2-part Vowel
120 $vstr = str_replace('0B4B','0B47 0B3E', $vstr); // 2-part Vowel
121 $vstr = str_replace('0B4C','0B47 0B57', $vstr); // 2-part Vowel
122 $orprebasedvowels = "(0B47)";
123 // No nukta
124 $halant = "0B4D";
125 $vstr = preg_replace('/([A-F0-9]{4}) '.$orprebasedvowels.'/','\\2 \\1', $vstr); // vowel sign pre-based shift left
126 $vstr = preg_replace('/([A-F0-9]{4}) '.$halant.' '.$orprebasedvowels.'/','\\2 \\1 '.$halant, $vstr); // before CHAR HALANT
127 $vstr = preg_replace('/([A-F0-9]{4}) '.$halant.' '.$orprebasedvowels.'/','\\2 \\1 '.$halant, $vstr); // before CHAR HALANT
128 }
129
130 // MALAYALAM
131 else if ($lang=='ml') {
132 // Chillus - old forms - remove ZWNJ after
133 // This font Volt rules recognises e.g. "Na Halant(Virama)" as ChilluN
134 $vstr = preg_replace('/(0D23 0D4D|0D28 0D4D|0D30 0D4D|0D32 0D4D|0D33 0D4D) 200D/','\\1', $vstr);
135 // See Chillus in Unicode [http://en.wikipedia.org/wiki/Malayalam_script]
136 $vstr = str_replace('0D7A','0D23 0D4D', $vstr); // [mlymChilluNn]
137 $vstr = str_replace('0D7B','0D28 0D4D', $vstr); // [mlymChilluN]
138 $vstr = str_replace('0D7C','0D30 0D4D', $vstr); // [mlymChilluR]
139 $vstr = str_replace('0D7D','0D32 0D4D', $vstr); // [mlymChilluL]
140 $vstr = str_replace('0D7E','0D33 0D4D', $vstr); // [mlymChilluLl]
141/*
142 // Chillus - 0D7A-0D7E not in font directly, but as E005-E009
143 $vstr = preg_replace('/0D23 0D4D 200D/','0D7A', $vstr);
144 $vstr = preg_replace('/0D28 0D4D 200D/','0D7B', $vstr);
145 $vstr = preg_replace('/0D30 0D4D 200D/','0D7C', $vstr);
146 $vstr = preg_replace('/0D32 0D4D 200D/','0D7D', $vstr);
147 $vstr = preg_replace('/0D33 0D4D 200D/','0D7E', $vstr);
148
149 $vstr = preg_replace('/0D7F/','E004', $vstr); // [mlymChilluK]
150 $vstr = preg_replace('/0D7A/','E005', $vstr); // [mlymChilluNn]
151 $vstr = preg_replace('/0D7B/','E006', $vstr); // [mlymChilluN]
152 $vstr = preg_replace('/0D7C/','E007', $vstr); // [mlymChilluR]
153 $vstr = preg_replace('/0D7D/','E008', $vstr); // [mlymChilluL]
154 $vstr = preg_replace('/0D7E/','E009', $vstr); // [mlymChilluLl]
155*/
156
157 // MALAYALAM double-part vowels []
158 $vstr = str_replace('0D4A','0D46 0D3E', $vstr); // 2-part Vowel
159 $vstr = str_replace('0D4B','0D47 0D3E', $vstr); // 2-part Vowel
160 $vstr = str_replace('0D4C','0D46 0D57', $vstr); // 2-part Vowel
161 $mlprebasedvowels = "(0D46|0D47|0D48)";
162 // No nukta
163 $halant = "0D4D";
164 $vstr = preg_replace('/([A-F0-9]{4}) '.$mlprebasedvowels.'/','\\2 \\1', $vstr); // vowel sign pre-based shift left
165 $vstr = preg_replace('/([A-F0-9]{4}) '.$halant.' '.$mlprebasedvowels.'/','\\2 \\1 '.$halant, $vstr); // before CHAR HALANT
166 }
167
168 // TELUGU
169 else if ($lang=='te') {
170 // TELUGU double-part vowels [0C48 -> 0C46 0C56]
171 $vstr = str_replace('0C48','0C46 0C56', $vstr); // 2-part Vowel
172 $prebasedvowels = "(0C46)";
173 $abvvowels = "(0C3E|0C3F|0C40|0C46|0C47|0C4A|0C4B|0C4C|0C55)";
174 // No nukta
175 $halant = "0C4D";
176 $tefullforms = "0C15|0C17|0C18|0C1A|0C1B|0C1C|0C1D|0C20|0C21|0C22|0C24|0C25|0C26|0C27|0C28|0C2A|0C2B|0C2D|0C2E|0C2F|0C30|0C33|0C35|0C36|0C37|0C38|0C39|E028|E029|E02A|E02B|E078|E07A|E07B";
177 $vstr = preg_replace('/('.$tefullforms .') '.$halant.' ('.$tefullforms .') '.$abvvowels .'/','\\1 \\3 '.$halant.' \\2', $vstr); // before HALANT
178 }
179
180
181 // KANNADA
182 else if ($lang=='kn') {
183 // KANNADA double-part vowels [0CC8 -> 0CC6 0CD6]
184 $vstr = str_replace('0CC0','0CBF 0CD5', $vstr); // 2-part Vowel
185 $vstr = str_replace('0CC7','0CC6 0CD5', $vstr); // 2-part Vowel
186 $vstr = str_replace('0CC8','0CC6 0CD6', $vstr); // 2-part Vowel AI - no glyph for single
187 $vstr = str_replace('0CCA','0CC6 0CC2', $vstr); // 2-part Vowel
188 $vstr = str_replace('0CCB','0CC6 0CC2 0CD5', $vstr); // 2-part Vowel
189 $prebasedvowels = "(0CBF|0CC6)";
190 $halant = "0CCD";
191 }
192
193
194 //============================
195
196 // SPECIALS
197
198 // DEVANAGARI Ra Halant Ra
199 if ($lang=='hi') {
200 $vstr = str_replace('0930 094D 0930','E05D 0930', $vstr); // Ra Halant Ra => halfRa FullRa
201 }
202
203 // GUJARATI
204 if ($lang=='gu') {
205 $vstr = str_replace('0AB0 0AC2','E02E', $vstr); // Ra VowelUu => SpecialForm RaUu
206 }
207
208 // TELUGU Ra Halant <Consonant> Halant => halfRa Halant<Consonant> Halant
209 if ($lang=='te') {
210 $vstr = preg_replace('/0C30 0C4D ([A-F0-9]{4}) 0C4D/','E021 0C4D \\1 0C4D', $vstr);
211 }
212
213 // KANNADA
214 // Reph at end of word becomes E0CC instead of E00B
215 if ($lang=='kn') {
216 $vstr = str_replace('0CB0 0CCD 0020','E0CC 0020', $vstr); // mPDF 5.3.87
217 }
218
219
220 //============================
221 // MAIN BIT FROM VOLT RULES
222 foreach($voltdata[$font] AS $rid=>$reps) {
223//echo $rid . ': ' . $vstr.'<br />';
224 $vstr = preg_replace('/'.$reps['match'].'/',$reps['replace'], $vstr);
225 }
226//echo $vstr.'<br />'; exit;
227
228
229 //============================
230
231 // SPECIALS
232
233 // KANNADA
234 // <Base> <BelowBase1> [<BelowBase2> ] MatraI -> <Base/MatraI ligature> <Belowbase1> etc
235 if ($lang=='kn') {
236 $matraI = "0CBF";
237 $knbase = preg_split('/\|/', "0C95|0C96|0C97|0C98|0C9A|0C9B|0C9C|0C9D|0CA0|0CA1|0CA2|0CA3|0CA4|0CA5|0CA6|0CA7|0CA8|0CAA|0CAB|0CAC|0CAD|0CAE|0CAF|0CB0|0CB2|0CB3|0CB5|0CB6|0CB7|0CB8|0CB9|E0A3|E07D|E07E");
238 $knmatraIligature = preg_split('/\|/', "E082|E083|E084|E085|E086|E087|E088|E089|E08A|E08B|E08C|E08D|E08E|E08F|E090|E091|E092|E093|E094|E095|E096|E097|E098|E099|E09A|E09B|E09C|E09D|E09E|E09F|E0A0|E0A4|E0A1|E0A2");
239 $belowbase1 = "E02E|E02F|E030|E031|E032|E033|E034|E035|E036|E037|E038|E039|E03A|E03B|E03C|E03D|E03E|E03F|E040|E041|E042|E043|E044|E045|E046|E047|E048|E049|E04A|E04B|E04C|E04D|E04E|E04F|E050|E081";
240 $belowbase2 = "E052|E053|E054|E055|E056|E057|E058|E059|E05A|E05B|E05C|E05D|E05E|E05F|E060|E061|E062|E063|E064|E065|E066|E067|E068|E069|E06A|E06B|E06C|E06D|E06E|E06F|E070|E071|E072|E073|E074|E081";
241 for ($i=0; $i<count($knbase);$i++) {
242 $vstr = preg_replace('/'.$knbase[$i].' ('.$belowbase1.') ('.$belowbase2.') '.$matraI.'/', $knmatraIligature[$i].' \\1 \\2', $vstr);
243 $vstr = preg_replace('/'.$knbase[$i].' ('.$belowbase1.') '.$matraI.'/', $knmatraIligature[$i].' \\1', $vstr);
244 }
245 }
246
247 // KANNADA
248 // [KanTtaFull] [matraI] => [KanTtaPartial] [matraI]
249 if ($lang=='kn') {
250 $vstr = preg_replace('/0C9F '.$matraI.'/', 'E015 '.$matraI, $vstr);
251 }
252
253 // ORIYA
254 if ($lang=='or') {
255 // SpecialCase Ra[0B30] Halant still left before [oryaFullNnNna] => E00F
256 $vstr = preg_replace('/0B30 '.$halant.' E00F/','E00F E069', $vstr); // convert to Reph
257 }
258
259 //============================
260 // SHIFT REPH
261
262 // DEVANAGARI Shift Reph [E015]
263 if ($lang=='hi') {
264 // FIRSTLY - halfRa = E05D - Change this to Reph [E015]
265 $himatchhalfforms = "E043|E044|E045|E046|E047|E048|E049|E04A|E04B|E04C|E04D|E04E|E04F|E050|E051|E052|E053|E054|E055|E056|E057|E058|E059|E05A|E05B|E05C|E05D|E05E|E05F|E060|E061|E062|E063|E064|E065|E066|E067|E068|E069|E06A|E06B|E06C|E06D|E06E|E06F|E070|E071|E072|E073|E074|E075|E076|E077|E078|E079|E07A|E07B|E07C|E07D|E07E|E07F|E080|E081|E082|E083|E084|E085|E086|E087|E088|E089|E08A|E0D3|E0D4|E0D5|E0D6|E0D7|E0D8|E0D9|E0DA|E0DB|E0DC|E0DD|E0DE|E0DF|E0E0|E0E1|E0E2|E0E3|E0E4|E0E5|E0E6|E0E7|E0E8|E0E9|E0EA|E0EB|E0EC|E0ED|E0EE|E0EF|E0F0|E0F1|E0F2|E0F3|E0F4|E0F5|E0F6|E0F7|E0F8|E0F9|E0FA|E0FB|E0FC|E0FD|E0FE|E0FF|E100|E101|E102|E103|E104|E105|E106|E107|E108|E109|E10A|E10B|E10C|E10D|E10E|E10F|E110|E111|E112|E113|E114|E115|E116|E117|E118|E119|E11A|E13D|E13E|E13F|E140|E141|E142|E143|E144|E145";
266 $himatchfullforms = "0915|0916|0917|0918|0919|091A|091B|091C|091D|091E|091F|0920|0921|0922|0923|0924|0925|0926|0927|0928|092A|092B|092C|092D|092E|092F|0930|0932|0933|0935|0936|0937|0938|0939|E028|E029|0958|0959|095A|E02A|E02B|E02C|E02D|095B|E02E|E02F|E030|E031|095C|095D|E032|E033|E034|E035|E036|0929|E037|095E|E038|E039|E03A|095F|0931|E03B|0934|E03C|E03D|E03E|E03F|E040|E041|E042|E08B|E08C|E08D|E08E|E08F|E090|E091|E092|E093|E094|E095|E096|E097|E098|E099|E09A|E09B|E09C|E09D|E09E|E09F|E0A0|E0A1|E0A2|E0A3|E0A4|E0A5|E0A6|E0A7|E0A8|E0A9|E0AA|E0AB|E0AC|E0AD|E0AE|E0AF|E0B0|E0B1|E0B2|E0B3|E0B4|E0B5|E0B6|E0B7|E0B8|E0B9|E0BA|E0BB|E0BC|E0BD|E0BE|E0BF|E0C0|E0C1|E0C2|E0C3|E0C4|E0C5|E0C6|E0C7|E0C8|E0C9|E0CA|E0CB|E0CC|E0CD|E0CE|E0CF|E0D0|E0D1|E0D2|E11E|E11F|E120|E121|E122|E123|E124|E125|E126|E127|E128|E129|E12A|E12B|E12C|E12D|E12E|E12F|E130|E131|E132|E133";
267 $vstr = preg_replace('/E05D ('.$himatchhalfforms.'|'.$himatchfullforms.')/', 'E015 \\1', $vstr);
268
269 // Reph = E015 - Shift Right to just after end of syllable
270 // FullAllForms + HalfAllForms + 093E matraA
271 while(preg_match('/E015 ('.$himatchhalfforms.')/', $vstr)) {
272 $vstr = preg_replace('/E015 ('.$himatchhalfforms.')/', '\\1 E015', $vstr);
273 }
274 $vstr = preg_replace('/E015 ('.$himatchfullforms.')/', '\\1 E015', $vstr);
275
276 // Now shift it beyond post-based vowels // ??? Need to add others e.g. 0949,094A,094B,094C + presentation forms like E198
277 $vstr = str_replace('E015 093E', '093E E015', $vstr);
278 $vstr = preg_replace('/E015 (0940|E194|E195|E196|E197|E198)/', '\\1 E014', $vstr); // (Small) reph [E014] to Right of matraI
279 $vstr = str_replace('E015 0947', '0947 E014', $vstr); // (Small) reph [E014] to Right of matraI
280 }
281
282 // BENGALI Shift Reph [E068]
283 else if ($lang=='bn') {
284 $bnfullconjuncts = "E002|E003|E004|E041|E042|E043|E044|E045|E046|E047|E048|E049|E04A|E04B|E04C|E04D|E04E|E04F|E050|E051|E052|E053|E054|E055|E056|E057|E058|E059|E05A|E05B|E05C|E05D|E05E|E05F|E060|E061|E062|E063|E064|E065|E06A|E06B|E06C|E06D|E06E|E06F|E070|E071|E072|E073|E074|E075|E076|E077|E078|E079|E07A|E07B|E07C|E07D|E07E|E07F|E080|E081|E082|E083|E084|E085|E086|E087|E088|E089|E08A|E08B|E08C|E08D|E08E|E08F|E090|E091|E092|E093|E094|E095|E096|E097|E098|E099|E09A|E09B|E09C|E09D|E09E|E09F|E0A0|E0A1|E0A2|E0A3|E0A4|E0A5|E0A6|E0A7|E0A8|E0A9|E0AA|E0AB|E0AC|E0AD|E0AE|E0AF|E0B0|E0B1|E0B2|E0B3|E0B4|E0B5|E0B6|E0B7|E0B8|E0B9|E0BA|E0BB|E0BC|E0BD|E0BE|E0BF|E0C0|E0C1|E0C2|E0C3|E0C4|E0C5|E0C6|E0C7|E0C8|E0C9|E0CA|E0CB|E0CC|E0CD|E0CE|E0CF|E0D0|E0D1|E0D2|E0D3|E0D4|E0D5|E0D6|E0D7|E0D8|E0D9|E0DA|E0DB|E0DC|E0DD|E0DE|E0DF|E0E0|E0E1|E0E2|E0E3|E0E4|E0E5|E0E6|E0E7|E0E8|E0E9|E0EA|E0EB|E0EC|E0ED|E0EE|E0EF|E0F0|E0F1|E0F2|E0F3|E0F4|E0F5|E0F6|E0F7|E0F8|E0F9|E0FA|E0FB|E0FC|E0FD|E0FE|E0FF|E100|E101|E102|E103|E104|E105|E106|E107|E108|E109|E10A|E10B|E10C|E10D|E10E|E10F|E110|E111|E112|E113|E114|E115|E116|E117|E118|E119|E11A|E11B|E11C|E11D|E11E|E11F|E120|E121|E122|E123|E124|E125|E126|E127|E128|E129|E12A|E12B|E12C|E12D|E12E|E12F|E130|E131|E132|E133|E134|E135|E136|E137|E138|E139|E13A|E13B|E13C|E13D|E13E|E13F|E140|E141|E142|E143|E144|E145|E146|E147|E148|E149|E14A|E14B|E14C|E14D|E14E|E14F|E150|E151|E152|E153|E154|E155|E156|E157|E158|E159|E15A|E15B|E15C|E15D|E15E|E15F|E160|E161|E162|E163|E164|E165|E166|E167|E168|E169|E16A|E16B|E16C|E16D|E16E|E16F|E170|E171|E172|E173|E174|E175|E176|E177|E178|E179|E17A|E17B|E17C|E17D|E17E|E17F|E180|E181|E182|E183|E184|E185|E186|E187|E188|E189|E18A|E18B|E18C|E18D|E18E|E18F|E190|E191|E192|E193|E194|E195|E196|E197|E198|E199|E19A";
285 // $bnfullcons - set above;
286 $vstr = preg_replace('/E068 ('.$bnfullconjuncts.'|'.$bnfullcons.')/', '\\1 E068', $vstr);
287 // ? Need to shift it beyond post-base vowels 09BE, 09C0, 09D7 haven't found so can't test??
288 $vstr = preg_replace('/E068 (09BE|09C0|09D7)/', '\\1 E068', $vstr);
289 }
290
291 // GUJARATI Shift Reph [E032]
292 else if ($lang=='gu') {
293 $gufullforms = "0A95|0A96|0A97|0A98|0A99|0A9A|0A9B|0A9C|0A9D|0A9E|0A9F|0AA0|0AA1|0AA2|0AA3|0AA4|0AA5|0AA6|0AA7|0AA8|0AAA|0AAB|0AAC|0AAD|0AAE|0AAF|0AB0|0AB2|0AB3|0AB5|0AB6|0AB7|0AB8|0AB9|E002|E003|E004|E005|E006|E007|E008|E009|E00A|E00B|E00C|E00D|E00E|E00F|E010|E011|E012|E013|E014|E015|E016|E017|E018|E019|E01A|E01B|E01C|E01D|E01E|E01F|E020|E021|E022|E023|E024|E025|E026|E027|E05E|E05F|E060|E061|E062|E063|E064|E065|E066|E067|E068|E069|E06A|E06B|E06C|E06D|E06E|E06F|E070|E071|E072|E073|E074|E075|E076|E077|E078|E079|E07A|E07B|E07C|E07D|E07E|E07F|E080|E081|E082|E083|E084|E085|E086|E087|E088|E089|E08A|E08B|E08C|E08D|E08E|E08F|E090|E091|E092|E093|E094|E095|E096|E097|E098|E099|E09A|E09B|E09C|E09D|E09E|E09F|E0A0|E0A1|E0A2|E0A3|E0A4|E0A5";
294 $vstr = preg_replace('/E032 ('.$gufullforms.')/', '\\1 E032', $vstr);
295 // Now shift it beyond post-based vowels // ??? Need to add others e.g. 0949,094A,094B,094C + presentation forms like E198
296 // ? Need to shift it beyond post-base vowels 0ABE, 0AC0 haven't found so can't test??
297 $vstr = preg_replace('/E032 (0ABE|0AC0)/', '\\1 E032', $vstr);
298 }
299
300
301 // TELUGU Shift Reph to LEFT [E046|E069|E077] [TelRaSmallOne] => E046 [TelRaSmallTwo] => E069 [TelRaSmallThree] => E077
302 else if ($lang=='te') {
303 // tefullforms defined earlier
304 $tepartialforms = "E00D|E00E|E00F|E010|E011|E012|E013|E014|E015|E016|E017|E018|E019|E01A|E01B|E01C|E01D|E01E|E01F|E020|E021|E022|E023|E024|E025|E026|E027|E07C|E07D|E07E";
305 $matraligs = "E07F|E080|E081|E082|E083|E084|E085|E086|E087|E088|E089|E08A|E08B|E08C|E08D|E08E|E08F|E090|E091|E092|E093|E094|E095|E096|E097|E098|E099|E09A|E09B|E09C|E09D|E09E|E09F|E0A0|E0A1|E0A2|E0A3|E0A4|E0A5|E0A6|E0A7|E0A8|E0A9|E0AA|E0AB|E0AC|E0AD|E0AE|E0AF";
306 $tevowels = "0C3E|0C3F|0C40|0C46|0C47|0C56|0C4A|0C4B|0C4C"
307 ."|0C41|0C42|0C43|0C44"; // post matras
308 $vstr = preg_replace('/('.$tevowels.') (E046|E069|E077)/', '\\2 \\1', $vstr);
309 while(preg_match('/('.$tepartialforms.') (E046|E069|E077)/', $vstr)) {
310 $vstr = preg_replace('/('.$tepartialforms.') (E046|E069|E077)/', '\\2 \\1', $vstr);
311 }
312 $vstr = preg_replace('/('.$tefullforms .'|'.$matraligs.') (E046|E069|E077)/', '\\2 \\1', $vstr);
313 }
314
315
316 // KANNADA Shift Reph to RIGHT [E00B]
317 else if ($lang=='kn') {
318 $knfullforms = "0C95|0C96|0C97|0C98|0C99|0C9A|0C9B|0C9C|0C9D|0C9E|0C9F|0CA0|0CA1|0CA2|0CA3|0CA4|0CA5|0CA6|0CA7|0CA8|0CAA|0CAB|0CAC|0CAD|0CAE|0CAF|0CB0|0CB1|0CB2|0CB3|0CB5|0CB6|0CB7|0CB8|0CB9|E07D|E07E|E0A3";
319 $knpartialforms = "E00C|E00D|E00E|E00F|E010|E011|E012|E013|E014|0C9E|E015|E016|E017|E018|E019|E01A|E01B|E01C|E01D|E01E|E01F|E020|E021|E022|E023|E024|E025|E026|E027|E028|E029|E02A|E02B|E02C|E02D|E07F";
320 while(preg_match('/E00B ('.$knpartialforms.')/', $vstr)) {
321 $vstr = preg_replace('/E00B ('.$knpartialforms.')/', '\\1 E00B', $vstr);
322 }
323 // mPDF 5.3.47 Also move Reph to right of matraIligatures
324 $knfullforms .= "|E082|E083|E084|E085|E086|E087|E088|E089|E08A|E08B|E08C|E08D|E08E|E08F|E090|E091|E092|E093|E094|E095|E096|E097|E098|E099|E09A|E09B|E09C|E09D|E09E|E09F|E0A0|E0A4|E0A1|E0A2";
325 $vstr = preg_replace('/E00B ('.$knfullforms.')/', '\\1 E00B', $vstr);
326
327 // ? Need to shift it beyond base or below-base forms - haven't found so can't test??
328 // mPDF 5.3.87
329 // E004 added to list (which is a transformed version of 0CBE)
330 $knvowels = "0CBE|0CC0|0CC1|0CC2|0CC3|0CC4|0CC7|0CC8|0CCA|0CCB|0CD5|0CD6|E004";
331 $vstr = preg_replace('/E00B ('.$knvowels.')/', '\\1 E00B', $vstr);
332 }
333
334
335 // ORIYA Shift Reph to RIGHT [E069|E06A|E06B|E06C]
336 else if ($lang=='or') {
337 $orrephs = "E069|E06A|E06B|E06C";
338 $orfullforms = "0B15|0B16|0B17|0B18|0B19|0B1A|0B1B|0B1C|0B1D|0B1E|0B1F|0B20|0B21|0B22|0B23|0B24|0B25|0B26|0B27|0B28|0B29|0B2A|0B2B|0B2C|0B2D|0B2E|0B2F|0B30|0B31|0B32|0B33|0B34|0B35|0B36|0B37|0B38|E003|E004|E005|E006|E007|E008|E009|E00A|E00B|E00C|E00D|E00E|E00F|E010|E011|E012|E013|E014|E015|E016|E017|E018|E019|E01A|E01B|E01C|E01D|E01E|E01F|E020|E021|E022|E023|E024|E025|E026|E027|E028|E029|E02A|E02B|E02C|E02D|E02E|E02F|E030|E031|E032|E033|E034|E035|E036|E037";
339 // E123 - E147 FullHalant forms ? add to FullForms
340 $orpartialforms = "E090|E091|E092|E093|E094|E095|E096|E097|E098|E099|E09A|E09B|E09C|E09D|E09E|E09F|E0A0|E0A1|E0A2|E0A3|E0A4|E0A5|E0A6|E0A7|E0A8|E0A9|E0AA|E0AB|E0AC|E0AD|E0AE|E0AF|E0B0|E0B1|E0B2|E0B3|E0B4|E0B5|E0B6|E0B7|E0B8|E0B9|E0BA|E0BB|E0BC|E0BD|E0BE|E0BF|E0C0|E0C1|E0C2|E0C3|E0C4|E0C5|E0C6|E0C7|E0C8|E0C9|E0CA|E0CB|E0CC|E0CD|E0CE|E0CF|E0D0|E0D1|E0D2|E0D3|E0D4|E0D5|E0D6|E0D7|E0D8|E0D9|E0DA|E0DB|E0DC|E0DD|E0DE|E0DF|E0E0|E0E1|E0E2|E0E3|E0E4|E0E5|E0E6|E0E7|E0E8|E0E9|E0EA|E0EB|E0EC|E0ED|E0EE|E0EF|E0F0|E0F1|E0F2|E0F3|E0F4|E0F5";
341
342 // Combined MatraIReph[E06D] split [0B3F & E069] to allow reph to be shifted forwards
343 $vstr = preg_replace('/('.$orfullforms.') E06D ('.$orfullforms.') 0B3E/', '\\1 0B3F E069 \\2 0B3E', $vstr);
344
345
346 while(preg_match('/('.$orrephs.') ('.$orpartialforms.')/', $vstr)) {
347 $vstr = preg_replace('/('.$orrephs.') ('.$orpartialforms.')/', '\\2 \\1', $vstr);
348 }
349 $vstr = preg_replace('/('.$orrephs.') ('.$orfullforms.')/', '\\2 \\1', $vstr);
350
351
352 // Combine Reph and MatraI
353 $vstr = str_replace('E069 0B3F', 'E06D', $vstr); // Reph and MatraI -> MatraIReph
354 $vstr = str_replace('E06A 0B3F', 'E06E', $vstr); // Reph and MatraI -> MatraIReph
355 $vstr = str_replace('E06B 0B3F', 'E06F', $vstr); // Reph and MatraI -> MatraIReph
356 }
357
358
359 // MALAYALAM Shift Reph to LEFT [E00E] (mlylmRaVattu)
360 else if ($lang=='ml') {
361 $halant = "0D4D";
362 $vstr = preg_replace('/([A-F0-9]{4}) '.$halant.' 0D30/','E00E \\1', $vstr); // 0D30 = Ra
363 $vstr = preg_replace('/([A-F0-9]{4}) '.$halant.' '.$mlprebasedvowels .' 0D30/','\\2 E00E \\1', $vstr); // 0D30 = Ra
364
365 $mlfullforms = "0D15|0D16|0D17|0D18|0D19|0D1A|0D1B|0D1C|0D1D|0D1E|0D1F|0D20|0D21|0D22|0D23|0D24|0D25|0D26|0D27|0D28|0D2A|0D2B|0D2C|0D2D|0D2E|0D2F|0D30|0D31|0D32|0D33|0D34|0D35|0D36|0D37|0D38|0D39"
366 ."|E010|E011|E012|E013|E014|E015|E016|E017|E018|E019|E01A|E01B|E01C|E01D|E01E|E01F|E020|E021|E022|E023|E024|E025|E026|E027|E028|E029|E02A|E02B|E02C|E02D|E02E|E02F|E030|E031|E032|E033|E034|E035|E036|E037|E038|E039|E03A|E03B|E03C|E03D|E03E|E03F|E040|E041|E042|E043|E044|E045|E046|E047|E048|E049|E04A|E04B|E04C|E04D|E04E|E04F|E050|E051|E052|E053|E054|E055|E056|E057|E058|E059|E05A|E05B|E05C|E05D|E05E|E05F|E060|E061|E062|E063|E064|E065|E066|E067|E068|E069|E06A|E06B|E06C|E06D|E06E|E06F|E070|E071|E072|E073|E074|E075|E076|E077|E078|E079|E07A|E07B|E07C|E07D";
367 // = FullConsonants + FullConjuncts
368
369 // = Add Chillu characters // mPDF 5.0.024
370 $mlfullforms .= "|E004|E005|E006|E007|E008|E009";
371 while(preg_match('/('.$mlfullforms.') E00E/', $vstr))
372 $vstr = preg_replace('/('.$mlfullforms.') E00E/', 'E00E \\1', $vstr);
373 }
374
375 //============================
376
377 // SHIFT post-based vowels to Left of SmallForms (NOT to left of full forms)
378
379 // TELUGU Shift
380 if ($lang=='te') {
381 // NB $tevowels defined above
382 // NB $tefullforms defined above
383 $tebelowbase1 = "E02C|E02D|E02E|E02F|E030|E031|E032|E033|E034|E035|E036|E037|E038|E039|E03A|E03B|E03C|E03D|E03E|E03F|E040|E041|E042|E043|E044|E045|E046|E047|E048|E049|E04A|E04B|E04C|E04D|E04E"; //'Small1KaToHa'
384 $tebelowbase2 = "E04F|E050|E051|E052|E053|E054|E055|E056|E057|E058|E059|E05A|E05B|E05C|E05D|E05E|E05F|E060|E061|E062|E063|E064|E065|E066|E067|E068|E069|E06A|E06B|E06C|E06D|E06E|E06F|E070|E071"; // 'Small2KaToHa'
385 $vstr = preg_replace('/('.$tebelowbase2.') ('.$tevowels.')/', '\\2 \\1', $vstr);
386 $vstr = preg_replace('/('.$tebelowbase1.') ('.$tevowels.')/', '\\2 \\1', $vstr);
387 }
388
389
390 // KANNADA Shift
391 else if ($lang=='kn') {
392 $knvowels = "0CBE|0CC0|0CC1|0CC2|0CC3|0CC4|0CC7|0CC8|0CCA|0CCB|0CD5|0CD6"
393 // mPDF 5.3.87 Shouldn't swop E082 and E047 (belowbase1) below
394 // E082 is a matraIligature
395 // ."|E082|E083|E084|E085|E086|E087|E088|E089|E08A|E08B|E08C|E08D|E08E|E08F|E090|E091|E092|E093|E094|E095|E096|E097|E098|E099|E09A|E09B|E09C|E09D|E09E|E09F|E0A0|E0A1|E0A2|E0A3|E0A4|E0A5|E0A6|E0A7|E0A8|E0A9|E0AA|E0AB"
396 ."|E004|E007|E008|E009|E00A";
397
398
399 // NB $knvowels defined above
400 // NB $fullforms defined above
401 // $belowbase1/2 defined above
402 $vstr = preg_replace('/('.$belowbase2.') ('.$knvowels.')/', '\\2 \\1', $vstr);
403 // mPDF 5.3.87
404 $vstr = preg_replace('/('.$belowbase1.') ('.$knvowels.')/', '\\2 \\1', $vstr);
405
406 //$vstr = preg_replace('/('.$fullforms.') ('.$knvowels.')/', '\\2 \\1', $vstr);
407 }
408
409 //============================
410 // Clear unwanted ZWJ, ZWNJ
411 // MALAYALAM
412 if ($lang=='ml') {
413 $vstr = preg_replace('/(200C|200D) /','', $vstr);
414 }
415
416 //============================
417 // END & PUT IT BACK TOGETHER
418 $vstr = preg_replace('/^0020 (.*) 0020$/', '\\1', $vstr);
419
420 $varr = explode(" ",$vstr);
421 $e = '';
422 foreach($varr AS $v) {
423 $e.=code2utf(hexdec($v));
424 }
425 //============================
426
427 return $e;
428}
429
430
431}
432
433?> \ No newline at end of file
diff --git a/inc/3rdparty/libraries/mpdf/classes/meter.php b/inc/3rdparty/libraries/mpdf/classes/meter.php
new file mode 100644
index 00000000..46fa9d5a
--- /dev/null
+++ b/inc/3rdparty/libraries/mpdf/classes/meter.php
@@ -0,0 +1,224 @@
1<?php
2
3class meter {
4
5
6function __construct() {
7
8}
9
10function makeSVG($tag, $type, $value, $max, $min, $optimum, $low, $high) {
11 $svg = '';
12 if ($tag == 'meter') {
13
14 if ($type=='2') {
15 /////////////////////////////////////////////////////////////////////////////////////
16 ///////// CUSTOM <meter type="2">
17 /////////////////////////////////////////////////////////////////////////////////////
18 $h = 10;
19 $w = 160;
20 $border_radius = 0.143; // Factor of Height
21
22 $svg = '<?xml version="1.0" encoding="UTF-8"?>
23 <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
24 <svg width="'.$w.'px" height="'.$h.'px" viewBox="0 0 '.$w.' '.$h.'" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" ><g>
25
26
27 <defs>
28 <linearGradient id="GrGRAY" x1="0" y1="0" x2="0" y2="1" gradientUnits="boundingBox">
29 <stop offset="0%" stop-color="rgb(222, 222, 222)" />
30 <stop offset="20%" stop-color="rgb(232, 232, 232)" />
31 <stop offset="25%" stop-color="rgb(232, 232, 232)" />
32 <stop offset="100%" stop-color="rgb(182, 182, 182)" />
33 </linearGradient>
34
35 </defs>
36';
37 $svg .= '<rect x="0" y="0" width="'.$w.'" height="'.$h.'" fill="#f4f4f4" stroke="none" />';
38
39 // LOW to HIGH region
40 //if ($low && $high && ($low != $min || $high != $max)) {
41 if ($low && $high) {
42 $barx = (($low-$min) / ($max-$min) ) * $w;
43 $barw = (($high-$low) / ($max-$min) ) * $w;
44 $svg .= '<rect x="'.$barx.'" y="0" width="'.$barw.'" height="'.$h.'" fill="url(#GrGRAY)" stroke="#888888" stroke-width="0.5px" />';
45 }
46
47 // OPTIMUM Marker (? AVERAGE)
48 if ($optimum) {
49 $barx = (($optimum-$min) / ($max-$min) ) * $w;
50 $barw = $h/2;
51 $barcol = '#888888';
52 $svg .= '<rect x="'.$barx.'" y="0" rx="'.($h*$border_radius).'px" ry="'.($h*$border_radius).'px" width="'.$barw.'" height="'.$h.'" fill="'.$barcol.'" stroke="none" />';
53 }
54
55 // VALUE Marker
56 if ($value) {
57 if ($min != $low && $value < $low) { $col = 'orange'; }
58 else if ($max != $high && $value > $high) { $col = 'orange'; }
59 else { $col = '#008800'; }
60 $cx = (($value-$min) / ($max-$min) ) * $w;
61 $cy = $h/2;
62 $rx = $h/3.5;
63 $ry = $h/2.2;
64 $svg .= '<ellipse fill="'.$col.'" stroke="#000000" stroke-width="0.5px" cx="'.$cx.'" cy="'.$cy.'" rx="'.$rx.'" ry="'.$ry.'"/>';
65 }
66
67 // BoRDER
68 $svg .= '<rect x="0" y="0" width="'.$w.'" height="'.$h.'" fill="none" stroke="#888888" stroke-width="0.5px" />';
69
70 $svg .= '</g></svg>';
71 }
72 else {
73 /////////////////////////////////////////////////////////////////////////////////////
74 ///////// DEFAULT <meter>
75 /////////////////////////////////////////////////////////////////////////////////////
76 $h = 10;
77 $w = 50;
78 $border_radius = 0.143; // Factor of Height
79
80 $svg = '<?xml version="1.0" encoding="UTF-8"?>
81 <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
82 <svg width="'.$w.'px" height="'.$h.'px" viewBox="0 0 '.$w.' '.$h.'" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" ><g>
83
84 <defs>
85 <linearGradient id="GrGRAY" x1="0" y1="0" x2="0" y2="1" gradientUnits="boundingBox">
86 <stop offset="0%" stop-color="rgb(222, 222, 222)" />
87 <stop offset="20%" stop-color="rgb(232, 232, 232)" />
88 <stop offset="25%" stop-color="rgb(232, 232, 232)" />
89 <stop offset="100%" stop-color="rgb(182, 182, 182)" />
90 </linearGradient>
91
92 <linearGradient id="GrRED" x1="0" y1="0" x2="0" y2="1" gradientUnits="boundingBox">
93 <stop offset="0%" stop-color="rgb(255, 162, 162)" />
94 <stop offset="20%" stop-color="rgb(255, 218, 218)" />
95 <stop offset="25%" stop-color="rgb(255, 218, 218)" />
96 <stop offset="100%" stop-color="rgb(255, 0, 0)" />
97 </linearGradient>
98
99 <linearGradient id="GrGREEN" x1="0" y1="0" x2="0" y2="1" gradientUnits="boundingBox">
100 <stop offset="0%" stop-color="rgb(102, 230, 102)" />
101 <stop offset="20%" stop-color="rgb(218, 255, 218)" />
102 <stop offset="25%" stop-color="rgb(218, 255, 218)" />
103 <stop offset="100%" stop-color="rgb(0, 148, 0)" />
104 </linearGradient>
105
106 <linearGradient id="GrBLUE" x1="0" y1="0" x2="0" y2="1" gradientUnits="boundingBox">
107 <stop offset="0%" stop-color="rgb(102, 102, 230)" />
108 <stop offset="20%" stop-color="rgb(238, 238, 238)" />
109 <stop offset="25%" stop-color="rgb(238, 238, 238)" />
110 <stop offset="100%" stop-color="rgb(0, 0, 128)" />
111 </linearGradient>
112
113 <linearGradient id="GrORANGE" x1="0" y1="0" x2="0" y2="1" gradientUnits="boundingBox">
114 <stop offset="0%" stop-color="rgb(255, 186, 0)" />
115 <stop offset="20%" stop-color="rgb(255, 238, 168)" />
116 <stop offset="25%" stop-color="rgb(255, 238, 168)" />
117 <stop offset="100%" stop-color="rgb(255, 155, 0)" />
118 </linearGradient>
119 </defs>
120
121 <rect x="0" y="0" rx="'.($h*$border_radius).'px" ry="'.($h*$border_radius).'px" width="'.$w.'" height="'.$h.'" fill="url(#GrGRAY)" stroke="none" />
122';
123
124 if ($value) {
125 $barw = (($value-$min) / ($max-$min) ) * $w;
126 if ($optimum < $low) {
127 if ($value < $low) { $barcol = 'url(#GrGREEN)'; }
128 else if ($value > $high) { $barcol = 'url(#GrRED)'; }
129 else { $barcol = 'url(#GrORANGE)'; }
130 }
131 else if ($optimum > $high) {
132 if ($value < $low) { $barcol = 'url(#GrRED)'; }
133 else if ($value > $high) { $barcol = 'url(#GrGREEN)'; }
134 else { $barcol = 'url(#GrORANGE)'; }
135 }
136 else {
137 if ($value < $low) { $barcol = 'url(#GrORANGE)'; }
138 else if ($value > $high) { $barcol = 'url(#GrORANGE)'; }
139 else { $barcol = 'url(#GrGREEN)'; }
140 }
141 $svg .= '<rect x="0" y="0" rx="'.($h*$border_radius).'px" ry="'.($h*$border_radius).'px" width="'.$barw.'" height="'.$h.'" fill="'.$barcol.'" stroke="none" />';
142 }
143
144
145 // Borders
146 //$svg .= '<rect x="0" y="0" rx="'.($h*$border_radius).'px" ry="'.($h*$border_radius).'px" width="'.$w.'" height="'.$h.'" fill="none" stroke="#888888" stroke-width="0.5px" />';
147 if ($value) {
148 // $svg .= '<rect x="0" y="0" rx="'.($h*$border_radius).'px" ry="'.($h*$border_radius).'px" width="'.$barw.'" height="'.$h.'" fill="none" stroke="#888888" stroke-width="0.5px" />';
149 }
150
151
152 $svg .= '</g></svg>';
153 }
154 }
155 else { // $tag == 'progress'
156
157 if ($type=='2') {
158 /////////////////////////////////////////////////////////////////////////////////////
159 ///////// CUSTOM <progress type="2">
160 /////////////////////////////////////////////////////////////////////////////////////
161 }
162 else {
163 /////////////////////////////////////////////////////////////////////////////////////
164 ///////// DEFAULT <progress>
165 /////////////////////////////////////////////////////////////////////////////////////
166 $h = 10;
167 $w = 100;
168 $border_radius = 0.143; // Factor of Height
169
170 if ($value or $value==='0') {
171 $fill = 'url(#GrGRAY)';
172 }
173 else {
174 $fill = '#f8f8f8';
175 }
176
177 $svg = '<svg width="'.$w.'px" height="'.$h.'px" viewBox="0 0 '.$w.' '.$h.'"><g>
178
179 <defs>
180 <linearGradient id="GrGRAY" x1="0" y1="0" x2="0" y2="1" gradientUnits="boundingBox">
181 <stop offset="0%" stop-color="rgb(222, 222, 222)" />
182 <stop offset="20%" stop-color="rgb(232, 232, 232)" />
183 <stop offset="25%" stop-color="rgb(232, 232, 232)" />
184 <stop offset="100%" stop-color="rgb(182, 182, 182)" />
185 </linearGradient>
186
187 <linearGradient id="GrGREEN" x1="0" y1="0" x2="0" y2="1" gradientUnits="boundingBox">
188 <stop offset="0%" stop-color="rgb(102, 230, 102)" />
189 <stop offset="20%" stop-color="rgb(218, 255, 218)" />
190 <stop offset="25%" stop-color="rgb(218, 255, 218)" />
191 <stop offset="100%" stop-color="rgb(0, 148, 0)" />
192 </linearGradient>
193
194 </defs>
195
196 <rect x="0" y="0" rx="'.($h*$border_radius).'px" ry="'.($h*$border_radius).'px" width="'.$w.'" height="'.$h.'" fill="'.$fill.'" stroke="none" />
197';
198
199 if ($value) {
200 $barw = (($value-$min) / ($max-$min) ) * $w;
201 $barcol = 'url(#GrGREEN)';
202 $svg .= '<rect x="0" y="0" rx="'.($h*$border_radius).'px" ry="'.($h*$border_radius).'px" width="'.$barw.'" height="'.$h.'" fill="'.$barcol.'" stroke="none" />';
203 }
204
205
206 // Borders
207 $svg .= '<rect x="0" y="0" rx="'.($h*$border_radius).'px" ry="'.($h*$border_radius).'px" width="'.$w.'" height="'.$h.'" fill="none" stroke="#888888" stroke-width="0.5px" />';
208 if ($value) {
209 // $svg .= '<rect x="0" y="0" rx="'.($h*$border_radius).'px" ry="'.($h*$border_radius).'px" width="'.$barw.'" height="'.$h.'" fill="none" stroke="#888888" stroke-width="0.5px" />';
210 }
211
212
213 $svg .= '</g></svg>';
214
215 }
216 }
217
218 return $svg;
219}
220
221
222} // end of class
223
224?> \ No newline at end of file
diff --git a/inc/3rdparty/libraries/mpdf/classes/svg.php b/inc/3rdparty/libraries/mpdf/classes/svg.php
new file mode 100644
index 00000000..3efe50f9
--- /dev/null
+++ b/inc/3rdparty/libraries/mpdf/classes/svg.php
@@ -0,0 +1,2600 @@
1<?php
2// svg class modified for mPDF version 4.4.003 by Ian Back: based on -
3// svg2pdf fpdf class
4// sylvain briand (syb@godisaduck.com), modified by rick trevino (rtrevino1@yahoo.com)
5// http://www.godisaduck.com/svg2pdf_with_fpdf
6// http://rhodopsin.blogspot.com
7//
8// cette class etendue est open source, toute modification devra cependant etre repertoriée~
9
10
11// NB UNITS - Works in pixels as main units - converting to PDF units when outputing to PDF string
12// and on returning size
13
14class SVG {
15
16 var $svg_gradient; // array - contient les infos sur les gradient fill du svg classé par id du svg
17 var $svg_shadinglist; // array - contient les ids des objet shading
18 var $svg_info; // array contenant les infos du svg voulue par l'utilisateur
19 var $svg_attribs; // array - holds all attributes of root <svg> tag
20 var $svg_style; // array contenant les style de groupes du svg
21 var $svg_string; // String contenant le tracage du svg en lui même.
22 var $txt_data; // array - holds string info to write txt to image
23 var $txt_style; // array - current text style
24 var $mpdf_ref;
25 var $xbase; // mPDF 4.4.003
26 var $ybase; // mPDF 4.4.003
27 var $svg_error; // mPDF 4.4.003
28 var $subPathInit; // mPDF 4.4.003
29 var $spxstart; // mPDF 4.4.003
30 var $spystart; // mPDF 4.4.003
31 var $kp; // mPDF 4.4.003 convert pixels to PDF units
32 var $pathBBox; // mPDF 5.0.039
33
34 function SVG(&$mpdf){
35 $this->svg_gradient = array();
36 $this->svg_shadinglist = array();
37 $this->txt_data = array();
38 $this->svg_string = '';
39 $this->svg_info = array();
40 $this->svg_attribs = array();
41 $this->xbase = 0;
42 $this->ybase = 0;
43 $this->svg_error = false;
44 $this->subPathInit = false; // mPDF 4.4.003
45 $this->dashesUsed = false; // mPDF 5.0
46 $this->mpdf_ref =& $mpdf;
47
48 $this->kp = 72 / $mpdf->img_dpi; // mPDF 4.4.003 constant To convert pixels to pts/PDF units
49 $this->kf = 1; // mPDF 5.0.039 constant To convert font size if re-mapped
50 $this->pathBBox = array(); // mPDF 5.0.039
51
52 $this->svg_style = array(
53 array(
54 'fill' => 'black', // mPDF 4.4.008
55 'fill-opacity' => 1, // remplissage opaque par defaut
56 'fill-rule' => 'nonzero', // mode de remplissage par defaut
57 'stroke' => 'none', // pas de trait par defaut
58 'stroke-linecap' => 'butt', // style de langle par defaut
59 'stroke-linejoin' => 'miter', //
60 'stroke-miterlimit' => 4, // limite de langle par defaut
61 'stroke-opacity' => 1, // trait opaque par defaut
62 'stroke-width' => 1, // mPDF 4.4.011
63 'stroke-dasharray' => 0, // mPDF 4.4.003
64 'stroke-dashoffset' => 0, // mPDF 4.4.003
65 'color' => '' // mPDF 4.4.005
66 )
67 );
68
69 $this->txt_style = array(
70 array(
71 'fill' => 'black', // pas de remplissage par defaut
72 'font-family' => $mpdf->default_font,
73 'font-size' => $mpdf->default_font_size, // ****** this is pts
74 'font-weight' => 'normal', // normal | bold
75 'font-style' => 'normal', // italic | normal
76 'text-anchor' => 'start', // alignment: start, middle, end
77/* mPDF 5.0.041 */
78 'fill-opacity' => 1, // remplissage opaque par defaut
79 'fill-rule' => 'nonzero', // mode de remplissage par defaut
80 'stroke' => 'none', // pas de trait par defaut
81 'stroke-opacity' => 1, // trait opaque par defaut
82 'stroke-width' => 1, // mPDF 4.4.011
83 'color' => '' // mPDF 4.4.005
84 )
85 );
86
87
88
89 }
90
91 function svgGradient($gradient_info, $attribs, $element){
92 $n = count($this->mpdf_ref->gradients)+1;
93
94 // Get bounding dimensions of element
95 $w = 100;
96 $h = 100;
97 $x_offset = 0;
98 $y_offset = 0;
99 if ($element=='rect') {
100 $w = $attribs['width'];
101 $h = $attribs['height'];
102 $x_offset = $attribs['x'];
103 $y_offset = $attribs['y'];
104 }
105 else if ($element=='ellipse') {
106 $w = $attribs['rx']*2;
107 $h = $attribs['ry']*2;
108 $x_offset = $attribs['cx']-$attribs['rx'];
109 $y_offset = $attribs['cy']-$attribs['ry'];
110 }
111 else if ($element=='circle') {
112 $w = $attribs['r']*2;
113 $h = $attribs['r']*2;
114 $x_offset = $attribs['cx']-$attribs['r'];
115 $y_offset = $attribs['cy']-$attribs['r'];
116 }
117 else if ($element=='polygon') {
118 $pts = preg_split('/[ ,]+/', trim($attribs['points']));
119 $maxr=$maxb=0;
120 $minl=$mint=999999;
121 for ($i=0;$i<count($pts); $i++) {
122 if ($i % 2 == 0) { // x values
123 $minl = min($minl,$pts[$i]);
124 $maxr = max($maxr,$pts[$i]);
125 }
126 else { // y values
127 $mint = min($mint,$pts[$i]);
128 $maxb = max($maxb,$pts[$i]);
129 }
130 }
131 $w = $maxr-$minl;
132 $h = $maxb-$mint;
133 $x_offset = $minl;
134 $y_offset = $mint;
135 }
136 else if ($element=='path') {
137 // mPDF 5.0.039
138 if (is_array($this->pathBBox) && $this->pathBBox[2]>0) {
139 $w = $this->pathBBox[2];
140 $h = $this->pathBBox[3];
141 $x_offset = $this->pathBBox[0];
142 $y_offset = $this->pathBBox[1];
143 }
144 else {
145 preg_match_all('/([a-z]|[A-Z])([ ,\-.\d]+)*/', $attribs['d'], $commands, PREG_SET_ORDER);
146 $maxr=$maxb=0;
147 $minl=$mint=999999;
148 foreach($commands as $c){
149 if(count($c)==3){
150 list($tmp, $cmd, $arg) = $c;
151 if ($cmd=='M' || $cmd=='L' || $cmd=='C' || $cmd=='S' || $cmd=='Q' || $cmd=='T') {
152 $pts = preg_split('/[ ,]+/', trim($arg));
153 for ($i=0;$i<count($pts); $i++) {
154 if ($i % 2 == 0) { // x values
155 $minl = min($minl,$pts[$i]);
156 $maxr = max($maxr,$pts[$i]);
157 }
158 else { // y values
159 $mint = min($mint,$pts[$i]);
160 $maxb = max($maxb,$pts[$i]);
161 }
162 }
163 }
164 if ($cmd=='H') { // sets new x
165 $minl = min($minl,$arg);
166 $maxr = max($maxr,$arg);
167 }
168 if ($cmd=='V') { // sets new y
169 $mint = min($mint,$arg);
170 $maxb = max($maxb,$arg);
171 }
172 }
173 }
174 $w = $maxr-$minl;
175 $h = $maxb-$mint;
176 $x_offset = $minl;
177 $y_offset = $mint;
178 }
179 }
180 if (!$w || $w==-999999) { $w = 100; }
181 if (!$h || $h==-999999) { $h = 100; }
182 if ($x_offset==999999) { $x_offset = 0; }
183 if ($y_offset==999999) { $y_offset = 0; }
184
185 // mPDF 4.5.010
186 // TRANSFORMATIONS
187 $transformations = '';
188 if (isset($gradient_info['transform'])){
189 preg_match_all('/(matrix|translate|scale|rotate|skewX|skewY)\((.*?)\)/is',$gradient_info['transform'],$m);
190 if (count($m[0])) {
191 for($i=0; $i<count($m[0]); $i++) {
192 $c = strtolower($m[1][$i]);
193 $v = trim($m[2][$i]);
194 $vv = preg_split('/[ ,]+/',$v);
195 if ($c=='matrix' && count($vv)==6) {
196 // mPDF 5.0.039
197 // Note angle of rotation is reversed (from SVG to PDF), so vv[1] and vv[2] are negated
198 // cf svgDefineStyle()
199 $transformations .= sprintf(' %.3F %.3F %.3F %.3F %.3F %.3F cm ', $vv[0], -$vv[1], -$vv[2], $vv[3], $vv[4]*$this->kp, -$vv[5]*$this->kp);
200 }
201 else if ($c=='translate' && count($vv)) {
202 $tm[4] = $vv[0];
203 if (count($vv)==2) { $t_y = -$vv[1]; }
204 else { $t_y = 0; }
205 $tm[5] = $t_y;
206 $transformations .= sprintf(' 1 0 0 1 %.3F %.3F cm ', $tm[4]*$this->kp, $tm[5]*$this->kp);
207 }
208 else if ($c=='scale' && count($vv)) {
209 if (count($vv)==2) { $s_y = $vv[1]; }
210 else { $s_y = $vv[0]; }
211 $tm[0] = $vv[0];
212 $tm[3] = $s_y;
213 $transformations .= sprintf(' %.3F 0 0 %.3F 0 0 cm ', $tm[0], $tm[3]);
214 }
215 else if ($c=='rotate' && count($vv)) {
216 $tm[0] = cos(deg2rad(-$vv[0]));
217 $tm[1] = sin(deg2rad(-$vv[0]));
218 $tm[2] = -$tm[1];
219 $tm[3] = $tm[0];
220 if (count($vv)==3) {
221 $transformations .= sprintf(' 1 0 0 1 %.3F %.3F cm ', $vv[1]*$this->kp, -$vv[2]*$this->kp);
222 }
223 $transformations .= sprintf(' %.3F %.3F %.3F %.3F 0 0 cm ', $tm[0], $tm[1], $tm[2], $tm[3]);
224 if (count($vv)==3) {
225 $transformations .= sprintf(' 1 0 0 1 %.3F %.3F cm ', -$vv[1]*$this->kp, $vv[2]*$this->kp);
226 }
227 }
228 else if ($c=='skewx' && count($vv)) {
229 $tm[2] = tan(deg2rad(-$vv[0]));
230 $transformations .= sprintf(' 1 0 %.3F 1 0 0 cm ', $tm[2]);
231 }
232 else if ($c=='skewy' && count($vv)) {
233 $tm[1] = tan(deg2rad(-$vv[0]));
234 $transformations .= sprintf(' 1 %.3F 0 1 0 0 cm ', $tm[1]);
235 }
236
237 }
238 }
239 }
240
241
242 $return = "";
243
244 // mPDF 5.0.039
245 if (isset($gradient_info['units']) && strtolower($gradient_info['units'])=='userspaceonuse') {
246 if ($transformations) { $return .= $transformations; }
247 }
248 // mPDF 5.0.040
249 $spread = 'P'; // pad
250 if (isset($gradient_info['spread'])) {
251 if (strtolower($gradient_info['spread'])=='reflect') { $spread = 'F'; } // reflect
252 else if (strtolower($gradient_info['spread'])=='repeat') { $spread = 'R'; } // repeat
253 }
254
255
256 for ($i=0; $i<(count($gradient_info['color'])); $i++) {
257 if (stristr($gradient_info['color'][$i]['offset'], '%')!== false) { $gradient_info['color'][$i]['offset'] = ($gradient_info['color'][$i]['offset']+0)/100; }
258 if (stristr($gradient_info['color'][($i+1)]['offset'], '%')!== false) { $gradient_info['color'][($i+1)]['offset'] = ($gradient_info['color'][($i+1)]['offset']+0)/100; }
259 if ($gradient_info['color'][$i]['offset']<0) { $gradient_info['color'][$i]['offset'] = 0; }
260 if ($gradient_info['color'][$i]['offset']>1) { $gradient_info['color'][$i]['offset'] = 1; }
261 if ($i>0) {
262 if ($gradient_info['color'][$i]['offset']<$gradient_info['color'][($i-1)]['offset']) {
263 $gradient_info['color'][$i]['offset']=$gradient_info['color'][($i-1)]['offset'];
264 }
265 }
266 }
267
268 if ($gradient_info['color'][0]['offset']>0) {
269 array_unshift($gradient_info['color'], $gradient_info['color'][0]);
270 $gradient_info['color'][0]['offset'] = 0;
271 }
272 $ns = count($gradient_info['color']);
273 if ($gradient_info['color'][($ns-1)]['offset']<1) {
274 $gradient_info['color'][] = $gradient_info['color'][($ns-1)];
275 $gradient_info['color'][($ns)]['offset'] = 1;
276 }
277 $ns = count($gradient_info['color']);
278
279
280
281
282 if ($gradient_info['type'] == 'linear'){
283 // mPDF 4.4.003
284 if (isset($gradient_info['units']) && strtolower($gradient_info['units'])=='userspaceonuse') {
285 if (isset($gradient_info['info']['x1'])) { $gradient_info['info']['x1'] = ($gradient_info['info']['x1']-$x_offset) / $w; }
286 if (isset($gradient_info['info']['y1'])) { $gradient_info['info']['y1'] = ($gradient_info['info']['y1']-$y_offset) / $h; }
287 if (isset($gradient_info['info']['x2'])) { $gradient_info['info']['x2'] = ($gradient_info['info']['x2']-$x_offset) / $w; }
288 if (isset($gradient_info['info']['y2'])) { $gradient_info['info']['y2'] = ($gradient_info['info']['y2']-$y_offset) / $h; }
289 }
290 if (isset($gradient_info['info']['x1'])) { $x1 = $gradient_info['info']['x1']; }
291 else { $x1 = 0; }
292 if (isset($gradient_info['info']['y1'])) { $y1 = $gradient_info['info']['y1']; }
293 else { $y1 = 0; }
294 if (isset($gradient_info['info']['x2'])) { $x2 = $gradient_info['info']['x2']; }
295 else { $x2 = 1; }
296 if (isset($gradient_info['info']['y2'])) { $y2 = $gradient_info['info']['y2']; }
297 else { $y2 = 0; }
298
299 if (stristr($x1, '%')!== false) { $x1 = ($x1+0)/100; }
300 if (stristr($x2, '%')!== false) { $x2 = ($x2+0)/100; }
301 if (stristr($y1, '%')!== false) { $y1 = ($y1+0)/100; }
302 if (stristr($y2, '%')!== false) { $y2 = ($y2+0)/100; }
303
304 // mPDF 5.0.042
305 $bboxw = $w;
306 $bboxh = $h;
307 $usex = $x_offset;
308 $usey = $y_offset;
309 $usew = $bboxw;
310 $useh = $bboxh;
311 if (isset($gradient_info['units']) && strtolower($gradient_info['units'])=='userspaceonuse') {
312 $angle = rad2deg(atan2(($gradient_info['info']['y2']-$gradient_info['info']['y1']), ($gradient_info['info']['x2']-$gradient_info['info']['x1'])));
313 if ($angle < 0) { $angle += 360; }
314 else if ($angle > 360) { $angle -= 360; }
315 if ($angle!=0 && $angle!=360 && $angle!=90 && $angle!=180 && $angle!=270) {
316 if ($w >= $h) {
317 $y1 *= $h/$w ;
318 $y2 *= $h/$w ;
319 $usew = $useh = $bboxw;
320 }
321 else {
322 $x1 *= $w/$h ;
323 $x2 *= $w/$h ;
324 $usew = $useh = $bboxh;
325 }
326 }
327 }
328 $a = $usew; // width
329 $d = -$useh; // height
330 $e = $usex; // x- offset
331 $f = -$usey; // -y-offset
332
333 $return .= sprintf('%.3F 0 0 %.3F %.3F %.3F cm ', $a*$this->kp, $d*$this->kp, $e*$this->kp, $f*$this->kp);
334
335 // mPDF 5.0.039
336 if (isset($gradient_info['units']) && strtolower($gradient_info['units'])=='objectboundingbox') {
337 if ($transformations) { $return .= $transformations; }
338 }
339
340 // mPDF 5.0.020
341 $trans = false;
342
343 // mPDF 5.0.040
344 if ($spread=='R' || $spread=='F') { // Repeat / Reflect
345 $offs = array();
346 for($i=0;$i<$ns;$i++) {
347 $offs[$i] = $gradient_info['color'][$i]['offset'];
348 }
349 $gp = 0;
350 $inside=true;
351 while($inside) {
352 $gp++;
353 for($i=0;$i<$ns;$i++) {
354 if ($spread=='F' && ($gp % 2) == 1) { // Reflect
355 $gradient_info['color'][(($ns*$gp)+$i)] = $gradient_info['color'][(($ns*($gp-1))+($ns-$i-1))];
356 $tmp = $gp+(1-$offs[($ns-$i-1)]) ;
357 $gradient_info['color'][(($ns*$gp)+$i)]['offset'] = $tmp;
358 }
359 else { // Reflect
360 $gradient_info['color'][(($ns*$gp)+$i)] = $gradient_info['color'][$i];
361 $tmp = $gp+$offs[$i] ;
362 $gradient_info['color'][(($ns*$gp)+$i)]['offset'] = $tmp;
363 }
364 // IF STILL INSIDE BOX OR STILL VALID
365 // Point on axis to test
366 $px1 = $x1 + ($x2-$x1)*$tmp;
367 $py1 = $y1 + ($y2-$y1)*$tmp;
368 // Get perpendicular axis
369 $alpha = atan2($y2-$y1, $x2-$x1);
370 $alpha += M_PI/2; // rotate 90 degrees
371 // Get arbitrary point to define line perpendicular to axis
372 $px2 = $px1+cos($alpha);
373 $py2 = $py1+sin($alpha);
374
375 $res1 = _testIntersect($px1, $py1, $px2, $py2, 0, 0, 0, 1); // $x=0 vert axis
376 $res2 = _testIntersect($px1, $py1, $px2, $py2, 1, 0, 1, 1); // $x=1 vert axis
377 $res3 = _testIntersect($px1, $py1, $px2, $py2, 0, 0, 1, 0); // $y=0 horiz axis
378 $res4 = _testIntersect($px1, $py1, $px2, $py2, 0, 1, 1, 1); // $y=1 horiz axis
379 if (!$res1 && !$res2 && !$res3 && !$res4) { $inside = false; }
380 }
381 }
382
383 $inside=true;
384 $gp = 0;
385 while($inside) {
386 $gp++;
387 $newarr = array();
388 for($i=0;$i<$ns;$i++) {
389 if ($spread=='F') { // Reflect
390 $newarr[$i] = $gradient_info['color'][($ns-$i-1)];
391 if (($gp % 2) == 1) {
392 $tmp = -$gp+(1-$offs[($ns-$i-1)]);
393 $newarr[$i]['offset'] = $tmp;
394 }
395 else {
396 $tmp = -$gp+$offs[$i];
397 $newarr[$i]['offset'] = $tmp;
398 }
399 }
400 else { // Reflect
401 $newarr[$i] = $gradient_info['color'][$i];
402 $tmp = -$gp+$offs[$i];
403 $newarr[$i]['offset'] = $tmp;
404 }
405
406 // IF STILL INSIDE BOX OR STILL VALID
407 // Point on axis to test
408 $px1 = $x1 + ($x2-$x1)*$tmp;
409 $py1 = $y1 + ($y2-$y1)*$tmp;
410 // Get perpendicular axis
411 $alpha = atan2($y2-$y1, $x2-$x1);
412 $alpha += M_PI/2; // rotate 90 degrees
413 // Get arbitrary point to define line perpendicular to axis
414 $px2 = $px1+cos($alpha);
415 $py2 = $py1+sin($alpha);
416
417 $res1 = _testIntersect($px1, $py1, $px2, $py2, 0, 0, 0, 1); // $x=0 vert axis
418 $res2 = _testIntersect($px1, $py1, $px2, $py2, 1, 0, 1, 1); // $x=1 vert axis
419 $res3 = _testIntersect($px1, $py1, $px2, $py2, 0, 0, 1, 0); // $y=0 horiz axis
420 $res4 = _testIntersect($px1, $py1, $px2, $py2, 0, 1, 1, 1); // $y=1 horiz axis
421 if (!$res1 && !$res2 && !$res3 && !$res4) { $inside = false; }
422 }
423 for($i=($ns-1);$i>=0;$i--) {
424 if (isset($newarr[$i]['offset'])) array_unshift($gradient_info['color'], $newarr[$i]);
425 }
426 }
427 }
428
429 // mPDF 4.4.007 Gradient STOPs
430 $stops = count($gradient_info['color']);
431 if ($stops < 2) { return ''; }
432
433 // mPDF 5.0.042
434 $range = $gradient_info['color'][count($gradient_info['color'])-1]['offset']-$gradient_info['color'][0]['offset'];
435 $min = $gradient_info['color'][0]['offset'];
436
437 for ($i=0; $i<($stops); $i++) {
438 // mPDF 5.0.051
439 if (!$gradient_info['color'][$i]['color']) {
440 if ($gradient_info['colorspace']=='RGB') $gradient_info['color'][$i]['color'] = '0 0 0';
441 else if ($gradient_info['colorspace']=='Gray') $gradient_info['color'][$i]['color'] = '0';
442 else if ($gradient_info['colorspace']=='CMYK') $gradient_info['color'][$i]['color'] = '1 1 1 1';
443 }
444 $offset = ($gradient_info['color'][$i]['offset'] - $min)/$range;
445 $this->mpdf_ref->gradients[$n]['stops'][] = array(
446 'col' => $gradient_info['color'][$i]['color'],
447 'opacity' => $gradient_info['color'][$i]['opacity'],
448 'offset' => $offset);
449 if ($gradient_info['color'][$i]['opacity']<1) { $trans = true; }
450 }
451 $grx1 = $x1 + ($x2-$x1)*$gradient_info['color'][0]['offset'];
452 $gry1 = $y1 + ($y2-$y1)*$gradient_info['color'][0]['offset'];
453 $grx2 = $x1 + ($x2-$x1)*$gradient_info['color'][count($gradient_info['color'])-1]['offset'];
454 $gry2 = $y1 + ($y2-$y1)*$gradient_info['color'][count($gradient_info['color'])-1]['offset'];
455
456 $this->mpdf_ref->gradients[$n]['coords']=array($grx1, $gry1, $grx2, $gry2);
457
458 $this->mpdf_ref->gradients[$n]['colorspace'] = $gradient_info['colorspace']; // mPDF 5.0.051
459
460 $this->mpdf_ref->gradients[$n]['type'] = 2;
461 $this->mpdf_ref->gradients[$n]['fo'] = true;
462
463 $this->mpdf_ref->gradients[$n]['extend']=array('true','true');
464 if ($trans) {
465 $this->mpdf_ref->gradients[$n]['trans'] = true;
466 $return .= ' /TGS'.($n).' gs ';
467 }
468 $return .= ' /Sh'.($n).' sh ';
469 $return .= " Q\n";
470 }
471 else if ($gradient_info['type'] == 'radial'){
472 // mPDF 4.4.003
473 if (isset($gradient_info['units']) && strtolower($gradient_info['units'])=='userspaceonuse') {
474 if ($w > $h) { $h = $w; }
475 else { $w = $h; }
476 if (isset($gradient_info['info']['x0'])) { $gradient_info['info']['x0'] = ($gradient_info['info']['x0']-$x_offset) / $w; }
477 if (isset($gradient_info['info']['y0'])) { $gradient_info['info']['y0'] = ($gradient_info['info']['y0']-$y_offset) / $h; }
478 if (isset($gradient_info['info']['x1'])) { $gradient_info['info']['x1'] = ($gradient_info['info']['x1']-$x_offset) / $w; }
479 if (isset($gradient_info['info']['y1'])) { $gradient_info['info']['y1'] = ($gradient_info['info']['y1']-$y_offset) / $h; }
480 if (isset($gradient_info['info']['r'])) { $gradient_info['info']['rx'] = $gradient_info['info']['r'] / $w; }
481 if (isset($gradient_info['info']['r'])) { $gradient_info['info']['ry'] = $gradient_info['info']['r'] / $h; }
482 }
483
484 if ($gradient_info['info']['x0'] || $gradient_info['info']['x0']===0) { $x0 = $gradient_info['info']['x0']; }
485 else { $x0 = 0.5; }
486 if ($gradient_info['info']['y0'] || $gradient_info['info']['y0']===0) { $y0 = $gradient_info['info']['y0']; }
487 else { $y0 = 0.5; }
488 if ($gradient_info['info']['rx'] || $gradient_info['info']['rx']===0) { $rx = $gradient_info['info']['rx']; }
489 else if ($gradient_info['info']['r'] || $gradient_info['info']['r']===0) { $rx = $gradient_info['info']['r']; }
490 else { $rx = 0.5; }
491 if ($gradient_info['info']['ry'] || $gradient_info['info']['ry']===0) { $ry = $gradient_info['info']['ry']; }
492 else if ($gradient_info['info']['r'] || $gradient_info['info']['r']===0) { $ry = $gradient_info['info']['r']; }
493 else { $ry = 0.5; }
494 if ($gradient_info['info']['x1'] || $gradient_info['info']['x1']===0) { $x1 = $gradient_info['info']['x1']; }
495 else { $x1 = $x0; }
496 if ($gradient_info['info']['y1'] || $gradient_info['info']['y1']===0) { $y1 = $gradient_info['info']['y1']; }
497 else { $y1 = $y0; }
498
499 if (stristr($x1, '%')!== false) { $x1 = ($x1+0)/100; }
500 if (stristr($x0, '%')!== false) { $x0 = ($x0+0)/100; }
501 if (stristr($y1, '%')!== false) { $y1 = ($y1+0)/100; }
502 if (stristr($y0, '%')!== false) { $y0 = ($y0+0)/100; }
503 if (stristr($rx, '%')!== false) { $rx = ($rx+0)/100; }
504 if (stristr($ry, '%')!== false) { $ry = ($ry+0)/100; }
505
506 // mPDF 5.0.043
507 $bboxw = $w;
508 $bboxh = $h;
509 $usex = $x_offset;
510 $usey = $y_offset;
511 $usew = $bboxw;
512 $useh = $bboxh;
513 if (isset($gradient_info['units']) && strtolower($gradient_info['units'])=='userspaceonuse') {
514 $angle = rad2deg(atan2(($gradient_info['info']['y0']-$gradient_info['info']['y1']), ($gradient_info['info']['x0']-$gradient_info['info']['x1'])));
515 if ($angle < 0) { $angle += 360; }
516 else if ($angle > 360) { $angle -= 360; }
517 if ($angle!=0 && $angle!=360 && $angle!=90 && $angle!=180 && $angle!=270) {
518 if ($w >= $h) {
519 $y1 *= $h/$w ;
520 $y0 *= $h/$w ;
521 $rx *= $h/$w ;
522 $ry *= $h/$w ;
523 $usew = $useh = $bboxw;
524 }
525 else {
526 $x1 *= $w/$h ;
527 $x0 *= $w/$h ;
528 $rx *= $w/$h ;
529 $ry *= $w/$h ;
530 $usew = $useh = $bboxh;
531 }
532 }
533 }
534 $a = $usew; // width
535 $d = -$useh; // height
536 $e = $usex; // x- offset
537 $f = -$usey; // -y-offset
538
539 $r = $rx;
540
541
542 $return .= sprintf('%.3F 0 0 %.3F %.3F %.3F cm ', $a*$this->kp, $d*$this->kp, $e*$this->kp, $f*$this->kp);
543
544 // mPDF 5.0.039
545 if (isset($gradient_info['units']) && strtolower($gradient_info['units'])=='objectboundingbox') {
546 if ($transformations) { $return .= $transformations; }
547 }
548
549 // x1 and y1 (fx, fy) should be inside the circle defined by x0 y0 and r else error in mPDF
550 while (pow(($x1-$x0),2) + pow(($y1 - $y0),2) >= pow($r,2)) { $r += 0.05; }
551
552 // mPDF 5.0.040
553 if ($spread=='R' || $spread=='F') { // Repeat / Reflect
554 $offs = array();
555 for($i=0;$i<$ns;$i++) {
556 $offs[$i] = $gradient_info['color'][$i]['offset'];
557 }
558 $gp = 0;
559 $inside=true;
560 while($inside) {
561 $gp++;
562 for($i=0;$i<$ns;$i++) {
563 if ($spread=='F' && ($gp % 2) == 1) { // Reflect
564 $gradient_info['color'][(($ns*$gp)+$i)] = $gradient_info['color'][(($ns*($gp-1))+($ns-$i-1))];
565 $tmp = $gp+(1-$offs[($ns-$i-1)]) ;
566 $gradient_info['color'][(($ns*$gp)+$i)]['offset'] = $tmp;
567 }
568 else { // Reflect
569 $gradient_info['color'][(($ns*$gp)+$i)] = $gradient_info['color'][$i];
570 $tmp = $gp+$offs[$i] ;
571 $gradient_info['color'][(($ns*$gp)+$i)]['offset'] = $tmp;
572 }
573 // IF STILL INSIDE BOX OR STILL VALID
574 // TEST IF circle (perimeter) intersects with
575 // or is enclosed
576 // Point on axis to test
577 $px = $x1 + ($x0-$x1)*$tmp;
578 $py = $y1 + ($y0-$y1)*$tmp;
579 $pr = $r*$tmp;
580 $res = _testIntersectCircle($px, $py, $pr);
581 if (!$res) { $inside = false; }
582 }
583 }
584 }
585
586 // mPDF 4.4.007 Gradient STOPs
587 $stops = count($gradient_info['color']);
588 if ($stops < 2) { return ''; }
589
590 // mPDF 5.0.043
591 $range = $gradient_info['color'][count($gradient_info['color'])-1]['offset']-$gradient_info['color'][0]['offset'];
592 $min = $gradient_info['color'][0]['offset'];
593
594 for ($i=0; $i<($stops); $i++) {
595 // mPDF 5.0.051
596 if (!$gradient_info['color'][$i]['color']) {
597 if ($gradient_info['colorspace']=='RGB') $gradient_info['color'][$i]['color'] = '0 0 0';
598 else if ($gradient_info['colorspace']=='Gray') $gradient_info['color'][$i]['color'] = '0';
599 else if ($gradient_info['colorspace']=='CMYK') $gradient_info['color'][$i]['color'] = '1 1 1 1';
600 }
601 $offset = ($gradient_info['color'][$i]['offset'] - $min)/$range;
602 $this->mpdf_ref->gradients[$n]['stops'][] = array(
603 'col' => $gradient_info['color'][$i]['color'],
604 'opacity' => $gradient_info['color'][$i]['opacity'],
605 'offset' => $offset);
606 if ($gradient_info['color'][$i]['opacity']<1) { $trans = true; }
607 }
608 $grx1 = $x1 + ($x0-$x1)*$gradient_info['color'][0]['offset'];
609 $gry1 = $y1 + ($y0-$y1)*$gradient_info['color'][0]['offset'];
610 $grx2 = $x1 + ($x0-$x1)*$gradient_info['color'][count($gradient_info['color'])-1]['offset'];
611 $gry2 = $y1 + ($y0-$y1)*$gradient_info['color'][count($gradient_info['color'])-1]['offset'];
612 $grir = $r*$gradient_info['color'][0]['offset'];
613 $grr = $r*$gradient_info['color'][count($gradient_info['color'])-1]['offset'];
614
615 $this->mpdf_ref->gradients[$n]['coords']=array($grx1, $gry1, $grx2, $gry2, abs($grr), abs($grir) );
616
617 $grx1 = $x1 + ($x0-$x1)*$first_stop;
618 $gry1 = $y1 + ($y0-$y1)*$first_stop;
619 $grx2 = $x1 + ($x0-$x1)*$last_stop;
620 $gry2 = $y1 + ($y0-$y1)*$last_stop;
621 $grir = $r*$first_stop;
622 $grr = $r*$last_stop;
623 $this->mpdf_ref->gradients[$n]['colorspace'] = $gradient_info['colorspace']; // mPDF 5.0.051
624
625 $this->mpdf_ref->gradients[$n]['type'] = 3;
626 $this->mpdf_ref->gradients[$n]['fo'] = true;
627
628 $this->mpdf_ref->gradients[$n]['extend']=array('true','true');
629 if ($trans) {
630 $this->mpdf_ref->gradients[$n]['trans'] = true;
631 $return .= ' /TGS'.($n).' gs ';
632 }
633 $return .= ' /Sh'.($n).' sh ';
634 $return .= " Q\n";
635
636
637 }
638
639 return $return;
640 }
641
642
643 function svgOffset ($attribs){
644 // save all <svg> tag attributes
645 $this->svg_attribs = $attribs;
646 if(isset($this->svg_attribs['viewBox'])) {
647 $vb = preg_split('/\s+/is', trim($this->svg_attribs['viewBox']));
648 if (count($vb)==4) {
649 $this->svg_info['x'] = $vb[0];
650 $this->svg_info['y'] = $vb[1];
651 $this->svg_info['w'] = $vb[2];
652 $this->svg_info['h'] = $vb[3];
653// return; // mPDF 5.0.005
654 }
655 }
656
657 $svg_w = $this->mpdf_ref->ConvertSize($attribs['width']); // mm (interprets numbers as pixels)
658 $svg_h = $this->mpdf_ref->ConvertSize($attribs['height']); // mm
659
660///*
661 // mPDF 5.0.005
662 if ($this->svg_info['w']) { // if 'w' set by viewBox
663 if ($svg_w) { // if width also set, use these values to determine to set size of "pixel"
664 $this->kp *= ($svg_w/0.2645) / $this->svg_info['w'];
665 $this->kf = ($svg_w/0.2645) / $this->svg_info['w']; // mPDF 5.0.039
666 }
667 else if ($svg_h) {
668 $this->kp *= ($svg_h/0.2645) / $this->svg_info['h'];
669 $this->kf = ($svg_h/0.2645) / $this->svg_info['h']; // mPDF 5.0.039
670 }
671 return;
672 }
673//*/
674
675 // Added to handle file without height or width specified
676 if (!$svg_w && !$svg_h) { $svg_w = $svg_h = $this->mpdf_ref->blk[$this->mpdf_ref->blklvl]['inner_width'] ; } // DEFAULT
677 if (!$svg_w) { $svg_w = $svg_h; }
678 if (!$svg_h) { $svg_h = $svg_w; }
679
680 $this->svg_info['x'] = 0;
681 $this->svg_info['y'] = 0;
682 $this->svg_info['w'] = $svg_w/0.2645; // mm->pixels
683 $this->svg_info['h'] = $svg_h/0.2645; // mm->pixels
684
685 }
686
687
688 //
689 // check if points are within svg, if not, set to max
690 function svg_overflow($x,$y)
691 {
692 $x2 = $x;
693 $y2 = $y;
694 if(isset($this->svg_attribs['overflow']))
695 {
696 if($this->svg_attribs['overflow'] == 'hidden')
697 {
698 // Not sure if this is supposed to strip off units, but since I dont use any I will omlt this step
699 $svg_w = preg_replace("/([0-9\.]*)(.*)/i","$1",$this->svg_attribs['width']);
700 $svg_h = preg_replace("/([0-9\.]*)(.*)/i","$1",$this->svg_attribs['height']);
701
702 // $xmax = floor($this->svg_attribs['width']);
703 $xmax = floor($svg_w);
704 $xmin = 0;
705 // $ymax = floor(($this->svg_attribs['height'] * -1));
706 $ymax = floor(($svg_h * -1));
707 $ymin = 0;
708
709 if($x > $xmax) $x2 = $xmax; // right edge
710 if($x < $xmin) $x2 = $xmin; // left edge
711 if($y < $ymax) $y2 = $ymax; // bottom
712 if($y > $ymin) $y2 = $ymin; // top
713
714 }
715 }
716
717
718 return array( 'x' => $x2, 'y' => $y2);
719 }
720
721
722
723 function svgDefineStyle($critere_style){
724
725 $tmp = count($this->svg_style)-1;
726 $current_style = $this->svg_style[$tmp];
727
728 unset($current_style['transformations']);
729
730 // TRANSFORM SCALE
731 $transformations = '';
732 if (isset($critere_style['transform'])){
733 preg_match_all('/(matrix|translate|scale|rotate|skewX|skewY)\((.*?)\)/is',$critere_style['transform'],$m);
734 if (count($m[0])) {
735 for($i=0; $i<count($m[0]); $i++) {
736 $c = strtolower($m[1][$i]);
737 $v = trim($m[2][$i]);
738 $vv = preg_split('/[ ,]+/',$v);
739 if ($c=='matrix' && count($vv)==6) {
740 // mPDF 5.0.039
741 // Note angle of rotation is reversed (from SVG to PDF), so vv[1] and vv[2] are negated
742 $transformations .= sprintf(' %.3F %.3F %.3F %.3F %.3F %.3F cm ', $vv[0], -$vv[1], -$vv[2], $vv[3], $vv[4]*$this->kp, -$vv[5]*$this->kp);
743
744/*
745// The long way of doing this??
746// need to reverse angle of rotation from SVG to PDF
747$sx=sqrt(pow($vv[0],2)+pow($vv[2],2));
748if ($vv[0] < 0) { $sx *= -1; } // change sign
749$sy=sqrt(pow($vv[1],2)+pow($vv[3],2));
750if ($vv[3] < 0) { $sy *= -1; } // change sign
751
752// rotation angle is
753$t=atan2($vv[1],$vv[3]);
754$t=atan2(-$vv[2],$vv[0]); // Should be the same value or skew has been applied
755
756// Reverse angle
757$t *= -1;
758
759// Rebuild matrix
760$ma = $sx * cos($t);
761$mb = $sy * sin($t);
762$mc = -$sx * sin($t);
763$md = $sy * cos($t);
764
765// $transformations .= sprintf(' %.3F %.3F %.3F %.3F %.3F %.3F cm ', $ma, $mb, $mc, $md, $vv[4]*$this->kp, -$vv[5]*$this->kp);
766*/
767
768 }
769 else if ($c=='translate' && count($vv)) {
770 $tm[4] = $vv[0];
771 if (count($vv)==2) { $t_y = -$vv[1]; }
772 else { $t_y = 0; }
773 $tm[5] = $t_y;
774 $transformations .= sprintf(' 1 0 0 1 %.3F %.3F cm ', $tm[4]*$this->kp, $tm[5]*$this->kp);
775 }
776 else if ($c=='scale' && count($vv)) {
777 if (count($vv)==2) { $s_y = $vv[1]; }
778 else { $s_y = $vv[0]; }
779 $tm[0] = $vv[0];
780 $tm[3] = $s_y;
781 $transformations .= sprintf(' %.3F 0 0 %.3F 0 0 cm ', $tm[0], $tm[3]);
782 }
783 else if ($c=='rotate' && count($vv)) {
784 $tm[0] = cos(deg2rad(-$vv[0]));
785 $tm[1] = sin(deg2rad(-$vv[0]));
786 $tm[2] = -$tm[1];
787 $tm[3] = $tm[0];
788 if (count($vv)==3) {
789 $transformations .= sprintf(' 1 0 0 1 %.3F %.3F cm ', $vv[1]*$this->kp, -$vv[2]*$this->kp);
790 }
791 $transformations .= sprintf(' %.3F %.3F %.3F %.3F 0 0 cm ', $tm[0], $tm[1], $tm[2], $tm[3]);
792 if (count($vv)==3) {
793 $transformations .= sprintf(' 1 0 0 1 %.3F %.3F cm ', -$vv[1]*$this->kp, $vv[2]*$this->kp);
794 }
795 }
796 else if ($c=='skewx' && count($vv)) {
797 $tm[2] = tan(deg2rad(-$vv[0]));
798 $transformations .= sprintf(' 1 0 %.3F 1 0 0 cm ', $tm[2]);
799 }
800 else if ($c=='skewy' && count($vv)) {
801 $tm[1] = tan(deg2rad(-$vv[0]));
802 $transformations .= sprintf(' 1 %.3F 0 1 0 0 cm ', $tm[1]);
803 }
804
805 }
806 }
807 $current_style['transformations'] = $transformations;
808 }
809
810 if (isset($critere_style['style'])){
811 if (preg_match('/fill:\s*rgb\((\d+),\s*(\d+),\s*(\d+)\)/',$critere_style['style'], $m)) {
812 $current_style['fill'] = '#'.str_pad(dechex($m[1]), 2, "0", STR_PAD_LEFT).str_pad(dechex($m[2]), 2, "0", STR_PAD_LEFT).str_pad(dechex($m[3]), 2, "0", STR_PAD_LEFT);
813 }
814 else { $tmp = preg_replace("/(.*)fill:\s*([a-z0-9#_()]*|none)(.*)/i","$2",$critere_style['style']); // mPDF 4.4.003
815 if ($tmp != $critere_style['style']){ $current_style['fill'] = $tmp; }
816 }
817
818 $tmp = preg_replace("/(.*)fill-opacity:\s*([a-z0-9.]*|none)(.*)/i","$2",$critere_style['style']);
819 if ($tmp != $critere_style['style']){ $current_style['fill-opacity'] = $tmp;}
820
821 $tmp = preg_replace("/(.*)fill-rule:\s*([a-z0-9#]*|none)(.*)/i","$2",$critere_style['style']);
822 if ($tmp != $critere_style['style']){ $current_style['fill-rule'] = $tmp;}
823
824 if (preg_match('/stroke:\s*rgb\((\d+),\s*(\d+),\s*(\d+)\)/',$critere_style['style'], $m)) {
825 $current_style['stroke'] = '#'.str_pad(dechex($m[1]), 2, "0", STR_PAD_LEFT).str_pad(dechex($m[2]), 2, "0", STR_PAD_LEFT).str_pad(dechex($m[3]), 2, "0", STR_PAD_LEFT);
826 }
827 else { $tmp = preg_replace("/(.*)stroke:\s*([a-z0-9#]*|none)(.*)/i","$2",$critere_style['style']);
828 if ($tmp != $critere_style['style']){ $current_style['stroke'] = $tmp; }
829 }
830
831 $tmp = preg_replace("/(.*)stroke-linecap:\s*([a-z0-9#]*|none)(.*)/i","$2",$critere_style['style']);
832 if ($tmp != $critere_style['style']){ $current_style['stroke-linecap'] = $tmp;}
833
834 $tmp = preg_replace("/(.*)stroke-linejoin:\s*([a-z0-9#]*|none)(.*)/i","$2",$critere_style['style']);
835 if ($tmp != $critere_style['style']){ $current_style['stroke-linejoin'] = $tmp;}
836
837 $tmp = preg_replace("/(.*)stroke-miterlimit:\s*([a-z0-9#]*|none)(.*)/i","$2",$critere_style['style']);
838 if ($tmp != $critere_style['style']){ $current_style['stroke-miterlimit'] = $tmp;}
839
840 $tmp = preg_replace("/(.*)stroke-opacity:\s*([a-z0-9.]*|none)(.*)/i","$2",$critere_style['style']);
841 if ($tmp != $critere_style['style']){ $current_style['stroke-opacity'] = $tmp; }
842
843 $tmp = preg_replace("/(.*)stroke-width:\s*([a-z0-9.]*|none)(.*)/i","$2",$critere_style['style']);
844 if ($tmp != $critere_style['style']){ $current_style['stroke-width'] = $tmp;}
845
846 // mPDF 4.4.003
847 $tmp = preg_replace("/(.*)stroke-dasharray:\s*([a-z0-9., ]*|none)(.*)/i","$2",$critere_style['style']);
848 if ($tmp != $critere_style['style']){ $current_style['stroke-dasharray'] = $tmp;}
849
850 // mPDF 4.4.003
851 $tmp = preg_replace("/(.*)stroke-dashoffset:\s*([a-z0-9.]*|none)(.*)/i","$2",$critere_style['style']);
852 if ($tmp != $critere_style['style']){ $current_style['stroke-dashoffset'] = $tmp;}
853
854 }
855 if(isset($critere_style['fill'])){
856 $current_style['fill'] = $critere_style['fill'];
857 }
858
859 if(isset($critere_style['fill-opacity'])){
860 $current_style['fill-opacity'] = $critere_style['fill-opacity'];
861 }
862
863 if(isset($critere_style['fill-rule'])){
864 $current_style['fill-rule'] = $critere_style['fill-rule'];
865 }
866
867 if(isset($critere_style['stroke'])){
868 $current_style['stroke'] = $critere_style['stroke'];
869 }
870
871 if(isset($critere_style['stroke-linecap'])){
872 $current_style['stroke-linecap'] = $critere_style['stroke-linecap'];
873 }
874
875 if(isset($critere_style['stroke-linejoin'])){
876 $current_style['stroke-linejoin'] = $critere_style['stroke-linejoin'];
877 }
878
879 if(isset($critere_style['stroke-miterlimit'])){
880 $current_style['stroke-miterlimit'] = $critere_style['stroke-miterlimit'];
881 }
882
883 if(isset($critere_style['stroke-opacity'])){
884 $current_style['stroke-opacity'] = $critere_style['stroke-opacity'];
885 }
886
887 if(isset($critere_style['stroke-width'])){
888 $current_style['stroke-width'] = $critere_style['stroke-width'];
889 }
890
891 // mPDF 4.4.003
892 if(isset($critere_style['stroke-dasharray'])){
893 $current_style['stroke-dasharray'] = $critere_style['stroke-dasharray'];
894 }
895 if(isset($critere_style['stroke-dashoffset'])){
896 $current_style['stroke-dashoffset'] = $critere_style['stroke-dashoffset'];
897 }
898
899 // mPDF 4.4.005 Used as indirect setting for currentColor
900 if(isset($critere_style['color']) && $critere_style['color'] != 'inherit'){
901 $current_style['color'] = $critere_style['color'];
902 }
903
904 return $current_style;
905
906 }
907
908 //
909 // Cette fonction ecrit le style dans le stream svg.
910 function svgStyle($critere_style, $attribs, $element){
911 $path_style = '';
912 if (substr_count($critere_style['fill'],'url')>0){
913 //
914 // couleur degradé
915 $id_gradient = preg_replace("/url\(#([\w_]*)\)/i","$1",$critere_style['fill']);
916 if ($id_gradient != $critere_style['fill']) {
917 if (isset($this->svg_gradient[$id_gradient])) {
918 $fill_gradient = $this->svgGradient($this->svg_gradient[$id_gradient], $attribs, $element);
919 if ($fill_gradient) { // mPDF 4.4.003
920 $path_style = "q ";
921 $w = "W";
922 $style .= 'N';
923 }
924 }
925 }
926
927 }
928 // mPDF 4.4.005 Used as indirect setting for currentColor
929 else if (strtolower($critere_style['fill']) == 'currentcolor'){
930 $col = $this->mpdf_ref->ConvertColor($critere_style['color']);
931 if ($col) {
932 // mPDF 5.0.051
933 // mPDF 5.3.74
934 if ($col{0}==5) { $critere_style['fill-opacity'] = ord($col{4}/100); } // RGBa
935 if ($col{0}==6) { $critere_style['fill-opacity'] = ord($col{5}/100); } // CMYKa
936 $path_style .= $this->mpdf_ref->SetFColor($col, true).' '; // mPDF 5.0.051
937 $style .= 'F';
938 }
939 }
940 else if ($critere_style['fill'] != 'none'){
941 $col = $this->mpdf_ref->ConvertColor($critere_style['fill']);
942 if ($col) {
943 // mPDF 5.0.051
944 // mPDF 5.3.74
945 if ($col{0}==5) { $critere_style['fill-opacity'] = ord($col{4}/100); } // RGBa
946 if ($col{0}==6) { $critere_style['fill-opacity'] = ord($col{5}/100); } // CMYKa
947 $path_style .= $this->mpdf_ref->SetFColor($col, true).' '; // mPDF 5.0.051
948 $style .= 'F';
949 }
950 }
951
952 // mPDF 5.0.040
953 if (substr_count($critere_style['stroke'],'url')>0){
954/*
955 // Cannot put a gradient on a "stroke" in PDF?
956 $id_gradient = preg_replace("/url\(#([\w_]*)\)/i","$1",$critere_style['stroke']);
957 if ($id_gradient != $critere_style['stroke']) {
958 if (isset($this->svg_gradient[$id_gradient])) {
959 $fill_gradient = $this->svgGradient($this->svg_gradient[$id_gradient], $attribs, $element);
960 if ($fill_gradient) {
961 $path_style = "q ";
962 $w = "W";
963 $style .= 'D';
964 }
965 }
966 }
967*/
968 }
969 // mPDF 4.4.005 Used as indirect setting for currentColor
970 else if (strtolower($critere_style['stroke']) == 'currentcolor'){
971 $col = $this->mpdf_ref->ConvertColor($critere_style['color']);
972 if ($col) {
973 // mPDF 5.0.051
974 // mPDF 5.3.74
975 if ($col{0}==5) { $critere_style['stroke-opacity'] = ord($col{4}/100); } // RGBa
976 if ($col{0}==6) { $critere_style['stroke-opacity'] = ord($col{5}/100); } // CMYKa
977 $path_style .= $this->mpdf_ref->SetDColor($col, true).' '; // mPDF 5.0.051
978 $style .= 'D';
979 $lw = $this->ConvertSVGSizePixels($critere_style['stroke-width']);
980 $path_style .= sprintf('%.3F w ',$lw*$this->kp);
981 }
982 }
983 else if ($critere_style['stroke'] != 'none'){
984 $col = $this->mpdf_ref->ConvertColor($critere_style['stroke']);
985 if ($col) {
986 // mPDF 5.0.051
987 // mPDF 5.3.74
988 if ($col{0}==5) { $critere_style['stroke-opacity'] = ord($col{4}/100); } // RGBa
989 if ($col{0}==6) { $critere_style['stroke-opacity'] = ord($col{5}/100); } // CMYKa
990 $path_style .= $this->mpdf_ref->SetDColor($col, true).' '; // mPDF 5.0.051
991 $style .= 'D';
992 $lw = $this->ConvertSVGSizePixels($critere_style['stroke-width']); // mPDF 4.4.003
993 $path_style .= sprintf('%.3F w ',$lw*$this->kp);
994 }
995 }
996
997
998 if ($critere_style['stroke'] != 'none'){
999 if ($critere_style['stroke-linejoin'] == 'miter'){
1000 $path_style .= ' 0 j ';
1001 }
1002 else if ($critere_style['stroke-linejoin'] == 'round'){
1003 $path_style .= ' 1 j ';
1004 }
1005 else if ($critere_style['stroke-linejoin'] == 'bevel'){
1006 $path_style .= ' 2 j ';
1007 }
1008
1009 if ($critere_style['stroke-linecap'] == 'butt'){
1010 $path_style .= ' 0 J ';
1011 }
1012 else if ($critere_style['stroke-linecap'] == 'round'){
1013 $path_style .= ' 1 J ';
1014 }
1015 else if ($critere_style['stroke-linecap'] == 'square'){
1016 $path_style .= ' 2 J ';
1017 }
1018
1019 if (isset($critere_style['stroke-miterlimit'])){
1020 if ($critere_style['stroke-miterlimit'] == 'none'){
1021 }
1022 else if (preg_match('/^[\d.]+$/',$critere_style['stroke-miterlimit'])) {
1023 $path_style .= sprintf('%.2F M ',$critere_style['stroke-miterlimit']);
1024 }
1025 }
1026 // mPDF 4.4.003
1027 if (isset($critere_style['stroke-dasharray'])){
1028 $off = 0;
1029 $d = preg_split('/[ ,]/',$critere_style['stroke-dasharray']);
1030 if (count($d) == 1 && $d[0]==0) {
1031 $path_style .= '[] 0 d ';
1032 }
1033 else {
1034 if (count($d) % 2 == 1) { $d = array_merge($d, $d); } // 5, 3, 1 => 5,3,1,5,3,1 OR 3 => 3,3
1035 $arr = '';
1036 for($i=0; $i<count($d); $i+=2) {
1037 $arr .= sprintf('%.3F %.3F ', $d[$i]*$this->kp, $d[$i+1]*$this->kp);
1038 }
1039 if (isset($critere_style['stroke-dashoffset'])){ $off = $critere_style['stroke-dashoffset'] + 0; }
1040 $path_style .= sprintf('[%s] %.3F d ', $arr, $off*$this->kp);
1041 }
1042 }
1043 }
1044
1045 // mPDF 4.4.003
1046 if ($critere_style['fill-rule']=='evenodd') { $fr = '*'; }
1047 else { $fr = ''; }
1048
1049 // mPDF 4.4.003
1050 if (isset($critere_style['fill-opacity'])) {
1051 $opacity = 1;
1052 if ($critere_style['fill-opacity'] == 0) { $opacity = 0; }
1053 else if ($critere_style['fill-opacity'] > 1) { $opacity = 1; }
1054 else if ($critere_style['fill-opacity'] > 0) { $opacity = $critere_style['fill-opacity']; }
1055 else if ($critere_style['fill-opacity'] < 0) { $opacity = 0; }
1056 $gs = $this->mpdf_ref->AddExtGState(array('ca'=>$opacity, 'BM'=>'/Normal'));
1057 $this->mpdf_ref->extgstates[$gs]['fo'] = true; // mPDF 5.0.039
1058 $path_style .= sprintf(' /GS%d gs ', $gs);
1059 }
1060
1061 // mPDF 4.4.003
1062 if (isset($critere_style['stroke-opacity'])) {
1063 $opacity = 1;
1064 if ($critere_style['stroke-opacity'] == 0) { $opacity = 0; }
1065 else if ($critere_style['stroke-opacity'] > 1) { $opacity = 1; }
1066 else if ($critere_style['stroke-opacity'] > 0) { $opacity = $critere_style['stroke-opacity']; }
1067 else if ($critere_style['stroke-opacity'] < 0) { $opacity = 0; }
1068 $gs = $this->mpdf_ref->AddExtGState(array('CA'=>$opacity, 'BM'=>'/Normal'));
1069 $this->mpdf_ref->extgstates[$gs]['fo'] = true; // mPDF 5.0.039
1070 $path_style .= sprintf(' /GS%d gs ', $gs);
1071 }
1072
1073 switch ($style){
1074 case 'F':
1075 $op = 'f';
1076 break;
1077 case 'FD':
1078 $op = 'B';
1079 break;
1080 case 'ND':
1081 $op = 'S';
1082 break;
1083 case 'D':
1084 $op = 'S';
1085 break;
1086 default:
1087 $op = 'n';
1088 }
1089
1090 // mPDF 5.0
1091 $prestyle = $path_style.' ';
1092 $poststyle = $w.' '. $op.$fr.' '.$fill_gradient."\n";
1093 return array($prestyle,$poststyle);
1094
1095 }
1096
1097 //
1098 // fonction retracant les <path />
1099 function svgPath($command, $arguments){
1100 $path_cmd = '';
1101 $newsubpath = false; // mPDF 4.4.003
1102 // mPDF 5.0.039
1103 $minl = $this->pathBBox[0];
1104 $mint = $this->pathBBox[1];
1105 $maxr = $this->pathBBox[2]+$this->pathBBox[0];
1106 $maxb = $this->pathBBox[3]+$this->pathBBox[1];
1107 // mPDF 5.0.040
1108 $start = array($this->xbase, -$this->ybase);
1109
1110 // mPDF 4.4.003
1111 preg_match_all('/[\-^]?[\d.]+(e[\-]?[\d]+){0,1}/i', $arguments, $a, PREG_SET_ORDER);
1112
1113 // if the command is a capital letter, the coords go absolute, otherwise relative
1114 if(strtolower($command) == $command) $relative = true;
1115 else $relative = false;
1116
1117
1118 $ile_argumentow = count($a);
1119
1120 // each command may have different needs for arguments [1 to 8]
1121
1122 switch(strtolower($command)){
1123 case 'm': // move
1124 for($i = 0; $i<$ile_argumentow; $i+=2){
1125 $x = $a[$i][0];
1126 $y = $a[$i+1][0];
1127 if($relative){
1128 $pdfx = ($this->xbase + $x);
1129 $pdfy = ($this->ybase - $y);
1130 $this->xbase += $x;
1131 $this->ybase += -$y;
1132 }
1133 else{
1134 $pdfx = $x;
1135 $pdfy = -$y ;
1136 $this->xbase = $x;
1137 $this->ybase = -$y;
1138 }
1139 $pdf_pt = $this->svg_overflow($pdfx,$pdfy);
1140 // mPDF 5.0.039
1141 $minl = min($minl,$pdf_pt['x']);
1142 $maxr = max($maxr,$pdf_pt['x']);
1143 $mint = min($mint,-$pdf_pt['y']);
1144 $maxb = max($maxb,-$pdf_pt['y']);
1145 if($i == 0) $path_cmd .= sprintf('%.3F %.3F m ', $pdf_pt['x']*$this->kp, $pdf_pt['y']*$this->kp);
1146 else $path_cmd .= sprintf('%.3F %.3F l ', $pdf_pt['x']*$this->kp, $pdf_pt['y']*$this->kp);
1147 // mPDF 4.4.003 Save start points of subpath
1148 if ($this->subPathInit) {
1149 $this->spxstart = $this->xbase;
1150 $this->spystart = $this->ybase;
1151 $this->subPathInit = false;
1152 }
1153 }
1154 break;
1155 case 'l': // a simple line
1156 for($i = 0; $i<$ile_argumentow; $i+=2){
1157 $x = ($a[$i][0]);
1158 $y = ($a[$i+1][0]);
1159 if($relative){
1160 $pdfx = ($this->xbase + $x);
1161 $pdfy = ($this->ybase - $y);
1162 $this->xbase += $x;
1163 $this->ybase += -$y;
1164 }
1165 else{
1166 $pdfx = $x ;
1167 $pdfy = -$y ;
1168 $this->xbase = $x;
1169 $this->ybase = -$y;
1170 }
1171 $pdf_pt = $this->svg_overflow($pdfx,$pdfy);
1172 // mPDF 5.0.039
1173 $minl = min($minl,$pdf_pt['x']);
1174 $maxr = max($maxr,$pdf_pt['x']);
1175 $mint = min($mint,-$pdf_pt['y']);
1176 $maxb = max($maxb,-$pdf_pt['y']);
1177 $path_cmd .= sprintf('%.3F %.3F l ', $pdf_pt['x']*$this->kp, $pdf_pt['y']*$this->kp);
1178 }
1179 break;
1180 case 'h': // a very simple horizontal line
1181 for($i = 0; $i<$ile_argumentow; $i++){
1182 $x = ($a[$i][0]);
1183 if($relative){
1184 $y = 0;
1185 $pdfx = ($this->xbase + $x) ;
1186 $pdfy = ($this->ybase - $y) ;
1187 $this->xbase += $x;
1188 $this->ybase += -$y;
1189 }
1190 else{
1191 $y = -$this->ybase;
1192 $pdfx = $x;
1193 $pdfy = -$y;
1194 $this->xbase = $x;
1195 $this->ybase = -$y;
1196 }
1197 $pdf_pt = $this->svg_overflow($pdfx,$pdfy);
1198 // mPDF 5.0.039
1199 $minl = min($minl,$pdf_pt['x']);
1200 $maxr = max($maxr,$pdf_pt['x']);
1201 $mint = min($mint,-$pdf_pt['y']);
1202 $maxb = max($maxb,-$pdf_pt['y']);
1203 $path_cmd .= sprintf('%.3F %.3F l ', $pdf_pt['x']*$this->kp, $pdf_pt['y']*$this->kp);
1204 }
1205 break;
1206 case 'v': // the simplest line, vertical
1207 for($i = 0; $i<$ile_argumentow; $i++){
1208 $y = ($a[$i][0]);
1209 if($relative){
1210 $x = 0;
1211 $pdfx = ($this->xbase + $x);
1212 $pdfy = ($this->ybase - $y);
1213 $this->xbase += $x;
1214 $this->ybase += -$y;
1215 }
1216 else{
1217 $x = $this->xbase;
1218 $pdfx = $x;
1219 $pdfy = -$y;
1220 $this->xbase = $x;
1221 $this->ybase = -$y;
1222 }
1223 $pdf_pt = $this->svg_overflow($pdfx,$pdfy);
1224 // mPDF 5.0.039
1225 $minl = min($minl,$pdf_pt['x']);
1226 $maxr = max($maxr,$pdf_pt['x']);
1227 $mint = min($mint,-$pdf_pt['y']);
1228 $maxb = max($maxb,-$pdf_pt['y']);
1229 $path_cmd .= sprintf('%.3F %.3F l ', $pdf_pt['x']*$this->kp, $pdf_pt['y']*$this->kp);
1230 }
1231 break;
1232 case 's': // bezier with first vertex equal first control
1233 // mPDF 4.4.003
1234 if (!($this->lastcommand == 'C' || $this->lastcommand == 'c' || $this->lastcommand == 'S' || $this->lastcommand == 's')) {
1235 $this->lastcontrolpoints = array(0,0);
1236 }
1237 for($i = 0; $i<$ile_argumentow; $i += 4){
1238 $x1 = $this->lastcontrolpoints[0];
1239 $y1 = $this->lastcontrolpoints[1];
1240 $x2 = ($a[$i][0]);
1241 $y2 = ($a[$i+1][0]);
1242 $x = ($a[$i+2][0]);
1243 $y = ($a[$i+3][0]);
1244 if($relative){
1245 $pdfx1 = ($this->xbase + $x1);
1246 $pdfy1 = ($this->ybase - $y1);
1247 $pdfx2 = ($this->xbase + $x2);
1248 $pdfy2 = ($this->ybase - $y2);
1249 $pdfx = ($this->xbase + $x);
1250 $pdfy = ($this->ybase - $y);
1251 $this->xbase += $x;
1252 $this->ybase += -$y;
1253 }
1254 else{
1255 $pdfx1 = $this->xbase + $x1;
1256 $pdfy1 = $this->ybase -$y1;
1257 $pdfx2 = $x2;
1258 $pdfy2 = -$y2;
1259 $pdfx = $x;
1260 $pdfy = -$y;
1261 $this->xbase = $x;
1262 $this->ybase = -$y;
1263 }
1264 $this->lastcontrolpoints = array(($pdfx-$pdfx2),-($pdfy-$pdfy2)); // mPDF 4.4.003 always relative
1265
1266 $pdf_pt = $this->svg_overflow($pdfx,$pdfy);
1267
1268 // mPDF 5.0.040
1269 $curves = array($pdfx1,-$pdfy1,$pdfx2,-$pdfy2,$pdfx,-$pdfy);
1270 $bx = calc_bezier_bbox($start, $curves);
1271 $minl = min($minl,$bx[0]);
1272 $maxr = max($maxr,$bx[2]);
1273 $mint = min($mint,$bx[1]);
1274 $maxb = max($maxb,$bx[3]);
1275
1276 if( ($pdf_pt['x'] != $pdfx) || ($pdf_pt['y'] != $pdfy) )
1277 {
1278 $path_cmd .= sprintf('%.3F %.3F l ', $pdf_pt['x']*$this->kp, $pdf_pt['y']*$this->kp);
1279 }
1280 else
1281 {
1282 $path_cmd .= sprintf('%.3F %.3F %.3F %.3F %.3F %.3F c ', $pdfx1*$this->kp, $pdfy1*$this->kp, $pdfx2*$this->kp, $pdfy2*$this->kp, $pdfx*$this->kp, $pdfy*$this->kp);
1283 }
1284
1285 }
1286 break;
1287 case 'c': // bezier with second vertex equal second control
1288 for($i = 0; $i<$ile_argumentow; $i += 6){
1289 $x1 = ($a[$i][0]);
1290 $y1 = ($a[$i+1][0]);
1291 $x2 = ($a[$i+2][0]);
1292 $y2 = ($a[$i+3][0]);
1293 $x = ($a[$i+4][0]);
1294 $y = ($a[$i+5][0]);
1295
1296
1297 if($relative){
1298 $pdfx1 = ($this->xbase + $x1);
1299 $pdfy1 = ($this->ybase - $y1);
1300 $pdfx2 = ($this->xbase + $x2);
1301 $pdfy2 = ($this->ybase - $y2);
1302 $pdfx = ($this->xbase + $x);
1303 $pdfy = ($this->ybase - $y);
1304 $this->xbase += $x;
1305 $this->ybase += -$y;
1306 }
1307 else{
1308 $pdfx1 = $x1;
1309 $pdfy1 = -$y1;
1310 $pdfx2 = $x2;
1311 $pdfy2 = -$y2;
1312 $pdfx = $x;
1313 $pdfy = -$y;
1314 $this->xbase = $x;
1315 $this->ybase = -$y;
1316 }
1317 $this->lastcontrolpoints = array(($pdfx-$pdfx2),-($pdfy-$pdfy2)); // mPDF 4.4.003 always relative
1318 // $pdf_pt2 = $this->svg_overflow($pdfx2,$pdfy2);
1319 // $pdf_pt1 = $this->svg_overflow($pdfx1,$pdfy1);
1320 $pdf_pt = $this->svg_overflow($pdfx,$pdfy);
1321
1322 // mPDF 5.0.040
1323 $curves = array($pdfx1,-$pdfy1,$pdfx2,-$pdfy2,$pdfx,-$pdfy);
1324 $bx = calc_bezier_bbox($start, $curves);
1325 $minl = min($minl,$bx[0]);
1326 $maxr = max($maxr,$bx[2]);
1327 $mint = min($mint,$bx[1]);
1328 $maxb = max($maxb,$bx[3]);
1329
1330 if( ($pdf_pt['x'] != $pdfx) || ($pdf_pt['y'] != $pdfy) )
1331 {
1332 $path_cmd .= sprintf('%.3F %.3F l ', $pdf_pt['x']*$this->kp, $pdf_pt['y']*$this->kp);
1333 }
1334 else
1335 {
1336 $path_cmd .= sprintf('%.3F %.3F %.3F %.3F %.3F %.3F c ', $pdfx1*$this->kp, $pdfy1*$this->kp, $pdfx2*$this->kp, $pdfy2*$this->kp, $pdfx*$this->kp, $pdfy*$this->kp);
1337 }
1338
1339 }
1340 break;
1341
1342 case 'q': // bezier quadratic avec point de control
1343 for($i = 0; $i<$ile_argumentow; $i += 4){
1344 $x1 = ($a[$i][0]);
1345 $y1 = ($a[$i+1][0]);
1346 $x = ($a[$i+2][0]);
1347 $y = ($a[$i+3][0]);
1348 if($relative){
1349 $pdfx = ($this->xbase + $x);
1350 $pdfy = ($this->ybase - $y);
1351
1352 $pdfx1 = ($this->xbase + ($x1*2/3));
1353 $pdfy1 = ($this->ybase - ($y1*2/3));
1354 // mPDF 4.4.003
1355 $pdfx2 = $pdfx1 + 1/3 *($x);
1356 $pdfy2 = $pdfy1 + 1/3 *(-$y) ;
1357
1358 $this->xbase += $x;
1359 $this->ybase += -$y;
1360 }
1361 else{
1362 $pdfx = $x;
1363 $pdfy = -$y;
1364
1365 $pdfx1 = ($this->xbase+(($x1-$this->xbase)*2/3));
1366 $pdfy1 = ($this->ybase-(($y1+$this->ybase)*2/3));
1367
1368 $pdfx2 = ($x+(($x1-$x)*2/3));
1369 $pdfy2 = (-$y-(($y1-$y)*2/3));
1370
1371 // mPDF 4.4.003
1372 $pdfx2 = $pdfx1 + 1/3 *($x - $this->xbase);
1373 $pdfy2 = $pdfy1 + 1/3 *(-$y - $this->ybase) ;
1374
1375 $this->xbase = $x;
1376 $this->ybase = -$y;
1377 }
1378 $this->lastcontrolpoints = array(($pdfx-$pdfx2),-($pdfy-$pdfy2)); // mPDF 4.4.003 always relative
1379
1380 $pdf_pt = $this->svg_overflow($pdfx,$pdfy);
1381
1382 // mPDF 5.0.040
1383 $curves = array($pdfx1,-$pdfy1,$pdfx2,-$pdfy2,$pdfx,-$pdfy);
1384 $bx = calc_bezier_bbox($start, $curves);
1385 $minl = min($minl,$bx[0]);
1386 $maxr = max($maxr,$bx[2]);
1387 $mint = min($mint,$bx[1]);
1388 $maxb = max($maxb,$bx[3]);
1389
1390 if( ($pdf_pt['x'] != $pdfx) || ($pdf_pt['y'] != $pdfy) )
1391 {
1392 $path_cmd .= sprintf('%.3F %.3F l ', $pdf_pt['x']*$this->kp, $pdf_pt['y']*$this->kp);
1393 }
1394 else
1395 {
1396 $path_cmd .= sprintf('%.3F %.3F %.3F %.3F %.3F %.3F c ', $pdfx1*$this->kp, $pdfy1*$this->kp, $pdfx2*$this->kp, $pdfy2*$this->kp, $pdfx*$this->kp, $pdfy*$this->kp);
1397 }
1398 }
1399 break;
1400 case 't': // bezier quadratic avec point de control simetrique a lancien point de control
1401 // mPDF 4.4.003
1402 if (!($this->lastcommand == 'Q' || $this->lastcommand == 'q' || $this->lastcommand == 'T' || $this->lastcommand == 't')) {
1403 $this->lastcontrolpoints = array(0,0);
1404 }
1405 for($i = 0; $i<$ile_argumentow; $i += 2){
1406 $x = ($a[$i][0]);
1407 $y = ($a[$i+1][0]);
1408
1409 $x1 = $this->lastcontrolpoints[0];
1410 $y1 = $this->lastcontrolpoints[1];
1411
1412 if($relative){
1413 $pdfx = ($this->xbase + $x);
1414 $pdfy = ($this->ybase - $y);
1415
1416 $pdfx1 = ($this->xbase + ($x1)); // mPDF 4.4.003
1417 $pdfy1 = ($this->ybase - ($y1)); // mPDF 4.4.003
1418 // mPDF 4.4.003
1419 $pdfx2 = $pdfx1 + 1/3 *($x);
1420 $pdfy2 = $pdfy1 + 1/3 *(-$y) ;
1421
1422 $this->xbase += $x;
1423 $this->ybase += -$y;
1424 }
1425 else{
1426 $pdfx = $x;
1427 $pdfy = -$y;
1428
1429 $pdfx1 = ($this->xbase + ($x1)); // mPDF 4.4.003
1430 $pdfy1 = ($this->ybase - ($y1)); // mPDF 4.4.003
1431 // mPDF 4.4.003
1432 $pdfx2 = $pdfx1 + 1/3 *($x - $this->xbase);
1433 $pdfy2 = $pdfy1 + 1/3 *(-$y - $this->ybase) ;
1434
1435 $this->xbase = $x;
1436 $this->ybase = -$y;
1437 }
1438
1439 $this->lastcontrolpoints = array(($pdfx-$pdfx2),-($pdfy-$pdfy2)); // mPDF 4.4.003 always relative
1440
1441 // mPDF 5.0.040
1442 $curves = array($pdfx1,-$pdfy1,$pdfx2,-$pdfy2,$pdfx,-$pdfy);
1443 $bx = calc_bezier_bbox($start, $curves);
1444 $minl = min($minl,$bx[0]);
1445 $maxr = max($maxr,$bx[2]);
1446 $mint = min($mint,$bx[1]);
1447 $maxb = max($maxb,$bx[3]);
1448
1449 $path_cmd .= sprintf('%.3F %.3F %.3F %.3F %.3F %.3F c ', $pdfx1*$this->kp, $pdfy1*$this->kp, $pdfx2*$this->kp, $pdfy2*$this->kp, $pdfx*$this->kp, $pdfy*$this->kp);
1450 }
1451
1452 break;
1453 case 'a': // Elliptical arc
1454 for($i = 0; $i<$ile_argumentow; $i += 7){
1455 $rx = ($a[$i][0]);
1456 $ry = ($a[$i+1][0]);
1457 $angle = ($a[$i+2][0]); //x-axis-rotation
1458 $largeArcFlag = ($a[$i+3][0]);
1459 $sweepFlag = ($a[$i+4][0]);
1460 $x2 = ($a[$i+5][0]);
1461 $y2 = ($a[$i+6][0]);
1462 $x1 = $this->xbase;
1463 $y1 = -$this->ybase;
1464 if($relative){
1465 $x2 = $this->xbase + $x2;
1466 $y2 = -$this->ybase + $y2;
1467 $this->xbase += ($a[$i+5][0]);
1468 $this->ybase += -($a[$i+6][0]);
1469 }
1470 else{
1471 $this->xbase = $x2;
1472 $this->ybase = -$y2;
1473 }
1474 // mPDF 5.0.039 // mPDF 5.0.040
1475 list($pcmd, $bounds) = $this->Arcto($x1, $y1, $x2, $y2, $rx, $ry, $angle, $largeArcFlag, $sweepFlag);
1476 $minl = min($minl,$x2,min($bounds[0]));
1477 $maxr = max($maxr,$x2,max($bounds[0]));
1478 $mint = min($mint,$y2,min($bounds[1]));
1479 $maxb = max($maxb,$y2,max($bounds[1]));
1480 $path_cmd .= $pcmd;
1481
1482 }
1483 break;
1484 case'z':
1485 $path_cmd .= 'h ';
1486 // mPDF 4.4.003
1487 $this->subPathInit = true;
1488 $newsubpath = true;
1489 $this->xbase = $this->spxstart;
1490 $this->ybase = $this->spystart;
1491 break;
1492 default:
1493 break;
1494 }
1495
1496 if (!$newsubpath) { $this->subPathInit = false; } // mPDF 4.4.003
1497 $this->lastcommand = $command;
1498 // mPDF 5.0.039
1499 $this->pathBBox[0] = $minl;
1500 $this->pathBBox[1] = $mint;
1501 $this->pathBBox[2] = $maxr - $this->pathBBox[0];
1502 $this->pathBBox[3] = $maxb - $this->pathBBox[1];
1503 return $path_cmd;
1504
1505 }
1506
1507function Arcto($x1, $y1, $x2, $y2, $rx, $ry, $angle, $largeArcFlag, $sweepFlag) {
1508
1509 // mPDF 5.0.040
1510 $bounds = array(0=>array($x1,$x2),1=>array($y1,$y2));
1511 // 1. Treat out-of-range parameters as described in
1512 // http://www.w3.org/TR/SVG/implnote.html#ArcImplementationNotes
1513 // If the endpoints (x1, y1) and (x2, y2) are identical, then this
1514 // is equivalent to omitting the elliptical arc segment entirely
1515 if ($x1 == $x2 && $y1 == $y2) return array('', $bounds); // mPD 5.0.040
1516
1517 // If rX = 0 or rY = 0 then this arc is treated as a straight line
1518 // segment (a "lineto") joining the endpoints.
1519 if ($rx == 0.0 || $ry == 0.0) {
1520 // return array(Lineto(x2, y2), $bounds); // mPD 5.0.040
1521 }
1522
1523 // If rX or rY have negative signs, these are dropped; the absolute
1524 // value is used instead.
1525 if ($rx<0.0) $rx = -$rx;
1526 if ($ry<0.0) $ry = -$ry;
1527
1528 // 2. convert to center parameterization as shown in
1529 // http://www.w3.org/TR/SVG/implnote.html
1530 $sinPhi = sin(deg2rad($angle));
1531 $cosPhi = cos(deg2rad($angle));
1532
1533 $x1dash = $cosPhi * ($x1-$x2)/2.0 + $sinPhi * ($y1-$y2)/2.0;
1534 $y1dash = -$sinPhi * ($x1-$x2)/2.0 + $cosPhi * ($y1-$y2)/2.0;
1535
1536
1537 $numerator = $rx*$rx*$ry*$ry - $rx*$rx*$y1dash*$y1dash - $ry*$ry*$x1dash*$x1dash;
1538
1539 if ($numerator < 0.0) {
1540 // If rX , rY and are such that there is no solution (basically,
1541 // the ellipse is not big enough to reach from (x1, y1) to (x2,
1542 // y2)) then the ellipse is scaled up uniformly until there is
1543 // exactly one solution (until the ellipse is just big enough).
1544
1545 // -> find factor s, such that numerator' with rx'=s*rx and
1546 // ry'=s*ry becomes 0 :
1547 $s = sqrt(1.0 - $numerator/($rx*$rx*$ry*$ry));
1548
1549 $rx *= $s;
1550 $ry *= $s;
1551 $root = 0.0;
1552
1553 }
1554 else {
1555 $root = ($largeArcFlag == $sweepFlag ? -1.0 : 1.0) * sqrt( $numerator/($rx*$rx*$y1dash*$y1dash+$ry*$ry*$x1dash*$x1dash) );
1556 }
1557
1558 $cxdash = $root*$rx*$y1dash/$ry;
1559 $cydash = -$root*$ry*$x1dash/$rx;
1560
1561 $cx = $cosPhi * $cxdash - $sinPhi * $cydash + ($x1+$x2)/2.0;
1562 $cy = $sinPhi * $cxdash + $cosPhi * $cydash + ($y1+$y2)/2.0;
1563
1564
1565 $theta1 = $this->CalcVectorAngle(1.0, 0.0, ($x1dash-$cxdash)/$rx, ($y1dash-$cydash)/$ry);
1566 $dtheta = $this->CalcVectorAngle(($x1dash-$cxdash)/$rx, ($y1dash-$cydash)/$ry, (-$x1dash-$cxdash)/$rx, (-$y1dash-$cydash)/$ry);
1567 if (!$sweepFlag && $dtheta>0)
1568 $dtheta -= 2.0*M_PI;
1569 else if ($sweepFlag && $dtheta<0)
1570 $dtheta += 2.0*M_PI;
1571
1572 // 3. convert into cubic bezier segments <= 90deg
1573 $segments = ceil(abs($dtheta/(M_PI/2.0)));
1574 $delta = $dtheta/$segments;
1575 $t = 8.0/3.0 * sin($delta/4.0) * sin($delta/4.0) / sin($delta/2.0);
1576 $coords = array();
1577 for ($i = 0; $i < $segments; $i++) {
1578 $cosTheta1 = cos($theta1);
1579 $sinTheta1 = sin($theta1);
1580 $theta2 = $theta1 + $delta;
1581 $cosTheta2 = cos($theta2);
1582 $sinTheta2 = sin($theta2);
1583
1584 // a) calculate endpoint of the segment:
1585 $xe = $cosPhi * $rx*$cosTheta2 - $sinPhi * $ry*$sinTheta2 + $cx;
1586 $ye = $sinPhi * $rx*$cosTheta2 + $cosPhi * $ry*$sinTheta2 + $cy;
1587
1588 // b) calculate gradients at start/end points of segment:
1589 $dx1 = $t * ( - $cosPhi * $rx*$sinTheta1 - $sinPhi * $ry*$cosTheta1);
1590 $dy1 = $t * ( - $sinPhi * $rx*$sinTheta1 + $cosPhi * $ry*$cosTheta1);
1591
1592 $dxe = $t * ( $cosPhi * $rx*$sinTheta2 + $sinPhi * $ry*$cosTheta2);
1593 $dye = $t * ( $sinPhi * $rx*$sinTheta2 - $cosPhi * $ry*$cosTheta2);
1594
1595 // c) draw the cubic bezier:
1596 $coords[$i] = array(($x1+$dx1), ($y1+$dy1), ($xe+$dxe), ($ye+$dye), $xe, $ye);
1597
1598 // do next segment
1599 $theta1 = $theta2;
1600 $x1 = $xe;
1601 $y1 = $ye;
1602 }
1603 $path = ' ';
1604 foreach($coords AS $c) {
1605 $cpx1 = $c[0];
1606 $cpy1 = $c[1];
1607 $cpx2 = $c[2];
1608 $cpy2 = $c[3];
1609 $x2 = $c[4];
1610 $y2 = $c[5];
1611 $path .= sprintf('%.3F %.3F %.3F %.3F %.3F %.3F c ', $cpx1*$this->kp, -$cpy1*$this->kp, $cpx2*$this->kp, -$cpy2*$this->kp, $x2*$this->kp, -$y2*$this->kp) ."\n";
1612
1613 // mPDF 5.0.040
1614 $bounds[0][] = $c[4];
1615 $bounds[1][] = $c[5];
1616 }
1617 return array($path, $bounds); // mPD 5.0.040
1618}
1619
1620
1621 function CalcVectorAngle($ux, $uy, $vx, $vy) {
1622 $ta = atan2($uy, $ux);
1623 $tb = atan2($vy, $vx);
1624 if ($tb >= $ta)
1625 return ($tb-$ta);
1626 return (6.28318530718 - ($ta-$tb));
1627 }
1628
1629
1630 // mPDF 4.4.003
1631 function ConvertSVGSizePixels($size=5,$maxsize='x'){
1632 // maxsize in pixels (user units) or 'y' or 'x'
1633 // e.g. $w = $this->ConvertSVGSizePixels($arguments['w'],$this->svg_info['w']*(25.4/$this->mpdf_ref->dpi));
1634 // usefontsize - setfalse for e.g. margins - will ignore fontsize for % values
1635 // Depends of maxsize value to make % work properly. Usually maxsize == pagewidth
1636 // For text $maxsize = Fontsize
1637 // Setting e.g. margin % will use maxsize (pagewidth) and em will use fontsize
1638
1639 if ($maxsize == 'y') { $maxsize = $this->svg_info['h']; }
1640 else if ($maxsize == 'x') { $maxsize = $this->svg_info['w']; }
1641 $maxsize *= (25.4/$this->mpdf_ref->dpi); // convert pixels to mm
1642 $fontsize=$this->mpdf_ref->FontSize;
1643 //Return as pixels
1644 $size = $this->mpdf_ref->ConvertSize($size,$maxsize,$fontsize,false) * 1/(25.4/$this->mpdf_ref->dpi);
1645 return $size;
1646 }
1647
1648 // mPDF 4.4.003
1649 function ConvertSVGSizePts($size=5){
1650 // usefontsize - setfalse for e.g. margins - will ignore fontsize for % values
1651 // Depends of maxsize value to make % work properly. Usually maxsize == pagewidth
1652 // For text $maxsize = Fontsize
1653 // Setting e.g. margin % will use maxsize (pagewidth) and em will use fontsize
1654 $maxsize=$this->mpdf_ref->FontSize;
1655 //Return as pts
1656 $size = $this->mpdf_ref->ConvertSize($size,$maxsize,false,true) * 72/25.4;
1657 return $size;
1658 }
1659
1660
1661 //
1662 // fonction retracant les <rect />
1663 function svgRect($arguments){
1664
1665 if ($arguments['h']==0 || $arguments['w']==0) { return ''; } // mPDF 4.4.003
1666
1667 $x = $this->ConvertSVGSizePixels($arguments['x'],'x'); // mPDF 4.4.003
1668 $y = $this->ConvertSVGSizePixels($arguments['y'],'y'); // mPDF 4.4.003
1669 $h = $this->ConvertSVGSizePixels($arguments['h'],'y'); // mPDF 4.4.003
1670 $w = $this->ConvertSVGSizePixels($arguments['w'],'x'); // mPDF 4.4.003
1671 $rx = $this->ConvertSVGSizePixels($arguments['rx'],'x'); // mPDF 4.4.003
1672 $ry = $this->ConvertSVGSizePixels($arguments['ry'],'y'); // mPDF 4.4.003
1673
1674 if ($rx > $w/2) { $rx = $w/2; } // mPDF 4.4.003
1675 if ($ry > $h/2) { $ry = $h/2; } // mPDF 4.4.003
1676
1677 if ($rx>0 and $ry == 0){$ry = $rx;}
1678 if ($ry>0 and $rx == 0){$rx = $ry;}
1679
1680 if ($rx == 0 and $ry == 0){
1681 // trace un rectangle sans angle arrondit
1682 $path_cmd = sprintf('%.3F %.3F m ', ($x*$this->kp), -($y*$this->kp));
1683 $path_cmd .= sprintf('%.3F %.3F l ', (($x+$w)*$this->kp), -($y*$this->kp));
1684 $path_cmd .= sprintf('%.3F %.3F l ', (($x+$w)*$this->kp), -(($y+$h)*$this->kp));
1685 $path_cmd .= sprintf('%.3F %.3F l ', ($x)*$this->kp, -(($y+$h)*$this->kp));
1686 $path_cmd .= sprintf('%.3F %.3F l h ', ($x*$this->kp), -($y*$this->kp));
1687
1688
1689 }
1690 else {
1691 // trace un rectangle avec les arrondit
1692 // les points de controle du bezier sont deduis grace a la constante kappa
1693 $kappa = 4*(sqrt(2)-1)/3;
1694
1695 $kx = $kappa*$rx;
1696 $ky = $kappa*$ry;
1697
1698 $path_cmd = sprintf('%.3F %.3F m ', ($x+$rx)*$this->kp, -$y*$this->kp);
1699 $path_cmd .= sprintf('%.3F %.3F l ', ($x+($w-$rx))*$this->kp, -$y*$this->kp);
1700 $path_cmd .= sprintf('%.3F %.3F %.3F %.3F %.3F %.3F c ', ($x+($w-$rx+$kx))*$this->kp, -$y*$this->kp, ($x+$w)*$this->kp, (-$y+(-$ry+$ky))*$this->kp, ($x+$w)*$this->kp, (-$y+(-$ry))*$this->kp );
1701 $path_cmd .= sprintf('%.3F %.3F l ', ($x+$w)*$this->kp, (-$y+(-$h+$ry))*$this->kp);
1702 $path_cmd .= sprintf('%.3F %.3F %.3F %.3F %.3F %.3F c ', ($x+$w)*$this->kp, (-$y+(-$h-$ky+$ry))*$this->kp, ($x+($w-$rx+$kx))*$this->kp, (-$y+(-$h))*$this->kp, ($x+($w-$rx))*$this->kp, (-$y+(-$h))*$this->kp );
1703
1704 $path_cmd .= sprintf('%.3F %.3F l ', ($x+$rx)*$this->kp, (-$y+(-$h))*$this->kp);
1705 $path_cmd .= sprintf('%.3F %.3F %.3F %.3F %.3F %.3F c ', ($x+($rx-$kx))*$this->kp, (-$y+(-$h))*$this->kp, $x*$this->kp, (-$y+(-$h-$ky+$ry))*$this->kp, $x*$this->kp, (-$y+(-$h+$ry))*$this->kp );
1706 $path_cmd .= sprintf('%.3F %.3F l ', $x*$this->kp, (-$y+(-$ry))*$this->kp);
1707 $path_cmd .= sprintf('%.3F %.3F %.3F %.3F %.3F %.3F c h ', $x*$this->kp, (-$y+(-$ry+$ky))*$this->kp, ($x+($rx-$kx))*$this->kp, -$y*$this->kp, ($x+$rx)*$this->kp, -$y*$this->kp );
1708
1709
1710 }
1711 return $path_cmd;
1712 }
1713
1714 //
1715 // fonction retracant les <ellipse /> et <circle />
1716 // le cercle est tracé grave a 4 bezier cubic, les poitn de controles
1717 // sont deduis grace a la constante kappa * rayon
1718 function svgEllipse($arguments){
1719 if ($arguments['rx']==0 || $arguments['ry']==0) { return ''; } // mPDF 4.4.003
1720
1721 $kappa = 4*(sqrt(2)-1)/3;
1722
1723 $cx = $this->ConvertSVGSizePixels($arguments['cx'],'x'); // mPDF 4.4.003
1724 $cy = $this->ConvertSVGSizePixels($arguments['cy'],'y'); // mPDF 4.4.003
1725 $rx = $this->ConvertSVGSizePixels($arguments['rx'],'x'); // mPDF 4.4.003
1726 $ry = $this->ConvertSVGSizePixels($arguments['ry'],'y'); // mPDF 4.4.003
1727
1728 $x1 = $cx;
1729 $y1 = -$cy+$ry;
1730
1731 $x2 = $cx+$rx;
1732 $y2 = -$cy;
1733
1734 $x3 = $cx;
1735 $y3 = -$cy-$ry;
1736
1737 $x4 = $cx-$rx;
1738 $y4 = -$cy;
1739
1740 $path_cmd = sprintf('%.3F %.3F m ', $x1*$this->kp, $y1*$this->kp);
1741 $path_cmd .= sprintf('%.3F %.3F %.3F %.3F %.3F %.3F c ', ($x1+($rx*$kappa))*$this->kp, $y1*$this->kp, $x2*$this->kp, ($y2+($ry*$kappa))*$this->kp, $x2*$this->kp, $y2*$this->kp);
1742 $path_cmd .= sprintf('%.3F %.3F %.3F %.3F %.3F %.3F c ', $x2*$this->kp, ($y2-($ry*$kappa))*$this->kp, ($x3+($rx*$kappa))*$this->kp, $y3*$this->kp, $x3*$this->kp, $y3*$this->kp);
1743 $path_cmd .= sprintf('%.3F %.3F %.3F %.3F %.3F %.3F c ', ($x3-($rx*$kappa))*$this->kp, $y3*$this->kp, $x4*$this->kp, ($y4-($ry*$kappa))*$this->kp, $x4*$this->kp, $y4*$this->kp);
1744 $path_cmd .= sprintf('%.3F %.3F %.3F %.3F %.3F %.3F c ', $x4*$this->kp, ($y4+($ry*$kappa))*$this->kp, ($x1-($rx*$kappa))*$this->kp, $y1*$this->kp, $x1*$this->kp, $y1*$this->kp);
1745 $path_cmd .= 'h ';
1746
1747 return $path_cmd;
1748
1749 }
1750
1751 //
1752 // fonction retracant les <polyline /> et les <line />
1753 function svgPolyline($arguments,$ispolyline=true){
1754 if ($ispolyline) {
1755 $xbase = $arguments[0] ;
1756 $ybase = - $arguments[1] ;
1757 }
1758 else {
1759 if ($arguments[0]==$arguments[2] && $arguments[1]==$arguments[3]) { return ''; } // mPDF 4.4.003 Zero length line
1760 $xbase = $this->ConvertSVGSizePixels($arguments[0],'x'); // mPDF 4.4.003
1761 $ybase = - $this->ConvertSVGSizePixels($arguments[1],'y'); // mPDF 4.4.003
1762 }
1763 $path_cmd = sprintf('%.3F %.3F m ', $xbase*$this->kp, $ybase*$this->kp);
1764 for ($i = 2; $i<count($arguments);$i += 2) {
1765 if ($ispolyline) {
1766 $tmp_x = $arguments[$i] ;
1767 $tmp_y = - $arguments[($i+1)] ;
1768 }
1769 else {
1770 $tmp_x = $this->ConvertSVGSizePixels($arguments[$i],'x') ; // mPDF 4.4.003
1771 $tmp_y = - $this->ConvertSVGSizePixels($arguments[($i+1)],'y') ; // mPDF 4.4.003
1772 }
1773 $path_cmd .= sprintf('%.3F %.3F l ', $tmp_x*$this->kp, $tmp_y*$this->kp);
1774 }
1775
1776 // $path_cmd .= 'h '; // ?? In error - don't close subpath here
1777 return $path_cmd;
1778
1779 }
1780
1781 //
1782 // fonction retracant les <polygone />
1783 function svgPolygon($arguments){
1784 $xbase = $arguments[0] ;
1785 $ybase = - $arguments[1] ;
1786 $path_cmd = sprintf('%.3F %.3F m ', $xbase*$this->kp, $ybase*$this->kp);
1787 for ($i = 2; $i<count($arguments);$i += 2) {
1788 $tmp_x = $arguments[$i] ;
1789 $tmp_y = - $arguments[($i+1)] ;
1790
1791 $path_cmd .= sprintf('%.3F %.3F l ', $tmp_x*$this->kp, $tmp_y*$this->kp);
1792
1793 }
1794 $path_cmd .= sprintf('%.3F %.3F l ', $xbase*$this->kp, $ybase*$this->kp);
1795 $path_cmd .= 'h ';
1796 return $path_cmd;
1797
1798 }
1799
1800 //
1801 // write string to image
1802 function svgText() {
1803 // $tmp = count($this->txt_style)-1;
1804 $current_style = array_pop($this->txt_style);
1805 $style = '';
1806 $render = -1;
1807 if(isset($this->txt_data[2]))
1808 {
1809 // select font
1810 $style .= ($current_style['font-weight'] == 'bold')?'B':'';
1811 $style .= ($current_style['font-style'] == 'italic')?'I':'';
1812 $size = $current_style['font-size']*$this->kf; // mPDF 5.0.039
1813
1814 // mPDF 5.0
1815 $current_style['font-family'] = $this->mpdf_ref->SetFont($current_style['font-family'],$style,$size,false);
1816 $this->mpdf_ref->CurrentFont['fo'] = true; // mPDF 5.0.039
1817
1818
1819 // mPDF 5.0.041
1820 $opacitystr = '';
1821 $opacity = 1;
1822 if (isset($current_style['fill-opacity'])) {
1823 if ($current_style['fill-opacity'] == 0) { $opacity = 0; }
1824 else if ($current_style['fill-opacity'] > 1) { $opacity = 1; }
1825 else if ($current_style['fill-opacity'] > 0) { $opacity = $current_style['fill-opacity']; }
1826 else if ($current_style['fill-opacity'] < 0) { $opacity = 0; }
1827 }
1828 $gs = $this->mpdf_ref->AddExtGState(array('ca'=>$opacity, 'BM'=>'/Normal'));
1829 $this->mpdf_ref->extgstates[$gs]['fo'] = true; // mPDF 5.0.039
1830 $opacitystr = sprintf(' /GS%d gs ', $gs);
1831
1832 // mPDF 5.0.051
1833 $fillstr = '';
1834 if (isset($current_style['fill']) && $current_style['fill']!='none') {
1835 $col = $this->mpdf_ref->ConvertColor($current_style['fill']);
1836 // mPDF 5.0.051
1837 $fillstr = $this->mpdf_ref->SetFColor($col, true);
1838 $render = "0"; // Fill (only)
1839 }
1840 $strokestr = '';
1841 if (isset($current_style['stroke-width']) && $current_style['stroke-width']>0 && $current_style['stroke']!='none') {
1842 $scol = $this->mpdf_ref->ConvertColor($current_style['stroke']);
1843 if ($scol) {
1844 $strokestr .= $this->mpdf_ref->SetDColor($scol, true).' '; // mPDF 5.0.051
1845 }
1846 $linewidth = $this->ConvertSVGSizePixels($current_style['stroke-width']);
1847 if ($linewidth > 0) {
1848 $strokestr .= sprintf('%.3F w 1 J 1 j ',$linewidth*$this->kp);
1849 if ($render == -1) { $render = "1"; } // stroke only
1850 else { $render = "2"; } // fill and stroke
1851 }
1852 }
1853 if ($render == -1) { return ''; }
1854
1855 $x = $this->ConvertSVGSizePixels($this->txt_data[0],'x'); // mPDF 4.4.003
1856 $y = $this->ConvertSVGSizePixels($this->txt_data[1],'y'); // mPDF 4.4.003
1857 $txt = $this->txt_data[2];
1858
1859 // mPDF 4.4.003
1860 $txt = preg_replace('/\f/','',$txt);
1861 $txt = preg_replace('/\r/','',$txt);
1862 $txt = preg_replace('/\n/',' ',$txt);
1863 $txt = preg_replace('/\t/',' ',$txt);
1864 $txt = preg_replace("/[ ]+/u",' ',$txt);
1865
1866 $txt = trim($txt);
1867
1868 $txt = $this->mpdf_ref->purify_utf8_text($txt);
1869 if ($this->mpdf_ref->text_input_as_HTML) {
1870 $txt = $this->mpdf_ref->all_entities_to_utf8($txt);
1871 }
1872
1873 // mPDF 5.0
1874 if ($this->mpdf_ref->usingCoreFont) { $txt = mb_convert_encoding($txt,$this->mpdf_ref->mb_enc,'UTF-8'); }
1875 if (preg_match("/([".$this->mpdf_ref->pregRTLchars."])/u", $txt)) { $this->mpdf_ref->biDirectional = true; } // mPDF 4.4.003
1876
1877 $this->mpdf_ref->magic_reverse_dir($txt, true, 'ltr'); // mPDF 5.0.054
1878 $this->mpdf_ref->ConvertIndic($txt);
1879
1880
1881 if ($current_style['text-anchor']=='middle') {
1882 $tw = $this->mpdf_ref->GetStringWidth($txt)*_MPDFK/2; // mPDF 4.4.003 // mPDF 5.4.09
1883 }
1884 else if ($current_style['text-anchor']=='end') {
1885 $tw = $this->mpdf_ref->GetStringWidth($txt)*_MPDFK; // mPDF 4.4.003 // mPDF 5.4.09
1886 }
1887 else $tw = 0;
1888
1889 if (!$this->mpdf_ref->usingCoreFont) {
1890 $this->mpdf_ref->UTF8StringToArray($txt); // mPDF 5.0 adds chars to subset list
1891 $txt= $this->mpdf_ref->UTF8ToUTF16BE($txt, false);
1892 }
1893 $txt='('.$this->mpdf_ref->_escape($txt).')';
1894 $this->mpdf_ref->CurrentFont['used']= true;
1895
1896 $pdfx = $x - $tw/$this->kp; // mPDF 4.4.009
1897 $pdfy = -$y ;
1898 $xbase = $x;
1899 $ybase = -$y;
1900
1901 // mPDF 5.0.041
1902 // mPDF 5.0.051
1903 $path_cmd = sprintf('q BT /F%d %s %.3F Tf %.3F %.3F Td %s Tr %s %s %s Tj ET Q ',$this->mpdf_ref->CurrentFont['i'],$opacitystr, $this->mpdf_ref->FontSizePt,$pdfx*$this->kp,$pdfy*$this->kp,$render,$fillstr,$strokestr,$txt)."\n";
1904 unset($this->txt_data[0], $this->txt_data[1],$this->txt_data[2]);
1905
1906 // mPDF 5.4.12
1907 if (isset($current_style['font-size-parent'])) {
1908 $this->mpdf_ref->SetFontSize($current_style['font-size-parent']);
1909 }
1910 }
1911 else
1912 {
1913 return ' ';
1914 }
1915// $path_cmd .= 'h '; // mPDF 5.0
1916 return $path_cmd;
1917 }
1918
1919
1920function svgDefineTxtStyle($critere_style)
1921{
1922 // get copy of current/default txt style, and modify it with supplied attributes
1923 $tmp = count($this->txt_style)-1;
1924 $current_style = $this->txt_style[$tmp];
1925 if (isset($critere_style['style'])){
1926 if (preg_match('/fill:\s*rgb\((\d+),\s*(\d+),\s*(\d+)\)/',$critere_style['style'], $m)) {
1927 $current_style['fill'] = '#'.str_pad(dechex($m[1]), 2, "0", STR_PAD_LEFT).str_pad(dechex($m[2]), 2, "0", STR_PAD_LEFT).str_pad(dechex($m[3]), 2, "0", STR_PAD_LEFT);
1928 }
1929 else { $tmp = preg_replace("/(.*)fill:\s*([a-z0-9#_()]*|none)(.*)/i","$2",$critere_style['style']);
1930 if ($tmp != $critere_style['style']){ $current_style['fill'] = $tmp; }
1931 }
1932
1933 $tmp = preg_replace("/(.*)fill-opacity:\s*([a-z0-9.]*|none)(.*)/i","$2",$critere_style['style']);
1934 if ($tmp != $critere_style['style']){ $current_style['fill-opacity'] = $tmp;}
1935
1936 $tmp = preg_replace("/(.*)fill-rule:\s*([a-z0-9#]*|none)(.*)/i","$2",$critere_style['style']);
1937 if ($tmp != $critere_style['style']){ $current_style['fill-rule'] = $tmp;}
1938
1939 if (preg_match('/stroke:\s*rgb\((\d+),\s*(\d+),\s*(\d+)\)/',$critere_style['style'], $m)) {
1940 $current_style['stroke'] = '#'.str_pad(dechex($m[1]), 2, "0", STR_PAD_LEFT).str_pad(dechex($m[2]), 2, "0", STR_PAD_LEFT).str_pad(dechex($m[3]), 2, "0", STR_PAD_LEFT);
1941 }
1942 else { $tmp = preg_replace("/(.*)stroke:\s*([a-z0-9#]*|none)(.*)/i","$2",$critere_style['style']);
1943 if ($tmp != $critere_style['style']){ $current_style['stroke'] = $tmp; }
1944 }
1945
1946 $tmp = preg_replace("/(.*)stroke-linecap:\s*([a-z0-9#]*|none)(.*)/i","$2",$critere_style['style']);
1947 if ($tmp != $critere_style['style']){ $current_style['stroke-linecap'] = $tmp;}
1948
1949 $tmp = preg_replace("/(.*)stroke-linejoin:\s*([a-z0-9#]*|none)(.*)/i","$2",$critere_style['style']);
1950 if ($tmp != $critere_style['style']){ $current_style['stroke-linejoin'] = $tmp;}
1951
1952 $tmp = preg_replace("/(.*)stroke-miterlimit:\s*([a-z0-9#]*|none)(.*)/i","$2",$critere_style['style']);
1953 if ($tmp != $critere_style['style']){ $current_style['stroke-miterlimit'] = $tmp;}
1954
1955 $tmp = preg_replace("/(.*)stroke-opacity:\s*([a-z0-9.]*|none)(.*)/i","$2",$critere_style['style']);
1956 if ($tmp != $critere_style['style']){ $current_style['stroke-opacity'] = $tmp; }
1957
1958 $tmp = preg_replace("/(.*)stroke-width:\s*([a-z0-9.]*|none)(.*)/i","$2",$critere_style['style']);
1959 if ($tmp != $critere_style['style']){ $current_style['stroke-width'] = $tmp;}
1960
1961 $tmp = preg_replace("/(.*)stroke-dasharray:\s*([a-z0-9., ]*|none)(.*)/i","$2",$critere_style['style']);
1962 if ($tmp != $critere_style['style']){ $current_style['stroke-dasharray'] = $tmp;}
1963
1964 $tmp = preg_replace("/(.*)stroke-dashoffset:\s*([a-z0-9.]*|none)(.*)/i","$2",$critere_style['style']);
1965 if ($tmp != $critere_style['style']){ $current_style['stroke-dashoffset'] = $tmp;}
1966
1967 // mPDF 5.0.039
1968 $tmp = preg_replace("/(.*)font-family:\s*([a-z0-9.]*|none)(.*)/i","$2",$critere_style['style']);
1969 if ($tmp != $critere_style['style']){ $critere_style['font-family'] = $tmp;}
1970
1971 $tmp = preg_replace("/(.*)font-size:\s*([a-z0-9.]*|none)(.*)/i","$2",$critere_style['style']);
1972 if ($tmp != $critere_style['style']){ $critere_style['font-size'] = $tmp;}
1973
1974 $tmp = preg_replace("/(.*)font-weight:\s*([a-z0-9.]*|none)(.*)/i","$2",$critere_style['style']);
1975 if ($tmp != $critere_style['style']){ $critere_style['font-weight'] = $tmp;}
1976
1977 $tmp = preg_replace("/(.*)font-style:\s*([a-z0-9.]*|none)(.*)/i","$2",$critere_style['style']);
1978 if ($tmp != $critere_style['style']){ $critere_style['font-style'] = $tmp;}
1979
1980 }
1981
1982 if (isset($critere_style['font'])){
1983
1984 // [ [ <'font-style'> || <'font-variant'> || <'font-weight'> ]?<'font-size'> [ / <'line-height'> ]? <'font-family'> ]
1985
1986 $tmp = preg_replace("/(.*)(italic|oblique)(.*)/i","$2",$critere_style['font']);
1987 if ($tmp != $critere_style['font']){
1988 if($tmp == 'oblique'){
1989 $tmp = 'italic';
1990 }
1991 $current_style['font-style'] = $tmp;
1992 }
1993 $tmp = preg_replace("/(.*)(bold|bolder)(.*)/i","$2",$critere_style['font']);
1994 if ($tmp != $critere_style['font']){
1995 if($tmp == 'bolder'){
1996 $tmp = 'bold';
1997 }
1998 $current_style['font-weight'] = $tmp;
1999 }
2000
2001 // select digits not followed by percent sign nor preceeded by forward slash
2002 $tmp = preg_replace("/(.*)\b(\d+)[\b|\/](.*)/i","$2",$critere_style['font']);
2003 if ($tmp != $critere_style['font']){
2004 $current_style['font-size'] = $this->ConvertSVGSizePts($tmp);
2005 $this->mpdf_ref->SetFont('','',$current_style['font-size'],false);
2006 }
2007
2008 }
2009
2010 if(isset($critere_style['fill'])){
2011 $current_style['fill'] = $critere_style['fill'];
2012 }
2013 if(isset($critere_style['stroke'])){
2014 $current_style['stroke'] = $critere_style['stroke'];
2015 }
2016 if(isset($critere_style['stroke-width'])){
2017 $current_style['stroke-width'] = $critere_style['stroke-width'];
2018 }
2019
2020 if(isset($critere_style['font-style'])){
2021 if(strtolower($critere_style['font-style']) == 'oblique')
2022 {
2023 $critere_style['font-style'] = 'italic';
2024 }
2025 $current_style['font-style'] = $critere_style['font-style'];
2026 }
2027
2028 if(isset($critere_style['font-weight'])){
2029 if(strtolower($critere_style['font-weight']) == 'bolder')
2030 {
2031 $critere_style['font-weight'] = 'bold';
2032 }
2033 $current_style['font-weight'] = $critere_style['font-weight'];
2034 }
2035
2036 if(isset($critere_style['font-size'])){
2037 // mPDF 5.4.12
2038 if (strpos($critere_style['font-size'], '%')!==false) {
2039 $current_style['font-size-parent'] = $current_style['font-size'];
2040 }
2041 $current_style['font-size'] = $this->ConvertSVGSizePts($critere_style['font-size']);
2042 $this->mpdf_ref->SetFont('','',$current_style['font-size'],false);
2043 }
2044
2045 if(isset($critere_style['font-family'])){
2046 $v = $critere_style['font-family'];
2047 $aux_fontlist = explode(",",$v);
2048 $found = 0;
2049 foreach($aux_fontlist AS $f) {
2050 $fonttype = trim($f);
2051 $fonttype = preg_replace('/["\']*(.*?)["\']*/','\\1',$fonttype);
2052 $fonttype = preg_replace('/ /','',$fonttype);
2053 $v = strtolower(trim($fonttype));
2054 if (isset($this->mpdf_ref->fonttrans[$v]) && $this->mpdf_ref->fonttrans[$v]) { $v = $this->mpdf_ref->fonttrans[$v]; }
2055 if ((!$this->mpdf_ref->usingCoreFont && in_array($v,$this->mpdf_ref->available_unifonts)) ||
2056 ($this->mpdf_ref->usingCoreFont && in_array($v,array('courier','times','helvetica','arial'))) ||
2057 in_array($v, array('sjis','uhc','big5','gb'))) {
2058 $current_style['font-family'] = $v;
2059 $found = 1;
2060 break;
2061 }
2062 }
2063 if (!$found) {
2064 foreach($aux_fontlist AS $f) {
2065 $fonttype = trim($f);
2066 $fonttype = preg_replace('/["\']*(.*?)["\']*/','\\1',$fonttype);
2067 $fonttype = preg_replace('/ /','',$fonttype);
2068 $v = strtolower(trim($fonttype));
2069 if (isset($this->mpdf_ref->fonttrans[$v]) && $this->mpdf_ref->fonttrans[$v]) { $v = $this->mpdf_ref->fonttrans[$v]; }
2070 if (in_array($v,$this->mpdf_ref->sans_fonts) || in_array($v,$this->mpdf_ref->serif_fonts) || in_array($v,$this->mpdf_ref->mono_fonts) ) {
2071 $current_style['font-family'] = $v;
2072 break;
2073 }
2074 }
2075 }
2076 }
2077
2078 if(isset($critere_style['text-anchor'])){
2079 $current_style['text-anchor'] = $critere_style['text-anchor'];
2080 }
2081
2082 // add current style to text style array (will remove it later after writing text to svg_string)
2083 array_push($this->txt_style,$current_style);
2084}
2085
2086
2087
2088 //
2089 // fonction ajoutant un gradient
2090 function svgAddGradient($id,$array_gradient){
2091
2092 $this->svg_gradient[$id] = $array_gradient;
2093
2094 }
2095 //
2096 // Ajoute une couleur dans le gradient correspondant
2097
2098 //
2099 // function ecrivant dans le svgstring
2100 function svgWriteString($content){
2101
2102 $this->svg_string .= $content;
2103
2104 }
2105
2106
2107
2108 // analise le svg et renvoie aux fonctions precedente our le traitement
2109 function ImageSVG($data){
2110 $this->svg_info = array();
2111
2112 // mPDF 4.4.006
2113 if (preg_match('/<!ENTITY/si',$data)) {
2114 // Get User-defined entities
2115 preg_match_all('/<!ENTITY\s+([a-z]+)\s+\"(.*?)\">/si',$data, $ent);
2116 // Replace entities
2117 for ($i=0; $i<count($ent[0]); $i++) {
2118 $data = preg_replace('/&'.preg_quote($ent[1][$i],'/').';/is', $ent[2][$i], $data);
2119 }
2120 }
2121
2122
2123 // mPDF 4.4.003
2124 if (preg_match('/xlink:href=/si',$data)) {
2125 // Get links
2126 preg_match_all('/(<(linearGradient|radialgradient)[^>]*)xlink:href=["\']#(.*?)["\'](.*?)\/>/si',$data, $links);
2127 if (count($links[0])) { $links[5] = array(); } // mPDF 4.5.010
2128 // Delete links from data - keeping in $links
2129 for ($i=0; $i<count($links[0]); $i++) {
2130 $links[5][$i] = 'tmpLink'.RAND(100000,9999999); // mPDF 4.5.010
2131 $data = preg_replace('/'.preg_quote($links[0][$i],'/').'/is', '<MYLINKS'.$links[5][$i].'>' , $data); // mPDF 4.5.010
2132 }
2133 // Get targets
2134 preg_match_all('/<(linearGradient|radialgradient)([^>]*)id=["\'](.*?)["\'](.*?)>(.*?)<\/(linearGradient|radialgradient)>/si',$data, $m);
2135 $targets = array();
2136 $stops = array();
2137 // keeping in $targets
2138 for ($i=0; $i<count($m[0]); $i++) {
2139 $stops[$m[3][$i]] = $m[5][$i];
2140 }
2141 // Add back links this time as targets (gradients)
2142 for ($i=0; $i<count($links[0]); $i++) {
2143 $def = $links[1][$i] .' '.$links[4][$i].'>'. $stops[$links[3][$i]].'</'.$links[2][$i] .'>' ; // mPDF 4.5.010
2144 $data = preg_replace('/<MYLINKS'.$links[5][$i].'>/is', $def , $data); // mPDF 4.5.010
2145 }
2146 }
2147 // mPDF 4.4.003 - Removes <pattern>
2148 $data = preg_replace('/<pattern.*?<\/pattern>/is', '', $data);
2149 // mPDF 4.4.003 - Removes <marker>
2150 $data = preg_replace('/<marker.*?<\/marker>/is', '', $data);
2151
2152 $this->svg_info['data'] = $data;
2153
2154 $this->svg_string = '';
2155
2156 //
2157 // chargement unique des fonctions
2158 if(!function_exists("xml_svg2pdf_start")){ // mPDF 5.3.76
2159
2160 function xml_svg2pdf_start($parser, $name, $attribs){
2161 //
2162 // definition
2163 global $svg_class, $last_gradid;
2164
2165 // mPDF 4.4.003
2166 $svg_class->xbase = 0;
2167 $svg_class->ybase = 0;
2168 switch (strtolower($name)){
2169
2170 // mPDF 5.0.039 - Don't output stuff inside <defs>
2171 case 'defs':
2172 $svg_class->inDefs = true;
2173 return;
2174
2175 case 'svg':
2176 $svg_class->svgOffset($attribs);
2177 break;
2178
2179 case 'path':
2180 $path = $attribs['d'];
2181 // mPDF 5.6.65
2182 preg_match_all('/([MZLHVCSQTAmzlhvcsqta])([eE ,\-.\d]+)*/', $path, $commands, PREG_SET_ORDER);
2183 $path_cmd = '';
2184 $svg_class->subPathInit = true;
2185 // mPDF 5.0.039
2186 $svg_class->pathBBox = array(999999,999999,-999999,-999999);
2187 foreach($commands as $c){
2188 if(count($c)==3 || $c[2]==''){
2189 list($tmp, $command, $arguments) = $c;
2190 }
2191 else{
2192 list($tmp, $command) = $c;
2193 $arguments = '';
2194 }
2195
2196 $path_cmd .= $svg_class->svgPath($command, $arguments);
2197 }
2198 // mPDF 5.0.039
2199 if ($svg_class->pathBBox[2]==-1999998) { $svg_class->pathBBox[2] = 100; }
2200 if ($svg_class->pathBBox[3]==-1999998) { $svg_class->pathBBox[3] = 100; }
2201 if ($svg_class->pathBBox[0]==999999) { $svg_class->pathBBox[0] = 0; }
2202 if ($svg_class->pathBBox[1]==999999) { $svg_class->pathBBox[1] = 0; }
2203 $critere_style = $attribs;
2204 unset($critere_style['d']);
2205 $path_style = $svg_class->svgDefineStyle($critere_style);
2206 break;
2207
2208 case 'rect':
2209 if (!isset($attribs['x'])) {$attribs['x'] = 0;}
2210 if (!isset($attribs['y'])) {$attribs['y'] = 0;}
2211 if (!isset($attribs['rx'])) {$attribs['rx'] = 0;}
2212 if (!isset($attribs['ry'])) {$attribs['ry'] = 0;}
2213 $arguments = array(
2214 'x' => $attribs['x'],
2215 'y' => $attribs['y'],
2216 'w' => $attribs['width'],
2217 'h' => $attribs['height'],
2218 'rx' => $attribs['rx'],
2219 'ry' => $attribs['ry']
2220 );
2221 $path_cmd = $svg_class->svgRect($arguments);
2222 $critere_style = $attribs;
2223 unset($critere_style['x'],$critere_style['y'],$critere_style['rx'],$critere_style['ry'],$critere_style['height'],$critere_style['width']);
2224 $path_style = $svg_class->svgDefineStyle($critere_style);
2225 break;
2226
2227 case 'circle':
2228 if (!isset($attribs['cx'])) {$attribs['cx'] = 0;}
2229 if (!isset($attribs['cy'])) {$attribs['cy'] = 0;}
2230 $arguments = array(
2231 'cx' => $attribs['cx'],
2232 'cy' => $attribs['cy'],
2233 'rx' => $attribs['r'],
2234 'ry' => $attribs['r']
2235 );
2236 $path_cmd = $svg_class->svgEllipse($arguments);
2237 $critere_style = $attribs;
2238 unset($critere_style['cx'],$critere_style['cy'],$critere_style['r']);
2239 $path_style = $svg_class->svgDefineStyle($critere_style);
2240 break;
2241
2242 case 'ellipse':
2243 if (!isset($attribs['cx'])) {$attribs['cx'] = 0;}
2244 if (!isset($attribs['cy'])) {$attribs['cy'] = 0;}
2245 $arguments = array(
2246 'cx' => $attribs['cx'],
2247 'cy' => $attribs['cy'],
2248 'rx' => $attribs['rx'],
2249 'ry' => $attribs['ry']
2250 );
2251 $path_cmd = $svg_class->svgEllipse($arguments);
2252 $critere_style = $attribs;
2253 unset($critere_style['cx'],$critere_style['cy'],$critere_style['rx'],$critere_style['ry']);
2254 $path_style = $svg_class->svgDefineStyle($critere_style);
2255 break;
2256
2257 case 'line':
2258 $arguments = array($attribs['x1'],$attribs['y1'],$attribs['x2'],$attribs['y2']);
2259 $path_cmd = $svg_class->svgPolyline($arguments,false); // mPDF 4.4.003
2260 $critere_style = $attribs;
2261 unset($critere_style['x1'],$critere_style['y1'],$critere_style['x2'],$critere_style['y2']);
2262 $path_style = $svg_class->svgDefineStyle($critere_style);
2263 break;
2264
2265 case 'polyline':
2266 $path = $attribs['points'];
2267 preg_match_all('/[0-9\-\.]*/',$path, $tmp, PREG_SET_ORDER);
2268 $arguments = array();
2269 for ($i=0;$i<count($tmp);$i++){
2270 if ($tmp[$i][0] !=''){
2271 array_push($arguments, $tmp[$i][0]);
2272 }
2273 }
2274 $path_cmd = $svg_class->svgPolyline($arguments);
2275 $critere_style = $attribs;
2276 unset($critere_style['points']);
2277 $path_style = $svg_class->svgDefineStyle($critere_style);
2278 break;
2279
2280 case 'polygon':
2281 $path = $attribs['points'];
2282 preg_match_all('/([\-]*[0-9\.]+)/',$path, $tmp);
2283 $arguments = array();
2284 for ($i=0;$i<count($tmp[0]);$i++){
2285 if ($tmp[0][$i] !=''){
2286 array_push($arguments, $tmp[0][$i]);
2287 }
2288 }
2289 $path_cmd = $svg_class->svgPolygon($arguments);
2290 // definition du style de la forme:
2291 $critere_style = $attribs;
2292 unset($critere_style['points']);
2293 $path_style = $svg_class->svgDefineStyle($critere_style);
2294 break;
2295
2296 case 'lineargradient':
2297 $tmp_gradient = array(
2298 'type' => 'linear',
2299 'info' => array(
2300 'x1' => $attribs['x1'],
2301 'y1' => $attribs['y1'],
2302 'x2' => $attribs['x2'],
2303 'y2' => $attribs['y2']
2304 ),
2305 'transform' => $attribs['gradientTransform'],
2306 'units' => $attribs['gradientUnits'], /* mPDF 4.4.003 */
2307 'spread' => $attribs['spreadMethod'], /* mPDF 5.0.040 */
2308 'color' => array()
2309 );
2310
2311 $last_gradid = $attribs['id'];
2312 $svg_class->svgAddGradient($attribs['id'],$tmp_gradient);
2313 break;
2314
2315 case 'radialgradient':
2316 $tmp_gradient = array(
2317 'type' => 'radial',
2318 'info' => array(
2319 'x0' => $attribs['cx'],
2320 'y0' => $attribs['cy'],
2321 'x1' => $attribs['fx'],
2322 'y1' => $attribs['fy'],
2323 'r' => $attribs['r']
2324 ),
2325 'transform' => $attribs['gradientTransform'],
2326 'units' => $attribs['gradientUnits'], /* mPDF 4.4.003 */
2327 'spread' => $attribs['spreadMethod'], /* mPDF 5.0.040 */
2328 'color' => array()
2329 );
2330
2331 $last_gradid = $attribs['id'];
2332
2333 $svg_class->svgAddGradient($attribs['id'],$tmp_gradient);
2334
2335 break;
2336
2337 case 'stop':
2338 if (!$last_gradid) break;
2339 // mPDF 4.4.003 // mPDF 5.0.040
2340 if (isset($attribs['style']) AND preg_match('/stop-color:\s*([^;]*)/i',$attribs['style'],$m)) {
2341 $color = trim($m[1]);
2342 } else if (isset($attribs['stop-color'])) {
2343 $color = $attribs['stop-color'];
2344 }
2345 $col = $svg_class->mpdf_ref->ConvertColor($color);
2346
2347 // mPDF 5.0.051
2348 // mPDF 5.3.74
2349 if ($col{0}==3 || $col{0}==5) { // RGB
2350 $color_final = sprintf('%.3F %.3F %.3F',ord($col{1})/255,ord($col{2})/255,ord($col{3})/255);
2351 $svg_class->svg_gradient[$last_gradid]['colorspace']='RGB';
2352 }
2353 else if ($col{0}==4 || $col{0}==6) { // CMYK
2354 $color_final = sprintf('%.3F %.3F %.3F %.3F',ord($col{1})/100,ord($col{2})/100,ord($col{3})/100,ord($col{4})/100);
2355 $svg_class->svg_gradient[$last_gradid]['colorspace']='CMYK';
2356 }
2357 else if ($col{0}==1) { // Grayscale
2358 $color_final = sprintf('%.3F',ord($col{1})/255);
2359 $svg_class->svg_gradient[$last_gradid]['colorspace']='Gray';
2360 }
2361
2362
2363 // mPDF 5.0.020
2364 $stop_opacity = 1;
2365 // mPDF 4.4.003
2366 if (isset($attribs['style']) AND preg_match('/stop-opacity:\s*([0-9.]*)/i',$attribs['style'],$m)) {
2367 $stop_opacity = $m[1];
2368 } else if (isset($attribs['stop-opacity'])) {
2369 $stop_opacity = $attribs['stop-opacity'];
2370 }
2371 // mPDF 5.0.051
2372 // mPDF 5.3.74
2373 else if ($col{0}==5) { // RGBa
2374 $stop_opacity = ord($col{4}/100);
2375 }
2376 else if ($col{0}==6) { // CMYKa
2377 $stop_opacity = ord($col{5}/100);
2378 }
2379
2380 $tmp_color = array(
2381 'color' => $color_final,
2382 'offset' => $attribs['offset'],
2383 'opacity' => $stop_opacity
2384 );
2385 array_push($svg_class->svg_gradient[$last_gradid]['color'],$tmp_color);
2386 break;
2387
2388
2389 case 'a':
2390 if (isset($attribs['xlink:href'])) {
2391 unset($attribs['xlink:href']); // this should be a hyperlink
2392 // not handled like a xlink:href in other elements
2393 } // then continue like a <g>
2394 case 'g':
2395 $array_style = $svg_class->svgDefineStyle($attribs);
2396 if ($array_style['transformations']) {
2397 $svg_class->svgWriteString(' q '.$array_style['transformations']);
2398 }
2399 array_push($svg_class->svg_style,$array_style);
2400
2401 $svg_class->svgDefineTxtStyle($attribs); // mPDF 4.4.003
2402
2403 break;
2404
2405 case 'text':
2406 // mPDF 4.4.003
2407 $array_style = $svg_class->svgDefineStyle($attribs);
2408 if ($array_style['transformations']) {
2409 $svg_class->svgWriteString(' q '.$array_style['transformations']);
2410 }
2411 array_push($svg_class->svg_style,$array_style);
2412
2413 $svg_class->txt_data = array();
2414 $svg_class->txt_data[0] = $attribs['x'];
2415 $svg_class->txt_data[1] = $attribs['y'];
2416 $critere_style = $attribs;
2417 unset($critere_style['x'], $critere_style['y']);
2418 $svg_class->svgDefineTxtStyle($critere_style);
2419 break;
2420 }
2421
2422 //
2423 //insertion des path et du style dans le flux de donné general.
2424 if (isset($path_cmd) && $path_cmd) { // mPDF 4.4.003
2425 // mPDF 5.0
2426 list($prestyle,$poststyle) = $svg_class->svgStyle($path_style, $attribs, strtolower($name));
2427 if ($path_style['transformations']) { // transformation on an element
2428 $svg_class->svgWriteString(" q ".$path_style['transformations']. " $prestyle $path_cmd $poststyle" . " Q\n");
2429 }
2430 else {
2431 $svg_class->svgWriteString("$prestyle $path_cmd $poststyle\n");
2432 }
2433 }
2434 }
2435
2436 function characterData($parser, $data)
2437 {
2438 global $svg_class;
2439 if(isset($svg_class->txt_data[2])) {
2440 $svg_class->txt_data[2] .= $data;
2441 }
2442 else {
2443 $svg_class->txt_data[2] = $data;
2444 }
2445 }
2446
2447
2448 function xml_svg2pdf_end($parser, $name){
2449 global $svg_class;
2450 switch($name){
2451
2452 case "g":
2453 case "a":
2454 $tmp = count($svg_class->svg_style)-1;
2455 $current_style = $svg_class->svg_style[$tmp];
2456 if ($current_style['transformations']) {
2457 $svg_class->svgWriteString(" Q\n");
2458 }
2459 array_pop($svg_class->svg_style);
2460
2461 array_pop($svg_class->txt_style); // mPDF 4.4.003
2462
2463 break;
2464 case 'radialgradient':
2465 case 'lineargradient':
2466 $last_gradid = '';
2467 break;
2468 case "text":
2469 $path_cmd = $svg_class->svgText();
2470 // echo 'path >> '.$path_cmd."<br><br>";
2471 // echo "style >> ".$get_style[1]."<br><br>";
2472 $svg_class->svgWriteString($path_cmd);
2473 // mPDF 4.4.003
2474 $tmp = count($svg_class->svg_style)-1;
2475 $current_style = $svg_class->svg_style[$tmp];
2476 if ($current_style['transformations']) {
2477 $svg_class->svgWriteString(" Q\n");
2478 }
2479 array_pop($svg_class->svg_style);
2480
2481 break;
2482 }
2483 // mPDF 5.0.039 - Don't output stuff inside <defs>
2484 if ($name == 'defs') {
2485 $svg_class->inDefs = false;
2486 }
2487
2488 }
2489
2490 }
2491
2492 $svg2pdf_xml='';
2493 global $svg_class;
2494 $svg_class = $this;
2495 // mPDF 5.0.039 - Don't output stuff inside <defs>
2496 $svg_class->inDefs = false;
2497 $svg2pdf_xml_parser = xml_parser_create("utf-8");
2498 xml_parser_set_option($svg2pdf_xml_parser, XML_OPTION_CASE_FOLDING, false);
2499 xml_set_element_handler($svg2pdf_xml_parser, "xml_svg2pdf_start", "xml_svg2pdf_end");
2500 xml_set_character_data_handler($svg2pdf_xml_parser, "characterData");
2501 xml_parse($svg2pdf_xml_parser, $data);
2502 // mPDF 4.4.003
2503 if ($this->svg_error) { return false; }
2504 else {
2505 return array('x'=>$this->svg_info['x']*$this->kp,'y'=>-$this->svg_info['y']*$this->kp,'w'=>$this->svg_info['w']*$this->kp,'h'=>-$this->svg_info['h']*$this->kp,'data'=>$svg_class->svg_string);
2506 }
2507
2508 }
2509
2510}
2511
2512// END OF CLASS
2513
2514
2515// mPDF 5.0.040
2516function calc_bezier_bbox($start, $c) {
2517 $P0 = array($start[0],$start[1]);
2518 $P1 = array($c[0],$c[1]);
2519 $P2 = array($c[2],$c[3]);
2520 $P3 = array($c[4],$c[5]);
2521 $bounds = array();
2522 $bounds[0][] = $P0[0];
2523 $bounds[1][] = $P0[1];
2524 $bounds[0][] = $P3[0];
2525 $bounds[1][] = $P3[1];
2526 for ($i=0;$i<=1;$i++) {
2527 $b = 6 * $P0[$i] - 12 * $P1[$i] + 6 * $P2[$i];
2528 $a = -3 * $P0[$i] + 9 * $P1[$i] - 9 * $P2[$i] + 3 * $P3[$i];
2529 $c = 3 * $P1[$i] - 3 * $P0[$i];
2530 if ($a == 0) {
2531 if ($b == 0) { continue; }
2532 $t = -$c / $b;
2533 if ($t>0 && $t<1) {
2534 $bounds[$i][] = (pow((1-$t),3) * $P0[$i] + 3 * pow((1-$t),2) * $t * $P1[$i] + 3 * (1-$t) * pow($t,2) * $P2[$i] + pow($t,3) * $P3[$i]);
2535 }
2536 continue;
2537 }
2538 $b2ac = pow($b, 2) - 4 * $c * $a;
2539 if ($b2ac < 0) { continue; }
2540 $t1 = (-$b + sqrt($b2ac))/(2 * $a);
2541 if ($t1>0 && $t1<1) {
2542 $bounds[$i][] = (pow((1-$t1),3) * $P0[$i] + 3 * pow((1-$t1),2) * $t1 * $P1[$i] + 3 * (1-$t1) * pow($t1,2) * $P2[$i] + pow($t1,3) * $P3[$i]);
2543 }
2544 $t2 = (-$b - sqrt($b2ac))/(2 * $a);
2545 if ($t2>0 && $t2<1) {
2546 $bounds[$i][] = (pow((1-$t2),3) * $P0[$i] + 3 * pow((1-$t2),2) * $t2 * $P1[$i] + 3 * (1-$t2) * pow($t2,2) * $P2[$i] + pow($t2,3) * $P3[$i]);
2547 }
2548 }
2549 $x = min($bounds[0]);
2550 $x2 = max($bounds[0]);
2551 $y = min($bounds[1]);
2552 $y2 = max($bounds[1]);
2553 return array($x, $y, $x2, $y2);
2554}
2555
2556// mPDF 5.0.040
2557function _testIntersectCircle($cx, $cy, $cr) {
2558 // Tests whether a circle fully encloses a rectangle 0,0,1,1
2559 // to see if any further radial gradients need adding (SVG)
2560 // If centre of circle is inside 0,0,1,1 square
2561 if ($cx >= 0 && $cx <= 1 && $cy >= 0 && $cy <= 1) {
2562 $maxd = 1.5;
2563 }
2564 // distance to four corners
2565 else {
2566 $d1 = sqrt(pow(($cy-0),2) + pow(($cx-0),2));
2567 $d2 = sqrt(pow(($cy-1),2) + pow(($cx-0),2));
2568 $d3 = sqrt(pow(($cy-0),2) + pow(($cx-1),2));
2569 $d4 = sqrt(pow(($cy-1),2) + pow(($cx-1),2));
2570 $maxd = max($d1,$d2,$d3,$d4);
2571 }
2572 if ($cr < $maxd) { return true; }
2573 else { return false; }
2574}
2575
2576// mPDF 5.0.040
2577function _testIntersect($x1, $y1, $x2, $y2, $x3, $y3, $x4, $y4) {
2578 // Tests whether line (x1, y1) and (x2, y2) [a gradient axis (perpendicular)]
2579 // intersects with a specific line segment (x3, y3) and (x4, y4)
2580 $a1 = $y2-$y1;
2581 $b1 = $x1-$x2;
2582 $c1 = $a1*$x1+$b1*$y1;
2583 $a2 = $y4-$y3;
2584 $b2 = $x3-$x4;
2585 $c2 = $a2*$x3+$b2*$y3;
2586 $det = $a1*$b2 - $a2*$b1;
2587 if($det == 0){ //Lines are parallel
2588 return false;
2589 }
2590 else{
2591 $x = ($b2*$c1 - $b1*$c2)/$det;
2592 $y = ($a1*$c2 - $a2*$c1)/$det;
2593 if ($x >= $x3 && $x <= $x4 && $y >= $y3 && $y <= $y4) { return true; }
2594 }
2595 return false;
2596}
2597
2598
2599
2600?> \ No newline at end of file
diff --git a/inc/3rdparty/libraries/mpdf/classes/tocontents.php b/inc/3rdparty/libraries/mpdf/classes/tocontents.php
new file mode 100644
index 00000000..4e0065d2
--- /dev/null
+++ b/inc/3rdparty/libraries/mpdf/classes/tocontents.php
@@ -0,0 +1,468 @@
1<?php
2
3class tocontents {
4
5var $mpdf = null;
6var $_toc;
7var $TOCmark;
8var $TOCoutdent; // mPDF 5.6.31
9var $TOCpreHTML;
10var $TOCpostHTML;
11var $TOCbookmarkText;
12var $TOCusePaging;
13var $TOCuseLinking;
14var $TOCorientation;
15var $TOC_margin_left;
16var $TOC_margin_right;
17var $TOC_margin_top;
18var $TOC_margin_bottom;
19var $TOC_margin_header;
20var $TOC_margin_footer;
21var $TOC_odd_header_name;
22var $TOC_even_header_name;
23var $TOC_odd_footer_name;
24var $TOC_even_footer_name;
25var $TOC_odd_header_value;
26var $TOC_even_header_value;
27var $TOC_odd_footer_value;
28var $TOC_even_footer_value;
29var $TOC_page_selector;
30var $m_TOC;
31
32function tocontents(&$mpdf) {
33 $this->mpdf = $mpdf;
34 $this->_toc=array();
35 $this->TOCmark = 0;
36 $this->m_TOC=array();
37}
38
39function TOCpagebreak($tocfont='', $tocfontsize='', $tocindent='', $TOCusePaging=true, $TOCuseLinking='', $toc_orientation='', $toc_mgl='',$toc_mgr='',$toc_mgt='',$toc_mgb='',$toc_mgh='',$toc_mgf='',$toc_ohname='',$toc_ehname='',$toc_ofname='',$toc_efname='',$toc_ohvalue=0,$toc_ehvalue=0,$toc_ofvalue=0, $toc_efvalue=0, $toc_preHTML='', $toc_postHTML='', $toc_bookmarkText='', $resetpagenum='', $pagenumstyle='', $suppress='', $orientation='', $mgl='',$mgr='',$mgt='',$mgb='',$mgh='',$mgf='',$ohname='',$ehname='',$ofname='',$efname='',$ohvalue=0,$ehvalue=0,$ofvalue=0,$efvalue=0, $toc_id=0, $pagesel='', $toc_pagesel='', $sheetsize='', $toc_sheetsize='', $tocoutdent='') { // mPDF 5.6.19
40 if (strtoupper($toc_id)=='ALL') { $toc_id = '_mpdf_all'; }
41 else if (!$toc_id) { $toc_id = 0; }
42 else { $toc_id = strtolower($toc_id); }
43
44 if ($TOCusePaging === false || strtolower($TOCusePaging) == "off" || $TOCusePaging === 0 || $TOCusePaging === "0" || $TOCusePaging === "") { $TOCusePaging = false; }
45 else { $TOCusePaging = true; }
46 if (!$TOCuseLinking) { $TOCuseLinking = false; }
47 if ($toc_id) {
48 $this->m_TOC[$toc_id]['TOCmark'] = $this->mpdf->page;
49 $this->m_TOC[$toc_id]['TOCoutdent'] = $tocoutdent;
50 $this->m_TOC[$toc_id]['TOCorientation'] = $toc_orientation;
51 $this->m_TOC[$toc_id]['TOCuseLinking'] = $TOCuseLinking;
52 $this->m_TOC[$toc_id]['TOCusePaging'] = $TOCusePaging;
53
54 if ($toc_preHTML) { $this->m_TOC[$toc_id]['TOCpreHTML'] = $toc_preHTML; }
55 if ($toc_postHTML) { $this->m_TOC[$toc_id]['TOCpostHTML'] = $toc_postHTML; }
56 if ($toc_bookmarkText) { $this->m_TOC[$toc_id]['TOCbookmarkText'] = $toc_bookmarkText; }
57
58 $this->m_TOC[$toc_id]['TOC_margin_left'] = $toc_mgl;
59 $this->m_TOC[$toc_id]['TOC_margin_right'] = $toc_mgr;
60 $this->m_TOC[$toc_id]['TOC_margin_top'] = $toc_mgt;
61 $this->m_TOC[$toc_id]['TOC_margin_bottom'] = $toc_mgb;
62 $this->m_TOC[$toc_id]['TOC_margin_header'] = $toc_mgh;
63 $this->m_TOC[$toc_id]['TOC_margin_footer'] = $toc_mgf;
64 $this->m_TOC[$toc_id]['TOC_odd_header_name'] = $toc_ohname;
65 $this->m_TOC[$toc_id]['TOC_even_header_name'] = $toc_ehname;
66 $this->m_TOC[$toc_id]['TOC_odd_footer_name'] = $toc_ofname;
67 $this->m_TOC[$toc_id]['TOC_even_footer_name'] = $toc_efname;
68 $this->m_TOC[$toc_id]['TOC_odd_header_value'] = $toc_ohvalue;
69 $this->m_TOC[$toc_id]['TOC_even_header_value'] = $toc_ehvalue;
70 $this->m_TOC[$toc_id]['TOC_odd_footer_value'] = $toc_ofvalue;
71 $this->m_TOC[$toc_id]['TOC_even_footer_value'] = $toc_efvalue;
72 $this->m_TOC[$toc_id]['TOC_page_selector'] = $toc_pagesel;
73 $this->m_TOC[$toc_id]['TOCsheetsize'] = $toc_sheetsize;
74 }
75 else {
76 $this->TOCmark = $this->mpdf->page;
77 $this->TOCoutdent = $tocoutdent;
78 $this->TOCorientation = $toc_orientation;
79 $this->TOCuseLinking = $TOCuseLinking;
80 $this->TOCusePaging = $TOCusePaging;
81
82 if ($toc_preHTML) { $this->TOCpreHTML = $toc_preHTML; }
83 if ($toc_postHTML) { $this->TOCpostHTML = $toc_postHTML; }
84 if ($toc_bookmarkText) { $this->TOCbookmarkText = $toc_bookmarkText; }
85
86 $this->TOC_margin_left = $toc_mgl;
87 $this->TOC_margin_right = $toc_mgr;
88 $this->TOC_margin_top = $toc_mgt;
89 $this->TOC_margin_bottom = $toc_mgb;
90 $this->TOC_margin_header = $toc_mgh;
91 $this->TOC_margin_footer = $toc_mgf;
92 $this->TOC_odd_header_name = $toc_ohname;
93 $this->TOC_even_header_name = $toc_ehname;
94 $this->TOC_odd_footer_name = $toc_ofname;
95 $this->TOC_even_footer_name = $toc_efname;
96 $this->TOC_odd_header_value = $toc_ohvalue;
97 $this->TOC_even_header_value = $toc_ehvalue;
98 $this->TOC_odd_footer_value = $toc_ofvalue;
99 $this->TOC_even_footer_value = $toc_efvalue;
100 $this->TOC_page_selector = $toc_pagesel;
101 $this->TOCsheetsize = $toc_sheetsize;
102 }
103}
104
105// Initiate, and Mark a place for the Table of Contents to be inserted
106function TOC($tocfont='', $tocfontsize=0, $tocindent=0, $resetpagenum='', $pagenumstyle='', $suppress='', $toc_orientation='', $TOCusePaging=true, $TOCuseLinking=false, $toc_id=0, $tocoutdent='') { // mPDF 5.6.19) {
107 if (strtoupper($toc_id)=='ALL') { $toc_id = '_mpdf_all'; }
108 else if (!$toc_id) { $toc_id = 0; }
109 else { $toc_id = strtolower($toc_id); }
110 // To use odd and even pages
111 // Cannot start table of contents on an even page
112 if (($this->mpdf->mirrorMargins) && (($this->mpdf->page)%2==0)) { // EVEN
113 if ($this->mpdf->ColActive) {
114 if (count($this->mpdf->columnbuffer)) { $this->mpdf->printcolumnbuffer(); }
115 }
116 $this->mpdf->AddPage($this->mpdf->CurOrientation,'',$resetpagenum, $pagenumstyle, $suppress);
117 }
118 else {
119 $this->mpdf->PageNumSubstitutions[] = array('from'=>$this->mpdf->page, 'reset'=> $resetpagenum, 'type'=>$pagenumstyle, 'suppress'=>$suppress);
120 }
121 if ($toc_id) {
122 $this->m_TOC[$toc_id]['TOCmark'] = $this->mpdf->page;
123 $this->m_TOC[$toc_id]['TOCoutdent'] = $tocoutdent;
124 $this->m_TOC[$toc_id]['TOCorientation'] = $toc_orientation;
125 $this->m_TOC[$toc_id]['TOCuseLinking'] = $TOCuseLinking;
126 $this->m_TOC[$toc_id]['TOCusePaging'] = $TOCusePaging;
127 }
128 else {
129 $this->TOCmark = $this->mpdf->page;
130 $this->TOCoutdent = $tocoutdent;
131 $this->TOCorientation = $toc_orientation;
132 $this->TOCuseLinking = $TOCuseLinking;
133 $this->TOCusePaging = $TOCusePaging;
134 }
135}
136
137
138function insertTOC() {
139 $notocs = 0;
140 if ($this->TOCmark) { $notocs = 1; }
141 $notocs += count($this->m_TOC);
142
143 if ($notocs==0) { return; }
144
145 if (count($this->m_TOC)) { reset($this->m_TOC); }
146 $added_toc_pages = 0;
147
148 if ($this->mpdf->ColActive) { $this->mpdf->SetColumns(0); }
149 if (($this->mpdf->mirrorMargins) && (($this->mpdf->page)%2==1)) { // ODD
150 $this->mpdf->AddPage($this->mpdf->CurOrientation);
151 $extrapage = true;
152 }
153 else { $extrapage = false; }
154
155 for ($toci = 0; $toci<$notocs; $toci++) {
156 if ($toci==0 && $this->TOCmark) {
157 $toc_id = 0;
158 $toc_page = $this->TOCmark;
159 $tocoutdent = $this->TOCoutdent;
160 $toc_orientation = $this->TOCorientation;
161 $TOCuseLinking = $this->TOCuseLinking;
162 $TOCusePaging = $this->TOCusePaging;
163 $toc_preHTML = $this->TOCpreHTML;
164 $toc_postHTML = $this->TOCpostHTML;
165 $toc_bookmarkText = $this->TOCbookmarkText;
166 $toc_mgl = $this->TOC_margin_left;
167 $toc_mgr = $this->TOC_margin_right;
168 $toc_mgt = $this->TOC_margin_top;
169 $toc_mgb = $this->TOC_margin_bottom;
170 $toc_mgh = $this->TOC_margin_header;
171 $toc_mgf = $this->TOC_margin_footer;
172 $toc_ohname = $this->TOC_odd_header_name;
173 $toc_ehname = $this->TOC_even_header_name;
174 $toc_ofname = $this->TOC_odd_footer_name;
175 $toc_efname = $this->TOC_even_footer_name;
176 $toc_ohvalue = $this->TOC_odd_header_value;
177 $toc_ehvalue = $this->TOC_even_header_value;
178 $toc_ofvalue = $this->TOC_odd_footer_value;
179 $toc_efvalue = $this->TOC_even_footer_value;
180 $toc_page_selector = $this->TOC_page_selector;
181 $toc_sheet_size = $this->TOCsheetsize;
182 }
183 else {
184 $arr = current($this->m_TOC);
185
186 $toc_id = key($this->m_TOC);
187 $toc_page = $this->m_TOC[$toc_id]['TOCmark'];
188 $tocoutdent = $this->m_TOC[$toc_id]['TOCoutdent'];
189 $toc_orientation = $this->m_TOC[$toc_id]['TOCorientation'];
190 $TOCuseLinking = $this->m_TOC[$toc_id]['TOCuseLinking'];
191 $TOCusePaging = $this->m_TOC[$toc_id]['TOCusePaging'];
192 if (isset($this->m_TOC[$toc_id]['TOCpreHTML'])) { $toc_preHTML = $this->m_TOC[$toc_id]['TOCpreHTML']; }
193 else { $toc_preHTML = ''; }
194 if (isset($this->m_TOC[$toc_id]['TOCpostHTML'])) { $toc_postHTML = $this->m_TOC[$toc_id]['TOCpostHTML']; }
195 else { $toc_postHTML = ''; }
196 if (isset($this->m_TOC[$toc_id]['TOCbookmarkText'])) { $toc_bookmarkText = $this->m_TOC[$toc_id]['TOCbookmarkText']; }
197 else { $toc_bookmarkText = ''; } // *BOOKMARKS*
198 $toc_mgl = $this->m_TOC[$toc_id]['TOC_margin_left'];
199 $toc_mgr = $this->m_TOC[$toc_id]['TOC_margin_right'];
200 $toc_mgt = $this->m_TOC[$toc_id]['TOC_margin_top'];
201 $toc_mgb = $this->m_TOC[$toc_id]['TOC_margin_bottom'];
202 $toc_mgh = $this->m_TOC[$toc_id]['TOC_margin_header'];
203 $toc_mgf = $this->m_TOC[$toc_id]['TOC_margin_footer'];
204 $toc_ohname = $this->m_TOC[$toc_id]['TOC_odd_header_name'];
205 $toc_ehname = $this->m_TOC[$toc_id]['TOC_even_header_name'];
206 $toc_ofname = $this->m_TOC[$toc_id]['TOC_odd_footer_name'];
207 $toc_efname = $this->m_TOC[$toc_id]['TOC_even_footer_name'];
208 $toc_ohvalue = $this->m_TOC[$toc_id]['TOC_odd_header_value'];
209 $toc_ehvalue = $this->m_TOC[$toc_id]['TOC_even_header_value'];
210 $toc_ofvalue = $this->m_TOC[$toc_id]['TOC_odd_footer_value'];
211 $toc_efvalue = $this->m_TOC[$toc_id]['TOC_even_footer_value'];
212 $toc_page_selector = $this->m_TOC[$toc_id]['TOC_page_selector'];
213 $toc_sheet_size = $this->m_TOC[$toc_id]['TOCsheetsize'];
214 next($this->m_TOC);
215 }
216
217 // mPDF 5.6.31
218
219 if (!$toc_orientation) { $toc_orientation= $this->mpdf->DefOrientation; }
220 $this->mpdf->AddPage($toc_orientation, '', '', '', "on", $toc_mgl, $toc_mgr, $toc_mgt, $toc_mgb, $toc_mgh, $toc_mgf, $toc_ohname, $toc_ehname, $toc_ofname, $toc_efname, $toc_ohvalue, $toc_ehvalue, $toc_ofvalue, $toc_efvalue, $toc_page_selector, $toc_sheet_size );
221
222 $this->mpdf->writingToC = true; // mPDF 5.6.38
223 // mPDF 5.6.31
224 $tocstart=count($this->mpdf->pages);
225 if ($toc_preHTML) { $this->mpdf->WriteHTML($toc_preHTML); }
226
227
228 // mPDF 5.6.19
229 $html ='<div class="mpdf_toc" id="mpdf_toc_'.$toc_id.'">';
230 foreach($this->_toc as $t) {
231 if ($t['toc_id']==='_mpdf_all' || $t['toc_id']===$toc_id ) {
232 $html .= '<div class="mpdf_toc_level_'.$t['l'].'">';
233 if ($TOCuseLinking) { $html .= '<a class="mpdf_toc_a" href="#__mpdfinternallink_'.$t['link'].'">'; }
234 $html .= '<span class="mpdf_toc_t_level_'.$t['l'].'">'.$t['t'].'</span>';
235 if ($TOCuseLinking) { $html .= '</a>'; }
236 if (!$tocoutdent) { $tocoutdent = '0'; }
237 if ($TOCusePaging) { $html .= ' <dottab outdent="'.$tocoutdent.'" /> ';
238 if ($TOCuseLinking) { $html .= '<a class="mpdf_toc_a" href="#__mpdfinternallink_'.$t['link'].'">'; }
239 $html .= '<span class="mpdf_toc_p_level_'.$t['l'].'">'.$this->mpdf->docPageNum($t['p']).'</span>';
240 if ($TOCuseLinking) { $html .= '</a>'; }
241 }
242 $html .= '</div>';
243 }
244 }
245 $html .= '</div>';
246 $this->mpdf->WriteHTML($html);
247
248 if ($toc_postHTML) { $this->mpdf->WriteHTML($toc_postHTML); }
249 $this->mpdf->writingToC = false; // mPDF 5.6.38
250 $this->mpdf->AddPage($toc_orientation,'E');
251
252 $n_toc = $this->mpdf->page - $tocstart + 1;
253
254 if ($toci==0 && $this->TOCmark) {
255 $TOC_start = $tocstart ;
256 $TOC_end = $this->mpdf->page;
257 $TOC_npages = $n_toc;
258 }
259 else {
260 $this->m_TOC[$toc_id]['start'] = $tocstart ;
261 $this->m_TOC[$toc_id]['end'] = $this->mpdf->page;
262 $this->m_TOC[$toc_id]['npages'] = $n_toc;
263 }
264 }
265
266 $s = '';
267
268 $s .= $this->mpdf->PrintBodyBackgrounds();
269
270 $s .= $this->mpdf->PrintPageBackgrounds();
271 $this->mpdf->pages[$this->mpdf->page] = preg_replace('/(___BACKGROUND___PATTERNS'.date('jY').')/', "\n".$s."\n".'\\1', $this->mpdf->pages[$this->mpdf->page]);
272 $this->mpdf->pageBackgrounds = array();
273
274 //Page footer
275 $this->mpdf->InFooter=true;
276 $this->mpdf->Footer();
277 $this->mpdf->InFooter=false;
278
279 // 2nd time through to move pages etc.
280 $added_toc_pages = 0;
281 if (count($this->m_TOC)) { reset($this->m_TOC); }
282
283 for ($toci = 0; $toci<$notocs; $toci++) {
284 if ($toci==0 && $this->TOCmark) {
285 $toc_id = 0;
286 $toc_page = $this->TOCmark + $added_toc_pages;
287 $toc_orientation = $this->TOCorientation;
288 $TOCuseLinking = $this->TOCuseLinking;
289 $TOCusePaging = $this->TOCusePaging;
290 $toc_bookmarkText = $this->TOCbookmarkText; // *BOOKMARKS*
291
292 $tocstart = $TOC_start ;
293 $tocend = $n = $TOC_end;
294 $n_toc = $TOC_npages;
295 }
296 else {
297 $arr = current($this->m_TOC);
298
299 $toc_id = key($this->m_TOC);
300 $toc_page = $this->m_TOC[$toc_id]['TOCmark'] + $added_toc_pages;
301 $toc_orientation = $this->m_TOC[$toc_id]['TOCorientation'];
302 $TOCuseLinking = $this->m_TOC[$toc_id]['TOCuseLinking'];
303 $TOCusePaging = $this->m_TOC[$toc_id]['TOCusePaging'];
304 $toc_bookmarkText = $this->m_TOC[$toc_id]['TOCbookmarkText']; // *BOOKMARKS*
305
306 $tocstart = $this->m_TOC[$toc_id]['start'] ;
307 $tocend = $n = $this->m_TOC[$toc_id]['end'] ;
308 $n_toc = $this->m_TOC[$toc_id]['npages'] ;
309
310 next($this->m_TOC);
311 }
312
313 // Now pages moved
314 $added_toc_pages += $n_toc;
315
316 $this->mpdf->MovePages($toc_page, $tocstart, $tocend) ;
317 $this->mpdf->pgsIns[$toc_page] = $tocend - $tocstart + 1;
318
319/*-- BOOKMARKS --*/
320 // Insert new Bookmark for Bookmark
321 if ($toc_bookmarkText) {
322 $insert = -1;
323 foreach($this->mpdf->BMoutlines as $i=>$o) {
324 if($o['p']<$toc_page) { // i.e. before point of insertion
325 $insert = $i;
326 }
327 }
328 $txt = $this->mpdf->purify_utf8_text($toc_bookmarkText);
329 if ($this->mpdf->text_input_as_HTML) {
330 $txt = $this->mpdf->all_entities_to_utf8($txt);
331 }
332 $newBookmark[0] = array('t'=>$txt,'l'=>0,'y'=>0,'p'=>$toc_page );
333 array_splice($this->mpdf->BMoutlines,($insert+1),0,$newBookmark);
334 }
335/*-- END BOOKMARKS --*/
336
337 }
338
339 // Delete empty page that was inserted earlier
340 if ($extrapage) {
341 unset($this->mpdf->pages[count($this->mpdf->pages)]);
342 $this->mpdf->page--; // Reset page pointer
343 }
344
345
346}
347
348
349function openTagTOC($attr) {
350 if (isset($attr['OUTDENT']) && $attr['OUTDENT']) { $tocoutdent = $attr['OUTDENT']; } else { $tocoutdent = ''; } // mPDF 5.6.19
351 if (isset($attr['RESETPAGENUM']) && $attr['RESETPAGENUM']) { $resetpagenum = $attr['RESETPAGENUM']; } else { $resetpagenum = ''; }
352 if (isset($attr['PAGENUMSTYLE']) && $attr['PAGENUMSTYLE']) { $pagenumstyle = $attr['PAGENUMSTYLE']; } else { $pagenumstyle= ''; }
353 if (isset($attr['SUPPRESS']) && $attr['SUPPRESS']) { $suppress = $attr['SUPPRESS']; } else { $suppress = ''; }
354 if (isset($attr['TOC-ORIENTATION']) && $attr['TOC-ORIENTATION']) { $toc_orientation = $attr['TOC-ORIENTATION']; } else { $toc_orientation = ''; }
355 if (isset($attr['PAGING']) && (strtoupper($attr['PAGING'])=='OFF' || $attr['PAGING']==='0')) { $paging = false; }
356 else { $paging = true; }
357 if (isset($attr['LINKS']) && (strtoupper($attr['LINKS'])=='ON' || $attr['LINKS']==1)) { $links = true; }
358 else { $links = false; }
359 if (isset($attr['NAME']) && $attr['NAME']) { $toc_id = strtolower($attr['NAME']); } else { $toc_id = 0; }
360 $this->TOC('',0,0,$resetpagenum, $pagenumstyle, $suppress, $toc_orientation, $paging, $links, $toc_id, $tocoutdent); // mPDF 5.6.19 5.6.31
361}
362
363
364function openTagTOCPAGEBREAK($attr) {
365 if (isset($attr['NAME']) && $attr['NAME']) { $toc_id = strtolower($attr['NAME']); } else { $toc_id = 0; }
366 if ($toc_id) {
367 if (isset($attr['OUTDENT']) && $attr['OUTDENT']) { $this->m_TOC[$toc_id]['TOCoutdent'] = $attr['OUTDENT']; } else { $this->m_TOC[$toc_id]['TOCoutdent'] = ''; } // mPDF 5.6.19
368 if (isset($attr['TOC-ORIENTATION']) && $attr['TOC-ORIENTATION']) { $this->m_TOC[$toc_id]['TOCorientation'] = $attr['TOC-ORIENTATION']; } else { $this->m_TOC[$toc_id]['TOCorientation'] = ''; }
369 if (isset($attr['PAGING']) && (strtoupper($attr['PAGING'])=='OFF' || $attr['PAGING']==='0')) { $this->m_TOC[$toc_id]['TOCusePaging'] = false; }
370 else { $this->m_TOC[$toc_id]['TOCusePaging'] = true; }
371 if (isset($attr['LINKS']) && (strtoupper($attr['LINKS'])=='ON' || $attr['LINKS']==1)) { $this->m_TOC[$toc_id]['TOCuseLinking'] = true; }
372 else { $this->m_TOC[$toc_id]['TOCuseLinking'] = false; }
373
374 $this->m_TOC[$toc_id]['TOC_margin_left'] = $this->m_TOC[$toc_id]['TOC_margin_right'] = $this->m_TOC[$toc_id]['TOC_margin_top'] = $this->m_TOC[$toc_id]['TOC_margin_bottom'] = $this->m_TOC[$toc_id]['TOC_margin_header'] = $this->m_TOC[$toc_id]['TOC_margin_footer'] = '';
375 if (isset($attr['TOC-MARGIN-RIGHT'])) { $this->m_TOC[$toc_id]['TOC_margin_right'] = $this->mpdf->ConvertSize($attr['TOC-MARGIN-RIGHT'],$this->mpdf->w,$this->mpdf->FontSize,false); }
376 if (isset($attr['TOC-MARGIN-LEFT'])) { $this->m_TOC[$toc_id]['TOC_margin_left'] = $this->mpdf->ConvertSize($attr['TOC-MARGIN-LEFT'],$this->mpdf->w,$this->mpdf->FontSize,false); }
377 if (isset($attr['TOC-MARGIN-TOP'])) { $this->m_TOC[$toc_id]['TOC_margin_top'] = $this->mpdf->ConvertSize($attr['TOC-MARGIN-TOP'],$this->mpdf->w,$this->mpdf->FontSize,false); }
378 if (isset($attr['TOC-MARGIN-BOTTOM'])) { $this->m_TOC[$toc_id]['TOC_margin_bottom'] = $this->mpdf->ConvertSize($attr['TOC-MARGIN-BOTTOM'],$this->mpdf->w,$this->mpdf->FontSize,false); }
379 if (isset($attr['TOC-MARGIN-HEADER'])) { $this->m_TOC[$toc_id]['TOC_margin_header'] = $this->mpdf->ConvertSize($attr['TOC-MARGIN-HEADER'],$this->mpdf->w,$this->mpdf->FontSize,false); }
380 if (isset($attr['TOC-MARGIN-FOOTER'])) { $this->m_TOC[$toc_id]['TOC_margin_footer'] = $this->mpdf->ConvertSize($attr['TOC-MARGIN-FOOTER'],$this->mpdf->w,$this->mpdf->FontSize,false); }
381 $this->m_TOC[$toc_id]['TOC_odd_header_name'] = $this->m_TOC[$toc_id]['TOC_even_header_name'] = $this->m_TOC[$toc_id]['TOC_odd_footer_name'] = $this->m_TOC[$toc_id]['TOC_even_footer_name'] = '';
382 if (isset($attr['TOC-ODD-HEADER-NAME']) && $attr['TOC-ODD-HEADER-NAME']) { $this->m_TOC[$toc_id]['TOC_odd_header_name'] = $attr['TOC-ODD-HEADER-NAME']; }
383 if (isset($attr['TOC-EVEN-HEADER-NAME']) && $attr['TOC-EVEN-HEADER-NAME']) { $this->m_TOC[$toc_id]['TOC_even_header_name'] = $attr['TOC-EVEN-HEADER-NAME']; }
384 if (isset($attr['TOC-ODD-FOOTER-NAME']) && $attr['TOC-ODD-FOOTER-NAME']) { $this->m_TOC[$toc_id]['TOC_odd_footer_name'] = $attr['TOC-ODD-FOOTER-NAME']; }
385 if (isset($attr['TOC-EVEN-FOOTER-NAME']) && $attr['TOC-EVEN-FOOTER-NAME']) { $this->m_TOC[$toc_id]['TOC_even_footer_name'] = $attr['TOC-EVEN-FOOTER-NAME']; }
386 $this->m_TOC[$toc_id]['TOC_odd_header_value'] = $this->m_TOC[$toc_id]['TOC_even_header_value'] = $this->m_TOC[$toc_id]['TOC_odd_footer_value'] = $this->m_TOC[$toc_id]['TOC_even_footer_value'] = 0;
387 if (isset($attr['TOC-ODD-HEADER-VALUE']) && ($attr['TOC-ODD-HEADER-VALUE']=='1' || strtoupper($attr['TOC-ODD-HEADER-VALUE'])=='ON')) { $this->m_TOC[$toc_id]['TOC_odd_header_value'] = 1; }
388 else if (isset($attr['TOC-ODD-HEADER-VALUE']) && ($attr['TOC-ODD-HEADER-VALUE']=='-1' || strtoupper($attr['TOC-ODD-HEADER-VALUE'])=='OFF')) { $this->m_TOC[$toc_id]['TOC_odd_header_value'] = -1; }
389 if (isset($attr['TOC-EVEN-HEADER-VALUE']) && ($attr['TOC-EVEN-HEADER-VALUE']=='1' || strtoupper($attr['TOC-EVEN-HEADER-VALUE'])=='ON')) { $this->m_TOC[$toc_id]['TOC_even_header_value'] = 1; }
390 else if (isset($attr['TOC-EVEN-HEADER-VALUE']) && ($attr['TOC-EVEN-HEADER-VALUE']=='-1' || strtoupper($attr['TOC-EVEN-HEADER-VALUE'])=='OFF')) { $this->m_TOC[$toc_id]['TOC_even_header_value'] = -1; }
391 if (isset($attr['TOC-ODD-FOOTER-VALUE']) && ($attr['TOC-ODD-FOOTER-VALUE']=='1' || strtoupper($attr['TOC-ODD-FOOTER-VALUE'])=='ON')) { $this->m_TOC[$toc_id]['TOC_odd_footer_value'] = 1; }
392 else if (isset($attr['TOC-ODD-FOOTER-VALUE']) && ($attr['TOC-ODD-FOOTER-VALUE']=='-1' || strtoupper($attr['TOC-ODD-FOOTER-VALUE'])=='OFF')) { $this->m_TOC[$toc_id]['TOC_odd_footer_value'] = -1; }
393 if (isset($attr['TOC-EVEN-FOOTER-VALUE']) && ($attr['TOC-EVEN-FOOTER-VALUE']=='1' || strtoupper($attr['TOC-EVEN-FOOTER-VALUE'])=='ON')) { $this->m_TOC[$toc_id]['TOC_even_footer_value'] = 1; }
394 else if (isset($attr['TOC-EVEN-FOOTER-VALUE']) && ($attr['TOC-EVEN-FOOTER-VALUE']=='-1' || strtoupper($attr['TOC-EVEN-FOOTER-VALUE'])=='OFF')) { $this->m_TOC[$toc_id]['TOC_even_footer_value'] = -1; }
395 if (isset($attr['TOC-PAGE-SELECTOR']) && $attr['TOC-PAGE-SELECTOR']) { $this->m_TOC[$toc_id]['TOC_page_selector'] = $attr['TOC-PAGE-SELECTOR']; }
396 else { $this->m_TOC[$toc_id]['TOC_page_selector'] = ''; }
397 if (isset($attr['TOC-SHEET-SIZE']) && $attr['TOC-SHEET-SIZE']) { $this->m_TOC[$toc_id]['TOCsheetsize'] = $attr['TOC-SHEET-SIZE']; } else { $this->m_TOC[$toc_id]['TOCsheetsize'] = ''; }
398
399
400 if (isset($attr['TOC-PREHTML']) && $attr['TOC-PREHTML']) { $this->m_TOC[$toc_id]['TOCpreHTML'] = htmlspecialchars_decode($attr['TOC-PREHTML'],ENT_QUOTES); }
401 if (isset($attr['TOC-POSTHTML']) && $attr['TOC-POSTHTML']) { $this->m_TOC[$toc_id]['TOCpostHTML'] = htmlspecialchars_decode($attr['TOC-POSTHTML'],ENT_QUOTES); }
402
403 if (isset($attr['TOC-BOOKMARKTEXT']) && $attr['TOC-BOOKMARKTEXT']) { $this->m_TOC[$toc_id]['TOCbookmarkText'] = htmlspecialchars_decode($attr['TOC-BOOKMARKTEXT'],ENT_QUOTES); } // *BOOKMARKS*
404 }
405 else {
406 if (isset($attr['OUTDENT']) && $attr['OUTDENT']) { $this->TOCoutdent = $attr['OUTDENT']; } else { $this->TOCoutdent = ''; } // mPDF 5.6.19
407 if (isset($attr['TOC-ORIENTATION']) && $attr['TOC-ORIENTATION']) { $this->TOCorientation = $attr['TOC-ORIENTATION']; } else { $this->TOCorientation = ''; }
408 if (isset($attr['PAGING']) && (strtoupper($attr['PAGING'])=='OFF' || $attr['PAGING']==='0')) { $this->TOCusePaging = false; }
409 else { $this->TOCusePaging = true; }
410 if (isset($attr['LINKS']) && (strtoupper($attr['LINKS'])=='ON' || $attr['LINKS']==1)) { $this->TOCuseLinking = true; }
411 else { $this->TOCuseLinking = false; }
412
413 $this->TOC_margin_left = $this->TOC_margin_right = $this->TOC_margin_top = $this->TOC_margin_bottom = $this->TOC_margin_header = $this->TOC_margin_footer = '';
414 if (isset($attr['TOC-MARGIN-RIGHT'])) { $this->TOC_margin_right = $this->mpdf->ConvertSize($attr['TOC-MARGIN-RIGHT'],$this->mpdf->w,$this->mpdf->FontSize,false); }
415 if (isset($attr['TOC-MARGIN-LEFT'])) { $this->TOC_margin_left = $this->mpdf->ConvertSize($attr['TOC-MARGIN-LEFT'],$this->mpdf->w,$this->mpdf->FontSize,false); }
416 if (isset($attr['TOC-MARGIN-TOP'])) { $this->TOC_margin_top = $this->mpdf->ConvertSize($attr['TOC-MARGIN-TOP'],$this->mpdf->w,$this->mpdf->FontSize,false); }
417 if (isset($attr['TOC-MARGIN-BOTTOM'])) { $this->TOC_margin_bottom = $this->mpdf->ConvertSize($attr['TOC-MARGIN-BOTTOM'],$this->mpdf->w,$this->mpdf->FontSize,false); }
418 if (isset($attr['TOC-MARGIN-HEADER'])) { $this->TOC_margin_header = $this->mpdf->ConvertSize($attr['TOC-MARGIN-HEADER'],$this->mpdf->w,$this->mpdf->FontSize,false); }
419 if (isset($attr['TOC-MARGIN-FOOTER'])) { $this->TOC_margin_footer = $this->mpdf->ConvertSize($attr['TOC-MARGIN-FOOTER'],$this->mpdf->w,$this->mpdf->FontSize,false); }
420 $this->TOC_odd_header_name = $this->TOC_even_header_name = $this->TOC_odd_footer_name = $this->TOC_even_footer_name = '';
421 if (isset($attr['TOC-ODD-HEADER-NAME']) && $attr['TOC-ODD-HEADER-NAME']) { $this->TOC_odd_header_name = $attr['TOC-ODD-HEADER-NAME']; }
422 if (isset($attr['TOC-EVEN-HEADER-NAME']) && $attr['TOC-EVEN-HEADER-NAME']) { $this->TOC_even_header_name = $attr['TOC-EVEN-HEADER-NAME']; }
423 if (isset($attr['TOC-ODD-FOOTER-NAME']) && $attr['TOC-ODD-FOOTER-NAME']) { $this->TOC_odd_footer_name = $attr['TOC-ODD-FOOTER-NAME']; }
424 if (isset($attr['TOC-EVEN-FOOTER-NAME']) && $attr['TOC-EVEN-FOOTER-NAME']) { $this->TOC_even_footer_name = $attr['TOC-EVEN-FOOTER-NAME']; }
425 $this->TOC_odd_header_value = $this->TOC_even_header_value = $this->TOC_odd_footer_value = $this->TOC_even_footer_value = 0;
426 if (isset($attr['TOC-ODD-HEADER-VALUE']) && ($attr['TOC-ODD-HEADER-VALUE']=='1' || strtoupper($attr['TOC-ODD-HEADER-VALUE'])=='ON')) { $this->TOC_odd_header_value = 1; }
427 else if (isset($attr['TOC-ODD-HEADER-VALUE']) && ($attr['TOC-ODD-HEADER-VALUE']=='-1' || strtoupper($attr['TOC-ODD-HEADER-VALUE'])=='OFF')) { $this->TOC_odd_header_value = -1; }
428 if (isset($attr['TOC-EVEN-HEADER-VALUE']) && ($attr['TOC-EVEN-HEADER-VALUE']=='1' || strtoupper($attr['TOC-EVEN-HEADER-VALUE'])=='ON')) { $this->TOC_even_header_value = 1; }
429 else if (isset($attr['TOC-EVEN-HEADER-VALUE']) && ($attr['TOC-EVEN-HEADER-VALUE']=='-1' || strtoupper($attr['TOC-EVEN-HEADER-VALUE'])=='OFF')) { $this->TOC_even_header_value = -1; }
430
431 if (isset($attr['TOC-ODD-FOOTER-VALUE']) && ($attr['TOC-ODD-FOOTER-VALUE']=='1' || strtoupper($attr['TOC-ODD-FOOTER-VALUE'])=='ON')) { $this->TOC_odd_footer_value = 1; }
432 else if (isset($attr['TOC-ODD-FOOTER-VALUE']) && ($attr['TOC-ODD-FOOTER-VALUE']=='-1' || strtoupper($attr['TOC-ODD-FOOTER-VALUE'])=='OFF')) { $this->TOC_odd_footer_value = -1; }
433 if (isset($attr['TOC-EVEN-FOOTER-VALUE']) && ($attr['TOC-EVEN-FOOTER-VALUE']=='1' || strtoupper($attr['TOC-EVEN-FOOTER-VALUE'])=='ON')) { $this->TOC_even_footer_value = 1; }
434 else if (isset($attr['TOC-EVEN-FOOTER-VALUE']) && ($attr['TOC-EVEN-FOOTER-VALUE']=='-1' || strtoupper($attr['TOC-EVEN-FOOTER-VALUE'])=='OFF')) { $this->TOC_even_footer_value = -1; }
435 if (isset($attr['TOC-PAGE-SELECTOR']) && $attr['TOC-PAGE-SELECTOR']) { $this->TOC_page_selector = $attr['TOC-PAGE-SELECTOR']; }
436 else { $this->TOC_page_selector = ''; }
437 if (isset($attr['TOC-SHEET-SIZE']) && $attr['TOC-SHEET-SIZE']) { $this->TOCsheetsize = $attr['TOC-SHEET-SIZE']; } else { $this->TOCsheetsize = ''; }
438
439 if (isset($attr['TOC-PREHTML']) && $attr['TOC-PREHTML']) { $this->TOCpreHTML = htmlspecialchars_decode($attr['TOC-PREHTML'],ENT_QUOTES); }
440 if (isset($attr['TOC-POSTHTML']) && $attr['TOC-POSTHTML']) { $this->TOCpostHTML = htmlspecialchars_decode($attr['TOC-POSTHTML'],ENT_QUOTES); }
441 if (isset($attr['TOC-BOOKMARKTEXT']) && $attr['TOC-BOOKMARKTEXT']) { $this->TOCbookmarkText = htmlspecialchars_decode($attr['TOC-BOOKMARKTEXT'],ENT_QUOTES); }
442 }
443
444 if ($this->mpdf->y == $this->mpdf->tMargin && (!$this->mpdf->mirrorMargins ||($this->mpdf->mirrorMargins && $this->mpdf->page % 2==1))) {
445 if ($toc_id) { $this->m_TOC[$toc_id]['TOCmark'] = $this->mpdf->page; }
446 else { $this->TOCmark = $this->mpdf->page; }
447 // Don't add a page
448 if ($this->mpdf->page==1 && count($this->mpdf->PageNumSubstitutions)==0) {
449 $resetpagenum = '';
450 $pagenumstyle = '';
451 $suppress = '';
452 if (isset($attr['RESETPAGENUM'])) { $resetpagenum = $attr['RESETPAGENUM']; }
453 if (isset($attr['PAGENUMSTYLE'])) { $pagenumstyle = $attr['PAGENUMSTYLE']; }
454 if (isset($attr['SUPPRESS'])) { $suppress = $attr['SUPPRESS']; }
455 if (!$suppress) { $suppress = 'off'; }
456 if (!$resetpagenum) { $resetpagenum= 1; }
457 $this->mpdf->PageNumSubstitutions[] = array('from'=>1, 'reset'=> $resetpagenum, 'type'=>$pagenumstyle, 'suppress'=> $suppress);
458 }
459 return array(true, $toc_id);
460 }
461 // No break - continues as PAGEBREAK...
462 return array(false, $toc_id);
463}
464
465
466}
467
468?> \ No newline at end of file
diff --git a/inc/3rdparty/libraries/mpdf/classes/ttfontsuni.php b/inc/3rdparty/libraries/mpdf/classes/ttfontsuni.php
new file mode 100644
index 00000000..1c93b600
--- /dev/null
+++ b/inc/3rdparty/libraries/mpdf/classes/ttfontsuni.php
@@ -0,0 +1,2065 @@
1<?php
2
3/*******************************************************************************
4* TTFontFile class *
5* *
6* Version: 2.01 *
7* Date: 2012-02-25 *
8* Author: Ian Back <ianb@bpm1.com> *
9* License: LGPL *
10* Copyright (c) Ian Back, 2010 *
11* This class is based on The ReportLab Open Source PDF library *
12* written in Python - http://www.reportlab.com/software/opensource/ *
13* together with ideas from the OpenOffice source code and others. *
14* This header must be retained in any redistribution or *
15* modification of the file. *
16* *
17*******************************************************************************/
18
19// Define the value used in the "head" table of a created TTF file
20// 0x74727565 "true" for Mac
21// 0x00010000 for Windows
22// Either seems to work for a font embedded in a PDF file
23// when read by Adobe Reader on a Windows PC(!)
24if (!defined('_TTF_MAC_HEADER')) define("_TTF_MAC_HEADER", false);
25
26// Recalculate correct metadata/profiles when making subset fonts (not SIP/SMP)
27// e.g. xMin, xMax, maxNContours
28if (!defined('_RECALC_PROFILE')) define("_RECALC_PROFILE", false);
29
30// TrueType Font Glyph operators
31define("GF_WORDS",(1 << 0));
32define("GF_SCALE",(1 << 3));
33define("GF_MORE",(1 << 5));
34define("GF_XYSCALE",(1 << 6));
35define("GF_TWOBYTWO",(1 << 7));
36
37
38
39class TTFontFile {
40
41var $unAGlyphs; // mPDF 5.4.05
42var $panose;
43var $maxUni;
44var $sFamilyClass;
45var $sFamilySubClass;
46var $sipset;
47var $smpset;
48var $_pos;
49var $numTables;
50var $searchRange;
51var $entrySelector;
52var $rangeShift;
53var $tables;
54var $otables;
55var $filename;
56var $fh;
57var $glyphPos;
58var $charToGlyph;
59var $ascent;
60var $descent;
61var $name;
62var $familyName;
63var $styleName;
64var $fullName;
65var $uniqueFontID;
66var $unitsPerEm;
67var $bbox;
68var $capHeight;
69var $stemV;
70var $italicAngle;
71var $flags;
72var $underlinePosition;
73var $underlineThickness;
74var $charWidths;
75var $defaultWidth;
76var $maxStrLenRead;
77var $numTTCFonts;
78var $TTCFonts;
79var $maxUniChar;
80var $kerninfo;
81
82 function TTFontFile() {
83 $this->maxStrLenRead = 200000; // Maximum size of glyf table to read in as string (otherwise reads each glyph from file)
84 }
85
86
87 function getMetrics($file, $TTCfontID=0, $debug=false, $BMPonly=false, $kerninfo=false, $unAGlyphs=false) { // mPDF 5.4.05
88 $this->unAGlyphs = $unAGlyphs; // mPDF 5.4.05
89 $this->filename = $file;
90 $this->fh = fopen($file,'rb') or die('Can\'t open file ' . $file);
91 $this->_pos = 0;
92 $this->charWidths = '';
93 $this->glyphPos = array();
94 $this->charToGlyph = array();
95 $this->tables = array();
96 $this->otables = array();
97 $this->kerninfo = array();
98 $this->ascent = 0;
99 $this->descent = 0;
100 $this->numTTCFonts = 0;
101 $this->TTCFonts = array();
102 $this->version = $version = $this->read_ulong();
103 $this->panose = array();
104 if ($version==0x4F54544F)
105 die("Postscript outlines are not supported");
106 if ($version==0x74746366 && !$TTCfontID)
107 die("ERROR - You must define the TTCfontID for a TrueType Collection in config_fonts.php (". $file.")");
108 if (!in_array($version, array(0x00010000,0x74727565)) && !$TTCfontID)
109 die("Not a TrueType font: version=".$version);
110 if ($TTCfontID > 0) {
111 $this->version = $version = $this->read_ulong(); // TTC Header version now
112 if (!in_array($version, array(0x00010000,0x00020000)))
113 die("ERROR - Error parsing TrueType Collection: version=".$version." - " . $file);
114 $this->numTTCFonts = $this->read_ulong();
115 for ($i=1; $i<=$this->numTTCFonts; $i++) {
116 $this->TTCFonts[$i]['offset'] = $this->read_ulong();
117 }
118 $this->seek($this->TTCFonts[$TTCfontID]['offset']);
119 $this->version = $version = $this->read_ulong(); // TTFont version again now
120 }
121 $this->readTableDirectory($debug);
122 $this->extractInfo($debug, $BMPonly, $kerninfo);
123 fclose($this->fh);
124 }
125
126
127 function readTableDirectory($debug=false) {
128 $this->numTables = $this->read_ushort();
129 $this->searchRange = $this->read_ushort();
130 $this->entrySelector = $this->read_ushort();
131 $this->rangeShift = $this->read_ushort();
132 $this->tables = array();
133 for ($i=0;$i<$this->numTables;$i++) {
134 $record = array();
135 $record['tag'] = $this->read_tag();
136 $record['checksum'] = array($this->read_ushort(),$this->read_ushort());
137 $record['offset'] = $this->read_ulong();
138 $record['length'] = $this->read_ulong();
139 $this->tables[$record['tag']] = $record;
140 }
141 if ($debug) $this->checksumTables();
142 }
143
144 function checksumTables() {
145 // Check the checksums for all tables
146 foreach($this->tables AS $t) {
147 if ($t['length'] > 0 && $t['length'] < $this->maxStrLenRead) { // 1.02
148 $table = $this->get_chunk($t['offset'], $t['length']);
149 $checksum = $this->calcChecksum($table);
150 if ($t['tag'] == 'head') {
151 $up = unpack('n*', substr($table,8,4));
152 $adjustment[0] = $up[1];
153 $adjustment[1] = $up[2];
154 $checksum = $this->sub32($checksum, $adjustment);
155 }
156 $xchecksum = $t['checksum'];
157 if ($xchecksum != $checksum)
158 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])));
159 }
160 }
161 }
162
163 function sub32($x, $y) {
164 $xlo = $x[1];
165 $xhi = $x[0];
166 $ylo = $y[1];
167 $yhi = $y[0];
168 if ($ylo > $xlo) { $xlo += 1 << 16; $yhi += 1; }
169 $reslo = $xlo-$ylo;
170 if ($yhi > $xhi) { $xhi += 1 << 16; }
171 $reshi = $xhi-$yhi;
172 $reshi = $reshi & 0xFFFF;
173 return array($reshi, $reslo);
174 }
175
176 function calcChecksum($data) {
177 if (strlen($data) % 4) { $data .= str_repeat("\0",(4-(strlen($data) % 4))); }
178 $len = strlen($data);
179 $hi=0x0000;
180 $lo=0x0000;
181 for($i=0;$i<$len;$i+=4) {
182 $hi += (ord($data[$i])<<8) + ord($data[$i+1]);
183 $lo += (ord($data[$i+2])<<8) + ord($data[$i+3]);
184 $hi += ($lo >> 16) & 0xFFFF;
185 $lo = $lo & 0xFFFF;
186 }
187 return array($hi, $lo);
188 }
189
190 function get_table_pos($tag) {
191 $offset = $this->tables[$tag]['offset'];
192 $length = $this->tables[$tag]['length'];
193 return array($offset, $length);
194 }
195
196 function seek($pos) {
197 $this->_pos = $pos;
198 fseek($this->fh,$this->_pos);
199 }
200
201 function skip($delta) {
202 $this->_pos = $this->_pos + $delta;
203 fseek($this->fh,$delta,SEEK_CUR);
204 }
205
206 function seek_table($tag, $offset_in_table = 0) {
207 $tpos = $this->get_table_pos($tag);
208 $this->_pos = $tpos[0] + $offset_in_table;
209 fseek($this->fh, $this->_pos);
210 return $this->_pos;
211 }
212
213 function read_tag() {
214 $this->_pos += 4;
215 return fread($this->fh,4);
216 }
217
218 function read_short() {
219 $this->_pos += 2;
220 $s = fread($this->fh,2);
221 $a = (ord($s[0])<<8) + ord($s[1]);
222 if ($a & (1 << 15) ) {
223 $a = ($a - (1 << 16));
224 }
225 return $a;
226 }
227
228 function unpack_short($s) {
229 $a = (ord($s[0])<<8) + ord($s[1]);
230 if ($a & (1 << 15) ) {
231 $a = ($a - (1 << 16));
232 }
233 return $a;
234 }
235
236 function read_ushort() {
237 $this->_pos += 2;
238 $s = fread($this->fh,2);
239 return (ord($s[0])<<8) + ord($s[1]);
240 }
241
242 function read_ulong() {
243 $this->_pos += 4;
244 $s = fread($this->fh,4);
245 // if large uInt32 as an integer, PHP converts it to -ve
246 return (ord($s[0])*16777216) + (ord($s[1])<<16) + (ord($s[2])<<8) + ord($s[3]); // 16777216 = 1<<24
247 }
248
249 function get_ushort($pos) {
250 fseek($this->fh,$pos);
251 $s = fread($this->fh,2);
252 return (ord($s[0])<<8) + ord($s[1]);
253 }
254
255 function get_ulong($pos) {
256 fseek($this->fh,$pos);
257 $s = fread($this->fh,4);
258 // iF large uInt32 as an integer, PHP converts it to -ve
259 return (ord($s[0])*16777216) + (ord($s[1])<<16) + (ord($s[2])<<8) + ord($s[3]); // 16777216 = 1<<24
260 }
261
262 function pack_short($val) {
263 if ($val<0) {
264 $val = abs($val);
265 $val = ~$val;
266 $val += 1;
267 }
268 return pack("n",$val);
269 }
270
271 function splice($stream, $offset, $value) {
272 return substr($stream,0,$offset) . $value . substr($stream,$offset+strlen($value));
273 }
274
275 function _set_ushort($stream, $offset, $value) {
276 $up = pack("n", $value);
277 return $this->splice($stream, $offset, $up);
278 }
279
280 function _set_short($stream, $offset, $val) {
281 if ($val<0) {
282 $val = abs($val);
283 $val = ~$val;
284 $val += 1;
285 }
286 $up = pack("n",$val);
287 return $this->splice($stream, $offset, $up);
288 }
289
290 function get_chunk($pos, $length) {
291 fseek($this->fh,$pos);
292 if ($length <1) { return ''; }
293 return (fread($this->fh,$length));
294 }
295
296 function get_table($tag) {
297 list($pos, $length) = $this->get_table_pos($tag);
298 if ($length == 0) { return ''; }
299 fseek($this->fh,$pos);
300 return (fread($this->fh,$length));
301 }
302
303 function add($tag, $data) {
304 if ($tag == 'head') {
305 $data = $this->splice($data, 8, "\0\0\0\0");
306 }
307 $this->otables[$tag] = $data;
308 }
309
310
311
312/////////////////////////////////////////////////////////////////////////////////////////
313 function getCTG($file, $TTCfontID=0, $debug=false, $unAGlyphs=false) { // mPDF 5.4.05
314 $this->unAGlyphs = $unAGlyphs; // mPDF 5.4.05
315 $this->filename = $file;
316 $this->fh = fopen($file,'rb') or die('Can\'t open file ' . $file);
317 $this->_pos = 0;
318 $this->charWidths = '';
319 $this->glyphPos = array();
320 $this->charToGlyph = array();
321 $this->tables = array();
322 $this->numTTCFonts = 0;
323 $this->TTCFonts = array();
324 $this->skip(4);
325 if ($TTCfontID > 0) {
326 $this->version = $version = $this->read_ulong(); // TTC Header version now
327 if (!in_array($version, array(0x00010000,0x00020000)))
328 die("ERROR - Error parsing TrueType Collection: version=".$version." - " . $file);
329 $this->numTTCFonts = $this->read_ulong();
330 for ($i=1; $i<=$this->numTTCFonts; $i++) {
331 $this->TTCFonts[$i]['offset'] = $this->read_ulong();
332 }
333 $this->seek($this->TTCFonts[$TTCfontID]['offset']);
334 $this->version = $version = $this->read_ulong(); // TTFont version again now
335 }
336 $this->readTableDirectory($debug);
337
338
339 // cmap - Character to glyph index mapping table
340 $cmap_offset = $this->seek_table("cmap");
341 $this->skip(2);
342 $cmapTableCount = $this->read_ushort();
343 $unicode_cmap_offset = 0;
344 for ($i=0;$i<$cmapTableCount;$i++) {
345 $platformID = $this->read_ushort();
346 $encodingID = $this->read_ushort();
347 $offset = $this->read_ulong();
348 $save_pos = $this->_pos;
349 if ($platformID == 3 && $encodingID == 1) { // Microsoft, Unicode
350 $format = $this->get_ushort($cmap_offset + $offset);
351 if ($format == 4) {
352 $unicode_cmap_offset = $cmap_offset + $offset;
353 break;
354 }
355 }
356 else if ($platformID == 0) { // Unicode -- assume all encodings are compatible
357 $format = $this->get_ushort($cmap_offset + $offset);
358 if ($format == 4) {
359 $unicode_cmap_offset = $cmap_offset + $offset;
360 break;
361 }
362 }
363 $this->seek($save_pos );
364 }
365
366 $glyphToChar = array();
367 $charToGlyph = array();
368 $this->getCMAP4($unicode_cmap_offset, $glyphToChar, $charToGlyph );
369
370 fclose($this->fh);
371 return ($charToGlyph);
372 }
373
374/////////////////////////////////////////////////////////////////////////////////////////
375 function getTTCFonts($file) {
376 $this->filename = $file;
377 $this->fh = fopen($file,'rb');
378 if (!$this->fh) { return ('ERROR - Can\'t open file ' . $file); }
379 $this->numTTCFonts = 0;
380 $this->TTCFonts = array();
381 $this->version = $version = $this->read_ulong();
382 if ($version==0x74746366) {
383 $this->version = $version = $this->read_ulong(); // TTC Header version now
384 if (!in_array($version, array(0x00010000,0x00020000)))
385 return("ERROR - Error parsing TrueType Collection: version=".$version." - " . $file);
386 }
387 else {
388 return("ERROR - Not a TrueType Collection: version=".$version." - " . $file);
389 }
390 $this->numTTCFonts = $this->read_ulong();
391 for ($i=1; $i<=$this->numTTCFonts; $i++) {
392 $this->TTCFonts[$i]['offset'] = $this->read_ulong();
393 }
394 }
395
396
397
398/////////////////////////////////////////////////////////////////////////////////////////
399
400/////////////////////////////////////////////////////////////////////////////////////////
401
402 function extractInfo($debug=false, $BMPonly=false, $kerninfo=false) {
403 $this->panose = array();
404 $this->sFamilyClass = 0;
405 $this->sFamilySubClass = 0;
406 ///////////////////////////////////
407 // name - Naming table
408 ///////////////////////////////////
409 $name_offset = $this->seek_table("name");
410 $format = $this->read_ushort();
411 if ($format != 0 && $format != 1)
412 die("Unknown name table format ".$format);
413 $numRecords = $this->read_ushort();
414 $string_data_offset = $name_offset + $this->read_ushort();
415 $names = array(1=>'',2=>'',3=>'',4=>'',6=>'');
416 $K = array_keys($names);
417 $nameCount = count($names);
418 for ($i=0;$i<$numRecords; $i++) {
419 $platformId = $this->read_ushort();
420 $encodingId = $this->read_ushort();
421 $languageId = $this->read_ushort();
422 $nameId = $this->read_ushort();
423 $length = $this->read_ushort();
424 $offset = $this->read_ushort();
425 if (!in_array($nameId,$K)) continue;
426 $N = '';
427 if ($platformId == 3 && $encodingId == 1 && $languageId == 0x409) { // Microsoft, Unicode, US English, PS Name
428 $opos = $this->_pos;
429 $this->seek($string_data_offset + $offset);
430 if ($length % 2 != 0)
431 die("PostScript name is UTF-16BE string of odd length");
432 $length /= 2;
433 $N = '';
434 while ($length > 0) {
435 $char = $this->read_ushort();
436 $N .= (chr($char));
437 $length -= 1;
438 }
439 $this->_pos = $opos;
440 $this->seek($opos);
441 }
442 else if ($platformId == 1 && $encodingId == 0 && $languageId == 0) { // Macintosh, Roman, English, PS Name
443 $opos = $this->_pos;
444 $N = $this->get_chunk($string_data_offset + $offset, $length);
445 $this->_pos = $opos;
446 $this->seek($opos);
447 }
448 if ($N && $names[$nameId]=='') {
449 $names[$nameId] = $N;
450 $nameCount -= 1;
451 if ($nameCount==0) break;
452 }
453 }
454 if ($names[6])
455 $psName = $names[6];
456 else if ($names[4])
457 $psName = preg_replace('/ /','-',$names[4]);
458 else if ($names[1])
459 $psName = preg_replace('/ /','-',$names[1]);
460 else
461 $psName = '';
462 if (!$psName)
463 die("Could not find PostScript font name: ".$this->filename);
464 if ($debug) {
465 for ($i=0;$i<count($psName);$i++) {
466 $c = $psName[$i];
467 $oc = ord($c);
468 if ($oc>126 || strpos(' [](){}<>/%',$c)!==false)
469 die("psName=".$psName." contains invalid character ".$c." ie U+".ord(c));
470 }
471 }
472 $this->name = $psName;
473 if ($names[1]) { $this->familyName = $names[1]; } else { $this->familyName = $psName; }
474 if ($names[2]) { $this->styleName = $names[2]; } else { $this->styleName = 'Regular'; }
475 if ($names[4]) { $this->fullName = $names[4]; } else { $this->fullName = $psName; }
476 if ($names[3]) { $this->uniqueFontID = $names[3]; } else { $this->uniqueFontID = $psName; }
477
478 if ($names[6]) { $this->fullName = $names[6]; }
479
480 ///////////////////////////////////
481 // head - Font header table
482 ///////////////////////////////////
483 $this->seek_table("head");
484 if ($debug) {
485 $ver_maj = $this->read_ushort();
486 $ver_min = $this->read_ushort();
487 if ($ver_maj != 1)
488 die('Unknown head table version '. $ver_maj .'.'. $ver_min);
489 $this->fontRevision = $this->read_ushort() . $this->read_ushort();
490
491 $this->skip(4);
492 $magic = $this->read_ulong();
493 if ($magic != 0x5F0F3CF5)
494 die('Invalid head table magic ' .$magic);
495 $this->skip(2);
496 }
497 else {
498 $this->skip(18);
499 }
500 $this->unitsPerEm = $unitsPerEm = $this->read_ushort();
501 $scale = 1000 / $unitsPerEm;
502 $this->skip(16);
503 $xMin = $this->read_short();
504 $yMin = $this->read_short();
505 $xMax = $this->read_short();
506 $yMax = $this->read_short();
507 $this->bbox = array(($xMin*$scale), ($yMin*$scale), ($xMax*$scale), ($yMax*$scale));
508 $this->skip(3*2);
509 $indexToLocFormat = $this->read_ushort();
510 $glyphDataFormat = $this->read_ushort();
511 if ($glyphDataFormat != 0)
512 die('Unknown glyph data format '.$glyphDataFormat);
513
514 ///////////////////////////////////
515 // hhea metrics table
516 ///////////////////////////////////
517 // ttf2t1 seems to use this value rather than the one in OS/2 - so put in for compatibility
518 if (isset($this->tables["hhea"])) {
519 $this->seek_table("hhea");
520 $this->skip(4);
521 $hheaAscender = $this->read_short();
522 $hheaDescender = $this->read_short();
523 $this->ascent = ($hheaAscender *$scale);
524 $this->descent = ($hheaDescender *$scale);
525 }
526
527 ///////////////////////////////////
528 // OS/2 - OS/2 and Windows metrics table
529 ///////////////////////////////////
530 if (isset($this->tables["OS/2"])) {
531 $this->seek_table("OS/2");
532 $version = $this->read_ushort();
533 $this->skip(2);
534 $usWeightClass = $this->read_ushort();
535 $this->skip(2);
536 $fsType = $this->read_ushort();
537 if ($fsType == 0x0002 || ($fsType & 0x0300) != 0) {
538 global $overrideTTFFontRestriction;
539 if (!$overrideTTFFontRestriction) die('ERROR - Font file '.$this->filename.' cannot be embedded due to copyright restrictions.');
540 $this->restrictedUse = true;
541 }
542 $this->skip(20);
543 $sF = $this->read_short();
544 $this->sFamilyClass = ($sF >> 8);
545 $this->sFamilySubClass = ($sF & 0xFF);
546 $this->_pos += 10; //PANOSE = 10 byte length
547 $panose = fread($this->fh,10);
548 $this->panose = array();
549 for ($p=0;$p<strlen($panose);$p++) { $this->panose[] = ord($panose[$p]); }
550 $this->skip(26);
551 $sTypoAscender = $this->read_short();
552 $sTypoDescender = $this->read_short();
553 if (!$this->ascent) $this->ascent = ($sTypoAscender*$scale);
554 if (!$this->descent) $this->descent = ($sTypoDescender*$scale);
555 if ($version > 1) {
556 $this->skip(16);
557 $sCapHeight = $this->read_short();
558 $this->capHeight = ($sCapHeight*$scale);
559 }
560 else {
561 $this->capHeight = $this->ascent;
562 }
563 }
564 else {
565 $usWeightClass = 500;
566 if (!$this->ascent) $this->ascent = ($yMax*$scale);
567 if (!$this->descent) $this->descent = ($yMin*$scale);
568 $this->capHeight = $this->ascent;
569 }
570 $this->stemV = 50 + intval(pow(($usWeightClass / 65.0),2));
571
572 ///////////////////////////////////
573 // post - PostScript table
574 ///////////////////////////////////
575 $this->seek_table("post");
576 if ($debug) {
577 $ver_maj = $this->read_ushort();
578 $ver_min = $this->read_ushort();
579 if ($ver_maj <1 || $ver_maj >4)
580 die('Unknown post table version '.$ver_maj);
581 }
582 else {
583 $this->skip(4);
584 }
585 $this->italicAngle = $this->read_short() + $this->read_ushort() / 65536.0;
586 $this->underlinePosition = $this->read_short() * $scale;
587 $this->underlineThickness = $this->read_short() * $scale;
588 $isFixedPitch = $this->read_ulong();
589
590 $this->flags = 4;
591
592 if ($this->italicAngle!= 0)
593 $this->flags = $this->flags | 64;
594 if ($usWeightClass >= 600)
595 $this->flags = $this->flags | 262144;
596 if ($isFixedPitch)
597 $this->flags = $this->flags | 1;
598
599 ///////////////////////////////////
600 // hhea - Horizontal header table
601 ///////////////////////////////////
602 $this->seek_table("hhea");
603 if ($debug) {
604 $ver_maj = $this->read_ushort();
605 $ver_min = $this->read_ushort();
606 if ($ver_maj != 1)
607 die('Unknown hhea table version '.$ver_maj);
608 $this->skip(28);
609 }
610 else {
611 $this->skip(32);
612 }
613 $metricDataFormat = $this->read_ushort();
614 if ($metricDataFormat != 0)
615 die('Unknown horizontal metric data format '.$metricDataFormat);
616 $numberOfHMetrics = $this->read_ushort();
617 if ($numberOfHMetrics == 0)
618 die('Number of horizontal metrics is 0');
619
620 ///////////////////////////////////
621 // maxp - Maximum profile table
622 ///////////////////////////////////
623 $this->seek_table("maxp");
624 if ($debug) {
625 $ver_maj = $this->read_ushort();
626 $ver_min = $this->read_ushort();
627 if ($ver_maj != 1)
628 die('Unknown maxp table version '.$ver_maj);
629 }
630 else {
631 $this->skip(4);
632 }
633 $numGlyphs = $this->read_ushort();
634
635
636 ///////////////////////////////////
637 // cmap - Character to glyph index mapping table
638 ///////////////////////////////////
639 $cmap_offset = $this->seek_table("cmap");
640 $this->skip(2);
641 $cmapTableCount = $this->read_ushort();
642 $unicode_cmap_offset = 0;
643 for ($i=0;$i<$cmapTableCount;$i++) {
644 $platformID = $this->read_ushort();
645 $encodingID = $this->read_ushort();
646 $offset = $this->read_ulong();
647 $save_pos = $this->_pos;
648 if (($platformID == 3 && $encodingID == 1) || $platformID == 0) { // Microsoft, Unicode
649 $format = $this->get_ushort($cmap_offset + $offset);
650 if ($format == 4) {
651 if (!$unicode_cmap_offset) $unicode_cmap_offset = $cmap_offset + $offset;
652 if ($BMPonly) break;
653 }
654 }
655 // Microsoft, Unicode Format 12 table HKCS
656 else if ((($platformID == 3 && $encodingID == 10) || $platformID == 0) && !$BMPonly) {
657 $format = $this->get_ushort($cmap_offset + $offset);
658 if ($format == 12) {
659 $unicode_cmap_offset = $cmap_offset + $offset;
660 break;
661 }
662 }
663 $this->seek($save_pos );
664 }
665 if (!$unicode_cmap_offset)
666 die('Font ('.$this->filename .') does not have cmap for Unicode (platform 3, encoding 1, format 4, or platform 0, any encoding, format 4)');
667
668
669 $sipset = false;
670 $smpset = false;
671 // Format 12 CMAP does characters above Unicode BMP i.e. some HKCS characters U+20000 and above
672 if ($format == 12 && !$BMPonly) {
673 $this->maxUniChar = 0;
674 $this->seek($unicode_cmap_offset + 4);
675 $length = $this->read_ulong();
676 $limit = $unicode_cmap_offset + $length;
677 $this->skip(4);
678
679 $nGroups = $this->read_ulong();
680
681 $glyphToChar = array();
682 $charToGlyph = array();
683 for($i=0; $i<$nGroups ; $i++) {
684 $startCharCode = $this->read_ulong();
685 $endCharCode = $this->read_ulong();
686 $startGlyphCode = $this->read_ulong();
687 if (($endCharCode > 0x20000 && $endCharCode < 0x2A6DF) || ($endCharCode > 0x2F800 && $endCharCode < 0x2FA1F)) {
688 $sipset = true;
689 }
690 else if ($endCharCode > 0x10000 && $endCharCode < 0x1FFFF) {
691 $smpset = true;
692 }
693 $offset = 0;
694 for ($unichar=$startCharCode;$unichar<=$endCharCode;$unichar++) {
695 $glyph = $startGlyphCode + $offset ;
696 $offset++;
697 $charToGlyph[$unichar] = $glyph;
698 if ($unichar < 196608) { $this->maxUniChar = max($unichar,$this->maxUniChar); }
699 $glyphToChar[$glyph][] = $unichar;
700 }
701 }
702 }
703 else {
704
705 $glyphToChar = array();
706 $charToGlyph = array();
707 $this->getCMAP4($unicode_cmap_offset, $glyphToChar, $charToGlyph );
708
709 }
710 $this->sipset = $sipset ;
711 $this->smpset = $smpset ;
712
713 ///////////////////////////////////
714 // hmtx - Horizontal metrics table
715 ///////////////////////////////////
716 $this->getHMTX($numberOfHMetrics, $numGlyphs, $glyphToChar, $scale);
717
718 ///////////////////////////////////
719 // kern - Kerning pair table
720 ///////////////////////////////////
721 if ($kerninfo) {
722 // Recognises old form of Kerning table - as required by Windows - Format 0 only
723 $kern_offset = $this->seek_table("kern");
724 $version = $this->read_ushort();
725 $nTables = $this->read_ushort();
726 // subtable header
727 $sversion = $this->read_ushort();
728 $slength = $this->read_ushort();
729 $scoverage = $this->read_ushort();
730 $format = $scoverage >> 8;
731 if ($kern_offset && $version==0 && $format==0) {
732 // Format 0
733 $nPairs = $this->read_ushort();
734 $this->skip(6);
735 for ($i=0; $i<$nPairs; $i++) {
736 $left = $this->read_ushort();
737 $right = $this->read_ushort();
738 $val = $this->read_short();
739 if (count($glyphToChar[$left])==1 && count($glyphToChar[$right])==1) {
740 if ($left != 32 && $right != 32) {
741 $this->kerninfo[$glyphToChar[$left][0]][$glyphToChar[$right][0]] = intval($val*$scale);
742 }
743 }
744 }
745 }
746 }
747 }
748
749
750/////////////////////////////////////////////////////////////////////////////////////////
751
752
753 function makeSubset($file, &$subset, $TTCfontID=0, $debug=false, $unAGlyphs=false) { // mPDF 5.4.05
754 $this->unAGlyphs = $unAGlyphs; // mPDF 5.4.05
755 $this->filename = $file;
756 $this->fh = fopen($file ,'rb') or die('Can\'t open file ' . $file);
757 $this->_pos = 0;
758 $this->charWidths = '';
759 $this->glyphPos = array();
760 $this->charToGlyph = array();
761 $this->tables = array();
762 $this->otables = array();
763 $this->ascent = 0;
764 $this->descent = 0;
765 $this->numTTCFonts = 0;
766 $this->TTCFonts = array();
767 $this->skip(4);
768 $this->maxUni = 0;
769 if ($TTCfontID > 0) {
770 $this->version = $version = $this->read_ulong(); // TTC Header version now
771 if (!in_array($version, array(0x00010000,0x00020000)))
772 die("ERROR - Error parsing TrueType Collection: version=".$version." - " . $file);
773 $this->numTTCFonts = $this->read_ulong();
774 for ($i=1; $i<=$this->numTTCFonts; $i++) {
775 $this->TTCFonts[$i]['offset'] = $this->read_ulong();
776 }
777 $this->seek($this->TTCFonts[$TTCfontID]['offset']);
778 $this->version = $version = $this->read_ulong(); // TTFont version again now
779 }
780 $this->readTableDirectory($debug);
781
782 ///////////////////////////////////
783 // head - Font header table
784 ///////////////////////////////////
785 $this->seek_table("head");
786 $this->skip(50);
787 $indexToLocFormat = $this->read_ushort();
788 $glyphDataFormat = $this->read_ushort();
789
790 ///////////////////////////////////
791 // hhea - Horizontal header table
792 ///////////////////////////////////
793 $this->seek_table("hhea");
794 $this->skip(32);
795 $metricDataFormat = $this->read_ushort();
796 $orignHmetrics = $numberOfHMetrics = $this->read_ushort();
797
798 ///////////////////////////////////
799 // maxp - Maximum profile table
800 ///////////////////////////////////
801 $this->seek_table("maxp");
802 $this->skip(4);
803 $numGlyphs = $this->read_ushort();
804
805
806 ///////////////////////////////////
807 // cmap - Character to glyph index mapping table
808 ///////////////////////////////////
809 $cmap_offset = $this->seek_table("cmap");
810 $this->skip(2);
811 $cmapTableCount = $this->read_ushort();
812 $unicode_cmap_offset = 0;
813 for ($i=0;$i<$cmapTableCount;$i++) {
814 $platformID = $this->read_ushort();
815 $encodingID = $this->read_ushort();
816 $offset = $this->read_ulong();
817 $save_pos = $this->_pos;
818 if (($platformID == 3 && $encodingID == 1) || $platformID == 0) { // Microsoft, Unicode
819 $format = $this->get_ushort($cmap_offset + $offset);
820 if ($format == 4) {
821 $unicode_cmap_offset = $cmap_offset + $offset;
822 break;
823 }
824 }
825 $this->seek($save_pos );
826 }
827
828 if (!$unicode_cmap_offset)
829 die('Font ('.$this->filename .') does not have Unicode cmap (platform 3, encoding 1, format 4, or platform 0 [any encoding] format 4)');
830
831
832 $glyphToChar = array();
833 $charToGlyph = array();
834 $this->getCMAP4($unicode_cmap_offset, $glyphToChar, $charToGlyph );
835
836 $this->charToGlyph = $charToGlyph;
837
838
839 ///////////////////////////////////
840 // hmtx - Horizontal metrics table
841 ///////////////////////////////////
842 $scale = 1; // not used
843 $this->getHMTX($numberOfHMetrics, $numGlyphs, $glyphToChar, $scale);
844
845 ///////////////////////////////////
846 // loca - Index to location
847 ///////////////////////////////////
848 $this->getLOCA($indexToLocFormat, $numGlyphs);
849
850 $subsetglyphs = array(0=>0, 1=>1, 2=>2);
851 $subsetCharToGlyph = array();
852 foreach($subset AS $code) {
853 if (isset($this->charToGlyph[$code])) {
854 $subsetglyphs[$this->charToGlyph[$code]] = $code; // Old Glyph ID => Unicode
855 $subsetCharToGlyph[$code] = $this->charToGlyph[$code]; // Unicode to old GlyphID
856
857 }
858 $this->maxUni = max($this->maxUni, $code);
859 }
860
861 list($start,$dummy) = $this->get_table_pos('glyf');
862
863 $glyphSet = array();
864 ksort($subsetglyphs);
865 $n = 0;
866 $fsLastCharIndex = 0; // maximum Unicode index (character code) in this font, according to the cmap subtable for platform ID 3 and platform- specific encoding ID 0 or 1.
867 foreach($subsetglyphs AS $originalGlyphIdx => $uni) {
868 $fsLastCharIndex = max($fsLastCharIndex , $uni);
869 $glyphSet[$originalGlyphIdx] = $n; // old glyphID to new glyphID
870 $n++;
871 }
872
873 ksort($subsetCharToGlyph);
874 foreach($subsetCharToGlyph AS $uni => $originalGlyphIdx) {
875 $codeToGlyph[$uni] = $glyphSet[$originalGlyphIdx] ;
876 }
877 $this->codeToGlyph = $codeToGlyph;
878
879 ksort($subsetglyphs);
880 foreach($subsetglyphs AS $originalGlyphIdx => $uni) {
881 $this->getGlyphs($originalGlyphIdx, $start, $glyphSet, $subsetglyphs);
882 }
883
884 $numGlyphs = $numberOfHMetrics = count($subsetglyphs );
885
886 ///////////////////////////////////
887 // name - table copied from the original
888 ///////////////////////////////////
889 $this->add('name', $this->get_table('name'));
890
891 ///////////////////////////////////
892 //tables copied from the original
893 ///////////////////////////////////
894 $tags = array ('cvt ', 'fpgm', 'prep', 'gasp');
895 foreach($tags AS $tag) {
896 if (isset($this->tables[$tag])) { $this->add($tag, $this->get_table($tag)); }
897 }
898
899 ///////////////////////////////////
900 // post - PostScript
901 ///////////////////////////////////
902 if (isset($this->tables['post'])) {
903 $opost = $this->get_table('post');
904 $post = "\x00\x03\x00\x00" . substr($opost,4,12) . "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";
905 $this->add('post', $post);
906 }
907
908 ///////////////////////////////////
909 // Sort CID2GID map into segments of contiguous codes
910 ///////////////////////////////////
911 ksort($codeToGlyph);
912 unset($codeToGlyph[0]);
913 //unset($codeToGlyph[65535]);
914 $rangeid = 0;
915 $range = array();
916 $prevcid = -2;
917 $prevglidx = -1;
918 // for each character
919 foreach ($codeToGlyph as $cid => $glidx) {
920 if ($cid == ($prevcid + 1) && $glidx == ($prevglidx + 1)) {
921 $range[$rangeid][] = $glidx;
922 } else {
923 // new range
924 $rangeid = $cid;
925 $range[$rangeid] = array();
926 $range[$rangeid][] = $glidx;
927 }
928 $prevcid = $cid;
929 $prevglidx = $glidx;
930 }
931
932
933
934 ///////////////////////////////////
935 // CMap table
936 ///////////////////////////////////
937 // cmap - Character to glyph mapping
938 $segCount = count($range) + 1; // + 1 Last segment has missing character 0xFFFF
939 $searchRange = 1;
940 $entrySelector = 0;
941 while ($searchRange * 2 <= $segCount ) {
942 $searchRange = $searchRange * 2;
943 $entrySelector = $entrySelector + 1;
944 }
945 $searchRange = $searchRange * 2;
946 $rangeShift = $segCount * 2 - $searchRange;
947 $length = 16 + (8*$segCount ) + ($numGlyphs+1);
948 $cmap = array(0, 3, // Index : version, number of encoding subtables
949 0, 0, // Encoding Subtable : platform (UNI=0), encoding 0
950 0, 28, // Encoding Subtable : offset (hi,lo)
951 0, 3, // Encoding Subtable : platform (UNI=0), encoding 3
952 0, 28, // Encoding Subtable : offset (hi,lo)
953 3, 1, // Encoding Subtable : platform (MS=3), encoding 1
954 0, 28, // Encoding Subtable : offset (hi,lo)
955 4, $length, 0, // Format 4 Mapping subtable: format, length, language
956 $segCount*2,
957 $searchRange,
958 $entrySelector,
959 $rangeShift);
960
961 // endCode(s)
962 foreach($range AS $start=>$subrange) {
963 $endCode = $start + (count($subrange)-1);
964 $cmap[] = $endCode; // endCode(s)
965 }
966 $cmap[] = 0xFFFF; // endCode of last Segment
967 $cmap[] = 0; // reservedPad
968
969 // startCode(s)
970 foreach($range AS $start=>$subrange) {
971 $cmap[] = $start; // startCode(s)
972 }
973 $cmap[] = 0xFFFF; // startCode of last Segment
974 // idDelta(s)
975 foreach($range AS $start=>$subrange) {
976 $idDelta = -($start-$subrange[0]);
977 $n += count($subrange);
978 $cmap[] = $idDelta; // idDelta(s)
979 }
980 $cmap[] = 1; // idDelta of last Segment
981 // idRangeOffset(s)
982 foreach($range AS $subrange) {
983 $cmap[] = 0; // idRangeOffset[segCount] Offset in bytes to glyph indexArray, or 0
984
985 }
986 $cmap[] = 0; // idRangeOffset of last Segment
987 foreach($range AS $subrange) {
988 foreach($subrange AS $glidx) {
989 $cmap[] = $glidx;
990 }
991 }
992 $cmap[] = 0; // Mapping for last character
993 $cmapstr = '';
994 foreach($cmap AS $cm) { $cmapstr .= pack("n",$cm); }
995 $this->add('cmap', $cmapstr);
996
997
998 ///////////////////////////////////
999 // glyf - Glyph data
1000 ///////////////////////////////////
1001 list($glyfOffset,$glyfLength) = $this->get_table_pos('glyf');
1002 if ($glyfLength < $this->maxStrLenRead) {
1003 $glyphData = $this->get_table('glyf');
1004 }
1005
1006 $offsets = array();
1007 $glyf = '';
1008 $pos = 0;
1009 $hmtxstr = '';
1010 $xMinT = 0;
1011 $yMinT = 0;
1012 $xMaxT = 0;
1013 $yMaxT = 0;
1014 $advanceWidthMax = 0;
1015 $minLeftSideBearing = 0;
1016 $minRightSideBearing = 0;
1017 $xMaxExtent = 0;
1018 $maxPoints = 0; // points in non-compound glyph
1019 $maxContours = 0; // contours in non-compound glyph
1020 $maxComponentPoints = 0; // points in compound glyph
1021 $maxComponentContours = 0; // contours in compound glyph
1022 $maxComponentElements = 0; // number of glyphs referenced at top level
1023 $maxComponentDepth = 0; // levels of recursion, set to 0 if font has only simple glyphs
1024 $this->glyphdata = array();
1025
1026 foreach($subsetglyphs AS $originalGlyphIdx => $uni) {
1027 // hmtx - Horizontal Metrics
1028 $hm = $this->getHMetric($orignHmetrics, $originalGlyphIdx);
1029 $hmtxstr .= $hm;
1030
1031 $offsets[] = $pos;
1032 $glyphPos = $this->glyphPos[$originalGlyphIdx];
1033 $glyphLen = $this->glyphPos[$originalGlyphIdx + 1] - $glyphPos;
1034 if ($glyfLength < $this->maxStrLenRead) {
1035 $data = substr($glyphData,$glyphPos,$glyphLen);
1036 }
1037 else {
1038 if ($glyphLen > 0) $data = $this->get_chunk($glyfOffset+$glyphPos,$glyphLen);
1039 else $data = '';
1040 }
1041
1042 if ($glyphLen > 0) {
1043 if (_RECALC_PROFILE) {
1044 $xMin = $this->unpack_short(substr($data,2,2));
1045 $yMin = $this->unpack_short(substr($data,4,2));
1046 $xMax = $this->unpack_short(substr($data,6,2));
1047 $yMax = $this->unpack_short(substr($data,8,2));
1048 $xMinT = min($xMinT,$xMin);
1049 $yMinT = min($yMinT,$yMin);
1050 $xMaxT = max($xMaxT,$xMax);
1051 $yMaxT = max($yMaxT,$yMax);
1052 $aw = $this->unpack_short(substr($hm,0,2));
1053 $lsb = $this->unpack_short(substr($hm,2,2));
1054 $advanceWidthMax = max($advanceWidthMax,$aw);
1055 $minLeftSideBearing = min($minLeftSideBearing,$lsb);
1056 $minRightSideBearing = min($minRightSideBearing,($aw - $lsb - ($xMax - $xMin)));
1057 $xMaxExtent = max($xMaxExtent,($lsb + ($xMax - $xMin)));
1058 }
1059 $up = unpack("n", substr($data,0,2));
1060 }
1061 if ($glyphLen > 2 && ($up[1] & (1 << 15)) ) { // If number of contours <= -1 i.e. composiste glyph
1062 $pos_in_glyph = 10;
1063 $flags = GF_MORE;
1064 $nComponentElements = 0;
1065 while ($flags & GF_MORE) {
1066 $nComponentElements += 1; // number of glyphs referenced at top level
1067 $up = unpack("n", substr($data,$pos_in_glyph,2));
1068 $flags = $up[1];
1069 $up = unpack("n", substr($data,$pos_in_glyph+2,2));
1070 $glyphIdx = $up[1];
1071 $this->glyphdata[$originalGlyphIdx]['compGlyphs'][] = $glyphIdx;
1072 $data = $this->_set_ushort($data, $pos_in_glyph + 2, $glyphSet[$glyphIdx]);
1073 $pos_in_glyph += 4;
1074 if ($flags & GF_WORDS) { $pos_in_glyph += 4; }
1075 else { $pos_in_glyph += 2; }
1076 if ($flags & GF_SCALE) { $pos_in_glyph += 2; }
1077 else if ($flags & GF_XYSCALE) { $pos_in_glyph += 4; }
1078 else if ($flags & GF_TWOBYTWO) { $pos_in_glyph += 8; }
1079 }
1080 $maxComponentElements = max($maxComponentElements, $nComponentElements);
1081 }
1082 // Simple Glyph
1083 else if (_RECALC_PROFILE && $glyphLen > 2 && $up[1] < (1 << 15) && $up[1] > 0) { // Number of contours > 0 simple glyph
1084 $nContours = $up[1];
1085 $this->glyphdata[$originalGlyphIdx]['nContours'] = $nContours;
1086 $maxContours = max($maxContours, $nContours);
1087
1088 // Count number of points in simple glyph
1089 $pos_in_glyph = 10 + ($nContours * 2) - 2; // Last endContourPoint
1090 $up = unpack("n", substr($data,$pos_in_glyph,2));
1091 $points = $up[1]+1;
1092 $this->glyphdata[$originalGlyphIdx]['nPoints'] = $points;
1093 $maxPoints = max($maxPoints, $points);
1094 }
1095
1096 $glyf .= $data;
1097 $pos += $glyphLen;
1098 if ($pos % 4 != 0) {
1099 $padding = 4 - ($pos % 4);
1100 $glyf .= str_repeat("\0",$padding);
1101 $pos += $padding;
1102 }
1103 }
1104
1105 if (_RECALC_PROFILE) {
1106 foreach($this->glyphdata AS $originalGlyphIdx => $val) {
1107 $maxdepth = $depth = -1;
1108 $points = 0;
1109 $contours = 0;
1110 $this->getGlyphData($originalGlyphIdx, $maxdepth, $depth, $points, $contours) ;
1111 $maxComponentDepth = max($maxComponentDepth , $maxdepth);
1112 $maxComponentPoints = max($maxComponentPoints , $points);
1113 $maxComponentContours = max($maxComponentContours , $contours);
1114 }
1115 }
1116
1117
1118 $offsets[] = $pos;
1119 $this->add('glyf', $glyf);
1120
1121 ///////////////////////////////////
1122 // hmtx - Horizontal Metrics
1123 ///////////////////////////////////
1124 $this->add('hmtx', $hmtxstr);
1125
1126
1127 ///////////////////////////////////
1128 // loca - Index to location
1129 ///////////////////////////////////
1130 $locastr = '';
1131 if ((($pos + 1) >> 1) > 0xFFFF) {
1132 $indexToLocFormat = 1; // long format
1133 foreach($offsets AS $offset) { $locastr .= pack("N",$offset); }
1134 }
1135 else {
1136 $indexToLocFormat = 0; // short format
1137 foreach($offsets AS $offset) { $locastr .= pack("n",($offset/2)); }
1138 }
1139 $this->add('loca', $locastr);
1140
1141 ///////////////////////////////////
1142 // head - Font header
1143 ///////////////////////////////////
1144 $head = $this->get_table('head');
1145 $head = $this->_set_ushort($head, 50, $indexToLocFormat);
1146 if (_RECALC_PROFILE) {
1147 $head = $this->_set_short($head, 36, $xMinT); // for all glyph bounding boxes
1148 $head = $this->_set_short($head, 38, $yMinT); // for all glyph bounding boxes
1149 $head = $this->_set_short($head, 40, $xMaxT); // for all glyph bounding boxes
1150 $head = $this->_set_short($head, 42, $yMaxT); // for all glyph bounding boxes
1151 $head[17] = chr($head[17] & ~(1 << 4)); // Unset Bit 4 (as hdmx/LTSH tables not included)
1152 }
1153 $this->add('head', $head);
1154
1155
1156 ///////////////////////////////////
1157 // hhea - Horizontal Header
1158 ///////////////////////////////////
1159 $hhea = $this->get_table('hhea');
1160 $hhea = $this->_set_ushort($hhea, 34, $numberOfHMetrics);
1161 if (_RECALC_PROFILE) {
1162 $hhea = $this->_set_ushort($hhea, 10, $advanceWidthMax);
1163 $hhea = $this->_set_short($hhea, 12, $minLeftSideBearing);
1164 $hhea = $this->_set_short($hhea, 14, $minRightSideBearing);
1165 $hhea = $this->_set_short($hhea, 16, $xMaxExtent);
1166 }
1167 $this->add('hhea', $hhea);
1168
1169 ///////////////////////////////////
1170 // maxp - Maximum Profile
1171 ///////////////////////////////////
1172 $maxp = $this->get_table('maxp');
1173 $maxp = $this->_set_ushort($maxp, 4, $numGlyphs);
1174 if (_RECALC_PROFILE) {
1175 $maxp = $this->_set_ushort($maxp, 6, $maxPoints); // points in non-compound glyph
1176 $maxp = $this->_set_ushort($maxp, 8, $maxContours); // contours in non-compound glyph
1177 $maxp = $this->_set_ushort($maxp, 10, $maxComponentPoints); // points in compound glyph
1178 $maxp = $this->_set_ushort($maxp, 12, $maxComponentContours); // contours in compound glyph
1179 $maxp = $this->_set_ushort($maxp, 28, $maxComponentElements); // number of glyphs referenced at top level
1180 $maxp = $this->_set_ushort($maxp, 30, $maxComponentDepth); // levels of recursion, set to 0 if font has only simple glyphs
1181 }
1182 $this->add('maxp', $maxp);
1183
1184
1185 ///////////////////////////////////
1186 // OS/2 - OS/2
1187 ///////////////////////////////////
1188 if (isset($this->tables['OS/2'])) {
1189 $os2_offset = $this->seek_table("OS/2");
1190 if (_RECALC_PROFILE) {
1191 $fsSelection = $this->get_ushort($os2_offset+62);
1192 $fsSelection = ($fsSelection & ~(1 << 6)); // 2-byte bit field containing information concerning the nature of the font patterns
1193 // bit#0 = Italic; bit#5=Bold
1194 // Match name table's font subfamily string
1195 // Clear bit#6 used for 'Regular' and optional
1196 }
1197
1198 // NB Currently this method never subsets characters above BMP
1199 // Could set nonBMP bit according to $this->maxUni
1200 $nonBMP = $this->get_ushort($os2_offset+46);
1201 $nonBMP = ($nonBMP & ~(1 << 9)); // Unset Bit 57 (indicates non-BMP) - for interactive forms
1202
1203 $os2 = $this->get_table('OS/2');
1204 if (_RECALC_PROFILE) {
1205 $os2 = $this->_set_ushort($os2, 62, $fsSelection);
1206 $os2 = $this->_set_ushort($os2, 66, $fsLastCharIndex);
1207 $os2 = $this->_set_ushort($os2, 42, 0x0000); // ulCharRange (ulUnicodeRange) bits 24-31 | 16-23
1208 $os2 = $this->_set_ushort($os2, 44, 0x0000); // ulCharRange (Unicode ranges) bits 8-15 | 0-7
1209 $os2 = $this->_set_ushort($os2, 46, $nonBMP); // ulCharRange (Unicode ranges) bits 56-63 | 48-55
1210 $os2 = $this->_set_ushort($os2, 48, 0x0000); // ulCharRange (Unicode ranges) bits 40-47 | 32-39
1211 $os2 = $this->_set_ushort($os2, 50, 0x0000); // ulCharRange (Unicode ranges) bits 88-95 | 80-87
1212 $os2 = $this->_set_ushort($os2, 52, 0x0000); // ulCharRange (Unicode ranges) bits 72-79 | 64-71
1213 $os2 = $this->_set_ushort($os2, 54, 0x0000); // ulCharRange (Unicode ranges) bits 120-127 | 112-119
1214 $os2 = $this->_set_ushort($os2, 56, 0x0000); // ulCharRange (Unicode ranges) bits 104-111 | 96-103
1215 }
1216 $os2 = $this->_set_ushort($os2, 46, $nonBMP); // Unset Bit 57 (indicates non-BMP) - for interactive forms
1217
1218 $this->add('OS/2', $os2 );
1219 }
1220
1221 fclose($this->fh);
1222 // Put the TTF file together
1223 $stm = '';
1224 $this->endTTFile($stm);
1225 //file_put_contents('testfont.ttf', $stm); exit;
1226 return $stm ;
1227 }
1228
1229//================================================================================
1230
1231 // Also does SMP
1232 function makeSubsetSIP($file, &$subset, $TTCfontID=0, $debug=false) {
1233 $this->fh = fopen($file ,'rb') or die('Can\'t open file ' . $file);
1234 $this->filename = $file;
1235 $this->_pos = 0;
1236 $this->unAGlyphs = false; // mPDF 5.4.05
1237 $this->charWidths = '';
1238 $this->glyphPos = array();
1239 $this->charToGlyph = array();
1240 $this->tables = array();
1241 $this->otables = array();
1242 $this->ascent = 0;
1243 $this->descent = 0;
1244 $this->numTTCFonts = 0;
1245 $this->TTCFonts = array();
1246 $this->skip(4);
1247 if ($TTCfontID > 0) {
1248 $this->version = $version = $this->read_ulong(); // TTC Header version now
1249 if (!in_array($version, array(0x00010000,0x00020000)))
1250 die("ERROR - Error parsing TrueType Collection: version=".$version." - " . $file);
1251 $this->numTTCFonts = $this->read_ulong();
1252 for ($i=1; $i<=$this->numTTCFonts; $i++) {
1253 $this->TTCFonts[$i]['offset'] = $this->read_ulong();
1254 }
1255 $this->seek($this->TTCFonts[$TTCfontID]['offset']);
1256 $this->version = $version = $this->read_ulong(); // TTFont version again now
1257 }
1258 $this->readTableDirectory($debug);
1259
1260
1261
1262 ///////////////////////////////////
1263 // head - Font header table
1264 ///////////////////////////////////
1265 $this->seek_table("head");
1266 $this->skip(50);
1267 $indexToLocFormat = $this->read_ushort();
1268 $glyphDataFormat = $this->read_ushort();
1269
1270 ///////////////////////////////////
1271 // hhea - Horizontal header table
1272 ///////////////////////////////////
1273 $this->seek_table("hhea");
1274 $this->skip(32);
1275 $metricDataFormat = $this->read_ushort();
1276 $orignHmetrics = $numberOfHMetrics = $this->read_ushort();
1277
1278 ///////////////////////////////////
1279 // maxp - Maximum profile table
1280 ///////////////////////////////////
1281 $this->seek_table("maxp");
1282 $this->skip(4);
1283 $numGlyphs = $this->read_ushort();
1284
1285
1286 ///////////////////////////////////
1287 // cmap - Character to glyph index mapping table
1288 ///////////////////////////////////
1289
1290 $cmap_offset = $this->seek_table("cmap");
1291 $this->skip(2);
1292 $cmapTableCount = $this->read_ushort();
1293 $unicode_cmap_offset = 0;
1294 for ($i=0;$i<$cmapTableCount;$i++) {
1295 $platformID = $this->read_ushort();
1296 $encodingID = $this->read_ushort();
1297 $offset = $this->read_ulong();
1298 $save_pos = $this->_pos;
1299 if (($platformID == 3 && $encodingID == 10) || $platformID == 0) { // Microsoft, Unicode Format 12 table HKCS
1300 $format = $this->get_ushort($cmap_offset + $offset);
1301 if ($format == 12) {
1302 $unicode_cmap_offset = $cmap_offset + $offset;
1303 break;
1304 }
1305 }
1306 $this->seek($save_pos );
1307 }
1308
1309 if (!$unicode_cmap_offset)
1310 die('Font does not have cmap for Unicode (platform 3, encoding 1, format 4, or platform 0, any encoding, format 4)');
1311 // Format 12 CMAP does characters above Unicode BMP i.e. some HKCS characters U+20000 and above
1312 if ($format == 12) {
1313 $this->maxUniChar = 0;
1314 $this->seek($unicode_cmap_offset + 4);
1315 $length = $this->read_ulong();
1316 $limit = $unicode_cmap_offset + $length;
1317 $this->skip(4);
1318
1319 $nGroups = $this->read_ulong();
1320
1321 $glyphToChar = array();
1322 $charToGlyph = array();
1323 for($i=0; $i<$nGroups ; $i++) {
1324 $startCharCode = $this->read_ulong();
1325 $endCharCode = $this->read_ulong();
1326 $startGlyphCode = $this->read_ulong();
1327 $offset = 0;
1328 for ($unichar=$startCharCode;$unichar<=$endCharCode;$unichar++) {
1329 $glyph = $startGlyphCode + $offset ;
1330 $offset++;
1331 $charToGlyph[$unichar] = $glyph;
1332 if ($unichar < 196608) { $this->maxUniChar = max($unichar,$this->maxUniChar); }
1333 $glyphToChar[$glyph][] = $unichar;
1334 }
1335 }
1336 }
1337 else
1338 die('Font does not have cmap for Unicode (format 12)');
1339
1340
1341 ///////////////////////////////////
1342 // hmtx - Horizontal metrics table
1343 ///////////////////////////////////
1344 $scale = 1; // not used here
1345 $this->getHMTX($numberOfHMetrics, $numGlyphs, $glyphToChar, $scale);
1346
1347 ///////////////////////////////////
1348 // loca - Index to location
1349 ///////////////////////////////////
1350 $this->getLOCA($indexToLocFormat, $numGlyphs);
1351
1352 ///////////////////////////////////////////////////////////////////
1353
1354 $glyphMap = array(0=>0);
1355 $glyphSet = array(0=>0);
1356 $codeToGlyph = array();
1357 // Set a substitute if ASCII characters do not have glyphs
1358 if (isset($charToGlyph[0x3F])) { $subs = $charToGlyph[0x3F]; } // Question mark
1359 else { $subs = $charToGlyph[32]; }
1360 foreach($subset AS $code) {
1361 if (isset($charToGlyph[$code]))
1362 $originalGlyphIdx = $charToGlyph[$code];
1363 else if ($code<128) {
1364 $originalGlyphIdx = $subs;
1365 }
1366 else { $originalGlyphIdx = 0; }
1367 if (!isset($glyphSet[$originalGlyphIdx])) {
1368 $glyphSet[$originalGlyphIdx] = count($glyphMap);
1369 $glyphMap[] = $originalGlyphIdx;
1370 }
1371 $codeToGlyph[$code] = $glyphSet[$originalGlyphIdx];
1372 }
1373
1374 list($start,$dummy) = $this->get_table_pos('glyf');
1375
1376 $n = 0;
1377 while ($n < count($glyphMap)) {
1378 $originalGlyphIdx = $glyphMap[$n];
1379 $glyphPos = $this->glyphPos[$originalGlyphIdx];
1380 $glyphLen = $this->glyphPos[$originalGlyphIdx + 1] - $glyphPos;
1381 $n += 1;
1382 if (!$glyphLen) continue;
1383 $this->seek($start + $glyphPos);
1384 $numberOfContours = $this->read_short();
1385 if ($numberOfContours < 0) {
1386 $this->skip(8);
1387 $flags = GF_MORE;
1388 while ($flags & GF_MORE) {
1389 $flags = $this->read_ushort();
1390 $glyphIdx = $this->read_ushort();
1391 if (!isset($glyphSet[$glyphIdx])) {
1392 $glyphSet[$glyphIdx] = count($glyphMap);
1393 $glyphMap[] = $glyphIdx;
1394 }
1395 if ($flags & GF_WORDS)
1396 $this->skip(4);
1397 else
1398 $this->skip(2);
1399 if ($flags & GF_SCALE)
1400 $this->skip(2);
1401 else if ($flags & GF_XYSCALE)
1402 $this->skip(4);
1403 else if ($flags & GF_TWOBYTWO)
1404 $this->skip(8);
1405 }
1406 }
1407 }
1408
1409 $numGlyphs = $n = count($glyphMap);
1410 $numberOfHMetrics = $n;
1411
1412 ///////////////////////////////////
1413 // name
1414 ///////////////////////////////////
1415 // Needs to have a name entry in 3,0 (e.g. symbol) - original font will be 3,1 (i.e. Unicode)
1416 $name = $this->get_table('name');
1417 $name_offset = $this->seek_table("name");
1418 $format = $this->read_ushort();
1419 $numRecords = $this->read_ushort();
1420 $string_data_offset = $name_offset + $this->read_ushort();
1421 for ($i=0;$i<$numRecords; $i++) {
1422 $platformId = $this->read_ushort();
1423 $encodingId = $this->read_ushort();
1424 if ($platformId == 3 && $encodingId == 1) {
1425 $pos = 6 + ($i * 12) + 2;
1426 $name = $this->_set_ushort($name, $pos, 0x00); // Change encoding to 3,0 rather than 3,1
1427 }
1428 $this->skip(8);
1429 }
1430 $this->add('name', $name);
1431
1432 ///////////////////////////////////
1433 // OS/2
1434 ///////////////////////////////////
1435 if (isset($this->tables['OS/2'])) {
1436 $os2 = $this->get_table('OS/2');
1437 $os2 = $this->_set_ushort($os2, 42, 0x00); // ulCharRange (Unicode ranges)
1438 $os2 = $this->_set_ushort($os2, 44, 0x00); // ulCharRange (Unicode ranges)
1439 $os2 = $this->_set_ushort($os2, 46, 0x00); // ulCharRange (Unicode ranges)
1440 $os2 = $this->_set_ushort($os2, 48, 0x00); // ulCharRange (Unicode ranges)
1441
1442 $os2 = $this->_set_ushort($os2, 50, 0x00); // ulCharRange (Unicode ranges)
1443 $os2 = $this->_set_ushort($os2, 52, 0x00); // ulCharRange (Unicode ranges)
1444 $os2 = $this->_set_ushort($os2, 54, 0x00); // ulCharRange (Unicode ranges)
1445 $os2 = $this->_set_ushort($os2, 56, 0x00); // ulCharRange (Unicode ranges)
1446 // Set Symbol character only in ulCodePageRange
1447 $os2 = $this->_set_ushort($os2, 78, 0x8000); // ulCodePageRange = Bit #31 Symbol **** 78 = Bit 16-31
1448 $os2 = $this->_set_ushort($os2, 80, 0x0000); // ulCodePageRange = Bit #31 Symbol **** 80 = Bit 0-15
1449 $os2 = $this->_set_ushort($os2, 82, 0x0000); // ulCodePageRange = Bit #32- Symbol **** 82 = Bits 48-63
1450 $os2 = $this->_set_ushort($os2, 84, 0x0000); // ulCodePageRange = Bit #32- Symbol **** 84 = Bits 32-47
1451
1452 $os2 = $this->_set_ushort($os2, 64, 0x01); // FirstCharIndex
1453 $os2 = $this->_set_ushort($os2, 66, count($subset)); // LastCharIndex
1454 // Set PANOSE first bit to 5 for Symbol
1455 $os2 = $this->splice($os2, 32, chr(5).chr(0).chr(1).chr(0).chr(1).chr(0).chr(0).chr(0).chr(0).chr(0));
1456 $this->add('OS/2', $os2 );
1457 }
1458
1459
1460 ///////////////////////////////////
1461 //tables copied from the original
1462 ///////////////////////////////////
1463 $tags = array ('cvt ', 'fpgm', 'prep', 'gasp');
1464 foreach($tags AS $tag) { // 1.02
1465 if (isset($this->tables[$tag])) { $this->add($tag, $this->get_table($tag)); }
1466 }
1467
1468 ///////////////////////////////////
1469 // post - PostScript
1470 ///////////////////////////////////
1471 if (isset($this->tables['post'])) {
1472 $opost = $this->get_table('post');
1473 $post = "\x00\x03\x00\x00" . substr($opost,4,12) . "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";
1474 }
1475 $this->add('post', $post);
1476
1477 ///////////////////////////////////
1478 // hhea - Horizontal Header
1479 ///////////////////////////////////
1480 $hhea = $this->get_table('hhea');
1481 $hhea = $this->_set_ushort($hhea, 34, $numberOfHMetrics);
1482 $this->add('hhea', $hhea);
1483
1484 ///////////////////////////////////
1485 // maxp - Maximum Profile
1486 ///////////////////////////////////
1487 $maxp = $this->get_table('maxp');
1488 $maxp = $this->_set_ushort($maxp, 4, $numGlyphs);
1489 $this->add('maxp', $maxp);
1490
1491
1492 ///////////////////////////////////
1493 // CMap table Formats [1,0,]6 and [3,0,]4
1494 ///////////////////////////////////
1495 ///////////////////////////////////
1496 // Sort CID2GID map into segments of contiguous codes
1497 ///////////////////////////////////
1498 $rangeid = 0;
1499 $range = array();
1500 $prevcid = -2;
1501 $prevglidx = -1;
1502 // for each character
1503 foreach ($subset as $cid => $code) {
1504 $glidx = $codeToGlyph[$code];
1505 if ($cid == ($prevcid + 1) && $glidx == ($prevglidx + 1)) {
1506 $range[$rangeid][] = $glidx;
1507 } else {
1508 // new range
1509 $rangeid = $cid;
1510 $range[$rangeid] = array();
1511 $range[$rangeid][] = $glidx;
1512 }
1513 $prevcid = $cid;
1514 $prevglidx = $glidx;
1515 }
1516 // cmap - Character to glyph mapping
1517 $segCount = count($range) + 1; // + 1 Last segment has missing character 0xFFFF
1518 $searchRange = 1;
1519 $entrySelector = 0;
1520 while ($searchRange * 2 <= $segCount ) {
1521 $searchRange = $searchRange * 2;
1522 $entrySelector = $entrySelector + 1;
1523 }
1524 $searchRange = $searchRange * 2;
1525 $rangeShift = $segCount * 2 - $searchRange;
1526 $length = 16 + (8*$segCount ) + ($numGlyphs+1);
1527 $cmap = array(
1528 4, $length, 0, // Format 4 Mapping subtable: format, length, language
1529 $segCount*2,
1530 $searchRange,
1531 $entrySelector,
1532 $rangeShift);
1533
1534 // endCode(s)
1535 foreach($range AS $start=>$subrange) {
1536 $endCode = $start + (count($subrange)-1);
1537 $cmap[] = $endCode; // endCode(s)
1538 }
1539 $cmap[] = 0xFFFF; // endCode of last Segment
1540 $cmap[] = 0; // reservedPad
1541
1542 // startCode(s)
1543 foreach($range AS $start=>$subrange) {
1544 $cmap[] = $start; // startCode(s)
1545 }
1546 $cmap[] = 0xFFFF; // startCode of last Segment
1547 // idDelta(s)
1548 foreach($range AS $start=>$subrange) {
1549 $idDelta = -($start-$subrange[0]);
1550 $n += count($subrange);
1551 $cmap[] = $idDelta; // idDelta(s)
1552 }
1553 $cmap[] = 1; // idDelta of last Segment
1554 // idRangeOffset(s)
1555 foreach($range AS $subrange) {
1556 $cmap[] = 0; // idRangeOffset[segCount] Offset in bytes to glyph indexArray, or 0
1557
1558 }
1559 $cmap[] = 0; // idRangeOffset of last Segment
1560 foreach($range AS $subrange) {
1561 foreach($subrange AS $glidx) {
1562 $cmap[] = $glidx;
1563 }
1564 }
1565 $cmap[] = 0; // Mapping for last character
1566 $cmapstr4 = '';
1567 foreach($cmap AS $cm) { $cmapstr4 .= pack("n",$cm); }
1568
1569 ///////////////////////////////////
1570 // cmap - Character to glyph mapping
1571 ///////////////////////////////////
1572 $entryCount = count($subset);
1573 $length = 10 + $entryCount * 2;
1574
1575 $off = 20 + $length;
1576 $hoff = $off >> 16;
1577 $loff = $off & 0xFFFF;
1578
1579 $cmap = array(0, 2, // Index : version, number of subtables
1580 1, 0, // Subtable : platform, encoding
1581 0, 20, // offset (hi,lo)
1582 3, 0, // Subtable : platform, encoding
1583 $hoff, $loff, // offset (hi,lo)
1584 6, $length, // Format 6 Mapping table: format, length
1585 0, 1, // language, First char code
1586 $entryCount
1587 );
1588 $cmapstr = '';
1589 foreach($subset AS $code) { $cmap[] = $codeToGlyph[$code]; }
1590 foreach($cmap AS $cm) { $cmapstr .= pack("n",$cm); }
1591 $cmapstr .= $cmapstr4;
1592 $this->add('cmap', $cmapstr);
1593
1594 ///////////////////////////////////
1595 // hmtx - Horizontal Metrics
1596 ///////////////////////////////////
1597 $hmtxstr = '';
1598 for($n=0;$n<$numGlyphs;$n++) {
1599 $originalGlyphIdx = $glyphMap[$n];
1600 $hm = $this->getHMetric($orignHmetrics, $originalGlyphIdx);
1601 $hmtxstr .= $hm;
1602 }
1603 $this->add('hmtx', $hmtxstr);
1604
1605 ///////////////////////////////////
1606 // glyf - Glyph data
1607 ///////////////////////////////////
1608 list($glyfOffset,$glyfLength) = $this->get_table_pos('glyf');
1609 if ($glyfLength < $this->maxStrLenRead) {
1610 $glyphData = $this->get_table('glyf');
1611 }
1612
1613 $offsets = array();
1614 $glyf = '';
1615 $pos = 0;
1616 for ($n=0;$n<$numGlyphs;$n++) {
1617 $offsets[] = $pos;
1618 $originalGlyphIdx = $glyphMap[$n];
1619 $glyphPos = $this->glyphPos[$originalGlyphIdx];
1620 $glyphLen = $this->glyphPos[$originalGlyphIdx + 1] - $glyphPos;
1621 if ($glyfLength < $this->maxStrLenRead) {
1622 $data = substr($glyphData,$glyphPos,$glyphLen);
1623 }
1624 else {
1625 if ($glyphLen > 0) $data = $this->get_chunk($glyfOffset+$glyphPos,$glyphLen);
1626 else $data = '';
1627 }
1628 if ($glyphLen > 0) $up = unpack("n", substr($data,0,2));
1629 if ($glyphLen > 2 && ($up[1] & (1 << 15)) ) {
1630 $pos_in_glyph = 10;
1631 $flags = GF_MORE;
1632 while ($flags & GF_MORE) {
1633 $up = unpack("n", substr($data,$pos_in_glyph,2));
1634 $flags = $up[1];
1635 $up = unpack("n", substr($data,$pos_in_glyph+2,2));
1636 $glyphIdx = $up[1];
1637 $data = $this->_set_ushort($data, $pos_in_glyph + 2, $glyphSet[$glyphIdx]);
1638 $pos_in_glyph += 4;
1639 if ($flags & GF_WORDS) { $pos_in_glyph += 4; }
1640 else { $pos_in_glyph += 2; }
1641 if ($flags & GF_SCALE) { $pos_in_glyph += 2; }
1642 else if ($flags & GF_XYSCALE) { $pos_in_glyph += 4; }
1643 else if ($flags & GF_TWOBYTWO) { $pos_in_glyph += 8; }
1644 }
1645 }
1646 $glyf .= $data;
1647 $pos += $glyphLen;
1648 if ($pos % 4 != 0) {
1649 $padding = 4 - ($pos % 4);
1650 $glyf .= str_repeat("\0",$padding);
1651 $pos += $padding;
1652 }
1653 }
1654 $offsets[] = $pos;
1655 $this->add('glyf', $glyf);
1656
1657 ///////////////////////////////////
1658 // loca - Index to location
1659 ///////////////////////////////////
1660 $locastr = '';
1661 if ((($pos + 1) >> 1) > 0xFFFF) {
1662 $indexToLocFormat = 1; // long format
1663 foreach($offsets AS $offset) { $locastr .= pack("N",$offset); }
1664 }
1665 else {
1666 $indexToLocFormat = 0; // short format
1667 foreach($offsets AS $offset) { $locastr .= pack("n",($offset/2)); }
1668 }
1669 $this->add('loca', $locastr);
1670
1671 ///////////////////////////////////
1672 // head - Font header
1673 ///////////////////////////////////
1674 $head = $this->get_table('head');
1675 $head = $this->_set_ushort($head, 50, $indexToLocFormat);
1676 $this->add('head', $head);
1677
1678 fclose($this->fh);
1679
1680 // Put the TTF file together
1681 $stm = '';
1682 $this->endTTFile($stm);
1683 //file_put_contents('testfont.ttf', $stm); exit;
1684 return $stm ;
1685 }
1686
1687 //////////////////////////////////////////////////////////////////////////////////
1688 // Recursively get composite glyph data
1689 function getGlyphData($originalGlyphIdx, &$maxdepth, &$depth, &$points, &$contours) {
1690 $depth++;
1691 $maxdepth = max($maxdepth, $depth);
1692 if (count($this->glyphdata[$originalGlyphIdx]['compGlyphs'])) {
1693 foreach($this->glyphdata[$originalGlyphIdx]['compGlyphs'] AS $glyphIdx) {
1694 $this->getGlyphData($glyphIdx, $maxdepth, $depth, $points, $contours);
1695 }
1696 }
1697 else if (($this->glyphdata[$originalGlyphIdx]['nContours'] > 0) && $depth > 0) { // simple
1698 $contours += $this->glyphdata[$originalGlyphIdx]['nContours'];
1699 $points += $this->glyphdata[$originalGlyphIdx]['nPoints'];
1700 }
1701 $depth--;
1702 }
1703
1704
1705 //////////////////////////////////////////////////////////////////////////////////
1706 // Recursively get composite glyphs
1707 function getGlyphs($originalGlyphIdx, &$start, &$glyphSet, &$subsetglyphs) {
1708 $glyphPos = $this->glyphPos[$originalGlyphIdx];
1709 $glyphLen = $this->glyphPos[$originalGlyphIdx + 1] - $glyphPos;
1710 if (!$glyphLen) {
1711 return;
1712 }
1713 $this->seek($start + $glyphPos);
1714 $numberOfContours = $this->read_short();
1715 if ($numberOfContours < 0) {
1716 $this->skip(8);
1717 $flags = GF_MORE;
1718 while ($flags & GF_MORE) {
1719 $flags = $this->read_ushort();
1720 $glyphIdx = $this->read_ushort();
1721 if (!isset($glyphSet[$glyphIdx])) {
1722 $glyphSet[$glyphIdx] = count($subsetglyphs); // old glyphID to new glyphID
1723 $subsetglyphs[$glyphIdx] = true;
1724 }
1725 $savepos = ftell($this->fh);
1726 $this->getGlyphs($glyphIdx, $start, $glyphSet, $subsetglyphs);
1727 $this->seek($savepos);
1728 if ($flags & GF_WORDS)
1729 $this->skip(4);
1730 else
1731 $this->skip(2);
1732 if ($flags & GF_SCALE)
1733 $this->skip(2);
1734 else if ($flags & GF_XYSCALE)
1735 $this->skip(4);
1736 else if ($flags & GF_TWOBYTWO)
1737 $this->skip(8);
1738 }
1739 }
1740 }
1741
1742 //////////////////////////////////////////////////////////////////////////////////
1743
1744 function getHMTX($numberOfHMetrics, $numGlyphs, &$glyphToChar, $scale) {
1745 $start = $this->seek_table("hmtx");
1746 $aw = 0;
1747 $this->charWidths = str_pad('', 256*256*2, "\x00");
1748 if ($this->maxUniChar > 65536) { $this->charWidths .= str_pad('', 256*256*2, "\x00"); } // Plane 1 SMP
1749 if ($this->maxUniChar > 131072) { $this->charWidths .= str_pad('', 256*256*2, "\x00"); } // Plane 2 SMP
1750 $nCharWidths = 0;
1751 if (($numberOfHMetrics*4) < $this->maxStrLenRead) {
1752 $data = $this->get_chunk($start,($numberOfHMetrics*4));
1753 $arr = unpack("n*", $data);
1754 }
1755 else { $this->seek($start); }
1756 for( $glyph=0; $glyph<$numberOfHMetrics; $glyph++) {
1757 if (($numberOfHMetrics*4) < $this->maxStrLenRead) {
1758 $aw = $arr[($glyph*2)+1];
1759 }
1760 else {
1761 $aw = $this->read_ushort();
1762 $lsb = $this->read_ushort();
1763 }
1764 if (isset($glyphToChar[$glyph]) || $glyph == 0) {
1765
1766 if ($aw >= (1 << 15) ) { $aw = 0; } // 1.03 Some (arabic) fonts have -ve values for width
1767 // although should be unsigned value - comes out as e.g. 65108 (intended -50)
1768 if ($glyph == 0) {
1769 $this->defaultWidth = $scale*$aw;
1770 continue;
1771 }
1772 foreach($glyphToChar[$glyph] AS $char) {
1773 //$this->charWidths[$char] = intval(round($scale*$aw));
1774 if ($char != 0 && $char != 65535) {
1775 $w = intval(round($scale*$aw));
1776 if ($w == 0) { $w = 65535; }
1777 if ($char < 196608) {
1778 $this->charWidths[$char*2] = chr($w >> 8);
1779 $this->charWidths[$char*2 + 1] = chr($w & 0xFF);
1780 $nCharWidths++;
1781 }
1782 }
1783 }
1784 }
1785 }
1786 $data = $this->get_chunk(($start+$numberOfHMetrics*4),($numGlyphs*2));
1787 $arr = unpack("n*", $data);
1788 $diff = $numGlyphs-$numberOfHMetrics;
1789 $w = intval(round($scale*$aw));
1790 if ($w == 0) { $w = 65535; }
1791 for( $pos=0; $pos<$diff; $pos++) {
1792 $glyph = $pos + $numberOfHMetrics;
1793 if (isset($glyphToChar[$glyph])) {
1794 foreach($glyphToChar[$glyph] AS $char) {
1795 if ($char != 0 && $char != 65535) {
1796 if ($char < 196608) {
1797 $this->charWidths[$char*2] = chr($w >> 8);
1798 $this->charWidths[$char*2 + 1] = chr($w & 0xFF);
1799 $nCharWidths++;
1800 }
1801 }
1802 }
1803 }
1804 }
1805 // NB 65535 is a set width of 0
1806 // First bytes define number of chars in font
1807 $this->charWidths[0] = chr($nCharWidths >> 8);
1808 $this->charWidths[1] = chr($nCharWidths & 0xFF);
1809 }
1810
1811 function getHMetric($numberOfHMetrics, $gid) {
1812 $start = $this->seek_table("hmtx");
1813 if ($gid < $numberOfHMetrics) {
1814 $this->seek($start+($gid*4));
1815 $hm = fread($this->fh,4);
1816 }
1817 else {
1818 $this->seek($start+(($numberOfHMetrics-1)*4));
1819 $hm = fread($this->fh,2);
1820 $this->seek($start+($numberOfHMetrics*2)+($gid*2));
1821 $hm .= fread($this->fh,2);
1822 }
1823 return $hm;
1824 }
1825
1826 function getLOCA($indexToLocFormat, $numGlyphs) {
1827 $start = $this->seek_table('loca');
1828 $this->glyphPos = array();
1829 if ($indexToLocFormat == 0) {
1830 $data = $this->get_chunk($start,($numGlyphs*2)+2);
1831 $arr = unpack("n*", $data);
1832 for ($n=0; $n<=$numGlyphs; $n++) {
1833 $this->glyphPos[] = ($arr[$n+1] * 2);
1834 }
1835 }
1836 else if ($indexToLocFormat == 1) {
1837 $data = $this->get_chunk($start,($numGlyphs*4)+4);
1838 $arr = unpack("N*", $data);
1839 for ($n=0; $n<=$numGlyphs; $n++) {
1840 $this->glyphPos[] = ($arr[$n+1]);
1841 }
1842 }
1843 else
1844 die('Unknown location table format '.$indexToLocFormat);
1845 }
1846
1847
1848 // CMAP Format 4
1849 function getCMAP4($unicode_cmap_offset, &$glyphToChar, &$charToGlyph ) {
1850 $this->maxUniChar = 0;
1851 $this->seek($unicode_cmap_offset + 2);
1852 $length = $this->read_ushort();
1853 $limit = $unicode_cmap_offset + $length;
1854 $this->skip(2);
1855
1856 $segCount = $this->read_ushort() / 2;
1857 $this->skip(6);
1858 $endCount = array();
1859 for($i=0; $i<$segCount; $i++) { $endCount[] = $this->read_ushort(); }
1860 $this->skip(2);
1861 $startCount = array();
1862 for($i=0; $i<$segCount; $i++) { $startCount[] = $this->read_ushort(); }
1863 $idDelta = array();
1864 for($i=0; $i<$segCount; $i++) { $idDelta[] = $this->read_short(); } // ???? was unsigned short
1865 $idRangeOffset_start = $this->_pos;
1866 $idRangeOffset = array();
1867 for($i=0; $i<$segCount; $i++) { $idRangeOffset[] = $this->read_ushort(); }
1868
1869 for ($n=0;$n<$segCount;$n++) {
1870 $endpoint = ($endCount[$n] + 1);
1871 for ($unichar=$startCount[$n];$unichar<$endpoint;$unichar++) {
1872 if ($idRangeOffset[$n] == 0)
1873 $glyph = ($unichar + $idDelta[$n]) & 0xFFFF;
1874 else {
1875 $offset = ($unichar - $startCount[$n]) * 2 + $idRangeOffset[$n];
1876 $offset = $idRangeOffset_start + 2 * $n + $offset;
1877 if ($offset >= $limit)
1878 $glyph = 0;
1879 else {
1880 $glyph = $this->get_ushort($offset);
1881 if ($glyph != 0)
1882 $glyph = ($glyph + $idDelta[$n]) & 0xFFFF;
1883 }
1884 }
1885 $charToGlyph[$unichar] = $glyph;
1886 if ($unichar < 196608) { $this->maxUniChar = max($unichar,$this->maxUniChar); }
1887 $glyphToChar[$glyph][] = $unichar;
1888 }
1889 }
1890
1891 // mPDF 5.4.05
1892 if ($this->unAGlyphs) {
1893 if (isset($this->tables['post'])) {
1894 $this->seek_table("post");
1895 $formata = $this->read_ushort();
1896 $formatb = $this->read_ushort();
1897 // Only works on Format 2.0
1898 if ($formata != 2 || $formatb != 0) { die("Cannot set unAGlyphs for this font (".$file."). POST table must be in Format 2."); }
1899 $this->skip(28);
1900 $nGlyfs = $this->read_ushort();
1901 $glyphNameIndex = array();
1902 for ($i=0; $i<$nGlyfs; $i++) {
1903 $glyphNameIndex[($this->read_ushort())] = $i;
1904 }
1905
1906 $opost = $this->get_table('post');
1907 $ptr = 34+($nGlyfs*2);
1908 for ($i=0; $i<$nGlyfs; $i++) {
1909 $len = ord(substr($opost,$ptr,1));
1910 $ptr++;
1911 $name = substr($opost,$ptr,$len);
1912 $gid = $glyphNameIndex[$i+258];
1913 // Select uni0600.xxx(x) - uni06FF.xxx(x)
1914 if (preg_match('/^uni(06[0-9a-f]{2})\.(fina|medi|init|fin|med|ini)$/i',$name,$m)) {
1915 if (!isset($glyphToChar[$gid]) || (isset($glyphToChar[$gid]) && is_array($glyphToChar[$gid]) && count($glyphToChar[$gid])==1 && $glyphToChar[$gid][0]>57343 && $glyphToChar[$gid][0]<63489)) { // if set in PUA private use area E000-F8FF, or NOT Unicode mapped
1916 $uni = hexdec($m[1]);
1917 $form = strtoupper(substr($m[2],0,1));
1918 // Assign new PUA Unicode between F500 - F7FF
1919 $bit = $uni & 0xFF;
1920 if ($form == 'I') { $bit += 0xF600; }
1921 else if ($form == 'M') { $bit += 0xF700; }
1922 else { $bit += 0xF500; }
1923 // ADD TO CMAP
1924 $glyphToChar[$gid][] = $bit;
1925 $charToGlyph[$bit] = $gid;
1926 }
1927 }
1928 // LAM with ALEF ligatures (Mandatory ligatures)
1929 else if (preg_match('/^uni064406(22|23|25|27)(\.fina|\.fin){0,1}$/i',$name,$m)) {
1930 if ($m[1]=='22') {
1931 if ($m[2]) { $uni = hexdec('FEF6'); } else { $uni = hexdec('FEF5'); }
1932 }
1933 else if ($m[1]=='23') {
1934 if ($m[2]) { $uni = hexdec('FEF8'); } else { $uni = hexdec('FEF7'); }
1935 }
1936 else if ($m[1]=='25') {
1937 if ($m[2]) { $uni = hexdec('FEFA'); } else { $uni = hexdec('FEF9'); }
1938 }
1939 else if ($m[1]=='27') {
1940 if ($m[2]) { $uni = hexdec('FEFC'); } else { $uni = hexdec('FEFB'); }
1941 }
1942 if (!isset($glyphToChar[$gid]) || (isset($glyphToChar[$gid]) && is_array($glyphToChar[$gid]) && count($glyphToChar[$gid])==1 && $glyphToChar[$gid][0]>57343 && $glyphToChar[$gid][0]<63489)) { // if set in PUA private use area E000-F8FF, or NOT Unicode mapped
1943 // ADD TO CMAP
1944 $glyphToChar[$gid][] = $uni;
1945 $charToGlyph[$uni] = $gid;
1946 }
1947 }
1948 $ptr += $len;
1949 }
1950 }
1951 }
1952
1953 }
1954
1955
1956 // Put the TTF file together
1957 function endTTFile(&$stm) {
1958 $stm = '';
1959 $numTables = count($this->otables);
1960 $searchRange = 1;
1961 $entrySelector = 0;
1962 while ($searchRange * 2 <= $numTables) {
1963 $searchRange = $searchRange * 2;
1964 $entrySelector = $entrySelector + 1;
1965 }
1966 $searchRange = $searchRange * 16;
1967 $rangeShift = $numTables * 16 - $searchRange;
1968
1969 // Header
1970 if (_TTF_MAC_HEADER) {
1971 $stm .= (pack("Nnnnn", 0x74727565, $numTables, $searchRange, $entrySelector, $rangeShift)); // Mac
1972 }
1973 else {
1974 $stm .= (pack("Nnnnn", 0x00010000 , $numTables, $searchRange, $entrySelector, $rangeShift)); // Windows
1975 }
1976
1977 // Table directory
1978 $tables = $this->otables;
1979 ksort ($tables);
1980 $offset = 12 + $numTables * 16;
1981 foreach ($tables AS $tag=>$data) {
1982 if ($tag == 'head') { $head_start = $offset; }
1983 $stm .= $tag;
1984 $checksum = $this->calcChecksum($data);
1985 $stm .= pack("nn", $checksum[0],$checksum[1]);
1986 $stm .= pack("NN", $offset, strlen($data));
1987 $paddedLength = (strlen($data)+3)&~3;
1988 $offset = $offset + $paddedLength;
1989 }
1990
1991 // Table data
1992 foreach ($tables AS $tag=>$data) {
1993 $data .= "\0\0\0";
1994 $stm .= substr($data,0,(strlen($data)&~3));
1995 }
1996
1997 $checksum = $this->calcChecksum($stm);
1998 $checksum = $this->sub32(array(0xB1B0,0xAFBA), $checksum);
1999 $chk = pack("nn", $checksum[0],$checksum[1]);
2000 $stm = $this->splice($stm,($head_start + 8),$chk);
2001 return $stm ;
2002 }
2003
2004
2005 function repackageTTF($file, $TTCfontID=0, $debug=false, $unAGlyphs=false) { // mPDF 5.4.05
2006 $this->unAGlyphs = $unAGlyphs; // mPDF 5.4.05
2007 $this->filename = $file;
2008 $this->fh = fopen($file ,'rb') or die('Can\'t open file ' . $file);
2009 $this->_pos = 0;
2010 $this->charWidths = '';
2011 $this->glyphPos = array();
2012 $this->charToGlyph = array();
2013 $this->tables = array();
2014 $this->otables = array();
2015 $this->ascent = 0;
2016 $this->descent = 0;
2017 $this->numTTCFonts = 0;
2018 $this->TTCFonts = array();
2019 $this->skip(4);
2020 $this->maxUni = 0;
2021 if ($TTCfontID > 0) {
2022 $this->version = $version = $this->read_ulong(); // TTC Header version now
2023 if (!in_array($version, array(0x00010000,0x00020000)))
2024 die("ERROR - Error parsing TrueType Collection: version=".$version." - " . $file);
2025 $this->numTTCFonts = $this->read_ulong();
2026 for ($i=1; $i<=$this->numTTCFonts; $i++) {
2027 $this->TTCFonts[$i]['offset'] = $this->read_ulong();
2028 }
2029 $this->seek($this->TTCFonts[$TTCfontID]['offset']);
2030 $this->version = $version = $this->read_ulong(); // TTFont version again now
2031 }
2032 $this->readTableDirectory($debug);
2033 $tags = array ('OS/2', 'cmap', 'glyf', 'head', 'hhea', 'hmtx', 'loca', 'maxp', 'name', 'post', 'cvt ', 'fpgm', 'gasp', 'prep');
2034/*
2035Tables which require glyphIndex
2036hdmx
2037kern
2038LTSH
2039
2040Tables which do NOT require glyphIndex
2041VDMX
2042
2043GDEF
2044GPOS
2045GSUB
2046JSTF
2047
2048DSIG
2049PCLT - not recommended
2050*/
2051
2052 foreach($tags AS $tag) {
2053 if (isset($this->tables[$tag])) { $this->add($tag, $this->get_table($tag)); }
2054 }
2055 fclose($this->fh);
2056 $stm = '';
2057 $this->endTTFile($stm);
2058 return $stm ;
2059 }
2060
2061
2062}
2063
2064
2065?> \ No newline at end of file
diff --git a/inc/3rdparty/libraries/mpdf/classes/ttfontsuni_analysis.php b/inc/3rdparty/libraries/mpdf/classes/ttfontsuni_analysis.php
new file mode 100644
index 00000000..3f2cccef
--- /dev/null
+++ b/inc/3rdparty/libraries/mpdf/classes/ttfontsuni_analysis.php
@@ -0,0 +1,463 @@
1<?php
2
3require_once(_MPDF_PATH.'classes/ttfontsuni.php');
4
5class TTFontFile_Analysis EXTENDS TTFontFile {
6
7 // Used to get font information from files in directory
8 function extractCoreInfo($file, $TTCfontID=0) {
9 $this->filename = $file;
10 $this->fh = fopen($file,'rb');
11 if (!$this->fh) { return ('ERROR - Can\'t open file ' . $file); }
12 $this->_pos = 0;
13 $this->charWidths = '';
14 $this->glyphPos = array();
15 $this->charToGlyph = array();
16 $this->tables = array();
17 $this->otables = array();
18 $this->ascent = 0;
19 $this->descent = 0;
20 $this->numTTCFonts = 0;
21 $this->TTCFonts = array();
22 $this->version = $version = $this->read_ulong();
23 $this->panose = array(); // mPDF 5.0
24 if ($version==0x4F54544F)
25 return("ERROR - NOT ADDED as Postscript outlines are not supported - " . $file);
26 if ($version==0x74746366) {
27 if ($TTCfontID > 0) {
28 $this->version = $version = $this->read_ulong(); // TTC Header version now
29 if (!in_array($version, array(0x00010000,0x00020000)))
30 return("ERROR - NOT ADDED as Error parsing TrueType Collection: version=".$version." - " . $file);
31 }
32 else return("ERROR - Error parsing TrueType Collection - " . $file);
33 $this->numTTCFonts = $this->read_ulong();
34 for ($i=1; $i<=$this->numTTCFonts; $i++) {
35 $this->TTCFonts[$i]['offset'] = $this->read_ulong();
36 }
37 $this->seek($this->TTCFonts[$TTCfontID]['offset']);
38 $this->version = $version = $this->read_ulong(); // TTFont version again now
39 $this->readTableDirectory(false);
40 }
41 else {
42 if (!in_array($version, array(0x00010000,0x74727565)))
43 return("ERROR - NOT ADDED as Not a TrueType font: version=".$version." - " . $file);
44 $this->readTableDirectory(false);
45 }
46
47/* Included for testing...
48 $cmap_offset = $this->seek_table("cmap");
49 $this->skip(2);
50 $cmapTableCount = $this->read_ushort();
51 $unicode_cmap_offset = 0;
52 for ($i=0;$i<$cmapTableCount;$i++) {
53 $x[$i]['platformId'] = $this->read_ushort();
54 $x[$i]['encodingId'] = $this->read_ushort();
55 $x[$i]['offset'] = $this->read_ulong();
56 $save_pos = $this->_pos;
57 $x[$i]['format'] = $this->get_ushort($cmap_offset + $x[$i]['offset'] );
58 $this->seek($save_pos );
59 }
60 print_r($x); exit;
61*/
62 ///////////////////////////////////
63 // name - Naming table
64 ///////////////////////////////////
65
66/* Test purposes - displays table of names
67 $name_offset = $this->seek_table("name");
68 $format = $this->read_ushort();
69 if ($format != 0 && $format != 1) // mPDF 5.3.73
70 die("Unknown name table format ".$format);
71 $numRecords = $this->read_ushort();
72 $string_data_offset = $name_offset + $this->read_ushort();
73 for ($i=0;$i<$numRecords; $i++) {
74 $x[$i]['platformId'] = $this->read_ushort();
75 $x[$i]['encodingId'] = $this->read_ushort();
76 $x[$i]['languageId'] = $this->read_ushort();
77 $x[$i]['nameId'] = $this->read_ushort();
78 $x[$i]['length'] = $this->read_ushort();
79 $x[$i]['offset'] = $this->read_ushort();
80
81 $N = '';
82 if ($x[$i]['platformId'] == 1 && $x[$i]['encodingId'] == 0 && $x[$i]['languageId'] == 0) { // Roman
83 $opos = $this->_pos;
84 $N = $this->get_chunk($string_data_offset + $x[$i]['offset'] , $x[$i]['length'] );
85 $this->_pos = $opos;
86 $this->seek($opos);
87 }
88 else { // Unicode
89 $opos = $this->_pos;
90 $this->seek($string_data_offset + $x[$i]['offset'] );
91 $length = $x[$i]['length'] ;
92 if ($length % 2 != 0)
93 $length -= 1;
94 // die("PostScript name is UTF-16BE string of odd length");
95 $length /= 2;
96 $N = '';
97 while ($length > 0) {
98 $char = $this->read_ushort();
99 $N .= (chr($char));
100 $length -= 1;
101 }
102 $this->_pos = $opos;
103 $this->seek($opos);
104 }
105 $x[$i]['names'][$nameId] = $N;
106 }
107 print_r($x); exit;
108*/
109
110 $name_offset = $this->seek_table("name");
111 $format = $this->read_ushort();
112 if ($format != 0 && $format != 1) // mPDF 5.3.73
113 return("ERROR - NOT ADDED as Unknown name table format ".$format." - " . $file);
114 $numRecords = $this->read_ushort();
115 $string_data_offset = $name_offset + $this->read_ushort();
116 $names = array(1=>'',2=>'',3=>'',4=>'',6=>'');
117 $K = array_keys($names);
118 $nameCount = count($names);
119 for ($i=0;$i<$numRecords; $i++) {
120 $platformId = $this->read_ushort();
121 $encodingId = $this->read_ushort();
122 $languageId = $this->read_ushort();
123 $nameId = $this->read_ushort();
124 $length = $this->read_ushort();
125 $offset = $this->read_ushort();
126 if (!in_array($nameId,$K)) continue;
127 $N = '';
128 if ($platformId == 3 && $encodingId == 1 && $languageId == 0x409) { // Microsoft, Unicode, US English, PS Name
129 $opos = $this->_pos;
130 $this->seek($string_data_offset + $offset);
131 if ($length % 2 != 0)
132 $length += 1;
133 $length /= 2;
134 $N = '';
135 while ($length > 0) {
136 $char = $this->read_ushort();
137 $N .= (chr($char));
138 $length -= 1;
139 }
140 $this->_pos = $opos;
141 $this->seek($opos);
142 }
143 else if ($platformId == 1 && $encodingId == 0 && $languageId == 0) { // Macintosh, Roman, English, PS Name
144 $opos = $this->_pos;
145 $N = $this->get_chunk($string_data_offset + $offset, $length);
146 $this->_pos = $opos;
147 $this->seek($opos);
148 }
149 if ($N && $names[$nameId]=='') {
150 $names[$nameId] = $N;
151 $nameCount -= 1;
152 if ($nameCount==0) break;
153 }
154 }
155 if ($names[6])
156 $psName = preg_replace('/ /','-',$names[6]);
157 else if ($names[4])
158 $psName = preg_replace('/ /','-',$names[4]);
159 else if ($names[1])
160 $psName = preg_replace('/ /','-',$names[1]);
161 else
162 $psName = '';
163 if (!$names[1] && !$psName)
164 return("ERROR - NOT ADDED as Could not find valid font name - " . $file);
165 $this->name = $psName;
166 if ($names[1]) { $this->familyName = $names[1]; } else { $this->familyName = $psName; }
167 if ($names[2]) { $this->styleName = $names[2]; } else { $this->styleName = 'Regular'; }
168
169 ///////////////////////////////////
170 // head - Font header table
171 ///////////////////////////////////
172 $this->seek_table("head");
173 $ver_maj = $this->read_ushort();
174 $ver_min = $this->read_ushort();
175 if ($ver_maj != 1)
176 return('ERROR - NOT ADDED as Unknown head table version '. $ver_maj .'.'. $ver_min." - " . $file);
177 $this->fontRevision = $this->read_ushort() . $this->read_ushort();
178 $this->skip(4);
179 $magic = $this->read_ulong();
180 if ($magic != 0x5F0F3CF5)
181 return('ERROR - NOT ADDED as Invalid head table magic ' .$magic." - " . $file);
182 $this->skip(2);
183 $this->unitsPerEm = $unitsPerEm = $this->read_ushort();
184 $scale = 1000 / $unitsPerEm;
185 $this->skip(24);
186 $macStyle = $this->read_short();
187 $this->skip(4);
188 $indexLocFormat = $this->read_short();
189
190 ///////////////////////////////////
191 // OS/2 - OS/2 and Windows metrics table
192 ///////////////////////////////////
193 $sFamily = '';
194 $panose = '';
195 $fsSelection = '';
196 if (isset($this->tables["OS/2"])) {
197 $this->seek_table("OS/2");
198 $this->skip(30);
199 $sF = $this->read_short();
200 $sFamily = ($sF >> 8);
201 $this->_pos += 10; //PANOSE = 10 byte length
202 $panose = fread($this->fh,10);
203 $this->panose = array();
204 for ($p=0;$p<strlen($panose);$p++) { $this->panose[] = ord($panose[$p]); }
205 $this->skip(20);
206 $fsSelection = $this->read_short();
207 }
208
209 ///////////////////////////////////
210 // post - PostScript table
211 ///////////////////////////////////
212 $this->seek_table("post");
213 $this->skip(4);
214 $this->italicAngle = $this->read_short() + $this->read_ushort() / 65536.0;
215 $this->skip(4);
216 $isFixedPitch = $this->read_ulong();
217
218
219
220 ///////////////////////////////////
221 // cmap - Character to glyph index mapping table
222 ///////////////////////////////////
223 $cmap_offset = $this->seek_table("cmap");
224 $this->skip(2);
225 $cmapTableCount = $this->read_ushort();
226 $unicode_cmap_offset = 0;
227 for ($i=0;$i<$cmapTableCount;$i++) {
228 $platformID = $this->read_ushort();
229 $encodingID = $this->read_ushort();
230 $offset = $this->read_ulong();
231 $save_pos = $this->_pos;
232 if (($platformID == 3 && $encodingID == 1) || $platformID == 0) { // Microsoft, Unicode
233 $format = $this->get_ushort($cmap_offset + $offset);
234 if ($format == 4) {
235 if (!$unicode_cmap_offset) $unicode_cmap_offset = $cmap_offset + $offset;
236 }
237 }
238 else if ((($platformID == 3 && $encodingID == 10) || $platformID == 0)) { // Microsoft, Unicode Format 12 table HKCS
239 $format = $this->get_ushort($cmap_offset + $offset);
240 if ($format == 12) {
241 $unicode_cmap_offset = $cmap_offset + $offset;
242 break;
243 }
244 }
245 $this->seek($save_pos );
246 }
247
248 if (!$unicode_cmap_offset)
249 return('ERROR - Font ('.$this->filename .') NOT ADDED as it is not Unicode encoded, and cannot be used by mPDF');
250
251 $rtl = false;
252 $indic = false;
253 $cjk = false;
254 $sip = false;
255 $smp = false;
256 $pua = false;
257 $puaag = false;
258 $glyphToChar = array();
259 $unAGlyphs = '';
260 // Format 12 CMAP does characters above Unicode BMP i.e. some HKCS characters U+20000 and above
261 if ($format == 12) {
262 $this->seek($unicode_cmap_offset + 4);
263 $length = $this->read_ulong();
264 $limit = $unicode_cmap_offset + $length;
265 $this->skip(4);
266 $nGroups = $this->read_ulong();
267 for($i=0; $i<$nGroups ; $i++) {
268 $startCharCode = $this->read_ulong();
269 $endCharCode = $this->read_ulong();
270 $startGlyphCode = $this->read_ulong();
271 if (($endCharCode > 0x20000 && $endCharCode < 0x2A6DF) || ($endCharCode > 0x2F800 && $endCharCode < 0x2FA1F)) {
272 $sip = true;
273 }
274 if ($endCharCode > 0x10000 && $endCharCode < 0x1FFFF) {
275 $smp = true;
276 }
277 if (($endCharCode > 0x0590 && $endCharCode < 0x077F) || ($endCharCode > 0xFE70 && $endCharCode < 0xFEFF) || ($endCharCode > 0xFB50 && $endCharCode < 0xFDFF)) {
278 $rtl = true;
279 }
280 if ($endCharCode > 0x0900 && $endCharCode < 0x0DFF) {
281 $indic = true;
282 }
283 if ($endCharCode > 0xE000 && $endCharCode < 0xF8FF) {
284 $pua = true;
285 if ($endCharCode > 0xF500 && $endCharCode < 0xF7FF) {
286 $puaag = true;
287 }
288 }
289 if (($endCharCode > 0x2E80 && $endCharCode < 0x4DC0) || ($endCharCode > 0x4E00 && $endCharCode < 0xA4CF) || ($endCharCode > 0xAC00 && $endCharCode < 0xD7AF) || ($endCharCode > 0xF900 && $endCharCode < 0xFAFF) || ($endCharCode > 0xFE30 && $endCharCode < 0xFE4F)) {
290 $cjk = true;
291 }
292
293 $offset = 0;
294 // Get each glyphToChar - only point if going to analyse un-mapped Arabic Glyphs
295 if (isset($this->tables['post'])) {
296 for ($unichar=$startCharCode;$unichar<=$endCharCode;$unichar++) {
297 $glyph = $startGlyphCode + $offset ;
298 $offset++;
299 $glyphToChar[$glyph][] = $unichar;
300 }
301 }
302
303
304 }
305 }
306
307 else { // Format 4 CMap
308 $this->seek($unicode_cmap_offset + 2);
309 $length = $this->read_ushort();
310 $limit = $unicode_cmap_offset + $length;
311 $this->skip(2);
312
313 $segCount = $this->read_ushort() / 2;
314 $this->skip(6);
315 $endCount = array();
316 for($i=0; $i<$segCount; $i++) { $endCount[] = $this->read_ushort(); }
317 $this->skip(2);
318 $startCount = array();
319 for($i=0; $i<$segCount; $i++) { $startCount[] = $this->read_ushort(); }
320 $idDelta = array();
321 for($i=0; $i<$segCount; $i++) { $idDelta[] = $this->read_short(); }
322 $idRangeOffset_start = $this->_pos;
323 $idRangeOffset = array();
324 for($i=0; $i<$segCount; $i++) { $idRangeOffset[] = $this->read_ushort(); }
325
326 for ($n=0;$n<$segCount;$n++) {
327 if (($endCount[$n] > 0x0590 && $endCount[$n] < 0x077F) || ($endCount[$n] > 0xFE70 && $endCount[$n] < 0xFEFF) || ($endCount[$n] > 0xFB50 && $endCount[$n] < 0xFDFF)) {
328 $rtl = true;
329 }
330 if ($endCount[$n] > 0x0900 && $endCount[$n] < 0x0DFF) {
331 $indic = true;
332 }
333 if (($endCount[$n] > 0x2E80 && $endCount[$n] < 0x4DC0) || ($endCount[$n] > 0x4E00 && $endCount[$n] < 0xA4CF) || ($endCount[$n] > 0xAC00 && $endCount[$n] < 0xD7AF) || ($endCount[$n] > 0xF900 && $endCount[$n] < 0xFAFF) || ($endCount[$n] > 0xFE30 && $endCount[$n] < 0xFE4F)) {
334 $cjk = true;
335 }
336 if ($endCount[$n] > 0xE000 && $endCount[$n] < 0xF8FF) {
337 $pua = true;
338 if ($endCount[$n] > 0xF500 && $endCount[$n] < 0xF7FF) {
339 $puaag = true;
340 }
341 }
342 // Get each glyphToChar - only point if going to analyse un-mapped Arabic Glyphs
343 if (isset($this->tables['post'])) {
344 $endpoint = ($endCount[$n] + 1);
345 for ($unichar=$startCount[$n];$unichar<$endpoint;$unichar++) {
346 if ($idRangeOffset[$n] == 0)
347 $glyph = ($unichar + $idDelta[$n]) & 0xFFFF;
348 else {
349 $offset = ($unichar - $startCount[$n]) * 2 + $idRangeOffset[$n];
350 $offset = $idRangeOffset_start + 2 * $n + $offset;
351 if ($offset >= $limit)
352 $glyph = 0;
353 else {
354 $glyph = $this->get_ushort($offset);
355 if ($glyph != 0)
356 $glyph = ($glyph + $idDelta[$n]) & 0xFFFF;
357 }
358 }
359 $glyphToChar[$glyph][] = $unichar;
360 }
361 }
362
363 }
364 }
365 // 'POST' table for un-mapped arabic glyphs
366 if (isset($this->tables['post'])) {
367 $this->seek_table("post");
368 // Only works on Format 2.0
369 $formata = $this->read_ushort();
370 $formatb = $this->read_ushort();
371 if ($formata == 2 && $formatb == 0) {
372 $this->skip(28);
373 $nGlyfs = $this->read_ushort();
374 $glyphNameIndex = array();
375 for ($i=0; $i<$nGlyfs; $i++) {
376 $glyphNameIndex[($this->read_ushort())] = $i;
377 }
378
379 $opost = $this->get_table('post');
380 $ptr = 34+($nGlyfs*2);
381 for ($i=0; $i<$nGlyfs; $i++) {
382 $len = ord(substr($opost,$ptr,1));
383 $ptr++;
384 $name = substr($opost,$ptr,$len);
385 $gid = $glyphNameIndex[$i+258];
386 // Select uni0600.xxx(x) - uni06FF.xxx(x)
387 if (preg_match('/^uni(06[0-9a-f]{2})\.(fina|medi|init|fin|med|ini)$/i',$name,$m)) {
388 if (!isset($glyphToChar[$gid]) || (isset($glyphToChar[$gid]) && is_array($glyphToChar[$gid]) && count($glyphToChar[$gid])==1 && $glyphToChar[$gid][0]>57343 && $glyphToChar[$gid][0]<63489)) { // if set in PUA private use area E000-F8FF, or NOT Unicode mapped
389 $uni = hexdec($m[1]);
390 $form = strtoupper(substr($m[2],0,1));
391 // Assign new PUA Unicode between F500 - F7FF
392 $bit = $uni & 0xFF;
393 if ($form == 'I') { $bit += 0xF600; }
394 else if ($form == 'M') { $bit += 0xF700; }
395 else { $bit += 0xF500; }
396 $unAGlyphs .= $gid;
397 $name = 'uni'.strtoupper($m[1]).'.'.strtolower($m[2]);
398 $unAGlyphs .= ' : '.$name;
399 $unihexstr = $m[1];
400 $unAGlyphs .= ' : '.$unihexstr;
401 $unAGlyphs .= ' : '.$uni;
402 $unAGlyphs .= ' : '.$form;
403 // if already set in PUA private use area E000-F8FF
404 if (isset($glyphToChar[$gid]) && $glyphToChar[$gid][0]>57343 && $glyphToChar[$gid][0]<63489) {
405 $unAGlyphs .= ' : '.$glyphToChar[$gid][0].' {'.dechex($glyphToChar[$gid][0]).'}';
406 }
407 //else $unAGlyphs .= ':';
408 $unAGlyphs .= ' : '.strtoupper(dechex($bit));
409 $unAGlyphs .= '<br />';
410 }
411 }
412 $ptr += $len;
413 }
414 if ($unAGlyphs) {
415 $unAGlyphs = 'GID:Name:Unicode base Hex:Dec:Form:PUA Unicode<br />'.$unAGlyphs ;
416 }
417 }
418 }
419
420
421
422 $bold = false;
423 $italic = false;
424 $ftype = '';
425 if ($macStyle & (1 << 0)) { $bold = true; } // bit 0 bold
426 else if ($fsSelection & (1 << 5)) { $bold = true; } // 5 BOLD Characters are emboldened
427
428 if ($macStyle & (1 << 1)) { $italic = true; } // bit 1 italic
429 else if ($fsSelection & (1 << 0)) { $italic = true; } // 0 ITALIC Font contains Italic characters, otherwise they are upright
430 else if ($this->italicAngle <> 0) { $italic = true; }
431
432 if ($isFixedPitch ) { $ftype = 'mono'; }
433 else if ($sFamily >0 && $sFamily <8) { $ftype = 'serif'; }
434 else if ($sFamily ==8) { $ftype = 'sans'; }
435 else if ($sFamily ==10) { $ftype = 'cursive'; }
436 // Use PANOSE
437 if ($panose) {
438 $bFamilyType=ord($panose[0]);
439 if ($bFamilyType==2) {
440 $bSerifStyle=ord($panose[1]);
441 if (!$ftype) {
442 if ($bSerifStyle>1 && $bSerifStyle<11) { $ftype = 'serif'; }
443 else if ($bSerifStyle>10) { $ftype = 'sans'; }
444 }
445 $bProportion=ord($panose[3]);
446 if ($bProportion==9 || $bProportion==1) { $ftype = 'mono'; } // ==1 i.e. No Fit needed for OCR-a and -b
447 }
448 else if ($bFamilyType==3) {
449 $ftype = 'cursive';
450 }
451 }
452
453 fclose($this->fh);
454 return array($this->familyName, $bold, $italic, $ftype, $TTCfontID, $rtl, $indic, $cjk, $sip, $smp, $puaag, $pua, $unAGlyphs);
455 }
456
457
458
459
460}
461
462
463?> \ No newline at end of file
diff --git a/inc/3rdparty/libraries/mpdf/classes/wmf.php b/inc/3rdparty/libraries/mpdf/classes/wmf.php
new file mode 100644
index 00000000..2efef00b
--- /dev/null
+++ b/inc/3rdparty/libraries/mpdf/classes/wmf.php
@@ -0,0 +1,236 @@
1<?php
2
3class wmf {
4
5var $mpdf = null;
6var $gdiObjectArray;
7
8function wmf(&$mpdf) {
9 $this->mpdf = $mpdf;
10}
11
12
13function _getWMFimage($data) {
14 $k = _MPDFK;
15
16 $this->gdiObjectArray = array();
17 $a=unpack('stest',"\1\0");
18 if ($a['test']!=1)
19 return array(0, 'Error parsing WMF image - Big-endian architecture not supported');
20 // check for Aldus placeable metafile header
21 $key = unpack('Lmagic', substr($data, 0, 4));
22 $p = 18; // WMF header
23 if ($key['magic'] == (int)0x9AC6CDD7) { $p +=22; } // Aldus header
24 // define some state variables
25 $wo=null; // window origin
26 $we=null; // window extent
27 $polyFillMode = 0;
28 $nullPen = false;
29 $nullBrush = false;
30 $endRecord = false;
31 $wmfdata = '';
32 while ($p < strlen($data) && !$endRecord) {
33 $recordInfo = unpack('Lsize/Sfunc', substr($data, $p, 6)); $p += 6;
34 // size of record given in WORDs (= 2 bytes)
35 $size = $recordInfo['size'];
36 // func is number of GDI function
37 $func = $recordInfo['func'];
38 if ($size > 3) {
39 $parms = substr($data, $p, 2*($size-3)); $p += 2*($size-3);
40 }
41 switch ($func) {
42 case 0x020b: // SetWindowOrg
43 // do not allow window origin to be changed
44 // after drawing has begun
45 if (!$wmfdata)
46 $wo = array_reverse(unpack('s2', $parms));
47 break;
48 case 0x020c: // SetWindowExt
49 // do not allow window extent to be changed
50 // after drawing has begun
51 if (!$wmfdata)
52 $we = array_reverse(unpack('s2', $parms));
53 break;
54 case 0x02fc: // CreateBrushIndirect
55 $brush = unpack('sstyle/Cr/Cg/Cb/Ca/Shatch', $parms);
56 $brush['type'] = 'B';
57 $this->_AddGDIObject($brush);
58 break;
59 case 0x02fa: // CreatePenIndirect
60 $pen = unpack('Sstyle/swidth/sdummy/Cr/Cg/Cb/Ca', $parms);
61 // convert width from twips to user unit
62 $pen['width'] /= (20 * $k);
63 $pen['type'] = 'P';
64 $this->_AddGDIObject($pen);
65 break;
66
67 // MUST create other GDI objects even if we don't handle them
68 case 0x06fe: // CreateBitmap
69 case 0x02fd: // CreateBitmapIndirect
70 case 0x00f8: // CreateBrush
71 case 0x02fb: // CreateFontIndirect
72 case 0x00f7: // CreatePalette
73 case 0x01f9: // CreatePatternBrush
74 case 0x06ff: // CreateRegion
75 case 0x0142: // DibCreatePatternBrush
76 $dummyObject = array('type'=>'D');
77 $this->_AddGDIObject($dummyObject);
78 break;
79 case 0x0106: // SetPolyFillMode
80 $polyFillMode = unpack('smode', $parms);
81 $polyFillMode = $polyFillMode['mode'];
82 break;
83 case 0x01f0: // DeleteObject
84 $idx = unpack('Sidx', $parms);
85 $idx = $idx['idx'];
86 $this->_DeleteGDIObject($idx);
87 break;
88 case 0x012d: // SelectObject
89 $idx = unpack('Sidx', $parms);
90 $idx = $idx['idx'];
91 $obj = $this->_GetGDIObject($idx);
92 switch ($obj['type']) {
93 case 'B':
94 $nullBrush = false;
95 if ($obj['style'] == 1) { $nullBrush = true; }
96 else {
97 $wmfdata .= $this->mpdf->SetFColor($this->mpdf->ConvertColor('rgb('.$obj['r'].','.$obj['g'].','.$obj['b'].')'), true)."\n";
98 }
99 break;
100 case 'P':
101 $nullPen = false;
102 $dashArray = array();
103 // dash parameters are custom
104 switch ($obj['style']) {
105 case 0: // PS_SOLID
106 break;
107 case 1: // PS_DASH
108 $dashArray = array(3,1);
109 break;
110 case 2: // PS_DOT
111 $dashArray = array(0.5,0.5);
112 break;
113 case 3: // PS_DASHDOT
114 $dashArray = array(2,1,0.5,1);
115 break;
116 case 4: // PS_DASHDOTDOT
117 $dashArray = array(2,1,0.5,1,0.5,1);
118 break;
119 case 5: // PS_NULL
120 $nullPen = true;
121 break;
122 }
123 if (!$nullPen) {
124 $wmfdata .= $this->mpdf->SetDColor($this->mpdf->ConvertColor('rgb('.$obj['r'].','.$obj['g'].','.$obj['b'].')'), true)."\n";
125 $wmfdata .= sprintf("%.3F w\n",$obj['width']*$k);
126 }
127 if (!empty($dashArray)) {
128 $s = '[';
129 for ($i=0; $i<count($dashArray);$i++) {
130 $s .= $dashArray[$i] * $k;
131 if ($i != count($dashArray)-1) { $s .= ' '; }
132 }
133 $s .= '] 0 d';
134 $wmfdata .= $s."\n";
135 }
136 break;
137 }
138 break;
139 case 0x0325: // Polyline
140 case 0x0324: // Polygon
141 $coords = unpack('s'.($size-3), $parms);
142 $numpoints = $coords[1];
143 for ($i = $numpoints; $i > 0; $i--) {
144 $px = $coords[2*$i];
145 $py = $coords[2*$i+1];
146
147 if ($i < $numpoints) { $wmfdata .= $this->_LineTo($px, $py); }
148 else { $wmfdata .= $this->_MoveTo($px, $py); }
149 }
150 if ($func == 0x0325) { $op = 's'; }
151 else if ($func == 0x0324) {
152 if ($nullPen) {
153 if ($nullBrush) { $op = 'n'; } // no op
154 else { $op = 'f'; } // fill
155 }
156 else {
157 if ($nullBrush) { $op = 's'; } // stroke
158 else { $op = 'b'; } // stroke and fill
159 }
160 if ($polyFillMode==1 && ($op=='b' || $op=='f')) { $op .= '*'; } // use even-odd fill rule
161 }
162 $wmfdata .= $op."\n";
163 break;
164 case 0x0538: // PolyPolygon
165 $coords = unpack('s'.($size-3), $parms);
166 $numpolygons = $coords[1];
167 $adjustment = $numpolygons;
168 for ($j = 1; $j <= $numpolygons; $j++) {
169 $numpoints = $coords[$j + 1];
170 for ($i = $numpoints; $i > 0; $i--) {
171 $px = $coords[2*$i + $adjustment];
172 $py = $coords[2*$i+1 + $adjustment];
173 if ($i == $numpoints) { $wmfdata .= $this->_MoveTo($px, $py); }
174 else { $wmfdata .= $this->_LineTo($px, $py); }
175 }
176 $adjustment += $numpoints * 2;
177 }
178
179 if ($nullPen) {
180 if ($nullBrush) { $op = 'n'; } // no op
181 else { $op = 'f'; } // fill
182 }
183 else {
184 if ($nullBrush) { $op = 's'; } // stroke
185 else { $op = 'b'; } // stroke and fill
186 }
187 if ($polyFillMode==1 && ($op=='b' || $op=='f')) { $op .= '*'; } // use even-odd fill rule
188 $wmfdata .= $op."\n";
189 break;
190 case 0x0000:
191 $endRecord = true;
192 break;
193 }
194 }
195
196
197 return array(1,$wmfdata,$wo,$we);
198}
199
200
201function _MoveTo($x, $y) {
202 return "$x $y m\n";
203}
204
205// a line must have been started using _MoveTo() first
206function _LineTo($x, $y) {
207 return "$x $y l\n";
208}
209
210function _AddGDIObject($obj) {
211 // find next available slot
212 $idx = 0;
213 if (!empty($this->gdiObjectArray)) {
214 $empty = false;
215 $i = 0;
216 while (!$empty) {
217 $empty = !isset($this->gdiObjectArray[$i]);
218 $i++;
219 }
220 $idx = $i-1;
221 }
222 $this->gdiObjectArray[$idx] = $obj;
223}
224
225function _GetGDIObject($idx) {
226 return $this->gdiObjectArray[$idx];
227}
228
229function _DeleteGDIObject($idx) {
230 unset($this->gdiObjectArray[$idx]);
231}
232
233
234}
235
236?> \ No newline at end of file