Skip to content

Commit

Permalink
Read and write hyperlink for drawing image
Browse files Browse the repository at this point in the history
Fixes #490
  • Loading branch information
tezrik authored and PowerKiKi committed Jul 15, 2018
1 parent b05d07a commit 17d4a54
Show file tree
Hide file tree
Showing 9 changed files with 253 additions and 5 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/).

### Added

- Add excel function EXACT(value1, value2) support
- Add excel function EXACT(value1, value2) support - [595](https://github.com/PHPOffice/PhpSpreadsheet/pull/595)
- Support workbook view attributes for Xlsx format - [#523](https://github.com/PHPOffice/PhpSpreadsheet/issues/523)
- Read and write hyperlink for drawing image - [#490](https://github.com/PHPOffice/PhpSpreadsheet/pull/490)

### Fixed

Expand Down
18 changes: 18 additions & 0 deletions docs/references/features-cross-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -750,6 +750,24 @@
<td></td>
<td></td>
</tr>
<tr>
<td style="padding-left: 1em;">Drawing hyperlink</td>
<td></td>
<td style="text-align: center; color: green;">✔</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td style="text-align: center; color: green;">✔</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td>$drawing->getHyperlink()->getUrl()</td>
<td>$drawing->setHyperlink()->setUrl($url)</td>
</tr>
<tr>
<td><strong>Cell Formatting</strong></td>
<td></td>
Expand Down
54 changes: 54 additions & 0 deletions samples/Reader/20_Reader_worksheet_hyperlink_image.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<?php

use PhpOffice\PhpSpreadsheet\Spreadsheet;

require __DIR__ . '/../Header.php';
$inputFileType = 'Xlsx';

$helper->log('Start');

$spreadsheet = new Spreadsheet();

$aSheet = $spreadsheet->getActiveSheet();

$gdImage = @imagecreatetruecolor(120, 20);
$textColor = imagecolorallocate($gdImage, 255, 255, 255);
imagestring($gdImage, 1, 5, 5, 'Created with PhpSpreadsheet', $textColor);

$baseUrl = 'https://phpspreadsheet.readthedocs.io/';

$drawing = new \PhpOffice\PhpSpreadsheet\Worksheet\MemoryDrawing();
$drawing->setName('In-Memory image 1');
$drawing->setDescription('In-Memory image 1');
$drawing->setCoordinates('A1');
$drawing->setImageResource($gdImage);
$drawing->setRenderingFunction(
\PhpOffice\PhpSpreadsheet\Worksheet\MemoryDrawing::RENDERING_JPEG
);
$drawing->setMimeType(\PhpOffice\PhpSpreadsheet\Worksheet\MemoryDrawing::MIMETYPE_DEFAULT);
$drawing->setHeight(36);
$helper->log('Write image');

$hyperLink = new \PhpOffice\PhpSpreadsheet\Cell\Hyperlink($baseUrl, 'test image');
$drawing->setHyperlink($hyperLink);
$helper->log('Write link: ' . $baseUrl);

$drawing->setWorksheet($aSheet);

$filename = tempnam(\PhpOffice\PhpSpreadsheet\Shared\File::sysGetTempDir(), 'phpspreadsheet-test');

$writer = \PhpOffice\PhpSpreadsheet\IOFactory::createWriter($spreadsheet, $inputFileType);
$writer->save($filename);

$reader = \PhpOffice\PhpSpreadsheet\IOFactory::createReader($inputFileType);

$reloadedSpreadsheet = $reader->load($filename);
unlink($filename);

$helper->log('reloaded Spreadsheet');

foreach ($reloadedSpreadsheet->getActiveSheet()->getDrawingCollection() as $pDrawing) {
$helper->log('Read link: ' . $pDrawing->getHyperlink()->getUrl());
}

$helper->log('end');
8 changes: 8 additions & 0 deletions src/PhpSpreadsheet/Cell/Hyperlink.php
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,14 @@ public function isInternal()
return strpos($this->url, 'sheet://') !== false;
}

/**
* @return string
*/
public function getTypeHyperlink()
{
return $this->isInternal() ? '' : 'External';
}

/**
* Get hash code.
*
Expand Down
37 changes: 36 additions & 1 deletion src/PhpSpreadsheet/Reader/Xlsx.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace PhpOffice\PhpSpreadsheet\Reader;

use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
use PhpOffice\PhpSpreadsheet\Cell\Hyperlink;
use PhpOffice\PhpSpreadsheet\Document\Properties;
use PhpOffice\PhpSpreadsheet\NamedRange;
use PhpOffice\PhpSpreadsheet\Reader\Xlsx\Chart;
Expand Down Expand Up @@ -1669,9 +1670,12 @@ public function load($pFilename)
Settings::getLibXmlLoaderOptions()
);
$images = [];

$hyperlinks = [];
if ($relsDrawing && $relsDrawing->Relationship) {
foreach ($relsDrawing->Relationship as $ele) {
if ($ele['Type'] == 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink') {
$hyperlinks[(string) $ele['Id']] = (string) $ele['Target'];
}
if ($ele['Type'] == 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/image') {
$images[(string) $ele['Id']] = self::dirAdd($fileDrawing, $ele['Target']);
} elseif ($ele['Type'] == 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/chart') {
Expand Down Expand Up @@ -1699,6 +1703,9 @@ public function load($pFilename)
$xfrm = $oneCellAnchor->pic->spPr->children('http://schemas.openxmlformats.org/drawingml/2006/main')->xfrm;
/** @var SimpleXMLElement $outerShdw */
$outerShdw = $oneCellAnchor->pic->spPr->children('http://schemas.openxmlformats.org/drawingml/2006/main')->effectLst->outerShdw;
/** @var \SimpleXMLElement $hlinkClick */
$hlinkClick = $oneCellAnchor->pic->nvPicPr->cNvPr->children('http://schemas.openxmlformats.org/drawingml/2006/main')->hlinkClick;

$objDrawing = new \PhpOffice\PhpSpreadsheet\Worksheet\Drawing();
$objDrawing->setName((string) self::getArrayItem($oneCellAnchor->pic->nvPicPr->cNvPr->attributes(), 'name'));
$objDrawing->setDescription((string) self::getArrayItem($oneCellAnchor->pic->nvPicPr->cNvPr->attributes(), 'descr'));
Expand Down Expand Up @@ -1729,6 +1736,9 @@ public function load($pFilename)
$shadow->getColor()->setRGB(self::getArrayItem($outerShdw->srgbClr->attributes(), 'val'));
$shadow->setAlpha(self::getArrayItem($outerShdw->srgbClr->alpha->attributes(), 'val') / 1000);
}

$this->readHyperLinkDrawing($objDrawing, $oneCellAnchor, $hyperlinks);

$objDrawing->setWorksheet($docSheet);
} else {
// ? Can charts be positioned with a oneCellAnchor ?
Expand All @@ -1746,6 +1756,7 @@ public function load($pFilename)
$blip = $twoCellAnchor->pic->blipFill->children('http://schemas.openxmlformats.org/drawingml/2006/main')->blip;
$xfrm = $twoCellAnchor->pic->spPr->children('http://schemas.openxmlformats.org/drawingml/2006/main')->xfrm;
$outerShdw = $twoCellAnchor->pic->spPr->children('http://schemas.openxmlformats.org/drawingml/2006/main')->effectLst->outerShdw;
$hlinkClick = $twoCellAnchor->pic->nvPicPr->cNvPr->children('http://schemas.openxmlformats.org/drawingml/2006/main')->hlinkClick;
$objDrawing = new \PhpOffice\PhpSpreadsheet\Worksheet\Drawing();
$objDrawing->setName((string) self::getArrayItem($twoCellAnchor->pic->nvPicPr->cNvPr->attributes(), 'name'));
$objDrawing->setDescription((string) self::getArrayItem($twoCellAnchor->pic->nvPicPr->cNvPr->attributes(), 'descr'));
Expand Down Expand Up @@ -1777,6 +1788,9 @@ public function load($pFilename)
$shadow->getColor()->setRGB(self::getArrayItem($outerShdw->srgbClr->attributes(), 'val'));
$shadow->setAlpha(self::getArrayItem($outerShdw->srgbClr->alpha->attributes(), 'val') / 1000);
}

$this->readHyperLinkDrawing($objDrawing, $twoCellAnchor, $hyperlinks);

$objDrawing->setWorksheet($docSheet);
} elseif (($this->includeCharts) && ($twoCellAnchor->graphicFrame)) {
$fromCoordinate = Coordinate::stringFromColumnIndex(((string) $twoCellAnchor->from->col) + 1) . ($twoCellAnchor->from->row + 1);
Expand Down Expand Up @@ -2427,6 +2441,27 @@ private static function boolean($value)
return $value === 'true' || $value === 'TRUE';
}

/**
* @param \PhpOffice\PhpSpreadsheet\Worksheet\Drawing $objDrawing
* @param \SimpleXMLElement $cellAnchor
* @param array $hyperlinks
*/
private function readHyperLinkDrawing($objDrawing, $cellAnchor, $hyperlinks)
{
$hlinkClick = $cellAnchor->pic->nvPicPr->cNvPr->children('http://schemas.openxmlformats.org/drawingml/2006/main')->hlinkClick;

if ($hlinkClick->count() === 0) {
return;
}

$hlinkId = (string) $hlinkClick->attributes('http://schemas.openxmlformats.org/officeDocument/2006/relationships')['id'];
$hyperlink = new Hyperlink(
$hyperlinks[$hlinkId],
(string) self::getArrayItem($cellAnchor->pic->nvPicPr->cNvPr->attributes(), 'name')
);
$objDrawing->setHyperlink($hyperlink);
}

private function readProtection(Spreadsheet $excel, SimpleXMLElement $xmlWorkbook)
{
if (!$xmlWorkbook->workbookProtection) {
Expand Down
24 changes: 24 additions & 0 deletions src/PhpSpreadsheet/Worksheet/BaseDrawing.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace PhpOffice\PhpSpreadsheet\Worksheet;

use PhpOffice\PhpSpreadsheet\Cell\Hyperlink;
use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException;
use PhpOffice\PhpSpreadsheet\IComparable;

Expand Down Expand Up @@ -98,6 +99,13 @@ class BaseDrawing implements IComparable
*/
protected $shadow;

/**
* Image hyperlink.
*
* @var null|Hyperlink
*/
private $hyperlink;

/**
* Create a new BaseDrawing.
*/
Expand Down Expand Up @@ -508,4 +516,20 @@ public function __clone()
}
}
}

/**
* @param null|Hyperlink $pHyperlink
*/
public function setHyperlink(Hyperlink $pHyperlink = null)
{
$this->hyperlink = $pHyperlink;
}

/**
* @return null|Hyperlink
*/
public function getHyperlink()
{
return $this->hyperlink;
}
}
30 changes: 28 additions & 2 deletions src/PhpSpreadsheet/Writer/Xlsx/Drawing.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,12 @@ public function writeDrawings(\PhpOffice\PhpSpreadsheet\Worksheet\Worksheet $pWo
$i = 1;
$iterator = $pWorksheet->getDrawingCollection()->getIterator();
while ($iterator->valid()) {
$this->writeDrawing($objWriter, $iterator->current(), $i);
/** @var BaseDrawing $pDrawing */
$pDrawing = $iterator->current();
$pRelationId = $i;
$hlinkClickId = $pDrawing->getHyperlink() === null ? null : ++$i;

$this->writeDrawing($objWriter, $pDrawing, $pRelationId, $hlinkClickId);

$iterator->next();
++$i;
Expand Down Expand Up @@ -150,10 +155,11 @@ public function writeChart(XMLWriter $objWriter, \PhpOffice\PhpSpreadsheet\Chart
* @param XMLWriter $objWriter XML Writer
* @param BaseDrawing $pDrawing
* @param int $pRelationId
* @param null|int $hlinkClickId
*
* @throws WriterException
*/
public function writeDrawing(XMLWriter $objWriter, BaseDrawing $pDrawing, $pRelationId = -1)
public function writeDrawing(XMLWriter $objWriter, BaseDrawing $pDrawing, $pRelationId = -1, $hlinkClickId = null)
{
if ($pRelationId >= 0) {
// xdr:oneCellAnchor
Expand Down Expand Up @@ -187,6 +193,10 @@ public function writeDrawing(XMLWriter $objWriter, BaseDrawing $pDrawing, $pRela
$objWriter->writeAttribute('id', $pRelationId);
$objWriter->writeAttribute('name', $pDrawing->getName());
$objWriter->writeAttribute('descr', $pDrawing->getDescription());

//a:hlinkClick
$this->writeHyperLinkDrawing($objWriter, $hlinkClickId);

$objWriter->endElement();

// xdr:cNvPicPr
Expand Down Expand Up @@ -490,4 +500,20 @@ public function allDrawings(Spreadsheet $spreadsheet)

return $aDrawings;
}

/**
* @param XMLWriter $objWriter
* @param null|int $hlinkClickId
*/
private function writeHyperLinkDrawing(XMLWriter $objWriter, $hlinkClickId)
{
if ($hlinkClickId === null) {
return;
}

$objWriter->startElement('a:hlinkClick');
$objWriter->writeAttribute('xmlns:r', 'http://schemas.openxmlformats.org/officeDocument/2006/relationships');
$objWriter->writeAttribute('r:id', 'rId' . $hlinkClickId);
$objWriter->endElement();
}
}
33 changes: 32 additions & 1 deletion src/PhpSpreadsheet/Writer/Xlsx/Rels.php
Original file line number Diff line number Diff line change
Expand Up @@ -329,12 +329,16 @@ public function writeDrawingRelationships(\PhpOffice\PhpSpreadsheet\Worksheet\Wo
if ($iterator->current() instanceof \PhpOffice\PhpSpreadsheet\Worksheet\Drawing
|| $iterator->current() instanceof MemoryDrawing) {
// Write relationship for image drawing
/** @var \PhpOffice\PhpSpreadsheet\Worksheet\Drawing $drawing */
$drawing = $iterator->current();
$this->writeRelationship(
$objWriter,
$i,
'http://schemas.openxmlformats.org/officeDocument/2006/relationships/image',
'../media/' . str_replace(' ', '', $iterator->current()->getIndexedFilename())
'../media/' . str_replace(' ', '', $drawing->getIndexedFilename())
);

$i = $this->writeDrawingHyperLink($objWriter, $drawing, $i);
}

$iterator->next();
Expand Down Expand Up @@ -432,4 +436,31 @@ private function writeRelationship(XMLWriter $objWriter, $pId, $pType, $pTarget,
throw new WriterException('Invalid parameters passed.');
}
}

/**
* @param $objWriter
* @param \PhpOffice\PhpSpreadsheet\Worksheet\Drawing $drawing
* @param $i
*
* @throws WriterException
*
* @return int
*/
private function writeDrawingHyperLink($objWriter, $drawing, $i)
{
if ($drawing->getHyperlink() === null) {
return $i;
}

++$i;
$this->writeRelationship(
$objWriter,
$i,
'http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink',
$drawing->getHyperlink()->getUrl(),
$drawing->getHyperlink()->getTypeHyperlink()
);

return $i;
}
}
51 changes: 51 additions & 0 deletions tests/PhpSpreadsheetTests/Functional/DrawingImageHyperlinkTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<?php
/**
* Created by PhpStorm.
* User: yuzhakov
* Date: 08.05.18
* Time: 12:00.
*/

namespace PhpOffice\PhpSpreadsheetTests\Functional;

use PhpOffice\PhpSpreadsheet\Cell\Hyperlink;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Worksheet\MemoryDrawing;

class DrawingImageHyperlinkTest extends AbstractFunctional
{
/**
* @throws \PhpOffice\PhpSpreadsheet\Exception
*/
public function testDrawingImageHyperlinkTest()
{
$baseUrl = 'https://github.com/PHPOffice/PhpSpreadsheet';
$spreadsheet = new Spreadsheet();

$aSheet = $spreadsheet->getActiveSheet();

$gdImage = @imagecreatetruecolor(120, 20);
$textColor = imagecolorallocate($gdImage, 255, 255, 255);
imagestring($gdImage, 1, 5, 5, 'Created with PhpSpreadsheet', $textColor);

$drawing = new MemoryDrawing();
$drawing->setName('In-Memory image 1');
$drawing->setDescription('In-Memory image 1');
$drawing->setCoordinates('A1');
$drawing->setImageResource($gdImage);
$drawing->setRenderingFunction(
MemoryDrawing::RENDERING_JPEG
);
$drawing->setMimeType(MemoryDrawing::MIMETYPE_DEFAULT);
$drawing->setHeight(36);
$hyperLink = new Hyperlink($baseUrl, 'test image');
$drawing->setHyperlink($hyperLink);
$drawing->setWorksheet($aSheet);

$reloadedSpreadsheet = $this->writeAndReload($spreadsheet, 'Xlsx');

foreach ($reloadedSpreadsheet->getActiveSheet()->getDrawingCollection() as $pDrawing) {
self::assertEquals('https://github.com/PHPOffice/PhpSpreadsheet', $pDrawing->getHyperlink()->getUrl(), 'functional test drawing hyperlink');
}
}
}

0 comments on commit 17d4a54

Please sign in to comment.