Alter Version

This commit is contained in:
Walter Hupfeld
2024-02-16 15:35:01 +01:00
parent 6e85cec1da
commit 0df6729f8b
393 changed files with 173746 additions and 6 deletions

View File

@@ -0,0 +1,146 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Reader\Xlsx;
use PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column;
use PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column\Rule;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
use SimpleXMLElement;
class AutoFilter
{
private $worksheet;
private $worksheetXml;
public function __construct(Worksheet $workSheet, SimpleXMLElement $worksheetXml)
{
$this->worksheet = $workSheet;
$this->worksheetXml = $worksheetXml;
}
public function load(): void
{
// Remove all "$" in the auto filter range
$autoFilterRange = preg_replace('/\$/', '', $this->worksheetXml->autoFilter['ref']);
if (strpos($autoFilterRange, ':') !== false) {
$this->readAutoFilter($autoFilterRange, $this->worksheetXml);
}
}
private function readAutoFilter($autoFilterRange, $xmlSheet): void
{
$autoFilter = $this->worksheet->getAutoFilter();
$autoFilter->setRange($autoFilterRange);
foreach ($xmlSheet->autoFilter->filterColumn as $filterColumn) {
$column = $autoFilter->getColumnByOffset((int) $filterColumn['colId']);
// Check for standard filters
if ($filterColumn->filters) {
$column->setFilterType(Column::AUTOFILTER_FILTERTYPE_FILTER);
$filters = $filterColumn->filters;
if ((isset($filters['blank'])) && ($filters['blank'] == 1)) {
// Operator is undefined, but always treated as EQUAL
$column->createRule()->setRule(null, '')->setRuleType(Rule::AUTOFILTER_RULETYPE_FILTER);
}
// Standard filters are always an OR join, so no join rule needs to be set
// Entries can be either filter elements
foreach ($filters->filter as $filterRule) {
// Operator is undefined, but always treated as EQUAL
$column->createRule()->setRule(null, (string) $filterRule['val'])->setRuleType(Rule::AUTOFILTER_RULETYPE_FILTER);
}
// Or Date Group elements
$this->readDateRangeAutoFilter($filters, $column);
}
// Check for custom filters
$this->readCustomAutoFilter($filterColumn, $column);
// Check for dynamic filters
$this->readDynamicAutoFilter($filterColumn, $column);
// Check for dynamic filters
$this->readTopTenAutoFilter($filterColumn, $column);
}
}
private function readDateRangeAutoFilter(SimpleXMLElement $filters, Column $column): void
{
foreach ($filters->dateGroupItem as $dateGroupItem) {
// Operator is undefined, but always treated as EQUAL
$column->createRule()->setRule(
null,
[
'year' => (string) $dateGroupItem['year'],
'month' => (string) $dateGroupItem['month'],
'day' => (string) $dateGroupItem['day'],
'hour' => (string) $dateGroupItem['hour'],
'minute' => (string) $dateGroupItem['minute'],
'second' => (string) $dateGroupItem['second'],
],
(string) $dateGroupItem['dateTimeGrouping']
)->setRuleType(Rule::AUTOFILTER_RULETYPE_DATEGROUP);
}
}
private function readCustomAutoFilter(SimpleXMLElement $filterColumn, Column $column): void
{
if ($filterColumn->customFilters) {
$column->setFilterType(Column::AUTOFILTER_FILTERTYPE_CUSTOMFILTER);
$customFilters = $filterColumn->customFilters;
// Custom filters can an AND or an OR join;
// and there should only ever be one or two entries
if ((isset($customFilters['and'])) && ($customFilters['and'] == 1)) {
$column->setJoin(Column::AUTOFILTER_COLUMN_JOIN_AND);
}
foreach ($customFilters->customFilter as $filterRule) {
$column->createRule()->setRule(
(string) $filterRule['operator'],
(string) $filterRule['val']
)->setRuleType(Rule::AUTOFILTER_RULETYPE_CUSTOMFILTER);
}
}
}
private function readDynamicAutoFilter(SimpleXMLElement $filterColumn, Column $column): void
{
if ($filterColumn->dynamicFilter) {
$column->setFilterType(Column::AUTOFILTER_FILTERTYPE_DYNAMICFILTER);
// We should only ever have one dynamic filter
foreach ($filterColumn->dynamicFilter as $filterRule) {
// Operator is undefined, but always treated as EQUAL
$column->createRule()->setRule(
null,
(string) $filterRule['val'],
(string) $filterRule['type']
)->setRuleType(Rule::AUTOFILTER_RULETYPE_DYNAMICFILTER);
if (isset($filterRule['val'])) {
$column->setAttribute('val', (string) $filterRule['val']);
}
if (isset($filterRule['maxVal'])) {
$column->setAttribute('maxVal', (string) $filterRule['maxVal']);
}
}
}
}
private function readTopTenAutoFilter(SimpleXMLElement $filterColumn, Column $column): void
{
if ($filterColumn->top10) {
$column->setFilterType(Column::AUTOFILTER_FILTERTYPE_TOPTENFILTER);
// We should only ever have one top10 filter
foreach ($filterColumn->top10 as $filterRule) {
$column->createRule()->setRule(
(((isset($filterRule['percent'])) && ($filterRule['percent'] == 1))
? Rule::AUTOFILTER_COLUMN_RULE_TOPTEN_PERCENT
: Rule::AUTOFILTER_COLUMN_RULE_TOPTEN_BY_VALUE
),
(string) $filterRule['val'],
(((isset($filterRule['top'])) && ($filterRule['top'] == 1))
? Rule::AUTOFILTER_COLUMN_RULE_TOPTEN_TOP
: Rule::AUTOFILTER_COLUMN_RULE_TOPTEN_BOTTOM
)
)->setRuleType(Rule::AUTOFILTER_RULETYPE_TOPTENFILTER);
}
}
}
}

View File

@@ -0,0 +1,19 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Reader\Xlsx;
class BaseParserClass
{
protected static function boolean($value)
{
if (is_object($value)) {
$value = (string) $value;
}
if (is_numeric($value)) {
return (bool) $value;
}
return $value === strtolower('true');
}
}

View File

@@ -0,0 +1,567 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Reader\Xlsx;
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
use PhpOffice\PhpSpreadsheet\Chart\DataSeries;
use PhpOffice\PhpSpreadsheet\Chart\DataSeriesValues;
use PhpOffice\PhpSpreadsheet\Chart\Layout;
use PhpOffice\PhpSpreadsheet\Chart\Legend;
use PhpOffice\PhpSpreadsheet\Chart\PlotArea;
use PhpOffice\PhpSpreadsheet\Chart\Title;
use PhpOffice\PhpSpreadsheet\RichText\RichText;
use PhpOffice\PhpSpreadsheet\Style\Color;
use PhpOffice\PhpSpreadsheet\Style\Font;
use SimpleXMLElement;
class Chart
{
/**
* @param string $name
* @param string $format
*
* @return null|bool|float|int|string
*/
private static function getAttribute(SimpleXMLElement $component, $name, $format)
{
$attributes = $component->attributes();
if (isset($attributes[$name])) {
if ($format == 'string') {
return (string) $attributes[$name];
} elseif ($format == 'integer') {
return (int) $attributes[$name];
} elseif ($format == 'boolean') {
return (bool) ($attributes[$name] === '0' || $attributes[$name] !== 'true') ? false : true;
}
return (float) $attributes[$name];
}
return null;
}
private static function readColor($color, $background = false)
{
if (isset($color['rgb'])) {
return (string) $color['rgb'];
} elseif (isset($color['indexed'])) {
return Color::indexedColor($color['indexed'] - 7, $background)->getARGB();
}
}
/**
* @param string $chartName
*
* @return \PhpOffice\PhpSpreadsheet\Chart\Chart
*/
public static function readChart(SimpleXMLElement $chartElements, $chartName)
{
$namespacesChartMeta = $chartElements->getNamespaces(true);
$chartElementsC = $chartElements->children($namespacesChartMeta['c']);
$XaxisLabel = $YaxisLabel = $legend = $title = null;
$dispBlanksAs = $plotVisOnly = null;
foreach ($chartElementsC as $chartElementKey => $chartElement) {
switch ($chartElementKey) {
case 'chart':
foreach ($chartElement as $chartDetailsKey => $chartDetails) {
$chartDetailsC = $chartDetails->children($namespacesChartMeta['c']);
switch ($chartDetailsKey) {
case 'plotArea':
$plotAreaLayout = $XaxisLable = $YaxisLable = null;
$plotSeries = $plotAttributes = [];
foreach ($chartDetails as $chartDetailKey => $chartDetail) {
switch ($chartDetailKey) {
case 'layout':
$plotAreaLayout = self::chartLayoutDetails($chartDetail, $namespacesChartMeta);
break;
case 'catAx':
if (isset($chartDetail->title)) {
$XaxisLabel = self::chartTitle($chartDetail->title->children($namespacesChartMeta['c']), $namespacesChartMeta);
}
break;
case 'dateAx':
if (isset($chartDetail->title)) {
$XaxisLabel = self::chartTitle($chartDetail->title->children($namespacesChartMeta['c']), $namespacesChartMeta);
}
break;
case 'valAx':
if (isset($chartDetail->title)) {
$YaxisLabel = self::chartTitle($chartDetail->title->children($namespacesChartMeta['c']), $namespacesChartMeta);
}
break;
case 'barChart':
case 'bar3DChart':
$barDirection = self::getAttribute($chartDetail->barDir, 'val', 'string');
$plotSer = self::chartDataSeries($chartDetail, $namespacesChartMeta, $chartDetailKey);
$plotSer->setPlotDirection($barDirection);
$plotSeries[] = $plotSer;
$plotAttributes = self::readChartAttributes($chartDetail);
break;
case 'lineChart':
case 'line3DChart':
$plotSeries[] = self::chartDataSeries($chartDetail, $namespacesChartMeta, $chartDetailKey);
$plotAttributes = self::readChartAttributes($chartDetail);
break;
case 'areaChart':
case 'area3DChart':
$plotSeries[] = self::chartDataSeries($chartDetail, $namespacesChartMeta, $chartDetailKey);
$plotAttributes = self::readChartAttributes($chartDetail);
break;
case 'doughnutChart':
case 'pieChart':
case 'pie3DChart':
$explosion = isset($chartDetail->ser->explosion);
$plotSer = self::chartDataSeries($chartDetail, $namespacesChartMeta, $chartDetailKey);
$plotSer->setPlotStyle($explosion);
$plotSeries[] = $plotSer;
$plotAttributes = self::readChartAttributes($chartDetail);
break;
case 'scatterChart':
$scatterStyle = self::getAttribute($chartDetail->scatterStyle, 'val', 'string');
$plotSer = self::chartDataSeries($chartDetail, $namespacesChartMeta, $chartDetailKey);
$plotSer->setPlotStyle($scatterStyle);
$plotSeries[] = $plotSer;
$plotAttributes = self::readChartAttributes($chartDetail);
break;
case 'bubbleChart':
$bubbleScale = self::getAttribute($chartDetail->bubbleScale, 'val', 'integer');
$plotSer = self::chartDataSeries($chartDetail, $namespacesChartMeta, $chartDetailKey);
$plotSer->setPlotStyle($bubbleScale);
$plotSeries[] = $plotSer;
$plotAttributes = self::readChartAttributes($chartDetail);
break;
case 'radarChart':
$radarStyle = self::getAttribute($chartDetail->radarStyle, 'val', 'string');
$plotSer = self::chartDataSeries($chartDetail, $namespacesChartMeta, $chartDetailKey);
$plotSer->setPlotStyle($radarStyle);
$plotSeries[] = $plotSer;
$plotAttributes = self::readChartAttributes($chartDetail);
break;
case 'surfaceChart':
case 'surface3DChart':
$wireFrame = self::getAttribute($chartDetail->wireframe, 'val', 'boolean');
$plotSer = self::chartDataSeries($chartDetail, $namespacesChartMeta, $chartDetailKey);
$plotSer->setPlotStyle($wireFrame);
$plotSeries[] = $plotSer;
$plotAttributes = self::readChartAttributes($chartDetail);
break;
case 'stockChart':
$plotSeries[] = self::chartDataSeries($chartDetail, $namespacesChartMeta, $chartDetailKey);
$plotAttributes = self::readChartAttributes($plotAreaLayout);
break;
}
}
if ($plotAreaLayout == null) {
$plotAreaLayout = new Layout();
}
$plotArea = new PlotArea($plotAreaLayout, $plotSeries);
self::setChartAttributes($plotAreaLayout, $plotAttributes);
break;
case 'plotVisOnly':
$plotVisOnly = self::getAttribute($chartDetails, 'val', 'string');
break;
case 'dispBlanksAs':
$dispBlanksAs = self::getAttribute($chartDetails, 'val', 'string');
break;
case 'title':
$title = self::chartTitle($chartDetails, $namespacesChartMeta);
break;
case 'legend':
$legendPos = 'r';
$legendLayout = null;
$legendOverlay = false;
foreach ($chartDetails as $chartDetailKey => $chartDetail) {
switch ($chartDetailKey) {
case 'legendPos':
$legendPos = self::getAttribute($chartDetail, 'val', 'string');
break;
case 'overlay':
$legendOverlay = self::getAttribute($chartDetail, 'val', 'boolean');
break;
case 'layout':
$legendLayout = self::chartLayoutDetails($chartDetail, $namespacesChartMeta);
break;
}
}
$legend = new Legend($legendPos, $legendLayout, $legendOverlay);
break;
}
}
}
}
$chart = new \PhpOffice\PhpSpreadsheet\Chart\Chart($chartName, $title, $legend, $plotArea, $plotVisOnly, $dispBlanksAs, $XaxisLabel, $YaxisLabel);
return $chart;
}
private static function chartTitle(SimpleXMLElement $titleDetails, array $namespacesChartMeta)
{
$caption = [];
$titleLayout = null;
foreach ($titleDetails as $titleDetailKey => $chartDetail) {
switch ($titleDetailKey) {
case 'tx':
$titleDetails = $chartDetail->rich->children($namespacesChartMeta['a']);
foreach ($titleDetails as $titleKey => $titleDetail) {
switch ($titleKey) {
case 'p':
$titleDetailPart = $titleDetail->children($namespacesChartMeta['a']);
$caption[] = self::parseRichText($titleDetailPart);
}
}
break;
case 'layout':
$titleLayout = self::chartLayoutDetails($chartDetail, $namespacesChartMeta);
break;
}
}
return new Title($caption, $titleLayout);
}
private static function chartLayoutDetails($chartDetail, $namespacesChartMeta)
{
if (!isset($chartDetail->manualLayout)) {
return null;
}
$details = $chartDetail->manualLayout->children($namespacesChartMeta['c']);
if ($details === null) {
return null;
}
$layout = [];
foreach ($details as $detailKey => $detail) {
$layout[$detailKey] = self::getAttribute($detail, 'val', 'string');
}
return new Layout($layout);
}
private static function chartDataSeries($chartDetail, $namespacesChartMeta, $plotType)
{
$multiSeriesType = null;
$smoothLine = false;
$seriesLabel = $seriesCategory = $seriesValues = $plotOrder = [];
$seriesDetailSet = $chartDetail->children($namespacesChartMeta['c']);
foreach ($seriesDetailSet as $seriesDetailKey => $seriesDetails) {
switch ($seriesDetailKey) {
case 'grouping':
$multiSeriesType = self::getAttribute($chartDetail->grouping, 'val', 'string');
break;
case 'ser':
$marker = null;
$seriesIndex = '';
foreach ($seriesDetails as $seriesKey => $seriesDetail) {
switch ($seriesKey) {
case 'idx':
$seriesIndex = self::getAttribute($seriesDetail, 'val', 'integer');
break;
case 'order':
$seriesOrder = self::getAttribute($seriesDetail, 'val', 'integer');
$plotOrder[$seriesIndex] = $seriesOrder;
break;
case 'tx':
$seriesLabel[$seriesIndex] = self::chartDataSeriesValueSet($seriesDetail, $namespacesChartMeta);
break;
case 'marker':
$marker = self::getAttribute($seriesDetail->symbol, 'val', 'string');
break;
case 'smooth':
$smoothLine = self::getAttribute($seriesDetail, 'val', 'boolean');
break;
case 'cat':
$seriesCategory[$seriesIndex] = self::chartDataSeriesValueSet($seriesDetail, $namespacesChartMeta);
break;
case 'val':
$seriesValues[$seriesIndex] = self::chartDataSeriesValueSet($seriesDetail, $namespacesChartMeta, $marker);
break;
case 'xVal':
$seriesCategory[$seriesIndex] = self::chartDataSeriesValueSet($seriesDetail, $namespacesChartMeta, $marker);
break;
case 'yVal':
$seriesValues[$seriesIndex] = self::chartDataSeriesValueSet($seriesDetail, $namespacesChartMeta, $marker);
break;
}
}
}
}
return new DataSeries($plotType, $multiSeriesType, $plotOrder, $seriesLabel, $seriesCategory, $seriesValues, $smoothLine);
}
private static function chartDataSeriesValueSet($seriesDetail, $namespacesChartMeta, $marker = null)
{
if (isset($seriesDetail->strRef)) {
$seriesSource = (string) $seriesDetail->strRef->f;
$seriesData = self::chartDataSeriesValues($seriesDetail->strRef->strCache->children($namespacesChartMeta['c']), 's');
return new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, $seriesSource, $seriesData['formatCode'], $seriesData['pointCount'], $seriesData['dataValues'], $marker);
} elseif (isset($seriesDetail->numRef)) {
$seriesSource = (string) $seriesDetail->numRef->f;
$seriesData = self::chartDataSeriesValues($seriesDetail->numRef->numCache->children($namespacesChartMeta['c']));
return new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_NUMBER, $seriesSource, $seriesData['formatCode'], $seriesData['pointCount'], $seriesData['dataValues'], $marker);
} elseif (isset($seriesDetail->multiLvlStrRef)) {
$seriesSource = (string) $seriesDetail->multiLvlStrRef->f;
$seriesData = self::chartDataSeriesValuesMultiLevel($seriesDetail->multiLvlStrRef->multiLvlStrCache->children($namespacesChartMeta['c']), 's');
$seriesData['pointCount'] = count($seriesData['dataValues']);
return new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, $seriesSource, $seriesData['formatCode'], $seriesData['pointCount'], $seriesData['dataValues'], $marker);
} elseif (isset($seriesDetail->multiLvlNumRef)) {
$seriesSource = (string) $seriesDetail->multiLvlNumRef->f;
$seriesData = self::chartDataSeriesValuesMultiLevel($seriesDetail->multiLvlNumRef->multiLvlNumCache->children($namespacesChartMeta['c']), 's');
$seriesData['pointCount'] = count($seriesData['dataValues']);
return new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, $seriesSource, $seriesData['formatCode'], $seriesData['pointCount'], $seriesData['dataValues'], $marker);
}
return null;
}
private static function chartDataSeriesValues($seriesValueSet, $dataType = 'n')
{
$seriesVal = [];
$formatCode = '';
$pointCount = 0;
foreach ($seriesValueSet as $seriesValueIdx => $seriesValue) {
switch ($seriesValueIdx) {
case 'ptCount':
$pointCount = self::getAttribute($seriesValue, 'val', 'integer');
break;
case 'formatCode':
$formatCode = (string) $seriesValue;
break;
case 'pt':
$pointVal = self::getAttribute($seriesValue, 'idx', 'integer');
if ($dataType == 's') {
$seriesVal[$pointVal] = (string) $seriesValue->v;
} elseif ($seriesValue->v === Functions::NA()) {
$seriesVal[$pointVal] = null;
} else {
$seriesVal[$pointVal] = (float) $seriesValue->v;
}
break;
}
}
return [
'formatCode' => $formatCode,
'pointCount' => $pointCount,
'dataValues' => $seriesVal,
];
}
private static function chartDataSeriesValuesMultiLevel($seriesValueSet, $dataType = 'n')
{
$seriesVal = [];
$formatCode = '';
$pointCount = 0;
foreach ($seriesValueSet->lvl as $seriesLevelIdx => $seriesLevel) {
foreach ($seriesLevel as $seriesValueIdx => $seriesValue) {
switch ($seriesValueIdx) {
case 'ptCount':
$pointCount = self::getAttribute($seriesValue, 'val', 'integer');
break;
case 'formatCode':
$formatCode = (string) $seriesValue;
break;
case 'pt':
$pointVal = self::getAttribute($seriesValue, 'idx', 'integer');
if ($dataType == 's') {
$seriesVal[$pointVal][] = (string) $seriesValue->v;
} elseif ($seriesValue->v === Functions::NA()) {
$seriesVal[$pointVal] = null;
} else {
$seriesVal[$pointVal][] = (float) $seriesValue->v;
}
break;
}
}
}
return [
'formatCode' => $formatCode,
'pointCount' => $pointCount,
'dataValues' => $seriesVal,
];
}
private static function parseRichText(SimpleXMLElement $titleDetailPart)
{
$value = new RichText();
$objText = null;
foreach ($titleDetailPart as $titleDetailElementKey => $titleDetailElement) {
if (isset($titleDetailElement->t)) {
$objText = $value->createTextRun((string) $titleDetailElement->t);
}
if (isset($titleDetailElement->rPr)) {
if (isset($titleDetailElement->rPr->rFont['val'])) {
$objText->getFont()->setName((string) $titleDetailElement->rPr->rFont['val']);
}
$fontSize = (self::getAttribute($titleDetailElement->rPr, 'sz', 'integer'));
if ($fontSize !== null) {
$objText->getFont()->setSize(floor($fontSize / 100));
}
$fontColor = (self::getAttribute($titleDetailElement->rPr, 'color', 'string'));
if ($fontColor !== null) {
$objText->getFont()->setColor(new Color(self::readColor($fontColor)));
}
$bold = self::getAttribute($titleDetailElement->rPr, 'b', 'boolean');
if ($bold !== null) {
$objText->getFont()->setBold($bold);
}
$italic = self::getAttribute($titleDetailElement->rPr, 'i', 'boolean');
if ($italic !== null) {
$objText->getFont()->setItalic($italic);
}
$baseline = self::getAttribute($titleDetailElement->rPr, 'baseline', 'integer');
if ($baseline !== null) {
if ($baseline > 0) {
$objText->getFont()->setSuperscript(true);
} elseif ($baseline < 0) {
$objText->getFont()->setSubscript(true);
}
}
$underscore = (self::getAttribute($titleDetailElement->rPr, 'u', 'string'));
if ($underscore !== null) {
if ($underscore == 'sng') {
$objText->getFont()->setUnderline(Font::UNDERLINE_SINGLE);
} elseif ($underscore == 'dbl') {
$objText->getFont()->setUnderline(Font::UNDERLINE_DOUBLE);
} else {
$objText->getFont()->setUnderline(Font::UNDERLINE_NONE);
}
}
$strikethrough = (self::getAttribute($titleDetailElement->rPr, 's', 'string'));
if ($strikethrough !== null) {
if ($strikethrough == 'noStrike') {
$objText->getFont()->setStrikethrough(false);
} else {
$objText->getFont()->setStrikethrough(true);
}
}
}
}
return $value;
}
private static function readChartAttributes($chartDetail)
{
$plotAttributes = [];
if (isset($chartDetail->dLbls)) {
if (isset($chartDetail->dLbls->howLegendKey)) {
$plotAttributes['showLegendKey'] = self::getAttribute($chartDetail->dLbls->showLegendKey, 'val', 'string');
}
if (isset($chartDetail->dLbls->showVal)) {
$plotAttributes['showVal'] = self::getAttribute($chartDetail->dLbls->showVal, 'val', 'string');
}
if (isset($chartDetail->dLbls->showCatName)) {
$plotAttributes['showCatName'] = self::getAttribute($chartDetail->dLbls->showCatName, 'val', 'string');
}
if (isset($chartDetail->dLbls->showSerName)) {
$plotAttributes['showSerName'] = self::getAttribute($chartDetail->dLbls->showSerName, 'val', 'string');
}
if (isset($chartDetail->dLbls->showPercent)) {
$plotAttributes['showPercent'] = self::getAttribute($chartDetail->dLbls->showPercent, 'val', 'string');
}
if (isset($chartDetail->dLbls->showBubbleSize)) {
$plotAttributes['showBubbleSize'] = self::getAttribute($chartDetail->dLbls->showBubbleSize, 'val', 'string');
}
if (isset($chartDetail->dLbls->showLeaderLines)) {
$plotAttributes['showLeaderLines'] = self::getAttribute($chartDetail->dLbls->showLeaderLines, 'val', 'string');
}
}
return $plotAttributes;
}
/**
* @param mixed $plotAttributes
*/
private static function setChartAttributes(Layout $plotArea, $plotAttributes): void
{
foreach ($plotAttributes as $plotAttributeKey => $plotAttributeValue) {
switch ($plotAttributeKey) {
case 'showLegendKey':
$plotArea->setShowLegendKey($plotAttributeValue);
break;
case 'showVal':
$plotArea->setShowVal($plotAttributeValue);
break;
case 'showCatName':
$plotArea->setShowCatName($plotAttributeValue);
break;
case 'showSerName':
$plotArea->setShowSerName($plotAttributeValue);
break;
case 'showPercent':
$plotArea->setShowPercent($plotAttributeValue);
break;
case 'showBubbleSize':
$plotArea->setShowBubbleSize($plotAttributeValue);
break;
case 'showLeaderLines':
$plotArea->setShowLeaderLines($plotAttributeValue);
break;
}
}
}
}

View File

@@ -0,0 +1,209 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Reader\Xlsx;
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
use PhpOffice\PhpSpreadsheet\Reader\IReadFilter;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
use SimpleXMLElement;
class ColumnAndRowAttributes extends BaseParserClass
{
private $worksheet;
private $worksheetXml;
public function __construct(Worksheet $workSheet, ?SimpleXMLElement $worksheetXml = null)
{
$this->worksheet = $workSheet;
$this->worksheetXml = $worksheetXml;
}
/**
* Set Worksheet column attributes by attributes array passed.
*
* @param string $columnAddress A, B, ... DX, ...
* @param array $columnAttributes array of attributes (indexes are attribute name, values are value)
* 'xfIndex', 'visible', 'collapsed', 'outlineLevel', 'width', ... ?
*/
private function setColumnAttributes($columnAddress, array $columnAttributes): void
{
if (isset($columnAttributes['xfIndex'])) {
$this->worksheet->getColumnDimension($columnAddress)->setXfIndex($columnAttributes['xfIndex']);
}
if (isset($columnAttributes['visible'])) {
$this->worksheet->getColumnDimension($columnAddress)->setVisible($columnAttributes['visible']);
}
if (isset($columnAttributes['collapsed'])) {
$this->worksheet->getColumnDimension($columnAddress)->setCollapsed($columnAttributes['collapsed']);
}
if (isset($columnAttributes['outlineLevel'])) {
$this->worksheet->getColumnDimension($columnAddress)->setOutlineLevel($columnAttributes['outlineLevel']);
}
if (isset($columnAttributes['width'])) {
$this->worksheet->getColumnDimension($columnAddress)->setWidth($columnAttributes['width']);
}
}
/**
* Set Worksheet row attributes by attributes array passed.
*
* @param int $rowNumber 1, 2, 3, ... 99, ...
* @param array $rowAttributes array of attributes (indexes are attribute name, values are value)
* 'xfIndex', 'visible', 'collapsed', 'outlineLevel', 'rowHeight', ... ?
*/
private function setRowAttributes($rowNumber, array $rowAttributes): void
{
if (isset($rowAttributes['xfIndex'])) {
$this->worksheet->getRowDimension($rowNumber)->setXfIndex($rowAttributes['xfIndex']);
}
if (isset($rowAttributes['visible'])) {
$this->worksheet->getRowDimension($rowNumber)->setVisible($rowAttributes['visible']);
}
if (isset($rowAttributes['collapsed'])) {
$this->worksheet->getRowDimension($rowNumber)->setCollapsed($rowAttributes['collapsed']);
}
if (isset($rowAttributes['outlineLevel'])) {
$this->worksheet->getRowDimension($rowNumber)->setOutlineLevel($rowAttributes['outlineLevel']);
}
if (isset($rowAttributes['rowHeight'])) {
$this->worksheet->getRowDimension($rowNumber)->setRowHeight($rowAttributes['rowHeight']);
}
}
/**
* @param IReadFilter $readFilter
* @param bool $readDataOnly
*/
public function load(?IReadFilter $readFilter = null, $readDataOnly = false): void
{
if ($this->worksheetXml === null) {
return;
}
$columnsAttributes = [];
$rowsAttributes = [];
if (isset($this->worksheetXml->cols)) {
$columnsAttributes = $this->readColumnAttributes($this->worksheetXml->cols, $readDataOnly);
}
if ($this->worksheetXml->sheetData && $this->worksheetXml->sheetData->row) {
$rowsAttributes = $this->readRowAttributes($this->worksheetXml->sheetData->row, $readDataOnly);
}
// set columns/rows attributes
$columnsAttributesAreSet = [];
foreach ($columnsAttributes as $columnCoordinate => $columnAttributes) {
if (
$readFilter === null ||
!$this->isFilteredColumn($readFilter, $columnCoordinate, $rowsAttributes)
) {
if (!isset($columnsAttributesAreSet[$columnCoordinate])) {
$this->setColumnAttributes($columnCoordinate, $columnAttributes);
$columnsAttributesAreSet[$columnCoordinate] = true;
}
}
}
$rowsAttributesAreSet = [];
foreach ($rowsAttributes as $rowCoordinate => $rowAttributes) {
if (
$readFilter === null ||
!$this->isFilteredRow($readFilter, $rowCoordinate, $columnsAttributes)
) {
if (!isset($rowsAttributesAreSet[$rowCoordinate])) {
$this->setRowAttributes($rowCoordinate, $rowAttributes);
$rowsAttributesAreSet[$rowCoordinate] = true;
}
}
}
}
private function isFilteredColumn(IReadFilter $readFilter, $columnCoordinate, array $rowsAttributes)
{
foreach ($rowsAttributes as $rowCoordinate => $rowAttributes) {
if (!$readFilter->readCell($columnCoordinate, $rowCoordinate, $this->worksheet->getTitle())) {
return true;
}
}
return false;
}
private function readColumnAttributes(SimpleXMLElement $worksheetCols, $readDataOnly)
{
$columnAttributes = [];
foreach ($worksheetCols->col as $column) {
$startColumn = Coordinate::stringFromColumnIndex((int) $column['min']);
$endColumn = Coordinate::stringFromColumnIndex((int) $column['max']);
++$endColumn;
for ($columnAddress = $startColumn; $columnAddress !== $endColumn; ++$columnAddress) {
$columnAttributes[$columnAddress] = $this->readColumnRangeAttributes($column, $readDataOnly);
if ((int) ($column['max']) == 16384) {
break;
}
}
}
return $columnAttributes;
}
private function readColumnRangeAttributes(SimpleXMLElement $column, $readDataOnly)
{
$columnAttributes = [];
if ($column['style'] && !$readDataOnly) {
$columnAttributes['xfIndex'] = (int) $column['style'];
}
if (self::boolean($column['hidden'])) {
$columnAttributes['visible'] = false;
}
if (self::boolean($column['collapsed'])) {
$columnAttributes['collapsed'] = true;
}
if (((int) $column['outlineLevel']) > 0) {
$columnAttributes['outlineLevel'] = (int) $column['outlineLevel'];
}
$columnAttributes['width'] = (float) $column['width'];
return $columnAttributes;
}
private function isFilteredRow(IReadFilter $readFilter, $rowCoordinate, array $columnsAttributes)
{
foreach ($columnsAttributes as $columnCoordinate => $columnAttributes) {
if (!$readFilter->readCell($columnCoordinate, $rowCoordinate, $this->worksheet->getTitle())) {
return true;
}
}
return false;
}
private function readRowAttributes(SimpleXMLElement $worksheetRow, $readDataOnly)
{
$rowAttributes = [];
foreach ($worksheetRow as $row) {
if ($row['ht'] && !$readDataOnly) {
$rowAttributes[(int) $row['r']]['rowHeight'] = (float) $row['ht'];
}
if (self::boolean($row['hidden'])) {
$rowAttributes[(int) $row['r']]['visible'] = false;
}
if (self::boolean($row['collapsed'])) {
$rowAttributes[(int) $row['r']]['collapsed'] = true;
}
if ((int) $row['outlineLevel'] > 0) {
$rowAttributes[(int) $row['r']]['outlineLevel'] = (int) $row['outlineLevel'];
}
if ($row['s'] && !$readDataOnly) {
$rowAttributes[(int) $row['r']]['xfIndex'] = (int) $row['s'];
}
}
return $rowAttributes;
}
}

View File

@@ -0,0 +1,97 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Reader\Xlsx;
use PhpOffice\PhpSpreadsheet\Style\Conditional;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
use SimpleXMLElement;
class ConditionalStyles
{
private $worksheet;
private $worksheetXml;
private $dxfs;
public function __construct(Worksheet $workSheet, SimpleXMLElement $worksheetXml, array $dxfs = [])
{
$this->worksheet = $workSheet;
$this->worksheetXml = $worksheetXml;
$this->dxfs = $dxfs;
}
public function load(): void
{
$this->setConditionalStyles(
$this->worksheet,
$this->readConditionalStyles($this->worksheetXml)
);
}
private function readConditionalStyles($xmlSheet)
{
$conditionals = [];
foreach ($xmlSheet->conditionalFormatting as $conditional) {
foreach ($conditional->cfRule as $cfRule) {
if (
((string) $cfRule['type'] == Conditional::CONDITION_NONE
|| (string) $cfRule['type'] == Conditional::CONDITION_CELLIS
|| (string) $cfRule['type'] == Conditional::CONDITION_CONTAINSTEXT
|| (string) $cfRule['type'] == Conditional::CONDITION_CONTAINSBLANKS
|| (string) $cfRule['type'] == Conditional::CONDITION_NOTCONTAINSBLANKS
|| (string) $cfRule['type'] == Conditional::CONDITION_EXPRESSION)
&& isset($this->dxfs[(int) ($cfRule['dxfId'])])
) {
$conditionals[(string) $conditional['sqref']][(int) ($cfRule['priority'])] = $cfRule;
}
}
}
return $conditionals;
}
private function setConditionalStyles(Worksheet $worksheet, array $conditionals): void
{
foreach ($conditionals as $ref => $cfRules) {
ksort($cfRules);
$conditionalStyles = $this->readStyleRules($cfRules);
// Extract all cell references in $ref
$cellBlocks = explode(' ', str_replace('$', '', strtoupper($ref)));
foreach ($cellBlocks as $cellBlock) {
$worksheet->getStyle($cellBlock)->setConditionalStyles($conditionalStyles);
}
}
}
private function readStyleRules($cfRules)
{
$conditionalStyles = [];
foreach ($cfRules as $cfRule) {
$objConditional = new Conditional();
$objConditional->setConditionType((string) $cfRule['type']);
$objConditional->setOperatorType((string) $cfRule['operator']);
if ((string) $cfRule['text'] != '') {
$objConditional->setText((string) $cfRule['text']);
}
if (isset($cfRule['stopIfTrue']) && (int) $cfRule['stopIfTrue'] === 1) {
$objConditional->setStopIfTrue(true);
}
if (count($cfRule->formula) > 1) {
foreach ($cfRule->formula as $formula) {
$objConditional->addCondition((string) $formula);
}
} else {
$objConditional->addCondition((string) $cfRule->formula);
}
$objConditional->setStyle(clone $this->dxfs[(int) ($cfRule['dxfId'])]);
$conditionalStyles[] = $objConditional;
}
return $conditionalStyles;
}
}

View File

@@ -0,0 +1,51 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Reader\Xlsx;
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
use SimpleXMLElement;
class DataValidations
{
private $worksheet;
private $worksheetXml;
public function __construct(Worksheet $workSheet, SimpleXMLElement $worksheetXml)
{
$this->worksheet = $workSheet;
$this->worksheetXml = $worksheetXml;
}
public function load(): void
{
foreach ($this->worksheetXml->dataValidations->dataValidation as $dataValidation) {
// Uppercase coordinate
$range = strtoupper($dataValidation['sqref']);
$rangeSet = explode(' ', $range);
foreach ($rangeSet as $range) {
$stRange = $this->worksheet->shrinkRangeToFit($range);
// Extract all cell references in $range
foreach (Coordinate::extractAllCellReferencesInRange($stRange) as $reference) {
// Create validation
$docValidation = $this->worksheet->getCell($reference)->getDataValidation();
$docValidation->setType((string) $dataValidation['type']);
$docValidation->setErrorStyle((string) $dataValidation['errorStyle']);
$docValidation->setOperator((string) $dataValidation['operator']);
$docValidation->setAllowBlank($dataValidation['allowBlank'] != 0);
$docValidation->setShowDropDown($dataValidation['showDropDown'] == 0);
$docValidation->setShowInputMessage($dataValidation['showInputMessage'] != 0);
$docValidation->setShowErrorMessage($dataValidation['showErrorMessage'] != 0);
$docValidation->setErrorTitle((string) $dataValidation['errorTitle']);
$docValidation->setError((string) $dataValidation['error']);
$docValidation->setPromptTitle((string) $dataValidation['promptTitle']);
$docValidation->setPrompt((string) $dataValidation['prompt']);
$docValidation->setFormula1((string) $dataValidation->formula1);
$docValidation->setFormula2((string) $dataValidation->formula2);
}
}
}
}
}

View File

@@ -0,0 +1,59 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Reader\Xlsx;
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
use SimpleXMLElement;
class Hyperlinks
{
private $worksheet;
private $hyperlinks = [];
public function __construct(Worksheet $workSheet)
{
$this->worksheet = $workSheet;
}
public function readHyperlinks(SimpleXMLElement $relsWorksheet): void
{
foreach ($relsWorksheet->Relationship as $element) {
if ($element['Type'] == 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink') {
$this->hyperlinks[(string) $element['Id']] = (string) $element['Target'];
}
}
}
public function setHyperlinks(SimpleXMLElement $worksheetXml): void
{
foreach ($worksheetXml->hyperlink as $hyperlink) {
$this->setHyperlink($hyperlink, $this->worksheet);
}
}
private function setHyperlink(SimpleXMLElement $hyperlink, Worksheet $worksheet): void
{
// Link url
$linkRel = $hyperlink->attributes('http://schemas.openxmlformats.org/officeDocument/2006/relationships');
foreach (Coordinate::extractAllCellReferencesInRange($hyperlink['ref']) as $cellReference) {
$cell = $worksheet->getCell($cellReference);
if (isset($linkRel['id'])) {
$hyperlinkUrl = $this->hyperlinks[(string) $linkRel['id']];
if (isset($hyperlink['location'])) {
$hyperlinkUrl .= '#' . (string) $hyperlink['location'];
}
$cell->getHyperlink()->setUrl($hyperlinkUrl);
} elseif (isset($hyperlink['location'])) {
$cell->getHyperlink()->setUrl('sheet://' . (string) $hyperlink['location']);
}
// Tooltip
if (isset($hyperlink['tooltip'])) {
$cell->getHyperlink()->setTooltip((string) $hyperlink['tooltip']);
}
}
}
}

View File

@@ -0,0 +1,164 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Reader\Xlsx;
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
use SimpleXMLElement;
class PageSetup extends BaseParserClass
{
private $worksheet;
private $worksheetXml;
public function __construct(Worksheet $workSheet, ?SimpleXMLElement $worksheetXml = null)
{
$this->worksheet = $workSheet;
$this->worksheetXml = $worksheetXml;
}
public function load(array $unparsedLoadedData)
{
if (!$this->worksheetXml) {
return $unparsedLoadedData;
}
$this->margins($this->worksheetXml, $this->worksheet);
$unparsedLoadedData = $this->pageSetup($this->worksheetXml, $this->worksheet, $unparsedLoadedData);
$this->headerFooter($this->worksheetXml, $this->worksheet);
$this->pageBreaks($this->worksheetXml, $this->worksheet);
return $unparsedLoadedData;
}
private function margins(SimpleXMLElement $xmlSheet, Worksheet $worksheet): void
{
if ($xmlSheet->pageMargins) {
$docPageMargins = $worksheet->getPageMargins();
$docPageMargins->setLeft((float) ($xmlSheet->pageMargins['left']));
$docPageMargins->setRight((float) ($xmlSheet->pageMargins['right']));
$docPageMargins->setTop((float) ($xmlSheet->pageMargins['top']));
$docPageMargins->setBottom((float) ($xmlSheet->pageMargins['bottom']));
$docPageMargins->setHeader((float) ($xmlSheet->pageMargins['header']));
$docPageMargins->setFooter((float) ($xmlSheet->pageMargins['footer']));
}
}
private function pageSetup(SimpleXMLElement $xmlSheet, Worksheet $worksheet, array $unparsedLoadedData)
{
if ($xmlSheet->pageSetup) {
$docPageSetup = $worksheet->getPageSetup();
if (isset($xmlSheet->pageSetup['orientation'])) {
$docPageSetup->setOrientation((string) $xmlSheet->pageSetup['orientation']);
}
if (isset($xmlSheet->pageSetup['paperSize'])) {
$docPageSetup->setPaperSize((int) ($xmlSheet->pageSetup['paperSize']));
}
if (isset($xmlSheet->pageSetup['scale'])) {
$docPageSetup->setScale((int) ($xmlSheet->pageSetup['scale']), false);
}
if (isset($xmlSheet->pageSetup['fitToHeight']) && (int) ($xmlSheet->pageSetup['fitToHeight']) >= 0) {
$docPageSetup->setFitToHeight((int) ($xmlSheet->pageSetup['fitToHeight']), false);
}
if (isset($xmlSheet->pageSetup['fitToWidth']) && (int) ($xmlSheet->pageSetup['fitToWidth']) >= 0) {
$docPageSetup->setFitToWidth((int) ($xmlSheet->pageSetup['fitToWidth']), false);
}
if (
isset($xmlSheet->pageSetup['firstPageNumber'], $xmlSheet->pageSetup['useFirstPageNumber']) &&
self::boolean((string) $xmlSheet->pageSetup['useFirstPageNumber'])
) {
$docPageSetup->setFirstPageNumber((int) ($xmlSheet->pageSetup['firstPageNumber']));
}
if (isset($xmlSheet->pageSetup['pageOrder'])) {
$docPageSetup->setPageOrder((string) $xmlSheet->pageSetup['pageOrder']);
}
$relAttributes = $xmlSheet->pageSetup->attributes('http://schemas.openxmlformats.org/officeDocument/2006/relationships');
if (isset($relAttributes['id'])) {
$unparsedLoadedData['sheets'][$worksheet->getCodeName()]['pageSetupRelId'] = (string) $relAttributes['id'];
}
}
return $unparsedLoadedData;
}
private function headerFooter(SimpleXMLElement $xmlSheet, Worksheet $worksheet): void
{
if ($xmlSheet->headerFooter) {
$docHeaderFooter = $worksheet->getHeaderFooter();
if (
isset($xmlSheet->headerFooter['differentOddEven']) &&
self::boolean((string) $xmlSheet->headerFooter['differentOddEven'])
) {
$docHeaderFooter->setDifferentOddEven(true);
} else {
$docHeaderFooter->setDifferentOddEven(false);
}
if (
isset($xmlSheet->headerFooter['differentFirst']) &&
self::boolean((string) $xmlSheet->headerFooter['differentFirst'])
) {
$docHeaderFooter->setDifferentFirst(true);
} else {
$docHeaderFooter->setDifferentFirst(false);
}
if (
isset($xmlSheet->headerFooter['scaleWithDoc']) &&
!self::boolean((string) $xmlSheet->headerFooter['scaleWithDoc'])
) {
$docHeaderFooter->setScaleWithDocument(false);
} else {
$docHeaderFooter->setScaleWithDocument(true);
}
if (
isset($xmlSheet->headerFooter['alignWithMargins']) &&
!self::boolean((string) $xmlSheet->headerFooter['alignWithMargins'])
) {
$docHeaderFooter->setAlignWithMargins(false);
} else {
$docHeaderFooter->setAlignWithMargins(true);
}
$docHeaderFooter->setOddHeader((string) $xmlSheet->headerFooter->oddHeader);
$docHeaderFooter->setOddFooter((string) $xmlSheet->headerFooter->oddFooter);
$docHeaderFooter->setEvenHeader((string) $xmlSheet->headerFooter->evenHeader);
$docHeaderFooter->setEvenFooter((string) $xmlSheet->headerFooter->evenFooter);
$docHeaderFooter->setFirstHeader((string) $xmlSheet->headerFooter->firstHeader);
$docHeaderFooter->setFirstFooter((string) $xmlSheet->headerFooter->firstFooter);
}
}
private function pageBreaks(SimpleXMLElement $xmlSheet, Worksheet $worksheet): void
{
if ($xmlSheet->rowBreaks && $xmlSheet->rowBreaks->brk) {
$this->rowBreaks($xmlSheet, $worksheet);
}
if ($xmlSheet->colBreaks && $xmlSheet->colBreaks->brk) {
$this->columnBreaks($xmlSheet, $worksheet);
}
}
private function rowBreaks(SimpleXMLElement $xmlSheet, Worksheet $worksheet): void
{
foreach ($xmlSheet->rowBreaks->brk as $brk) {
if ($brk['man']) {
$worksheet->setBreak("A{$brk['id']}", Worksheet::BREAK_ROW);
}
}
}
private function columnBreaks(SimpleXMLElement $xmlSheet, Worksheet $worksheet): void
{
foreach ($xmlSheet->colBreaks->brk as $brk) {
if ($brk['man']) {
$worksheet->setBreak(
Coordinate::stringFromColumnIndex(((int) $brk['id']) + 1) . '1',
Worksheet::BREAK_COLUMN
);
}
}
}
}

View File

@@ -0,0 +1,92 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Reader\Xlsx;
use PhpOffice\PhpSpreadsheet\Document\Properties as DocumentProperties;
use PhpOffice\PhpSpreadsheet\Reader\Security\XmlScanner;
use PhpOffice\PhpSpreadsheet\Settings;
use SimpleXMLElement;
class Properties
{
private $securityScanner;
private $docProps;
public function __construct(XmlScanner $securityScanner, DocumentProperties $docProps)
{
$this->securityScanner = $securityScanner;
$this->docProps = $docProps;
}
private function extractPropertyData($propertyData)
{
return simplexml_load_string(
$this->securityScanner->scan($propertyData),
'SimpleXMLElement',
Settings::getLibXmlLoaderOptions()
);
}
public function readCoreProperties($propertyData): void
{
$xmlCore = $this->extractPropertyData($propertyData);
if (is_object($xmlCore)) {
$xmlCore->registerXPathNamespace('dc', 'http://purl.org/dc/elements/1.1/');
$xmlCore->registerXPathNamespace('dcterms', 'http://purl.org/dc/terms/');
$xmlCore->registerXPathNamespace('cp', 'http://schemas.openxmlformats.org/package/2006/metadata/core-properties');
$this->docProps->setCreator((string) self::getArrayItem($xmlCore->xpath('dc:creator')));
$this->docProps->setLastModifiedBy((string) self::getArrayItem($xmlCore->xpath('cp:lastModifiedBy')));
$this->docProps->setCreated(strtotime(self::getArrayItem($xmlCore->xpath('dcterms:created')))); //! respect xsi:type
$this->docProps->setModified(strtotime(self::getArrayItem($xmlCore->xpath('dcterms:modified')))); //! respect xsi:type
$this->docProps->setTitle((string) self::getArrayItem($xmlCore->xpath('dc:title')));
$this->docProps->setDescription((string) self::getArrayItem($xmlCore->xpath('dc:description')));
$this->docProps->setSubject((string) self::getArrayItem($xmlCore->xpath('dc:subject')));
$this->docProps->setKeywords((string) self::getArrayItem($xmlCore->xpath('cp:keywords')));
$this->docProps->setCategory((string) self::getArrayItem($xmlCore->xpath('cp:category')));
}
}
public function readExtendedProperties($propertyData): void
{
$xmlCore = $this->extractPropertyData($propertyData);
if (is_object($xmlCore)) {
if (isset($xmlCore->Company)) {
$this->docProps->setCompany((string) $xmlCore->Company);
}
if (isset($xmlCore->Manager)) {
$this->docProps->setManager((string) $xmlCore->Manager);
}
}
}
public function readCustomProperties($propertyData): void
{
$xmlCore = $this->extractPropertyData($propertyData);
if (is_object($xmlCore)) {
foreach ($xmlCore as $xmlProperty) {
/** @var SimpleXMLElement $xmlProperty */
$cellDataOfficeAttributes = $xmlProperty->attributes();
if (isset($cellDataOfficeAttributes['name'])) {
$propertyName = (string) $cellDataOfficeAttributes['name'];
$cellDataOfficeChildren = $xmlProperty->children('http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes');
$attributeType = $cellDataOfficeChildren->getName();
$attributeValue = (string) $cellDataOfficeChildren->{$attributeType};
$attributeValue = DocumentProperties::convertProperty($attributeValue, $attributeType);
$attributeType = DocumentProperties::convertPropertyType($attributeType);
$this->docProps->setCustomProperty($propertyName, $attributeValue, $attributeType);
}
}
}
}
private static function getArrayItem(array $array, $key = 0)
{
return $array[$key] ?? null;
}
}

View File

@@ -0,0 +1,135 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Reader\Xlsx;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
use SimpleXMLElement;
class SheetViewOptions extends BaseParserClass
{
private $worksheet;
private $worksheetXml;
public function __construct(Worksheet $workSheet, ?SimpleXMLElement $worksheetXml = null)
{
$this->worksheet = $workSheet;
$this->worksheetXml = $worksheetXml;
}
/**
* @param bool $readDataOnly
*/
public function load($readDataOnly = false): void
{
if ($this->worksheetXml === null) {
return;
}
if (isset($this->worksheetXml->sheetPr)) {
$this->tabColor($this->worksheetXml->sheetPr);
$this->codeName($this->worksheetXml->sheetPr);
$this->outlines($this->worksheetXml->sheetPr);
$this->pageSetup($this->worksheetXml->sheetPr);
}
if (isset($this->worksheetXml->sheetFormatPr)) {
$this->sheetFormat($this->worksheetXml->sheetFormatPr);
}
if (!$readDataOnly && isset($this->worksheetXml->printOptions)) {
$this->printOptions($this->worksheetXml->printOptions);
}
}
private function tabColor(SimpleXMLElement $sheetPr): void
{
if (isset($sheetPr->tabColor, $sheetPr->tabColor['rgb'])) {
$this->worksheet->getTabColor()->setARGB((string) $sheetPr->tabColor['rgb']);
}
}
private function codeName(SimpleXMLElement $sheetPr): void
{
if (isset($sheetPr['codeName'])) {
$this->worksheet->setCodeName((string) $sheetPr['codeName'], false);
}
}
private function outlines(SimpleXMLElement $sheetPr): void
{
if (isset($sheetPr->outlinePr)) {
if (
isset($sheetPr->outlinePr['summaryRight']) &&
!self::boolean((string) $sheetPr->outlinePr['summaryRight'])
) {
$this->worksheet->setShowSummaryRight(false);
} else {
$this->worksheet->setShowSummaryRight(true);
}
if (
isset($sheetPr->outlinePr['summaryBelow']) &&
!self::boolean((string) $sheetPr->outlinePr['summaryBelow'])
) {
$this->worksheet->setShowSummaryBelow(false);
} else {
$this->worksheet->setShowSummaryBelow(true);
}
}
}
private function pageSetup(SimpleXMLElement $sheetPr): void
{
if (isset($sheetPr->pageSetUpPr)) {
if (
isset($sheetPr->pageSetUpPr['fitToPage']) &&
!self::boolean((string) $sheetPr->pageSetUpPr['fitToPage'])
) {
$this->worksheet->getPageSetup()->setFitToPage(false);
} else {
$this->worksheet->getPageSetup()->setFitToPage(true);
}
}
}
private function sheetFormat(SimpleXMLElement $sheetFormatPr): void
{
if (
isset($sheetFormatPr['customHeight']) &&
self::boolean((string) $sheetFormatPr['customHeight']) &&
isset($sheetFormatPr['defaultRowHeight'])
) {
$this->worksheet->getDefaultRowDimension()
->setRowHeight((float) $sheetFormatPr['defaultRowHeight']);
}
if (isset($sheetFormatPr['defaultColWidth'])) {
$this->worksheet->getDefaultColumnDimension()
->setWidth((float) $sheetFormatPr['defaultColWidth']);
}
if (
isset($sheetFormatPr['zeroHeight']) &&
((string) $sheetFormatPr['zeroHeight'] === '1')
) {
$this->worksheet->getDefaultRowDimension()->setZeroHeight(true);
}
}
private function printOptions(SimpleXMLElement $printOptions): void
{
if (self::boolean((string) $printOptions['gridLinesSet'])) {
$this->worksheet->setShowGridlines(true);
}
if (self::boolean((string) $printOptions['gridLines'])) {
$this->worksheet->setPrintGridlines(true);
}
if (self::boolean((string) $printOptions['horizontalCentered'])) {
$this->worksheet->getPageSetup()->setHorizontalCentered(true);
}
if (self::boolean((string) $printOptions['verticalCentered'])) {
$this->worksheet->getPageSetup()->setVerticalCentered(true);
}
}
}

View File

@@ -0,0 +1,138 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Reader\Xlsx;
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
use SimpleXMLElement;
class SheetViews extends BaseParserClass
{
private $sheetViewXml;
private $worksheet;
public function __construct(SimpleXMLElement $sheetViewXml, Worksheet $workSheet)
{
$this->sheetViewXml = $sheetViewXml;
$this->worksheet = $workSheet;
}
public function load(): void
{
$this->zoomScale();
$this->view();
$this->gridLines();
$this->headers();
$this->direction();
$this->showZeros();
if (isset($this->sheetViewXml->pane)) {
$this->pane();
}
if (isset($this->sheetViewXml->selection, $this->sheetViewXml->selection['sqref'])) {
$this->selection();
}
}
private function zoomScale(): void
{
if (isset($this->sheetViewXml['zoomScale'])) {
$zoomScale = (int) ($this->sheetViewXml['zoomScale']);
if ($zoomScale <= 0) {
// setZoomScale will throw an Exception if the scale is less than or equals 0
// that is OK when manually creating documents, but we should be able to read all documents
$zoomScale = 100;
}
$this->worksheet->getSheetView()->setZoomScale($zoomScale);
}
if (isset($this->sheetViewXml['zoomScaleNormal'])) {
$zoomScaleNormal = (int) ($this->sheetViewXml['zoomScaleNormal']);
if ($zoomScaleNormal <= 0) {
// setZoomScaleNormal will throw an Exception if the scale is less than or equals 0
// that is OK when manually creating documents, but we should be able to read all documents
$zoomScaleNormal = 100;
}
$this->worksheet->getSheetView()->setZoomScaleNormal($zoomScaleNormal);
}
}
private function view(): void
{
if (isset($this->sheetViewXml['view'])) {
$this->worksheet->getSheetView()->setView((string) $this->sheetViewXml['view']);
}
}
private function gridLines(): void
{
if (isset($this->sheetViewXml['showGridLines'])) {
$this->worksheet->setShowGridLines(
self::boolean((string) $this->sheetViewXml['showGridLines'])
);
}
}
private function headers(): void
{
if (isset($this->sheetViewXml['showRowColHeaders'])) {
$this->worksheet->setShowRowColHeaders(
self::boolean((string) $this->sheetViewXml['showRowColHeaders'])
);
}
}
private function direction(): void
{
if (isset($this->sheetViewXml['rightToLeft'])) {
$this->worksheet->setRightToLeft(
self::boolean((string) $this->sheetViewXml['rightToLeft'])
);
}
}
private function showZeros(): void
{
if (isset($this->sheetViewXml['showZeros'])) {
$this->worksheet->getSheetView()->setShowZeros(
self::boolean((string) $this->sheetViewXml['showZeros'])
);
}
}
private function pane(): void
{
$xSplit = 0;
$ySplit = 0;
$topLeftCell = null;
if (isset($this->sheetViewXml->pane['xSplit'])) {
$xSplit = (int) ($this->sheetViewXml->pane['xSplit']);
}
if (isset($this->sheetViewXml->pane['ySplit'])) {
$ySplit = (int) ($this->sheetViewXml->pane['ySplit']);
}
if (isset($this->sheetViewXml->pane['topLeftCell'])) {
$topLeftCell = (string) $this->sheetViewXml->pane['topLeftCell'];
}
$this->worksheet->freezePane(
Coordinate::stringFromColumnIndex($xSplit + 1) . ($ySplit + 1),
$topLeftCell
);
}
private function selection(): void
{
$sqref = (string) $this->sheetViewXml->selection['sqref'];
$sqref = explode(' ', $sqref);
$sqref = $sqref[0];
$this->worksheet->setSelectedCells($sqref);
}
}

View File

@@ -0,0 +1,282 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Reader\Xlsx;
use PhpOffice\PhpSpreadsheet\Style\Alignment;
use PhpOffice\PhpSpreadsheet\Style\Border;
use PhpOffice\PhpSpreadsheet\Style\Borders;
use PhpOffice\PhpSpreadsheet\Style\Color;
use PhpOffice\PhpSpreadsheet\Style\Fill;
use PhpOffice\PhpSpreadsheet\Style\Font;
use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
use PhpOffice\PhpSpreadsheet\Style\Protection;
use PhpOffice\PhpSpreadsheet\Style\Style;
use SimpleXMLElement;
class Styles extends BaseParserClass
{
/**
* Theme instance.
*
* @var Theme
*/
private static $theme = null;
private $styles = [];
private $cellStyles = [];
private $styleXml;
public function __construct(SimpleXMLElement $styleXml)
{
$this->styleXml = $styleXml;
}
public function setStyleBaseData(?Theme $theme = null, $styles = [], $cellStyles = []): void
{
self::$theme = $theme;
$this->styles = $styles;
$this->cellStyles = $cellStyles;
}
private static function readFontStyle(Font $fontStyle, SimpleXMLElement $fontStyleXml): void
{
$fontStyle->setName((string) $fontStyleXml->name['val']);
$fontStyle->setSize((float) $fontStyleXml->sz['val']);
if (isset($fontStyleXml->b)) {
$fontStyle->setBold(!isset($fontStyleXml->b['val']) || self::boolean((string) $fontStyleXml->b['val']));
}
if (isset($fontStyleXml->i)) {
$fontStyle->setItalic(!isset($fontStyleXml->i['val']) || self::boolean((string) $fontStyleXml->i['val']));
}
if (isset($fontStyleXml->strike)) {
$fontStyle->setStrikethrough(!isset($fontStyleXml->strike['val']) || self::boolean((string) $fontStyleXml->strike['val']));
}
$fontStyle->getColor()->setARGB(self::readColor($fontStyleXml->color));
if (isset($fontStyleXml->u) && !isset($fontStyleXml->u['val'])) {
$fontStyle->setUnderline(Font::UNDERLINE_SINGLE);
} elseif (isset($fontStyleXml->u, $fontStyleXml->u['val'])) {
$fontStyle->setUnderline((string) $fontStyleXml->u['val']);
}
if (isset($fontStyleXml->vertAlign, $fontStyleXml->vertAlign['val'])) {
$verticalAlign = strtolower((string) $fontStyleXml->vertAlign['val']);
if ($verticalAlign === 'superscript') {
$fontStyle->setSuperscript(true);
}
if ($verticalAlign === 'subscript') {
$fontStyle->setSubscript(true);
}
}
}
private static function readNumberFormat(NumberFormat $numfmtStyle, SimpleXMLElement $numfmtStyleXml): void
{
if ($numfmtStyleXml->count() === 0) {
return;
}
$numfmt = $numfmtStyleXml->attributes();
if ($numfmt->count() > 0 && isset($numfmt['formatCode'])) {
$numfmtStyle->setFormatCode((string) $numfmt['formatCode']);
}
}
private static function readFillStyle(Fill $fillStyle, SimpleXMLElement $fillStyleXml): void
{
if ($fillStyleXml->gradientFill) {
/** @var SimpleXMLElement $gradientFill */
$gradientFill = $fillStyleXml->gradientFill[0];
if (!empty($gradientFill['type'])) {
$fillStyle->setFillType((string) $gradientFill['type']);
}
$fillStyle->setRotation((float) ($gradientFill['degree']));
$gradientFill->registerXPathNamespace('sml', 'http://schemas.openxmlformats.org/spreadsheetml/2006/main');
$fillStyle->getStartColor()->setARGB(self::readColor(self::getArrayItem($gradientFill->xpath('sml:stop[@position=0]'))->color));
$fillStyle->getEndColor()->setARGB(self::readColor(self::getArrayItem($gradientFill->xpath('sml:stop[@position=1]'))->color));
} elseif ($fillStyleXml->patternFill) {
$patternType = (string) $fillStyleXml->patternFill['patternType'] != '' ? (string) $fillStyleXml->patternFill['patternType'] : 'solid';
$fillStyle->setFillType($patternType);
if ($fillStyleXml->patternFill->fgColor) {
$fillStyle->getStartColor()->setARGB(self::readColor($fillStyleXml->patternFill->fgColor, true));
} else {
$fillStyle->getStartColor()->setARGB('FF000000');
}
if ($fillStyleXml->patternFill->bgColor) {
$fillStyle->getEndColor()->setARGB(self::readColor($fillStyleXml->patternFill->bgColor, true));
}
}
}
private static function readBorderStyle(Borders $borderStyle, SimpleXMLElement $borderStyleXml): void
{
$diagonalUp = self::boolean((string) $borderStyleXml['diagonalUp']);
$diagonalDown = self::boolean((string) $borderStyleXml['diagonalDown']);
if (!$diagonalUp && !$diagonalDown) {
$borderStyle->setDiagonalDirection(Borders::DIAGONAL_NONE);
} elseif ($diagonalUp && !$diagonalDown) {
$borderStyle->setDiagonalDirection(Borders::DIAGONAL_UP);
} elseif (!$diagonalUp && $diagonalDown) {
$borderStyle->setDiagonalDirection(Borders::DIAGONAL_DOWN);
} else {
$borderStyle->setDiagonalDirection(Borders::DIAGONAL_BOTH);
}
self::readBorder($borderStyle->getLeft(), $borderStyleXml->left);
self::readBorder($borderStyle->getRight(), $borderStyleXml->right);
self::readBorder($borderStyle->getTop(), $borderStyleXml->top);
self::readBorder($borderStyle->getBottom(), $borderStyleXml->bottom);
self::readBorder($borderStyle->getDiagonal(), $borderStyleXml->diagonal);
}
private static function readBorder(Border $border, SimpleXMLElement $borderXml): void
{
if (isset($borderXml['style'])) {
$border->setBorderStyle((string) $borderXml['style']);
}
if (isset($borderXml->color)) {
$border->getColor()->setARGB(self::readColor($borderXml->color));
}
}
private static function readAlignmentStyle(Alignment $alignment, SimpleXMLElement $alignmentXml): void
{
$alignment->setHorizontal((string) $alignmentXml->alignment['horizontal']);
$alignment->setVertical((string) $alignmentXml->alignment['vertical']);
$textRotation = 0;
if ((int) $alignmentXml->alignment['textRotation'] <= 90) {
$textRotation = (int) $alignmentXml->alignment['textRotation'];
} elseif ((int) $alignmentXml->alignment['textRotation'] > 90) {
$textRotation = 90 - (int) $alignmentXml->alignment['textRotation'];
}
$alignment->setTextRotation((int) $textRotation);
$alignment->setWrapText(self::boolean((string) $alignmentXml->alignment['wrapText']));
$alignment->setShrinkToFit(self::boolean((string) $alignmentXml->alignment['shrinkToFit']));
$alignment->setIndent((int) ((string) $alignmentXml->alignment['indent']) > 0 ? (int) ((string) $alignmentXml->alignment['indent']) : 0);
$alignment->setReadOrder((int) ((string) $alignmentXml->alignment['readingOrder']) > 0 ? (int) ((string) $alignmentXml->alignment['readingOrder']) : 0);
}
private function readStyle(Style $docStyle, $style): void
{
if ($style->numFmt instanceof SimpleXMLElement) {
self::readNumberFormat($docStyle->getNumberFormat(), $style->numFmt);
} else {
$docStyle->getNumberFormat()->setFormatCode($style->numFmt);
}
if (isset($style->font)) {
self::readFontStyle($docStyle->getFont(), $style->font);
}
if (isset($style->fill)) {
self::readFillStyle($docStyle->getFill(), $style->fill);
}
if (isset($style->border)) {
self::readBorderStyle($docStyle->getBorders(), $style->border);
}
if (isset($style->alignment->alignment)) {
self::readAlignmentStyle($docStyle->getAlignment(), $style->alignment);
}
// protection
if (isset($style->protection)) {
$this->readProtectionLocked($docStyle, $style);
$this->readProtectionHidden($docStyle, $style);
}
// top-level style settings
if (isset($style->quotePrefix)) {
$docStyle->setQuotePrefix(true);
}
}
private function readProtectionLocked(Style $docStyle, $style): void
{
if (isset($style->protection['locked'])) {
if (self::boolean((string) $style->protection['locked'])) {
$docStyle->getProtection()->setLocked(Protection::PROTECTION_PROTECTED);
} else {
$docStyle->getProtection()->setLocked(Protection::PROTECTION_UNPROTECTED);
}
}
}
private function readProtectionHidden(Style $docStyle, $style): void
{
if (isset($style->protection['hidden'])) {
if (self::boolean((string) $style->protection['hidden'])) {
$docStyle->getProtection()->setHidden(Protection::PROTECTION_PROTECTED);
} else {
$docStyle->getProtection()->setHidden(Protection::PROTECTION_UNPROTECTED);
}
}
}
private static function readColor($color, $background = false)
{
if (isset($color['rgb'])) {
return (string) $color['rgb'];
} elseif (isset($color['indexed'])) {
return Color::indexedColor($color['indexed'] - 7, $background)->getARGB();
} elseif (isset($color['theme'])) {
if (self::$theme !== null) {
$returnColour = self::$theme->getColourByIndex((int) $color['theme']);
if (isset($color['tint'])) {
$tintAdjust = (float) $color['tint'];
$returnColour = Color::changeBrightness($returnColour, $tintAdjust);
}
return 'FF' . $returnColour;
}
}
return ($background) ? 'FFFFFFFF' : 'FF000000';
}
public function dxfs($readDataOnly = false)
{
$dxfs = [];
if (!$readDataOnly && $this->styleXml) {
// Conditional Styles
if ($this->styleXml->dxfs) {
foreach ($this->styleXml->dxfs->dxf as $dxf) {
$style = new Style(false, true);
$this->readStyle($style, $dxf);
$dxfs[] = $style;
}
}
// Cell Styles
if ($this->styleXml->cellStyles) {
foreach ($this->styleXml->cellStyles->cellStyle as $cellStyle) {
if ((int) ($cellStyle['builtinId']) == 0) {
if (isset($this->cellStyles[(int) ($cellStyle['xfId'])])) {
// Set default style
$style = new Style();
$this->readStyle($style, $this->cellStyles[(int) ($cellStyle['xfId'])]);
// normal style, currently not using it for anything
}
}
}
}
}
return $dxfs;
}
public function styles()
{
return $this->styles;
}
private static function getArrayItem($array, $key = 0)
{
return $array[$key] ?? null;
}
}

View File

@@ -0,0 +1,93 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Reader\Xlsx;
class Theme
{
/**
* Theme Name.
*
* @var string
*/
private $themeName;
/**
* Colour Scheme Name.
*
* @var string
*/
private $colourSchemeName;
/**
* Colour Map.
*
* @var array of string
*/
private $colourMap;
/**
* Create a new Theme.
*
* @param mixed $themeName
* @param mixed $colourSchemeName
* @param mixed $colourMap
*/
public function __construct($themeName, $colourSchemeName, $colourMap)
{
// Initialise values
$this->themeName = $themeName;
$this->colourSchemeName = $colourSchemeName;
$this->colourMap = $colourMap;
}
/**
* Get Theme Name.
*
* @return string
*/
public function getThemeName()
{
return $this->themeName;
}
/**
* Get colour Scheme Name.
*
* @return string
*/
public function getColourSchemeName()
{
return $this->colourSchemeName;
}
/**
* Get colour Map Value by Position.
*
* @param mixed $index
*
* @return string
*/
public function getColourByIndex($index)
{
if (isset($this->colourMap[$index])) {
return $this->colourMap[$index];
}
return null;
}
/**
* Implement PHP __clone to create a deep clone, not just a shallow copy.
*/
public function __clone()
{
$vars = get_object_vars($this);
foreach ($vars as $key => $value) {
if ((is_object($value)) && ($key != '_parent')) {
$this->$key = clone $value;
} else {
$this->$key = $value;
}
}
}
}