Skip to content

Commit f82eb47

Browse files
committed
Eliminate JSON Dependency
JSON is used in exactly 2 places in the code and should not be used in either. XMLWriter uses it to cast a float to string. Aside from being silly, this actually causes a problem in Shared/HtmlTest, where one of the results should be 4000 but is instead tested for 3999.9999... The error should have been immaterial (a simple cast will give the correct answer), but the test was wrong, insisting on an exact match for a floating point answer. It should use assertEqualsWithDelta. Next, the reason why JSON got it "wrong" was because the conversions in Shared/Converter use the wrong order of operations - multiplications should be performed before divisions to help avoid rounding problems, but Converter was doing the divisions first. All the conversions there are changed to multiply before dividing. Finally, Shared/Html checks for width units of points or pixels, but should check for inches and centimers as well. Writer/Html outputs tracking properties as a JSON string for some unfathomable reason. It is changed to output each of the three (author, id, and date) as its own discrete property. Tests didn't actually confirm the value of the properties; they do now. Oh, yes, htmlspecialchars is needed for author and id, otherwise the html may wind up broken. Some simple changes are made to TestHelperDOCX to avoid intermittent problems on Windows, and to make the code a little cleaner.
1 parent 5271a02 commit f82eb47

File tree

9 files changed

+86
-35
lines changed

9 files changed

+86
-35
lines changed

.gitignore

+2-1
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,5 @@ phpword.ini
2424
/nbproject
2525
/.php_cs.cache
2626
/.phpunit.result.cache
27-
/public
27+
/.phpunit.cache
28+
/public

composer.json

-1
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,6 @@
109109
"php": "^7.1|^8.0",
110110
"ext-dom": "*",
111111
"ext-gd": "*",
112-
"ext-json": "*",
113112
"ext-xml": "*",
114113
"ext-zip": "*",
115114
"phpoffice/math": "^0.2"

src/PhpWord/Shared/Converter.php

+12-12
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ class Converter
4040
*/
4141
public static function cmToTwip($centimeter = 1)
4242
{
43-
return $centimeter / self::INCH_TO_CM * self::INCH_TO_TWIP;
43+
return $centimeter * self::INCH_TO_TWIP / self::INCH_TO_CM;
4444
}
4545

4646
/**
@@ -64,7 +64,7 @@ public static function cmToInch($centimeter = 1)
6464
*/
6565
public static function cmToPixel($centimeter = 1)
6666
{
67-
return $centimeter / self::INCH_TO_CM * self::INCH_TO_PIXEL;
67+
return $centimeter * self::INCH_TO_PIXEL / self::INCH_TO_CM;
6868
}
6969

7070
/**
@@ -76,7 +76,7 @@ public static function cmToPixel($centimeter = 1)
7676
*/
7777
public static function cmToPoint($centimeter = 1)
7878
{
79-
return $centimeter / self::INCH_TO_CM * self::INCH_TO_POINT;
79+
return $centimeter * self::INCH_TO_POINT / self::INCH_TO_CM;
8080
}
8181

8282
/**
@@ -88,7 +88,7 @@ public static function cmToPoint($centimeter = 1)
8888
*/
8989
public static function cmToEmu($centimeter = 1)
9090
{
91-
return round($centimeter / self::INCH_TO_CM * self::INCH_TO_PIXEL * self::PIXEL_TO_EMU);
91+
return round($centimeter * self::INCH_TO_PIXEL * self::PIXEL_TO_EMU / self::INCH_TO_CM);
9292
}
9393

9494
/**
@@ -160,7 +160,7 @@ public static function inchToEmu($inch = 1)
160160
*/
161161
public static function pixelToTwip($pixel = 1)
162162
{
163-
return $pixel / self::INCH_TO_PIXEL * self::INCH_TO_TWIP;
163+
return $pixel * self::INCH_TO_TWIP / self::INCH_TO_PIXEL;
164164
}
165165

166166
/**
@@ -172,7 +172,7 @@ public static function pixelToTwip($pixel = 1)
172172
*/
173173
public static function pixelToCm($pixel = 1)
174174
{
175-
return $pixel / self::INCH_TO_PIXEL * self::INCH_TO_CM;
175+
return $pixel * self::INCH_TO_CM / self::INCH_TO_PIXEL;
176176
}
177177

178178
/**
@@ -184,7 +184,7 @@ public static function pixelToCm($pixel = 1)
184184
*/
185185
public static function pixelToPoint($pixel = 1)
186186
{
187-
return $pixel / self::INCH_TO_PIXEL * self::INCH_TO_POINT;
187+
return $pixel * self::INCH_TO_POINT / self::INCH_TO_PIXEL;
188188
}
189189

190190
/**
@@ -208,7 +208,7 @@ public static function pixelToEmu($pixel = 1)
208208
*/
209209
public static function pointToTwip($point = 1)
210210
{
211-
return $point / self::INCH_TO_POINT * self::INCH_TO_TWIP;
211+
return $point * self::INCH_TO_TWIP / self::INCH_TO_POINT;
212212
}
213213

214214
/**
@@ -220,7 +220,7 @@ public static function pointToTwip($point = 1)
220220
*/
221221
public static function pointToPixel($point = 1)
222222
{
223-
return $point / self::INCH_TO_POINT * self::INCH_TO_PIXEL;
223+
return $point * self::INCH_TO_PIXEL / self::INCH_TO_POINT;
224224
}
225225

226226
/**
@@ -232,7 +232,7 @@ public static function pointToPixel($point = 1)
232232
*/
233233
public static function pointToEmu($point = 1)
234234
{
235-
return round($point / self::INCH_TO_POINT * self::INCH_TO_PIXEL * self::PIXEL_TO_EMU);
235+
return round($point * self::INCH_TO_PIXEL * self::PIXEL_TO_EMU / self::INCH_TO_POINT);
236236
}
237237

238238
/**
@@ -244,7 +244,7 @@ public static function pointToEmu($point = 1)
244244
*/
245245
public static function pointToCm($point = 1)
246246
{
247-
return $point / self::INCH_TO_POINT * self::INCH_TO_CM;
247+
return $point * self::INCH_TO_CM / self::INCH_TO_POINT;
248248
}
249249

250250
/**
@@ -268,7 +268,7 @@ public static function emuToPixel($emu = 1)
268268
*/
269269
public static function picaToPoint($pica = 1)
270270
{
271-
return $pica / self::INCH_TO_PICA * self::INCH_TO_POINT;
271+
return $pica * self::INCH_TO_POINT / self::INCH_TO_PICA;
272272
}
273273

274274
/**

src/PhpWord/Shared/Html.php

+6
Original file line numberDiff line numberDiff line change
@@ -1477,6 +1477,12 @@ protected static function convertHtmlSize(string $size): float
14771477
if (false !== strpos($size, 'px')) {
14781478
return (float) str_replace('px', '', $size);
14791479
}
1480+
if (false !== strpos($size, 'cm')) {
1481+
return Converter::cmToPixel((float) str_replace('cm', '', $size));
1482+
}
1483+
if (false !== strpos($size, 'in')) {
1484+
return Converter::inchToPixel((float) str_replace('in', '', $size));
1485+
}
14801486

14811487
return (float) $size;
14821488
}

src/PhpWord/Shared/XMLWriter.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ public function writeAttributeIf($condition, $attribute, $value): void
180180
public function writeAttribute($name, $value)
181181
{
182182
if (is_float($value)) {
183-
$value = json_encode($value);
183+
$value = (string) $value;
184184
}
185185

186186
return parent::writeAttribute($name, $value ?? '');

src/PhpWord/Writer/HTML/Element/Text.php

+13-11
Original file line numberDiff line numberDiff line change
@@ -163,20 +163,22 @@ private function writeTrackChangeOpening()
163163

164164
$content = '';
165165
if (($changed->getChangeType() == TrackChange::INSERTED)) {
166-
$content .= '<ins data-phpword-prop=\'';
166+
$content .= '<ins';
167167
} elseif ($changed->getChangeType() == TrackChange::DELETED) {
168-
$content .= '<del data-phpword-prop=\'';
168+
$content .= '<del';
169169
}
170-
171-
$changedProp = ['changed' => ['author' => $changed->getAuthor(), 'id' => $this->element->getElementId()]];
172-
if ($changed->getDate() != null) {
173-
$changedProp['changed']['date'] = $changed->getDate()->format('Y-m-d\TH:i:s\Z');
170+
$author = htmlspecialchars($changed->getAuthor(), ENT_QUOTES);
171+
$content .= " data-phpword-chg-author='$author'";
172+
$elementId = htmlspecialchars($this->element->getElementId(), ENT_QUOTES);
173+
$content .= " data-phpword-chg-id='$elementId'";
174+
$date = $changed->getDate();
175+
if ($date !== null) {
176+
$dateout = $date->format('Y-m-d\TH:i:s\Z');
177+
$content .= " data-phpword-chg-timestamp='$dateout'";
174178
}
175-
$content .= json_encode($changedProp);
176-
$content .= '\' ';
177-
$content .= 'title="' . $changed->getAuthor();
178-
if ($changed->getDate() != null) {
179-
$dateUser = $changed->getDate()->format('Y-m-d H:i:s');
179+
$content .= ' title="' . $author;
180+
if ($date !== null) {
181+
$dateUser = $date->format('Y-m-d H:i:s');
180182
$content .= ' - ' . $dateUser;
181183
}
182184
$content .= '">';

tests/PhpWordTests/Shared/HtmlTest.php

+5-2
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,8 @@ public function testParseWidth(string $htmlSize, float $docxSize, string $docxUn
256256
$doc = TestHelperDOCX::getDocument($phpWord, 'Word2007');
257257
$xpath = '/w:document/w:body/w:tbl/w:tblPr/w:tblW';
258258
self::assertTrue($doc->elementExists($xpath));
259-
self::assertEquals($docxSize, $doc->getElement($xpath)->getAttribute('w:w'));
259+
$actual = (float) $doc->getElement($xpath)->getAttribute('w:w');
260+
self::assertEqualsWithDelta($docxSize, $actual, 1.0e-12);
260261
self::assertEquals($docxUnit, $doc->getElement($xpath)->getAttribute('w:type'));
261262
}
262263

@@ -1371,9 +1372,11 @@ public static function providerParseWidth(): array
13711372
return [
13721373
['auto', 5000, TblWidth::PERCENT],
13731374
['100%', 5000, TblWidth::PERCENT],
1374-
['200pt', 3999.999999999999, TblWidth::TWIP],
1375+
['200pt', 4000, TblWidth::TWIP],
13751376
['300px', 4500, TblWidth::TWIP],
13761377
['400', 6000, TblWidth::TWIP],
1378+
['2in', 2880, TblWidth::TWIP],
1379+
['2.54cm', 1440, TblWidth::TWIP],
13771380
];
13781381
}
13791382

tests/PhpWordTests/TestHelperDOCX.php

+18-6
Original file line numberDiff line numberDiff line change
@@ -47,13 +47,14 @@ class TestHelperDOCX
4747
*/
4848
public static function getDocument(PhpWord $phpWord, $writerName = 'Word2007')
4949
{
50+
$tempdir = self::getTempDirPhpunit();
5051
self::$file = tempnam(Settings::getTempDir(), 'PhpWord');
5152
if (false === self::$file) {
5253
throw new CreateTemporaryFileException();
5354
}
5455

55-
if (!is_dir(Settings::getTempDir() . '/PhpWord_Unit_Test/')) {
56-
mkdir(Settings::getTempDir() . '/PhpWord_Unit_Test/');
56+
if (!is_dir($tempdir)) {
57+
mkdir($tempdir);
5758
}
5859

5960
$xmlWriter = IOFactory::createWriter($phpWord, $writerName);
@@ -62,11 +63,11 @@ public static function getDocument(PhpWord $phpWord, $writerName = 'Word2007')
6263
$zip = new ZipArchive();
6364
$res = $zip->open(self::$file);
6465
if (true === $res) {
65-
$zip->extractTo(Settings::getTempDir() . '/PhpWord_Unit_Test/');
66+
$zip->extractTo($tempdir);
6667
$zip->close();
6768
}
6869

69-
$doc = new XmlDocument(Settings::getTempDir() . '/PhpWord_Unit_Test/');
70+
$doc = new XmlDocument($tempdir);
7071
if ($writerName === 'ODText') {
7172
$doc->setDefaultFile('content.xml');
7273
}
@@ -83,8 +84,9 @@ public static function clear(): void
8384
unlink(self::$file);
8485
self::$file = '';
8586
}
86-
if (is_dir(Settings::getTempDir() . '/PhpWord_Unit_Test/')) {
87-
self::deleteDir(Settings::getTempDir() . '/PhpWord_Unit_Test/');
87+
$tempdir = self::getTempDirPhpunit();
88+
if (is_dir($tempdir)) {
89+
self::deleteDir($tempdir);
8890
}
8991
}
9092

@@ -117,4 +119,14 @@ public static function getFile()
117119
{
118120
return self::$file;
119121
}
122+
123+
/**
124+
* Get temporary directory for PhpUnit.
125+
*
126+
* @return string
127+
*/
128+
private static function getTempDirPhpunit()
129+
{
130+
return Settings::getTempDir() . '/PhpWord_Unit_Test';
131+
}
120132
}

tests/PhpWordTests/Writer/HTML/ElementTest.php

+29-1
Original file line numberDiff line numberDiff line change
@@ -72,13 +72,41 @@ public function testWriteTrackChanges(): void
7272
$text = $section->addText('my dummy text');
7373
$text->setChangeInfo(TrackChange::INSERTED, 'author name');
7474
$text2 = $section->addText('my other text');
75-
$text2->setTrackChange(new TrackChange(TrackChange::DELETED, 'another author', new DateTime()));
75+
$deleteTime = new DateTime();
76+
$deleteAuthor = "Spec O'char";
77+
$deleteTrack = new TrackChange(TrackChange::DELETED, $deleteAuthor, $deleteTime);
78+
$text2->setTrackChange($deleteTrack);
7679

7780
$dom = Helper::getAsHTML($phpWord);
7881
$xpath = new DOMXPath($dom);
7982

8083
self::assertEquals(1, $xpath->query('/html/body/div/p[1]/ins')->length);
8184
self::assertEquals(1, $xpath->query('/html/body/div/p[2]/del')->length);
85+
$node = $xpath->query('/html/body/div/p[2]/del');
86+
self::assertNotFalse($node);
87+
$allAttributes = $node[0]->attributes;
88+
self::assertCount(4, $allAttributes);
89+
$node = $xpath->query('/html/body/div/p[2]/del');
90+
self::assertNotFalse($node);
91+
92+
$attributes = $node[0]->attributes[0];
93+
self::assertSame('data-phpword-chg-author', $attributes->name);
94+
self::assertSame($deleteAuthor, $attributes->value);
95+
96+
$text2Id = $text2->getElementId();
97+
$attributes = $node[0]->attributes[1];
98+
self::assertSame('data-phpword-chg-id', $attributes->name);
99+
self::assertSame($text2Id, $attributes->value);
100+
101+
$attributes = $node[0]->attributes[2];
102+
self::assertSame('data-phpword-chg-timestamp', $attributes->name);
103+
self::assertSame($deleteTime->format('Y-m-d\TH:i:s\Z'), $attributes->value);
104+
105+
$attributes = $node[0]->attributes[3];
106+
self::assertSame('title', $attributes->name);
107+
$expected = $deleteAuthor . ' - '
108+
. $deleteTime->format('Y-m-d H:i:s');
109+
self::assertSame($expected, $attributes->value);
82110
}
83111

84112
/**

0 commit comments

Comments
 (0)