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