Skip to content

Commit d5ca82a

Browse files
committed
Formula : Add Element (& Writer/Reader Word2007/ODText)
1 parent a836c32 commit d5ca82a

33 files changed

+629
-68
lines changed

composer.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,8 @@
6868
"ext-dom": "*",
6969
"ext-json": "*",
7070
"ext-xml": "*",
71-
"laminas/laminas-escaper": ">=2.6"
71+
"laminas/laminas-escaper": ">=2.6",
72+
"phpoffice/math": "dev-master"
7273
},
7374
"require-dev": {
7475
"ext-zip": "*",

composer.lock

Lines changed: 54 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/PhpWord/Element/AbstractContainer.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
namespace PhpOffice\PhpWord\Element;
1919

2020
use BadMethodCallException;
21+
use PhpOffice\Math\Math;
2122
use ReflectionClass;
2223

2324
/**
@@ -47,6 +48,7 @@
4748
* @method Chart addChart(string $type, array $categories, array $values, array $style = null, $seriesName = null)
4849
* @method FormField addFormField(string $type, mixed $fStyle = null, mixed $pStyle = null)
4950
* @method SDT addSDT(string $type)
51+
* @method Formula addFormula(Math $math)
5052
* @method \PhpOffice\PhpWord\Element\OLEObject addObject(string $source, mixed $style = null) deprecated, use addOLEObject instead
5153
*
5254
* @since 0.10.0
@@ -88,6 +90,7 @@ public function __call($function, $args)
8890
'Footnote', 'Endnote', 'CheckBox', 'TextBox', 'Field',
8991
'Line', 'Shape', 'Title', 'TOC', 'PageBreak',
9092
'Chart', 'FormField', 'SDT', 'Comment',
93+
'Formula',
9194
];
9295
$functions = [];
9396
foreach ($elements as $element) {

src/PhpWord/Element/Formula.php

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
<?php
2+
/**
3+
* This file is part of PHPWord - A pure PHP library for reading and writing
4+
* word processing documents.
5+
*
6+
* PHPWord is free software distributed under the terms of the GNU Lesser
7+
* General Public License version 3 as published by the Free Software Foundation.
8+
*
9+
* For the full copyright and license information, please read the LICENSE
10+
* file that was distributed with this source code. For the full list of
11+
* contributors, visit https://github.com/PHPOffice/PHPWord/contributors.
12+
*
13+
* @see https://github.com/PHPOffice/PHPWord
14+
*
15+
* @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
16+
*/
17+
18+
declare(strict_types=1);
19+
20+
namespace PhpOffice\PhpWord\Element;
21+
22+
use PhpOffice\Math\Math;
23+
24+
/**
25+
* Formula element.
26+
*/
27+
class Formula extends AbstractElement
28+
{
29+
/**
30+
* @var Math
31+
*/
32+
protected $math;
33+
34+
/**
35+
* Create a new Formula Element.
36+
*/
37+
public function __construct(Math $math)
38+
{
39+
$this->setMath($math);
40+
}
41+
42+
public function setMath(Math $math): self
43+
{
44+
$this->math = $math;
45+
46+
return $this;
47+
}
48+
49+
public function getMath(): Math
50+
{
51+
return $this->math;
52+
}
53+
}

src/PhpWord/Reader/ODText.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ public function load($docFile)
5959
* @param string $docFile
6060
* @param string $xmlFile
6161
*/
62-
private function readPart(PhpWord $phpWord, $relationships, $partName, $docFile, $xmlFile): void
62+
private function readPart(PhpWord $phpWord, array $relationships, string $partName, string $docFile, string $xmlFile): void
6363
{
6464
$partClass = "PhpOffice\\PhpWord\\Reader\\ODText\\{$partName}";
6565
if (class_exists($partClass)) {
@@ -77,7 +77,7 @@ private function readPart(PhpWord $phpWord, $relationships, $partName, $docFile,
7777
*
7878
* @return array
7979
*/
80-
private function readRelationships($docFile)
80+
private function readRelationships(string $docFile): array
8181
{
8282
$rels = [];
8383
$xmlFile = 'META-INF/manifest.xml';

src/PhpWord/Reader/ODText/Content.php

Lines changed: 42 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
namespace PhpOffice\PhpWord\Reader\ODText;
1919

2020
use DateTime;
21+
use PhpOffice\Math\Reader\MathML;
2122
use PhpOffice\PhpWord\Element\TrackChange;
2223
use PhpOffice\PhpWord\PhpWord;
2324
use PhpOffice\PhpWord\Shared\XMLReader;
@@ -51,37 +52,52 @@ public function read(PhpWord $phpWord): void
5152

5253
break;
5354
case 'text:p': // Paragraph
54-
$children = $node->childNodes;
55-
foreach ($children as $child) {
56-
switch ($child->nodeName) {
57-
case 'text:change-start':
58-
$changeId = $child->getAttribute('text:change-id');
59-
if (isset($trackedChanges[$changeId])) {
60-
$changed = $trackedChanges[$changeId];
61-
}
55+
$element = $xmlReader->getElement('draw:frame/draw:object', $node);
56+
if ($element) {
57+
$mathFile = str_replace('./', '', $element->getAttribute('xlink:href')) . '/content.xml';
6258

63-
break;
64-
case 'text:change-end':
65-
unset($changed);
59+
$xmlReaderObject = new XMLReader();
60+
$mathElement = $xmlReaderObject->getDomFromZip($this->docFile, $mathFile);
6661

67-
break;
68-
case 'text:change':
69-
$changeId = $child->getAttribute('text:change-id');
70-
if (isset($trackedChanges[$changeId])) {
71-
$changed = $trackedChanges[$changeId];
72-
}
62+
$mathXML = $mathElement->saveXML($mathElement);
63+
64+
$reader = new MathML();
65+
$math = $reader->read($mathXML);
66+
67+
$section->addFormula($math);
68+
} else {
69+
$children = $node->childNodes;
70+
foreach ($children as $child) {
71+
switch ($child->nodeName) {
72+
case 'text:change-start':
73+
$changeId = $child->getAttribute('text:change-id');
74+
if (isset($trackedChanges[$changeId])) {
75+
$changed = $trackedChanges[$changeId];
76+
}
77+
78+
break;
79+
case 'text:change-end':
80+
unset($changed);
7381

74-
break;
82+
break;
83+
case 'text:change':
84+
$changeId = $child->getAttribute('text:change-id');
85+
if (isset($trackedChanges[$changeId])) {
86+
$changed = $trackedChanges[$changeId];
87+
}
88+
89+
break;
90+
}
7591
}
76-
}
7792

78-
$element = $section->addText($node->nodeValue);
79-
if (isset($changed) && is_array($changed)) {
80-
$element->setTrackChange($changed['changed']);
81-
if (isset($changed['textNodes'])) {
82-
foreach ($changed['textNodes'] as $changedNode) {
83-
$element = $section->addText($changedNode->nodeValue);
84-
$element->setTrackChange($changed['changed']);
93+
$element = $section->addText($node->nodeValue);
94+
if (isset($changed) && is_array($changed)) {
95+
$element->setTrackChange($changed['changed']);
96+
if (isset($changed['textNodes'])) {
97+
foreach ($changed['textNodes'] as $changedNode) {
98+
$element = $section->addText($changedNode->nodeValue);
99+
$element->setTrackChange($changed['changed']);
100+
}
85101
}
86102
}
87103
}

src/PhpWord/Reader/Word2007/AbstractPart.php

Lines changed: 41 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
use DateTime;
2121
use DOMElement;
2222
use InvalidArgumentException;
23+
use PhpOffice\Math\Reader\OfficeMathML;
2324
use PhpOffice\PhpWord\ComplexType\TblWidth as TblWidthComplexType;
2425
use PhpOffice\PhpWord\Element\AbstractContainer;
2526
use PhpOffice\PhpWord\Element\AbstractElement;
@@ -189,25 +190,7 @@ protected function getCommentReference(string $id): array
189190
protected function readParagraph(XMLReader $xmlReader, DOMElement $domNode, $parent, $docPart = 'document'): void
190191
{
191192
// Paragraph style
192-
$paragraphStyle = null;
193-
$headingDepth = null;
194-
if ($xmlReader->elementExists('w:commentReference', $domNode)
195-
|| $xmlReader->elementExists('w:commentRangeStart', $domNode)
196-
|| $xmlReader->elementExists('w:commentRangeEnd', $domNode)
197-
) {
198-
$nodes = $xmlReader->getElements('w:commentReference|w:commentRangeStart|w:commentRangeEnd', $domNode);
199-
$node = current(iterator_to_array($nodes));
200-
if ($node) {
201-
$attributeIdentifier = $node->attributes->getNamedItem('id');
202-
if ($attributeIdentifier) {
203-
$id = $attributeIdentifier->nodeValue;
204-
}
205-
}
206-
}
207-
if ($xmlReader->elementExists('w:pPr', $domNode)) {
208-
$paragraphStyle = $this->readParagraphStyle($xmlReader, $domNode);
209-
$headingDepth = $this->getHeadingDepth($paragraphStyle);
210-
}
193+
$paragraphStyle = $xmlReader->elementExists('w:pPr', $domNode) ? $this->readParagraphStyle($xmlReader, $domNode) : null;
211194

212195
// PreserveText
213196
if ($xmlReader->elementExists('w:r/w:instrText', $domNode)) {
@@ -234,8 +217,26 @@ protected function readParagraph(XMLReader $xmlReader, DOMElement $domNode, $par
234217
}
235218
}
236219
$parent->addPreserveText(htmlspecialchars($textContent, ENT_QUOTES, 'UTF-8'), $fontStyle, $paragraphStyle);
237-
} elseif ($xmlReader->elementExists('w:pPr/w:numPr', $domNode)) {
238-
// List item
220+
221+
return;
222+
}
223+
224+
// Formula
225+
$xmlReader->registerNamespace('m', 'http://schemas.openxmlformats.org/officeDocument/2006/math');
226+
if ($xmlReader->elementExists('m:oMath', $domNode)) {
227+
$mathElement = $xmlReader->getElement('m:oMath', $domNode);
228+
$mathXML = $mathElement->ownerDocument->saveXML($mathElement);
229+
230+
$reader = new OfficeMathML();
231+
$math = $reader->read($mathXML);
232+
233+
$parent->addFormula($math);
234+
235+
return;
236+
}
237+
238+
// List item
239+
if ($xmlReader->elementExists('w:pPr/w:numPr', $domNode)) {
239240
$numId = $xmlReader->getAttribute('w:val', $domNode, 'w:pPr/w:numPr/w:numId');
240241
$levelId = $xmlReader->getAttribute('w:val', $domNode, 'w:pPr/w:numPr/w:ilvl');
241242
$nodes = $xmlReader->getElements('*', $domNode);
@@ -245,8 +246,13 @@ protected function readParagraph(XMLReader $xmlReader, DOMElement $domNode, $par
245246
foreach ($nodes as $node) {
246247
$this->readRun($xmlReader, $node, $listItemRun, $docPart, $paragraphStyle);
247248
}
248-
} elseif ($headingDepth !== null) {
249-
// Heading or Title
249+
250+
return;
251+
}
252+
253+
// Heading or Title
254+
$headingDepth = $xmlReader->elementExists('w:pPr', $domNode) ? $this->getHeadingDepth($paragraphStyle) : null;
255+
if ($headingDepth !== null) {
250256
$textContent = null;
251257
$nodes = $xmlReader->getElements('w:r|w:hyperlink', $domNode);
252258
if ($nodes->length === 1) {
@@ -258,17 +264,19 @@ protected function readParagraph(XMLReader $xmlReader, DOMElement $domNode, $par
258264
}
259265
}
260266
$parent->addTitle($textContent, $headingDepth);
267+
268+
return;
269+
}
270+
271+
// Text and TextRun
272+
$textRunContainers = $xmlReader->countElements('w:r|w:ins|w:del|w:hyperlink|w:smartTag|w:commentReference|w:commentRangeStart|w:commentRangeEnd', $domNode);
273+
if (0 === $textRunContainers) {
274+
$parent->addTextBreak(null, $paragraphStyle);
261275
} else {
262-
// Text and TextRun
263-
$textRunContainers = $xmlReader->countElements('w:r|w:ins|w:del|w:hyperlink|w:smartTag|w:commentReference|w:commentRangeStart|w:commentRangeEnd', $domNode);
264-
if (0 === $textRunContainers) {
265-
$parent->addTextBreak(null, $paragraphStyle);
266-
} else {
267-
$nodes = $xmlReader->getElements('*', $domNode);
268-
$paragraph = $parent->addTextRun($paragraphStyle);
269-
foreach ($nodes as $node) {
270-
$this->readRun($xmlReader, $node, $paragraph, $docPart, $paragraphStyle);
271-
}
276+
$nodes = $xmlReader->getElements('*', $domNode);
277+
$paragraph = $parent->addTextRun($paragraphStyle);
278+
foreach ($nodes as $node) {
279+
$this->readRun($xmlReader, $node, $paragraph, $docPart, $paragraphStyle);
272280
}
273281
}
274282
}

0 commit comments

Comments
 (0)