This commit is contained in:
steven 2025-08-11 22:23:30 +02:00
commit 72a26edcff
22092 changed files with 2101903 additions and 0 deletions

View file

@ -0,0 +1,125 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Writer\Xlsx;
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
use PhpOffice\PhpSpreadsheet\Shared\XMLWriter;
use PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column;
use PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column\Rule;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet as ActualWorksheet;
class AutoFilter extends WriterPart
{
/**
* Write AutoFilter.
*/
public static function writeAutoFilter(XMLWriter $objWriter, ActualWorksheet $worksheet): void
{
$autoFilterRange = $worksheet->getAutoFilter()->getRange();
if (!empty($autoFilterRange)) {
// autoFilter
$objWriter->startElement('autoFilter');
// Strip any worksheet reference from the filter coordinates
$range = Coordinate::splitRange($autoFilterRange);
$range = $range[0];
// Strip any worksheet ref
[$ws, $range[0]] = ActualWorksheet::extractSheetTitle($range[0], true);
$range = implode(':', $range);
$objWriter->writeAttribute('ref', str_replace('$', '', $range));
$columns = $worksheet->getAutoFilter()->getColumns();
if (count($columns) > 0) {
foreach ($columns as $columnID => $column) {
$colId = $worksheet->getAutoFilter()->getColumnOffset($columnID);
self::writeAutoFilterColumn($objWriter, $column, $colId);
}
}
$objWriter->endElement();
}
}
/**
* Write AutoFilter's filterColumn.
*/
public static function writeAutoFilterColumn(XMLWriter $objWriter, Column $column, int $colId): void
{
$rules = $column->getRules();
if (count($rules) > 0) {
$objWriter->startElement('filterColumn');
$objWriter->writeAttribute('colId', "$colId");
$objWriter->startElement($column->getFilterType());
if ($column->getJoin() == Column::AUTOFILTER_COLUMN_JOIN_AND) {
$objWriter->writeAttribute('and', '1');
}
foreach ($rules as $rule) {
self::writeAutoFilterColumnRule($column, $rule, $objWriter);
}
$objWriter->endElement();
$objWriter->endElement();
}
}
/**
* Write AutoFilter's filterColumn Rule.
*/
private static function writeAutoFilterColumnRule(Column $column, Rule $rule, XMLWriter $objWriter): void
{
if (
($column->getFilterType() === Column::AUTOFILTER_FILTERTYPE_FILTER)
&& ($rule->getOperator() === Rule::AUTOFILTER_COLUMN_RULE_EQUAL)
&& ($rule->getValue() === '')
) {
// Filter rule for Blanks
$objWriter->writeAttribute('blank', '1');
} elseif ($rule->getRuleType() === Rule::AUTOFILTER_RULETYPE_DYNAMICFILTER) {
// Dynamic Filter Rule
$objWriter->writeAttribute('type', $rule->getGrouping());
$val = $column->getAttribute('val');
if ($val !== null) {
$objWriter->writeAttribute('val', "$val");
}
$maxVal = $column->getAttribute('maxVal');
if ($maxVal !== null) {
$objWriter->writeAttribute('maxVal', "$maxVal");
}
} elseif ($rule->getRuleType() === Rule::AUTOFILTER_RULETYPE_TOPTENFILTER) {
// Top 10 Filter Rule
$ruleValue = $rule->getValue();
if (!is_array($ruleValue)) {
$objWriter->writeAttribute('val', "$ruleValue");
}
$objWriter->writeAttribute('percent', (($rule->getOperator() === Rule::AUTOFILTER_COLUMN_RULE_TOPTEN_PERCENT) ? '1' : '0'));
$objWriter->writeAttribute('top', (($rule->getGrouping() === Rule::AUTOFILTER_COLUMN_RULE_TOPTEN_TOP) ? '1' : '0'));
} else {
// Filter, DateGroupItem or CustomFilter
$objWriter->startElement($rule->getRuleType());
if ($rule->getOperator() !== Rule::AUTOFILTER_COLUMN_RULE_EQUAL) {
$objWriter->writeAttribute('operator', $rule->getOperator());
}
if ($rule->getRuleType() === Rule::AUTOFILTER_RULETYPE_DATEGROUP) {
// Date Group filters
$ruleValue = $rule->getValue();
if (is_array($ruleValue)) {
foreach ($ruleValue as $key => $value) {
$objWriter->writeAttribute($key, "$value");
}
}
$objWriter->writeAttribute('dateTimeGrouping', $rule->getGrouping());
} else {
$ruleValue = $rule->getValue();
if (!is_array($ruleValue)) {
$objWriter->writeAttribute('val', "$ruleValue");
}
}
$objWriter->endElement();
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,236 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Writer\Xlsx;
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
use PhpOffice\PhpSpreadsheet\Comment;
use PhpOffice\PhpSpreadsheet\Reader\Xlsx\Namespaces;
use PhpOffice\PhpSpreadsheet\Shared\XMLWriter;
class Comments extends WriterPart
{
/**
* Write comments to XML format.
*
* @return string XML Output
*/
public function writeComments(\PhpOffice\PhpSpreadsheet\Worksheet\Worksheet $worksheet): string
{
// 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');
// Comments cache
$comments = $worksheet->getComments();
// Authors cache
$authors = [];
$authorId = 0;
foreach ($comments as $comment) {
if (!isset($authors[$comment->getAuthor()])) {
$authors[$comment->getAuthor()] = $authorId++;
}
}
// comments
$objWriter->startElement('comments');
$objWriter->writeAttribute('xmlns', Namespaces::MAIN);
// Loop through authors
$objWriter->startElement('authors');
foreach ($authors as $author => $index) {
$objWriter->writeElement('author', $author);
}
$objWriter->endElement();
// Loop through comments
$objWriter->startElement('commentList');
foreach ($comments as $key => $value) {
$this->writeComment($objWriter, $key, $value, $authors);
}
$objWriter->endElement();
$objWriter->endElement();
// Return
return $objWriter->getData();
}
/**
* Write comment to XML format.
*
* @param string $cellReference Cell reference
* @param Comment $comment Comment
* @param array $authors Array of authors
*/
private function writeComment(XMLWriter $objWriter, string $cellReference, Comment $comment, array $authors): void
{
// comment
$objWriter->startElement('comment');
$objWriter->writeAttribute('ref', $cellReference);
$objWriter->writeAttribute('authorId', $authors[$comment->getAuthor()]);
// text
$objWriter->startElement('text');
$this->getParentWriter()->getWriterPartstringtable()->writeRichText($objWriter, $comment->getText());
$objWriter->endElement();
$objWriter->endElement();
}
/**
* Write VML comments to XML format.
*
* @return string XML Output
*/
public function writeVMLComments(\PhpOffice\PhpSpreadsheet\Worksheet\Worksheet $worksheet): string
{
// 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');
// Comments cache
$comments = $worksheet->getComments();
// xml
$objWriter->startElement('xml');
$objWriter->writeAttribute('xmlns:v', Namespaces::URN_VML);
$objWriter->writeAttribute('xmlns:o', Namespaces::URN_MSOFFICE);
$objWriter->writeAttribute('xmlns:x', Namespaces::URN_EXCEL);
// o:shapelayout
$objWriter->startElement('o:shapelayout');
$objWriter->writeAttribute('v:ext', 'edit');
// o:idmap
$objWriter->startElement('o:idmap');
$objWriter->writeAttribute('v:ext', 'edit');
$objWriter->writeAttribute('data', '1');
$objWriter->endElement();
$objWriter->endElement();
// v:shapetype
$objWriter->startElement('v:shapetype');
$objWriter->writeAttribute('id', '_x0000_t202');
$objWriter->writeAttribute('coordsize', '21600,21600');
$objWriter->writeAttribute('o:spt', '202');
$objWriter->writeAttribute('path', 'm,l,21600r21600,l21600,xe');
// v:stroke
$objWriter->startElement('v:stroke');
$objWriter->writeAttribute('joinstyle', 'miter');
$objWriter->endElement();
// v:path
$objWriter->startElement('v:path');
$objWriter->writeAttribute('gradientshapeok', 't');
$objWriter->writeAttribute('o:connecttype', 'rect');
$objWriter->endElement();
$objWriter->endElement();
// Loop through comments
foreach ($comments as $key => $value) {
$this->writeVMLComment($objWriter, $key, $value);
}
$objWriter->endElement();
// Return
return $objWriter->getData();
}
/**
* Write VML comment to XML format.
*
* @param string $cellReference Cell reference, eg: 'A1'
* @param Comment $comment Comment
*/
private function writeVMLComment(XMLWriter $objWriter, string $cellReference, Comment $comment): void
{
// Metadata
[$column, $row] = Coordinate::indexesFromString($cellReference);
$id = 1024 + $column + $row;
$id = substr("$id", 0, 4);
// v:shape
$objWriter->startElement('v:shape');
$objWriter->writeAttribute('id', '_x0000_s' . $id);
$objWriter->writeAttribute('type', '#_x0000_t202');
$objWriter->writeAttribute('style', 'position:absolute;margin-left:' . $comment->getMarginLeft() . ';margin-top:' . $comment->getMarginTop() . ';width:' . $comment->getWidth() . ';height:' . $comment->getHeight() . ';z-index:1;visibility:' . ($comment->getVisible() ? 'visible' : 'hidden'));
$objWriter->writeAttribute('fillcolor', '#' . $comment->getFillColor()->getRGB());
$objWriter->writeAttribute('o:insetmode', 'auto');
// v:fill
$objWriter->startElement('v:fill');
$objWriter->writeAttribute('color2', '#' . $comment->getFillColor()->getRGB());
if ($comment->hasBackgroundImage()) {
$bgImage = $comment->getBackgroundImage();
$objWriter->writeAttribute('o:relid', 'rId' . $bgImage->getImageIndex());
$objWriter->writeAttribute('o:title', $bgImage->getName());
$objWriter->writeAttribute('type', 'frame');
}
$objWriter->endElement();
// v:shadow
$objWriter->startElement('v:shadow');
$objWriter->writeAttribute('on', 't');
$objWriter->writeAttribute('color', 'black');
$objWriter->writeAttribute('obscured', 't');
$objWriter->endElement();
// v:path
$objWriter->startElement('v:path');
$objWriter->writeAttribute('o:connecttype', 'none');
$objWriter->endElement();
// v:textbox
$objWriter->startElement('v:textbox');
$objWriter->writeAttribute('style', 'mso-direction-alt:auto');
// div
$objWriter->startElement('div');
$objWriter->writeAttribute('style', 'text-align:left');
$objWriter->endElement();
$objWriter->endElement();
// x:ClientData
$objWriter->startElement('x:ClientData');
$objWriter->writeAttribute('ObjectType', 'Note');
// x:MoveWithCells
$objWriter->writeElement('x:MoveWithCells', '');
// x:SizeWithCells
$objWriter->writeElement('x:SizeWithCells', '');
// x:AutoFill
$objWriter->writeElement('x:AutoFill', 'False');
// x:Row
$objWriter->writeElement('x:Row', (string) ($row - 1));
// x:Column
$objWriter->writeElement('x:Column', (string) ($column - 1));
$objWriter->endElement();
$objWriter->endElement();
}
}

View file

@ -0,0 +1,275 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Writer\Xlsx;
use PhpOffice\PhpSpreadsheet\Reader\Xlsx\Namespaces;
use PhpOffice\PhpSpreadsheet\Shared\File;
use PhpOffice\PhpSpreadsheet\Shared\XMLWriter;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Worksheet\MemoryDrawing;
use PhpOffice\PhpSpreadsheet\Writer\Exception as WriterException;
class ContentTypes extends WriterPart
{
/**
* Write content types to XML format.
*
* @param bool $includeCharts Flag indicating if we should include drawing details for charts
*
* @return string XML Output
*/
public function writeContentTypes(Spreadsheet $spreadsheet, bool $includeCharts = false): string
{
// 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');
// Types
$objWriter->startElement('Types');
$objWriter->writeAttribute('xmlns', Namespaces::CONTENT_TYPES);
// Theme
$this->writeOverrideContentType($objWriter, '/xl/theme/theme1.xml', 'application/vnd.openxmlformats-officedocument.theme+xml');
// Styles
$this->writeOverrideContentType($objWriter, '/xl/styles.xml', 'application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml');
// Rels
$this->writeDefaultContentType($objWriter, 'rels', 'application/vnd.openxmlformats-package.relationships+xml');
// XML
$this->writeDefaultContentType($objWriter, 'xml', 'application/xml');
// VML
$this->writeDefaultContentType($objWriter, 'vml', 'application/vnd.openxmlformats-officedocument.vmlDrawing');
// Workbook
if ($spreadsheet->hasMacros()) { //Macros in workbook ?
// 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
// 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
$this->writeOverrideContentType($objWriter, '/xl/vbaProjectSignature.bin', 'application/vnd.ms-office.vbaProjectSignature');
}
} else {
// no macros in workbook, so standard type
$this->writeOverrideContentType($objWriter, '/xl/workbook.xml', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml');
}
// DocProps
$this->writeOverrideContentType($objWriter, '/docProps/app.xml', 'application/vnd.openxmlformats-officedocument.extended-properties+xml');
$this->writeOverrideContentType($objWriter, '/docProps/core.xml', 'application/vnd.openxmlformats-package.core-properties+xml');
$customPropertyList = $spreadsheet->getProperties()->getCustomProperties();
if (!empty($customPropertyList)) {
$this->writeOverrideContentType($objWriter, '/docProps/custom.xml', 'application/vnd.openxmlformats-officedocument.custom-properties+xml');
}
// Worksheets
$sheetCount = $spreadsheet->getSheetCount();
for ($i = 0; $i < $sheetCount; ++$i) {
$this->writeOverrideContentType($objWriter, '/xl/worksheets/sheet' . ($i + 1) . '.xml', 'application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml');
}
// Shared strings
$this->writeOverrideContentType($objWriter, '/xl/sharedStrings.xml', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml');
// Table
$table = 1;
for ($i = 0; $i < $sheetCount; ++$i) {
$tableCount = $spreadsheet->getSheet($i)->getTableCollection()->count();
for ($t = 1; $t <= $tableCount; ++$t) {
$this->writeOverrideContentType($objWriter, '/xl/tables/table' . $table++ . '.xml', 'application/vnd.openxmlformats-officedocument.spreadsheetml.table+xml');
}
}
// Add worksheet relationship content types
$unparsedLoadedData = $spreadsheet->getUnparsedLoadedData();
$chart = 1;
for ($i = 0; $i < $sheetCount; ++$i) {
$drawings = $spreadsheet->getSheet($i)->getDrawingCollection();
$drawingCount = count($drawings);
$chartCount = ($includeCharts) ? $spreadsheet->getSheet($i)->getChartCount() : 0;
$hasUnparsedDrawing = isset($unparsedLoadedData['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) || $hasUnparsedDrawing) {
$this->writeOverrideContentType($objWriter, '/xl/drawings/drawing' . ($i + 1) . '.xml', 'application/vnd.openxmlformats-officedocument.drawing+xml');
}
// If we have charts, then we need a chart relationship for every individual chart
if ($chartCount > 0) {
for ($c = 0; $c < $chartCount; ++$c) {
$this->writeOverrideContentType($objWriter, '/xl/charts/chart' . $chart++ . '.xml', 'application/vnd.openxmlformats-officedocument.drawingml.chart+xml');
}
}
}
// Comments
for ($i = 0; $i < $sheetCount; ++$i) {
if (count($spreadsheet->getSheet($i)->getComments()) > 0) {
$this->writeOverrideContentType($objWriter, '/xl/comments' . ($i + 1) . '.xml', 'application/vnd.openxmlformats-officedocument.spreadsheetml.comments+xml');
}
}
// Add media content-types
$aMediaContentTypes = [];
$mediaCount = $this->getParentWriter()->getDrawingHashTable()->count();
for ($i = 0; $i < $mediaCount; ++$i) {
$extension = '';
$mimeType = '';
if ($this->getParentWriter()->getDrawingHashTable()->getByIndex($i) instanceof \PhpOffice\PhpSpreadsheet\Worksheet\Drawing) {
$extension = strtolower($this->getParentWriter()->getDrawingHashTable()->getByIndex($i)->getExtension());
$mimeType = $this->getImageMimeType($this->getParentWriter()->getDrawingHashTable()->getByIndex($i)->getPath());
} elseif ($this->getParentWriter()->getDrawingHashTable()->getByIndex($i) instanceof MemoryDrawing) {
$extension = strtolower($this->getParentWriter()->getDrawingHashTable()->getByIndex($i)->getMimeType());
$extension = explode('/', $extension);
$extension = $extension[1];
$mimeType = $this->getParentWriter()->getDrawingHashTable()->getByIndex($i)->getMimeType();
}
if (!isset($aMediaContentTypes[$extension])) {
$aMediaContentTypes[$extension] = $mimeType;
$this->writeDefaultContentType($objWriter, $extension, $mimeType);
}
}
if ($spreadsheet->hasRibbonBinObjects()) {
// Some additional objects in the ribbon ?
// we need to write "Extension" but not already write for media content
$tabRibbonTypes = array_diff($spreadsheet->getRibbonBinObjects('types') ?? [], array_keys($aMediaContentTypes));
foreach ($tabRibbonTypes as $aRibbonType) {
$mimeType = 'image/.' . $aRibbonType; //we wrote $mimeType like customUI Editor
$this->writeDefaultContentType($objWriter, $aRibbonType, $mimeType);
}
}
$sheetCount = $spreadsheet->getSheetCount();
for ($i = 0; $i < $sheetCount; ++$i) {
if (count($spreadsheet->getSheet($i)->getHeaderFooter()->getImages()) > 0) {
foreach ($spreadsheet->getSheet($i)->getHeaderFooter()->getImages() as $image) {
if (!isset($aMediaContentTypes[strtolower($image->getExtension())])) {
$aMediaContentTypes[strtolower($image->getExtension())] = $this->getImageMimeType($image->getPath());
$this->writeDefaultContentType($objWriter, strtolower($image->getExtension()), $aMediaContentTypes[strtolower($image->getExtension())]);
}
}
}
if (count($spreadsheet->getSheet($i)->getComments()) > 0) {
foreach ($spreadsheet->getSheet($i)->getComments() as $comment) {
if (!$comment->hasBackgroundImage()) {
continue;
}
$bgImage = $comment->getBackgroundImage();
$bgImageExtentionKey = strtolower($bgImage->getImageFileExtensionForSave(false));
if (!isset($aMediaContentTypes[$bgImageExtentionKey])) {
$aMediaContentTypes[$bgImageExtentionKey] = $bgImage->getImageMimeType();
$this->writeDefaultContentType($objWriter, $bgImageExtentionKey, $aMediaContentTypes[$bgImageExtentionKey]);
}
}
}
$bgImage = $spreadsheet->getSheet($i)->getBackgroundImage();
$mimeType = $spreadsheet->getSheet($i)->getBackgroundMime();
$extension = $spreadsheet->getSheet($i)->getBackgroundExtension();
if ($bgImage !== '' && !isset($aMediaContentTypes[$mimeType])) {
$this->writeDefaultContentType($objWriter, $extension, $mimeType);
}
}
// unparsed defaults
if (isset($unparsedLoadedData['default_content_types'])) {
foreach ($unparsedLoadedData['default_content_types'] as $extName => $contentType) {
$this->writeDefaultContentType($objWriter, $extName, $contentType);
}
}
// unparsed overrides
if (isset($unparsedLoadedData['override_content_types'])) {
foreach ($unparsedLoadedData['override_content_types'] as $partName => $overrideType) {
$this->writeOverrideContentType($objWriter, $partName, $overrideType);
}
}
$objWriter->endElement();
// Return
return $objWriter->getData();
}
private static int $three = 3; // phpstan silliness
/**
* Get image mime type.
*
* @param string $filename Filename
*
* @return string Mime Type
*/
private function getImageMimeType(string $filename): string
{
if (File::fileExists($filename)) {
$image = getimagesize($filename);
return image_type_to_mime_type((is_array($image) && count($image) >= self::$three) ? $image[2] : 0);
}
throw new WriterException("File $filename does not exist");
}
/**
* Write Default content type.
*
* @param string $partName Part name
* @param string $contentType Content type
*/
private function writeDefaultContentType(XMLWriter $objWriter, string $partName, string $contentType): void
{
if ($partName != '' && $contentType != '') {
// Write content type
$objWriter->startElement('Default');
$objWriter->writeAttribute('Extension', $partName);
$objWriter->writeAttribute('ContentType', $contentType);
$objWriter->endElement();
} else {
throw new WriterException('Invalid parameters passed.');
}
}
/**
* Write Override content type.
*
* @param string $partName Part name
* @param string $contentType Content type
*/
private function writeOverrideContentType(XMLWriter $objWriter, string $partName, string $contentType): void
{
if ($partName != '' && $contentType != '') {
// Write content type
$objWriter->startElement('Override');
$objWriter->writeAttribute('PartName', $partName);
$objWriter->writeAttribute('ContentType', $contentType);
$objWriter->endElement();
} else {
throw new WriterException('Invalid parameters passed.');
}
}
}

View file

@ -0,0 +1,242 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Writer\Xlsx;
use Exception;
use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
use PhpOffice\PhpSpreadsheet\DefinedName;
use PhpOffice\PhpSpreadsheet\Shared\XMLWriter;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet as ActualWorksheet;
class DefinedNames
{
private XMLWriter $objWriter;
private Spreadsheet $spreadsheet;
public function __construct(XMLWriter $objWriter, Spreadsheet $spreadsheet)
{
$this->objWriter = $objWriter;
$this->spreadsheet = $spreadsheet;
}
public function write(): void
{
// Write defined names
$this->objWriter->startElement('definedNames');
// Named ranges
if (count($this->spreadsheet->getDefinedNames()) > 0) {
// Named ranges
$this->writeNamedRangesAndFormulae();
}
// Other defined names
$sheetCount = $this->spreadsheet->getSheetCount();
for ($i = 0; $i < $sheetCount; ++$i) {
// NamedRange for autoFilter
$this->writeNamedRangeForAutofilter($this->spreadsheet->getSheet($i), $i);
// NamedRange for Print_Titles
$this->writeNamedRangeForPrintTitles($this->spreadsheet->getSheet($i), $i);
// NamedRange for Print_Area
$this->writeNamedRangeForPrintArea($this->spreadsheet->getSheet($i), $i);
}
$this->objWriter->endElement();
}
/**
* Write defined names.
*/
private function writeNamedRangesAndFormulae(): void
{
// Loop named ranges
$definedNames = $this->spreadsheet->getDefinedNames();
foreach ($definedNames as $definedName) {
$this->writeDefinedName($definedName);
}
}
/**
* Write Defined Name for named range.
*/
private function writeDefinedName(DefinedName $definedName): void
{
// definedName for named range
$local = -1;
if ($definedName->getLocalOnly() && $definedName->getScope() !== null) {
try {
$local = $definedName->getScope()->getParentOrThrow()->getIndex($definedName->getScope());
} catch (Exception) {
// See issue 2266 - deleting sheet which contains
// defined names will cause Exception above.
return;
}
}
$this->objWriter->startElement('definedName');
$this->objWriter->writeAttribute('name', $definedName->getName());
if ($local >= 0) {
$this->objWriter->writeAttribute(
'localSheetId',
"$local"
);
}
$definedRange = $this->getDefinedRange($definedName);
$this->objWriter->writeRawData($definedRange);
$this->objWriter->endElement();
}
/**
* Write Defined Name for autoFilter.
*/
private function writeNamedRangeForAutofilter(ActualWorksheet $worksheet, int $worksheetId = 0): void
{
// NamedRange for autoFilter
$autoFilterRange = $worksheet->getAutoFilter()->getRange();
if (!empty($autoFilterRange)) {
$this->objWriter->startElement('definedName');
$this->objWriter->writeAttribute('name', '_xlnm._FilterDatabase');
$this->objWriter->writeAttribute('localSheetId', "$worksheetId");
$this->objWriter->writeAttribute('hidden', '1');
// Create absolute coordinate and write as raw text
$range = Coordinate::splitRange($autoFilterRange);
$range = $range[0];
// Strip any worksheet ref so we can make the cell ref absolute
[, $range[0]] = ActualWorksheet::extractSheetTitle($range[0], true);
$range[0] = Coordinate::absoluteCoordinate($range[0] ?? '');
if (count($range) > 1) {
$range[1] = Coordinate::absoluteCoordinate($range[1]);
}
$range = implode(':', $range);
$this->objWriter->writeRawData('\'' . str_replace("'", "''", $worksheet->getTitle()) . '\'!' . $range);
$this->objWriter->endElement();
}
}
/**
* Write Defined Name for PrintTitles.
*/
private function writeNamedRangeForPrintTitles(ActualWorksheet $worksheet, int $worksheetId = 0): void
{
// NamedRange for PrintTitles
if ($worksheet->getPageSetup()->isColumnsToRepeatAtLeftSet() || $worksheet->getPageSetup()->isRowsToRepeatAtTopSet()) {
$this->objWriter->startElement('definedName');
$this->objWriter->writeAttribute('name', '_xlnm.Print_Titles');
$this->objWriter->writeAttribute('localSheetId', "$worksheetId");
// Setting string
$settingString = '';
// Columns to repeat
if ($worksheet->getPageSetup()->isColumnsToRepeatAtLeftSet()) {
$repeat = $worksheet->getPageSetup()->getColumnsToRepeatAtLeft();
$settingString .= '\'' . str_replace("'", "''", $worksheet->getTitle()) . '\'!$' . $repeat[0] . ':$' . $repeat[1];
}
// Rows to repeat
if ($worksheet->getPageSetup()->isRowsToRepeatAtTopSet()) {
if ($worksheet->getPageSetup()->isColumnsToRepeatAtLeftSet()) {
$settingString .= ',';
}
$repeat = $worksheet->getPageSetup()->getRowsToRepeatAtTop();
$settingString .= '\'' . str_replace("'", "''", $worksheet->getTitle()) . '\'!$' . $repeat[0] . ':$' . $repeat[1];
}
$this->objWriter->writeRawData($settingString);
$this->objWriter->endElement();
}
}
/**
* Write Defined Name for PrintTitles.
*/
private function writeNamedRangeForPrintArea(ActualWorksheet $worksheet, int $worksheetId = 0): void
{
// NamedRange for PrintArea
if ($worksheet->getPageSetup()->isPrintAreaSet()) {
$this->objWriter->startElement('definedName');
$this->objWriter->writeAttribute('name', '_xlnm.Print_Area');
$this->objWriter->writeAttribute('localSheetId', "$worksheetId");
// Print area
$printArea = Coordinate::splitRange($worksheet->getPageSetup()->getPrintArea());
$chunks = [];
foreach ($printArea as $printAreaRect) {
$printAreaRect[0] = Coordinate::absoluteReference($printAreaRect[0]);
$printAreaRect[1] = Coordinate::absoluteReference($printAreaRect[1]);
$chunks[] = '\'' . str_replace("'", "''", $worksheet->getTitle()) . '\'!' . implode(':', $printAreaRect);
}
$this->objWriter->writeRawData(implode(',', $chunks));
$this->objWriter->endElement();
}
}
private function getDefinedRange(DefinedName $definedName): string
{
$definedRange = $definedName->getValue();
$splitCount = preg_match_all(
'/' . Calculation::CALCULATION_REGEXP_CELLREF_RELATIVE . '/mui',
$definedRange,
$splitRanges,
PREG_OFFSET_CAPTURE
);
$lengths = array_map('strlen', array_column($splitRanges[0], 0));
$offsets = array_column($splitRanges[0], 1);
$worksheets = $splitRanges[2];
$columns = $splitRanges[6];
$rows = $splitRanges[7];
while ($splitCount > 0) {
--$splitCount;
$length = $lengths[$splitCount];
$offset = $offsets[$splitCount];
$worksheet = $worksheets[$splitCount][0];
$column = $columns[$splitCount][0];
$row = $rows[$splitCount][0];
$newRange = '';
if (empty($worksheet)) {
if (($offset === 0) || ($definedRange[$offset - 1] !== ':')) {
// We should have a worksheet
$ws = $definedName->getWorksheet();
$worksheet = ($ws === null) ? null : $ws->getTitle();
}
} else {
$worksheet = str_replace("''", "'", trim($worksheet, "'"));
}
if (!empty($worksheet)) {
$newRange = "'" . str_replace("'", "''", $worksheet) . "'!";
}
$newRange = "{$newRange}{$column}{$row}";
$definedRange = substr($definedRange, 0, $offset) . $newRange . substr($definedRange, $offset + $length);
}
if (str_starts_with($definedRange, '=')) {
$definedRange = substr($definedRange, 1);
}
return $definedRange;
}
}

View file

@ -0,0 +1,250 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Writer\Xlsx;
use PhpOffice\PhpSpreadsheet\Document\Properties;
use PhpOffice\PhpSpreadsheet\Reader\Xlsx\Namespaces;
use PhpOffice\PhpSpreadsheet\Shared\Date;
use PhpOffice\PhpSpreadsheet\Shared\XMLWriter;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
class DocProps extends WriterPart
{
/**
* Write docProps/app.xml to XML format.
*
* @return string XML Output
*/
public function writeDocPropsApp(Spreadsheet $spreadsheet): string
{
// 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');
// Properties
$objWriter->startElement('Properties');
$objWriter->writeAttribute('xmlns', Namespaces::EXTENDED_PROPERTIES);
$objWriter->writeAttribute('xmlns:vt', Namespaces::PROPERTIES_VTYPES);
// Application
$objWriter->writeElement('Application', 'Microsoft Excel');
// DocSecurity
$objWriter->writeElement('DocSecurity', '0');
// ScaleCrop
$objWriter->writeElement('ScaleCrop', 'false');
// HeadingPairs
$objWriter->startElement('HeadingPairs');
// Vector
$objWriter->startElement('vt:vector');
$objWriter->writeAttribute('size', '2');
$objWriter->writeAttribute('baseType', 'variant');
// Variant
$objWriter->startElement('vt:variant');
$objWriter->writeElement('vt:lpstr', 'Worksheets');
$objWriter->endElement();
// Variant
$objWriter->startElement('vt:variant');
$objWriter->writeElement('vt:i4', (string) $spreadsheet->getSheetCount());
$objWriter->endElement();
$objWriter->endElement();
$objWriter->endElement();
// TitlesOfParts
$objWriter->startElement('TitlesOfParts');
// Vector
$objWriter->startElement('vt:vector');
$objWriter->writeAttribute('size', (string) $spreadsheet->getSheetCount());
$objWriter->writeAttribute('baseType', 'lpstr');
$sheetCount = $spreadsheet->getSheetCount();
for ($i = 0; $i < $sheetCount; ++$i) {
$objWriter->writeElement('vt:lpstr', $spreadsheet->getSheet($i)->getTitle());
}
$objWriter->endElement();
$objWriter->endElement();
// Company
$objWriter->writeElement('Company', $spreadsheet->getProperties()->getCompany());
// Company
$objWriter->writeElement('Manager', $spreadsheet->getProperties()->getManager());
// LinksUpToDate
$objWriter->writeElement('LinksUpToDate', 'false');
// SharedDoc
$objWriter->writeElement('SharedDoc', 'false');
// HyperlinkBase
$objWriter->writeElement('HyperlinkBase', $spreadsheet->getProperties()->getHyperlinkBase());
// HyperlinksChanged
$objWriter->writeElement('HyperlinksChanged', 'false');
// AppVersion
$objWriter->writeElement('AppVersion', '12.0000');
$objWriter->endElement();
// Return
return $objWriter->getData();
}
/**
* Write docProps/core.xml to XML format.
*
* @return string XML Output
*/
public function writeDocPropsCore(Spreadsheet $spreadsheet): string
{
// 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');
// cp:coreProperties
$objWriter->startElement('cp:coreProperties');
$objWriter->writeAttribute('xmlns:cp', Namespaces::CORE_PROPERTIES2);
$objWriter->writeAttribute('xmlns:dc', Namespaces::DC_ELEMENTS);
$objWriter->writeAttribute('xmlns:dcterms', Namespaces::DC_TERMS);
$objWriter->writeAttribute('xmlns:dcmitype', Namespaces::DC_DCMITYPE);
$objWriter->writeAttribute('xmlns:xsi', Namespaces::SCHEMA_INSTANCE);
// dc:creator
$objWriter->writeElement('dc:creator', $spreadsheet->getProperties()->getCreator());
// cp:lastModifiedBy
$objWriter->writeElement('cp:lastModifiedBy', $spreadsheet->getProperties()->getLastModifiedBy());
// dcterms:created
$objWriter->startElement('dcterms:created');
$objWriter->writeAttribute('xsi:type', 'dcterms:W3CDTF');
$created = $spreadsheet->getProperties()->getCreated();
$date = Date::dateTimeFromTimestamp("$created");
$objWriter->writeRawData($date->format(DATE_W3C));
$objWriter->endElement();
// dcterms:modified
$objWriter->startElement('dcterms:modified');
$objWriter->writeAttribute('xsi:type', 'dcterms:W3CDTF');
$created = $spreadsheet->getProperties()->getModified();
$date = Date::dateTimeFromTimestamp("$created");
$objWriter->writeRawData($date->format(DATE_W3C));
$objWriter->endElement();
// dc:title
$objWriter->writeElement('dc:title', $spreadsheet->getProperties()->getTitle());
// dc:description
$objWriter->writeElement('dc:description', $spreadsheet->getProperties()->getDescription());
// dc:subject
$objWriter->writeElement('dc:subject', $spreadsheet->getProperties()->getSubject());
// cp:keywords
$objWriter->writeElement('cp:keywords', $spreadsheet->getProperties()->getKeywords());
// cp:category
$objWriter->writeElement('cp:category', $spreadsheet->getProperties()->getCategory());
$objWriter->endElement();
// Return
return $objWriter->getData();
}
/**
* Write docProps/custom.xml to XML format.
*
* @return null|string XML Output
*/
public function writeDocPropsCustom(Spreadsheet $spreadsheet): ?string
{
$customPropertyList = $spreadsheet->getProperties()->getCustomProperties();
if (empty($customPropertyList)) {
return null;
}
// 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');
// cp:coreProperties
$objWriter->startElement('Properties');
$objWriter->writeAttribute('xmlns', Namespaces::CUSTOM_PROPERTIES);
$objWriter->writeAttribute('xmlns:vt', Namespaces::PROPERTIES_VTYPES);
foreach ($customPropertyList as $key => $customProperty) {
$propertyValue = $spreadsheet->getProperties()->getCustomPropertyValue($customProperty);
$propertyType = $spreadsheet->getProperties()->getCustomPropertyType($customProperty);
$objWriter->startElement('property');
$objWriter->writeAttribute('fmtid', '{D5CDD505-2E9C-101B-9397-08002B2CF9AE}');
$objWriter->writeAttribute('pid', (string) ($key + 2));
$objWriter->writeAttribute('name', $customProperty);
switch ($propertyType) {
case Properties::PROPERTY_TYPE_INTEGER:
$objWriter->writeElement('vt:i4', $propertyValue); // @phpstan-ignore-line
break;
case Properties::PROPERTY_TYPE_FLOAT:
$objWriter->writeElement('vt:r8', sprintf('%F', $propertyValue));
break;
case Properties::PROPERTY_TYPE_BOOLEAN:
$objWriter->writeElement('vt:bool', ($propertyValue) ? 'true' : 'false');
break;
case Properties::PROPERTY_TYPE_DATE:
$objWriter->startElement('vt:filetime');
$date = Date::dateTimeFromTimestamp("$propertyValue");
$objWriter->writeRawData($date->format(DATE_W3C));
$objWriter->endElement();
break;
default:
$objWriter->writeElement('vt:lpwstr', $propertyValue); // @phpstan-ignore-line
break;
}
$objWriter->endElement();
}
$objWriter->endElement();
return $objWriter->getData();
}
}

View file

@ -0,0 +1,583 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Writer\Xlsx;
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
use PhpOffice\PhpSpreadsheet\Reader\Xlsx\Namespaces;
use PhpOffice\PhpSpreadsheet\Shared\Drawing as SharedDrawing;
use PhpOffice\PhpSpreadsheet\Shared\XMLWriter;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Worksheet\BaseDrawing;
use PhpOffice\PhpSpreadsheet\Worksheet\HeaderFooterDrawing;
use PhpOffice\PhpSpreadsheet\Writer\Exception as WriterException;
class Drawing extends WriterPart
{
/**
* Write drawings to XML format.
*
* @param bool $includeCharts Flag indicating if we should include drawing details for charts
*
* @return string XML Output
*/
public function writeDrawings(\PhpOffice\PhpSpreadsheet\Worksheet\Worksheet $worksheet, bool $includeCharts = false): string
{
// 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');
// xdr:wsDr
$objWriter->startElement('xdr:wsDr');
$objWriter->writeAttribute('xmlns:xdr', Namespaces::SPREADSHEET_DRAWING);
$objWriter->writeAttribute('xmlns:a', Namespaces::DRAWINGML);
// Loop through images and write drawings
$i = 1;
$iterator = $worksheet->getDrawingCollection()->getIterator();
while ($iterator->valid()) {
/** @var BaseDrawing $pDrawing */
$pDrawing = $iterator->current();
$pRelationId = $i;
$hlinkClickId = $pDrawing->getHyperlink() === null ? null : ++$i;
$this->writeDrawing($objWriter, $pDrawing, $pRelationId, $hlinkClickId);
$iterator->next();
++$i;
}
if ($includeCharts) {
$chartCount = $worksheet->getChartCount();
// Loop through charts and write the chart position
if ($chartCount > 0) {
for ($c = 0; $c < $chartCount; ++$c) {
$chart = $worksheet->getChartByIndex((string) $c);
if ($chart !== false) {
$this->writeChart($objWriter, $chart, $c + $i);
}
}
}
}
// unparsed AlternateContent
$unparsedLoadedData = $worksheet->getParentOrThrow()->getUnparsedLoadedData();
if (isset($unparsedLoadedData['sheets'][$worksheet->getCodeName()]['drawingAlternateContents'])) {
foreach ($unparsedLoadedData['sheets'][$worksheet->getCodeName()]['drawingAlternateContents'] as $drawingAlternateContent) {
$objWriter->writeRaw($drawingAlternateContent);
}
}
$objWriter->endElement();
// Return
return $objWriter->getData();
}
/**
* Write drawings to XML format.
*/
public function writeChart(XMLWriter $objWriter, \PhpOffice\PhpSpreadsheet\Chart\Chart $chart, int $relationId = -1): void
{
$tl = $chart->getTopLeftPosition();
$tlColRow = Coordinate::indexesFromString($tl['cell']);
$br = $chart->getBottomRightPosition();
$isTwoCellAnchor = $br['cell'] !== '';
if ($isTwoCellAnchor) {
$brColRow = Coordinate::indexesFromString($br['cell']);
$objWriter->startElement('xdr:twoCellAnchor');
$objWriter->startElement('xdr:from');
$objWriter->writeElement('xdr:col', (string) ($tlColRow[0] - 1));
$objWriter->writeElement('xdr:colOff', self::stringEmu($tl['xOffset']));
$objWriter->writeElement('xdr:row', (string) ($tlColRow[1] - 1));
$objWriter->writeElement('xdr:rowOff', self::stringEmu($tl['yOffset']));
$objWriter->endElement();
$objWriter->startElement('xdr:to');
$objWriter->writeElement('xdr:col', (string) ($brColRow[0] - 1));
$objWriter->writeElement('xdr:colOff', self::stringEmu($br['xOffset']));
$objWriter->writeElement('xdr:row', (string) ($brColRow[1] - 1));
$objWriter->writeElement('xdr:rowOff', self::stringEmu($br['yOffset']));
$objWriter->endElement();
} elseif ($chart->getOneCellAnchor()) {
$objWriter->startElement('xdr:oneCellAnchor');
$objWriter->startElement('xdr:from');
$objWriter->writeElement('xdr:col', (string) ($tlColRow[0] - 1));
$objWriter->writeElement('xdr:colOff', self::stringEmu($tl['xOffset']));
$objWriter->writeElement('xdr:row', (string) ($tlColRow[1] - 1));
$objWriter->writeElement('xdr:rowOff', self::stringEmu($tl['yOffset']));
$objWriter->endElement();
$objWriter->startElement('xdr:ext');
$objWriter->writeAttribute('cx', self::stringEmu($br['xOffset']));
$objWriter->writeAttribute('cy', self::stringEmu($br['yOffset']));
$objWriter->endElement();
} else {
$objWriter->startElement('xdr:absoluteAnchor');
$objWriter->startElement('xdr:pos');
$objWriter->writeAttribute('x', '0');
$objWriter->writeAttribute('y', '0');
$objWriter->endElement();
$objWriter->startElement('xdr:ext');
$objWriter->writeAttribute('cx', self::stringEmu($br['xOffset']));
$objWriter->writeAttribute('cy', self::stringEmu($br['yOffset']));
$objWriter->endElement();
}
$objWriter->startElement('xdr:graphicFrame');
$objWriter->writeAttribute('macro', '');
$objWriter->startElement('xdr:nvGraphicFramePr');
$objWriter->startElement('xdr:cNvPr');
$objWriter->writeAttribute('name', 'Chart ' . $relationId);
$objWriter->writeAttribute('id', (string) (1025 * $relationId));
$objWriter->endElement();
$objWriter->startElement('xdr:cNvGraphicFramePr');
$objWriter->startElement('a:graphicFrameLocks');
$objWriter->endElement();
$objWriter->endElement();
$objWriter->endElement();
$objWriter->startElement('xdr:xfrm');
$objWriter->startElement('a:off');
$objWriter->writeAttribute('x', '0');
$objWriter->writeAttribute('y', '0');
$objWriter->endElement();
$objWriter->startElement('a:ext');
$objWriter->writeAttribute('cx', '0');
$objWriter->writeAttribute('cy', '0');
$objWriter->endElement();
$objWriter->endElement();
$objWriter->startElement('a:graphic');
$objWriter->startElement('a:graphicData');
$objWriter->writeAttribute('uri', Namespaces::CHART);
$objWriter->startElement('c:chart');
$objWriter->writeAttribute('xmlns:c', Namespaces::CHART);
$objWriter->writeAttribute('xmlns:r', Namespaces::SCHEMA_OFFICE_DOCUMENT);
$objWriter->writeAttribute('r:id', 'rId' . $relationId);
$objWriter->endElement();
$objWriter->endElement();
$objWriter->endElement();
$objWriter->endElement();
$objWriter->startElement('xdr:clientData');
$objWriter->endElement();
$objWriter->endElement();
}
/**
* Write drawings to XML format.
*/
public function writeDrawing(XMLWriter $objWriter, BaseDrawing $drawing, int $relationId = -1, ?int $hlinkClickId = null): void
{
if ($relationId >= 0) {
$isTwoCellAnchor = $drawing->getCoordinates2() !== '';
if ($isTwoCellAnchor) {
// xdr:twoCellAnchor
$objWriter->startElement('xdr:twoCellAnchor');
if ($drawing->validEditAs()) {
$objWriter->writeAttribute('editAs', $drawing->getEditAs());
}
// Image location
$aCoordinates = Coordinate::indexesFromString($drawing->getCoordinates());
$aCoordinates2 = Coordinate::indexesFromString($drawing->getCoordinates2());
// xdr:from
$objWriter->startElement('xdr:from');
$objWriter->writeElement('xdr:col', (string) ($aCoordinates[0] - 1));
$objWriter->writeElement('xdr:colOff', self::stringEmu($drawing->getOffsetX()));
$objWriter->writeElement('xdr:row', (string) ($aCoordinates[1] - 1));
$objWriter->writeElement('xdr:rowOff', self::stringEmu($drawing->getOffsetY()));
$objWriter->endElement();
// xdr:to
$objWriter->startElement('xdr:to');
$objWriter->writeElement('xdr:col', (string) ($aCoordinates2[0] - 1));
$objWriter->writeElement('xdr:colOff', self::stringEmu($drawing->getOffsetX2()));
$objWriter->writeElement('xdr:row', (string) ($aCoordinates2[1] - 1));
$objWriter->writeElement('xdr:rowOff', self::stringEmu($drawing->getOffsetY2()));
$objWriter->endElement();
} else {
// xdr:oneCellAnchor
$objWriter->startElement('xdr:oneCellAnchor');
// Image location
$aCoordinates = Coordinate::indexesFromString($drawing->getCoordinates());
// xdr:from
$objWriter->startElement('xdr:from');
$objWriter->writeElement('xdr:col', (string) ($aCoordinates[0] - 1));
$objWriter->writeElement('xdr:colOff', self::stringEmu($drawing->getOffsetX()));
$objWriter->writeElement('xdr:row', (string) ($aCoordinates[1] - 1));
$objWriter->writeElement('xdr:rowOff', self::stringEmu($drawing->getOffsetY()));
$objWriter->endElement();
// xdr:ext
$objWriter->startElement('xdr:ext');
$objWriter->writeAttribute('cx', self::stringEmu($drawing->getWidth()));
$objWriter->writeAttribute('cy', self::stringEmu($drawing->getHeight()));
$objWriter->endElement();
}
// xdr:pic
$objWriter->startElement('xdr:pic');
// xdr:nvPicPr
$objWriter->startElement('xdr:nvPicPr');
// xdr:cNvPr
$objWriter->startElement('xdr:cNvPr');
$objWriter->writeAttribute('id', (string) $relationId);
$objWriter->writeAttribute('name', $drawing->getName());
$objWriter->writeAttribute('descr', $drawing->getDescription());
//a:hlinkClick
$this->writeHyperLinkDrawing($objWriter, $hlinkClickId);
$objWriter->endElement();
// xdr:cNvPicPr
$objWriter->startElement('xdr:cNvPicPr');
// a:picLocks
$objWriter->startElement('a:picLocks');
$objWriter->writeAttribute('noChangeAspect', '1');
$objWriter->endElement();
$objWriter->endElement();
$objWriter->endElement();
// xdr:blipFill
$objWriter->startElement('xdr:blipFill');
// a:blip
$objWriter->startElement('a:blip');
$objWriter->writeAttribute('xmlns:r', Namespaces::SCHEMA_OFFICE_DOCUMENT);
$objWriter->writeAttribute('r:embed', 'rId' . $relationId);
$objWriter->endElement();
$srcRect = $drawing->getSrcRect();
if (!empty($srcRect)) {
$objWriter->startElement('a:srcRect');
foreach ($srcRect as $key => $value) {
$objWriter->writeAttribute($key, (string) $value);
}
$objWriter->endElement(); // a:srcRect
$objWriter->startElement('a:stretch');
$objWriter->endElement(); // a:stretch
} else {
// a:stretch
$objWriter->startElement('a:stretch');
$objWriter->writeElement('a:fillRect', null);
$objWriter->endElement();
}
$objWriter->endElement();
// xdr:spPr
$objWriter->startElement('xdr:spPr');
// a:xfrm
$objWriter->startElement('a:xfrm');
$objWriter->writeAttribute('rot', (string) SharedDrawing::degreesToAngle($drawing->getRotation()));
self::writeAttributeIf($objWriter, $drawing->getFlipVertical(), 'flipV', '1');
self::writeAttributeIf($objWriter, $drawing->getFlipHorizontal(), 'flipH', '1');
if ($isTwoCellAnchor) {
$objWriter->startElement('a:ext');
$objWriter->writeAttribute('cx', self::stringEmu($drawing->getWidth()));
$objWriter->writeAttribute('cy', self::stringEmu($drawing->getHeight()));
$objWriter->endElement();
}
$objWriter->endElement();
// a:prstGeom
$objWriter->startElement('a:prstGeom');
$objWriter->writeAttribute('prst', 'rect');
// a:avLst
$objWriter->writeElement('a:avLst', null);
$objWriter->endElement();
if ($drawing->getShadow()->getVisible()) {
// a:effectLst
$objWriter->startElement('a:effectLst');
// a:outerShdw
$objWriter->startElement('a:outerShdw');
$objWriter->writeAttribute('blurRad', self::stringEmu($drawing->getShadow()->getBlurRadius()));
$objWriter->writeAttribute('dist', self::stringEmu($drawing->getShadow()->getDistance()));
$objWriter->writeAttribute('dir', (string) SharedDrawing::degreesToAngle($drawing->getShadow()->getDirection()));
$objWriter->writeAttribute('algn', $drawing->getShadow()->getAlignment());
$objWriter->writeAttribute('rotWithShape', '0');
// a:srgbClr
$objWriter->startElement('a:srgbClr');
$objWriter->writeAttribute('val', $drawing->getShadow()->getColor()->getRGB());
// a:alpha
$objWriter->startElement('a:alpha');
$objWriter->writeAttribute('val', (string) ($drawing->getShadow()->getAlpha() * 1000));
$objWriter->endElement();
$objWriter->endElement();
$objWriter->endElement();
$objWriter->endElement();
}
$objWriter->endElement();
$objWriter->endElement();
// xdr:clientData
$objWriter->writeElement('xdr:clientData', null);
$objWriter->endElement();
} else {
throw new WriterException('Invalid parameters passed.');
}
}
/**
* Write VML header/footer images to XML format.
*
* @return string XML Output
*/
public function writeVMLHeaderFooterImages(\PhpOffice\PhpSpreadsheet\Worksheet\Worksheet $worksheet): string
{
// 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');
// Header/footer images
$images = $worksheet->getHeaderFooter()->getImages();
// xml
$objWriter->startElement('xml');
$objWriter->writeAttribute('xmlns:v', Namespaces::URN_VML);
$objWriter->writeAttribute('xmlns:o', Namespaces::URN_MSOFFICE);
$objWriter->writeAttribute('xmlns:x', Namespaces::URN_EXCEL);
// o:shapelayout
$objWriter->startElement('o:shapelayout');
$objWriter->writeAttribute('v:ext', 'edit');
// o:idmap
$objWriter->startElement('o:idmap');
$objWriter->writeAttribute('v:ext', 'edit');
$objWriter->writeAttribute('data', '1');
$objWriter->endElement();
$objWriter->endElement();
// v:shapetype
$objWriter->startElement('v:shapetype');
$objWriter->writeAttribute('id', '_x0000_t75');
$objWriter->writeAttribute('coordsize', '21600,21600');
$objWriter->writeAttribute('o:spt', '75');
$objWriter->writeAttribute('o:preferrelative', 't');
$objWriter->writeAttribute('path', 'm@4@5l@4@11@9@11@9@5xe');
$objWriter->writeAttribute('filled', 'f');
$objWriter->writeAttribute('stroked', 'f');
// v:stroke
$objWriter->startElement('v:stroke');
$objWriter->writeAttribute('joinstyle', 'miter');
$objWriter->endElement();
// v:formulas
$objWriter->startElement('v:formulas');
// v:f
$objWriter->startElement('v:f');
$objWriter->writeAttribute('eqn', 'if lineDrawn pixelLineWidth 0');
$objWriter->endElement();
// v:f
$objWriter->startElement('v:f');
$objWriter->writeAttribute('eqn', 'sum @0 1 0');
$objWriter->endElement();
// v:f
$objWriter->startElement('v:f');
$objWriter->writeAttribute('eqn', 'sum 0 0 @1');
$objWriter->endElement();
// v:f
$objWriter->startElement('v:f');
$objWriter->writeAttribute('eqn', 'prod @2 1 2');
$objWriter->endElement();
// v:f
$objWriter->startElement('v:f');
$objWriter->writeAttribute('eqn', 'prod @3 21600 pixelWidth');
$objWriter->endElement();
// v:f
$objWriter->startElement('v:f');
$objWriter->writeAttribute('eqn', 'prod @3 21600 pixelHeight');
$objWriter->endElement();
// v:f
$objWriter->startElement('v:f');
$objWriter->writeAttribute('eqn', 'sum @0 0 1');
$objWriter->endElement();
// v:f
$objWriter->startElement('v:f');
$objWriter->writeAttribute('eqn', 'prod @6 1 2');
$objWriter->endElement();
// v:f
$objWriter->startElement('v:f');
$objWriter->writeAttribute('eqn', 'prod @7 21600 pixelWidth');
$objWriter->endElement();
// v:f
$objWriter->startElement('v:f');
$objWriter->writeAttribute('eqn', 'sum @8 21600 0');
$objWriter->endElement();
// v:f
$objWriter->startElement('v:f');
$objWriter->writeAttribute('eqn', 'prod @7 21600 pixelHeight');
$objWriter->endElement();
// v:f
$objWriter->startElement('v:f');
$objWriter->writeAttribute('eqn', 'sum @10 21600 0');
$objWriter->endElement();
$objWriter->endElement();
// v:path
$objWriter->startElement('v:path');
$objWriter->writeAttribute('o:extrusionok', 'f');
$objWriter->writeAttribute('gradientshapeok', 't');
$objWriter->writeAttribute('o:connecttype', 'rect');
$objWriter->endElement();
// o:lock
$objWriter->startElement('o:lock');
$objWriter->writeAttribute('v:ext', 'edit');
$objWriter->writeAttribute('aspectratio', 't');
$objWriter->endElement();
$objWriter->endElement();
// Loop through images
foreach ($images as $key => $value) {
$this->writeVMLHeaderFooterImage($objWriter, $key, $value);
}
$objWriter->endElement();
// Return
return $objWriter->getData();
}
/**
* Write VML comment to XML format.
*
* @param string $reference Reference
*/
private function writeVMLHeaderFooterImage(XMLWriter $objWriter, string $reference, HeaderFooterDrawing $image): void
{
// Calculate object id
preg_match('{(\d+)}', md5($reference), $m);
$id = 1500 + ((int) substr($m[1], 0, 2) * 1);
// Calculate offset
$width = $image->getWidth();
$height = $image->getHeight();
$marginLeft = $image->getOffsetX();
$marginTop = $image->getOffsetY();
// v:shape
$objWriter->startElement('v:shape');
$objWriter->writeAttribute('id', $reference);
$objWriter->writeAttribute('o:spid', '_x0000_s' . $id);
$objWriter->writeAttribute('type', '#_x0000_t75');
$objWriter->writeAttribute('style', "position:absolute;margin-left:{$marginLeft}px;margin-top:{$marginTop}px;width:{$width}px;height:{$height}px;z-index:1");
// v:imagedata
$objWriter->startElement('v:imagedata');
$objWriter->writeAttribute('o:relid', 'rId' . $reference);
$objWriter->writeAttribute('o:title', $image->getName());
$objWriter->endElement();
// o:lock
$objWriter->startElement('o:lock');
$objWriter->writeAttribute('v:ext', 'edit');
$objWriter->writeAttribute('textRotation', 't');
$objWriter->endElement();
$objWriter->endElement();
}
/**
* Get an array of all drawings.
*
* @return BaseDrawing[] All drawings in PhpSpreadsheet
*/
public function allDrawings(Spreadsheet $spreadsheet): array
{
// Get an array of all drawings
$aDrawings = [];
// Loop through PhpSpreadsheet
$sheetCount = $spreadsheet->getSheetCount();
for ($i = 0; $i < $sheetCount; ++$i) {
// Loop through images and add to array
$iterator = $spreadsheet->getSheet($i)->getDrawingCollection()->getIterator();
while ($iterator->valid()) {
$aDrawings[] = $iterator->current();
$iterator->next();
}
}
return $aDrawings;
}
private function writeHyperLinkDrawing(XMLWriter $objWriter, ?int $hlinkClickId): void
{
if ($hlinkClickId === null) {
return;
}
$objWriter->startElement('a:hlinkClick');
$objWriter->writeAttribute('xmlns:r', Namespaces::SCHEMA_OFFICE_DOCUMENT);
$objWriter->writeAttribute('r:id', 'rId' . $hlinkClickId);
$objWriter->endElement();
}
private static function stringEmu(int $pixelValue): string
{
return (string) SharedDrawing::pixelsToEMU($pixelValue);
}
private static function writeAttributeIf(XMLWriter $objWriter, ?bool $condition, string $attr, string $val): void
{
if ($condition) {
$objWriter->writeAttribute($attr, $val);
}
}
}

View file

@ -0,0 +1,193 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Writer\Xlsx;
class FunctionPrefix
{
const XLFNREGEXP = '/(?:_xlfn\.)?((?:_xlws\.)?\b('
// functions added with Excel 2010
. 'beta[.]dist'
. '|beta[.]inv'
. '|binom[.]dist'
. '|binom[.]inv'
. '|ceiling[.]precise'
. '|chisq[.]dist'
. '|chisq[.]dist[.]rt'
. '|chisq[.]inv'
. '|chisq[.]inv[.]rt'
. '|chisq[.]test'
. '|confidence[.]norm'
. '|confidence[.]t'
. '|covariance[.]p'
. '|covariance[.]s'
. '|erf[.]precise'
. '|erfc[.]precise'
. '|expon[.]dist'
. '|f[.]dist'
. '|f[.]dist[.]rt'
. '|f[.]inv'
. '|f[.]inv[.]rt'
. '|f[.]test'
. '|floor[.]precise'
. '|gamma[.]dist'
. '|gamma[.]inv'
. '|gammaln[.]precise'
. '|lognorm[.]dist'
. '|lognorm[.]inv'
. '|mode[.]mult'
. '|mode[.]sngl'
. '|negbinom[.]dist'
. '|networkdays[.]intl'
. '|norm[.]dist'
. '|norm[.]inv'
. '|norm[.]s[.]dist'
. '|norm[.]s[.]inv'
. '|percentile[.]exc'
. '|percentile[.]inc'
. '|percentrank[.]exc'
. '|percentrank[.]inc'
. '|poisson[.]dist'
. '|quartile[.]exc'
. '|quartile[.]inc'
. '|rank[.]avg'
. '|rank[.]eq'
. '|stdev[.]p'
. '|stdev[.]s'
. '|t[.]dist'
. '|t[.]dist[.]2t'
. '|t[.]dist[.]rt'
. '|t[.]inv'
. '|t[.]inv[.]2t'
. '|t[.]test'
. '|var[.]p'
. '|var[.]s'
. '|weibull[.]dist'
. '|z[.]test'
// functions added with Excel 2013
. '|acot'
. '|acoth'
. '|arabic'
. '|averageifs'
. '|binom[.]dist[.]range'
. '|bitand'
. '|bitlshift'
. '|bitor'
. '|bitrshift'
. '|bitxor'
. '|ceiling[.]math'
. '|combina'
. '|cot'
. '|coth'
. '|csc'
. '|csch'
. '|days'
. '|dbcs'
. '|decimal'
. '|encodeurl'
. '|filterxml'
. '|floor[.]math'
. '|formulatext'
. '|gamma'
. '|gauss'
. '|ifna'
. '|imcosh'
. '|imcot'
. '|imcsc'
. '|imcsch'
. '|imsec'
. '|imsech'
. '|imsinh'
. '|imtan'
. '|isformula'
. '|iso[.]ceiling'
. '|isoweeknum'
. '|munit'
. '|numbervalue'
. '|pduration'
. '|permutationa'
. '|phi'
. '|rri'
. '|sec'
. '|sech'
. '|sheet'
. '|sheets'
. '|skew[.]p'
. '|unichar'
. '|unicode'
. '|webservice'
. '|xor'
// functions added with Excel 2016
. '|forecast[.]et2'
. '|forecast[.]ets[.]confint'
. '|forecast[.]ets[.]seasonality'
. '|forecast[.]ets[.]stat'
. '|forecast[.]linear'
. '|switch'
// functions added with Excel 2019
. '|concat'
. '|ifs'
. '|maxifs'
. '|minifs'
. '|sumifs'
. '|textjoin'
// functions added with Excel 365
. '|filter'
. '|randarray'
. '|anchorarray'
. '|sequence'
. '|sort'
. '|sortby'
. '|unique'
. '|xlookup'
. '|xmatch'
. '|arraytotext'
. '|call'
. '|let'
. '|lambda'
. '|single'
. '|register[.]id'
. '|textafter'
. '|textbefore'
. '|textsplit'
. '|valuetotext'
. '))\s*\(/Umui';
const XLWSREGEXP = '/(?<!_xlws\.)('
// functions added with Excel 365
. 'filter'
. '|sort'
. ')\s*\(/mui';
/**
* Prefix function name in string with _xlfn. where required.
*/
protected static function addXlfnPrefix(string $functionString): string
{
return (string) preg_replace(self::XLFNREGEXP, '_xlfn.$1(', $functionString);
}
/**
* Prefix function name in string with _xlws. where required.
*/
protected static function addXlwsPrefix(string $functionString): string
{
return (string) preg_replace(self::XLWSREGEXP, '_xlws.$1(', $functionString);
}
/**
* Prefix function name in string with _xlfn. where required.
*/
public static function addFunctionPrefix(string $functionString): string
{
return self::addXlwsPrefix(self::addXlfnPrefix($functionString));
}
/**
* Prefix function name in string with _xlfn. where required.
* Leading character, expected to be equals sign, is stripped.
*/
public static function addFunctionPrefixStripEquals(string $functionString): string
{
return self::addFunctionPrefix(substr($functionString, 1));
}
}

View file

@ -0,0 +1,511 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Writer\Xlsx;
use PhpOffice\PhpSpreadsheet\Reader\Xlsx\Namespaces;
use PhpOffice\PhpSpreadsheet\Shared\XMLWriter;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Worksheet\BaseDrawing;
use PhpOffice\PhpSpreadsheet\Worksheet\MemoryDrawing;
use PhpOffice\PhpSpreadsheet\Writer\Exception as WriterException;
class Rels extends WriterPart
{
/**
* Write relationships to XML format.
*
* @return string XML Output
*/
public function writeRelationships(Spreadsheet $spreadsheet): string
{
// 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');
// Relationships
$objWriter->startElement('Relationships');
$objWriter->writeAttribute('xmlns', Namespaces::RELATIONSHIPS);
$customPropertyList = $spreadsheet->getProperties()->getCustomProperties();
if (!empty($customPropertyList)) {
// Relationship docProps/app.xml
$this->writeRelationship(
$objWriter,
4,
Namespaces::RELATIONSHIPS_CUSTOM_PROPERTIES,
'docProps/custom.xml'
);
}
// Relationship docProps/app.xml
$this->writeRelationship(
$objWriter,
3,
Namespaces::RELATIONSHIPS_EXTENDED_PROPERTIES,
'docProps/app.xml'
);
// Relationship docProps/core.xml
$this->writeRelationship(
$objWriter,
2,
Namespaces::CORE_PROPERTIES,
'docProps/core.xml'
);
// Relationship xl/workbook.xml
$this->writeRelationship(
$objWriter,
1,
Namespaces::OFFICE_DOCUMENT,
'xl/workbook.xml'
);
// a custom UI in workbook ?
$target = $spreadsheet->getRibbonXMLData('target');
if ($spreadsheet->hasRibbon()) {
$this->writeRelationShip(
$objWriter,
5,
Namespaces::EXTENSIBILITY,
is_string($target) ? $target : ''
);
}
$objWriter->endElement();
return $objWriter->getData();
}
/**
* Write workbook relationships to XML format.
*
* @return string XML Output
*/
public function writeWorkbookRelationships(Spreadsheet $spreadsheet): string
{
// 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');
// Relationships
$objWriter->startElement('Relationships');
$objWriter->writeAttribute('xmlns', Namespaces::RELATIONSHIPS);
// Relationship styles.xml
$this->writeRelationship(
$objWriter,
1,
Namespaces::STYLES,
'styles.xml'
);
// Relationship theme/theme1.xml
$this->writeRelationship(
$objWriter,
2,
Namespaces::THEME2,
'theme/theme1.xml'
);
// Relationship sharedStrings.xml
$this->writeRelationship(
$objWriter,
3,
Namespaces::SHARED_STRINGS,
'sharedStrings.xml'
);
// Relationships with sheets
$sheetCount = $spreadsheet->getSheetCount();
for ($i = 0; $i < $sheetCount; ++$i) {
$this->writeRelationship(
$objWriter,
($i + 1 + 3),
Namespaces::WORKSHEET,
'worksheets/sheet' . ($i + 1) . '.xml'
);
}
// Relationships for vbaProject if needed
// id : just after the last sheet
if ($spreadsheet->hasMacros()) {
$this->writeRelationShip(
$objWriter,
($i + 1 + 3),
Namespaces::VBA,
'vbaProject.bin'
);
++$i; //increment i if needed for an another relation
}
$objWriter->endElement();
return $objWriter->getData();
}
/**
* Write worksheet relationships to XML format.
*
* Numbering is as follows:
* rId1 - Drawings
* rId_hyperlink_x - Hyperlinks
*
* @param bool $includeCharts Flag indicating if we should write charts
* @param int $tableRef Table ID
*
* @return string XML Output
*/
public function writeWorksheetRelationships(\PhpOffice\PhpSpreadsheet\Worksheet\Worksheet $worksheet, int $worksheetId = 1, bool $includeCharts = false, int $tableRef = 1, array &$zipContent = []): string
{
// 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');
// Relationships
$objWriter->startElement('Relationships');
$objWriter->writeAttribute('xmlns', Namespaces::RELATIONSHIPS);
// Write drawing relationships?
$drawingOriginalIds = [];
$unparsedLoadedData = $worksheet->getParentOrThrow()->getUnparsedLoadedData();
if (isset($unparsedLoadedData['sheets'][$worksheet->getCodeName()]['drawingOriginalIds'])) {
$drawingOriginalIds = $unparsedLoadedData['sheets'][$worksheet->getCodeName()]['drawingOriginalIds'];
}
if ($includeCharts) {
$charts = $worksheet->getChartCollection();
} else {
$charts = [];
}
if (($worksheet->getDrawingCollection()->count() > 0) || (count($charts) > 0) || $drawingOriginalIds) {
$rId = 1;
// Use original $relPath to get original $rId.
// Take first. In future can be overwritten.
// (! synchronize with \PhpOffice\PhpSpreadsheet\Writer\Xlsx\Worksheet::writeDrawings)
reset($drawingOriginalIds);
$relPath = key($drawingOriginalIds);
if (isset($drawingOriginalIds[$relPath])) {
$rId = (int) (substr($drawingOriginalIds[$relPath], 3));
}
// Generate new $relPath to write drawing relationship
$relPath = '../drawings/drawing' . $worksheetId . '.xml';
$this->writeRelationship(
$objWriter,
$rId,
Namespaces::RELATIONSHIPS_DRAWING,
$relPath
);
}
$backgroundImage = $worksheet->getBackgroundImage();
if ($backgroundImage !== '') {
$rId = 'Bg';
$uniqueName = md5(mt_rand(0, 9999) . time() . mt_rand(0, 9999));
$relPath = "../media/$uniqueName." . $worksheet->getBackgroundExtension();
$this->writeRelationship(
$objWriter,
$rId,
Namespaces::IMAGE,
$relPath
);
$zipContent["xl/media/$uniqueName." . $worksheet->getBackgroundExtension()] = $backgroundImage;
}
// Write hyperlink relationships?
$i = 1;
foreach ($worksheet->getHyperlinkCollection() as $hyperlink) {
if (!$hyperlink->isInternal()) {
$this->writeRelationship(
$objWriter,
'_hyperlink_' . $i,
Namespaces::HYPERLINK,
$hyperlink->getUrl(),
'External'
);
++$i;
}
}
// Write comments relationship?
$i = 1;
if (count($worksheet->getComments()) > 0 || isset($unparsedLoadedData['sheets'][$worksheet->getCodeName()]['legacyDrawing'])) {
$this->writeRelationship(
$objWriter,
'_comments_vml' . $i,
Namespaces::VML,
'../drawings/vmlDrawing' . $worksheetId . '.vml'
);
}
if (count($worksheet->getComments()) > 0) {
$this->writeRelationship(
$objWriter,
'_comments' . $i,
Namespaces::COMMENTS,
'../comments' . $worksheetId . '.xml'
);
}
// Write Table
$tableCount = $worksheet->getTableCollection()->count();
for ($i = 1; $i <= $tableCount; ++$i) {
$this->writeRelationship(
$objWriter,
'_table_' . $i,
Namespaces::RELATIONSHIPS_TABLE,
'../tables/table' . $tableRef++ . '.xml'
);
}
// Write header/footer relationship?
$i = 1;
if (count($worksheet->getHeaderFooter()->getImages()) > 0) {
$this->writeRelationship(
$objWriter,
'_headerfooter_vml' . $i,
Namespaces::VML,
'../drawings/vmlDrawingHF' . $worksheetId . '.vml'
);
}
$this->writeUnparsedRelationship($worksheet, $objWriter, 'ctrlProps', Namespaces::RELATIONSHIPS_CTRLPROP);
$this->writeUnparsedRelationship($worksheet, $objWriter, 'vmlDrawings', Namespaces::VML);
$this->writeUnparsedRelationship($worksheet, $objWriter, 'printerSettings', Namespaces::RELATIONSHIPS_PRINTER_SETTINGS);
$objWriter->endElement();
return $objWriter->getData();
}
private function writeUnparsedRelationship(\PhpOffice\PhpSpreadsheet\Worksheet\Worksheet $worksheet, XMLWriter $objWriter, string $relationship, string $type): void
{
$unparsedLoadedData = $worksheet->getParentOrThrow()->getUnparsedLoadedData();
if (!isset($unparsedLoadedData['sheets'][$worksheet->getCodeName()][$relationship])) {
return;
}
foreach ($unparsedLoadedData['sheets'][$worksheet->getCodeName()][$relationship] as $rId => $value) {
if (!str_starts_with($rId, '_headerfooter_vml')) {
$this->writeRelationship(
$objWriter,
$rId,
$type,
$value['relFilePath']
);
}
}
}
/**
* Write drawing relationships to XML format.
*
* @param int $chartRef Chart ID
* @param bool $includeCharts Flag indicating if we should write charts
*
* @return string XML Output
*/
public function writeDrawingRelationships(\PhpOffice\PhpSpreadsheet\Worksheet\Worksheet $worksheet, int &$chartRef, bool $includeCharts = false): string
{
// 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');
// Relationships
$objWriter->startElement('Relationships');
$objWriter->writeAttribute('xmlns', Namespaces::RELATIONSHIPS);
// Loop through images and write relationships
$i = 1;
$iterator = $worksheet->getDrawingCollection()->getIterator();
while ($iterator->valid()) {
$drawing = $iterator->current();
if (
$drawing instanceof \PhpOffice\PhpSpreadsheet\Worksheet\Drawing
|| $drawing instanceof MemoryDrawing
) {
// Write relationship for image drawing
$this->writeRelationship(
$objWriter,
$i,
Namespaces::IMAGE,
'../media/' . $drawing->getIndexedFilename()
);
$i = $this->writeDrawingHyperLink($objWriter, $drawing, $i);
}
$iterator->next();
++$i;
}
if ($includeCharts) {
// Loop through charts and write relationships
$chartCount = $worksheet->getChartCount();
if ($chartCount > 0) {
for ($c = 0; $c < $chartCount; ++$c) {
$this->writeRelationship(
$objWriter,
$i++,
Namespaces::RELATIONSHIPS_CHART,
'../charts/chart' . ++$chartRef . '.xml'
);
}
}
}
$objWriter->endElement();
return $objWriter->getData();
}
/**
* Write header/footer drawing relationships to XML format.
*
* @return string XML Output
*/
public function writeHeaderFooterDrawingRelationships(\PhpOffice\PhpSpreadsheet\Worksheet\Worksheet $worksheet): string
{
// 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');
// Relationships
$objWriter->startElement('Relationships');
$objWriter->writeAttribute('xmlns', Namespaces::RELATIONSHIPS);
// Loop through images and write relationships
foreach ($worksheet->getHeaderFooter()->getImages() as $key => $value) {
// Write relationship for image drawing
$this->writeRelationship(
$objWriter,
$key,
Namespaces::IMAGE,
'../media/' . $value->getIndexedFilename()
);
}
$objWriter->endElement();
return $objWriter->getData();
}
public function writeVMLDrawingRelationships(\PhpOffice\PhpSpreadsheet\Worksheet\Worksheet $worksheet): string
{
// 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');
// Relationships
$objWriter->startElement('Relationships');
$objWriter->writeAttribute('xmlns', Namespaces::RELATIONSHIPS);
// Loop through images and write relationships
foreach ($worksheet->getComments() as $comment) {
if (!$comment->hasBackgroundImage()) {
continue;
}
$bgImage = $comment->getBackgroundImage();
$this->writeRelationship(
$objWriter,
$bgImage->getImageIndex(),
Namespaces::IMAGE,
'../media/' . $bgImage->getMediaFilename()
);
}
$objWriter->endElement();
return $objWriter->getData();
}
/**
* Write Override content type.
*
* @param int|string $id Relationship ID. rId will be prepended!
* @param string $type Relationship type
* @param string $target Relationship target
* @param string $targetMode Relationship target mode
*/
private function writeRelationship(XMLWriter $objWriter, $id, string $type, string $target, string $targetMode = ''): void
{
if ($type != '' && $target != '') {
// Write relationship
$objWriter->startElement('Relationship');
$objWriter->writeAttribute('Id', 'rId' . $id);
$objWriter->writeAttribute('Type', $type);
$objWriter->writeAttribute('Target', $target);
if ($targetMode != '') {
$objWriter->writeAttribute('TargetMode', $targetMode);
}
$objWriter->endElement();
} else {
throw new WriterException('Invalid parameters passed.');
}
}
private function writeDrawingHyperLink(XMLWriter $objWriter, BaseDrawing $drawing, int $i): int
{
if ($drawing->getHyperlink() === null) {
return $i;
}
++$i;
$this->writeRelationship(
$objWriter,
$i,
Namespaces::HYPERLINK,
$drawing->getHyperlink()->getUrl(),
$drawing->getHyperlink()->getTypeHyperlink()
);
return $i;
}
}

View file

@ -0,0 +1,46 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Writer\Xlsx;
use PhpOffice\PhpSpreadsheet\Reader\Xlsx\Namespaces;
use PhpOffice\PhpSpreadsheet\Shared\XMLWriter;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
class RelsRibbon extends WriterPart
{
/**
* Write relationships for additional objects of custom UI (ribbon).
*
* @return string XML Output
*/
public function writeRibbonRelationships(Spreadsheet $spreadsheet): string
{
// 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');
// Relationships
$objWriter->startElement('Relationships');
$objWriter->writeAttribute('xmlns', Namespaces::RELATIONSHIPS);
$localRels = $spreadsheet->getRibbonBinObjects('names');
if (is_array($localRels)) {
foreach ($localRels as $aId => $aTarget) {
$objWriter->startElement('Relationship');
$objWriter->writeAttribute('Id', $aId);
$objWriter->writeAttribute('Type', Namespaces::IMAGE);
$objWriter->writeAttribute('Target', $aTarget);
$objWriter->endElement();
}
}
$objWriter->endElement();
return $objWriter->getData();
}
}

View file

@ -0,0 +1,40 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Writer\Xlsx;
use PhpOffice\PhpSpreadsheet\Reader\Xlsx\Namespaces;
use PhpOffice\PhpSpreadsheet\Shared\XMLWriter;
class RelsVBA extends WriterPart
{
/**
* Write relationships for a signed VBA Project.
*
* @return string XML Output
*/
public function writeVBARelationships(): string
{
// 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');
// Relationships
$objWriter->startElement('Relationships');
$objWriter->writeAttribute('xmlns', Namespaces::RELATIONSHIPS);
$objWriter->startElement('Relationship');
$objWriter->writeAttribute('Id', 'rId1');
$objWriter->writeAttribute('Type', Namespaces::VBA_SIGNATURE);
$objWriter->writeAttribute('Target', 'vbaProjectSignature.bin');
$objWriter->endElement();
$objWriter->endElement();
return $objWriter->getData();
}
}

View file

@ -0,0 +1,344 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Writer\Xlsx;
use PhpOffice\PhpSpreadsheet\Cell\Cell;
use PhpOffice\PhpSpreadsheet\Cell\DataType;
use PhpOffice\PhpSpreadsheet\Chart\ChartColor;
use PhpOffice\PhpSpreadsheet\Reader\Xlsx\Namespaces;
use PhpOffice\PhpSpreadsheet\RichText\RichText;
use PhpOffice\PhpSpreadsheet\RichText\Run;
use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
use PhpOffice\PhpSpreadsheet\Shared\XMLWriter;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet as ActualWorksheet;
class StringTable extends WriterPart
{
/**
* Create worksheet stringtable.
*
* @param string[] $existingTable Existing table to eventually merge with
*
* @return string[] String table for worksheet
*/
public function createStringTable(ActualWorksheet $worksheet, ?array $existingTable = null): array
{
// Create string lookup table
$aStringTable = [];
// Is an existing table given?
if (($existingTable !== null) && is_array($existingTable)) {
$aStringTable = $existingTable;
}
// Fill index array
$aFlippedStringTable = $this->flipStringTable($aStringTable);
// Loop through cells
foreach ($worksheet->getCellCollection()->getCoordinates() as $coordinate) {
/** @var Cell $cell */
$cell = $worksheet->getCellCollection()->get($coordinate);
$cellValue = $cell->getValue();
if (
!is_object($cellValue)
&& ($cellValue !== null)
&& $cellValue !== ''
&& ($cell->getDataType() == DataType::TYPE_STRING || $cell->getDataType() == DataType::TYPE_STRING2 || $cell->getDataType() == DataType::TYPE_NULL)
&& !isset($aFlippedStringTable[$cellValue])
) {
$aStringTable[] = $cellValue;
$aFlippedStringTable[$cellValue] = true;
} elseif (
$cellValue instanceof RichText
&& ($cellValue !== null)
&& !isset($aFlippedStringTable[$cellValue->getHashCode()])
) {
$aStringTable[] = $cellValue;
$aFlippedStringTable[$cellValue->getHashCode()] = true;
}
}
return $aStringTable;
}
/**
* Write string table to XML format.
*
* @param (RichText|string)[] $stringTable
*
* @return string XML Output
*/
public function writeStringTable(array $stringTable): string
{
// 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');
// String table
$objWriter->startElement('sst');
$objWriter->writeAttribute('xmlns', Namespaces::MAIN);
$objWriter->writeAttribute('uniqueCount', (string) count($stringTable));
// Loop through string table
foreach ($stringTable as $textElement) {
$objWriter->startElement('si');
if (!($textElement instanceof RichText)) {
$textToWrite = StringHelper::controlCharacterPHP2OOXML($textElement);
$objWriter->startElement('t');
if ($textToWrite !== trim($textToWrite)) {
$objWriter->writeAttribute('xml:space', 'preserve');
}
$objWriter->writeRawData($textToWrite);
$objWriter->endElement();
} else {
$this->writeRichText($objWriter, $textElement);
}
$objWriter->endElement();
}
$objWriter->endElement();
return $objWriter->getData();
}
/**
* Write Rich Text.
*
* @param ?string $prefix Optional Namespace prefix
*/
public function writeRichText(XMLWriter $objWriter, RichText $richText, ?string $prefix = null): void
{
if ($prefix !== null) {
$prefix .= ':';
}
// Loop through rich text elements
$elements = $richText->getRichTextElements();
foreach ($elements as $element) {
// r
$objWriter->startElement($prefix . 'r');
// rPr
if ($element instanceof Run && $element->getFont() !== null) {
// rPr
$objWriter->startElement($prefix . 'rPr');
// rFont
if ($element->getFont()->getName() !== null) {
$objWriter->startElement($prefix . 'rFont');
$objWriter->writeAttribute('val', $element->getFont()->getName());
$objWriter->endElement();
}
// Bold
$objWriter->startElement($prefix . 'b');
$objWriter->writeAttribute('val', ($element->getFont()->getBold() ? 'true' : 'false'));
$objWriter->endElement();
// Italic
$objWriter->startElement($prefix . 'i');
$objWriter->writeAttribute('val', ($element->getFont()->getItalic() ? 'true' : 'false'));
$objWriter->endElement();
// Superscript / subscript
if ($element->getFont()->getSuperscript() || $element->getFont()->getSubscript()) {
$objWriter->startElement($prefix . 'vertAlign');
if ($element->getFont()->getSuperscript()) {
$objWriter->writeAttribute('val', 'superscript');
} elseif ($element->getFont()->getSubscript()) {
$objWriter->writeAttribute('val', 'subscript');
}
$objWriter->endElement();
}
// Strikethrough
$objWriter->startElement($prefix . 'strike');
$objWriter->writeAttribute('val', ($element->getFont()->getStrikethrough() ? 'true' : 'false'));
$objWriter->endElement();
// Color
if ($element->getFont()->getColor()->getARGB() !== null) {
$objWriter->startElement($prefix . 'color');
$objWriter->writeAttribute('rgb', $element->getFont()->getColor()->getARGB());
$objWriter->endElement();
}
// Size
if ($element->getFont()->getSize() !== null) {
$objWriter->startElement($prefix . 'sz');
$objWriter->writeAttribute('val', (string) $element->getFont()->getSize());
$objWriter->endElement();
}
// Underline
if ($element->getFont()->getUnderline() !== null) {
$objWriter->startElement($prefix . 'u');
$objWriter->writeAttribute('val', $element->getFont()->getUnderline());
$objWriter->endElement();
}
$objWriter->endElement();
}
// t
$objWriter->startElement($prefix . 't');
$objWriter->writeAttribute('xml:space', 'preserve');
$objWriter->writeRawData(StringHelper::controlCharacterPHP2OOXML($element->getText()));
$objWriter->endElement();
$objWriter->endElement();
}
}
/**
* Write Rich Text.
*
* @param RichText|string $richText text string or Rich text
* @param string $prefix Optional Namespace prefix
*/
public function writeRichTextForCharts(XMLWriter $objWriter, $richText = null, string $prefix = ''): void
{
if (!($richText instanceof RichText)) {
$textRun = $richText;
$richText = new RichText();
$run = $richText->createTextRun($textRun ?? '');
$run->setFont(null);
}
if ($prefix !== '') {
$prefix .= ':';
}
// Loop through rich text elements
$elements = $richText->getRichTextElements();
foreach ($elements as $element) {
// r
$objWriter->startElement($prefix . 'r');
if ($element->getFont() !== null) {
// rPr
$objWriter->startElement($prefix . 'rPr');
$fontSize = $element->getFont()->getSize();
if (is_numeric($fontSize)) {
$fontSize *= (($fontSize < 100) ? 100 : 1);
$objWriter->writeAttribute('sz', (string) $fontSize);
}
// Bold
$objWriter->writeAttribute('b', ($element->getFont()->getBold() ? '1' : '0'));
// Italic
$objWriter->writeAttribute('i', ($element->getFont()->getItalic() ? '1' : '0'));
// Underline
$underlineType = $element->getFont()->getUnderline();
switch ($underlineType) {
case 'single':
$underlineType = 'sng';
break;
case 'double':
$underlineType = 'dbl';
break;
}
if ($underlineType !== null) {
$objWriter->writeAttribute('u', $underlineType);
}
// Strikethrough
$objWriter->writeAttribute('strike', ($element->getFont()->getStriketype() ?: 'noStrike'));
// Superscript/subscript
if ($element->getFont()->getBaseLine()) {
$objWriter->writeAttribute('baseline', (string) $element->getFont()->getBaseLine());
}
// Color
$this->writeChartTextColor($objWriter, $element->getFont()->getChartColor(), $prefix);
// Underscore Color
$this->writeChartTextColor($objWriter, $element->getFont()->getUnderlineColor(), $prefix, 'uFill');
// fontName
if ($element->getFont()->getLatin()) {
$objWriter->startElement($prefix . 'latin');
$objWriter->writeAttribute('typeface', $element->getFont()->getLatin());
$objWriter->endElement();
}
if ($element->getFont()->getEastAsian()) {
$objWriter->startElement($prefix . 'ea');
$objWriter->writeAttribute('typeface', $element->getFont()->getEastAsian());
$objWriter->endElement();
}
if ($element->getFont()->getComplexScript()) {
$objWriter->startElement($prefix . 'cs');
$objWriter->writeAttribute('typeface', $element->getFont()->getComplexScript());
$objWriter->endElement();
}
$objWriter->endElement();
}
// t
$objWriter->startElement($prefix . 't');
$objWriter->writeRawData(StringHelper::controlCharacterPHP2OOXML($element->getText()));
$objWriter->endElement();
$objWriter->endElement();
}
}
private function writeChartTextColor(XMLWriter $objWriter, ?ChartColor $underlineColor, string $prefix, ?string $openTag = ''): void
{
if ($underlineColor !== null) {
$type = $underlineColor->getType();
$value = $underlineColor->getValue();
if (!empty($type) && !empty($value)) {
if ($openTag !== '') {
$objWriter->startElement($prefix . $openTag);
}
$objWriter->startElement($prefix . 'solidFill');
$objWriter->startElement($prefix . $type);
$objWriter->writeAttribute('val', $value);
$alpha = $underlineColor->getAlpha();
if (is_numeric($alpha)) {
$objWriter->startElement('a:alpha');
$objWriter->writeAttribute('val', ChartColor::alphaToXml((int) $alpha));
$objWriter->endElement();
}
$objWriter->endElement(); // srgbClr/schemeClr/prstClr
$objWriter->endElement(); // solidFill
if ($openTag !== '') {
$objWriter->endElement(); // uFill
}
}
}
}
/**
* Flip string table (for index searching).
*
* @param array $stringTable Stringtable
*/
public function flipStringTable(array $stringTable): array
{
// Return value
$returnValue = [];
// Loop through stringtable and add flipped items to $returnValue
foreach ($stringTable as $key => $value) {
if (!$value instanceof RichText) {
$returnValue[$value] = $key;
} elseif ($value instanceof RichText) {
$returnValue[$value->getHashCode()] = $key;
}
}
return $returnValue;
}
}

View file

@ -0,0 +1,726 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Writer\Xlsx;
use PhpOffice\PhpSpreadsheet\Reader\Xlsx\Namespaces;
use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
use PhpOffice\PhpSpreadsheet\Shared\XMLWriter;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Style\Alignment;
use PhpOffice\PhpSpreadsheet\Style\Border;
use PhpOffice\PhpSpreadsheet\Style\Borders;
use PhpOffice\PhpSpreadsheet\Style\Conditional;
use PhpOffice\PhpSpreadsheet\Style\Fill;
use PhpOffice\PhpSpreadsheet\Style\Font;
use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
use PhpOffice\PhpSpreadsheet\Style\Protection;
class Style extends WriterPart
{
/**
* Write styles to XML format.
*
* @return string XML Output
*/
public function writeStyles(Spreadsheet $spreadsheet): string
{
// 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');
// styleSheet
$objWriter->startElement('styleSheet');
$objWriter->writeAttribute('xml:space', 'preserve');
$objWriter->writeAttribute('xmlns', Namespaces::MAIN);
// numFmts
$objWriter->startElement('numFmts');
$objWriter->writeAttribute('count', (string) $this->getParentWriter()->getNumFmtHashTable()->count());
// numFmt
for ($i = 0; $i < $this->getParentWriter()->getNumFmtHashTable()->count(); ++$i) {
$this->writeNumFmt($objWriter, $this->getParentWriter()->getNumFmtHashTable()->getByIndex($i), $i);
}
$objWriter->endElement();
// fonts
$objWriter->startElement('fonts');
$objWriter->writeAttribute('count', (string) $this->getParentWriter()->getFontHashTable()->count());
// font
for ($i = 0; $i < $this->getParentWriter()->getFontHashTable()->count(); ++$i) {
$thisfont = $this->getParentWriter()->getFontHashTable()->getByIndex($i);
if ($thisfont !== null) {
$this->writeFont($objWriter, $thisfont);
}
}
$objWriter->endElement();
// fills
$objWriter->startElement('fills');
$objWriter->writeAttribute('count', (string) $this->getParentWriter()->getFillHashTable()->count());
// fill
for ($i = 0; $i < $this->getParentWriter()->getFillHashTable()->count(); ++$i) {
$thisfill = $this->getParentWriter()->getFillHashTable()->getByIndex($i);
if ($thisfill !== null) {
$this->writeFill($objWriter, $thisfill);
}
}
$objWriter->endElement();
// borders
$objWriter->startElement('borders');
$objWriter->writeAttribute('count', (string) $this->getParentWriter()->getBordersHashTable()->count());
// border
for ($i = 0; $i < $this->getParentWriter()->getBordersHashTable()->count(); ++$i) {
$thisborder = $this->getParentWriter()->getBordersHashTable()->getByIndex($i);
if ($thisborder !== null) {
$this->writeBorder($objWriter, $thisborder);
}
}
$objWriter->endElement();
// cellStyleXfs
$objWriter->startElement('cellStyleXfs');
$objWriter->writeAttribute('count', '1');
// xf
$objWriter->startElement('xf');
$objWriter->writeAttribute('numFmtId', '0');
$objWriter->writeAttribute('fontId', '0');
$objWriter->writeAttribute('fillId', '0');
$objWriter->writeAttribute('borderId', '0');
$objWriter->endElement();
$objWriter->endElement();
// cellXfs
$objWriter->startElement('cellXfs');
$objWriter->writeAttribute('count', (string) count($spreadsheet->getCellXfCollection()));
// xf
$alignment = new Alignment();
$defaultAlignHash = $alignment->getHashCode();
if ($defaultAlignHash !== $spreadsheet->getDefaultStyle()->getAlignment()->getHashCode()) {
$defaultAlignHash = '';
}
foreach ($spreadsheet->getCellXfCollection() as $cellXf) {
$this->writeCellStyleXf($objWriter, $cellXf, $spreadsheet, $defaultAlignHash);
}
$objWriter->endElement();
// cellStyles
$objWriter->startElement('cellStyles');
$objWriter->writeAttribute('count', '1');
// cellStyle
$objWriter->startElement('cellStyle');
$objWriter->writeAttribute('name', 'Normal');
$objWriter->writeAttribute('xfId', '0');
$objWriter->writeAttribute('builtinId', '0');
$objWriter->endElement();
$objWriter->endElement();
// dxfs
$objWriter->startElement('dxfs');
$objWriter->writeAttribute('count', (string) $this->getParentWriter()->getStylesConditionalHashTable()->count());
// dxf
for ($i = 0; $i < $this->getParentWriter()->getStylesConditionalHashTable()->count(); ++$i) {
$thisstyle = $this->getParentWriter()->getStylesConditionalHashTable()->getByIndex($i);
if ($thisstyle !== null) {
$this->writeCellStyleDxf($objWriter, $thisstyle->getStyle());
}
}
$objWriter->endElement();
// tableStyles
$objWriter->startElement('tableStyles');
$objWriter->writeAttribute('defaultTableStyle', 'TableStyleMedium9');
$objWriter->writeAttribute('defaultPivotStyle', 'PivotTableStyle1');
$objWriter->endElement();
$objWriter->endElement();
// Return
return $objWriter->getData();
}
/**
* Write Fill.
*/
private function writeFill(XMLWriter $objWriter, Fill $fill): void
{
// Check if this is a pattern type or gradient type
if (
$fill->getFillType() === Fill::FILL_GRADIENT_LINEAR
|| $fill->getFillType() === Fill::FILL_GRADIENT_PATH
) {
// Gradient fill
$this->writeGradientFill($objWriter, $fill);
} elseif ($fill->getFillType() !== null) {
// Pattern fill
$this->writePatternFill($objWriter, $fill);
}
}
/**
* Write Gradient Fill.
*/
private function writeGradientFill(XMLWriter $objWriter, Fill $fill): void
{
// fill
$objWriter->startElement('fill');
// gradientFill
$objWriter->startElement('gradientFill');
$objWriter->writeAttribute('type', (string) $fill->getFillType());
$objWriter->writeAttribute('degree', (string) $fill->getRotation());
// stop
$objWriter->startElement('stop');
$objWriter->writeAttribute('position', '0');
// color
if ($fill->getStartColor()->getARGB() !== null) {
$objWriter->startElement('color');
$objWriter->writeAttribute('rgb', $fill->getStartColor()->getARGB());
$objWriter->endElement();
}
$objWriter->endElement();
// stop
$objWriter->startElement('stop');
$objWriter->writeAttribute('position', '1');
// color
if ($fill->getEndColor()->getARGB() !== null) {
$objWriter->startElement('color');
$objWriter->writeAttribute('rgb', $fill->getEndColor()->getARGB());
$objWriter->endElement();
}
$objWriter->endElement();
$objWriter->endElement();
$objWriter->endElement();
}
private static function writePatternColors(Fill $fill): bool
{
if ($fill->getFillType() === Fill::FILL_NONE) {
return false;
}
return $fill->getFillType() === Fill::FILL_SOLID || $fill->getColorsChanged();
}
/**
* Write Pattern Fill.
*/
private function writePatternFill(XMLWriter $objWriter, Fill $fill): void
{
// fill
$objWriter->startElement('fill');
// patternFill
$objWriter->startElement('patternFill');
$objWriter->writeAttribute('patternType', (string) $fill->getFillType());
if (self::writePatternColors($fill)) {
// fgColor
if ($fill->getStartColor()->getARGB()) {
if (!$fill->getEndColor()->getARGB() && $fill->getFillType() === Fill::FILL_SOLID) {
$objWriter->startElement('bgColor');
$objWriter->writeAttribute('rgb', $fill->getStartColor()->getARGB());
} else {
$objWriter->startElement('fgColor');
$objWriter->writeAttribute('rgb', $fill->getStartColor()->getARGB());
}
$objWriter->endElement();
}
// bgColor
if ($fill->getEndColor()->getARGB()) {
$objWriter->startElement('bgColor');
$objWriter->writeAttribute('rgb', $fill->getEndColor()->getARGB());
$objWriter->endElement();
}
}
$objWriter->endElement();
$objWriter->endElement();
}
private function startFont(XMLWriter $objWriter, bool &$fontStarted): void
{
if (!$fontStarted) {
$fontStarted = true;
$objWriter->startElement('font');
}
}
/**
* Write Font.
*/
private function writeFont(XMLWriter $objWriter, Font $font): void
{
$fontStarted = false;
// font
// Weird! The order of these elements actually makes a difference when opening Xlsx
// files in Excel2003 with the compatibility pack. It's not documented behaviour,
// and makes for a real WTF!
// Bold. We explicitly write this element also when false (like MS Office Excel 2007 does
// for conditional formatting). Otherwise it will apparently not be picked up in conditional
// formatting style dialog
if ($font->getBold() !== null) {
$this->startFont($objWriter, $fontStarted);
$objWriter->startElement('b');
$objWriter->writeAttribute('val', $font->getBold() ? '1' : '0');
$objWriter->endElement();
}
// Italic
if ($font->getItalic() !== null) {
$this->startFont($objWriter, $fontStarted);
$objWriter->startElement('i');
$objWriter->writeAttribute('val', $font->getItalic() ? '1' : '0');
$objWriter->endElement();
}
// Strikethrough
if ($font->getStrikethrough() !== null) {
$this->startFont($objWriter, $fontStarted);
$objWriter->startElement('strike');
$objWriter->writeAttribute('val', $font->getStrikethrough() ? '1' : '0');
$objWriter->endElement();
}
// Underline
if ($font->getUnderline() !== null) {
$this->startFont($objWriter, $fontStarted);
$objWriter->startElement('u');
$objWriter->writeAttribute('val', $font->getUnderline());
$objWriter->endElement();
}
// Superscript / subscript
if ($font->getSuperscript() === true || $font->getSubscript() === true) {
$this->startFont($objWriter, $fontStarted);
$objWriter->startElement('vertAlign');
if ($font->getSuperscript() === true) {
$objWriter->writeAttribute('val', 'superscript');
} elseif ($font->getSubscript() === true) {
$objWriter->writeAttribute('val', 'subscript');
}
$objWriter->endElement();
}
// Size
if ($font->getSize() !== null) {
$this->startFont($objWriter, $fontStarted);
$objWriter->startElement('sz');
$objWriter->writeAttribute('val', StringHelper::formatNumber($font->getSize()));
$objWriter->endElement();
}
// Foreground color
if ($font->getColor()->getARGB() !== null) {
$this->startFont($objWriter, $fontStarted);
$objWriter->startElement('color');
$objWriter->writeAttribute('rgb', $font->getColor()->getARGB());
$objWriter->endElement();
}
// Name
if ($font->getName() !== null) {
$this->startFont($objWriter, $fontStarted);
$objWriter->startElement('name');
$objWriter->writeAttribute('val', $font->getName());
$objWriter->endElement();
}
if (!empty($font->getScheme())) {
$this->startFont($objWriter, $fontStarted);
$objWriter->startElement('scheme');
$objWriter->writeAttribute('val', $font->getScheme());
$objWriter->endElement();
}
if ($fontStarted) {
$objWriter->endElement();
}
}
/**
* Write Border.
*/
private function writeBorder(XMLWriter $objWriter, Borders $borders): void
{
// Write border
$objWriter->startElement('border');
// Diagonal?
switch ($borders->getDiagonalDirection()) {
case Borders::DIAGONAL_UP:
$objWriter->writeAttribute('diagonalUp', 'true');
$objWriter->writeAttribute('diagonalDown', 'false');
break;
case Borders::DIAGONAL_DOWN:
$objWriter->writeAttribute('diagonalUp', 'false');
$objWriter->writeAttribute('diagonalDown', 'true');
break;
case Borders::DIAGONAL_BOTH:
$objWriter->writeAttribute('diagonalUp', 'true');
$objWriter->writeAttribute('diagonalDown', 'true');
break;
}
// BorderPr
$this->writeBorderPr($objWriter, 'left', $borders->getLeft());
$this->writeBorderPr($objWriter, 'right', $borders->getRight());
$this->writeBorderPr($objWriter, 'top', $borders->getTop());
$this->writeBorderPr($objWriter, 'bottom', $borders->getBottom());
$this->writeBorderPr($objWriter, 'diagonal', $borders->getDiagonal());
$objWriter->endElement();
}
/**
* Write Cell Style Xf.
*/
private function writeCellStyleXf(XMLWriter $objWriter, \PhpOffice\PhpSpreadsheet\Style\Style $style, Spreadsheet $spreadsheet, string $defaultAlignHash): void
{
// xf
$objWriter->startElement('xf');
$objWriter->writeAttribute('xfId', '0');
$objWriter->writeAttribute('fontId', (string) (int) $this->getParentWriter()->getFontHashTable()->getIndexForHashCode($style->getFont()->getHashCode()));
if ($style->getQuotePrefix()) {
$objWriter->writeAttribute('quotePrefix', '1');
}
if ($style->getNumberFormat()->getBuiltInFormatCode() === false) {
$objWriter->writeAttribute('numFmtId', (string) (int) ($this->getParentWriter()->getNumFmtHashTable()->getIndexForHashCode($style->getNumberFormat()->getHashCode()) + 164));
} else {
$objWriter->writeAttribute('numFmtId', (string) (int) $style->getNumberFormat()->getBuiltInFormatCode());
}
$objWriter->writeAttribute('fillId', (string) (int) $this->getParentWriter()->getFillHashTable()->getIndexForHashCode($style->getFill()->getHashCode()));
$objWriter->writeAttribute('borderId', (string) (int) $this->getParentWriter()->getBordersHashTable()->getIndexForHashCode($style->getBorders()->getHashCode()));
// Apply styles?
$objWriter->writeAttribute('applyFont', ($spreadsheet->getDefaultStyle()->getFont()->getHashCode() != $style->getFont()->getHashCode()) ? '1' : '0');
$objWriter->writeAttribute('applyNumberFormat', ($spreadsheet->getDefaultStyle()->getNumberFormat()->getHashCode() != $style->getNumberFormat()->getHashCode()) ? '1' : '0');
$objWriter->writeAttribute('applyFill', ($spreadsheet->getDefaultStyle()->getFill()->getHashCode() != $style->getFill()->getHashCode()) ? '1' : '0');
$objWriter->writeAttribute('applyBorder', ($spreadsheet->getDefaultStyle()->getBorders()->getHashCode() != $style->getBorders()->getHashCode()) ? '1' : '0');
if ($defaultAlignHash !== '' && $defaultAlignHash === $style->getAlignment()->getHashCode()) {
$applyAlignment = '0';
} else {
$applyAlignment = '1';
}
$objWriter->writeAttribute('applyAlignment', $applyAlignment);
if ($style->getProtection()->getLocked() != Protection::PROTECTION_INHERIT || $style->getProtection()->getHidden() != Protection::PROTECTION_INHERIT) {
$objWriter->writeAttribute('applyProtection', 'true');
}
// alignment
if ($applyAlignment === '1') {
$objWriter->startElement('alignment');
$vertical = Alignment::VERTICAL_ALIGNMENT_FOR_XLSX[$style->getAlignment()->getVertical()] ?? '';
$horizontal = Alignment::HORIZONTAL_ALIGNMENT_FOR_XLSX[$style->getAlignment()->getHorizontal()] ?? '';
if ($horizontal !== '') {
$objWriter->writeAttribute('horizontal', $horizontal);
}
if ($vertical !== '') {
$objWriter->writeAttribute('vertical', $vertical);
}
if ($style->getAlignment()->getTextRotation() >= 0) {
$textRotation = $style->getAlignment()->getTextRotation();
} else {
$textRotation = 90 - $style->getAlignment()->getTextRotation();
}
$objWriter->writeAttribute('textRotation', (string) $textRotation);
$objWriter->writeAttribute('wrapText', ($style->getAlignment()->getWrapText() ? 'true' : 'false'));
$objWriter->writeAttribute('shrinkToFit', ($style->getAlignment()->getShrinkToFit() ? 'true' : 'false'));
if ($style->getAlignment()->getIndent() > 0) {
$objWriter->writeAttribute('indent', (string) $style->getAlignment()->getIndent());
}
if ($style->getAlignment()->getReadOrder() > 0) {
$objWriter->writeAttribute('readingOrder', (string) $style->getAlignment()->getReadOrder());
}
$objWriter->endElement();
}
// protection
if ($style->getProtection()->getLocked() != Protection::PROTECTION_INHERIT || $style->getProtection()->getHidden() != Protection::PROTECTION_INHERIT) {
$objWriter->startElement('protection');
if ($style->getProtection()->getLocked() != Protection::PROTECTION_INHERIT) {
$objWriter->writeAttribute('locked', ($style->getProtection()->getLocked() == Protection::PROTECTION_PROTECTED ? 'true' : 'false'));
}
if ($style->getProtection()->getHidden() != Protection::PROTECTION_INHERIT) {
$objWriter->writeAttribute('hidden', ($style->getProtection()->getHidden() == Protection::PROTECTION_PROTECTED ? 'true' : 'false'));
}
$objWriter->endElement();
}
$objWriter->endElement();
}
/**
* Write Cell Style Dxf.
*/
private function writeCellStyleDxf(XMLWriter $objWriter, \PhpOffice\PhpSpreadsheet\Style\Style $style): void
{
// dxf
$objWriter->startElement('dxf');
// font
$this->writeFont($objWriter, $style->getFont());
// numFmt
$this->writeNumFmt($objWriter, $style->getNumberFormat());
// fill
$this->writeFill($objWriter, $style->getFill());
// alignment
$horizontal = Alignment::HORIZONTAL_ALIGNMENT_FOR_XLSX[$style->getAlignment()->getHorizontal()] ?? '';
$vertical = Alignment::VERTICAL_ALIGNMENT_FOR_XLSX[$style->getAlignment()->getVertical()] ?? '';
$rotation = $style->getAlignment()->getTextRotation();
if ($horizontal || $vertical || $rotation !== null) {
$objWriter->startElement('alignment');
if ($horizontal) {
$objWriter->writeAttribute('horizontal', $horizontal);
}
if ($vertical) {
$objWriter->writeAttribute('vertical', $vertical);
}
if ($rotation !== null) {
if ($rotation >= 0) {
$textRotation = $rotation;
} else {
$textRotation = 90 - $rotation;
}
$objWriter->writeAttribute('textRotation', (string) $textRotation);
}
$objWriter->endElement();
}
// border
$this->writeBorder($objWriter, $style->getBorders());
// protection
if ((!empty($style->getProtection()->getLocked())) || (!empty($style->getProtection()->getHidden()))) {
if (
$style->getProtection()->getLocked() !== Protection::PROTECTION_INHERIT
|| $style->getProtection()->getHidden() !== Protection::PROTECTION_INHERIT
) {
$objWriter->startElement('protection');
if (
($style->getProtection()->getLocked() !== null)
&& ($style->getProtection()->getLocked() !== Protection::PROTECTION_INHERIT)
) {
$objWriter->writeAttribute('locked', ($style->getProtection()->getLocked() == Protection::PROTECTION_PROTECTED ? 'true' : 'false'));
}
if (
($style->getProtection()->getHidden() !== null)
&& ($style->getProtection()->getHidden() !== Protection::PROTECTION_INHERIT)
) {
$objWriter->writeAttribute('hidden', ($style->getProtection()->getHidden() == Protection::PROTECTION_PROTECTED ? 'true' : 'false'));
}
$objWriter->endElement();
}
}
$objWriter->endElement();
}
/**
* Write BorderPr.
*
* @param string $name Element name
*/
private function writeBorderPr(XMLWriter $objWriter, string $name, Border $border): void
{
// Write BorderPr
if ($border->getBorderStyle() === Border::BORDER_OMIT) {
return;
}
$objWriter->startElement($name);
if ($border->getBorderStyle() !== Border::BORDER_NONE) {
$objWriter->writeAttribute('style', $border->getBorderStyle());
// color
if ($border->getColor()->getARGB() !== null) {
$objWriter->startElement('color');
$objWriter->writeAttribute('rgb', $border->getColor()->getARGB());
$objWriter->endElement();
}
}
$objWriter->endElement();
}
/**
* Write NumberFormat.
*
* @param int $id Number Format identifier
*/
private function writeNumFmt(XMLWriter $objWriter, ?NumberFormat $numberFormat, int $id = 0): void
{
// Translate formatcode
$formatCode = ($numberFormat === null) ? null : $numberFormat->getFormatCode();
// numFmt
if ($formatCode !== null) {
$objWriter->startElement('numFmt');
$objWriter->writeAttribute('numFmtId', (string) ($id + 164));
$objWriter->writeAttribute('formatCode', $formatCode);
$objWriter->endElement();
}
}
/**
* Get an array of all styles.
*
* @return \PhpOffice\PhpSpreadsheet\Style\Style[] All styles in PhpSpreadsheet
*/
public function allStyles(Spreadsheet $spreadsheet): array
{
return $spreadsheet->getCellXfCollection();
}
/**
* Get an array of all conditional styles.
*
* @return Conditional[] All conditional styles in PhpSpreadsheet
*/
public function allConditionalStyles(Spreadsheet $spreadsheet): array
{
// Get an array of all styles
$aStyles = [];
$sheetCount = $spreadsheet->getSheetCount();
for ($i = 0; $i < $sheetCount; ++$i) {
foreach ($spreadsheet->getSheet($i)->getConditionalStylesCollection() as $conditionalStyles) {
foreach ($conditionalStyles as $conditionalStyle) {
$aStyles[] = $conditionalStyle;
}
}
}
return $aStyles;
}
/**
* Get an array of all fills.
*
* @return Fill[] All fills in PhpSpreadsheet
*/
public function allFills(Spreadsheet $spreadsheet): array
{
// Get an array of unique fills
$aFills = [];
// Two first fills are predefined
$fill0 = new Fill();
$fill0->setFillType(Fill::FILL_NONE);
$aFills[] = $fill0;
$fill1 = new Fill();
$fill1->setFillType(Fill::FILL_PATTERN_GRAY125);
$aFills[] = $fill1;
// The remaining fills
$aStyles = $this->allStyles($spreadsheet);
foreach ($aStyles as $style) {
if (!isset($aFills[$style->getFill()->getHashCode()])) {
$aFills[$style->getFill()->getHashCode()] = $style->getFill();
}
}
return $aFills;
}
/**
* Get an array of all fonts.
*
* @return Font[] All fonts in PhpSpreadsheet
*/
public function allFonts(Spreadsheet $spreadsheet): array
{
// Get an array of unique fonts
$aFonts = [];
$aStyles = $this->allStyles($spreadsheet);
foreach ($aStyles as $style) {
if (!isset($aFonts[$style->getFont()->getHashCode()])) {
$aFonts[$style->getFont()->getHashCode()] = $style->getFont();
}
}
return $aFonts;
}
/**
* Get an array of all borders.
*
* @return Borders[] All borders in PhpSpreadsheet
*/
public function allBorders(Spreadsheet $spreadsheet): array
{
// Get an array of unique borders
$aBorders = [];
$aStyles = $this->allStyles($spreadsheet);
foreach ($aStyles as $style) {
if (!isset($aBorders[$style->getBorders()->getHashCode()])) {
$aBorders[$style->getBorders()->getHashCode()] = $style->getBorders();
}
}
return $aBorders;
}
/**
* Get an array of all number formats.
*
* @return NumberFormat[] All number formats in PhpSpreadsheet
*/
public function allNumberFormats(Spreadsheet $spreadsheet): array
{
// Get an array of unique number formats
$aNumFmts = [];
$aStyles = $this->allStyles($spreadsheet);
foreach ($aStyles as $style) {
if ($style->getNumberFormat()->getBuiltInFormatCode() === false && !isset($aNumFmts[$style->getNumberFormat()->getHashCode()])) {
$aNumFmts[$style->getNumberFormat()->getHashCode()] = $style->getNumberFormat();
}
}
return $aNumFmts;
}
}

View file

@ -0,0 +1,115 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Writer\Xlsx;
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
use PhpOffice\PhpSpreadsheet\Reader\Xlsx\Namespaces;
use PhpOffice\PhpSpreadsheet\Shared\XMLWriter;
use PhpOffice\PhpSpreadsheet\Worksheet\Table as WorksheetTable;
class Table extends WriterPart
{
/**
* Write Table to XML format.
*
* @param int $tableRef Table ID
*
* @return string XML Output
*/
public function writeTable(WorksheetTable $table, int $tableRef): string
{
// 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');
// Table
$name = 'Table' . $tableRef;
$range = $table->getRange();
$objWriter->startElement('table');
$objWriter->writeAttribute('xml:space', 'preserve');
$objWriter->writeAttribute('xmlns', Namespaces::MAIN);
$objWriter->writeAttribute('id', (string) $tableRef);
$objWriter->writeAttribute('name', $name);
$objWriter->writeAttribute('displayName', $table->getName() ?: $name);
$objWriter->writeAttribute('ref', $range);
$objWriter->writeAttribute('headerRowCount', $table->getShowHeaderRow() ? '1' : '0');
$objWriter->writeAttribute('totalsRowCount', $table->getShowTotalsRow() ? '1' : '0');
// Table Boundaries
[$rangeStart, $rangeEnd] = Coordinate::rangeBoundaries($table->getRange());
// Table Auto Filter
if ($table->getShowHeaderRow() && $table->getAllowFilter() === true) {
$objWriter->startElement('autoFilter');
$objWriter->writeAttribute('ref', $range);
$objWriter->endElement();
foreach (range($rangeStart[0], $rangeEnd[0]) as $offset => $columnIndex) {
$column = $table->getColumnByOffset($offset);
if (!$column->getShowFilterButton()) {
$objWriter->startElement('filterColumn');
$objWriter->writeAttribute('colId', (string) $offset);
$objWriter->writeAttribute('hiddenButton', '1');
$objWriter->endElement();
} else {
$column = $table->getAutoFilter()->getColumnByOffset($offset);
AutoFilter::writeAutoFilterColumn($objWriter, $column, $offset);
}
}
}
// Table Columns
$objWriter->startElement('tableColumns');
$objWriter->writeAttribute('count', (string) ($rangeEnd[0] - $rangeStart[0] + 1));
foreach (range($rangeStart[0], $rangeEnd[0]) as $offset => $columnIndex) {
$worksheet = $table->getWorksheet();
if (!$worksheet) {
continue;
}
$column = $table->getColumnByOffset($offset);
$cell = $worksheet->getCell([$columnIndex, $rangeStart[1]]);
$objWriter->startElement('tableColumn');
$objWriter->writeAttribute('id', (string) ($offset + 1));
$objWriter->writeAttribute('name', $table->getShowHeaderRow() ? $cell->getValue() : 'Column' . ($offset + 1));
if ($table->getShowTotalsRow()) {
if ($column->getTotalsRowLabel()) {
$objWriter->writeAttribute('totalsRowLabel', $column->getTotalsRowLabel());
}
if ($column->getTotalsRowFunction()) {
$objWriter->writeAttribute('totalsRowFunction', $column->getTotalsRowFunction());
}
}
if ($column->getColumnFormula()) {
$objWriter->writeElement('calculatedColumnFormula', $column->getColumnFormula());
}
$objWriter->endElement();
}
$objWriter->endElement();
// Table Styles
$objWriter->startElement('tableStyleInfo');
$objWriter->writeAttribute('name', $table->getStyle()->getTheme());
$objWriter->writeAttribute('showFirstColumn', $table->getStyle()->getShowFirstColumn() ? '1' : '0');
$objWriter->writeAttribute('showLastColumn', $table->getStyle()->getShowLastColumn() ? '1' : '0');
$objWriter->writeAttribute('showRowStripes', $table->getStyle()->getShowRowStripes() ? '1' : '0');
$objWriter->writeAttribute('showColumnStripes', $table->getStyle()->getShowColumnStripes() ? '1' : '0');
$objWriter->endElement();
$objWriter->endElement();
// Return
return $objWriter->getData();
}
}

View file

@ -0,0 +1,744 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Writer\Xlsx;
use PhpOffice\PhpSpreadsheet\Reader\Xlsx\Namespaces;
use PhpOffice\PhpSpreadsheet\Shared\XMLWriter;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Theme as SpreadsheetTheme;
class Theme extends WriterPart
{
/**
* Write theme to XML format.
*
* @return string XML Output
*/
public function writeTheme(Spreadsheet $spreadsheet): string
{
// 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);
}
$theme = $spreadsheet->getTheme();
// XML header
$objWriter->startDocument('1.0', 'UTF-8', 'yes');
// a:theme
$objWriter->startElement('a:theme');
$objWriter->writeAttribute('xmlns:a', Namespaces::DRAWINGML);
$objWriter->writeAttribute('name', 'Office Theme');
// a:themeElements
$objWriter->startElement('a:themeElements');
// a:clrScheme
$objWriter->startElement('a:clrScheme');
$objWriter->writeAttribute('name', $theme->getThemeColorName());
$this->writeColourScheme($objWriter, $theme);
$objWriter->endElement();
// a:fontScheme
$objWriter->startElement('a:fontScheme');
$objWriter->writeAttribute('name', $theme->getThemeFontName());
// a:majorFont
$objWriter->startElement('a:majorFont');
$this->writeFonts(
$objWriter,
$theme->getMajorFontLatin(),
$theme->getMajorFontEastAsian(),
$theme->getMajorFontComplexScript(),
$theme->getMajorFontSubstitutions()
);
$objWriter->endElement(); // a:majorFont
// a:minorFont
$objWriter->startElement('a:minorFont');
$this->writeFonts(
$objWriter,
$theme->getMinorFontLatin(),
$theme->getMinorFontEastAsian(),
$theme->getMinorFontComplexScript(),
$theme->getMinorFontSubstitutions()
);
$objWriter->endElement(); // a:minorFont
$objWriter->endElement(); // a:fontScheme
// a:fmtScheme
$objWriter->startElement('a:fmtScheme');
$objWriter->writeAttribute('name', 'Office');
// a:fillStyleLst
$objWriter->startElement('a:fillStyleLst');
// a:solidFill
$objWriter->startElement('a:solidFill');
// a:schemeClr
$objWriter->startElement('a:schemeClr');
$objWriter->writeAttribute('val', 'phClr');
$objWriter->endElement();
$objWriter->endElement();
// a:gradFill
$objWriter->startElement('a:gradFill');
$objWriter->writeAttribute('rotWithShape', '1');
// a:gsLst
$objWriter->startElement('a:gsLst');
// a:gs
$objWriter->startElement('a:gs');
$objWriter->writeAttribute('pos', '0');
// a:schemeClr
$objWriter->startElement('a:schemeClr');
$objWriter->writeAttribute('val', 'phClr');
// a:tint
$objWriter->startElement('a:tint');
$objWriter->writeAttribute('val', '50000');
$objWriter->endElement();
// a:satMod
$objWriter->startElement('a:satMod');
$objWriter->writeAttribute('val', '300000');
$objWriter->endElement();
$objWriter->endElement();
$objWriter->endElement();
// a:gs
$objWriter->startElement('a:gs');
$objWriter->writeAttribute('pos', '35000');
// a:schemeClr
$objWriter->startElement('a:schemeClr');
$objWriter->writeAttribute('val', 'phClr');
// a:tint
$objWriter->startElement('a:tint');
$objWriter->writeAttribute('val', '37000');
$objWriter->endElement();
// a:satMod
$objWriter->startElement('a:satMod');
$objWriter->writeAttribute('val', '300000');
$objWriter->endElement();
$objWriter->endElement();
$objWriter->endElement();
// a:gs
$objWriter->startElement('a:gs');
$objWriter->writeAttribute('pos', '100000');
// a:schemeClr
$objWriter->startElement('a:schemeClr');
$objWriter->writeAttribute('val', 'phClr');
// a:tint
$objWriter->startElement('a:tint');
$objWriter->writeAttribute('val', '15000');
$objWriter->endElement();
// a:satMod
$objWriter->startElement('a:satMod');
$objWriter->writeAttribute('val', '350000');
$objWriter->endElement();
$objWriter->endElement();
$objWriter->endElement();
$objWriter->endElement();
// a:lin
$objWriter->startElement('a:lin');
$objWriter->writeAttribute('ang', '16200000');
$objWriter->writeAttribute('scaled', '1');
$objWriter->endElement();
$objWriter->endElement();
// a:gradFill
$objWriter->startElement('a:gradFill');
$objWriter->writeAttribute('rotWithShape', '1');
// a:gsLst
$objWriter->startElement('a:gsLst');
// a:gs
$objWriter->startElement('a:gs');
$objWriter->writeAttribute('pos', '0');
// a:schemeClr
$objWriter->startElement('a:schemeClr');
$objWriter->writeAttribute('val', 'phClr');
// a:shade
$objWriter->startElement('a:shade');
$objWriter->writeAttribute('val', '51000');
$objWriter->endElement();
// a:satMod
$objWriter->startElement('a:satMod');
$objWriter->writeAttribute('val', '130000');
$objWriter->endElement();
$objWriter->endElement();
$objWriter->endElement();
// a:gs
$objWriter->startElement('a:gs');
$objWriter->writeAttribute('pos', '80000');
// a:schemeClr
$objWriter->startElement('a:schemeClr');
$objWriter->writeAttribute('val', 'phClr');
// a:shade
$objWriter->startElement('a:shade');
$objWriter->writeAttribute('val', '93000');
$objWriter->endElement();
// a:satMod
$objWriter->startElement('a:satMod');
$objWriter->writeAttribute('val', '130000');
$objWriter->endElement();
$objWriter->endElement();
$objWriter->endElement();
// a:gs
$objWriter->startElement('a:gs');
$objWriter->writeAttribute('pos', '100000');
// a:schemeClr
$objWriter->startElement('a:schemeClr');
$objWriter->writeAttribute('val', 'phClr');
// a:shade
$objWriter->startElement('a:shade');
$objWriter->writeAttribute('val', '94000');
$objWriter->endElement();
// a:satMod
$objWriter->startElement('a:satMod');
$objWriter->writeAttribute('val', '135000');
$objWriter->endElement();
$objWriter->endElement();
$objWriter->endElement();
$objWriter->endElement();
// a:lin
$objWriter->startElement('a:lin');
$objWriter->writeAttribute('ang', '16200000');
$objWriter->writeAttribute('scaled', '0');
$objWriter->endElement();
$objWriter->endElement();
$objWriter->endElement();
// a:lnStyleLst
$objWriter->startElement('a:lnStyleLst');
// a:ln
$objWriter->startElement('a:ln');
$objWriter->writeAttribute('w', '9525');
$objWriter->writeAttribute('cap', 'flat');
$objWriter->writeAttribute('cmpd', 'sng');
$objWriter->writeAttribute('algn', 'ctr');
// a:solidFill
$objWriter->startElement('a:solidFill');
// a:schemeClr
$objWriter->startElement('a:schemeClr');
$objWriter->writeAttribute('val', 'phClr');
// a:shade
$objWriter->startElement('a:shade');
$objWriter->writeAttribute('val', '95000');
$objWriter->endElement();
// a:satMod
$objWriter->startElement('a:satMod');
$objWriter->writeAttribute('val', '105000');
$objWriter->endElement();
$objWriter->endElement();
$objWriter->endElement();
// a:prstDash
$objWriter->startElement('a:prstDash');
$objWriter->writeAttribute('val', 'solid');
$objWriter->endElement();
$objWriter->endElement();
// a:ln
$objWriter->startElement('a:ln');
$objWriter->writeAttribute('w', '25400');
$objWriter->writeAttribute('cap', 'flat');
$objWriter->writeAttribute('cmpd', 'sng');
$objWriter->writeAttribute('algn', 'ctr');
// a:solidFill
$objWriter->startElement('a:solidFill');
// a:schemeClr
$objWriter->startElement('a:schemeClr');
$objWriter->writeAttribute('val', 'phClr');
$objWriter->endElement();
$objWriter->endElement();
// a:prstDash
$objWriter->startElement('a:prstDash');
$objWriter->writeAttribute('val', 'solid');
$objWriter->endElement();
$objWriter->endElement();
// a:ln
$objWriter->startElement('a:ln');
$objWriter->writeAttribute('w', '38100');
$objWriter->writeAttribute('cap', 'flat');
$objWriter->writeAttribute('cmpd', 'sng');
$objWriter->writeAttribute('algn', 'ctr');
// a:solidFill
$objWriter->startElement('a:solidFill');
// a:schemeClr
$objWriter->startElement('a:schemeClr');
$objWriter->writeAttribute('val', 'phClr');
$objWriter->endElement();
$objWriter->endElement();
// a:prstDash
$objWriter->startElement('a:prstDash');
$objWriter->writeAttribute('val', 'solid');
$objWriter->endElement();
$objWriter->endElement();
$objWriter->endElement();
// a:effectStyleLst
$objWriter->startElement('a:effectStyleLst');
// a:effectStyle
$objWriter->startElement('a:effectStyle');
// a:effectLst
$objWriter->startElement('a:effectLst');
// a:outerShdw
$objWriter->startElement('a:outerShdw');
$objWriter->writeAttribute('blurRad', '40000');
$objWriter->writeAttribute('dist', '20000');
$objWriter->writeAttribute('dir', '5400000');
$objWriter->writeAttribute('rotWithShape', '0');
// a:srgbClr
$objWriter->startElement('a:srgbClr');
$objWriter->writeAttribute('val', '000000');
// a:alpha
$objWriter->startElement('a:alpha');
$objWriter->writeAttribute('val', '38000');
$objWriter->endElement();
$objWriter->endElement();
$objWriter->endElement();
$objWriter->endElement();
$objWriter->endElement();
// a:effectStyle
$objWriter->startElement('a:effectStyle');
// a:effectLst
$objWriter->startElement('a:effectLst');
// a:outerShdw
$objWriter->startElement('a:outerShdw');
$objWriter->writeAttribute('blurRad', '40000');
$objWriter->writeAttribute('dist', '23000');
$objWriter->writeAttribute('dir', '5400000');
$objWriter->writeAttribute('rotWithShape', '0');
// a:srgbClr
$objWriter->startElement('a:srgbClr');
$objWriter->writeAttribute('val', '000000');
// a:alpha
$objWriter->startElement('a:alpha');
$objWriter->writeAttribute('val', '35000');
$objWriter->endElement();
$objWriter->endElement();
$objWriter->endElement();
$objWriter->endElement();
$objWriter->endElement();
// a:effectStyle
$objWriter->startElement('a:effectStyle');
// a:effectLst
$objWriter->startElement('a:effectLst');
// a:outerShdw
$objWriter->startElement('a:outerShdw');
$objWriter->writeAttribute('blurRad', '40000');
$objWriter->writeAttribute('dist', '23000');
$objWriter->writeAttribute('dir', '5400000');
$objWriter->writeAttribute('rotWithShape', '0');
// a:srgbClr
$objWriter->startElement('a:srgbClr');
$objWriter->writeAttribute('val', '000000');
// a:alpha
$objWriter->startElement('a:alpha');
$objWriter->writeAttribute('val', '35000');
$objWriter->endElement();
$objWriter->endElement();
$objWriter->endElement();
$objWriter->endElement();
// a:scene3d
$objWriter->startElement('a:scene3d');
// a:camera
$objWriter->startElement('a:camera');
$objWriter->writeAttribute('prst', 'orthographicFront');
// a:rot
$objWriter->startElement('a:rot');
$objWriter->writeAttribute('lat', '0');
$objWriter->writeAttribute('lon', '0');
$objWriter->writeAttribute('rev', '0');
$objWriter->endElement();
$objWriter->endElement();
// a:lightRig
$objWriter->startElement('a:lightRig');
$objWriter->writeAttribute('rig', 'threePt');
$objWriter->writeAttribute('dir', 't');
// a:rot
$objWriter->startElement('a:rot');
$objWriter->writeAttribute('lat', '0');
$objWriter->writeAttribute('lon', '0');
$objWriter->writeAttribute('rev', '1200000');
$objWriter->endElement();
$objWriter->endElement();
$objWriter->endElement();
// a:sp3d
$objWriter->startElement('a:sp3d');
// a:bevelT
$objWriter->startElement('a:bevelT');
$objWriter->writeAttribute('w', '63500');
$objWriter->writeAttribute('h', '25400');
$objWriter->endElement();
$objWriter->endElement();
$objWriter->endElement();
$objWriter->endElement();
// a:bgFillStyleLst
$objWriter->startElement('a:bgFillStyleLst');
// a:solidFill
$objWriter->startElement('a:solidFill');
// a:schemeClr
$objWriter->startElement('a:schemeClr');
$objWriter->writeAttribute('val', 'phClr');
$objWriter->endElement();
$objWriter->endElement();
// a:gradFill
$objWriter->startElement('a:gradFill');
$objWriter->writeAttribute('rotWithShape', '1');
// a:gsLst
$objWriter->startElement('a:gsLst');
// a:gs
$objWriter->startElement('a:gs');
$objWriter->writeAttribute('pos', '0');
// a:schemeClr
$objWriter->startElement('a:schemeClr');
$objWriter->writeAttribute('val', 'phClr');
// a:tint
$objWriter->startElement('a:tint');
$objWriter->writeAttribute('val', '40000');
$objWriter->endElement();
// a:satMod
$objWriter->startElement('a:satMod');
$objWriter->writeAttribute('val', '350000');
$objWriter->endElement();
$objWriter->endElement();
$objWriter->endElement();
// a:gs
$objWriter->startElement('a:gs');
$objWriter->writeAttribute('pos', '40000');
// a:schemeClr
$objWriter->startElement('a:schemeClr');
$objWriter->writeAttribute('val', 'phClr');
// a:tint
$objWriter->startElement('a:tint');
$objWriter->writeAttribute('val', '45000');
$objWriter->endElement();
// a:shade
$objWriter->startElement('a:shade');
$objWriter->writeAttribute('val', '99000');
$objWriter->endElement();
// a:satMod
$objWriter->startElement('a:satMod');
$objWriter->writeAttribute('val', '350000');
$objWriter->endElement();
$objWriter->endElement();
$objWriter->endElement();
// a:gs
$objWriter->startElement('a:gs');
$objWriter->writeAttribute('pos', '100000');
// a:schemeClr
$objWriter->startElement('a:schemeClr');
$objWriter->writeAttribute('val', 'phClr');
// a:shade
$objWriter->startElement('a:shade');
$objWriter->writeAttribute('val', '20000');
$objWriter->endElement();
// a:satMod
$objWriter->startElement('a:satMod');
$objWriter->writeAttribute('val', '255000');
$objWriter->endElement();
$objWriter->endElement();
$objWriter->endElement();
$objWriter->endElement();
// a:path
$objWriter->startElement('a:path');
$objWriter->writeAttribute('path', 'circle');
// a:fillToRect
$objWriter->startElement('a:fillToRect');
$objWriter->writeAttribute('l', '50000');
$objWriter->writeAttribute('t', '-80000');
$objWriter->writeAttribute('r', '50000');
$objWriter->writeAttribute('b', '180000');
$objWriter->endElement();
$objWriter->endElement();
$objWriter->endElement();
// a:gradFill
$objWriter->startElement('a:gradFill');
$objWriter->writeAttribute('rotWithShape', '1');
// a:gsLst
$objWriter->startElement('a:gsLst');
// a:gs
$objWriter->startElement('a:gs');
$objWriter->writeAttribute('pos', '0');
// a:schemeClr
$objWriter->startElement('a:schemeClr');
$objWriter->writeAttribute('val', 'phClr');
// a:tint
$objWriter->startElement('a:tint');
$objWriter->writeAttribute('val', '80000');
$objWriter->endElement();
// a:satMod
$objWriter->startElement('a:satMod');
$objWriter->writeAttribute('val', '300000');
$objWriter->endElement();
$objWriter->endElement();
$objWriter->endElement();
// a:gs
$objWriter->startElement('a:gs');
$objWriter->writeAttribute('pos', '100000');
// a:schemeClr
$objWriter->startElement('a:schemeClr');
$objWriter->writeAttribute('val', 'phClr');
// a:shade
$objWriter->startElement('a:shade');
$objWriter->writeAttribute('val', '30000');
$objWriter->endElement();
// a:satMod
$objWriter->startElement('a:satMod');
$objWriter->writeAttribute('val', '200000');
$objWriter->endElement();
$objWriter->endElement();
$objWriter->endElement();
$objWriter->endElement();
// a:path
$objWriter->startElement('a:path');
$objWriter->writeAttribute('path', 'circle');
// a:fillToRect
$objWriter->startElement('a:fillToRect');
$objWriter->writeAttribute('l', '50000');
$objWriter->writeAttribute('t', '50000');
$objWriter->writeAttribute('r', '50000');
$objWriter->writeAttribute('b', '50000');
$objWriter->endElement();
$objWriter->endElement();
$objWriter->endElement();
$objWriter->endElement();
$objWriter->endElement();
$objWriter->endElement();
// a:objectDefaults
$objWriter->writeElement('a:objectDefaults', null);
// a:extraClrSchemeLst
$objWriter->writeElement('a:extraClrSchemeLst', null);
$objWriter->endElement();
// Return
return $objWriter->getData();
}
/**
* Write fonts to XML format.
*
* @param string[] $fontSet
*/
private function writeFonts(XMLWriter $objWriter, string $latinFont, string $eastAsianFont, string $complexScriptFont, array $fontSet): void
{
// a:latin
$objWriter->startElement('a:latin');
$objWriter->writeAttribute('typeface', $latinFont);
$objWriter->endElement();
// a:ea
$objWriter->startElement('a:ea');
$objWriter->writeAttribute('typeface', $eastAsianFont);
$objWriter->endElement();
// a:cs
$objWriter->startElement('a:cs');
$objWriter->writeAttribute('typeface', $complexScriptFont);
$objWriter->endElement();
foreach ($fontSet as $fontScript => $typeface) {
$objWriter->startElement('a:font');
$objWriter->writeAttribute('script', $fontScript);
$objWriter->writeAttribute('typeface', $typeface);
$objWriter->endElement();
}
}
/**
* Write colour scheme to XML format.
*/
private function writeColourScheme(XMLWriter $objWriter, SpreadsheetTheme $theme): void
{
$themeArray = $theme->getThemeColors();
// a:dk1
$objWriter->startElement('a:dk1');
$objWriter->startElement('a:sysClr');
$objWriter->writeAttribute('val', 'windowText');
$objWriter->writeAttribute('lastClr', $themeArray['dk1'] ?? '000000');
$objWriter->endElement(); // a:sysClr
$objWriter->endElement(); // a:dk1
// a:lt1
$objWriter->startElement('a:lt1');
$objWriter->startElement('a:sysClr');
$objWriter->writeAttribute('val', 'window');
$objWriter->writeAttribute('lastClr', $themeArray['lt1'] ?? 'FFFFFF');
$objWriter->endElement(); // a:sysClr
$objWriter->endElement(); // a:lt1
foreach ($themeArray as $colourName => $colourValue) {
if ($colourName !== 'dk1' && $colourName !== 'lt1') {
$objWriter->startElement('a:' . $colourName);
$objWriter->startElement('a:srgbClr');
$objWriter->writeAttribute('val', $colourValue);
$objWriter->endElement(); // a:srgbClr
$objWriter->endElement(); // a:$colourName
}
}
}
}

View file

@ -0,0 +1,214 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Writer\Xlsx;
use PhpOffice\PhpSpreadsheet\Reader\Xlsx\Namespaces;
use PhpOffice\PhpSpreadsheet\Shared\Date;
use PhpOffice\PhpSpreadsheet\Shared\XMLWriter;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Writer\Exception as WriterException;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx\DefinedNames as DefinedNamesWriter;
class Workbook extends WriterPart
{
/**
* Write workbook to XML format.
*
* @param bool $recalcRequired Indicate whether formulas should be recalculated before writing
*
* @return string XML Output
*/
public function writeWorkbook(Spreadsheet $spreadsheet, bool $recalcRequired = false): string
{
// Create XML writer
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');
// workbook
$objWriter->startElement('workbook');
$objWriter->writeAttribute('xml:space', 'preserve');
$objWriter->writeAttribute('xmlns', Namespaces::MAIN);
$objWriter->writeAttribute('xmlns:r', Namespaces::SCHEMA_OFFICE_DOCUMENT);
// fileVersion
$this->writeFileVersion($objWriter);
// workbookPr
$this->writeWorkbookPr($objWriter);
// workbookProtection
$this->writeWorkbookProtection($objWriter, $spreadsheet);
// bookViews
if ($this->getParentWriter()->getOffice2003Compatibility() === false) {
$this->writeBookViews($objWriter, $spreadsheet);
}
// sheets
$this->writeSheets($objWriter, $spreadsheet);
// definedNames
(new DefinedNamesWriter($objWriter, $spreadsheet))->write();
// calcPr
$this->writeCalcPr($objWriter, $recalcRequired);
$objWriter->endElement();
// Return
return $objWriter->getData();
}
/**
* Write file version.
*/
private function writeFileVersion(XMLWriter $objWriter): void
{
$objWriter->startElement('fileVersion');
$objWriter->writeAttribute('appName', 'xl');
$objWriter->writeAttribute('lastEdited', '4');
$objWriter->writeAttribute('lowestEdited', '4');
$objWriter->writeAttribute('rupBuild', '4505');
$objWriter->endElement();
}
/**
* Write WorkbookPr.
*/
private function writeWorkbookPr(XMLWriter $objWriter): void
{
$objWriter->startElement('workbookPr');
if (Date::getExcelCalendar() === Date::CALENDAR_MAC_1904) {
$objWriter->writeAttribute('date1904', '1');
}
$objWriter->writeAttribute('codeName', 'ThisWorkbook');
$objWriter->endElement();
}
/**
* Write BookViews.
*/
private function writeBookViews(XMLWriter $objWriter, Spreadsheet $spreadsheet): void
{
// bookViews
$objWriter->startElement('bookViews');
// workbookView
$objWriter->startElement('workbookView');
$objWriter->writeAttribute('activeTab', (string) $spreadsheet->getActiveSheetIndex());
$objWriter->writeAttribute('autoFilterDateGrouping', ($spreadsheet->getAutoFilterDateGrouping() ? 'true' : 'false'));
$objWriter->writeAttribute('firstSheet', (string) $spreadsheet->getFirstSheetIndex());
$objWriter->writeAttribute('minimized', ($spreadsheet->getMinimized() ? 'true' : 'false'));
$objWriter->writeAttribute('showHorizontalScroll', ($spreadsheet->getShowHorizontalScroll() ? 'true' : 'false'));
$objWriter->writeAttribute('showSheetTabs', ($spreadsheet->getShowSheetTabs() ? 'true' : 'false'));
$objWriter->writeAttribute('showVerticalScroll', ($spreadsheet->getShowVerticalScroll() ? 'true' : 'false'));
$objWriter->writeAttribute('tabRatio', (string) $spreadsheet->getTabRatio());
$objWriter->writeAttribute('visibility', $spreadsheet->getVisibility());
$objWriter->endElement();
$objWriter->endElement();
}
/**
* Write WorkbookProtection.
*/
private function writeWorkbookProtection(XMLWriter $objWriter, Spreadsheet $spreadsheet): void
{
if ($spreadsheet->getSecurity()->isSecurityEnabled()) {
$objWriter->startElement('workbookProtection');
$objWriter->writeAttribute('lockRevision', ($spreadsheet->getSecurity()->getLockRevision() ? 'true' : 'false'));
$objWriter->writeAttribute('lockStructure', ($spreadsheet->getSecurity()->getLockStructure() ? 'true' : 'false'));
$objWriter->writeAttribute('lockWindows', ($spreadsheet->getSecurity()->getLockWindows() ? 'true' : 'false'));
if ($spreadsheet->getSecurity()->getRevisionsPassword() != '') {
$objWriter->writeAttribute('revisionsPassword', $spreadsheet->getSecurity()->getRevisionsPassword());
}
if ($spreadsheet->getSecurity()->getWorkbookPassword() != '') {
$objWriter->writeAttribute('workbookPassword', $spreadsheet->getSecurity()->getWorkbookPassword());
}
$objWriter->endElement();
}
}
/**
* Write calcPr.
*
* @param bool $recalcRequired Indicate whether formulas should be recalculated before writing
*/
private function writeCalcPr(XMLWriter $objWriter, bool $recalcRequired = true): void
{
$objWriter->startElement('calcPr');
// Set the calcid to a higher value than Excel itself will use, otherwise Excel will always recalc
// If MS Excel does do a recalc, then users opening a file in MS Excel will be prompted to save on exit
// because the file has changed
$objWriter->writeAttribute('calcId', '999999');
$objWriter->writeAttribute('calcMode', 'auto');
// fullCalcOnLoad isn't needed if we've recalculating for the save
$objWriter->writeAttribute('calcCompleted', ($recalcRequired) ? '1' : '0');
$objWriter->writeAttribute('fullCalcOnLoad', ($recalcRequired) ? '0' : '1');
$objWriter->writeAttribute('forceFullCalc', ($recalcRequired) ? '0' : '1');
$objWriter->endElement();
}
/**
* Write sheets.
*/
private function writeSheets(XMLWriter $objWriter, Spreadsheet $spreadsheet): void
{
// Write sheets
$objWriter->startElement('sheets');
$sheetCount = $spreadsheet->getSheetCount();
for ($i = 0; $i < $sheetCount; ++$i) {
// sheet
$this->writeSheet(
$objWriter,
$spreadsheet->getSheet($i)->getTitle(),
($i + 1),
($i + 1 + 3),
$spreadsheet->getSheet($i)->getSheetState()
);
}
$objWriter->endElement();
}
/**
* Write sheet.
*
* @param string $worksheetName Sheet name
* @param int $worksheetId Sheet id
* @param int $relId Relationship ID
* @param string $sheetState Sheet state (visible, hidden, veryHidden)
*/
private function writeSheet(XMLWriter $objWriter, string $worksheetName, int $worksheetId = 1, int $relId = 1, string $sheetState = 'visible'): void
{
if ($worksheetName != '') {
// Write sheet
$objWriter->startElement('sheet');
$objWriter->writeAttribute('name', $worksheetName);
$objWriter->writeAttribute('sheetId', (string) $worksheetId);
if ($sheetState !== 'visible' && $sheetState != '') {
$objWriter->writeAttribute('state', $sheetState);
}
$objWriter->writeAttribute('r:id', 'rId' . $relId);
$objWriter->endElement();
} else {
throw new WriterException('Invalid parameters passed.');
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,29 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Writer\Xlsx;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
abstract class WriterPart
{
/**
* Parent Xlsx object.
*/
private Xlsx $parentWriter;
/**
* Get parent Xlsx object.
*/
public function getParentWriter(): Xlsx
{
return $this->parentWriter;
}
/**
* Set parent Xlsx object.
*/
public function __construct(Xlsx $writer)
{
$this->parentWriter = $writer;
}
}