package and depencies

This commit is contained in:
RafficMohammed
2023-01-08 02:57:24 +05:30
parent d5332eb421
commit 1d54b8bc7f
4309 changed files with 193331 additions and 172289 deletions

View File

@@ -6,7 +6,7 @@ abstract class BaseWriter implements IWriter
{
/**
* Write charts that are defined in the workbook?
* Identifies whether the Writer should write definitions for any charts that exist in the PhpSpreadsheet object;.
* Identifies whether the Writer should write definitions for any charts that exist in the PhpSpreadsheet object.
*
* @var bool
*/
@@ -50,9 +50,9 @@ abstract class BaseWriter implements IWriter
return $this->includeCharts;
}
public function setIncludeCharts($pValue)
public function setIncludeCharts($includeCharts)
{
$this->includeCharts = (bool) $pValue;
$this->includeCharts = (bool) $includeCharts;
return $this;
}
@@ -62,9 +62,9 @@ abstract class BaseWriter implements IWriter
return $this->preCalculateFormulas;
}
public function setPreCalculateFormulas($pValue)
public function setPreCalculateFormulas($precalculateFormulas)
{
$this->preCalculateFormulas = (bool) $pValue;
$this->preCalculateFormulas = (bool) $precalculateFormulas;
return $this;
}
@@ -74,15 +74,15 @@ abstract class BaseWriter implements IWriter
return $this->useDiskCaching;
}
public function setUseDiskCaching($pValue, $pDirectory = null)
public function setUseDiskCaching($useDiskCache, $cacheDirectory = null)
{
$this->useDiskCaching = $pValue;
$this->useDiskCaching = $useDiskCache;
if ($pDirectory !== null) {
if (is_dir($pDirectory)) {
$this->diskCachingDirectory = $pDirectory;
if ($cacheDirectory !== null) {
if (is_dir($cacheDirectory)) {
$this->diskCachingDirectory = $cacheDirectory;
} else {
throw new Exception("Directory does not exist: $pDirectory");
throw new Exception("Directory does not exist: $cacheDirectory");
}
}
@@ -94,6 +94,16 @@ abstract class BaseWriter implements IWriter
return $this->diskCachingDirectory;
}
protected function processFlags(int $flags): void
{
if (((bool) ($flags & self::SAVE_WITH_CHARTS)) === true) {
$this->setIncludeCharts(true);
}
if (((bool) ($flags & self::DISABLE_PRECALCULATE_FORMULAE)) === true) {
$this->setPreCalculateFormulas(false);
}
}
/**
* Open file handle.
*
@@ -108,7 +118,14 @@ abstract class BaseWriter implements IWriter
return;
}
$fileHandle = $filename ? fopen($filename, 'wb+') : false;
$mode = 'wb';
$scheme = parse_url($filename, PHP_URL_SCHEME);
if ($scheme === 's3') {
// @codeCoverageIgnoreStart
$mode = 'w';
// @codeCoverageIgnoreEnd
}
$fileHandle = $filename ? fopen($filename, $mode) : false;
if ($fileHandle === false) {
throw new Exception('Could not open file "' . $filename . '" for writing.');
}

View File

@@ -43,7 +43,7 @@ class Csv extends BaseWriter
private $sheetIndex = 0;
/**
* Whether to write a BOM (for UTF8).
* Whether to write a UTF8 BOM.
*
* @var bool
*/
@@ -65,9 +65,14 @@ class Csv extends BaseWriter
private $excelCompatibility = false;
/**
* Create a new CSV.
* Output encoding.
*
* @param Spreadsheet $spreadsheet Spreadsheet object
* @var string
*/
private $outputEncoding = '';
/**
* Create a new CSV.
*/
public function __construct(Spreadsheet $spreadsheet)
{
@@ -77,10 +82,12 @@ class Csv extends BaseWriter
/**
* Save PhpSpreadsheet to file.
*
* @param resource|string $pFilename
* @param resource|string $filename
*/
public function save($pFilename): void
public function save($filename, int $flags = 0): void
{
$this->processFlags($flags);
// Fetch sheet
$sheet = $this->spreadsheet->getSheet($this->sheetIndex);
@@ -90,7 +97,7 @@ class Csv extends BaseWriter
Calculation::setArrayReturnType(Calculation::RETURN_ARRAY_AS_VALUE);
// Open file
$this->openFileHandle($pFilename);
$this->openFileHandle($filename);
if ($this->excelCompatibility) {
$this->setUseBOM(true); // Enforce UTF-8 BOM Header
@@ -127,132 +134,82 @@ class Csv extends BaseWriter
Calculation::getInstance($this->spreadsheet)->getDebugLog()->setWriteDebugLog($saveDebugLog);
}
/**
* Get delimiter.
*
* @return string
*/
public function getDelimiter()
public function getDelimiter(): string
{
return $this->delimiter;
}
/**
* Set delimiter.
*
* @param string $pValue Delimiter, defaults to ','
*
* @return $this
*/
public function setDelimiter($pValue)
public function setDelimiter(string $delimiter): self
{
$this->delimiter = $pValue;
$this->delimiter = $delimiter;
return $this;
}
/**
* Get enclosure.
*
* @return string
*/
public function getEnclosure()
public function getEnclosure(): string
{
return $this->enclosure;
}
/**
* Set enclosure.
*
* @param string $pValue Enclosure, defaults to "
*
* @return $this
*/
public function setEnclosure($pValue = '"')
public function setEnclosure(string $enclosure = '"'): self
{
$this->enclosure = $pValue;
$this->enclosure = $enclosure;
return $this;
}
/**
* Get line ending.
*
* @return string
*/
public function getLineEnding()
public function getLineEnding(): string
{
return $this->lineEnding;
}
/**
* Set line ending.
*
* @param string $pValue Line ending, defaults to OS line ending (PHP_EOL)
*
* @return $this
*/
public function setLineEnding($pValue)
public function setLineEnding(string $lineEnding): self
{
$this->lineEnding = $pValue;
$this->lineEnding = $lineEnding;
return $this;
}
/**
* Get whether BOM should be used.
*
* @return bool
*/
public function getUseBOM()
public function getUseBOM(): bool
{
return $this->useBOM;
}
/**
* Set whether BOM should be used.
*
* @param bool $pValue Use UTF-8 byte-order mark? Defaults to false
*
* @return $this
* Set whether BOM should be used, typically when non-ASCII characters are used.
*/
public function setUseBOM($pValue)
public function setUseBOM(bool $useBOM): self
{
$this->useBOM = $pValue;
$this->useBOM = $useBOM;
return $this;
}
/**
* Get whether a separator line should be included.
*
* @return bool
*/
public function getIncludeSeparatorLine()
public function getIncludeSeparatorLine(): bool
{
return $this->includeSeparatorLine;
}
/**
* Set whether a separator line should be included as the first line of the file.
*
* @param bool $pValue Use separator line? Defaults to false
*
* @return $this
*/
public function setIncludeSeparatorLine($pValue)
public function setIncludeSeparatorLine(bool $includeSeparatorLine): self
{
$this->includeSeparatorLine = $pValue;
$this->includeSeparatorLine = $includeSeparatorLine;
return $this;
}
/**
* Get whether the file should be saved with full Excel Compatibility.
*
* @return bool
*/
public function getExcelCompatibility()
public function getExcelCompatibility(): bool
{
return $this->excelCompatibility;
}
@@ -260,42 +217,41 @@ class Csv extends BaseWriter
/**
* Set whether the file should be saved with full Excel Compatibility.
*
* @param bool $pValue Set the file to be written as a fully Excel compatible csv file
* @param bool $excelCompatibility Set the file to be written as a fully Excel compatible csv file
* Note that this overrides other settings such as useBOM, enclosure and delimiter
*
* @return $this
*/
public function setExcelCompatibility($pValue)
public function setExcelCompatibility(bool $excelCompatibility): self
{
$this->excelCompatibility = $pValue;
$this->excelCompatibility = $excelCompatibility;
return $this;
}
/**
* Get sheet index.
*
* @return int
*/
public function getSheetIndex()
public function getSheetIndex(): int
{
return $this->sheetIndex;
}
/**
* Set sheet index.
*
* @param int $pValue Sheet index
*
* @return $this
*/
public function setSheetIndex($pValue)
public function setSheetIndex(int $sheetIndex): self
{
$this->sheetIndex = $pValue;
$this->sheetIndex = $sheetIndex;
return $this;
}
public function getOutputEncoding(): string
{
return $this->outputEncoding;
}
public function setOutputEncoding(string $outputEnconding): self
{
$this->outputEncoding = $outputEnconding;
return $this;
}
/** @var bool */
private $enclosureRequired = true;
public function setEnclosureRequired(bool $value): self
@@ -310,13 +266,27 @@ class Csv extends BaseWriter
return $this->enclosureRequired;
}
/**
* Convert boolean to TRUE/FALSE; otherwise return element cast to string.
*
* @param mixed $element
*/
private static function elementToString($element): string
{
if (is_bool($element)) {
return $element ? 'TRUE' : 'FALSE';
}
return (string) $element;
}
/**
* Write line to CSV file.
*
* @param resource $pFileHandle PHP filehandle
* @param array $pValues Array containing values in a row
* @param resource $fileHandle PHP filehandle
* @param array $values Array containing values in a row
*/
private function writeLine($pFileHandle, array $pValues): void
private function writeLine($fileHandle, array $values): void
{
// No leading delimiter
$delimiter = '';
@@ -324,7 +294,8 @@ class Csv extends BaseWriter
// Build the line
$line = '';
foreach ($pValues as $element) {
foreach ($values as $element) {
$element = self::elementToString($element);
// Add delimiter
$line .= $delimiter;
$delimiter = $this->delimiter;
@@ -347,6 +318,9 @@ class Csv extends BaseWriter
$line .= $this->lineEnding;
// Write to file
fwrite($pFileHandle, $line);
if ($this->outputEncoding != '') {
$line = mb_convert_encoding($line, $this->outputEncoding);
}
fwrite($fileHandle, /** @scrutinizer ignore-type */ $line);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -6,8 +6,14 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet;
interface IWriter
{
public const SAVE_WITH_CHARTS = 1;
public const DISABLE_PRECALCULATE_FORMULAE = 2;
/**
* IWriter constructor.
*
* @param Spreadsheet $spreadsheet The spreadsheet that we want to save using this Writer
*/
public function __construct(Spreadsheet $spreadsheet);
@@ -25,11 +31,11 @@ interface IWriter
* Set to true, to advise the Writer to include any charts that exist in the PhpSpreadsheet object.
* Set to false (the default) to ignore charts.
*
* @param bool $pValue
* @param bool $includeCharts
*
* @return IWriter
*/
public function setIncludeCharts($pValue);
public function setIncludeCharts($includeCharts);
/**
* Get Pre-Calculate Formulas flag
@@ -48,18 +54,23 @@ interface IWriter
* Set to true (the default) to advise the Writer to calculate all formulae on save
* Set to false to prevent precalculation of formulae on save.
*
* @param bool $pValue Pre-Calculate Formulas?
* @param bool $precalculateFormulas Pre-Calculate Formulas?
*
* @return IWriter
*/
public function setPreCalculateFormulas($pValue);
public function setPreCalculateFormulas($precalculateFormulas);
/**
* Save PhpSpreadsheet to file.
*
* @param resource|string $pFilename Name of the file to save
* @param resource|string $filename Name of the file to save
* @param int $flags Flags that can change the behaviour of the Writer:
* self::SAVE_WITH_CHARTS Save any charts that are defined (if the Writer supports Charts)
* self::DISABLE_PRECALCULATE_FORMULAE Don't Precalculate formulae before saving the file
*
* @throws Exception
*/
public function save($pFilename);
public function save($filename, int $flags = 0): void;
/**
* Get use disk caching where possible?
@@ -71,12 +82,12 @@ interface IWriter
/**
* Set use disk caching where possible?
*
* @param bool $pValue
* @param string $pDirectory Disk caching directory
* @param bool $useDiskCache
* @param string $cacheDirectory Disk caching directory
*
* @return IWriter
*/
public function setUseDiskCaching($pValue, $pDirectory = null);
public function setUseDiskCaching($useDiskCache, $cacheDirectory = null);
/**
* Get disk caching directory.

View File

@@ -2,7 +2,6 @@
namespace PhpOffice\PhpSpreadsheet\Writer;
use PhpOffice\PhpSpreadsheet\Shared\File;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Writer\Exception as WriterException;
use PhpOffice\PhpSpreadsheet\Writer\Ods\Content;
@@ -18,13 +17,6 @@ use ZipStream\ZipStream;
class Ods extends BaseWriter
{
/**
* Private writer parts.
*
* @var Ods\WriterPart[]
*/
private $writerParts = [];
/**
* Private PhpSpreadsheet.
*
@@ -32,6 +24,41 @@ class Ods extends BaseWriter
*/
private $spreadSheet;
/**
* @var Content
*/
private $writerPartContent;
/**
* @var Meta
*/
private $writerPartMeta;
/**
* @var MetaInf
*/
private $writerPartMetaInf;
/**
* @var Mimetype
*/
private $writerPartMimetype;
/**
* @var Settings
*/
private $writerPartSettings;
/**
* @var Styles
*/
private $writerPartStyles;
/**
* @var Thumbnails
*/
private $writerPartThumbnails;
/**
* Create a new Ods.
*/
@@ -39,62 +66,78 @@ class Ods extends BaseWriter
{
$this->setSpreadsheet($spreadsheet);
$writerPartsArray = [
'content' => Content::class,
'meta' => Meta::class,
'meta_inf' => MetaInf::class,
'mimetype' => Mimetype::class,
'settings' => Settings::class,
'styles' => Styles::class,
'thumbnails' => Thumbnails::class,
];
foreach ($writerPartsArray as $writer => $class) {
$this->writerParts[$writer] = new $class($this);
}
$this->writerPartContent = new Content($this);
$this->writerPartMeta = new Meta($this);
$this->writerPartMetaInf = new MetaInf($this);
$this->writerPartMimetype = new Mimetype($this);
$this->writerPartSettings = new Settings($this);
$this->writerPartStyles = new Styles($this);
$this->writerPartThumbnails = new Thumbnails($this);
}
/**
* Get writer part.
*
* @param string $pPartName Writer part name
*
* @return null|Ods\WriterPart
*/
public function getWriterPart($pPartName)
public function getWriterPartContent(): Content
{
if ($pPartName != '' && isset($this->writerParts[strtolower($pPartName)])) {
return $this->writerParts[strtolower($pPartName)];
}
return $this->writerPartContent;
}
return null;
public function getWriterPartMeta(): Meta
{
return $this->writerPartMeta;
}
public function getWriterPartMetaInf(): MetaInf
{
return $this->writerPartMetaInf;
}
public function getWriterPartMimetype(): Mimetype
{
return $this->writerPartMimetype;
}
public function getWriterPartSettings(): Settings
{
return $this->writerPartSettings;
}
public function getWriterPartStyles(): Styles
{
return $this->writerPartStyles;
}
public function getWriterPartThumbnails(): Thumbnails
{
return $this->writerPartThumbnails;
}
/**
* Save PhpSpreadsheet to file.
*
* @param resource|string $pFilename
* @param resource|string $filename
*/
public function save($pFilename): void
public function save($filename, int $flags = 0): void
{
if (!$this->spreadSheet) {
throw new WriterException('PhpSpreadsheet object unassigned.');
}
$this->processFlags($flags);
// garbage collect
$this->spreadSheet->garbageCollect();
$this->openFileHandle($pFilename);
$this->openFileHandle($filename);
$zip = $this->createZip();
$zip->addFile('META-INF/manifest.xml', $this->getWriterPart('meta_inf')->writeManifest());
$zip->addFile('Thumbnails/thumbnail.png', $this->getWriterPart('thumbnails')->writeThumbnail());
$zip->addFile('content.xml', $this->getWriterPart('content')->write());
$zip->addFile('meta.xml', $this->getWriterPart('meta')->write());
$zip->addFile('mimetype', $this->getWriterPart('mimetype')->write());
$zip->addFile('settings.xml', $this->getWriterPart('settings')->write());
$zip->addFile('styles.xml', $this->getWriterPart('styles')->write());
$zip->addFile('META-INF/manifest.xml', $this->getWriterPartMetaInf()->write());
$zip->addFile('Thumbnails/thumbnail.png', $this->getWriterPartthumbnails()->write());
// Settings always need to be written before Content; Styles after Content
$zip->addFile('settings.xml', $this->getWriterPartsettings()->write());
$zip->addFile('content.xml', $this->getWriterPartcontent()->write());
$zip->addFile('meta.xml', $this->getWriterPartmeta()->write());
$zip->addFile('mimetype', $this->getWriterPartmimetype()->write());
$zip->addFile('styles.xml', $this->getWriterPartstyles()->write());
// Close file
try {

View File

@@ -0,0 +1,66 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Writer\Ods;
use PhpOffice\PhpSpreadsheet\Shared\XMLWriter;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
class AutoFilters
{
/**
* @var XMLWriter
*/
private $objWriter;
/**
* @var Spreadsheet
*/
private $spreadsheet;
public function __construct(XMLWriter $objWriter, Spreadsheet $spreadsheet)
{
$this->objWriter = $objWriter;
$this->spreadsheet = $spreadsheet;
}
/** @var mixed */
private static $scrutinizerFalse = false;
public function write(): void
{
$wrapperWritten = self::$scrutinizerFalse;
$sheetCount = $this->spreadsheet->getSheetCount();
for ($i = 0; $i < $sheetCount; ++$i) {
$worksheet = $this->spreadsheet->getSheet($i);
$autofilter = $worksheet->getAutoFilter();
if ($autofilter !== null && !empty($autofilter->getRange())) {
if ($wrapperWritten === false) {
$this->objWriter->startElement('table:database-ranges');
$wrapperWritten = true;
}
$this->objWriter->startElement('table:database-range');
$this->objWriter->writeAttribute('table:orientation', 'column');
$this->objWriter->writeAttribute('table:display-filter-buttons', 'true');
$this->objWriter->writeAttribute(
'table:target-range-address',
$this->formatRange($worksheet, $autofilter)
);
$this->objWriter->endElement();
}
}
if ($wrapperWritten === true) {
$this->objWriter->endElement();
}
}
protected function formatRange(Worksheet $worksheet, Autofilter $autofilter): string
{
$title = $worksheet->getTitle();
$range = $autofilter->getRange();
return "'{$title}'.{$range}";
}
}

View File

@@ -0,0 +1,262 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Writer\Ods\Cell;
use PhpOffice\PhpSpreadsheet\Helper\Dimension;
use PhpOffice\PhpSpreadsheet\Shared\XMLWriter;
use PhpOffice\PhpSpreadsheet\Style\Alignment;
use PhpOffice\PhpSpreadsheet\Style\Fill;
use PhpOffice\PhpSpreadsheet\Style\Font;
use PhpOffice\PhpSpreadsheet\Style\Style as CellStyle;
use PhpOffice\PhpSpreadsheet\Worksheet\ColumnDimension;
use PhpOffice\PhpSpreadsheet\Worksheet\RowDimension;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
class Style
{
public const CELL_STYLE_PREFIX = 'ce';
public const COLUMN_STYLE_PREFIX = 'co';
public const ROW_STYLE_PREFIX = 'ro';
public const TABLE_STYLE_PREFIX = 'ta';
private $writer;
public function __construct(XMLWriter $writer)
{
$this->writer = $writer;
}
private function mapHorizontalAlignment(string $horizontalAlignment): string
{
switch ($horizontalAlignment) {
case Alignment::HORIZONTAL_CENTER:
case Alignment::HORIZONTAL_CENTER_CONTINUOUS:
case Alignment::HORIZONTAL_DISTRIBUTED:
return 'center';
case Alignment::HORIZONTAL_RIGHT:
return 'end';
case Alignment::HORIZONTAL_FILL:
case Alignment::HORIZONTAL_JUSTIFY:
return 'justify';
}
return 'start';
}
private function mapVerticalAlignment(string $verticalAlignment): string
{
switch ($verticalAlignment) {
case Alignment::VERTICAL_TOP:
return 'top';
case Alignment::VERTICAL_CENTER:
return 'middle';
case Alignment::VERTICAL_DISTRIBUTED:
case Alignment::VERTICAL_JUSTIFY:
return 'automatic';
}
return 'bottom';
}
private function writeFillStyle(Fill $fill): void
{
switch ($fill->getFillType()) {
case Fill::FILL_SOLID:
$this->writer->writeAttribute('fo:background-color', sprintf(
'#%s',
strtolower($fill->getStartColor()->getRGB())
));
break;
case Fill::FILL_GRADIENT_LINEAR:
case Fill::FILL_GRADIENT_PATH:
/// TODO :: To be implemented
break;
case Fill::FILL_NONE:
default:
}
}
private function writeCellProperties(CellStyle $style): void
{
// Align
$hAlign = $style->getAlignment()->getHorizontal();
$vAlign = $style->getAlignment()->getVertical();
$wrap = $style->getAlignment()->getWrapText();
$this->writer->startElement('style:table-cell-properties');
if (!empty($vAlign) || $wrap) {
if (!empty($vAlign)) {
$vAlign = $this->mapVerticalAlignment($vAlign);
$this->writer->writeAttribute('style:vertical-align', $vAlign);
}
if ($wrap) {
$this->writer->writeAttribute('fo:wrap-option', 'wrap');
}
}
$this->writer->writeAttribute('style:rotation-align', 'none');
// Fill
if ($fill = $style->getFill()) {
$this->writeFillStyle($fill);
}
$this->writer->endElement();
if (!empty($hAlign)) {
$hAlign = $this->mapHorizontalAlignment($hAlign);
$this->writer->startElement('style:paragraph-properties');
$this->writer->writeAttribute('fo:text-align', $hAlign);
$this->writer->endElement();
}
}
protected function mapUnderlineStyle(Font $font): string
{
switch ($font->getUnderline()) {
case Font::UNDERLINE_DOUBLE:
case Font::UNDERLINE_DOUBLEACCOUNTING:
return'double';
case Font::UNDERLINE_SINGLE:
case Font::UNDERLINE_SINGLEACCOUNTING:
return'single';
}
return 'none';
}
protected function writeTextProperties(CellStyle $style): void
{
// Font
$this->writer->startElement('style:text-properties');
$font = $style->getFont();
if ($font->getBold()) {
$this->writer->writeAttribute('fo:font-weight', 'bold');
$this->writer->writeAttribute('style:font-weight-complex', 'bold');
$this->writer->writeAttribute('style:font-weight-asian', 'bold');
}
if ($font->getItalic()) {
$this->writer->writeAttribute('fo:font-style', 'italic');
}
if ($color = $font->getColor()) {
$this->writer->writeAttribute('fo:color', sprintf('#%s', $color->getRGB()));
}
if ($family = $font->getName()) {
$this->writer->writeAttribute('fo:font-family', $family);
}
if ($size = $font->getSize()) {
$this->writer->writeAttribute('fo:font-size', sprintf('%.1Fpt', $size));
}
if ($font->getUnderline() && $font->getUnderline() !== Font::UNDERLINE_NONE) {
$this->writer->writeAttribute('style:text-underline-style', 'solid');
$this->writer->writeAttribute('style:text-underline-width', 'auto');
$this->writer->writeAttribute('style:text-underline-color', 'font-color');
$underline = $this->mapUnderlineStyle($font);
$this->writer->writeAttribute('style:text-underline-type', $underline);
}
$this->writer->endElement(); // Close style:text-properties
}
protected function writeColumnProperties(ColumnDimension $columnDimension): void
{
$this->writer->startElement('style:table-column-properties');
$this->writer->writeAttribute(
'style:column-width',
round($columnDimension->getWidth(Dimension::UOM_CENTIMETERS), 3) . 'cm'
);
$this->writer->writeAttribute('fo:break-before', 'auto');
// End
$this->writer->endElement(); // Close style:table-column-properties
}
public function writeColumnStyles(ColumnDimension $columnDimension, int $sheetId): void
{
$this->writer->startElement('style:style');
$this->writer->writeAttribute('style:family', 'table-column');
$this->writer->writeAttribute(
'style:name',
sprintf('%s_%d_%d', self::COLUMN_STYLE_PREFIX, $sheetId, $columnDimension->getColumnNumeric())
);
$this->writeColumnProperties($columnDimension);
// End
$this->writer->endElement(); // Close style:style
}
protected function writeRowProperties(RowDimension $rowDimension): void
{
$this->writer->startElement('style:table-row-properties');
$this->writer->writeAttribute(
'style:row-height',
round($rowDimension->getRowHeight(Dimension::UOM_CENTIMETERS), 3) . 'cm'
);
$this->writer->writeAttribute('style:use-optimal-row-height', 'false');
$this->writer->writeAttribute('fo:break-before', 'auto');
// End
$this->writer->endElement(); // Close style:table-row-properties
}
public function writeRowStyles(RowDimension $rowDimension, int $sheetId): void
{
$this->writer->startElement('style:style');
$this->writer->writeAttribute('style:family', 'table-row');
$this->writer->writeAttribute(
'style:name',
sprintf('%s_%d_%d', self::ROW_STYLE_PREFIX, $sheetId, $rowDimension->getRowIndex())
);
$this->writeRowProperties($rowDimension);
// End
$this->writer->endElement(); // Close style:style
}
public function writeTableStyle(Worksheet $worksheet, int $sheetId): void
{
$this->writer->startElement('style:style');
$this->writer->writeAttribute('style:family', 'table');
$this->writer->writeAttribute(
'style:name',
sprintf('%s%d', self::TABLE_STYLE_PREFIX, $sheetId)
);
$this->writer->startElement('style:table-properties');
$this->writer->writeAttribute(
'table:display',
$worksheet->getSheetState() === Worksheet::SHEETSTATE_VISIBLE ? 'true' : 'false'
);
$this->writer->endElement(); // Close style:table-properties
$this->writer->endElement(); // Close style:style
}
public function write(CellStyle $style): void
{
$this->writer->startElement('style:style');
$this->writer->writeAttribute('style:name', self::CELL_STYLE_PREFIX . $style->getIndex());
$this->writer->writeAttribute('style:family', 'table-cell');
$this->writer->writeAttribute('style:parent-style-name', 'Default');
// Alignment, fill colour, etc
$this->writeCellProperties($style);
// style:text-properties
$this->writeTextProperties($style);
// End
$this->writer->endElement(); // Close style:style
}
}

View File

@@ -7,13 +7,13 @@ use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
use PhpOffice\PhpSpreadsheet\Cell\DataType;
use PhpOffice\PhpSpreadsheet\Shared\XMLWriter;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Style\Fill;
use PhpOffice\PhpSpreadsheet\Style\Font;
use PhpOffice\PhpSpreadsheet\Worksheet\Row;
use PhpOffice\PhpSpreadsheet\Worksheet\RowCellIterator;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
use PhpOffice\PhpSpreadsheet\Writer\Exception;
use PhpOffice\PhpSpreadsheet\Writer\Ods;
use PhpOffice\PhpSpreadsheet\Writer\Ods\Cell\Comment;
use PhpOffice\PhpSpreadsheet\Writer\Ods\Cell\Style;
/**
* @author Alexander Pervakov <frost-nzcr4@jagmort.com>
@@ -22,7 +22,6 @@ class Content extends WriterPart
{
const NUMBER_COLS_REPEATED_MAX = 1024;
const NUMBER_ROWS_REPEATED_MAX = 1048576;
const CELL_STYLE_PREFIX = 'ce';
private $formulaConvertor;
@@ -41,7 +40,7 @@ class Content extends WriterPart
*
* @return string XML Output
*/
public function write()
public function write(): string
{
$objWriter = null;
if ($this->getParentWriter()->getUseDiskCaching()) {
@@ -103,6 +102,7 @@ class Content extends WriterPart
$this->writeSheets($objWriter);
(new AutoFilters($objWriter, $this->getParentWriter()->getSpreadsheet()))->write();
// Defined names (ranges and formulae)
(new NamedExpressions($objWriter, $this->getParentWriter()->getSpreadsheet(), $this->formulaConvertor))->write();
@@ -120,14 +120,22 @@ class Content extends WriterPart
{
$spreadsheet = $this->getParentWriter()->getSpreadsheet(); /** @var Spreadsheet $spreadsheet */
$sheetCount = $spreadsheet->getSheetCount();
for ($i = 0; $i < $sheetCount; ++$i) {
for ($sheetIndex = 0; $sheetIndex < $sheetCount; ++$sheetIndex) {
$objWriter->startElement('table:table');
$objWriter->writeAttribute('table:name', $spreadsheet->getSheet($i)->getTitle());
$objWriter->writeAttribute('table:name', $spreadsheet->getSheet($sheetIndex)->getTitle());
$objWriter->writeAttribute('table:style-name', Style::TABLE_STYLE_PREFIX . (string) ($sheetIndex + 1));
$objWriter->writeElement('office:forms');
$objWriter->startElement('table:table-column');
$objWriter->writeAttribute('table:number-columns-repeated', self::NUMBER_COLS_REPEATED_MAX);
$objWriter->endElement();
$this->writeRows($objWriter, $spreadsheet->getSheet($i));
foreach ($spreadsheet->getSheet($sheetIndex)->getColumnDimensions() as $columnDimension) {
$objWriter->startElement('table:table-column');
$objWriter->writeAttribute(
'table:style-name',
sprintf('%s_%d_%d', Style::COLUMN_STYLE_PREFIX, $sheetIndex, $columnDimension->getColumnNumeric())
);
$objWriter->writeAttribute('table:default-cell-style-name', 'ce0');
// $objWriter->writeAttribute('table:number-columns-repeated', self::NUMBER_COLS_REPEATED_MAX);
$objWriter->endElement();
}
$this->writeRows($objWriter, $spreadsheet->getSheet($sheetIndex), $sheetIndex);
$objWriter->endElement();
}
}
@@ -135,47 +143,49 @@ class Content extends WriterPart
/**
* Write rows of the specified sheet.
*/
private function writeRows(XMLWriter $objWriter, Worksheet $sheet): void
private function writeRows(XMLWriter $objWriter, Worksheet $sheet, int $sheetIndex): void
{
$numberRowsRepeated = self::NUMBER_ROWS_REPEATED_MAX;
$span_row = 0;
$rows = $sheet->getRowIterator();
while ($rows->valid()) {
foreach ($rows as $row) {
$cellIterator = $row->getCellIterator();
--$numberRowsRepeated;
$row = $rows->current();
if ($row->getCellIterator()->valid()) {
if ($cellIterator->valid()) {
$objWriter->startElement('table:table-row');
if ($span_row) {
$objWriter->startElement('table:table-row');
if ($span_row > 1) {
$objWriter->writeAttribute('table:number-rows-repeated', $span_row);
}
$objWriter->startElement('table:table-cell');
$objWriter->writeAttribute('table:number-columns-repeated', self::NUMBER_COLS_REPEATED_MAX);
$objWriter->endElement();
$objWriter->writeAttribute('table:number-columns-repeated', (string) self::NUMBER_COLS_REPEATED_MAX);
$objWriter->endElement();
$span_row = 0;
} else {
if ($sheet->getRowDimension($row->getRowIndex())->getRowHeight() > 0) {
$objWriter->writeAttribute(
'table:style-name',
sprintf('%s_%d_%d', Style::ROW_STYLE_PREFIX, $sheetIndex, $row->getRowIndex())
);
}
$this->writeCells($objWriter, $cellIterator);
}
$objWriter->startElement('table:table-row');
$this->writeCells($objWriter, $row);
$objWriter->endElement();
} else {
++$span_row;
}
$rows->next();
}
}
/**
* Write cells of the specified row.
*/
private function writeCells(XMLWriter $objWriter, Row $row): void
private function writeCells(XMLWriter $objWriter, RowCellIterator $cells): void
{
$numberColsRepeated = self::NUMBER_COLS_REPEATED_MAX;
$prevColumn = -1;
$cells = $row->getCellIterator();
while ($cells->valid()) {
foreach ($cells as $cell) {
/** @var \PhpOffice\PhpSpreadsheet\Cell\Cell $cell */
$cell = $cells->current();
$column = Coordinate::columnIndexFromString($cell->getColumn()) - 1;
$this->writeCellSpan($objWriter, $column, $prevColumn);
@@ -185,7 +195,7 @@ class Content extends WriterPart
// Style XF
$style = $cell->getXfIndex();
if ($style !== null) {
$objWriter->writeAttribute('table:style-name', self::CELL_STYLE_PREFIX . $style);
$objWriter->writeAttribute('table:style-name', Style::CELL_STYLE_PREFIX . $style);
}
switch ($cell->getDataType()) {
@@ -196,7 +206,10 @@ class Content extends WriterPart
break;
case DataType::TYPE_ERROR:
throw new Exception('Writing of error not implemented yet.');
$objWriter->writeAttribute('table:formula', 'of:=#NULL!');
$objWriter->writeAttribute('office:value-type', 'string');
$objWriter->writeAttribute('office:string-value', '');
$objWriter->writeElement('text:p', '#NULL!');
break;
case DataType::TYPE_FORMULA:
@@ -217,10 +230,6 @@ class Content extends WriterPart
$objWriter->writeAttribute('office:value', $formulaValue);
$objWriter->writeElement('text:p', $formulaValue);
break;
case DataType::TYPE_INLINE:
throw new Exception('Writing of inline not implemented yet.');
break;
case DataType::TYPE_NUMERIC:
$objWriter->writeAttribute('office:value-type', 'float');
@@ -228,6 +237,8 @@ class Content extends WriterPart
$objWriter->writeElement('text:p', $cell->getValue());
break;
case DataType::TYPE_INLINE:
// break intentionally omitted
case DataType::TYPE_STRING:
$objWriter->writeAttribute('office:value-type', 'string');
$objWriter->writeElement('text:p', $cell->getValue());
@@ -237,8 +248,8 @@ class Content extends WriterPart
Comment::write($objWriter, $cell);
$objWriter->endElement();
$prevColumn = $column;
$cells->next();
}
$numberColsRepeated = $numberColsRepeated - $prevColumn - 1;
if ($numberColsRepeated > 0) {
if ($numberColsRepeated > 1) {
@@ -274,89 +285,31 @@ class Content extends WriterPart
*/
private function writeXfStyles(XMLWriter $writer, Spreadsheet $spreadsheet): void
{
$styleWriter = new Style($writer);
$sheetCount = $spreadsheet->getSheetCount();
for ($i = 0; $i < $sheetCount; ++$i) {
$worksheet = $spreadsheet->getSheet($i);
$styleWriter->writeTableStyle($worksheet, $i + 1);
$worksheet->calculateColumnWidths();
foreach ($worksheet->getColumnDimensions() as $columnDimension) {
if ($columnDimension->getWidth() !== -1.0) {
$styleWriter->writeColumnStyles($columnDimension, $i);
}
}
}
for ($i = 0; $i < $sheetCount; ++$i) {
$worksheet = $spreadsheet->getSheet($i);
foreach ($worksheet->getRowDimensions() as $rowDimension) {
if ($rowDimension->getRowHeight() > 0.0) {
$styleWriter->writeRowStyles($rowDimension, $i);
}
}
}
foreach ($spreadsheet->getCellXfCollection() as $style) {
$writer->startElement('style:style');
$writer->writeAttribute('style:name', self::CELL_STYLE_PREFIX . $style->getIndex());
$writer->writeAttribute('style:family', 'table-cell');
$writer->writeAttribute('style:parent-style-name', 'Default');
// style:text-properties
// Font
$writer->startElement('style:text-properties');
$font = $style->getFont();
if ($font->getBold()) {
$writer->writeAttribute('fo:font-weight', 'bold');
$writer->writeAttribute('style:font-weight-complex', 'bold');
$writer->writeAttribute('style:font-weight-asian', 'bold');
}
if ($font->getItalic()) {
$writer->writeAttribute('fo:font-style', 'italic');
}
if ($color = $font->getColor()) {
$writer->writeAttribute('fo:color', sprintf('#%s', $color->getRGB()));
}
if ($family = $font->getName()) {
$writer->writeAttribute('fo:font-family', $family);
}
if ($size = $font->getSize()) {
$writer->writeAttribute('fo:font-size', sprintf('%.1Fpt', $size));
}
if ($font->getUnderline() && $font->getUnderline() != Font::UNDERLINE_NONE) {
$writer->writeAttribute('style:text-underline-style', 'solid');
$writer->writeAttribute('style:text-underline-width', 'auto');
$writer->writeAttribute('style:text-underline-color', 'font-color');
switch ($font->getUnderline()) {
case Font::UNDERLINE_DOUBLE:
$writer->writeAttribute('style:text-underline-type', 'double');
break;
case Font::UNDERLINE_SINGLE:
$writer->writeAttribute('style:text-underline-type', 'single');
break;
}
}
$writer->endElement(); // Close style:text-properties
// style:table-cell-properties
$writer->startElement('style:table-cell-properties');
$writer->writeAttribute('style:rotation-align', 'none');
// Fill
if ($fill = $style->getFill()) {
switch ($fill->getFillType()) {
case Fill::FILL_SOLID:
$writer->writeAttribute('fo:background-color', sprintf(
'#%s',
strtolower($fill->getStartColor()->getRGB())
));
break;
case Fill::FILL_GRADIENT_LINEAR:
case Fill::FILL_GRADIENT_PATH:
/// TODO :: To be implemented
break;
case Fill::FILL_NONE:
default:
}
}
$writer->endElement(); // Close style:table-cell-properties
// End
$writer->endElement(); // Close style:style
$styleWriter->write($style);
}
}
@@ -374,9 +327,9 @@ class Content extends WriterPart
$start = Coordinate::coordinateFromString($startCell);
$end = Coordinate::coordinateFromString($endCell);
$columnSpan = Coordinate::columnIndexFromString($end[0]) - Coordinate::columnIndexFromString($start[0]) + 1;
$rowSpan = $end[1] - $start[1] + 1;
$rowSpan = ((int) $end[1]) - ((int) $start[1]) + 1;
$objWriter->writeAttribute('table:number-columns-spanned', $columnSpan);
$objWriter->writeAttribute('table:number-rows-spanned', $rowSpan);
$objWriter->writeAttribute('table:number-columns-spanned', (string) $columnSpan);
$objWriter->writeAttribute('table:number-rows-spanned', (string) $rowSpan);
}
}

View File

@@ -2,6 +2,8 @@
namespace PhpOffice\PhpSpreadsheet\Writer\Ods;
use PhpOffice\PhpSpreadsheet\Document\Properties;
use PhpOffice\PhpSpreadsheet\Shared\Date;
use PhpOffice\PhpSpreadsheet\Shared\XMLWriter;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
@@ -10,15 +12,11 @@ class Meta extends WriterPart
/**
* Write meta.xml to XML format.
*
* @param Spreadsheet $spreadsheet
*
* @return string XML Output
*/
public function write(?Spreadsheet $spreadsheet = null)
public function write(): string
{
if (!$spreadsheet) {
$spreadsheet = $this->getParentWriter()->getSpreadsheet();
}
$spreadsheet = $this->getParentWriter()->getSpreadsheet();
$objWriter = null;
if ($this->getParentWriter()->getUseDiskCaching()) {
@@ -45,15 +43,24 @@ class Meta extends WriterPart
$objWriter->writeElement('meta:initial-creator', $spreadsheet->getProperties()->getCreator());
$objWriter->writeElement('dc:creator', $spreadsheet->getProperties()->getCreator());
$objWriter->writeElement('meta:creation-date', date(DATE_W3C, $spreadsheet->getProperties()->getCreated()));
$objWriter->writeElement('dc:date', date(DATE_W3C, $spreadsheet->getProperties()->getCreated()));
$created = $spreadsheet->getProperties()->getCreated();
$date = Date::dateTimeFromTimestamp("$created");
$date->setTimeZone(Date::getDefaultOrLocalTimeZone());
$objWriter->writeElement('meta:creation-date', $date->format(DATE_W3C));
$created = $spreadsheet->getProperties()->getModified();
$date = Date::dateTimeFromTimestamp("$created");
$date->setTimeZone(Date::getDefaultOrLocalTimeZone());
$objWriter->writeElement('dc:date', $date->format(DATE_W3C));
$objWriter->writeElement('dc:title', $spreadsheet->getProperties()->getTitle());
$objWriter->writeElement('dc:description', $spreadsheet->getProperties()->getDescription());
$objWriter->writeElement('dc:subject', $spreadsheet->getProperties()->getSubject());
$keywords = explode(' ', $spreadsheet->getProperties()->getKeywords());
foreach ($keywords as $keyword) {
$objWriter->writeElement('meta:keyword', $keyword);
}
$objWriter->writeElement('meta:keyword', $spreadsheet->getProperties()->getKeywords());
// Don't know if this changed over time, but the keywords are all
// in a single declaration now.
//$keywords = explode(' ', $spreadsheet->getProperties()->getKeywords());
//foreach ($keywords as $keyword) {
// $objWriter->writeElement('meta:keyword', $keyword);
//}
//<meta:document-statistic meta:table-count="XXX" meta:cell-count="XXX" meta:object-count="XXX"/>
$objWriter->startElement('meta:user-defined');
@@ -66,10 +73,50 @@ class Meta extends WriterPart
$objWriter->writeRaw($spreadsheet->getProperties()->getCategory());
$objWriter->endElement();
self::writeDocPropsCustom($objWriter, $spreadsheet);
$objWriter->endElement();
$objWriter->endElement();
return $objWriter->getData();
}
private static function writeDocPropsCustom(XMLWriter $objWriter, Spreadsheet $spreadsheet): void
{
$customPropertyList = $spreadsheet->getProperties()->getCustomProperties();
foreach ($customPropertyList as $key => $customProperty) {
$propertyValue = $spreadsheet->getProperties()->getCustomPropertyValue($customProperty);
$propertyType = $spreadsheet->getProperties()->getCustomPropertyType($customProperty);
$objWriter->startElement('meta:user-defined');
$objWriter->writeAttribute('meta:name', $customProperty);
switch ($propertyType) {
case Properties::PROPERTY_TYPE_INTEGER:
case Properties::PROPERTY_TYPE_FLOAT:
$objWriter->writeAttribute('meta:value-type', 'float');
$objWriter->writeRawData($propertyValue);
break;
case Properties::PROPERTY_TYPE_BOOLEAN:
$objWriter->writeAttribute('meta:value-type', 'boolean');
$objWriter->writeRawData($propertyValue ? 'true' : 'false');
break;
case Properties::PROPERTY_TYPE_DATE:
$objWriter->writeAttribute('meta:value-type', 'date');
$dtobj = Date::dateTimeFromTimestamp($propertyValue ?? 0);
$objWriter->writeRawData($dtobj->format(DATE_W3C));
break;
default:
$objWriter->writeRawData($propertyValue);
break;
}
$objWriter->endElement();
}
}
}

View File

@@ -11,7 +11,7 @@ class MetaInf extends WriterPart
*
* @return string XML Output
*/
public function writeManifest()
public function write(): string
{
$objWriter = null;
if ($this->getParentWriter()->getUseDiskCaching()) {

View File

@@ -2,18 +2,14 @@
namespace PhpOffice\PhpSpreadsheet\Writer\Ods;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
class Mimetype extends WriterPart
{
/**
* Write mimetype to plain text format.
*
* @param Spreadsheet $spreadsheet
*
* @return string XML Output
*/
public function write(?Spreadsheet $spreadsheet = null)
public function write(): string
{
return 'application/vnd.oasis.opendocument.spreadsheet';
}

View File

@@ -10,24 +10,29 @@ use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
class NamedExpressions
{
/** @var XMLWriter */
private $objWriter;
/** @var Spreadsheet */
private $spreadsheet;
/** @var Formula */
private $formulaConvertor;
public function __construct(XMLWriter $objWriter, Spreadsheet $spreadsheet, $formulaConvertor)
public function __construct(XMLWriter $objWriter, Spreadsheet $spreadsheet, Formula $formulaConvertor)
{
$this->objWriter = $objWriter;
$this->spreadsheet = $spreadsheet;
$this->formulaConvertor = $formulaConvertor;
}
public function write(): void
public function write(): string
{
$this->objWriter->startElement('table:named-expressions');
$this->writeExpressions();
$this->objWriter->endElement();
return '';
}
private function writeExpressions(): void
@@ -49,23 +54,29 @@ class NamedExpressions
private function writeNamedFormula(DefinedName $definedName, Worksheet $defaultWorksheet): void
{
$title = ($definedName->getWorksheet() !== null) ? $definedName->getWorksheet()->getTitle() : $defaultWorksheet->getTitle();
$this->objWriter->writeAttribute('table:name', $definedName->getName());
$this->objWriter->writeAttribute(
'table:expression',
$this->formulaConvertor->convertFormula($definedName->getValue(), $definedName->getWorksheet()->getTitle())
$this->formulaConvertor->convertFormula($definedName->getValue(), $title)
);
$this->objWriter->writeAttribute('table:base-cell-address', $this->convertAddress(
$definedName,
"'" . (($definedName->getWorksheet() !== null) ? $definedName->getWorksheet()->getTitle() : $defaultWorksheet->getTitle()) . "'!\$A\$1"
"'" . $title . "'!\$A\$1"
));
}
private function writeNamedRange(DefinedName $definedName): void
{
$baseCell = '$A$1';
$ws = $definedName->getWorksheet();
if ($ws !== null) {
$baseCell = "'" . $ws->getTitle() . "'!$baseCell";
}
$this->objWriter->writeAttribute('table:name', $definedName->getName());
$this->objWriter->writeAttribute('table:base-cell-address', $this->convertAddress(
$definedName,
"'" . $definedName->getWorksheet()->getTitle() . "'!\$A\$1"
$baseCell
));
$this->objWriter->writeAttribute('table:cell-range-address', $this->convertAddress($definedName, $definedName->getValue()));
}
@@ -98,7 +109,10 @@ class NamedExpressions
if (empty($worksheet)) {
if (($offset === 0) || ($address[$offset - 1] !== ':')) {
// We need a worksheet
$worksheet = $definedName->getWorksheet()->getTitle();
$ws = $definedName->getWorksheet();
if ($ws !== null) {
$worksheet = $ws->getTitle();
}
}
} else {
$worksheet = str_replace("''", "'", trim($worksheet, "'"));

View File

@@ -2,21 +2,21 @@
namespace PhpOffice\PhpSpreadsheet\Writer\Ods;
use PhpOffice\PhpSpreadsheet\Cell\CellAddress;
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
use PhpOffice\PhpSpreadsheet\Shared\XMLWriter;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
class Settings extends WriterPart
{
/**
* Write settings.xml to XML format.
*
* @param Spreadsheet $spreadsheet
*
* @return string XML Output
*/
public function write(?Spreadsheet $spreadsheet = null)
public function write(): string
{
$objWriter = null;
if ($this->getParentWriter()->getUseDiskCaching()) {
$objWriter = new XMLWriter(XMLWriter::STORAGE_DISK, $this->getParentWriter()->getDiskCachingDirectory());
} else {
@@ -39,14 +39,114 @@ class Settings extends WriterPart
$objWriter->writeAttribute('config:name', 'ooo:view-settings');
$objWriter->startElement('config:config-item-map-indexed');
$objWriter->writeAttribute('config:name', 'Views');
$objWriter->endElement();
$objWriter->endElement();
$objWriter->startElement('config:config-item-map-entry');
$spreadsheet = $this->getParentWriter()->getSpreadsheet();
$objWriter->startElement('config:config-item');
$objWriter->writeAttribute('config:name', 'ViewId');
$objWriter->writeAttribute('config:type', 'string');
$objWriter->text('view1');
$objWriter->endElement(); // ViewId
$objWriter->startElement('config:config-item-map-named');
$this->writeAllWorksheetSettings($objWriter, $spreadsheet);
$wstitle = $spreadsheet->getActiveSheet()->getTitle();
$objWriter->startElement('config:config-item');
$objWriter->writeAttribute('config:name', 'ActiveTable');
$objWriter->writeAttribute('config:type', 'string');
$objWriter->text($wstitle);
$objWriter->endElement(); // config:config-item ActiveTable
$objWriter->endElement(); // config:config-item-map-entry
$objWriter->endElement(); // config:config-item-map-indexed Views
$objWriter->endElement(); // config:config-item-set ooo:view-settings
$objWriter->startElement('config:config-item-set');
$objWriter->writeAttribute('config:name', 'ooo:configuration-settings');
$objWriter->endElement();
$objWriter->endElement();
$objWriter->endElement();
$objWriter->endElement(); // config:config-item-set ooo:configuration-settings
$objWriter->endElement(); // office:settings
$objWriter->endElement(); // office:document-settings
return $objWriter->getData();
}
private function writeAllWorksheetSettings(XMLWriter $objWriter, Spreadsheet $spreadsheet): void
{
$objWriter->writeAttribute('config:name', 'Tables');
foreach ($spreadsheet->getWorksheetIterator() as $worksheet) {
$this->writeWorksheetSettings($objWriter, $worksheet);
}
$objWriter->endElement(); // config:config-item-map-entry Tables
}
private function writeWorksheetSettings(XMLWriter $objWriter, Worksheet $worksheet): void
{
$objWriter->startElement('config:config-item-map-entry');
$objWriter->writeAttribute('config:name', $worksheet->getTitle());
$this->writeSelectedCells($objWriter, $worksheet);
if ($worksheet->getFreezePane() !== null) {
$this->writeFreezePane($objWriter, $worksheet);
}
$objWriter->endElement(); // config:config-item-map-entry Worksheet
}
private function writeSelectedCells(XMLWriter $objWriter, Worksheet $worksheet): void
{
$selected = $worksheet->getSelectedCells();
if (preg_match('/^([a-z]+)([0-9]+)/i', $selected, $matches) === 1) {
$colSel = Coordinate::columnIndexFromString($matches[1]) - 1;
$rowSel = (int) $matches[2] - 1;
$objWriter->startElement('config:config-item');
$objWriter->writeAttribute('config:name', 'CursorPositionX');
$objWriter->writeAttribute('config:type', 'int');
$objWriter->text((string) $colSel);
$objWriter->endElement();
$objWriter->startElement('config:config-item');
$objWriter->writeAttribute('config:name', 'CursorPositionY');
$objWriter->writeAttribute('config:type', 'int');
$objWriter->text((string) $rowSel);
$objWriter->endElement();
}
}
private function writeSplitValue(XMLWriter $objWriter, string $splitMode, string $type, string $value): void
{
$objWriter->startElement('config:config-item');
$objWriter->writeAttribute('config:name', $splitMode);
$objWriter->writeAttribute('config:type', $type);
$objWriter->text($value);
$objWriter->endElement();
}
private function writeFreezePane(XMLWriter $objWriter, Worksheet $worksheet): void
{
$freezePane = CellAddress::fromCellAddress($worksheet->getFreezePane());
if ($freezePane->cellAddress() === 'A1') {
return;
}
$columnId = $freezePane->columnId();
$columnName = $freezePane->columnName();
$row = $freezePane->rowId();
$this->writeSplitValue($objWriter, 'HorizontalSplitMode', 'short', '2');
$this->writeSplitValue($objWriter, 'HorizontalSplitPosition', 'int', (string) ($columnId - 1));
$this->writeSplitValue($objWriter, 'PositionLeft', 'short', '0');
$this->writeSplitValue($objWriter, 'PositionRight', 'short', (string) ($columnId - 1));
for ($column = 'A'; $column !== $columnName; ++$column) {
$worksheet->getColumnDimension($column)->setAutoSize(true);
}
$this->writeSplitValue($objWriter, 'VerticalSplitMode', 'short', '2');
$this->writeSplitValue($objWriter, 'VerticalSplitPosition', 'int', (string) ($row - 1));
$this->writeSplitValue($objWriter, 'PositionTop', 'short', '0');
$this->writeSplitValue($objWriter, 'PositionBottom', 'short', (string) ($row - 1));
$this->writeSplitValue($objWriter, 'ActiveSplitRange', 'short', '3');
}
}

View File

@@ -3,18 +3,15 @@
namespace PhpOffice\PhpSpreadsheet\Writer\Ods;
use PhpOffice\PhpSpreadsheet\Shared\XMLWriter;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
class Styles extends WriterPart
{
/**
* Write styles.xml to XML format.
*
* @param Spreadsheet $spreadsheet
*
* @return string XML Output
*/
public function write(?Spreadsheet $spreadsheet = null)
public function write(): string
{
$objWriter = null;
if ($this->getParentWriter()->getUseDiskCaching()) {

View File

@@ -2,18 +2,14 @@
namespace PhpOffice\PhpSpreadsheet\Writer\Ods;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
class Thumbnails extends WriterPart
{
/**
* Write Thumbnails/thumbnail.png to PNG format.
*
* @param Spreadsheet $spreadsheet
*
* @return string XML Output
*/
public function writeThumbnail(?Spreadsheet $spreadsheet = null)
public function write(): string
{
return '';
}

View File

@@ -30,4 +30,6 @@ abstract class WriterPart
{
$this->parentWriter = $writer;
}
abstract public function write(): string;
}

View File

@@ -26,14 +26,14 @@ abstract class Pdf extends Html
/**
* Orientation (Over-ride).
*
* @var string
* @var ?string
*/
protected $orientation;
/**
* Paper size (Over-ride).
*
* @var int
* @var ?int
*/
protected $paperSize;
@@ -155,7 +155,7 @@ abstract class Pdf extends Html
/**
* Get Paper Size.
*
* @return int
* @return ?int
*/
public function getPaperSize()
{
@@ -165,23 +165,21 @@ abstract class Pdf extends Html
/**
* Set Paper Size.
*
* @param string $pValue Paper size see PageSetup::PAPERSIZE_*
* @param int $paperSize Paper size see PageSetup::PAPERSIZE_*
*
* @return self
*/
public function setPaperSize($pValue)
public function setPaperSize($paperSize)
{
$this->paperSize = $pValue;
$this->paperSize = $paperSize;
return $this;
}
/**
* Get Orientation.
*
* @return string
*/
public function getOrientation()
public function getOrientation(): ?string
{
return $this->orientation;
}
@@ -189,13 +187,13 @@ abstract class Pdf extends Html
/**
* Set Orientation.
*
* @param string $pValue Page orientation see PageSetup::ORIENTATION_*
* @param string $orientation Page orientation see PageSetup::ORIENTATION_*
*
* @return self
*/
public function setOrientation($pValue)
public function setOrientation($orientation)
{
$this->orientation = $pValue;
$this->orientation = $orientation;
return $this;
}
@@ -213,16 +211,16 @@ abstract class Pdf extends Html
/**
* Set temporary storage directory.
*
* @param string $pValue Temporary storage directory
* @param string $temporaryDirectory Temporary storage directory
*
* @return self
*/
public function setTempDir($pValue)
public function setTempDir($temporaryDirectory)
{
if (is_dir($pValue)) {
$this->tempDir = $pValue;
if (is_dir($temporaryDirectory)) {
$this->tempDir = $temporaryDirectory;
} else {
throw new WriterException("Directory does not exist: $pValue");
throw new WriterException("Directory does not exist: $temporaryDirectory");
}
return $this;
@@ -231,14 +229,14 @@ abstract class Pdf extends Html
/**
* Save Spreadsheet to PDF file, pre-save.
*
* @param string $pFilename Name of the file to save as
* @param string $filename Name of the file to save as
*
* @return resource
*/
protected function prepareForSave($pFilename)
protected function prepareForSave($filename)
{
// Open file
$this->openFileHandle($pFilename);
$this->openFileHandle($filename);
return $this->fileHandle;
}

View File

@@ -7,6 +7,13 @@ use PhpOffice\PhpSpreadsheet\Writer\Pdf;
class Dompdf extends Pdf
{
/**
* embed images, or link to images.
*
* @var bool
*/
protected $embedImages = true;
/**
* Gets the implementation of external PDF library that should be used.
*
@@ -20,52 +27,33 @@ class Dompdf extends Pdf
/**
* Save Spreadsheet to file.
*
* @param string $pFilename Name of the file to save as
* @param string $filename Name of the file to save as
*/
public function save($pFilename): void
public function save($filename, int $flags = 0): void
{
$fileHandle = parent::prepareForSave($pFilename);
// Default PDF paper size
$paperSize = 'LETTER'; // Letter (8.5 in. by 11 in.)
$fileHandle = parent::prepareForSave($filename);
// Check for paper size and page orientation
if ($this->getSheetIndex() === null) {
$orientation = ($this->spreadsheet->getSheet(0)->getPageSetup()->getOrientation()
== PageSetup::ORIENTATION_LANDSCAPE) ? 'L' : 'P';
$printPaperSize = $this->spreadsheet->getSheet(0)->getPageSetup()->getPaperSize();
} else {
$orientation = ($this->spreadsheet->getSheet($this->getSheetIndex())->getPageSetup()->getOrientation()
== PageSetup::ORIENTATION_LANDSCAPE) ? 'L' : 'P';
$printPaperSize = $this->spreadsheet->getSheet($this->getSheetIndex())->getPageSetup()->getPaperSize();
$setup = $this->spreadsheet->getSheet($this->getSheetIndex() ?? 0)->getPageSetup();
$orientation = $this->getOrientation() ?? $setup->getOrientation();
$orientation = ($orientation === PageSetup::ORIENTATION_LANDSCAPE) ? 'L' : 'P';
$printPaperSize = $this->getPaperSize() ?? $setup->getPaperSize();
$paperSize = self::$paperSizes[$printPaperSize] ?? PageSetup::getPaperSizeDefault();
if (is_array($paperSize) && count($paperSize) === 2) {
$paperSize = [0.0, 0.0, $paperSize[0], $paperSize[1]];
}
$orientation = ($orientation == 'L') ? 'landscape' : 'portrait';
// Override Page Orientation
if ($this->getOrientation() !== null) {
$orientation = ($this->getOrientation() == PageSetup::ORIENTATION_DEFAULT)
? PageSetup::ORIENTATION_PORTRAIT
: $this->getOrientation();
}
// Override Paper Size
if ($this->getPaperSize() !== null) {
$printPaperSize = $this->getPaperSize();
}
if (isset(self::$paperSizes[$printPaperSize])) {
$paperSize = self::$paperSizes[$printPaperSize];
}
// Create PDF
$pdf = $this->createExternalWriterInstance();
$pdf->setPaper(strtolower($paperSize), $orientation);
$pdf->setPaper($paperSize, $orientation);
$pdf->loadHtml($this->generateHTMLAll());
$pdf->render();
// Write to file
fwrite($fileHandle, $pdf->output());
fwrite($fileHandle, $pdf->output() ?? '');
parent::restoreStateAfterSave();
}

View File

@@ -3,10 +3,14 @@
namespace PhpOffice\PhpSpreadsheet\Writer\Pdf;
use PhpOffice\PhpSpreadsheet\Worksheet\PageSetup;
use PhpOffice\PhpSpreadsheet\Writer\Html;
use PhpOffice\PhpSpreadsheet\Writer\Pdf;
class Mpdf extends Pdf
{
/** @var bool */
protected $isMPdf = true;
/**
* Gets the implementation of external PDF library that should be used.
*
@@ -22,49 +26,24 @@ class Mpdf extends Pdf
/**
* Save Spreadsheet to file.
*
* @param string $pFilename Name of the file to save as
* @param string $filename Name of the file to save as
*/
public function save($pFilename): void
public function save($filename, int $flags = 0): void
{
$fileHandle = parent::prepareForSave($pFilename);
// Default PDF paper size
$paperSize = 'LETTER'; // Letter (8.5 in. by 11 in.)
$fileHandle = parent::prepareForSave($filename);
// Check for paper size and page orientation
if (null === $this->getSheetIndex()) {
$orientation = ($this->spreadsheet->getSheet(0)->getPageSetup()->getOrientation()
== PageSetup::ORIENTATION_LANDSCAPE) ? 'L' : 'P';
$printPaperSize = $this->spreadsheet->getSheet(0)->getPageSetup()->getPaperSize();
} else {
$orientation = ($this->spreadsheet->getSheet($this->getSheetIndex())->getPageSetup()->getOrientation()
== PageSetup::ORIENTATION_LANDSCAPE) ? 'L' : 'P';
$printPaperSize = $this->spreadsheet->getSheet($this->getSheetIndex())->getPageSetup()->getPaperSize();
}
$this->setOrientation($orientation);
// Override Page Orientation
if (null !== $this->getOrientation()) {
$orientation = ($this->getOrientation() == PageSetup::ORIENTATION_DEFAULT)
? PageSetup::ORIENTATION_PORTRAIT
: $this->getOrientation();
}
$orientation = strtoupper($orientation);
// Override Paper Size
if (null !== $this->getPaperSize()) {
$printPaperSize = $this->getPaperSize();
}
if (isset(self::$paperSizes[$printPaperSize])) {
$paperSize = self::$paperSizes[$printPaperSize];
}
$setup = $this->spreadsheet->getSheet($this->getSheetIndex() ?? 0)->getPageSetup();
$orientation = $this->getOrientation() ?? $setup->getOrientation();
$orientation = ($orientation === PageSetup::ORIENTATION_LANDSCAPE) ? 'L' : 'P';
$printPaperSize = $this->getPaperSize() ?? $setup->getPaperSize();
$paperSize = self::$paperSizes[$printPaperSize] ?? PageSetup::getPaperSizeDefault();
// Create PDF
$config = ['tempDir' => $this->tempDir . '/mpdf'];
$pdf = $this->createExternalWriterInstance($config);
$ortmp = $orientation;
$pdf->_setPageSize(strtoupper($paperSize), $ortmp);
$pdf->_setPageSize($paperSize, $ortmp);
$pdf->DefOrientation = $orientation;
$pdf->AddPageByArray([
'orientation' => $orientation,
@@ -82,6 +61,14 @@ class Mpdf extends Pdf
$pdf->SetCreator($this->spreadsheet->getProperties()->getCreator());
$html = $this->generateHTMLAll();
$bodyLocation = strpos($html, Html::BODY_LINE);
// Make sure first data presented to Mpdf includes body tag
// so that Mpdf doesn't parse it as content. Issue 2432.
if ($bodyLocation !== false) {
$bodyLocation += strlen(Html::BODY_LINE);
$pdf->WriteHTML(substr($html, 0, $bodyLocation));
$html = substr($html, $bodyLocation);
}
foreach (\array_chunk(\explode(PHP_EOL, $html), 1000) as $lines) {
$pdf->WriteHTML(\implode(PHP_EOL, $lines));
}

View File

@@ -24,7 +24,7 @@ class Tcpdf extends Pdf
*
* @param string $orientation Page orientation
* @param string $unit Unit measure
* @param string $paperSize Paper size
* @param array|string $paperSize Paper size
*
* @return \TCPDF implementation
*/
@@ -36,42 +36,22 @@ class Tcpdf extends Pdf
/**
* Save Spreadsheet to file.
*
* @param string $pFilename Name of the file to save as
* @param string $filename Name of the file to save as
*/
public function save($pFilename): void
public function save($filename, int $flags = 0): void
{
$fileHandle = parent::prepareForSave($pFilename);
$fileHandle = parent::prepareForSave($filename);
// Default PDF paper size
$paperSize = 'LETTER'; // Letter (8.5 in. by 11 in.)
// Check for paper size and page orientation
if ($this->getSheetIndex() === null) {
$orientation = ($this->spreadsheet->getSheet(0)->getPageSetup()->getOrientation()
== PageSetup::ORIENTATION_LANDSCAPE) ? 'L' : 'P';
$printPaperSize = $this->spreadsheet->getSheet(0)->getPageSetup()->getPaperSize();
$printMargins = $this->spreadsheet->getSheet(0)->getPageMargins();
} else {
$orientation = ($this->spreadsheet->getSheet($this->getSheetIndex())->getPageSetup()->getOrientation()
== PageSetup::ORIENTATION_LANDSCAPE) ? 'L' : 'P';
$printPaperSize = $this->spreadsheet->getSheet($this->getSheetIndex())->getPageSetup()->getPaperSize();
$printMargins = $this->spreadsheet->getSheet($this->getSheetIndex())->getPageMargins();
}
// Override Page Orientation
if ($this->getOrientation() !== null) {
$orientation = ($this->getOrientation() == PageSetup::ORIENTATION_LANDSCAPE)
? 'L'
: 'P';
}
// Override Paper Size
if ($this->getPaperSize() !== null) {
$printPaperSize = $this->getPaperSize();
}
if (isset(self::$paperSizes[$printPaperSize])) {
$paperSize = self::$paperSizes[$printPaperSize];
}
$setup = $this->spreadsheet->getSheet($this->getSheetIndex() ?? 0)->getPageSetup();
$orientation = $this->getOrientation() ?? $setup->getOrientation();
$orientation = ($orientation === PageSetup::ORIENTATION_LANDSCAPE) ? 'L' : 'P';
$printPaperSize = $this->getPaperSize() ?? $setup->getPaperSize();
$paperSize = self::$paperSizes[$printPaperSize] ?? PageSetup::getPaperSizeDefault();
$printMargins = $this->spreadsheet->getSheet($this->getSheetIndex() ?? 0)->getPageMargins();
// Create PDF
$pdf = $this->createExternalWriterInstance($orientation, 'pt', $paperSize);
@@ -97,7 +77,7 @@ class Tcpdf extends Pdf
$pdf->SetCreator($this->spreadsheet->getProperties()->getCreator());
// Write to file
fwrite($fileHandle, $pdf->output($pFilename, 'S'));
fwrite($fileHandle, $pdf->output('', 'S'));
parent::restoreStateAfterSave();
}

View File

@@ -4,10 +4,10 @@ namespace PhpOffice\PhpSpreadsheet\Writer;
use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
use PhpOffice\PhpSpreadsheet\Cell\Cell;
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
use PhpOffice\PhpSpreadsheet\RichText\RichText;
use PhpOffice\PhpSpreadsheet\RichText\Run;
use PhpOffice\PhpSpreadsheet\Shared\Drawing as SharedDrawing;
use PhpOffice\PhpSpreadsheet\Shared\Escher;
use PhpOffice\PhpSpreadsheet\Shared\Escher\DgContainer;
use PhpOffice\PhpSpreadsheet\Shared\Escher\DgContainer\SpgrContainer;
@@ -23,6 +23,9 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Worksheet\BaseDrawing;
use PhpOffice\PhpSpreadsheet\Worksheet\Drawing;
use PhpOffice\PhpSpreadsheet\Worksheet\MemoryDrawing;
use PhpOffice\PhpSpreadsheet\Writer\Xls\Parser;
use PhpOffice\PhpSpreadsheet\Writer\Xls\Workbook;
use PhpOffice\PhpSpreadsheet\Writer\Xls\Worksheet;
class Xls extends BaseWriter
{
@@ -64,7 +67,7 @@ class Xls extends BaseWriter
/**
* Formula parser.
*
* @var \PhpOffice\PhpSpreadsheet\Writer\Xls\Parser
* @var Parser
*/
private $parser;
@@ -78,24 +81,24 @@ class Xls extends BaseWriter
/**
* Basic OLE object summary information.
*
* @var array
* @var string
*/
private $summaryInformation;
/**
* Extended OLE object document summary information.
*
* @var array
* @var string
*/
private $documentSummaryInformation;
/**
* @var \PhpOffice\PhpSpreadsheet\Writer\Xls\Workbook
* @var Workbook
*/
private $writerWorkbook;
/**
* @var \PhpOffice\PhpSpreadsheet\Writer\Xls\Worksheet[]
* @var Worksheet[]
*/
private $writerWorksheets;
@@ -114,10 +117,12 @@ class Xls extends BaseWriter
/**
* Save Spreadsheet to file.
*
* @param resource|string $pFilename
* @param resource|string $filename
*/
public function save($pFilename): void
public function save($filename, int $flags = 0): void
{
$this->processFlags($flags);
// garbage collect
$this->spreadsheet->garbageCollect();
@@ -156,8 +161,9 @@ class Xls extends BaseWriter
// add fonts from rich text eleemnts
for ($i = 0; $i < $countSheets; ++$i) {
foreach ($this->writerWorksheets[$i]->phpSheet->getCoordinates() as $coordinate) {
$cell = $this->writerWorksheets[$i]->phpSheet->getCell($coordinate);
foreach ($this->writerWorksheets[$i]->phpSheet->getCellCollection()->getCoordinates() as $coordinate) {
/** @var Cell $cell */
$cell = $this->writerWorksheets[$i]->phpSheet->getCellCollection()->get($coordinate);
$cVal = $cell->getValue();
if ($cVal instanceof RichText) {
$elements = $cVal->getRichTextElements();
@@ -216,9 +222,10 @@ class Xls extends BaseWriter
$arrRootData[] = $OLE_DocumentSummaryInformation;
}
$root = new Root(time(), time(), $arrRootData);
$time = $this->spreadsheet->getProperties()->getModified();
$root = new Root($time, $time, $arrRootData);
// save the OLE file
$this->openFileHandle($pFilename);
$this->openFileHandle($filename);
$root->save($this->fileHandle);
$this->maybeCloseFileHandle();
@@ -240,8 +247,6 @@ class Xls extends BaseWriter
// sheet index
$sheetIndex = $sheet->getParent()->getIndex($sheet);
$escher = null;
// check if there are any shapes for this sheet
$filterRange = $sheet->getAutoFilter()->getRange();
if (count($sheet->getDrawingCollection()) == 0 && empty($filterRange)) {
@@ -388,7 +393,7 @@ class Xls extends BaseWriter
}
}
private function processMemoryDrawing(BstoreContainer &$bstoreContainer, BaseDrawing $drawing, string $renderingFunctionx): void
private function processMemoryDrawing(BstoreContainer &$bstoreContainer, MemoryDrawing $drawing, string $renderingFunctionx): void
{
switch ($renderingFunctionx) {
case MemoryDrawing::RENDERING_JPEG:
@@ -418,8 +423,9 @@ class Xls extends BaseWriter
$bstoreContainer->addBSE($BSE);
}
private function processDrawing(BstoreContainer &$bstoreContainer, BaseDrawing $drawing): void
private function processDrawing(BstoreContainer &$bstoreContainer, Drawing $drawing): void
{
$blipType = null;
$blipData = '';
$filename = $drawing->getPath();
@@ -428,8 +434,12 @@ class Xls extends BaseWriter
switch ($imageFormat) {
case 1: // GIF, not supported by BIFF8, we convert to PNG
$blipType = BSE::BLIPTYPE_PNG;
$newImage = @imagecreatefromgif($filename);
if ($newImage === false) {
throw new Exception("Unable to create image from $filename");
}
ob_start();
imagepng(imagecreatefromgif($filename));
imagepng($newImage);
$blipData = ob_get_contents();
ob_end_clean();
@@ -446,8 +456,12 @@ class Xls extends BaseWriter
break;
case 6: // Windows DIB (BMP), we convert to PNG
$blipType = BSE::BLIPTYPE_PNG;
$newImage = @imagecreatefrombmp($filename);
if ($newImage === false) {
throw new Exception("Unable to create image from $filename");
}
ob_start();
imagepng(SharedDrawing::imagecreatefrombmp($filename));
imagepng($newImage);
$blipData = ob_get_contents();
ob_end_clean();
@@ -759,7 +773,10 @@ class Xls extends BaseWriter
return $data;
}
private function writeSummaryPropOle(int $dataProp, int &$dataSection_NumProps, array &$dataSection, int $sumdata, int $typdata): void
/**
* @param float|int $dataProp
*/
private function writeSummaryPropOle($dataProp, int &$dataSection_NumProps, array &$dataSection, int $sumdata, int $typdata): void
{
if ($dataProp) {
$dataSection[] = [

View File

@@ -49,7 +49,7 @@ class BIFFwriter
/**
* The string containing the data of the BIFF stream.
*
* @var string
* @var null|string
*/
public $_data;

View File

@@ -0,0 +1,78 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Writer\Xls;
use PhpOffice\PhpSpreadsheet\Cell\DataValidation;
class CellDataValidation
{
/**
* @var array<string, int>
*/
protected static $validationTypeMap = [
DataValidation::TYPE_NONE => 0x00,
DataValidation::TYPE_WHOLE => 0x01,
DataValidation::TYPE_DECIMAL => 0x02,
DataValidation::TYPE_LIST => 0x03,
DataValidation::TYPE_DATE => 0x04,
DataValidation::TYPE_TIME => 0x05,
DataValidation::TYPE_TEXTLENGTH => 0x06,
DataValidation::TYPE_CUSTOM => 0x07,
];
/**
* @var array<string, int>
*/
protected static $errorStyleMap = [
DataValidation::STYLE_STOP => 0x00,
DataValidation::STYLE_WARNING => 0x01,
DataValidation::STYLE_INFORMATION => 0x02,
];
/**
* @var array<string, int>
*/
protected static $operatorMap = [
DataValidation::OPERATOR_BETWEEN => 0x00,
DataValidation::OPERATOR_NOTBETWEEN => 0x01,
DataValidation::OPERATOR_EQUAL => 0x02,
DataValidation::OPERATOR_NOTEQUAL => 0x03,
DataValidation::OPERATOR_GREATERTHAN => 0x04,
DataValidation::OPERATOR_LESSTHAN => 0x05,
DataValidation::OPERATOR_GREATERTHANOREQUAL => 0x06,
DataValidation::OPERATOR_LESSTHANOREQUAL => 0x07,
];
public static function type(DataValidation $dataValidation): int
{
$validationType = $dataValidation->getType();
if (is_string($validationType) && array_key_exists($validationType, self::$validationTypeMap)) {
return self::$validationTypeMap[$validationType];
}
return self::$validationTypeMap[DataValidation::TYPE_NONE];
}
public static function errorStyle(DataValidation $dataValidation): int
{
$errorStyle = $dataValidation->getErrorStyle();
if (is_string($errorStyle) && array_key_exists($errorStyle, self::$errorStyleMap)) {
return self::$errorStyleMap[$errorStyle];
}
return self::$errorStyleMap[DataValidation::STYLE_STOP];
}
public static function operator(DataValidation $dataValidation): int
{
$operator = $dataValidation->getOperator();
if (is_string($operator) && array_key_exists($operator, self::$operatorMap)) {
return self::$operatorMap[$operator];
}
return self::$operatorMap[DataValidation::OPERATOR_BETWEEN];
}
}

View File

@@ -0,0 +1,76 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Writer\Xls;
use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException;
use PhpOffice\PhpSpreadsheet\Style\ConditionalFormatting\Wizard;
class ConditionalHelper
{
/**
* Formula parser.
*
* @var Parser
*/
protected $parser;
/**
* @var mixed
*/
protected $condition;
/**
* @var string
*/
protected $cellRange;
/**
* @var null|string
*/
protected $tokens;
/**
* @var int
*/
protected $size;
public function __construct(Parser $parser)
{
$this->parser = $parser;
}
/**
* @param mixed $condition
*/
public function processCondition($condition, string $cellRange): void
{
$this->condition = $condition;
$this->cellRange = $cellRange;
if (is_int($condition) || is_float($condition)) {
$this->size = ($condition <= 65535 ? 3 : 0x0000);
$this->tokens = pack('Cv', 0x1E, $condition);
} else {
try {
$formula = Wizard\WizardAbstract::reverseAdjustCellRef((string) $condition, $cellRange);
$this->parser->parse($formula);
$this->tokens = $this->parser->toReversePolish();
$this->size = strlen($this->tokens ?? '');
} catch (PhpSpreadsheetException $e) {
// In the event of a parser error with a formula value, we set the expression to ptgInt + 0
$this->tokens = pack('Cv', 0x1E, 0);
$this->size = 3;
}
}
}
public function tokens(): ?string
{
return $this->tokens;
}
public function size(): int
{
return $this->size;
}
}

View File

@@ -0,0 +1,28 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Writer\Xls;
class ErrorCode
{
/**
* @var array<string, int>
*/
protected static $errorCodeMap = [
'#NULL!' => 0x00,
'#DIV/0!' => 0x07,
'#VALUE!' => 0x0F,
'#REF!' => 0x17,
'#NAME?' => 0x1D,
'#NUM!' => 0x24,
'#N/A' => 0x2A,
];
public static function error(string $errorCode): int
{
if (array_key_exists($errorCode, self::$errorCodeMap)) {
return self::$errorCodeMap[$errorCode];
}
return 0;
}
}

View File

@@ -413,15 +413,13 @@ class Escher
// the client anchor
if ($this->object->getStartCoordinates()) {
$clientAnchorData = '';
$recVer = 0x0;
$recInstance = 0x0;
$recType = 0xF010;
// start coordinates
[$column, $row] = Coordinate::coordinateFromString($this->object->getStartCoordinates());
$c1 = Coordinate::columnIndexFromString($column) - 1;
[$column, $row] = Coordinate::indexesFromString($this->object->getStartCoordinates());
$c1 = $column - 1;
$r1 = $row - 1;
// start offsetX
@@ -431,8 +429,8 @@ class Escher
$startOffsetY = $this->object->getStartOffsetY();
// end coordinates
[$column, $row] = Coordinate::coordinateFromString($this->object->getEndCoordinates());
$c2 = Coordinate::columnIndexFromString($column) - 1;
[$column, $row] = Coordinate::indexesFromString($this->object->getEndCoordinates());
$c2 = $column - 1;
$r2 = $row - 1;
// end offsetX

View File

@@ -39,6 +39,9 @@ class Font
$this->colorIndex = $colorIndex;
}
/** @var int */
private static $notImplemented = 0;
/**
* Get font record data.
*
@@ -46,8 +49,8 @@ class Font
*/
public function writeFont()
{
$font_outline = 0;
$font_shadow = 0;
$font_outline = self::$notImplemented;
$font_shadow = self::$notImplemented;
$icv = $this->colorIndex; // Index to color palette
if ($this->font->getSuperscript()) {
@@ -58,7 +61,7 @@ class Font
$sss = 0;
}
$bFamily = 0; // Font family
$bCharSet = \PhpOffice\PhpSpreadsheet\Shared\Font::getCharsetFromFontName($this->font->getName()); // Character set
$bCharSet = \PhpOffice\PhpSpreadsheet\Shared\Font::getCharsetFromFontName((string) $this->font->getName()); // Character set
$record = 0x31; // Record identifier
$reserved = 0x00; // Reserved
@@ -87,12 +90,12 @@ class Font
self::mapBold($this->font->getBold()),
// Superscript/Subscript
$sss,
self::mapUnderline($this->font->getUnderline()),
self::mapUnderline((string) $this->font->getUnderline()),
$bFamily,
$bCharSet,
$reserved
);
$data .= StringHelper::UTF8toBIFF8UnicodeShort($this->font->getName());
$data .= StringHelper::UTF8toBIFF8UnicodeShort((string) $this->font->getName());
$length = strlen($data);
$header = pack('vv', $record, $length);
@@ -102,14 +105,10 @@ class Font
/**
* Map to BIFF5-BIFF8 codes for bold.
*
* @param bool $bold
*
* @return int
*/
private static function mapBold($bold)
private static function mapBold(?bool $bold): int
{
if ($bold) {
if ($bold === true) {
return 0x2BC; // 700 = Bold font weight
}
@@ -119,7 +118,7 @@ class Font
/**
* Map of BIFF2-BIFF8 codes for underline styles.
*
* @var array of int
* @var int[]
*/
private static $mapUnderline = [
\PhpOffice\PhpSpreadsheet\Style\Font::UNDERLINE_NONE => 0x00,

View File

@@ -78,7 +78,7 @@ class Parser
/**
* The parse tree to be generated.
*
* @var string
* @var array|string
*/
public $parseTree;
@@ -469,6 +469,7 @@ class Parser
'BAHTTEXT' => [368, 1, 0, 0],
];
/** @var Spreadsheet */
private $spreadsheet;
/**
@@ -527,10 +528,10 @@ class Parser
} elseif (preg_match('/^' . Calculation::CALCULATION_REGEXP_DEFINEDNAME . '$/mui', $token) && $this->spreadsheet->getDefinedName($token) !== null) {
return $this->convertDefinedName($token);
// commented so argument number can be processed correctly. See toReversePolish().
/*elseif (preg_match("/[A-Z0-9\xc0-\xdc\.]+/", $token))
{
return($this->convertFunction($token, $this->_func_args));
}*/
/*elseif (preg_match("/[A-Z0-9\xc0-\xdc\.]+/", $token))
{
return($this->convertFunction($token, $this->_func_args));
}*/
// if it's an argument, ignore the token (the argument remains)
} elseif ($token == 'arg') {
return '';
@@ -597,10 +598,9 @@ class Parser
if ($args >= 0) {
return pack('Cv', $this->ptg['ptgFuncV'], $this->functions[$token][0]);
}
// Variable number of args eg. SUM($i, $j, $k, ..).
if ($args == -1) {
return pack('CCv', $this->ptg['ptgFuncVarV'], $num_args, $this->functions[$token][0]);
}
return pack('CCv', $this->ptg['ptgFuncVarV'], $num_args, $this->functions[$token][0]);
}
/**
@@ -747,12 +747,14 @@ class Parser
return pack('C', 0xFF);
}
private function convertDefinedName(string $name): void
private function convertDefinedName(string $name): string
{
if (strlen($name) > 255) {
throw new WriterException('Defined Name is too long');
}
throw new WriterException('Cannot yet write formulae with defined names to Xls');
/*
$nameReference = 1;
foreach ($this->spreadsheet->getDefinedNames() as $definedName) {
if ($name === $definedName->getName()) {
@@ -763,8 +765,9 @@ class Parser
$ptgRef = pack('Cvxx', $this->ptg['ptgName'], $nameReference);
throw new WriterException('Cannot yet write formulae with defined names to Xls');
// return $ptgRef;
return $ptgRef;
*/
}
/**
@@ -778,8 +781,7 @@ class Parser
*/
private function getRefIndex($ext_ref)
{
$ext_ref = preg_replace("/^'/", '', $ext_ref); // Remove leading ' if any.
$ext_ref = preg_replace("/'$/", '', $ext_ref); // Remove trailing ' if any.
$ext_ref = (string) preg_replace(["/^'/", "/'$/"], ['', ''], $ext_ref); // Remove leading and trailing ' if any.
$ext_ref = str_replace('\'\'', '\'', $ext_ref); // Replace escaped '' with '
// Check if there is a sheet range eg., Sheet1:Sheet2.
@@ -851,10 +853,10 @@ class Parser
* called by the addWorksheet() method of the
* \PhpOffice\PhpSpreadsheet\Writer\Xls\Workbook class.
*
* @see \PhpOffice\PhpSpreadsheet\Writer\Xls\Workbook::addWorksheet()
*
* @param string $name The name of the worksheet being added
* @param int $index The index of the worksheet being added
*
* @see \PhpOffice\PhpSpreadsheet\Writer\Xls\Workbook::addWorksheet()
*/
public function setExtSheet($name, $index): void
{
@@ -966,8 +968,9 @@ class Parser
/**
* Advance to the next valid token.
*/
private function advance()
private function advance(): void
{
$token = '';
$i = $this->currentCharacter;
$formula_length = strlen($this->formula);
// eat up white spaces
@@ -995,7 +998,7 @@ class Parser
$this->currentCharacter = $i + 1;
$this->currentToken = $token;
return 1;
return;
}
if ($i < ($formula_length - 2)) {
@@ -1035,7 +1038,6 @@ class Parser
case '%':
return $token;
break;
case '>':
if ($this->lookAhead === '=') { // it's a GE token
break;
@@ -1043,7 +1045,6 @@ class Parser
return $token;
break;
case '<':
// it's a LE or a NE token
if (($this->lookAhead === '=') || ($this->lookAhead === '>')) {
@@ -1052,7 +1053,6 @@ class Parser
return $token;
break;
default:
// if it's a reference A1 or $A$1 or $A1 or A$1
if (preg_match('/^\$?[A-Ia-i]?[A-Za-z]\$?\d+$/', $token) && !preg_match('/\d/', $this->lookAhead) && ($this->lookAhead !== ':') && ($this->lookAhead !== '.') && ($this->lookAhead !== '!')) {
@@ -1148,10 +1148,6 @@ class Parser
$this->advance();
$result2 = $this->expression();
$result = $this->createTree('ptgNE', $result, $result2);
} elseif ($this->currentToken == '&') {
$this->advance();
$result2 = $this->expression();
$result = $this->createTree('ptgConcat', $result, $result2);
}
return $result;
@@ -1202,6 +1198,11 @@ class Parser
return $this->createTree('ptgUplus', $result2, '');
}
$result = $this->term();
while ($this->currentToken === '&') {
$this->advance();
$result2 = $this->expression();
$result = $this->createTree('ptgConcat', $result, $result2);
}
while (
($this->currentToken == '+') ||
($this->currentToken == '-') ||
@@ -1229,9 +1230,9 @@ class Parser
* This function just introduces a ptgParen element in the tree, so that Excel
* doesn't get confused when working with a parenthesized formula afterwards.
*
* @see fact()
*
* @return array The parsed ptg'd tree
*
* @see fact()
*/
private function parenthesizedExpression()
{
@@ -1277,7 +1278,8 @@ class Parser
*/
private function fact()
{
if ($this->currentToken === '(') {
$currentToken = $this->currentToken;
if ($currentToken === '(') {
$this->advance(); // eat the "("
$result = $this->parenthesizedExpression();
if ($this->currentToken !== ')') {
@@ -1444,6 +1446,9 @@ class Parser
if (empty($tree)) { // If it's the first call use parseTree
$tree = $this->parseTree;
}
if (!is_array($tree) || !isset($tree['left'], $tree['right'], $tree['value'])) {
throw new WriterException('Unexpected non-array');
}
if (is_array($tree['left'])) {
$converted_tree = $this->toReversePolish($tree['left']);
@@ -1473,7 +1478,8 @@ class Parser
} else {
$left_tree = '';
}
// add it's left subtree and return.
// add its left subtree and return.
return $left_tree . $this->convertFunction($tree['value'], $tree['right']);
}
$converted_tree = $this->convert($tree['value']);

View File

@@ -0,0 +1,59 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Writer\Xls\Style;
use PhpOffice\PhpSpreadsheet\Style\Alignment;
class CellAlignment
{
/**
* @var array<string, int>
*/
private static $horizontalMap = [
Alignment::HORIZONTAL_GENERAL => 0,
Alignment::HORIZONTAL_LEFT => 1,
Alignment::HORIZONTAL_RIGHT => 3,
Alignment::HORIZONTAL_CENTER => 2,
Alignment::HORIZONTAL_CENTER_CONTINUOUS => 6,
Alignment::HORIZONTAL_JUSTIFY => 5,
];
/**
* @var array<string, int>
*/
private static $verticalMap = [
Alignment::VERTICAL_BOTTOM => 2,
Alignment::VERTICAL_TOP => 0,
Alignment::VERTICAL_CENTER => 1,
Alignment::VERTICAL_JUSTIFY => 3,
];
public static function horizontal(Alignment $alignment): int
{
$horizontalAlignment = $alignment->getHorizontal();
if (is_string($horizontalAlignment) && array_key_exists($horizontalAlignment, self::$horizontalMap)) {
return self::$horizontalMap[$horizontalAlignment];
}
return self::$horizontalMap[Alignment::HORIZONTAL_GENERAL];
}
public static function wrap(Alignment $alignment): int
{
$wrap = $alignment->getWrapText();
return ($wrap === true) ? 1 : 0;
}
public static function vertical(Alignment $alignment): int
{
$verticalAlignment = $alignment->getVertical();
if (is_string($verticalAlignment) && array_key_exists($verticalAlignment, self::$verticalMap)) {
return self::$verticalMap[$verticalAlignment];
}
return self::$verticalMap[Alignment::VERTICAL_BOTTOM];
}
}

View File

@@ -0,0 +1,39 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Writer\Xls\Style;
use PhpOffice\PhpSpreadsheet\Style\Border;
class CellBorder
{
/**
* @var array<string, int>
*/
protected static $styleMap = [
Border::BORDER_NONE => 0x00,
Border::BORDER_THIN => 0x01,
Border::BORDER_MEDIUM => 0x02,
Border::BORDER_DASHED => 0x03,
Border::BORDER_DOTTED => 0x04,
Border::BORDER_THICK => 0x05,
Border::BORDER_DOUBLE => 0x06,
Border::BORDER_HAIR => 0x07,
Border::BORDER_MEDIUMDASHED => 0x08,
Border::BORDER_DASHDOT => 0x09,
Border::BORDER_MEDIUMDASHDOT => 0x0A,
Border::BORDER_DASHDOTDOT => 0x0B,
Border::BORDER_MEDIUMDASHDOTDOT => 0x0C,
Border::BORDER_SLANTDASHDOT => 0x0D,
];
public static function style(Border $border): int
{
$borderStyle = $border->getBorderStyle();
if (is_string($borderStyle) && array_key_exists($borderStyle, self::$styleMap)) {
return self::$styleMap[$borderStyle];
}
return self::$styleMap[Border::BORDER_NONE];
}
}

View File

@@ -0,0 +1,46 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Writer\Xls\Style;
use PhpOffice\PhpSpreadsheet\Style\Fill;
class CellFill
{
/**
* @var array<string, int>
*/
protected static $fillStyleMap = [
Fill::FILL_NONE => 0x00,
Fill::FILL_SOLID => 0x01,
Fill::FILL_PATTERN_MEDIUMGRAY => 0x02,
Fill::FILL_PATTERN_DARKGRAY => 0x03,
Fill::FILL_PATTERN_LIGHTGRAY => 0x04,
Fill::FILL_PATTERN_DARKHORIZONTAL => 0x05,
Fill::FILL_PATTERN_DARKVERTICAL => 0x06,
Fill::FILL_PATTERN_DARKDOWN => 0x07,
Fill::FILL_PATTERN_DARKUP => 0x08,
Fill::FILL_PATTERN_DARKGRID => 0x09,
Fill::FILL_PATTERN_DARKTRELLIS => 0x0A,
Fill::FILL_PATTERN_LIGHTHORIZONTAL => 0x0B,
Fill::FILL_PATTERN_LIGHTVERTICAL => 0x0C,
Fill::FILL_PATTERN_LIGHTDOWN => 0x0D,
Fill::FILL_PATTERN_LIGHTUP => 0x0E,
Fill::FILL_PATTERN_LIGHTGRID => 0x0F,
Fill::FILL_PATTERN_LIGHTTRELLIS => 0x10,
Fill::FILL_PATTERN_GRAY125 => 0x11,
Fill::FILL_PATTERN_GRAY0625 => 0x12,
Fill::FILL_GRADIENT_LINEAR => 0x00, // does not exist in BIFF8
Fill::FILL_GRADIENT_PATH => 0x00, // does not exist in BIFF8
];
public static function style(Fill $fill): int
{
$fillStyle = $fill->getFillType();
if (is_string($fillStyle) && array_key_exists($fillStyle, self::$fillStyleMap)) {
return self::$fillStyleMap[$fillStyle];
}
return self::$fillStyleMap[Fill::FILL_NONE];
}
}

View File

@@ -0,0 +1,90 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Writer\Xls\Style;
use PhpOffice\PhpSpreadsheet\Style\Color;
class ColorMap
{
/**
* @var array<string, int>
*/
private static $colorMap = [
'#000000' => 0x08,
'#FFFFFF' => 0x09,
'#FF0000' => 0x0A,
'#00FF00' => 0x0B,
'#0000FF' => 0x0C,
'#FFFF00' => 0x0D,
'#FF00FF' => 0x0E,
'#00FFFF' => 0x0F,
'#800000' => 0x10,
'#008000' => 0x11,
'#000080' => 0x12,
'#808000' => 0x13,
'#800080' => 0x14,
'#008080' => 0x15,
'#C0C0C0' => 0x16,
'#808080' => 0x17,
'#9999FF' => 0x18,
'#993366' => 0x19,
'#FFFFCC' => 0x1A,
'#CCFFFF' => 0x1B,
'#660066' => 0x1C,
'#FF8080' => 0x1D,
'#0066CC' => 0x1E,
'#CCCCFF' => 0x1F,
// '#000080' => 0x20,
// '#FF00FF' => 0x21,
// '#FFFF00' => 0x22,
// '#00FFFF' => 0x23,
// '#800080' => 0x24,
// '#800000' => 0x25,
// '#008080' => 0x26,
// '#0000FF' => 0x27,
'#00CCFF' => 0x28,
// '#CCFFFF' => 0x29,
'#CCFFCC' => 0x2A,
'#FFFF99' => 0x2B,
'#99CCFF' => 0x2C,
'#FF99CC' => 0x2D,
'#CC99FF' => 0x2E,
'#FFCC99' => 0x2F,
'#3366FF' => 0x30,
'#33CCCC' => 0x31,
'#99CC00' => 0x32,
'#FFCC00' => 0x33,
'#FF9900' => 0x34,
'#FF6600' => 0x35,
'#666699' => 0x36,
'#969696' => 0x37,
'#003366' => 0x38,
'#339966' => 0x39,
'#003300' => 0x3A,
'#333300' => 0x3B,
'#993300' => 0x3C,
// '#993366' => 0x3D,
'#333399' => 0x3E,
'#333333' => 0x3F,
];
public static function lookup(Color $color, int $defaultIndex = 0x00): int
{
$colorRgb = $color->getRGB();
if (is_string($colorRgb) && array_key_exists("#{$colorRgb}", self::$colorMap)) {
return self::$colorMap["#{$colorRgb}"];
}
// TODO Try and map RGB value to nearest colour within the define pallette
// $red = Color::getRed($colorRgb, false);
// $green = Color::getGreen($colorRgb, false);
// $blue = Color::getBlue($colorRgb, false);
// $paletteSpace = 3;
// $newColor = ($red * $paletteSpace / 256) * ($paletteSpace * $paletteSpace) +
// ($green * $paletteSpace / 256) * $paletteSpace +
// ($blue * $paletteSpace / 256);
return $defaultIndex;
}
}

View File

@@ -169,10 +169,13 @@ class Workbook extends BIFFwriter
/**
* Escher object corresponding to MSODRAWINGGROUP.
*
* @var \PhpOffice\PhpSpreadsheet\Shared\Escher
* @var null|\PhpOffice\PhpSpreadsheet\Shared\Escher
*/
private $escher;
/** @var mixed */
private static $scrutinizerFalse = false;
/**
* Class constructor.
*
@@ -249,7 +252,7 @@ class Workbook extends BIFFwriter
$xfWriter->setDiagColor($this->addColor($style->getBorders()->getDiagonal()->getColor()->getRGB()));
// Add the number format if it is not a built-in one and not already added
if ($style->getNumberFormat()->getBuiltInFormatCode() === false) {
if ($style->getNumberFormat()->getBuiltInFormatCode() === self::$scrutinizerFalse) {
$numberFormatHashCode = $style->getNumberFormat()->getHashCode();
if (isset($this->addedNumberFormats[$numberFormatHashCode])) {
@@ -408,13 +411,13 @@ class Workbook extends BIFFwriter
* Assemble worksheets into a workbook and send the BIFF data to an OLE
* storage.
*
* @param array $pWorksheetSizes The sizes in bytes of the binary worksheet streams
* @param array $worksheetSizes The sizes in bytes of the binary worksheet streams
*
* @return string Binary data for workbook stream
*/
public function writeWorkbook(array $pWorksheetSizes)
public function writeWorkbook(array $worksheetSizes)
{
$this->worksheetSizes = $pWorksheetSizes;
$this->worksheetSizes = $worksheetSizes;
// Calculate the number of selected worksheet tabs and call the finalization
// methods for each worksheet
@@ -523,9 +526,9 @@ class Workbook extends BIFFwriter
$this->writeStyle();
}
private function parseDefinedNameValue(DefinedName $pDefinedName): string
private function parseDefinedNameValue(DefinedName $definedName): string
{
$definedRange = $pDefinedName->getValue();
$definedRange = $definedName->getValue();
$splitCount = preg_match_all(
'/' . Calculation::CALCULATION_REGEXP_CELLREF . '/mui',
$definedRange,
@@ -551,8 +554,8 @@ class Workbook extends BIFFwriter
$newRange = '';
if (empty($worksheet)) {
if (($offset === 0) || ($definedRange[$offset - 1] !== ':')) {
// We need a worksheet
$worksheet = $pDefinedName->getWorksheet()->getTitle();
// We should have a worksheet
$worksheet = $definedName->getWorksheet() ? $definedName->getWorksheet()->getTitle() : null;
}
} else {
$worksheet = str_replace("''", "'", trim($worksheet, "'"));
@@ -591,7 +594,7 @@ class Workbook extends BIFFwriter
// parse formula
try {
$error = $this->parser->parse($range);
$this->parser->parse($range);
$formulaData = $this->parser->toReversePolish();
// make sure tRef3d is of type tRef3dR (0x3A)
@@ -600,7 +603,11 @@ class Workbook extends BIFFwriter
}
if ($definedName->getLocalOnly()) {
// local scope
/**
* local scope.
*
* @phpstan-ignore-next-line
*/
$scope = $this->spreadsheet->getIndex($definedName->getScope()) + 1;
} else {
// global scope
@@ -678,13 +685,13 @@ class Workbook extends BIFFwriter
$formulaData = '';
for ($j = 0; $j < $countPrintArea; ++$j) {
$printAreaRect = $printArea[$j]; // e.g. A3:J6
$printAreaRect[0] = Coordinate::coordinateFromString($printAreaRect[0]);
$printAreaRect[1] = Coordinate::coordinateFromString($printAreaRect[1]);
$printAreaRect[0] = Coordinate::indexesFromString($printAreaRect[0]);
$printAreaRect[1] = Coordinate::indexesFromString($printAreaRect[1]);
$print_rowmin = $printAreaRect[0][1] - 1;
$print_rowmax = $printAreaRect[1][1] - 1;
$print_colmin = Coordinate::columnIndexFromString($printAreaRect[0][0]) - 1;
$print_colmax = Coordinate::columnIndexFromString($printAreaRect[1][0]) - 1;
$print_colmin = $printAreaRect[0][0] - 1;
$print_colmax = $printAreaRect[1][0] - 1;
// construct formula data manually because parser does not recognize absolute 3d cell references
$formulaData .= pack('Cvvvvv', 0x3B, $i, $print_rowmin, $print_rowmax, $print_colmin, $print_colmax);
@@ -756,8 +763,8 @@ class Workbook extends BIFFwriter
* Write a short NAME record.
*
* @param string $name
* @param string $sheetIndex 1-based sheet index the defined name applies to. 0 = global
* @param integer[][] $rangeBounds range boundaries
* @param int $sheetIndex 1-based sheet index the defined name applies to. 0 = global
* @param int[][] $rangeBounds range boundaries
* @param bool $isHidden
*
* @return string Complete binary record data
@@ -839,10 +846,9 @@ class Workbook extends BIFFwriter
/**
* Writes Excel BIFF BOUNDSHEET record.
*
* @param Worksheet $sheet Worksheet name
* @param int $offset Location of worksheet BOF
*/
private function writeBoundSheet($sheet, $offset): void
private function writeBoundSheet(\PhpOffice\PhpSpreadsheet\Worksheet\Worksheet $sheet, $offset): void
{
$sheetname = $sheet->getTitle();
$record = 0x0085; // Record identifier
@@ -870,7 +876,7 @@ class Workbook extends BIFFwriter
// sheet type
$st = 0x00;
$grbit = 0x0000; // Visibility and sheet type
//$grbit = 0x0000; // Visibility and sheet type
$data = pack('VCC', $offset, $ss, $st);
$data .= StringHelper::UTF8toBIFF8UnicodeShort($sheetname);
@@ -904,7 +910,7 @@ class Workbook extends BIFFwriter
$record = 0x0017; // Record identifier
$length = 2 + 6 * $totalReferences; // Number of bytes to follow
$supbook_index = 0; // FIXME: only using internal SUPBOOK record
//$supbook_index = 0; // FIXME: only using internal SUPBOOK record
$header = pack('vv', $record, $length);
$data = pack('v', $totalReferences);
for ($i = 0; $i < $totalReferences; ++$i) {
@@ -1171,21 +1177,17 @@ class Workbook extends BIFFwriter
/**
* Get Escher object.
*
* @return \PhpOffice\PhpSpreadsheet\Shared\Escher
*/
public function getEscher()
public function getEscher(): ?\PhpOffice\PhpSpreadsheet\Shared\Escher
{
return $this->escher;
}
/**
* Set Escher object.
*
* @param \PhpOffice\PhpSpreadsheet\Shared\Escher $pValue
*/
public function setEscher(?\PhpOffice\PhpSpreadsheet\Shared\Escher $pValue = null): void
public function setEscher(?\PhpOffice\PhpSpreadsheet\Shared\Escher $escher): void
{
$this->escher = $pValue;
$this->escher = $escher;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -3,11 +3,12 @@
namespace PhpOffice\PhpSpreadsheet\Writer\Xls;
use PhpOffice\PhpSpreadsheet\Style\Alignment;
use PhpOffice\PhpSpreadsheet\Style\Border;
use PhpOffice\PhpSpreadsheet\Style\Borders;
use PhpOffice\PhpSpreadsheet\Style\Fill;
use PhpOffice\PhpSpreadsheet\Style\Protection;
use PhpOffice\PhpSpreadsheet\Style\Style;
use PhpOffice\PhpSpreadsheet\Writer\Xls\Style\CellAlignment;
use PhpOffice\PhpSpreadsheet\Writer\Xls\Style\CellBorder;
use PhpOffice\PhpSpreadsheet\Writer\Xls\Style\CellFill;
// Original file header of PEAR::Spreadsheet_Excel_Writer_Format (used as the base for this class):
// -----------------------------------------------------------------------------------------
@@ -115,6 +116,21 @@ class Xf
*/
private $rightBorderColor;
/**
* @var int
*/
private $diag;
/**
* @var int
*/
private $diagColor;
/**
* @var Style
*/
private $style;
/**
* Constructor.
*
@@ -132,14 +148,14 @@ class Xf
$this->foregroundColor = 0x40;
$this->backgroundColor = 0x41;
$this->_diag = 0;
$this->diag = 0;
$this->bottomBorderColor = 0x40;
$this->topBorderColor = 0x40;
$this->leftBorderColor = 0x40;
$this->rightBorderColor = 0x40;
$this->_diag_color = 0x40;
$this->_style = $style;
$this->diagColor = 0x40;
$this->style = $style;
}
/**
@@ -153,39 +169,39 @@ class Xf
if ($this->isStyleXf) {
$style = 0xFFF5;
} else {
$style = self::mapLocked($this->_style->getProtection()->getLocked());
$style |= self::mapHidden($this->_style->getProtection()->getHidden()) << 1;
$style = self::mapLocked($this->style->getProtection()->getLocked());
$style |= self::mapHidden($this->style->getProtection()->getHidden()) << 1;
}
// Flags to indicate if attributes have been set.
$atr_num = ($this->numberFormatIndex != 0) ? 1 : 0;
$atr_fnt = ($this->fontIndex != 0) ? 1 : 0;
$atr_alc = ((int) $this->_style->getAlignment()->getWrapText()) ? 1 : 0;
$atr_bdr = (self::mapBorderStyle($this->_style->getBorders()->getBottom()->getBorderStyle()) ||
self::mapBorderStyle($this->_style->getBorders()->getTop()->getBorderStyle()) ||
self::mapBorderStyle($this->_style->getBorders()->getLeft()->getBorderStyle()) ||
self::mapBorderStyle($this->_style->getBorders()->getRight()->getBorderStyle())) ? 1 : 0;
$atr_pat = (($this->foregroundColor != 0x40) ||
($this->backgroundColor != 0x41) ||
self::mapFillType($this->_style->getFill()->getFillType())) ? 1 : 0;
$atr_prot = self::mapLocked($this->_style->getProtection()->getLocked())
| self::mapHidden($this->_style->getProtection()->getHidden());
$atr_alc = ((int) $this->style->getAlignment()->getWrapText()) ? 1 : 0;
$atr_bdr = (CellBorder::style($this->style->getBorders()->getBottom()) ||
CellBorder::style($this->style->getBorders()->getTop()) ||
CellBorder::style($this->style->getBorders()->getLeft()) ||
CellBorder::style($this->style->getBorders()->getRight())) ? 1 : 0;
$atr_pat = ($this->foregroundColor != 0x40) ? 1 : 0;
$atr_pat = ($this->backgroundColor != 0x41) ? 1 : $atr_pat;
$atr_pat = CellFill::style($this->style->getFill()) ? 1 : $atr_pat;
$atr_prot = self::mapLocked($this->style->getProtection()->getLocked())
| self::mapHidden($this->style->getProtection()->getHidden());
// Zero the default border colour if the border has not been set.
if (self::mapBorderStyle($this->_style->getBorders()->getBottom()->getBorderStyle()) == 0) {
if (CellBorder::style($this->style->getBorders()->getBottom()) == 0) {
$this->bottomBorderColor = 0;
}
if (self::mapBorderStyle($this->_style->getBorders()->getTop()->getBorderStyle()) == 0) {
if (CellBorder::style($this->style->getBorders()->getTop()) == 0) {
$this->topBorderColor = 0;
}
if (self::mapBorderStyle($this->_style->getBorders()->getRight()->getBorderStyle()) == 0) {
if (CellBorder::style($this->style->getBorders()->getRight()) == 0) {
$this->rightBorderColor = 0;
}
if (self::mapBorderStyle($this->_style->getBorders()->getLeft()->getBorderStyle()) == 0) {
if (CellBorder::style($this->style->getBorders()->getLeft()) == 0) {
$this->leftBorderColor = 0;
}
if (self::mapBorderStyle($this->_style->getBorders()->getDiagonal()->getBorderStyle()) == 0) {
$this->_diag_color = 0;
if (CellBorder::style($this->style->getBorders()->getDiagonal()) == 0) {
$this->diagColor = 0;
}
$record = 0x00E0; // Record identifier
@@ -194,9 +210,10 @@ class Xf
$ifnt = $this->fontIndex; // Index to FONT record
$ifmt = $this->numberFormatIndex; // Index to FORMAT record
$align = $this->mapHAlign($this->_style->getAlignment()->getHorizontal()); // Alignment
$align |= (int) $this->_style->getAlignment()->getWrapText() << 3;
$align |= self::mapVAlign($this->_style->getAlignment()->getVertical()) << 4;
// Alignment
$align = CellAlignment::horizontal($this->style->getAlignment());
$align |= CellAlignment::wrap($this->style->getAlignment()) << 3;
$align |= CellAlignment::vertical($this->style->getAlignment()) << 4;
$align |= $this->textJustLast << 7;
$used_attrib = $atr_num << 2;
@@ -209,35 +226,35 @@ class Xf
$icv = $this->foregroundColor; // fg and bg pattern colors
$icv |= $this->backgroundColor << 7;
$border1 = self::mapBorderStyle($this->_style->getBorders()->getLeft()->getBorderStyle()); // Border line style and color
$border1 |= self::mapBorderStyle($this->_style->getBorders()->getRight()->getBorderStyle()) << 4;
$border1 |= self::mapBorderStyle($this->_style->getBorders()->getTop()->getBorderStyle()) << 8;
$border1 |= self::mapBorderStyle($this->_style->getBorders()->getBottom()->getBorderStyle()) << 12;
$border1 = CellBorder::style($this->style->getBorders()->getLeft()); // Border line style and color
$border1 |= CellBorder::style($this->style->getBorders()->getRight()) << 4;
$border1 |= CellBorder::style($this->style->getBorders()->getTop()) << 8;
$border1 |= CellBorder::style($this->style->getBorders()->getBottom()) << 12;
$border1 |= $this->leftBorderColor << 16;
$border1 |= $this->rightBorderColor << 23;
$diagonalDirection = $this->_style->getBorders()->getDiagonalDirection();
$diagonalDirection = $this->style->getBorders()->getDiagonalDirection();
$diag_tl_to_rb = $diagonalDirection == Borders::DIAGONAL_BOTH
|| $diagonalDirection == Borders::DIAGONAL_DOWN;
|| $diagonalDirection == Borders::DIAGONAL_DOWN;
$diag_tr_to_lb = $diagonalDirection == Borders::DIAGONAL_BOTH
|| $diagonalDirection == Borders::DIAGONAL_UP;
|| $diagonalDirection == Borders::DIAGONAL_UP;
$border1 |= $diag_tl_to_rb << 30;
$border1 |= $diag_tr_to_lb << 31;
$border2 = $this->topBorderColor; // Border color
$border2 |= $this->bottomBorderColor << 7;
$border2 |= $this->_diag_color << 14;
$border2 |= self::mapBorderStyle($this->_style->getBorders()->getDiagonal()->getBorderStyle()) << 21;
$border2 |= self::mapFillType($this->_style->getFill()->getFillType()) << 26;
$border2 |= $this->diagColor << 14;
$border2 |= CellBorder::style($this->style->getBorders()->getDiagonal()) << 21;
$border2 |= CellFill::style($this->style->getFill()) << 26;
$header = pack('vv', $record, $length);
//BIFF8 options: identation, shrinkToFit and text direction
$biff8_options = $this->_style->getAlignment()->getIndent();
$biff8_options |= (int) $this->_style->getAlignment()->getShrinkToFit() << 4;
$biff8_options = $this->style->getAlignment()->getIndent();
$biff8_options |= (int) $this->style->getAlignment()->getShrinkToFit() << 4;
$data = pack('vvvC', $ifnt, $ifmt, $style, $align);
$data .= pack('CCC', self::mapTextRotation($this->_style->getAlignment()->getTextRotation()), $biff8_options, $used_attrib);
$data .= pack('CCC', self::mapTextRotation($this->style->getAlignment()->getTextRotation()), $biff8_options, $used_attrib);
$data .= pack('VVv', $border1, $border2, $icv);
return $header . $data;
@@ -300,7 +317,7 @@ class Xf
*/
public function setDiagColor($colorIndex): void
{
$this->_diag_color = $colorIndex;
$this->diagColor = $colorIndex;
}
/**
@@ -344,148 +361,6 @@ class Xf
$this->fontIndex = $value;
}
/**
* Map of BIFF2-BIFF8 codes for border styles.
*
* @var array of int
*/
private static $mapBorderStyles = [
Border::BORDER_NONE => 0x00,
Border::BORDER_THIN => 0x01,
Border::BORDER_MEDIUM => 0x02,
Border::BORDER_DASHED => 0x03,
Border::BORDER_DOTTED => 0x04,
Border::BORDER_THICK => 0x05,
Border::BORDER_DOUBLE => 0x06,
Border::BORDER_HAIR => 0x07,
Border::BORDER_MEDIUMDASHED => 0x08,
Border::BORDER_DASHDOT => 0x09,
Border::BORDER_MEDIUMDASHDOT => 0x0A,
Border::BORDER_DASHDOTDOT => 0x0B,
Border::BORDER_MEDIUMDASHDOTDOT => 0x0C,
Border::BORDER_SLANTDASHDOT => 0x0D,
];
/**
* Map border style.
*
* @param string $borderStyle
*
* @return int
*/
private static function mapBorderStyle($borderStyle)
{
if (isset(self::$mapBorderStyles[$borderStyle])) {
return self::$mapBorderStyles[$borderStyle];
}
return 0x00;
}
/**
* Map of BIFF2-BIFF8 codes for fill types.
*
* @var array of int
*/
private static $mapFillTypes = [
Fill::FILL_NONE => 0x00,
Fill::FILL_SOLID => 0x01,
Fill::FILL_PATTERN_MEDIUMGRAY => 0x02,
Fill::FILL_PATTERN_DARKGRAY => 0x03,
Fill::FILL_PATTERN_LIGHTGRAY => 0x04,
Fill::FILL_PATTERN_DARKHORIZONTAL => 0x05,
Fill::FILL_PATTERN_DARKVERTICAL => 0x06,
Fill::FILL_PATTERN_DARKDOWN => 0x07,
Fill::FILL_PATTERN_DARKUP => 0x08,
Fill::FILL_PATTERN_DARKGRID => 0x09,
Fill::FILL_PATTERN_DARKTRELLIS => 0x0A,
Fill::FILL_PATTERN_LIGHTHORIZONTAL => 0x0B,
Fill::FILL_PATTERN_LIGHTVERTICAL => 0x0C,
Fill::FILL_PATTERN_LIGHTDOWN => 0x0D,
Fill::FILL_PATTERN_LIGHTUP => 0x0E,
Fill::FILL_PATTERN_LIGHTGRID => 0x0F,
Fill::FILL_PATTERN_LIGHTTRELLIS => 0x10,
Fill::FILL_PATTERN_GRAY125 => 0x11,
Fill::FILL_PATTERN_GRAY0625 => 0x12,
Fill::FILL_GRADIENT_LINEAR => 0x00, // does not exist in BIFF8
Fill::FILL_GRADIENT_PATH => 0x00, // does not exist in BIFF8
];
/**
* Map fill type.
*
* @param string $fillType
*
* @return int
*/
private static function mapFillType($fillType)
{
if (isset(self::$mapFillTypes[$fillType])) {
return self::$mapFillTypes[$fillType];
}
return 0x00;
}
/**
* Map of BIFF2-BIFF8 codes for horizontal alignment.
*
* @var array of int
*/
private static $mapHAlignments = [
Alignment::HORIZONTAL_GENERAL => 0,
Alignment::HORIZONTAL_LEFT => 1,
Alignment::HORIZONTAL_CENTER => 2,
Alignment::HORIZONTAL_RIGHT => 3,
Alignment::HORIZONTAL_FILL => 4,
Alignment::HORIZONTAL_JUSTIFY => 5,
Alignment::HORIZONTAL_CENTER_CONTINUOUS => 6,
];
/**
* Map to BIFF2-BIFF8 codes for horizontal alignment.
*
* @param string $hAlign
*
* @return int
*/
private function mapHAlign($hAlign)
{
if (isset(self::$mapHAlignments[$hAlign])) {
return self::$mapHAlignments[$hAlign];
}
return 0;
}
/**
* Map of BIFF2-BIFF8 codes for vertical alignment.
*
* @var array of int
*/
private static $mapVAlignments = [
Alignment::VERTICAL_TOP => 0,
Alignment::VERTICAL_CENTER => 1,
Alignment::VERTICAL_BOTTOM => 2,
Alignment::VERTICAL_JUSTIFY => 3,
];
/**
* Map to BIFF2-BIFF8 codes for vertical alignment.
*
* @param string $vAlign
*
* @return int
*/
private static function mapVAlign($vAlign)
{
if (isset(self::$mapVAlignments[$vAlign])) {
return self::$mapVAlignments[$vAlign];
}
return 2;
}
/**
* Map to BIFF8 codes for text rotation angle.
*
@@ -497,15 +372,22 @@ class Xf
{
if ($textRotation >= 0) {
return $textRotation;
} elseif ($textRotation == -165) {
return 255;
} elseif ($textRotation < 0) {
return 90 - $textRotation;
}
if ($textRotation == Alignment::TEXTROTATION_STACK_PHPSPREADSHEET) {
return Alignment::TEXTROTATION_STACK_EXCEL;
}
return 90 - $textRotation;
}
private const LOCK_ARRAY = [
Protection::PROTECTION_INHERIT => 1,
Protection::PROTECTION_PROTECTED => 1,
Protection::PROTECTION_UNPROTECTED => 0,
];
/**
* Map locked.
* Map locked values.
*
* @param string $locked
*
@@ -513,18 +395,15 @@ class Xf
*/
private static function mapLocked($locked)
{
switch ($locked) {
case Protection::PROTECTION_INHERIT:
return 1;
case Protection::PROTECTION_PROTECTED:
return 1;
case Protection::PROTECTION_UNPROTECTED:
return 0;
default:
return 1;
}
return array_key_exists($locked, self::LOCK_ARRAY) ? self::LOCK_ARRAY[$locked] : 1;
}
private const HIDDEN_ARRAY = [
Protection::PROTECTION_INHERIT => 0,
Protection::PROTECTION_PROTECTED => 1,
Protection::PROTECTION_UNPROTECTED => 0,
];
/**
* Map hidden.
*
@@ -534,15 +413,6 @@ class Xf
*/
private static function mapHidden($hidden)
{
switch ($hidden) {
case Protection::PROTECTION_INHERIT:
return 0;
case Protection::PROTECTION_PROTECTED:
return 1;
case Protection::PROTECTION_UNPROTECTED:
return 0;
default:
return 0;
}
return array_key_exists($hidden, self::HIDDEN_ARRAY) ? self::HIDDEN_ARRAY[$hidden] : 0;
}
}

View File

@@ -5,8 +5,13 @@ namespace PhpOffice\PhpSpreadsheet\Writer;
use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
use PhpOffice\PhpSpreadsheet\HashTable;
use PhpOffice\PhpSpreadsheet\Shared\File;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
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\Worksheet\BaseDrawing;
use PhpOffice\PhpSpreadsheet\Worksheet\Drawing as WorksheetDrawing;
use PhpOffice\PhpSpreadsheet\Worksheet\MemoryDrawing;
use PhpOffice\PhpSpreadsheet\Writer\Exception as WriterException;
@@ -20,6 +25,7 @@ use PhpOffice\PhpSpreadsheet\Writer\Xlsx\RelsRibbon;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx\RelsVBA;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx\StringTable;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx\Style;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx\Table;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx\Theme;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx\Workbook;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx\Worksheet;
@@ -37,13 +43,6 @@ class Xlsx extends BaseWriter
*/
private $office2003compatibility = false;
/**
* Private writer parts.
*
* @var Xlsx\WriterPart[]
*/
private $writerParts = [];
/**
* Private Spreadsheet.
*
@@ -61,49 +60,49 @@ class Xlsx extends BaseWriter
/**
* Private unique Conditional HashTable.
*
* @var HashTable
* @var HashTable<Conditional>
*/
private $stylesConditionalHashTable;
/**
* Private unique Style HashTable.
*
* @var HashTable
* @var HashTable<\PhpOffice\PhpSpreadsheet\Style\Style>
*/
private $styleHashTable;
/**
* Private unique Fill HashTable.
*
* @var HashTable
* @var HashTable<Fill>
*/
private $fillHashTable;
/**
* Private unique \PhpOffice\PhpSpreadsheet\Style\Font HashTable.
*
* @var HashTable
* @var HashTable<Font>
*/
private $fontHashTable;
/**
* Private unique Borders HashTable.
*
* @var HashTable
* @var HashTable<Borders>
*/
private $bordersHashTable;
/**
* Private unique NumberFormat HashTable.
*
* @var HashTable
* @var HashTable<NumberFormat>
*/
private $numFmtHashTable;
/**
* Private unique \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet\BaseDrawing HashTable.
*
* @var HashTable
* @var HashTable<BaseDrawing>
*/
private $drawingHashTable;
@@ -114,6 +113,76 @@ class Xlsx extends BaseWriter
*/
private $zip;
/**
* @var Chart
*/
private $writerPartChart;
/**
* @var Comments
*/
private $writerPartComments;
/**
* @var ContentTypes
*/
private $writerPartContentTypes;
/**
* @var DocProps
*/
private $writerPartDocProps;
/**
* @var Drawing
*/
private $writerPartDrawing;
/**
* @var Rels
*/
private $writerPartRels;
/**
* @var RelsRibbon
*/
private $writerPartRelsRibbon;
/**
* @var RelsVBA
*/
private $writerPartRelsVBA;
/**
* @var StringTable
*/
private $writerPartStringTable;
/**
* @var Style
*/
private $writerPartStyle;
/**
* @var Theme
*/
private $writerPartTheme;
/**
* @var Table
*/
private $writerPartTable;
/**
* @var Workbook
*/
private $writerPartWorkbook;
/**
* @var Worksheet
*/
private $writerPartWorksheet;
/**
* Create a new Xlsx Writer.
*/
@@ -122,68 +191,121 @@ class Xlsx extends BaseWriter
// Assign PhpSpreadsheet
$this->setSpreadsheet($spreadsheet);
$writerPartsArray = [
'stringtable' => StringTable::class,
'contenttypes' => ContentTypes::class,
'docprops' => DocProps::class,
'rels' => Rels::class,
'theme' => Theme::class,
'style' => Style::class,
'workbook' => Workbook::class,
'worksheet' => Worksheet::class,
'drawing' => Drawing::class,
'comments' => Comments::class,
'chart' => Chart::class,
'relsvba' => RelsVBA::class,
'relsribbonobjects' => RelsRibbon::class,
];
// Initialise writer parts
// and Assign their parent IWriters
foreach ($writerPartsArray as $writer => $class) {
$this->writerParts[$writer] = new $class($this);
}
$hashTablesArray = ['stylesConditionalHashTable', 'fillHashTable', 'fontHashTable',
'bordersHashTable', 'numFmtHashTable', 'drawingHashTable',
'styleHashTable',
];
$this->writerPartChart = new Chart($this);
$this->writerPartComments = new Comments($this);
$this->writerPartContentTypes = new ContentTypes($this);
$this->writerPartDocProps = new DocProps($this);
$this->writerPartDrawing = new Drawing($this);
$this->writerPartRels = new Rels($this);
$this->writerPartRelsRibbon = new RelsRibbon($this);
$this->writerPartRelsVBA = new RelsVBA($this);
$this->writerPartStringTable = new StringTable($this);
$this->writerPartStyle = new Style($this);
$this->writerPartTheme = new Theme($this);
$this->writerPartTable = new Table($this);
$this->writerPartWorkbook = new Workbook($this);
$this->writerPartWorksheet = new Worksheet($this);
// Set HashTable variables
foreach ($hashTablesArray as $tableName) {
$this->$tableName = new HashTable();
}
// @phpstan-ignore-next-line
$this->bordersHashTable = new HashTable();
// @phpstan-ignore-next-line
$this->drawingHashTable = new HashTable();
// @phpstan-ignore-next-line
$this->fillHashTable = new HashTable();
// @phpstan-ignore-next-line
$this->fontHashTable = new HashTable();
// @phpstan-ignore-next-line
$this->numFmtHashTable = new HashTable();
// @phpstan-ignore-next-line
$this->styleHashTable = new HashTable();
// @phpstan-ignore-next-line
$this->stylesConditionalHashTable = new HashTable();
}
/**
* Get writer part.
*
* @param string $pPartName Writer part name
*
* @return \PhpOffice\PhpSpreadsheet\Writer\Xlsx\WriterPart
*/
public function getWriterPart($pPartName)
public function getWriterPartChart(): Chart
{
if ($pPartName != '' && isset($this->writerParts[strtolower($pPartName)])) {
return $this->writerParts[strtolower($pPartName)];
}
return $this->writerPartChart;
}
return null;
public function getWriterPartComments(): Comments
{
return $this->writerPartComments;
}
public function getWriterPartContentTypes(): ContentTypes
{
return $this->writerPartContentTypes;
}
public function getWriterPartDocProps(): DocProps
{
return $this->writerPartDocProps;
}
public function getWriterPartDrawing(): Drawing
{
return $this->writerPartDrawing;
}
public function getWriterPartRels(): Rels
{
return $this->writerPartRels;
}
public function getWriterPartRelsRibbon(): RelsRibbon
{
return $this->writerPartRelsRibbon;
}
public function getWriterPartRelsVBA(): RelsVBA
{
return $this->writerPartRelsVBA;
}
public function getWriterPartStringTable(): StringTable
{
return $this->writerPartStringTable;
}
public function getWriterPartStyle(): Style
{
return $this->writerPartStyle;
}
public function getWriterPartTheme(): Theme
{
return $this->writerPartTheme;
}
public function getWriterPartTable(): Table
{
return $this->writerPartTable;
}
public function getWriterPartWorkbook(): Workbook
{
return $this->writerPartWorkbook;
}
public function getWriterPartWorksheet(): Worksheet
{
return $this->writerPartWorksheet;
}
/**
* Save PhpSpreadsheet to file.
*
* @param resource|string $pFilename
* @param resource|string $filename
*/
public function save($pFilename): void
public function save($filename, int $flags = 0): void
{
$this->processFlags($flags);
// garbage collect
$this->pathNames = [];
$this->spreadSheet->garbageCollect();
$this->openFileHandle($pFilename);
$saveDebugLog = Calculation::getInstance($this->spreadSheet)->getDebugLog()->getWriteDebugLog();
Calculation::getInstance($this->spreadSheet)->getDebugLog()->setWriteDebugLog(false);
$saveDateReturnType = Functions::getReturnDateType();
@@ -192,91 +314,89 @@ class Xlsx extends BaseWriter
// Create string lookup table
$this->stringTable = [];
for ($i = 0; $i < $this->spreadSheet->getSheetCount(); ++$i) {
$this->stringTable = $this->getWriterPart('StringTable')->createStringTable($this->spreadSheet->getSheet($i), $this->stringTable);
$this->stringTable = $this->getWriterPartStringTable()->createStringTable($this->spreadSheet->getSheet($i), $this->stringTable);
}
// Create styles dictionaries
$this->styleHashTable->addFromSource($this->getWriterPart('Style')->allStyles($this->spreadSheet));
$this->stylesConditionalHashTable->addFromSource($this->getWriterPart('Style')->allConditionalStyles($this->spreadSheet));
$this->fillHashTable->addFromSource($this->getWriterPart('Style')->allFills($this->spreadSheet));
$this->fontHashTable->addFromSource($this->getWriterPart('Style')->allFonts($this->spreadSheet));
$this->bordersHashTable->addFromSource($this->getWriterPart('Style')->allBorders($this->spreadSheet));
$this->numFmtHashTable->addFromSource($this->getWriterPart('Style')->allNumberFormats($this->spreadSheet));
$this->styleHashTable->addFromSource($this->getWriterPartStyle()->allStyles($this->spreadSheet));
$this->stylesConditionalHashTable->addFromSource($this->getWriterPartStyle()->allConditionalStyles($this->spreadSheet));
$this->fillHashTable->addFromSource($this->getWriterPartStyle()->allFills($this->spreadSheet));
$this->fontHashTable->addFromSource($this->getWriterPartStyle()->allFonts($this->spreadSheet));
$this->bordersHashTable->addFromSource($this->getWriterPartStyle()->allBorders($this->spreadSheet));
$this->numFmtHashTable->addFromSource($this->getWriterPartStyle()->allNumberFormats($this->spreadSheet));
// Create drawing dictionary
$this->drawingHashTable->addFromSource($this->getWriterPart('Drawing')->allDrawings($this->spreadSheet));
$options = new Archive();
$options->setEnableZip64(false);
$options->setOutputStream($this->fileHandle);
$this->zip = new ZipStream(null, $options);
$this->drawingHashTable->addFromSource($this->getWriterPartDrawing()->allDrawings($this->spreadSheet));
$zipContent = [];
// Add [Content_Types].xml to ZIP file
$this->addZipFile('[Content_Types].xml', $this->getWriterPart('ContentTypes')->writeContentTypes($this->spreadSheet, $this->includeCharts));
$zipContent['[Content_Types].xml'] = $this->getWriterPartContentTypes()->writeContentTypes($this->spreadSheet, $this->includeCharts);
//if hasMacros, add the vbaProject.bin file, Certificate file(if exists)
if ($this->spreadSheet->hasMacros()) {
$macrosCode = $this->spreadSheet->getMacrosCode();
if ($macrosCode !== null) {
// we have the code ?
$this->addZipFile('xl/vbaProject.bin', $macrosCode); //allways in 'xl', allways named vbaProject.bin
$zipContent['xl/vbaProject.bin'] = $macrosCode; //allways in 'xl', allways named vbaProject.bin
if ($this->spreadSheet->hasMacrosCertificate()) {
//signed macros ?
// Yes : add the certificate file and the related rels file
$this->addZipFile('xl/vbaProjectSignature.bin', $this->spreadSheet->getMacrosCertificate());
$this->addZipFile('xl/_rels/vbaProject.bin.rels', $this->getWriterPart('RelsVBA')->writeVBARelationships($this->spreadSheet));
$zipContent['xl/vbaProjectSignature.bin'] = $this->spreadSheet->getMacrosCertificate();
$zipContent['xl/_rels/vbaProject.bin.rels'] = $this->getWriterPartRelsVBA()->writeVBARelationships();
}
}
}
//a custom UI in this workbook ? add it ("base" xml and additional objects (pictures) and rels)
if ($this->spreadSheet->hasRibbon()) {
$tmpRibbonTarget = $this->spreadSheet->getRibbonXMLData('target');
$this->addZipFile($tmpRibbonTarget, $this->spreadSheet->getRibbonXMLData('data'));
$tmpRibbonTarget = is_string($tmpRibbonTarget) ? $tmpRibbonTarget : '';
$zipContent[$tmpRibbonTarget] = $this->spreadSheet->getRibbonXMLData('data');
if ($this->spreadSheet->hasRibbonBinObjects()) {
$tmpRootPath = dirname($tmpRibbonTarget) . '/';
$ribbonBinObjects = $this->spreadSheet->getRibbonBinObjects('data'); //the files to write
foreach ($ribbonBinObjects as $aPath => $aContent) {
$this->addZipFile($tmpRootPath . $aPath, $aContent);
if (is_array($ribbonBinObjects)) {
foreach ($ribbonBinObjects as $aPath => $aContent) {
$zipContent[$tmpRootPath . $aPath] = $aContent;
}
}
//the rels for files
$this->addZipFile($tmpRootPath . '_rels/' . basename($tmpRibbonTarget) . '.rels', $this->getWriterPart('RelsRibbonObjects')->writeRibbonRelationships($this->spreadSheet));
$zipContent[$tmpRootPath . '_rels/' . basename($tmpRibbonTarget) . '.rels'] = $this->getWriterPartRelsRibbon()->writeRibbonRelationships($this->spreadSheet);
}
}
// Add relationships to ZIP file
$this->addZipFile('_rels/.rels', $this->getWriterPart('Rels')->writeRelationships($this->spreadSheet));
$this->addZipFile('xl/_rels/workbook.xml.rels', $this->getWriterPart('Rels')->writeWorkbookRelationships($this->spreadSheet));
$zipContent['_rels/.rels'] = $this->getWriterPartRels()->writeRelationships($this->spreadSheet);
$zipContent['xl/_rels/workbook.xml.rels'] = $this->getWriterPartRels()->writeWorkbookRelationships($this->spreadSheet);
// Add document properties to ZIP file
$this->addZipFile('docProps/app.xml', $this->getWriterPart('DocProps')->writeDocPropsApp($this->spreadSheet));
$this->addZipFile('docProps/core.xml', $this->getWriterPart('DocProps')->writeDocPropsCore($this->spreadSheet));
$customPropertiesPart = $this->getWriterPart('DocProps')->writeDocPropsCustom($this->spreadSheet);
$zipContent['docProps/app.xml'] = $this->getWriterPartDocProps()->writeDocPropsApp($this->spreadSheet);
$zipContent['docProps/core.xml'] = $this->getWriterPartDocProps()->writeDocPropsCore($this->spreadSheet);
$customPropertiesPart = $this->getWriterPartDocProps()->writeDocPropsCustom($this->spreadSheet);
if ($customPropertiesPart !== null) {
$this->addZipFile('docProps/custom.xml', $customPropertiesPart);
$zipContent['docProps/custom.xml'] = $customPropertiesPart;
}
// Add theme to ZIP file
$this->addZipFile('xl/theme/theme1.xml', $this->getWriterPart('Theme')->writeTheme($this->spreadSheet));
$zipContent['xl/theme/theme1.xml'] = $this->getWriterPartTheme()->writeTheme();
// Add string table to ZIP file
$this->addZipFile('xl/sharedStrings.xml', $this->getWriterPart('StringTable')->writeStringTable($this->stringTable));
$zipContent['xl/sharedStrings.xml'] = $this->getWriterPartStringTable()->writeStringTable($this->stringTable);
// Add styles to ZIP file
$this->addZipFile('xl/styles.xml', $this->getWriterPart('Style')->writeStyles($this->spreadSheet));
$zipContent['xl/styles.xml'] = $this->getWriterPartStyle()->writeStyles($this->spreadSheet);
// Add workbook to ZIP file
$this->addZipFile('xl/workbook.xml', $this->getWriterPart('Workbook')->writeWorkbook($this->spreadSheet, $this->preCalculateFormulas));
$zipContent['xl/workbook.xml'] = $this->getWriterPartWorkbook()->writeWorkbook($this->spreadSheet, $this->preCalculateFormulas);
$chartCount = 0;
// Add worksheets
for ($i = 0; $i < $this->spreadSheet->getSheetCount(); ++$i) {
$this->addZipFile('xl/worksheets/sheet' . ($i + 1) . '.xml', $this->getWriterPart('Worksheet')->writeWorksheet($this->spreadSheet->getSheet($i), $this->stringTable, $this->includeCharts));
$zipContent['xl/worksheets/sheet' . ($i + 1) . '.xml'] = $this->getWriterPartWorksheet()->writeWorksheet($this->spreadSheet->getSheet($i), $this->stringTable, $this->includeCharts);
if ($this->includeCharts) {
$charts = $this->spreadSheet->getSheet($i)->getChartCollection();
if (count($charts) > 0) {
foreach ($charts as $chart) {
$this->addZipFile('xl/charts/chart' . ($chartCount + 1) . '.xml', $this->getWriterPart('Chart')->writeChart($chart, $this->preCalculateFormulas));
$zipContent['xl/charts/chart' . ($chartCount + 1) . '.xml'] = $this->getWriterPartChart()->writeChart($chart, $this->preCalculateFormulas);
++$chartCount;
}
}
@@ -284,22 +404,23 @@ class Xlsx extends BaseWriter
}
$chartRef1 = 0;
$tableRef1 = 1;
// Add worksheet relationships (drawings, ...)
for ($i = 0; $i < $this->spreadSheet->getSheetCount(); ++$i) {
// Add relationships
$this->addZipFile('xl/worksheets/_rels/sheet' . ($i + 1) . '.xml.rels', $this->getWriterPart('Rels')->writeWorksheetRelationships($this->spreadSheet->getSheet($i), ($i + 1), $this->includeCharts));
$zipContent['xl/worksheets/_rels/sheet' . ($i + 1) . '.xml.rels'] = $this->getWriterPartRels()->writeWorksheetRelationships($this->spreadSheet->getSheet($i), ($i + 1), $this->includeCharts, $tableRef1);
// Add unparsedLoadedData
$sheetCodeName = $this->spreadSheet->getSheet($i)->getCodeName();
$unparsedLoadedData = $this->spreadSheet->getUnparsedLoadedData();
if (isset($unparsedLoadedData['sheets'][$sheetCodeName]['ctrlProps'])) {
foreach ($unparsedLoadedData['sheets'][$sheetCodeName]['ctrlProps'] as $ctrlProp) {
$this->addZipFile($ctrlProp['filePath'], $ctrlProp['content']);
$zipContent[$ctrlProp['filePath']] = $ctrlProp['content'];
}
}
if (isset($unparsedLoadedData['sheets'][$sheetCodeName]['printerSettings'])) {
foreach ($unparsedLoadedData['sheets'][$sheetCodeName]['printerSettings'] as $ctrlProp) {
$this->addZipFile($ctrlProp['filePath'], $ctrlProp['content']);
$zipContent[$ctrlProp['filePath']] = $ctrlProp['content'];
}
}
@@ -312,13 +433,13 @@ class Xlsx extends BaseWriter
// Add drawing and image relationship parts
if (($drawingCount > 0) || ($chartCount > 0)) {
// Drawing relationships
$this->addZipFile('xl/drawings/_rels/drawing' . ($i + 1) . '.xml.rels', $this->getWriterPart('Rels')->writeDrawingRelationships($this->spreadSheet->getSheet($i), $chartRef1, $this->includeCharts));
$zipContent['xl/drawings/_rels/drawing' . ($i + 1) . '.xml.rels'] = $this->getWriterPartRels()->writeDrawingRelationships($this->spreadSheet->getSheet($i), $chartRef1, $this->includeCharts);
// Drawings
$this->addZipFile('xl/drawings/drawing' . ($i + 1) . '.xml', $this->getWriterPart('Drawing')->writeDrawings($this->spreadSheet->getSheet($i), $this->includeCharts));
$zipContent['xl/drawings/drawing' . ($i + 1) . '.xml'] = $this->getWriterPartDrawing()->writeDrawings($this->spreadSheet->getSheet($i), $this->includeCharts);
} elseif (isset($unparsedLoadedData['sheets'][$sheetCodeName]['drawingAlternateContents'])) {
// Drawings
$this->addZipFile('xl/drawings/drawing' . ($i + 1) . '.xml', $this->getWriterPart('Drawing')->writeDrawings($this->spreadSheet->getSheet($i), $this->includeCharts));
$zipContent['xl/drawings/drawing' . ($i + 1) . '.xml'] = $this->getWriterPartDrawing()->writeDrawings($this->spreadSheet->getSheet($i), $this->includeCharts);
}
// Add unparsed drawings
@@ -326,41 +447,59 @@ class Xlsx extends BaseWriter
foreach ($unparsedLoadedData['sheets'][$sheetCodeName]['Drawings'] as $relId => $drawingXml) {
$drawingFile = array_search($relId, $unparsedLoadedData['sheets'][$sheetCodeName]['drawingOriginalIds']);
if ($drawingFile !== false) {
$drawingFile = ltrim($drawingFile, '.');
$this->addZipFile('xl' . $drawingFile, $drawingXml);
//$drawingFile = ltrim($drawingFile, '.');
//$zipContent['xl' . $drawingFile] = $drawingXml;
$zipContent['xl/drawings/drawing' . ($i + 1) . '.xml'] = $drawingXml;
}
}
}
// Add comment relationship parts
if (count($this->spreadSheet->getSheet($i)->getComments()) > 0) {
// VML Comments relationships
$zipContent['xl/drawings/_rels/vmlDrawing' . ($i + 1) . '.vml.rels'] = $this->getWriterPartRels()->writeVMLDrawingRelationships($this->spreadSheet->getSheet($i));
// VML Comments
$this->addZipFile('xl/drawings/vmlDrawing' . ($i + 1) . '.vml', $this->getWriterPart('Comments')->writeVMLComments($this->spreadSheet->getSheet($i)));
$zipContent['xl/drawings/vmlDrawing' . ($i + 1) . '.vml'] = $this->getWriterPartComments()->writeVMLComments($this->spreadSheet->getSheet($i));
// Comments
$this->addZipFile('xl/comments' . ($i + 1) . '.xml', $this->getWriterPart('Comments')->writeComments($this->spreadSheet->getSheet($i)));
$zipContent['xl/comments' . ($i + 1) . '.xml'] = $this->getWriterPartComments()->writeComments($this->spreadSheet->getSheet($i));
// Media
foreach ($this->spreadSheet->getSheet($i)->getComments() as $comment) {
if ($comment->hasBackgroundImage()) {
$image = $comment->getBackgroundImage();
$zipContent['xl/media/' . $image->getMediaFilename()] = $this->processDrawing($image);
}
}
}
// Add unparsed relationship parts
if (isset($unparsedLoadedData['sheets'][$sheetCodeName]['vmlDrawings'])) {
foreach ($unparsedLoadedData['sheets'][$sheetCodeName]['vmlDrawings'] as $vmlDrawing) {
$this->addZipFile($vmlDrawing['filePath'], $vmlDrawing['content']);
$zipContent[$vmlDrawing['filePath']] = $vmlDrawing['content'];
}
}
// Add header/footer relationship parts
if (count($this->spreadSheet->getSheet($i)->getHeaderFooter()->getImages()) > 0) {
// VML Drawings
$this->addZipFile('xl/drawings/vmlDrawingHF' . ($i + 1) . '.vml', $this->getWriterPart('Drawing')->writeVMLHeaderFooterImages($this->spreadSheet->getSheet($i)));
$zipContent['xl/drawings/vmlDrawingHF' . ($i + 1) . '.vml'] = $this->getWriterPartDrawing()->writeVMLHeaderFooterImages($this->spreadSheet->getSheet($i));
// VML Drawing relationships
$this->addZipFile('xl/drawings/_rels/vmlDrawingHF' . ($i + 1) . '.vml.rels', $this->getWriterPart('Rels')->writeHeaderFooterDrawingRelationships($this->spreadSheet->getSheet($i)));
$zipContent['xl/drawings/_rels/vmlDrawingHF' . ($i + 1) . '.vml.rels'] = $this->getWriterPartRels()->writeHeaderFooterDrawingRelationships($this->spreadSheet->getSheet($i));
// Media
foreach ($this->spreadSheet->getSheet($i)->getHeaderFooter()->getImages() as $image) {
$this->addZipFile('xl/media/' . $image->getIndexedFilename(), file_get_contents($image->getPath()));
$zipContent['xl/media/' . $image->getIndexedFilename()] = file_get_contents($image->getPath());
}
}
// Add Table parts
$tables = $this->spreadSheet->getSheet($i)->getTableCollection();
foreach ($tables as $table) {
$zipContent['xl/tables/table' . $tableRef1 . '.xml'] = $this->getWriterPartTable()->writeTable($table, $tableRef1++);
}
}
// Add media
@@ -381,23 +520,35 @@ class Xlsx extends BaseWriter
$imageContents = file_get_contents($imagePath);
}
$this->addZipFile('xl/media/' . str_replace(' ', '_', $this->getDrawingHashTable()->getByIndex($i)->getIndexedFilename()), $imageContents);
$zipContent['xl/media/' . $this->getDrawingHashTable()->getByIndex($i)->getIndexedFilename()] = $imageContents;
} elseif ($this->getDrawingHashTable()->getByIndex($i) instanceof MemoryDrawing) {
ob_start();
/** @var callable */
$callable = $this->getDrawingHashTable()->getByIndex($i)->getRenderingFunction();
call_user_func(
$this->getDrawingHashTable()->getByIndex($i)->getRenderingFunction(),
$callable,
$this->getDrawingHashTable()->getByIndex($i)->getImageResource()
);
$imageContents = ob_get_contents();
ob_end_clean();
$this->addZipFile('xl/media/' . str_replace(' ', '_', $this->getDrawingHashTable()->getByIndex($i)->getIndexedFilename()), $imageContents);
$zipContent['xl/media/' . $this->getDrawingHashTable()->getByIndex($i)->getIndexedFilename()] = $imageContents;
}
}
Functions::setReturnDateType($saveDateReturnType);
Calculation::getInstance($this->spreadSheet)->getDebugLog()->setWriteDebugLog($saveDebugLog);
$this->openFileHandle($filename);
$options = new Archive();
$options->setEnableZip64(false);
$options->setOutputStream($this->fileHandle);
$this->zip = new ZipStream(null, $options);
$this->addZipFiles($zipContent);
// Close file
try {
$this->zip->finish();
@@ -445,7 +596,7 @@ class Xlsx extends BaseWriter
/**
* Get Style HashTable.
*
* @return HashTable
* @return HashTable<\PhpOffice\PhpSpreadsheet\Style\Style>
*/
public function getStyleHashTable()
{
@@ -455,7 +606,7 @@ class Xlsx extends BaseWriter
/**
* Get Conditional HashTable.
*
* @return HashTable
* @return HashTable<Conditional>
*/
public function getStylesConditionalHashTable()
{
@@ -465,7 +616,7 @@ class Xlsx extends BaseWriter
/**
* Get Fill HashTable.
*
* @return HashTable
* @return HashTable<Fill>
*/
public function getFillHashTable()
{
@@ -475,7 +626,7 @@ class Xlsx extends BaseWriter
/**
* Get \PhpOffice\PhpSpreadsheet\Style\Font HashTable.
*
* @return HashTable
* @return HashTable<Font>
*/
public function getFontHashTable()
{
@@ -485,7 +636,7 @@ class Xlsx extends BaseWriter
/**
* Get Borders HashTable.
*
* @return HashTable
* @return HashTable<Borders>
*/
public function getBordersHashTable()
{
@@ -495,7 +646,7 @@ class Xlsx extends BaseWriter
/**
* Get NumberFormat HashTable.
*
* @return HashTable
* @return HashTable<NumberFormat>
*/
public function getNumFmtHashTable()
{
@@ -505,7 +656,7 @@ class Xlsx extends BaseWriter
/**
* Get \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet\BaseDrawing HashTable.
*
* @return HashTable
* @return HashTable<BaseDrawing>
*/
public function getDrawingHashTable()
{
@@ -525,17 +676,18 @@ class Xlsx extends BaseWriter
/**
* Set Office2003 compatibility.
*
* @param bool $pValue Office2003 compatibility?
* @param bool $office2003compatibility Office2003 compatibility?
*
* @return $this
*/
public function setOffice2003Compatibility($pValue)
public function setOffice2003Compatibility($office2003compatibility)
{
$this->office2003compatibility = $pValue;
$this->office2003compatibility = $office2003compatibility;
return $this;
}
/** @var array */
private $pathNames = [];
private function addZipFile(string $path, string $content): void
@@ -545,4 +697,59 @@ class Xlsx extends BaseWriter
$this->zip->addFile($path, $content);
}
}
private function addZipFiles(array $zipContent): void
{
foreach ($zipContent as $path => $content) {
$this->addZipFile($path, $content);
}
}
/**
* @return mixed
*/
private function processDrawing(WorksheetDrawing $drawing)
{
$data = null;
$filename = $drawing->getPath();
$imageData = getimagesize($filename);
if (!empty($imageData)) {
switch ($imageData[2]) {
case 1: // GIF, not supported by BIFF8, we convert to PNG
$image = imagecreatefromgif($filename);
if ($image !== false) {
ob_start();
imagepng($image);
$data = ob_get_contents();
ob_end_clean();
}
break;
case 2: // JPEG
$data = file_get_contents($filename);
break;
case 3: // PNG
$data = file_get_contents($filename);
break;
case 6: // Windows DIB (BMP), we convert to PNG
$image = imagecreatefrombmp($filename);
if ($image !== false) {
ob_start();
imagepng($image);
$data = ob_get_contents();
ob_end_clean();
}
break;
}
}
return $data;
}
}

View File

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

File diff suppressed because it is too large Load Diff

View File

@@ -4,6 +4,7 @@ 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
@@ -13,7 +14,7 @@ class Comments extends WriterPart
*
* @return string XML Output
*/
public function writeComments(\PhpOffice\PhpSpreadsheet\Worksheet\Worksheet $pWorksheet)
public function writeComments(\PhpOffice\PhpSpreadsheet\Worksheet\Worksheet $worksheet)
{
// Create XML writer
$objWriter = null;
@@ -27,7 +28,7 @@ class Comments extends WriterPart
$objWriter->startDocument('1.0', 'UTF-8', 'yes');
// Comments cache
$comments = $pWorksheet->getComments();
$comments = $worksheet->getComments();
// Authors cache
$authors = [];
@@ -40,7 +41,7 @@ class Comments extends WriterPart
// comments
$objWriter->startElement('comments');
$objWriter->writeAttribute('xmlns', 'http://schemas.openxmlformats.org/spreadsheetml/2006/main');
$objWriter->writeAttribute('xmlns', Namespaces::MAIN);
// Loop through authors
$objWriter->startElement('authors');
@@ -65,21 +66,20 @@ class Comments extends WriterPart
/**
* Write comment to XML format.
*
* @param XMLWriter $objWriter XML Writer
* @param string $pCellReference Cell reference
* @param Comment $pComment Comment
* @param array $pAuthors Array of authors
* @param string $cellReference Cell reference
* @param Comment $comment Comment
* @param array $authors Array of authors
*/
private function writeComment(XMLWriter $objWriter, $pCellReference, Comment $pComment, array $pAuthors): void
private function writeComment(XMLWriter $objWriter, $cellReference, Comment $comment, array $authors): void
{
// comment
$objWriter->startElement('comment');
$objWriter->writeAttribute('ref', $pCellReference);
$objWriter->writeAttribute('authorId', $pAuthors[$pComment->getAuthor()]);
$objWriter->writeAttribute('ref', $cellReference);
$objWriter->writeAttribute('authorId', $authors[$comment->getAuthor()]);
// text
$objWriter->startElement('text');
$this->getParentWriter()->getWriterPart('stringtable')->writeRichText($objWriter, $pComment->getText());
$this->getParentWriter()->getWriterPartstringtable()->writeRichText($objWriter, $comment->getText());
$objWriter->endElement();
$objWriter->endElement();
@@ -90,7 +90,7 @@ class Comments extends WriterPart
*
* @return string XML Output
*/
public function writeVMLComments(\PhpOffice\PhpSpreadsheet\Worksheet\Worksheet $pWorksheet)
public function writeVMLComments(\PhpOffice\PhpSpreadsheet\Worksheet\Worksheet $worksheet)
{
// Create XML writer
$objWriter = null;
@@ -104,13 +104,13 @@ class Comments extends WriterPart
$objWriter->startDocument('1.0', 'UTF-8', 'yes');
// Comments cache
$comments = $pWorksheet->getComments();
$comments = $worksheet->getComments();
// xml
$objWriter->startElement('xml');
$objWriter->writeAttribute('xmlns:v', 'urn:schemas-microsoft-com:vml');
$objWriter->writeAttribute('xmlns:o', 'urn:schemas-microsoft-com:office:office');
$objWriter->writeAttribute('xmlns:x', 'urn:schemas-microsoft-com:office:excel');
$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');
@@ -158,29 +158,33 @@ class Comments extends WriterPart
/**
* Write VML comment to XML format.
*
* @param XMLWriter $objWriter XML Writer
* @param string $pCellReference Cell reference, eg: 'A1'
* @param Comment $pComment Comment
* @param string $cellReference Cell reference, eg: 'A1'
* @param Comment $comment Comment
*/
private function writeVMLComment(XMLWriter $objWriter, $pCellReference, Comment $pComment): void
private function writeVMLComment(XMLWriter $objWriter, $cellReference, Comment $comment): void
{
// Metadata
[$column, $row] = Coordinate::coordinateFromString($pCellReference);
$column = Coordinate::columnIndexFromString($column);
[$column, $row] = Coordinate::indexesFromString($cellReference);
$id = 1024 + $column + $row;
$id = substr($id, 0, 4);
$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:' . $pComment->getMarginLeft() . ';margin-top:' . $pComment->getMarginTop() . ';width:' . $pComment->getWidth() . ';height:' . $pComment->getHeight() . ';z-index:1;visibility:' . ($pComment->getVisible() ? 'visible' : 'hidden'));
$objWriter->writeAttribute('fillcolor', '#' . $pComment->getFillColor()->getRGB());
$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', '#' . $pComment->getFillColor()->getRGB());
$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
@@ -220,10 +224,10 @@ class Comments extends WriterPart
$objWriter->writeElement('x:AutoFill', 'False');
// x:Row
$objWriter->writeElement('x:Row', ($row - 1));
$objWriter->writeElement('x:Row', (string) ($row - 1));
// x:Column
$objWriter->writeElement('x:Column', ($column - 1));
$objWriter->writeElement('x:Column', (string) ($column - 1));
$objWriter->endElement();

View File

@@ -2,6 +2,7 @@
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;
@@ -32,7 +33,7 @@ class ContentTypes extends WriterPart
// Types
$objWriter->startElement('Types');
$objWriter->writeAttribute('xmlns', 'http://schemas.openxmlformats.org/package/2006/content-types');
$objWriter->writeAttribute('xmlns', Namespaces::CONTENT_TYPES);
// Theme
$this->writeOverrideContentType($objWriter, '/xl/theme/theme1.xml', 'application/vnd.openxmlformats-officedocument.theme+xml');
@@ -85,6 +86,16 @@ class ContentTypes extends WriterPart
// 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;
@@ -141,7 +152,7 @@ class ContentTypes extends WriterPart
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));
$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);
@@ -158,6 +169,23 @@ class ContentTypes extends WriterPart
}
}
}
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]);
}
}
}
}
// unparsed defaults
@@ -183,35 +211,34 @@ class ContentTypes extends WriterPart
/**
* Get image mime type.
*
* @param string $pFile Filename
* @param string $filename Filename
*
* @return string Mime Type
*/
private function getImageMimeType($pFile)
private function getImageMimeType($filename)
{
if (File::fileExists($pFile)) {
$image = getimagesize($pFile);
if (File::fileExists($filename)) {
$image = getimagesize($filename);
return image_type_to_mime_type($image[2]);
return image_type_to_mime_type((is_array($image) && count($image) >= 3) ? $image[2] : 0);
}
throw new WriterException("File $pFile does not exist");
throw new WriterException("File $filename does not exist");
}
/**
* Write Default content type.
*
* @param XMLWriter $objWriter XML Writer
* @param string $pPartname Part name
* @param string $pContentType Content type
* @param string $partName Part name
* @param string $contentType Content type
*/
private function writeDefaultContentType(XMLWriter $objWriter, $pPartname, $pContentType): void
private function writeDefaultContentType(XMLWriter $objWriter, $partName, $contentType): void
{
if ($pPartname != '' && $pContentType != '') {
if ($partName != '' && $contentType != '') {
// Write content type
$objWriter->startElement('Default');
$objWriter->writeAttribute('Extension', $pPartname);
$objWriter->writeAttribute('ContentType', $pContentType);
$objWriter->writeAttribute('Extension', $partName);
$objWriter->writeAttribute('ContentType', $contentType);
$objWriter->endElement();
} else {
throw new WriterException('Invalid parameters passed.');
@@ -221,17 +248,16 @@ class ContentTypes extends WriterPart
/**
* Write Override content type.
*
* @param XMLWriter $objWriter XML Writer
* @param string $pPartname Part name
* @param string $pContentType Content type
* @param string $partName Part name
* @param string $contentType Content type
*/
private function writeOverrideContentType(XMLWriter $objWriter, $pPartname, $pContentType): void
private function writeOverrideContentType(XMLWriter $objWriter, $partName, $contentType): void
{
if ($pPartname != '' && $pContentType != '') {
if ($partName != '' && $contentType != '') {
// Write content type
$objWriter->startElement('Override');
$objWriter->writeAttribute('PartName', $pPartname);
$objWriter->writeAttribute('ContentType', $pContentType);
$objWriter->writeAttribute('PartName', $partName);
$objWriter->writeAttribute('ContentType', $contentType);
$objWriter->endElement();
} else {
throw new WriterException('Invalid parameters passed.');

View File

@@ -2,17 +2,20 @@
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;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet as ActualWorksheet;
class DefinedNames
{
/** @var XMLWriter */
private $objWriter;
/** @var Spreadsheet */
private $spreadsheet;
public function __construct(XMLWriter $objWriter, Spreadsheet $spreadsheet)
@@ -63,16 +66,134 @@ class DefinedNames
/**
* Write Defined Name for named range.
*/
private function writeDefinedName(DefinedName $pDefinedName): void
private function writeDefinedName(DefinedName $definedName): void
{
// definedName for named range
$local = -1;
if ($definedName->getLocalOnly() && $definedName->getScope() !== null) {
try {
$local = $definedName->getScope()->getParent()->getIndex($definedName->getScope());
} catch (Exception $e) {
// See issue 2266 - deleting sheet which contains
// defined names will cause Exception above.
return;
}
}
$this->objWriter->startElement('definedName');
$this->objWriter->writeAttribute('name', $pDefinedName->getName());
if ($pDefinedName->getLocalOnly() && $pDefinedName->getScope() !== null) {
$this->objWriter->writeAttribute('localSheetId', $pDefinedName->getScope()->getParent()->getIndex($pDefinedName->getScope()));
$this->objWriter->writeAttribute('name', $definedName->getName());
if ($local >= 0) {
$this->objWriter->writeAttribute(
'localSheetId',
"$local"
);
}
$definedRange = $pDefinedName->getValue();
$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,
@@ -99,21 +220,17 @@ class DefinedNames
if (empty($worksheet)) {
if (($offset === 0) || ($definedRange[$offset - 1] !== ':')) {
// We should have a worksheet
$worksheet = $pDefinedName->getWorksheet()->getTitle();
$ws = $definedName->getWorksheet();
$worksheet = ($ws === null) ? null : $ws->getTitle();
}
} else {
$worksheet = str_replace("''", "'", trim($worksheet, "'"));
}
if (!empty($worksheet)) {
$newRange = "'" . str_replace("'", "''", $worksheet) . "'!";
}
if (!empty($column)) {
$newRange .= $column;
}
if (!empty($row)) {
$newRange .= $row;
}
$newRange = "{$newRange}{$column}{$row}";
$definedRange = substr($definedRange, 0, $offset) . $newRange . substr($definedRange, $offset + $length);
}
@@ -122,102 +239,6 @@ class DefinedNames
$definedRange = substr($definedRange, 1);
}
$this->objWriter->writeRawData($definedRange);
$this->objWriter->endElement();
}
/**
* Write Defined Name for autoFilter.
*/
private function writeNamedRangeForAutofilter(Worksheet $pSheet, int $pSheetId = 0): void
{
// NamedRange for autoFilter
$autoFilterRange = $pSheet->getAutoFilter()->getRange();
if (!empty($autoFilterRange)) {
$this->objWriter->startElement('definedName');
$this->objWriter->writeAttribute('name', '_xlnm._FilterDatabase');
$this->objWriter->writeAttribute('localSheetId', $pSheetId);
$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
[$ws, $range[0]] = Worksheet::extractSheetTitle($range[0], true);
$range[0] = Coordinate::absoluteCoordinate($range[0]);
$range[1] = Coordinate::absoluteCoordinate($range[1]);
$range = implode(':', $range);
$this->objWriter->writeRawData('\'' . str_replace("'", "''", $pSheet->getTitle()) . '\'!' . $range);
$this->objWriter->endElement();
}
}
/**
* Write Defined Name for PrintTitles.
*/
private function writeNamedRangeForPrintTitles(Worksheet $pSheet, int $pSheetId = 0): void
{
// NamedRange for PrintTitles
if ($pSheet->getPageSetup()->isColumnsToRepeatAtLeftSet() || $pSheet->getPageSetup()->isRowsToRepeatAtTopSet()) {
$this->objWriter->startElement('definedName');
$this->objWriter->writeAttribute('name', '_xlnm.Print_Titles');
$this->objWriter->writeAttribute('localSheetId', $pSheetId);
// Setting string
$settingString = '';
// Columns to repeat
if ($pSheet->getPageSetup()->isColumnsToRepeatAtLeftSet()) {
$repeat = $pSheet->getPageSetup()->getColumnsToRepeatAtLeft();
$settingString .= '\'' . str_replace("'", "''", $pSheet->getTitle()) . '\'!$' . $repeat[0] . ':$' . $repeat[1];
}
// Rows to repeat
if ($pSheet->getPageSetup()->isRowsToRepeatAtTopSet()) {
if ($pSheet->getPageSetup()->isColumnsToRepeatAtLeftSet()) {
$settingString .= ',';
}
$repeat = $pSheet->getPageSetup()->getRowsToRepeatAtTop();
$settingString .= '\'' . str_replace("'", "''", $pSheet->getTitle()) . '\'!$' . $repeat[0] . ':$' . $repeat[1];
}
$this->objWriter->writeRawData($settingString);
$this->objWriter->endElement();
}
}
/**
* Write Defined Name for PrintTitles.
*/
private function writeNamedRangeForPrintArea(Worksheet $pSheet, int $pSheetId = 0): void
{
// NamedRange for PrintArea
if ($pSheet->getPageSetup()->isPrintAreaSet()) {
$this->objWriter->startElement('definedName');
$this->objWriter->writeAttribute('name', '_xlnm.Print_Area');
$this->objWriter->writeAttribute('localSheetId', $pSheetId);
// Print area
$printArea = Coordinate::splitRange($pSheet->getPageSetup()->getPrintArea());
$chunks = [];
foreach ($printArea as $printAreaRect) {
$printAreaRect[0] = Coordinate::absoluteReference($printAreaRect[0]);
$printAreaRect[1] = Coordinate::absoluteReference($printAreaRect[1]);
$chunks[] = '\'' . str_replace("'", "''", $pSheet->getTitle()) . '\'!' . implode(':', $printAreaRect);
}
$this->objWriter->writeRawData(implode(',', $chunks));
$this->objWriter->endElement();
}
return $definedRange;
}
}

View File

@@ -2,6 +2,9 @@
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;
@@ -27,8 +30,8 @@ class DocProps extends WriterPart
// Properties
$objWriter->startElement('Properties');
$objWriter->writeAttribute('xmlns', 'http://schemas.openxmlformats.org/officeDocument/2006/extended-properties');
$objWriter->writeAttribute('xmlns:vt', 'http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes');
$objWriter->writeAttribute('xmlns', Namespaces::EXTENDED_PROPERTIES);
$objWriter->writeAttribute('xmlns:vt', Namespaces::PROPERTIES_VTYPES);
// Application
$objWriter->writeElement('Application', 'Microsoft Excel');
@@ -54,7 +57,7 @@ class DocProps extends WriterPart
// Variant
$objWriter->startElement('vt:variant');
$objWriter->writeElement('vt:i4', $spreadsheet->getSheetCount());
$objWriter->writeElement('vt:i4', (string) $spreadsheet->getSheetCount());
$objWriter->endElement();
$objWriter->endElement();
@@ -66,7 +69,7 @@ class DocProps extends WriterPart
// Vector
$objWriter->startElement('vt:vector');
$objWriter->writeAttribute('size', $spreadsheet->getSheetCount());
$objWriter->writeAttribute('size', (string) $spreadsheet->getSheetCount());
$objWriter->writeAttribute('baseType', 'lpstr');
$sheetCount = $spreadsheet->getSheetCount();
@@ -122,11 +125,11 @@ class DocProps extends WriterPart
// cp:coreProperties
$objWriter->startElement('cp:coreProperties');
$objWriter->writeAttribute('xmlns:cp', 'http://schemas.openxmlformats.org/package/2006/metadata/core-properties');
$objWriter->writeAttribute('xmlns:dc', 'http://purl.org/dc/elements/1.1/');
$objWriter->writeAttribute('xmlns:dcterms', 'http://purl.org/dc/terms/');
$objWriter->writeAttribute('xmlns:dcmitype', 'http://purl.org/dc/dcmitype/');
$objWriter->writeAttribute('xmlns:xsi', 'http://www.w3.org/2001/XMLSchema-instance');
$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());
@@ -137,13 +140,17 @@ class DocProps extends WriterPart
// dcterms:created
$objWriter->startElement('dcterms:created');
$objWriter->writeAttribute('xsi:type', 'dcterms:W3CDTF');
$objWriter->writeRawData(date(DATE_W3C, $spreadsheet->getProperties()->getCreated()));
$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');
$objWriter->writeRawData(date(DATE_W3C, $spreadsheet->getProperties()->getModified()));
$created = $spreadsheet->getProperties()->getModified();
$date = Date::dateTimeFromTimestamp("$created");
$objWriter->writeRawData($date->format(DATE_W3C));
$objWriter->endElement();
// dc:title
@@ -170,13 +177,13 @@ class DocProps extends WriterPart
/**
* Write docProps/custom.xml to XML format.
*
* @return string XML Output
* @return null|string XML Output
*/
public function writeDocPropsCustom(Spreadsheet $spreadsheet)
{
$customPropertyList = $spreadsheet->getProperties()->getCustomProperties();
if (empty($customPropertyList)) {
return;
return null;
}
// Create XML writer
@@ -192,8 +199,8 @@ class DocProps extends WriterPart
// cp:coreProperties
$objWriter->startElement('Properties');
$objWriter->writeAttribute('xmlns', 'http://schemas.openxmlformats.org/officeDocument/2006/custom-properties');
$objWriter->writeAttribute('xmlns:vt', 'http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes');
$objWriter->writeAttribute('xmlns', Namespaces::CUSTOM_PROPERTIES);
$objWriter->writeAttribute('xmlns:vt', Namespaces::PROPERTIES_VTYPES);
foreach ($customPropertyList as $key => $customProperty) {
$propertyValue = $spreadsheet->getProperties()->getCustomPropertyValue($customProperty);
@@ -201,25 +208,26 @@ class DocProps extends WriterPart
$objWriter->startElement('property');
$objWriter->writeAttribute('fmtid', '{D5CDD505-2E9C-101B-9397-08002B2CF9AE}');
$objWriter->writeAttribute('pid', $key + 2);
$objWriter->writeAttribute('pid', (string) ($key + 2));
$objWriter->writeAttribute('name', $customProperty);
switch ($propertyType) {
case 'i':
case Properties::PROPERTY_TYPE_INTEGER:
$objWriter->writeElement('vt:i4', $propertyValue);
break;
case 'f':
$objWriter->writeElement('vt:r8', $propertyValue);
case Properties::PROPERTY_TYPE_FLOAT:
$objWriter->writeElement('vt:r8', sprintf('%F', $propertyValue));
break;
case 'b':
case Properties::PROPERTY_TYPE_BOOLEAN:
$objWriter->writeElement('vt:bool', ($propertyValue) ? 'true' : 'false');
break;
case 'd':
case Properties::PROPERTY_TYPE_DATE:
$objWriter->startElement('vt:filetime');
$objWriter->writeRawData(date(DATE_W3C, $propertyValue));
$date = Date::dateTimeFromTimestamp("$propertyValue");
$objWriter->writeRawData($date->format(DATE_W3C));
$objWriter->endElement();
break;

View File

@@ -3,6 +3,8 @@
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;
@@ -18,7 +20,7 @@ class Drawing extends WriterPart
*
* @return string XML Output
*/
public function writeDrawings(\PhpOffice\PhpSpreadsheet\Worksheet\Worksheet $pWorksheet, $includeCharts = false)
public function writeDrawings(\PhpOffice\PhpSpreadsheet\Worksheet\Worksheet $worksheet, $includeCharts = false)
{
// Create XML writer
$objWriter = null;
@@ -33,12 +35,12 @@ class Drawing extends WriterPart
// xdr:wsDr
$objWriter->startElement('xdr:wsDr');
$objWriter->writeAttribute('xmlns:xdr', 'http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing');
$objWriter->writeAttribute('xmlns:a', 'http://schemas.openxmlformats.org/drawingml/2006/main');
$objWriter->writeAttribute('xmlns:xdr', Namespaces::SPREADSHEET_DRAWING);
$objWriter->writeAttribute('xmlns:a', Namespaces::DRAWINGML);
// Loop through images and write drawings
$i = 1;
$iterator = $pWorksheet->getDrawingCollection()->getIterator();
$iterator = $worksheet->getDrawingCollection()->getIterator();
while ($iterator->valid()) {
/** @var BaseDrawing $pDrawing */
$pDrawing = $iterator->current();
@@ -52,19 +54,22 @@ class Drawing extends WriterPart
}
if ($includeCharts) {
$chartCount = $pWorksheet->getChartCount();
$chartCount = $worksheet->getChartCount();
// Loop through charts and write the chart position
if ($chartCount > 0) {
for ($c = 0; $c < $chartCount; ++$c) {
$this->writeChart($objWriter, $pWorksheet->getChartByIndex($c), $c + $i);
$chart = $worksheet->getChartByIndex((string) $c);
if ($chart !== false) {
$this->writeChart($objWriter, $chart, $c + $i);
}
}
}
}
// unparsed AlternateContent
$unparsedLoadedData = $pWorksheet->getParent()->getUnparsedLoadedData();
if (isset($unparsedLoadedData['sheets'][$pWorksheet->getCodeName()]['drawingAlternateContents'])) {
foreach ($unparsedLoadedData['sheets'][$pWorksheet->getCodeName()]['drawingAlternateContents'] as $drawingAlternateContent) {
$unparsedLoadedData = $worksheet->getParent()->getUnparsedLoadedData();
if (isset($unparsedLoadedData['sheets'][$worksheet->getCodeName()]['drawingAlternateContents'])) {
foreach ($unparsedLoadedData['sheets'][$worksheet->getCodeName()]['drawingAlternateContents'] as $drawingAlternateContent) {
$objWriter->writeRaw($drawingAlternateContent);
}
}
@@ -78,37 +83,63 @@ class Drawing extends WriterPart
/**
* Write drawings to XML format.
*
* @param XMLWriter $objWriter XML Writer
* @param int $pRelationId
* @param int $relationId
*/
public function writeChart(XMLWriter $objWriter, \PhpOffice\PhpSpreadsheet\Chart\Chart $pChart, $pRelationId = -1): void
public function writeChart(XMLWriter $objWriter, \PhpOffice\PhpSpreadsheet\Chart\Chart $chart, $relationId = -1): void
{
$tl = $pChart->getTopLeftPosition();
$tl['colRow'] = Coordinate::coordinateFromString($tl['cell']);
$br = $pChart->getBottomRightPosition();
$br['colRow'] = Coordinate::coordinateFromString($br['cell']);
$tl = $chart->getTopLeftPosition();
$tlColRow = Coordinate::indexesFromString($tl['cell']);
$br = $chart->getBottomRightPosition();
$objWriter->startElement('xdr:twoCellAnchor');
$isTwoCellAnchor = $br['cell'] !== '';
if ($isTwoCellAnchor) {
$brColRow = Coordinate::indexesFromString($br['cell']);
$objWriter->startElement('xdr:from');
$objWriter->writeElement('xdr:col', Coordinate::columnIndexFromString($tl['colRow'][0]) - 1);
$objWriter->writeElement('xdr:colOff', \PhpOffice\PhpSpreadsheet\Shared\Drawing::pixelsToEMU($tl['xOffset']));
$objWriter->writeElement('xdr:row', $tl['colRow'][1] - 1);
$objWriter->writeElement('xdr:rowOff', \PhpOffice\PhpSpreadsheet\Shared\Drawing::pixelsToEMU($tl['yOffset']));
$objWriter->endElement();
$objWriter->startElement('xdr:to');
$objWriter->writeElement('xdr:col', Coordinate::columnIndexFromString($br['colRow'][0]) - 1);
$objWriter->writeElement('xdr:colOff', \PhpOffice\PhpSpreadsheet\Shared\Drawing::pixelsToEMU($br['xOffset']));
$objWriter->writeElement('xdr:row', $br['colRow'][1] - 1);
$objWriter->writeElement('xdr:rowOff', \PhpOffice\PhpSpreadsheet\Shared\Drawing::pixelsToEMU($br['yOffset']));
$objWriter->endElement();
$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 ' . $pRelationId);
$objWriter->writeAttribute('id', 1025 * $pRelationId);
$objWriter->writeAttribute('name', 'Chart ' . $relationId);
$objWriter->writeAttribute('id', (string) (1025 * $relationId));
$objWriter->endElement();
$objWriter->startElement('xdr:cNvGraphicFramePr');
$objWriter->startElement('a:graphicFrameLocks');
@@ -129,11 +160,11 @@ class Drawing extends WriterPart
$objWriter->startElement('a:graphic');
$objWriter->startElement('a:graphicData');
$objWriter->writeAttribute('uri', 'http://schemas.openxmlformats.org/drawingml/2006/chart');
$objWriter->writeAttribute('uri', Namespaces::CHART);
$objWriter->startElement('c:chart');
$objWriter->writeAttribute('xmlns:c', 'http://schemas.openxmlformats.org/drawingml/2006/chart');
$objWriter->writeAttribute('xmlns:r', 'http://schemas.openxmlformats.org/officeDocument/2006/relationships');
$objWriter->writeAttribute('r:id', 'rId' . $pRelationId);
$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();
@@ -148,32 +179,58 @@ class Drawing extends WriterPart
/**
* Write drawings to XML format.
*
* @param XMLWriter $objWriter XML Writer
* @param int $pRelationId
* @param int $relationId
* @param null|int $hlinkClickId
*/
public function writeDrawing(XMLWriter $objWriter, BaseDrawing $pDrawing, $pRelationId = -1, $hlinkClickId = null): void
public function writeDrawing(XMLWriter $objWriter, BaseDrawing $drawing, $relationId = -1, $hlinkClickId = null): void
{
if ($pRelationId >= 0) {
// xdr:oneCellAnchor
$objWriter->startElement('xdr:oneCellAnchor');
// Image location
$aCoordinates = Coordinate::coordinateFromString($pDrawing->getCoordinates());
$aCoordinates[0] = Coordinate::columnIndexFromString($aCoordinates[0]);
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', $aCoordinates[0] - 1);
$objWriter->writeElement('xdr:colOff', \PhpOffice\PhpSpreadsheet\Shared\Drawing::pixelsToEMU($pDrawing->getOffsetX()));
$objWriter->writeElement('xdr:row', $aCoordinates[1] - 1);
$objWriter->writeElement('xdr:rowOff', \PhpOffice\PhpSpreadsheet\Shared\Drawing::pixelsToEMU($pDrawing->getOffsetY()));
$objWriter->endElement();
// 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', \PhpOffice\PhpSpreadsheet\Shared\Drawing::pixelsToEMU($pDrawing->getWidth()));
$objWriter->writeAttribute('cy', \PhpOffice\PhpSpreadsheet\Shared\Drawing::pixelsToEMU($pDrawing->getHeight()));
$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');
@@ -183,9 +240,9 @@ class Drawing extends WriterPart
// xdr:cNvPr
$objWriter->startElement('xdr:cNvPr');
$objWriter->writeAttribute('id', $pRelationId);
$objWriter->writeAttribute('name', $pDrawing->getName());
$objWriter->writeAttribute('descr', $pDrawing->getDescription());
$objWriter->writeAttribute('id', (string) $relationId);
$objWriter->writeAttribute('name', $drawing->getName());
$objWriter->writeAttribute('descr', $drawing->getDescription());
//a:hlinkClick
$this->writeHyperLinkDrawing($objWriter, $hlinkClickId);
@@ -209,8 +266,8 @@ class Drawing extends WriterPart
// a:blip
$objWriter->startElement('a:blip');
$objWriter->writeAttribute('xmlns:r', 'http://schemas.openxmlformats.org/officeDocument/2006/relationships');
$objWriter->writeAttribute('r:embed', 'rId' . $pRelationId);
$objWriter->writeAttribute('xmlns:r', Namespaces::SCHEMA_OFFICE_DOCUMENT);
$objWriter->writeAttribute('r:embed', 'rId' . $relationId);
$objWriter->endElement();
// a:stretch
@@ -225,7 +282,13 @@ class Drawing extends WriterPart
// a:xfrm
$objWriter->startElement('a:xfrm');
$objWriter->writeAttribute('rot', \PhpOffice\PhpSpreadsheet\Shared\Drawing::degreesToAngle($pDrawing->getRotation()));
$objWriter->writeAttribute('rot', (string) SharedDrawing::degreesToAngle($drawing->getRotation()));
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
@@ -237,25 +300,25 @@ class Drawing extends WriterPart
$objWriter->endElement();
if ($pDrawing->getShadow()->getVisible()) {
if ($drawing->getShadow()->getVisible()) {
// a:effectLst
$objWriter->startElement('a:effectLst');
// a:outerShdw
$objWriter->startElement('a:outerShdw');
$objWriter->writeAttribute('blurRad', \PhpOffice\PhpSpreadsheet\Shared\Drawing::pixelsToEMU($pDrawing->getShadow()->getBlurRadius()));
$objWriter->writeAttribute('dist', \PhpOffice\PhpSpreadsheet\Shared\Drawing::pixelsToEMU($pDrawing->getShadow()->getDistance()));
$objWriter->writeAttribute('dir', \PhpOffice\PhpSpreadsheet\Shared\Drawing::degreesToAngle($pDrawing->getShadow()->getDirection()));
$objWriter->writeAttribute('algn', $pDrawing->getShadow()->getAlignment());
$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', $pDrawing->getShadow()->getColor()->getRGB());
$objWriter->writeAttribute('val', $drawing->getShadow()->getColor()->getRGB());
// a:alpha
$objWriter->startElement('a:alpha');
$objWriter->writeAttribute('val', $pDrawing->getShadow()->getAlpha() * 1000);
$objWriter->writeAttribute('val', (string) ($drawing->getShadow()->getAlpha() * 1000));
$objWriter->endElement();
$objWriter->endElement();
@@ -282,7 +345,7 @@ class Drawing extends WriterPart
*
* @return string XML Output
*/
public function writeVMLHeaderFooterImages(\PhpOffice\PhpSpreadsheet\Worksheet\Worksheet $pWorksheet)
public function writeVMLHeaderFooterImages(\PhpOffice\PhpSpreadsheet\Worksheet\Worksheet $worksheet)
{
// Create XML writer
$objWriter = null;
@@ -296,13 +359,13 @@ class Drawing extends WriterPart
$objWriter->startDocument('1.0', 'UTF-8', 'yes');
// Header/footer images
$images = $pWorksheet->getHeaderFooter()->getImages();
$images = $worksheet->getHeaderFooter()->getImages();
// xml
$objWriter->startElement('xml');
$objWriter->writeAttribute('xmlns:v', 'urn:schemas-microsoft-com:vml');
$objWriter->writeAttribute('xmlns:o', 'urn:schemas-microsoft-com:office:office');
$objWriter->writeAttribute('xmlns:x', 'urn:schemas-microsoft-com:office:excel');
$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');
@@ -425,33 +488,31 @@ class Drawing extends WriterPart
/**
* Write VML comment to XML format.
*
* @param XMLWriter $objWriter XML Writer
* @param string $pReference Reference
* @param HeaderFooterDrawing $pImage Image
* @param string $reference Reference
*/
private function writeVMLHeaderFooterImage(XMLWriter $objWriter, $pReference, HeaderFooterDrawing $pImage): void
private function writeVMLHeaderFooterImage(XMLWriter $objWriter, $reference, HeaderFooterDrawing $image): void
{
// Calculate object id
preg_match('{(\d+)}', md5($pReference), $m);
$id = 1500 + (substr($m[1], 0, 2) * 1);
preg_match('{(\d+)}', md5($reference), $m);
$id = 1500 + ((int) substr($m[1], 0, 2) * 1);
// Calculate offset
$width = $pImage->getWidth();
$height = $pImage->getHeight();
$marginLeft = $pImage->getOffsetX();
$marginTop = $pImage->getOffsetY();
$width = $image->getWidth();
$height = $image->getHeight();
$marginLeft = $image->getOffsetX();
$marginTop = $image->getOffsetY();
// v:shape
$objWriter->startElement('v:shape');
$objWriter->writeAttribute('id', $pReference);
$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' . $pReference);
$objWriter->writeAttribute('o:title', $pImage->getName());
$objWriter->writeAttribute('o:relid', 'rId' . $reference);
$objWriter->writeAttribute('o:title', $image->getName());
$objWriter->endElement();
// o:lock
@@ -466,7 +527,7 @@ class Drawing extends WriterPart
/**
* Get an array of all drawings.
*
* @return \PhpOffice\PhpSpreadsheet\Worksheet\Drawing[] All drawings in PhpSpreadsheet
* @return BaseDrawing[] All drawings in PhpSpreadsheet
*/
public function allDrawings(Spreadsheet $spreadsheet)
{
@@ -498,8 +559,13 @@ class Drawing extends WriterPart
}
$objWriter->startElement('a:hlinkClick');
$objWriter->writeAttribute('xmlns:r', 'http://schemas.openxmlformats.org/officeDocument/2006/relationships');
$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);
}
}

View File

@@ -2,14 +2,15 @@
namespace PhpOffice\PhpSpreadsheet\Writer\Xlsx;
class Xlfn
class FunctionPrefix
{
const XLFNREGEXP = '/(?<!_xlfn[.])\\b('
const XLFNREGEXP = '/(?:_xlfn\.)?((?:_xlws\.)?('
// functions added with Excel 2010
. 'beta[.]dist'
. '|beta[.]inv'
. '|binom[.]dist'
. '|binom[.]inv'
. '|ceiling[.]precise'
. '|chisq[.]dist'
. '|chisq[.]dist[.]rt'
. '|chisq[.]inv'
@@ -27,6 +28,7 @@ class Xlfn
. '|f[.]inv'
. '|f[.]inv[.]rt'
. '|f[.]test'
. '|floor[.]precise'
. '|gamma[.]dist'
. '|gamma[.]inv'
. '|gammaln[.]precise'
@@ -132,28 +134,61 @@ class Xlfn
// functions added with Excel 365
. '|filter'
. '|randarray'
. '|anchorarray'
. '|sequence'
. '|sort'
. '|sortby'
. '|unique'
. '|xlookup'
. '|xmatch'
. ')(?=\\s*[(])/i';
. '|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.
*/
public static function addXlfn(string $funcstring): string
protected static function addXlfnPrefix(string $functionString): string
{
return preg_replace(self::XLFNREGEXP, '_xlfn.$1', $funcstring);
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 addXlfnStripEquals(string $funcstring): string
public static function addFunctionPrefixStripEquals(string $functionString): string
{
return self::addXlfn(substr($funcstring, 1));
return self::addFunctionPrefix(substr($functionString, 1));
}
}

View File

@@ -2,8 +2,10 @@
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;
@@ -29,7 +31,7 @@ class Rels extends WriterPart
// Relationships
$objWriter->startElement('Relationships');
$objWriter->writeAttribute('xmlns', 'http://schemas.openxmlformats.org/package/2006/relationships');
$objWriter->writeAttribute('xmlns', Namespaces::RELATIONSHIPS);
$customPropertyList = $spreadsheet->getProperties()->getCustomProperties();
if (!empty($customPropertyList)) {
@@ -37,7 +39,7 @@ class Rels extends WriterPart
$this->writeRelationship(
$objWriter,
4,
'http://schemas.openxmlformats.org/officeDocument/2006/relationships/custom-properties',
Namespaces::RELATIONSHIPS_CUSTOM_PROPERTIES,
'docProps/custom.xml'
);
}
@@ -46,7 +48,7 @@ class Rels extends WriterPart
$this->writeRelationship(
$objWriter,
3,
'http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties',
Namespaces::RELATIONSHIPS_EXTENDED_PROPERTIES,
'docProps/app.xml'
);
@@ -54,7 +56,7 @@ class Rels extends WriterPart
$this->writeRelationship(
$objWriter,
2,
'http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties',
Namespaces::CORE_PROPERTIES,
'docProps/core.xml'
);
@@ -62,16 +64,17 @@ class Rels extends WriterPart
$this->writeRelationship(
$objWriter,
1,
'http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument',
Namespaces::OFFICE_DOCUMENT,
'xl/workbook.xml'
);
// a custom UI in workbook ?
$target = $spreadsheet->getRibbonXMLData('target');
if ($spreadsheet->hasRibbon()) {
$this->writeRelationShip(
$objWriter,
5,
'http://schemas.microsoft.com/office/2006/relationships/ui/extensibility',
$spreadsheet->getRibbonXMLData('target')
Namespaces::EXTENSIBILITY,
is_string($target) ? $target : ''
);
}
@@ -100,13 +103,13 @@ class Rels extends WriterPart
// Relationships
$objWriter->startElement('Relationships');
$objWriter->writeAttribute('xmlns', 'http://schemas.openxmlformats.org/package/2006/relationships');
$objWriter->writeAttribute('xmlns', Namespaces::RELATIONSHIPS);
// Relationship styles.xml
$this->writeRelationship(
$objWriter,
1,
'http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles',
Namespaces::STYLES,
'styles.xml'
);
@@ -114,7 +117,7 @@ class Rels extends WriterPart
$this->writeRelationship(
$objWriter,
2,
'http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme',
Namespaces::THEME2,
'theme/theme1.xml'
);
@@ -122,7 +125,7 @@ class Rels extends WriterPart
$this->writeRelationship(
$objWriter,
3,
'http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings',
Namespaces::SHARED_STRINGS,
'sharedStrings.xml'
);
@@ -132,7 +135,7 @@ class Rels extends WriterPart
$this->writeRelationship(
$objWriter,
($i + 1 + 3),
'http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet',
Namespaces::WORKSHEET,
'worksheets/sheet' . ($i + 1) . '.xml'
);
}
@@ -142,7 +145,7 @@ class Rels extends WriterPart
$this->writeRelationShip(
$objWriter,
($i + 1 + 3),
'http://schemas.microsoft.com/office/2006/relationships/vbaProject',
Namespaces::VBA,
'vbaProject.bin'
);
++$i; //increment i if needed for an another relation
@@ -160,12 +163,13 @@ class Rels extends WriterPart
* rId1 - Drawings
* rId_hyperlink_x - Hyperlinks
*
* @param int $pWorksheetId
* @param int $worksheetId
* @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 $pWorksheet, $pWorksheetId = 1, $includeCharts = false)
public function writeWorksheetRelationships(\PhpOffice\PhpSpreadsheet\Worksheet\Worksheet $worksheet, $worksheetId = 1, $includeCharts = false, $tableRef = 1)
{
// Create XML writer
$objWriter = null;
@@ -180,22 +184,22 @@ class Rels extends WriterPart
// Relationships
$objWriter->startElement('Relationships');
$objWriter->writeAttribute('xmlns', 'http://schemas.openxmlformats.org/package/2006/relationships');
$objWriter->writeAttribute('xmlns', Namespaces::RELATIONSHIPS);
// Write drawing relationships?
$drawingOriginalIds = [];
$unparsedLoadedData = $pWorksheet->getParent()->getUnparsedLoadedData();
if (isset($unparsedLoadedData['sheets'][$pWorksheet->getCodeName()]['drawingOriginalIds'])) {
$drawingOriginalIds = $unparsedLoadedData['sheets'][$pWorksheet->getCodeName()]['drawingOriginalIds'];
$unparsedLoadedData = $worksheet->getParent()->getUnparsedLoadedData();
if (isset($unparsedLoadedData['sheets'][$worksheet->getCodeName()]['drawingOriginalIds'])) {
$drawingOriginalIds = $unparsedLoadedData['sheets'][$worksheet->getCodeName()]['drawingOriginalIds'];
}
if ($includeCharts) {
$charts = $pWorksheet->getChartCollection();
$charts = $worksheet->getChartCollection();
} else {
$charts = [];
}
if (($pWorksheet->getDrawingCollection()->count() > 0) || (count($charts) > 0) || $drawingOriginalIds) {
if (($worksheet->getDrawingCollection()->count() > 0) || (count($charts) > 0) || $drawingOriginalIds) {
$rId = 1;
// Use original $relPath to get original $rId.
@@ -208,23 +212,23 @@ class Rels extends WriterPart
}
// Generate new $relPath to write drawing relationship
$relPath = '../drawings/drawing' . $pWorksheetId . '.xml';
$relPath = '../drawings/drawing' . $worksheetId . '.xml';
$this->writeRelationship(
$objWriter,
$rId,
'http://schemas.openxmlformats.org/officeDocument/2006/relationships/drawing',
Namespaces::RELATIONSHIPS_DRAWING,
$relPath
);
}
// Write hyperlink relationships?
$i = 1;
foreach ($pWorksheet->getHyperlinkCollection() as $hyperlink) {
foreach ($worksheet->getHyperlinkCollection() as $hyperlink) {
if (!$hyperlink->isInternal()) {
$this->writeRelationship(
$objWriter,
'_hyperlink_' . $i,
'http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink',
Namespaces::HYPERLINK,
$hyperlink->getUrl(),
'External'
);
@@ -235,68 +239,81 @@ class Rels extends WriterPart
// Write comments relationship?
$i = 1;
if (count($pWorksheet->getComments()) > 0) {
if (count($worksheet->getComments()) > 0) {
$this->writeRelationship(
$objWriter,
'_comments_vml' . $i,
'http://schemas.openxmlformats.org/officeDocument/2006/relationships/vmlDrawing',
'../drawings/vmlDrawing' . $pWorksheetId . '.vml'
Namespaces::VML,
'../drawings/vmlDrawing' . $worksheetId . '.vml'
);
$this->writeRelationship(
$objWriter,
'_comments' . $i,
'http://schemas.openxmlformats.org/officeDocument/2006/relationships/comments',
'../comments' . $pWorksheetId . '.xml'
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($pWorksheet->getHeaderFooter()->getImages()) > 0) {
if (count($worksheet->getHeaderFooter()->getImages()) > 0) {
$this->writeRelationship(
$objWriter,
'_headerfooter_vml' . $i,
'http://schemas.openxmlformats.org/officeDocument/2006/relationships/vmlDrawing',
'../drawings/vmlDrawingHF' . $pWorksheetId . '.vml'
Namespaces::VML,
'../drawings/vmlDrawingHF' . $worksheetId . '.vml'
);
}
$this->writeUnparsedRelationship($pWorksheet, $objWriter, 'ctrlProps', 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/ctrlProp');
$this->writeUnparsedRelationship($pWorksheet, $objWriter, 'vmlDrawings', 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/vmlDrawing');
$this->writeUnparsedRelationship($pWorksheet, $objWriter, 'printerSettings', 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/printerSettings');
$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 $pWorksheet, XMLWriter $objWriter, $relationship, $type): void
private function writeUnparsedRelationship(\PhpOffice\PhpSpreadsheet\Worksheet\Worksheet $worksheet, XMLWriter $objWriter, string $relationship, string $type): void
{
$unparsedLoadedData = $pWorksheet->getParent()->getUnparsedLoadedData();
if (!isset($unparsedLoadedData['sheets'][$pWorksheet->getCodeName()][$relationship])) {
$unparsedLoadedData = $worksheet->getParent()->getUnparsedLoadedData();
if (!isset($unparsedLoadedData['sheets'][$worksheet->getCodeName()][$relationship])) {
return;
}
foreach ($unparsedLoadedData['sheets'][$pWorksheet->getCodeName()][$relationship] as $rId => $value) {
$this->writeRelationship(
$objWriter,
$rId,
$type,
$value['relFilePath']
);
foreach ($unparsedLoadedData['sheets'][$worksheet->getCodeName()][$relationship] as $rId => $value) {
if (substr($rId, 0, 17) !== '_headerfooter_vml') {
$this->writeRelationship(
$objWriter,
$rId,
$type,
$value['relFilePath']
);
}
}
}
/**
* Write drawing relationships to XML format.
*
* @param int &$chartRef Chart ID
* @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 $pWorksheet, &$chartRef, $includeCharts = false)
public function writeDrawingRelationships(\PhpOffice\PhpSpreadsheet\Worksheet\Worksheet $worksheet, &$chartRef, $includeCharts = false)
{
// Create XML writer
$objWriter = null;
@@ -311,24 +328,23 @@ class Rels extends WriterPart
// Relationships
$objWriter->startElement('Relationships');
$objWriter->writeAttribute('xmlns', 'http://schemas.openxmlformats.org/package/2006/relationships');
$objWriter->writeAttribute('xmlns', Namespaces::RELATIONSHIPS);
// Loop through images and write relationships
$i = 1;
$iterator = $pWorksheet->getDrawingCollection()->getIterator();
$iterator = $worksheet->getDrawingCollection()->getIterator();
while ($iterator->valid()) {
$drawing = $iterator->current();
if (
$iterator->current() instanceof \PhpOffice\PhpSpreadsheet\Worksheet\Drawing
|| $iterator->current() instanceof MemoryDrawing
$drawing instanceof \PhpOffice\PhpSpreadsheet\Worksheet\Drawing
|| $drawing instanceof MemoryDrawing
) {
// Write relationship for image drawing
/** @var \PhpOffice\PhpSpreadsheet\Worksheet\Drawing $drawing */
$drawing = $iterator->current();
$this->writeRelationship(
$objWriter,
$i,
'http://schemas.openxmlformats.org/officeDocument/2006/relationships/image',
'../media/' . str_replace(' ', '', $drawing->getIndexedFilename())
Namespaces::IMAGE,
'../media/' . $drawing->getIndexedFilename()
);
$i = $this->writeDrawingHyperLink($objWriter, $drawing, $i);
@@ -340,13 +356,13 @@ class Rels extends WriterPart
if ($includeCharts) {
// Loop through charts and write relationships
$chartCount = $pWorksheet->getChartCount();
$chartCount = $worksheet->getChartCount();
if ($chartCount > 0) {
for ($c = 0; $c < $chartCount; ++$c) {
$this->writeRelationship(
$objWriter,
$i++,
'http://schemas.openxmlformats.org/officeDocument/2006/relationships/chart',
Namespaces::RELATIONSHIPS_CHART,
'../charts/chart' . ++$chartRef . '.xml'
);
}
@@ -363,7 +379,7 @@ class Rels extends WriterPart
*
* @return string XML Output
*/
public function writeHeaderFooterDrawingRelationships(\PhpOffice\PhpSpreadsheet\Worksheet\Worksheet $pWorksheet)
public function writeHeaderFooterDrawingRelationships(\PhpOffice\PhpSpreadsheet\Worksheet\Worksheet $worksheet)
{
// Create XML writer
$objWriter = null;
@@ -378,15 +394,15 @@ class Rels extends WriterPart
// Relationships
$objWriter->startElement('Relationships');
$objWriter->writeAttribute('xmlns', 'http://schemas.openxmlformats.org/package/2006/relationships');
$objWriter->writeAttribute('xmlns', Namespaces::RELATIONSHIPS);
// Loop through images and write relationships
foreach ($pWorksheet->getHeaderFooter()->getImages() as $key => $value) {
foreach ($worksheet->getHeaderFooter()->getImages() as $key => $value) {
// Write relationship for image drawing
$this->writeRelationship(
$objWriter,
$key,
'http://schemas.openxmlformats.org/officeDocument/2006/relationships/image',
Namespaces::IMAGE,
'../media/' . $value->getIndexedFilename()
);
}
@@ -396,26 +412,62 @@ class Rels extends WriterPart
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 XMLWriter $objWriter XML Writer
* @param int $pId Relationship ID. rId will be prepended!
* @param string $pType Relationship type
* @param string $pTarget Relationship target
* @param string $pTargetMode Relationship target mode
* @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, $pId, $pType, $pTarget, $pTargetMode = ''): void
private function writeRelationship(XMLWriter $objWriter, $id, $type, $target, $targetMode = ''): void
{
if ($pType != '' && $pTarget != '') {
if ($type != '' && $target != '') {
// Write relationship
$objWriter->startElement('Relationship');
$objWriter->writeAttribute('Id', 'rId' . $pId);
$objWriter->writeAttribute('Type', $pType);
$objWriter->writeAttribute('Target', $pTarget);
$objWriter->writeAttribute('Id', 'rId' . $id);
$objWriter->writeAttribute('Type', $type);
$objWriter->writeAttribute('Target', $target);
if ($pTargetMode != '') {
$objWriter->writeAttribute('TargetMode', $pTargetMode);
if ($targetMode != '') {
$objWriter->writeAttribute('TargetMode', $targetMode);
}
$objWriter->endElement();
@@ -424,14 +476,7 @@ class Rels extends WriterPart
}
}
/**
* @param $objWriter
* @param \PhpOffice\PhpSpreadsheet\Worksheet\Drawing $drawing
* @param $i
*
* @return int
*/
private function writeDrawingHyperLink($objWriter, $drawing, $i)
private function writeDrawingHyperLink(XMLWriter $objWriter, BaseDrawing $drawing, int $i): int
{
if ($drawing->getHyperlink() === null) {
return $i;
@@ -441,7 +486,7 @@ class Rels extends WriterPart
$this->writeRelationship(
$objWriter,
$i,
'http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink',
Namespaces::HYPERLINK,
$drawing->getHyperlink()->getUrl(),
$drawing->getHyperlink()->getTypeHyperlink()
);

View File

@@ -2,6 +2,7 @@
namespace PhpOffice\PhpSpreadsheet\Writer\Xlsx;
use PhpOffice\PhpSpreadsheet\Reader\Xlsx\Namespaces;
use PhpOffice\PhpSpreadsheet\Shared\XMLWriter;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
@@ -27,13 +28,13 @@ class RelsRibbon extends WriterPart
// Relationships
$objWriter->startElement('Relationships');
$objWriter->writeAttribute('xmlns', 'http://schemas.openxmlformats.org/package/2006/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', 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/image');
$objWriter->writeAttribute('Type', Namespaces::IMAGE);
$objWriter->writeAttribute('Target', $aTarget);
$objWriter->endElement();
}

View File

@@ -2,8 +2,8 @@
namespace PhpOffice\PhpSpreadsheet\Writer\Xlsx;
use PhpOffice\PhpSpreadsheet\Reader\Xlsx\Namespaces;
use PhpOffice\PhpSpreadsheet\Shared\XMLWriter;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
class RelsVBA extends WriterPart
{
@@ -12,7 +12,7 @@ class RelsVBA extends WriterPart
*
* @return string XML Output
*/
public function writeVBARelationships(Spreadsheet $spreadsheet)
public function writeVBARelationships()
{
// Create XML writer
$objWriter = null;
@@ -27,10 +27,10 @@ class RelsVBA extends WriterPart
// Relationships
$objWriter->startElement('Relationships');
$objWriter->writeAttribute('xmlns', 'http://schemas.openxmlformats.org/package/2006/relationships');
$objWriter->writeAttribute('xmlns', Namespaces::RELATIONSHIPS);
$objWriter->startElement('Relationship');
$objWriter->writeAttribute('Id', 'rId1');
$objWriter->writeAttribute('Type', 'http://schemas.microsoft.com/office/2006/relationships/vbaProjectSignature');
$objWriter->writeAttribute('Type', Namespaces::VBA_SIGNATURE);
$objWriter->writeAttribute('Target', 'vbaProjectSignature.bin');
$objWriter->endElement();
$objWriter->endElement();

View File

@@ -2,48 +2,49 @@
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;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet as ActualWorksheet;
class StringTable extends WriterPart
{
/**
* Create worksheet stringtable.
*
* @param Worksheet $pSheet Worksheet
* @param string[] $pExistingTable Existing table to eventually merge with
* @param string[] $existingTable Existing table to eventually merge with
*
* @return string[] String table for worksheet
*/
public function createStringTable(Worksheet $pSheet, $pExistingTable = null)
public function createStringTable(ActualWorksheet $worksheet, $existingTable = null)
{
// Create string lookup table
$aStringTable = [];
$cellCollection = null;
$aFlippedStringTable = null; // For faster lookup
// Is an existing table given?
if (($pExistingTable !== null) && is_array($pExistingTable)) {
$aStringTable = $pExistingTable;
if (($existingTable !== null) && is_array($existingTable)) {
$aStringTable = $existingTable;
}
// Fill index array
$aFlippedStringTable = $this->flipStringTable($aStringTable);
// Loop through cells
foreach ($pSheet->getCoordinates() as $coordinate) {
$cell = $pSheet->getCell($coordinate);
foreach ($worksheet->getCellCollection()->getCoordinates() as $coordinate) {
/** @var Cell $cell */
$cell = $worksheet->getCellCollection()->get($coordinate);
$cellValue = $cell->getValue();
if (
!is_object($cellValue) &&
($cellValue !== null) &&
$cellValue !== '' &&
!isset($aFlippedStringTable[$cellValue]) &&
($cell->getDataType() == DataType::TYPE_STRING || $cell->getDataType() == DataType::TYPE_STRING2 || $cell->getDataType() == DataType::TYPE_NULL)
($cell->getDataType() == DataType::TYPE_STRING || $cell->getDataType() == DataType::TYPE_STRING2 || $cell->getDataType() == DataType::TYPE_NULL) &&
!isset($aFlippedStringTable[$cellValue])
) {
$aStringTable[] = $cellValue;
$aFlippedStringTable[$cellValue] = true;
@@ -63,11 +64,11 @@ class StringTable extends WriterPart
/**
* Write string table to XML format.
*
* @param string[] $pStringTable
* @param (string|RichText)[] $stringTable
*
* @return string XML Output
*/
public function writeStringTable(array $pStringTable)
public function writeStringTable(array $stringTable)
{
// Create XML writer
$objWriter = null;
@@ -82,14 +83,14 @@ class StringTable extends WriterPart
// String table
$objWriter->startElement('sst');
$objWriter->writeAttribute('xmlns', 'http://schemas.openxmlformats.org/spreadsheetml/2006/main');
$objWriter->writeAttribute('uniqueCount', count($pStringTable));
$objWriter->writeAttribute('xmlns', Namespaces::MAIN);
$objWriter->writeAttribute('uniqueCount', (string) count($stringTable));
// Loop through string table
foreach ($pStringTable as $textElement) {
foreach ($stringTable as $textElement) {
$objWriter->startElement('si');
if (!$textElement instanceof RichText) {
if (!($textElement instanceof RichText)) {
$textToWrite = StringHelper::controlCharacterPHP2OOXML($textElement);
$objWriter->startElement('t');
if ($textToWrite !== trim($textToWrite)) {
@@ -97,7 +98,7 @@ class StringTable extends WriterPart
}
$objWriter->writeRawData($textToWrite);
$objWriter->endElement();
} elseif ($textElement instanceof RichText) {
} else {
$this->writeRichText($objWriter, $textElement);
}
@@ -112,31 +113,31 @@ class StringTable extends WriterPart
/**
* Write Rich Text.
*
* @param XMLWriter $objWriter XML Writer
* @param RichText $pRichText Rich text
* @param string $prefix Optional Namespace prefix
*/
public function writeRichText(XMLWriter $objWriter, RichText $pRichText, $prefix = null): void
public function writeRichText(XMLWriter $objWriter, RichText $richText, $prefix = null): void
{
if ($prefix !== null) {
$prefix .= ':';
}
// Loop through rich text elements
$elements = $pRichText->getRichTextElements();
$elements = $richText->getRichTextElements();
foreach ($elements as $element) {
// r
$objWriter->startElement($prefix . 'r');
// rPr
if ($element instanceof Run) {
if ($element instanceof Run && $element->getFont() !== null) {
// rPr
$objWriter->startElement($prefix . 'rPr');
// rFont
$objWriter->startElement($prefix . 'rFont');
$objWriter->writeAttribute('val', $element->getFont()->getName());
$objWriter->endElement();
if ($element->getFont()->getName() !== null) {
$objWriter->startElement($prefix . 'rFont');
$objWriter->writeAttribute('val', $element->getFont()->getName());
$objWriter->endElement();
}
// Bold
$objWriter->startElement($prefix . 'b');
@@ -165,19 +166,25 @@ class StringTable extends WriterPart
$objWriter->endElement();
// Color
$objWriter->startElement($prefix . 'color');
$objWriter->writeAttribute('rgb', $element->getFont()->getColor()->getARGB());
$objWriter->endElement();
if ($element->getFont()->getColor()->getARGB() !== null) {
$objWriter->startElement($prefix . 'color');
$objWriter->writeAttribute('rgb', $element->getFont()->getColor()->getARGB());
$objWriter->endElement();
}
// Size
$objWriter->startElement($prefix . 'sz');
$objWriter->writeAttribute('val', $element->getFont()->getSize());
$objWriter->endElement();
if ($element->getFont()->getSize() !== null) {
$objWriter->startElement($prefix . 'sz');
$objWriter->writeAttribute('val', (string) $element->getFont()->getSize());
$objWriter->endElement();
}
// Underline
$objWriter->startElement($prefix . 'u');
$objWriter->writeAttribute('val', $element->getFont()->getUnderline());
$objWriter->endElement();
if ($element->getFont()->getUnderline() !== null) {
$objWriter->startElement($prefix . 'u');
$objWriter->writeAttribute('val', $element->getFont()->getUnderline());
$objWriter->endElement();
}
$objWriter->endElement();
}
@@ -195,57 +202,86 @@ class StringTable extends WriterPart
/**
* Write Rich Text.
*
* @param XMLWriter $objWriter XML Writer
* @param RichText|string $pRichText text string or Rich text
* @param RichText|string $richText text string or Rich text
* @param string $prefix Optional Namespace prefix
*/
public function writeRichTextForCharts(XMLWriter $objWriter, $pRichText = null, $prefix = null): void
public function writeRichTextForCharts(XMLWriter $objWriter, $richText = null, $prefix = ''): void
{
if (!$pRichText instanceof RichText) {
$textRun = $pRichText;
$pRichText = new RichText();
$pRichText->createTextRun($textRun);
if (!($richText instanceof RichText)) {
$textRun = $richText;
$richText = new RichText();
$run = $richText->createTextRun($textRun ?? '');
$run->setFont(null);
}
if ($prefix !== null) {
if ($prefix !== '') {
$prefix .= ':';
}
// Loop through rich text elements
$elements = $pRichText->getRichTextElements();
$elements = $richText->getRichTextElements();
foreach ($elements as $element) {
// r
$objWriter->startElement($prefix . 'r');
if ($element->getFont() !== null) {
// rPr
$objWriter->startElement($prefix . 'rPr');
$size = $element->getFont()->getSize();
if (is_numeric($size)) {
$objWriter->writeAttribute('sz', (string) (int) ($size * 100));
}
// rPr
$objWriter->startElement($prefix . 'rPr');
// 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';
// 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;
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());
}
break;
// 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();
}
$objWriter->writeAttribute('u', $underlineType);
// Strikethrough
$objWriter->writeAttribute('strike', ($element->getFont()->getStrikethrough() ? 'sngStrike' : 'noStrike'));
// rFont
$objWriter->startElement($prefix . 'latin');
$objWriter->writeAttribute('typeface', $element->getFont()->getName());
$objWriter->endElement();
$objWriter->endElement();
// t
$objWriter->startElement($prefix . 't');
@@ -256,6 +292,33 @@ class StringTable extends WriterPart
}
}
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).
*

View File

@@ -2,9 +2,11 @@
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;
@@ -36,11 +38,11 @@ class Style extends WriterPart
// styleSheet
$objWriter->startElement('styleSheet');
$objWriter->writeAttribute('xml:space', 'preserve');
$objWriter->writeAttribute('xmlns', 'http://schemas.openxmlformats.org/spreadsheetml/2006/main');
$objWriter->writeAttribute('xmlns', Namespaces::MAIN);
// numFmts
$objWriter->startElement('numFmts');
$objWriter->writeAttribute('count', $this->getParentWriter()->getNumFmtHashTable()->count());
$objWriter->writeAttribute('count', (string) $this->getParentWriter()->getNumFmtHashTable()->count());
// numFmt
for ($i = 0; $i < $this->getParentWriter()->getNumFmtHashTable()->count(); ++$i) {
@@ -51,54 +53,63 @@ class Style extends WriterPart
// fonts
$objWriter->startElement('fonts');
$objWriter->writeAttribute('count', $this->getParentWriter()->getFontHashTable()->count());
$objWriter->writeAttribute('count', (string) $this->getParentWriter()->getFontHashTable()->count());
// font
for ($i = 0; $i < $this->getParentWriter()->getFontHashTable()->count(); ++$i) {
$this->writeFont($objWriter, $this->getParentWriter()->getFontHashTable()->getByIndex($i));
$thisfont = $this->getParentWriter()->getFontHashTable()->getByIndex($i);
if ($thisfont !== null) {
$this->writeFont($objWriter, $thisfont);
}
}
$objWriter->endElement();
// fills
$objWriter->startElement('fills');
$objWriter->writeAttribute('count', $this->getParentWriter()->getFillHashTable()->count());
$objWriter->writeAttribute('count', (string) $this->getParentWriter()->getFillHashTable()->count());
// fill
for ($i = 0; $i < $this->getParentWriter()->getFillHashTable()->count(); ++$i) {
$this->writeFill($objWriter, $this->getParentWriter()->getFillHashTable()->getByIndex($i));
$thisfill = $this->getParentWriter()->getFillHashTable()->getByIndex($i);
if ($thisfill !== null) {
$this->writeFill($objWriter, $thisfill);
}
}
$objWriter->endElement();
// borders
$objWriter->startElement('borders');
$objWriter->writeAttribute('count', $this->getParentWriter()->getBordersHashTable()->count());
$objWriter->writeAttribute('count', (string) $this->getParentWriter()->getBordersHashTable()->count());
// border
for ($i = 0; $i < $this->getParentWriter()->getBordersHashTable()->count(); ++$i) {
$this->writeBorder($objWriter, $this->getParentWriter()->getBordersHashTable()->getByIndex($i));
$thisborder = $this->getParentWriter()->getBordersHashTable()->getByIndex($i);
if ($thisborder !== null) {
$this->writeBorder($objWriter, $thisborder);
}
}
$objWriter->endElement();
// cellStyleXfs
$objWriter->startElement('cellStyleXfs');
$objWriter->writeAttribute('count', 1);
$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->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', count($spreadsheet->getCellXfCollection()));
$objWriter->writeAttribute('count', (string) count($spreadsheet->getCellXfCollection()));
// xf
foreach ($spreadsheet->getCellXfCollection() as $cellXf) {
@@ -109,24 +120,27 @@ class Style extends WriterPart
// cellStyles
$objWriter->startElement('cellStyles');
$objWriter->writeAttribute('count', 1);
$objWriter->writeAttribute('count', '1');
// cellStyle
$objWriter->startElement('cellStyle');
$objWriter->writeAttribute('name', 'Normal');
$objWriter->writeAttribute('xfId', 0);
$objWriter->writeAttribute('builtinId', 0);
$objWriter->writeAttribute('xfId', '0');
$objWriter->writeAttribute('builtinId', '0');
$objWriter->endElement();
$objWriter->endElement();
// dxfs
$objWriter->startElement('dxfs');
$objWriter->writeAttribute('count', $this->getParentWriter()->getStylesConditionalHashTable()->count());
$objWriter->writeAttribute('count', (string) $this->getParentWriter()->getStylesConditionalHashTable()->count());
// dxf
for ($i = 0; $i < $this->getParentWriter()->getStylesConditionalHashTable()->count(); ++$i) {
$this->writeCellStyleDxf($objWriter, $this->getParentWriter()->getStylesConditionalHashTable()->getByIndex($i)->getStyle());
$thisstyle = $this->getParentWriter()->getStylesConditionalHashTable()->getByIndex($i);
if ($thisstyle !== null) {
$this->writeCellStyleDxf($objWriter, $thisstyle->getStyle());
}
}
$objWriter->endElement();
@@ -145,49 +159,45 @@ class Style extends WriterPart
/**
* Write Fill.
*
* @param XMLWriter $objWriter XML Writer
* @param Fill $pFill Fill style
*/
private function writeFill(XMLWriter $objWriter, Fill $pFill): void
private function writeFill(XMLWriter $objWriter, Fill $fill): void
{
// Check if this is a pattern type or gradient type
if (
$pFill->getFillType() === Fill::FILL_GRADIENT_LINEAR ||
$pFill->getFillType() === Fill::FILL_GRADIENT_PATH
$fill->getFillType() === Fill::FILL_GRADIENT_LINEAR ||
$fill->getFillType() === Fill::FILL_GRADIENT_PATH
) {
// Gradient fill
$this->writeGradientFill($objWriter, $pFill);
} elseif ($pFill->getFillType() !== null) {
$this->writeGradientFill($objWriter, $fill);
} elseif ($fill->getFillType() !== null) {
// Pattern fill
$this->writePatternFill($objWriter, $pFill);
$this->writePatternFill($objWriter, $fill);
}
}
/**
* Write Gradient Fill.
*
* @param XMLWriter $objWriter XML Writer
* @param Fill $pFill Fill style
*/
private function writeGradientFill(XMLWriter $objWriter, Fill $pFill): void
private function writeGradientFill(XMLWriter $objWriter, Fill $fill): void
{
// fill
$objWriter->startElement('fill');
// gradientFill
$objWriter->startElement('gradientFill');
$objWriter->writeAttribute('type', $pFill->getFillType());
$objWriter->writeAttribute('degree', $pFill->getRotation());
$objWriter->writeAttribute('type', (string) $fill->getFillType());
$objWriter->writeAttribute('degree', (string) $fill->getRotation());
// stop
$objWriter->startElement('stop');
$objWriter->writeAttribute('position', '0');
// color
$objWriter->startElement('color');
$objWriter->writeAttribute('rgb', $pFill->getStartColor()->getARGB());
$objWriter->endElement();
if ($fill->getStartColor()->getARGB() !== null) {
$objWriter->startElement('color');
$objWriter->writeAttribute('rgb', $fill->getStartColor()->getARGB());
$objWriter->endElement();
}
$objWriter->endElement();
@@ -196,9 +206,11 @@ class Style extends WriterPart
$objWriter->writeAttribute('position', '1');
// color
$objWriter->startElement('color');
$objWriter->writeAttribute('rgb', $pFill->getEndColor()->getARGB());
$objWriter->endElement();
if ($fill->getEndColor()->getARGB() !== null) {
$objWriter->startElement('color');
$objWriter->writeAttribute('rgb', $fill->getEndColor()->getARGB());
$objWriter->endElement();
}
$objWriter->endElement();
@@ -207,34 +219,38 @@ class Style extends WriterPart
$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.
*
* @param XMLWriter $objWriter XML Writer
* @param Fill $pFill Fill style
*/
private function writePatternFill(XMLWriter $objWriter, Fill $pFill): void
private function writePatternFill(XMLWriter $objWriter, Fill $fill): void
{
// fill
$objWriter->startElement('fill');
// patternFill
$objWriter->startElement('patternFill');
$objWriter->writeAttribute('patternType', $pFill->getFillType());
$objWriter->writeAttribute('patternType', (string) $fill->getFillType());
if ($pFill->getFillType() !== Fill::FILL_NONE) {
if (self::writePatternColors($fill)) {
// fgColor
if ($pFill->getStartColor()->getARGB()) {
if ($fill->getStartColor()->getARGB()) {
$objWriter->startElement('fgColor');
$objWriter->writeAttribute('rgb', $pFill->getStartColor()->getARGB());
$objWriter->writeAttribute('rgb', $fill->getStartColor()->getARGB());
$objWriter->endElement();
}
}
if ($pFill->getFillType() !== Fill::FILL_NONE) {
// bgColor
if ($pFill->getEndColor()->getARGB()) {
if ($fill->getEndColor()->getARGB()) {
$objWriter->startElement('bgColor');
$objWriter->writeAttribute('rgb', $pFill->getEndColor()->getARGB());
$objWriter->writeAttribute('rgb', $fill->getEndColor()->getARGB());
$objWriter->endElement();
}
}
@@ -246,11 +262,8 @@ class Style extends WriterPart
/**
* Write Font.
*
* @param XMLWriter $objWriter XML Writer
* @param Font $pFont Font style
*/
private function writeFont(XMLWriter $objWriter, Font $pFont): void
private function writeFont(XMLWriter $objWriter, Font $font): void
{
// font
$objWriter->startElement('font');
@@ -261,62 +274,62 @@ class Style extends WriterPart
// 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 ($pFont->getBold() !== null) {
if ($font->getBold() !== null) {
$objWriter->startElement('b');
$objWriter->writeAttribute('val', $pFont->getBold() ? '1' : '0');
$objWriter->writeAttribute('val', $font->getBold() ? '1' : '0');
$objWriter->endElement();
}
// Italic
if ($pFont->getItalic() !== null) {
if ($font->getItalic() !== null) {
$objWriter->startElement('i');
$objWriter->writeAttribute('val', $pFont->getItalic() ? '1' : '0');
$objWriter->writeAttribute('val', $font->getItalic() ? '1' : '0');
$objWriter->endElement();
}
// Strikethrough
if ($pFont->getStrikethrough() !== null) {
if ($font->getStrikethrough() !== null) {
$objWriter->startElement('strike');
$objWriter->writeAttribute('val', $pFont->getStrikethrough() ? '1' : '0');
$objWriter->writeAttribute('val', $font->getStrikethrough() ? '1' : '0');
$objWriter->endElement();
}
// Underline
if ($pFont->getUnderline() !== null) {
if ($font->getUnderline() !== null) {
$objWriter->startElement('u');
$objWriter->writeAttribute('val', $pFont->getUnderline());
$objWriter->writeAttribute('val', $font->getUnderline());
$objWriter->endElement();
}
// Superscript / subscript
if ($pFont->getSuperscript() === true || $pFont->getSubscript() === true) {
if ($font->getSuperscript() === true || $font->getSubscript() === true) {
$objWriter->startElement('vertAlign');
if ($pFont->getSuperscript() === true) {
if ($font->getSuperscript() === true) {
$objWriter->writeAttribute('val', 'superscript');
} elseif ($pFont->getSubscript() === true) {
} elseif ($font->getSubscript() === true) {
$objWriter->writeAttribute('val', 'subscript');
}
$objWriter->endElement();
}
// Size
if ($pFont->getSize() !== null) {
if ($font->getSize() !== null) {
$objWriter->startElement('sz');
$objWriter->writeAttribute('val', StringHelper::formatNumber($pFont->getSize()));
$objWriter->writeAttribute('val', StringHelper::formatNumber($font->getSize()));
$objWriter->endElement();
}
// Foreground color
if ($pFont->getColor()->getARGB() !== null) {
if ($font->getColor()->getARGB() !== null) {
$objWriter->startElement('color');
$objWriter->writeAttribute('rgb', $pFont->getColor()->getARGB());
$objWriter->writeAttribute('rgb', $font->getColor()->getARGB());
$objWriter->endElement();
}
// Name
if ($pFont->getName() !== null) {
if ($font->getName() !== null) {
$objWriter->startElement('name');
$objWriter->writeAttribute('val', $pFont->getName());
$objWriter->writeAttribute('val', $font->getName());
$objWriter->endElement();
}
@@ -325,16 +338,13 @@ class Style extends WriterPart
/**
* Write Border.
*
* @param XMLWriter $objWriter XML Writer
* @param Borders $pBorders Borders style
*/
private function writeBorder(XMLWriter $objWriter, Borders $pBorders): void
private function writeBorder(XMLWriter $objWriter, Borders $borders): void
{
// Write border
$objWriter->startElement('border');
// Diagonal?
switch ($pBorders->getDiagonalDirection()) {
switch ($borders->getDiagonalDirection()) {
case Borders::DIAGONAL_UP:
$objWriter->writeAttribute('diagonalUp', 'true');
$objWriter->writeAttribute('diagonalDown', 'false');
@@ -353,82 +363,87 @@ class Style extends WriterPart
}
// BorderPr
$this->writeBorderPr($objWriter, 'left', $pBorders->getLeft());
$this->writeBorderPr($objWriter, 'right', $pBorders->getRight());
$this->writeBorderPr($objWriter, 'top', $pBorders->getTop());
$this->writeBorderPr($objWriter, 'bottom', $pBorders->getBottom());
$this->writeBorderPr($objWriter, 'diagonal', $pBorders->getDiagonal());
$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();
}
/** @var mixed */
private static $scrutinizerFalse = false;
/**
* Write Cell Style Xf.
*
* @param XMLWriter $objWriter XML Writer
* @param \PhpOffice\PhpSpreadsheet\Style\Style $pStyle Style
* @param Spreadsheet $spreadsheet Workbook
*/
private function writeCellStyleXf(XMLWriter $objWriter, \PhpOffice\PhpSpreadsheet\Style\Style $pStyle, Spreadsheet $spreadsheet): void
private function writeCellStyleXf(XMLWriter $objWriter, \PhpOffice\PhpSpreadsheet\Style\Style $style, Spreadsheet $spreadsheet): void
{
// xf
$objWriter->startElement('xf');
$objWriter->writeAttribute('xfId', 0);
$objWriter->writeAttribute('fontId', (int) $this->getParentWriter()->getFontHashTable()->getIndexForHashCode($pStyle->getFont()->getHashCode()));
if ($pStyle->getQuotePrefix()) {
$objWriter->writeAttribute('quotePrefix', 1);
$objWriter->writeAttribute('xfId', '0');
$objWriter->writeAttribute('fontId', (string) (int) $this->getParentWriter()->getFontHashTable()->getIndexForHashCode($style->getFont()->getHashCode()));
if ($style->getQuotePrefix()) {
$objWriter->writeAttribute('quotePrefix', '1');
}
if ($pStyle->getNumberFormat()->getBuiltInFormatCode() === false) {
$objWriter->writeAttribute('numFmtId', (int) ($this->getParentWriter()->getNumFmtHashTable()->getIndexForHashCode($pStyle->getNumberFormat()->getHashCode()) + 164));
if ($style->getNumberFormat()->getBuiltInFormatCode() === self::$scrutinizerFalse) {
$objWriter->writeAttribute('numFmtId', (string) (int) ($this->getParentWriter()->getNumFmtHashTable()->getIndexForHashCode($style->getNumberFormat()->getHashCode()) + 164));
} else {
$objWriter->writeAttribute('numFmtId', (int) $pStyle->getNumberFormat()->getBuiltInFormatCode());
$objWriter->writeAttribute('numFmtId', (string) (int) $style->getNumberFormat()->getBuiltInFormatCode());
}
$objWriter->writeAttribute('fillId', (int) $this->getParentWriter()->getFillHashTable()->getIndexForHashCode($pStyle->getFill()->getHashCode()));
$objWriter->writeAttribute('borderId', (int) $this->getParentWriter()->getBordersHashTable()->getIndexForHashCode($pStyle->getBorders()->getHashCode()));
$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() != $pStyle->getFont()->getHashCode()) ? '1' : '0');
$objWriter->writeAttribute('applyNumberFormat', ($spreadsheet->getDefaultStyle()->getNumberFormat()->getHashCode() != $pStyle->getNumberFormat()->getHashCode()) ? '1' : '0');
$objWriter->writeAttribute('applyFill', ($spreadsheet->getDefaultStyle()->getFill()->getHashCode() != $pStyle->getFill()->getHashCode()) ? '1' : '0');
$objWriter->writeAttribute('applyBorder', ($spreadsheet->getDefaultStyle()->getBorders()->getHashCode() != $pStyle->getBorders()->getHashCode()) ? '1' : '0');
$objWriter->writeAttribute('applyAlignment', ($spreadsheet->getDefaultStyle()->getAlignment()->getHashCode() != $pStyle->getAlignment()->getHashCode()) ? '1' : '0');
if ($pStyle->getProtection()->getLocked() != Protection::PROTECTION_INHERIT || $pStyle->getProtection()->getHidden() != Protection::PROTECTION_INHERIT) {
$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');
$objWriter->writeAttribute('applyAlignment', ($spreadsheet->getDefaultStyle()->getAlignment()->getHashCode() != $style->getAlignment()->getHashCode()) ? '1' : '0');
if ($style->getProtection()->getLocked() != Protection::PROTECTION_INHERIT || $style->getProtection()->getHidden() != Protection::PROTECTION_INHERIT) {
$objWriter->writeAttribute('applyProtection', 'true');
}
// alignment
$objWriter->startElement('alignment');
$objWriter->writeAttribute('horizontal', $pStyle->getAlignment()->getHorizontal());
$objWriter->writeAttribute('vertical', $pStyle->getAlignment()->getVertical());
$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);
}
$textRotation = 0;
if ($pStyle->getAlignment()->getTextRotation() >= 0) {
$textRotation = $pStyle->getAlignment()->getTextRotation();
} elseif ($pStyle->getAlignment()->getTextRotation() < 0) {
$textRotation = 90 - $pStyle->getAlignment()->getTextRotation();
if ($style->getAlignment()->getTextRotation() >= 0) {
$textRotation = $style->getAlignment()->getTextRotation();
} else {
$textRotation = 90 - $style->getAlignment()->getTextRotation();
}
$objWriter->writeAttribute('textRotation', $textRotation);
$objWriter->writeAttribute('textRotation', (string) $textRotation);
$objWriter->writeAttribute('wrapText', ($pStyle->getAlignment()->getWrapText() ? 'true' : 'false'));
$objWriter->writeAttribute('shrinkToFit', ($pStyle->getAlignment()->getShrinkToFit() ? 'true' : 'false'));
$objWriter->writeAttribute('wrapText', ($style->getAlignment()->getWrapText() ? 'true' : 'false'));
$objWriter->writeAttribute('shrinkToFit', ($style->getAlignment()->getShrinkToFit() ? 'true' : 'false'));
if ($pStyle->getAlignment()->getIndent() > 0) {
$objWriter->writeAttribute('indent', $pStyle->getAlignment()->getIndent());
if ($style->getAlignment()->getIndent() > 0) {
$objWriter->writeAttribute('indent', (string) $style->getAlignment()->getIndent());
}
if ($pStyle->getAlignment()->getReadOrder() > 0) {
$objWriter->writeAttribute('readingOrder', $pStyle->getAlignment()->getReadOrder());
if ($style->getAlignment()->getReadOrder() > 0) {
$objWriter->writeAttribute('readingOrder', (string) $style->getAlignment()->getReadOrder());
}
$objWriter->endElement();
// protection
if ($pStyle->getProtection()->getLocked() != Protection::PROTECTION_INHERIT || $pStyle->getProtection()->getHidden() != Protection::PROTECTION_INHERIT) {
if ($style->getProtection()->getLocked() != Protection::PROTECTION_INHERIT || $style->getProtection()->getHidden() != Protection::PROTECTION_INHERIT) {
$objWriter->startElement('protection');
if ($pStyle->getProtection()->getLocked() != Protection::PROTECTION_INHERIT) {
$objWriter->writeAttribute('locked', ($pStyle->getProtection()->getLocked() == Protection::PROTECTION_PROTECTED ? 'true' : 'false'));
if ($style->getProtection()->getLocked() != Protection::PROTECTION_INHERIT) {
$objWriter->writeAttribute('locked', ($style->getProtection()->getLocked() == Protection::PROTECTION_PROTECTED ? 'true' : 'false'));
}
if ($pStyle->getProtection()->getHidden() != Protection::PROTECTION_INHERIT) {
$objWriter->writeAttribute('hidden', ($pStyle->getProtection()->getHidden() == 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();
}
@@ -438,65 +453,64 @@ class Style extends WriterPart
/**
* Write Cell Style Dxf.
*
* @param XMLWriter $objWriter XML Writer
* @param \PhpOffice\PhpSpreadsheet\Style\Style $pStyle Style
*/
private function writeCellStyleDxf(XMLWriter $objWriter, \PhpOffice\PhpSpreadsheet\Style\Style $pStyle): void
private function writeCellStyleDxf(XMLWriter $objWriter, \PhpOffice\PhpSpreadsheet\Style\Style $style): void
{
// dxf
$objWriter->startElement('dxf');
// font
$this->writeFont($objWriter, $pStyle->getFont());
$this->writeFont($objWriter, $style->getFont());
// numFmt
$this->writeNumFmt($objWriter, $pStyle->getNumberFormat());
$this->writeNumFmt($objWriter, $style->getNumberFormat());
// fill
$this->writeFill($objWriter, $pStyle->getFill());
$this->writeFill($objWriter, $style->getFill());
// alignment
$objWriter->startElement('alignment');
if ($pStyle->getAlignment()->getHorizontal() !== null) {
$objWriter->writeAttribute('horizontal', $pStyle->getAlignment()->getHorizontal());
$horizontal = Alignment::HORIZONTAL_ALIGNMENT_FOR_XLSX[$style->getAlignment()->getHorizontal()] ?? '';
if ($horizontal) {
$objWriter->writeAttribute('horizontal', $horizontal);
}
if ($pStyle->getAlignment()->getVertical() !== null) {
$objWriter->writeAttribute('vertical', $pStyle->getAlignment()->getVertical());
$vertical = Alignment::VERTICAL_ALIGNMENT_FOR_XLSX[$style->getAlignment()->getVertical()] ?? '';
if ($vertical) {
$objWriter->writeAttribute('vertical', $vertical);
}
if ($pStyle->getAlignment()->getTextRotation() !== null) {
if ($style->getAlignment()->getTextRotation() !== null) {
$textRotation = 0;
if ($pStyle->getAlignment()->getTextRotation() >= 0) {
$textRotation = $pStyle->getAlignment()->getTextRotation();
} elseif ($pStyle->getAlignment()->getTextRotation() < 0) {
$textRotation = 90 - $pStyle->getAlignment()->getTextRotation();
if ($style->getAlignment()->getTextRotation() >= 0) {
$textRotation = $style->getAlignment()->getTextRotation();
} else {
$textRotation = 90 - $style->getAlignment()->getTextRotation();
}
$objWriter->writeAttribute('textRotation', $textRotation);
$objWriter->writeAttribute('textRotation', (string) $textRotation);
}
$objWriter->endElement();
// border
$this->writeBorder($objWriter, $pStyle->getBorders());
$this->writeBorder($objWriter, $style->getBorders());
// protection
if (($pStyle->getProtection()->getLocked() !== null) || ($pStyle->getProtection()->getHidden() !== null)) {
if ((!empty($style->getProtection()->getLocked())) || (!empty($style->getProtection()->getHidden()))) {
if (
$pStyle->getProtection()->getLocked() !== Protection::PROTECTION_INHERIT ||
$pStyle->getProtection()->getHidden() !== Protection::PROTECTION_INHERIT
$style->getProtection()->getLocked() !== Protection::PROTECTION_INHERIT ||
$style->getProtection()->getHidden() !== Protection::PROTECTION_INHERIT
) {
$objWriter->startElement('protection');
if (
($pStyle->getProtection()->getLocked() !== null) &&
($pStyle->getProtection()->getLocked() !== Protection::PROTECTION_INHERIT)
($style->getProtection()->getLocked() !== null) &&
($style->getProtection()->getLocked() !== Protection::PROTECTION_INHERIT)
) {
$objWriter->writeAttribute('locked', ($pStyle->getProtection()->getLocked() == Protection::PROTECTION_PROTECTED ? 'true' : 'false'));
$objWriter->writeAttribute('locked', ($style->getProtection()->getLocked() == Protection::PROTECTION_PROTECTED ? 'true' : 'false'));
}
if (
($pStyle->getProtection()->getHidden() !== null) &&
($pStyle->getProtection()->getHidden() !== Protection::PROTECTION_INHERIT)
($style->getProtection()->getHidden() !== null) &&
($style->getProtection()->getHidden() !== Protection::PROTECTION_INHERIT)
) {
$objWriter->writeAttribute('hidden', ($pStyle->getProtection()->getHidden() == Protection::PROTECTION_PROTECTED ? 'true' : 'false'));
$objWriter->writeAttribute('hidden', ($style->getProtection()->getHidden() == Protection::PROTECTION_PROTECTED ? 'true' : 'false'));
}
$objWriter->endElement();
}
@@ -508,42 +522,40 @@ class Style extends WriterPart
/**
* Write BorderPr.
*
* @param XMLWriter $objWriter XML Writer
* @param string $pName Element name
* @param Border $pBorder Border style
* @param string $name Element name
*/
private function writeBorderPr(XMLWriter $objWriter, $pName, Border $pBorder): void
private function writeBorderPr(XMLWriter $objWriter, $name, Border $border): void
{
// Write BorderPr
if ($pBorder->getBorderStyle() != Border::BORDER_NONE) {
$objWriter->startElement($pName);
$objWriter->writeAttribute('style', $pBorder->getBorderStyle());
if ($border->getBorderStyle() != Border::BORDER_NONE) {
$objWriter->startElement($name);
$objWriter->writeAttribute('style', $border->getBorderStyle());
// color
$objWriter->startElement('color');
$objWriter->writeAttribute('rgb', $pBorder->getColor()->getARGB());
$objWriter->endElement();
if ($border->getColor()->getARGB() !== null) {
$objWriter->startElement('color');
$objWriter->writeAttribute('rgb', $border->getColor()->getARGB());
$objWriter->endElement();
$objWriter->endElement();
$objWriter->endElement();
}
}
}
/**
* Write NumberFormat.
*
* @param XMLWriter $objWriter XML Writer
* @param NumberFormat $pNumberFormat Number Format
* @param int $pId Number Format identifier
* @param int $id Number Format identifier
*/
private function writeNumFmt(XMLWriter $objWriter, NumberFormat $pNumberFormat, $pId = 0): void
private function writeNumFmt(XMLWriter $objWriter, ?NumberFormat $numberFormat, $id = 0): void
{
// Translate formatcode
$formatCode = $pNumberFormat->getFormatCode();
$formatCode = ($numberFormat === null) ? null : $numberFormat->getFormatCode();
// numFmt
if ($formatCode !== null) {
$objWriter->startElement('numFmt');
$objWriter->writeAttribute('numFmtId', ($pId + 164));
$objWriter->writeAttribute('numFmtId', (string) ($id + 164));
$objWriter->writeAttribute('formatCode', $formatCode);
$objWriter->endElement();
}

View File

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

View File

@@ -2,15 +2,15 @@
namespace PhpOffice\PhpSpreadsheet\Writer\Xlsx;
use PhpOffice\PhpSpreadsheet\Reader\Xlsx\Namespaces;
use PhpOffice\PhpSpreadsheet\Shared\XMLWriter;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
class Theme extends WriterPart
{
/**
* Map of Major fonts to write.
*
* @var array of string
* @var string[]
*/
private static $majorFonts = [
'Jpan' => ' Pゴシック',
@@ -48,7 +48,7 @@ class Theme extends WriterPart
/**
* Map of Minor fonts to write.
*
* @var array of string
* @var string[]
*/
private static $minorFonts = [
'Jpan' => ' Pゴシック',
@@ -86,7 +86,7 @@ class Theme extends WriterPart
/**
* Map of core colours.
*
* @var array of string
* @var string[]
*/
private static $colourScheme = [
'dk2' => '1F497D',
@@ -106,7 +106,7 @@ class Theme extends WriterPart
*
* @return string XML Output
*/
public function writeTheme(Spreadsheet $spreadsheet)
public function writeTheme()
{
// Create XML writer
$objWriter = null;
@@ -121,7 +121,7 @@ class Theme extends WriterPart
// a:theme
$objWriter->startElement('a:theme');
$objWriter->writeAttribute('xmlns:a', 'http://schemas.openxmlformats.org/drawingml/2006/main');
$objWriter->writeAttribute('xmlns:a', Namespaces::DRAWINGML);
$objWriter->writeAttribute('name', 'Office Theme');
// a:themeElements
@@ -784,13 +784,9 @@ class Theme extends WriterPart
/**
* Write fonts to XML format.
*
* @param XMLWriter $objWriter
* @param string $latinFont
* @param array of string $fontSet
*
* @return string XML Output
* @param string[] $fontSet
*/
private function writeFonts($objWriter, $latinFont, $fontSet)
private function writeFonts(XMLWriter $objWriter, string $latinFont, array $fontSet): void
{
// a:latin
$objWriter->startElement('a:latin');
@@ -817,12 +813,8 @@ class Theme extends WriterPart
/**
* Write colour scheme to XML format.
*
* @param XMLWriter $objWriter
*
* @return string XML Output
*/
private function writeColourScheme($objWriter)
private function writeColourScheme(XMLWriter $objWriter): void
{
foreach (self::$colourScheme as $colourName => $colourValue) {
$objWriter->startElement('a:' . $colourName);

View File

@@ -2,6 +2,7 @@
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;
@@ -32,8 +33,8 @@ class Workbook extends WriterPart
// workbook
$objWriter->startElement('workbook');
$objWriter->writeAttribute('xml:space', 'preserve');
$objWriter->writeAttribute('xmlns', 'http://schemas.openxmlformats.org/spreadsheetml/2006/main');
$objWriter->writeAttribute('xmlns:r', 'http://schemas.openxmlformats.org/officeDocument/2006/relationships');
$objWriter->writeAttribute('xmlns', Namespaces::MAIN);
$objWriter->writeAttribute('xmlns:r', Namespaces::SCHEMA_OFFICE_DOCUMENT);
// fileVersion
$this->writeFileVersion($objWriter);
@@ -66,8 +67,6 @@ class Workbook extends WriterPart
/**
* Write file version.
*
* @param XMLWriter $objWriter XML Writer
*/
private function writeFileVersion(XMLWriter $objWriter): void
{
@@ -81,8 +80,6 @@ class Workbook extends WriterPart
/**
* Write WorkbookPr.
*
* @param XMLWriter $objWriter XML Writer
*/
private function writeWorkbookPr(XMLWriter $objWriter): void
{
@@ -99,8 +96,6 @@ class Workbook extends WriterPart
/**
* Write BookViews.
*
* @param XMLWriter $objWriter XML Writer
*/
private function writeBookViews(XMLWriter $objWriter, Spreadsheet $spreadsheet): void
{
@@ -110,14 +105,14 @@ class Workbook extends WriterPart
// workbookView
$objWriter->startElement('workbookView');
$objWriter->writeAttribute('activeTab', $spreadsheet->getActiveSheetIndex());
$objWriter->writeAttribute('activeTab', (string) $spreadsheet->getActiveSheetIndex());
$objWriter->writeAttribute('autoFilterDateGrouping', ($spreadsheet->getAutoFilterDateGrouping() ? 'true' : 'false'));
$objWriter->writeAttribute('firstSheet', $spreadsheet->getFirstSheetIndex());
$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', $spreadsheet->getTabRatio());
$objWriter->writeAttribute('tabRatio', (string) $spreadsheet->getTabRatio());
$objWriter->writeAttribute('visibility', $spreadsheet->getVisibility());
$objWriter->endElement();
@@ -127,8 +122,6 @@ class Workbook extends WriterPart
/**
* Write WorkbookProtection.
*
* @param XMLWriter $objWriter XML Writer
*/
private function writeWorkbookProtection(XMLWriter $objWriter, Spreadsheet $spreadsheet): void
{
@@ -153,7 +146,6 @@ class Workbook extends WriterPart
/**
* Write calcPr.
*
* @param XMLWriter $objWriter XML Writer
* @param bool $recalcRequired Indicate whether formulas should be recalculated before writing
*/
private function writeCalcPr(XMLWriter $objWriter, $recalcRequired = true): void
@@ -166,17 +158,15 @@ class Workbook extends WriterPart
$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->writeAttribute('calcCompleted', ($recalcRequired) ? '1' : '0');
$objWriter->writeAttribute('fullCalcOnLoad', ($recalcRequired) ? '0' : '1');
$objWriter->writeAttribute('forceFullCalc', ($recalcRequired) ? '0' : '1');
$objWriter->endElement();
}
/**
* Write sheets.
*
* @param XMLWriter $objWriter XML Writer
*/
private function writeSheets(XMLWriter $objWriter, Spreadsheet $spreadsheet): void
{
@@ -200,23 +190,22 @@ class Workbook extends WriterPart
/**
* Write sheet.
*
* @param XMLWriter $objWriter XML Writer
* @param string $pSheetname Sheet name
* @param int $pSheetId Sheet id
* @param int $pRelId Relationship ID
* @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, $pSheetname, $pSheetId = 1, $pRelId = 1, $sheetState = 'visible'): void
private function writeSheet(XMLWriter $objWriter, $worksheetName, $worksheetId = 1, $relId = 1, $sheetState = 'visible'): void
{
if ($pSheetname != '') {
if ($worksheetName != '') {
// Write sheet
$objWriter->startElement('sheet');
$objWriter->writeAttribute('name', $pSheetname);
$objWriter->writeAttribute('sheetId', $pSheetId);
$objWriter->writeAttribute('name', $worksheetName);
$objWriter->writeAttribute('sheetId', (string) $worksheetId);
if ($sheetState !== 'visible' && $sheetState != '') {
$objWriter->writeAttribute('state', $sheetState);
}
$objWriter->writeAttribute('r:id', 'rId' . $pRelId);
$objWriter->writeAttribute('r:id', 'rId' . $relId);
$objWriter->endElement();
} else {
throw new WriterException('Invalid parameters passed.');

File diff suppressed because it is too large Load Diff

View File

@@ -26,8 +26,8 @@ abstract class WriterPart
/**
* Set parent Xlsx object.
*/
public function __construct(Xlsx $pWriter)
public function __construct(Xlsx $writer)
{
$this->parentWriter = $pWriter;
$this->parentWriter = $writer;
}
}