diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index c0a7befdb..22e2d4f3b 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -25,17 +25,14 @@ jobs: php-version: ["7.1", "7.2", "7.3", "7.4", "8.0", "8.1", "8.2"] os: [ubuntu-latest] experimental: [false] - php-extensions: ["bcmath, imagick, gd"] + php-extensions: ["bcmath, curl, imagick, gd"] coverage-extension: ["none"] # Add more specific tests include: - - { php-version: '5.5', experimental: false, os: ubuntu-latest, php-extensions: 'bcmath, imagick, gd', coverage-extension: 'xdebug' } - - { php-version: '5.6', experimental: false, os: ubuntu-latest, php-extensions: 'bcmath, imagick, gd', coverage-extension: 'xdebug' } - - { php-version: '7.0', experimental: false, os: ubuntu-latest, php-extensions: 'bcmath, imagick, gd', coverage-extension: 'xdebug' } - #- { php-version: '8.2', experimental: false, os: macos-latest, php-extensions: 'bcmath, imagick, gd', coverage-extension: 'none' } - - { php-version: '8.2', experimental: false, os: windows-latest, php-extensions: 'bcmath, imagick, gd', coverage-extension: 'none' } - - { php-version: '8.3', experimental: true, os: ubuntu-latest, php-extensions: 'bcmath, imagick, gd', coverage-extension: 'pcov' } - - { php-version: 'nightly', experimental: true, os: ubuntu-latest, php-extensions: 'bcmath, imagick, gd', coverage-extension: 'pcov' } + #- { php-version: '8.2', experimental: false, os: macos-latest, php-extensions: 'bcmath, curl, imagick, gd', coverage-extension: 'none' } + - { php-version: '8.2', experimental: false, os: windows-latest, php-extensions: 'bcmath, curl, imagick, gd', coverage-extension: 'none' } + - { php-version: '8.3', experimental: true, os: ubuntu-latest, php-extensions: 'bcmath, curl, imagick, gd', coverage-extension: 'pcov' } + - { php-version: 'nightly', experimental: true, os: ubuntu-latest, php-extensions: 'bcmath, curl, imagick, gd', coverage-extension: 'pcov' } env: PDFINFO_BINARY: ${{ (matrix.os == 'ubuntu-latest') && '/usr/bin/pdfinfo' || ((matrix.os == 'macos-latest') && '/usr/local/bin/pdfinfo' || 'C:\ProgramData\Chocolatey\bin\pdfinfo.exe') }} steps: diff --git a/CHANGELOG.TXT b/CHANGELOG.TXT index 4437e2cf3..a6485e3a3 100644 --- a/CHANGELOG.TXT +++ b/CHANGELOG.TXT @@ -1,3 +1,10 @@ +6.8.0 (2024-12-23) + - Requires PHP 7.1+ and curl extension. + - Escape error message. + - Use strict time-constant function to compare TCPDF-tag hashes. + - Add K_CURLOPTS config array to set custom cURL options (NOTE: some defaults have changed). + - Add some addTTFfont fixes from tc-lib-pdf-font. + 6.7.8 (2024-12-13) - Improve SVG detection by checking for (mandatory) namespace. - Use late state binding now that minimum PHP version is 5.5. diff --git a/VERSION b/VERSION index eed6de4bb..e029aa99b 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -6.7.8 +6.8.0 diff --git a/composer.json b/composer.json index 2ca002a43..b0db28898 100644 --- a/composer.json +++ b/composer.json @@ -12,7 +12,7 @@ "barcodes" ], "homepage": "http://www.tcpdf.org/", - "version": "6.7.8", + "version": "6.8.0", "license": "LGPL-3.0-or-later", "authors": [ { @@ -22,7 +22,8 @@ } ], "require": { - "php": ">=5.5.0" + "php": ">=7.1.0", + "ext-curl": "*" }, "autoload": { "classmap": [ diff --git a/include/tcpdf_fonts.php b/include/tcpdf_fonts.php index f4518d9ef..4973b2855 100644 --- a/include/tcpdf_fonts.php +++ b/include/tcpdf_fonts.php @@ -1,13 +1,13 @@ 0) { + if (preg_match_all('#dup[\s]([0-9]+)[\s]*+/([^\s]*+)[\s]put#sU', $font, $fmap, PREG_SET_ORDER) > 0) { foreach ($fmap as $v) { $imap[$v[2]] = $v[1]; } @@ -229,22 +230,22 @@ public static function addTTFfont($fontfile, $fonttype='', $enc='', $flags=32, $ $eplain .= chr($chr ^ ($r >> 8)); $r = ((($chr + $r) * $c1 + $c2) % 65536); } - if (preg_match('#/ForceBold[\s]*([^\s]*)#', $eplain, $matches) > 0) { + if (preg_match('#/ForceBold[\s]*+([^\s]*+)#', $eplain, $matches) > 0) { if ($matches[1] == 'true') { $fmetric['Flags'] |= 0x40000; } } - if (preg_match('#/StdVW[\s]*\[([^\]]*)#', $eplain, $matches) > 0) { + if (preg_match('#/StdVW[\s]*+\[([^\]]*+)#', $eplain, $matches) > 0) { $fmetric['StemV'] = intval($matches[1]); } else { $fmetric['StemV'] = 70; } - if (preg_match('#/StdHW[\s]*\[([^\]]*)#', $eplain, $matches) > 0) { + if (preg_match('#/StdHW[\s]*+\[([^\]]*+)#', $eplain, $matches) > 0) { $fmetric['StemH'] = intval($matches[1]); } else { $fmetric['StemH'] = 30; } - if (preg_match('#/BlueValues[\s]*\[([^\]]*)#', $eplain, $matches) > 0) { + if (preg_match('#/BlueValues[\s]*+\[([^\]]*+)#', $eplain, $matches) > 0) { $bv = explode(' ', $matches[1]); if (count($bv) >= 6) { $v1 = intval($bv[2]); @@ -265,7 +266,7 @@ public static function addTTFfont($fontfile, $fonttype='', $enc='', $flags=32, $ $fmetric['CapHeight'] = 700; } // get the number of random bytes at the beginning of charstrings - if (preg_match('#/lenIV[\s]*([0-9]*)#', $eplain, $matches) > 0) { + if (preg_match('#/lenIV[\s]*+([\d]*+)#', $eplain, $matches) > 0) { $lenIV = intval($matches[1]); } else { $lenIV = 4; @@ -273,7 +274,7 @@ public static function addTTFfont($fontfile, $fonttype='', $enc='', $flags=32, $ $fmetric['Leading'] = 0; // get charstring data $eplain = substr($eplain, (strpos($eplain, '/CharStrings') + 1)); - preg_match_all('#/([A-Za-z0-9\.]*)[\s][0-9]+[\s]RD[\s](.*)[\s]ND#sU', $eplain, $matches, PREG_SET_ORDER); + preg_match_all('#/([A-Za-z0-9\.]*+)[\s][0-9]+[\s]RD[\s](.*)[\s]ND#sU', $eplain, $matches, PREG_SET_ORDER); if (!empty($enc) AND isset(TCPDF_FONT_DATA::$encmap[$enc])) { $enc_map = TCPDF_FONT_DATA::$encmap[$enc]; } else { diff --git a/include/tcpdf_static.php b/include/tcpdf_static.php index 16828944c..2e18d9c7d 100644 --- a/include/tcpdf_static.php +++ b/include/tcpdf_static.php @@ -1,13 +1,13 @@ * @package com.tecnick.tcpdf * @author Nicola Asuni - * @version 1.1.2 + * @version 1.1.5 */ /** @@ -46,7 +46,7 @@ * Static methods used by the TCPDF class. * @package com.tecnick.tcpdf * @brief PHP class for generating PDF documents without requiring external extensions. - * @version 1.1.1 + * @version 1.1.5 * @author Nicola Asuni - info@tecnick.com */ class TCPDF_STATIC { @@ -55,7 +55,7 @@ class TCPDF_STATIC { * Current TCPDF version. * @private static */ - private static $tcpdf_version = '6.7.8'; + private static $tcpdf_version = '6.8.0'; /** * String alias for total number of pages. @@ -106,6 +106,31 @@ class TCPDF_STATIC { */ public static $pageboxes = array('MediaBox', 'CropBox', 'BleedBox', 'TrimBox', 'ArtBox'); + /** + * Array of default cURL options for curl_setopt_array. + * + * @var array cURL options. + */ + protected const CURLOPT_DEFAULT = [ + CURLOPT_CONNECTTIMEOUT => 5, + CURLOPT_MAXREDIRS => 5, + CURLOPT_PROTOCOLS => CURLPROTO_HTTPS | CURLPROTO_HTTP | CURLPROTO_FTP | CURLPROTO_FTPS, + CURLOPT_SSL_VERIFYHOST => 2, + CURLOPT_SSL_VERIFYPEER => true, + CURLOPT_TIMEOUT => 30, + CURLOPT_USERAGENT => 'tcpdf', + ]; + + /** + * Array of fixed cURL options for curl_setopt_array. + * + * @var array cURL options. + */ + protected const CURLOPT_FIXED = [ + CURLOPT_FAILONERROR => true, + CURLOPT_RETURNTRANSFER => true, + ]; + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /** @@ -1823,23 +1848,19 @@ public static function fopenLocal($filename, $mode) { */ public static function url_exists($url) { $crs = curl_init(); - // encode query params in URL to get right response form the server - $url = self::encodeUrlQuery($url); - curl_setopt($crs, CURLOPT_URL, $url); - curl_setopt($crs, CURLOPT_NOBODY, true); - curl_setopt($crs, CURLOPT_FAILONERROR, true); - if ((ini_get('open_basedir') == '') && (!ini_get('safe_mode'))) { - curl_setopt($crs, CURLOPT_FOLLOWLOCATION, true); - } - curl_setopt($crs, CURLOPT_CONNECTTIMEOUT, 5); - curl_setopt($crs, CURLOPT_TIMEOUT, 30); - curl_setopt($crs, CURLOPT_SSL_VERIFYPEER, false); - curl_setopt($crs, CURLOPT_SSL_VERIFYHOST, false); - curl_setopt($crs, CURLOPT_USERAGENT, 'tc-lib-file'); - curl_setopt($crs, CURLOPT_MAXREDIRS, 5); - if (defined('CURLOPT_PROTOCOLS')) { - curl_setopt($crs, CURLOPT_PROTOCOLS, CURLPROTO_HTTPS | CURLPROTO_HTTP | CURLPROTO_FTP | CURLPROTO_FTPS); - } + $curlopts = []; + if ( + (ini_get('open_basedir') == '') + && (ini_get('safe_mode') === '' + || ini_get('safe_mode') === false) + ) { + $curlopts[CURLOPT_FOLLOWLOCATION] = true; + } + $curlopts = array_replace($curlopts, self::CURLOPT_DEFAULT); + $curlopts = array_replace($curlopts, K_CURLOPTS); + $curlopts = array_replace($curlopts, self::CURLOPT_FIXED); + $curlopts[CURLOPT_URL] = $url; + curl_setopt_array($crs, $curlopts); curl_exec($crs); $code = curl_getinfo($crs, CURLINFO_HTTP_CODE); curl_close($crs); @@ -1960,21 +1981,19 @@ public static function fileGetContents($file) { ) { // try to get remote file data using cURL $crs = curl_init(); - curl_setopt($crs, CURLOPT_URL, $path); - curl_setopt($crs, CURLOPT_FAILONERROR, true); - curl_setopt($crs, CURLOPT_RETURNTRANSFER, true); - if ((ini_get('open_basedir') == '') && (!ini_get('safe_mode'))) { - curl_setopt($crs, CURLOPT_FOLLOWLOCATION, true); - } - curl_setopt($crs, CURLOPT_CONNECTTIMEOUT, 5); - curl_setopt($crs, CURLOPT_TIMEOUT, 30); - curl_setopt($crs, CURLOPT_SSL_VERIFYPEER, false); - curl_setopt($crs, CURLOPT_SSL_VERIFYHOST, false); - curl_setopt($crs, CURLOPT_USERAGENT, 'tc-lib-file'); - curl_setopt($crs, CURLOPT_MAXREDIRS, 5); - if (defined('CURLOPT_PROTOCOLS')) { - curl_setopt($crs, CURLOPT_PROTOCOLS, CURLPROTO_HTTPS | CURLPROTO_HTTP | CURLPROTO_FTP | CURLPROTO_FTPS); + $curlopts = []; + if ( + (ini_get('open_basedir') == '') + && (ini_get('safe_mode') === '' + || ini_get('safe_mode') === false) + ) { + $curlopts[CURLOPT_FOLLOWLOCATION] = true; } + $curlopts = array_replace($curlopts, self::CURLOPT_DEFAULT); + $curlopts = array_replace($curlopts, K_CURLOPTS); + $curlopts = array_replace($curlopts, self::CURLOPT_FIXED); + $curlopts[CURLOPT_URL] = $url; + curl_setopt_array($crs, $curlopts); $ret = curl_exec($crs); curl_close($crs); if ($ret !== false) { diff --git a/tcpdf.php b/tcpdf.php index 3404c52b3..7d967234b 100644 --- a/tcpdf.php +++ b/tcpdf.php @@ -1,9 +1,9 @@ * @package com.tecnick.tcpdf * @author Nicola Asuni - * @version 6.7.8 + * @version 6.8.0 */ // TCPDF configuration @@ -128,7 +128,7 @@ * TCPDF project (http://www.tcpdf.org) has been originally derived in 2002 from the Public Domain FPDF class by Olivier Plathey (http://www.fpdf.org), but now is almost entirely rewritten.
* @package com.tecnick.tcpdf * @brief PHP class for generating PDF documents without requiring external extensions. - * @version 6.7.8 + * @version 6.8.0 * @author Nicola Asuni - info@tecnick.com * @IgnoreAnnotation("protected") * @IgnoreAnnotation("public") @@ -3007,6 +3007,7 @@ public function setAllowLocalFiles($allowLocalFiles) { public function Error($msg) { // unset all class variables $this->_destroy(true); + $msg = htmlspecialchars($msg, ENT_QUOTES, 'UTF-8'); if (defined('K_TCPDF_THROW_EXCEPTION_ERROR') AND !K_TCPDF_THROW_EXCEPTION_ERROR) { die('TCPDF ERROR: '.$msg); } else { @@ -17259,7 +17260,7 @@ protected function unserializeTCPDFtag($data) { $hlen = intval(substr($data, 0, $hpos)); $hash = substr($data, $hpos + 1, $hlen); $encoded = substr($data, $hpos + 2 + $hlen); - if ($hash != $this->hashTCPDFtag($encoded)) { + if (!hash_equals( $this->hashTCPDFtag($encoded), $hash)) { $this->Error('Invalid parameters'); } return json_decode(urldecode($encoded), true); @@ -19124,7 +19125,7 @@ protected function openHTMLTagHandler($dom, $key, $cell) { $imglink = ''; if (isset($this->HREF['url']) AND !TCPDF_STATIC::empty_string($this->HREF['url'])) { $imglink = $this->HREF['url']; - if ($imglink[0] == '#') { + if ($imglink[0] == '#' AND is_numeric($imglink[1])) { // convert url to internal link $lnkdata = explode(',', $imglink); if (isset($lnkdata[0])) { @@ -23485,7 +23486,7 @@ protected function setSVGStyles($svgstyle, $prevsvgstyle, $x=0, $y=0, $w=1, $h=1 if (preg_match('/font-family[\s]*:[\s]*([^\;\"]*)/si', $svgstyle['font'], $regs)) { $font_family = $this->getFontFamilyName($regs[1]); } else { - $font_family = $svgstyle['font-family']; + $font_family = $this->getFontFamilyName($svgstyle['font-family']); } if (preg_match('/font-size[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) { $font_size = trim($regs[1]); diff --git a/tcpdf_autoconfig.php b/tcpdf_autoconfig.php index 2bcfccb82..d34e25977 100644 --- a/tcpdf_autoconfig.php +++ b/tcpdf_autoconfig.php @@ -240,6 +240,11 @@ define('K_TIMEZONE', @date_default_timezone_get()); } +// Custom cURL options for curl_setopt_array. +if (!defined('K_CURLOPTS')) { + define('K_CURLOPTS', array()); +} + //============================================================+ // END OF FILE //============================================================+ diff --git a/tests/composer.json b/tests/composer.json index 3edd4a90f..cf0da4167 100644 --- a/tests/composer.json +++ b/tests/composer.json @@ -12,7 +12,7 @@ } ], "require": { - "PHP": ">=5.3.0", + "PHP": ">=7.1.0", "cs278/mktemp": "^1.2.0" }, "suggest": { diff --git a/tests/launch.php b/tests/launch.php index 0ece8cd07..2a21ef7dd 100644 --- a/tests/launch.php +++ b/tests/launch.php @@ -160,6 +160,7 @@ function printLaunchHelp() */ $phpExtensions = array( 'bcmath' => null, + 'curl' => null, 'gd' => null, 'imagick' => null, 'json' => null, diff --git a/tests/launch.sh b/tests/launch.sh index bf24718de..3536806fb 100755 --- a/tests/launch.sh +++ b/tests/launch.sh @@ -34,6 +34,9 @@ echo "php extension dir: ${PHP_EXT_DIR}" BCMATH_EXT="-d extension=$(find ${PHP_EXT_DIR} -type f -name 'bcmath.so')" echo "bcmath found at: ${BCMATH_EXT}" +CURL_EXT="-d extension=$(find ${PHP_EXT_DIR} -type f -name 'curl.so')" +echo "curl found at: ${CURL_EXT}" + COVERAGE_EXTENSION="-d extension=pcov.so" IMAGICK_OR_GD="-dextension=gd.so" JSON_EXT="-dextension=json.so" @@ -86,6 +89,7 @@ for file in $EXAMPLE_FILES; do -d date.timezone=UTC \ ${IMAGICK_OR_GD} ${COVERAGE_EXTENSION} \ ${BCMATH_EXT} \ + ${CURL_EXT} \ ${JSON_EXT} \ ${XML_EXT} \ -d display_errors=on \ @@ -152,7 +156,9 @@ for file in $EXAMPLE_BARCODE_FILES; do ${PHP_BINARY} -n \ -d include_path="${TEMP_FOLDER}" \ -d date.timezone=UTC \ - ${BCMATH_EXT} ${COVERAGE_EXTENSION} \ + ${BCMATH_EXT} \ + ${CURL_EXT} \ + ${COVERAGE_EXTENSION} \ -d display_errors=on \ -d error_reporting=-1 \ -d pcov.directory="${ROOT_DIR}" \