|
26 | 26 | use PhpOffice\PhpWord\Element\Row;
|
27 | 27 | use PhpOffice\PhpWord\Element\Table;
|
28 | 28 | use PhpOffice\PhpWord\Settings;
|
| 29 | +use PhpOffice\PhpWord\SimpleType\Border; |
29 | 30 | use PhpOffice\PhpWord\SimpleType\Jc;
|
30 | 31 | use PhpOffice\PhpWord\SimpleType\NumberFormat;
|
31 | 32 | use PhpOffice\PhpWord\Style\Paragraph;
|
|
37 | 38 | */
|
38 | 39 | class Html
|
39 | 40 | {
|
| 41 | + private const SPECIAL_BORDER_WIDTHS = ['thin' => '0.5pt', 'thick' => '3.5pt', 'medium' => '2.0pt']; |
| 42 | + |
40 | 43 | protected static $listIndex = 0;
|
41 | 44 |
|
42 | 45 | protected static $xpath;
|
@@ -142,7 +145,7 @@ protected static function parseInlineStyle($node, $styles = [])
|
142 | 145 | break;
|
143 | 146 | case 'bgcolor':
|
144 | 147 | // tables, rows, cells e.g. <tr bgColor="#FF0000">
|
145 |
| - $styles['bgColor'] = trim($val, '# '); |
| 148 | + HtmlColours::setArrayColour($styles, 'bgColor', $val); |
146 | 149 |
|
147 | 150 | break;
|
148 | 151 | case 'valign':
|
@@ -421,9 +424,10 @@ protected static function parseTable($node, $element, &$styles)
|
421 | 424 | }
|
422 | 425 |
|
423 | 426 | $attributes = $node->attributes;
|
424 |
| - if ($attributes->getNamedItem('border') !== null) { |
| 427 | + if ($attributes->getNamedItem('border') !== null && is_object($newElement->getStyle())) { |
425 | 428 | $border = (int) $attributes->getNamedItem('border')->value;
|
426 |
| - $newElement->getStyle()->setBorderSize(Converter::pixelToTwip($border)); |
| 429 | + $newElement->getStyle()->setBorderSize((int) Converter::pixelToTwip($border)); |
| 430 | + $newElement->getStyle()->setBorderStyle(($border === 0) ? 'none' : 'single'); |
427 | 431 | }
|
428 | 432 |
|
429 | 433 | return $newElement;
|
@@ -720,11 +724,11 @@ protected static function parseStyleDeclarations(array $selectors, array $styles
|
720 | 724 |
|
721 | 725 | break;
|
722 | 726 | case 'color':
|
723 |
| - $styles['color'] = trim($value, '#'); |
| 727 | + HtmlColours::setArrayColour($styles, 'color', $value); |
724 | 728 |
|
725 | 729 | break;
|
726 | 730 | case 'background-color':
|
727 |
| - $styles['bgColor'] = trim($value, '#'); |
| 731 | + HtmlColours::setArrayColour($styles, 'bgColor', $value); |
728 | 732 |
|
729 | 733 | break;
|
730 | 734 | case 'line-height':
|
@@ -804,7 +808,7 @@ protected static function parseStyleDeclarations(array $selectors, array $styles
|
804 | 808 |
|
805 | 809 | break;
|
806 | 810 | case 'border-width':
|
807 |
| - $styles['borderSize'] = Converter::cssToPoint($value); |
| 811 | + $styles['borderSize'] = Converter::cssToPoint(self::SPECIAL_BORDER_WIDTHS[$value] ?? $value); |
808 | 812 |
|
809 | 813 | break;
|
810 | 814 | case 'border-style':
|
@@ -834,29 +838,46 @@ protected static function parseStyleDeclarations(array $selectors, array $styles
|
834 | 838 | case 'border-bottom':
|
835 | 839 | case 'border-right':
|
836 | 840 | case 'border-left':
|
837 |
| - // must have exact order [width color style], e.g. "1px #0011CC solid" or "2pt green solid" |
838 |
| - // Word does not accept shortened hex colors e.g. #CCC, only full e.g. #CCCCCC |
839 |
| - if (preg_match('/([0-9]+[^0-9]*)\s+(\#[a-fA-F0-9]+|[a-zA-Z]+)\s+([a-z]+)/', $value, $matches)) { |
840 |
| - if (false !== strpos($property, '-')) { |
841 |
| - $tmp = explode('-', $property); |
842 |
| - $which = $tmp[1]; |
843 |
| - $which = ucfirst($which); // e.g. bottom -> Bottom |
844 |
| - } else { |
845 |
| - $which = ''; |
846 |
| - } |
847 |
| - // Note - border width normalization: |
848 |
| - // Width of border in Word is calculated differently than HTML borders, usually showing up too bold. |
849 |
| - // Smallest 1px (or 1pt) appears in Word like 2-3px/pt in HTML once converted to twips. |
850 |
| - // Therefore we need to normalize converted twip value to cca 1/2 of value. |
851 |
| - // This may be adjusted, if better ratio or formula found. |
852 |
| - // BC change: up to ver. 0.17.0 was $size converted to points - Converter::cssToPoint($size) |
853 |
| - $size = Converter::cssToTwip($matches[1]); |
| 841 | + $stylePattern = '/(^|\\s)(none|hidden|dotted|dashed|solid|double|groove|ridge|inset|outset)(\\s|$)/'; |
| 842 | + if (!preg_match($stylePattern, $value, $matches)) { |
| 843 | + break; |
| 844 | + } |
| 845 | + $borderStyle = $matches[2]; |
| 846 | + $value = preg_replace($stylePattern, ' ', $value) ?? ''; |
| 847 | + $borderSize = $borderColor = null; |
| 848 | + $sizePattern = '/(^|\\s)([0-9]+([.][0-9]+)?+(%|[a-z]*)|thick|thin|medium)(\\s|$)/'; |
| 849 | + if (preg_match($sizePattern, $value, $matches)) { |
| 850 | + $borderSize = $matches[2]; |
| 851 | + $borderSize = self::SPECIAL_BORDER_WIDTHS[$borderSize] ?? $borderSize; |
| 852 | + $value = preg_replace($sizePattern, ' ', $value) ?? ''; |
| 853 | + } |
| 854 | + $colorPattern = '/(^|\\s)([#][a-fA-F0-9]{6}|[#][a-fA-F0-9]{3}|[a-z][a-z0-9]+)(\\s|$)/'; |
| 855 | + if (preg_match($colorPattern, $value, $matches)) { |
| 856 | + $borderColor = HtmlColours::convertColour($matches[2]); |
| 857 | + } |
| 858 | + if (false !== strpos($property, '-')) { |
| 859 | + $tmp = explode('-', $property); |
| 860 | + $which = $tmp[1]; |
| 861 | + $which = ucfirst($which); // e.g. bottom -> Bottom |
| 862 | + } else { |
| 863 | + $which = ''; |
| 864 | + } |
| 865 | + // Note - border width normalization: |
| 866 | + // Width of border in Word is calculated differently than HTML borders, usually showing up too bold. |
| 867 | + // Smallest 1px (or 1pt) appears in Word like 2-3px/pt in HTML once converted to twips. |
| 868 | + // Therefore we need to normalize converted twip value to cca 1/2 of value. |
| 869 | + // This may be adjusted, if better ratio or formula found. |
| 870 | + // BC change: up to ver. 0.17.0 was $size converted to points - Converter::cssToPoint($size) |
| 871 | + if ($borderSize !== null) { |
| 872 | + $size = Converter::cssToTwip($borderSize); |
854 | 873 | $size = (int) ($size / 2);
|
855 | 874 | // valid variants may be e.g. borderSize, borderTopSize, borderLeftColor, etc ..
|
856 | 875 | $styles["border{$which}Size"] = $size; // twips
|
857 |
| - $styles["border{$which}Color"] = trim($matches[2], '#'); |
858 |
| - $styles["border{$which}Style"] = self::mapBorderStyle($matches[3]); |
859 | 876 | }
|
| 877 | + if (!empty($borderColor)) { |
| 878 | + $styles["border{$which}Color"] = $borderColor; |
| 879 | + } |
| 880 | + $styles["border{$which}Style"] = self::mapBorderStyle($borderStyle); |
860 | 881 |
|
861 | 882 | break;
|
862 | 883 | case 'vertical-align':
|
@@ -1006,21 +1027,23 @@ protected static function mapBorderStyle($cssBorderStyle)
|
1006 | 1027 | case 'dotted':
|
1007 | 1028 | case 'double':
|
1008 | 1029 | return $cssBorderStyle;
|
| 1030 | + case 'hidden': |
| 1031 | + return 'none'; |
1009 | 1032 | default:
|
1010 | 1033 | return 'single';
|
1011 | 1034 | }
|
1012 | 1035 | }
|
1013 | 1036 |
|
1014 | 1037 | protected static function mapBorderColor(&$styles, $cssBorderColor): void
|
1015 | 1038 | {
|
1016 |
| - $numColors = substr_count($cssBorderColor, '#'); |
| 1039 | + $colors = explode(' ', $cssBorderColor); |
| 1040 | + $numColors = count($colors); |
1017 | 1041 | if ($numColors === 1) {
|
1018 |
| - $styles['borderColor'] = trim($cssBorderColor, '#'); |
1019 |
| - } elseif ($numColors > 1) { |
1020 |
| - $colors = explode(' ', $cssBorderColor); |
| 1042 | + HtmlColours::setArrayColour($styles, 'borderColor', $cssBorderColor); |
| 1043 | + } else { |
1021 | 1044 | $borders = ['borderTopColor', 'borderRightColor', 'borderBottomColor', 'borderLeftColor'];
|
1022 | 1045 | for ($i = 0; $i < min(4, $numColors, count($colors)); ++$i) {
|
1023 |
| - $styles[$borders[$i]] = trim($colors[$i], '#'); |
| 1046 | + HtmlColours::setArrayColour($styles, $borders[$i], $colors[$i]); |
1024 | 1047 | }
|
1025 | 1048 | }
|
1026 | 1049 | }
|
|
0 commit comments