Skip to content

Commit 8a57259

Browse files
authored
Merge pull request #4142 from oleibman/imgtransparent
Image Transparency/Opacity Including Html Reader Changes
2 parents c6ede15 + cbf7861 commit 8a57259

File tree

13 files changed

+407
-27
lines changed

13 files changed

+407
-27
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ and this project adheres to [Semantic Versioning](https://semver.org).
2929
- Xlsx Writer Duplicate ContentTypes Entry for Background Image. [Issue #4179](https://github.com/PHPOffice/PhpSpreadsheet/issues/4179) [PR #4180](https://github.com/PHPOffice/PhpSpreadsheet/pull/4180)
3030
- Check strictNullComparison outside of loops. [PR #3347](https://github.com/PHPOffice/PhpSpreadsheet/pull/3347)
3131
- SUMIFS Does Not Require xlfn. [Issue #4182](https://github.com/PHPOffice/PhpSpreadsheet/issues/4182) [PR #4186](https://github.com/PHPOffice/PhpSpreadsheet/pull/4186)
32+
- Image Transparency/Opacity with Html Reader Changes. [Discussion #4117](https://github.com/PHPOffice/PhpSpreadsheet/discussions/4117) [PR #4142](https://github.com/PHPOffice/PhpSpreadsheet/pull/4142)
3233

3334
## 2024-09-29 - 3.3.0 (no 3.0.\*, 3.1.\*, 3.2.\*)
3435

samples/Basic4/53_ImageOpacity.php

+69
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
<?php
2+
3+
require __DIR__ . '/../Header.php';
4+
5+
use PhpOffice\PhpSpreadsheet\Spreadsheet;
6+
use PhpOffice\PhpSpreadsheet\Worksheet\Drawing;
7+
8+
//var_dump(realpath(__DIR__ . '/../images/blue_square.png'));
9+
//exit();
10+
11+
$path = __DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'images/blue_square.png';
12+
$spreadsheet = new Spreadsheet();
13+
$spreadsheet->getProperties()->setTitle('53_ImageOpacity');
14+
15+
$helper->log('Add image to spreadsheet 6 times with different opacities');
16+
$sheet = $spreadsheet->getActiveSheet();
17+
$sheet->setTitle('Squares different opacities');
18+
$sheet->setShowGridLines(false);
19+
20+
$drawing = new Drawing();
21+
$drawing->setName('Blue Square opacity not specified');
22+
$drawing->setPath($path);
23+
$drawing->setCoordinates('A1');
24+
$drawing->setCoordinates2('B5');
25+
$drawing->setWorksheet($sheet);
26+
27+
$drawing = new Drawing();
28+
$drawing->setName('Blue Square opacity 80%');
29+
$drawing->setPath($path);
30+
$drawing->setCoordinates('C1');
31+
$drawing->setCoordinates2('D5');
32+
$drawing->setOpacity(80000);
33+
$drawing->setWorksheet($sheet);
34+
35+
$drawing = new Drawing();
36+
$drawing->setName('Blue Square opacity 60%');
37+
$drawing->setPath($path);
38+
$drawing->setCoordinates('E1');
39+
$drawing->setCoordinates2('F5');
40+
$drawing->setOpacity(60000);
41+
$drawing->setWorksheet($sheet);
42+
43+
$drawing = new Drawing();
44+
$drawing->setName('Blue Square opacity 40%');
45+
$drawing->setPath($path);
46+
$drawing->setCoordinates('A8');
47+
$drawing->setCoordinates2('B12');
48+
$drawing->setOpacity(40000);
49+
$drawing->setWorksheet($sheet);
50+
51+
$drawing = new Drawing();
52+
$drawing->setName('Blue Square opacity 20%');
53+
$drawing->setPath($path);
54+
$drawing->setCoordinates('C8');
55+
$drawing->setCoordinates2('D12');
56+
$drawing->setOpacity(20000);
57+
$drawing->setWorksheet($sheet);
58+
59+
$drawing = new Drawing();
60+
$drawing->setName('Blue Square opacity 0%');
61+
$drawing->setPath($path);
62+
$drawing->setCoordinates('E8');
63+
$drawing->setCoordinates2('F12');
64+
$drawing->setOpacity(0);
65+
$drawing->setWorksheet($sheet);
66+
67+
// Save
68+
$helper->write($spreadsheet, __FILE__, ['Xlsx', 'Html', 'Dompdf', 'Mpdf']);
69+
$spreadsheet->disconnectWorksheets();

samples/images/blue_square.png

2.3 KB
Loading

samples/templates/27template.xlsx

582 Bytes
Binary file not shown.

src/PhpSpreadsheet/Helper/Sample.php

+3
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,9 @@ public function write(Spreadsheet $spreadsheet, string $filename, array $writers
120120
// Write documents
121121
foreach ($writers as $writerType) {
122122
$path = $this->getFilename($filename, mb_strtolower($writerType));
123+
if (preg_match('/(mpdf|tcpdf)$/', $path)) {
124+
$path .= '.pdf';
125+
}
123126
$writer = IOFactory::createWriter($spreadsheet, $writerType);
124127
$writer->setIncludeCharts($withCharts);
125128
if ($writerCallback !== null) {

src/PhpSpreadsheet/Reader/Html.php

+61-8
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,7 @@ private function processDomElementTitle(Worksheet $sheet, int &$row, string &$co
315315

316316
try {
317317
$sheet->setTitle($cellContent, true, true);
318+
$sheet->getParent()?->getProperties()?->setTitle($cellContent);
318319
} catch (SpreadsheetException) {
319320
// leave default title if too long or illegal chars
320321
}
@@ -476,6 +477,11 @@ private function processDomElementImg(Worksheet $sheet, int &$row, string &$colu
476477
private function processDomElementTable(Worksheet $sheet, int &$row, string &$column, string &$cellContent, DOMElement $child, array &$attributeArray): void
477478
{
478479
if ($child->nodeName === 'table') {
480+
if (isset($attributeArray['class'])) {
481+
$classes = explode(' ', $attributeArray['class']);
482+
$sheet->setShowGridlines(in_array('gridlines', $classes, true));
483+
$sheet->setPrintGridlines(in_array('gridlinesp', $classes, true));
484+
}
479485
$this->currentColumn = 'A';
480486
$this->flushCell($sheet, $column, $row, $cellContent, $attributeArray);
481487
$column = $this->setTableStartColumn($column);
@@ -1038,14 +1044,21 @@ private function insertImage(Worksheet $sheet, string $column, int $row, array $
10381044
if (!isset($attributes['src'])) {
10391045
return;
10401046
}
1047+
$styleArray = self::getStyleArray($attributes);
10411048

1042-
$src = urldecode($attributes['src']);
1043-
$width = isset($attributes['width']) ? (float) $attributes['width'] : null;
1044-
$height = isset($attributes['height']) ? (float) $attributes['height'] : null;
1049+
$src = $attributes['src'];
1050+
if (substr($src, 0, 5) !== 'data:') {
1051+
$src = urldecode($src);
1052+
}
1053+
$width = isset($attributes['width']) ? (float) $attributes['width'] : ($styleArray['width'] ?? null);
1054+
$height = isset($attributes['height']) ? (float) $attributes['height'] : ($styleArray['height'] ?? null);
10451055
$name = $attributes['alt'] ?? null;
10461056

10471057
$drawing = new Drawing();
1048-
$drawing->setPath($src);
1058+
$drawing->setPath($src, false);
1059+
if ($drawing->getPath() === '') {
1060+
return;
1061+
}
10491062
$drawing->setWorksheet($sheet);
10501063
$drawing->setCoordinates($column . $row);
10511064
$drawing->setOffsetX(0);
@@ -1057,10 +1070,12 @@ private function insertImage(Worksheet $sheet, string $column, int $row, array $
10571070
}
10581071

10591072
if ($width) {
1060-
$drawing->setWidth((int) $width);
1061-
}
1062-
1063-
if ($height) {
1073+
if ($height) {
1074+
$drawing->setWidthAndHeight((int) $width, (int) $height);
1075+
} else {
1076+
$drawing->setWidth((int) $width);
1077+
}
1078+
} elseif ($height) {
10641079
$drawing->setHeight((int) $height);
10651080
}
10661081

@@ -1071,6 +1086,44 @@ private function insertImage(Worksheet $sheet, string $column, int $row, array $
10711086
$sheet->getRowDimension($row)->setRowHeight(
10721087
$drawing->getHeight() * 0.9
10731088
);
1089+
1090+
if (isset($styleArray['opacity'])) {
1091+
$opacity = $styleArray['opacity'];
1092+
if (is_numeric($opacity)) {
1093+
$drawing->setOpacity((int) ($opacity * 100000));
1094+
}
1095+
}
1096+
}
1097+
1098+
private static function getStyleArray(array $attributes): array
1099+
{
1100+
$styleArray = [];
1101+
if (isset($attributes['style'])) {
1102+
$styles = explode(';', $attributes['style']);
1103+
foreach ($styles as $style) {
1104+
$value = explode(':', $style);
1105+
if (count($value) === 2) {
1106+
$arrayKey = trim($value[0]);
1107+
$arrayValue = trim($value[1]);
1108+
if ($arrayKey === 'width') {
1109+
if (substr($arrayValue, -2) === 'px') {
1110+
$arrayValue = (string) (((float) substr($arrayValue, 0, -2)));
1111+
} else {
1112+
$arrayValue = (new CssDimension($arrayValue))->width();
1113+
}
1114+
} elseif ($arrayKey === 'height') {
1115+
if (substr($arrayValue, -2) === 'px') {
1116+
$arrayValue = substr($arrayValue, 0, -2);
1117+
} else {
1118+
$arrayValue = (new CssDimension($arrayValue))->height();
1119+
}
1120+
}
1121+
$styleArray[$arrayKey] = $arrayValue;
1122+
}
1123+
}
1124+
}
1125+
1126+
return $styleArray;
10741127
}
10751128

10761129
private const BORDER_MAPPINGS = [

src/PhpSpreadsheet/Reader/Xlsx.php

+13-10
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,6 @@ public function listWorksheetInfo(string $filename): array
231231
if ($xmlWorkbook->sheets) {
232232
$dir = dirname($relTarget);
233233

234-
/** @var SimpleXMLElement $eleSheet */
235234
foreach ($xmlWorkbook->sheets->sheet as $eleSheet) {
236235
$tmpInfo = [
237236
'worksheetName' => (string) self::getAttributes($eleSheet)['name'],
@@ -743,7 +742,6 @@ protected function loadSpreadsheetFromFile(string $filename): Spreadsheet
743742
$charts = $chartDetails = [];
744743

745744
if ($xmlWorkbookNS->sheets) {
746-
/** @var SimpleXMLElement $eleSheet */
747745
foreach ($xmlWorkbookNS->sheets->sheet as $eleSheet) {
748746
$eleSheetAttr = self::getAttributes($eleSheet);
749747
++$oldSheetId;
@@ -1428,14 +1426,17 @@ protected function loadSpreadsheetFromFile(string $filename): Spreadsheet
14281426
foreach ($xmlDrawingChildren->oneCellAnchor as $oneCellAnchor) {
14291427
$oneCellAnchor = self::testSimpleXml($oneCellAnchor);
14301428
if ($oneCellAnchor->pic->blipFill) {
1431-
/** @var SimpleXMLElement $blip */
1429+
$objDrawing = new \PhpOffice\PhpSpreadsheet\Worksheet\Drawing();
14321430
$blip = $oneCellAnchor->pic->blipFill->children(Namespaces::DRAWINGML)->blip;
1433-
/** @var SimpleXMLElement $xfrm */
1431+
if (isset($blip, $blip->alphaModFix)) {
1432+
$temp = (string) $blip->alphaModFix->attributes()->amt;
1433+
if (is_numeric($temp)) {
1434+
$objDrawing->setOpacity((int) $temp);
1435+
}
1436+
}
14341437
$xfrm = $oneCellAnchor->pic->spPr->children(Namespaces::DRAWINGML)->xfrm;
1435-
/** @var SimpleXMLElement $outerShdw */
14361438
$outerShdw = $oneCellAnchor->pic->spPr->children(Namespaces::DRAWINGML)->effectLst->outerShdw;
14371439

1438-
$objDrawing = new \PhpOffice\PhpSpreadsheet\Worksheet\Drawing();
14391440
$objDrawing->setName(self::getArrayItemString(self::getAttributes($oneCellAnchor->pic->nvPicPr->cNvPr), 'name'));
14401441
$objDrawing->setDescription(self::getArrayItemString(self::getAttributes($oneCellAnchor->pic->nvPicPr->cNvPr), 'descr'));
14411442
$embedImageKey = self::getArrayItemString(
@@ -1498,7 +1499,6 @@ protected function loadSpreadsheetFromFile(string $filename): Spreadsheet
14981499
$height = Drawing::EMUToPixels(self::getArrayItemIntOrSxml(self::getAttributes($oneCellAnchor->ext), 'cy'));
14991500

15001501
$graphic = $oneCellAnchor->graphicFrame->children(Namespaces::DRAWINGML)->graphic;
1501-
/** @var SimpleXMLElement $chartRef */
15021502
$chartRef = $graphic->graphicData->children(Namespaces::CHART)->chart;
15031503
$thisChart = (string) self::getAttributes($chartRef, $xmlNamespaceBase);
15041504

@@ -1520,6 +1520,12 @@ protected function loadSpreadsheetFromFile(string $filename): Spreadsheet
15201520
if ($twoCellAnchor->pic->blipFill) {
15211521
$objDrawing = new \PhpOffice\PhpSpreadsheet\Worksheet\Drawing();
15221522
$blip = $twoCellAnchor->pic->blipFill->children(Namespaces::DRAWINGML)->blip;
1523+
if (isset($blip, $blip->alphaModFix)) {
1524+
$temp = (string) $blip->alphaModFix->attributes()->amt;
1525+
if (is_numeric($temp)) {
1526+
$objDrawing->setOpacity((int) $temp);
1527+
}
1528+
}
15231529
if (isset($twoCellAnchor->pic->blipFill->children(Namespaces::DRAWINGML)->srcRect)) {
15241530
$objDrawing->setSrcRect($twoCellAnchor->pic->blipFill->children(Namespaces::DRAWINGML)->srcRect->attributes());
15251531
}
@@ -1597,7 +1603,6 @@ protected function loadSpreadsheetFromFile(string $filename): Spreadsheet
15971603
$toOffsetX = Drawing::EMUToPixels($twoCellAnchor->to->colOff);
15981604
$toOffsetY = Drawing::EMUToPixels($twoCellAnchor->to->rowOff);
15991605
$graphic = $twoCellAnchor->graphicFrame->children(Namespaces::DRAWINGML)->graphic;
1600-
/** @var SimpleXMLElement $chartRef */
16011606
$chartRef = $graphic->graphicData->children(Namespaces::CHART)->chart;
16021607
$thisChart = (string) self::getAttributes($chartRef, $xmlNamespaceBase);
16031608

@@ -1617,7 +1622,6 @@ protected function loadSpreadsheetFromFile(string $filename): Spreadsheet
16171622
foreach ($xmlDrawingChildren->absoluteAnchor as $absoluteAnchor) {
16181623
if (($this->includeCharts) && ($absoluteAnchor->graphicFrame)) {
16191624
$graphic = $absoluteAnchor->graphicFrame->children(Namespaces::DRAWINGML)->graphic;
1620-
/** @var SimpleXMLElement $chartRef */
16211625
$chartRef = $graphic->graphicData->children(Namespaces::CHART)->chart;
16221626
$thisChart = (string) self::getAttributes($chartRef, $xmlNamespaceBase);
16231627
$width = Drawing::EMUToPixels((int) self::getArrayItemString(self::getAttributes($absoluteAnchor->ext), 'cx')[0]);
@@ -1900,7 +1904,6 @@ private function parseRichText(?SimpleXMLElement $is): RichText
19001904
$value->createText(StringHelper::controlCharacterOOXML2PHP((string) $is->t));
19011905
} elseif ($is !== null) {
19021906
if (is_object($is->r)) {
1903-
/** @var SimpleXMLElement $run */
19041907
foreach ($is->r as $run) {
19051908
if (!isset($run->rPr)) {
19061909
$value->createText(StringHelper::controlCharacterOOXML2PHP((string) $run->t));

src/PhpSpreadsheet/Worksheet/BaseDrawing.php

+24-6
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,12 @@ class BaseDrawing implements IComparable
131131
/** @var null|SimpleXMLElement|string[] */
132132
protected $srcRect = [];
133133

134+
/**
135+
* Percentage multiplied by 100,000, e.g. 40% = 40,000.
136+
* Opacity=x is the same as transparency=100000-x.
137+
*/
138+
protected ?int $opacity = null;
139+
134140
/**
135141
* Create a new BaseDrawing.
136142
*/
@@ -344,19 +350,19 @@ public function setHeight(int $height): self
344350
*/
345351
public function setWidthAndHeight(int $width, int $height): self
346352
{
347-
$xratio = $width / ($this->width != 0 ? $this->width : 1);
348-
$yratio = $height / ($this->height != 0 ? $this->height : 1);
349-
if ($this->resizeProportional && !($width == 0 || $height == 0)) {
353+
if ($this->width === 0 || $this->height === 0 || $width === 0 || $height === 0 || !$this->resizeProportional) {
354+
$this->width = $width;
355+
$this->height = $height;
356+
} else {
357+
$xratio = $width / $this->width;
358+
$yratio = $height / $this->height;
350359
if (($xratio * $this->height) < $height) {
351360
$this->height = (int) ceil($xratio * $this->height);
352361
$this->width = $width;
353362
} else {
354363
$this->width = (int) ceil($yratio * $this->width);
355364
$this->height = $height;
356365
}
357-
} else {
358-
$this->width = $width;
359-
$this->height = $height;
360366
}
361367

362368
return $this;
@@ -546,4 +552,16 @@ public function getFlipVertical(): bool
546552
{
547553
return $this->flipVertical;
548554
}
555+
556+
public function setOpacity(?int $opacity): self
557+
{
558+
$this->opacity = $opacity;
559+
560+
return $this;
561+
}
562+
563+
public function getOpacity(): ?int
564+
{
565+
return $this->opacity;
566+
}
549567
}

src/PhpSpreadsheet/Writer/Html.php

+10-2
Original file line numberDiff line numberDiff line change
@@ -622,6 +622,14 @@ private function writeImageInCell(string $coordinates): string
622622
// Write images
623623
$drawing = $this->sheetDrawings[$coordinates] ?? null;
624624
if ($drawing !== null) {
625+
$opacity = '';
626+
$opacityValue = $drawing->getOpacity();
627+
if ($opacityValue !== null) {
628+
$opacityValue = $opacityValue / 100000;
629+
if ($opacityValue >= 0.0 && $opacityValue <= 1.0) {
630+
$opacity = "opacity:$opacityValue; ";
631+
}
632+
}
625633
$filedesc = $drawing->getDescription();
626634
$filedesc = $filedesc ? htmlspecialchars($filedesc, ENT_QUOTES) : 'Embedded image';
627635
if ($drawing instanceof Drawing && $drawing->getPath() !== '') {
@@ -655,7 +663,7 @@ private function writeImageInCell(string $coordinates): string
655663
}
656664
}
657665

658-
$html .= '<img style="position: absolute; z-index: 1; left: '
666+
$html .= '<img style="' . $opacity . 'position: absolute; z-index: 1; left: '
659667
. $drawing->getOffsetX() . 'px; top: ' . $drawing->getOffsetY() . 'px; width: '
660668
. $drawing->getWidth() . 'px; height: ' . $drawing->getHeight() . 'px;" src="'
661669
. $imageData . '" alt="' . $filedesc . '" />';
@@ -678,7 +686,7 @@ private function writeImageInCell(string $coordinates): string
678686
// Its use here is suspect and is being eliminated.
679687
// width: X sets width of supplied image.
680688
// As a result, images bigger than cell will be contained and images smaller will not get stretched
681-
$html .= '<img alt="' . $filedesc . '" src="' . $dataUri . '" style="width:' . $drawing->getWidth() . 'px;left: '
689+
$html .= '<img alt="' . $filedesc . '" src="' . $dataUri . '" style="' . $opacity . 'width:' . $drawing->getWidth() . 'px;left: '
682690
. $drawing->getOffsetX() . 'px; top: ' . $drawing->getOffsetY() . 'px;position: absolute; z-index: 1;" />';
683691
}
684692
}

src/PhpSpreadsheet/Writer/Xlsx/Drawing.php

+7-1
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,13 @@ public function writeDrawing(XMLWriter $objWriter, BaseDrawing $drawing, int $re
263263
$objWriter->startElement('a:blip');
264264
$objWriter->writeAttribute('xmlns:r', Namespaces::SCHEMA_OFFICE_DOCUMENT);
265265
$objWriter->writeAttribute('r:embed', 'rId' . $relationId);
266-
$objWriter->endElement();
266+
$temp = $drawing->getOpacity();
267+
if (is_int($temp) && $temp >= 0 && $temp <= 100000) {
268+
$objWriter->startElement('a:alphaModFix');
269+
$objWriter->writeAttribute('amt', "$temp");
270+
$objWriter->endElement(); // a:alphaModFix
271+
}
272+
$objWriter->endElement(); // a:blip
267273

268274
$srcRect = $drawing->getSrcRect();
269275
if (!empty($srcRect)) {

0 commit comments

Comments
 (0)