diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon
index 86a542769a..5e1f9cd2c2 100644
--- a/phpstan-baseline.neon
+++ b/phpstan-baseline.neon
@@ -380,11 +380,6 @@ parameters:
count: 1
path: src/PhpWord/Shared/Html.php
- -
- message: "#^Cannot call method setBorderSize\\(\\) on PhpOffice\\\\PhpWord\\\\Style\\\\Table\\|string\\.$#"
- count: 1
- path: src/PhpWord/Shared/Html.php
-
-
message: "#^Cannot call method setStyleName\\(\\) on PhpOffice\\\\PhpWord\\\\Style\\\\Table\\|string\\.$#"
count: 1
diff --git a/src/PhpWord/Reader/Word2007/AbstractPart.php b/src/PhpWord/Reader/Word2007/AbstractPart.php
index 96d4a46f8a..697478c7e8 100644
--- a/src/PhpWord/Reader/Word2007/AbstractPart.php
+++ b/src/PhpWord/Reader/Word2007/AbstractPart.php
@@ -792,35 +792,46 @@ protected function readTableStyle(XMLReader $xmlReader, DOMElement $domNode)
$borders = array_merge($margins, ['insideH', 'insideV']);
if ($xmlReader->elementExists('w:tblPr', $domNode)) {
+ $tblStyleName = '';
if ($xmlReader->elementExists('w:tblPr/w:tblStyle', $domNode)) {
- $style = $xmlReader->getAttribute('w:val', $domNode, 'w:tblPr/w:tblStyle');
- } else {
- $styleNode = $xmlReader->getElement('w:tblPr', $domNode);
- $styleDefs = [];
- foreach ($margins as $side) {
- $ucfSide = ucfirst($side);
- $styleDefs["cellMargin$ucfSide"] = [self::READ_VALUE, "w:tblCellMar/w:$side", 'w:w'];
- }
- foreach ($borders as $side) {
- $ucfSide = ucfirst($side);
- $styleDefs["border{$ucfSide}Size"] = [self::READ_VALUE, "w:tblBorders/w:$side", 'w:sz'];
- $styleDefs["border{$ucfSide}Color"] = [self::READ_VALUE, "w:tblBorders/w:$side", 'w:color'];
- $styleDefs["border{$ucfSide}Style"] = [self::READ_VALUE, "w:tblBorders/w:$side", 'w:val'];
- }
- $styleDefs['layout'] = [self::READ_VALUE, 'w:tblLayout', 'w:type'];
- $styleDefs['bidiVisual'] = [self::READ_TRUE, 'w:bidiVisual'];
- $styleDefs['cellSpacing'] = [self::READ_VALUE, 'w:tblCellSpacing', 'w:w'];
- $style = $this->readStyleDefs($xmlReader, $styleNode, $styleDefs);
-
- $tablePositionNode = $xmlReader->getElement('w:tblpPr', $styleNode);
- if ($tablePositionNode !== null) {
- $style['position'] = $this->readTablePosition($xmlReader, $tablePositionNode);
- }
+ $tblStyleName = $xmlReader->getAttribute('w:val', $domNode, 'w:tblPr/w:tblStyle');
+ }
+ $styleNode = $xmlReader->getElement('w:tblPr', $domNode);
+ $styleDefs = [];
- $indentNode = $xmlReader->getElement('w:tblInd', $styleNode);
- if ($indentNode !== null) {
- $style['indent'] = $this->readTableIndent($xmlReader, $indentNode);
- }
+ foreach ($margins as $side) {
+ $ucfSide = ucfirst($side);
+ $styleDefs["cellMargin$ucfSide"] = [self::READ_VALUE, "w:tblCellMar/w:$side", 'w:w'];
+ }
+ foreach ($borders as $side) {
+ $ucfSide = ucfirst($side);
+ $styleDefs["border{$ucfSide}Size"] = [self::READ_VALUE, "w:tblBorders/w:$side", 'w:sz'];
+ $styleDefs["border{$ucfSide}Color"] = [self::READ_VALUE, "w:tblBorders/w:$side", 'w:color'];
+ $styleDefs["border{$ucfSide}Style"] = [self::READ_VALUE, "w:tblBorders/w:$side", 'w:val'];
+ }
+ $styleDefs['layout'] = [self::READ_VALUE, 'w:tblLayout', 'w:type'];
+ $styleDefs['bidiVisual'] = [self::READ_TRUE, 'w:bidiVisual'];
+ $styleDefs['cellSpacing'] = [self::READ_VALUE, 'w:tblCellSpacing', 'w:w'];
+ $style = $this->readStyleDefs($xmlReader, $styleNode, $styleDefs);
+
+ $tablePositionNode = $xmlReader->getElement('w:tblpPr', $styleNode);
+ if ($tablePositionNode !== null) {
+ $style['position'] = $this->readTablePosition($xmlReader, $tablePositionNode);
+ }
+
+ $indentNode = $xmlReader->getElement('w:tblInd', $styleNode);
+ if ($indentNode !== null) {
+ $style['indent'] = $this->readTableIndent($xmlReader, $indentNode);
+ }
+ if ($xmlReader->elementExists('w:basedOn', $domNode)) {
+ $style['basedOn'] = $xmlReader->getAttribute('w:val', $domNode, 'w:basedOn');
+ }
+ if ($tblStyleName !== '') {
+ $style['tblStyle'] = $tblStyleName;
+ }
+ // this may be unneeded
+ if ($xmlReader->elementExists('w:name', $domNode)) {
+ $style['styleName'] = $xmlReader->getAttribute('w:val', $domNode, 'w:name');
}
}
diff --git a/src/PhpWord/Reader/Word2007/Styles.php b/src/PhpWord/Reader/Word2007/Styles.php
index d0777c3026..81aaf203c1 100644
--- a/src/PhpWord/Reader/Word2007/Styles.php
+++ b/src/PhpWord/Reader/Word2007/Styles.php
@@ -69,8 +69,9 @@ public function read(PhpWord $phpWord): void
foreach ($nodes as $node) {
$type = $xmlReader->getAttribute('w:type', $node);
$name = $xmlReader->getAttribute('w:val', $node, 'w:name');
+ $styleId = $xmlReader->getAttribute('w:styleId', $node);
if (null === $name) {
- $name = $xmlReader->getAttribute('w:styleId', $node);
+ $name = $styleId;
}
$headingMatches = [];
preg_match('/Heading\s*(\d)/i', $name, $headingMatches);
@@ -102,7 +103,8 @@ public function read(PhpWord $phpWord): void
case 'table':
$tStyle = $this->readTableStyle($xmlReader, $node);
if (!empty($tStyle)) {
- $phpWord->addTableStyle($name, $tStyle);
+ $newTable = $phpWord->addTableStyle($styleId, $tStyle);
+ $newTable->setStyleName($name);
}
break;
diff --git a/src/PhpWord/Shared/Html.php b/src/PhpWord/Shared/Html.php
index 4573daf2a9..f114a4e35f 100644
--- a/src/PhpWord/Shared/Html.php
+++ b/src/PhpWord/Shared/Html.php
@@ -29,6 +29,7 @@
use PhpOffice\PhpWord\Element\Table;
use PhpOffice\PhpWord\Element\TextRun;
use PhpOffice\PhpWord\Settings;
+use PhpOffice\PhpWord\SimpleType\Border;
use PhpOffice\PhpWord\SimpleType\Jc;
use PhpOffice\PhpWord\SimpleType\NumberFormat;
use PhpOffice\PhpWord\Style\Paragraph;
@@ -40,6 +41,8 @@
*/
class Html
{
+ private const SPECIAL_BORDER_WIDTHS = ['thin' => '0.5pt', 'thick' => '3.5pt', 'medium' => '2.0pt'];
+
private const RGB_REGEXP = '/^\s*rgb\s*[(]\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*[)]\s*$/';
protected static $listIndex = 0;
@@ -149,7 +152,7 @@ protected static function parseInlineStyle($node, $styles = [])
break;
case 'bgcolor':
// tables, rows, cells e.g.
- $styles['bgColor'] = self::convertRgb($val);
+ HtmlColours::setArrayColour($styles, 'bgColor', self::convertRgb($val));
break;
case 'valign':
@@ -429,9 +432,10 @@ protected static function parseTable($node, $element, &$styles)
}
$attributes = $node->attributes;
- if ($attributes->getNamedItem('border')) {
+ if ($attributes->getNamedItem('border') !== null && is_object($newElement->getStyle())) {
$border = (int) $attributes->getNamedItem('border')->nodeValue;
- $newElement->getStyle()->setBorderSize(Converter::pixelToTwip($border));
+ $newElement->getStyle()->setBorderSize((int) Converter::pixelToTwip($border));
+ $newElement->getStyle()->setBorderStyle(($border === 0) ? 'none' : 'single');
}
return $newElement;
@@ -732,11 +736,11 @@ protected static function parseStyleDeclarations(array $selectors, array $styles
break;
case 'color':
- $styles['color'] = self::convertRgb($value);
+ HtmlColours::setArrayColour($styles, 'color', self::convertRgb($value));
break;
case 'background-color':
- $styles['bgColor'] = self::convertRgb($value);
+ HtmlColours::setArrayColour($styles, 'bgColor', self::convertRgb($value));
break;
case 'line-height':
@@ -868,7 +872,7 @@ protected static function parseStyleDeclarations(array $selectors, array $styles
break;
case 'border-width':
- $styles['borderSize'] = Converter::cssToPoint($value);
+ $styles['borderSize'] = Converter::cssToPoint(self::SPECIAL_BORDER_WIDTHS[$value] ?? $value);
break;
case 'border-style':
@@ -898,29 +902,46 @@ protected static function parseStyleDeclarations(array $selectors, array $styles
case 'border-bottom':
case 'border-right':
case 'border-left':
- // must have exact order [width color style], e.g. "1px #0011CC solid" or "2pt green solid"
- // Word does not accept shortened hex colors e.g. #CCC, only full e.g. #CCCCCC
- if (preg_match('/([0-9]+[^0-9]*)\s+(\#[a-fA-F0-9]+|[a-zA-Z]+)\s+([a-z]+)/', $value, $matches)) {
- if (false !== strpos($property, '-')) {
- $tmp = explode('-', $property);
- $which = $tmp[1];
- $which = ucfirst($which); // e.g. bottom -> Bottom
- } else {
- $which = '';
- }
- // Note - border width normalization:
- // Width of border in Word is calculated differently than HTML borders, usually showing up too bold.
- // Smallest 1px (or 1pt) appears in Word like 2-3px/pt in HTML once converted to twips.
- // Therefore we need to normalize converted twip value to cca 1/2 of value.
- // This may be adjusted, if better ratio or formula found.
- // BC change: up to ver. 0.17.0 was $size converted to points - Converter::cssToPoint($size)
- $size = Converter::cssToTwip($matches[1]);
+ $stylePattern = '/(^|\\s)(none|hidden|dotted|dashed|solid|double|groove|ridge|inset|outset)(\\s|$)/';
+ if (!preg_match($stylePattern, $value, $matches)) {
+ break;
+ }
+ $borderStyle = $matches[2];
+ $value = preg_replace($stylePattern, ' ', $value) ?? '';
+ $borderSize = $borderColor = null;
+ $sizePattern = '/(^|\\s)([0-9]+([.][0-9]+)?+(%|[a-z]*)|thick|thin|medium)(\\s|$)/';
+ if (preg_match($sizePattern, $value, $matches)) {
+ $borderSize = $matches[2];
+ $borderSize = self::SPECIAL_BORDER_WIDTHS[$borderSize] ?? $borderSize;
+ $value = preg_replace($sizePattern, ' ', $value) ?? '';
+ }
+ $colorPattern = '/(^|\\s)([#][a-fA-F0-9]{6}|[#][a-fA-F0-9]{3}|[a-z][a-z0-9]+)(\\s|$)/';
+ if (preg_match($colorPattern, $value, $matches)) {
+ $borderColor = HtmlColours::convertColour($matches[2]);
+ }
+ if (false !== strpos($property, '-')) {
+ $tmp = explode('-', $property);
+ $which = $tmp[1];
+ $which = ucfirst($which); // e.g. bottom -> Bottom
+ } else {
+ $which = '';
+ }
+ // Note - border width normalization:
+ // Width of border in Word is calculated differently than HTML borders, usually showing up too bold.
+ // Smallest 1px (or 1pt) appears in Word like 2-3px/pt in HTML once converted to twips.
+ // Therefore we need to normalize converted twip value to cca 1/2 of value.
+ // This may be adjusted, if better ratio or formula found.
+ // BC change: up to ver. 0.17.0 was $size converted to points - Converter::cssToPoint($size)
+ if ($borderSize !== null) {
+ $size = Converter::cssToTwip($borderSize);
$size = (int) ($size / 2);
// valid variants may be e.g. borderSize, borderTopSize, borderLeftColor, etc ..
$styles["border{$which}Size"] = $size; // twips
- $styles["border{$which}Color"] = trim($matches[2], '#');
- $styles["border{$which}Style"] = self::mapBorderStyle($matches[3]);
}
+ if (!empty($borderColor)) {
+ $styles["border{$which}Color"] = $borderColor;
+ }
+ $styles["border{$which}Style"] = self::mapBorderStyle($borderStyle);
break;
case 'vertical-align':
@@ -1069,6 +1090,8 @@ protected static function mapBorderStyle($cssBorderStyle)
case 'dotted':
case 'double':
return $cssBorderStyle;
+ case 'hidden':
+ return 'none';
default:
return 'single';
}
@@ -1076,14 +1099,14 @@ protected static function mapBorderStyle($cssBorderStyle)
protected static function mapBorderColor(&$styles, $cssBorderColor): void
{
- $numColors = substr_count($cssBorderColor, '#');
+ $colors = explode(' ', $cssBorderColor);
+ $numColors = count($colors);
if ($numColors === 1) {
- $styles['borderColor'] = trim($cssBorderColor, '#');
- } elseif ($numColors > 1) {
- $colors = explode(' ', $cssBorderColor);
+ HtmlColours::setArrayColour($styles, 'borderColor', $cssBorderColor);
+ } else {
$borders = ['borderTopColor', 'borderRightColor', 'borderBottomColor', 'borderLeftColor'];
for ($i = 0; $i < min(4, $numColors, count($colors)); ++$i) {
- $styles[$borders[$i]] = trim($colors[$i], '#');
+ HtmlColours::setArrayColour($styles, $borders[$i], $colors[$i]);
}
}
}
diff --git a/src/PhpWord/Shared/HtmlColours.php b/src/PhpWord/Shared/HtmlColours.php
new file mode 100644
index 0000000000..40bc0096c6
--- /dev/null
+++ b/src/PhpWord/Shared/HtmlColours.php
@@ -0,0 +1,549 @@
+ 'f0f8ff',
+ 'antiquewhite' => 'faebd7',
+ 'antiquewhite1' => 'ffefdb',
+ 'antiquewhite2' => 'eedfcc',
+ 'antiquewhite3' => 'cdc0b0',
+ 'antiquewhite4' => '8b8378',
+ 'aqua' => '00ffff',
+ 'aquamarine1' => '7fffd4',
+ 'aquamarine2' => '76eec6',
+ 'aquamarine4' => '458b74',
+ 'azure1' => 'f0ffff',
+ 'azure2' => 'e0eeee',
+ 'azure3' => 'c1cdcd',
+ 'azure4' => '838b8b',
+ 'beige' => 'f5f5dc',
+ 'bisque1' => 'ffe4c4',
+ 'bisque2' => 'eed5b7',
+ 'bisque3' => 'cdb79e',
+ 'bisque4' => '8b7d6b',
+ 'black' => '000000',
+ 'blanchedalmond' => 'ffebcd',
+ 'blue' => '0000ff',
+ 'blue1' => '0000ff',
+ 'blue2' => '0000ee',
+ 'blue4' => '00008b',
+ 'blueviolet' => '8a2be2',
+ 'brown' => 'a52a2a',
+ 'brown1' => 'ff4040',
+ 'brown2' => 'ee3b3b',
+ 'brown3' => 'cd3333',
+ 'brown4' => '8b2323',
+ 'burlywood' => 'deb887',
+ 'burlywood1' => 'ffd39b',
+ 'burlywood2' => 'eec591',
+ 'burlywood3' => 'cdaa7d',
+ 'burlywood4' => '8b7355',
+ 'cadetblue' => '5f9ea0',
+ 'cadetblue1' => '98f5ff',
+ 'cadetblue2' => '8ee5ee',
+ 'cadetblue3' => '7ac5cd',
+ 'cadetblue4' => '53868b',
+ 'chartreuse1' => '7fff00',
+ 'chartreuse2' => '76ee00',
+ 'chartreuse3' => '66cd00',
+ 'chartreuse4' => '458b00',
+ 'chocolate' => 'd2691e',
+ 'chocolate1' => 'ff7f24',
+ 'chocolate2' => 'ee7621',
+ 'chocolate3' => 'cd661d',
+ 'coral' => 'ff7f50',
+ 'coral1' => 'ff7256',
+ 'coral2' => 'ee6a50',
+ 'coral3' => 'cd5b45',
+ 'coral4' => '8b3e2f',
+ 'cornflowerblue' => '6495ed',
+ 'cornsilk1' => 'fff8dc',
+ 'cornsilk2' => 'eee8cd',
+ 'cornsilk3' => 'cdc8b1',
+ 'cornsilk4' => '8b8878',
+ 'cyan1' => '00ffff',
+ 'cyan2' => '00eeee',
+ 'cyan3' => '00cdcd',
+ 'cyan4' => '008b8b',
+ 'darkgoldenrod' => 'b8860b',
+ 'darkgoldenrod1' => 'ffb90f',
+ 'darkgoldenrod2' => 'eead0e',
+ 'darkgoldenrod3' => 'cd950c',
+ 'darkgoldenrod4' => '8b6508',
+ 'darkgreen' => '006400',
+ 'darkkhaki' => 'bdb76b',
+ 'darkolivegreen' => '556b2f',
+ 'darkolivegreen1' => 'caff70',
+ 'darkolivegreen2' => 'bcee68',
+ 'darkolivegreen3' => 'a2cd5a',
+ 'darkolivegreen4' => '6e8b3d',
+ 'darkorange' => 'ff8c00',
+ 'darkorange1' => 'ff7f00',
+ 'darkorange2' => 'ee7600',
+ 'darkorange3' => 'cd6600',
+ 'darkorange4' => '8b4500',
+ 'darkorchid' => '9932cc',
+ 'darkorchid1' => 'bf3eff',
+ 'darkorchid2' => 'b23aee',
+ 'darkorchid3' => '9a32cd',
+ 'darkorchid4' => '68228b',
+ 'darksalmon' => 'e9967a',
+ 'darkseagreen' => '8fbc8f',
+ 'darkseagreen1' => 'c1ffc1',
+ 'darkseagreen2' => 'b4eeb4',
+ 'darkseagreen3' => '9bcd9b',
+ 'darkseagreen4' => '698b69',
+ 'darkslateblue' => '483d8b',
+ 'darkslategray' => '2f4f4f',
+ 'darkslategray1' => '97ffff',
+ 'darkslategray2' => '8deeee',
+ 'darkslategray3' => '79cdcd',
+ 'darkslategray4' => '528b8b',
+ 'darkturquoise' => '00ced1',
+ 'darkviolet' => '9400d3',
+ 'deeppink1' => 'ff1493',
+ 'deeppink2' => 'ee1289',
+ 'deeppink3' => 'cd1076',
+ 'deeppink4' => '8b0a50',
+ 'deepskyblue1' => '00bfff',
+ 'deepskyblue2' => '00b2ee',
+ 'deepskyblue3' => '009acd',
+ 'deepskyblue4' => '00688b',
+ 'dimgray' => '696969',
+ 'dodgerblue1' => '1e90ff',
+ 'dodgerblue2' => '1c86ee',
+ 'dodgerblue3' => '1874cd',
+ 'dodgerblue4' => '104e8b',
+ 'firebrick' => 'b22222',
+ 'firebrick1' => 'ff3030',
+ 'firebrick2' => 'ee2c2c',
+ 'firebrick3' => 'cd2626',
+ 'firebrick4' => '8b1a1a',
+ 'floralwhite' => 'fffaf0',
+ 'forestgreen' => '228b22',
+ 'fuchsia' => 'ff00ff',
+ 'gainsboro' => 'dcdcdc',
+ 'ghostwhite' => 'f8f8ff',
+ 'gold1' => 'ffd700',
+ 'gold2' => 'eec900',
+ 'gold3' => 'cdad00',
+ 'gold4' => '8b7500',
+ 'goldenrod' => 'daa520',
+ 'goldenrod1' => 'ffc125',
+ 'goldenrod2' => 'eeb422',
+ 'goldenrod3' => 'cd9b1d',
+ 'goldenrod4' => '8b6914',
+ 'gray' => 'bebebe',
+ 'gray1' => '030303',
+ 'gray10' => '1a1a1a',
+ 'gray11' => '1c1c1c',
+ 'gray12' => '1f1f1f',
+ 'gray13' => '212121',
+ 'gray14' => '242424',
+ 'gray15' => '262626',
+ 'gray16' => '292929',
+ 'gray17' => '2b2b2b',
+ 'gray18' => '2e2e2e',
+ 'gray19' => '303030',
+ 'gray2' => '050505',
+ 'gray20' => '333333',
+ 'gray21' => '363636',
+ 'gray22' => '383838',
+ 'gray23' => '3b3b3b',
+ 'gray24' => '3d3d3d',
+ 'gray25' => '404040',
+ 'gray26' => '424242',
+ 'gray27' => '454545',
+ 'gray28' => '474747',
+ 'gray29' => '4a4a4a',
+ 'gray3' => '080808',
+ 'gray30' => '4d4d4d',
+ 'gray31' => '4f4f4f',
+ 'gray32' => '525252',
+ 'gray33' => '545454',
+ 'gray34' => '575757',
+ 'gray35' => '595959',
+ 'gray36' => '5c5c5c',
+ 'gray37' => '5e5e5e',
+ 'gray38' => '616161',
+ 'gray39' => '636363',
+ 'gray4' => '0a0a0a',
+ 'gray40' => '666666',
+ 'gray41' => '696969',
+ 'gray42' => '6b6b6b',
+ 'gray43' => '6e6e6e',
+ 'gray44' => '707070',
+ 'gray45' => '737373',
+ 'gray46' => '757575',
+ 'gray47' => '787878',
+ 'gray48' => '7a7a7a',
+ 'gray49' => '7d7d7d',
+ 'gray5' => '0d0d0d',
+ 'gray50' => '7f7f7f',
+ 'gray51' => '828282',
+ 'gray52' => '858585',
+ 'gray53' => '878787',
+ 'gray54' => '8a8a8a',
+ 'gray55' => '8c8c8c',
+ 'gray56' => '8f8f8f',
+ 'gray57' => '919191',
+ 'gray58' => '949494',
+ 'gray59' => '969696',
+ 'gray6' => '0f0f0f',
+ 'gray60' => '999999',
+ 'gray61' => '9c9c9c',
+ 'gray62' => '9e9e9e',
+ 'gray63' => 'a1a1a1',
+ 'gray64' => 'a3a3a3',
+ 'gray65' => 'a6a6a6',
+ 'gray66' => 'a8a8a8',
+ 'gray67' => 'ababab',
+ 'gray68' => 'adadad',
+ 'gray69' => 'b0b0b0',
+ 'gray7' => '121212',
+ 'gray70' => 'b3b3b3',
+ 'gray71' => 'b5b5b5',
+ 'gray72' => 'b8b8b8',
+ 'gray73' => 'bababa',
+ 'gray74' => 'bdbdbd',
+ 'gray75' => 'bfbfbf',
+ 'gray76' => 'c2c2c2',
+ 'gray77' => 'c4c4c4',
+ 'gray78' => 'c7c7c7',
+ 'gray79' => 'c9c9c9',
+ 'gray8' => '141414',
+ 'gray80' => 'cccccc',
+ 'gray81' => 'cfcfcf',
+ 'gray82' => 'd1d1d1',
+ 'gray83' => 'd4d4d4',
+ 'gray84' => 'd6d6d6',
+ 'gray85' => 'd9d9d9',
+ 'gray86' => 'dbdbdb',
+ 'gray87' => 'dedede',
+ 'gray88' => 'e0e0e0',
+ 'gray89' => 'e3e3e3',
+ 'gray9' => '171717',
+ 'gray90' => 'e5e5e5',
+ 'gray91' => 'e8e8e8',
+ 'gray92' => 'ebebeb',
+ 'gray93' => 'ededed',
+ 'gray94' => 'f0f0f0',
+ 'gray95' => 'f2f2f2',
+ 'gray97' => 'f7f7f7',
+ 'gray98' => 'fafafa',
+ 'gray99' => 'fcfcfc',
+ 'green' => '00ff00',
+ 'green1' => '00ff00',
+ 'green2' => '00ee00',
+ 'green3' => '00cd00',
+ 'green4' => '008b00',
+ 'greenyellow' => 'adff2f',
+ 'honeydew1' => 'f0fff0',
+ 'honeydew2' => 'e0eee0',
+ 'honeydew3' => 'c1cdc1',
+ 'honeydew4' => '838b83',
+ 'hotpink' => 'ff69b4',
+ 'hotpink1' => 'ff6eb4',
+ 'hotpink2' => 'ee6aa7',
+ 'hotpink3' => 'cd6090',
+ 'hotpink4' => '8b3a62',
+ 'indianred' => 'cd5c5c',
+ 'indianred1' => 'ff6a6a',
+ 'indianred2' => 'ee6363',
+ 'indianred3' => 'cd5555',
+ 'indianred4' => '8b3a3a',
+ 'ivory1' => 'fffff0',
+ 'ivory2' => 'eeeee0',
+ 'ivory3' => 'cdcdc1',
+ 'ivory4' => '8b8b83',
+ 'khaki' => 'f0e68c',
+ 'khaki1' => 'fff68f',
+ 'khaki2' => 'eee685',
+ 'khaki3' => 'cdc673',
+ 'khaki4' => '8b864e',
+ 'lavender' => 'e6e6fa',
+ 'lavenderblush1' => 'fff0f5',
+ 'lavenderblush2' => 'eee0e5',
+ 'lavenderblush3' => 'cdc1c5',
+ 'lavenderblush4' => '8b8386',
+ 'lawngreen' => '7cfc00',
+ 'lemonchiffon1' => 'fffacd',
+ 'lemonchiffon2' => 'eee9bf',
+ 'lemonchiffon3' => 'cdc9a5',
+ 'lemonchiffon4' => '8b8970',
+ 'light' => 'eedd82',
+ 'lightblue' => 'add8e6',
+ 'lightblue1' => 'bfefff',
+ 'lightblue2' => 'b2dfee',
+ 'lightblue3' => '9ac0cd',
+ 'lightblue4' => '68838b',
+ 'lightcoral' => 'f08080',
+ 'lightcyan1' => 'e0ffff',
+ 'lightcyan2' => 'd1eeee',
+ 'lightcyan3' => 'b4cdcd',
+ 'lightcyan4' => '7a8b8b',
+ 'lightgoldenrod1' => 'ffec8b',
+ 'lightgoldenrod2' => 'eedc82',
+ 'lightgoldenrod3' => 'cdbe70',
+ 'lightgoldenrod4' => '8b814c',
+ 'lightgoldenrodyellow' => 'fafad2',
+ 'lightgray' => 'd3d3d3',
+ 'lightpink' => 'ffb6c1',
+ 'lightpink1' => 'ffaeb9',
+ 'lightpink2' => 'eea2ad',
+ 'lightpink3' => 'cd8c95',
+ 'lightpink4' => '8b5f65',
+ 'lightsalmon1' => 'ffa07a',
+ 'lightsalmon2' => 'ee9572',
+ 'lightsalmon3' => 'cd8162',
+ 'lightsalmon4' => '8b5742',
+ 'lightseagreen' => '20b2aa',
+ 'lightskyblue' => '87cefa',
+ 'lightskyblue1' => 'b0e2ff',
+ 'lightskyblue2' => 'a4d3ee',
+ 'lightskyblue3' => '8db6cd',
+ 'lightskyblue4' => '607b8b',
+ 'lightslateblue' => '8470ff',
+ 'lightslategray' => '778899',
+ 'lightsteelblue' => 'b0c4de',
+ 'lightsteelblue1' => 'cae1ff',
+ 'lightsteelblue2' => 'bcd2ee',
+ 'lightsteelblue3' => 'a2b5cd',
+ 'lightsteelblue4' => '6e7b8b',
+ 'lightyellow1' => 'ffffe0',
+ 'lightyellow2' => 'eeeed1',
+ 'lightyellow3' => 'cdcdb4',
+ 'lightyellow4' => '8b8b7a',
+ 'lime' => '00ff00',
+ 'limegreen' => '32cd32',
+ 'linen' => 'faf0e6',
+ 'magenta' => 'ff00ff',
+ 'magenta2' => 'ee00ee',
+ 'magenta3' => 'cd00cd',
+ 'magenta4' => '8b008b',
+ 'maroon' => 'b03060',
+ 'maroon1' => 'ff34b3',
+ 'maroon2' => 'ee30a7',
+ 'maroon3' => 'cd2990',
+ 'maroon4' => '8b1c62',
+ 'medium' => '66cdaa',
+ 'mediumaquamarine' => '66cdaa',
+ 'mediumblue' => '0000cd',
+ 'mediumorchid' => 'ba55d3',
+ 'mediumorchid1' => 'e066ff',
+ 'mediumorchid2' => 'd15fee',
+ 'mediumorchid3' => 'b452cd',
+ 'mediumorchid4' => '7a378b',
+ 'mediumpurple' => '9370db',
+ 'mediumpurple1' => 'ab82ff',
+ 'mediumpurple2' => '9f79ee',
+ 'mediumpurple3' => '8968cd',
+ 'mediumpurple4' => '5d478b',
+ 'mediumseagreen' => '3cb371',
+ 'mediumslateblue' => '7b68ee',
+ 'mediumspringgreen' => '00fa9a',
+ 'mediumturquoise' => '48d1cc',
+ 'mediumvioletred' => 'c71585',
+ 'midnightblue' => '191970',
+ 'mintcream' => 'f5fffa',
+ 'mistyrose1' => 'ffe4e1',
+ 'mistyrose2' => 'eed5d2',
+ 'mistyrose3' => 'cdb7b5',
+ 'mistyrose4' => '8b7d7b',
+ 'moccasin' => 'ffe4b5',
+ 'navajowhite1' => 'ffdead',
+ 'navajowhite2' => 'eecfa1',
+ 'navajowhite3' => 'cdb38b',
+ 'navajowhite4' => '8b795e',
+ 'navy' => '000080',
+ 'navyblue' => '000080',
+ 'oldlace' => 'fdf5e6',
+ 'olive' => '808000',
+ 'olivedrab' => '6b8e23',
+ 'olivedrab1' => 'c0ff3e',
+ 'olivedrab2' => 'b3ee3a',
+ 'olivedrab4' => '698b22',
+ 'orange' => 'ffa500',
+ 'orange1' => 'ffa500',
+ 'orange2' => 'ee9a00',
+ 'orange3' => 'cd8500',
+ 'orange4' => '8b5a00',
+ 'orangered1' => 'ff4500',
+ 'orangered2' => 'ee4000',
+ 'orangered3' => 'cd3700',
+ 'orangered4' => '8b2500',
+ 'orchid' => 'da70d6',
+ 'orchid1' => 'ff83fa',
+ 'orchid2' => 'ee7ae9',
+ 'orchid3' => 'cd69c9',
+ 'orchid4' => '8b4789',
+ 'pale' => 'db7093',
+ 'palegoldenrod' => 'eee8aa',
+ 'palegreen' => '98fb98',
+ 'palegreen1' => '9aff9a',
+ 'palegreen2' => '90ee90',
+ 'palegreen3' => '7ccd7c',
+ 'palegreen4' => '548b54',
+ 'paleturquoise' => 'afeeee',
+ 'paleturquoise1' => 'bbffff',
+ 'paleturquoise2' => 'aeeeee',
+ 'paleturquoise3' => '96cdcd',
+ 'paleturquoise4' => '668b8b',
+ 'palevioletred' => 'db7093',
+ 'palevioletred1' => 'ff82ab',
+ 'palevioletred2' => 'ee799f',
+ 'palevioletred3' => 'cd6889',
+ 'palevioletred4' => '8b475d',
+ 'papayawhip' => 'ffefd5',
+ 'peachpuff1' => 'ffdab9',
+ 'peachpuff2' => 'eecbad',
+ 'peachpuff3' => 'cdaf95',
+ 'peachpuff4' => '8b7765',
+ 'pink' => 'ffc0cb',
+ 'pink1' => 'ffb5c5',
+ 'pink2' => 'eea9b8',
+ 'pink3' => 'cd919e',
+ 'pink4' => '8b636c',
+ 'plum' => 'dda0dd',
+ 'plum1' => 'ffbbff',
+ 'plum2' => 'eeaeee',
+ 'plum3' => 'cd96cd',
+ 'plum4' => '8b668b',
+ 'powderblue' => 'b0e0e6',
+ 'purple' => 'a020f0',
+ 'rebeccapurple' => '663399',
+ 'purple1' => '9b30ff',
+ 'purple2' => '912cee',
+ 'purple3' => '7d26cd',
+ 'purple4' => '551a8b',
+ 'red' => 'ff0000',
+ 'red1' => 'ff0000',
+ 'red2' => 'ee0000',
+ 'red3' => 'cd0000',
+ 'red4' => '8b0000',
+ 'rosybrown' => 'bc8f8f',
+ 'rosybrown1' => 'ffc1c1',
+ 'rosybrown2' => 'eeb4b4',
+ 'rosybrown3' => 'cd9b9b',
+ 'rosybrown4' => '8b6969',
+ 'royalblue' => '4169e1',
+ 'royalblue1' => '4876ff',
+ 'royalblue2' => '436eee',
+ 'royalblue3' => '3a5fcd',
+ 'royalblue4' => '27408b',
+ 'saddlebrown' => '8b4513',
+ 'salmon' => 'fa8072',
+ 'salmon1' => 'ff8c69',
+ 'salmon2' => 'ee8262',
+ 'salmon3' => 'cd7054',
+ 'salmon4' => '8b4c39',
+ 'sandybrown' => 'f4a460',
+ 'seagreen1' => '54ff9f',
+ 'seagreen2' => '4eee94',
+ 'seagreen3' => '43cd80',
+ 'seagreen4' => '2e8b57',
+ 'seashell1' => 'fff5ee',
+ 'seashell2' => 'eee5de',
+ 'seashell3' => 'cdc5bf',
+ 'seashell4' => '8b8682',
+ 'sienna' => 'a0522d',
+ 'sienna1' => 'ff8247',
+ 'sienna2' => 'ee7942',
+ 'sienna3' => 'cd6839',
+ 'sienna4' => '8b4726',
+ 'silver' => 'c0c0c0',
+ 'skyblue' => '87ceeb',
+ 'skyblue1' => '87ceff',
+ 'skyblue2' => '7ec0ee',
+ 'skyblue3' => '6ca6cd',
+ 'skyblue4' => '4a708b',
+ 'slateblue' => '6a5acd',
+ 'slateblue1' => '836fff',
+ 'slateblue2' => '7a67ee',
+ 'slateblue3' => '6959cd',
+ 'slateblue4' => '473c8b',
+ 'slategray' => '708090',
+ 'slategray1' => 'c6e2ff',
+ 'slategray2' => 'b9d3ee',
+ 'slategray3' => '9fb6cd',
+ 'slategray4' => '6c7b8b',
+ 'snow1' => 'fffafa',
+ 'snow2' => 'eee9e9',
+ 'snow3' => 'cdc9c9',
+ 'snow4' => '8b8989',
+ 'springgreen1' => '00ff7f',
+ 'springgreen2' => '00ee76',
+ 'springgreen3' => '00cd66',
+ 'springgreen4' => '008b45',
+ 'steelblue' => '4682b4',
+ 'steelblue1' => '63b8ff',
+ 'steelblue2' => '5cacee',
+ 'steelblue3' => '4f94cd',
+ 'steelblue4' => '36648b',
+ 'tan' => 'd2b48c',
+ 'tan1' => 'ffa54f',
+ 'tan2' => 'ee9a49',
+ 'tan3' => 'cd853f',
+ 'tan4' => '8b5a2b',
+ 'teal' => '008080',
+ 'thistle' => 'd8bfd8',
+ 'thistle1' => 'ffe1ff',
+ 'thistle2' => 'eed2ee',
+ 'thistle3' => 'cdb5cd',
+ 'thistle4' => '8b7b8b',
+ 'tomato1' => 'ff6347',
+ 'tomato2' => 'ee5c42',
+ 'tomato3' => 'cd4f39',
+ 'tomato4' => '8b3626',
+ 'turquoise' => '40e0d0',
+ 'turquoise1' => '00f5ff',
+ 'turquoise2' => '00e5ee',
+ 'turquoise3' => '00c5cd',
+ 'turquoise4' => '00868b',
+ 'violet' => 'ee82ee',
+ 'violetred' => 'd02090',
+ 'violetred1' => 'ff3e96',
+ 'violetred2' => 'ee3a8c',
+ 'violetred3' => 'cd3278',
+ 'violetred4' => '8b2252',
+ 'wheat' => 'f5deb3',
+ 'wheat1' => 'ffe7ba',
+ 'wheat2' => 'eed8ae',
+ 'wheat3' => 'cdba96',
+ 'wheat4' => '8b7e66',
+ 'white' => 'ffffff',
+ 'whitesmoke' => 'f5f5f5',
+ 'yellow' => 'ffff00',
+ 'yellow1' => 'ffff00',
+ 'yellow2' => 'eeee00',
+ 'yellow3' => 'cdcd00',
+ 'yellow4' => '8b8b00',
+ 'yellowgreen' => '9acd32',
+ ];
+
+ public static function colourNameLookup(string $colorName): string
+ {
+ return self::COLOUR_MAP[$colorName] ?? '';
+ }
+
+ public static function convertColour(string $colorName): string
+ {
+ $colorName = trim($colorName);
+ if (preg_match('/^[#][a-fA-F0-9]{6}$/', $colorName) === 1) {
+ return substr($colorName, 1);
+ }
+ if (preg_match('/^[#][a-fA-F0-9]{3}$/', $colorName) === 1) {
+ return "{$colorName[1]}{$colorName[1]}{$colorName[2]}{$colorName[2]}{$colorName[3]}{$colorName[3]}";
+ }
+
+ return self::COLOUR_MAP[$colorName] ?? $colorName;
+ }
+
+ public static function setArrayColour(array &$array, string $index, string $colorName): void
+ {
+ $array[$index] = self::convertColour($colorName);
+ }
+}
diff --git a/src/PhpWord/Style/AbstractStyle.php b/src/PhpWord/Style/AbstractStyle.php
index 338ae6a5ef..5a553c43c4 100644
--- a/src/PhpWord/Style/AbstractStyle.php
+++ b/src/PhpWord/Style/AbstractStyle.php
@@ -51,6 +51,9 @@ abstract class AbstractStyle
*/
protected $aliases = [];
+ /** @var string */
+ protected $basedOn = '';
+
/**
* Is this an automatic style? (Used primarily in OpenDocument driver).
*
@@ -84,6 +87,18 @@ public function setStyleName($value)
return $this;
}
+ public function getBasedOn(): string
+ {
+ return $this->basedOn;
+ }
+
+ public function setBasedOn(string $value): self
+ {
+ $this->basedOn = $value;
+
+ return $this;
+ }
+
/**
* Get index number.
*
diff --git a/src/PhpWord/Style/Border.php b/src/PhpWord/Style/Border.php
index 722e09931a..d97b884fdb 100644
--- a/src/PhpWord/Style/Border.php
+++ b/src/PhpWord/Style/Border.php
@@ -529,6 +529,14 @@ public function setBorderBottomStyle($value = null)
public function hasBorder()
{
$borders = $this->getBorderSize();
+ if ($borders !== array_filter($borders, 'is_null')) {
+ return true;
+ }
+ $borders = $this->getBorderColor();
+ if ($borders !== array_filter($borders, 'is_null')) {
+ return true;
+ }
+ $borders = $this->getBorderStyle();
return $borders !== array_filter($borders, 'is_null');
}
diff --git a/src/PhpWord/Style/Paragraph.php b/src/PhpWord/Style/Paragraph.php
index 5dda985fe5..a71c4c0ffc 100644
--- a/src/PhpWord/Style/Paragraph.php
+++ b/src/PhpWord/Style/Paragraph.php
@@ -70,7 +70,7 @@ class Paragraph extends Border
*
* @var string
*/
- private $basedOn = 'Normal';
+ protected $basedOn = 'Normal';
/**
* Style for next paragraph.
@@ -274,30 +274,6 @@ public function setAlignment($value)
return $this;
}
- /**
- * Get parent style ID.
- *
- * @return string
- */
- public function getBasedOn()
- {
- return $this->basedOn;
- }
-
- /**
- * Set parent style ID.
- *
- * @param string $value
- *
- * @return self
- */
- public function setBasedOn($value = 'Normal')
- {
- $this->basedOn = $value;
-
- return $this;
- }
-
/**
* Get style for next paragraph.
*
diff --git a/src/PhpWord/Style/Table.php b/src/PhpWord/Style/Table.php
index 21e8a74823..a44b89a5f2 100644
--- a/src/PhpWord/Style/Table.php
+++ b/src/PhpWord/Style/Table.php
@@ -111,6 +111,20 @@ class Table extends Border
*/
private $borderInsideVColor;
+ /**
+ * Border style inside horizontal.
+ *
+ * @var string
+ */
+ protected $borderInsideHStyle = '';
+
+ /**
+ * Border style inside vertical.
+ *
+ * @var string
+ */
+ protected $borderInsideVStyle = '';
+
/**
* Shading.
*
@@ -169,6 +183,9 @@ class Table extends Border
*/
private $bidiVisual;
+ /** @var string */
+ private $tblStyle = '';
+
/**
* Create new table style.
*
@@ -261,6 +278,42 @@ public function getBorderSize()
];
}
+ /**
+ * Get border style.
+ *
+ * @return string[]
+ */
+ public function getBorderStyle()
+ {
+ return [
+ $this->getBorderTopStyle(),
+ $this->getBorderLeftStyle(),
+ $this->getBorderRightStyle(),
+ $this->getBorderBottomStyle(),
+ $this->getBorderInsideHStyle(),
+ $this->getBorderInsideVStyle(),
+ ];
+ }
+
+ /**
+ * Set border style.
+ *
+ * @param string $value
+ *
+ * @return self
+ */
+ public function setBorderStyle($value = null)
+ {
+ $this->setBorderTopStyle($value);
+ $this->setBorderLeftStyle($value);
+ $this->setBorderRightStyle($value);
+ $this->setBorderBottomStyle($value);
+ $this->setBorderInsideHStyle($value);
+ $this->setBorderInsideVStyle($value);
+
+ return $this;
+ }
+
/**
* Set TLRBHV Border Size.
*
@@ -316,6 +369,26 @@ public function setBorderColor($value = null)
return $this;
}
+ /**
+ * Get border style inside horizontal.
+ *
+ * @return string
+ */
+ public function getBorderInsideHStyle()
+ {
+ return (string) $this->getTableOnlyProperty('borderInsideHStyle');
+ }
+
+ /**
+ * Get border style inside horizontal.
+ *
+ * @return string
+ */
+ public function getBorderInsideVStyle()
+ {
+ return (string) $this->getTableOnlyProperty('borderInsideVStyle');
+ }
+
/**
* Get border size inside horizontal.
*
@@ -338,6 +411,34 @@ public function setBorderInsideHSize($value = null)
return $this->setTableOnlyProperty('borderInsideHSize', $value);
}
+ /**
+ * Set border style inside horizontal.
+ *
+ * @param string $value
+ *
+ * @return self
+ */
+ public function setBorderInsideHStyle($value = '')
+ {
+ $this->setTableOnlyProperty('borderInsideHStyle', $value, false);
+
+ return $this;
+ }
+
+ /**
+ * Set border style inside horizontal.
+ *
+ * @param ?string $value
+ *
+ * @return self
+ */
+ public function setBorderInsideVStyle($value = null)
+ {
+ $this->setTableOnlyProperty('borderInsideVStyle', $value, false);
+
+ return $this;
+ }
+
/**
* Get border color inside horizontal.
*
@@ -792,4 +893,16 @@ public function setBidiVisual($bidi)
return $this;
}
+
+ public function getTblStyle(): string
+ {
+ return $this->tblStyle;
+ }
+
+ public function setTblStyle(string $tblStyle): self
+ {
+ $this->tblStyle = $tblStyle;
+
+ return $this;
+ }
}
diff --git a/src/PhpWord/Writer/HTML/Element/Table.php b/src/PhpWord/Writer/HTML/Element/Table.php
index b66e2f8c92..98f69e3f3f 100644
--- a/src/PhpWord/Writer/HTML/Element/Table.php
+++ b/src/PhpWord/Writer/HTML/Element/Table.php
@@ -42,7 +42,8 @@ public function write()
$rows = $this->element->getRows();
$rowCount = count($rows);
if ($rowCount > 0) {
- $content .= 'getTableStyle($this->element->getStyle()) . '>' . PHP_EOL;
+ $tableCss = $this->getTableStyle($this->element->getStyle());
+ $content .= '' . PHP_EOL;
for ($i = 0; $i < $rowCount; ++$i) {
/** @var \PhpOffice\PhpWord\Element\Row $row Type hint */
@@ -54,7 +55,7 @@ public function write()
$rowCellCount = count($rowCells);
for ($j = 0; $j < $rowCellCount; ++$j) {
$cellStyle = $rowCells[$j]->getStyle();
- $cellStyleCss = $this->getTableStyle($cellStyle);
+ $cellStyleCss = $this->getTableStyle($cellStyle) ?: $tableCss;
$cellBgColor = $cellStyle->getBgColor();
$cellFgColor = null;
if ($cellBgColor && $cellBgColor !== 'auto') {
diff --git a/src/PhpWord/Writer/HTML/Part/Head.php b/src/PhpWord/Writer/HTML/Part/Head.php
index 79235e1c4a..bbbec3ece6 100644
--- a/src/PhpWord/Writer/HTML/Part/Head.php
+++ b/src/PhpWord/Writer/HTML/Part/Head.php
@@ -121,6 +121,9 @@ private function writeStyles(): string
'td' => [
'border' => '1px solid black',
],
+ 'th' => [
+ 'border' => '1px solid black',
+ ],
] as $selector => $style) {
$styleWriter = new GenericStyleWriter($style);
$css .= $selector . ' {' . $styleWriter->write() . '}' . PHP_EOL;
diff --git a/src/PhpWord/Writer/HTML/Style/Font.php b/src/PhpWord/Writer/HTML/Style/Font.php
index 1001b64d2e..07cb6f64f3 100644
--- a/src/PhpWord/Writer/HTML/Style/Font.php
+++ b/src/PhpWord/Writer/HTML/Style/Font.php
@@ -74,6 +74,13 @@ public function write()
} elseif ($style->isRTL() === false) {
$css['direction'] = 'ltr';
}
+ $shading = $style->getShading();
+ if ($shading !== null) {
+ $fill = $shading->getFill();
+ if (!empty($fill)) {
+ $css['background-color'] = preg_match('/^[0-9a-fA-F]{6}$/', $fill) ? "#$fill" : $fill;
+ }
+ }
return $this->assembleCss($css);
}
diff --git a/src/PhpWord/Writer/HTML/Style/Table.php b/src/PhpWord/Writer/HTML/Style/Table.php
index a59d1cdba4..5b19be5e75 100644
--- a/src/PhpWord/Writer/HTML/Style/Table.php
+++ b/src/PhpWord/Writer/HTML/Style/Table.php
@@ -58,7 +58,7 @@ public function write()
if ($outval === 'single') {
$outval = 'solid';
}
- if (is_string($outval) && 1 == preg_match('/^[a-z]+$/', $outval)) {
+ if (is_string($outval) && 1 === preg_match('/^[a-z]+$/', $outval)) {
$css['border-' . lcfirst($direction) . '-style'] = $outval;
}
}
@@ -66,7 +66,9 @@ public function write()
$method = 'getBorder' . $direction . 'Color';
if (method_exists($style, $method)) {
$outval = $style->{$method}();
- if (is_string($outval) && 1 == preg_match('/^[a-z]+$/', $outval)) {
+ if (is_string($outval) && 1 === preg_match('/^[a-zA-Z0-9]{6}$/', $outval)) {
+ $css['border-' . lcfirst($direction) . '-color'] = "#$outval";
+ } elseif (is_string($outval) && 1 === preg_match('/^[a-z][a-z0-9]+$/', $outval)) {
$css['border-' . lcfirst($direction) . '-color'] = $outval;
}
}
diff --git a/src/PhpWord/Writer/Word2007/Part/Styles.php b/src/PhpWord/Writer/Word2007/Part/Styles.php
index edf0314cc2..a19895cea2 100644
--- a/src/PhpWord/Writer/Word2007/Part/Styles.php
+++ b/src/PhpWord/Writer/Word2007/Part/Styles.php
@@ -60,12 +60,13 @@ public function write()
if ($styleName == 'Normal') {
continue;
}
+ $name = $style->getStyleName();
// Get style class and execute if the private method exists
$styleClass = substr(get_class($style), strrpos(get_class($style), '\\') + 1);
$method = "write{$styleClass}Style";
if (method_exists($this, $method)) {
- $this->$method($xmlWriter, $styleName, $style);
+ $this->$method($xmlWriter, $styleName, $style, $name);
}
}
}
@@ -169,7 +170,7 @@ private function writeDefaultStyles(XMLWriter $xmlWriter, $styles): void
*
* @param string $styleName
*/
- private function writeFontStyle(XMLWriter $xmlWriter, $styleName, FontStyle $style): void
+ private function writeFontStyle(XMLWriter $xmlWriter, $styleName, FontStyle $style, string $name): void
{
$paragraphStyle = $style->getParagraph();
$styleType = $style->getStyleType();
@@ -212,7 +213,7 @@ private function writeFontStyle(XMLWriter $xmlWriter, $styleName, FontStyle $sty
if (null !== $paragraphStyle) {
if ($paragraphStyle->getStyleName() != null) {
$xmlWriter->writeElementBlock('w:basedOn', 'w:val', $paragraphStyle->getStyleName());
- } elseif ($paragraphStyle->getBasedOn() != null) {
+ } elseif ($paragraphStyle->getBasedOn() !== '') {
$xmlWriter->writeElementBlock('w:basedOn', 'w:val', $paragraphStyle->getBasedOn());
}
}
@@ -235,7 +236,7 @@ private function writeFontStyle(XMLWriter $xmlWriter, $styleName, FontStyle $sty
*
* @param string $styleName
*/
- private function writeParagraphStyle(XMLWriter $xmlWriter, $styleName, ParagraphStyle $style): void
+ private function writeParagraphStyle(XMLWriter $xmlWriter, $styleName, ParagraphStyle $style, string $name): void
{
$xmlWriter->startElement('w:style');
$xmlWriter->writeAttribute('w:type', 'paragraph');
@@ -247,7 +248,7 @@ private function writeParagraphStyle(XMLWriter $xmlWriter, $styleName, Paragraph
// Parent style
$basedOn = $style->getBasedOn();
- $xmlWriter->writeElementIf(null !== $basedOn, 'w:basedOn', 'w:val', $basedOn);
+ $xmlWriter->writeElementIf('' !== $basedOn, 'w:basedOn', 'w:val', $basedOn);
// Next paragraph style
$next = $style->getNext();
@@ -265,15 +266,18 @@ private function writeParagraphStyle(XMLWriter $xmlWriter, $styleName, Paragraph
*
* @param string $styleName
*/
- private function writeTableStyle(XMLWriter $xmlWriter, $styleName, TableStyle $style): void
+ private function writeTableStyle(XMLWriter $xmlWriter, $styleName, TableStyle $style, string $name): void
{
$xmlWriter->startElement('w:style');
$xmlWriter->writeAttribute('w:type', 'table');
$xmlWriter->writeAttribute('w:customStyle', '1');
$xmlWriter->writeAttribute('w:styleId', $styleName);
$xmlWriter->startElement('w:name');
- $xmlWriter->writeAttribute('w:val', $styleName);
+ $xmlWriter->writeAttribute('w:val', $name ?: $styleName);
$xmlWriter->endElement();
+ $basedOn = $style->getBasedOn();
+ $xmlWriter->writeElementIf('' !== $basedOn, 'w:basedOn', 'w:val', $basedOn);
+
$xmlWriter->startElement('w:uiPriority');
$xmlWriter->writeAttribute('w:val', '99');
$xmlWriter->endElement();
diff --git a/src/PhpWord/Writer/Word2007/Style/MarginBorder.php b/src/PhpWord/Writer/Word2007/Style/MarginBorder.php
index b464e66ffb..ed405442a1 100644
--- a/src/PhpWord/Writer/Word2007/Style/MarginBorder.php
+++ b/src/PhpWord/Writer/Word2007/Style/MarginBorder.php
@@ -65,14 +65,12 @@ public function write(): void
$sides = ['top', 'left', 'right', 'bottom', 'insideH', 'insideV'];
foreach ($this->sizes as $i => $size) {
- if ($size !== null) {
- $color = null;
- if (isset($this->colors[$i])) {
- $color = $this->colors[$i];
- }
- $style = $this->styles[$i] ?? 'single';
- $this->writeSide($xmlWriter, $sides[$i], $this->sizes[$i], $color, $style);
+ $color = null;
+ if (isset($this->colors[$i])) {
+ $color = $this->colors[$i];
}
+ $style = $this->styles[$i] ?? 'single';
+ $this->writeSide($xmlWriter, $sides[$i], $this->sizes[$i], $color, $style);
}
}
@@ -80,8 +78,8 @@ public function write(): void
* Write side.
*
* @param string $side
- * @param int $width
- * @param string $color
+ * @param ?int $width
+ * @param ?string $color
* @param string $borderStyle
*/
private function writeSide(XMLWriter $xmlWriter, $side, $width, $color = null, $borderStyle = 'solid'): void
@@ -94,7 +92,7 @@ private function writeSide(XMLWriter $xmlWriter, $side, $width, $color = null, $
}
}
$xmlWriter->writeAttribute('w:val', $borderStyle);
- $xmlWriter->writeAttribute('w:sz', $width);
+ $xmlWriter->writeAttributeIf($width != null, 'w:sz', $width);
$xmlWriter->writeAttributeIf($color != null, 'w:color', $color);
if (!empty($this->attributes)) {
if (isset($this->attributes['space'])) {
diff --git a/src/PhpWord/Writer/Word2007/Style/Table.php b/src/PhpWord/Writer/Word2007/Style/Table.php
index 446fc3b1a4..efa6614149 100644
--- a/src/PhpWord/Writer/Word2007/Style/Table.php
+++ b/src/PhpWord/Writer/Word2007/Style/Table.php
@@ -64,6 +64,8 @@ private function writeStyle(XMLWriter $xmlWriter, TableStyle $style): void
{
// w:tblPr
$xmlWriter->startElement('w:tblPr');
+ $tblStyle = $style->getTblStyle();
+ $xmlWriter->writeElementIf($tblStyle !== '', 'w:tblStyle', 'w:val', $tblStyle);
// Table alignment
if ('' !== $style->getAlignment()) {
@@ -140,6 +142,7 @@ private function writeBorder(XMLWriter $xmlWriter, TableStyle $style): void
$styleWriter = new MarginBorder($xmlWriter);
$styleWriter->setSizes($style->getBorderSize());
$styleWriter->setColors($style->getBorderColor());
+ $styleWriter->setStyles($style->getBorderStyle());
$styleWriter->write();
$xmlWriter->endElement(); // w:tblBorders
diff --git a/tests/PhpWordTests/Reader/Word2007/StyleTableTest.php b/tests/PhpWordTests/Reader/Word2007/StyleTableTest.php
new file mode 100644
index 0000000000..cb8c255018
--- /dev/null
+++ b/tests/PhpWordTests/Reader/Word2007/StyleTableTest.php
@@ -0,0 +1,56 @@
+load($file);
+ self::assertSame('Times New Roman', $phpWord->getDefaultFontName());
+
+ $elements = $phpWord->getSection(0)->getElements();
+ self::assertInstanceOf(Table::class, $elements[2]);
+ $style = $elements[2]->getStyle();
+ self::assertIsObject($style);
+ self::assertSame('Tablaconcuadrcula', $style->getTblStyle());
+ self::assertSame('none', $style->getBorderTopStyle());
+ $baseStyle = Style::getStyle('Tablaconcuadrcula');
+ self::assertInstanceOf(TableStyle::class, $baseStyle);
+ self::assertSame('Table Grid', $baseStyle->getStyleName());
+ self::assertSame('Tablanormal', $baseStyle->getBasedOn());
+ self::assertSame('single', $baseStyle->getBorderTopStyle());
+ }
+}
diff --git a/tests/PhpWordTests/Shared/Html2402Test.php b/tests/PhpWordTests/Shared/Html2402Test.php
new file mode 100644
index 0000000000..043aa0361e
--- /dev/null
+++ b/tests/PhpWordTests/Shared/Html2402Test.php
@@ -0,0 +1,209 @@
+
+
+
+ header a |
+ header b |
+ header c |
+
+
+
+ 1 | 2 |
+ This is bold text | | 6 |
+
+
+HTML;
+ $phpWord = new PhpWord();
+ $section = $phpWord->addSection();
+ Html::addHtml($section, $html, false, false);
+ $elements = $section->getElements();
+ $table = $elements[0];
+ self::assertInstanceOf(Table::class, $table);
+ $style = $table->getStyle();
+ self::assertInstanceOf(TableStyle::class, $style);
+ self::assertSame('none', $style->getBorderBottomStyle());
+ $rows = $table->getRows();
+ self::assertCount(3, $rows);
+ $cells = $rows[1]->getCells();
+ self::assertCount(2, $cells);
+ self::assertSame('dotted', $cells[0]->getStyle()->getBorderRightStyle());
+ self::assertSame('FF0000', $cells[0]->getStyle()->getBorderRightColor());
+ self::assertEmpty($cells[1]->getStyle()->getBorderRightStyle());
+ $writer = new HtmlWriter($phpWord);
+ $content = $writer->getContent();
+ $substring = 'table-layout: auto; border-top-style: none; border-top-width: 0pt; border-left-style: none; border-left-width: 0pt; border-bottom-style: none; border-bottom-width: 0pt; border-right-style: none; border-right-width: 0pt;';
+ $count = substr_count($content, $substring);
+ $expected = substr_count($content, 'header c', $content);
+ }
+
+ public function testParseTableStyleBorderNone(): void
+ {
+ $html = <<
+
+
+ header a |
+ header b |
+ header c |
+
+
+
+ 1 | 2 |
+ This is bold text | | 6 |
+
+
+HTML;
+ $phpWord = new PhpWord();
+ $section = $phpWord->addSection();
+ Html::addHtml($section, $html, false, false);
+ $elements = $section->getElements();
+ $table = $elements[0];
+ self::assertInstanceOf(Table::class, $table);
+ $style = $table->getStyle();
+ self::assertInstanceOf(TableStyle::class, $style);
+ self::assertSame('none', $style->getBorderBottomStyle());
+ $rows = $table->getRows();
+ self::assertCount(3, $rows);
+ $cells = $rows[1]->getCells();
+ self::assertCount(2, $cells);
+ self::assertSame('dotted', $cells[0]->getStyle()->getBorderRightStyle());
+ self::assertSame('ff0000', $cells[0]->getStyle()->getBorderRightColor());
+ self::assertEmpty($cells[1]->getStyle()->getBorderRightStyle());
+ $writer = new HtmlWriter($phpWord);
+ $content = $writer->getContent();
+ $substring = 'table-layout: auto; border-top-style: none; border-left-style: none; border-bottom-style: none; border-right-style: none;';
+ $count = substr_count($content, $substring);
+ $expected = substr_count($content, '
+
+
+ header a |
+ header b |
+ header c |
+
+
+
+ 1 | 2 |
+ This is bold text | | 6 |
+
+
+HTML;
+ $phpWord = new PhpWord();
+ $section = $phpWord->addSection();
+ Html::addHtml($section, $html, false, false);
+ $elements = $section->getElements();
+ $table = $elements[0];
+ self::assertInstanceOf(Table::class, $table);
+ $style = $table->getStyle();
+ self::assertInstanceOf(TableStyle::class, $style);
+ self::assertSame('none', $style->getBorderBottomStyle());
+ $rows = $table->getRows();
+ self::assertCount(3, $rows);
+ $cells = $rows[1]->getCells();
+ self::assertCount(2, $cells);
+ self::assertSame('dotted', $cells[0]->getStyle()->getBorderRightStyle());
+ self::assertSame('ff0000', $cells[0]->getStyle()->getBorderRightColor());
+ self::assertEmpty($cells[1]->getStyle()->getBorderRightStyle());
+ }
+
+ public function testParseTableStyleBorder2px(): void
+ {
+ $html = <<
+
+
+ header a |
+ header b |
+ header c |
+
+
+
+ 1 | 2 |
+ This is bold text | | 6 |
+
+
+HTML;
+ $phpWord = new PhpWord();
+ $section = $phpWord->addSection();
+ Html::addHtml($section, $html, false, false);
+ $elements = $section->getElements();
+ $table = $elements[0];
+ self::assertInstanceOf(Table::class, $table);
+ $style = $table->getStyle();
+ self::assertInstanceOf(TableStyle::class, $style);
+ self::assertSame('dashed', $style->getBorderBottomStyle());
+ self::assertSame('dashed', $style->getBorderInsideHStyle());
+ self::assertSame('dashed', $style->getBorderInsideVStyle());
+ self::assertSame(15, $style->getBorderBottomSize());
+ self::assertSame('00ff00', $style->getBorderBottomColor());
+ $rows = $table->getRows();
+ self::assertCount(3, $rows);
+ $cells = $rows[1]->getCells();
+ self::assertCount(2, $cells);
+ self::assertSame('dotted', $cells[0]->getStyle()->getBorderRightStyle());
+ self::assertSame('ff0000', $cells[0]->getStyle()->getBorderRightColor());
+ self::assertEmpty($cells[1]->getStyle()->getBorderRightStyle());
+ $writer = new HtmlWriter($phpWord);
+ $content = $writer->getContent();
+
+ $substring = 'table-layout: auto; border-top-style: dashed; border-top-color: #00ff00; border-top-width: 0.75pt; border-left-style: dashed; border-left-color: #00ff00; border-left-width: 0.75pt; border-bottom-style: dashed; border-bottom-color: #00ff00; border-bottom-width: 0.75pt; border-right-style: dashed; border-right-color: #00ff00; border-right-width: 0.75pt;';
+ $count = substr_count($content, $substring);
+ $expected = substr_count($content, 'elementExists($xpath));
- self::assertEquals('red', $doc->getElement($xpath)->getAttribute('w:fill'));
+ self::assertEquals('ff0000', $doc->getElement($xpath)->getAttribute('w:fill'));
}
/**
@@ -1180,7 +1180,7 @@ public function testParseHorizontalRule(): void
self::assertTrue($doc->elementExists($xpath));
self::assertEquals('single', $doc->getElement($xpath)->getAttribute('w:val'));
self::assertEquals((int) (5 * 15 / 2), $doc->getElement($xpath)->getAttribute('w:sz'));
- self::assertEquals('lightblue', $doc->getElement($xpath)->getAttribute('w:color'));
+ self::assertEquals('add8e6', $doc->getElement($xpath)->getAttribute('w:color'));
$xpath = '/w:document/w:body/w:p[4]/w:pPr/w:spacing';
self::assertTrue($doc->elementExists($xpath));
diff --git a/tests/PhpWordTests/Writer/ODText/Part/ContentTest.php b/tests/PhpWordTests/Writer/ODText/Part/ContentTest.php
index e1dc78a32b..e6cad15ea0 100644
--- a/tests/PhpWordTests/Writer/ODText/Part/ContentTest.php
+++ b/tests/PhpWordTests/Writer/ODText/Part/ContentTest.php
@@ -19,6 +19,7 @@
namespace PhpOffice\PhpWordTests\Writer\ODText\Part;
use PhpOffice\PhpWord\PhpWord;
+use PhpOffice\PhpWord\Settings;
use PhpOffice\PhpWord\SimpleType\Jc;
use PhpOffice\PhpWordTests\TestHelperDOCX;
@@ -29,11 +30,20 @@
*/
class ContentTest extends \PHPUnit\Framework\TestCase
{
+ /** @var string */
+ private $defaultFontName;
+
/**
* Executed before each method of the class.
*/
+ protected function setUp(): void
+ {
+ $this->defaultFontName = Settings::getDefaultFontName();
+ }
+
protected function tearDown(): void
{
+ Settings::setDefaultFontName($this->defaultFontName);
TestHelperDOCX::clear();
}
diff --git a/tests/PhpWordTests/_files/documents/word.2474.docx b/tests/PhpWordTests/_files/documents/word.2474.docx
new file mode 100644
index 0000000000..8ecbaef2b3
Binary files /dev/null and b/tests/PhpWordTests/_files/documents/word.2474.docx differ