Skip to content

Commit d2435cd

Browse files
author
MarkBaker
committed
Ensure that a basic metadata file containing a cell metadata definition is written and linked; and then tag array formula cells with the cm tag to link them to that cell metadata definition
1 parent 5d258f1 commit d2435cd

File tree

6 files changed

+215
-3
lines changed

6 files changed

+215
-3
lines changed

src/PhpSpreadsheet/Cell/Cell.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -327,10 +327,10 @@ public function getCalculatedValue($resetLog = true)
327327
// as well as updating the spillage cells,
328328
// so we need to restore this cell to its formula value, attributes, and datatype
329329
$worksheet->getCell($coordinate);
330-
$this->updateInCollection();
331330
$this->value = $value;
332331
$this->dataType = $datatype;
333332
$this->formulaAttributes = $formulaAttributes;
333+
$this->updateInCollection();
334334
}
335335

336336
// Now we just extract the top-left value from the array to get the result for this specific cell

src/PhpSpreadsheet/Writer/Xlsx.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
use PhpOffice\PhpSpreadsheet\Writer\Xlsx\ContentTypes;
2121
use PhpOffice\PhpSpreadsheet\Writer\Xlsx\DocProps;
2222
use PhpOffice\PhpSpreadsheet\Writer\Xlsx\Drawing;
23+
use PhpOffice\PhpSpreadsheet\Writer\Xlsx\Metadata;
2324
use PhpOffice\PhpSpreadsheet\Writer\Xlsx\Rels;
2425
use PhpOffice\PhpSpreadsheet\Writer\Xlsx\RelsRibbon;
2526
use PhpOffice\PhpSpreadsheet\Writer\Xlsx\RelsVBA;
@@ -157,6 +158,11 @@ class Xlsx extends BaseWriter
157158
*/
158159
private $writerPartStringTable;
159160

161+
/**
162+
* @var Metadata
163+
*/
164+
private $writerPartMetadata;
165+
160166
/**
161167
* @var Style
162168
*/
@@ -193,6 +199,7 @@ public function __construct(Spreadsheet $spreadsheet)
193199
$this->writerPartRels = new Rels($this);
194200
$this->writerPartRelsRibbon = new RelsRibbon($this);
195201
$this->writerPartRelsVBA = new RelsVBA($this);
202+
$this->writerPartMetadata = new Metadata($this);
196203
$this->writerPartStringTable = new StringTable($this);
197204
$this->writerPartStyle = new Style($this);
198205
$this->writerPartTheme = new Theme($this);
@@ -256,6 +263,11 @@ public function getWriterPartRelsVBA(): RelsVBA
256263
return $this->writerPartRelsVBA;
257264
}
258265

266+
public function getWriterPartMetadata(): Metadata
267+
{
268+
return $this->writerPartMetadata;
269+
}
270+
259271
public function getWriterPartStringTable(): StringTable
260272
{
261273
return $this->writerPartStringTable;
@@ -364,6 +376,9 @@ public function save($filename, int $flags = 0): void
364376
// Add theme to ZIP file
365377
$zipContent['xl/theme/theme1.xml'] = $this->getWriterPartTheme()->writeTheme($this->spreadSheet);
366378

379+
// Add metadata to ZIP file
380+
$zipContent['xl/metadata.xml'] = $this->getWriterPartMetadata()->writeMetadata();
381+
367382
// Add string table to ZIP file
368383
$zipContent['xl/sharedStrings.xml'] = $this->getWriterPartStringTable()->writeStringTable($this->stringTable);
369384

src/PhpSpreadsheet/Writer/Xlsx/ContentTypes.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,9 @@ public function writeContentTypes(Spreadsheet $spreadsheet, $includeCharts = fal
8282
$this->writeOverrideContentType($objWriter, '/xl/worksheets/sheet' . ($i + 1) . '.xml', 'application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml');
8383
}
8484

85+
// Shared strings
86+
$this->writeOverrideContentType($objWriter, '/xl/metadata.xml', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheetMetadata+xml');
87+
8588
// Shared strings
8689
$this->writeOverrideContentType($objWriter, '/xl/sharedStrings.xml', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml');
8790

Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
<?php
2+
3+
namespace PhpOffice\PhpSpreadsheet\Writer\Xlsx;
4+
5+
use PhpOffice\PhpSpreadsheet\Shared\XMLWriter;
6+
7+
class Metadata extends WriterPart
8+
{
9+
protected const METADATA_TYPES = [
10+
[
11+
'cellMeta' => '1',
12+
'coerce' => '1',
13+
'assign' => '1',
14+
'clearComments' => '1',
15+
'clearFormats' => '1',
16+
'rowColShift' => '1',
17+
'splitFirst' => '1',
18+
'merge' => '1',
19+
'pasteValues' => '1',
20+
'pasteAll' => '1',
21+
'copy' => '1',
22+
'minSupportedVersion' => '120000',
23+
'name' => 'XLDAPR',
24+
],
25+
[
26+
'coerce' => '1',
27+
'assign' => '1',
28+
'clearComments' => '1',
29+
'clearFormats' => '1',
30+
'rowColShift' => '1',
31+
'splitFirst' => '1',
32+
'merge' => '1',
33+
'pasteValues' => '1',
34+
'pasteAll' => '1',
35+
'copy' => '1',
36+
'minSupportedVersion' => '120000',
37+
'name' => 'XLRICHVALUE',
38+
],
39+
];
40+
41+
protected const FUTURE_METADATA = [
42+
'XLDAPR' => [
43+
[
44+
'uri' => '{bdbb8cdc-fa1e-496e-a857-3c3f30c029c3}',
45+
'xda:dynamicArrayProperties' => [
46+
'fCollapsed' => '0',
47+
'fDynamic' => '1',
48+
],
49+
],
50+
],
51+
'XLRICHVALUE' => [
52+
[
53+
'uri' => '{3e2802c4-a4d2-4d8b-9148-e3be6c30e623}',
54+
'xlrd:rvb' => [
55+
'i' => '0',
56+
],
57+
],
58+
],
59+
];
60+
61+
protected const EXTRA_METADATA = [
62+
'cellMetadata' => [
63+
[
64+
'v' => '0',
65+
't' => '1',
66+
],
67+
],
68+
'valueMetadata' => [
69+
[
70+
'v' => '0',
71+
't' => '2',
72+
],
73+
],
74+
];
75+
76+
/**
77+
* Write metadata to XML format.
78+
*
79+
* @return string XML Output
80+
*/
81+
public function writeMetadata()
82+
{
83+
// Create XML writer
84+
$objWriter = null;
85+
if ($this->getParentWriter()->getUseDiskCaching()) {
86+
$objWriter = new XMLWriter(XMLWriter::STORAGE_DISK, $this->getParentWriter()->getDiskCachingDirectory());
87+
} else {
88+
$objWriter = new XMLWriter(XMLWriter::STORAGE_MEMORY);
89+
}
90+
91+
// XML header
92+
$objWriter->startDocument('1.0', 'UTF-8', 'yes');
93+
94+
$objWriter->startElement('metadata');
95+
$objWriter->writeAttribute('xmlns:xda', 'http://schemas.microsoft.com/office/spreadsheetml/2017/dynamicarray');
96+
$objWriter->writeAttribute('xmlns:xlrd', 'http://schemas.microsoft.com/office/spreadsheetml/2017/richdata');
97+
$objWriter->writeAttribute('xmlns', 'http://schemas.openxmlformats.org/spreadsheetml/2006/main');
98+
99+
$this->writeMetadataTypes($objWriter);
100+
$this->writeFutureMetadata($objWriter);
101+
$this->writeExtraMetadata($objWriter);
102+
103+
$objWriter->endElement(); // metadata
104+
105+
return $objWriter->getData();
106+
}
107+
108+
private function writeMetadataTypes(XMLWriter $objWriter): void
109+
{
110+
$objWriter->startElement('metadataTypes');
111+
$objWriter->writeAttribute('count', (string) count(self::METADATA_TYPES));
112+
113+
foreach (self::METADATA_TYPES as $metadataType) {
114+
$objWriter->startElement('metadataType');
115+
116+
foreach ($metadataType as $metadataTypeKey => $metadataTypeValue) {
117+
$objWriter->writeAttribute($metadataTypeKey, $metadataTypeValue);
118+
}
119+
120+
$objWriter->endElement(); //metadataType
121+
}
122+
123+
$objWriter->endElement(); //metadataTypes
124+
}
125+
126+
private function writeFutureMetadata(XMLWriter $objWriter): void
127+
{
128+
foreach (self::FUTURE_METADATA as $name => $futureMetadata) {
129+
$objWriter->startElement('futureMetadata');
130+
$objWriter->writeAttribute('count', (string) count($futureMetadata));
131+
$objWriter->writeAttribute('name', $name);
132+
133+
foreach ($futureMetadata as $futureMetadatum) {
134+
$objWriter->startElement('bk');
135+
$objWriter->startElement('extLst');
136+
137+
$ext = array_shift($futureMetadatum);
138+
$objWriter->startElement('ext');
139+
$objWriter->writeAttribute('uri', $ext);
140+
141+
foreach ($futureMetadatum as $extElementName => $extElementProperties) {
142+
$objWriter->startElement($extElementName);
143+
144+
foreach ($extElementProperties as $extElementPropertyName => $extElementPropertyValue) {
145+
$objWriter->writeAttribute($extElementPropertyName, $extElementPropertyValue);
146+
}
147+
148+
$objWriter->endElement(); // ext
149+
}
150+
151+
$objWriter->endElement(); // ext
152+
$objWriter->endElement(); // extLst
153+
$objWriter->endElement(); // bk
154+
}
155+
156+
$objWriter->endElement(); // futureMetadata
157+
}
158+
}
159+
160+
private function writeExtraMetadata(XMLWriter $objWriter): void
161+
{
162+
foreach (self::EXTRA_METADATA as $name => $metadata) {
163+
$objWriter->startElement($name);
164+
$objWriter->writeAttribute('count', (string) count($metadata));
165+
166+
foreach ($metadata as $metadatum) {
167+
$objWriter->startElement('bk');
168+
$objWriter->startElement('rc');
169+
170+
foreach ($metadatum as $attributeName => $attrbuteValue) {
171+
$objWriter->writeAttribute($attributeName, $attrbuteValue);
172+
}
173+
174+
$objWriter->endElement(); // rc
175+
$objWriter->endElement(); // bk
176+
}
177+
178+
$objWriter->endElement();
179+
}
180+
}
181+
}

src/PhpSpreadsheet/Writer/Xlsx/Rels.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,17 @@ public function writeWorkbookRelationships(Spreadsheet $spreadsheet)
137137
'worksheets/sheet' . ($i + 1) . '.xml'
138138
);
139139
}
140+
141+
// Relationship sharedStrings.xml
142+
// id : just after the last sheet
143+
$this->writeRelationship(
144+
$objWriter,
145+
($i + 1 + 3),
146+
'http://schemas.openxmlformats.org/officeDocument/2006/relationships/sheetMetadata',
147+
'metadata.xml'
148+
);
149+
++$i; //increment i if needed for an another relation
150+
140151
// Relationships for vbaProject if needed
141152
// id : just after the last sheet
142153
if ($spreadsheet->hasMacros()) {

src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1265,11 +1265,13 @@ private function writeCellFormula(XMLWriter $objWriter, string $cellValue, Cell
12651265

12661266
$attributes = $cell->getFormulaAttributes();
12671267
if (($attributes['t'] ?? null) === 'array') {
1268+
$objWriter->writeAttribute('cm', '1');
1269+
12681270
$objWriter->startElement('f');
12691271
$objWriter->writeAttribute('t', 'array');
12701272
$objWriter->writeAttribute('ref', $attributes['ref'] ?? $cell->getCoordinate());
1271-
$objWriter->writeAttribute('aca', '1');
1272-
$objWriter->writeAttribute('ca', '1');
1273+
$objWriter->writeAttribute('aca', '1'); // Always calculate array, true
1274+
$objWriter->writeAttribute('ca', '1'); // Calculate cell, true
12731275
$objWriter->text(Xlfn::addXlfnStripEquals($cellValue));
12741276
$objWriter->endElement();
12751277
} else {

0 commit comments

Comments
 (0)