init
This commit is contained in:
commit
72a26edcff
22092 changed files with 2101903 additions and 0 deletions
125
lib/PhpSpreadsheet/Writer/Xlsx/AutoFilter.php
Normal file
125
lib/PhpSpreadsheet/Writer/Xlsx/AutoFilter.php
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
1931
lib/PhpSpreadsheet/Writer/Xlsx/Chart.php
Normal file
1931
lib/PhpSpreadsheet/Writer/Xlsx/Chart.php
Normal file
File diff suppressed because it is too large
Load diff
236
lib/PhpSpreadsheet/Writer/Xlsx/Comments.php
Normal file
236
lib/PhpSpreadsheet/Writer/Xlsx/Comments.php
Normal 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();
|
||||
}
|
||||
}
|
||||
275
lib/PhpSpreadsheet/Writer/Xlsx/ContentTypes.php
Normal file
275
lib/PhpSpreadsheet/Writer/Xlsx/ContentTypes.php
Normal 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.');
|
||||
}
|
||||
}
|
||||
}
|
||||
242
lib/PhpSpreadsheet/Writer/Xlsx/DefinedNames.php
Normal file
242
lib/PhpSpreadsheet/Writer/Xlsx/DefinedNames.php
Normal 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;
|
||||
}
|
||||
}
|
||||
250
lib/PhpSpreadsheet/Writer/Xlsx/DocProps.php
Normal file
250
lib/PhpSpreadsheet/Writer/Xlsx/DocProps.php
Normal 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();
|
||||
}
|
||||
}
|
||||
583
lib/PhpSpreadsheet/Writer/Xlsx/Drawing.php
Normal file
583
lib/PhpSpreadsheet/Writer/Xlsx/Drawing.php
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
193
lib/PhpSpreadsheet/Writer/Xlsx/FunctionPrefix.php
Normal file
193
lib/PhpSpreadsheet/Writer/Xlsx/FunctionPrefix.php
Normal 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));
|
||||
}
|
||||
}
|
||||
511
lib/PhpSpreadsheet/Writer/Xlsx/Rels.php
Normal file
511
lib/PhpSpreadsheet/Writer/Xlsx/Rels.php
Normal 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;
|
||||
}
|
||||
}
|
||||
46
lib/PhpSpreadsheet/Writer/Xlsx/RelsRibbon.php
Normal file
46
lib/PhpSpreadsheet/Writer/Xlsx/RelsRibbon.php
Normal 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();
|
||||
}
|
||||
}
|
||||
40
lib/PhpSpreadsheet/Writer/Xlsx/RelsVBA.php
Normal file
40
lib/PhpSpreadsheet/Writer/Xlsx/RelsVBA.php
Normal 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();
|
||||
}
|
||||
}
|
||||
344
lib/PhpSpreadsheet/Writer/Xlsx/StringTable.php
Normal file
344
lib/PhpSpreadsheet/Writer/Xlsx/StringTable.php
Normal 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;
|
||||
}
|
||||
}
|
||||
726
lib/PhpSpreadsheet/Writer/Xlsx/Style.php
Normal file
726
lib/PhpSpreadsheet/Writer/Xlsx/Style.php
Normal 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;
|
||||
}
|
||||
}
|
||||
115
lib/PhpSpreadsheet/Writer/Xlsx/Table.php
Normal file
115
lib/PhpSpreadsheet/Writer/Xlsx/Table.php
Normal 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();
|
||||
}
|
||||
}
|
||||
744
lib/PhpSpreadsheet/Writer/Xlsx/Theme.php
Normal file
744
lib/PhpSpreadsheet/Writer/Xlsx/Theme.php
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
214
lib/PhpSpreadsheet/Writer/Xlsx/Workbook.php
Normal file
214
lib/PhpSpreadsheet/Writer/Xlsx/Workbook.php
Normal 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.');
|
||||
}
|
||||
}
|
||||
}
|
||||
1581
lib/PhpSpreadsheet/Writer/Xlsx/Worksheet.php
Normal file
1581
lib/PhpSpreadsheet/Writer/Xlsx/Worksheet.php
Normal file
File diff suppressed because it is too large
Load diff
29
lib/PhpSpreadsheet/Writer/Xlsx/WriterPart.php
Normal file
29
lib/PhpSpreadsheet/Writer/Xlsx/WriterPart.php
Normal 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;
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue