Support to read and write unsupported data for XLSX
This will let users read a file that contains data that are not properly supported and write them back to a new file untouched. - load workbookProtection attributes - save loaded pageSetup[r:id] - save loaded sheet's AlternateContent - save loaded unparsed VmlDrawings - save loaded drawing files `rId` - save loaded draw's AlternateContent - save loaded control properties - save loaded printer settings - save loaded unparsed override content types (for ctrlProp, ...) Closes #435
This commit is contained in:
parent
064076ac6d
commit
83c759e951
|
@ -7,9 +7,14 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
|||
|
||||
## [Unreleased]
|
||||
|
||||
|
||||
### Added
|
||||
|
||||
- Support to read Xlsm templates with form elements, macros, printer settings, protected elements and back compatibility drawing, and save result without losing important elements of document - [#435](https://github.com/PHPOffice/PhpSpreadsheet/issues/435)
|
||||
|
||||
### Fixed
|
||||
|
||||
- GH-332 Subtotal Calculation
|
||||
- Subtotal 9 in a group that has other subtotals 9 exclude the totals of the other subtotals in the range - [#332](https://github.com/PHPOffice/PhpSpreadsheet/issues/332)
|
||||
|
||||
## [1.2.1] - 2018-04-10
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@ use PhpOffice\PhpSpreadsheet\Style\Style;
|
|||
use PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column;
|
||||
use PhpOffice\PhpSpreadsheet\Worksheet\HeaderFooterDrawing;
|
||||
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
|
||||
use SimpleXMLElement;
|
||||
use XMLReader;
|
||||
use ZipArchive;
|
||||
|
||||
|
@ -158,7 +159,7 @@ class Xlsx extends BaseReader
|
|||
$zip->open($pFilename);
|
||||
|
||||
$rels = simplexml_load_string(
|
||||
//~ http://schemas.openxmlformats.org/package/2006/relationships"
|
||||
//~ http://schemas.openxmlformats.org/package/2006/relationships"
|
||||
$this->securityScan($this->getFromZipArchive($zip, '_rels/.rels')),
|
||||
'SimpleXMLElement',
|
||||
Settings::getLibXmlLoaderOptions()
|
||||
|
@ -167,7 +168,7 @@ class Xlsx extends BaseReader
|
|||
if ($rel['Type'] == 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument') {
|
||||
$dir = dirname($rel['Target']);
|
||||
$relsWorkbook = simplexml_load_string(
|
||||
//~ http://schemas.openxmlformats.org/package/2006/relationships"
|
||||
//~ http://schemas.openxmlformats.org/package/2006/relationships"
|
||||
$this->securityScan(
|
||||
$this->getFromZipArchive($zip, "$dir/_rels/" . basename($rel['Target']) . '.rels')
|
||||
),
|
||||
|
@ -184,7 +185,7 @@ class Xlsx extends BaseReader
|
|||
}
|
||||
|
||||
$xmlWorkbook = simplexml_load_string(
|
||||
//~ http://schemas.openxmlformats.org/spreadsheetml/2006/main"
|
||||
//~ http://schemas.openxmlformats.org/spreadsheetml/2006/main"
|
||||
$this->securityScan(
|
||||
$this->getFromZipArchive($zip, "{$rel['Target']}")
|
||||
),
|
||||
|
@ -193,7 +194,7 @@ class Xlsx extends BaseReader
|
|||
);
|
||||
if ($xmlWorkbook->sheets) {
|
||||
$dir = dirname($rel['Target']);
|
||||
/** @var \SimpleXMLElement $eleSheet */
|
||||
/** @var SimpleXMLElement $eleSheet */
|
||||
foreach ($xmlWorkbook->sheets->sheet as $eleSheet) {
|
||||
$tmpInfo = [
|
||||
'worksheetName' => (string) $eleSheet['name'],
|
||||
|
@ -336,13 +337,14 @@ class Xlsx extends BaseReader
|
|||
$excel->removeCellStyleXfByIndex(0); // remove the default style
|
||||
$excel->removeCellXfByIndex(0); // remove the default style
|
||||
}
|
||||
$unparsedLoadedData = [];
|
||||
|
||||
$zip = new ZipArchive();
|
||||
$zip->open($pFilename);
|
||||
|
||||
// Read the theme first, because we need the colour scheme when reading the styles
|
||||
$wbRels = simplexml_load_string(
|
||||
//~ http://schemas.openxmlformats.org/package/2006/relationships"
|
||||
//~ http://schemas.openxmlformats.org/package/2006/relationships"
|
||||
$this->securityScan($this->getFromZipArchive($zip, 'xl/_rels/workbook.xml.rels')),
|
||||
'SimpleXMLElement',
|
||||
Settings::getLibXmlLoaderOptions()
|
||||
|
@ -389,7 +391,7 @@ class Xlsx extends BaseReader
|
|||
}
|
||||
|
||||
$rels = simplexml_load_string(
|
||||
//~ http://schemas.openxmlformats.org/package/2006/relationships"
|
||||
//~ http://schemas.openxmlformats.org/package/2006/relationships"
|
||||
$this->securityScan($this->getFromZipArchive($zip, '_rels/.rels')),
|
||||
'SimpleXMLElement',
|
||||
Settings::getLibXmlLoaderOptions()
|
||||
|
@ -444,7 +446,7 @@ class Xlsx extends BaseReader
|
|||
);
|
||||
if (is_object($xmlCore)) {
|
||||
$docProps = $excel->getProperties();
|
||||
/** @var \SimpleXMLElement $xmlProperty */
|
||||
/** @var SimpleXMLElement $xmlProperty */
|
||||
foreach ($xmlCore as $xmlProperty) {
|
||||
$cellDataOfficeAttributes = $xmlProperty->attributes();
|
||||
if (isset($cellDataOfficeAttributes['name'])) {
|
||||
|
@ -471,7 +473,7 @@ class Xlsx extends BaseReader
|
|||
case 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument':
|
||||
$dir = dirname($rel['Target']);
|
||||
$relsWorkbook = simplexml_load_string(
|
||||
//~ http://schemas.openxmlformats.org/package/2006/relationships"
|
||||
//~ http://schemas.openxmlformats.org/package/2006/relationships"
|
||||
$this->securityScan($this->getFromZipArchive($zip, "$dir/_rels/" . basename($rel['Target']) . '.rels')),
|
||||
'SimpleXMLElement',
|
||||
Settings::getLibXmlLoaderOptions()
|
||||
|
@ -481,7 +483,7 @@ class Xlsx extends BaseReader
|
|||
$sharedStrings = [];
|
||||
$xpath = self::getArrayItem($relsWorkbook->xpath("rel:Relationship[@Type='http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings']"));
|
||||
$xmlStrings = simplexml_load_string(
|
||||
//~ http://schemas.openxmlformats.org/spreadsheetml/2006/main"
|
||||
//~ http://schemas.openxmlformats.org/spreadsheetml/2006/main"
|
||||
$this->securityScan($this->getFromZipArchive($zip, "$dir/$xpath[Target]")),
|
||||
'SimpleXMLElement',
|
||||
Settings::getLibXmlLoaderOptions()
|
||||
|
@ -528,7 +530,7 @@ class Xlsx extends BaseReader
|
|||
$cellStyles = [];
|
||||
$xpath = self::getArrayItem($relsWorkbook->xpath("rel:Relationship[@Type='http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles']"));
|
||||
$xmlStyles = simplexml_load_string(
|
||||
//~ http://schemas.openxmlformats.org/spreadsheetml/2006/main"
|
||||
//~ http://schemas.openxmlformats.org/spreadsheetml/2006/main"
|
||||
$this->securityScan($this->getFromZipArchive($zip, "$dir/$xpath[Target]")),
|
||||
'SimpleXMLElement',
|
||||
Settings::getLibXmlLoaderOptions()
|
||||
|
@ -639,7 +641,7 @@ class Xlsx extends BaseReader
|
|||
}
|
||||
|
||||
$xmlWorkbook = simplexml_load_string(
|
||||
//~ http://schemas.openxmlformats.org/spreadsheetml/2006/main"
|
||||
//~ http://schemas.openxmlformats.org/spreadsheetml/2006/main"
|
||||
$this->securityScan($this->getFromZipArchive($zip, "{$rel['Target']}")),
|
||||
'SimpleXMLElement',
|
||||
Settings::getLibXmlLoaderOptions()
|
||||
|
@ -655,6 +657,9 @@ class Xlsx extends BaseReader
|
|||
}
|
||||
}
|
||||
|
||||
// Set protection
|
||||
$this->readProtection($excel, $xmlWorkbook);
|
||||
|
||||
$sheetId = 0; // keep track of new sheet id in final workbook
|
||||
$oldSheetId = -1; // keep track of old sheet id in final workbook
|
||||
$countSkippedSheets = 0; // keep track of number of skipped sheets
|
||||
|
@ -663,7 +668,7 @@ class Xlsx extends BaseReader
|
|||
$charts = $chartDetails = [];
|
||||
|
||||
if ($xmlWorkbook->sheets) {
|
||||
/** @var \SimpleXMLElement $eleSheet */
|
||||
/** @var SimpleXMLElement $eleSheet */
|
||||
foreach ($xmlWorkbook->sheets->sheet as $eleSheet) {
|
||||
++$oldSheetId;
|
||||
|
||||
|
@ -688,7 +693,7 @@ class Xlsx extends BaseReader
|
|||
$docSheet->setTitle((string) $eleSheet['name'], false, false);
|
||||
$fileWorksheet = $worksheets[(string) self::getArrayItem($eleSheet->attributes('http://schemas.openxmlformats.org/officeDocument/2006/relationships'), 'id')];
|
||||
$xmlSheet = simplexml_load_string(
|
||||
//~ http://schemas.openxmlformats.org/spreadsheetml/2006/main"
|
||||
//~ http://schemas.openxmlformats.org/spreadsheetml/2006/main"
|
||||
$this->securityScan($this->getFromZipArchive($zip, "$dir/$fileWorksheet")),
|
||||
'SimpleXMLElement',
|
||||
Settings::getLibXmlLoaderOptions()
|
||||
|
@ -1069,7 +1074,7 @@ class Xlsx extends BaseReader
|
|||
// Or Date Group elements
|
||||
foreach ($filters->dateGroupItem as $dateGroupItem) {
|
||||
$column->createRule()->setRule(
|
||||
// Operator is undefined, but always treated as EQUAL
|
||||
// Operator is undefined, but always treated as EQUAL
|
||||
null,
|
||||
[
|
||||
'year' => (string) $dateGroupItem['year'],
|
||||
|
@ -1107,7 +1112,7 @@ class Xlsx extends BaseReader
|
|||
// We should only ever have one dynamic filter
|
||||
foreach ($filterColumn->dynamicFilter as $filterRule) {
|
||||
$column->createRule()->setRule(
|
||||
// Operator is undefined, but always treated as EQUAL
|
||||
// Operator is undefined, but always treated as EQUAL
|
||||
null,
|
||||
(string) $filterRule['val'],
|
||||
(string) $filterRule['type']
|
||||
|
@ -1185,6 +1190,11 @@ class Xlsx extends BaseReader
|
|||
self::boolean((string) $xmlSheet->pageSetup['useFirstPageNumber'])) {
|
||||
$docPageSetup->setFirstPageNumber((int) ($xmlSheet->pageSetup['firstPageNumber']));
|
||||
}
|
||||
|
||||
$relAttributes = $xmlSheet->pageSetup->attributes('http://schemas.openxmlformats.org/officeDocument/2006/relationships');
|
||||
if (isset($relAttributes['id'])) {
|
||||
$unparsedLoadedData['sheets'][$docSheet->getCodeName()]['pageSetupRelId'] = (string) $relAttributes['id'];
|
||||
}
|
||||
}
|
||||
|
||||
if ($xmlSheet && $xmlSheet->headerFooter && !$this->readDataOnly) {
|
||||
|
@ -1268,13 +1278,23 @@ class Xlsx extends BaseReader
|
|||
}
|
||||
}
|
||||
|
||||
// unparsed sheet AlternateContent
|
||||
if ($xmlSheet && !$this->readDataOnly) {
|
||||
$mc = $xmlSheet->children('http://schemas.openxmlformats.org/markup-compatibility/2006');
|
||||
if ($mc->AlternateContent) {
|
||||
foreach ($mc->AlternateContent as $alternateContent) {
|
||||
$unparsedLoadedData['sheets'][$docSheet->getCodeName()]['AlternateContents'][] = $alternateContent->asXML();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add hyperlinks
|
||||
$hyperlinks = [];
|
||||
if (!$this->readDataOnly) {
|
||||
// Locate hyperlink relations
|
||||
if ($zip->locateName(dirname("$dir/$fileWorksheet") . '/_rels/' . basename($fileWorksheet) . '.rels')) {
|
||||
$relsWorksheet = simplexml_load_string(
|
||||
//~ http://schemas.openxmlformats.org/package/2006/relationships"
|
||||
//~ http://schemas.openxmlformats.org/package/2006/relationships"
|
||||
$this->securityScan(
|
||||
$this->getFromZipArchive($zip, dirname("$dir/$fileWorksheet") . '/_rels/' . basename($fileWorksheet) . '.rels')
|
||||
),
|
||||
|
@ -1290,7 +1310,7 @@ class Xlsx extends BaseReader
|
|||
|
||||
// Loop through hyperlinks
|
||||
if ($xmlSheet && $xmlSheet->hyperlinks) {
|
||||
/** @var \SimpleXMLElement $hyperlink */
|
||||
/** @var SimpleXMLElement $hyperlink */
|
||||
foreach ($xmlSheet->hyperlinks->hyperlink as $hyperlink) {
|
||||
// Link url
|
||||
$linkRel = $hyperlink->attributes('http://schemas.openxmlformats.org/officeDocument/2006/relationships');
|
||||
|
@ -1323,7 +1343,7 @@ class Xlsx extends BaseReader
|
|||
// Locate comment relations
|
||||
if ($zip->locateName(dirname("$dir/$fileWorksheet") . '/_rels/' . basename($fileWorksheet) . '.rels')) {
|
||||
$relsWorksheet = simplexml_load_string(
|
||||
//~ http://schemas.openxmlformats.org/package/2006/relationships"
|
||||
//~ http://schemas.openxmlformats.org/package/2006/relationships"
|
||||
$this->securityScan(
|
||||
$this->getFromZipArchive($zip, dirname("$dir/$fileWorksheet") . '/_rels/' . basename($fileWorksheet) . '.rels')
|
||||
),
|
||||
|
@ -1367,6 +1387,9 @@ class Xlsx extends BaseReader
|
|||
}
|
||||
}
|
||||
|
||||
// later we will remove from it real vmlComments
|
||||
$unparsedVmlDrawings = $vmlComments;
|
||||
|
||||
// Loop through VML comments
|
||||
foreach ($vmlComments as $relName => $relPath) {
|
||||
// Load VML comments file
|
||||
|
@ -1431,16 +1454,31 @@ class Xlsx extends BaseReader
|
|||
$comment->setVisible($stylePair[1] == 'visible');
|
||||
}
|
||||
}
|
||||
|
||||
unset($unparsedVmlDrawings[$relName]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// unparsed vmlDrawing
|
||||
if ($unparsedVmlDrawings) {
|
||||
foreach ($unparsedVmlDrawings as $rId => $relPath) {
|
||||
$rId = substr($rId, 3); // rIdXXX
|
||||
$unparsedVmlDrawing = &$unparsedLoadedData['sheets'][$docSheet->getCodeName()]['vmlDrawings'];
|
||||
$unparsedVmlDrawing[$rId] = [];
|
||||
$unparsedVmlDrawing[$rId]['filePath'] = self::dirAdd("$dir/$fileWorksheet", $relPath);
|
||||
$unparsedVmlDrawing[$rId]['relFilePath'] = $relPath;
|
||||
$unparsedVmlDrawing[$rId]['content'] = $this->securityScan($this->getFromZipArchive($zip, $unparsedVmlDrawing[$rId]['filePath']));
|
||||
unset($unparsedVmlDrawing);
|
||||
}
|
||||
}
|
||||
|
||||
// Header/footer images
|
||||
if ($xmlSheet && $xmlSheet->legacyDrawingHF && !$this->readDataOnly) {
|
||||
if ($zip->locateName(dirname("$dir/$fileWorksheet") . '/_rels/' . basename($fileWorksheet) . '.rels')) {
|
||||
$relsWorksheet = simplexml_load_string(
|
||||
//~ http://schemas.openxmlformats.org/package/2006/relationships"
|
||||
//~ http://schemas.openxmlformats.org/package/2006/relationships"
|
||||
$this->securityScan(
|
||||
$this->getFromZipArchive($zip, dirname("$dir/$fileWorksheet") . '/_rels/' . basename($fileWorksheet) . '.rels')
|
||||
),
|
||||
|
@ -1458,7 +1496,7 @@ class Xlsx extends BaseReader
|
|||
if ($vmlRelationship != '') {
|
||||
// Fetch linked images
|
||||
$relsVML = simplexml_load_string(
|
||||
//~ http://schemas.openxmlformats.org/package/2006/relationships"
|
||||
//~ http://schemas.openxmlformats.org/package/2006/relationships"
|
||||
$this->securityScan(
|
||||
$this->getFromZipArchive($zip, dirname($vmlRelationship) . '/_rels/' . basename($vmlRelationship) . '.rels')
|
||||
),
|
||||
|
@ -1521,7 +1559,7 @@ class Xlsx extends BaseReader
|
|||
// TODO: Autoshapes from twoCellAnchors!
|
||||
if ($zip->locateName(dirname("$dir/$fileWorksheet") . '/_rels/' . basename($fileWorksheet) . '.rels')) {
|
||||
$relsWorksheet = simplexml_load_string(
|
||||
//~ http://schemas.openxmlformats.org/package/2006/relationships"
|
||||
//~ http://schemas.openxmlformats.org/package/2006/relationships"
|
||||
$this->securityScan(
|
||||
$this->getFromZipArchive($zip, dirname("$dir/$fileWorksheet") . '/_rels/' . basename($fileWorksheet) . '.rels')
|
||||
),
|
||||
|
@ -1538,7 +1576,7 @@ class Xlsx extends BaseReader
|
|||
foreach ($xmlSheet->drawing as $drawing) {
|
||||
$fileDrawing = $drawings[(string) self::getArrayItem($drawing->attributes('http://schemas.openxmlformats.org/officeDocument/2006/relationships'), 'id')];
|
||||
$relsDrawing = simplexml_load_string(
|
||||
//~ http://schemas.openxmlformats.org/package/2006/relationships"
|
||||
//~ http://schemas.openxmlformats.org/package/2006/relationships"
|
||||
$this->securityScan(
|
||||
$this->getFromZipArchive($zip, dirname($fileDrawing) . '/_rels/' . basename($fileDrawing) . '.rels')
|
||||
),
|
||||
|
@ -1570,11 +1608,11 @@ class Xlsx extends BaseReader
|
|||
if ($xmlDrawing->oneCellAnchor) {
|
||||
foreach ($xmlDrawing->oneCellAnchor as $oneCellAnchor) {
|
||||
if ($oneCellAnchor->pic->blipFill) {
|
||||
/** @var \SimpleXMLElement $blip */
|
||||
/** @var SimpleXMLElement $blip */
|
||||
$blip = $oneCellAnchor->pic->blipFill->children('http://schemas.openxmlformats.org/drawingml/2006/main')->blip;
|
||||
/** @var \SimpleXMLElement $xfrm */
|
||||
/** @var SimpleXMLElement $xfrm */
|
||||
$xfrm = $oneCellAnchor->pic->spPr->children('http://schemas.openxmlformats.org/drawingml/2006/main')->xfrm;
|
||||
/** @var \SimpleXMLElement $outerShdw */
|
||||
/** @var SimpleXMLElement $outerShdw */
|
||||
$outerShdw = $oneCellAnchor->pic->spPr->children('http://schemas.openxmlformats.org/drawingml/2006/main')->effectLst->outerShdw;
|
||||
$objDrawing = new \PhpOffice\PhpSpreadsheet\Worksheet\Drawing();
|
||||
$objDrawing->setName((string) self::getArrayItem($oneCellAnchor->pic->nvPicPr->cNvPr->attributes(), 'name'));
|
||||
|
@ -1663,7 +1701,7 @@ class Xlsx extends BaseReader
|
|||
$toOffsetX = Drawing::EMUToPixels($twoCellAnchor->to->colOff);
|
||||
$toOffsetY = Drawing::EMUToPixels($twoCellAnchor->to->rowOff);
|
||||
$graphic = $twoCellAnchor->graphicFrame->children('http://schemas.openxmlformats.org/drawingml/2006/main')->graphic;
|
||||
/** @var \SimpleXMLElement $chartRef */
|
||||
/** @var SimpleXMLElement $chartRef */
|
||||
$chartRef = $graphic->graphicData->children('http://schemas.openxmlformats.org/drawingml/2006/chart')->chart;
|
||||
$thisChart = (string) $chartRef->attributes('http://schemas.openxmlformats.org/officeDocument/2006/relationships');
|
||||
|
||||
|
@ -1680,9 +1718,33 @@ class Xlsx extends BaseReader
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// store original rId of drawing files
|
||||
$unparsedLoadedData['sheets'][$docSheet->getCodeName()]['drawingOriginalIds'] = [];
|
||||
foreach ($relsWorksheet->Relationship as $ele) {
|
||||
if ($ele['Type'] == 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/drawing') {
|
||||
$unparsedLoadedData['sheets'][$docSheet->getCodeName()]['drawingOriginalIds'][(string) $ele['Target']] = (string) $ele['Id'];
|
||||
}
|
||||
}
|
||||
|
||||
// unparsed drawing AlternateContent
|
||||
$xmlAltDrawing = simplexml_load_string(
|
||||
$this->securityScan($this->getFromZipArchive($zip, $fileDrawing)),
|
||||
'SimpleXMLElement',
|
||||
Settings::getLibXmlLoaderOptions()
|
||||
)->children('http://schemas.openxmlformats.org/markup-compatibility/2006');
|
||||
|
||||
if ($xmlAltDrawing->AlternateContent) {
|
||||
foreach ($xmlAltDrawing->AlternateContent as $alternateContent) {
|
||||
$unparsedLoadedData['sheets'][$docSheet->getCodeName()]['drawingAlternateContents'][] = $alternateContent->asXML();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->readFormControlProperties($excel, $zip, $dir, $fileWorksheet, $docSheet, $unparsedLoadedData);
|
||||
$this->readPrinterSettings($excel, $zip, $dir, $fileWorksheet, $docSheet, $unparsedLoadedData);
|
||||
|
||||
// Loop through definedNames
|
||||
if ($xmlWorkbook->definedNames) {
|
||||
foreach ($xmlWorkbook->definedNames->definedName as $definedName) {
|
||||
|
@ -1852,6 +1914,18 @@ class Xlsx extends BaseReader
|
|||
'SimpleXMLElement',
|
||||
Settings::getLibXmlLoaderOptions()
|
||||
);
|
||||
|
||||
// Default content types
|
||||
foreach ($contentTypes->Default as $contentType) {
|
||||
switch ($contentType['ContentType']) {
|
||||
case 'application/vnd.openxmlformats-officedocument.spreadsheetml.printerSettings':
|
||||
$unparsedLoadedData['default_content_types'][(string) $contentType['Extension']] = (string) $contentType['ContentType'];
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Override content types
|
||||
foreach ($contentTypes->Override as $contentType) {
|
||||
switch ($contentType['ContentType']) {
|
||||
case 'application/vnd.openxmlformats-officedocument.drawingml.chart+xml':
|
||||
|
@ -1876,10 +1950,20 @@ class Xlsx extends BaseReader
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
// unparsed
|
||||
case 'application/vnd.ms-excel.controlproperties+xml':
|
||||
$unparsedLoadedData['override_content_types'][(string) $contentType['PartName']] = (string) $contentType['ContentType'];
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$excel->setUnparsedLoadedData($unparsedLoadedData);
|
||||
|
||||
$zip->close();
|
||||
|
||||
return $excel;
|
||||
|
@ -1912,7 +1996,7 @@ class Xlsx extends BaseReader
|
|||
|
||||
/**
|
||||
* @param Style $docStyle
|
||||
* @param \SimpleXMLElement|\stdClass $style
|
||||
* @param SimpleXMLElement|\stdClass $style
|
||||
*/
|
||||
private static function readStyle(Style $docStyle, $style)
|
||||
{
|
||||
|
@ -1953,7 +2037,7 @@ class Xlsx extends BaseReader
|
|||
// fill
|
||||
if (isset($style->fill)) {
|
||||
if ($style->fill->gradientFill) {
|
||||
/** @var \SimpleXMLElement $gradientFill */
|
||||
/** @var SimpleXMLElement $gradientFill */
|
||||
$gradientFill = $style->fill->gradientFill[0];
|
||||
if (!empty($gradientFill['type'])) {
|
||||
$docStyle->getFill()->setFillType((string) $gradientFill['type']);
|
||||
|
@ -2042,7 +2126,7 @@ class Xlsx extends BaseReader
|
|||
|
||||
/**
|
||||
* @param Border $docBorder
|
||||
* @param \SimpleXMLElement $eleBorder
|
||||
* @param SimpleXMLElement $eleBorder
|
||||
*/
|
||||
private static function readBorder(Border $docBorder, $eleBorder)
|
||||
{
|
||||
|
@ -2055,7 +2139,7 @@ class Xlsx extends BaseReader
|
|||
}
|
||||
|
||||
/**
|
||||
* @param \SimpleXMLElement | null $is
|
||||
* @param SimpleXMLElement | null $is
|
||||
*
|
||||
* @return RichText
|
||||
*/
|
||||
|
@ -2215,4 +2299,95 @@ class Xlsx extends BaseReader
|
|||
|
||||
return $value === 'true' || $value === 'TRUE';
|
||||
}
|
||||
|
||||
private function readProtection(Spreadsheet $excel, SimpleXMLElement $xmlWorkbook)
|
||||
{
|
||||
if (!$xmlWorkbook->workbookProtection) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($xmlWorkbook->workbookProtection['lockRevision']) {
|
||||
$excel->getSecurity()->setLockRevision((bool) $xmlWorkbook->workbookProtection['lockRevision']);
|
||||
}
|
||||
|
||||
if ($xmlWorkbook->workbookProtection['lockStructure']) {
|
||||
$excel->getSecurity()->setLockStructure((bool) $xmlWorkbook->workbookProtection['lockStructure']);
|
||||
}
|
||||
|
||||
if ($xmlWorkbook->workbookProtection['lockWindows']) {
|
||||
$excel->getSecurity()->setLockWindows((bool) $xmlWorkbook->workbookProtection['lockWindows']);
|
||||
}
|
||||
|
||||
if ($xmlWorkbook->workbookProtection['revisionsPassword']) {
|
||||
$excel->getSecurity()->setRevisionPassword((string) $xmlWorkbook->workbookProtection['revisionsPassword'], true);
|
||||
}
|
||||
|
||||
if ($xmlWorkbook->workbookProtection['workbookPassword']) {
|
||||
$excel->getSecurity()->setWorkbookPassword((string) $xmlWorkbook->workbookProtection['workbookPassword'], true);
|
||||
}
|
||||
}
|
||||
|
||||
private function readFormControlProperties(Spreadsheet $excel, ZipArchive $zip, $dir, $fileWorksheet, $docSheet, array &$unparsedLoadedData)
|
||||
{
|
||||
if (!$zip->locateName(dirname("$dir/$fileWorksheet") . '/_rels/' . basename($fileWorksheet) . '.rels')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$relsWorksheet = simplexml_load_string(
|
||||
//~ http://schemas.openxmlformats.org/package/2006/relationships"
|
||||
$this->securityScan(
|
||||
$this->getFromZipArchive($zip, dirname("$dir/$fileWorksheet") . '/_rels/' . basename($fileWorksheet) . '.rels')
|
||||
),
|
||||
'SimpleXMLElement',
|
||||
Settings::getLibXmlLoaderOptions()
|
||||
);
|
||||
$ctrlProps = [];
|
||||
foreach ($relsWorksheet->Relationship as $ele) {
|
||||
if ($ele['Type'] == 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/ctrlProp') {
|
||||
$ctrlProps[(string) $ele['Id']] = $ele;
|
||||
}
|
||||
}
|
||||
|
||||
$unparsedCtrlProps = &$unparsedLoadedData['sheets'][$docSheet->getCodeName()]['ctrlProps'];
|
||||
foreach ($ctrlProps as $rId => $ctrlProp) {
|
||||
$rId = substr($rId, 3); // rIdXXX
|
||||
$unparsedCtrlProps[$rId] = [];
|
||||
$unparsedCtrlProps[$rId]['filePath'] = self::dirAdd("$dir/$fileWorksheet", $ctrlProp['Target']);
|
||||
$unparsedCtrlProps[$rId]['relFilePath'] = (string) $ctrlProp['Target'];
|
||||
$unparsedCtrlProps[$rId]['content'] = $this->securityScan($this->getFromZipArchive($zip, $unparsedCtrlProps[$rId]['filePath']));
|
||||
}
|
||||
unset($unparsedCtrlProps);
|
||||
}
|
||||
|
||||
private function readPrinterSettings(Spreadsheet $excel, ZipArchive $zip, $dir, $fileWorksheet, $docSheet, array &$unparsedLoadedData)
|
||||
{
|
||||
if (!$zip->locateName(dirname("$dir/$fileWorksheet") . '/_rels/' . basename($fileWorksheet) . '.rels')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$relsWorksheet = simplexml_load_string(
|
||||
//~ http://schemas.openxmlformats.org/package/2006/relationships"
|
||||
$this->securityScan(
|
||||
$this->getFromZipArchive($zip, dirname("$dir/$fileWorksheet") . '/_rels/' . basename($fileWorksheet) . '.rels')
|
||||
),
|
||||
'SimpleXMLElement',
|
||||
Settings::getLibXmlLoaderOptions()
|
||||
);
|
||||
$sheetPrinterSettings = [];
|
||||
foreach ($relsWorksheet->Relationship as $ele) {
|
||||
if ($ele['Type'] == 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/printerSettings') {
|
||||
$sheetPrinterSettings[(string) $ele['Id']] = $ele;
|
||||
}
|
||||
}
|
||||
|
||||
$unparsedPrinterSettings = &$unparsedLoadedData['sheets'][$docSheet->getCodeName()]['printerSettings'];
|
||||
foreach ($sheetPrinterSettings as $rId => $printerSettings) {
|
||||
$rId = substr($rId, 3); // rIdXXX
|
||||
$unparsedPrinterSettings[$rId] = [];
|
||||
$unparsedPrinterSettings[$rId]['filePath'] = self::dirAdd("$dir/$fileWorksheet", $printerSettings['Target']);
|
||||
$unparsedPrinterSettings[$rId]['relFilePath'] = (string) $printerSettings['Target'];
|
||||
$unparsedPrinterSettings[$rId]['content'] = $this->securityScan($this->getFromZipArchive($zip, $unparsedPrinterSettings[$rId]['filePath']));
|
||||
}
|
||||
unset($unparsedPrinterSettings);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -115,6 +115,14 @@ class Spreadsheet
|
|||
*/
|
||||
private $ribbonBinObjects;
|
||||
|
||||
/**
|
||||
* List of unparsed loaded data for export to same format with better compatibility.
|
||||
* It has to be minimized when the library start to support currently unparsed data.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $unparsedLoadedData = [];
|
||||
|
||||
/**
|
||||
* The workbook has macros ?
|
||||
*
|
||||
|
@ -256,6 +264,32 @@ class Spreadsheet
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* List of unparsed loaded data for export to same format with better compatibility.
|
||||
* It has to be minimized when the library start to support currently unparsed data.
|
||||
*
|
||||
* @internal
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getUnparsedLoadedData()
|
||||
{
|
||||
return $this->unparsedLoadedData;
|
||||
}
|
||||
|
||||
/**
|
||||
* List of unparsed loaded data for export to same format with better compatibility.
|
||||
* It has to be minimized when the library start to support currently unparsed data.
|
||||
*
|
||||
* @internal
|
||||
*
|
||||
* @param array $unparsedLoadedData
|
||||
*/
|
||||
public function setUnparsedLoadedData(array $unparsedLoadedData)
|
||||
{
|
||||
$this->unparsedLoadedData = $unparsedLoadedData;
|
||||
}
|
||||
|
||||
/**
|
||||
* return the extension of a filename. Internal use for a array_map callback (php<5.3 don't like lambda function).
|
||||
*
|
||||
|
|
|
@ -290,12 +290,25 @@ class Xlsx extends BaseWriter
|
|||
}
|
||||
}
|
||||
|
||||
$chartRef1 = $chartRef2 = 0;
|
||||
$chartRef1 = 0;
|
||||
// Add worksheet relationships (drawings, ...)
|
||||
for ($i = 0; $i < $this->spreadSheet->getSheetCount(); ++$i) {
|
||||
// Add relationships
|
||||
$zip->addFromString('xl/worksheets/_rels/sheet' . ($i + 1) . '.xml.rels', $this->getWriterPart('Rels')->writeWorksheetRelationships($this->spreadSheet->getSheet($i), ($i + 1), $this->includeCharts));
|
||||
|
||||
// Add unparsedLoadedData
|
||||
$sheetCodeName = $this->spreadSheet->getSheet($i)->getCodeName();
|
||||
if (isset($this->spreadSheet->getUnparsedLoadedData()['sheets'][$sheetCodeName]['ctrlProps'])) {
|
||||
foreach ($this->spreadSheet->getUnparsedLoadedData()['sheets'][$sheetCodeName]['ctrlProps'] as $ctrlProp) {
|
||||
$zip->addFromString($ctrlProp['filePath'], $ctrlProp['content']);
|
||||
}
|
||||
}
|
||||
if (isset($this->spreadSheet->getUnparsedLoadedData()['sheets'][$sheetCodeName]['printerSettings'])) {
|
||||
foreach ($this->spreadSheet->getUnparsedLoadedData()['sheets'][$sheetCodeName]['printerSettings'] as $ctrlProp) {
|
||||
$zip->addFromString($ctrlProp['filePath'], $ctrlProp['content']);
|
||||
}
|
||||
}
|
||||
|
||||
$drawings = $this->spreadSheet->getSheet($i)->getDrawingCollection();
|
||||
$drawingCount = count($drawings);
|
||||
if ($this->includeCharts) {
|
||||
|
@ -307,6 +320,9 @@ class Xlsx extends BaseWriter
|
|||
// Drawing relationships
|
||||
$zip->addFromString('xl/drawings/_rels/drawing' . ($i + 1) . '.xml.rels', $this->getWriterPart('Rels')->writeDrawingRelationships($this->spreadSheet->getSheet($i), $chartRef1, $this->includeCharts));
|
||||
|
||||
// Drawings
|
||||
$zip->addFromString('xl/drawings/drawing' . ($i + 1) . '.xml', $this->getWriterPart('Drawing')->writeDrawings($this->spreadSheet->getSheet($i), $this->includeCharts));
|
||||
} elseif (isset($this->spreadSheet->getUnparsedLoadedData()['sheets'][$sheetCodeName]['drawingAlternateContents'])) {
|
||||
// Drawings
|
||||
$zip->addFromString('xl/drawings/drawing' . ($i + 1) . '.xml', $this->getWriterPart('Drawing')->writeDrawings($this->spreadSheet->getSheet($i), $this->includeCharts));
|
||||
}
|
||||
|
@ -320,6 +336,13 @@ class Xlsx extends BaseWriter
|
|||
$zip->addFromString('xl/comments' . ($i + 1) . '.xml', $this->getWriterPart('Comments')->writeComments($this->spreadSheet->getSheet($i)));
|
||||
}
|
||||
|
||||
// Add unparsed relationship parts
|
||||
if (isset($this->spreadSheet->getUnparsedLoadedData()['sheets'][$this->spreadSheet->getSheet($i)->getCodeName()]['vmlDrawings'])) {
|
||||
foreach ($this->spreadSheet->getUnparsedLoadedData()['sheets'][$this->spreadSheet->getSheet($i)->getCodeName()]['vmlDrawings'] as $vmlDrawing) {
|
||||
$zip->addFromString($vmlDrawing['filePath'], $vmlDrawing['content']);
|
||||
}
|
||||
}
|
||||
|
||||
// Add header/footer relationship parts
|
||||
if (count($this->spreadSheet->getSheet($i)->getHeaderFooter()->getImages()) > 0) {
|
||||
// VML Drawings
|
||||
|
|
|
@ -57,7 +57,8 @@ class ContentTypes extends WriterPart
|
|||
// Yes : not standard content but "macroEnabled"
|
||||
$this->writeOverrideContentType($objWriter, '/xl/workbook.xml', 'application/vnd.ms-excel.sheet.macroEnabled.main+xml');
|
||||
//... and define a new type for the VBA project
|
||||
$this->writeDefaultContentType($objWriter, 'bin', 'application/vnd.ms-office.vbaProject');
|
||||
// Better use Override, because we can use 'bin' also for xl\printerSettings\printerSettings1.bin
|
||||
$this->writeOverrideContentType($objWriter, '/xl/vbaProject.bin', 'application/vnd.ms-office.vbaProject');
|
||||
if ($spreadsheet->hasMacrosCertificate()) {
|
||||
// signed macros ?
|
||||
// Yes : add needed information
|
||||
|
@ -93,9 +94,10 @@ class ContentTypes extends WriterPart
|
|||
$drawings = $spreadsheet->getSheet($i)->getDrawingCollection();
|
||||
$drawingCount = count($drawings);
|
||||
$chartCount = ($includeCharts) ? $spreadsheet->getSheet($i)->getChartCount() : 0;
|
||||
$hasUnparsedDrawing = isset($spreadsheet->getUnparsedLoadedData()['sheets'][$spreadsheet->getSheet($i)->getCodeName()]['drawingOriginalIds']);
|
||||
|
||||
// We need a drawing relationship for the worksheet if we have either drawings or charts
|
||||
if (($drawingCount > 0) || ($chartCount > 0)) {
|
||||
if (($drawingCount > 0) || ($chartCount > 0) || $hasUnparsedDrawing) {
|
||||
$this->writeOverrideContentType($objWriter, '/xl/drawings/drawing' . ($i + 1) . '.xml', 'application/vnd.openxmlformats-officedocument.drawing+xml');
|
||||
}
|
||||
|
||||
|
@ -160,6 +162,20 @@ class ContentTypes extends WriterPart
|
|||
}
|
||||
}
|
||||
|
||||
// unparsed defaults
|
||||
if (isset($spreadsheet->getUnparsedLoadedData()['default_content_types'])) {
|
||||
foreach ($spreadsheet->getUnparsedLoadedData()['default_content_types'] as $extName => $contentType) {
|
||||
$this->writeDefaultContentType($objWriter, $extName, $contentType);
|
||||
}
|
||||
}
|
||||
|
||||
// unparsed overrides
|
||||
if (isset($spreadsheet->getUnparsedLoadedData()['override_content_types'])) {
|
||||
foreach ($spreadsheet->getUnparsedLoadedData()['override_content_types'] as $partName => $overrideType) {
|
||||
$this->writeOverrideContentType($objWriter, $partName, $overrideType);
|
||||
}
|
||||
}
|
||||
|
||||
$objWriter->endElement();
|
||||
|
||||
// Return
|
||||
|
|
|
@ -59,6 +59,13 @@ class Drawing extends WriterPart
|
|||
}
|
||||
}
|
||||
|
||||
// unparsed AlternateContent
|
||||
if (isset($pWorksheet->getParent()->getUnparsedLoadedData()['sheets'][$pWorksheet->getCodeName()]['drawingAlternateContents'])) {
|
||||
foreach ($pWorksheet->getParent()->getUnparsedLoadedData()['sheets'][$pWorksheet->getCodeName()]['drawingAlternateContents'] as $drawingAlternateContent) {
|
||||
$objWriter->writeRaw($drawingAlternateContent);
|
||||
}
|
||||
}
|
||||
|
||||
$objWriter->endElement();
|
||||
|
||||
// Return
|
||||
|
|
|
@ -195,18 +195,30 @@ class Rels extends WriterPart
|
|||
|
||||
// Write drawing relationships?
|
||||
$d = 0;
|
||||
$drawingOriginalIds = [];
|
||||
if (isset($pWorksheet->getParent()->getUnparsedLoadedData()['sheets'][$pWorksheet->getCodeName()]['drawingOriginalIds'])) {
|
||||
$drawingOriginalIds = $pWorksheet->getParent()->getUnparsedLoadedData()['sheets'][$pWorksheet->getCodeName()]['drawingOriginalIds'];
|
||||
}
|
||||
|
||||
if ($includeCharts) {
|
||||
$charts = $pWorksheet->getChartCollection();
|
||||
} else {
|
||||
$charts = [];
|
||||
}
|
||||
if (($pWorksheet->getDrawingCollection()->count() > 0) ||
|
||||
(count($charts) > 0)) {
|
||||
|
||||
if (($pWorksheet->getDrawingCollection()->count() > 0) || (count($charts) > 0) || $drawingOriginalIds) {
|
||||
$relPath = '../drawings/drawing' . $pWorksheetId . '.xml';
|
||||
$rId = ++$d;
|
||||
|
||||
if (isset($drawingOriginalIds[$relPath])) {
|
||||
$rId = (int) (substr($drawingOriginalIds[$relPath], 3));
|
||||
}
|
||||
|
||||
$this->writeRelationship(
|
||||
$objWriter,
|
||||
++$d,
|
||||
$rId,
|
||||
'http://schemas.openxmlformats.org/officeDocument/2006/relationships/drawing',
|
||||
'../drawings/drawing' . $pWorksheetId . '.xml'
|
||||
$relPath
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -255,11 +267,31 @@ class Rels extends WriterPart
|
|||
);
|
||||
}
|
||||
|
||||
$this->writeUnparsedRelationship($pWorksheet, $objWriter, 'ctrlProps', 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/ctrlProp');
|
||||
$this->writeUnparsedRelationship($pWorksheet, $objWriter, 'vmlDrawings', 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/vmlDrawing');
|
||||
$this->writeUnparsedRelationship($pWorksheet, $objWriter, 'printerSettings', 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/printerSettings');
|
||||
|
||||
$objWriter->endElement();
|
||||
|
||||
return $objWriter->getData();
|
||||
}
|
||||
|
||||
private function writeUnparsedRelationship(\PhpOffice\PhpSpreadsheet\Worksheet\Worksheet $pWorksheet, XMLWriter $objWriter, $relationship, $type)
|
||||
{
|
||||
if (!isset($pWorksheet->getParent()->getUnparsedLoadedData()['sheets'][$pWorksheet->getCodeName()][$relationship])) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($pWorksheet->getParent()->getUnparsedLoadedData()['sheets'][$pWorksheet->getCodeName()][$relationship] as $rId => $value) {
|
||||
$this->writeRelationship(
|
||||
$objWriter,
|
||||
$rId,
|
||||
$type,
|
||||
$value['relFilePath']
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write drawing relationships to XML format.
|
||||
*
|
||||
|
|
|
@ -51,6 +51,12 @@ class Worksheet extends WriterPart
|
|||
$objWriter->writeAttribute('xmlns', 'http://schemas.openxmlformats.org/spreadsheetml/2006/main');
|
||||
$objWriter->writeAttribute('xmlns:r', 'http://schemas.openxmlformats.org/officeDocument/2006/relationships');
|
||||
|
||||
$objWriter->writeAttribute('xmlns:xdr', 'http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing');
|
||||
$objWriter->writeAttribute('xmlns:x14', 'http://schemas.microsoft.com/office/spreadsheetml/2009/9/main');
|
||||
$objWriter->writeAttribute('xmlns:mc', 'http://schemas.openxmlformats.org/markup-compatibility/2006');
|
||||
$objWriter->writeAttribute('mc:Ignorable', 'x14ac');
|
||||
$objWriter->writeAttribute('xmlns:x14ac', 'http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac');
|
||||
|
||||
// sheetPr
|
||||
$this->writeSheetPr($objWriter, $pSheet);
|
||||
|
||||
|
@ -114,6 +120,9 @@ class Worksheet extends WriterPart
|
|||
// LegacyDrawingHF
|
||||
$this->writeLegacyDrawingHF($objWriter, $pSheet);
|
||||
|
||||
// AlternateContent
|
||||
$this->writeAlternateContent($objWriter, $pSheet);
|
||||
|
||||
$objWriter->endElement();
|
||||
|
||||
// Return
|
||||
|
@ -283,7 +292,7 @@ class Worksheet extends WriterPart
|
|||
$objWriter->writeAttribute('pane', $pane);
|
||||
}
|
||||
$objWriter->writeAttribute('activeCell', $activeCell);
|
||||
$objWriter->writeAttribute('sqref', $activeCell);
|
||||
$objWriter->writeAttribute('sqref', $pSheet->getSelectedCells());
|
||||
$objWriter->endElement();
|
||||
|
||||
$objWriter->endElement();
|
||||
|
@ -843,6 +852,10 @@ class Worksheet extends WriterPart
|
|||
$objWriter->writeAttribute('useFirstPageNumber', '1');
|
||||
}
|
||||
|
||||
if (isset($pSheet->getParent()->getUnparsedLoadedData()['sheets'][$pSheet->getCodeName()]['pageSetupRelId'])) {
|
||||
$objWriter->writeAttribute('r:id', $pSheet->getParent()->getUnparsedLoadedData()['sheets'][$pSheet->getCodeName()]['pageSetupRelId']);
|
||||
}
|
||||
|
||||
$objWriter->endElement();
|
||||
}
|
||||
|
||||
|
@ -1142,16 +1155,26 @@ class Worksheet extends WriterPart
|
|||
* @param PhpspreadsheetWorksheet $pSheet Worksheet
|
||||
* @param bool $includeCharts Flag indicating if we should include drawing details for charts
|
||||
*/
|
||||
private function writeDrawings(XMLWriter $objWriter = null, PhpspreadsheetWorksheet $pSheet = null, $includeCharts = false)
|
||||
private function writeDrawings(XMLWriter $objWriter, PhpspreadsheetWorksheet $pSheet, $includeCharts = false)
|
||||
{
|
||||
$hasUnparsedDrawing = isset($pSheet->getParent()->getUnparsedLoadedData()['sheets'][$pSheet->getCodeName()]['drawingOriginalIds']);
|
||||
$chartCount = ($includeCharts) ? $pSheet->getChartCollection()->count() : 0;
|
||||
// If sheet contains drawings, add the relationships
|
||||
if (($pSheet->getDrawingCollection()->count() > 0) ||
|
||||
($chartCount > 0)) {
|
||||
$objWriter->startElement('drawing');
|
||||
$objWriter->writeAttribute('r:id', 'rId1');
|
||||
$objWriter->endElement();
|
||||
if ($chartCount == 0 && $pSheet->getDrawingCollection()->count() == 0 && !$hasUnparsedDrawing) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If sheet contains drawings, add the relationships
|
||||
$objWriter->startElement('drawing');
|
||||
|
||||
$rId = 'rId1';
|
||||
if (isset($pSheet->getParent()->getUnparsedLoadedData()['sheets'][$pSheet->getCodeName()]['drawingOriginalIds'])) {
|
||||
$drawingOriginalIds = $pSheet->getParent()->getUnparsedLoadedData()['sheets'][$pSheet->getCodeName()]['drawingOriginalIds'];
|
||||
// take first. In future can be overriten
|
||||
$rId = reset($drawingOriginalIds);
|
||||
}
|
||||
|
||||
$objWriter->writeAttribute('r:id', $rId);
|
||||
$objWriter->endElement();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1185,4 +1208,15 @@ class Worksheet extends WriterPart
|
|||
$objWriter->endElement();
|
||||
}
|
||||
}
|
||||
|
||||
private function writeAlternateContent(XMLWriter $objWriter, PhpspreadsheetWorksheet $pSheet)
|
||||
{
|
||||
if (empty($pSheet->getParent()->getUnparsedLoadedData()['sheets'][$pSheet->getCodeName()]['AlternateContents'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($pSheet->getParent()->getUnparsedLoadedData()['sheets'][$pSheet->getCodeName()]['AlternateContents'] as $alternateContent) {
|
||||
$objWriter->writeRaw($alternateContent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,103 @@
|
|||
<?php
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheetTests\Writer\Xlsx;
|
||||
|
||||
use Exception;
|
||||
use PhpOffice\PhpSpreadsheet\Settings;
|
||||
use PhpOffice\PhpSpreadsheet\Shared\File;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use ZipArchive;
|
||||
|
||||
class UnparsedDataTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* Test load and save Xlsx file with unparsed data (form elements, protected sheets, alternate contents, printer settings,..).
|
||||
*/
|
||||
public function testLoadSaveXlsxWithUnparsedData()
|
||||
{
|
||||
$sampleFilename = './data/Writer/XLSX/form_pass_print.xlsm';
|
||||
$resultFilename = tempnam(File::sysGetTempDir(), 'phpspreadsheet-test');
|
||||
Settings::setLibXmlLoaderOptions(null); // reset to default options
|
||||
$reader = new \PhpOffice\PhpSpreadsheet\Reader\Xlsx();
|
||||
$excel = $reader->load($sampleFilename);
|
||||
|
||||
$excel->getSheet(1)->setCellValue('B1', '222');
|
||||
|
||||
$writer = new \PhpOffice\PhpSpreadsheet\Writer\Xlsx($excel);
|
||||
$writer->save($resultFilename);
|
||||
self::assertFileExists($resultFilename);
|
||||
|
||||
$resultZip = new ZipArchive();
|
||||
$resultZip->open($resultFilename);
|
||||
$resultContentTypesRaw = $resultZip->getFromName('[Content_Types].xml');
|
||||
$resultControlPropRaw = $resultZip->getFromName('xl/ctrlProps/ctrlProp1.xml');
|
||||
$resultDrawingRaw = $resultZip->getFromName('xl/drawings/drawing1.xml');
|
||||
$resultVmlDrawingRaw = $resultZip->getFromName('xl/drawings/vmlDrawing1.vml');
|
||||
$resultPrinterSettingsRaw = $resultZip->getFromName('xl/printerSettings/printerSettings1.bin');
|
||||
$resultVbaProjectRaw = $resultZip->getFromName('xl/vbaProject.bin');
|
||||
$resultWorkbookRaw = $resultZip->getFromName('xl/workbook.xml');
|
||||
$resultSheet1RelsRaw = $resultZip->getFromName('xl/worksheets/_rels/sheet1.xml.rels');
|
||||
$resultSheet1Raw = $resultZip->getFromName('xl/worksheets/sheet1.xml');
|
||||
$resultSheet2Raw = $resultZip->getFromName('xl/worksheets/sheet2.xml');
|
||||
if (false === $resultZip->close()) {
|
||||
throw new Exception("Could not close zip file \"{$resultFilename}\".");
|
||||
}
|
||||
unlink($resultFilename);
|
||||
|
||||
// [Content_Types].xml
|
||||
$this->assertTrue(strpos($resultContentTypesRaw, 'application/vnd.openxmlformats-officedocument.spreadsheetml.printerSettings') > 0, 'Content type for printerSettings not found!');
|
||||
$this->assertTrue(strpos($resultContentTypesRaw, 'application/vnd.ms-office.vbaProject') > 0, 'Content type for VbaProject not found!');
|
||||
$this->assertTrue(strpos($resultContentTypesRaw, 'application/vnd.ms-excel.controlproperties+xml') > 0, 'Content type for ctrlProp not found!');
|
||||
|
||||
// xl/ctrlProps/ctrlProp1.xml
|
||||
$this->assertTrue(!empty($resultControlPropRaw), 'ctrlProp not found!');
|
||||
|
||||
// xl/drawings/drawing1.xml
|
||||
$this->assertTrue(strpos($resultDrawingRaw, '<mc:AlternateContent') > 0, 'AlternateContent at drawing.xml not found!');
|
||||
|
||||
// xl/drawings/vmlDrawing1.vml
|
||||
$this->assertTrue(!empty($resultVmlDrawingRaw), 'vmlDrawing not found!');
|
||||
|
||||
// xl/printerSettings/printerSettings1.bin
|
||||
$this->assertTrue(!empty($resultPrinterSettingsRaw), 'printerSettings.bin not found!');
|
||||
|
||||
// xl/vbaProject.bin
|
||||
$this->assertTrue(!empty($resultVbaProjectRaw), 'vbaProject.bin not found!');
|
||||
|
||||
// xl/workbook.xml
|
||||
$xmlWorkbook = simplexml_load_string($resultWorkbookRaw, 'SimpleXMLElement', Settings::getLibXmlLoaderOptions());
|
||||
if (!$xmlWorkbook->workbookProtection) {
|
||||
$this->fail('workbook.xml/workbookProtection not found!');
|
||||
} else {
|
||||
$this->assertEquals($xmlWorkbook->workbookProtection['workbookPassword'], 'CBEB', 'workbook.xml/workbookProtection[workbookPassword] is wrong!');
|
||||
$this->assertEquals($xmlWorkbook->workbookProtection['lockStructure'], 'true', 'workbook.xml/workbookProtection[lockStructure] is wrong!');
|
||||
|
||||
$this->assertEquals($xmlWorkbook->sheets->sheet[0]['state'], '', 'workbook.xml/sheets/sheet[0][state] is wrong!');
|
||||
$this->assertEquals($xmlWorkbook->sheets->sheet[1]['state'], 'hidden', 'workbook.xml/sheets/sheet[1][state] is wrong!');
|
||||
}
|
||||
unset($xmlWorkbook);
|
||||
|
||||
// xl/worksheets/_rels/sheet1.xml.rels
|
||||
$this->assertTrue(strpos($resultSheet1RelsRaw, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/printerSettings') > 0, 'Sheet relation with printerSettings not found!');
|
||||
$this->assertTrue(strpos($resultSheet1RelsRaw, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/vmlDrawing') > 0, 'Sheet relation with vmlDrawing not found!');
|
||||
$this->assertTrue(strpos($resultSheet1RelsRaw, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/ctrlProp') > 0, 'Sheet relation with ctrlProp not found!');
|
||||
|
||||
// xl/worksheets/sheet1.xml
|
||||
$this->assertTrue(strpos($resultSheet1Raw, '<mc:AlternateContent') > 0, 'AlternateContent at sheet1.xml not found!');
|
||||
$xmlWorksheet = simplexml_load_string($resultSheet1Raw, 'SimpleXMLElement', Settings::getLibXmlLoaderOptions());
|
||||
$pageSetupAttributes = $xmlWorksheet->pageSetup->attributes('http://schemas.openxmlformats.org/officeDocument/2006/relationships');
|
||||
$this->assertTrue(!empty($pageSetupAttributes['id']), 'sheet1.xml/pageSetup[r:id] not found!');
|
||||
if (!$xmlWorksheet->sheetProtection) {
|
||||
$this->fail('sheet1.xml/sheetProtection not found!');
|
||||
} else {
|
||||
$this->assertEquals($xmlWorksheet->sheetProtection['password'], 'CBEB', 'sheet1.xml/sheetProtection[password] is wrong!');
|
||||
$this->assertEquals($xmlWorksheet->sheetProtection['sheet'], 'true', 'sheet1.xml/sheetProtection[sheet] is wrong!');
|
||||
$this->assertEquals($xmlWorksheet->sheetProtection['objects'], 'true', 'sheet1.xml/sheetProtection[objects] is wrong!');
|
||||
$this->assertEquals($xmlWorksheet->sheetProtection['scenarios'], 'true', 'sheet1.xml/sheetProtection[scenarios] is wrong!');
|
||||
}
|
||||
unset($xmlWorksheet);
|
||||
|
||||
// xl/worksheets/sheet2.xml
|
||||
$this->assertTrue(!empty($resultSheet2Raw), 'sheet2.xml not found!');
|
||||
}
|
||||
}
|
Binary file not shown.
Loading…
Reference in New Issue