Skip to content

Commit 958c8c9

Browse files
authored
Merge branch 'master' into pr1321
2 parents a377f44 + 29c0f21 commit 958c8c9

File tree

8 files changed

+198
-6
lines changed

8 files changed

+198
-6
lines changed

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ and this project adheres to [Semantic Versioning](https://semver.org).
2929

3030
### Fixed
3131

32-
- Nothing yet.
32+
- Better Handling of legacyDrawing Xml. [Issue #4105](https://github.com/PHPOffice/PhpSpreadsheet/issues/4105) [PR #4122](https://github.com/PHPOffice/PhpSpreadsheet/pull/4122)
3333

3434
## 2024-08-07 - 2.2.2
3535

docs/topics/conditional-formatting.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ The Wizards know which operator types match up with condition types, and provide
7171

7272
---
7373

74-
Note that `$conditionalStyles` is an array: it is possible to apply several conditions to the same range of cells. If we also wanted to highlight values that were less than 10 in the the A1:A10 range, we can add a second style rule.
74+
Note that `$conditionalStyles` is an array: it is possible to apply several conditions to the same range of cells. If we also wanted to highlight values that were less than 10 in the A1:A10 range, we can add a second style rule.
7575

7676
In Excel, we would do this by selecting the range again, and going through the same process, this time selecting the "Highlight Cells Rules", then "Less Than" from the "Conditional Styles" menu, entering the value "10" in the prompt box, and selecting the appropriate style.
7777

docs/topics/defined-names.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,7 @@ Even though `CHARGE_RATE` references a cell on a different worksheet, because is
263263

264264
However, a Named Range can be locally scoped so that it is only available when referenced from a specific worksheet, or it can be globally scoped. This means that you can use the same Named Range name with different values on different worksheets.
265265

266-
Building further on our timesheet, perhaps we use a different worksheet for each client, and we use the same hourly rate when billing most of our clients; but for one particular client (perhaps doing work for a a friend) we use a lower rate.
266+
Building further on our timesheet, perhaps we use a different worksheet for each client, and we use the same hourly rate when billing most of our clients; but for one particular client (perhaps doing work for a friend) we use a lower rate.
267267

268268
```php
269269
$clients = [

src/PhpSpreadsheet/Calculation/Web/Service.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ public static function webService(string $url): string
2929
return ExcelError::VALUE(); // Invalid protocol
3030
}
3131

32-
// Get results from the the webservice
32+
// Get results from the webservice
3333
$client = Settings::getHttpClient();
3434
$requestFactory = Settings::getRequestFactory();
3535
$request = $requestFactory->createRequest('GET', $url);

src/PhpSpreadsheet/Reader/Xlsx.php

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1643,7 +1643,9 @@ protected function loadSpreadsheetFromFile(string $filename): Spreadsheet
16431643
foreach ($xmlSheet->legacyDrawing as $drawing) {
16441644
$drawingRelId = (string) self::getArrayItem(self::getAttributes($drawing, $xmlNamespaceBase), 'id');
16451645
if (isset($vmlDrawingContents[$drawingRelId])) {
1646-
$unparsedLoadedData['sheets'][$docSheet->getCodeName()]['legacyDrawing'] = $vmlDrawingContents[$drawingRelId];
1646+
if (self::onlyNoteVml($vmlDrawingContents[$drawingRelId]) === false) {
1647+
$unparsedLoadedData['sheets'][$docSheet->getCodeName()]['legacyDrawing'] = $vmlDrawingContents[$drawingRelId];
1648+
}
16471649
}
16481650
}
16491651
}
@@ -2391,4 +2393,36 @@ private static function storeFormulaAttributes(SimpleXMLElement $f, Worksheet $d
23912393
$docSheet->getCell($r)->setFormulaAttributes($formulaAttributes);
23922394
}
23932395
}
2396+
2397+
private static function onlyNoteVml(string $data): bool
2398+
{
2399+
$data = str_replace('<br>', '<br/>', $data);
2400+
2401+
try {
2402+
$sxml = @simplexml_load_string($data);
2403+
} catch (Throwable) {
2404+
$sxml = false;
2405+
}
2406+
2407+
if ($sxml === false) {
2408+
return false;
2409+
}
2410+
$shapes = $sxml->children(Namespaces::URN_VML);
2411+
foreach ($shapes->shape as $shape) {
2412+
$clientData = $shape->children(Namespaces::URN_EXCEL);
2413+
if (!isset($clientData->ClientData)) {
2414+
return false;
2415+
}
2416+
$attrs = $clientData->ClientData->attributes();
2417+
if (!isset($attrs['ObjectType'])) {
2418+
return false;
2419+
}
2420+
$objectType = (string) $attrs['ObjectType'];
2421+
if ($objectType !== 'Note') {
2422+
return false;
2423+
}
2424+
}
2425+
2426+
return true;
2427+
}
23942428
}

src/PhpSpreadsheet/Spreadsheet.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1581,4 +1581,14 @@ public function getExcelCalendar(): int
15811581
{
15821582
return $this->excelCalendar;
15831583
}
1584+
1585+
public function deleteLegacyDrawing(Worksheet $worksheet): void
1586+
{
1587+
unset($this->unparsedLoadedData['sheets'][$worksheet->getCodeName()]['legacyDrawing']);
1588+
}
1589+
1590+
public function getLegacyDrawing(Worksheet $worksheet): ?string
1591+
{
1592+
return $this->unparsedLoadedData['sheets'][$worksheet->getCodeName()]['legacyDrawing'] ?? null;
1593+
}
15841594
}
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PhpOffice\PhpSpreadsheetTests\Reader\Xlsx;
6+
7+
use PhpOffice\PhpSpreadsheet\Reader\Xlsx as XlsxReader;
8+
use PhpOffice\PhpSpreadsheet\Shared\File;
9+
use PhpOffice\PhpSpreadsheet\Spreadsheet;
10+
use PhpOffice\PhpSpreadsheet\Writer\Xlsx as XlsxWriter;
11+
use PHPUnit\Framework\TestCase;
12+
13+
class VmlTest extends TestCase
14+
{
15+
private string $outfile1 = '';
16+
17+
private string $outfile2 = '';
18+
19+
protected function tearDown(): void
20+
{
21+
if ($this->outfile1 !== '') {
22+
unlink($this->outfile1);
23+
$this->outfile1 = '';
24+
}
25+
if ($this->outfile2 !== '') {
26+
unlink($this->outfile2);
27+
$this->outfile2 = '';
28+
}
29+
}
30+
31+
public function testAddComments(): void
32+
{
33+
$spreadsheet = new Spreadsheet();
34+
$sheet = $spreadsheet->getActiveSheet();
35+
$sheet->getComment('A1')->getText()->createText('top left cell');
36+
$writer = new XlsxWriter($spreadsheet);
37+
$this->outfile1 = File::temporaryFileName();
38+
$writer->save($this->outfile1);
39+
$spreadsheet->disconnectWorksheets();
40+
41+
$reader = new XlsxReader();
42+
$file = 'zip://' . $this->outfile1 . '#xl/worksheets/sheet1.xml';
43+
$sheetContents = file_get_contents($file) ?: '';
44+
self::assertStringContainsString('<legacyDrawing ', $sheetContents);
45+
$file = 'zip://' . $this->outfile1 . '#xl/drawings/vmlDrawing1.vml';
46+
$vmlContents = file_get_contents($file) ?: '';
47+
$count = substr_count($vmlContents, '<v:shape ');
48+
self::assertSame(1, $count);
49+
$count = substr_count($vmlContents, '<x:ClientData ObjectType="Note">');
50+
self::assertSame(1, $count);
51+
52+
$spreadsheet2 = $reader->load($this->outfile1);
53+
$sheet2 = $spreadsheet2->getActiveSheet();
54+
self::assertSame('top left cell', $sheet2->getComment('A1')->getText()->getPlainText());
55+
self::assertNull($spreadsheet2->getLegacyDrawing($sheet2));
56+
$sheet2->getComment('H1')->getText()->createText('Show me');
57+
$sheet2->getComment('H2')->getText()->createText('Hide me');
58+
$sheet2->getComment('H1')->setVisible(true);
59+
$writer = new XlsxWriter($spreadsheet2);
60+
$this->outfile2 = File::temporaryFileName();
61+
$writer->save($this->outfile2);
62+
$spreadsheet2->disconnectWorksheets();
63+
64+
$file = 'zip://' . $this->outfile2 . '#xl/worksheets/sheet1.xml';
65+
$sheetContents = file_get_contents($file) ?: '';
66+
self::assertStringContainsString('<legacyDrawing ', $sheetContents);
67+
$file = 'zip://' . $this->outfile2 . '#xl/drawings/vmlDrawing1.vml';
68+
$vmlContents = file_get_contents($file) ?: '';
69+
$count = substr_count($vmlContents, '<v:shape ');
70+
self::assertSame(3, $count);
71+
$count = substr_count($vmlContents, '<x:ClientData ObjectType="Note">');
72+
self::assertSame(3, $count);
73+
74+
$reader = new XlsxReader();
75+
$spreadsheet3 = $reader->load($this->outfile2);
76+
$sheet3 = $spreadsheet3->getActiveSheet();
77+
self::assertSame('top left cell', $sheet3->getComment('A1')->getText()->getPlainText());
78+
self::assertSame('Show me', $sheet3->getComment('H1')->getText()->getPlainText());
79+
self::assertSame('Hide me', $sheet3->getComment('H2')->getText()->getPlainText());
80+
self::assertNull($spreadsheet3->getLegacyDrawing($sheet3));
81+
self::assertTrue($sheet3->getComment('H1')->getVisible());
82+
self::assertFalse($sheet3->getComment('H2')->getVisible());
83+
$spreadsheet3->disconnectWorksheets();
84+
}
85+
86+
public function testDeleteNullLegacy(): void
87+
{
88+
$spreadsheet = new Spreadsheet();
89+
$sheet = $spreadsheet->getActiveSheet();
90+
$sheet->getComment('A1')->getText()->createText('top left cell');
91+
self::assertNull($spreadsheet->getLegacyDrawing($sheet));
92+
$spreadsheet->deleteLegacyDrawing($sheet);
93+
$writer = new XlsxWriter($spreadsheet);
94+
$this->outfile1 = File::temporaryFileName();
95+
$writer->save($this->outfile1);
96+
$spreadsheet->disconnectWorksheets();
97+
98+
$reader = new XlsxReader();
99+
$file = 'zip://' . $this->outfile1 . '#xl/worksheets/sheet1.xml';
100+
$sheetContents = file_get_contents($file) ?: '';
101+
self::assertStringContainsString('<legacyDrawing ', $sheetContents);
102+
$file = 'zip://' . $this->outfile1 . '#xl/drawings/vmlDrawing1.vml';
103+
$vmlContents = file_get_contents($file) ?: '';
104+
$count = substr_count($vmlContents, '<v:shape ');
105+
self::assertSame(1, $count);
106+
$count = substr_count($vmlContents, '<x:ClientData ObjectType="Note">');
107+
self::assertSame(1, $count);
108+
109+
$spreadsheet2 = $reader->load($this->outfile1);
110+
$sheet2 = $spreadsheet2->getActiveSheet();
111+
self::assertSame('top left cell', $sheet2->getComment('A1')->getText()->getPlainText());
112+
$spreadsheet2->disconnectWorksheets();
113+
}
114+
115+
public function testAddCommentDeleteFormControls(): void
116+
{
117+
$infile = 'samples/Reader2/sampleData/formscomments.xlsx';
118+
$reader = new XlsxReader();
119+
$reader->setLoadSheetsOnly('FormsComments');
120+
$spreadsheet = $reader->load($infile);
121+
self::assertTrue(true);
122+
$sheet = $spreadsheet->getActiveSheet();
123+
self::assertSame('row1', $sheet->getCell('H1')->getValue());
124+
self::assertStringContainsString('Hello', $sheet->getComment('F1')->getText()->getPlainText());
125+
$vmlContents = $spreadsheet->getLegacyDrawing($sheet) ?? '';
126+
$count = substr_count($vmlContents, '<v:shape ');
127+
self::assertSame(4, $count);
128+
$count = substr_count($vmlContents, '<x:ClientData ');
129+
self::assertSame(4, $count);
130+
$count = substr_count($vmlContents, '<x:ClientData ObjectType="Note"');
131+
self::assertSame(1, $count);
132+
$spreadsheet->deleteLegacyDrawing($sheet);
133+
$sheet->getComment('F2')->getText()->createText('Goodbye');
134+
$writer = new XlsxWriter($spreadsheet);
135+
$this->outfile1 = File::temporaryFileName();
136+
$writer->save($this->outfile1);
137+
$spreadsheet->disconnectWorksheets();
138+
139+
$reader2 = new XlsxReader();
140+
$spreadsheet2 = $reader2->load($this->outfile1);
141+
$sheet2 = $spreadsheet2->getActiveSheet();
142+
self::assertNull($spreadsheet2->getLegacyDrawing($sheet2));
143+
self::assertSame('row1', $sheet2->getCell('H1')->getValue());
144+
self::assertStringContainsString('Hello', $sheet2->getComment('F1')->getText()->getPlainText());
145+
self::assertStringContainsString('Goodbye', $sheet2->getComment('F2')->getText()->getPlainText());
146+
$spreadsheet2->disconnectWorksheets();
147+
}
148+
}

tests/PhpSpreadsheetTests/Reader/Xml/DataValidationsTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ private function validationAssertions(): array
9292
'Float between 2 and 5: 7' => [false, 'G1', 7],
9393
'Integer not between -5 and 5: 3' => [false, 'F2', 3],
9494
'Integer not between -5 and 5: -1' => [false, 'F2', -1],
95-
'Integer not not between -5 and 5: 7' => [true, 'F2', 7],
95+
'Integer not between -5 and 5: 7' => [true, 'F2', 7],
9696
'Any integer except 7: -1' => [true, 'F3', -1],
9797
'Any integer except 7: 7' => [false, 'F3', 7],
9898
'Only -3: -1' => [false, 'F4', -1],

0 commit comments

Comments
 (0)