diff --git a/docs/changes/1.x/1.4.0.md b/docs/changes/1.x/1.4.0.md
index ed095035e3..5f542dec40 100644
--- a/docs/changes/1.x/1.4.0.md
+++ b/docs/changes/1.x/1.4.0.md
@@ -25,6 +25,7 @@
- Writer ODText: Support for images inside a textRun by [@Progi1984](https://github.com/Progi1984) fixing [#2240](https://github.com/PHPOffice/PHPWord/issues/2240) in [#2668](https://github.com/PHPOffice/PHPWord/pull/2668)
- Allow vAlign and vMerge on Style\Cell to be set to null by [@SpraxDev](https://github.com/SpraxDev) fixing [#2673](https://github.com/PHPOffice/PHPWord/issues/2673) in [#2676](https://github.com/PHPOffice/PHPWord/pull/2676)
- Reader HTML: Support for differents size units for table by [@Progi1984](https://github.com/Progi1984) fixing [#2384](https://github.com/PHPOffice/PHPWord/issues/2384), [#2701](https://github.com/PHPOffice/PHPWord/issues/2701) in [#2725](https://github.com/PHPOffice/PHPWord/pull/2725)
+- Html addHTML : Support different order for inline style border by [@Azamat8405](https://github.com/Azamat8405) fixing [#1670](https://github.com/PHPOffice/PHPWord/issues/1670)
- Reader Word2007: Respect paragraph indent units by [@tugmaks](https://github.com/tugmaks) & [@Progi1984](https://github.com/Progi1984) fixing [#507](https://github.com/PHPOffice/PHPWord/issues/507) in [#2726](https://github.com/PHPOffice/PHPWord/pull/2726)
- Reader Word2007: Support Header elements within Title elements by [@SpraxDev](https://github.com/SpraxDev) fixing [#2616](https://github.com/PHPOffice/PHPWord/issues/2616), [#2426](https://github.com/PHPOffice/PHPWord/issues/2426) in [#2674](https://github.com/PHPOffice/PHPWord/pull/2674)
- Reader HTML: Support for inherit value for property line-height by [@Progi1984](https://github.com/Progi1984) fixing [#2683](https://github.com/PHPOffice/PHPWord/issues/2683) in [#2733](https://github.com/PHPOffice/PHPWord/pull/2733)
diff --git a/docs/usage/shared/html.md b/docs/usage/shared/html.md
new file mode 100644
index 0000000000..cb2a6101bd
--- /dev/null
+++ b/docs/usage/shared/html.md
@@ -0,0 +1,51 @@
+# HTML
+
+## HTML
+You can generate a Word file from html.
+
+``` php
+addSection([
+ 'orientation' => 'landscape',
+ 'marginLeft' => (int)round(20 * 56.6929133858),
+ 'marginRight' => (int)round(20 * 56.6929133858),
+ 'marginTop' => (int)round(20 * 56.6929133858),
+ 'marginBottom' => (int)round(20 * 56.6929133858),
+ ]);
+
+ $html = '
+
+
+ border-left:solid blue 2px; |
+ border-right:solid 2px red; |
+
+
+
+
+ |
+ |
+
+
';
+
+ $fullHTML = false;
+ $preserveWhiteSpace = true;
+
+ $options = [];
+ $options['IMG_SRC_SEARCH'] = 'path-to-img/name.jpg';
+ $options['IMG_SRC_REPLACE'] = 'another-path-to-img/new-name.jpg';
+
+ Html::addHtml($phpSectionInstance, $html, $fullHTML, $preserveWhiteSpace, $options);
+
+ $fqName = new PhpOffice\PhpWord\Writer\Word2007($phpWordInstance);
+ $fqName->save('./test.docx');
+```
+
+$html - $html param must have root node such as "html" if it is full html;
+
+$fullHTML - If $html is not full html, it may not have a root element. In this case $fullHTML should be false.
+
+$preserveWhiteSpace - Do not remove redundant white space. Default to true.
+
+$options - which could be applied when such an element occurs in the parseNode function.
\ No newline at end of file
diff --git a/src/PhpWord/Shared/Html.php b/src/PhpWord/Shared/Html.php
index 170dc5dff3..abe61a60df 100644
--- a/src/PhpWord/Shared/Html.php
+++ b/src/PhpWord/Shared/Html.php
@@ -895,9 +895,24 @@ 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"
+ $value = preg_replace('/ +/', ' ', trim($value));
+ $valueArr = explode(' ', strtolower($value));
+
+ $size = $color = $style = null;
// 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)) {
+ // we determine the order of color and style by checking value of the color.
+ $namedColors = self::mapNamedBorderColor();
+ foreach ($valueArr as $tmpValue) {
+ if (strpos($tmpValue, '#') === 0 || isset($namedColors[$tmpValue])) {
+ $color = trim($tmpValue, '#');
+ } elseif (preg_match('/[\-\d]+(px|pt|cm|mm|in|pc)/', $tmpValue) === 1) {
+ $size = Converter::cssToTwip($tmpValue);
+ } else {
+ $style = self::mapBorderStyle($tmpValue);
+ }
+ }
+
+ if ($size !== null && $color !== null && $style !== null) {
if (false !== strpos($property, '-')) {
$tmp = explode('-', $property);
$which = $tmp[1];
@@ -911,12 +926,11 @@ protected static function parseStyleDeclarations(array $selectors, array $styles
// 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]);
$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]);
+ $styles["border{$which}Color"] = $color;
+ $styles["border{$which}Style"] = $style;
}
break;
@@ -1065,12 +1079,169 @@ protected static function mapBorderStyle($cssBorderStyle)
case 'dashed':
case 'dotted':
case 'double':
+ case 'inset':
+ case 'outset':
return $cssBorderStyle;
default:
return 'single';
}
}
+ protected static function mapNamedBorderColor(): array
+ {
+ $colors = [];
+ $colors['aliceblue'] = '#f0f8ff';
+ $colors['antiquewhite'] = '#faebd7';
+ $colors['aqua'] = '#00ffff';
+ $colors['aquamarine'] = '#7fffd4';
+ $colors['azure'] = '#f0ffff';
+ $colors['beige'] = '#f5f5dc';
+ $colors['bisque'] = '#ffe4c4';
+ $colors['black'] = '#000000';
+ $colors['blanchedalmond'] = '#ffebcd';
+ $colors['blue'] = '#0000ff';
+ $colors['blueviolet'] = '#8a2be2';
+ $colors['brown'] = '#a52a2a';
+ $colors['burlywood'] = '#deb887';
+ $colors['cadetblue'] = '#5f9ea0';
+ $colors['chartreuse'] = '#7fff00';
+ $colors['chocolate'] = '#d2691e';
+ $colors['coral'] = '#ff7f50';
+ $colors['cornflowerblue'] = '#6495ed';
+ $colors['cornsilk'] = '#fff8dc';
+ $colors['crimson'] = '#dc143c';
+ $colors['cyan'] = '#00ffff';
+ $colors['darkblue'] = '#00008b';
+ $colors['darkcyan'] = '#008b8b';
+ $colors['darkgoldenrod'] = '#b8860b';
+ $colors['darkgray'] = '#a9a9a9';
+ $colors['darkgrey'] = '#a9a9a9';
+ $colors['darkgreen'] = '#006400';
+ $colors['darkkhaki'] = '#bdb76b';
+ $colors['darkmagenta'] = '#8b008b';
+ $colors['darkolivegreen'] = '#556b2f';
+ $colors['darkorange'] = '#ff8c00';
+ $colors['darkorchid'] = '#9932cc';
+ $colors['darkred'] = '#8b0000';
+ $colors['darksalmon'] = '#e9967a';
+ $colors['darkseagreen'] = '#8fbc8f';
+ $colors['darkslateblue'] = '#483d8b';
+ $colors['darkslategray'] = '#2f4f4f';
+ $colors['darkslategrey'] = '#2f4f4f';
+ $colors['darkturquoise'] = '#00ced1';
+ $colors['darkviolet'] = '#9400d3';
+ $colors['deeppink'] = '#ff1493';
+ $colors['deepskyblue'] = '#00bfff';
+ $colors['dimgray'] = '#696969';
+ $colors['dimgrey'] = '#696969';
+ $colors['dodgerblue'] = '#1e90ff';
+ $colors['firebrick'] = '#b22222';
+ $colors['floralwhite'] = '#fffaf0';
+ $colors['forestgreen'] = '#228b22';
+ $colors['fuchsia'] = '#ff00ff';
+ $colors['gainsboro'] = '#dcdcdc';
+ $colors['ghostwhite'] = '#f8f8ff';
+ $colors['gold'] = '#ffd700';
+ $colors['goldenrod'] = '#daa520';
+ $colors['gray'] = '#808080';
+ $colors['grey'] = '#808080';
+ $colors['green'] = '#008000';
+ $colors['greenyellow'] = '#adff2f';
+ $colors['honeydew'] = '#f0fff0';
+ $colors['hotpink'] = '#ff69b4';
+ $colors['indianred'] = '#cd5c5c';
+ $colors['indigo'] = '#4b0082';
+ $colors['ivory'] = '#fffff0';
+ $colors['khaki'] = '#f0e68c';
+ $colors['lavender'] = '#e6e6fa';
+ $colors['lavenderblush'] = '#fff0f5';
+ $colors['lawngreen'] = '#7cfc00';
+ $colors['lemonchiffon'] = '#fffacd';
+ $colors['lightblue'] = '#add8e6';
+ $colors['lightcoral'] = '#f08080';
+ $colors['lightcyan'] = '#e0ffff';
+ $colors['lightgoldenrodyellow'] = '#fafad2';
+ $colors['lightgray'] = '#d3d3d3';
+ $colors['lightgrey'] = '#d3d3d3';
+ $colors['lightgreen'] = '#90ee90';
+ $colors['lightpink'] = '#ffb6c1';
+ $colors['lightsalmon'] = '#ffa07a';
+ $colors['lightseagreen'] = '#20b2aa';
+ $colors['lightskyblue'] = '#87cefa';
+ $colors['lightslategray'] = '#778899';
+ $colors['lightslategrey'] = '#778899';
+ $colors['lightsteelblue'] = '#b0c4de';
+ $colors['lightyellow'] = '#ffffe0';
+ $colors['lime'] = '#00ff00';
+ $colors['limegreen'] = '#32cd32';
+ $colors['linen'] = '#faf0e6';
+ $colors['magenta'] = '#ff00ff';
+ $colors['maroon'] = '#800000';
+ $colors['mediumaquamarine'] = '#66cdaa';
+ $colors['mediumblue'] = '#0000cd';
+ $colors['mediumorchid'] = '#ba55d3';
+ $colors['mediumpurple'] = '#9370db';
+ $colors['mediumseagreen'] = '#3cb371';
+ $colors['mediumslateblue'] = '#7b68ee';
+ $colors['mediumspringgreen'] = '#00fa9a';
+ $colors['mediumturquoise'] = '#48d1cc';
+ $colors['mediumvioletred'] = '#c71585';
+ $colors['midnightblue'] = '#191970';
+ $colors['mintcream'] = '#f5fffa';
+ $colors['mistyrose'] = '#ffe4e1';
+ $colors['moccasin'] = '#ffe4b5';
+ $colors['navajowhite'] = '#ffdead';
+ $colors['navy'] = '#000080';
+ $colors['oldlace'] = '#fdf5e6';
+ $colors['olive'] = '#808000';
+ $colors['olivedrab'] = '#6b8e23';
+ $colors['orange'] = '#ffa500';
+ $colors['orangered'] = '#ff4500';
+ $colors['orchid'] = '#da70d6';
+ $colors['palegoldenrod'] = '#eee8aa';
+ $colors['palegreen'] = '#98fb98';
+ $colors['paleturquoise'] = '#afeeee';
+ $colors['palevioletred'] = '#db7093';
+ $colors['papayawhip'] = '#ffefd5';
+ $colors['peachpuff'] = '#ffdab9';
+ $colors['peru'] = '#cd853f';
+ $colors['pink'] = '#ffc0cb';
+ $colors['plum'] = '#dda0dd';
+ $colors['powderblue'] = '#b0e0e6';
+ $colors['purple'] = '#800080';
+ $colors['rebeccapurple'] = '#663399';
+ $colors['red'] = '#ff0000';
+ $colors['rosybrown'] = '#bc8f8f';
+ $colors['royalblue'] = '#4169e1';
+ $colors['saddlebrown'] = '#8b4513';
+ $colors['salmon'] = '#fa8072';
+ $colors['sandybrown'] = '#f4a460';
+ $colors['seagreen'] = '#2e8b57';
+ $colors['seashell'] = '#fff5ee';
+ $colors['sienna'] = '#a0522d';
+ $colors['silver'] = '#c0c0c0';
+ $colors['skyblue'] = '#87ceeb';
+ $colors['slateblue'] = '#6a5acd';
+ $colors['slategray'] = '#708090';
+ $colors['slategrey'] = '#708090';
+ $colors['snow'] = '#fffafa';
+ $colors['springgreen'] = '#00ff7f';
+ $colors['steelblue'] = '#4682b4';
+ $colors['tan'] = '#d2b48c';
+ $colors['teal'] = '#008080';
+ $colors['thistle'] = '#d8bfd8';
+ $colors['tomato'] = '#ff6347';
+ $colors['turquoise'] = '#40e0d0';
+ $colors['violet'] = '#ee82ee';
+ $colors['wheat'] = '#f5deb3';
+ $colors['white'] = '#ffffff';
+ $colors['whitesmoke'] = '#f5f5f5';
+ $colors['yellow'] = '#ffff00';
+ $colors['yellowgreen'] = '#9acd32';
+
+ return $colors;
+ }
+
protected static function mapBorderColor(&$styles, $cssBorderColor): void
{
$numColors = substr_count($cssBorderColor, '#');
diff --git a/src/PhpWord/Writer/Word2007/Style/Table.php b/src/PhpWord/Writer/Word2007/Style/Table.php
index 446fc3b1a4..e415b1efe4 100644
--- a/src/PhpWord/Writer/Word2007/Style/Table.php
+++ b/src/PhpWord/Writer/Word2007/Style/Table.php
@@ -140,6 +140,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/Shared/HtmlTest.php b/tests/PhpWordTests/Shared/HtmlTest.php
index 42d8aa598b..4014d8289c 100644
--- a/tests/PhpWordTests/Shared/HtmlTest.php
+++ b/tests/PhpWordTests/Shared/HtmlTest.php
@@ -1415,4 +1415,76 @@ public function testParseRubyHtml(): void
$doc->getElementAttribute('/w:document/w:body/w:p/w:r/w:ruby/w:rubyPr/w:lid', 'w:val')
);
}
+
+ public function testParseBorderOrderHtml(): void
+ {
+ $phpWord = new PhpWord();
+ $section = $phpWord->addSection();
+
+ $html = '
+
+
+ border:1px solid red; |
+ border:1px green dotted; |
+ border:solid 2px #b8860b; |
+ border-left:solid blue 1px; |
+
+
+
';
+ Html::addHtml($section, $html);
+ $doc = TestHelperDOCX::getDocument($phpWord, 'Word2007');
+
+ // [1]
+ $path = '/w:document/w:body/w:tbl/w:tr/w:tc[1]/w:tcPr/w:tcBorders/w:top';
+ self::assertTrue($doc->elementExists($path));
+ self::assertEquals('red', $doc->getElementAttribute($path, 'w:color'));
+ self::assertEquals(7, $doc->getElementAttribute($path, 'w:sz'));
+ self::assertEquals('single', $doc->getElementAttribute($path, 'w:val'));
+
+ $path = '/w:document/w:body/w:tbl/w:tr/w:tc[1]/w:tcPr/w:tcBorders/w:left';
+ self::assertTrue($doc->elementExists($path));
+ self::assertEquals('red', $doc->getElementAttribute($path, 'w:color'));
+ self::assertEquals(7, $doc->getElementAttribute($path, 'w:sz'));
+ self::assertEquals('single', $doc->getElementAttribute($path, 'w:val'));
+
+ $path = '/w:document/w:body/w:tbl/w:tr/w:tc[1]/w:tcPr/w:tcBorders/w:bottom';
+ self::assertTrue($doc->elementExists($path));
+ self::assertEquals('red', $doc->getElementAttribute($path, 'w:color'));
+ self::assertEquals(7, $doc->getElementAttribute($path, 'w:sz'));
+ self::assertEquals('single', $doc->getElementAttribute($path, 'w:val'));
+
+ $path = '/w:document/w:body/w:tbl/w:tr/w:tc[1]/w:tcPr/w:tcBorders/w:right';
+ self::assertTrue($doc->elementExists($path));
+ self::assertEquals('red', $doc->getElementAttribute($path, 'w:color'));
+ self::assertEquals(7, $doc->getElementAttribute($path, 'w:sz'));
+ self::assertEquals('single', $doc->getElementAttribute($path, 'w:val'));
+
+ // [2]
+ $path = '/w:document/w:body/w:tbl/w:tr/w:tc[2]/w:tcPr/w:tcBorders/w:top';
+ self::assertTrue($doc->elementExists($path));
+ self::assertEquals('green', $doc->getElementAttribute($path, 'w:color'));
+ self::assertEquals(7, $doc->getElementAttribute($path, 'w:sz'));
+ self::assertEquals('dotted', $doc->getElementAttribute($path, 'w:val'));
+
+ // [3]
+ $path = '/w:document/w:body/w:tbl/w:tr/w:tc[3]/w:tcPr/w:tcBorders/w:top';
+ self::assertTrue($doc->elementExists($path));
+ self::assertEquals('b8860b', $doc->getElementAttribute($path, 'w:color'));
+ self::assertEquals(15, $doc->getElementAttribute($path, 'w:sz'));
+ self::assertEquals('single', $doc->getElementAttribute($path, 'w:val'));
+
+ // [4]
+ $path = '/w:document/w:body/w:tbl/w:tr/w:tc[4]/w:tcPr/w:tcBorders/w:left';
+ self::assertTrue($doc->elementExists($path));
+ self::assertEquals('blue', $doc->getElementAttribute($path, 'w:color'));
+ self::assertEquals(7, $doc->getElementAttribute($path, 'w:sz'));
+ self::assertEquals('single', $doc->getElementAttribute($path, 'w:val'));
+
+ $path = '/w:document/w:body/w:tbl/w:tr/w:tc[4]/w:tcPr/w:tcBorders/w:right';
+ self::assertNotTrue($doc->elementExists($path));
+ $path = '/w:document/w:body/w:tbl/w:tr/w:tc[4]/w:tcPr/w:tcBorders/w:top';
+ self::assertNotTrue($doc->elementExists($path));
+ $path = '/w:document/w:body/w:tbl/w:tr/w:tc[4]/w:tcPr/w:tcBorders/w:bottom';
+ self::assertNotTrue($doc->elementExists($path));
+ }
}