Skip to content

Add two cell anchor drawing #2532

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Mar 10, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,11 @@ and this project adheres to [Semantic Versioning](https://semver.org).
- Full support of the above CF Rules for the Xlsx Reader and Writer; even when the file being loaded has CF rules listed in the `<extLst><ext><ConditionalFormattings>` element for the worksheet rather than the `<ConditionalFormatting>` element.
- Provision of a CellMatcher to identify if rules are matched for a cell, and which matching style will be applied.
- Improved documentation and examples, covering all supported CF rule types.
- Add support for one digit decimals (FORMAT_NUMBER_0, FORMAT_PERCENTAGE_0). [PR #2525](https://github.com/PHPOffice/PhpSpreadsheet/pull/2525)
- Initial work enabling Excel function implementations for handling arrays as arguments when used in "array formulae" [#2562](https://github.com/PHPOffice/PhpSpreadsheet/issues/2562)
- Enable most of the Date/Time functions to accept array arguments [#2573](https://github.com/PHPOffice/PhpSpreadsheet/issues/2573)
- Array ready functions - Text, Math/Trig, Statistical, Engineering and Logical [#2580](https://github.com/PHPOffice/PhpSpreadsheet/issues/2580)
- Add support for one digit decimals (FORMAT_NUMBER_0, FORMAT_PERCENTAGE_0). [PR #2525](https://github.com/PHPOffice/PhpSpreadsheet/pull/2525)
- Initial work enabling Excel function implementations for handling arrays as arguments when used in "array formulae" [#2562](https://github.com/PHPOffice/PhpSpreadsheet/issues/2562)
- Enable most of the Date/Time functions to accept array arguments [#2573](https://github.com/PHPOffice/PhpSpreadsheet/issues/2573)
- Array ready functions - Text, Math/Trig, Statistical, Engineering and Logical [#2580](https://github.com/PHPOffice/PhpSpreadsheet/issues/2580)
- Support for two cell anchor drawing of images. [#2532](https://github.com/PHPOffice/PhpSpreadsheet/pull/2532)

### Changed

Expand Down
10 changes: 7 additions & 3 deletions phpstan-baseline.neon
Original file line number Diff line number Diff line change
Expand Up @@ -5485,6 +5485,11 @@ 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
Expand All @@ -5497,12 +5502,12 @@ parameters:

-
message: "#^Parameter \\#2 \\$content of method XMLWriter\\:\\:writeElement\\(\\) expects string\\|null, int given\\.$#"
count: 12
count: 20
path: src/PhpSpreadsheet/Writer/Xlsx/Drawing.php

-
message: "#^Parameter \\#2 \\$value of method XMLWriter\\:\\:writeAttribute\\(\\) expects string, int given\\.$#"
count: 8
count: 10
path: src/PhpSpreadsheet/Writer/Xlsx/Drawing.php

-
Expand Down Expand Up @@ -6009,4 +6014,3 @@ parameters:
message: "#^Cannot call method getExtension\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\BaseDrawing\\|null\\.$#"
count: 2
path: tests/PhpSpreadsheetTests/Writer/Xlsx/WmfTest.php

6 changes: 6 additions & 0 deletions src/PhpSpreadsheet/Reader/Xlsx.php
Original file line number Diff line number Diff line change
Expand Up @@ -1340,6 +1340,12 @@ protected function loadSpreadsheetFromFile(string $filename): Spreadsheet

$objDrawing->setOffsetX(Drawing::EMUToPixels($twoCellAnchor->from->colOff));
$objDrawing->setOffsetY(Drawing::EMUToPixels($twoCellAnchor->from->rowOff));

$objDrawing->setCoordinates2(Coordinate::stringFromColumnIndex(((int) $twoCellAnchor->to->col) + 1) . ($twoCellAnchor->to->row + 1));

$objDrawing->setOffsetX2(Drawing::EMUToPixels($twoCellAnchor->to->colOff));
$objDrawing->setOffsetY2(Drawing::EMUToPixels($twoCellAnchor->to->rowOff));

$objDrawing->setResizeProportional(false);

if ($xfrm) {
Expand Down
99 changes: 99 additions & 0 deletions src/PhpSpreadsheet/Worksheet/BaseDrawing.php
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,27 @@ class BaseDrawing implements IComparable
*/
protected $offsetY;

/**
* Coordinates2.
*
* @var null|string
*/
protected $coordinates2;

/**
* Offset X2.
*
* @var int
*/
protected $offsetX2;

/**
* Offset Y2.
*
* @var int
*/
protected $offsetY2;

/**
* Width.
*
Expand Down Expand Up @@ -125,6 +146,9 @@ public function __construct()
$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;
Expand Down Expand Up @@ -301,6 +325,78 @@ public function getOffsetY()
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.
*
Expand Down Expand Up @@ -497,6 +593,9 @@ public function getHashCode()
$this->coordinates .
$this->offsetX .
$this->offsetY .
$this->coordinates2 .
$this->offsetX2 .
$this->offsetY2 .
$this->width .
$this->height .
$this->rotation .
Expand Down
65 changes: 48 additions & 17 deletions src/PhpSpreadsheet/Writer/Xlsx/Drawing.php
Original file line number Diff line number Diff line change
Expand Up @@ -153,24 +153,49 @@ public function writeChart(XMLWriter $objWriter, \PhpOffice\PhpSpreadsheet\Chart
public function writeDrawing(XMLWriter $objWriter, BaseDrawing $drawing, $relationId = -1, $hlinkClickId = null): void
{
if ($relationId >= 0) {
// xdr:oneCellAnchor
$objWriter->startElement('xdr:oneCellAnchor');
// Image location
$aCoordinates = Coordinate::indexesFromString($drawing->getCoordinates());

// 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->endElement();
$isTwoCellAnchor = $drawing->getCoordinates2() !== null;
if ($isTwoCellAnchor) {
// xdr:twoCellAnchor
$objWriter->startElement('xdr:twoCellAnchor');
// 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->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->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->endElement();
} else {
// xdr:oneCellAnchor
$objWriter->startElement('xdr:oneCellAnchor');
// Image location
$aCoordinates = Coordinate::indexesFromString($drawing->getCoordinates());

// xdr:from
$objWriter->startElement('xdr:from');
$objWriter->writeElement('xdr:col', $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->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->endElement();
}

// xdr:pic
$objWriter->startElement('xdr:pic');
Expand Down Expand Up @@ -223,6 +248,12 @@ public function writeDrawing(XMLWriter $objWriter, BaseDrawing $drawing, $relati
// a:xfrm
$objWriter->startElement('a:xfrm');
$objWriter->writeAttribute('rot', \PhpOffice\PhpSpreadsheet\Shared\Drawing::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->endElement();
}
$objWriter->endElement();

// a:prstGeom
Expand Down
52 changes: 52 additions & 0 deletions tests/PhpSpreadsheetTests/Writer/Xlsx/DrawingsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -430,4 +430,56 @@ public function testBuildWithDifferentImageFormats(): void

self::assertNotNull($reloadedSpreadsheet);
}

/**
* Test save and load XLSX file with drawing image that coordinate is two cell anchor.
*/
public function testTwoCellAnchorDrawing(): void
{
$reader = new Xlsx();
$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);
$drawing->setCoordinates('A1');
$drawing->setOffsetX(30);
$drawing->setOffsetY(10);
$drawing->setCoordinates2('E8');
$drawing->setOffsetX2(-50);
$drawing->setOffsetY2(-20);
$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();

// Check image coordinates.
$drawingCollection = $sheet->getDrawingCollection();
$drawing = $drawingCollection[0];
self::assertNotNull($drawing);

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);

unlink($tempFileName);

self::assertNotNull($reloadedSpreadsheet);
}
}