Skip to content

Commit

Permalink
Merge branch 'master' into word1692
Browse files Browse the repository at this point in the history
  • Loading branch information
oleibman committed Jan 7, 2024
2 parents 3638e1b + 050e74e commit a6e8940
Show file tree
Hide file tree
Showing 27 changed files with 716 additions and 151 deletions.
231 changes: 116 additions & 115 deletions composer.lock

Large diffs are not rendered by default.

6 changes: 5 additions & 1 deletion docs/changes/1.x/1.2.0.md
@@ -1,4 +1,4 @@
# [1.2.0](https://github.com/PHPOffice/PHPWord/tree/1.2.0) (WIP)
# [1.2.0](https://github.com/PHPOffice/PHPWord/tree/1.2.0)

[Full Changelog](https://github.com/PHPOffice/PHPWord/compare/1.1.0...1.2.0)

Expand Down Expand Up @@ -29,6 +29,9 @@
- PDF Writer : Added support for PageBreak
- PDF Writer : Added callback for modifying the HTML
- Added Support for Language, both for document overall and individual text elements
- Template : Set a checkbox by [@nxtpge](https://github.com/nxtpge) in [#2509](https://github.com/PHPOffice/PHPWord/pull/2509)
- ODText / RTF / Word2007 Writer : Add field FILENAME by [@milkyway-git](https://github.com/milkyway-git) in [#2510](https://github.com/PHPOffice/PHPWord/pull/2510)
- ODText Reader : Improve Section Reader by [@oleibman](https://github.com/oleibman) in [#2507](https://github.com/PHPOffice/PHPWord/pull/2507)

### Bug fixes

Expand All @@ -39,6 +42,7 @@
- Template Processor : Fixed choose dimention for Float Value by [@gdevilbat](https://github.com/gdevilbat) in GH-2449
- HTML Parser : Fix image parsing from url without extension by [@JokubasR](https://github.com/JokubasR) in GH-2459
- Word2007 Reader : Fixed reading of Office365 DocX file by [@filippotoso](https://github.com/filippotoso) & [@lfglopes](https://github.com/lfglopes) in [#2506](https://github.com/PHPOffice/PHPWord/pull/2506)
- Word2007 Reader : Check for null on $fontDefaultStyle by [@spatialfree](https://github.com/spatialfree) in [#2513](https://github.com/PHPOffice/PHPWord/pull/2513)

### Miscellaneous

Expand Down
3 changes: 2 additions & 1 deletion docs/usage/elements/field.md
Expand Up @@ -7,6 +7,7 @@ Currently the following fields are supported:
- DATE
- XE
- INDEX
- FILENAME

``` php
<?php
Expand Down Expand Up @@ -36,4 +37,4 @@ $section->addField('XE', array(), array(), $fieldText);

//this actually adds the index
$section->addField('INDEX', array(), array('\\e " " \\h "A" \\c "3"'), 'right click to update index');
```
```
25 changes: 25 additions & 0 deletions docs/usage/template.md
Expand Up @@ -38,6 +38,31 @@ You can also set multiple values by passing all of them in an array.
$templateProcessor->setValues(array('firstname' => 'John', 'lastname' => 'Doe'));
```

## setCheckbox

Given a template containing a checkbox control with the title or tag named:

``` clean
${checkbox}
```
The following will check the checkbox:

``` php
<?php

$templateProcessor->setCheckbox('checkbox', true);
```

!!! note annotate "To add a checkbox and set its title or tag in a template"

1. Go to **Developer** tab > **Controls** group
2. Select the **Check Box Content Control**
3. Right-click on your checkbox
4. Click on **Properties**
5. Set the title or the tag

These steps may change regarding the version of Microsoft Word used.

## setMacroOpeningChars

You can define a custom opening macro. The following will set ``{#`` as the opening search pattern.
Expand Down
5 changes: 4 additions & 1 deletion mkdocs.yml
Expand Up @@ -55,6 +55,7 @@ nav:
- Comment: 'usage/elements/comment.md'
- Field: 'usage/elements/field.md'
- Footnote & Endnote: 'usage/elements/note.md'
- Formula: 'usage/elements/formula.md'
- Image: 'usage/elements/image.md'
- Line: 'usage/elements/line.md'
- Link: 'usage/elements/link.md'
Expand Down Expand Up @@ -85,8 +86,10 @@ nav:
- How to: 'howto.md'
- Credits: 'credits.md'
- Releases:
- '2.x':
- '2.0.0 (WIP)': 'changes/2.x/2.0.0.md'
- '1.x':
- '1.2.0 (WIP)': 'changes/1.x/1.2.0.md'
- '1.2.0': 'changes/1.x/1.2.0.md'
- '1.1.0': 'changes/1.x/1.1.0.md'
- '1.0.0': 'changes/1.x/1.0.0.md'
- '0.x':
Expand Down
5 changes: 0 additions & 5 deletions phpstan-baseline.neon
Expand Up @@ -165,11 +165,6 @@ parameters:
count: 1
path: src/PhpWord/Reader/HTML.php

-
message: "#^Call to an undefined method DOMNode\\:\\:getAttribute\\(\\)\\.$#"
count: 2
path: src/PhpWord/Reader/ODText/Content.php

-
message: "#^Offset 'textNodes' on array\\{changed\\: PhpOffice\\\\PhpWord\\\\Element\\\\TrackChange, textNodes\\: DOMNodeList\\<DOMElement\\>\\} in isset\\(\\) always exists and is not nullable\\.$#"
count: 1
Expand Down
3 changes: 3 additions & 0 deletions samples/Sample_27_Field.php
Expand Up @@ -26,6 +26,9 @@

$section->addText('Number of pages field:');
$section->addField('NUMPAGES', ['numformat' => '0,00', 'format' => 'Arabic'], ['PreserveFormat']);

$section->addText('Filename field:');
$section->addField('FILENAME', ['format' => 'Upper'], ['Path', 'PreserveFormat']);
$section->addTextBreak();

$textrun = $section->addTextRun();
Expand Down
21 changes: 21 additions & 0 deletions samples/Sample_42_TemplateSetCheckbox.php
@@ -0,0 +1,21 @@
<?php

include_once 'Sample_Header.php';

use PhpOffice\PhpWord\TemplateProcessor;

// Template processor instance creation
echo date('H:i:s'), ' Creating new TemplateProcessor instance...', EOL;
$filename = 'Sample_42_TemplateSetCheckbox.docx';
$templateProcessor = new TemplateProcessor(__DIR__ . "/resources/{$filename}");

$templateProcessor->setCheckbox('checkbox', true);
$templateProcessor->setCheckbox('checkbox2', false);

echo date('H:i:s'), ' Saving the result document...', EOL;
$templateProcessor->saveAs(__DIR__ . "/results/{$filename}");

echo getEndingNotes(['Word2007' => 'docx'], "results/{$filename}");
if (!CLI) {
include_once 'Sample_Footer.php';
}
File renamed without changes.
Binary file not shown.
6 changes: 6 additions & 0 deletions src/PhpWord/Element/Field.php
Expand Up @@ -85,6 +85,12 @@ class Field extends AbstractElement
'properties' => ['StyleIdentifier' => ''],
'options' => ['PreserveFormat'],
],
'FILENAME' => [
'properties' => [
'format' => ['Upper', 'Lower', 'FirstCap', 'Caps'],
],
'options' => ['Path', 'PreserveFormat'],
],
];

/**
Expand Down
12 changes: 12 additions & 0 deletions src/PhpWord/Element/TextRun.php
Expand Up @@ -78,4 +78,16 @@ public function setParagraphStyle($style = null)

return $this->paragraphStyle;
}

public function getText(): string
{
$outstr = '';
foreach ($this->getElements() as $element) {
if ($element instanceof Text) {
$outstr .= $element->getText();
}
}

return $outstr;
}
}
2 changes: 1 addition & 1 deletion src/PhpWord/Reader/MsDoc.php
Expand Up @@ -1871,7 +1871,7 @@ private function readPrl($data, $pos, $cbNum)
break;
// sprmCHps
case 0x43:
$oStylePrl->styleFont['size'] = dechex($operand / 2);
$oStylePrl->styleFont['size'] = $operand / 2;

break;
// sprmCIss
Expand Down
81 changes: 75 additions & 6 deletions src/PhpWord/Reader/ODText/Content.php
Expand Up @@ -18,7 +18,10 @@
namespace PhpOffice\PhpWord\Reader\ODText;

use DateTime;
use DOMElement;
use DOMNodeList;
use PhpOffice\Math\Reader\MathML;
use PhpOffice\PhpWord\Element\Section;
use PhpOffice\PhpWord\Element\TrackChange;
use PhpOffice\PhpWord\PhpWord;
use PhpOffice\PhpWord\Shared\XMLReader;
Expand All @@ -30,6 +33,9 @@
*/
class Content extends AbstractPart
{
/** @var ?Section */
private $section;

/**
* Read content.xml.
*/
Expand All @@ -41,17 +47,28 @@ public function read(PhpWord $phpWord): void
$trackedChanges = [];

$nodes = $xmlReader->getElements('office:body/office:text/*');
$this->section = null;
$this->processNodes($nodes, $xmlReader, $phpWord);
$this->section = null;
}

/** @param DOMNodeList<DOMElement> $nodes */
public function processNodes(DOMNodeList $nodes, XMLReader $xmlReader, PhpWord $phpWord): void
{
if ($nodes->length > 0) {
$section = $phpWord->addSection();
foreach ($nodes as $node) {
// $styleName = $xmlReader->getAttribute('text:style-name', $node);
switch ($node->nodeName) {
case 'text:h': // Heading
$depth = $xmlReader->getAttribute('text:outline-level', $node);
$section->addTitle($node->nodeValue, $depth);
$this->getSection($phpWord)->addTitle($node->nodeValue, $depth);

break;
case 'text:p': // Paragraph
$styleName = $xmlReader->getAttribute('text:style-name', $node);
if (substr($styleName, 0, 2) === 'SB') {
break;
}
$element = $xmlReader->getElement('draw:frame/draw:object', $node);
if ($element) {
$mathFile = str_replace('./', '', $element->getAttribute('xlink:href')) . '/content.xml';
Expand All @@ -65,11 +82,13 @@ public function read(PhpWord $phpWord): void
$reader = new MathML();
$math = $reader->read($mathXML);

$section->addFormula($math);
$this->getSection($phpWord)->addFormula($math);
}
}
} else {
$children = $node->childNodes;
$spans = false;
/** @var DOMElement $child */
foreach ($children as $child) {
switch ($child->nodeName) {
case 'text:change-start':
Expand All @@ -89,16 +108,49 @@ public function read(PhpWord $phpWord): void
$changed = $trackedChanges[$changeId];
}

break;
case 'text:span':
$spans = true;

break;
}
}

$element = $section->addText($node->nodeValue);
if ($spans) {
$element = $this->getSection($phpWord)->addTextRun();
foreach ($children as $child) {
switch ($child->nodeName) {
case 'text:span':
/** @var DOMElement $child2 */
foreach ($child->childNodes as $child2) {
switch ($child2->nodeName) {
case '#text':
$element->addText($child2->nodeValue);

break;
case 'text:tab':
$element->addText("\t");

break;
case 'text:s':
$spaces = (int) $child2->getAttribute('text:c') ?: 1;
$element->addText(str_repeat(' ', $spaces));

break;
}
}

break;
}
}
} else {
$element = $this->getSection($phpWord)->addText($node->nodeValue);
}
if (isset($changed) && is_array($changed)) {
$element->setTrackChange($changed['changed']);
if (isset($changed['textNodes'])) {
foreach ($changed['textNodes'] as $changedNode) {
$element = $section->addText($changedNode->nodeValue);
$element = $this->getSection($phpWord)->addText($changedNode->nodeValue);
$element->setTrackChange($changed['changed']);
}
}
Expand All @@ -110,7 +162,7 @@ public function read(PhpWord $phpWord): void
$listItems = $xmlReader->getElements('text:list-item/text:p', $node);
foreach ($listItems as $listItem) {
// $listStyleName = $xmlReader->getAttribute('text:style-name', $listItem);
$section->addListItem($listItem->nodeValue, 0);
$this->getSection($phpWord)->addListItem($listItem->nodeValue, 0);
}

break;
Expand All @@ -129,9 +181,26 @@ public function read(PhpWord $phpWord): void
$trackedChanges[$changedRegion->getAttribute('text:id')] = ['changed' => $changed, 'textNodes' => $textNodes];
}

break;
case 'text:section': // Section
// $sectionStyleName = $xmlReader->getAttribute('text:style-name', $listItem);
$this->section = $phpWord->addSection();
$children = $node->childNodes;
$this->processNodes($children, $xmlReader, $phpWord);

break;
}
}
}
}

private function getSection(PhpWord $phpWord): Section
{
$section = $this->section;
if ($section === null) {
$section = $this->section = $phpWord->addSection();
}

return $section;
}
}
18 changes: 10 additions & 8 deletions src/PhpWord/Reader/Word2007/Styles.php
Expand Up @@ -39,14 +39,16 @@ public function read(PhpWord $phpWord): void
$fontDefaults = $xmlReader->getElement('w:docDefaults/w:rPrDefault');
if ($fontDefaults !== null) {
$fontDefaultStyle = $this->readFontStyle($xmlReader, $fontDefaults);
if (array_key_exists('name', $fontDefaultStyle)) {
$phpWord->setDefaultFontName($fontDefaultStyle['name']);
}
if (array_key_exists('size', $fontDefaultStyle)) {
$phpWord->setDefaultFontSize($fontDefaultStyle['size']);
}
if (array_key_exists('lang', $fontDefaultStyle)) {
$phpWord->getSettings()->setThemeFontLang(new Language($fontDefaultStyle['lang']));
if ($fontDefaultStyle) {
if (array_key_exists('name', $fontDefaultStyle)) {
$phpWord->setDefaultFontName($fontDefaultStyle['name']);
}
if (array_key_exists('size', $fontDefaultStyle)) {
$phpWord->setDefaultFontSize($fontDefaultStyle['size']);
}
if (array_key_exists('lang', $fontDefaultStyle)) {
$phpWord->getSettings()->setThemeFontLang(new Language($fontDefaultStyle['lang']));
}
}
}

Expand Down
21 changes: 21 additions & 0 deletions src/PhpWord/TemplateProcessor.php
Expand Up @@ -372,6 +372,27 @@ public function setValues(array $values): void
}
}

public function setCheckbox(string $search, bool $checked): void
{
$search = static::ensureMacroCompleted($search);
$blockType = 'w:sdt';

$where = $this->findContainingXmlBlockForMacro($search, $blockType);
if (!is_array($where)) {
return;
}

$block = $this->getSlice($where['start'], $where['end']);

$val = $checked ? '1' : '0';
$block = preg_replace('/(<w14:checked w14:val=)".*?"(\/>)/', '$1"' . $val . '"$2', $block);

$text = $checked ? '☒' : '☐';
$block = preg_replace('/(<w:t>).*?(<\/w:t>)/', '$1' . $text . '$2', $block);

$this->replaceXmlBlock($search, $block, $blockType);
}

/**
* @param string $search
*/
Expand Down

0 comments on commit a6e8940

Please sign in to comment.