Skip to content

Commit 572f4e9

Browse files
authored
Add two cell anchor drawing (#2532)
* Add two cell anhor drawing. * Add "Support for two cell anchor drawing of images." to CHANGELOG.md * Add pull-request link to "Support for two cell anchor drawing of images." of CHANGELOG.md
1 parent ce5f91e commit 572f4e9

File tree

6 files changed

+217
-24
lines changed

6 files changed

+217
-24
lines changed

CHANGELOG.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -75,10 +75,11 @@ and this project adheres to [Semantic Versioning](https://semver.org).
7575
- 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.
7676
- Provision of a CellMatcher to identify if rules are matched for a cell, and which matching style will be applied.
7777
- Improved documentation and examples, covering all supported CF rule types.
78-
- Add support for one digit decimals (FORMAT_NUMBER_0, FORMAT_PERCENTAGE_0). [PR #2525](https://github.com/PHPOffice/PhpSpreadsheet/pull/2525)
79-
- Initial work enabling Excel function implementations for handling arrays as arguments when used in "array formulae" [#2562](https://github.com/PHPOffice/PhpSpreadsheet/issues/2562)
80-
- Enable most of the Date/Time functions to accept array arguments [#2573](https://github.com/PHPOffice/PhpSpreadsheet/issues/2573)
81-
- Array ready functions - Text, Math/Trig, Statistical, Engineering and Logical [#2580](https://github.com/PHPOffice/PhpSpreadsheet/issues/2580)
78+
- Add support for one digit decimals (FORMAT_NUMBER_0, FORMAT_PERCENTAGE_0). [PR #2525](https://github.com/PHPOffice/PhpSpreadsheet/pull/2525)
79+
- Initial work enabling Excel function implementations for handling arrays as arguments when used in "array formulae" [#2562](https://github.com/PHPOffice/PhpSpreadsheet/issues/2562)
80+
- Enable most of the Date/Time functions to accept array arguments [#2573](https://github.com/PHPOffice/PhpSpreadsheet/issues/2573)
81+
- Array ready functions - Text, Math/Trig, Statistical, Engineering and Logical [#2580](https://github.com/PHPOffice/PhpSpreadsheet/issues/2580)
82+
- Support for two cell anchor drawing of images. [#2532](https://github.com/PHPOffice/PhpSpreadsheet/pull/2532)
8283

8384
### Changed
8485

phpstan-baseline.neon

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5485,6 +5485,11 @@ parameters:
54855485
count: 2
54865486
path: src/PhpSpreadsheet/Writer/Xlsx/DocProps.php
54875487

5488+
-
5489+
message: "#^Parameter \\#1 \\$coordinates of static method PhpOffice\\\\PhpSpreadsheet\\\\Cell\\\\Coordinate\\:\\:indexesFromString\\(\\) expects string, string\\|null given\\.$#"
5490+
count: 1
5491+
path: src/PhpSpreadsheet/Writer/Xlsx/Drawing.php
5492+
54885493
-
54895494
message: "#^Parameter \\#1 \\$index of method PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\Worksheet\\:\\:getChartByIndex\\(\\) expects string, int\\<0, max\\> given\\.$#"
54905495
count: 1
@@ -5497,12 +5502,12 @@ parameters:
54975502

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

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

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

src/PhpSpreadsheet/Reader/Xlsx.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1340,6 +1340,12 @@ protected function loadSpreadsheetFromFile(string $filename): Spreadsheet
13401340

13411341
$objDrawing->setOffsetX(Drawing::EMUToPixels($twoCellAnchor->from->colOff));
13421342
$objDrawing->setOffsetY(Drawing::EMUToPixels($twoCellAnchor->from->rowOff));
1343+
1344+
$objDrawing->setCoordinates2(Coordinate::stringFromColumnIndex(((int) $twoCellAnchor->to->col) + 1) . ($twoCellAnchor->to->row + 1));
1345+
1346+
$objDrawing->setOffsetX2(Drawing::EMUToPixels($twoCellAnchor->to->colOff));
1347+
$objDrawing->setOffsetY2(Drawing::EMUToPixels($twoCellAnchor->to->rowOff));
1348+
13431349
$objDrawing->setResizeProportional(false);
13441350

13451351
if ($xfrm) {

src/PhpSpreadsheet/Worksheet/BaseDrawing.php

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,27 @@ class BaseDrawing implements IComparable
6464
*/
6565
protected $offsetY;
6666

67+
/**
68+
* Coordinates2.
69+
*
70+
* @var null|string
71+
*/
72+
protected $coordinates2;
73+
74+
/**
75+
* Offset X2.
76+
*
77+
* @var int
78+
*/
79+
protected $offsetX2;
80+
81+
/**
82+
* Offset Y2.
83+
*
84+
* @var int
85+
*/
86+
protected $offsetY2;
87+
6788
/**
6889
* Width.
6990
*
@@ -125,6 +146,9 @@ public function __construct()
125146
$this->coordinates = 'A1';
126147
$this->offsetX = 0;
127148
$this->offsetY = 0;
149+
$this->coordinates2 = null;
150+
$this->offsetX2 = 0;
151+
$this->offsetY2 = 0;
128152
$this->width = 0;
129153
$this->height = 0;
130154
$this->resizeProportional = true;
@@ -301,6 +325,78 @@ public function getOffsetY()
301325
return $this->offsetY;
302326
}
303327

328+
/**
329+
* Get Coordinates2.
330+
*
331+
* @return null|string
332+
*/
333+
public function getCoordinates2()
334+
{
335+
return $this->coordinates2;
336+
}
337+
338+
/**
339+
* Set Coordinates2.
340+
*
341+
* @param null|string $coordinates2 eg: 'A1'
342+
*
343+
* @return $this
344+
*/
345+
public function setCoordinates2($coordinates2)
346+
{
347+
$this->coordinates2 = $coordinates2;
348+
349+
return $this;
350+
}
351+
352+
/**
353+
* Get OffsetX2.
354+
*
355+
* @return int
356+
*/
357+
public function getOffsetX2()
358+
{
359+
return $this->offsetX2;
360+
}
361+
362+
/**
363+
* Set OffsetX2.
364+
*
365+
* @param int $offsetX2
366+
*
367+
* @return $this
368+
*/
369+
public function setOffsetX2($offsetX2)
370+
{
371+
$this->offsetX2 = $offsetX2;
372+
373+
return $this;
374+
}
375+
376+
/**
377+
* Get OffsetY2.
378+
*
379+
* @return int
380+
*/
381+
public function getOffsetY2()
382+
{
383+
return $this->offsetY2;
384+
}
385+
386+
/**
387+
* Set OffsetY2.
388+
*
389+
* @param int $offsetY2
390+
*
391+
* @return $this
392+
*/
393+
public function setOffsetY2($offsetY2)
394+
{
395+
$this->offsetY2 = $offsetY2;
396+
397+
return $this;
398+
}
399+
304400
/**
305401
* Set OffsetY.
306402
*
@@ -497,6 +593,9 @@ public function getHashCode()
497593
$this->coordinates .
498594
$this->offsetX .
499595
$this->offsetY .
596+
$this->coordinates2 .
597+
$this->offsetX2 .
598+
$this->offsetY2 .
500599
$this->width .
501600
$this->height .
502601
$this->rotation .

src/PhpSpreadsheet/Writer/Xlsx/Drawing.php

Lines changed: 48 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -153,24 +153,49 @@ public function writeChart(XMLWriter $objWriter, \PhpOffice\PhpSpreadsheet\Chart
153153
public function writeDrawing(XMLWriter $objWriter, BaseDrawing $drawing, $relationId = -1, $hlinkClickId = null): void
154154
{
155155
if ($relationId >= 0) {
156-
// xdr:oneCellAnchor
157-
$objWriter->startElement('xdr:oneCellAnchor');
158-
// Image location
159-
$aCoordinates = Coordinate::indexesFromString($drawing->getCoordinates());
160-
161-
// xdr:from
162-
$objWriter->startElement('xdr:from');
163-
$objWriter->writeElement('xdr:col', $aCoordinates[0] - 1);
164-
$objWriter->writeElement('xdr:colOff', \PhpOffice\PhpSpreadsheet\Shared\Drawing::pixelsToEMU($drawing->getOffsetX()));
165-
$objWriter->writeElement('xdr:row', $aCoordinates[1] - 1);
166-
$objWriter->writeElement('xdr:rowOff', \PhpOffice\PhpSpreadsheet\Shared\Drawing::pixelsToEMU($drawing->getOffsetY()));
167-
$objWriter->endElement();
156+
$isTwoCellAnchor = $drawing->getCoordinates2() !== null;
157+
if ($isTwoCellAnchor) {
158+
// xdr:twoCellAnchor
159+
$objWriter->startElement('xdr:twoCellAnchor');
160+
// Image location
161+
$aCoordinates = Coordinate::indexesFromString($drawing->getCoordinates());
162+
$aCoordinates2 = Coordinate::indexesFromString($drawing->getCoordinates2());
163+
164+
// xdr:from
165+
$objWriter->startElement('xdr:from');
166+
$objWriter->writeElement('xdr:col', $aCoordinates[0] - 1);
167+
$objWriter->writeElement('xdr:colOff', \PhpOffice\PhpSpreadsheet\Shared\Drawing::pixelsToEMU($drawing->getOffsetX()));
168+
$objWriter->writeElement('xdr:row', $aCoordinates[1] - 1);
169+
$objWriter->writeElement('xdr:rowOff', \PhpOffice\PhpSpreadsheet\Shared\Drawing::pixelsToEMU($drawing->getOffsetY()));
170+
$objWriter->endElement();
168171

169-
// xdr:ext
170-
$objWriter->startElement('xdr:ext');
171-
$objWriter->writeAttribute('cx', \PhpOffice\PhpSpreadsheet\Shared\Drawing::pixelsToEMU($drawing->getWidth()));
172-
$objWriter->writeAttribute('cy', \PhpOffice\PhpSpreadsheet\Shared\Drawing::pixelsToEMU($drawing->getHeight()));
173-
$objWriter->endElement();
172+
// xdr:to
173+
$objWriter->startElement('xdr:to');
174+
$objWriter->writeElement('xdr:col', $aCoordinates2[0] - 1);
175+
$objWriter->writeElement('xdr:colOff', \PhpOffice\PhpSpreadsheet\Shared\Drawing::pixelsToEMU($drawing->getOffsetX2()));
176+
$objWriter->writeElement('xdr:row', $aCoordinates2[1] - 1);
177+
$objWriter->writeElement('xdr:rowOff', \PhpOffice\PhpSpreadsheet\Shared\Drawing::pixelsToEMU($drawing->getOffsetY2()));
178+
$objWriter->endElement();
179+
} else {
180+
// xdr:oneCellAnchor
181+
$objWriter->startElement('xdr:oneCellAnchor');
182+
// Image location
183+
$aCoordinates = Coordinate::indexesFromString($drawing->getCoordinates());
184+
185+
// xdr:from
186+
$objWriter->startElement('xdr:from');
187+
$objWriter->writeElement('xdr:col', $aCoordinates[0] - 1);
188+
$objWriter->writeElement('xdr:colOff', \PhpOffice\PhpSpreadsheet\Shared\Drawing::pixelsToEMU($drawing->getOffsetX()));
189+
$objWriter->writeElement('xdr:row', $aCoordinates[1] - 1);
190+
$objWriter->writeElement('xdr:rowOff', \PhpOffice\PhpSpreadsheet\Shared\Drawing::pixelsToEMU($drawing->getOffsetY()));
191+
$objWriter->endElement();
192+
193+
// xdr:ext
194+
$objWriter->startElement('xdr:ext');
195+
$objWriter->writeAttribute('cx', \PhpOffice\PhpSpreadsheet\Shared\Drawing::pixelsToEMU($drawing->getWidth()));
196+
$objWriter->writeAttribute('cy', \PhpOffice\PhpSpreadsheet\Shared\Drawing::pixelsToEMU($drawing->getHeight()));
197+
$objWriter->endElement();
198+
}
174199

175200
// xdr:pic
176201
$objWriter->startElement('xdr:pic');
@@ -223,6 +248,12 @@ public function writeDrawing(XMLWriter $objWriter, BaseDrawing $drawing, $relati
223248
// a:xfrm
224249
$objWriter->startElement('a:xfrm');
225250
$objWriter->writeAttribute('rot', \PhpOffice\PhpSpreadsheet\Shared\Drawing::degreesToAngle($drawing->getRotation()));
251+
if ($isTwoCellAnchor) {
252+
$objWriter->startElement('a:ext');
253+
$objWriter->writeAttribute('cx', \PhpOffice\PhpSpreadsheet\Shared\Drawing::pixelsToEMU($drawing->getWidth()));
254+
$objWriter->writeAttribute('cy', \PhpOffice\PhpSpreadsheet\Shared\Drawing::pixelsToEMU($drawing->getHeight()));
255+
$objWriter->endElement();
256+
}
226257
$objWriter->endElement();
227258

228259
// a:prstGeom

tests/PhpSpreadsheetTests/Writer/Xlsx/DrawingsTest.php

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -430,4 +430,56 @@ public function testBuildWithDifferentImageFormats(): void
430430

431431
self::assertNotNull($reloadedSpreadsheet);
432432
}
433+
434+
/**
435+
* Test save and load XLSX file with drawing image that coordinate is two cell anchor.
436+
*/
437+
public function testTwoCellAnchorDrawing(): void
438+
{
439+
$reader = new Xlsx();
440+
$spreadsheet = new Spreadsheet();
441+
$sheet = $spreadsheet->getActiveSheet();
442+
443+
// Add gif image that coordinates is two cell anchor.
444+
$drawing = new Drawing();
445+
$drawing->setName('Green Square');
446+
$drawing->setPath('tests/data/Writer/XLSX/green_square.gif');
447+
self::assertEquals($drawing->getWidth(), 150);
448+
self::assertEquals($drawing->getHeight(), 150);
449+
$drawing->setCoordinates('A1');
450+
$drawing->setOffsetX(30);
451+
$drawing->setOffsetY(10);
452+
$drawing->setCoordinates2('E8');
453+
$drawing->setOffsetX2(-50);
454+
$drawing->setOffsetY2(-20);
455+
$drawing->setWorksheet($sheet);
456+
457+
// Write file
458+
$writer = IOFactory::createWriter($spreadsheet, 'Xlsx');
459+
$tempFileName = File::sysGetTempDir() . '/drawings_image_that_two_cell_anchor.xlsx';
460+
$writer->save($tempFileName);
461+
462+
// Read new file
463+
$reloadedSpreadsheet = $reader->load($tempFileName);
464+
$sheet = $reloadedSpreadsheet->getActiveSheet();
465+
466+
// Check image coordinates.
467+
$drawingCollection = $sheet->getDrawingCollection();
468+
$drawing = $drawingCollection[0];
469+
self::assertNotNull($drawing);
470+
471+
self::assertEquals($drawing->getWidth(), 150);
472+
self::assertEquals($drawing->getHeight(), 150);
473+
self::assertEquals($drawing->getCoordinates(), 'A1');
474+
self::assertEquals($drawing->getOffsetX(), 30);
475+
self::assertEquals($drawing->getOffsetY(), 10);
476+
self::assertEquals($drawing->getCoordinates2(), 'E8');
477+
self::assertEquals($drawing->getOffsetX2(), -50);
478+
self::assertEquals($drawing->getOffsetY2(), -20);
479+
self::assertEquals($drawing->getWorksheet(), $sheet);
480+
481+
unlink($tempFileName);
482+
483+
self::assertNotNull($reloadedSpreadsheet);
484+
}
433485
}

0 commit comments

Comments
 (0)