add HTML table parsing

This commit is contained in:
troosan 2017-11-15 22:49:13 +01:00
parent c9350d3e80
commit 8eb72c976a
10 changed files with 452 additions and 61 deletions

View File

@ -14,6 +14,20 @@ $html .= '<ul><li>Item 1</li><li>Item 2</li><ul><li>Item 2.1</li><li>Item 2.1</l
$html .= '<p>Ordered (numbered) list:</p>'; $html .= '<p>Ordered (numbered) list:</p>';
$html .= '<ol><li>Item 1</li><li>Item 2</li></ol>'; $html .= '<ol><li>Item 1</li><li>Item 2</li></ol>';
$html .= '<table style="width: 50%; border: 6px #0000FF double;">
<thead>
<tr style="background-color: #FF0000; text-align: center; color: #FFFFFF; font-weight: bold; ">
<th>header a</th>
<th>header b</th>
<th style="background-color: #FFFF00; border-width: 12px"><span style="background-color: #00FF00;">header c</span></th>
</tr>
</thead>
<tbody>
<tr><td style="border-style: dotted;">1</td><td colspan="2">2</td></tr>
<tr><td>4</td><td>5</td><td>6</td></tr>
</tbody>
</table>';
\PhpOffice\PhpWord\Shared\Html::addHtml($section, $html); \PhpOffice\PhpWord\Shared\Html::addHtml($section, $html);
// Save file // Save file

View File

@ -296,25 +296,40 @@ class Converter
*/ */
public static function cssToPoint($value) public static function cssToPoint($value)
{ {
preg_match('/^[+-]?([0-9]+.?[0-9]+)?(px|em|ex|%|in|cm|mm|pt|pc)$/i', $value, $matches); if ($value == '0') {
$size = $matches[1]; return 0;
$unit = $matches[2];
switch ($unit) {
case 'pt':
return $size;
case 'px':
return self::pixelToPoint($size);
case 'cm':
return self::cmToPoint($size);
case 'mm':
return self::cmToPoint($size / 10);
case 'in':
return self::inchToPoint($size);
case 'pc':
return self::picaToPoint($size);
default:
return null;
} }
if (preg_match('/^[+-]?([0-9]+\.?[0-9]*)?(px|em|ex|%|in|cm|mm|pt|pc)$/i', $value, $matches)) {
$size = $matches[1];
$unit = $matches[2];
switch ($unit) {
case 'pt':
return $size;
case 'px':
return self::pixelToPoint($size);
case 'cm':
return self::cmToPoint($size);
case 'mm':
return self::cmToPoint($size / 10);
case 'in':
return self::inchToPoint($size);
case 'pc':
return self::picaToPoint($size);
}
}
return null;
}
/**
* Transforms a size in CSS format (eg. 10px, 10px, ...) to twips
*
* @param string $value
* @return float
*/
public static function cssToTwip($value)
{
return self::pointToTwip(self::cssToPoint($value));
} }
} }

View File

@ -18,6 +18,8 @@
namespace PhpOffice\PhpWord\Shared; namespace PhpOffice\PhpWord\Shared;
use PhpOffice\PhpWord\Element\AbstractContainer; use PhpOffice\PhpWord\Element\AbstractContainer;
use PhpOffice\PhpWord\Element\Row;
use PhpOffice\PhpWord\Element\Table;
use PhpOffice\PhpWord\SimpleType\Jc; use PhpOffice\PhpWord\SimpleType\Jc;
/** /**
@ -99,7 +101,7 @@ class Html
protected static function parseNode($node, $element, $styles = array(), $data = array()) protected static function parseNode($node, $element, $styles = array(), $data = array())
{ {
// Populate styles array // Populate styles array
$styleTypes = array('font', 'paragraph', 'list'); $styleTypes = array('font', 'paragraph', 'list', 'table', 'row', 'cell');
foreach ($styleTypes as $styleType) { foreach ($styleTypes as $styleType) {
if (!isset($styles[$styleType])) { if (!isset($styles[$styleType])) {
$styles[$styleType] = array(); $styles[$styleType] = array();
@ -124,10 +126,11 @@ class Html
'u' => array('Property', null, null, $styles, null, 'underline', 'single'), 'u' => array('Property', null, null, $styles, null, 'underline', 'single'),
'sup' => array('Property', null, null, $styles, null, 'superScript', true), 'sup' => array('Property', null, null, $styles, null, 'superScript', true),
'sub' => array('Property', null, null, $styles, null, 'subScript', true), 'sub' => array('Property', null, null, $styles, null, 'subScript', true),
'span' => array('Property', null, null, $styles, null, 'span', $node), 'span' => array('Span', $node, null, $styles, null, null, null),
'table' => array('Table', $node, $element, $styles, null, 'addTable', true), 'table' => array('Table', $node, $element, $styles, null, null, null),
'tr' => array('Table', $node, $element, $styles, null, 'addRow', true), 'tr' => array('Row', $node, $element, $styles, null, null, null),
'td' => array('Table', $node, $element, $styles, null, 'addCell', true), 'td' => array('Cell', $node, $element, $styles, null, null, null),
'th' => array('Cell', $node, $element, $styles, null, null, null),
'ul' => array('List', null, null, $styles, $data, 3, null), 'ul' => array('List', null, null, $styles, $data, 3, null),
'ol' => array('List', null, null, $styles, $data, 7, null), 'ol' => array('List', null, null, $styles, $data, 7, null),
'li' => array('ListItem', $node, $element, $styles, $data, null, null), 'li' => array('ListItem', $node, $element, $styles, $data, null, null),
@ -179,7 +182,7 @@ class Html
$cNodes = $node->childNodes; $cNodes = $node->childNodes;
if (count($cNodes) > 0) { if (count($cNodes) > 0) {
foreach ($cNodes as $cNode) { foreach ($cNodes as $cNode) {
if ($element instanceof AbstractContainer) { if ($element instanceof AbstractContainer || $element instanceof Table || $element instanceof Row) {
self::parseNode($cNode, $element, $styles, $data); self::parseNode($cNode, $element, $styles, $data);
} }
} }
@ -197,7 +200,7 @@ class Html
*/ */
private static function parseParagraph($node, $element, &$styles) private static function parseParagraph($node, $element, &$styles)
{ {
$styles['paragraph'] = self::parseInlineStyle($node, $styles['paragraph']); $styles['paragraph'] = self::recursiveParseStylesInHierarchy($node, $styles['paragraph']);
$newElement = $element->addTextRun($styles['paragraph']); $newElement = $element->addTextRun($styles['paragraph']);
return $newElement; return $newElement;
@ -231,7 +234,12 @@ class Html
*/ */
private static function parseText($node, $element, &$styles) private static function parseText($node, $element, &$styles)
{ {
$styles['font'] = self::parseInlineStyle($node, $styles['font']); $styles['font'] = self::recursiveParseStylesInHierarchy($node, $styles['font']);
//alignment applies on paragraph, not on font. Let's copy it there
if (isset($styles['font']['alignment'])) {
$styles['paragraph']['alignment'] = $styles['font']['alignment'];
}
if (is_callable(array($element, 'addText'))) { if (is_callable(array($element, 'addText'))) {
$element->addText($node->nodeValue, $styles['font'], $styles['paragraph']); $element->addText($node->nodeValue, $styles['font'], $styles['paragraph']);
@ -247,16 +255,18 @@ class Html
*/ */
private static function parseProperty(&$styles, $argument1, $argument2) private static function parseProperty(&$styles, $argument1, $argument2)
{ {
if ($argument1 !== 'span') { $styles['font'][$argument1] = $argument2;
$styles['font'][$argument1] = $argument2; }
} else {
if (!is_null($argument2->attributes)) { /**
$nodeAttr = $argument2->attributes->getNamedItem('style'); * Parse span node
if (!is_null($nodeAttr) && property_exists($nodeAttr, 'value')) { *
$styles['font'] = self::parseStyle($nodeAttr, $styles['font']); * @param \DOMNode $node
} * @param array &$styles
} */
} private static function parseSpan($node, &$styles)
{
self::parseInlineStyle($node, $styles['font']);
} }
/** /**
@ -270,11 +280,11 @@ class Html
* *
* @todo As soon as TableItem, RowItem and CellItem support relative width and height * @todo As soon as TableItem, RowItem and CellItem support relative width and height
*/ */
private static function parseTable($node, $element, &$styles, $argument1) private static function parseTable($node, $element, &$styles)
{ {
$styles['paragraph'] = self::parseInlineStyle($node, $styles['paragraph']); $elementStyles = self::parseInlineStyle($node, $styles['table']);
$newElement = $element->$argument1(); $newElement = $element->addTable($elementStyles);
// $attributes = $node->attributes; // $attributes = $node->attributes;
// if ($attributes->getNamedItem('width') !== null) { // if ($attributes->getNamedItem('width') !== null) {
@ -291,6 +301,62 @@ class Html
return $newElement; return $newElement;
} }
/**
* Parse a table row
*
* @param \DOMNode $node
* @param \PhpOffice\PhpWord\Element\Table $element
* @param array &$styles
* @return \PhpOffice\PhpWord\Element\AbstractContainer $element
*/
private static function parseRow($node, $element, &$styles)
{
$rowStyles = self::parseInlineStyle($node, $styles['row']);
if ($node->parentNode->nodeName == 'thead') {
$rowStyles['tblHeader'] = true;
}
return $element->addRow(null, $rowStyles);
}
/**
* Parse table cell
*
* @param \DOMNode $node
* @param \PhpOffice\PhpWord\Element\Table $element
* @param array &$styles
* @return \PhpOffice\PhpWord\Element\AbstractContainer $element
*/
private static function parseCell($node, $element, &$styles)
{
$cellStyles = self::recursiveParseStylesInHierarchy($node, $styles['cell']);
$colspan = $node->getAttribute('colspan');
if (!empty($colspan)) {
$cellStyles['gridSpan'] = $colspan - 0;
}
return $element->addCell(null, $cellStyles);
}
/**
* Recursively parses styles on parent nodes
* TODO if too slow, add caching of parent nodes, !! everything is static here so watch out for concurrency !!
*
* @param \DOMNode $node
* @param array &$styles
*/
private static function recursiveParseStylesInHierarchy(\DOMNode $node, array $style)
{
$parentStyle = self::parseInlineStyle($node, array());
$style = array_merge($parentStyle, $style);
if ($node->parentNode != null && XML_ELEMENT_NODE == $node->parentNode->nodeType) {
$style = self::recursiveParseStylesInHierarchy($node->parentNode, $style);
}
return $style;
}
/** /**
* Parse list node * Parse list node
* *
@ -400,9 +466,59 @@ class Html
} }
$styles['italic'] = $tValue; $styles['italic'] = $tValue;
break; break;
case 'border-color':
$styles['color'] = trim($cValue, '#');
break;
case 'border-width':
$styles['borderSize'] = Converter::cssToPoint($cValue);
break;
case 'border-style':
$styles['borderStyle'] = self::mapBorderStyle($cValue);
break;
case 'width':
if (preg_match('/([0-9]+[a-z]+)/', $cValue, $matches)) {
$styles['width'] = Converter::cssToTwip($matches[1]);
$styles['unit'] = \PhpOffice\PhpWord\Style\Table::WIDTH_TWIP;
} elseif (preg_match('/([0-9]+)%/', $cValue, $matches)) {
$styles['width'] = $matches[1] * 50;
$styles['unit'] = \PhpOffice\PhpWord\Style\Table::WIDTH_PERCENT;
} elseif (preg_match('/([0-9]+)/', $cValue, $matches)) {
$styles['width'] = $matches[1];
$styles['unit'] = \PhpOffice\PhpWord\Style\Table::WIDTH_AUTO;
}
break;
case 'border':
if (preg_match('/([0-9]+[^0-9]*)\s+(\#[a-fA-F0-9]+)\s+([a-z]+)/', $cValue, $matches)) {
$styles['borderSize'] = Converter::cssToPoint($matches[1]);
$styles['borderColor'] = trim($matches[2], '#');
$styles['borderStyle'] = self::mapBorderStyle($matches[3]);
}
break;
} }
} }
return $styles; return $styles;
} }
/**
* Transforms a CSS border style into a word border style
*
* @param string $cssBorderStyle
* @return null|string
*/
private static function mapBorderStyle($cssBorderStyle)
{
if ($cssBorderStyle == null) {
return null;
}
switch ($cssBorderStyle) {
case 'none':
case 'dashed':
case 'dotted':
case 'double':
return $cssBorderStyle;
case 'solid':
return 'single';
}
}
} }

View File

@ -36,6 +36,13 @@ class Border extends AbstractStyle
*/ */
protected $borderTopColor; protected $borderTopColor;
/**
* Border Top Style
*
* @var string
*/
protected $borderTopStyle;
/** /**
* Border Left Size * Border Left Size
* *
@ -50,6 +57,13 @@ class Border extends AbstractStyle
*/ */
protected $borderLeftColor; protected $borderLeftColor;
/**
* Border Left Style
*
* @var string
*/
protected $borderLeftStyle;
/** /**
* Border Right Size * Border Right Size
* *
@ -64,6 +78,13 @@ class Border extends AbstractStyle
*/ */
protected $borderRightColor; protected $borderRightColor;
/**
* Border Right Style
*
* @var string
*/
protected $borderRightStyle;
/** /**
* Border Bottom Size * Border Bottom Size
* *
@ -78,6 +99,13 @@ class Border extends AbstractStyle
*/ */
protected $borderBottomColor; protected $borderBottomColor;
/**
* Border Bottom Style
*
* @var string
*/
protected $borderBottomStyle;
/** /**
* Get border size * Get border size
* *
@ -140,6 +168,37 @@ class Border extends AbstractStyle
return $this; return $this;
} }
/**
* Get border style
*
* @return string[]
*/
public function getBorderStyle()
{
return array(
$this->getBorderTopStyle(),
$this->getBorderLeftStyle(),
$this->getBorderRightStyle(),
$this->getBorderBottomStyle(),
);
}
/**
* 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);
return $this;
}
/** /**
* Get border top size * Get border top size
* *
@ -186,6 +245,29 @@ class Border extends AbstractStyle
return $this; return $this;
} }
/**
* Get border top style
*
* @return string
*/
public function getBorderTopStyle()
{
return $this->borderTopStyle;
}
/**
* Set border top Style
*
* @param string $value
* @return self
*/
public function setBorderTopStyle($value = null)
{
$this->borderTopStyle = $value;
return $this;
}
/** /**
* Get border left size * Get border left size
* *
@ -232,6 +314,29 @@ class Border extends AbstractStyle
return $this; return $this;
} }
/**
* Get border left style
*
* @return string
*/
public function getBorderLeftStyle()
{
return $this->borderLeftStyle;
}
/**
* Set border left style
*
* @param string $value
* @return self
*/
public function setBorderLeftStyle($value = null)
{
$this->borderLeftStyle = $value;
return $this;
}
/** /**
* Get border right size * Get border right size
* *
@ -278,6 +383,29 @@ class Border extends AbstractStyle
return $this; return $this;
} }
/**
* Get border right style
*
* @return string
*/
public function getBorderRightStyle()
{
return $this->borderRightStyle;
}
/**
* Set border right style
*
* @param string $value
* @return self
*/
public function setBorderRightStyle($value = null)
{
$this->borderRightStyle = $value;
return $this;
}
/** /**
* Get border bottom size * Get border bottom size
* *
@ -324,6 +452,29 @@ class Border extends AbstractStyle
return $this; return $this;
} }
/**
* Get border bottom style
*
* @return string
*/
public function getBorderBottomStyle()
{
return $this->borderBottomStyle;
}
/**
* Set border bottom style
*
* @param string $value
* @return self
*/
public function setBorderBottomStyle($value = null)
{
$this->borderBottomStyle = $value;
return $this;
}
/** /**
* Check if any of the border is not null * Check if any of the border is not null
* *

View File

@ -32,13 +32,31 @@ class Cell extends Border
const VALIGN_BOTTOM = 'bottom'; const VALIGN_BOTTOM = 'bottom';
const VALIGN_BOTH = 'both'; const VALIGN_BOTH = 'both';
//Text direction constants
/** /**
* Text direction constants * Left to Right, Top to Bottom
* */
* @const string const TEXT_DIR_LRTB = 'lrTb';
/**
* Top to Bottom, Right to Left
*/
const TEXT_DIR_TBRL = 'tbRl';
/**
* Bottom to Top, Left to Right
*/ */
const TEXT_DIR_BTLR = 'btLr'; const TEXT_DIR_BTLR = 'btLr';
const TEXT_DIR_TBRL = 'tbRl'; /**
* Left to Right, Top to Bottom Rotated
*/
const TEXT_DIR_LRTBV = 'lrTbV';
/**
* Top to Bottom, Right to Left Rotated
*/
const TEXT_DIR_TBRLV = 'tbRlV';
/**
* Top to Bottom, Left to Right Rotated
*/
const TEXT_DIR_TBLRV = 'tbLrV';
/** /**
* Vertical merge (rowspan) constants * Vertical merge (rowspan) constants
@ -93,6 +111,20 @@ class Cell extends Border
*/ */
private $shading; private $shading;
/**
* Width
*
* @var int
*/
private $width;
/**
* Width type
*
* @var string
*/
private $widthType = Table::WIDTH_TWIP;
/** /**
* Get vertical align. * Get vertical align.
* *
@ -236,6 +268,51 @@ class Cell extends Border
return $this; return $this;
} }
/**
* Get cell width
*
* @return int
*/
public function getWidth()
{
return $this->width;
}
/**
* Set cell width
*
* @param int $value
* @return self
*/
public function setWidth($value)
{
$this->setIntVal($value);
return $this;
}
/**
* Get width type
*
* @return string
*/
public function getWidthType()
{
return $this->widthType;
}
/**
* Set width type
*
* @param string $value
*/
public function setWidthType($value)
{
$this->widthType = $this->setEnumVal($value, array(Table::WIDTH_AUTO, Table::WIDTH_PERCENT, Table::WIDTH_TWIP), Table::WIDTH_TWIP);
return $this;
}
/** /**
* Get default border color * Get default border color
* *

View File

@ -65,6 +65,7 @@ class Cell extends AbstractStyle
$styleWriter = new MarginBorder($xmlWriter); $styleWriter = new MarginBorder($xmlWriter);
$styleWriter->setSizes($style->getBorderSize()); $styleWriter->setSizes($style->getBorderSize());
$styleWriter->setColors($style->getBorderColor()); $styleWriter->setColors($style->getBorderColor());
$styleWriter->setStyles($style->getBorderStyle());
$styleWriter->setAttributes(array('defaultColor' => CellStyle::DEFAULT_BORDER_COLOR)); $styleWriter->setAttributes(array('defaultColor' => CellStyle::DEFAULT_BORDER_COLOR));
$styleWriter->write(); $styleWriter->write();

View File

@ -40,6 +40,13 @@ class MarginBorder extends AbstractStyle
*/ */
private $colors = array(); private $colors = array();
/**
* Border styles
*
* @var string[]
*/
private $styles = array();
/** /**
* Other attributes * Other attributes
* *
@ -62,7 +69,8 @@ class MarginBorder extends AbstractStyle
if (isset($this->colors[$i])) { if (isset($this->colors[$i])) {
$color = $this->colors[$i]; $color = $this->colors[$i];
} }
$this->writeSide($xmlWriter, $sides[$i], $this->sizes[$i], $color); $style = isset($this->styles[$i]) ? $this->styles[$i] : 'single';
$this->writeSide($xmlWriter, $sides[$i], $this->sizes[$i], $color, $style);
} }
} }
} }
@ -74,8 +82,9 @@ class MarginBorder extends AbstractStyle
* @param string $side * @param string $side
* @param int $width * @param int $width
* @param string $color * @param string $color
* @param string $borderStyle
*/ */
private function writeSide(XMLWriter $xmlWriter, $side, $width, $color = null) private function writeSide(XMLWriter $xmlWriter, $side, $width, $color = null, $borderStyle = 'solid')
{ {
$xmlWriter->startElement('w:' . $side); $xmlWriter->startElement('w:' . $side);
if (!empty($this->colors)) { if (!empty($this->colors)) {
@ -84,9 +93,9 @@ class MarginBorder extends AbstractStyle
$color = $this->attributes['defaultColor']; $color = $this->attributes['defaultColor'];
} }
} }
$xmlWriter->writeAttribute('w:val', 'single'); $xmlWriter->writeAttribute('w:val', $borderStyle);
$xmlWriter->writeAttribute('w:sz', $width); $xmlWriter->writeAttribute('w:sz', $width);
$xmlWriter->writeAttribute('w:color', $color); $xmlWriter->writeAttributeIf($color != null, 'w:color', $color);
if (!empty($this->attributes)) { if (!empty($this->attributes)) {
if (isset($this->attributes['space'])) { if (isset($this->attributes['space'])) {
$xmlWriter->writeAttribute('w:space', $this->attributes['space']); $xmlWriter->writeAttribute('w:space', $this->attributes['space']);
@ -119,6 +128,16 @@ class MarginBorder extends AbstractStyle
$this->colors = $value; $this->colors = $value;
} }
/**
* Set border styles.
*
* @param string[] $value
*/
public function setStyles($value)
{
$this->styles = $value;
}
/** /**
* Set attributes. * Set attributes.
* *

View File

@ -36,9 +36,9 @@ class Shading extends AbstractStyle
$xmlWriter = $this->getXmlWriter(); $xmlWriter = $this->getXmlWriter();
$xmlWriter->startElement('w:shd'); $xmlWriter->startElement('w:shd');
$xmlWriter->writeAttribute('w:val', $style->getPattern()); $xmlWriter->writeAttributeIf(!is_null($style->getPattern()), 'w:val', $style->getPattern());
$xmlWriter->writeAttribute('w:color', $style->getColor()); $xmlWriter->writeAttributeIf(!is_null($style->getColor()), 'w:color', $style->getColor());
$xmlWriter->writeAttribute('w:fill', $style->getFill()); $xmlWriter->writeAttributeIf(!is_null($style->getFill()), 'w:fill', $style->getFill());
$xmlWriter->endElement(); $xmlWriter->endElement();
} }
} }

View File

@ -187,25 +187,23 @@ class HtmlTest extends \PHPUnit\Framework\TestCase
{ {
$phpWord = new \PhpOffice\PhpWord\PhpWord(); $phpWord = new \PhpOffice\PhpWord\PhpWord();
$section = $phpWord->addSection(); $section = $phpWord->addSection();
$html = ' $html = '<table style="width: 50%; border: 6px #0000FF solid;">
<table style="width: 50%; border: 6px #0000FF solid;">
<thead> <thead>
<tr style="background-color: #FF0000; text-align: center; color: #FFFFFF; font-weight: bold; "> <tr style="background-color: #FF0000; text-align: center; color: #FFFFFF; font-weight: bold; ">
<th>a</th> <th>header a</th>
<th>b</th> <th>header b</th>
<th>c</th> <th style="border-color: #00FF00; border-width: 3px">header c</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr><td>1</td><td colspan="2">2</td></tr> <tr><td style="border-style: dotted;">1</td><td colspan="2">2</td></tr>
<tr><td>4</td><td>5</td><td>6</td></tr> <tr><td>4</td><td>5</td><td>6</td></tr>
</tbody> </tbody>
</table>'; </table>';
Html::addHtml($section, $html); Html::addHtml($section, $html);
$doc = TestHelperDOCX::getDocument($phpWord, 'Word2007'); $doc = TestHelperDOCX::getDocument($phpWord, 'Word2007');
// echo $doc->printXml(); $this->assertTrue($doc->elementExists('/w:document/w:body/w:tbl'));
$this->assertTrue($doc->elementExists('/w:document/w:body/w:p')); $this->assertTrue($doc->elementExists('/w:document/w:body/w:tbl/w:tr/w:tc'));
// $this->assertTrue($doc->elementExists('/w:document/w:body/w:p/w:tbl/w:tr/w:tc'));
} }
} }

View File

@ -45,7 +45,7 @@ class ElementTest extends \PHPUnit\Framework\TestCase
$elements = array( $elements = array(
'CheckBox', 'Container', 'Footnote', 'Image', 'Link', 'ListItem', 'ListItemRun', 'CheckBox', 'Container', 'Footnote', 'Image', 'Link', 'ListItem', 'ListItemRun',
'Object', 'PreserveText', 'Table', 'Text', 'TextBox', 'TextBreak', 'Title', 'TOC', 'Object', 'PreserveText', 'Table', 'Text', 'TextBox', 'TextBreak', 'Title', 'TOC',
'Field', 'Line', 'Shape', 'Chart', 'FormField', 'SDT', 'Field', 'Line', 'Shape', 'Chart', 'FormField', 'SDT', 'Bookmark',
); );
foreach ($elements as $element) { foreach ($elements as $element) {
$objectClass = 'PhpOffice\\PhpWord\\Writer\\Word2007\\Element\\' . $element; $objectClass = 'PhpOffice\\PhpWord\\Writer\\Word2007\\Element\\' . $element;