Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.2.x will end 22 April 2024 (12 months).
  • Bug fixes for security issues in 4.2.x will end 7 October 2024 (18 months).
  • PHP version: minimum PHP 8.0.0 Note: minimum PHP version has increased since Moodle 4.1. PHP 8.1.x is supported too.

Differences Between: [Versions 310 and 402] [Versions 311 and 402] [Versions 39 and 402] [Versions 400 and 402] [Versions 401 and 402]

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