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

This commit is contained in:
MarkBaker
2022-02-01 21:08:26 +01:00
parent 5d258f162b
commit d2435cde07
6 changed files with 215 additions and 3 deletions
+1 -1
View File
@@ -327,10 +327,10 @@ class Cell
// as well as updating the spillage cells,
// so we need to restore this cell to its formula value, attributes, and datatype
$worksheet->getCell($coordinate);
$this->updateInCollection();
$this->value = $value;
$this->dataType = $datatype;
$this->formulaAttributes = $formulaAttributes;
$this->updateInCollection();
}
// Now we just extract the top-left value from the array to get the result for this specific cell
+15
View File
@@ -20,6 +20,7 @@ use PhpOffice\PhpSpreadsheet\Writer\Xlsx\Comments;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx\ContentTypes;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx\DocProps;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx\Drawing;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx\Metadata;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx\Rels;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx\RelsRibbon;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx\RelsVBA;
@@ -157,6 +158,11 @@ class Xlsx extends BaseWriter
*/
private $writerPartStringTable;
/**
* @var Metadata
*/
private $writerPartMetadata;
/**
* @var Style
*/
@@ -193,6 +199,7 @@ class Xlsx extends BaseWriter
$this->writerPartRels = new Rels($this);
$this->writerPartRelsRibbon = new RelsRibbon($this);
$this->writerPartRelsVBA = new RelsVBA($this);
$this->writerPartMetadata = new Metadata($this);
$this->writerPartStringTable = new StringTable($this);
$this->writerPartStyle = new Style($this);
$this->writerPartTheme = new Theme($this);
@@ -256,6 +263,11 @@ class Xlsx extends BaseWriter
return $this->writerPartRelsVBA;
}
public function getWriterPartMetadata(): Metadata
{
return $this->writerPartMetadata;
}
public function getWriterPartStringTable(): StringTable
{
return $this->writerPartStringTable;
@@ -364,6 +376,9 @@ class Xlsx extends BaseWriter
// Add theme to ZIP file
$zipContent['xl/theme/theme1.xml'] = $this->getWriterPartTheme()->writeTheme($this->spreadSheet);
// Add metadata to ZIP file
$zipContent['xl/metadata.xml'] = $this->getWriterPartMetadata()->writeMetadata();
// Add string table to ZIP file
$zipContent['xl/sharedStrings.xml'] = $this->getWriterPartStringTable()->writeStringTable($this->stringTable);
@@ -82,6 +82,9 @@ class ContentTypes extends WriterPart
$this->writeOverrideContentType($objWriter, '/xl/worksheets/sheet' . ($i + 1) . '.xml', 'application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml');
}
// Shared strings
$this->writeOverrideContentType($objWriter, '/xl/metadata.xml', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheetMetadata+xml');
// Shared strings
$this->writeOverrideContentType($objWriter, '/xl/sharedStrings.xml', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml');
+181
View File
@@ -0,0 +1,181 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Writer\Xlsx;
use PhpOffice\PhpSpreadsheet\Shared\XMLWriter;
class Metadata extends WriterPart
{
protected const METADATA_TYPES = [
[
'cellMeta' => '1',
'coerce' => '1',
'assign' => '1',
'clearComments' => '1',
'clearFormats' => '1',
'rowColShift' => '1',
'splitFirst' => '1',
'merge' => '1',
'pasteValues' => '1',
'pasteAll' => '1',
'copy' => '1',
'minSupportedVersion' => '120000',
'name' => 'XLDAPR',
],
[
'coerce' => '1',
'assign' => '1',
'clearComments' => '1',
'clearFormats' => '1',
'rowColShift' => '1',
'splitFirst' => '1',
'merge' => '1',
'pasteValues' => '1',
'pasteAll' => '1',
'copy' => '1',
'minSupportedVersion' => '120000',
'name' => 'XLRICHVALUE',
],
];
protected const FUTURE_METADATA = [
'XLDAPR' => [
[
'uri' => '{bdbb8cdc-fa1e-496e-a857-3c3f30c029c3}',
'xda:dynamicArrayProperties' => [
'fCollapsed' => '0',
'fDynamic' => '1',
],
],
],
'XLRICHVALUE' => [
[
'uri' => '{3e2802c4-a4d2-4d8b-9148-e3be6c30e623}',
'xlrd:rvb' => [
'i' => '0',
],
],
],
];
protected const EXTRA_METADATA = [
'cellMetadata' => [
[
'v' => '0',
't' => '1',
],
],
'valueMetadata' => [
[
'v' => '0',
't' => '2',
],
],
];
/**
* Write metadata to XML format.
*
* @return string XML Output
*/
public function writeMetadata()
{
// Create XML writer
$objWriter = null;
if ($this->getParentWriter()->getUseDiskCaching()) {
$objWriter = new XMLWriter(XMLWriter::STORAGE_DISK, $this->getParentWriter()->getDiskCachingDirectory());
} else {
$objWriter = new XMLWriter(XMLWriter::STORAGE_MEMORY);
}
// XML header
$objWriter->startDocument('1.0', 'UTF-8', 'yes');
$objWriter->startElement('metadata');
$objWriter->writeAttribute('xmlns:xda', 'http://schemas.microsoft.com/office/spreadsheetml/2017/dynamicarray');
$objWriter->writeAttribute('xmlns:xlrd', 'http://schemas.microsoft.com/office/spreadsheetml/2017/richdata');
$objWriter->writeAttribute('xmlns', 'http://schemas.openxmlformats.org/spreadsheetml/2006/main');
$this->writeMetadataTypes($objWriter);
$this->writeFutureMetadata($objWriter);
$this->writeExtraMetadata($objWriter);
$objWriter->endElement(); // metadata
return $objWriter->getData();
}
private function writeMetadataTypes(XMLWriter $objWriter): void
{
$objWriter->startElement('metadataTypes');
$objWriter->writeAttribute('count', (string) count(self::METADATA_TYPES));
foreach (self::METADATA_TYPES as $metadataType) {
$objWriter->startElement('metadataType');
foreach ($metadataType as $metadataTypeKey => $metadataTypeValue) {
$objWriter->writeAttribute($metadataTypeKey, $metadataTypeValue);
}
$objWriter->endElement(); //metadataType
}
$objWriter->endElement(); //metadataTypes
}
private function writeFutureMetadata(XMLWriter $objWriter): void
{
foreach (self::FUTURE_METADATA as $name => $futureMetadata) {
$objWriter->startElement('futureMetadata');
$objWriter->writeAttribute('count', (string) count($futureMetadata));
$objWriter->writeAttribute('name', $name);
foreach ($futureMetadata as $futureMetadatum) {
$objWriter->startElement('bk');
$objWriter->startElement('extLst');
$ext = array_shift($futureMetadatum);
$objWriter->startElement('ext');
$objWriter->writeAttribute('uri', $ext);
foreach ($futureMetadatum as $extElementName => $extElementProperties) {
$objWriter->startElement($extElementName);
foreach ($extElementProperties as $extElementPropertyName => $extElementPropertyValue) {
$objWriter->writeAttribute($extElementPropertyName, $extElementPropertyValue);
}
$objWriter->endElement(); // ext
}
$objWriter->endElement(); // ext
$objWriter->endElement(); // extLst
$objWriter->endElement(); // bk
}
$objWriter->endElement(); // futureMetadata
}
}
private function writeExtraMetadata(XMLWriter $objWriter): void
{
foreach (self::EXTRA_METADATA as $name => $metadata) {
$objWriter->startElement($name);
$objWriter->writeAttribute('count', (string) count($metadata));
foreach ($metadata as $metadatum) {
$objWriter->startElement('bk');
$objWriter->startElement('rc');
foreach ($metadatum as $attributeName => $attrbuteValue) {
$objWriter->writeAttribute($attributeName, $attrbuteValue);
}
$objWriter->endElement(); // rc
$objWriter->endElement(); // bk
}
$objWriter->endElement();
}
}
}
+11
View File
@@ -137,6 +137,17 @@ class Rels extends WriterPart
'worksheets/sheet' . ($i + 1) . '.xml'
);
}
// Relationship sharedStrings.xml
// id : just after the last sheet
$this->writeRelationship(
$objWriter,
($i + 1 + 3),
'http://schemas.openxmlformats.org/officeDocument/2006/relationships/sheetMetadata',
'metadata.xml'
);
++$i; //increment i if needed for an another relation
// Relationships for vbaProject if needed
// id : just after the last sheet
if ($spreadsheet->hasMacros()) {
+4 -2
View File
@@ -1265,11 +1265,13 @@ class Worksheet extends WriterPart
$attributes = $cell->getFormulaAttributes();
if (($attributes['t'] ?? null) === 'array') {
$objWriter->writeAttribute('cm', '1');
$objWriter->startElement('f');
$objWriter->writeAttribute('t', 'array');
$objWriter->writeAttribute('ref', $attributes['ref'] ?? $cell->getCoordinate());
$objWriter->writeAttribute('aca', '1');
$objWriter->writeAttribute('ca', '1');
$objWriter->writeAttribute('aca', '1'); // Always calculate array, true
$objWriter->writeAttribute('ca', '1'); // Calculate cell, true
$objWriter->text(Xlfn::addXlfnStripEquals($cellValue));
$objWriter->endElement();
} else {