1
This commit is contained in:
@@ -21,22 +21,23 @@ namespace PhpOffice\PhpWord\Collection;
|
||||
* Collection abstract class.
|
||||
*
|
||||
* @since 0.10.0
|
||||
* @template T
|
||||
*/
|
||||
abstract class AbstractCollection
|
||||
{
|
||||
/**
|
||||
* Items.
|
||||
*
|
||||
* @var \PhpOffice\PhpWord\Element\AbstractContainer[]
|
||||
* @var T[]
|
||||
*/
|
||||
private $items = [];
|
||||
|
||||
/**
|
||||
* Get items.
|
||||
*
|
||||
* @return \PhpOffice\PhpWord\Element\AbstractContainer[]
|
||||
* @return T[]
|
||||
*/
|
||||
public function getItems()
|
||||
public function getItems(): array
|
||||
{
|
||||
return $this->items;
|
||||
}
|
||||
@@ -44,11 +45,9 @@ abstract class AbstractCollection
|
||||
/**
|
||||
* Get item by index.
|
||||
*
|
||||
* @param int $index
|
||||
*
|
||||
* @return ?\PhpOffice\PhpWord\Element\AbstractContainer
|
||||
* @return ?T
|
||||
*/
|
||||
public function getItem($index)
|
||||
public function getItem(int $index)
|
||||
{
|
||||
if (array_key_exists($index, $this->items)) {
|
||||
return $this->items[$index];
|
||||
@@ -60,10 +59,9 @@ abstract class AbstractCollection
|
||||
/**
|
||||
* Set item.
|
||||
*
|
||||
* @param int $index
|
||||
* @param ?\PhpOffice\PhpWord\Element\AbstractContainer $item
|
||||
* @param ?T $item
|
||||
*/
|
||||
public function setItem($index, $item): void
|
||||
public function setItem(int $index, $item): void
|
||||
{
|
||||
if (array_key_exists($index, $this->items)) {
|
||||
$this->items[$index] = $item;
|
||||
@@ -73,11 +71,9 @@ abstract class AbstractCollection
|
||||
/**
|
||||
* Add new item.
|
||||
*
|
||||
* @param \PhpOffice\PhpWord\Element\AbstractContainer $item
|
||||
*
|
||||
* @return int
|
||||
* @param T $item
|
||||
*/
|
||||
public function addItem($item)
|
||||
public function addItem($item): int
|
||||
{
|
||||
$index = $this->countItems();
|
||||
$this->items[$index] = $item;
|
||||
@@ -87,10 +83,8 @@ abstract class AbstractCollection
|
||||
|
||||
/**
|
||||
* Get item count.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function countItems()
|
||||
public function countItems(): int
|
||||
{
|
||||
return count($this->items);
|
||||
}
|
||||
|
||||
@@ -17,10 +17,13 @@
|
||||
|
||||
namespace PhpOffice\PhpWord\Collection;
|
||||
|
||||
use PhpOffice\PhpWord\Element\Bookmark;
|
||||
|
||||
/**
|
||||
* Bookmarks collection.
|
||||
*
|
||||
* @since 0.12.0
|
||||
* @extends AbstractCollection<Bookmark>
|
||||
*/
|
||||
class Bookmarks extends AbstractCollection
|
||||
{
|
||||
|
||||
@@ -17,10 +17,13 @@
|
||||
|
||||
namespace PhpOffice\PhpWord\Collection;
|
||||
|
||||
use PhpOffice\PhpWord\Element\Chart;
|
||||
|
||||
/**
|
||||
* Charts collection.
|
||||
*
|
||||
* @since 0.12.0
|
||||
* @extends AbstractCollection<Chart>
|
||||
*/
|
||||
class Charts extends AbstractCollection
|
||||
{
|
||||
|
||||
@@ -17,10 +17,13 @@
|
||||
|
||||
namespace PhpOffice\PhpWord\Collection;
|
||||
|
||||
use PhpOffice\PhpWord\Element\Comment;
|
||||
|
||||
/**
|
||||
* Comments collection.
|
||||
*
|
||||
* @since 0.12.0
|
||||
* @extends AbstractCollection<Comment>
|
||||
*/
|
||||
class Comments extends AbstractCollection
|
||||
{
|
||||
|
||||
@@ -17,10 +17,13 @@
|
||||
|
||||
namespace PhpOffice\PhpWord\Collection;
|
||||
|
||||
use PhpOffice\PhpWord\Element\Endnote;
|
||||
|
||||
/**
|
||||
* Endnotes collection.
|
||||
*
|
||||
* @since 0.10.0
|
||||
* @extends AbstractCollection<Endnote>
|
||||
*/
|
||||
class Endnotes extends AbstractCollection
|
||||
{
|
||||
|
||||
@@ -17,10 +17,13 @@
|
||||
|
||||
namespace PhpOffice\PhpWord\Collection;
|
||||
|
||||
use PhpOffice\PhpWord\Element\Footnote;
|
||||
|
||||
/**
|
||||
* Footnotes collection.
|
||||
*
|
||||
* @since 0.10.0
|
||||
* @extends AbstractCollection<Footnote>
|
||||
*/
|
||||
class Footnotes extends AbstractCollection
|
||||
{
|
||||
|
||||
@@ -17,10 +17,13 @@
|
||||
|
||||
namespace PhpOffice\PhpWord\Collection;
|
||||
|
||||
use PhpOffice\PhpWord\Element\Title;
|
||||
|
||||
/**
|
||||
* Titles collection.
|
||||
*
|
||||
* @since 0.10.0
|
||||
* @extends AbstractCollection<Title>
|
||||
*/
|
||||
class Titles extends AbstractCollection
|
||||
{
|
||||
|
||||
@@ -19,8 +19,10 @@ namespace PhpOffice\PhpWord\Element;
|
||||
|
||||
use DateTime;
|
||||
use InvalidArgumentException;
|
||||
use PhpOffice\PhpWord\Collection\Comments;
|
||||
use PhpOffice\PhpWord\Media;
|
||||
use PhpOffice\PhpWord\PhpWord;
|
||||
use PhpOffice\PhpWord\Style;
|
||||
|
||||
/**
|
||||
* Element abstract class.
|
||||
@@ -32,7 +34,7 @@ abstract class AbstractElement
|
||||
/**
|
||||
* PhpWord object.
|
||||
*
|
||||
* @var ?\PhpOffice\PhpWord\PhpWord
|
||||
* @var ?PhpWord
|
||||
*/
|
||||
protected $phpWord;
|
||||
|
||||
@@ -131,25 +133,25 @@ abstract class AbstractElement
|
||||
protected $collectionRelation = false;
|
||||
|
||||
/**
|
||||
* The start position for the linked comment.
|
||||
* The start position for the linked comments.
|
||||
*
|
||||
* @var Comment
|
||||
* @var Comments
|
||||
*/
|
||||
protected $commentRangeStart;
|
||||
protected $commentsRangeStart;
|
||||
|
||||
/**
|
||||
* The end position for the linked comment.
|
||||
* The end position for the linked comments.
|
||||
*
|
||||
* @var Comment
|
||||
* @var Comments
|
||||
*/
|
||||
protected $commentRangeEnd;
|
||||
protected $commentsRangeEnd;
|
||||
|
||||
/**
|
||||
* Get PhpWord.
|
||||
*
|
||||
* @return ?\PhpOffice\PhpWord\PhpWord
|
||||
* @return ?PhpWord
|
||||
*/
|
||||
public function getPhpWord()
|
||||
public function getPhpWord(): ?PhpWord
|
||||
{
|
||||
return $this->phpWord;
|
||||
}
|
||||
@@ -287,14 +289,28 @@ abstract class AbstractElement
|
||||
return $this->nestedLevel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get comments start.
|
||||
*
|
||||
* @return Comments
|
||||
*/
|
||||
public function getCommentsRangeStart(): ?Comments
|
||||
{
|
||||
return $this->commentsRangeStart;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get comment start.
|
||||
*
|
||||
* @return Comment
|
||||
*/
|
||||
public function getCommentRangeStart()
|
||||
public function getCommentRangeStart(): ?Comment
|
||||
{
|
||||
return $this->commentRangeStart;
|
||||
if ($this->commentsRangeStart != null) {
|
||||
return $this->commentsRangeStart->getItem($this->commentsRangeStart->countItems());
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -305,8 +321,30 @@ abstract class AbstractElement
|
||||
if ($this instanceof Comment) {
|
||||
throw new InvalidArgumentException('Cannot set a Comment on a Comment');
|
||||
}
|
||||
$this->commentRangeStart = $value;
|
||||
$this->commentRangeStart->setStartElement($this);
|
||||
if ($this->commentsRangeStart == null) {
|
||||
$this->commentsRangeStart = new Comments();
|
||||
}
|
||||
// Set ID early to avoid duplicates.
|
||||
if ($value->getElementId() == null) {
|
||||
$value->setElementId();
|
||||
}
|
||||
foreach ($this->commentsRangeStart->getItems() as $comment) {
|
||||
if ($value->getElementId() == $comment->getElementId()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
$idxItem = $this->commentsRangeStart->addItem($value);
|
||||
$this->commentsRangeStart->getItem($idxItem)->setStartElement($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get comments end.
|
||||
*
|
||||
* @return Comments
|
||||
*/
|
||||
public function getCommentsRangeEnd(): ?Comments
|
||||
{
|
||||
return $this->commentsRangeEnd;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -314,9 +352,13 @@ abstract class AbstractElement
|
||||
*
|
||||
* @return Comment
|
||||
*/
|
||||
public function getCommentRangeEnd()
|
||||
public function getCommentRangeEnd(): ?Comment
|
||||
{
|
||||
return $this->commentRangeEnd;
|
||||
if ($this->commentsRangeEnd != null) {
|
||||
return $this->commentsRangeEnd->getItem($this->commentsRangeEnd->countItems());
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -327,8 +369,20 @@ abstract class AbstractElement
|
||||
if ($this instanceof Comment) {
|
||||
throw new InvalidArgumentException('Cannot set a Comment on a Comment');
|
||||
}
|
||||
$this->commentRangeEnd = $value;
|
||||
$this->commentRangeEnd->setEndElement($this);
|
||||
if ($this->commentsRangeEnd == null) {
|
||||
$this->commentsRangeEnd = new Comments();
|
||||
}
|
||||
// Set ID early to avoid duplicates.
|
||||
if ($value->getElementId() == null) {
|
||||
$value->setElementId();
|
||||
}
|
||||
foreach ($this->commentsRangeEnd->getItems() as $comment) {
|
||||
if ($value->getElementId() == $comment->getElementId()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
$idxItem = $this->commentsRangeEnd->addItem($value);
|
||||
$this->commentsRangeEnd->getItem($idxItem)->setEndElement($this);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -428,7 +482,7 @@ abstract class AbstractElement
|
||||
* Set new style value.
|
||||
*
|
||||
* @param mixed $styleObject Style object
|
||||
* @param null|array|\PhpOffice\PhpWord\Style|string $styleValue Style value
|
||||
* @param null|array|string|Style $styleValue Style value
|
||||
* @param bool $returnObject Always return object
|
||||
*
|
||||
* @return mixed
|
||||
|
||||
@@ -83,9 +83,7 @@ class Comment extends TrackChange
|
||||
public function setStartElement(AbstractElement $value): void
|
||||
{
|
||||
$this->startElement = $value;
|
||||
if ($value->getCommentRangeStart() == null) {
|
||||
$value->setCommentRangeStart($this);
|
||||
}
|
||||
$value->setCommentRangeStart($this);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -104,9 +102,7 @@ class Comment extends TrackChange
|
||||
public function setEndElement(AbstractElement $value): void
|
||||
{
|
||||
$this->endElement = $value;
|
||||
if ($value->getCommentRangeEnd() == null) {
|
||||
$value->setCommentRangeEnd($this);
|
||||
}
|
||||
$value->setCommentRangeEnd($this);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -91,6 +91,10 @@ class Field extends AbstractElement
|
||||
],
|
||||
'options' => ['Path', 'PreserveFormat'],
|
||||
],
|
||||
'REF' => [
|
||||
'properties' => ['name' => ''],
|
||||
'options' => ['f', 'h', 'n', 'p', 'r', 't', 'w'],
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
|
||||
@@ -386,8 +386,9 @@ class Image extends AbstractElement
|
||||
$imageBinary = $this->source;
|
||||
} else {
|
||||
$fileHandle = fopen($actualSource, 'rb', false);
|
||||
if ($fileHandle !== false) {
|
||||
$imageBinary = fread($fileHandle, filesize($actualSource));
|
||||
$fileSize = filesize($actualSource);
|
||||
if ($fileHandle !== false && $fileSize > 0) {
|
||||
$imageBinary = fread($fileHandle, $fileSize);
|
||||
fclose($fileHandle);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,14 +32,14 @@ class TextRun extends AbstractContainer
|
||||
/**
|
||||
* Paragraph style.
|
||||
*
|
||||
* @var \PhpOffice\PhpWord\Style\Paragraph|string
|
||||
* @var Paragraph|string
|
||||
*/
|
||||
protected $paragraphStyle;
|
||||
|
||||
/**
|
||||
* Create new instance.
|
||||
*
|
||||
* @param array|\PhpOffice\PhpWord\Style\Paragraph|string $paragraphStyle
|
||||
* @param array|Paragraph|string $paragraphStyle
|
||||
*/
|
||||
public function __construct($paragraphStyle = null)
|
||||
{
|
||||
@@ -49,7 +49,7 @@ class TextRun extends AbstractContainer
|
||||
/**
|
||||
* Get Paragraph style.
|
||||
*
|
||||
* @return \PhpOffice\PhpWord\Style\Paragraph|string
|
||||
* @return Paragraph|string
|
||||
*/
|
||||
public function getParagraphStyle()
|
||||
{
|
||||
@@ -59,9 +59,9 @@ class TextRun extends AbstractContainer
|
||||
/**
|
||||
* Set Paragraph style.
|
||||
*
|
||||
* @param array|\PhpOffice\PhpWord\Style\Paragraph|string $style
|
||||
* @param array|Paragraph|string $style
|
||||
*
|
||||
* @return \PhpOffice\PhpWord\Style\Paragraph|string
|
||||
* @return Paragraph|string
|
||||
*/
|
||||
public function setParagraphStyle($style = null)
|
||||
{
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
|
||||
namespace PhpOffice\PhpWord;
|
||||
|
||||
use PhpOffice\PhpWord\Element\Text;
|
||||
use PhpOffice\PhpWord\Element\TextRun;
|
||||
use PhpOffice\PhpWord\Exception\Exception;
|
||||
use PhpOffice\PhpWord\Reader\ReaderInterface;
|
||||
use PhpOffice\PhpWord\Writer\WriterInterface;
|
||||
@@ -89,6 +91,43 @@ abstract class IOFactory
|
||||
return $reader->load($filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads PhpWord ${variable} from file.
|
||||
*
|
||||
* @param string $filename The name of the file
|
||||
*
|
||||
* @return array The extracted variables
|
||||
*/
|
||||
public static function extractVariables(string $filename, string $readerName = 'Word2007'): array
|
||||
{
|
||||
/** @var \PhpOffice\PhpWord\Reader\ReaderInterface $reader */
|
||||
$reader = self::createReader($readerName);
|
||||
$document = $reader->load($filename);
|
||||
$extractedVariables = [];
|
||||
foreach ($document->getSections() as $section) {
|
||||
$concatenatedText = '';
|
||||
foreach ($section->getElements() as $element) {
|
||||
if ($element instanceof TextRun) {
|
||||
foreach ($element->getElements() as $textElement) {
|
||||
if ($textElement instanceof Text) {
|
||||
$text = $textElement->getText();
|
||||
$concatenatedText .= $text;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
preg_match_all('/\$\{([^}]+)\}/', $concatenatedText, $matches);
|
||||
if (!empty($matches[1])) {
|
||||
foreach ($matches[1] as $match) {
|
||||
$trimmedMatch = trim($match);
|
||||
$extractedVariables[] = $trimmedMatch;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $extractedVariables;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if it's a concrete class (not abstract nor interface).
|
||||
*
|
||||
|
||||
@@ -134,7 +134,6 @@ class PhpWord
|
||||
if (in_array($function, $addCollection)) {
|
||||
$key = ucfirst(str_replace('add', '', $function) . 's');
|
||||
|
||||
/** @var \PhpOffice\PhpWord\Collection\AbstractCollection $collectionObject */
|
||||
$collectionObject = $this->collections[$key];
|
||||
|
||||
return $collectionObject->addItem($args[0] ?? null);
|
||||
|
||||
@@ -1279,10 +1279,12 @@ class MsDoc extends AbstractReader implements ReaderInterface
|
||||
break;
|
||||
}
|
||||
$strLen = $arrayRGFC[$key + 1] - $arrayRGFC[$key] - 1;
|
||||
for ($inc = 0; $inc < $strLen; ++$inc) {
|
||||
$byte = self::getInt1d($this->dataWorkDocument, $arrayRGFC[$key] + $inc);
|
||||
for ($inc = 0; $inc < ($strLen * 2); ++$inc) {
|
||||
$byte = self::getInt2d($this->dataWorkDocument, $arrayRGFC[$key] + ($inc * 2));
|
||||
if ($byte > 0) {
|
||||
$string .= chr($byte);
|
||||
$string .= mb_chr($byte, 'UTF-8');
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1871,7 +1873,7 @@ class MsDoc extends AbstractReader implements ReaderInterface
|
||||
break;
|
||||
// sprmCHps
|
||||
case 0x43:
|
||||
$oStylePrl->styleFont['size'] = dechex($operand / 2);
|
||||
$oStylePrl->styleFont['size'] = $operand / 2;
|
||||
|
||||
break;
|
||||
// sprmCIss
|
||||
@@ -2331,7 +2333,7 @@ class MsDoc extends AbstractReader implements ReaderInterface
|
||||
foreach ($this->arrayParagraphs as $itmParagraph) {
|
||||
$textPara = $itmParagraph;
|
||||
foreach ($this->arrayCharacters as $oCharacters) {
|
||||
$subText = substr($textPara, $oCharacters->pos_start, $oCharacters->pos_len);
|
||||
$subText = mb_substr($textPara, $oCharacters->pos_start, $oCharacters->pos_len);
|
||||
$subText = str_replace(chr(13), PHP_EOL, $subText);
|
||||
$arrayText = explode(PHP_EOL, $subText);
|
||||
if (end($arrayText) == '') {
|
||||
|
||||
@@ -24,6 +24,7 @@ use PhpOffice\Math\Reader\OfficeMathML;
|
||||
use PhpOffice\PhpWord\ComplexType\TblWidth as TblWidthComplexType;
|
||||
use PhpOffice\PhpWord\Element\AbstractContainer;
|
||||
use PhpOffice\PhpWord\Element\AbstractElement;
|
||||
use PhpOffice\PhpWord\Element\FormField;
|
||||
use PhpOffice\PhpWord\Element\TextRun;
|
||||
use PhpOffice\PhpWord\Element\TrackChange;
|
||||
use PhpOffice\PhpWord\PhpWord;
|
||||
@@ -192,25 +193,64 @@ abstract class AbstractPart
|
||||
// Paragraph style
|
||||
$paragraphStyle = $xmlReader->elementExists('w:pPr', $domNode) ? $this->readParagraphStyle($xmlReader, $domNode) : null;
|
||||
|
||||
// PreserveText
|
||||
if ($xmlReader->elementExists('w:r/w:instrText', $domNode)) {
|
||||
if ($xmlReader->elementExists('w:r/w:fldChar/w:ffData', $domNode)) {
|
||||
// FormField
|
||||
$partOfFormField = false;
|
||||
$formNodes = [];
|
||||
$formType = null;
|
||||
$textRunContainers = $xmlReader->countElements('w:r|w:ins|w:del|w:hyperlink|w:smartTag', $domNode);
|
||||
if ($textRunContainers > 0) {
|
||||
$nodes = $xmlReader->getElements('*', $domNode);
|
||||
$paragraph = $parent->addTextRun($paragraphStyle);
|
||||
foreach ($nodes as $node) {
|
||||
if ($xmlReader->elementExists('w:fldChar/w:ffData', $node)) {
|
||||
$partOfFormField = true;
|
||||
$formNodes[] = $node;
|
||||
if ($xmlReader->elementExists('w:fldChar/w:ffData/w:ddList', $node)) {
|
||||
$formType = 'dropdown';
|
||||
} elseif ($xmlReader->elementExists('w:fldChar/w:ffData/w:textInput', $node)) {
|
||||
$formType = 'textinput';
|
||||
} elseif ($xmlReader->elementExists('w:fldChar/w:ffData/w:checkBox', $node)) {
|
||||
$formType = 'checkbox';
|
||||
}
|
||||
} elseif ($partOfFormField &&
|
||||
$xmlReader->elementExists('w:fldChar', $node) &&
|
||||
'end' == $xmlReader->getAttribute('w:fldCharType', $node, 'w:fldChar')
|
||||
) {
|
||||
$formNodes[] = $node;
|
||||
$partOfFormField = false;
|
||||
// Process the form fields
|
||||
$this->readFormField($xmlReader, $formNodes, $paragraph, $paragraphStyle, $formType);
|
||||
} elseif ($partOfFormField) {
|
||||
$formNodes[] = $node;
|
||||
} else {
|
||||
// normal runs
|
||||
$this->readRun($xmlReader, $node, $paragraph, $docPart, $paragraphStyle);
|
||||
}
|
||||
}
|
||||
}
|
||||
} elseif ($xmlReader->elementExists('w:r/w:instrText', $domNode)) {
|
||||
// PreserveText
|
||||
$ignoreText = false;
|
||||
$textContent = '';
|
||||
$fontStyle = $this->readFontStyle($xmlReader, $domNode);
|
||||
$nodes = $xmlReader->getElements('w:r', $domNode);
|
||||
foreach ($nodes as $node) {
|
||||
$instrText = $xmlReader->getValue('w:instrText', $node);
|
||||
if ($xmlReader->elementExists('w:fldChar', $node)) {
|
||||
$fldCharType = $xmlReader->getAttribute('w:fldCharType', $node, 'w:fldChar');
|
||||
if ('begin' == $fldCharType) {
|
||||
$ignoreText = true;
|
||||
} elseif ('end' == $fldCharType) {
|
||||
$ignoreText = false;
|
||||
}
|
||||
if ($xmlReader->elementExists('w:lastRenderedPageBreak', $node)) {
|
||||
$parent->addPageBreak();
|
||||
}
|
||||
$instrText = $xmlReader->getValue('w:instrText', $node);
|
||||
if (null !== $instrText) {
|
||||
$textContent .= '{' . $instrText . '}';
|
||||
} else {
|
||||
if ($xmlReader->elementExists('w:fldChar', $node)) {
|
||||
$fldCharType = $xmlReader->getAttribute('w:fldCharType', $node, 'w:fldChar');
|
||||
if ('begin' == $fldCharType) {
|
||||
$ignoreText = true;
|
||||
} elseif ('end' == $fldCharType) {
|
||||
$ignoreText = false;
|
||||
}
|
||||
}
|
||||
if (false === $ignoreText) {
|
||||
$textContent .= $xmlReader->getValue('w:t', $node);
|
||||
}
|
||||
@@ -272,7 +312,7 @@ abstract class AbstractPart
|
||||
// Text and TextRun
|
||||
$textRunContainers = $xmlReader->countElements('w:r|w:ins|w:del|w:hyperlink|w:smartTag|w:commentReference|w:commentRangeStart|w:commentRangeEnd', $domNode);
|
||||
if (0 === $textRunContainers) {
|
||||
$parent->addTextBreak(null, $paragraphStyle);
|
||||
$parent->addTextBreak(1, $paragraphStyle);
|
||||
} else {
|
||||
$nodes = $xmlReader->getElements('*', $domNode);
|
||||
$paragraph = $parent->addTextRun($paragraphStyle);
|
||||
@@ -282,6 +322,109 @@ abstract class AbstractPart
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param DOMElement[] $domNodes
|
||||
* @param AbstractContainer $parent
|
||||
* @param mixed $paragraphStyle
|
||||
* @param string $formType
|
||||
*/
|
||||
private function readFormField(XMLReader $xmlReader, array $domNodes, $parent, $paragraphStyle, $formType): void
|
||||
{
|
||||
if (!in_array($formType, ['textinput', 'checkbox', 'dropdown'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$formField = $parent->addFormField($formType, null, $paragraphStyle);
|
||||
$ffData = $xmlReader->getElement('w:fldChar/w:ffData', $domNodes[0]);
|
||||
|
||||
foreach ($xmlReader->getElements('*', $ffData) as $node) {
|
||||
/** @var DOMElement $node */
|
||||
switch ($node->localName) {
|
||||
case 'name':
|
||||
$formField->setName($node->getAttribute('w:val'));
|
||||
|
||||
break;
|
||||
case 'ddList':
|
||||
$listEntries = [];
|
||||
foreach ($xmlReader->getElements('*', $node) as $ddListNode) {
|
||||
switch ($ddListNode->localName) {
|
||||
case 'result':
|
||||
$formField->setValue($xmlReader->getAttribute('w:val', $ddListNode));
|
||||
|
||||
break;
|
||||
case 'default':
|
||||
$formField->setDefault($xmlReader->getAttribute('w:val', $ddListNode));
|
||||
|
||||
break;
|
||||
case 'listEntry':
|
||||
$listEntries[] = $xmlReader->getAttribute('w:val', $ddListNode);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
$formField->setEntries($listEntries);
|
||||
if (null !== $formField->getValue()) {
|
||||
$formField->setText($listEntries[$formField->getValue()]);
|
||||
}
|
||||
|
||||
break;
|
||||
case 'textInput':
|
||||
foreach ($xmlReader->getElements('*', $node) as $ddListNode) {
|
||||
switch ($ddListNode->localName) {
|
||||
case 'default':
|
||||
$formField->setDefault($xmlReader->getAttribute('w:val', $ddListNode));
|
||||
|
||||
break;
|
||||
case 'format':
|
||||
case 'maxLength':
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
case 'checkBox':
|
||||
foreach ($xmlReader->getElements('*', $node) as $ddListNode) {
|
||||
switch ($ddListNode->localName) {
|
||||
case 'default':
|
||||
$formField->setDefault($xmlReader->getAttribute('w:val', $ddListNode));
|
||||
|
||||
break;
|
||||
case 'checked':
|
||||
$formField->setValue($xmlReader->getAttribute('w:val', $ddListNode));
|
||||
|
||||
break;
|
||||
case 'size':
|
||||
case 'sizeAuto':
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ('textinput' == $formType) {
|
||||
$ignoreText = true;
|
||||
$textContent = '';
|
||||
foreach ($domNodes as $node) {
|
||||
if ($xmlReader->elementExists('w:fldChar', $node)) {
|
||||
$fldCharType = $xmlReader->getAttribute('w:fldCharType', $node, 'w:fldChar');
|
||||
if ('separate' == $fldCharType) {
|
||||
$ignoreText = false;
|
||||
} elseif ('end' == $fldCharType) {
|
||||
$ignoreText = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (false === $ignoreText) {
|
||||
$textContent .= $xmlReader->getValue('w:t', $node);
|
||||
}
|
||||
}
|
||||
$formField->setValue(htmlspecialchars($textContent, ENT_QUOTES, 'UTF-8'));
|
||||
$formField->setText(htmlspecialchars($textContent, ENT_QUOTES, 'UTF-8'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the depth of the Heading, returns 0 for a Title.
|
||||
*
|
||||
@@ -529,6 +672,18 @@ abstract class AbstractPart
|
||||
'contextualSpacing' => [self::READ_TRUE, 'w:contextualSpacing'],
|
||||
'bidi' => [self::READ_TRUE, 'w:bidi'],
|
||||
'suppressAutoHyphens' => [self::READ_TRUE, 'w:suppressAutoHyphens'],
|
||||
'borderTopStyle' => [self::READ_VALUE, 'w:pBdr/w:top'],
|
||||
'borderTopColor' => [self::READ_VALUE, 'w:pBdr/w:top', 'w:color'],
|
||||
'borderTopSize' => [self::READ_VALUE, 'w:pBdr/w:top', 'w:sz'],
|
||||
'borderRightStyle' => [self::READ_VALUE, 'w:pBdr/w:right'],
|
||||
'borderRightColor' => [self::READ_VALUE, 'w:pBdr/w:right', 'w:color'],
|
||||
'borderRightSize' => [self::READ_VALUE, 'w:pBdr/w:right', 'w:sz'],
|
||||
'borderBottomStyle' => [self::READ_VALUE, 'w:pBdr/w:bottom'],
|
||||
'borderBottomColor' => [self::READ_VALUE, 'w:pBdr/w:bottom', 'w:color'],
|
||||
'borderBottomSize' => [self::READ_VALUE, 'w:pBdr/w:bottom', 'w:sz'],
|
||||
'borderLeftStyle' => [self::READ_VALUE, 'w:pBdr/w:left'],
|
||||
'borderLeftColor' => [self::READ_VALUE, 'w:pBdr/w:left', 'w:color'],
|
||||
'borderLeftSize' => [self::READ_VALUE, 'w:pBdr/w:left', 'w:sz'],
|
||||
];
|
||||
|
||||
return $this->readStyleDefs($xmlReader, $styleNode, $styleDefs);
|
||||
|
||||
@@ -138,8 +138,6 @@ class Document extends AbstractPart
|
||||
|
||||
/**
|
||||
* Read w:p node.
|
||||
*
|
||||
* @todo <w:lastRenderedPageBreak>
|
||||
*/
|
||||
private function readWPNode(XMLReader $xmlReader, DOMElement $node, Section &$section): void
|
||||
{
|
||||
|
||||
@@ -37,6 +37,8 @@ use PhpOffice\PhpWord\Style\Paragraph;
|
||||
*/
|
||||
class Html
|
||||
{
|
||||
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;
|
||||
|
||||
protected static $xpath;
|
||||
@@ -100,7 +102,7 @@ class Html
|
||||
* parse Inline style of a node.
|
||||
*
|
||||
* @param DOMNode $node Node to check on attributes and to compile a style array
|
||||
* @param array $styles is supplied, the inline style attributes are added to the already existing style
|
||||
* @param array<string, mixed> $styles is supplied, the inline style attributes are added to the already existing style
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
@@ -109,7 +111,9 @@ class Html
|
||||
if (XML_ELEMENT_NODE == $node->nodeType) {
|
||||
$attributes = $node->attributes; // get all the attributes(eg: id, class)
|
||||
|
||||
$bidi = ($attributes['dir'] ?? '') === 'rtl';
|
||||
$attributeDir = $attributes->getNamedItem('dir');
|
||||
$attributeDirValue = $attributeDir ? $attributeDir->nodeValue : '';
|
||||
$bidi = $attributeDirValue === 'rtl';
|
||||
foreach ($attributes as $attribute) {
|
||||
$val = $attribute->value;
|
||||
switch (strtolower($attribute->name)) {
|
||||
@@ -142,7 +146,7 @@ class Html
|
||||
break;
|
||||
case 'bgcolor':
|
||||
// tables, rows, cells e.g. <tr bgColor="#FF0000">
|
||||
$styles['bgColor'] = trim($val, '# ');
|
||||
$styles['bgColor'] = self::convertRgb($val);
|
||||
|
||||
break;
|
||||
case 'valign':
|
||||
@@ -157,15 +161,15 @@ class Html
|
||||
|
||||
$attributeIdentifier = $attributes->getNamedItem('id');
|
||||
if ($attributeIdentifier && self::$css) {
|
||||
$styles = self::parseStyleDeclarations(self::$css->getStyle('#' . $attributeIdentifier->value), $styles);
|
||||
$styles = self::parseStyleDeclarations(self::$css->getStyle('#' . $attributeIdentifier->nodeValue), $styles);
|
||||
}
|
||||
|
||||
$attributeClass = $attributes->getNamedItem('class');
|
||||
if ($attributeClass) {
|
||||
if (self::$css) {
|
||||
$styles = self::parseStyleDeclarations(self::$css->getStyle('.' . $attributeClass->value), $styles);
|
||||
$styles = self::parseStyleDeclarations(self::$css->getStyle('.' . $attributeClass->nodeValue), $styles);
|
||||
}
|
||||
$styles['className'] = $attributeClass->value;
|
||||
$styles['className'] = $attributeClass->nodeValue;
|
||||
}
|
||||
|
||||
$attributeStyle = $attributes->getNamedItem('style');
|
||||
@@ -323,10 +327,10 @@ class Html
|
||||
return;
|
||||
}
|
||||
|
||||
$inputType = $attributes->getNamedItem('type')->value;
|
||||
$inputType = $attributes->getNamedItem('type')->nodeValue;
|
||||
switch ($inputType) {
|
||||
case 'checkbox':
|
||||
$checked = ($checked = $attributes->getNamedItem('checked')) && $checked->value === 'true' ? true : false;
|
||||
$checked = ($checked = $attributes->getNamedItem('checked')) && $checked->nodeValue === 'true' ? true : false;
|
||||
$textrun = $element->addTextRun($styles['paragraph']);
|
||||
$textrun->addFormField('checkbox')->setValue($checked);
|
||||
|
||||
@@ -421,8 +425,8 @@ class Html
|
||||
}
|
||||
|
||||
$attributes = $node->attributes;
|
||||
if ($attributes->getNamedItem('border') !== null) {
|
||||
$border = (int) $attributes->getNamedItem('border')->value;
|
||||
if ($attributes->getNamedItem('border')) {
|
||||
$border = (int) $attributes->getNamedItem('border')->nodeValue;
|
||||
$newElement->getStyle()->setBorderSize(Converter::pixelToTwip($border));
|
||||
}
|
||||
|
||||
@@ -720,11 +724,11 @@ class Html
|
||||
|
||||
break;
|
||||
case 'color':
|
||||
$styles['color'] = trim($value, '#');
|
||||
$styles['color'] = self::convertRgb($value);
|
||||
|
||||
break;
|
||||
case 'background-color':
|
||||
$styles['bgColor'] = trim($value, '#');
|
||||
$styles['bgColor'] = self::convertRgb($value);
|
||||
|
||||
break;
|
||||
case 'line-height':
|
||||
@@ -898,12 +902,34 @@ class Html
|
||||
break;
|
||||
case 'width':
|
||||
$width = $attribute->value;
|
||||
|
||||
// pt
|
||||
if (false !== strpos($width, 'pt')) {
|
||||
$width = Converter::pointToPixel((float) str_replace('pt', '', $width));
|
||||
}
|
||||
|
||||
// px
|
||||
if (false !== strpos($width, 'px')) {
|
||||
$width = str_replace('px', '', $width);
|
||||
}
|
||||
|
||||
$style['width'] = $width;
|
||||
$style['unit'] = \PhpOffice\PhpWord\Style\Image::UNIT_PX;
|
||||
|
||||
break;
|
||||
case 'height':
|
||||
$height = $attribute->value;
|
||||
|
||||
// pt
|
||||
if (false !== strpos($height, 'pt')) {
|
||||
$height = Converter::pointToPixel((float) str_replace('pt', '', $height));
|
||||
}
|
||||
|
||||
// px
|
||||
if (false !== strpos($height, 'px')) {
|
||||
$height = str_replace('px', '', $height);
|
||||
}
|
||||
|
||||
$style['height'] = $height;
|
||||
$style['unit'] = \PhpOffice\PhpWord\Style\Image::UNIT_PX;
|
||||
|
||||
@@ -1130,7 +1156,11 @@ class Html
|
||||
}
|
||||
$styles['font'] = self::parseInlineStyle($node, $styles['font']);
|
||||
|
||||
if (strpos($target, '#') === 0) {
|
||||
if (empty($target)) {
|
||||
$target = '#';
|
||||
}
|
||||
|
||||
if (strpos($target, '#') === 0 && strlen($target) > 1) {
|
||||
return $element->addLink(substr($target, 1), $node->textContent, $styles['font'], $styles['paragraph'], true);
|
||||
}
|
||||
|
||||
@@ -1170,4 +1200,13 @@ class Html
|
||||
// - line - that is a shape, has different behaviour
|
||||
// - repeated text, e.g. underline "_", because of unpredictable line wrapping
|
||||
}
|
||||
|
||||
private static function convertRgb(string $rgb): string
|
||||
{
|
||||
if (preg_match(self::RGB_REGEXP, $rgb, $matches) === 1) {
|
||||
return sprintf('%02X%02X%02X', $matches[1], $matches[2], $matches[3]);
|
||||
}
|
||||
|
||||
return trim($rgb, '# ');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,9 @@ class PasswordEncoder
|
||||
const ALGORITHM_MAC = 'MAC';
|
||||
const ALGORITHM_HMAC = 'HMAC';
|
||||
|
||||
private const ALL_ONE_BITS = (PHP_INT_SIZE > 4) ? 0xFFFFFFFF : -1;
|
||||
private const HIGH_ORDER_BIT = (PHP_INT_SIZE > 4) ? 0x80000000 : PHP_INT_MIN;
|
||||
|
||||
/**
|
||||
* Mapping between algorithm name and algorithm ID.
|
||||
*
|
||||
@@ -128,7 +131,7 @@ class PasswordEncoder
|
||||
// build low-order word and hig-order word and combine them
|
||||
$combinedKey = self::buildCombinedKey($byteChars);
|
||||
// build reversed hexadecimal string
|
||||
$hex = str_pad(strtoupper(dechex($combinedKey & 0xFFFFFFFF)), 8, '0', \STR_PAD_LEFT);
|
||||
$hex = str_pad(strtoupper(dechex($combinedKey & self::ALL_ONE_BITS)), 8, '0', \STR_PAD_LEFT);
|
||||
$reversedHex = $hex[6] . $hex[7] . $hex[4] . $hex[5] . $hex[2] . $hex[3] . $hex[0] . $hex[1];
|
||||
|
||||
$generatedKey = mb_convert_encoding($reversedHex, 'UCS-2LE', 'UTF-8');
|
||||
@@ -232,10 +235,10 @@ class PasswordEncoder
|
||||
*/
|
||||
private static function int32($value)
|
||||
{
|
||||
$value = ($value & 0xFFFFFFFF);
|
||||
$value = $value & self::ALL_ONE_BITS;
|
||||
|
||||
if ($value & 0x80000000) {
|
||||
$value = -((~$value & 0xFFFFFFFF) + 1);
|
||||
if ($value & self::HIGH_ORDER_BIT) {
|
||||
$value = -((~$value & self::ALL_ONE_BITS) + 1);
|
||||
}
|
||||
|
||||
return $value;
|
||||
|
||||
@@ -61,7 +61,15 @@ class XMLReader
|
||||
}
|
||||
|
||||
$zip = new ZipArchive();
|
||||
$zip->open($zipFile);
|
||||
$openStatus = $zip->open($zipFile);
|
||||
if ($openStatus !== true) {
|
||||
/**
|
||||
* Throw an exception since making further calls on the ZipArchive would cause a fatal error.
|
||||
* This prevents fatal errors on corrupt archives and attempts to open old "doc" files.
|
||||
*/
|
||||
throw new Exception("The archive failed to load with the following error code: $openStatus");
|
||||
}
|
||||
|
||||
$content = $zip->getFromName(ltrim($xmlFile, '/'));
|
||||
$zip->close();
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ namespace PhpOffice\PhpWord\Shared;
|
||||
use PclZip;
|
||||
use PhpOffice\PhpWord\Exception\Exception;
|
||||
use PhpOffice\PhpWord\Settings;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* ZipArchive wrapper.
|
||||
@@ -162,13 +163,16 @@ class ZipArchive
|
||||
* Close the active archive.
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @codeCoverageIgnore Can't find any test case. Uncomment when found.
|
||||
*/
|
||||
public function close()
|
||||
{
|
||||
if (!$this->usePclzip) {
|
||||
if ($this->zip->close() === false) {
|
||||
try {
|
||||
$result = @$this->zip->close();
|
||||
} catch (Throwable $e) {
|
||||
$result = false;
|
||||
}
|
||||
if ($result === false) {
|
||||
throw new Exception("Could not close zip file {$this->filename}: ");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ class Border extends AbstractStyle
|
||||
/**
|
||||
* Border Top Color.
|
||||
*
|
||||
* @var string
|
||||
* @var null|string
|
||||
*/
|
||||
protected $borderTopColor;
|
||||
|
||||
@@ -55,7 +55,7 @@ class Border extends AbstractStyle
|
||||
/**
|
||||
* Border Left Color.
|
||||
*
|
||||
* @var string
|
||||
* @var null|string
|
||||
*/
|
||||
protected $borderLeftColor;
|
||||
|
||||
@@ -76,7 +76,7 @@ class Border extends AbstractStyle
|
||||
/**
|
||||
* Border Right Color.
|
||||
*
|
||||
* @var string
|
||||
* @var null|string
|
||||
*/
|
||||
protected $borderRightColor;
|
||||
|
||||
@@ -97,7 +97,7 @@ class Border extends AbstractStyle
|
||||
/**
|
||||
* Border Bottom Color.
|
||||
*
|
||||
* @var string
|
||||
* @var null|string
|
||||
*/
|
||||
protected $borderBottomColor;
|
||||
|
||||
@@ -171,7 +171,7 @@ class Border extends AbstractStyle
|
||||
/**
|
||||
* Get border color.
|
||||
*
|
||||
* @return string[]
|
||||
* @return array<null|string>
|
||||
*/
|
||||
public function getBorderColor()
|
||||
{
|
||||
@@ -186,7 +186,7 @@ class Border extends AbstractStyle
|
||||
/**
|
||||
* Set border color.
|
||||
*
|
||||
* @param string $value
|
||||
* @param null|string $value
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
@@ -259,7 +259,7 @@ class Border extends AbstractStyle
|
||||
/**
|
||||
* Get border top color.
|
||||
*
|
||||
* @return string
|
||||
* @return null|string
|
||||
*/
|
||||
public function getBorderTopColor()
|
||||
{
|
||||
@@ -269,7 +269,7 @@ class Border extends AbstractStyle
|
||||
/**
|
||||
* Set border top color.
|
||||
*
|
||||
* @param string $value
|
||||
* @param null|string $value
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
@@ -331,7 +331,7 @@ class Border extends AbstractStyle
|
||||
/**
|
||||
* Get border left color.
|
||||
*
|
||||
* @return string
|
||||
* @return null|string
|
||||
*/
|
||||
public function getBorderLeftColor()
|
||||
{
|
||||
@@ -341,7 +341,7 @@ class Border extends AbstractStyle
|
||||
/**
|
||||
* Set border left color.
|
||||
*
|
||||
* @param string $value
|
||||
* @param null|string $value
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
@@ -403,7 +403,7 @@ class Border extends AbstractStyle
|
||||
/**
|
||||
* Get border right color.
|
||||
*
|
||||
* @return string
|
||||
* @return null|string
|
||||
*/
|
||||
public function getBorderRightColor()
|
||||
{
|
||||
@@ -413,7 +413,7 @@ class Border extends AbstractStyle
|
||||
/**
|
||||
* Set border right color.
|
||||
*
|
||||
* @param string $value
|
||||
* @param null|string $value
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
@@ -475,7 +475,7 @@ class Border extends AbstractStyle
|
||||
/**
|
||||
* Get border bottom color.
|
||||
*
|
||||
* @return string
|
||||
* @return null|string
|
||||
*/
|
||||
public function getBorderBottomColor()
|
||||
{
|
||||
@@ -485,7 +485,7 @@ class Border extends AbstractStyle
|
||||
/**
|
||||
* Set border bottom color.
|
||||
*
|
||||
* @param string $value
|
||||
* @param null|string $value
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
|
||||
@@ -565,10 +565,8 @@ class Font extends AbstractStyle
|
||||
|
||||
/**
|
||||
* Get strikethrough.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isStrikethrough()
|
||||
public function isStrikethrough(): ?bool
|
||||
{
|
||||
return $this->strikethrough;
|
||||
}
|
||||
@@ -577,20 +575,16 @@ class Font extends AbstractStyle
|
||||
* Set strikethrough.
|
||||
*
|
||||
* @param bool $value
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setStrikethrough($value = true)
|
||||
public function setStrikethrough($value = true): self
|
||||
{
|
||||
return $this->setPairedVal($this->strikethrough, $this->doubleStrikethrough, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get double strikethrough.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isDoubleStrikethrough()
|
||||
public function isDoubleStrikethrough(): ?bool
|
||||
{
|
||||
return $this->doubleStrikethrough;
|
||||
}
|
||||
@@ -599,10 +593,8 @@ class Font extends AbstractStyle
|
||||
* Set double strikethrough.
|
||||
*
|
||||
* @param bool $value
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setDoubleStrikethrough($value = true)
|
||||
public function setDoubleStrikethrough($value = true): self
|
||||
{
|
||||
return $this->setPairedVal($this->doubleStrikethrough, $this->strikethrough, $value);
|
||||
}
|
||||
|
||||
@@ -146,18 +146,6 @@ class TemplateProcessor
|
||||
// Nothing to do here.
|
||||
}
|
||||
}
|
||||
// Temporary file
|
||||
if ($this->tempDocumentFilename && file_exists($this->tempDocumentFilename)) {
|
||||
unlink($this->tempDocumentFilename);
|
||||
}
|
||||
}
|
||||
|
||||
public function __wakeup(): void
|
||||
{
|
||||
$this->tempDocumentFilename = '';
|
||||
$this->zipClass = null;
|
||||
|
||||
throw new Exception('unserialize not permitted for this class');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -357,6 +345,15 @@ class TemplateProcessor
|
||||
$replace = $xmlEscaper->escape($replace);
|
||||
}
|
||||
|
||||
// convert carriage returns
|
||||
if (is_array($replace)) {
|
||||
foreach ($replace as &$item) {
|
||||
$item = $this->replaceCarriageReturns($item);
|
||||
}
|
||||
} else {
|
||||
$replace = $this->replaceCarriageReturns($replace);
|
||||
}
|
||||
|
||||
$this->tempDocumentHeaders = $this->setValueForPart($search, $replace, $this->tempDocumentHeaders, $limit);
|
||||
$this->tempDocumentMainPart = $this->setValueForPart($search, $replace, $this->tempDocumentMainPart, $limit);
|
||||
$this->tempDocumentFooters = $this->setValueForPart($search, $replace, $this->tempDocumentFooters, $limit);
|
||||
@@ -665,13 +662,13 @@ class TemplateProcessor
|
||||
foreach (array_keys($this->tempDocumentHeaders) as $headerIndex) {
|
||||
$searchParts[$this->getHeaderName($headerIndex)] = &$this->tempDocumentHeaders[$headerIndex];
|
||||
}
|
||||
foreach (array_keys($this->tempDocumentFooters) as $headerIndex) {
|
||||
$searchParts[$this->getFooterName($headerIndex)] = &$this->tempDocumentFooters[$headerIndex];
|
||||
foreach (array_keys($this->tempDocumentFooters) as $footerIndex) {
|
||||
$searchParts[$this->getFooterName($footerIndex)] = &$this->tempDocumentFooters[$footerIndex];
|
||||
}
|
||||
|
||||
// define templates
|
||||
// result can be verified via "Open XML SDK 2.5 Productivity Tool" (http://www.microsoft.com/en-us/download/details.aspx?id=30425)
|
||||
$imgTpl = '<w:pict><v:shape type="#_x0000_t75" style="width:{WIDTH};height:{HEIGHT}" stroked="f"><v:imagedata r:id="{RID}" o:title=""/></v:shape></w:pict>';
|
||||
$imgTpl = '<w:pict><v:shape type="#_x0000_t75" style="width:{WIDTH};height:{HEIGHT}" stroked="f" filled="f"><v:imagedata r:id="{RID}" o:title=""/></v:shape></w:pict>';
|
||||
|
||||
$i = 0;
|
||||
foreach ($searchParts as $partFileName => &$partContent) {
|
||||
@@ -808,8 +805,8 @@ class TemplateProcessor
|
||||
*/
|
||||
public function deleteRow(string $search): void
|
||||
{
|
||||
if ('${' !== substr($search, 0, 2) && '}' !== substr($search, -1)) {
|
||||
$search = '${' . $search . '}';
|
||||
if (self::$macroOpeningChars !== substr($search, 0, 2) && self::$macroClosingChars !== substr($search, -1)) {
|
||||
$search = self::$macroOpeningChars . $search . self::$macroClosingChars;
|
||||
}
|
||||
|
||||
$tagPos = strpos($this->tempDocumentMainPart, $search);
|
||||
@@ -1305,6 +1302,14 @@ class TemplateProcessor
|
||||
return $results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace carriage returns with xml.
|
||||
*/
|
||||
public function replaceCarriageReturns(string $string): string
|
||||
{
|
||||
return str_replace(["\r\n", "\r", "\n"], '</w:t><w:br/><w:t>', $string);
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces variables with values from array, array keys are the variable names.
|
||||
*
|
||||
@@ -1489,4 +1494,9 @@ class TemplateProcessor
|
||||
self::$macroOpeningChars = $macroOpeningChars;
|
||||
self::$macroClosingChars = $macroClosingChars;
|
||||
}
|
||||
|
||||
public function getTempDocumentFilename(): string
|
||||
{
|
||||
return $this->tempDocumentFilename;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,16 +65,9 @@ class Table extends AbstractElement
|
||||
$cellColSpan = $cellStyle->getGridSpan();
|
||||
$cellRowSpan = 1;
|
||||
$cellVMerge = $cellStyle->getVMerge();
|
||||
// If this is the first cell of the vertical merge, find out how man rows it spans
|
||||
// If this is the first cell of the vertical merge, find out how many rows it spans
|
||||
if ($cellVMerge === 'restart') {
|
||||
for ($k = $i + 1; $k < $rowCount; ++$k) {
|
||||
$kRowCells = $rows[$k]->getCells();
|
||||
if (isset($kRowCells[$j]) && $kRowCells[$j]->getStyle()->getVMerge() === 'continue') {
|
||||
++$cellRowSpan;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
$cellRowSpan = $this->calculateCellRowSpan($rows, $i, $j);
|
||||
}
|
||||
// Ignore cells that are merged vertically with previous rows
|
||||
if ($cellVMerge !== 'continue') {
|
||||
@@ -120,9 +113,7 @@ class Table extends AbstractElement
|
||||
return '';
|
||||
}
|
||||
if (is_string($tableStyle)) {
|
||||
$style = ' class="' . $tableStyle;
|
||||
|
||||
return $style . '"';
|
||||
return ' class="' . $tableStyle . '"';
|
||||
}
|
||||
|
||||
$styleWriter = new TableStyleWriter($tableStyle);
|
||||
@@ -133,4 +124,58 @@ class Table extends AbstractElement
|
||||
|
||||
return ' style="' . $style . '"';
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates cell rowspan.
|
||||
*
|
||||
* @param \PhpOffice\PhpWord\Element\Row[] $rows
|
||||
*/
|
||||
private function calculateCellRowSpan(array $rows, int $rowIndex, int $colIndex): int
|
||||
{
|
||||
$currentRow = $rows[$rowIndex];
|
||||
$currentRowCells = $currentRow->getCells();
|
||||
$shiftedColIndex = 0;
|
||||
|
||||
foreach ($currentRowCells as $cell) {
|
||||
if ($cell === $currentRowCells[$colIndex]) {
|
||||
break;
|
||||
}
|
||||
|
||||
$colSpan = 1;
|
||||
|
||||
if ($cell->getStyle()->getGridSpan() !== null) {
|
||||
$colSpan = $cell->getStyle()->getGridSpan();
|
||||
}
|
||||
|
||||
$shiftedColIndex += $colSpan;
|
||||
}
|
||||
|
||||
$rowCount = count($rows);
|
||||
$rowSpan = 1;
|
||||
|
||||
for ($i = $rowIndex + 1; $i < $rowCount; ++$i) {
|
||||
$rowCells = $rows[$i]->getCells();
|
||||
$colIndex = 0;
|
||||
|
||||
foreach ($rowCells as $cell) {
|
||||
if ($colIndex === $shiftedColIndex) {
|
||||
if ($cell->getStyle()->getVMerge() === 'continue') {
|
||||
++$rowSpan;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
$colSpan = 1;
|
||||
|
||||
if ($cell->getStyle()->getGridSpan() !== null) {
|
||||
$colSpan = $cell->getStyle()->getGridSpan();
|
||||
}
|
||||
|
||||
$colIndex += $colSpan;
|
||||
}
|
||||
}
|
||||
|
||||
return $rowSpan;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ use PhpOffice\PhpWord\Shared\Text as SharedText;
|
||||
use PhpOffice\PhpWord\Style;
|
||||
use PhpOffice\PhpWord\Style\Font as FontStyle;
|
||||
use PhpOffice\PhpWord\Style\Paragraph as ParagraphStyle;
|
||||
use PhpOffice\PhpWord\Writer\AbstractWriter;
|
||||
use PhpOffice\PhpWord\Writer\RTF as WriterRTF;
|
||||
use PhpOffice\PhpWord\Writer\RTF\Style\Font as FontStyleWriter;
|
||||
use PhpOffice\PhpWord\Writer\RTF\Style\Paragraph as ParagraphStyleWriter;
|
||||
|
||||
@@ -38,7 +38,7 @@ abstract class AbstractElement
|
||||
/**
|
||||
* Parent writer.
|
||||
*
|
||||
* @var \PhpOffice\PhpWord\Writer\AbstractWriter
|
||||
* @var WriterRTF
|
||||
*/
|
||||
protected $parentWriter;
|
||||
|
||||
@@ -82,7 +82,7 @@ abstract class AbstractElement
|
||||
*/
|
||||
protected $escaper;
|
||||
|
||||
public function __construct(AbstractWriter $parentWriter, Element $element, bool $withoutP = false)
|
||||
public function __construct(WriterRTF $parentWriter, Element $element, bool $withoutP = false)
|
||||
{
|
||||
$this->parentWriter = $parentWriter;
|
||||
$this->element = $element;
|
||||
|
||||
@@ -21,7 +21,10 @@ use PhpOffice\PhpWord\Element\Cell as CellElement;
|
||||
use PhpOffice\PhpWord\Element\Row as RowElement;
|
||||
use PhpOffice\PhpWord\Element\Table as TableElement;
|
||||
use PhpOffice\PhpWord\Settings;
|
||||
use PhpOffice\PhpWord\SimpleType\Border;
|
||||
use PhpOffice\PhpWord\Style;
|
||||
use PhpOffice\PhpWord\Style\Cell as CellStyle;
|
||||
use PhpOffice\PhpWord\Style\Table as TableStyle;
|
||||
|
||||
/**
|
||||
* Table element RTF writer.
|
||||
@@ -30,6 +33,11 @@ use PhpOffice\PhpWord\Style;
|
||||
*/
|
||||
class Table extends AbstractElement
|
||||
{
|
||||
/**
|
||||
* @var TableElement
|
||||
*/
|
||||
protected $element;
|
||||
|
||||
/**
|
||||
* Write element.
|
||||
*
|
||||
@@ -77,9 +85,18 @@ class Table extends AbstractElement
|
||||
private function writeRowDef(RowElement $row)
|
||||
{
|
||||
$content = '';
|
||||
$tableStyle = $this->element->getStyle();
|
||||
if (is_string($tableStyle)) {
|
||||
$tableStyle = Style::getStyle($tableStyle);
|
||||
if (!($tableStyle instanceof TableStyle)) {
|
||||
$tableStyle = null;
|
||||
}
|
||||
}
|
||||
|
||||
$rightMargin = 0;
|
||||
foreach ($row->getCells() as $cell) {
|
||||
$content .= $this->writeCellStyle($cell->getStyle(), $tableStyle);
|
||||
|
||||
$width = $cell->getWidth();
|
||||
$vMerge = $this->getVMerge($cell->getStyle()->getVMerge());
|
||||
if ($width === null) {
|
||||
@@ -127,6 +144,103 @@ class Table extends AbstractElement
|
||||
return $content;
|
||||
}
|
||||
|
||||
private function writeCellStyle(CellStyle $cell, ?TableStyle $table): string
|
||||
{
|
||||
$content = $this->writeCellBorder(
|
||||
't',
|
||||
$cell->getBorderTopStyle() ?: ($table ? $table->getBorderTopStyle() : null),
|
||||
(int) round($cell->getBorderTopSize() ?: ($table ? ($table->getBorderTopSize() ?: 0) : 0)),
|
||||
$cell->getBorderTopColor() ?? ($table ? $table->getBorderTopColor() : null)
|
||||
);
|
||||
$content .= $this->writeCellBorder(
|
||||
'l',
|
||||
$cell->getBorderLeftStyle() ?: ($table ? $table->getBorderLeftStyle() : null),
|
||||
(int) round($cell->getBorderLeftSize() ?: ($table ? ($table->getBorderLeftSize() ?: 0) : 0)),
|
||||
$cell->getBorderLeftColor() ?? ($table ? $table->getBorderLeftColor() : null)
|
||||
);
|
||||
$content .= $this->writeCellBorder(
|
||||
'b',
|
||||
$cell->getBorderBottomStyle() ?: ($table ? $table->getBorderBottomStyle() : null),
|
||||
(int) round($cell->getBorderBottomSize() ?: ($table ? ($table->getBorderBottomSize() ?: 0) : 0)),
|
||||
$cell->getBorderBottomColor() ?? ($table ? $table->getBorderBottomColor() : null)
|
||||
);
|
||||
$content .= $this->writeCellBorder(
|
||||
'r',
|
||||
$cell->getBorderRightStyle() ?: ($table ? $table->getBorderRightStyle() : null),
|
||||
(int) round($cell->getBorderRightSize() ?: ($table ? ($table->getBorderRightSize() ?: 0) : 0)),
|
||||
$cell->getBorderRightColor() ?? ($table ? $table->getBorderRightColor() : null)
|
||||
);
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
private function writeCellBorder(string $prefix, ?string $borderStyle, int $borderSize, ?string $borderColor): string
|
||||
{
|
||||
if ($borderSize == 0) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$content = '\clbrdr' . $prefix;
|
||||
/**
|
||||
* \brdrs Single-thickness border.
|
||||
* \brdrth Double-thickness border.
|
||||
* \brdrsh Shadowed border.
|
||||
* \brdrdb Double border.
|
||||
* \brdrdot Dotted border.
|
||||
* \brdrdash Dashed border.
|
||||
* \brdrhair Hairline border.
|
||||
* \brdrinset Inset border.
|
||||
* \brdrdashsm Dash border (small).
|
||||
* \brdrdashd Dot dash border.
|
||||
* \brdrdashdd Dot dot dash border.
|
||||
* \brdroutset Outset border.
|
||||
* \brdrtriple Triple border.
|
||||
* \brdrtnthsg Thick thin border (small).
|
||||
* \brdrthtnsg Thin thick border (small).
|
||||
* \brdrtnthtnsg Thin thick thin border (small).
|
||||
* \brdrtnthmg Thick thin border (medium).
|
||||
* \brdrthtnmg Thin thick border (medium).
|
||||
* \brdrtnthtnmg Thin thick thin border (medium).
|
||||
* \brdrtnthlg Thick thin border (large).
|
||||
* \brdrthtnlg Thin thick border (large).
|
||||
* \brdrtnthtnlg Thin thick thin border (large).
|
||||
* \brdrwavy Wavy border.
|
||||
* \brdrwavydb Double wavy border.
|
||||
* \brdrdashdotstr Striped border.
|
||||
* \brdremboss Emboss border.
|
||||
* \brdrengrave Engrave border.
|
||||
*/
|
||||
switch ($borderStyle) {
|
||||
case Border::DOTTED:
|
||||
$content .= '\brdrdot';
|
||||
|
||||
break;
|
||||
case Border::SINGLE:
|
||||
default:
|
||||
$content .= '\brdrs';
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// \brdrwN N is the width in twips (1/20 pt) of the pen used to draw the paragraph border line.
|
||||
// N cannot be greater than 75.
|
||||
// To obtain a larger border width, the \brdth control word can be used to obtain a width double that of N.
|
||||
// $borderSize is in eights of a point, i.e. 4 / 8 = .5pt
|
||||
// 1/20 pt => 1/8 / 2.5
|
||||
$content .= '\brdrw' . (int) ($borderSize / 2.5);
|
||||
|
||||
// \brdrcfN N is the color of the paragraph border, specified as an index into the color table in the RTF header.
|
||||
$colorIndex = 0;
|
||||
$index = array_search($borderColor, $this->parentWriter->getColorTable());
|
||||
if ($index !== false) {
|
||||
$colorIndex = (int) $index + 1;
|
||||
}
|
||||
$content .= '\brdrcf' . $colorIndex;
|
||||
$content .= PHP_EOL;
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get vertical merge style.
|
||||
*
|
||||
|
||||
@@ -21,6 +21,7 @@ use PhpOffice\PhpWord\Settings;
|
||||
use PhpOffice\PhpWord\Shared\Converter;
|
||||
use PhpOffice\PhpWord\Style;
|
||||
use PhpOffice\PhpWord\Style\Font;
|
||||
use PhpOffice\PhpWord\Style\Table;
|
||||
|
||||
/**
|
||||
* RTF header part writer.
|
||||
@@ -236,6 +237,14 @@ class Header extends AbstractPart
|
||||
$this->registerTableItem($this->fontTable, $style->getName(), $defaultFont);
|
||||
$this->registerTableItem($this->colorTable, $style->getColor(), $defaultColor);
|
||||
$this->registerTableItem($this->colorTable, $style->getFgColor(), $defaultColor);
|
||||
|
||||
return;
|
||||
}
|
||||
if ($style instanceof Table) {
|
||||
$this->registerTableItem($this->colorTable, $style->getBorderTopColor(), $defaultColor);
|
||||
$this->registerTableItem($this->colorTable, $style->getBorderRightColor(), $defaultColor);
|
||||
$this->registerTableItem($this->colorTable, $style->getBorderLeftColor(), $defaultColor);
|
||||
$this->registerTableItem($this->colorTable, $style->getBorderBottomColor(), $defaultColor);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -227,7 +227,6 @@ class Word2007 extends AbstractWriter implements WriterInterface
|
||||
$collection = $phpWord->$method();
|
||||
|
||||
// Add footnotes media files, relations, and contents
|
||||
/** @var \PhpOffice\PhpWord\Collection\AbstractCollection $collection Type hint */
|
||||
if ($collection->countItems() > 0) {
|
||||
$media = Media::getElements($noteType);
|
||||
$this->addFilesToPackage($zip, $media);
|
||||
@@ -260,7 +259,6 @@ class Word2007 extends AbstractWriter implements WriterInterface
|
||||
$partName = 'comments';
|
||||
|
||||
// Add comment relations and contents
|
||||
/** @var \PhpOffice\PhpWord\Collection\AbstractCollection $collection Type hint */
|
||||
if ($collection->countItems() > 0) {
|
||||
$this->relationships[] = ['target' => "{$partName}.xml", 'type' => $partName, 'rID' => ++$rId];
|
||||
|
||||
|
||||
@@ -126,14 +126,10 @@ abstract class AbstractElement
|
||||
*/
|
||||
protected function writeCommentRangeStart(): void
|
||||
{
|
||||
if ($this->element->getCommentRangeStart() != null) {
|
||||
$comment = $this->element->getCommentRangeStart();
|
||||
//only set the ID if it is not yet set, otherwise it will overwrite it
|
||||
if ($comment->getElementId() == null) {
|
||||
$comment->setElementId();
|
||||
if ($this->element->getCommentsRangeStart() != null) {
|
||||
foreach ($this->element->getCommentsRangeStart()->getItems() as $comment) {
|
||||
$this->xmlWriter->writeElementBlock('w:commentRangeStart', ['w:id' => $comment->getElementId()]);
|
||||
}
|
||||
|
||||
$this->xmlWriter->writeElementBlock('w:commentRangeStart', ['w:id' => $comment->getElementId()]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,28 +138,23 @@ abstract class AbstractElement
|
||||
*/
|
||||
protected function writeCommentRangeEnd(): void
|
||||
{
|
||||
if ($this->element->getCommentRangeEnd() != null) {
|
||||
$comment = $this->element->getCommentRangeEnd();
|
||||
//only set the ID if it is not yet set, otherwise it will overwrite it, this should normally not happen
|
||||
if ($comment->getElementId() == null) {
|
||||
$comment->setElementId(); // @codeCoverageIgnore
|
||||
} // @codeCoverageIgnore
|
||||
|
||||
$this->xmlWriter->writeElementBlock('w:commentRangeEnd', ['w:id' => $comment->getElementId()]);
|
||||
$this->xmlWriter->startElement('w:r');
|
||||
$this->xmlWriter->writeElementBlock('w:commentReference', ['w:id' => $comment->getElementId()]);
|
||||
$this->xmlWriter->endElement();
|
||||
} elseif ($this->element->getCommentRangeStart() != null && $this->element->getCommentRangeStart()->getEndElement() == null) {
|
||||
$comment = $this->element->getCommentRangeStart();
|
||||
//only set the ID if it is not yet set, otherwise it will overwrite it, this should normally not happen
|
||||
if ($comment->getElementId() == null) {
|
||||
$comment->setElementId(); // @codeCoverageIgnore
|
||||
} // @codeCoverageIgnore
|
||||
|
||||
$this->xmlWriter->writeElementBlock('w:commentRangeEnd', ['w:id' => $comment->getElementId()]);
|
||||
$this->xmlWriter->startElement('w:r');
|
||||
$this->xmlWriter->writeElementBlock('w:commentReference', ['w:id' => $comment->getElementId()]);
|
||||
$this->xmlWriter->endElement();
|
||||
if ($this->element->getCommentsRangeEnd() != null) {
|
||||
foreach ($this->element->getCommentsRangeEnd()->getItems() as $comment) {
|
||||
$this->xmlWriter->writeElementBlock('w:commentRangeEnd', ['w:id' => $comment->getElementId()]);
|
||||
$this->xmlWriter->startElement('w:r');
|
||||
$this->xmlWriter->writeElementBlock('w:commentReference', ['w:id' => $comment->getElementId()]);
|
||||
$this->xmlWriter->endElement();
|
||||
}
|
||||
}
|
||||
if ($this->element->getCommentsRangeStart() != null) {
|
||||
foreach ($this->element->getCommentsRangeStart()->getItems() as $comment) {
|
||||
if ($comment->getEndElement() == null) {
|
||||
$this->xmlWriter->writeElementBlock('w:commentRangeEnd', ['w:id' => $comment->getElementId()]);
|
||||
$this->xmlWriter->startElement('w:r');
|
||||
$this->xmlWriter->writeElementBlock('w:commentReference', ['w:id' => $comment->getElementId()]);
|
||||
$this->xmlWriter->endElement();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,9 @@
|
||||
|
||||
namespace PhpOffice\PhpWord\Writer\Word2007\Element;
|
||||
|
||||
use PhpOffice\PhpWord\Element\Field as ElementField;
|
||||
use PhpOffice\PhpWord\Element\TextRun;
|
||||
|
||||
/**
|
||||
* Field element writer.
|
||||
*
|
||||
@@ -30,7 +33,7 @@ class Field extends Text
|
||||
public function write(): void
|
||||
{
|
||||
$element = $this->getElement();
|
||||
if (!$element instanceof \PhpOffice\PhpWord\Element\Field) {
|
||||
if (!$element instanceof ElementField) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -42,7 +45,7 @@ class Field extends Text
|
||||
}
|
||||
}
|
||||
|
||||
private function writeDefault(\PhpOffice\PhpWord\Element\Field $element): void
|
||||
private function writeDefault(ElementField $element): void
|
||||
{
|
||||
$xmlWriter = $this->getXmlWriter();
|
||||
$this->startElementP();
|
||||
@@ -73,7 +76,7 @@ class Field extends Text
|
||||
$xmlWriter->endElement(); // w:r
|
||||
|
||||
if ($element->getText() != null) {
|
||||
if ($element->getText() instanceof \PhpOffice\PhpWord\Element\TextRun) {
|
||||
if ($element->getText() instanceof TextRun) {
|
||||
$containerWriter = new Container($xmlWriter, $element->getText(), true);
|
||||
$containerWriter->write();
|
||||
|
||||
@@ -120,7 +123,7 @@ class Field extends Text
|
||||
*
|
||||
* //TODO A lot of code duplication with general method, should maybe be refactored
|
||||
*/
|
||||
protected function writeMacrobutton(\PhpOffice\PhpWord\Element\Field $element): void
|
||||
protected function writeMacrobutton(ElementField $element): void
|
||||
{
|
||||
$xmlWriter = $this->getXmlWriter();
|
||||
$this->startElementP();
|
||||
@@ -159,7 +162,7 @@ class Field extends Text
|
||||
$this->endElementP(); // w:p
|
||||
}
|
||||
|
||||
private function buildPropertiesAndOptions(\PhpOffice\PhpWord\Element\Field $element)
|
||||
private function buildPropertiesAndOptions(ElementField $element)
|
||||
{
|
||||
$propertiesAndOptions = '';
|
||||
$properties = $element->getProperties();
|
||||
@@ -226,4 +229,104 @@ class Field extends Text
|
||||
|
||||
return $propertiesAndOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a REF field.
|
||||
*/
|
||||
protected function writeRef(ElementField $element): void
|
||||
{
|
||||
$xmlWriter = $this->getXmlWriter();
|
||||
$this->startElementP();
|
||||
|
||||
$xmlWriter->startElement('w:r');
|
||||
$xmlWriter->startElement('w:fldChar');
|
||||
$xmlWriter->writeAttribute('w:fldCharType', 'begin');
|
||||
$xmlWriter->endElement(); // w:fldChar
|
||||
$xmlWriter->endElement(); // w:r
|
||||
|
||||
$instruction = ' ' . $element->getType() . ' ';
|
||||
|
||||
foreach ($element->getProperties() as $property) {
|
||||
$instruction .= $property . ' ';
|
||||
}
|
||||
foreach ($element->getOptions() as $optionKey => $optionValue) {
|
||||
$instruction .= $this->convertRefOption($optionKey, $optionValue) . ' ';
|
||||
}
|
||||
|
||||
$xmlWriter->startElement('w:r');
|
||||
$this->writeFontStyle();
|
||||
$xmlWriter->startElement('w:instrText');
|
||||
$xmlWriter->writeAttribute('xml:space', 'preserve');
|
||||
$xmlWriter->text($instruction);
|
||||
$xmlWriter->endElement(); // w:instrText
|
||||
$xmlWriter->endElement(); // w:r
|
||||
|
||||
if ($element->getText() != null) {
|
||||
if ($element->getText() instanceof \PhpOffice\PhpWord\Element\TextRun) {
|
||||
$containerWriter = new Container($xmlWriter, $element->getText(), true);
|
||||
$containerWriter->write();
|
||||
|
||||
$xmlWriter->startElement('w:r');
|
||||
$xmlWriter->startElement('w:instrText');
|
||||
$xmlWriter->text('"' . $this->buildPropertiesAndOptions($element));
|
||||
$xmlWriter->endElement(); // w:instrText
|
||||
$xmlWriter->endElement(); // w:r
|
||||
|
||||
$xmlWriter->startElement('w:r');
|
||||
$xmlWriter->startElement('w:instrText');
|
||||
$xmlWriter->writeAttribute('xml:space', 'preserve');
|
||||
$xmlWriter->text(' ');
|
||||
$xmlWriter->endElement(); // w:instrText
|
||||
$xmlWriter->endElement(); // w:r
|
||||
}
|
||||
}
|
||||
|
||||
$xmlWriter->startElement('w:r');
|
||||
$xmlWriter->startElement('w:fldChar');
|
||||
$xmlWriter->writeAttribute('w:fldCharType', 'separate');
|
||||
$xmlWriter->endElement(); // w:fldChar
|
||||
$xmlWriter->endElement(); // w:r
|
||||
|
||||
$xmlWriter->startElement('w:r');
|
||||
$xmlWriter->startElement('w:rPr');
|
||||
$xmlWriter->startElement('w:noProof');
|
||||
$xmlWriter->endElement(); // w:noProof
|
||||
$xmlWriter->endElement(); // w:rPr
|
||||
$xmlWriter->writeElement('w:t', $element->getText() != null && is_string($element->getText()) ? $element->getText() : '1');
|
||||
$xmlWriter->endElement(); // w:r
|
||||
|
||||
$xmlWriter->startElement('w:r');
|
||||
$xmlWriter->startElement('w:fldChar');
|
||||
$xmlWriter->writeAttribute('w:fldCharType', 'end');
|
||||
$xmlWriter->endElement(); // w:fldChar
|
||||
$xmlWriter->endElement(); // w:r
|
||||
|
||||
$this->endElementP(); // w:p
|
||||
}
|
||||
|
||||
private function convertRefOption(string $optionKey, string $optionValue): string
|
||||
{
|
||||
if ($optionKey === 'NumberSeperatorSequence') {
|
||||
return '\\d ' . $optionValue;
|
||||
}
|
||||
|
||||
switch ($optionValue) {
|
||||
case 'IncrementAndInsertText':
|
||||
return '\\f';
|
||||
case 'CreateHyperLink':
|
||||
return '\\h';
|
||||
case 'NoTrailingPeriod':
|
||||
return '\\n';
|
||||
case 'IncludeAboveOrBelow':
|
||||
return '\\p';
|
||||
case 'InsertParagraphNumberRelativeContext':
|
||||
return '\\r';
|
||||
case 'SuppressNonDelimiterNonNumericalText':
|
||||
return '\\t';
|
||||
case 'InsertParagraphNumberFullContext':
|
||||
return '\\w';
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ class Footnote extends Text
|
||||
$xmlWriter->endElement(); // w:rStyle
|
||||
$xmlWriter->endElement(); // w:rPr
|
||||
$xmlWriter->startElement("w:{$this->referenceType}");
|
||||
$xmlWriter->writeAttribute('w:id', $element->getRelationId());
|
||||
$xmlWriter->writeAttribute('w:id', $element->getRelationId() + 1);
|
||||
$xmlWriter->endElement(); // w:$referenceType
|
||||
$xmlWriter->endElement(); // w:r
|
||||
|
||||
|
||||
@@ -103,8 +103,14 @@ class Table extends AbstractElement
|
||||
}
|
||||
|
||||
// Write cells
|
||||
foreach ($row->getCells() as $cell) {
|
||||
$this->writeCell($xmlWriter, $cell);
|
||||
$cells = $row->getCells();
|
||||
if (count($cells) === 0) {
|
||||
// issue 2505 - Word treats doc as corrupt if row without cell
|
||||
$this->writeCell($xmlWriter, new CellElement());
|
||||
} else {
|
||||
foreach ($cells as $cell) {
|
||||
$this->writeCell($xmlWriter, $cell);
|
||||
}
|
||||
}
|
||||
|
||||
$xmlWriter->endElement(); // w:tr
|
||||
|
||||
@@ -141,7 +141,7 @@ class Footnotes extends AbstractPart
|
||||
protected function writeNote(XMLWriter $xmlWriter, $element): void
|
||||
{
|
||||
$xmlWriter->startElement($this->elementNode);
|
||||
$xmlWriter->writeAttribute('w:id', $element->getRelationId());
|
||||
$xmlWriter->writeAttribute('w:id', $element->getRelationId() + 1);
|
||||
$xmlWriter->startElement('w:p');
|
||||
|
||||
// Paragraph style
|
||||
|
||||
@@ -117,8 +117,8 @@ class Font extends AbstractStyle
|
||||
$xmlWriter->writeElementIf($style->isItalic() !== null, 'w:iCs', 'w:val', $this->writeOnOf($style->isItalic()));
|
||||
|
||||
// Strikethrough, double strikethrough
|
||||
$xmlWriter->writeElementIf($style->isStrikethrough() !== null, 'w:strike', 'w:val', $this->writeOnOf($style->isStrikethrough()));
|
||||
$xmlWriter->writeElementIf($style->isDoubleStrikethrough() !== null, 'w:dstrike', 'w:val', $this->writeOnOf($style->isDoubleStrikethrough()));
|
||||
$xmlWriter->writeElementIf($style->isStrikethrough(), 'w:strike', 'w:val', $this->writeOnOf($style->isStrikethrough()));
|
||||
$xmlWriter->writeElementIf($style->isDoubleStrikethrough(), 'w:dstrike', 'w:val', $this->writeOnOf($style->isDoubleStrikethrough()));
|
||||
|
||||
// Small caps, all caps
|
||||
$xmlWriter->writeElementIf($style->isSmallCaps() !== null, 'w:smallCaps', 'w:val', $this->writeOnOf($style->isSmallCaps()));
|
||||
|
||||
@@ -120,7 +120,7 @@ class MarginBorder extends AbstractStyle
|
||||
/**
|
||||
* Set colors.
|
||||
*
|
||||
* @param string[] $value
|
||||
* @param array<null|string> $value
|
||||
*/
|
||||
public function setColors($value): void
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user