|
2 | 2 |
|
3 | 3 | //============================================================+ |
4 | 4 | // File name : tcpdf.php |
5 | | -// Version : 7.0.17 |
| 5 | +// Version : 7.0.18 |
6 | 6 | // Author : Nicola Asuni - Tecnick.com LTD - www.tecnick.com - info@tecnick.com |
7 | 7 | // License : GNU-LGPL v3 (https://www.gnu.org/copyleft/lesser.html) |
8 | 8 | // Copyright (C): 2002-2026 Nicola Asuni - Tecnick.com LTD |
|
15 | 15 | * See: https://tcpdf.org |
16 | 16 | * @package com.tecnick.tcpdf |
17 | 17 | * @author Nicola Asuni |
18 | | - * @version 7.0.17 |
| 18 | + * @version 7.0.18 |
19 | 19 | */ |
20 | 20 |
|
21 | 21 | // TCPDF configuration |
@@ -7642,8 +7642,11 @@ static function (array $match): string { |
7642 | 7642 | * - '@'-prefixed base64 image data is rewritten to a data URI; |
7643 | 7643 | * - relative local image paths are made absolute (src attributes, |
7644 | 7644 | * CSS url() values and the legacy img| list-bullet syntax); |
7645 | | - * - malformed hexadecimal color tokens, silently coerced by the |
7646 | | - * legacy parser, are repaired to valid values. |
| 7645 | + * - legacy page anchors (href="#N" or href="#N,y") become internal |
| 7646 | + * links to page N at ordinate y; |
| 7647 | + * - malformed hexadecimal colors, silently coerced by the legacy |
| 7648 | + * parser, are repaired in color attributes and CSS declaration values |
| 7649 | + * (see repairHtmlColors()). |
7647 | 7650 | */ |
7648 | 7651 | protected function normalizeHtmlMarkup(string $html): string |
7649 | 7652 | { |
@@ -7699,37 +7702,104 @@ protected function normalizeHtmlMarkup(string $html): string |
7699 | 7702 | $html, |
7700 | 7703 | ); |
7701 | 7704 |
|
| 7705 | + // Legacy page anchors: href="#N" or href="#N,y" link to page N at ordinate y. |
| 7706 | + $html = (string) preg_replace_callback( |
| 7707 | + '/(\bhref\s*=\s*["\'])#([0-9]+)(?:,([0-9]*\.?[0-9]*))?(["\'])/i', |
| 7708 | + fn(array $match): string => ( |
| 7709 | + ($match[1] ?? '') |
| 7710 | + . $this->engine()->addInternalLink(max(0, (int) ($match[2] ?? '1') - 1), (float) ($match[3] ?? '0')) |
| 7711 | + . ($match[4] ?? '') |
| 7712 | + ), |
| 7713 | + $html, |
| 7714 | + ); |
| 7715 | + |
| 7716 | + return self::repairHtmlColors($html); |
| 7717 | + } |
| 7718 | + |
| 7719 | + /** |
| 7720 | + * Repair malformed hexadecimal colors in the attributes whose name ends in |
| 7721 | + * "color" (color, bgcolor, bordercolor), in style attributes and in style |
| 7722 | + * blocks. Text content and other attributes are left unchanged. |
| 7723 | + */ |
| 7724 | + protected static function repairHtmlColors(string $html): string |
| 7725 | + { |
7702 | 7726 | $result = preg_replace_callback( |
7703 | | - '/#([0-9a-zA-Z]+)\b/', |
| 7727 | + '/<style\b[^>]*>.*?<\/style\s*>|<[a-zA-Z][^>]*>/is', |
7704 | 7728 | static function (array $match): string { |
7705 | | - $token = $match[0] ?? ''; |
7706 | | - $hex = strtolower($match[1] ?? ''); |
7707 | | - $len = strlen($hex); |
7708 | | - if (preg_match('/^[0-9a-f]+$/', $hex) === 1 && in_array($len, [3, 4, 6, 8], true)) { |
7709 | | - return $token; |
| 7729 | + $part = $match[0] ?? ''; |
| 7730 | + if (strncasecmp($part, '<style', 6) === 0) { |
| 7731 | + return self::repairCssColors($part); |
7710 | 7732 | } |
7711 | 7733 |
|
7712 | | - // Only repair tokens that could be a mistyped hex color: every |
7713 | | - // character must be a hex digit or a digit-look-alike letter |
7714 | | - // (l/o/i/s/g/z). This leaves CSS id selectors (#second), link |
7715 | | - // fragments (#chapter2) and placeholders (#TOC_...) untouched |
7716 | | - // instead of mangling them into bogus colors. |
7717 | | - if (preg_match('/^[0-9a-fgilosz]+$/', $hex) !== 1) { |
7718 | | - return $token; |
7719 | | - } |
| 7734 | + $part = (string) preg_replace_callback( |
| 7735 | + '/(\bstyle\s*=\s*)(?:"([^"]*)"|\'([^\']*)\')/i', |
| 7736 | + static fn(array $style): string => isset($style[3]) |
| 7737 | + ? ($style[1] ?? '') . "'" . self::repairCssColors($style[3]) . "'" |
| 7738 | + : ($style[1] ?? '') . '"' . self::repairCssColors($style[2] ?? '') . '"', |
| 7739 | + $part, |
| 7740 | + ); |
7720 | 7741 |
|
7721 | | - // Map common look-alike characters, drop the rest. |
7722 | | - $hex = strtr($hex, ['l' => '1', 'o' => '0', 'i' => '1', 's' => '5', 'g' => '6', 'z' => '2']); |
7723 | | - $hex = (string) preg_replace('/[^0-9a-f]/', '', $hex); |
7724 | | - $hex = substr(str_pad($hex, 6, '0'), 0, 6); |
7725 | | - return '#' . $hex; |
| 7742 | + return (string) preg_replace_callback( |
| 7743 | + '/(\b[a-z-]*color\s*=\s*["\']?)#([0-9a-zA-Z]+)\b/i', |
| 7744 | + static fn(array $attr): string => ($attr[1] ?? '') . self::repairHexColor('#' . ($attr[2] ?? '')), |
| 7745 | + $part, |
| 7746 | + ); |
7726 | 7747 | }, |
7727 | 7748 | $html, |
7728 | 7749 | ); |
7729 | 7750 |
|
7730 | 7751 | return $result === null ? $html : $result; |
7731 | 7752 | } |
7732 | 7753 |
|
| 7754 | + /** |
| 7755 | + * Repair malformed hexadecimal colors in the declaration values of a CSS |
| 7756 | + * text. Selectors, quoted strings and numeric character references are |
| 7757 | + * left unchanged. |
| 7758 | + */ |
| 7759 | + protected static function repairCssColors(string $css): string |
| 7760 | + { |
| 7761 | + return (string) preg_replace_callback( |
| 7762 | + '/:[^;{}]*+(?=[;}]|$)/', |
| 7763 | + static fn(array $decl): string => (string) preg_replace_callback( |
| 7764 | + '/"[^"]*"|\'[^\']*\'|(?<!&)#[0-9a-zA-Z]+\b/', |
| 7765 | + static fn(array $item): string => str_starts_with($item[0] ?? '', '#') |
| 7766 | + ? self::repairHexColor($item[0] ?? '') |
| 7767 | + : $item[0] ?? '', |
| 7768 | + $decl[0] ?? '', |
| 7769 | + ), |
| 7770 | + $css, |
| 7771 | + ); |
| 7772 | + } |
| 7773 | + |
| 7774 | + /** |
| 7775 | + * Repair a malformed hexadecimal color token ('#' followed by letters and |
| 7776 | + * digits). Valid 3, 4, 6 or 8 digit values and tokens that cannot be a |
| 7777 | + * mistyped color are returned unchanged; the others are padded or |
| 7778 | + * truncated to 6 digits. |
| 7779 | + */ |
| 7780 | + protected static function repairHexColor(string $color): string |
| 7781 | + { |
| 7782 | + $hex = strtolower(substr($color, 1)); |
| 7783 | + if (preg_match('/^[0-9a-f]+$/', $hex) === 1 && in_array(strlen($hex), [3, 4, 6, 8], true)) { |
| 7784 | + return $color; |
| 7785 | + } |
| 7786 | + |
| 7787 | + // Only repair tokens that could be a mistyped hex color: every |
| 7788 | + // character must be a hex digit or a digit-look-alike letter |
| 7789 | + // (l/o/i/s/g/z). This leaves CSS id selectors (#second), link |
| 7790 | + // fragments (#chapter2) and placeholders (#TOC_...) untouched |
| 7791 | + // instead of mangling them into bogus colors. |
| 7792 | + if (preg_match('/^[0-9a-fgilosz]+$/', $hex) !== 1) { |
| 7793 | + return $color; |
| 7794 | + } |
| 7795 | + |
| 7796 | + // Map common look-alike characters, drop the rest. |
| 7797 | + $hex = strtr($hex, ['l' => '1', 'o' => '0', 'i' => '1', 's' => '5', 'g' => '6', 'z' => '2']); |
| 7798 | + $hex = (string) preg_replace('/[^0-9a-f]/', '', $hex); |
| 7799 | + |
| 7800 | + return '#' . substr(str_pad($hex, 6, '0'), 0, 6); |
| 7801 | + } |
| 7802 | + |
7733 | 7803 | /** |
7734 | 7804 | * Render HTML containing embedded <tcpdf data="..."/> method calls |
7735 | 7805 | * (K_TCPDF_CALLS_IN_HTML feature): the markup is split at the tags, |
|
0 commit comments