Merge branch 'master' into Xls-Reader-Conditional-Formatting

This commit is contained in:
MarkBaker 2022-03-17 12:11:19 +01:00
commit 9b3c3f4adf
38 changed files with 1248 additions and 635 deletions

View File

@ -26,7 +26,7 @@ $config
'combine_consecutive_issets' => true,
'combine_consecutive_unsets' => true,
'combine_nested_dirname' => true,
'comment_to_phpdoc' => true,
'comment_to_phpdoc' => false, // interferes with annotations
'compact_nullable_typehint' => true,
'concat_space' => ['spacing' => 'one'],
'constant_case' => true,
@ -171,7 +171,7 @@ $config
'phpdoc_separation' => true,
'phpdoc_single_line_var_spacing' => true,
'phpdoc_summary' => true,
'phpdoc_to_comment' => true,
'phpdoc_to_comment' => false, // interferes with annotations
'phpdoc_to_param_type' => false, // Because experimental, but interesting for one shot use
'phpdoc_to_return_type' => false, // idem
'phpdoc_trim' => true,

View File

@ -39,6 +39,9 @@ and this project adheres to [Semantic Versioning](https://semver.org).
### Fixed
- Update Conditional Formatting ranges and rule conditions when inserting/deleting rows/columns [Issue #2678](https://github.com/PHPOffice/PhpSpreadsheet/issues/2678) [PR #2689](https://github.com/PHPOffice/PhpSpreadsheet/pull/2689)
- Allow `INDIRECT()` to accept row/column ranges as well as cell ranges [PR #2687](https://github.com/PHPOffice/PhpSpreadsheet/pull/2687)
- Fix bug when deleting cells with hyperlinks, where the hyperlink was then being "inherited" by whatever cell moved to that cell address.
- Fix bug in Conditional Formatting in the Xls Writer that resulted in a broken file when there were multiple conditional ranges in a worksheet.
- Fix Conditional Formatting in the Xls Writer to work with rules that contain string literals, cell references and formulae.
- Fix for setting Active Sheet to the first loaded worksheet when bookViews element isn't defined [Issue #2666](https://github.com/PHPOffice/PhpSpreadsheet/issues/2666) [PR #2669](https://github.com/PHPOffice/PhpSpreadsheet/pull/2669)

View File

@ -3165,31 +3165,11 @@ parameters:
count: 1
path: src/PhpSpreadsheet/ReferenceHelper.php
-
message: "#^Parameter \\#1 \\$index of method PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\RowDimension\\:\\:setRowIndex\\(\\) expects int, string given\\.$#"
count: 1
path: src/PhpSpreadsheet/ReferenceHelper.php
-
message: "#^Parameter \\#2 \\$callback of function uksort expects callable\\(\\(int\\|string\\), \\(int\\|string\\)\\)\\: int, array\\{'self', 'cellReverseSort'\\} given\\.$#"
count: 4
path: src/PhpSpreadsheet/ReferenceHelper.php
-
message: "#^Parameter \\#2 \\$callback of function uksort expects callable\\(\\(int\\|string\\), \\(int\\|string\\)\\)\\: int, array\\{'self', 'cellSort'\\} given\\.$#"
count: 4
path: src/PhpSpreadsheet/ReferenceHelper.php
-
message: "#^Parameter \\#3 \\$subject of function str_replace expects array\\|string, string\\|null given\\.$#"
count: 1
path: src/PhpSpreadsheet/ReferenceHelper.php
-
message: "#^Static property PhpOffice\\\\PhpSpreadsheet\\\\ReferenceHelper\\:\\:\\$instance \\(PhpOffice\\\\PhpSpreadsheet\\\\ReferenceHelper\\) in isset\\(\\) is not nullable\\.$#"
count: 1
path: src/PhpSpreadsheet/ReferenceHelper.php
-
message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\RichText\\\\Run\\:\\:\\$font \\(PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\Font\\) does not accept PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\Font\\|null\\.$#"
count: 1
@ -4240,26 +4220,6 @@ parameters:
count: 1
path: src/PhpSpreadsheet/Style/NumberFormat/PercentageFormatter.php
-
message: "#^Cannot call method getCell\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\Worksheet\\|null\\.$#"
count: 1
path: src/PhpSpreadsheet/Worksheet/BaseDrawing.php
-
message: "#^Cannot call method getDrawingCollection\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\Worksheet\\|null\\.$#"
count: 1
path: src/PhpSpreadsheet/Worksheet/BaseDrawing.php
-
message: "#^Cannot call method getHashCode\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\Worksheet\\|null\\.$#"
count: 1
path: src/PhpSpreadsheet/Worksheet/BaseDrawing.php
-
message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\BaseDrawing\\:\\:\\$shadow \\(PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\Drawing\\\\Shadow\\) does not accept PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\Drawing\\\\Shadow\\|null\\.$#"
count: 1
path: src/PhpSpreadsheet/Worksheet/BaseDrawing.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\CellIterator\\:\\:adjustForExistingOnlyRange\\(\\) has no return type specified\\.$#"
count: 1
@ -5270,36 +5230,6 @@ parameters:
count: 2
path: src/PhpSpreadsheet/Writer/Xlsx/DocProps.php
-
message: "#^Parameter \\#1 \\$coordinates of static method PhpOffice\\\\PhpSpreadsheet\\\\Cell\\\\Coordinate\\:\\:indexesFromString\\(\\) expects string, string\\|null given\\.$#"
count: 1
path: src/PhpSpreadsheet/Writer/Xlsx/Drawing.php
-
message: "#^Parameter \\#1 \\$index of method PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\Worksheet\\:\\:getChartByIndex\\(\\) expects string, int\\<0, max\\> given\\.$#"
count: 1
path: src/PhpSpreadsheet/Writer/Xlsx/Drawing.php
-
message: "#^Parameter \\#2 \\$chart of method PhpOffice\\\\PhpSpreadsheet\\\\Writer\\\\Xlsx\\\\Drawing\\:\\:writeChart\\(\\) expects PhpOffice\\\\PhpSpreadsheet\\\\Chart\\\\Chart, PhpOffice\\\\PhpSpreadsheet\\\\Chart\\\\Chart\\|false given\\.$#"
count: 1
path: src/PhpSpreadsheet/Writer/Xlsx/Drawing.php
-
message: "#^Parameter \\#2 \\$content of method XMLWriter\\:\\:writeElement\\(\\) expects string\\|null, int given\\.$#"
count: 20
path: src/PhpSpreadsheet/Writer/Xlsx/Drawing.php
-
message: "#^Parameter \\#2 \\$value of method XMLWriter\\:\\:writeAttribute\\(\\) expects string, int given\\.$#"
count: 10
path: src/PhpSpreadsheet/Writer/Xlsx/Drawing.php
-
message: "#^Parameter \\#2 \\$value of method XMLWriter\\:\\:writeAttribute\\(\\) expects string, int\\<0, max\\> given\\.$#"
count: 1
path: src/PhpSpreadsheet/Writer/Xlsx/Drawing.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Writer\\\\Xlsx\\\\Rels\\:\\:writeUnparsedRelationship\\(\\) has parameter \\$relationship with no type specified\\.$#"
count: 1

View File

@ -0,0 +1,78 @@
<?php
// Create new Spreadsheet object
use PhpOffice\PhpSpreadsheet\Helper\Dimension;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Worksheet\Drawing;
require __DIR__ . '/../Header.php';
$helper->log('Create new Spreadsheet object');
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
$sheet->getCell('A1')->setValue('twocell');
$sheet->getCell('A2')->setValue('twocell');
$sheet->getCell('A3')->setValue('onecell');
$sheet->getCell('A6')->setValue('absolute');
// Add a drawing to the worksheet
$helper->log('Add a drawing to the worksheet two-cell anchor not resized');
$drawing = new Drawing();
$drawing->setName('PhpSpreadsheet');
$drawing->setDescription('PhpSpreadsheet');
$drawing->setPath(__DIR__ . '/../images/PhpSpreadsheet_logo.png');
// anchor type will be two-cell because Coordinates2 is set
//$drawing->setAnchorType(Drawing::ANCHORTYPE_TWOCELL);
$drawing->setCoordinates('B1');
$drawing->setCoordinates2('B1');
$drawing->setOffsetX2($drawing->getImageWidth());
$drawing->setOffsetY2($drawing->getImageHeight());
$drawing->setWorksheet($spreadsheet->getActiveSheet());
// Add a drawing to the worksheet
$helper->log('Add a drawing to the worksheet two-cell anchor resized');
$drawing2 = new Drawing();
$drawing2->setName('PhpSpreadsheet');
$drawing2->setDescription('PhpSpreadsheet');
$drawing2->setPath(__DIR__ . '/../images/PhpSpreadsheet_logo.png');
// anchor type will be two-cell because Coordinates2 is set
//$drawing->setAnchorType(Drawing::ANCHORTYPE_TWOCELL);
$drawing2->setCoordinates('C2');
$drawing2->setCoordinates2('C2');
$drawing2->setOffsetX2($drawing->getImageWidth());
$drawing2->setOffsetY2($drawing->getImageHeight());
$drawing2->setWorksheet($spreadsheet->getActiveSheet());
$spreadsheet->getActiveSheet()->getColumnDimension('C')->setWidth($drawing->getImageWidth(), Dimension::UOM_PIXELS);
$spreadsheet->getActiveSheet()->getRowDimension(2)->setRowHeight($drawing->getImageHeight(), Dimension::UOM_PIXELS);
// Add a drawing to the worksheet one cell anchor
$helper->log('Add a drawing to the worksheet one-cell anchor');
$drawing3 = new Drawing();
$drawing3->setName('PhpSpreadsheet');
$drawing3->setDescription('PhpSpreadsheet');
$drawing3->setPath(__DIR__ . '/../images/PhpSpreadsheet_logo.png');
// anchor type will be one-cell because Coordinates2 is not set
//$drawing->setAnchorType(Drawing::ANCHORTYPE_ONECELL);
$drawing3->setCoordinates('D3');
$drawing3->setWorksheet($spreadsheet->getActiveSheet());
// Add a drawing to the worksheet
$helper->log('Add a drawing to the worksheet two-cell anchor resized absolute');
$drawing4 = new Drawing();
$drawing4->setName('PhpSpreadsheet');
$drawing4->setDescription('PhpSpreadsheet');
$drawing4->setPath(__DIR__ . '/../images/PhpSpreadsheet_logo.png');
// anchor type will be two-cell because Coordinates2 is set
//$drawing->setAnchorType(Drawing::ANCHORTYPE_TWOCELL);
$drawing4->setCoordinates('C6');
$drawing4->setCoordinates2('C6');
$drawing4->setOffsetX2($drawing->getImageWidth());
$drawing4->setOffsetY2($drawing->getImageHeight());
$drawing4->setWorksheet($spreadsheet->getActiveSheet());
$drawing4->setEditAs(Drawing::EDIT_AS_ABSOLUTE);
//$spreadsheet->getActiveSheet()->getColumnDimension('C')->setWidth($drawing->getImageWidth(), Dimension::UOM_PIXELS);
$spreadsheet->getActiveSheet()->getRowDimension(6)->setRowHeight($drawing->getImageHeight(), Dimension::UOM_PIXELS);
$helper->write($spreadsheet, __FILE__, ['Xlsx']);

View File

@ -3,7 +3,7 @@
use PhpOffice\PhpSpreadsheet\Chart\Chart;
use PhpOffice\PhpSpreadsheet\Chart\DataSeries;
use PhpOffice\PhpSpreadsheet\Chart\DataSeriesValues;
use PhpOffice\PhpSpreadsheet\Chart\Legend;
use PhpOffice\PhpSpreadsheet\Chart\Legend as ChartLegend;
use PhpOffice\PhpSpreadsheet\Chart\PlotArea;
use PhpOffice\PhpSpreadsheet\Chart\Title;
use PhpOffice\PhpSpreadsheet\IOFactory;
@ -71,7 +71,7 @@ $series = new DataSeries(
// Set the series in the plot area
$plotArea = new PlotArea(null, [$series]);
// Set the chart legend
$legend = new Legend(Legend::POSITION_TOPRIGHT, null, false);
$legend = new ChartLegend(ChartLegend::POSITION_TOPRIGHT, null, false);
$title = new Title('Test %age-Stacked Area Chart');
$yAxisLabel = new Title('Value ($k)');

View File

@ -3,7 +3,7 @@
use PhpOffice\PhpSpreadsheet\Chart\Chart;
use PhpOffice\PhpSpreadsheet\Chart\DataSeries;
use PhpOffice\PhpSpreadsheet\Chart\DataSeriesValues;
use PhpOffice\PhpSpreadsheet\Chart\Legend;
use PhpOffice\PhpSpreadsheet\Chart\Legend as ChartLegend;
use PhpOffice\PhpSpreadsheet\Chart\PlotArea;
use PhpOffice\PhpSpreadsheet\Chart\Title;
use PhpOffice\PhpSpreadsheet\IOFactory;
@ -74,7 +74,7 @@ $series->setPlotDirection(DataSeries::DIRECTION_BAR);
// Set the series in the plot area
$plotArea = new PlotArea(null, [$series]);
// Set the chart legend
$legend = new Legend(Legend::POSITION_RIGHT, null, false);
$legend = new ChartLegend(ChartLegend::POSITION_RIGHT, null, false);
$title = new Title('Test Chart');
$yAxisLabel = new Title('Value ($k)');

View File

@ -3,7 +3,7 @@
use PhpOffice\PhpSpreadsheet\Chart\Chart;
use PhpOffice\PhpSpreadsheet\Chart\DataSeries;
use PhpOffice\PhpSpreadsheet\Chart\DataSeriesValues;
use PhpOffice\PhpSpreadsheet\Chart\Legend;
use PhpOffice\PhpSpreadsheet\Chart\Legend as ChartLegend;
use PhpOffice\PhpSpreadsheet\Chart\PlotArea;
use PhpOffice\PhpSpreadsheet\Chart\Title;
use PhpOffice\PhpSpreadsheet\IOFactory;
@ -74,7 +74,7 @@ $series->setPlotDirection(DataSeries::DIRECTION_COL);
// Set the series in the plot area
$plotArea = new PlotArea(null, [$series]);
// Set the chart legend
$legend = new Legend(Legend::POSITION_RIGHT, null, false);
$legend = new ChartLegend(ChartLegend::POSITION_RIGHT, null, false);
$title = new Title('Test Column Chart');
$yAxisLabel = new Title('Value ($k)');

View File

@ -3,7 +3,7 @@
use PhpOffice\PhpSpreadsheet\Chart\Chart;
use PhpOffice\PhpSpreadsheet\Chart\DataSeries;
use PhpOffice\PhpSpreadsheet\Chart\DataSeriesValues;
use PhpOffice\PhpSpreadsheet\Chart\Legend;
use PhpOffice\PhpSpreadsheet\Chart\Legend as ChartLegend;
use PhpOffice\PhpSpreadsheet\Chart\PlotArea;
use PhpOffice\PhpSpreadsheet\Chart\Title;
use PhpOffice\PhpSpreadsheet\IOFactory;
@ -82,7 +82,7 @@ $series->setPlotDirection(DataSeries::DIRECTION_COL);
// Set the series in the plot area
$plotArea = new PlotArea(null, [$series]);
// Set the chart legend
$legend = new Legend(Legend::POSITION_BOTTOM, null, false);
$legend = new ChartLegend(ChartLegend::POSITION_BOTTOM, null, false);
$title = new Title('Test Grouped Column Chart');
$xAxisLabel = new Title('Financial Period');

View File

@ -3,7 +3,7 @@
use PhpOffice\PhpSpreadsheet\Chart\Chart;
use PhpOffice\PhpSpreadsheet\Chart\DataSeries;
use PhpOffice\PhpSpreadsheet\Chart\DataSeriesValues;
use PhpOffice\PhpSpreadsheet\Chart\Legend;
use PhpOffice\PhpSpreadsheet\Chart\Legend as ChartLegend;
use PhpOffice\PhpSpreadsheet\Chart\PlotArea;
use PhpOffice\PhpSpreadsheet\Chart\Title;
use PhpOffice\PhpSpreadsheet\IOFactory;
@ -128,7 +128,7 @@ $series3 = new DataSeries(
// Set the series in the plot area
$plotArea = new PlotArea(null, [$series1, $series2, $series3]);
// Set the chart legend
$legend = new Legend(Legend::POSITION_RIGHT, null, false);
$legend = new ChartLegend(ChartLegend::POSITION_RIGHT, null, false);
$title = new Title('Average Weather Chart for Crete');

View File

@ -3,7 +3,7 @@
use PhpOffice\PhpSpreadsheet\Chart\Chart;
use PhpOffice\PhpSpreadsheet\Chart\DataSeries;
use PhpOffice\PhpSpreadsheet\Chart\DataSeriesValues;
use PhpOffice\PhpSpreadsheet\Chart\Legend;
use PhpOffice\PhpSpreadsheet\Chart\Legend as ChartLegend;
use PhpOffice\PhpSpreadsheet\Chart\PlotArea;
use PhpOffice\PhpSpreadsheet\Chart\Title;
use PhpOffice\PhpSpreadsheet\IOFactory;
@ -72,7 +72,7 @@ $series = new DataSeries(
// Set the series in the plot area
$plotArea = new PlotArea(null, [$series]);
// Set the chart legend
$legend = new Legend(Legend::POSITION_TOPRIGHT, null, false);
$legend = new ChartLegend(ChartLegend::POSITION_TOPRIGHT, null, false);
$title = new Title('Test Stacked Line Chart');
$yAxisLabel = new Title('Value ($k)');

View File

@ -3,7 +3,7 @@
use PhpOffice\PhpSpreadsheet\Chart\Chart;
use PhpOffice\PhpSpreadsheet\Chart\DataSeries;
use PhpOffice\PhpSpreadsheet\Chart\DataSeriesValues;
use PhpOffice\PhpSpreadsheet\Chart\Legend;
use PhpOffice\PhpSpreadsheet\Chart\Legend as ChartLegend;
use PhpOffice\PhpSpreadsheet\Chart\PlotArea;
use PhpOffice\PhpSpreadsheet\Chart\Title;
use PhpOffice\PhpSpreadsheet\IOFactory;
@ -71,7 +71,7 @@ $series1 = new DataSeries(
// Set the series in the plot area
$plotArea1 = new PlotArea(null, [$series1]);
// Set the chart legend
$legend1 = new Legend(Legend::POSITION_TOPRIGHT, null, false);
$legend1 = new ChartLegend(ChartLegend::POSITION_TOPRIGHT, null, false);
$title1 = new Title('Test %age-Stacked Area Chart');
$yAxisLabel1 = new Title('Value ($k)');
@ -146,7 +146,7 @@ $series2->setPlotDirection(DataSeries::DIRECTION_COL);
// Set the series in the plot area
$plotArea2 = new PlotArea(null, [$series2]);
// Set the chart legend
$legend2 = new Legend(Legend::POSITION_RIGHT, null, false);
$legend2 = new ChartLegend(ChartLegend::POSITION_RIGHT, null, false);
$title2 = new Title('Test Column Chart');
$yAxisLabel2 = new Title('Value ($k)');

View File

@ -4,7 +4,7 @@ use PhpOffice\PhpSpreadsheet\Chart\Chart;
use PhpOffice\PhpSpreadsheet\Chart\DataSeries;
use PhpOffice\PhpSpreadsheet\Chart\DataSeriesValues;
use PhpOffice\PhpSpreadsheet\Chart\Layout;
use PhpOffice\PhpSpreadsheet\Chart\Legend;
use PhpOffice\PhpSpreadsheet\Chart\Legend as ChartLegend;
use PhpOffice\PhpSpreadsheet\Chart\PlotArea;
use PhpOffice\PhpSpreadsheet\Chart\Title;
use PhpOffice\PhpSpreadsheet\IOFactory;
@ -73,7 +73,7 @@ $layout1->setShowPercent(true);
// Set the series in the plot area
$plotArea1 = new PlotArea($layout1, [$series1]);
// Set the chart legend
$legend1 = new Legend(Legend::POSITION_RIGHT, null, false);
$legend1 = new ChartLegend(ChartLegend::POSITION_RIGHT, null, false);
$title1 = new Title('Test Pie Chart');

View File

@ -4,7 +4,7 @@ use PhpOffice\PhpSpreadsheet\Chart\Chart;
use PhpOffice\PhpSpreadsheet\Chart\DataSeries;
use PhpOffice\PhpSpreadsheet\Chart\DataSeriesValues;
use PhpOffice\PhpSpreadsheet\Chart\Layout;
use PhpOffice\PhpSpreadsheet\Chart\Legend;
use PhpOffice\PhpSpreadsheet\Chart\Legend as ChartLegend;
use PhpOffice\PhpSpreadsheet\Chart\PlotArea;
use PhpOffice\PhpSpreadsheet\Chart\Title;
use PhpOffice\PhpSpreadsheet\IOFactory;
@ -79,7 +79,7 @@ $layout1->setShowPercent(true);
// Set the series in the plot area
$plotArea1 = new PlotArea($layout1, [$series1]);
// Set the chart legend
$legend1 = new Legend(Legend::POSITION_RIGHT, null, false);
$legend1 = new ChartLegend(ChartLegend::POSITION_RIGHT, null, false);
$title1 = new Title('Test Pie Chart');

View File

@ -4,7 +4,7 @@ use PhpOffice\PhpSpreadsheet\Chart\Chart;
use PhpOffice\PhpSpreadsheet\Chart\DataSeries;
use PhpOffice\PhpSpreadsheet\Chart\DataSeriesValues;
use PhpOffice\PhpSpreadsheet\Chart\Layout;
use PhpOffice\PhpSpreadsheet\Chart\Legend;
use PhpOffice\PhpSpreadsheet\Chart\Legend as ChartLegend;
use PhpOffice\PhpSpreadsheet\Chart\PlotArea;
use PhpOffice\PhpSpreadsheet\Chart\Title;
use PhpOffice\PhpSpreadsheet\IOFactory;
@ -85,7 +85,7 @@ $layout = new Layout();
// Set the series in the plot area
$plotArea = new PlotArea($layout, [$series]);
// Set the chart legend
$legend = new Legend(Legend::POSITION_RIGHT, null, false);
$legend = new ChartLegend(ChartLegend::POSITION_RIGHT, null, false);
$title = new Title('Test Radar Chart');

View File

@ -3,7 +3,7 @@
use PhpOffice\PhpSpreadsheet\Chart\Chart;
use PhpOffice\PhpSpreadsheet\Chart\DataSeries;
use PhpOffice\PhpSpreadsheet\Chart\DataSeriesValues;
use PhpOffice\PhpSpreadsheet\Chart\Legend;
use PhpOffice\PhpSpreadsheet\Chart\Legend as ChartLegend;
use PhpOffice\PhpSpreadsheet\Chart\PlotArea;
use PhpOffice\PhpSpreadsheet\Chart\Title;
use PhpOffice\PhpSpreadsheet\IOFactory;
@ -68,7 +68,7 @@ $series = new DataSeries(
// Set the series in the plot area
$plotArea = new PlotArea(null, [$series]);
// Set the chart legend
$legend = new Legend(Legend::POSITION_TOPRIGHT, null, false);
$legend = new ChartLegend(ChartLegend::POSITION_TOPRIGHT, null, false);
$title = new Title('Test Scatter Chart');
$yAxisLabel = new Title('Value ($k)');

View File

@ -3,7 +3,7 @@
use PhpOffice\PhpSpreadsheet\Chart\Chart;
use PhpOffice\PhpSpreadsheet\Chart\DataSeries;
use PhpOffice\PhpSpreadsheet\Chart\DataSeriesValues;
use PhpOffice\PhpSpreadsheet\Chart\Legend;
use PhpOffice\PhpSpreadsheet\Chart\Legend as ChartLegend;
use PhpOffice\PhpSpreadsheet\Chart\PlotArea;
use PhpOffice\PhpSpreadsheet\Chart\Title;
use PhpOffice\PhpSpreadsheet\IOFactory;
@ -79,7 +79,7 @@ $series = new DataSeries(
// Set the series in the plot area
$plotArea = new PlotArea(null, [$series]);
// Set the chart legend
$legend = new Legend(Legend::POSITION_RIGHT, null, false);
$legend = new ChartLegend(ChartLegend::POSITION_RIGHT, null, false);
$title = new Title('Test Stock Chart');
$xAxisLabel = new Title('Counts');

View File

@ -3,7 +3,7 @@
use PhpOffice\PhpSpreadsheet\Chart\Chart;
use PhpOffice\PhpSpreadsheet\Chart\DataSeries;
use PhpOffice\PhpSpreadsheet\Chart\DataSeriesValues;
use PhpOffice\PhpSpreadsheet\Chart\Legend;
use PhpOffice\PhpSpreadsheet\Chart\Legend as ChartLegend;
use PhpOffice\PhpSpreadsheet\Chart\PlotArea;
use PhpOffice\PhpSpreadsheet\Chart\Title;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
@ -71,7 +71,7 @@ $series->setPlotDirection(DataSeries::DIRECTION_BAR);
// Set the series in the plot area
$plotArea = new PlotArea(null, [$series]);
// Set the chart legend
$legend = new Legend(Legend::POSITION_RIGHT, null, false);
$legend = new ChartLegend(ChartLegend::POSITION_RIGHT, null, false);
$title = new Title('Test Bar Chart');
$yAxisLabel = new Title('Value ($k)');

View File

@ -32,9 +32,10 @@ class HLookup extends LookupBase
}
$notExactMatch = (bool) ($notExactMatch ?? true);
$lookupArray = self::convertLiteralArray($lookupArray);
try {
self::validateLookupArray($lookupArray);
$lookupArray = self::convertLiteralArray($lookupArray);
$indexNumber = self::validateIndexLookup($lookupArray, $indexNumber);
} catch (Exception $e) {
return $e->getMessage();

View File

@ -72,11 +72,17 @@ class Indirect
[$cellAddress, $worksheet, $sheetName] = Helpers::extractWorksheet($cellAddress, $cell);
if (preg_match('/^' . Calculation::CALCULATION_REGEXP_COLUMNRANGE_RELATIVE . '$/miu', $cellAddress, $matches)) {
$cellAddress = self::handleRowColumnRanges($worksheet, ...explode(':', $cellAddress));
} elseif (preg_match('/^' . Calculation::CALCULATION_REGEXP_ROWRANGE_RELATIVE . '$/miu', $cellAddress, $matches)) {
$cellAddress = self::handleRowColumnRanges($worksheet, ...explode(':', $cellAddress));
}
[$cellAddress1, $cellAddress2, $cellAddress] = Helpers::extractCellAddresses($cellAddress, $a1, $cell->getWorkSheet(), $sheetName);
if (
(!preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF . '$/i', $cellAddress1, $matches)) ||
(($cellAddress2 !== null) && (!preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF . '$/i', $cellAddress2, $matches)))
(!preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF . '$/miu', $cellAddress1, $matches)) ||
(($cellAddress2 !== null) && (!preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF . '$/miu', $cellAddress2, $matches)))
) {
return ExcelError::REF();
}
@ -95,4 +101,22 @@ class Indirect
return Calculation::getInstance($worksheet !== null ? $worksheet->getParent() : null)
->extractCellRange($cellAddress, $worksheet, false);
}
private static function handleRowColumnRanges(?Worksheet $worksheet, string $start, string $end): string
{
// Being lazy, we're only checking a single row/column to get the max
if (ctype_digit($start) && $start <= 1048576) {
// Max 16,384 columns for Excel2007
$endColRef = ($worksheet !== null) ? $worksheet->getHighestDataColumn((int) $start) : 'XFD';
return "A{$start}:{$endColRef}{$end}";
} elseif (ctype_alpha($start) && strlen($start) <= 3) {
// Max 1,048,576 rows for Excel2007
$endRowRef = ($worksheet !== null) ? $worksheet->getHighestDataRow($start) : 1048576;
return "{$start}1:{$end}{$endRowRef}";
}
return "{$start}:{$end}";
}
}

View File

@ -7,6 +7,16 @@ use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
abstract class LookupBase
{
/**
* @param mixed $lookup_array
*/
protected static function validateLookupArray($lookup_array): void
{
if (!is_array($lookup_array)) {
throw new Exception(ExcelError::REF());
}
}
protected static function validateIndexLookup(array $lookup_array, $index_number): int
{
// index_number must be a number greater than or equal to 1

View File

@ -33,6 +33,7 @@ class VLookup extends LookupBase
$notExactMatch = (bool) ($notExactMatch ?? true);
try {
self::validateLookupArray($lookupArray);
$indexNumber = self::validateIndexLookup($lookupArray, $indexNumber);
} catch (Exception $e) {
return $e->getMessage();

View File

@ -0,0 +1,119 @@
<?php
namespace PhpOffice\PhpSpreadsheet;
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
class CellReferenceHelper
{
/**
* @var string
*/
protected $beforeCellAddress;
/**
* @var int
*/
protected $beforeColumn;
/**
* @var int
*/
protected $beforeRow;
/**
* @var int
*/
protected $numberOfColumns;
/**
* @var int
*/
protected $numberOfRows;
public function __construct(string $beforeCellAddress = 'A1', int $numberOfColumns = 0, int $numberOfRows = 0)
{
$this->beforeCellAddress = str_replace('$', '', $beforeCellAddress);
$this->numberOfColumns = $numberOfColumns;
$this->numberOfRows = $numberOfRows;
// Get coordinate of $beforeCellAddress
[$beforeColumn, $beforeRow] = Coordinate::coordinateFromString($beforeCellAddress);
$this->beforeColumn = (int) Coordinate::columnIndexFromString($beforeColumn);
$this->beforeRow = (int) $beforeRow;
}
public function beforeCellAddress(): string
{
return $this->beforeCellAddress;
}
public function refreshRequired(string $beforeCellAddress, int $numberOfColumns, int $numberOfRows): bool
{
return $this->beforeCellAddress !== $beforeCellAddress ||
$this->numberOfColumns !== $numberOfColumns ||
$this->numberOfRows !== $numberOfRows;
}
public function updateCellReference(string $cellReference = 'A1', bool $includeAbsoluteReferences = false): string
{
if (Coordinate::coordinateIsRange($cellReference)) {
throw new Exception('Only single cell references may be passed to this method.');
}
// Get coordinate of $cellReference
[$newColumn, $newRow] = Coordinate::coordinateFromString($cellReference);
$newColumnIndex = (int) Coordinate::columnIndexFromString(str_replace('$', '', $newColumn));
$newRowIndex = (int) str_replace('$', '', $newRow);
$absoluteColumn = $newColumn[0] === '$' ? '$' : '';
$absoluteRow = $newRow[0] === '$' ? '$' : '';
// Verify which parts should be updated
if ($includeAbsoluteReferences === false) {
$updateColumn = (($absoluteColumn !== '$') && $newColumnIndex >= $this->beforeColumn);
$updateRow = (($absoluteRow !== '$') && $newRowIndex >= $this->beforeRow);
} else {
$updateColumn = ($newColumnIndex >= $this->beforeColumn);
$updateRow = ($newRowIndex >= $this->beforeRow);
}
// Create new column reference
if ($updateColumn) {
$newColumn = ($includeAbsoluteReferences === false)
? Coordinate::stringFromColumnIndex($newColumnIndex + $this->numberOfColumns)
: $absoluteColumn . Coordinate::stringFromColumnIndex($newColumnIndex + $this->numberOfColumns);
}
// Create new row reference
if ($updateRow) {
$newRow = ($includeAbsoluteReferences === false)
? $newRowIndex + $this->numberOfRows
: $absoluteRow . (string) ($newRowIndex + $this->numberOfRows);
}
// Return new reference
return "{$newColumn}{$newRow}";
}
public function cellAddressInDeleteRange(string $cellAddress): bool
{
[$cellColumn, $cellRow] = Coordinate::coordinateFromString($cellAddress);
$cellColumnIndex = Coordinate::columnIndexFromString($cellColumn);
// Is cell within the range of rows/columns if we're deleting
if (
$this->numberOfRows < 0 &&
($cellRow >= ($this->beforeRow + $this->numberOfRows)) &&
($cellRow < $this->beforeRow)
) {
return true;
} elseif (
$this->numberOfColumns < 0 &&
($cellColumnIndex >= ($this->beforeColumn + $this->numberOfColumns)) &&
($cellColumnIndex < $this->beforeColumn)
) {
return true;
}
return false;
}
}

View File

@ -619,6 +619,7 @@ class Html
// Load the HTML file into the DOM object
// Note the use of error suppression, because typically this will be an html fragment, so not fully valid markup
$prefix = '<?xml encoding="UTF-8">';
/** @scrutinizer ignore-unhandled */
@$dom->loadHTML($prefix . $html, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
// Discard excess white space
$dom->preserveWhiteSpace = false;
@ -808,7 +809,7 @@ class Html
if (isset($callbacks[$callbackTag])) {
$elementHandler = $callbacks[$callbackTag];
if (method_exists($this, $elementHandler)) {
// @phpstan-ignore-next-line
/** @phpstan-ignore-next-line */
call_user_func([$this, $elementHandler], $element);
}
}

View File

@ -71,7 +71,7 @@ class MD5
*/
public function add(string $data): void
{
/** @phpstan-ignore-next-line */
// @phpstan-ignore-next-line
$words = array_values(unpack('V16', $data));
$A = $this->a;

View File

@ -1315,6 +1315,11 @@ class Xlsx extends BaseReader
$outerShdw = $twoCellAnchor->pic->spPr->children(Namespaces::DRAWINGML)->effectLst->outerShdw;
$hlinkClick = $twoCellAnchor->pic->nvPicPr->cNvPr->children(Namespaces::DRAWINGML)->hlinkClick;
$objDrawing = new \PhpOffice\PhpSpreadsheet\Worksheet\Drawing();
/** @scrutinizer ignore-call */
$editAs = $twoCellAnchor->attributes();
if (isset($editAs, $editAs['editAs'])) {
$objDrawing->setEditAs($editAs['editAs']);
}
$objDrawing->setName((string) self::getArrayItem(self::getAttributes($twoCellAnchor->pic->nvPicPr->cNvPr), 'name'));
$objDrawing->setDescription((string) self::getArrayItem(self::getAttributes($twoCellAnchor->pic->nvPicPr->cNvPr), 'descr'));
$embedImageKey = (string) self::getArrayItem(

View File

@ -5,6 +5,7 @@ namespace PhpOffice\PhpSpreadsheet;
use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
use PhpOffice\PhpSpreadsheet\Cell\DataType;
use PhpOffice\PhpSpreadsheet\Style\Conditional;
use PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
@ -20,10 +21,15 @@ class ReferenceHelper
/**
* Instance of this class.
*
* @var ReferenceHelper
* @var ?ReferenceHelper
*/
private static $instance;
/**
* @var CellReferenceHelper
*/
private $cellReferenceHelper;
/**
* Get an instance of this class.
*
@ -31,7 +37,7 @@ class ReferenceHelper
*/
public static function getInstance()
{
if (!isset(self::$instance) || (self::$instance === null)) {
if (self::$instance === null) {
self::$instance = new self();
}
@ -115,67 +121,32 @@ class ReferenceHelper
return ($ar < $br) ? 1 : -1;
}
/**
* Test whether a cell address falls within a defined range of cells.
*
* @param string $cellAddress Address of the cell we're testing
* @param int $beforeRow Number of the row we're inserting/deleting before
* @param int $numberOfRows Number of rows to insert/delete (negative values indicate deletion)
* @param int $beforeColumnIndex Index number of the column we're inserting/deleting before
* @param int $numberOfCols Number of columns to insert/delete (negative values indicate deletion)
*
* @return bool
*/
private static function cellAddressInDeleteRange($cellAddress, $beforeRow, $numberOfRows, $beforeColumnIndex, $numberOfCols)
{
[$cellColumn, $cellRow] = Coordinate::coordinateFromString($cellAddress);
$cellColumnIndex = Coordinate::columnIndexFromString($cellColumn);
// Is cell within the range of rows/columns if we're deleting
if (
$numberOfRows < 0 &&
($cellRow >= ($beforeRow + $numberOfRows)) &&
($cellRow < $beforeRow)
) {
return true;
} elseif (
$numberOfCols < 0 &&
($cellColumnIndex >= ($beforeColumnIndex + $numberOfCols)) &&
($cellColumnIndex < $beforeColumnIndex)
) {
return true;
}
return false;
}
/**
* Update page breaks when inserting/deleting rows/columns.
*
* @param Worksheet $worksheet The worksheet that we're editing
* @param string $beforeCellAddress Insert/Delete before this cell address (e.g. 'A1')
* @param int $beforeColumnIndex Index number of the column we're inserting/deleting before
* @param int $numberOfColumns Number of columns to insert/delete (negative values indicate deletion)
* @param int $beforeRow Number of the row we're inserting/deleting before
* @param int $numberOfRows Number of rows to insert/delete (negative values indicate deletion)
*/
protected function adjustPageBreaks(Worksheet $worksheet, $beforeCellAddress, $beforeColumnIndex, $numberOfColumns, $beforeRow, $numberOfRows): void
protected function adjustPageBreaks(Worksheet $worksheet, $numberOfColumns, $numberOfRows): void
{
$aBreaks = $worksheet->getBreaks();
($numberOfColumns > 0 || $numberOfRows > 0) ?
uksort($aBreaks, ['self', 'cellReverseSort']) : uksort($aBreaks, ['self', 'cellSort']);
($numberOfColumns > 0 || $numberOfRows > 0)
? uksort($aBreaks, [self::class, 'cellReverseSort'])
: uksort($aBreaks, [self::class, 'cellSort']);
foreach ($aBreaks as $key => $value) {
if (self::cellAddressInDeleteRange($key, $beforeRow, $numberOfRows, $beforeColumnIndex, $numberOfColumns)) {
foreach ($aBreaks as $cellAddress => $value) {
if ($this->cellReferenceHelper->cellAddressInDeleteRange($cellAddress) === true) {
// If we're deleting, then clear any defined breaks that are within the range
// of rows/columns that we're deleting
$worksheet->setBreak($key, Worksheet::BREAK_NONE);
$worksheet->setBreak($cellAddress, Worksheet::BREAK_NONE);
} else {
// Otherwise update any affected breaks by inserting a new break at the appropriate point
// and removing the old affected break
$newReference = $this->updateCellReference($key, $beforeCellAddress, $numberOfColumns, $numberOfRows);
if ($key != $newReference) {
$newReference = $this->updateCellReference($cellAddress);
if ($cellAddress !== $newReference) {
$worksheet->setBreak($newReference, $value)
->setBreak($key, Worksheet::BREAK_NONE);
->setBreak($cellAddress, Worksheet::BREAK_NONE);
}
}
}
@ -185,22 +156,17 @@ class ReferenceHelper
* Update cell comments when inserting/deleting rows/columns.
*
* @param Worksheet $worksheet The worksheet that we're editing
* @param string $beforeCellAddress Insert/Delete before this cell address (e.g. 'A1')
* @param int $beforeColumnIndex Index number of the column we're inserting/deleting before
* @param int $numberOfColumns Number of columns to insert/delete (negative values indicate deletion)
* @param int $beforeRow Number of the row we're inserting/deleting before
* @param int $numberOfRows Number of rows to insert/delete (negative values indicate deletion)
*/
protected function adjustComments($worksheet, $beforeCellAddress, $beforeColumnIndex, $numberOfColumns, $beforeRow, $numberOfRows): void
protected function adjustComments($worksheet): void
{
$aComments = $worksheet->getComments();
$aNewComments = []; // the new array of all comments
foreach ($aComments as $key => &$value) {
foreach ($aComments as $cellAddress => &$value) {
// Any comments inside a deleted range will be ignored
if (!self::cellAddressInDeleteRange($key, $beforeRow, $numberOfRows, $beforeColumnIndex, $numberOfColumns)) {
if ($this->cellReferenceHelper->cellAddressInDeleteRange($cellAddress) === false) {
// Otherwise build a new array of comments indexed by the adjusted cell reference
$newReference = $this->updateCellReference($key, $beforeCellAddress, $numberOfColumns, $numberOfRows);
$newReference = $this->updateCellReference($cellAddress);
$aNewComments[$newReference] = $value;
}
}
@ -212,44 +178,85 @@ class ReferenceHelper
* Update hyperlinks when inserting/deleting rows/columns.
*
* @param Worksheet $worksheet The worksheet that we're editing
* @param string $beforeCellAddress Insert/Delete before this cell address (e.g. 'A1')
* @param int $numberOfColumns Number of columns to insert/delete (negative values indicate deletion)
* @param int $numberOfRows Number of rows to insert/delete (negative values indicate deletion)
*/
protected function adjustHyperlinks($worksheet, $beforeCellAddress, $numberOfColumns, $numberOfRows): void
protected function adjustHyperlinks($worksheet, $numberOfColumns, $numberOfRows): void
{
$aHyperlinkCollection = $worksheet->getHyperlinkCollection();
($numberOfColumns > 0 || $numberOfRows > 0) ?
uksort($aHyperlinkCollection, ['self', 'cellReverseSort']) : uksort($aHyperlinkCollection, ['self', 'cellSort']);
($numberOfColumns > 0 || $numberOfRows > 0)
? uksort($aHyperlinkCollection, [self::class, 'cellReverseSort'])
: uksort($aHyperlinkCollection, [self::class, 'cellSort']);
foreach ($aHyperlinkCollection as $key => $value) {
$newReference = $this->updateCellReference($key, $beforeCellAddress, $numberOfColumns, $numberOfRows);
if ($key != $newReference) {
foreach ($aHyperlinkCollection as $cellAddress => $value) {
$newReference = $this->updateCellReference($cellAddress);
if ($this->cellReferenceHelper->cellAddressInDeleteRange($cellAddress) === true) {
$worksheet->setHyperlink($cellAddress, null);
} elseif ($cellAddress !== $newReference) {
$worksheet->setHyperlink($newReference, $value);
$worksheet->setHyperlink($key, null);
$worksheet->setHyperlink($cellAddress, null);
}
}
}
/**
* Update conditional formatting styles when inserting/deleting rows/columns.
*
* @param Worksheet $worksheet The worksheet that we're editing
* @param int $numberOfColumns Number of columns to insert/delete (negative values indicate deletion)
* @param int $numberOfRows Number of rows to insert/delete (negative values indicate deletion)
*/
protected function adjustConditionalFormatting($worksheet, $numberOfColumns, $numberOfRows): void
{
$aStyles = $worksheet->getConditionalStylesCollection();
($numberOfColumns > 0 || $numberOfRows > 0)
? uksort($aStyles, [self::class, 'cellReverseSort'])
: uksort($aStyles, [self::class, 'cellSort']);
foreach ($aStyles as $cellAddress => $cfRules) {
$worksheet->removeConditionalStyles($cellAddress);
$newReference = $this->updateCellReference($cellAddress);
foreach ($cfRules as &$cfRule) {
/** @var Conditional $cfRule */
$conditions = $cfRule->getConditions();
foreach ($conditions as &$condition) {
if (is_string($condition)) {
$condition = $this->updateFormulaReferences(
$condition,
$this->cellReferenceHelper->beforeCellAddress(),
$numberOfColumns,
$numberOfRows,
$worksheet->getTitle(),
true
);
}
}
$cfRule->setConditions($conditions);
}
$worksheet->setConditionalStyles($newReference, $cfRules);
}
}
/**
* Update data validations when inserting/deleting rows/columns.
*
* @param Worksheet $worksheet The worksheet that we're editing
* @param string $before Insert/Delete before this cell address (e.g. 'A1')
* @param int $numberOfColumns Number of columns to insert/delete (negative values indicate deletion)
* @param int $numberOfRows Number of rows to insert/delete (negative values indicate deletion)
*/
protected function adjustDataValidations(Worksheet $worksheet, $before, $numberOfColumns, $numberOfRows): void
protected function adjustDataValidations(Worksheet $worksheet, $numberOfColumns, $numberOfRows): void
{
$aDataValidationCollection = $worksheet->getDataValidationCollection();
($numberOfColumns > 0 || $numberOfRows > 0) ?
uksort($aDataValidationCollection, ['self', 'cellReverseSort']) : uksort($aDataValidationCollection, ['self', 'cellSort']);
($numberOfColumns > 0 || $numberOfRows > 0)
? uksort($aDataValidationCollection, [self::class, 'cellReverseSort'])
: uksort($aDataValidationCollection, [self::class, 'cellSort']);
foreach ($aDataValidationCollection as $key => $value) {
$newReference = $this->updateCellReference($key, $before, $numberOfColumns, $numberOfRows);
if ($key != $newReference) {
foreach ($aDataValidationCollection as $cellAddress => $value) {
$newReference = $this->updateCellReference($cellAddress);
if ($cellAddress !== $newReference) {
$worksheet->setDataValidation($newReference, $value);
$worksheet->setDataValidation($key, null);
$worksheet->setDataValidation($cellAddress, null);
}
}
}
@ -258,16 +265,13 @@ class ReferenceHelper
* Update merged cells when inserting/deleting rows/columns.
*
* @param Worksheet $worksheet The worksheet that we're editing
* @param string $beforeCellAddress Insert/Delete before this cell address (e.g. 'A1')
* @param int $numberOfColumns Number of columns to insert/delete (negative values indicate deletion)
* @param int $numberOfRows Number of rows to insert/delete (negative values indicate deletion)
*/
protected function adjustMergeCells(Worksheet $worksheet, $beforeCellAddress, $numberOfColumns, $numberOfRows): void
protected function adjustMergeCells(Worksheet $worksheet): void
{
$aMergeCells = $worksheet->getMergeCells();
$aNewMergeCells = []; // the new array of all merge cells
foreach ($aMergeCells as $key => &$value) {
$newReference = $this->updateCellReference($key, $beforeCellAddress, $numberOfColumns, $numberOfRows);
foreach ($aMergeCells as $cellAddress => &$value) {
$newReference = $this->updateCellReference($cellAddress);
$aNewMergeCells[$newReference] = $newReference;
}
$worksheet->setMergeCells($aNewMergeCells); // replace the merge cells array
@ -277,20 +281,20 @@ class ReferenceHelper
* Update protected cells when inserting/deleting rows/columns.
*
* @param Worksheet $worksheet The worksheet that we're editing
* @param string $beforeCellAddress Insert/Delete before this cell address (e.g. 'A1')
* @param int $numberOfColumns Number of columns to insert/delete (negative values indicate deletion)
* @param int $numberOfRows Number of rows to insert/delete (negative values indicate deletion)
*/
protected function adjustProtectedCells(Worksheet $worksheet, $beforeCellAddress, $numberOfColumns, $numberOfRows): void
protected function adjustProtectedCells(Worksheet $worksheet, $numberOfColumns, $numberOfRows): void
{
$aProtectedCells = $worksheet->getProtectedCells();
($numberOfColumns > 0 || $numberOfRows > 0) ?
uksort($aProtectedCells, ['self', 'cellReverseSort']) : uksort($aProtectedCells, ['self', 'cellSort']);
foreach ($aProtectedCells as $key => $value) {
$newReference = $this->updateCellReference($key, $beforeCellAddress, $numberOfColumns, $numberOfRows);
if ($key != $newReference) {
($numberOfColumns > 0 || $numberOfRows > 0)
? uksort($aProtectedCells, [self::class, 'cellReverseSort'])
: uksort($aProtectedCells, [self::class, 'cellSort']);
foreach ($aProtectedCells as $cellAddress => $value) {
$newReference = $this->updateCellReference($cellAddress);
if ($cellAddress !== $newReference) {
$worksheet->protectCells($newReference, $value, true);
$worksheet->unprotectCells($key);
$worksheet->unprotectCells($cellAddress);
}
}
}
@ -299,18 +303,15 @@ class ReferenceHelper
* Update column dimensions when inserting/deleting rows/columns.
*
* @param Worksheet $worksheet The worksheet that we're editing
* @param string $beforeCellAddress Insert/Delete before this cell address (e.g. 'A1')
* @param int $numberOfColumns Number of columns to insert/delete (negative values indicate deletion)
* @param int $numberOfRows Number of rows to insert/delete (negative values indicate deletion)
*/
protected function adjustColumnDimensions(Worksheet $worksheet, $beforeCellAddress, $numberOfColumns, $numberOfRows): void
protected function adjustColumnDimensions(Worksheet $worksheet): void
{
$aColumnDimensions = array_reverse($worksheet->getColumnDimensions(), true);
if (!empty($aColumnDimensions)) {
foreach ($aColumnDimensions as $objColumnDimension) {
$newReference = $this->updateCellReference($objColumnDimension->getColumnIndex() . '1', $beforeCellAddress, $numberOfColumns, $numberOfRows);
$newReference = $this->updateCellReference($objColumnDimension->getColumnIndex() . '1');
[$newReference] = Coordinate::coordinateFromString($newReference);
if ($objColumnDimension->getColumnIndex() != $newReference) {
if ($objColumnDimension->getColumnIndex() !== $newReference) {
$objColumnDimension->setColumnIndex($newReference);
}
}
@ -322,20 +323,19 @@ class ReferenceHelper
* Update row dimensions when inserting/deleting rows/columns.
*
* @param Worksheet $worksheet The worksheet that we're editing
* @param string $beforeCellAddress Insert/Delete before this cell address (e.g. 'A1')
* @param int $numberOfColumns Number of columns to insert/delete (negative values indicate deletion)
* @param int $beforeRow Number of the row we're inserting/deleting before
* @param int $numberOfRows Number of rows to insert/delete (negative values indicate deletion)
*/
protected function adjustRowDimensions(Worksheet $worksheet, $beforeCellAddress, $numberOfColumns, $beforeRow, $numberOfRows): void
protected function adjustRowDimensions(Worksheet $worksheet, $beforeRow, $numberOfRows): void
{
$aRowDimensions = array_reverse($worksheet->getRowDimensions(), true);
if (!empty($aRowDimensions)) {
foreach ($aRowDimensions as $objRowDimension) {
$newReference = $this->updateCellReference('A' . $objRowDimension->getRowIndex(), $beforeCellAddress, $numberOfColumns, $numberOfRows);
$newReference = $this->updateCellReference('A' . $objRowDimension->getRowIndex());
[, $newReference] = Coordinate::coordinateFromString($newReference);
if ($objRowDimension->getRowIndex() != $newReference) {
$objRowDimension->setRowIndex($newReference);
$newRoweference = (int) $newReference;
if ($objRowDimension->getRowIndex() !== $newRoweference) {
$objRowDimension->setRowIndex($newRoweference);
}
}
$worksheet->refreshRowDimensions();
@ -368,6 +368,13 @@ class ReferenceHelper
$remove = ($numberOfColumns < 0 || $numberOfRows < 0);
$allCoordinates = $worksheet->getCoordinates();
if (
$this->cellReferenceHelper === null ||
$this->cellReferenceHelper->refreshRequired($beforeCellAddress, $numberOfColumns, $numberOfRows)
) {
$this->cellReferenceHelper = new CellReferenceHelper($beforeCellAddress, $numberOfColumns, $numberOfRows);
}
// Get coordinate of $beforeCellAddress
[$beforeColumn, $beforeRow] = Coordinate::indexesFromString($beforeCellAddress);
@ -427,7 +434,7 @@ class ReferenceHelper
$worksheet->getCell($newCoordinate)->setXfIndex($cell->getXfIndex());
// Insert this cell at its new location
if ($cell->getDataType() == DataType::TYPE_FORMULA) {
if ($cell->getDataType() === DataType::TYPE_FORMULA) {
// Formula should be adjusted
$worksheet->getCell($newCoordinate)
->setValue($this->updateFormulaReferences($cell->getValue(), $beforeCellAddress, $numberOfColumns, $numberOfRows, $worksheet->getTitle()));
@ -441,7 +448,7 @@ class ReferenceHelper
} else {
/* We don't need to update styles for rows/columns before our insertion position,
but we do still need to adjust any formulae in those cells */
if ($cell->getDataType() == DataType::TYPE_FORMULA) {
if ($cell->getDataType() === DataType::TYPE_FORMULA) {
// Formula should be adjusted
$cell->setValue($this->updateFormulaReferences($cell->getValue(), $beforeCellAddress, $numberOfColumns, $numberOfRows, $worksheet->getTitle()));
}
@ -461,39 +468,42 @@ class ReferenceHelper
}
// Update worksheet: column dimensions
$this->adjustColumnDimensions($worksheet, $beforeCellAddress, $numberOfColumns, $numberOfRows);
$this->adjustColumnDimensions($worksheet);
// Update worksheet: row dimensions
$this->adjustRowDimensions($worksheet, $beforeCellAddress, $numberOfColumns, $beforeRow, $numberOfRows);
$this->adjustRowDimensions($worksheet, $beforeRow, $numberOfRows);
// Update worksheet: page breaks
$this->adjustPageBreaks($worksheet, $beforeCellAddress, $beforeColumn, $numberOfColumns, $beforeRow, $numberOfRows);
$this->adjustPageBreaks($worksheet, $numberOfColumns, $numberOfRows);
// Update worksheet: comments
$this->adjustComments($worksheet, $beforeCellAddress, $beforeColumn, $numberOfColumns, $beforeRow, $numberOfRows);
$this->adjustComments($worksheet);
// Update worksheet: hyperlinks
$this->adjustHyperlinks($worksheet, $beforeCellAddress, $numberOfColumns, $numberOfRows);
$this->adjustHyperlinks($worksheet, $numberOfColumns, $numberOfRows);
// Update worksheet: conditional formatting styles
$this->adjustConditionalFormatting($worksheet, $numberOfColumns, $numberOfRows);
// Update worksheet: data validations
$this->adjustDataValidations($worksheet, $beforeCellAddress, $numberOfColumns, $numberOfRows);
$this->adjustDataValidations($worksheet, $numberOfColumns, $numberOfRows);
// Update worksheet: merge cells
$this->adjustMergeCells($worksheet, $beforeCellAddress, $numberOfColumns, $numberOfRows);
$this->adjustMergeCells($worksheet);
// Update worksheet: protected cells
$this->adjustProtectedCells($worksheet, $beforeCellAddress, $numberOfColumns, $numberOfRows);
$this->adjustProtectedCells($worksheet, $numberOfColumns, $numberOfRows);
// Update worksheet: autofilter
$this->adjustAutoFilter($worksheet, $beforeCellAddress, $numberOfColumns, $numberOfRows);
$this->adjustAutoFilter($worksheet, $beforeCellAddress, $numberOfColumns);
// Update worksheet: freeze pane
if ($worksheet->getFreezePane()) {
$splitCell = $worksheet->getFreezePane() ?? '';
$topLeftCell = $worksheet->getTopLeftCell() ?? '';
$splitCell = $this->updateCellReference($splitCell, $beforeCellAddress, $numberOfColumns, $numberOfRows);
$topLeftCell = $this->updateCellReference($topLeftCell, $beforeCellAddress, $numberOfColumns, $numberOfRows);
$splitCell = $this->updateCellReference($splitCell);
$topLeftCell = $this->updateCellReference($topLeftCell);
$worksheet->freezePane($splitCell, $topLeftCell);
}
@ -501,14 +511,14 @@ class ReferenceHelper
// Page setup
if ($worksheet->getPageSetup()->isPrintAreaSet()) {
$worksheet->getPageSetup()->setPrintArea(
$this->updateCellReference($worksheet->getPageSetup()->getPrintArea(), $beforeCellAddress, $numberOfColumns, $numberOfRows)
$this->updateCellReference($worksheet->getPageSetup()->getPrintArea())
);
}
// Update worksheet: drawings
$aDrawings = $worksheet->getDrawingCollection();
foreach ($aDrawings as $objDrawing) {
$newReference = $this->updateCellReference($objDrawing->getCoordinates(), $beforeCellAddress, $numberOfColumns, $numberOfRows);
$newReference = $this->updateCellReference($objDrawing->getCoordinates());
if ($objDrawing->getCoordinates() != $newReference) {
$objDrawing->setCoordinates($newReference);
}
@ -518,7 +528,7 @@ class ReferenceHelper
if (count($worksheet->getParent()->getDefinedNames()) > 0) {
foreach ($worksheet->getParent()->getDefinedNames() as $definedName) {
if ($definedName->getWorksheet() !== null && $definedName->getWorksheet()->getHashCode() === $worksheet->getHashCode()) {
$definedName->setValue($this->updateCellReference($definedName->getValue(), $beforeCellAddress, $numberOfColumns, $numberOfRows));
$definedName->setValue($this->updateCellReference($definedName->getValue()));
}
}
}
@ -538,8 +548,21 @@ class ReferenceHelper
*
* @return string Updated formula
*/
public function updateFormulaReferences($formula = '', $beforeCellAddress = 'A1', $numberOfColumns = 0, $numberOfRows = 0, $worksheetName = '')
{
public function updateFormulaReferences(
$formula = '',
$beforeCellAddress = 'A1',
$numberOfColumns = 0,
$numberOfRows = 0,
$worksheetName = '',
bool $includeAbsoluteReferences = false
) {
if (
$this->cellReferenceHelper === null ||
$this->cellReferenceHelper->refreshRequired($beforeCellAddress, $numberOfColumns, $numberOfRows)
) {
$this->cellReferenceHelper = new CellReferenceHelper($beforeCellAddress, $numberOfColumns, $numberOfRows);
}
// Update cell references in the formula
$formulaBlocks = explode('"', $formula);
$i = false;
@ -549,13 +572,13 @@ class ReferenceHelper
$adjustCount = 0;
$newCellTokens = $cellTokens = [];
// Search for row ranges (e.g. 'Sheet1'!3:5 or 3:5) with or without $ absolutes (e.g. $3:5)
$matchCount = preg_match_all('/' . self::REFHELPER_REGEXP_ROWRANGE . '/i', ' ' . $formulaBlock . ' ', $matches, PREG_SET_ORDER);
$matchCount = preg_match_all('/' . self::REFHELPER_REGEXP_ROWRANGE . '/mui', ' ' . $formulaBlock . ' ', $matches, PREG_SET_ORDER);
if ($matchCount > 0) {
foreach ($matches as $match) {
$fromString = ($match[2] > '') ? $match[2] . '!' : '';
$fromString .= $match[3] . ':' . $match[4];
$modified3 = substr($this->updateCellReference('$A' . $match[3], $beforeCellAddress, $numberOfColumns, $numberOfRows), 2);
$modified4 = substr($this->updateCellReference('$A' . $match[4], $beforeCellAddress, $numberOfColumns, $numberOfRows), 2);
$modified3 = substr($this->updateCellReference('$A' . $match[3], $includeAbsoluteReferences), 2);
$modified4 = substr($this->updateCellReference('$A' . $match[4], $includeAbsoluteReferences), 2);
if ($match[3] . ':' . $match[4] !== $modified3 . ':' . $modified4) {
if (($match[2] == '') || (trim($match[2], "'") == $worksheetName)) {
@ -574,13 +597,13 @@ class ReferenceHelper
}
}
// Search for column ranges (e.g. 'Sheet1'!C:E or C:E) with or without $ absolutes (e.g. $C:E)
$matchCount = preg_match_all('/' . self::REFHELPER_REGEXP_COLRANGE . '/i', ' ' . $formulaBlock . ' ', $matches, PREG_SET_ORDER);
$matchCount = preg_match_all('/' . self::REFHELPER_REGEXP_COLRANGE . '/mui', ' ' . $formulaBlock . ' ', $matches, PREG_SET_ORDER);
if ($matchCount > 0) {
foreach ($matches as $match) {
$fromString = ($match[2] > '') ? $match[2] . '!' : '';
$fromString .= $match[3] . ':' . $match[4];
$modified3 = substr($this->updateCellReference($match[3] . '$1', $beforeCellAddress, $numberOfColumns, $numberOfRows), 0, -2);
$modified4 = substr($this->updateCellReference($match[4] . '$1', $beforeCellAddress, $numberOfColumns, $numberOfRows), 0, -2);
$modified3 = substr($this->updateCellReference($match[3] . '$1', $includeAbsoluteReferences), 0, -2);
$modified4 = substr($this->updateCellReference($match[4] . '$1', $includeAbsoluteReferences), 0, -2);
if ($match[3] . ':' . $match[4] !== $modified3 . ':' . $modified4) {
if (($match[2] == '') || (trim($match[2], "'") == $worksheetName)) {
@ -599,13 +622,13 @@ class ReferenceHelper
}
}
// Search for cell ranges (e.g. 'Sheet1'!A3:C5 or A3:C5) with or without $ absolutes (e.g. $A1:C$5)
$matchCount = preg_match_all('/' . self::REFHELPER_REGEXP_CELLRANGE . '/i', ' ' . $formulaBlock . ' ', $matches, PREG_SET_ORDER);
$matchCount = preg_match_all('/' . self::REFHELPER_REGEXP_CELLRANGE . '/mui', ' ' . $formulaBlock . ' ', $matches, PREG_SET_ORDER);
if ($matchCount > 0) {
foreach ($matches as $match) {
$fromString = ($match[2] > '') ? $match[2] . '!' : '';
$fromString .= $match[3] . ':' . $match[4];
$modified3 = $this->updateCellReference($match[3], $beforeCellAddress, $numberOfColumns, $numberOfRows);
$modified4 = $this->updateCellReference($match[4], $beforeCellAddress, $numberOfColumns, $numberOfRows);
$modified3 = $this->updateCellReference($match[3], $includeAbsoluteReferences);
$modified4 = $this->updateCellReference($match[4], $includeAbsoluteReferences);
if ($match[3] . $match[4] !== $modified3 . $modified4) {
if (($match[2] == '') || (trim($match[2], "'") == $worksheetName)) {
@ -625,14 +648,14 @@ class ReferenceHelper
}
}
// Search for cell references (e.g. 'Sheet1'!A3 or C5) with or without $ absolutes (e.g. $A1 or C$5)
$matchCount = preg_match_all('/' . self::REFHELPER_REGEXP_CELLREF . '/i', ' ' . $formulaBlock . ' ', $matches, PREG_SET_ORDER);
$matchCount = preg_match_all('/' . self::REFHELPER_REGEXP_CELLREF . '/mui', ' ' . $formulaBlock . ' ', $matches, PREG_SET_ORDER);
if ($matchCount > 0) {
foreach ($matches as $match) {
$fromString = ($match[2] > '') ? $match[2] . '!' : '';
$fromString .= $match[3];
$modified3 = $this->updateCellReference($match[3], $beforeCellAddress, $numberOfColumns, $numberOfRows);
$modified3 = $this->updateCellReference($match[3], $includeAbsoluteReferences);
if ($match[3] !== $modified3) {
if (($match[2] == '') || (trim($match[2], "'") == $worksheetName)) {
$toString = ($match[2] > '') ? $match[2] . '!' : '';
@ -809,13 +832,10 @@ class ReferenceHelper
* Update cell reference.
*
* @param string $cellReference Cell address or range of addresses
* @param string $beforeCellAddress Insert before this one
* @param int $numberOfColumns Number of columns to increment
* @param int $numberOfRows Number of rows to increment
*
* @return string Updated cell range
*/
public function updateCellReference($cellReference = 'A1', $beforeCellAddress = 'A1', $numberOfColumns = 0, $numberOfRows = 0)
private function updateCellReference($cellReference = 'A1', bool $includeAbsoluteReferences = false)
{
// Is it in another worksheet? Will not have to update anything.
if (strpos($cellReference, '!') !== false) {
@ -823,10 +843,10 @@ class ReferenceHelper
// Is it a range or a single cell?
} elseif (!Coordinate::coordinateIsRange($cellReference)) {
// Single cell
return $this->updateSingleCellReference($cellReference, $beforeCellAddress, $numberOfColumns, $numberOfRows);
return $this->cellReferenceHelper->updateCellReference($cellReference, $includeAbsoluteReferences);
} elseif (Coordinate::coordinateIsRange($cellReference)) {
// Range
return $this->updateCellRange($cellReference, $beforeCellAddress, $numberOfColumns, $numberOfRows);
return $this->updateCellRange($cellReference, $includeAbsoluteReferences);
}
// Return original
@ -865,13 +885,10 @@ class ReferenceHelper
* Update cell range.
*
* @param string $cellRange Cell range (e.g. 'B2:D4', 'B:C' or '2:3')
* @param string $beforeCellAddress Insert before this one
* @param int $numberOfColumns Number of columns to increment
* @param int $numberOfRows Number of rows to increment
*
* @return string Updated cell range
*/
private function updateCellRange($cellRange = 'A1:A1', $beforeCellAddress = 'A1', $numberOfColumns = 0, $numberOfRows = 0)
private function updateCellRange(string $cellRange = 'A1:A1', bool $includeAbsoluteReferences = false): string
{
if (!Coordinate::coordinateIsRange($cellRange)) {
throw new Exception('Only cell ranges may be passed to this method.');
@ -884,13 +901,15 @@ class ReferenceHelper
$jc = count($range[$i]);
for ($j = 0; $j < $jc; ++$j) {
if (ctype_alpha($range[$i][$j])) {
$r = Coordinate::coordinateFromString($this->updateSingleCellReference($range[$i][$j] . '1', $beforeCellAddress, $numberOfColumns, $numberOfRows));
$range[$i][$j] = $r[0];
$range[$i][$j] = Coordinate::coordinateFromString(
$this->cellReferenceHelper->updateCellReference($range[$i][$j] . '1', $includeAbsoluteReferences)
)[0];
} elseif (ctype_digit($range[$i][$j])) {
$r = Coordinate::coordinateFromString($this->updateSingleCellReference('A' . $range[$i][$j], $beforeCellAddress, $numberOfColumns, $numberOfRows));
$range[$i][$j] = $r[1];
$range[$i][$j] = Coordinate::coordinateFromString(
$this->cellReferenceHelper->updateCellReference('A' . $range[$i][$j], $includeAbsoluteReferences)
)[1];
} else {
$range[$i][$j] = $this->updateSingleCellReference($range[$i][$j], $beforeCellAddress, $numberOfColumns, $numberOfRows);
$range[$i][$j] = $this->cellReferenceHelper->updateCellReference($range[$i][$j], $includeAbsoluteReferences);
}
}
}
@ -899,46 +918,6 @@ class ReferenceHelper
return Coordinate::buildRange($range);
}
/**
* Update single cell reference.
*
* @param string $cellReference Single cell reference
* @param string $beforeCellAddress Insert before this one
* @param int $numberOfColumns Number of columns to increment
* @param int $numberOfRows Number of rows to increment
*
* @return string Updated cell reference
*/
private function updateSingleCellReference($cellReference = 'A1', $beforeCellAddress = 'A1', $numberOfColumns = 0, $numberOfRows = 0)
{
if (Coordinate::coordinateIsRange($cellReference)) {
throw new Exception('Only single cell references may be passed to this method.');
}
// Get coordinate of $beforeCellAddress
[$beforeColumn, $beforeRow] = Coordinate::coordinateFromString($beforeCellAddress);
// Get coordinate of $cellReference
[$newColumn, $newRow] = Coordinate::coordinateFromString($cellReference);
// Verify which parts should be updated
$updateColumn = (($newColumn[0] != '$') && ($beforeColumn[0] != '$') && (Coordinate::columnIndexFromString($newColumn) >= Coordinate::columnIndexFromString($beforeColumn)));
$updateRow = (($newRow[0] != '$') && ($beforeRow[0] != '$') && $newRow >= $beforeRow);
// Create new column reference
if ($updateColumn) {
$newColumn = Coordinate::stringFromColumnIndex(Coordinate::columnIndexFromString($newColumn) + $numberOfColumns);
}
// Create new row reference
if ($updateRow) {
$newRow = (int) $newRow + $numberOfRows;
}
// Return new reference
return $newColumn . $newRow;
}
private function clearColumnStrips(int $highestRow, int $beforeColumn, int $numberOfColumns, Worksheet $worksheet): void
{
for ($i = 1; $i <= $highestRow - 1; ++$i) {
@ -969,7 +948,7 @@ class ReferenceHelper
}
}
private function adjustAutoFilter(Worksheet $worksheet, string $beforeCellAddress, int $numberOfColumns, int $numberOfRows): void
private function adjustAutoFilter(Worksheet $worksheet, string $beforeCellAddress, int $numberOfColumns): void
{
$autoFilter = $worksheet->getAutoFilter();
$autoFilterRange = $autoFilter->getRange();
@ -999,7 +978,7 @@ class ReferenceHelper
}
$worksheet->setAutoFilter(
$this->updateCellReference($autoFilterRange, $beforeCellAddress, $numberOfColumns, $numberOfRows)
$this->updateCellReference($autoFilterRange)
);
}
}
@ -1055,17 +1034,8 @@ class ReferenceHelper
$coordinate = $beforeColumnName . $i;
if ($worksheet->cellExists($coordinate)) {
$xfIndex = $worksheet->getCell($coordinate)->getXfIndex();
$conditionalStyles = $worksheet->conditionalStylesExists($coordinate) ?
$worksheet->getConditionalStyles($coordinate) : false;
for ($j = $beforeColumn; $j <= $beforeColumn - 1 + $numberOfColumns; ++$j) {
$worksheet->getCellByColumnAndRow($j, $i)->setXfIndex($xfIndex);
if ($conditionalStyles) {
$cloned = [];
foreach ($conditionalStyles as $conditionalStyle) {
$cloned[] = clone $conditionalStyle;
}
$worksheet->setConditionalStyles(Coordinate::stringFromColumnIndex($j) . $i, $cloned);
}
}
}
}
@ -1079,17 +1049,8 @@ class ReferenceHelper
$coordinate = Coordinate::stringFromColumnIndex($i) . ($beforeRow - 1);
if ($worksheet->cellExists($coordinate)) {
$xfIndex = $worksheet->getCell($coordinate)->getXfIndex();
$conditionalStyles = $worksheet->conditionalStylesExists($coordinate) ?
$worksheet->getConditionalStyles($coordinate) : false;
for ($j = $beforeRow; $j <= $beforeRow - 1 + $numberOfRows; ++$j) {
$worksheet->getCell(Coordinate::stringFromColumnIndex($i) . $j)->setXfIndex($xfIndex);
if ($conditionalStyles) {
$cloned = [];
foreach ($conditionalStyles as $conditionalStyle) {
$cloned[] = clone $conditionalStyle;
}
$worksheet->setConditionalStyles(Coordinate::stringFromColumnIndex($i) . $j, $cloned);
}
}
}
}

View File

@ -54,7 +54,9 @@ class XMLWriter extends \XMLWriter
public function __destruct()
{
// Unlink temporary files
// There is nothing reasonable to do if unlink fails.
if ($this->tempFileName != '') {
/** @scrutinizer ignore-unhandled */
@unlink($this->tempFileName);
}
}

View File

@ -8,6 +8,22 @@ use PhpOffice\PhpSpreadsheet\IComparable;
class BaseDrawing implements IComparable
{
const EDIT_AS_ABSOLUTE = 'absolute';
const EDIT_AS_ONECELL = 'onecell';
const EDIT_AS_TWOCELL = 'twocell';
private const VALID_EDIT_AS = [
self::EDIT_AS_ABSOLUTE,
self::EDIT_AS_ONECELL,
self::EDIT_AS_TWOCELL,
];
/**
* The editAs attribute, used only with two cell anchor.
*
* @var string
*/
protected $editAs = '';
/**
* Image counter.
*
@ -27,14 +43,14 @@ class BaseDrawing implements IComparable
*
* @var string
*/
protected $name;
protected $name = '';
/**
* Description.
*
* @var string
*/
protected $description;
protected $description = '';
/**
* Worksheet.
@ -48,70 +64,84 @@ class BaseDrawing implements IComparable
*
* @var string
*/
protected $coordinates;
protected $coordinates = 'A1';
/**
* Offset X.
*
* @var int
*/
protected $offsetX;
protected $offsetX = 0;
/**
* Offset Y.
*
* @var int
*/
protected $offsetY;
protected $offsetY = 0;
/**
* Coordinates2.
*
* @var null|string
* @var string
*/
protected $coordinates2;
protected $coordinates2 = '';
/**
* Offset X2.
*
* @var int
*/
protected $offsetX2;
protected $offsetX2 = 0;
/**
* Offset Y2.
*
* @var int
*/
protected $offsetY2;
protected $offsetY2 = 0;
/**
* Width.
*
* @var int
*/
protected $width;
protected $width = 0;
/**
* Height.
*
* @var int
*/
protected $height;
protected $height = 0;
/**
* Pixel width of image. See $width for the size the Drawing will be in the sheet.
*
* @var int
*/
protected $imageWidth = 0;
/**
* Pixel width of image. See $height for the size the Drawing will be in the sheet.
*
* @var int
*/
protected $imageHeight = 0;
/**
* Proportional resize.
*
* @var bool
*/
protected $resizeProportional;
protected $resizeProportional = true;
/**
* Rotation.
*
* @var int
*/
protected $rotation;
protected $rotation = 0;
/**
* Shadow.
@ -132,7 +162,7 @@ class BaseDrawing implements IComparable
*
* @var int
*/
protected $type;
protected $type = IMAGETYPE_UNKNOWN;
/**
* Create a new BaseDrawing.
@ -140,91 +170,43 @@ class BaseDrawing implements IComparable
public function __construct()
{
// Initialise values
$this->name = '';
$this->description = '';
$this->worksheet = null;
$this->coordinates = 'A1';
$this->offsetX = 0;
$this->offsetY = 0;
$this->coordinates2 = null;
$this->offsetX2 = 0;
$this->offsetY2 = 0;
$this->width = 0;
$this->height = 0;
$this->resizeProportional = true;
$this->rotation = 0;
$this->shadow = new Drawing\Shadow();
$this->type = IMAGETYPE_UNKNOWN;
$this->setShadow();
// Set image index
++self::$imageCounter;
$this->imageIndex = self::$imageCounter;
}
/**
* Get image index.
*
* @return int
*/
public function getImageIndex()
public function getImageIndex(): int
{
return $this->imageIndex;
}
/**
* Get Name.
*
* @return string
*/
public function getName()
public function getName(): string
{
return $this->name;
}
/**
* Set Name.
*
* @param string $name
*
* @return $this
*/
public function setName($name)
public function setName(string $name): self
{
$this->name = $name;
return $this;
}
/**
* Get Description.
*
* @return string
*/
public function getDescription()
public function getDescription(): string
{
return $this->description;
}
/**
* Set Description.
*
* @param string $description
*
* @return $this
*/
public function setDescription($description)
public function setDescription(string $description): self
{
$this->description = $description;
return $this;
}
/**
* Get Worksheet.
*
* @return null|Worksheet
*/
public function getWorksheet()
public function getWorksheet(): ?Worksheet
{
return $this->worksheet;
}
@ -233,16 +215,16 @@ class BaseDrawing implements IComparable
* Set Worksheet.
*
* @param bool $overrideOld If a Worksheet has already been assigned, overwrite it and remove image from old Worksheet?
*
* @return $this
*/
public function setWorksheet(?Worksheet $worksheet = null, $overrideOld = false)
public function setWorksheet(?Worksheet $worksheet = null, bool $overrideOld = false): self
{
if ($this->worksheet === null) {
// Add drawing to \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet
$this->worksheet = $worksheet;
$this->worksheet->getCell($this->coordinates);
$this->worksheet->getDrawingCollection()->append($this);
if ($worksheet !== null) {
$this->worksheet = $worksheet;
$this->worksheet->getCell($this->coordinates);
$this->worksheet->getDrawingCollection()->append($this);
}
} else {
if ($overrideOld) {
// Remove drawing from old \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet
@ -267,168 +249,84 @@ class BaseDrawing implements IComparable
return $this;
}
/**
* Get Coordinates.
*
* @return string
*/
public function getCoordinates()
public function getCoordinates(): string
{
return $this->coordinates;
}
/**
* Set Coordinates.
*
* @param string $coordinates eg: 'A1'
*
* @return $this
*/
public function setCoordinates($coordinates)
public function setCoordinates(string $coordinates): self
{
$this->coordinates = $coordinates;
return $this;
}
/**
* Get OffsetX.
*
* @return int
*/
public function getOffsetX()
public function getOffsetX(): int
{
return $this->offsetX;
}
/**
* Set OffsetX.
*
* @param int $offsetX
*
* @return $this
*/
public function setOffsetX($offsetX)
public function setOffsetX(int $offsetX): self
{
$this->offsetX = $offsetX;
return $this;
}
/**
* Get OffsetY.
*
* @return int
*/
public function getOffsetY()
public function getOffsetY(): int
{
return $this->offsetY;
}
/**
* Get Coordinates2.
*
* @return null|string
*/
public function getCoordinates2()
{
return $this->coordinates2;
}
/**
* Set Coordinates2.
*
* @param null|string $coordinates2 eg: 'A1'
*
* @return $this
*/
public function setCoordinates2($coordinates2)
{
$this->coordinates2 = $coordinates2;
return $this;
}
/**
* Get OffsetX2.
*
* @return int
*/
public function getOffsetX2()
{
return $this->offsetX2;
}
/**
* Set OffsetX2.
*
* @param int $offsetX2
*
* @return $this
*/
public function setOffsetX2($offsetX2)
{
$this->offsetX2 = $offsetX2;
return $this;
}
/**
* Get OffsetY2.
*
* @return int
*/
public function getOffsetY2()
{
return $this->offsetY2;
}
/**
* Set OffsetY2.
*
* @param int $offsetY2
*
* @return $this
*/
public function setOffsetY2($offsetY2)
{
$this->offsetY2 = $offsetY2;
return $this;
}
/**
* Set OffsetY.
*
* @param int $offsetY
*
* @return $this
*/
public function setOffsetY($offsetY)
public function setOffsetY(int $offsetY): self
{
$this->offsetY = $offsetY;
return $this;
}
/**
* Get Width.
*
* @return int
*/
public function getWidth()
public function getCoordinates2(): string
{
return $this->coordinates2;
}
public function setCoordinates2(string $coordinates2): self
{
$this->coordinates2 = $coordinates2;
return $this;
}
public function getOffsetX2(): int
{
return $this->offsetX2;
}
public function setOffsetX2(int $offsetX2): self
{
$this->offsetX2 = $offsetX2;
return $this;
}
public function getOffsetY2(): int
{
return $this->offsetY2;
}
public function setOffsetY2(int $offsetY2): self
{
$this->offsetY2 = $offsetY2;
return $this;
}
public function getWidth(): int
{
return $this->width;
}
/**
* Set Width.
*
* @param int $width
*
* @return $this
*/
public function setWidth($width)
public function setWidth(int $width): self
{
// Resize proportional?
if ($this->resizeProportional && $width != 0) {
@ -442,24 +340,12 @@ class BaseDrawing implements IComparable
return $this;
}
/**
* Get Height.
*
* @return int
*/
public function getHeight()
public function getHeight(): int
{
return $this->height;
}
/**
* Set Height.
*
* @param int $height
*
* @return $this
*/
public function setHeight($height)
public function setHeight(int $height): self
{
// Resize proportional?
if ($this->resizeProportional && $height != 0) {
@ -482,14 +368,9 @@ class BaseDrawing implements IComparable
* $objDrawing->setWidthAndHeight(160,120);
* </code>
*
* @param int $width
* @param int $height
*
* @return $this
*
* @author Vincent@luo MSN:kele_100@hotmail.com
*/
public function setWidthAndHeight($width, $height)
public function setWidthAndHeight(int $width, int $height): self
{
$xratio = $width / ($this->width != 0 ? $this->width : 1);
$yratio = $height / ($this->height != 0 ? $this->height : 1);
@ -509,72 +390,38 @@ class BaseDrawing implements IComparable
return $this;
}
/**
* Get ResizeProportional.
*
* @return bool
*/
public function getResizeProportional()
public function getResizeProportional(): bool
{
return $this->resizeProportional;
}
/**
* Set ResizeProportional.
*
* @param bool $resizeProportional
*
* @return $this
*/
public function setResizeProportional($resizeProportional)
public function setResizeProportional(bool $resizeProportional): self
{
$this->resizeProportional = $resizeProportional;
return $this;
}
/**
* Get Rotation.
*
* @return int
*/
public function getRotation()
public function getRotation(): int
{
return $this->rotation;
}
/**
* Set Rotation.
*
* @param int $rotation
*
* @return $this
*/
public function setRotation($rotation)
public function setRotation(int $rotation): self
{
$this->rotation = $rotation;
return $this;
}
/**
* Get Shadow.
*
* @return Drawing\Shadow
*/
public function getShadow()
public function getShadow(): Drawing\Shadow
{
return $this->shadow;
}
/**
* Set Shadow.
*
* @return $this
*/
public function setShadow(?Drawing\Shadow $shadow = null)
public function setShadow(?Drawing\Shadow $shadow = null): self
{
$this->shadow = $shadow;
$this->shadow = $shadow ?? new Drawing\Shadow();
return $this;
}
@ -589,7 +436,7 @@ class BaseDrawing implements IComparable
return md5(
$this->name .
$this->description .
$this->worksheet->getHashCode() .
(($this->worksheet === null) ? '' : $this->worksheet->getHashCode()) .
$this->coordinates .
$this->offsetX .
$this->offsetY .
@ -626,10 +473,7 @@ class BaseDrawing implements IComparable
$this->hyperlink = $hyperlink;
}
/**
* @return null|Hyperlink
*/
public function getHyperlink()
public function getHyperlink(): ?Hyperlink
{
return $this->hyperlink;
}
@ -639,15 +483,19 @@ class BaseDrawing implements IComparable
*/
protected function setSizesAndType(string $path): void
{
if ($this->width == 0 && $this->height == 0 && $this->type == IMAGETYPE_UNKNOWN) {
if ($this->imageWidth === 0 && $this->imageHeight === 0 && $this->type === IMAGETYPE_UNKNOWN) {
$imageData = getimagesize($path);
if (is_array($imageData)) {
$this->width = $imageData[0];
$this->height = $imageData[1];
$this->imageWidth = $imageData[0];
$this->imageHeight = $imageData[1];
$this->type = $imageData[2];
}
}
if ($this->width === 0 && $this->height === 0) {
$this->width = $this->imageWidth;
$this->height = $this->imageHeight;
}
}
/**
@ -657,4 +505,31 @@ class BaseDrawing implements IComparable
{
return $this->type;
}
public function getImageWidth(): int
{
return $this->imageWidth;
}
public function getImageHeight(): int
{
return $this->imageHeight;
}
public function getEditAs(): string
{
return $this->editAs;
}
public function setEditAs(string $editAs): self
{
$this->editAs = $editAs;
return $this;
}
public function validEditAs(): bool
{
return in_array($this->editAs, self::VALID_EDIT_AS);
}
}

View File

@ -47,6 +47,9 @@ class MemoryDrawing extends BaseDrawing
*/
private $uniqueName;
/** @var null|resource */
private $alwaysNull;
/**
* Create a new MemoryDrawing.
*/
@ -56,6 +59,7 @@ class MemoryDrawing extends BaseDrawing
$this->renderingFunction = self::RENDERING_DEFAULT;
$this->mimeType = self::MIMETYPE_DEFAULT;
$this->uniqueName = md5(mt_rand(0, 9999) . time() . mt_rand(0, 9999));
$this->alwaysNull = null;
// Initialize parent
parent::__construct();
@ -64,8 +68,9 @@ class MemoryDrawing extends BaseDrawing
public function __destruct()
{
if ($this->imageResource) {
imagedestroy($this->imageResource);
$this->imageResource = null;
$rslt = @imagedestroy($this->imageResource);
// "Fix" for Scrutinizer
$this->imageResource = $rslt ? null : $this->alwaysNull;
}
}

View File

@ -35,8 +35,7 @@ class Row
*/
public function __destruct()
{
// @phpstan-ignore-next-line
$this->worksheet = null;
$this->worksheet = null; // @phpstan-ignore-line
}
/**

View File

@ -3,6 +3,7 @@
namespace PhpOffice\PhpSpreadsheet\Writer\Xlsx;
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
use PhpOffice\PhpSpreadsheet\Shared\Drawing as SharedDrawing;
use PhpOffice\PhpSpreadsheet\Shared\XMLWriter;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Worksheet\BaseDrawing;
@ -56,7 +57,10 @@ class Drawing extends WriterPart
// Loop through charts and write the chart position
if ($chartCount > 0) {
for ($c = 0; $c < $chartCount; ++$c) {
$this->writeChart($objWriter, $worksheet->getChartByIndex($c), $c + $i);
$chart = $worksheet->getChartByIndex((string) $c);
if ($chart !== false) {
$this->writeChart($objWriter, $chart, $c + $i);
}
}
}
}
@ -90,16 +94,16 @@ class Drawing extends WriterPart
$objWriter->startElement('xdr:twoCellAnchor');
$objWriter->startElement('xdr:from');
$objWriter->writeElement('xdr:col', $tlColRow[0] - 1);
$objWriter->writeElement('xdr:colOff', \PhpOffice\PhpSpreadsheet\Shared\Drawing::pixelsToEMU($tl['xOffset']));
$objWriter->writeElement('xdr:row', $tlColRow[1] - 1);
$objWriter->writeElement('xdr:rowOff', \PhpOffice\PhpSpreadsheet\Shared\Drawing::pixelsToEMU($tl['yOffset']));
$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', $brColRow[0] - 1);
$objWriter->writeElement('xdr:colOff', \PhpOffice\PhpSpreadsheet\Shared\Drawing::pixelsToEMU($br['xOffset']));
$objWriter->writeElement('xdr:row', $brColRow[1] - 1);
$objWriter->writeElement('xdr:rowOff', \PhpOffice\PhpSpreadsheet\Shared\Drawing::pixelsToEMU($br['yOffset']));
$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();
$objWriter->startElement('xdr:graphicFrame');
@ -107,7 +111,7 @@ class Drawing extends WriterPart
$objWriter->startElement('xdr:nvGraphicFramePr');
$objWriter->startElement('xdr:cNvPr');
$objWriter->writeAttribute('name', 'Chart ' . $relationId);
$objWriter->writeAttribute('id', 1025 * $relationId);
$objWriter->writeAttribute('id', (string) (1025 * $relationId));
$objWriter->endElement();
$objWriter->startElement('xdr:cNvGraphicFramePr');
$objWriter->startElement('a:graphicFrameLocks');
@ -153,28 +157,31 @@ class Drawing extends WriterPart
public function writeDrawing(XMLWriter $objWriter, BaseDrawing $drawing, $relationId = -1, $hlinkClickId = null): void
{
if ($relationId >= 0) {
$isTwoCellAnchor = $drawing->getCoordinates2() !== null;
$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($drawing->getOffsetX()));
$objWriter->writeElement('xdr:row', $aCoordinates[1] - 1);
$objWriter->writeElement('xdr:rowOff', \PhpOffice\PhpSpreadsheet\Shared\Drawing::pixelsToEMU($drawing->getOffsetY()));
$objWriter->writeElement('xdr:col', (string) ($aCoordinates[0] - 1));
$objWriter->writeElement('xdr:colOff', self::stringEmu($drawing->getOffsetX()));
$objWriter->writeElement('xdr:row', (string) ($aCoordinates[1] - 1));
$objWriter->writeElement('xdr:rowOff', self::stringEmu($drawing->getOffsetY()));
$objWriter->endElement();
// xdr:to
$objWriter->startElement('xdr:to');
$objWriter->writeElement('xdr:col', $aCoordinates2[0] - 1);
$objWriter->writeElement('xdr:colOff', \PhpOffice\PhpSpreadsheet\Shared\Drawing::pixelsToEMU($drawing->getOffsetX2()));
$objWriter->writeElement('xdr:row', $aCoordinates2[1] - 1);
$objWriter->writeElement('xdr:rowOff', \PhpOffice\PhpSpreadsheet\Shared\Drawing::pixelsToEMU($drawing->getOffsetY2()));
$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
@ -184,16 +191,16 @@ class Drawing extends WriterPart
// xdr:from
$objWriter->startElement('xdr:from');
$objWriter->writeElement('xdr:col', $aCoordinates[0] - 1);
$objWriter->writeElement('xdr:colOff', \PhpOffice\PhpSpreadsheet\Shared\Drawing::pixelsToEMU($drawing->getOffsetX()));
$objWriter->writeElement('xdr:row', $aCoordinates[1] - 1);
$objWriter->writeElement('xdr:rowOff', \PhpOffice\PhpSpreadsheet\Shared\Drawing::pixelsToEMU($drawing->getOffsetY()));
$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($drawing->getWidth()));
$objWriter->writeAttribute('cy', \PhpOffice\PhpSpreadsheet\Shared\Drawing::pixelsToEMU($drawing->getHeight()));
$objWriter->writeAttribute('cx', self::stringEmu($drawing->getWidth()));
$objWriter->writeAttribute('cy', self::stringEmu($drawing->getHeight()));
$objWriter->endElement();
}
@ -205,7 +212,7 @@ class Drawing extends WriterPart
// xdr:cNvPr
$objWriter->startElement('xdr:cNvPr');
$objWriter->writeAttribute('id', $relationId);
$objWriter->writeAttribute('id', (string) $relationId);
$objWriter->writeAttribute('name', $drawing->getName());
$objWriter->writeAttribute('descr', $drawing->getDescription());
@ -247,11 +254,11 @@ class Drawing extends WriterPart
// a:xfrm
$objWriter->startElement('a:xfrm');
$objWriter->writeAttribute('rot', \PhpOffice\PhpSpreadsheet\Shared\Drawing::degreesToAngle($drawing->getRotation()));
$objWriter->writeAttribute('rot', (string) SharedDrawing::degreesToAngle($drawing->getRotation()));
if ($isTwoCellAnchor) {
$objWriter->startElement('a:ext');
$objWriter->writeAttribute('cx', \PhpOffice\PhpSpreadsheet\Shared\Drawing::pixelsToEMU($drawing->getWidth()));
$objWriter->writeAttribute('cy', \PhpOffice\PhpSpreadsheet\Shared\Drawing::pixelsToEMU($drawing->getHeight()));
$objWriter->writeAttribute('cx', self::stringEmu($drawing->getWidth()));
$objWriter->writeAttribute('cy', self::stringEmu($drawing->getHeight()));
$objWriter->endElement();
}
$objWriter->endElement();
@ -271,9 +278,9 @@ class Drawing extends WriterPart
// a:outerShdw
$objWriter->startElement('a:outerShdw');
$objWriter->writeAttribute('blurRad', \PhpOffice\PhpSpreadsheet\Shared\Drawing::pixelsToEMU($drawing->getShadow()->getBlurRadius()));
$objWriter->writeAttribute('dist', \PhpOffice\PhpSpreadsheet\Shared\Drawing::pixelsToEMU($drawing->getShadow()->getDistance()));
$objWriter->writeAttribute('dir', \PhpOffice\PhpSpreadsheet\Shared\Drawing::degreesToAngle($drawing->getShadow()->getDirection()));
$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');
@ -283,7 +290,7 @@ class Drawing extends WriterPart
// a:alpha
$objWriter->startElement('a:alpha');
$objWriter->writeAttribute('val', $drawing->getShadow()->getAlpha() * 1000);
$objWriter->writeAttribute('val', (string) ($drawing->getShadow()->getAlpha() * 1000));
$objWriter->endElement();
$objWriter->endElement();
@ -528,4 +535,9 @@ class Drawing extends WriterPart
$objWriter->writeAttribute('r:id', 'rId' . $hlinkClickId);
$objWriter->endElement();
}
private static function stringEmu(int $pixelValue): string
{
return (string) SharedDrawing::pixelsToEMU($pixelValue);
}
}

View File

@ -0,0 +1,193 @@
<?php
namespace PhpOffice\PhpSpreadsheetTests;
use PhpOffice\PhpSpreadsheet\CellReferenceHelper;
use PHPUnit\Framework\TestCase;
class CellReferenceHelperTest extends TestCase
{
/**
* @dataProvider cellReferenceHelperInsertColumnsProvider
*/
public function testCellReferenceHelperInsertColumns(string $expectedResult, string $cellAddress): void
{
$cellReferenceHelper = new CellReferenceHelper('E5', 2, 0);
$result = $cellReferenceHelper->updateCellReference($cellAddress);
self::assertSame($expectedResult, $result);
}
public function cellReferenceHelperInsertColumnsProvider(): array
{
return [
['A1', 'A1'],
['D5', 'D5'],
['G5', 'E5'],
['$E5', '$E5'],
['G$5', 'E$5'],
['I5', 'G5'],
['$G$5', '$G$5'],
];
}
/**
* @dataProvider cellReferenceHelperDeleteColumnsProvider
*/
public function testCellReferenceHelperDeleteColumns(string $expectedResult, string $cellAddress): void
{
$cellReferenceHelper = new CellReferenceHelper('E5', -2, 0);
$result = $cellReferenceHelper->updateCellReference($cellAddress);
self::assertSame($expectedResult, $result);
}
public function cellReferenceHelperDeleteColumnsProvider(): array
{
return [
['A1', 'A1'],
['D5', 'D5'],
['C5', 'E5'],
['$E5', '$E5'],
['C$5', 'E$5'],
['E5', 'G5'],
['$G$5', '$G$5'],
];
}
/**
* @dataProvider cellReferenceHelperInsertRowsProvider
*/
public function testCellReferenceHelperInsertRows(string $expectedResult, string $cellAddress): void
{
$cellReferenceHelper = new CellReferenceHelper('E5', 0, 2);
$result = $cellReferenceHelper->updateCellReference($cellAddress);
self::assertSame($expectedResult, $result);
}
public function cellReferenceHelperInsertRowsProvider(): array
{
return [
['A1', 'A1'],
['E4', 'E4'],
['E7', 'E5'],
['E$5', 'E$5'],
['$E7', '$E5'],
['E11', 'E9'],
['$E$9', '$E$9'],
];
}
/**
* @dataProvider cellReferenceHelperDeleteRowsProvider
*/
public function testCellReferenceHelperDeleteRows(string $expectedResult, string $cellAddress): void
{
$cellReferenceHelper = new CellReferenceHelper('E5', 0, -2);
$result = $cellReferenceHelper->updateCellReference($cellAddress);
self::assertSame($expectedResult, $result);
}
public function cellReferenceHelperDeleteRowsProvider(): array
{
return [
['A1', 'A1'],
['E4', 'E4'],
['E3', 'E5'],
['E$5', 'E$5'],
['$E3', '$E5'],
['E7', 'E9'],
['$E$9', '$E$9'],
];
}
/**
* @dataProvider cellReferenceHelperInsertColumnsAbsoluteProvider
*/
public function testCellReferenceHelperInsertColumnsAbsolute(string $expectedResult, string $cellAddress): void
{
$cellReferenceHelper = new CellReferenceHelper('E5', 2, 0);
$result = $cellReferenceHelper->updateCellReference($cellAddress, true);
self::assertSame($expectedResult, $result);
}
public function cellReferenceHelperInsertColumnsAbsoluteProvider(): array
{
return [
['A1', 'A1'],
['D5', 'D5'],
['G5', 'E5'],
['$G5', '$E5'],
['G$5', 'E$5'],
['I5', 'G5'],
['$I$5', '$G$5'],
];
}
/**
* @dataProvider cellReferenceHelperDeleteColumnsAbsoluteProvider
*/
public function testCellReferenceHelperDeleteColumnsAbsolute(string $expectedResult, string $cellAddress): void
{
$cellReferenceHelper = new CellReferenceHelper('E5', -2, 0);
$result = $cellReferenceHelper->updateCellReference($cellAddress, true);
self::assertSame($expectedResult, $result);
}
public function cellReferenceHelperDeleteColumnsAbsoluteProvider(): array
{
return [
['A1', 'A1'],
['D5', 'D5'],
['C5', 'E5'],
['$C5', '$E5'],
['C$5', 'E$5'],
['E5', 'G5'],
['$E$5', '$G$5'],
];
}
/**
* @dataProvider cellReferenceHelperInsertRowsAbsoluteProvider
*/
public function testCellReferenceHelperInsertRowsAbsolute(string $expectedResult, string $cellAddress): void
{
$cellReferenceHelper = new CellReferenceHelper('E5', 0, 2);
$result = $cellReferenceHelper->updateCellReference($cellAddress, true);
self::assertSame($expectedResult, $result);
}
public function cellReferenceHelperInsertRowsAbsoluteProvider(): array
{
return [
['A1', 'A1'],
['E4', 'E4'],
['E7', 'E5'],
['E$7', 'E$5'],
['$E7', '$E5'],
['E11', 'E9'],
['$E$11', '$E$9'],
];
}
/**
* @dataProvider cellReferenceHelperDeleteRowsAbsoluteProvider
*/
public function testCellReferenceHelperDeleteRowsAbsolute(string $expectedResult, string $cellAddress): void
{
$cellReferenceHelper = new CellReferenceHelper('E5', 0, -2);
$result = $cellReferenceHelper->updateCellReference($cellAddress, true);
self::assertSame($expectedResult, $result);
}
public function cellReferenceHelperDeleteRowsAbsoluteProvider(): array
{
return [
['A1', 'A1'],
['E4', 'E4'],
['E3', 'E5'],
['E$3', 'E$5'],
['$E3', '$E5'],
['E7', 'E9'],
['$E$7', '$E$9'],
];
}
}

View File

@ -3,8 +3,12 @@
namespace PhpOffice\PhpSpreadsheetTests;
use PhpOffice\PhpSpreadsheet\Cell\DataType;
use PhpOffice\PhpSpreadsheet\Cell\Hyperlink;
use PhpOffice\PhpSpreadsheet\Comment;
use PhpOffice\PhpSpreadsheet\ReferenceHelper;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Style\ConditionalFormatting\Wizard;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
use PHPUnit\Framework\TestCase;
class ReferenceHelperTest extends TestCase
@ -177,4 +181,158 @@ class ReferenceHelperTest extends TestCase
self::assertNull($cells[1][1]);
self::assertArrayNotHasKey(2, $cells[1]);
}
public function testInsertRowsWithPageBreaks(): void
{
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
$sheet->fromArray([[1, 2], [3, 4], [5, 6], [7, 8], [9, 10]], null, 'A1', true);
$sheet->setBreak('A2', Worksheet::BREAK_ROW);
$sheet->setBreak('A5', Worksheet::BREAK_ROW);
$sheet->insertNewRowBefore(2, 2);
$breaks = $sheet->getBreaks();
ksort($breaks);
self::assertSame(['A4' => Worksheet::BREAK_ROW, 'A7' => Worksheet::BREAK_ROW], $breaks);
}
public function testDeleteRowsWithPageBreaks(): void
{
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
$sheet->fromArray([[1, 2], [3, 4], [5, 6], [7, 8], [9, 10]], null, 'A1', true);
$sheet->setBreak('A2', Worksheet::BREAK_ROW);
$sheet->setBreak('A5', Worksheet::BREAK_ROW);
$sheet->removeRow(2, 2);
$breaks = $sheet->getBreaks();
self::assertSame(['A3' => Worksheet::BREAK_ROW], $breaks);
}
public function testInsertRowsWithComments(): void
{
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
$sheet->fromArray([[1, 2], [3, 4], [5, 6], [7, 8], [9, 10]], null, 'A1', true);
$sheet->getComment('A2')->getText()->createText('First Comment');
$sheet->getComment('A5')->getText()->createText('Second Comment');
$sheet->insertNewRowBefore(2, 2);
$comments = array_map(
function (Comment $value) {
return $value->getText()->getPlainText();
},
$sheet->getComments()
);
self::assertSame(['A4' => 'First Comment', 'A7' => 'Second Comment'], $comments);
}
public function testDeleteRowsWithComments(): void
{
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
$sheet->fromArray([[1, 2], [3, 4], [5, 6], [7, 8], [9, 10]], null, 'A1', true);
$sheet->getComment('A2')->getText()->createText('First Comment');
$sheet->getComment('A5')->getText()->createText('Second Comment');
$sheet->removeRow(2, 2);
$comments = array_map(
function (Comment $value) {
return $value->getText()->getPlainText();
},
$sheet->getComments()
);
self::assertSame(['A3' => 'Second Comment'], $comments);
}
public function testInsertRowsWithHyperlinks(): void
{
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
$sheet->fromArray([[1, 2], [3, 4], [5, 6], [7, 8], [9, 10]], null, 'A1', true);
$sheet->getCell('A2')->getHyperlink()->setUrl('https://github.com/PHPOffice/PhpSpreadsheet');
$sheet->getCell('A5')->getHyperlink()->setUrl('https://phpspreadsheet.readthedocs.io/en/latest/');
$sheet->insertNewRowBefore(2, 2);
$hyperlinks = array_map(
function (Hyperlink $value) {
return $value->getUrl();
},
$sheet->getHyperlinkCollection()
);
ksort($hyperlinks);
self::assertSame(
[
'A4' => 'https://github.com/PHPOffice/PhpSpreadsheet',
'A7' => 'https://phpspreadsheet.readthedocs.io/en/latest/',
],
$hyperlinks
);
}
public function testDeleteRowsWithHyperlinks(): void
{
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
$sheet->fromArray([[1, 2], [3, 4], [5, 6], [7, 8], [9, 10]], null, 'A1', true);
$sheet->getCell('A2')->getHyperlink()->setUrl('https://github.com/PHPOffice/PhpSpreadsheet');
$sheet->getCell('A5')->getHyperlink()->setUrl('https://phpspreadsheet.readthedocs.io/en/latest/');
$sheet->removeRow(2, 2);
$hyperlinks = array_map(
function (Hyperlink $value) {
return $value->getUrl();
},
$sheet->getHyperlinkCollection()
);
self::assertSame(['A3' => 'https://phpspreadsheet.readthedocs.io/en/latest/'], $hyperlinks);
}
public function testInsertRowsWithConditionalFormatting(): void
{
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
$sheet->fromArray([[1, 2, 3, 4], [3, 4, 5, 6], [5, 6, 7, 8], [7, 8, 9, 10], [9, 10, 11, 12]], null, 'C3', true);
$sheet->getCell('H5')->setValue(5);
$cellRange = 'C3:F7';
$conditionalStyles = [];
$wizardFactory = new Wizard($cellRange);
/** @var Wizard\CellValue $cellWizard */
$cellWizard = $wizardFactory->newRule(Wizard::CELL_VALUE);
$cellWizard->equals('$H$5', Wizard::VALUE_TYPE_CELL);
$conditionalStyles[] = $cellWizard->getConditional();
$cellWizard->greaterThan('$H$5', Wizard::VALUE_TYPE_CELL);
$conditionalStyles[] = $cellWizard->getConditional();
$cellWizard->lessThan('$H$5', Wizard::VALUE_TYPE_CELL);
$conditionalStyles[] = $cellWizard->getConditional();
$spreadsheet->getActiveSheet()
->getStyle($cellWizard->getCellRange())
->setConditionalStyles($conditionalStyles);
$sheet->insertNewRowBefore(4, 2);
$styles = $sheet->getConditionalStylesCollection();
// verify that the conditional range has been updated
self::assertSame('C3:F9', array_keys($styles)[0]);
// verify that the conditions have been updated
foreach ($styles as $style) {
foreach ($style as $conditions) {
self::assertSame('$H$7', $conditions->getConditions()[0]);
}
}
}
}

View File

@ -2,7 +2,10 @@
namespace PhpOffice\PhpSpreadsheetTests\Worksheet;
use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Worksheet\Drawing;
use PhpOffice\PhpSpreadsheet\Worksheet\HeaderFooterDrawing;
use PhpOffice\PhpSpreadsheet\Worksheet\MemoryDrawing;
use PHPUnit\Framework\TestCase;
@ -41,4 +44,76 @@ class DrawingTest extends TestCase
$spreadsheet->disconnectWorksheets();
}
}
public function testChangeWorksheet(): void
{
$spreadsheet = new Spreadsheet();
$sheet1 = $spreadsheet->getActiveSheet();
$sheet2 = $spreadsheet->createSheet();
$drawing = new Drawing();
$drawing->setName('Green Square');
$drawing->setPath('tests/data/Writer/XLSX/green_square.gif');
self::assertEquals($drawing->getWidth(), 150);
self::assertEquals($drawing->getHeight(), 150);
$drawing->setCoordinates('A1');
$drawing->setOffsetX(30);
$drawing->setOffsetY(10);
$drawing->setWorksheet($sheet1);
try {
$drawing->setWorksheet($sheet2);
self::fail('Should throw exception when attempting set worksheet without specifying override');
} catch (PhpSpreadsheetException $e) {
self::assertStringContainsString('A Worksheet has already been assigned.', $e->getMessage());
}
self::assertSame($sheet1, $drawing->getWorksheet());
self::assertCount(1, $sheet1->getDrawingCollection());
self::assertCount(0, $sheet2->getDrawingCollection());
$drawing->setWorksheet($sheet2, true);
self::assertSame($sheet2, $drawing->getWorksheet());
self::assertCount(0, $sheet1->getDrawingCollection());
self::assertCount(1, $sheet2->getDrawingCollection());
}
public function testHeaderFooter(): void
{
$drawing1 = new HeaderFooterDrawing();
$drawing1->setName('Blue Square');
$drawing1->setPath('tests/data/Writer/XLSX/blue_square.png');
self::assertEquals($drawing1->getWidth(), 100);
self::assertEquals($drawing1->getHeight(), 100);
$drawing2 = new HeaderFooterDrawing();
$drawing2->setName('Blue Square');
$drawing2->setPath('tests/data/Writer/XLSX/blue_square.png');
self::assertSame($drawing1->getHashCode(), $drawing2->getHashCode());
$drawing2->setOffsetX(100);
self::assertNotEquals($drawing1->getHashCode(), $drawing2->getHashCode());
}
public function testSetWidthAndHeight(): void
{
$drawing = new Drawing();
$drawing->setName('Blue Square');
$drawing->setPath('tests/data/Writer/XLSX/blue_square.png');
self::assertSame(100, $drawing->getWidth());
self::assertSame(100, $drawing->getHeight());
self::assertTrue($drawing->getResizeProportional());
$drawing->setResizeProportional(false);
$drawing->setWidthAndHeight(150, 200);
self::assertSame(150, $drawing->getWidth());
self::assertSame(200, $drawing->getHeight());
$drawing->setResizeProportional(true);
$drawing->setWidthAndHeight(300, 250);
// width increase% more than height, so scale width
self::assertSame(188, $drawing->getWidth());
self::assertSame(250, $drawing->getHeight());
$drawing->setResizeProportional(false);
$drawing->setWidthAndHeight(150, 200);
$drawing->setResizeProportional(true);
// height increase% more than width, so scale height
$drawing->setWidthAndHeight(175, 350);
self::assertSame(175, $drawing->getWidth());
self::assertSame(234, $drawing->getHeight());
}
}

View File

@ -8,7 +8,9 @@ use PhpOffice\PhpSpreadsheet\IOFactory;
use PhpOffice\PhpSpreadsheet\Reader\Xlsx;
use PhpOffice\PhpSpreadsheet\Shared\File;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Worksheet\BaseDrawing;
use PhpOffice\PhpSpreadsheet\Worksheet\Drawing;
use PhpOffice\PhpSpreadsheet\Worksheet\MemoryDrawing;
use PhpOffice\PhpSpreadsheetTests\Functional\AbstractFunctional;
class DrawingsTest extends AbstractFunctional
@ -28,6 +30,8 @@ class DrawingsTest extends AbstractFunctional
// Fake assert. The only thing we need is to ensure the file is loaded without exception
self::assertNotNull($reloadedSpreadsheet);
$spreadsheet->disconnectWorksheets();
$reloadedSpreadsheet->disconnectWorksheets();
}
/**
@ -59,6 +63,8 @@ class DrawingsTest extends AbstractFunctional
// Fake assert. The only thing we need is to ensure the file is loaded without exception
self::assertNotNull($reloadedSpreadsheet);
$spreadsheet->disconnectWorksheets();
$reloadedSpreadsheet->disconnectWorksheets();
}
/**
@ -96,6 +102,8 @@ class DrawingsTest extends AbstractFunctional
unlink($tempFileName);
self::assertNotNull($reloadedSpreadsheet);
$spreadsheet->disconnectWorksheets();
$reloadedSpreadsheet->disconnectWorksheets();
}
/**
@ -173,7 +181,8 @@ class DrawingsTest extends AbstractFunctional
self::assertEquals($comment->getBackgroundImage()->getType(), IMAGETYPE_PNG);
unlink($tempFileName);
self::assertNotNull($reloadedSpreadsheet);
$spreadsheet->disconnectWorksheets();
$reloadedSpreadsheet->disconnectWorksheets();
}
/**
@ -287,6 +296,7 @@ class DrawingsTest extends AbstractFunctional
$drawing->setPath('tests/data/Writer/XLSX/orange_square_24_bit.bmp');
self::assertEquals($drawing->getWidth(), 70);
self::assertEquals($drawing->getHeight(), 70);
self::assertSame(IMAGETYPE_PNG, $drawing->getImageTypeForSave());
$comment = $sheet->getComment('A6');
$comment->setBackgroundImage($drawing);
$comment->setSizeAsBackgroundImage();
@ -304,6 +314,8 @@ class DrawingsTest extends AbstractFunctional
$drawing = new Drawing();
$drawing->setName('Purple Square');
$drawing->setPath('tests/data/Writer/XLSX/purple_square.tiff');
self::assertStringContainsString('purple_square.tiff', $drawing->getFilename());
self::assertFalse($drawing->getIsUrl());
$comment = $sheet->getComment('A7');
self::assertTrue($comment instanceof Comment);
self::assertFalse($comment->hasBackgroundImage());
@ -326,6 +338,14 @@ class DrawingsTest extends AbstractFunctional
self::assertEquals($e->getMessage(), 'Unsupported image type in comment background. Supported types: PNG, JPEG, BMP, GIF.');
}
try {
$drawing->getMediaFilename();
self::fail('Should throw exception when attempting to get media file name for tiff');
} catch (PhpSpreadsheetException $e) {
self::assertTrue($e instanceof PhpSpreadsheetException);
self::assertEquals($e->getMessage(), 'Unsupported image type in comment background. Supported types: PNG, JPEG, BMP, GIF.');
}
try {
$drawing->getImageFileExtensionForSave();
self::fail('Should throw exception when attempting to get image file extention for tiff');
@ -428,7 +448,8 @@ class DrawingsTest extends AbstractFunctional
unlink($tempFileName);
self::assertNotNull($reloadedSpreadsheet);
$spreadsheet->disconnectWorksheets();
$reloadedSpreadsheet->disconnectWorksheets();
}
/**
@ -436,7 +457,6 @@ class DrawingsTest extends AbstractFunctional
*/
public function testTwoCellAnchorDrawing(): void
{
$reader = new Xlsx();
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
@ -455,31 +475,133 @@ class DrawingsTest extends AbstractFunctional
$drawing->setWorksheet($sheet);
// Write file
$writer = IOFactory::createWriter($spreadsheet, 'Xlsx');
$tempFileName = File::sysGetTempDir() . '/drawings_image_that_two_cell_anchor.xlsx';
$writer->save($tempFileName);
// Read new file
$reloadedSpreadsheet = $reader->load($tempFileName);
$sheet = $reloadedSpreadsheet->getActiveSheet();
$reloadedSpreadsheet = $this->writeAndReload($spreadsheet, 'Xlsx');
$spreadsheet->disconnectWorksheets();
$rsheet = $reloadedSpreadsheet->getActiveSheet();
// Check image coordinates.
$drawingCollection = $sheet->getDrawingCollection();
$drawingCollection = $rsheet->getDrawingCollection();
self::assertCount(1, $drawingCollection);
$drawing = $drawingCollection[0];
self::assertNotNull($drawing);
self::assertSame(150, $drawing->getWidth());
self::assertSame(150, $drawing->getHeight());
self::assertSame('A1', $drawing->getCoordinates());
self::assertSame(30, $drawing->getOffsetX());
self::assertSame(10, $drawing->getOffsetY());
self::assertSame('E8', $drawing->getCoordinates2());
self::assertSame(-50, $drawing->getOffsetX2());
self::assertSame(-20, $drawing->getOffsetY2());
self::assertSame($rsheet, $drawing->getWorksheet());
$reloadedSpreadsheet->disconnectWorksheets();
}
/**
* Test editAs attribute for two-cell anchors.
*
* @dataProvider providerEditAs
*/
public function testTwoCellEditAs(string $editAs, ?string $expectedResult = null): void
{
if ($expectedResult === null) {
$expectedResult = $editAs;
}
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
// Add gif image that coordinates is two cell anchor.
$drawing = new Drawing();
$drawing->setName('Green Square');
$drawing->setPath('tests/data/Writer/XLSX/green_square.gif');
self::assertEquals($drawing->getWidth(), 150);
self::assertEquals($drawing->getHeight(), 150);
self::assertEquals($drawing->getCoordinates(), 'A1');
self::assertEquals($drawing->getOffsetX(), 30);
self::assertEquals($drawing->getOffsetY(), 10);
self::assertEquals($drawing->getCoordinates2(), 'E8');
self::assertEquals($drawing->getOffsetX2(), -50);
self::assertEquals($drawing->getOffsetY2(), -20);
self::assertEquals($drawing->getWorksheet(), $sheet);
$drawing->setCoordinates('A1');
$drawing->setOffsetX(30);
$drawing->setOffsetY(10);
$drawing->setCoordinates2('E8');
$drawing->setOffsetX2(-50);
$drawing->setOffsetY2(-20);
if ($editAs !== '') {
$drawing->setEditAs($editAs);
}
$drawing->setWorksheet($sheet);
unlink($tempFileName);
// Write file
$reloadedSpreadsheet = $this->writeAndReload($spreadsheet, 'Xlsx');
$spreadsheet->disconnectWorksheets();
$rsheet = $reloadedSpreadsheet->getActiveSheet();
self::assertNotNull($reloadedSpreadsheet);
// Check image coordinates.
$drawingCollection = $rsheet->getDrawingCollection();
$drawing = $drawingCollection[0];
self::assertNotNull($drawing);
self::assertSame(150, $drawing->getWidth());
self::assertSame(150, $drawing->getHeight());
self::assertSame('A1', $drawing->getCoordinates());
self::assertSame(30, $drawing->getOffsetX());
self::assertSame(10, $drawing->getOffsetY());
self::assertSame('E8', $drawing->getCoordinates2());
self::assertSame(-50, $drawing->getOffsetX2());
self::assertSame(-20, $drawing->getOffsetY2());
self::assertSame($rsheet, $drawing->getWorksheet());
self::assertSame($expectedResult, $drawing->getEditAs());
$reloadedSpreadsheet->disconnectWorksheets();
}
public function providerEditAs(): array
{
return [
'absolute' => ['absolute'],
'onecell' => ['onecell'],
'twocell' => ['twocell'],
'unset (will be treated as twocell)' => [''],
'unknown (will be treated as twocell)' => ['unknown', ''],
];
}
public function testMemoryDrawingDuplicateResource(): void
{
$gdImage = imagecreatetruecolor(120, 20);
$textColor = ($gdImage === false) ? false : imagecolorallocate($gdImage, 255, 255, 255);
if ($gdImage === false || $textColor === false) {
self::fail('imagecreatetruecolor or imagecolorallocate failed');
} else {
$spreadsheet = new Spreadsheet();
$aSheet = $spreadsheet->getActiveSheet();
imagestring($gdImage, 1, 5, 5, 'Created with PhpSpreadsheet', $textColor);
$listOfModes = [
BaseDrawing::EDIT_AS_TWOCELL,
BaseDrawing::EDIT_AS_ABSOLUTE,
BaseDrawing::EDIT_AS_ONECELL,
];
foreach ($listOfModes as $i => $mode) {
$drawing = new MemoryDrawing();
$drawing->setName('In-Memory image ' . $i);
$drawing->setDescription('In-Memory image ' . $i);
$drawing->setCoordinates('A' . ((4 * $i) + 1));
$drawing->setCoordinates2('D' . ((4 * $i) + 4));
$drawing->setEditAs($mode);
$drawing->setImageResource($gdImage);
$drawing->setRenderingFunction(
MemoryDrawing::RENDERING_JPEG
);
$drawing->setMimeType(MemoryDrawing::MIMETYPE_DEFAULT);
$drawing->setWorksheet($aSheet);
}
$reloadedSpreadsheet = $this->writeAndReload($spreadsheet, 'Xlsx');
$spreadsheet->disconnectWorksheets();
foreach ($reloadedSpreadsheet->getActiveSheet()->getDrawingCollection() as $index => $pDrawing) {
self::assertEquals($listOfModes[$index], $pDrawing->getEditAs(), 'functional test drawing twoCellAnchor');
}
$reloadedSpreadsheet->disconnectWorksheets();
}
}
}

View File

@ -34,4 +34,8 @@ return [
'supply a1 argument as int' => [900, 'A2:A4', 1],
'supply a1 argument as float' => [900, 'A2:A4', 7.3],
'supply a1 argument as string not permitted' => ['#VALUE!', 'A2:A4', '1'],
'row range' => [600, '1:3'],
'column range' => [1500, 'A:C'],
'row range on different sheet' => [66, 'OtherSheet!1:3'],
'column range on different sheet' => [165, 'OtherSheet!A:C'],
];

View File

@ -24,6 +24,13 @@ return [
2,
false,
],
[
'#REF!',
1,
'HELLO WORLD',
2,
false,
],
[
100,
1,

View File

@ -22,6 +22,34 @@ return [
'2020',
'=SUM(A1:C3)',
],
'column range' => [
'=SUM(B:C)',
2,
0,
'2020',
'=SUM(D:E)',
],
'column range with absolute' => [
'=SUM($B:C)',
2,
0,
'2020',
'=SUM($B:E)',
],
'row range' => [
'=SUM(2:3)',
0,
2,
'2020',
'=SUM(4:5)',
],
'row range with absolute' => [
'=SUM($2:3)',
0,
2,
'2020',
'=SUM($2:5)',
],
[
'=SUM(2020!C3:E5,2019!C3:E5)',
-2,