Test Failed
Push — main ( c8394f...8477f1 )
by Rafael
66:21
created

TCPDF_STATIC   F

Complexity

Total Complexity 442

Size/Duplication

Total Lines 2649
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 1366
dl 0
loc 2649
rs 0.8
c 0
b 0
f 0
wmc 442

How to fix   Complexity   

Complex Class

Complex classes like TCPDF_STATIC often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use TCPDF_STATIC, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
//============================================================+
4
// File name   : tcpdf_static.php
5
// Version     : 1.1.4
6
// Begin       : 2002-08-03
7
// Last Update : 2023-09-06
8
// Author      : Nicola Asuni - Tecnick.com LTD - www.tecnick.com - [email protected]
9
// License     : GNU-LGPL v3 (http://www.gnu.org/copyleft/lesser.html)
10
// -------------------------------------------------------------------
11
// Copyright (C) 2002-2023 Nicola Asuni - Tecnick.com LTD
12
//
13
// This file is part of TCPDF software library.
14
//
15
// TCPDF is free software: you can redistribute it and/or modify it
16
// under the terms of the GNU Lesser General Public License as
17
// published by the Free Software Foundation, either version 3 of the
18
// License, or (at your option) any later version.
19
//
20
// TCPDF is distributed in the hope that it will be useful, but
21
// WITHOUT ANY WARRANTY; without even the implied warranty of
22
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
23
// See the GNU Lesser General Public License for more details.
24
//
25
// You should have received a copy of the License
26
// along with TCPDF. If not, see
27
// <http://www.tecnick.com/pagefiles/tcpdf/LICENSE.TXT>.
28
//
29
// See LICENSE.TXT file for more information.
30
// -------------------------------------------------------------------
31
//
32
// Description :
33
//   Static methods used by the TCPDF class.
34
//
35
//============================================================+
36
37
/**
38
 * @file
39
 * This is a PHP class that contains static methods for the TCPDF class.<br>
40
 * @package com.tecnick.tcpdf
41
 * @author Nicola Asuni
42
 * @version 1.1.2
43
 */
44
45
/**
46
 * @class TCPDF_STATIC
47
 * Static methods used by the TCPDF class.
48
 * @package com.tecnick.tcpdf
49
 * @brief PHP class for generating PDF documents without requiring external extensions.
50
 * @version 1.1.1
51
 * @author Nicola Asuni - [email protected]
52
 */
53
class TCPDF_STATIC
54
{
55
    /**
56
     * Current TCPDF version.
57
     * @private static
58
     */
59
    private static $tcpdf_version = '6.7.5';
60
61
    /**
62
     * String alias for total number of pages.
63
     * @public static
64
     */
65
    public static $alias_tot_pages = '{:ptp:}';
66
67
    /**
68
     * String alias for page number.
69
     * @public static
70
     */
71
    public static $alias_num_page = '{:pnp:}';
72
73
    /**
74
     * String alias for total number of pages in a single group.
75
     * @public static
76
     */
77
    public static $alias_group_tot_pages = '{:ptg:}';
78
79
    /**
80
     * String alias for group page number.
81
     * @public static
82
     */
83
    public static $alias_group_num_page = '{:png:}';
84
85
    /**
86
     * String alias for right shift compensation used to correctly align page numbers on the right.
87
     * @public static
88
     */
89
    public static $alias_right_shift = '{rsc:';
90
91
    /**
92
     * Encryption padding string.
93
     * @public static
94
     */
95
    public static $enc_padding = "\x28\xBF\x4E\x5E\x4E\x75\x8A\x41\x64\x00\x4E\x56\xFF\xFA\x01\x08\x2E\x2E\x00\xB6\xD0\x68\x3E\x80\x2F\x0C\xA9\xFE\x64\x53\x69\x7A";
96
97
    /**
98
     * ByteRange placemark used during digital signature process.
99
     * @since 4.6.028 (2009-08-25)
100
     * @public static
101
     */
102
    public static $byterange_string = '/ByteRange[0 ********** ********** **********]';
103
104
    /**
105
     * Array page boxes names
106
     * @public static
107
     */
108
    public static $pageboxes = array('MediaBox', 'CropBox', 'BleedBox', 'TrimBox', 'ArtBox');
109
110
    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
111
112
    /**
113
     * Return the current TCPDF version.
114
     * @return string TCPDF version string
115
     * @since 5.9.012 (2010-11-10)
116
     * @public static
117
     */
118
    public static function getTCPDFVersion()
119
    {
120
        return self::$tcpdf_version;
121
    }
122
123
    /**
124
     * Return the current TCPDF producer.
125
     * @return string TCPDF producer string
126
     * @since 6.0.000 (2013-03-16)
127
     * @public static
128
     */
129
    public static function getTCPDFProducer()
130
    {
131
        return "\x54\x43\x50\x44\x46\x20" . self::getTCPDFVersion() . "\x20\x28\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x74\x63\x70\x64\x66\x2e\x6f\x72\x67\x29";
132
    }
133
134
    /**
135
     * Check if the URL exist.
136
     * @param string $url URL to check.
137
     * @return boolean true if the URl exist, false otherwise.
138
     * @since 5.9.204 (2013-01-28)
139
     * @public static
140
     */
141
    public static function isValidURL($url)
142
    {
143
        $headers = @get_headers($url);
144
        if ($headers === false) {
145
            return false;
146
        }
147
        return (strpos($headers[0], '200') !== false);
148
    }
149
150
    /**
151
     * Removes SHY characters from text.
152
     * Unicode Data:<ul>
153
     * <li>Name : SOFT HYPHEN, commonly abbreviated as SHY</li>
154
     * <li>HTML Entity (decimal): "&amp;#173;"</li>
155
     * <li>HTML Entity (hex): "&amp;#xad;"</li>
156
     * <li>HTML Entity (named): "&amp;shy;"</li>
157
     * <li>How to type in Microsoft Windows: [Alt +00AD] or [Alt 0173]</li>
158
     * <li>UTF-8 (hex): 0xC2 0xAD (c2ad)</li>
159
     * <li>UTF-8 character: chr(194).chr(173)</li>
160
     * </ul>
161
     * @param string $txt input string
162
     * @param boolean $unicode True if we are in unicode mode, false otherwise.
163
     * @return string without SHY characters.
164
     * @since (4.5.019) 2009-02-28
165
     * @public static
166
     */
167
    public static function removeSHY($txt = '', $unicode = true)
168
    {
169
        $txt = preg_replace('/([\\xc2]{1}[\\xad]{1})/', '', $txt);
170
        if (!$unicode) {
171
            $txt = preg_replace('/([\\xad]{1})/', '', $txt);
172
        }
173
        return $txt;
174
    }
175
176
177
    /**
178
     * Get the border mode accounting for multicell position (opens bottom side of multicell crossing pages)
179
     * @param string|array|int $brd Indicates if borders must be drawn around the cell block. The value can be a number:<ul><li>0: no border (default)</li><li>1: frame</li></ul>or a string containing some or all of the following characters (in any order):<ul><li>L: left</li><li>T: top</li><li>R: right</li><li>B: bottom</li></ul> or an array of line styles for each border group: array('LTRB' => array('width' => 2, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0)))
180
     * @param string $position multicell position: 'start', 'middle', 'end'
181
     * @param boolean $opencell True when the cell is left open at the page bottom, false otherwise.
182
     * @return array border mode array
183
     * @since 4.4.002 (2008-12-09)
184
     * @public static
185
     */
186
    public static function getBorderMode($brd, $position = 'start', $opencell = true)
187
    {
188
        if ((!$opencell) or empty($brd)) {
189
            return $brd;
190
        }
191
        if ($brd == 1) {
192
            $brd = 'LTRB';
193
        }
194
        if (is_string($brd)) {
195
            // convert string to array
196
            $slen = strlen($brd);
197
            $newbrd = array();
198
            for ($i = 0; $i < $slen; ++$i) {
199
                $newbrd[$brd[$i]] = array('cap' => 'square', 'join' => 'miter');
200
            }
201
            $brd = $newbrd;
202
        }
203
        foreach ($brd as $border => $style) {
204
            switch ($position) {
205
                case 'start': {
206
                    if (strpos($border, 'B') !== false) {
207
                        // remove bottom line
208
                        $newkey = str_replace('B', '', $border);
209
                        if (strlen($newkey) > 0) {
210
                            $brd[$newkey] = $style;
211
                        }
212
                        unset($brd[$border]);
213
                    }
214
                    break;
215
                }
216
                case 'middle': {
217
                    if (strpos($border, 'B') !== false) {
218
                        // remove bottom line
219
                        $newkey = str_replace('B', '', $border);
220
                        if (strlen($newkey) > 0) {
221
                            $brd[$newkey] = $style;
222
                        }
223
                        unset($brd[$border]);
224
                        $border = $newkey;
225
                    }
226
                    if (strpos($border, 'T') !== false) {
227
                        // remove bottom line
228
                        $newkey = str_replace('T', '', $border);
229
                        if (strlen($newkey) > 0) {
230
                            $brd[$newkey] = $style;
231
                        }
232
                        unset($brd[$border]);
233
                    }
234
                    break;
235
                }
236
                case 'end': {
237
                    if (strpos($border, 'T') !== false) {
238
                        // remove bottom line
239
                        $newkey = str_replace('T', '', $border);
240
                        if (strlen($newkey) > 0) {
241
                            $brd[$newkey] = $style;
242
                        }
243
                        unset($brd[$border]);
244
                    }
245
                    break;
246
                }
247
            }
248
        }
249
        return $brd;
250
    }
251
252
    /**
253
     * Determine whether a string is empty.
254
     * @param string $str string to be checked
255
     * @return bool true if string is empty
256
     * @since 4.5.044 (2009-04-16)
257
     * @public static
258
     */
259
    public static function empty_string($str)
260
    {
261
        return (is_null($str) or (is_string($str) and (strlen($str) == 0)));
262
    }
263
264
    /**
265
     * Returns a temporary filename for caching object on filesystem.
266
     * @param string $type Type of file (name of the subdir on the tcpdf cache folder).
267
     * @param string $file_id TCPDF file_id.
268
     * @return string filename.
269
     * @since 4.5.000 (2008-12-31)
270
     * @public static
271
     */
272
    public static function getObjFilename($type = 'tmp', $file_id = '')
273
    {
274
        return tempnam(K_PATH_CACHE, '__tcpdf_' . $file_id . '_' . $type . '_' . md5(TCPDF_STATIC::getRandomSeed()) . '_');
275
    }
276
277
    /**
278
     * Add "\" before "\", "(" and ")"
279
     * @param string $s string to escape.
280
     * @return string escaped string.
281
     * @public static
282
     */
283
    public static function _escape($s)
284
    {
285
        // the chr(13) substitution fixes the Bugs item #1421290.
286
        return strtr($s, array(')' => '\\)', '(' => '\\(', '\\' => '\\\\', chr(13) => '\r'));
287
    }
288
289
    /**
290
    * Escape some special characters (&lt; &gt; &amp;) for XML output.
291
    * @param string $str Input string to convert.
292
    * @return string converted string
293
    * @since 5.9.121 (2011-09-28)
294
     * @public static
295
     */
296
    public static function _escapeXML($str)
297
    {
298
        $replaceTable = array("\0" => '', '&' => '&amp;', '<' => '&lt;', '>' => '&gt;');
299
        $str = strtr($str === null ? '' : $str, $replaceTable);
300
        return $str;
301
    }
302
303
    /**
304
     * Creates a copy of a class object
305
     * @param object $object class object to be cloned
306
     * @return object cloned object
307
     * @since 4.5.029 (2009-03-19)
308
     * @public static
309
     */
310
    public static function objclone($object)
311
    {
312
        if (($object instanceof Imagick) and (version_compare(phpversion('imagick'), '3.0.1') !== 1)) {
313
            // on the versions after 3.0.1 the clone() method was deprecated in favour of clone keyword
314
            return @$object->clone();
315
        }
316
        return @clone($object);
317
    }
318
319
    /**
320
     * Output input data and compress it if possible.
321
     * @param string $data Data to output.
322
     * @param int $length Data length in bytes.
323
     * @since 5.9.086
324
     * @public static
325
     */
326
    public static function sendOutputData($data, $length)
327
    {
328
        if (!isset($_SERVER['HTTP_ACCEPT_ENCODING']) or empty($_SERVER['HTTP_ACCEPT_ENCODING'])) {
329
            // the content length may vary if the server is using compression
330
            header('Content-Length: ' . $length);
331
        }
332
        echo $data;
333
    }
334
335
    /**
336
     * Replace page number aliases with number.
337
     * @param string $page Page content.
338
     * @param array $replace Array of replacements (array keys are replacement strings, values are alias arrays).
339
     * @param int $diff If passed, this will be set to the total char number difference between alias and replacements.
340
     * @return array replaced page content and updated $diff parameter as array.
341
     * @public static
342
     */
343
    public static function replacePageNumAliases($page, $replace, $diff = 0)
344
    {
345
        foreach ($replace as $rep) {
346
            foreach ($rep[3] as $a) {
347
                if (strpos($page, $a) !== false) {
348
                    $page = str_replace($a, $rep[0], $page);
349
                    $diff += ($rep[2] - $rep[1]);
350
                }
351
            }
352
        }
353
        return array($page, $diff);
354
    }
355
356
    /**
357
     * Returns timestamp in seconds from formatted date-time.
358
     * @param string $date Formatted date-time.
359
     * @return int seconds.
360
     * @since 5.9.152 (2012-03-23)
361
     * @public static
362
     */
363
    public static function getTimestamp($date)
364
    {
365
        if (($date[0] == 'D') and ($date[1] == ':')) {
366
            // remove date prefix if present
367
            $date = substr($date, 2);
368
        }
369
        return strtotime($date);
370
    }
371
372
    /**
373
     * Returns a formatted date-time.
374
     * @param int $time Time in seconds.
375
     * @return string escaped date string.
376
     * @since 5.9.152 (2012-03-23)
377
     * @public static
378
     */
379
    public static function getFormattedDate($time)
380
    {
381
        return substr_replace(date('YmdHisO', intval($time)), '\'', (0 - 2), 0) . '\'';
382
    }
383
384
    /**
385
     * Returns a string containing random data to be used as a seed for encryption methods.
386
     * @param string $seed starting seed value
387
     * @return string containing random data
388
     * @author Nicola Asuni
389
     * @since 5.9.006 (2010-10-19)
390
     * @public static
391
     */
392
    public static function getRandomSeed($seed = '')
393
    {
394
        $rnd = uniqid(rand() . microtime(true), true);
395
        if (function_exists('posix_getpid')) {
396
            $rnd .= posix_getpid();
397
        }
398
        if (function_exists('openssl_random_pseudo_bytes') and (strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN')) {
399
            // this is not used on windows systems because it is very slow for a know bug
400
            $rnd .= openssl_random_pseudo_bytes(512);
401
        } else {
402
            for ($i = 0; $i < 23; ++$i) {
403
                $rnd .= uniqid('', true);
404
            }
405
        }
406
        return $rnd . $seed . __FILE__ . serialize($_SERVER) . microtime(true);
407
    }
408
409
    /**
410
     * Encrypts a string using MD5 and returns it's value as a binary string.
411
     * @param string $str input string
412
     * @return string MD5 encrypted binary string
413
     * @since 2.0.000 (2008-01-02)
414
     * @public static
415
     */
416
    public static function _md5_16($str)
417
    {
418
        return pack('H*', md5($str));
419
    }
420
421
    /**
422
     * Returns the input text encrypted using AES algorithm and the specified key.
423
     * This method requires openssl or mcrypt. Text is padded to 16bytes blocks
424
     * @param string $key encryption key
425
     * @param string $text input text to be encrypted
426
     * @return string encrypted text
427
     * @author Nicola Asuni
428
     * @since 5.0.005 (2010-05-11)
429
     * @public static
430
     */
431
    public static function _AES($key, $text)
432
    {
433
        // padding (RFC 2898, PKCS #5: Password-Based Cryptography Specification Version 2.0)
434
        $padding = 16 - (strlen($text) % 16);
435
        $text .= str_repeat(chr($padding), $padding);
436
        if (extension_loaded('openssl')) {
437
            $algo = 'aes-256-cbc';
438
            if (strlen($key) == 16) {
439
                $algo = 'aes-128-cbc';
440
            }
441
            $iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length($algo));
442
            $text = openssl_encrypt($text, $algo, $key, OPENSSL_RAW_DATA, $iv);
443
            return $iv . substr($text, 0, -16);
444
        }
445
        $iv = mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC), MCRYPT_RAND);
446
        $text = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $text, MCRYPT_MODE_CBC, $iv);
447
        $text = $iv . $text;
448
        return $text;
449
    }
450
451
    /**
452
     * Returns the input text encrypted using AES algorithm and the specified key.
453
     * This method requires openssl or mcrypt. Text is not padded
454
     * @param string $key encryption key
455
     * @param string $text input text to be encrypted
456
     * @return string encrypted text
457
     * @author Nicola Asuni
458
     * @since TODO
459
     * @public static
460
     */
461
    public static function _AESnopad($key, $text)
462
    {
463
        if (extension_loaded('openssl')) {
464
            $algo = 'aes-256-cbc';
465
            if (strlen($key) == 16) {
466
                $algo = 'aes-128-cbc';
467
            }
468
            $iv = str_repeat("\x00", openssl_cipher_iv_length($algo));
469
            $text = openssl_encrypt($text, $algo, $key, OPENSSL_RAW_DATA, $iv);
470
            return substr($text, 0, -16);
471
        }
472
        $iv = str_repeat("\x00", mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC));
473
        $text = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $text, MCRYPT_MODE_CBC, $iv);
474
        return $text;
475
    }
476
477
    /**
478
     * Returns the input text encrypted using RC4 algorithm and the specified key.
479
     * RC4 is the standard encryption algorithm used in PDF format
480
     * @param string $key Encryption key.
481
     * @param string $text Input text to be encrypted.
482
     * @param string $last_enc_key Reference to last RC4 key encrypted.
483
     * @param string $last_enc_key_c Reference to last RC4 computed key.
484
     * @return string encrypted text
485
     * @since 2.0.000 (2008-01-02)
486
     * @author Klemen Vodopivec, Nicola Asuni
487
     * @public static
488
     */
489
    public static function _RC4($key, $text, &$last_enc_key, &$last_enc_key_c)
490
    {
491
        if (function_exists('mcrypt_encrypt') and ($out = @mcrypt_encrypt(MCRYPT_ARCFOUR, $key, $text, MCRYPT_MODE_STREAM, ''))) {
492
            // try to use mcrypt function if exist
493
            return $out;
494
        }
495
        if ($last_enc_key != $key) {
496
            $k = str_repeat($key, (int) ((256 / strlen($key)) + 1));
497
            $rc4 = range(0, 255);
498
            $j = 0;
499
            for ($i = 0; $i < 256; ++$i) {
500
                $t = $rc4[$i];
501
                $j = ($j + $t + ord($k[$i])) % 256;
502
                $rc4[$i] = $rc4[$j];
503
                $rc4[$j] = $t;
504
            }
505
            $last_enc_key = $key;
506
            $last_enc_key_c = $rc4;
507
        } else {
508
            $rc4 = $last_enc_key_c;
509
        }
510
        $len = strlen($text);
511
        $a = 0;
512
        $b = 0;
513
        $out = '';
514
        for ($i = 0; $i < $len; ++$i) {
515
            $a = ($a + 1) % 256;
516
            $t = $rc4[$a];
517
            $b = ($b + $t) % 256;
518
            $rc4[$a] = $rc4[$b];
519
            $rc4[$b] = $t;
520
            $k = $rc4[($rc4[$a] + $rc4[$b]) % 256];
521
            $out .= chr(ord($text[$i]) ^ $k);
522
        }
523
        return $out;
524
    }
525
526
    /**
527
     * Return the permission code used on encryption (P value).
528
     * @param array $permissions the set of permissions (specify the ones you want to block).
529
     * @param int $mode encryption strength: 0 = RC4 40 bit; 1 = RC4 128 bit; 2 = AES 128 bit; 3 = AES 256 bit.
530
     * @since 5.0.005 (2010-05-12)
531
     * @author Nicola Asuni
532
     * @public static
533
     */
534
    public static function getUserPermissionCode($permissions, $mode = 0)
535
    {
536
        $options = array(
537
            'owner' => 2, // bit 2 -- inverted logic: cleared by default
538
            'print' => 4, // bit 3
539
            'modify' => 8, // bit 4
540
            'copy' => 16, // bit 5
541
            'annot-forms' => 32, // bit 6
542
            'fill-forms' => 256, // bit 9
543
            'extract' => 512, // bit 10
544
            'assemble' => 1024,// bit 11
545
            'print-high' => 2048 // bit 12
546
            );
547
        $protection = 2147422012; // 32 bit: (01111111 11111111 00001111 00111100)
548
        foreach ($permissions as $permission) {
549
            if (isset($options[$permission])) {
550
                if (($mode > 0) or ($options[$permission] <= 32)) {
551
                    // set only valid permissions
552
                    if ($options[$permission] == 2) {
553
                        // the logic for bit 2 is inverted (cleared by default)
554
                        $protection += $options[$permission];
555
                    } else {
556
                        $protection -= $options[$permission];
557
                    }
558
                }
559
            }
560
        }
561
        return $protection;
562
    }
563
564
    /**
565
     * Convert hexadecimal string to string
566
     * @param string $bs byte-string to convert
567
     * @return string
568
     * @since 5.0.005 (2010-05-12)
569
     * @author Nicola Asuni
570
     * @public static
571
     */
572
    public static function convertHexStringToString($bs)
573
    {
574
        $string = ''; // string to be returned
575
        $bslength = strlen($bs);
576
        if (($bslength % 2) != 0) {
577
            // padding
578
            $bs .= '0';
579
            ++$bslength;
580
        }
581
        for ($i = 0; $i < $bslength; $i += 2) {
582
            $string .= chr(hexdec($bs[$i] . $bs[($i + 1)]));
583
        }
584
        return $string;
585
    }
586
587
    /**
588
     * Convert string to hexadecimal string (byte string)
589
     * @param string $s string to convert
590
     * @return string byte string
591
     * @since 5.0.010 (2010-05-17)
592
     * @author Nicola Asuni
593
     * @public static
594
     */
595
    public static function convertStringToHexString($s)
596
    {
597
        $bs = '';
598
        $chars = preg_split('//', $s, -1, PREG_SPLIT_NO_EMPTY);
599
        foreach ($chars as $c) {
600
            $bs .= sprintf('%02s', dechex(ord($c)));
601
        }
602
        return $bs;
603
    }
604
605
    /**
606
     * Convert encryption P value to a string of bytes, low-order byte first.
607
     * @param string $protection 32bit encryption permission value (P value)
608
     * @return string
609
     * @since 5.0.005 (2010-05-12)
610
     * @author Nicola Asuni
611
     * @public static
612
     */
613
    public static function getEncPermissionsString($protection)
614
    {
615
        $binprot = sprintf('%032b', $protection);
616
        $str = chr(bindec(substr($binprot, 24, 8)));
617
        $str .= chr(bindec(substr($binprot, 16, 8)));
618
        $str .= chr(bindec(substr($binprot, 8, 8)));
619
        $str .= chr(bindec(substr($binprot, 0, 8)));
620
        return $str;
621
    }
622
623
    /**
624
     * Encode a name object.
625
     * @param string $name Name object to encode.
626
     * @return string Encoded name object.
627
     * @author Nicola Asuni
628
     * @since 5.9.097 (2011-06-23)
629
     * @public static
630
     */
631
    public static function encodeNameObject($name)
632
    {
633
        $escname = '';
634
        $length = strlen($name);
635
        for ($i = 0; $i < $length; ++$i) {
636
            $chr = $name[$i];
637
            if (preg_match('/[0-9a-zA-Z#_=-]/', $chr) == 1) {
638
                $escname .= $chr;
639
            } else {
640
                $escname .= sprintf('#%02X', ord($chr));
641
            }
642
        }
643
        return $escname;
644
    }
645
646
    /**
647
     * Convert JavaScript form fields properties array to Annotation Properties array.
648
     * @param array $prop javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
649
     * @param array $spot_colors Reference to spot colors array.
650
     * @param boolean $rtl True if in Right-To-Left text direction mode, false otherwise.
651
     * @return array of annotation properties
652
     * @author Nicola Asuni
653
     * @since 4.8.000 (2009-09-06)
654
     * @public static
655
     */
656
    public static function getAnnotOptFromJSProp($prop, &$spot_colors, $rtl = false)
657
    {
658
        if (isset($prop['aopt']) and is_array($prop['aopt'])) {
659
            // the annotation options are already defined
660
            return $prop['aopt'];
661
        }
662
        $opt = array(); // value to be returned
663
        // alignment: Controls how the text is laid out within the text field.
664
        if (isset($prop['alignment'])) {
665
            switch ($prop['alignment']) {
666
                case 'left': {
667
                    $opt['q'] = 0;
668
                    break;
669
                }
670
                case 'center': {
671
                    $opt['q'] = 1;
672
                    break;
673
                }
674
                case 'right': {
675
                    $opt['q'] = 2;
676
                    break;
677
                }
678
                default: {
679
                    $opt['q'] = ($rtl) ? 2 : 0;
680
                    break;
681
                }
682
            }
683
        }
684
        // lineWidth: Specifies the thickness of the border when stroking the perimeter of a field's rectangle.
685
        if (isset($prop['lineWidth'])) {
686
            $linewidth = intval($prop['lineWidth']);
687
        } else {
688
            $linewidth = 1;
689
        }
690
        // borderStyle: The border style for a field.
691
        if (isset($prop['borderStyle'])) {
692
            switch ($prop['borderStyle']) {
693
                case 'border.d':
694
                case 'dashed': {
695
                    $opt['border'] = array(0, 0, $linewidth, array(3, 2));
696
                    $opt['bs'] = array('w' => $linewidth, 's' => 'D', 'd' => array(3, 2));
697
                    break;
698
                }
699
                case 'border.b':
700
                case 'beveled': {
701
                    $opt['border'] = array(0, 0, $linewidth);
702
                    $opt['bs'] = array('w' => $linewidth, 's' => 'B');
703
                    break;
704
                }
705
                case 'border.i':
706
                case 'inset': {
707
                    $opt['border'] = array(0, 0, $linewidth);
708
                    $opt['bs'] = array('w' => $linewidth, 's' => 'I');
709
                    break;
710
                }
711
                case 'border.u':
712
                case 'underline': {
713
                    $opt['border'] = array(0, 0, $linewidth);
714
                    $opt['bs'] = array('w' => $linewidth, 's' => 'U');
715
                    break;
716
                }
717
                case 'border.s':
718
                case 'solid': {
719
                    $opt['border'] = array(0, 0, $linewidth);
720
                    $opt['bs'] = array('w' => $linewidth, 's' => 'S');
721
                    break;
722
                }
723
                default: {
724
                    break;
725
                }
726
            }
727
        }
728
        if (isset($prop['border']) and is_array($prop['border'])) {
729
            $opt['border'] = $prop['border'];
730
        }
731
        if (!isset($opt['mk'])) {
732
            $opt['mk'] = array();
733
        }
734
        if (!isset($opt['mk']['if'])) {
735
            $opt['mk']['if'] = array();
736
        }
737
        $opt['mk']['if']['a'] = array(0.5, 0.5);
738
        // buttonAlignX: Controls how space is distributed from the left of the button face with respect to the icon.
739
        if (isset($prop['buttonAlignX'])) {
740
            $opt['mk']['if']['a'][0] = $prop['buttonAlignX'];
741
        }
742
        // buttonAlignY: Controls how unused space is distributed from the bottom of the button face with respect to the icon.
743
        if (isset($prop['buttonAlignY'])) {
744
            $opt['mk']['if']['a'][1] = $prop['buttonAlignY'];
745
        }
746
        // buttonFitBounds: If true, the extent to which the icon may be scaled is set to the bounds of the button field.
747
        if (isset($prop['buttonFitBounds']) and ($prop['buttonFitBounds'] == 'true')) {
748
            $opt['mk']['if']['fb'] = true;
749
        }
750
        // buttonScaleHow: Controls how the icon is scaled (if necessary) to fit inside the button face.
751
        if (isset($prop['buttonScaleHow'])) {
752
            switch ($prop['buttonScaleHow']) {
753
                case 'scaleHow.proportional': {
754
                    $opt['mk']['if']['s'] = 'P';
755
                    break;
756
                }
757
                case 'scaleHow.anamorphic': {
758
                    $opt['mk']['if']['s'] = 'A';
759
                    break;
760
                }
761
            }
762
        }
763
        // buttonScaleWhen: Controls when an icon is scaled to fit inside the button face.
764
        if (isset($prop['buttonScaleWhen'])) {
765
            switch ($prop['buttonScaleWhen']) {
766
                case 'scaleWhen.always': {
767
                    $opt['mk']['if']['sw'] = 'A';
768
                    break;
769
                }
770
                case 'scaleWhen.never': {
771
                    $opt['mk']['if']['sw'] = 'N';
772
                    break;
773
                }
774
                case 'scaleWhen.tooBig': {
775
                    $opt['mk']['if']['sw'] = 'B';
776
                    break;
777
                }
778
                case 'scaleWhen.tooSmall': {
779
                    $opt['mk']['if']['sw'] = 'S';
780
                    break;
781
                }
782
            }
783
        }
784
        // buttonPosition: Controls how the text and the icon of the button are positioned with respect to each other within the button face.
785
        if (isset($prop['buttonPosition'])) {
786
            switch ($prop['buttonPosition']) {
787
                case 0:
788
                case 'position.textOnly': {
789
                    $opt['mk']['tp'] = 0;
790
                    break;
791
                }
792
                case 1:
793
                case 'position.iconOnly': {
794
                    $opt['mk']['tp'] = 1;
795
                    break;
796
                }
797
                case 2:
798
                case 'position.iconTextV': {
799
                    $opt['mk']['tp'] = 2;
800
                    break;
801
                }
802
                case 3:
803
                case 'position.textIconV': {
804
                    $opt['mk']['tp'] = 3;
805
                    break;
806
                }
807
                case 4:
808
                case 'position.iconTextH': {
809
                    $opt['mk']['tp'] = 4;
810
                    break;
811
                }
812
                case 5:
813
                case 'position.textIconH': {
814
                    $opt['mk']['tp'] = 5;
815
                    break;
816
                }
817
                case 6:
818
                case 'position.overlay': {
819
                    $opt['mk']['tp'] = 6;
820
                    break;
821
                }
822
            }
823
        }
824
        // fillColor: Specifies the background color for a field.
825
        if (isset($prop['fillColor'])) {
826
            if (is_array($prop['fillColor'])) {
827
                $opt['mk']['bg'] = $prop['fillColor'];
828
            } else {
829
                $opt['mk']['bg'] = TCPDF_COLORS::convertHTMLColorToDec($prop['fillColor'], $spot_colors);
830
            }
831
        }
832
        // strokeColor: Specifies the stroke color for a field that is used to stroke the rectangle of the field with a line as large as the line width.
833
        if (isset($prop['strokeColor'])) {
834
            if (is_array($prop['strokeColor'])) {
835
                $opt['mk']['bc'] = $prop['strokeColor'];
836
            } else {
837
                $opt['mk']['bc'] = TCPDF_COLORS::convertHTMLColorToDec($prop['strokeColor'], $spot_colors);
838
            }
839
        }
840
        // rotation: The rotation of a widget in counterclockwise increments.
841
        if (isset($prop['rotation'])) {
842
            $opt['mk']['r'] = $prop['rotation'];
843
        }
844
        // charLimit: Limits the number of characters that a user can type into a text field.
845
        if (isset($prop['charLimit'])) {
846
            $opt['maxlen'] = intval($prop['charLimit']);
847
        }
848
        $ff = 0;
849
        // readonly: The read-only characteristic of a field. If a field is read-only, the user can see the field but cannot change it.
850
        if (isset($prop['readonly']) and ($prop['readonly'] == 'true')) {
851
            $ff += 1 << 0;
852
        }
853
        // required: Specifies whether a field requires a value.
854
        if (isset($prop['required']) and ($prop['required'] == 'true')) {
855
            $ff += 1 << 1;
856
        }
857
        // multiline: Controls how text is wrapped within the field.
858
        if (isset($prop['multiline']) and ($prop['multiline'] == 'true')) {
859
            $ff += 1 << 12;
860
        }
861
        // password: Specifies whether the field should display asterisks when data is entered in the field.
862
        if (isset($prop['password']) and ($prop['password'] == 'true')) {
863
            $ff += 1 << 13;
864
        }
865
        // NoToggleToOff: If set, exactly one radio button shall be selected at all times; selecting the currently selected button has no effect.
866
        if (isset($prop['NoToggleToOff']) and ($prop['NoToggleToOff'] == 'true')) {
867
            $ff += 1 << 14;
868
        }
869
        // Radio: If set, the field is a set of radio buttons.
870
        if (isset($prop['Radio']) and ($prop['Radio'] == 'true')) {
871
            $ff += 1 << 15;
872
        }
873
        // Pushbutton: If set, the field is a pushbutton that does not retain a permanent value.
874
        if (isset($prop['Pushbutton']) and ($prop['Pushbutton'] == 'true')) {
875
            $ff += 1 << 16;
876
        }
877
        // Combo: If set, the field is a combo box; if clear, the field is a list box.
878
        if (isset($prop['Combo']) and ($prop['Combo'] == 'true')) {
879
            $ff += 1 << 17;
880
        }
881
        // editable: Controls whether a combo box is editable.
882
        if (isset($prop['editable']) and ($prop['editable'] == 'true')) {
883
            $ff += 1 << 18;
884
        }
885
        // Sort: If set, the field's option items shall be sorted alphabetically.
886
        if (isset($prop['Sort']) and ($prop['Sort'] == 'true')) {
887
            $ff += 1 << 19;
888
        }
889
        // fileSelect: If true, sets the file-select flag in the Options tab of the text field (Field is Used for File Selection).
890
        if (isset($prop['fileSelect']) and ($prop['fileSelect'] == 'true')) {
891
            $ff += 1 << 20;
892
        }
893
        // multipleSelection: If true, indicates that a list box allows a multiple selection of items.
894
        if (isset($prop['multipleSelection']) and ($prop['multipleSelection'] == 'true')) {
895
            $ff += 1 << 21;
896
        }
897
        // doNotSpellCheck: If true, spell checking is not performed on this editable text field.
898
        if (isset($prop['doNotSpellCheck']) and ($prop['doNotSpellCheck'] == 'true')) {
899
            $ff += 1 << 22;
900
        }
901
        // doNotScroll: If true, the text field does not scroll and the user, therefore, is limited by the rectangular region designed for the field.
902
        if (isset($prop['doNotScroll']) and ($prop['doNotScroll'] == 'true')) {
903
            $ff += 1 << 23;
904
        }
905
        // comb: If set to true, the field background is drawn as series of boxes (one for each character in the value of the field) and each character of the content is drawn within those boxes. The number of boxes drawn is determined from the charLimit property. It applies only to text fields. The setter will also raise if any of the following field properties are also set multiline, password, and fileSelect. A side-effect of setting this property is that the doNotScroll property is also set.
906
        if (isset($prop['comb']) and ($prop['comb'] == 'true')) {
907
            $ff += 1 << 24;
908
        }
909
        // radiosInUnison: If false, even if a group of radio buttons have the same name and export value, they behave in a mutually exclusive fashion, like HTML radio buttons.
910
        if (isset($prop['radiosInUnison']) and ($prop['radiosInUnison'] == 'true')) {
911
            $ff += 1 << 25;
912
        }
913
        // richText: If true, the field allows rich text formatting.
914
        if (isset($prop['richText']) and ($prop['richText'] == 'true')) {
915
            $ff += 1 << 25;
916
        }
917
        // commitOnSelChange: Controls whether a field value is committed after a selection change.
918
        if (isset($prop['commitOnSelChange']) and ($prop['commitOnSelChange'] == 'true')) {
919
            $ff += 1 << 26;
920
        }
921
        $opt['ff'] = $ff;
922
        // defaultValue: The default value of a field - that is, the value that the field is set to when the form is reset.
923
        if (isset($prop['defaultValue'])) {
924
            $opt['dv'] = $prop['defaultValue'];
925
        }
926
        $f = 4; // default value for annotation flags
927
        // readonly: The read-only characteristic of a field. If a field is read-only, the user can see the field but cannot change it.
928
        if (isset($prop['readonly']) and ($prop['readonly'] == 'true')) {
929
            $f += 1 << 6;
930
        }
931
        // display: Controls whether the field is hidden or visible on screen and in print.
932
        if (isset($prop['display'])) {
933
            if ($prop['display'] == 'display.visible') {
934
                //
935
            } elseif ($prop['display'] == 'display.hidden') {
936
                $f += 1 << 1;
937
            } elseif ($prop['display'] == 'display.noPrint') {
938
                $f -= 1 << 2;
939
            } elseif ($prop['display'] == 'display.noView') {
940
                $f += 1 << 5;
941
            }
942
        }
943
        $opt['f'] = $f;
944
        // currentValueIndices: Reads and writes single or multiple values of a list box or combo box.
945
        if (isset($prop['currentValueIndices']) and is_array($prop['currentValueIndices'])) {
946
            $opt['i'] = $prop['currentValueIndices'];
947
        }
948
        // value: The value of the field data that the user has entered.
949
        if (isset($prop['value'])) {
950
            if (is_array($prop['value'])) {
951
                $opt['opt'] = array();
952
                foreach ($prop['value'] as $key => $optval) {
953
                    // exportValues: An array of strings representing the export values for the field.
954
                    if (isset($prop['exportValues'][$key])) {
955
                        $opt['opt'][$key] = array($prop['exportValues'][$key], $prop['value'][$key]);
956
                    } else {
957
                        $opt['opt'][$key] = $prop['value'][$key];
958
                    }
959
                }
960
            } else {
961
                $opt['v'] = $prop['value'];
962
            }
963
        }
964
        // richValue: This property specifies the text contents and formatting of a rich text field.
965
        if (isset($prop['richValue'])) {
966
            $opt['rv'] = $prop['richValue'];
967
        }
968
        // submitName: If nonempty, used during form submission instead of name. Only applicable if submitting in HTML format (that is, URL-encoded).
969
        if (isset($prop['submitName'])) {
970
            $opt['tm'] = $prop['submitName'];
971
        }
972
        // name: Fully qualified field name.
973
        if (isset($prop['name'])) {
974
            $opt['t'] = $prop['name'];
975
        }
976
        // userName: The user name (short description string) of the field.
977
        if (isset($prop['userName'])) {
978
            $opt['tu'] = $prop['userName'];
979
        }
980
        // highlight: Defines how a button reacts when a user clicks it.
981
        if (isset($prop['highlight'])) {
982
            switch ($prop['highlight']) {
983
                case 'none':
984
                case 'highlight.n': {
985
                    $opt['h'] = 'N';
986
                    break;
987
                }
988
                case 'invert':
989
                case 'highlight.i': {
990
                    $opt['h'] = 'i';
991
                    break;
992
                }
993
                case 'push':
994
                case 'highlight.p': {
995
                    $opt['h'] = 'P';
996
                    break;
997
                }
998
                case 'outline':
999
                case 'highlight.o': {
1000
                    $opt['h'] = 'O';
1001
                    break;
1002
                }
1003
            }
1004
        }
1005
        // Unsupported options:
1006
        // - calcOrderIndex: Changes the calculation order of fields in the document.
1007
        // - delay: Delays the redrawing of a field's appearance.
1008
        // - defaultStyle: This property defines the default style attributes for the form field.
1009
        // - style: Allows the user to set the glyph style of a check box or radio button.
1010
        // - textColor, textFont, textSize
1011
        return $opt;
1012
    }
1013
1014
    /**
1015
     * Format the page numbers.
1016
     * This method can be overridden for custom formats.
1017
     * @param int $num page number
1018
     * @return string
1019
     * @since 4.2.005 (2008-11-06)
1020
     * @public static
1021
     */
1022
    public static function formatPageNumber($num)
1023
    {
1024
        return number_format((float)$num, 0, '', '.');
1025
    }
1026
1027
    /**
1028
     * Format the page numbers on the Table Of Content.
1029
     * This method can be overridden for custom formats.
1030
     * @param int $num page number
1031
     * @return string
1032
     * @since 4.5.001 (2009-01-04)
1033
     * @see addTOC(), addHTMLTOC()
1034
     * @public static
1035
     */
1036
    public static function formatTOCPageNumber($num)
1037
    {
1038
        return number_format((float)$num, 0, '', '.');
1039
    }
1040
1041
    /**
1042
     * Extracts the CSS properties from a CSS string.
1043
     * @param string $cssdata string containing CSS definitions.
1044
     * @return array An array where the keys are the CSS selectors and the values are the CSS properties.
1045
     * @author Nicola Asuni
1046
     * @since 5.1.000 (2010-05-25)
1047
     * @public static
1048
     */
1049
    public static function extractCSSproperties($cssdata)
1050
    {
1051
        if (empty($cssdata)) {
1052
            return array();
1053
        }
1054
        // remove comments
1055
        $cssdata = preg_replace('/\/\*[^\*]*\*\//', '', $cssdata);
1056
        // remove newlines and multiple spaces
1057
        $cssdata = preg_replace('/[\s]+/', ' ', $cssdata);
1058
        // remove some spaces
1059
        $cssdata = preg_replace('/[\s]*([;:\{\}]{1})[\s]*/', '\\1', $cssdata);
1060
        // remove empty blocks
1061
        $cssdata = preg_replace('/([^\}\{]+)\{\}/', '', $cssdata);
1062
        // replace media type parenthesis
1063
        $cssdata = preg_replace('/@media[\s]+([^\{]*)\{/i', '@media \\1§', $cssdata);
1064
        $cssdata = preg_replace('/\}\}/si', '}§', $cssdata);
1065
        // trim string
1066
        $cssdata = trim($cssdata);
1067
        // find media blocks (all, braille, embossed, handheld, print, projection, screen, speech, tty, tv)
1068
        $cssblocks = array();
1069
        $matches = array();
1070
        if (preg_match_all('/@media[\s]+([^\§]*)§([^§]*)§/i', $cssdata, $matches) > 0) {
1071
            foreach ($matches[1] as $key => $type) {
1072
                $cssblocks[$type] = $matches[2][$key];
1073
            }
1074
            // remove media blocks
1075
            $cssdata = preg_replace('/@media[\s]+([^\§]*)§([^§]*)§/i', '', $cssdata);
1076
        }
1077
        // keep 'all' and 'print' media, other media types are discarded
1078
        if (isset($cssblocks['all']) and !empty($cssblocks['all'])) {
1079
            $cssdata .= $cssblocks['all'];
1080
        }
1081
        if (isset($cssblocks['print']) and !empty($cssblocks['print'])) {
1082
            $cssdata .= $cssblocks['print'];
1083
        }
1084
        // reset css blocks array
1085
        $cssblocks = array();
1086
        $matches = array();
1087
        // explode css data string into array
1088
        if (substr($cssdata, -1) == '}') {
1089
            // remove last parethesis
1090
            $cssdata = substr($cssdata, 0, -1);
1091
        }
1092
        $matches = explode('}', $cssdata);
1093
        foreach ($matches as $key => $block) {
1094
            // index 0 contains the CSS selector, index 1 contains CSS properties
1095
            $cssblocks[$key] = explode('{', $block);
1096
            if (!isset($cssblocks[$key][1])) {
1097
                // remove empty definitions
1098
                unset($cssblocks[$key]);
1099
            }
1100
        }
1101
        // split groups of selectors (comma-separated list of selectors)
1102
        foreach ($cssblocks as $key => $block) {
1103
            if (strpos($block[0], ',') > 0) {
1104
                $selectors = explode(',', $block[0]);
1105
                foreach ($selectors as $sel) {
1106
                    $cssblocks[] = array(0 => trim($sel), 1 => $block[1]);
1107
                }
1108
                unset($cssblocks[$key]);
1109
            }
1110
        }
1111
        // covert array to selector => properties
1112
        $cssdata = array();
1113
        foreach ($cssblocks as $block) {
1114
            $selector = $block[0];
1115
            // calculate selector's specificity
1116
            $matches = array();
1117
            $a = 0; // the declaration is not from is a 'style' attribute
1118
            $b = intval(preg_match_all('/[\#]/', $selector, $matches)); // number of ID attributes
1119
            $c = intval(preg_match_all('/[\[\.]/', $selector, $matches)); // number of other attributes
1120
            $c += intval(preg_match_all('/[\:]link|visited|hover|active|focus|target|lang|enabled|disabled|checked|indeterminate|root|nth|first|last|only|empty|contains|not/i', $selector, $matches)); // number of pseudo-classes
1121
            $d = intval(preg_match_all('/[\>\+\~\s]{1}[a-zA-Z0-9]+/', ' ' . $selector, $matches)); // number of element names
1122
            $d += intval(preg_match_all('/[\:][\:]/', $selector, $matches)); // number of pseudo-elements
1123
            $specificity = $a . $b . $c . $d;
1124
            // add specificity to the beginning of the selector
1125
            $cssdata[$specificity . ' ' . $selector] = $block[1];
1126
        }
1127
        // sort selectors alphabetically to account for specificity
1128
        ksort($cssdata, SORT_STRING);
1129
        // return array
1130
        return $cssdata;
1131
    }
1132
1133
    /**
1134
     * Cleanup HTML code (requires HTML Tidy library).
1135
     * @param string $html htmlcode to fix
1136
     * @param string $default_css CSS commands to add
1137
     * @param array|null $tagvs parameters for setHtmlVSpace method
1138
     * @param array|null $tidy_options options for tidy_parse_string function
1139
     * @param array $tagvspaces Array of vertical spaces for tags.
1140
     * @return string XHTML code cleaned up
1141
     * @author Nicola Asuni
1142
     * @since 5.9.017 (2010-11-16)
1143
     * @see setHtmlVSpace()
1144
     * @public static
1145
     */
1146
    public static function fixHTMLCode($html, $default_css, $tagvs, $tidy_options, &$tagvspaces)
1147
    {
1148
        // configure parameters for HTML Tidy
1149
        if (TCPDF_STATIC::empty_string($tidy_options)) {
1150
            $tidy_options = array (
1151
                'clean' => 1,
1152
                'drop-empty-paras' => 0,
1153
                'drop-proprietary-attributes' => 1,
1154
                'fix-backslash' => 1,
1155
                'hide-comments' => 1,
1156
                'join-styles' => 1,
1157
                'lower-literals' => 1,
1158
                'merge-divs' => 1,
1159
                'merge-spans' => 1,
1160
                'output-xhtml' => 1,
1161
                'word-2000' => 1,
1162
                'wrap' => 0,
1163
                'output-bom' => 0,
1164
                //'char-encoding' => 'utf8',
1165
                //'input-encoding' => 'utf8',
1166
                //'output-encoding' => 'utf8'
1167
            );
1168
        }
1169
        // clean up the HTML code
1170
        $tidy = tidy_parse_string($html, $tidy_options);
1171
        // fix the HTML
1172
        $tidy->cleanRepair();
1173
        // get the CSS part
1174
        $tidy_head = tidy_get_head($tidy);
1175
        $css = $tidy_head->value;
1176
        $css = preg_replace('/<style([^>]+)>/ims', '<style>', $css);
1177
        $css = preg_replace('/<\/style>(.*)<style>/ims', "\n", $css);
1178
        $css = str_replace('/*<![CDATA[*/', '', $css);
1179
        $css = str_replace('/*]]>*/', '', $css);
1180
        preg_match('/<style>(.*)<\/style>/ims', $css, $matches);
1181
        if (isset($matches[1])) {
1182
            $css = strtolower($matches[1]);
1183
        } else {
1184
            $css = '';
1185
        }
1186
        // include default css
1187
        $css = '<style>' . $default_css . $css . '</style>';
1188
        // get the body part
1189
        $tidy_body = tidy_get_body($tidy);
1190
        $html = $tidy_body->value;
1191
        // fix some self-closing tags
1192
        $html = str_replace('<br>', '<br />', $html);
1193
        // remove some empty tag blocks
1194
        $html = preg_replace('/<div([^\>]*)><\/div>/', '', $html);
1195
        $html = preg_replace('/<p([^\>]*)><\/p>/', '', $html);
1196
        if (!TCPDF_STATIC::empty_string($tagvs)) {
1197
            // set vertical space for some XHTML tags
1198
            $tagvspaces = $tagvs;
1199
        }
1200
        // return the cleaned XHTML code + CSS
1201
        return $css . $html;
1202
    }
1203
1204
    /**
1205
     * Returns true if the CSS selector is valid for the selected HTML tag
1206
     * @param array $dom array of HTML tags and properties
1207
     * @param int $key key of the current HTML tag
1208
     * @param string $selector CSS selector string
1209
     * @return true if the selector is valid, false otherwise
1210
     * @since 5.1.000 (2010-05-25)
1211
     * @public static
1212
     */
1213
    public static function isValidCSSSelectorForTag($dom, $key, $selector)
1214
    {
1215
        $valid = false; // value to be returned
1216
        $tag = $dom[$key]['value'];
1217
        $class = array();
1218
        if (isset($dom[$key]['attribute']['class']) and !empty($dom[$key]['attribute']['class'])) {
1219
            $class = explode(' ', strtolower($dom[$key]['attribute']['class']));
1220
        }
1221
        $id = '';
1222
        if (isset($dom[$key]['attribute']['id']) and !empty($dom[$key]['attribute']['id'])) {
1223
            $id = strtolower($dom[$key]['attribute']['id']);
1224
        }
1225
        $selector = preg_replace('/([\>\+\~\s]{1})([\.]{1})([^\>\+\~\s]*)/si', '\\1*.\\3', $selector);
1226
        $matches = array();
1227
        if (preg_match_all('/([\>\+\~\s]{1})([a-zA-Z0-9\*]+)([^\>\+\~\s]*)/si', $selector, $matches, PREG_PATTERN_ORDER | PREG_OFFSET_CAPTURE) > 0) {
1228
            $parentop = array_pop($matches[1]);
1229
            $operator = $parentop[0];
1230
            $offset = $parentop[1];
1231
            $lasttag = array_pop($matches[2]);
1232
            $lasttag = strtolower(trim($lasttag[0]));
1233
            if (($lasttag == '*') or ($lasttag == $tag)) {
1234
                // the last element on selector is our tag or 'any tag'
1235
                $attrib = array_pop($matches[3]);
1236
                $attrib = strtolower(trim($attrib[0]));
1237
                if (!empty($attrib)) {
1238
                    // check if matches class, id, attribute, pseudo-class or pseudo-element
1239
                    switch ($attrib[0]) {
1240
                        case '.': { // class
1241
                            if (in_array(substr($attrib, 1), $class)) {
1242
                                $valid = true;
1243
                            }
1244
                            break;
1245
                        }
1246
                        case '#': { // ID
1247
                            if (substr($attrib, 1) == $id) {
1248
                                $valid = true;
1249
                            }
1250
                            break;
1251
                        }
1252
                        case '[': { // attribute
1253
                            $attrmatch = array();
1254
                            if (preg_match('/\[([a-zA-Z0-9]*)[\s]*([\~\^\$\*\|\=]*)[\s]*["]?([^"\]]*)["]?\]/i', $attrib, $attrmatch) > 0) {
1255
                                $att = strtolower($attrmatch[1]);
1256
                                $val = $attrmatch[3];
1257
                                if (isset($dom[$key]['attribute'][$att])) {
1258
                                    switch ($attrmatch[2]) {
1259
                                        case '=': {
1260
                                            if ($dom[$key]['attribute'][$att] == $val) {
1261
                                                $valid = true;
1262
                                            }
1263
                                            break;
1264
                                        }
1265
                                        case '~=': {
1266
                                            if (in_array($val, explode(' ', $dom[$key]['attribute'][$att]))) {
1267
                                                $valid = true;
1268
                                            }
1269
                                            break;
1270
                                        }
1271
                                        case '^=': {
1272
                                            if ($val == substr($dom[$key]['attribute'][$att], 0, strlen($val))) {
1273
                                                $valid = true;
1274
                                            }
1275
                                            break;
1276
                                        }
1277
                                        case '$=': {
1278
                                            if ($val == substr($dom[$key]['attribute'][$att], -strlen($val))) {
1279
                                                $valid = true;
1280
                                            }
1281
                                            break;
1282
                                        }
1283
                                        case '*=': {
1284
                                            if (strpos($dom[$key]['attribute'][$att], $val) !== false) {
1285
                                                $valid = true;
1286
                                            }
1287
                                            break;
1288
                                        }
1289
                                        case '|=': {
1290
                                            if ($dom[$key]['attribute'][$att] == $val) {
1291
                                                $valid = true;
1292
                                            } elseif (preg_match('/' . $val . '[\-]{1}/i', $dom[$key]['attribute'][$att]) > 0) {
1293
                                                $valid = true;
1294
                                            }
1295
                                            break;
1296
                                        }
1297
                                        default: {
1298
                                            $valid = true;
1299
                                        }
1300
                                    }
1301
                                }
1302
                            }
1303
                            break;
1304
                        }
1305
                        case ':': { // pseudo-class or pseudo-element
1306
                            if ($attrib[1] == ':') { // pseudo-element
1307
                                // pseudo-elements are not supported!
1308
                                // (::first-line, ::first-letter, ::before, ::after)
1309
                            } else { // pseudo-class
1310
                                // pseudo-classes are not supported!
1311
                                // (:root, :nth-child(n), :nth-last-child(n), :nth-of-type(n), :nth-last-of-type(n), :first-child, :last-child, :first-of-type, :last-of-type, :only-child, :only-of-type, :empty, :link, :visited, :active, :hover, :focus, :target, :lang(fr), :enabled, :disabled, :checked)
1312
                            }
1313
                            break;
1314
                        }
1315
                    } // end of switch
1316
                } else {
1317
                    $valid = true;
1318
                }
1319
                if ($valid and ($offset > 0)) {
1320
                    $valid = false;
1321
                    // check remaining selector part
1322
                    $selector = substr($selector, 0, $offset);
1323
                    switch ($operator) {
1324
                        case ' ': { // descendant of an element
1325
                            while ($dom[$key]['parent'] > 0) {
1326
                                if (self::isValidCSSSelectorForTag($dom, $dom[$key]['parent'], $selector)) {
1327
                                    $valid = true;
1328
                                    break;
1329
                                } else {
1330
                                    $key = $dom[$key]['parent'];
1331
                                }
1332
                            }
1333
                            break;
1334
                        }
1335
                        case '>': { // child of an element
1336
                            $valid = self::isValidCSSSelectorForTag($dom, $dom[$key]['parent'], $selector);
1337
                            break;
1338
                        }
1339
                        case '+': { // immediately preceded by an element
1340
                            for ($i = ($key - 1); $i > $dom[$key]['parent']; --$i) {
1341
                                if ($dom[$i]['tag'] and $dom[$i]['opening']) {
1342
                                    $valid = self::isValidCSSSelectorForTag($dom, $i, $selector);
1343
                                    break;
1344
                                }
1345
                            }
1346
                            break;
1347
                        }
1348
                        case '~': { // preceded by an element
1349
                            for ($i = ($key - 1); $i > $dom[$key]['parent']; --$i) {
1350
                                if ($dom[$i]['tag'] and $dom[$i]['opening']) {
1351
                                    if (self::isValidCSSSelectorForTag($dom, $i, $selector)) {
1352
                                        break;
1353
                                    }
1354
                                }
1355
                            }
1356
                            break;
1357
                        }
1358
                    }
1359
                }
1360
            }
1361
        }
1362
        return $valid;
1363
    }
1364
1365
    /**
1366
     * Returns the styles array that apply for the selected HTML tag.
1367
     * @param array $dom array of HTML tags and properties
1368
     * @param int $key key of the current HTML tag
1369
     * @param array $css array of CSS properties
1370
     * @return array containing CSS properties
1371
     * @since 5.1.000 (2010-05-25)
1372
     * @public static
1373
     */
1374
    public static function getCSSdataArray($dom, $key, $css)
1375
    {
1376
        $cssarray = array(); // style to be returned
1377
        // get parent CSS selectors
1378
        $selectors = array();
1379
        if (isset($dom[($dom[$key]['parent'])]['csssel'])) {
1380
            $selectors = $dom[($dom[$key]['parent'])]['csssel'];
1381
        }
1382
        // get all styles that apply
1383
        foreach($css as $selector => $style) {
1384
            $pos = strpos($selector, ' ');
1385
            // get specificity
1386
            $specificity = substr($selector, 0, $pos);
1387
            // remove specificity
1388
            $selector = substr($selector, $pos);
1389
            // check if this selector apply to current tag
1390
            if (self::isValidCSSSelectorForTag($dom, $key, $selector)) {
1391
                if (!in_array($selector, $selectors)) {
1392
                    // add style if not already added on parent selector
1393
                    $cssarray[] = array('k' => $selector, 's' => $specificity, 'c' => $style);
1394
                    $selectors[] = $selector;
1395
                }
1396
            }
1397
        }
1398
        if (isset($dom[$key]['attribute']['style'])) {
1399
            // attach inline style (latest properties have high priority)
1400
            $cssarray[] = array('k' => '', 's' => '1000', 'c' => $dom[$key]['attribute']['style']);
1401
        }
1402
        // order the css array to account for specificity
1403
        $cssordered = array();
1404
        foreach ($cssarray as $key => $val) {
1405
            $skey = sprintf('%04d', $key);
1406
            $cssordered[$val['s'] . '_' . $skey] = $val;
1407
        }
1408
        // sort selectors alphabetically to account for specificity
1409
        ksort($cssordered, SORT_STRING);
1410
        return array($selectors, $cssordered);
1411
    }
1412
1413
    /**
1414
     * Compact CSS data array into single string.
1415
     * @param array $css array of CSS properties
1416
     * @return string containing merged CSS properties
1417
     * @since 5.9.070 (2011-04-19)
1418
     * @public static
1419
     */
1420
    public static function getTagStyleFromCSSarray($css)
1421
    {
1422
        $tagstyle = ''; // value to be returned
1423
        foreach ($css as $style) {
1424
            // split single css commands
1425
            $csscmds = explode(';', $style['c']);
1426
            foreach ($csscmds as $cmd) {
1427
                if (!empty($cmd)) {
1428
                    $pos = strpos($cmd, ':');
1429
                    if ($pos !== false) {
1430
                        $cmd = substr($cmd, 0, ($pos + 1));
1431
                        if (strpos($tagstyle, $cmd) !== false) {
1432
                            // remove duplicate commands (last commands have high priority)
1433
                            $tagstyle = preg_replace('/' . $cmd . '[^;]+/i', '', $tagstyle);
1434
                        }
1435
                    }
1436
                }
1437
            }
1438
            $tagstyle .= ';' . $style['c'];
1439
        }
1440
        // remove multiple semicolons
1441
        $tagstyle = preg_replace('/[;]+/', ';', $tagstyle);
1442
        return $tagstyle;
1443
    }
1444
1445
    /**
1446
     * Returns the Roman representation of an integer number
1447
     * @param int $number number to convert
1448
     * @return string roman representation of the specified number
1449
     * @since 4.4.004 (2008-12-10)
1450
     * @public static
1451
     */
1452
    public static function intToRoman($number)
1453
    {
1454
        $roman = '';
1455
        if ($number >= 4000) {
1456
            // do not represent numbers above 4000 in Roman numerals
1457
            return strval($number);
1458
        }
1459
        while ($number >= 1000) {
1460
            $roman .= 'M';
1461
            $number -= 1000;
1462
        }
1463
        while ($number >= 900) {
1464
            $roman .= 'CM';
1465
            $number -= 900;
1466
        }
1467
        while ($number >= 500) {
1468
            $roman .= 'D';
1469
            $number -= 500;
1470
        }
1471
        while ($number >= 400) {
1472
            $roman .= 'CD';
1473
            $number -= 400;
1474
        }
1475
        while ($number >= 100) {
1476
            $roman .= 'C';
1477
            $number -= 100;
1478
        }
1479
        while ($number >= 90) {
1480
            $roman .= 'XC';
1481
            $number -= 90;
1482
        }
1483
        while ($number >= 50) {
1484
            $roman .= 'L';
1485
            $number -= 50;
1486
        }
1487
        while ($number >= 40) {
1488
            $roman .= 'XL';
1489
            $number -= 40;
1490
        }
1491
        while ($number >= 10) {
1492
            $roman .= 'X';
1493
            $number -= 10;
1494
        }
1495
        while ($number >= 9) {
1496
            $roman .= 'IX';
1497
            $number -= 9;
1498
        }
1499
        while ($number >= 5) {
1500
            $roman .= 'V';
1501
            $number -= 5;
1502
        }
1503
        while ($number >= 4) {
1504
            $roman .= 'IV';
1505
            $number -= 4;
1506
        }
1507
        while ($number >= 1) {
1508
            $roman .= 'I';
1509
            --$number;
1510
        }
1511
        return $roman;
1512
    }
1513
1514
    /**
1515
     * Find position of last occurrence of a substring in a string
1516
     * @param string $haystack The string to search in.
1517
     * @param string $needle substring to search.
1518
     * @param int $offset May be specified to begin searching an arbitrary number of characters into the string.
1519
     * @return int|false Returns the position where the needle exists. Returns FALSE if the needle was not found.
1520
     * @since 4.8.038 (2010-03-13)
1521
     * @public static
1522
     */
1523
    public static function revstrpos($haystack, $needle, $offset = 0)
1524
    {
1525
        $length = strlen($haystack);
1526
        $offset = ($offset > 0) ? ($length - $offset) : abs($offset);
1527
        $pos = strpos(strrev($haystack), strrev($needle), $offset);
1528
        return ($pos === false) ? false : ($length - $pos - strlen($needle));
1529
    }
1530
1531
    /**
1532
     * Returns an array of hyphenation patterns.
1533
     * @param string $file TEX file containing hypenation patterns. TEX patterns can be downloaded from http://www.ctan.org/tex-archive/language/hyph-utf8/tex/generic/hyph-utf8/patterns/
1534
     * @return array of hyphenation patterns
1535
     * @author Nicola Asuni
1536
     * @since 4.9.012 (2010-04-12)
1537
     * @public static
1538
     */
1539
    public static function getHyphenPatternsFromTEX($file)
1540
    {
1541
        // TEX patterns are available at:
1542
        // http://www.ctan.org/tex-archive/language/hyph-utf8/tex/generic/hyph-utf8/patterns/
1543
        $data = file_get_contents($file);
1544
        $patterns = array();
1545
        // remove comments
1546
        $data = preg_replace('/\%[^\n]*/', '', $data);
1547
        // extract the patterns part
1548
        preg_match('/\\\\patterns\{([^\}]*)\}/i', $data, $matches);
1549
        $data = trim(substr($matches[0], 10, -1));
1550
        // extract each pattern
1551
        $patterns_array = preg_split('/[\s]+/', $data);
1552
        // create new language array of patterns
1553
        $patterns = array();
1554
        foreach($patterns_array as $val) {
1555
            if (!TCPDF_STATIC::empty_string($val)) {
1556
                $val = trim($val);
1557
                $val = str_replace('\'', '\\\'', $val);
1558
                $key = preg_replace('/[0-9]+/', '', $val);
1559
                $patterns[$key] = $val;
1560
            }
1561
        }
1562
        return $patterns;
1563
    }
1564
1565
    /**
1566
     * Get the Path-Painting Operators.
1567
     * @param string $style Style of rendering. Possible values are:
1568
     * <ul>
1569
     *   <li>S or D: Stroke the path.</li>
1570
     *   <li>s or d: Close and stroke the path.</li>
1571
     *   <li>f or F: Fill the path, using the nonzero winding number rule to determine the region to fill.</li>
1572
     *   <li>f* or F*: Fill the path, using the even-odd rule to determine the region to fill.</li>
1573
     *   <li>B or FD or DF: Fill and then stroke the path, using the nonzero winding number rule to determine the region to fill.</li>
1574
     *   <li>B* or F*D or DF*: Fill and then stroke the path, using the even-odd rule to determine the region to fill.</li>
1575
     *   <li>b or fd or df: Close, fill, and then stroke the path, using the nonzero winding number rule to determine the region to fill.</li>
1576
     *   <li>b or f*d or df*: Close, fill, and then stroke the path, using the even-odd rule to determine the region to fill.</li>
1577
     *   <li>CNZ: Clipping mode using the even-odd rule to determine which regions lie inside the clipping path.</li>
1578
     *   <li>CEO: Clipping mode using the nonzero winding number rule to determine which regions lie inside the clipping path</li>
1579
     *   <li>n: End the path object without filling or stroking it.</li>
1580
     * </ul>
1581
     * @param string $default default style
1582
     * @return string
1583
     * @author Nicola Asuni
1584
     * @since 5.0.000 (2010-04-30)
1585
     * @public static
1586
     */
1587
    public static function getPathPaintOperator($style, $default = 'S')
1588
    {
1589
        $op = '';
1590
        switch($style) {
1591
            case 'S':
1592
            case 'D': {
1593
                $op = 'S';
1594
                break;
1595
            }
1596
            case 's':
1597
            case 'd': {
1598
                $op = 's';
1599
                break;
1600
            }
1601
            case 'f':
1602
            case 'F': {
1603
                $op = 'f';
1604
                break;
1605
            }
1606
            case 'f*':
1607
            case 'F*': {
1608
                $op = 'f*';
1609
                break;
1610
            }
1611
            case 'B':
1612
            case 'FD':
1613
            case 'DF': {
1614
                $op = 'B';
1615
                break;
1616
            }
1617
            case 'B*':
1618
            case 'F*D':
1619
            case 'DF*': {
1620
                $op = 'B*';
1621
                break;
1622
            }
1623
            case 'b':
1624
            case 'fd':
1625
            case 'df': {
1626
                $op = 'b';
1627
                break;
1628
            }
1629
            case 'b*':
1630
            case 'f*d':
1631
            case 'df*': {
1632
                $op = 'b*';
1633
                break;
1634
            }
1635
            case 'CNZ': {
1636
                $op = 'W n';
1637
                break;
1638
            }
1639
            case 'CEO': {
1640
                $op = 'W* n';
1641
                break;
1642
            }
1643
            case 'n': {
1644
                $op = 'n';
1645
                break;
1646
            }
1647
            default: {
1648
                if (!empty($default)) {
1649
                    $op = self::getPathPaintOperator($default, '');
1650
                } else {
1651
                    $op = '';
1652
                }
1653
            }
1654
        }
1655
        return $op;
1656
    }
1657
1658
    /**
1659
     * Get the product of two SVG tranformation matrices
1660
     * @param array $ta first SVG tranformation matrix
1661
     * @param array $tb second SVG tranformation matrix
1662
     * @return array transformation array
1663
     * @author Nicola Asuni
1664
     * @since 5.0.000 (2010-05-02)
1665
     * @public static
1666
     */
1667
    public static function getTransformationMatrixProduct($ta, $tb)
1668
    {
1669
        $tm = array();
1670
        $tm[0] = ($ta[0] * $tb[0]) + ($ta[2] * $tb[1]);
1671
        $tm[1] = ($ta[1] * $tb[0]) + ($ta[3] * $tb[1]);
1672
        $tm[2] = ($ta[0] * $tb[2]) + ($ta[2] * $tb[3]);
1673
        $tm[3] = ($ta[1] * $tb[2]) + ($ta[3] * $tb[3]);
1674
        $tm[4] = ($ta[0] * $tb[4]) + ($ta[2] * $tb[5]) + $ta[4];
1675
        $tm[5] = ($ta[1] * $tb[4]) + ($ta[3] * $tb[5]) + $ta[5];
1676
        return $tm;
1677
    }
1678
1679
    /**
1680
     * Get the tranformation matrix from SVG transform attribute
1681
     * @param string $attribute transformation
1682
     * @return array of transformations
1683
     * @author Nicola Asuni
1684
     * @since 5.0.000 (2010-05-02)
1685
     * @public static
1686
     */
1687
    public static function getSVGTransformMatrix($attribute)
1688
    {
1689
        // identity matrix
1690
        $tm = array(1, 0, 0, 1, 0, 0);
1691
        $transform = array();
1692
        if (preg_match_all('/(matrix|translate|scale|rotate|skewX|skewY)[\s]*\(([^\)]+)\)/si', $attribute, $transform, PREG_SET_ORDER) > 0) {
1693
            foreach ($transform as $key => $data) {
1694
                if (!empty($data[2])) {
1695
                    $a = 1;
1696
                    $b = 0;
1697
                    $c = 0;
1698
                    $d = 1;
1699
                    $e = 0;
1700
                    $f = 0;
1701
                    $regs = array();
1702
                    switch ($data[1]) {
1703
                        case 'matrix': {
1704
                            if (preg_match('/([a-z0-9\-\.]+)[\,\s]+([a-z0-9\-\.]+)[\,\s]+([a-z0-9\-\.]+)[\,\s]+([a-z0-9\-\.]+)[\,\s]+([a-z0-9\-\.]+)[\,\s]+([a-z0-9\-\.]+)/si', $data[2], $regs)) {
1705
                                $a = $regs[1];
1706
                                $b = $regs[2];
1707
                                $c = $regs[3];
1708
                                $d = $regs[4];
1709
                                $e = $regs[5];
1710
                                $f = $regs[6];
1711
                            }
1712
                            break;
1713
                        }
1714
                        case 'translate': {
1715
                            if (preg_match('/([a-z0-9\-\.]+)[\,\s]+([a-z0-9\-\.]+)/si', $data[2], $regs)) {
1716
                                $e = $regs[1];
1717
                                $f = $regs[2];
1718
                            } elseif (preg_match('/([a-z0-9\-\.]+)/si', $data[2], $regs)) {
1719
                                $e = $regs[1];
1720
                            }
1721
                            break;
1722
                        }
1723
                        case 'scale': {
1724
                            if (preg_match('/([a-z0-9\-\.]+)[\,\s]+([a-z0-9\-\.]+)/si', $data[2], $regs)) {
1725
                                $a = $regs[1];
1726
                                $d = $regs[2];
1727
                            } elseif (preg_match('/([a-z0-9\-\.]+)/si', $data[2], $regs)) {
1728
                                $a = $regs[1];
1729
                                $d = $a;
1730
                            }
1731
                            break;
1732
                        }
1733
                        case 'rotate': {
1734
                            if (preg_match('/([0-9\-\.]+)[\,\s]+([a-z0-9\-\.]+)[\,\s]+([a-z0-9\-\.]+)/si', $data[2], $regs)) {
1735
                                $ang = deg2rad($regs[1]);
1736
                                $x = $regs[2];
1737
                                $y = $regs[3];
1738
                                $a = cos($ang);
1739
                                $b = sin($ang);
1740
                                $c = -$b;
1741
                                $d = $a;
1742
                                $e = ($x * (1 - $a)) - ($y * $c);
1743
                                $f = ($y * (1 - $d)) - ($x * $b);
1744
                            } elseif (preg_match('/([0-9\-\.]+)/si', $data[2], $regs)) {
1745
                                $ang = deg2rad($regs[1]);
1746
                                $a = cos($ang);
1747
                                $b = sin($ang);
1748
                                $c = -$b;
1749
                                $d = $a;
1750
                                $e = 0;
1751
                                $f = 0;
1752
                            }
1753
                            break;
1754
                        }
1755
                        case 'skewX': {
1756
                            if (preg_match('/([0-9\-\.]+)/si', $data[2], $regs)) {
1757
                                $c = tan(deg2rad($regs[1]));
1758
                            }
1759
                            break;
1760
                        }
1761
                        case 'skewY': {
1762
                            if (preg_match('/([0-9\-\.]+)/si', $data[2], $regs)) {
1763
                                $b = tan(deg2rad($regs[1]));
1764
                            }
1765
                            break;
1766
                        }
1767
                    }
1768
                    $tm = self::getTransformationMatrixProduct($tm, array($a, $b, $c, $d, $e, $f));
1769
                }
1770
            }
1771
        }
1772
        return $tm;
1773
    }
1774
1775
    /**
1776
     * Returns the angle in radiants between two vectors
1777
     * @param int $x1 X coordinate of first vector point
1778
     * @param int $y1 Y coordinate of first vector point
1779
     * @param int $x2 X coordinate of second vector point
1780
     * @param int $y2 Y coordinate of second vector point
1781
     * @author Nicola Asuni
1782
     * @since 5.0.000 (2010-05-04)
1783
     * @public static
1784
     */
1785
    public static function getVectorsAngle($x1, $y1, $x2, $y2)
1786
    {
1787
        $dprod = ($x1 * $x2) + ($y1 * $y2);
1788
        $dist1 = sqrt(($x1 * $x1) + ($y1 * $y1));
1789
        $dist2 = sqrt(($x2 * $x2) + ($y2 * $y2));
1790
        $angle = acos($dprod / ($dist1 * $dist2));
1791
        if (is_nan($angle)) {
1792
            $angle = M_PI;
1793
        }
1794
        if ((($x1 * $y2) - ($x2 * $y1)) < 0) {
1795
            $angle *= -1;
1796
        }
1797
        return $angle;
1798
    }
1799
1800
    /**
1801
     * Split string by a regular expression.
1802
     * This is a wrapper for the preg_split function to avoid the bug: https://bugs.php.net/bug.php?id=45850
1803
     * @param string $pattern The regular expression pattern to search for without the modifiers, as a string.
1804
     * @param string $modifiers The modifiers part of the pattern,
1805
     * @param string $subject The input string.
1806
     * @param int $limit If specified, then only substrings up to limit are returned with the rest of the string being placed in the last substring. A limit of -1, 0 or NULL means "no limit" and, as is standard across PHP, you can use NULL to skip to the flags parameter.
1807
     * @param int $flags The flags as specified on the preg_split PHP function.
1808
     * @return array Returns an array containing substrings of subject split along boundaries matched by pattern.modifier
1809
     * @author Nicola Asuni
1810
     * @since 6.0.023
1811
     * @public static
1812
     */
1813
    public static function pregSplit($pattern, $modifiers, $subject, $limit = NULL, $flags = NULL)
1814
    {
1815
        // PHP 8.1 deprecates nulls for $limit and $flags
1816
        $limit = $limit === null ? -1 : $limit;
1817
        $flags = $flags === null ? 0 : $flags;
1818
        // the bug only happens on PHP 5.2 when using the u modifier
1819
        if ((strpos($modifiers, 'u') === FALSE) or (count(preg_split('//u', "\n\t", -1, PREG_SPLIT_NO_EMPTY)) == 2)) {
1820
            $ret = preg_split($pattern . $modifiers, $subject, $limit, $flags);
1821
            if ($ret === false) {
1822
                return array();
1823
            }
1824
            return is_array($ret) ? $ret : array();
1825
        }
1826
        // preg_split is bugged - try alternative solution
1827
        $ret = array();
1828
        while (($nl = strpos($subject, "\n")) !== FALSE) {
1829
            $ret = array_merge($ret, preg_split($pattern . $modifiers, substr($subject, 0, $nl), $limit, $flags));
1830
            $ret[] = "\n";
1831
            $subject = substr($subject, ($nl + 1));
1832
        }
1833
        if (strlen($subject) > 0) {
1834
            $ret = array_merge($ret, preg_split($pattern . $modifiers, $subject, $limit, $flags));
1835
        }
1836
        return $ret;
1837
    }
1838
1839
    /**
1840
     * Wrapper to use fopen only with local files
1841
     * @param string $filename Name of the file to open
1842
     * @param string $mode
1843
     * @return resource|false Returns a file pointer resource on success, or FALSE on error.
1844
     * @public static
1845
     */
1846
    public static function fopenLocal($filename, $mode)
1847
    {
1848
        // @CHANGE DOL
1849
        if (strpos($filename, '//') === 0) {
1850
            // Share folder on a (windows) server
1851
            // e.g.: "//[MyServerName]/[MySharedFolder]/"
1852
            //
1853
            // nothing to change
1854
        } elseif (strpos($filename, '://') === false) {
1855
            $filename = 'file://' . $filename;
1856
        } elseif (stream_is_local($filename) !== true) {
1857
            return false;
1858
        }
1859
        return fopen($filename, $mode);
1860
    }
1861
1862
    /**
1863
     * Check if the URL exist.
1864
     * @param string $url URL to check.
1865
     * @return bool Returns TRUE if the URL exists; FALSE otherwise.
1866
     * @public static
1867
     * @since 6.2.25
1868
     */
1869
    public static function url_exists($url)
1870
    {
1871
        $crs = curl_init();
1872
        // encode query params in URL to get right response form the server
1873
        $url = self::encodeUrlQuery($url);
1874
        curl_setopt($crs, CURLOPT_URL, $url);
1875
        curl_setopt($crs, CURLOPT_NOBODY, true);
1876
        curl_setopt($crs, CURLOPT_FAILONERROR, true);
1877
        if ((ini_get('open_basedir') == '') && (!ini_get('safe_mode'))) {
1878
            curl_setopt($crs, CURLOPT_FOLLOWLOCATION, true);
1879
        }
1880
        curl_setopt($crs, CURLOPT_CONNECTTIMEOUT, 5);
1881
        curl_setopt($crs, CURLOPT_TIMEOUT, 30);
1882
        curl_setopt($crs, CURLOPT_SSL_VERIFYPEER, false);
1883
        curl_setopt($crs, CURLOPT_SSL_VERIFYHOST, false);
1884
        curl_setopt($crs, CURLOPT_USERAGENT, 'tc-lib-file');
1885
        curl_setopt($crs, CURLOPT_MAXREDIRS, 5);
1886
        if (defined('CURLOPT_PROTOCOLS')) {
1887
            curl_setopt($crs, CURLOPT_PROTOCOLS, CURLPROTO_HTTPS | CURLPROTO_HTTP |  CURLPROTO_FTP | CURLPROTO_FTPS);
1888
        }
1889
        curl_exec($crs);
1890
        $code = curl_getinfo($crs, CURLINFO_HTTP_CODE);
1891
        curl_close($crs);
1892
        return ($code == 200);
1893
    }
1894
1895
    /**
1896
     * Encode query params in URL
1897
     *
1898
     * @param string $url
1899
     * @return string
1900
     * @since 6.3.3 (2019-11-01)
1901
     * @public static
1902
     */
1903
    public static function encodeUrlQuery($url)
1904
    {
1905
        $urlData = parse_url($url);
1906
        if (isset($urlData['query']) && $urlData['query']) {
1907
            $urlQueryData = array();
1908
            parse_str(urldecode($urlData['query']), $urlQueryData);
1909
            $port = isset($urlData['port']) ? ':' . $urlData['port'] : '';
1910
            $updatedUrl = $urlData['scheme'] . '://' . $urlData['host'] . $port . $urlData['path'] . '?' . http_build_query($urlQueryData);
1911
        } else {
1912
            $updatedUrl = $url;
1913
        }
1914
        return $updatedUrl;
1915
    }
1916
1917
    /**
1918
     * Wrapper for file_exists.
1919
     * Checks whether a file or directory exists.
1920
     * Only allows some protocols and local files.
1921
     * @param string $filename Path to the file or directory.
1922
     * @return bool Returns TRUE if the file or directory specified by filename exists; FALSE otherwise.
1923
     * @public static
1924
     */
1925
    public static function file_exists($filename)
1926
    {
1927
        if (preg_match('|^https?://|', $filename) == 1) {
1928
            return self::url_exists($filename);
1929
        }
1930
        if (strpos($filename, '://')) {
1931
            return false; // only support http and https wrappers for security reasons
1932
        }
1933
        return @file_exists($filename);
1934
    }
1935
1936
    /**
1937
     * Reads entire file into a string.
1938
     * The file can be also an URL.
1939
     * @param string $file Name of the file or URL to read.
1940
     * @return string|false The function returns the read data or FALSE on failure.
1941
     * @author Nicola Asuni
1942
     * @since 6.0.025
1943
     * @public static
1944
     */
1945
    public static function fileGetContents($file)
1946
    {
1947
        $alt = array($file);
1948
        //
1949
        if (
1950
            (strlen($file) > 1)
1951
            && ($file[0] === '/')
1952
            && ($file[1] !== '/')
1953
            && !empty($_SERVER['DOCUMENT_ROOT'])
1954
            && ($_SERVER['DOCUMENT_ROOT'] !== '/')
1955
        ) {
1956
            $findroot = strpos($file, $_SERVER['DOCUMENT_ROOT']);
1957
            if (($findroot === false) || ($findroot > 1)) {
1958
            $alt[] = htmlspecialchars_decode(urldecode($_SERVER['DOCUMENT_ROOT'] . $file));
1959
            }
1960
        }
1961
        //
1962
        $protocol = 'http';
1963
        if (!empty($_SERVER['HTTPS']) && (strtolower($_SERVER['HTTPS']) != 'off')) {
1964
            $protocol .= 's';
1965
        }
1966
        //
1967
        $url = $file;
1968
        if (preg_match('%^//%', $url) && !empty($_SERVER['HTTP_HOST'])) {
1969
            $url = $protocol . ':' . str_replace(' ', '%20', $url);
1970
        }
1971
        $url = htmlspecialchars_decode($url);
1972
        $alt[] = $url;
1973
        //
1974
        if (
1975
            preg_match('%^(https?)://%', $url)
1976
            && empty($_SERVER['HTTP_HOST'])
1977
            && empty($_SERVER['DOCUMENT_ROOT'])
1978
        ) {
1979
            $urldata = parse_url($url);
1980
            if (empty($urldata['query'])) {
1981
                $host = $protocol . '://' . $_SERVER['HTTP_HOST'];
1982
                if (strpos($url, $host) === 0) {
1983
                    // convert URL to full server path
1984
                    $tmp = str_replace($host, $_SERVER['DOCUMENT_ROOT'], $url);
1985
                    $alt[] = htmlspecialchars_decode(urldecode($tmp));
1986
                }
1987
            }
1988
        }
1989
        //
1990
        if (
1991
            isset($_SERVER['SCRIPT_URI'])
1992
            && !preg_match('%^(https?|ftp)://%', $file)
1993
            && !preg_match('%^//%', $file)
1994
        ) {
1995
            $urldata = @parse_url($_SERVER['SCRIPT_URI']);
1996
            $alt[] = $urldata['scheme'] . '://' . $urldata['host'] . (($file[0] == '/') ? '' : '/') . $file;
1997
        }
1998
        //
1999
        $alt = array_unique($alt);
2000
        foreach ($alt as $path) {
2001
            if (!self::file_exists($path)) {
2002
                continue;
2003
            }
2004
            $ret = @file_get_contents($path);
2005
            if ($ret != false) {
2006
                return $ret;
2007
            }
2008
            // try to use CURL for URLs
2009
            if (
2010
                !ini_get('allow_url_fopen')
2011
                && function_exists('curl_init')
2012
                && preg_match('%^(https?|ftp)://%', $path)
2013
            ) {
2014
                // try to get remote file data using cURL
2015
                $crs = curl_init();
2016
                curl_setopt($crs, CURLOPT_URL, $path);
2017
                curl_setopt($crs, CURLOPT_BINARYTRANSFER, true);
2018
                curl_setopt($crs, CURLOPT_FAILONERROR, true);
2019
                curl_setopt($crs, CURLOPT_RETURNTRANSFER, true);
2020
                if ((ini_get('open_basedir') == '') && (!ini_get('safe_mode'))) {
2021
                    curl_setopt($crs, CURLOPT_FOLLOWLOCATION, true);
2022
                }
2023
                curl_setopt($crs, CURLOPT_CONNECTTIMEOUT, 5);
2024
                curl_setopt($crs, CURLOPT_TIMEOUT, 30);
2025
                curl_setopt($crs, CURLOPT_SSL_VERIFYPEER, false);
2026
                curl_setopt($crs, CURLOPT_SSL_VERIFYHOST, false);
2027
                curl_setopt($crs, CURLOPT_USERAGENT, 'tc-lib-file');
2028
                curl_setopt($crs, CURLOPT_MAXREDIRS, 5);
2029
                if (defined('CURLOPT_PROTOCOLS')) {
2030
                    curl_setopt($crs, CURLOPT_PROTOCOLS, CURLPROTO_HTTPS | CURLPROTO_HTTP |  CURLPROTO_FTP | CURLPROTO_FTPS);
2031
                }
2032
                $ret = curl_exec($crs);
2033
                curl_close($crs);
2034
                if ($ret !== false) {
2035
                    return $ret;
2036
                }
2037
            }
2038
        }
2039
        return false;
2040
    }
2041
2042
    /**
2043
     * Get ULONG from string (Big Endian 32-bit unsigned integer).
2044
     * @param string $str string from where to extract value
2045
     * @param int $offset point from where to read the data
2046
     * @return int 32 bit value
2047
     * @author Nicola Asuni
2048
     * @since 5.2.000 (2010-06-02)
2049
     * @public static
2050
     */
2051
    public static function _getULONG($str, $offset)
2052
    {
2053
        $v = unpack('Ni', substr($str, $offset, 4));
2054
        return $v['i'];
2055
    }
2056
2057
    /**
2058
     * Get USHORT from string (Big Endian 16-bit unsigned integer).
2059
     * @param string $str string from where to extract value
2060
     * @param int $offset point from where to read the data
2061
     * @return int 16 bit value
2062
     * @author Nicola Asuni
2063
     * @since 5.2.000 (2010-06-02)
2064
     * @public static
2065
     */
2066
    public static function _getUSHORT($str, $offset)
2067
    {
2068
        $v = unpack('ni', substr($str, $offset, 2));
2069
        return $v['i'];
2070
    }
2071
2072
    /**
2073
     * Get SHORT from string (Big Endian 16-bit signed integer).
2074
     * @param string $str String from where to extract value.
2075
     * @param int $offset Point from where to read the data.
2076
     * @return int 16 bit value
2077
     * @author Nicola Asuni
2078
     * @since 5.2.000 (2010-06-02)
2079
     * @public static
2080
     */
2081
    public static function _getSHORT($str, $offset)
2082
    {
2083
        $v = unpack('si', substr($str, $offset, 2));
2084
        return $v['i'];
2085
    }
2086
2087
    /**
2088
     * Get FWORD from string (Big Endian 16-bit signed integer).
2089
     * @param string $str String from where to extract value.
2090
     * @param int $offset Point from where to read the data.
2091
     * @return int 16 bit value
2092
     * @author Nicola Asuni
2093
     * @since 5.9.123 (2011-09-30)
2094
     * @public static
2095
     */
2096
    public static function _getFWORD($str, $offset)
2097
    {
2098
        $v = self::_getUSHORT($str, $offset);
2099
        if ($v > 0x7fff) {
2100
            $v -= 0x10000;
2101
        }
2102
        return $v;
2103
    }
2104
2105
    /**
2106
     * Get UFWORD from string (Big Endian 16-bit unsigned integer).
2107
     * @param string $str string from where to extract value
2108
     * @param int $offset point from where to read the data
2109
     * @return int 16 bit value
2110
     * @author Nicola Asuni
2111
     * @since 5.9.123 (2011-09-30)
2112
     * @public static
2113
     */
2114
    public static function _getUFWORD($str, $offset)
2115
    {
2116
        $v = self::_getUSHORT($str, $offset);
2117
        return $v;
2118
    }
2119
2120
    /**
2121
     * Get FIXED from string (32-bit signed fixed-point number (16.16).
2122
     * @param string $str string from where to extract value
2123
     * @param int $offset point from where to read the data
2124
     * @return int 16 bit value
2125
     * @author Nicola Asuni
2126
     * @since 5.9.123 (2011-09-30)
2127
     * @public static
2128
     */
2129
    public static function _getFIXED($str, $offset)
2130
    {
2131
        // mantissa
2132
        $m = self::_getFWORD($str, $offset);
2133
        // fraction
2134
        $f = self::_getUSHORT($str, ($offset + 2));
2135
        $v = floatval('' . $m . '.' . $f . '');
2136
        return $v;
2137
    }
2138
2139
    /**
2140
     * Get BYTE from string (8-bit unsigned integer).
2141
     * @param string $str String from where to extract value.
2142
     * @param int $offset Point from where to read the data.
2143
     * @return int 8 bit value
2144
     * @author Nicola Asuni
2145
     * @since 5.2.000 (2010-06-02)
2146
     * @public static
2147
     */
2148
    public static function _getBYTE($str, $offset)
2149
    {
2150
        $v = unpack('Ci', substr($str, $offset, 1));
2151
        return $v['i'];
2152
    }
2153
    /**
2154
     * Binary-safe and URL-safe file read.
2155
     * Reads up to length bytes from the file pointer referenced by handle. Reading stops as soon as one of the following conditions is met: length bytes have been read; EOF (end of file) is reached.
2156
     * @param resource $handle
2157
     * @param int $length
2158
     * @return string|false Returns the read string or FALSE in case of error.
2159
     * @author Nicola Asuni
2160
     * @since 4.5.027 (2009-03-16)
2161
     * @public static
2162
     */
2163
    public static function rfread($handle, $length)
2164
    {
2165
        $data = fread($handle, $length);
2166
        if ($data === false) {
2167
            return false;
2168
        }
2169
        $rest = ($length - strlen($data));
2170
        if (($rest > 0) && !feof($handle)) {
2171
            $data .= self::rfread($handle, $rest);
2172
        }
2173
        return $data;
2174
    }
2175
2176
    /**
2177
     * Read a 4-byte (32 bit) integer from file.
2178
     * @param resource $f file resource.
2179
     * @return int 4-byte integer
2180
     * @public static
2181
     */
2182
    public static function _freadint($f)
2183
    {
2184
        $a = unpack('Ni', fread($f, 4));
2185
        return $a['i'];
2186
    }
2187
2188
    /**
2189
     * Array of page formats
2190
     * measures are calculated in this way: (inches * 72) or (millimeters * 72 / 25.4)
2191
     * @public static
2192
     *
2193
     * @var array<string,float[]>
2194
     */
2195
    public static $page_formats = array(
2196
        // ISO 216 A Series + 2 SIS 014711 extensions
2197
        'A0'                     => array( 2383.937,  3370.394), // = (  841 x 1189 ) mm  = ( 33.11 x 46.81 ) in
2198
        'A1'                     => array( 1683.780,  2383.937), // = (  594 x 841  ) mm  = ( 23.39 x 33.11 ) in
2199
        'A2'                     => array( 1190.551,  1683.780), // = (  420 x 594  ) mm  = ( 16.54 x 23.39 ) in
2200
        'A3'                     => array(  841.890,  1190.551), // = (  297 x 420  ) mm  = ( 11.69 x 16.54 ) in
2201
        'A4'                     => array(  595.276,   841.890), // = (  210 x 297  ) mm  = (  8.27 x 11.69 ) in
2202
        'A5'                     => array(  419.528,   595.276), // = (  148 x 210  ) mm  = (  5.83 x 8.27  ) in
2203
        'A6'                     => array(  297.638,   419.528), // = (  105 x 148  ) mm  = (  4.13 x 5.83  ) in
2204
        'A7'                     => array(  209.764,   297.638), // = (   74 x 105  ) mm  = (  2.91 x 4.13  ) in
2205
        'A8'                     => array(  147.402,   209.764), // = (   52 x 74   ) mm  = (  2.05 x 2.91  ) in
2206
        'A9'                     => array(  104.882,   147.402), // = (   37 x 52   ) mm  = (  1.46 x 2.05  ) in
2207
        'A10'                    => array(   73.701,   104.882), // = (   26 x 37   ) mm  = (  1.02 x 1.46  ) in
2208
        'A11'                    => array(   51.024,    73.701), // = (   18 x 26   ) mm  = (  0.71 x 1.02  ) in
2209
        'A12'                    => array(   36.850,    51.024), // = (   13 x 18   ) mm  = (  0.51 x 0.71  ) in
2210
        // ISO 216 B Series + 2 SIS 014711 extensions
2211
        'B0'                     => array( 2834.646,  4008.189), // = ( 1000 x 1414 ) mm  = ( 39.37 x 55.67 ) in
2212
        'B1'                     => array( 2004.094,  2834.646), // = (  707 x 1000 ) mm  = ( 27.83 x 39.37 ) in
2213
        'B2'                     => array( 1417.323,  2004.094), // = (  500 x 707  ) mm  = ( 19.69 x 27.83 ) in
2214
        'B3'                     => array( 1000.630,  1417.323), // = (  353 x 500  ) mm  = ( 13.90 x 19.69 ) in
2215
        'B4'                     => array(  708.661,  1000.630), // = (  250 x 353  ) mm  = (  9.84 x 13.90 ) in
2216
        'B5'                     => array(  498.898,   708.661), // = (  176 x 250  ) mm  = (  6.93 x 9.84  ) in
2217
        'B6'                     => array(  354.331,   498.898), // = (  125 x 176  ) mm  = (  4.92 x 6.93  ) in
2218
        'B7'                     => array(  249.449,   354.331), // = (   88 x 125  ) mm  = (  3.46 x 4.92  ) in
2219
        'B8'                     => array(  175.748,   249.449), // = (   62 x 88   ) mm  = (  2.44 x 3.46  ) in
2220
        'B9'                     => array(  124.724,   175.748), // = (   44 x 62   ) mm  = (  1.73 x 2.44  ) in
2221
        'B10'                    => array(   87.874,   124.724), // = (   31 x 44   ) mm  = (  1.22 x 1.73  ) in
2222
        'B11'                    => array(   62.362,    87.874), // = (   22 x 31   ) mm  = (  0.87 x 1.22  ) in
2223
        'B12'                    => array(   42.520,    62.362), // = (   15 x 22   ) mm  = (  0.59 x 0.87  ) in
2224
        // ISO 216 C Series + 2 SIS 014711 extensions + 5 EXTENSION
2225
        'C0'                     => array( 2599.370,  3676.535), // = (  917 x 1297 ) mm  = ( 36.10 x 51.06 ) in
2226
        'C1'                     => array( 1836.850,  2599.370), // = (  648 x 917  ) mm  = ( 25.51 x 36.10 ) in
2227
        'C2'                     => array( 1298.268,  1836.850), // = (  458 x 648  ) mm  = ( 18.03 x 25.51 ) in
2228
        'C3'                     => array(  918.425,  1298.268), // = (  324 x 458  ) mm  = ( 12.76 x 18.03 ) in
2229
        'C4'                     => array(  649.134,   918.425), // = (  229 x 324  ) mm  = (  9.02 x 12.76 ) in
2230
        'C5'                     => array(  459.213,   649.134), // = (  162 x 229  ) mm  = (  6.38 x 9.02  ) in
2231
        'C6'                     => array(  323.150,   459.213), // = (  114 x 162  ) mm  = (  4.49 x 6.38  ) in
2232
        'C7'                     => array(  229.606,   323.150), // = (   81 x 114  ) mm  = (  3.19 x 4.49  ) in
2233
        'C8'                     => array(  161.575,   229.606), // = (   57 x 81   ) mm  = (  2.24 x 3.19  ) in
2234
        'C9'                     => array(  113.386,   161.575), // = (   40 x 57   ) mm  = (  1.57 x 2.24  ) in
2235
        'C10'                    => array(   79.370,   113.386), // = (   28 x 40   ) mm  = (  1.10 x 1.57  ) in
2236
        'C11'                    => array(   56.693,    79.370), // = (   20 x 28   ) mm  = (  0.79 x 1.10  ) in
2237
        'C12'                    => array(   39.685,    56.693), // = (   14 x 20   ) mm  = (  0.55 x 0.79  ) in
2238
        'C76'                    => array(  229.606,   459.213), // = (   81 x 162  ) mm  = (  3.19 x 6.38  ) in
2239
        'DL'                     => array(  311.811,   623.622), // = (  110 x 220  ) mm  = (  4.33 x 8.66  ) in
2240
        'DLE'                    => array(  323.150,   637.795), // = (  114 x 225  ) mm  = (  4.49 x 8.86  ) in
2241
        'DLX'                    => array(  340.158,   666.142), // = (  120 x 235  ) mm  = (  4.72 x 9.25  ) in
2242
        'DLP'                    => array(  280.630,   595.276), // = (   99 x 210  ) mm  = (  3.90 x 8.27  ) in (1/3 A4)
2243
        // SIS 014711 E Series
2244
        'E0'                     => array( 2491.654,  3517.795), // = (  879 x 1241 ) mm  = ( 34.61 x 48.86 ) in
2245
        'E1'                     => array( 1757.480,  2491.654), // = (  620 x 879  ) mm  = ( 24.41 x 34.61 ) in
2246
        'E2'                     => array( 1247.244,  1757.480), // = (  440 x 620  ) mm  = ( 17.32 x 24.41 ) in
2247
        'E3'                     => array(  878.740,  1247.244), // = (  310 x 440  ) mm  = ( 12.20 x 17.32 ) in
2248
        'E4'                     => array(  623.622,   878.740), // = (  220 x 310  ) mm  = (  8.66 x 12.20 ) in
2249
        'E5'                     => array(  439.370,   623.622), // = (  155 x 220  ) mm  = (  6.10 x 8.66  ) in
2250
        'E6'                     => array(  311.811,   439.370), // = (  110 x 155  ) mm  = (  4.33 x 6.10  ) in
2251
        'E7'                     => array(  221.102,   311.811), // = (   78 x 110  ) mm  = (  3.07 x 4.33  ) in
2252
        'E8'                     => array(  155.906,   221.102), // = (   55 x 78   ) mm  = (  2.17 x 3.07  ) in
2253
        'E9'                     => array(  110.551,   155.906), // = (   39 x 55   ) mm  = (  1.54 x 2.17  ) in
2254
        'E10'                    => array(   76.535,   110.551), // = (   27 x 39   ) mm  = (  1.06 x 1.54  ) in
2255
        'E11'                    => array(   53.858,    76.535), // = (   19 x 27   ) mm  = (  0.75 x 1.06  ) in
2256
        'E12'                    => array(   36.850,    53.858), // = (   13 x 19   ) mm  = (  0.51 x 0.75  ) in
2257
        // SIS 014711 G Series
2258
        'G0'                     => array( 2715.591,  3838.110), // = (  958 x 1354 ) mm  = ( 37.72 x 53.31 ) in
2259
        'G1'                     => array( 1919.055,  2715.591), // = (  677 x 958  ) mm  = ( 26.65 x 37.72 ) in
2260
        'G2'                     => array( 1357.795,  1919.055), // = (  479 x 677  ) mm  = ( 18.86 x 26.65 ) in
2261
        'G3'                     => array(  958.110,  1357.795), // = (  338 x 479  ) mm  = ( 13.31 x 18.86 ) in
2262
        'G4'                     => array(  677.480,   958.110), // = (  239 x 338  ) mm  = (  9.41 x 13.31 ) in
2263
        'G5'                     => array(  479.055,   677.480), // = (  169 x 239  ) mm  = (  6.65 x 9.41  ) in
2264
        'G6'                     => array(  337.323,   479.055), // = (  119 x 169  ) mm  = (  4.69 x 6.65  ) in
2265
        'G7'                     => array(  238.110,   337.323), // = (   84 x 119  ) mm  = (  3.31 x 4.69  ) in
2266
        'G8'                     => array(  167.244,   238.110), // = (   59 x 84   ) mm  = (  2.32 x 3.31  ) in
2267
        'G9'                     => array(  119.055,   167.244), // = (   42 x 59   ) mm  = (  1.65 x 2.32  ) in
2268
        'G10'                    => array(   82.205,   119.055), // = (   29 x 42   ) mm  = (  1.14 x 1.65  ) in
2269
        'G11'                    => array(   59.528,    82.205), // = (   21 x 29   ) mm  = (  0.83 x 1.14  ) in
2270
        'G12'                    => array(   39.685,    59.528), // = (   14 x 21   ) mm  = (  0.55 x 0.83  ) in
2271
        // ISO Press
2272
        'RA0'                    => array( 2437.795,  3458.268), // = (  860 x 1220 ) mm  = ( 33.86 x 48.03 ) in
2273
        'RA1'                    => array( 1729.134,  2437.795), // = (  610 x 860  ) mm  = ( 24.02 x 33.86 ) in
2274
        'RA2'                    => array( 1218.898,  1729.134), // = (  430 x 610  ) mm  = ( 16.93 x 24.02 ) in
2275
        'RA3'                    => array(  864.567,  1218.898), // = (  305 x 430  ) mm  = ( 12.01 x 16.93 ) in
2276
        'RA4'                    => array(  609.449,   864.567), // = (  215 x 305  ) mm  = (  8.46 x 12.01 ) in
2277
        'SRA0'                   => array( 2551.181,  3628.346), // = (  900 x 1280 ) mm  = ( 35.43 x 50.39 ) in
2278
        'SRA1'                   => array( 1814.173,  2551.181), // = (  640 x 900  ) mm  = ( 25.20 x 35.43 ) in
2279
        'SRA2'                   => array( 1275.591,  1814.173), // = (  450 x 640  ) mm  = ( 17.72 x 25.20 ) in
2280
        'SRA3'                   => array(  907.087,  1275.591), // = (  320 x 450  ) mm  = ( 12.60 x 17.72 ) in
2281
        'SRA4'                   => array(  637.795,   907.087), // = (  225 x 320  ) mm  = (  8.86 x 12.60 ) in
2282
        // German DIN 476
2283
        '4A0'                    => array( 4767.874,  6740.787), // = ( 1682 x 2378 ) mm  = ( 66.22 x 93.62 ) in
2284
        '2A0'                    => array( 3370.394,  4767.874), // = ( 1189 x 1682 ) mm  = ( 46.81 x 66.22 ) in
2285
        // Variations on the ISO Standard
2286
        'A2_EXTRA'               => array( 1261.417,  1754.646), // = (  445 x 619  ) mm  = ( 17.52 x 24.37 ) in
2287
        'A3+'                    => array(  932.598,  1369.134), // = (  329 x 483  ) mm  = ( 12.95 x 19.02 ) in
2288
        'A3_EXTRA'               => array(  912.756,  1261.417), // = (  322 x 445  ) mm  = ( 12.68 x 17.52 ) in
2289
        'A3_SUPER'               => array(  864.567,  1440.000), // = (  305 x 508  ) mm  = ( 12.01 x 20.00 ) in
2290
        'SUPER_A3'               => array(  864.567,  1380.472), // = (  305 x 487  ) mm  = ( 12.01 x 19.17 ) in
2291
        'A4_EXTRA'               => array(  666.142,   912.756), // = (  235 x 322  ) mm  = (  9.25 x 12.68 ) in
2292
        'A4_SUPER'               => array(  649.134,   912.756), // = (  229 x 322  ) mm  = (  9.02 x 12.68 ) in
2293
        'SUPER_A4'               => array(  643.465,  1009.134), // = (  227 x 356  ) mm  = (  8.94 x 14.02 ) in
2294
        'A4_LONG'                => array(  595.276,   986.457), // = (  210 x 348  ) mm  = (  8.27 x 13.70 ) in
2295
        'F4'                     => array(  595.276,   935.433), // = (  210 x 330  ) mm  = (  8.27 x 12.99 ) in
2296
        'SO_B5_EXTRA'            => array(  572.598,   782.362), // = (  202 x 276  ) mm  = (  7.95 x 10.87 ) in
2297
        'A5_EXTRA'               => array(  490.394,   666.142), // = (  173 x 235  ) mm  = (  6.81 x 9.25  ) in
2298
        // ANSI Series
2299
        'ANSI_E'                 => array( 2448.000,  3168.000), // = (  864 x 1118 ) mm  = ( 34.00 x 44.00 ) in
2300
        'ANSI_D'                 => array( 1584.000,  2448.000), // = (  559 x 864  ) mm  = ( 22.00 x 34.00 ) in
2301
        'ANSI_C'                 => array( 1224.000,  1584.000), // = (  432 x 559  ) mm  = ( 17.00 x 22.00 ) in
2302
        'ANSI_B'                 => array(  792.000,  1224.000), // = (  279 x 432  ) mm  = ( 11.00 x 17.00 ) in
2303
        'ANSI_A'                 => array(  612.000,   792.000), // = (  216 x 279  ) mm  = (  8.50 x 11.00 ) in
2304
        // Traditional 'Loose' North American Paper Sizes
2305
        'USLEDGER'               => array( 1224.000,   792.000), // = (  432 x 279  ) mm  = ( 17.00 x 11.00 ) in
2306
        'LEDGER'                 => array( 1224.000,   792.000), // = (  432 x 279  ) mm  = ( 17.00 x 11.00 ) in
2307
        'ORGANIZERK'             => array(  792.000,  1224.000), // = (  279 x 432  ) mm  = ( 11.00 x 17.00 ) in
2308
        'BIBLE'                  => array(  792.000,  1224.000), // = (  279 x 432  ) mm  = ( 11.00 x 17.00 ) in
2309
        'USTABLOID'              => array(  792.000,  1224.000), // = (  279 x 432  ) mm  = ( 11.00 x 17.00 ) in
2310
        'TABLOID'                => array(  792.000,  1224.000), // = (  279 x 432  ) mm  = ( 11.00 x 17.00 ) in
2311
        'ORGANIZERM'             => array(  612.000,   792.000), // = (  216 x 279  ) mm  = (  8.50 x 11.00 ) in
2312
        'USLETTER'               => array(  612.000,   792.000), // = (  216 x 279  ) mm  = (  8.50 x 11.00 ) in
2313
        'LETTER'                 => array(  612.000,   792.000), // = (  216 x 279  ) mm  = (  8.50 x 11.00 ) in
2314
        'USLEGAL'                => array(  612.000,  1008.000), // = (  216 x 356  ) mm  = (  8.50 x 14.00 ) in
2315
        'LEGAL'                  => array(  612.000,  1008.000), // = (  216 x 356  ) mm  = (  8.50 x 14.00 ) in
2316
        'GOVERNMENTLETTER'       => array(  576.000,   756.000), // = (  203 x 267  ) mm  = (  8.00 x 10.50 ) in
2317
        'GLETTER'                => array(  576.000,   756.000), // = (  203 x 267  ) mm  = (  8.00 x 10.50 ) in
2318
        'JUNIORLEGAL'            => array(  576.000,   360.000), // = (  203 x 127  ) mm  = (  8.00 x 5.00  ) in
2319
        'JLEGAL'                 => array(  576.000,   360.000), // = (  203 x 127  ) mm  = (  8.00 x 5.00  ) in
2320
        // Other North American Paper Sizes
2321
        'QUADDEMY'               => array( 2520.000,  3240.000), // = (  889 x 1143 ) mm  = ( 35.00 x 45.00 ) in
2322
        'SUPER_B'                => array(  936.000,  1368.000), // = (  330 x 483  ) mm  = ( 13.00 x 19.00 ) in
2323
        'QUARTO'                 => array(  648.000,   792.000), // = (  229 x 279  ) mm  = (  9.00 x 11.00 ) in
2324
        'GOVERNMENTLEGAL'        => array(  612.000,   936.000), // = (  216 x 330  ) mm  = (  8.50 x 13.00 ) in
2325
        'FOLIO'                  => array(  612.000,   936.000), // = (  216 x 330  ) mm  = (  8.50 x 13.00 ) in
2326
        'MONARCH'                => array(  522.000,   756.000), // = (  184 x 267  ) mm  = (  7.25 x 10.50 ) in
2327
        'EXECUTIVE'              => array(  522.000,   756.000), // = (  184 x 267  ) mm  = (  7.25 x 10.50 ) in
2328
        'ORGANIZERL'             => array(  396.000,   612.000), // = (  140 x 216  ) mm  = (  5.50 x 8.50  ) in
2329
        'STATEMENT'              => array(  396.000,   612.000), // = (  140 x 216  ) mm  = (  5.50 x 8.50  ) in
2330
        'MEMO'                   => array(  396.000,   612.000), // = (  140 x 216  ) mm  = (  5.50 x 8.50  ) in
2331
        'FOOLSCAP'               => array(  595.440,   936.000), // = (  210 x 330  ) mm  = (  8.27 x 13.00 ) in
2332
        'COMPACT'                => array(  306.000,   486.000), // = (  108 x 171  ) mm  = (  4.25 x 6.75  ) in
2333
        'ORGANIZERJ'             => array(  198.000,   360.000), // = (   70 x 127  ) mm  = (  2.75 x 5.00  ) in
2334
        // Canadian standard CAN 2-9.60M
2335
        'P1'                     => array( 1587.402,  2437.795), // = (  560 x 860  ) mm  = ( 22.05 x 33.86 ) in
2336
        'P2'                     => array( 1218.898,  1587.402), // = (  430 x 560  ) mm  = ( 16.93 x 22.05 ) in
2337
        'P3'                     => array(  793.701,  1218.898), // = (  280 x 430  ) mm  = ( 11.02 x 16.93 ) in
2338
        'P4'                     => array(  609.449,   793.701), // = (  215 x 280  ) mm  = (  8.46 x 11.02 ) in
2339
        'P5'                     => array(  396.850,   609.449), // = (  140 x 215  ) mm  = (  5.51 x 8.46  ) in
2340
        'P6'                     => array(  303.307,   396.850), // = (  107 x 140  ) mm  = (  4.21 x 5.51  ) in
2341
        // North American Architectural Sizes
2342
        'ARCH_E'                 => array( 2592.000,  3456.000), // = (  914 x 1219 ) mm  = ( 36.00 x 48.00 ) in
2343
        'ARCH_E1'                => array( 2160.000,  3024.000), // = (  762 x 1067 ) mm  = ( 30.00 x 42.00 ) in
2344
        'ARCH_D'                 => array( 1728.000,  2592.000), // = (  610 x 914  ) mm  = ( 24.00 x 36.00 ) in
2345
        'BROADSHEET'             => array( 1296.000,  1728.000), // = (  457 x 610  ) mm  = ( 18.00 x 24.00 ) in
2346
        'ARCH_C'                 => array( 1296.000,  1728.000), // = (  457 x 610  ) mm  = ( 18.00 x 24.00 ) in
2347
        'ARCH_B'                 => array(  864.000,  1296.000), // = (  305 x 457  ) mm  = ( 12.00 x 18.00 ) in
2348
        'ARCH_A'                 => array(  648.000,   864.000), // = (  229 x 305  ) mm  = (  9.00 x 12.00 ) in
2349
        // -- North American Envelope Sizes
2350
        // - Announcement Envelopes
2351
        'ANNENV_A2'              => array(  314.640,   414.000), // = (  111 x 146  ) mm  = (  4.37 x 5.75  ) in
2352
        'ANNENV_A6'              => array(  342.000,   468.000), // = (  121 x 165  ) mm  = (  4.75 x 6.50  ) in
2353
        'ANNENV_A7'              => array(  378.000,   522.000), // = (  133 x 184  ) mm  = (  5.25 x 7.25  ) in
2354
        'ANNENV_A8'              => array(  396.000,   584.640), // = (  140 x 206  ) mm  = (  5.50 x 8.12  ) in
2355
        'ANNENV_A10'             => array(  450.000,   692.640), // = (  159 x 244  ) mm  = (  6.25 x 9.62  ) in
2356
        'ANNENV_SLIM'            => array(  278.640,   638.640), // = (   98 x 225  ) mm  = (  3.87 x 8.87  ) in
2357
        // - Commercial Envelopes
2358
        'COMMENV_N6_1/4'         => array(  252.000,   432.000), // = (   89 x 152  ) mm  = (  3.50 x 6.00  ) in
2359
        'COMMENV_N6_3/4'         => array(  260.640,   468.000), // = (   92 x 165  ) mm  = (  3.62 x 6.50  ) in
2360
        'COMMENV_N8'             => array(  278.640,   540.000), // = (   98 x 191  ) mm  = (  3.87 x 7.50  ) in
2361
        'COMMENV_N9'             => array(  278.640,   638.640), // = (   98 x 225  ) mm  = (  3.87 x 8.87  ) in
2362
        'COMMENV_N10'            => array(  296.640,   684.000), // = (  105 x 241  ) mm  = (  4.12 x 9.50  ) in
2363
        'COMMENV_N11'            => array(  324.000,   746.640), // = (  114 x 263  ) mm  = (  4.50 x 10.37 ) in
2364
        'COMMENV_N12'            => array(  342.000,   792.000), // = (  121 x 279  ) mm  = (  4.75 x 11.00 ) in
2365
        'COMMENV_N14'            => array(  360.000,   828.000), // = (  127 x 292  ) mm  = (  5.00 x 11.50 ) in
2366
        // - Catalogue Envelopes
2367
        'CATENV_N1'              => array(  432.000,   648.000), // = (  152 x 229  ) mm  = (  6.00 x 9.00  ) in
2368
        'CATENV_N1_3/4'          => array(  468.000,   684.000), // = (  165 x 241  ) mm  = (  6.50 x 9.50  ) in
2369
        'CATENV_N2'              => array(  468.000,   720.000), // = (  165 x 254  ) mm  = (  6.50 x 10.00 ) in
2370
        'CATENV_N3'              => array(  504.000,   720.000), // = (  178 x 254  ) mm  = (  7.00 x 10.00 ) in
2371
        'CATENV_N6'              => array(  540.000,   756.000), // = (  191 x 267  ) mm  = (  7.50 x 10.50 ) in
2372
        'CATENV_N7'              => array(  576.000,   792.000), // = (  203 x 279  ) mm  = (  8.00 x 11.00 ) in
2373
        'CATENV_N8'              => array(  594.000,   810.000), // = (  210 x 286  ) mm  = (  8.25 x 11.25 ) in
2374
        'CATENV_N9_1/2'          => array(  612.000,   756.000), // = (  216 x 267  ) mm  = (  8.50 x 10.50 ) in
2375
        'CATENV_N9_3/4'          => array(  630.000,   810.000), // = (  222 x 286  ) mm  = (  8.75 x 11.25 ) in
2376
        'CATENV_N10_1/2'         => array(  648.000,   864.000), // = (  229 x 305  ) mm  = (  9.00 x 12.00 ) in
2377
        'CATENV_N12_1/2'         => array(  684.000,   900.000), // = (  241 x 318  ) mm  = (  9.50 x 12.50 ) in
2378
        'CATENV_N13_1/2'         => array(  720.000,   936.000), // = (  254 x 330  ) mm  = ( 10.00 x 13.00 ) in
2379
        'CATENV_N14_1/4'         => array(  810.000,   882.000), // = (  286 x 311  ) mm  = ( 11.25 x 12.25 ) in
2380
        'CATENV_N14_1/2'         => array(  828.000,  1044.000), // = (  292 x 368  ) mm  = ( 11.50 x 14.50 ) in
2381
        // Japanese (JIS P 0138-61) Standard B-Series
2382
        'JIS_B0'                 => array( 2919.685,  4127.244), // = ( 1030 x 1456 ) mm  = ( 40.55 x 57.32 ) in
2383
        'JIS_B1'                 => array( 2063.622,  2919.685), // = (  728 x 1030 ) mm  = ( 28.66 x 40.55 ) in
2384
        'JIS_B2'                 => array( 1459.843,  2063.622), // = (  515 x 728  ) mm  = ( 20.28 x 28.66 ) in
2385
        'JIS_B3'                 => array( 1031.811,  1459.843), // = (  364 x 515  ) mm  = ( 14.33 x 20.28 ) in
2386
        'JIS_B4'                 => array(  728.504,  1031.811), // = (  257 x 364  ) mm  = ( 10.12 x 14.33 ) in
2387
        'JIS_B5'                 => array(  515.906,   728.504), // = (  182 x 257  ) mm  = (  7.17 x 10.12 ) in
2388
        'JIS_B6'                 => array(  362.835,   515.906), // = (  128 x 182  ) mm  = (  5.04 x 7.17  ) in
2389
        'JIS_B7'                 => array(  257.953,   362.835), // = (   91 x 128  ) mm  = (  3.58 x 5.04  ) in
2390
        'JIS_B8'                 => array(  181.417,   257.953), // = (   64 x 91   ) mm  = (  2.52 x 3.58  ) in
2391
        'JIS_B9'                 => array(  127.559,   181.417), // = (   45 x 64   ) mm  = (  1.77 x 2.52  ) in
2392
        'JIS_B10'                => array(   90.709,   127.559), // = (   32 x 45   ) mm  = (  1.26 x 1.77  ) in
2393
        'JIS_B11'                => array(   62.362,    90.709), // = (   22 x 32   ) mm  = (  0.87 x 1.26  ) in
2394
        'JIS_B12'                => array(   45.354,    62.362), // = (   16 x 22   ) mm  = (  0.63 x 0.87  ) in
2395
        // PA Series
2396
        'PA0'                    => array( 2381.102,  3174.803), // = (  840 x 1120 ) mm  = ( 33.07 x 44.09 ) in
2397
        'PA1'                    => array( 1587.402,  2381.102), // = (  560 x 840  ) mm  = ( 22.05 x 33.07 ) in
2398
        'PA2'                    => array( 1190.551,  1587.402), // = (  420 x 560  ) mm  = ( 16.54 x 22.05 ) in
2399
        'PA3'                    => array(  793.701,  1190.551), // = (  280 x 420  ) mm  = ( 11.02 x 16.54 ) in
2400
        'PA4'                    => array(  595.276,   793.701), // = (  210 x 280  ) mm  = (  8.27 x 11.02 ) in
2401
        'PA5'                    => array(  396.850,   595.276), // = (  140 x 210  ) mm  = (  5.51 x 8.27  ) in
2402
        'PA6'                    => array(  297.638,   396.850), // = (  105 x 140  ) mm  = (  4.13 x 5.51  ) in
2403
        'PA7'                    => array(  198.425,   297.638), // = (   70 x 105  ) mm  = (  2.76 x 4.13  ) in
2404
        'PA8'                    => array(  147.402,   198.425), // = (   52 x 70   ) mm  = (  2.05 x 2.76  ) in
2405
        'PA9'                    => array(   99.213,   147.402), // = (   35 x 52   ) mm  = (  1.38 x 2.05  ) in
2406
        'PA10'                   => array(   73.701,    99.213), // = (   26 x 35   ) mm  = (  1.02 x 1.38  ) in
2407
        // Standard Photographic Print Sizes
2408
        'PASSPORT_PHOTO'         => array(   99.213,   127.559), // = (   35 x 45   ) mm  = (  1.38 x 1.77  ) in
2409
        'E'                      => array(  233.858,   340.157), // = (   82 x 120  ) mm  = (  3.25 x 4.72  ) in
2410
        'L'                      => array(  252.283,   360.000), // = (   89 x 127  ) mm  = (  3.50 x 5.00  ) in
2411
        '3R'                     => array(  252.283,   360.000), // = (   89 x 127  ) mm  = (  3.50 x 5.00  ) in
2412
        'KG'                     => array(  289.134,   430.866), // = (  102 x 152  ) mm  = (  4.02 x 5.98  ) in
2413
        '4R'                     => array(  289.134,   430.866), // = (  102 x 152  ) mm  = (  4.02 x 5.98  ) in
2414
        '4D'                     => array(  340.157,   430.866), // = (  120 x 152  ) mm  = (  4.72 x 5.98  ) in
2415
        '2L'                     => array(  360.000,   504.567), // = (  127 x 178  ) mm  = (  5.00 x 7.01  ) in
2416
        '5R'                     => array(  360.000,   504.567), // = (  127 x 178  ) mm  = (  5.00 x 7.01  ) in
2417
        '8P'                     => array(  430.866,   575.433), // = (  152 x 203  ) mm  = (  5.98 x 7.99  ) in
2418
        '6R'                     => array(  430.866,   575.433), // = (  152 x 203  ) mm  = (  5.98 x 7.99  ) in
2419
        '6P'                     => array(  575.433,   720.000), // = (  203 x 254  ) mm  = (  7.99 x 10.00 ) in
2420
        '8R'                     => array(  575.433,   720.000), // = (  203 x 254  ) mm  = (  7.99 x 10.00 ) in
2421
        '6PW'                    => array(  575.433,   864.567), // = (  203 x 305  ) mm  = (  7.99 x 12.01 ) in
2422
        'S8R'                    => array(  575.433,   864.567), // = (  203 x 305  ) mm  = (  7.99 x 12.01 ) in
2423
        '4P'                     => array(  720.000,   864.567), // = (  254 x 305  ) mm  = ( 10.00 x 12.01 ) in
2424
        '10R'                    => array(  720.000,   864.567), // = (  254 x 305  ) mm  = ( 10.00 x 12.01 ) in
2425
        '4PW'                    => array(  720.000,  1080.000), // = (  254 x 381  ) mm  = ( 10.00 x 15.00 ) in
2426
        'S10R'                   => array(  720.000,  1080.000), // = (  254 x 381  ) mm  = ( 10.00 x 15.00 ) in
2427
        '11R'                    => array(  790.866,  1009.134), // = (  279 x 356  ) mm  = ( 10.98 x 14.02 ) in
2428
        'S11R'                   => array(  790.866,  1224.567), // = (  279 x 432  ) mm  = ( 10.98 x 17.01 ) in
2429
        '12R'                    => array(  864.567,  1080.000), // = (  305 x 381  ) mm  = ( 12.01 x 15.00 ) in
2430
        'S12R'                   => array(  864.567,  1292.598), // = (  305 x 456  ) mm  = ( 12.01 x 17.95 ) in
2431
        // Common Newspaper Sizes
2432
        'NEWSPAPER_BROADSHEET'   => array( 2125.984,  1700.787), // = (  750 x 600  ) mm  = ( 29.53 x 23.62 ) in
2433
        'NEWSPAPER_BERLINER'     => array( 1332.283,   892.913), // = (  470 x 315  ) mm  = ( 18.50 x 12.40 ) in
2434
        'NEWSPAPER_TABLOID'      => array( 1218.898,   793.701), // = (  430 x 280  ) mm  = ( 16.93 x 11.02 ) in
2435
        'NEWSPAPER_COMPACT'      => array( 1218.898,   793.701), // = (  430 x 280  ) mm  = ( 16.93 x 11.02 ) in
2436
        // Business Cards
2437
        'CREDIT_CARD'            => array(  153.014,   242.646), // = (   54 x 86   ) mm  = (  2.13 x 3.37  ) in
2438
        'BUSINESS_CARD'          => array(  153.014,   242.646), // = (   54 x 86   ) mm  = (  2.13 x 3.37  ) in
2439
        'BUSINESS_CARD_ISO7810'  => array(  153.014,   242.646), // = (   54 x 86   ) mm  = (  2.13 x 3.37  ) in
2440
        'BUSINESS_CARD_ISO216'   => array(  147.402,   209.764), // = (   52 x 74   ) mm  = (  2.05 x 2.91  ) in
2441
        'BUSINESS_CARD_IT'       => array(  155.906,   240.945), // = (   55 x 85   ) mm  = (  2.17 x 3.35  ) in
2442
        'BUSINESS_CARD_UK'       => array(  155.906,   240.945), // = (   55 x 85   ) mm  = (  2.17 x 3.35  ) in
2443
        'BUSINESS_CARD_FR'       => array(  155.906,   240.945), // = (   55 x 85   ) mm  = (  2.17 x 3.35  ) in
2444
        'BUSINESS_CARD_DE'       => array(  155.906,   240.945), // = (   55 x 85   ) mm  = (  2.17 x 3.35  ) in
2445
        'BUSINESS_CARD_ES'       => array(  155.906,   240.945), // = (   55 x 85   ) mm  = (  2.17 x 3.35  ) in
2446
        'BUSINESS_CARD_CA'       => array(  144.567,   252.283), // = (   51 x 89   ) mm  = (  2.01 x 3.50  ) in
2447
        'BUSINESS_CARD_US'       => array(  144.567,   252.283), // = (   51 x 89   ) mm  = (  2.01 x 3.50  ) in
2448
        'BUSINESS_CARD_JP'       => array(  155.906,   257.953), // = (   55 x 91   ) mm  = (  2.17 x 3.58  ) in
2449
        'BUSINESS_CARD_HK'       => array(  153.071,   255.118), // = (   54 x 90   ) mm  = (  2.13 x 3.54  ) in
2450
        'BUSINESS_CARD_AU'       => array(  155.906,   255.118), // = (   55 x 90   ) mm  = (  2.17 x 3.54  ) in
2451
        'BUSINESS_CARD_DK'       => array(  155.906,   255.118), // = (   55 x 90   ) mm  = (  2.17 x 3.54  ) in
2452
        'BUSINESS_CARD_SE'       => array(  155.906,   255.118), // = (   55 x 90   ) mm  = (  2.17 x 3.54  ) in
2453
        'BUSINESS_CARD_RU'       => array(  141.732,   255.118), // = (   50 x 90   ) mm  = (  1.97 x 3.54  ) in
2454
        'BUSINESS_CARD_CZ'       => array(  141.732,   255.118), // = (   50 x 90   ) mm  = (  1.97 x 3.54  ) in
2455
        'BUSINESS_CARD_FI'       => array(  141.732,   255.118), // = (   50 x 90   ) mm  = (  1.97 x 3.54  ) in
2456
        'BUSINESS_CARD_HU'       => array(  141.732,   255.118), // = (   50 x 90   ) mm  = (  1.97 x 3.54  ) in
2457
        'BUSINESS_CARD_IL'       => array(  141.732,   255.118), // = (   50 x 90   ) mm  = (  1.97 x 3.54  ) in
2458
        // Billboards
2459
        '4SHEET'                 => array( 2880.000,  4320.000), // = ( 1016 x 1524 ) mm  = ( 40.00 x 60.00 ) in
2460
        '6SHEET'                 => array( 3401.575,  5102.362), // = ( 1200 x 1800 ) mm  = ( 47.24 x 70.87 ) in
2461
        '12SHEET'                => array( 8640.000,  4320.000), // = ( 3048 x 1524 ) mm  = (120.00 x 60.00 ) in
2462
        '16SHEET'                => array( 5760.000,  8640.000), // = ( 2032 x 3048 ) mm  = ( 80.00 x 120.00) in
2463
        '32SHEET'                => array(11520.000,  8640.000), // = ( 4064 x 3048 ) mm  = (160.00 x 120.00) in
2464
        '48SHEET'                => array(17280.000,  8640.000), // = ( 6096 x 3048 ) mm  = (240.00 x 120.00) in
2465
        '64SHEET'                => array(23040.000,  8640.000), // = ( 8128 x 3048 ) mm  = (320.00 x 120.00) in
2466
        '96SHEET'                => array(34560.000,  8640.000), // = (12192 x 3048 ) mm  = (480.00 x 120.00) in
2467
        // -- Old European Sizes
2468
        // - Old Imperial English Sizes
2469
        'EN_EMPEROR'             => array( 3456.000,  5184.000), // = ( 1219 x 1829 ) mm  = ( 48.00 x 72.00 ) in
2470
        'EN_ANTIQUARIAN'         => array( 2232.000,  3816.000), // = (  787 x 1346 ) mm  = ( 31.00 x 53.00 ) in
2471
        'EN_GRAND_EAGLE'         => array( 2070.000,  3024.000), // = (  730 x 1067 ) mm  = ( 28.75 x 42.00 ) in
2472
        'EN_DOUBLE_ELEPHANT'     => array( 1926.000,  2880.000), // = (  679 x 1016 ) mm  = ( 26.75 x 40.00 ) in
2473
        'EN_ATLAS'               => array( 1872.000,  2448.000), // = (  660 x 864  ) mm  = ( 26.00 x 34.00 ) in
2474
        'EN_COLOMBIER'           => array( 1692.000,  2484.000), // = (  597 x 876  ) mm  = ( 23.50 x 34.50 ) in
2475
        'EN_ELEPHANT'            => array( 1656.000,  2016.000), // = (  584 x 711  ) mm  = ( 23.00 x 28.00 ) in
2476
        'EN_DOUBLE_DEMY'         => array( 1620.000,  2556.000), // = (  572 x 902  ) mm  = ( 22.50 x 35.50 ) in
2477
        'EN_IMPERIAL'            => array( 1584.000,  2160.000), // = (  559 x 762  ) mm  = ( 22.00 x 30.00 ) in
2478
        'EN_PRINCESS'            => array( 1548.000,  2016.000), // = (  546 x 711  ) mm  = ( 21.50 x 28.00 ) in
2479
        'EN_CARTRIDGE'           => array( 1512.000,  1872.000), // = (  533 x 660  ) mm  = ( 21.00 x 26.00 ) in
2480
        'EN_DOUBLE_LARGE_POST'   => array( 1512.000,  2376.000), // = (  533 x 838  ) mm  = ( 21.00 x 33.00 ) in
2481
        'EN_ROYAL'               => array( 1440.000,  1800.000), // = (  508 x 635  ) mm  = ( 20.00 x 25.00 ) in
2482
        'EN_SHEET'               => array( 1404.000,  1692.000), // = (  495 x 597  ) mm  = ( 19.50 x 23.50 ) in
2483
        'EN_HALF_POST'           => array( 1404.000,  1692.000), // = (  495 x 597  ) mm  = ( 19.50 x 23.50 ) in
2484
        'EN_SUPER_ROYAL'         => array( 1368.000,  1944.000), // = (  483 x 686  ) mm  = ( 19.00 x 27.00 ) in
2485
        'EN_DOUBLE_POST'         => array( 1368.000,  2196.000), // = (  483 x 775  ) mm  = ( 19.00 x 30.50 ) in
2486
        'EN_MEDIUM'              => array( 1260.000,  1656.000), // = (  445 x 584  ) mm  = ( 17.50 x 23.00 ) in
2487
        'EN_DEMY'                => array( 1260.000,  1620.000), // = (  445 x 572  ) mm  = ( 17.50 x 22.50 ) in
2488
        'EN_LARGE_POST'          => array( 1188.000,  1512.000), // = (  419 x 533  ) mm  = ( 16.50 x 21.00 ) in
2489
        'EN_COPY_DRAUGHT'        => array( 1152.000,  1440.000), // = (  406 x 508  ) mm  = ( 16.00 x 20.00 ) in
2490
        'EN_POST'                => array( 1116.000,  1386.000), // = (  394 x 489  ) mm  = ( 15.50 x 19.25 ) in
2491
        'EN_CROWN'               => array( 1080.000,  1440.000), // = (  381 x 508  ) mm  = ( 15.00 x 20.00 ) in
2492
        'EN_PINCHED_POST'        => array( 1062.000,  1332.000), // = (  375 x 470  ) mm  = ( 14.75 x 18.50 ) in
2493
        'EN_BRIEF'               => array(  972.000,  1152.000), // = (  343 x 406  ) mm  = ( 13.50 x 16.00 ) in
2494
        'EN_FOOLSCAP'            => array(  972.000,  1224.000), // = (  343 x 432  ) mm  = ( 13.50 x 17.00 ) in
2495
        'EN_SMALL_FOOLSCAP'      => array(  954.000,  1188.000), // = (  337 x 419  ) mm  = ( 13.25 x 16.50 ) in
2496
        'EN_POTT'                => array(  900.000,  1080.000), // = (  318 x 381  ) mm  = ( 12.50 x 15.00 ) in
2497
        // - Old Imperial Belgian Sizes
2498
        'BE_GRAND_AIGLE'         => array( 1984.252,  2948.031), // = (  700 x 1040 ) mm  = ( 27.56 x 40.94 ) in
2499
        'BE_COLOMBIER'           => array( 1757.480,  2409.449), // = (  620 x 850  ) mm  = ( 24.41 x 33.46 ) in
2500
        'BE_DOUBLE_CARRE'        => array( 1757.480,  2607.874), // = (  620 x 920  ) mm  = ( 24.41 x 36.22 ) in
2501
        'BE_ELEPHANT'            => array( 1746.142,  2182.677), // = (  616 x 770  ) mm  = ( 24.25 x 30.31 ) in
2502
        'BE_PETIT_AIGLE'         => array( 1700.787,  2381.102), // = (  600 x 840  ) mm  = ( 23.62 x 33.07 ) in
2503
        'BE_GRAND_JESUS'         => array( 1559.055,  2069.291), // = (  550 x 730  ) mm  = ( 21.65 x 28.74 ) in
2504
        'BE_JESUS'               => array( 1530.709,  2069.291), // = (  540 x 730  ) mm  = ( 21.26 x 28.74 ) in
2505
        'BE_RAISIN'              => array( 1417.323,  1842.520), // = (  500 x 650  ) mm  = ( 19.69 x 25.59 ) in
2506
        'BE_GRAND_MEDIAN'        => array( 1303.937,  1714.961), // = (  460 x 605  ) mm  = ( 18.11 x 23.82 ) in
2507
        'BE_DOUBLE_POSTE'        => array( 1233.071,  1601.575), // = (  435 x 565  ) mm  = ( 17.13 x 22.24 ) in
2508
        'BE_COQUILLE'            => array( 1218.898,  1587.402), // = (  430 x 560  ) mm  = ( 16.93 x 22.05 ) in
2509
        'BE_PETIT_MEDIAN'        => array( 1176.378,  1502.362), // = (  415 x 530  ) mm  = ( 16.34 x 20.87 ) in
2510
        'BE_RUCHE'               => array( 1020.472,  1303.937), // = (  360 x 460  ) mm  = ( 14.17 x 18.11 ) in
2511
        'BE_PROPATRIA'           => array(  977.953,  1218.898), // = (  345 x 430  ) mm  = ( 13.58 x 16.93 ) in
2512
        'BE_LYS'                 => array(  898.583,  1125.354), // = (  317 x 397  ) mm  = ( 12.48 x 15.63 ) in
2513
        'BE_POT'                 => array(  870.236,  1088.504), // = (  307 x 384  ) mm  = ( 12.09 x 15.12 ) in
2514
        'BE_ROSETTE'             => array(  765.354,   983.622), // = (  270 x 347  ) mm  = ( 10.63 x 13.66 ) in
2515
        // - Old Imperial French Sizes
2516
        'FR_UNIVERS'             => array( 2834.646,  3685.039), // = ( 1000 x 1300 ) mm  = ( 39.37 x 51.18 ) in
2517
        'FR_DOUBLE_COLOMBIER'    => array( 2551.181,  3571.654), // = (  900 x 1260 ) mm  = ( 35.43 x 49.61 ) in
2518
        'FR_GRANDE_MONDE'        => array( 2551.181,  3571.654), // = (  900 x 1260 ) mm  = ( 35.43 x 49.61 ) in
2519
        'FR_DOUBLE_SOLEIL'       => array( 2267.717,  3401.575), // = (  800 x 1200 ) mm  = ( 31.50 x 47.24 ) in
2520
        'FR_DOUBLE_JESUS'        => array( 2154.331,  3174.803), // = (  760 x 1120 ) mm  = ( 29.92 x 44.09 ) in
2521
        'FR_GRAND_AIGLE'         => array( 2125.984,  3004.724), // = (  750 x 1060 ) mm  = ( 29.53 x 41.73 ) in
2522
        'FR_PETIT_AIGLE'         => array( 1984.252,  2664.567), // = (  700 x 940  ) mm  = ( 27.56 x 37.01 ) in
2523
        'FR_DOUBLE_RAISIN'       => array( 1842.520,  2834.646), // = (  650 x 1000 ) mm  = ( 25.59 x 39.37 ) in
2524
        'FR_JOURNAL'             => array( 1842.520,  2664.567), // = (  650 x 940  ) mm  = ( 25.59 x 37.01 ) in
2525
        'FR_COLOMBIER_AFFICHE'   => array( 1785.827,  2551.181), // = (  630 x 900  ) mm  = ( 24.80 x 35.43 ) in
2526
        'FR_DOUBLE_CAVALIER'     => array( 1757.480,  2607.874), // = (  620 x 920  ) mm  = ( 24.41 x 36.22 ) in
2527
        'FR_CLOCHE'              => array( 1700.787,  2267.717), // = (  600 x 800  ) mm  = ( 23.62 x 31.50 ) in
2528
        'FR_SOLEIL'              => array( 1700.787,  2267.717), // = (  600 x 800  ) mm  = ( 23.62 x 31.50 ) in
2529
        'FR_DOUBLE_CARRE'        => array( 1587.402,  2551.181), // = (  560 x 900  ) mm  = ( 22.05 x 35.43 ) in
2530
        'FR_DOUBLE_COQUILLE'     => array( 1587.402,  2494.488), // = (  560 x 880  ) mm  = ( 22.05 x 34.65 ) in
2531
        'FR_JESUS'               => array( 1587.402,  2154.331), // = (  560 x 760  ) mm  = ( 22.05 x 29.92 ) in
2532
        'FR_RAISIN'              => array( 1417.323,  1842.520), // = (  500 x 650  ) mm  = ( 19.69 x 25.59 ) in
2533
        'FR_CAVALIER'            => array( 1303.937,  1757.480), // = (  460 x 620  ) mm  = ( 18.11 x 24.41 ) in
2534
        'FR_DOUBLE_COURONNE'     => array( 1303.937,  2040.945), // = (  460 x 720  ) mm  = ( 18.11 x 28.35 ) in
2535
        'FR_CARRE'               => array( 1275.591,  1587.402), // = (  450 x 560  ) mm  = ( 17.72 x 22.05 ) in
2536
        'FR_COQUILLE'            => array( 1247.244,  1587.402), // = (  440 x 560  ) mm  = ( 17.32 x 22.05 ) in
2537
        'FR_DOUBLE_TELLIERE'     => array( 1247.244,  1927.559), // = (  440 x 680  ) mm  = ( 17.32 x 26.77 ) in
2538
        'FR_DOUBLE_CLOCHE'       => array( 1133.858,  1700.787), // = (  400 x 600  ) mm  = ( 15.75 x 23.62 ) in
2539
        'FR_DOUBLE_POT'          => array( 1133.858,  1757.480), // = (  400 x 620  ) mm  = ( 15.75 x 24.41 ) in
2540
        'FR_ECU'                 => array( 1133.858,  1474.016), // = (  400 x 520  ) mm  = ( 15.75 x 20.47 ) in
2541
        'FR_COURONNE'            => array( 1020.472,  1303.937), // = (  360 x 460  ) mm  = ( 14.17 x 18.11 ) in
2542
        'FR_TELLIERE'            => array(  963.780,  1247.244), // = (  340 x 440  ) mm  = ( 13.39 x 17.32 ) in
2543
        'FR_POT'                 => array(  878.740,  1133.858), // = (  310 x 400  ) mm  = ( 12.20 x 15.75 ) in
2544
    );
2545
2546
2547
    /**
2548
     * Get page dimensions from format name.
2549
     * @param mixed $format The format name @see self::$page_format<ul>
2550
     * @return array containing page width and height in points
2551
     * @since 5.0.010 (2010-05-17)
2552
     * @public static
2553
     */
2554
    public static function getPageSizeFromFormat($format)
2555
    {
2556
        if (isset(self::$page_formats[$format])) {
2557
            return self::$page_formats[$format];
2558
        }
2559
        return self::$page_formats['A4'];
2560
    }
2561
2562
    /**
2563
     * Set page boundaries.
2564
     * @param int $page page number
2565
     * @param string $type valid values are: <ul><li>'MediaBox' : the boundaries of the physical medium on which the page shall be displayed or printed;</li><li>'CropBox' : the visible region of default user space;</li><li>'BleedBox' : the region to which the contents of the page shall be clipped when output in a production environment;</li><li>'TrimBox' : the intended dimensions of the finished page after trimming;</li><li>'ArtBox' : the page's meaningful content (including potential white space).</li></ul>
2566
     * @param float $llx lower-left x coordinate in user units.
2567
     * @param float $lly lower-left y coordinate in user units.
2568
     * @param float $urx upper-right x coordinate in user units.
2569
     * @param float $ury upper-right y coordinate in user units.
2570
     * @param boolean $points If true uses user units as unit of measure, otherwise uses PDF points.
2571
     * @param float $k Scale factor (number of points in user unit).
2572
     * @param array $pagedim Array of page dimensions.
2573
     * @return array pagedim array of page dimensions.
2574
     * @since 5.0.010 (2010-05-17)
2575
     * @public static
2576
     */
2577
    public static function setPageBoxes($page, $type, $llx, $lly, $urx, $ury, $points, $k, $pagedim = array())
2578
    {
2579
        if (!isset($pagedim[$page])) {
2580
            // initialize array
2581
            $pagedim[$page] = array();
2582
        }
2583
        if (!in_array($type, self::$pageboxes)) {
2584
            return;
2585
        }
2586
        if ($points) {
2587
            $k = 1;
2588
        }
2589
        $pagedim[$page][$type]['llx'] = ($llx * $k);
2590
        $pagedim[$page][$type]['lly'] = ($lly * $k);
2591
        $pagedim[$page][$type]['urx'] = ($urx * $k);
2592
        $pagedim[$page][$type]['ury'] = ($ury * $k);
2593
        return $pagedim;
2594
    }
2595
2596
    /**
2597
     * Swap X and Y coordinates of page boxes (change page boxes orientation).
2598
     * @param int $page page number
2599
     * @param array $pagedim Array of page dimensions.
2600
     * @return array pagedim array of page dimensions.
2601
     * @since 5.0.010 (2010-05-17)
2602
     * @public static
2603
     */
2604
    public static function swapPageBoxCoordinates($page, $pagedim)
2605
    {
2606
        foreach (self::$pageboxes as $type) {
2607
            // swap X and Y coordinates
2608
            if (isset($pagedim[$page][$type])) {
2609
                $tmp = $pagedim[$page][$type]['llx'];
2610
                $pagedim[$page][$type]['llx'] = $pagedim[$page][$type]['lly'];
2611
                $pagedim[$page][$type]['lly'] = $tmp;
2612
                $tmp = $pagedim[$page][$type]['urx'];
2613
                $pagedim[$page][$type]['urx'] = $pagedim[$page][$type]['ury'];
2614
                $pagedim[$page][$type]['ury'] = $tmp;
2615
            }
2616
        }
2617
        return $pagedim;
2618
    }
2619
2620
    /**
2621
     * Get the canonical page layout mode.
2622
     * @param string $layout The page layout. Possible values are:<ul><li>SinglePage Display one page at a time</li><li>OneColumn Display the pages in one column</li><li>TwoColumnLeft Display the pages in two columns, with odd-numbered pages on the left</li><li>TwoColumnRight Display the pages in two columns, with odd-numbered pages on the right</li><li>TwoPageLeft (PDF 1.5) Display the pages two at a time, with odd-numbered pages on the left</li><li>TwoPageRight (PDF 1.5) Display the pages two at a time, with odd-numbered pages on the right</li></ul>
2623
     * @return string Canonical page layout name.
2624
     * @public static
2625
     */
2626
    public static function getPageLayoutMode($layout = 'SinglePage')
2627
    {
2628
        switch ($layout) {
2629
            case 'default':
2630
            case 'single':
2631
            case 'SinglePage': {
2632
                $layout_mode = 'SinglePage';
2633
                break;
2634
            }
2635
            case 'continuous':
2636
            case 'OneColumn': {
2637
                $layout_mode = 'OneColumn';
2638
                break;
2639
            }
2640
            case 'two':
2641
            case 'TwoColumnLeft': {
2642
                $layout_mode = 'TwoColumnLeft';
2643
                break;
2644
            }
2645
            case 'TwoColumnRight': {
2646
                $layout_mode = 'TwoColumnRight';
2647
                break;
2648
            }
2649
            case 'TwoPageLeft': {
2650
                $layout_mode = 'TwoPageLeft';
2651
                break;
2652
            }
2653
            case 'TwoPageRight': {
2654
                $layout_mode = 'TwoPageRight';
2655
                break;
2656
            }
2657
            default: {
2658
                $layout_mode = 'SinglePage';
2659
            }
2660
        }
2661
        return $layout_mode;
2662
    }
2663
2664
    /**
2665
     * Get the canonical page layout mode.
2666
     * @param string $mode A name object specifying how the document should be displayed when opened:<ul><li>UseNone Neither document outline nor thumbnail images visible</li><li>UseOutlines Document outline visible</li><li>UseThumbs Thumbnail images visible</li><li>FullScreen Full-screen mode, with no menu bar, window controls, or any other window visible</li><li>UseOC (PDF 1.5) Optional content group panel visible</li><li>UseAttachments (PDF 1.6) Attachments panel visible</li></ul>
2667
     * @return string Canonical page mode name.
2668
     * @public static
2669
     */
2670
    public static function getPageMode($mode = 'UseNone')
2671
    {
2672
        switch ($mode) {
2673
            case 'UseNone': {
2674
                $page_mode = 'UseNone';
2675
                break;
2676
            }
2677
            case 'UseOutlines': {
2678
                $page_mode = 'UseOutlines';
2679
                break;
2680
            }
2681
            case 'UseThumbs': {
2682
                $page_mode = 'UseThumbs';
2683
                break;
2684
            }
2685
            case 'FullScreen': {
2686
                $page_mode = 'FullScreen';
2687
                break;
2688
            }
2689
            case 'UseOC': {
2690
                $page_mode = 'UseOC';
2691
                break;
2692
            }
2693
            case '': {
2694
                $page_mode = 'UseAttachments';
2695
                break;
2696
            }
2697
            default: {
2698
                $page_mode = 'UseNone';
2699
            }
2700
        }
2701
        return $page_mode;
2702
    }
2703
} // END OF TCPDF_STATIC CLASS
2704
2705
//============================================================+
2706
// END OF FILE
2707
//============================================================+
2708