Html Writer Apply Rotation To Images

Fix #4875. If rotation is applied to a Drawing, Html Writer will now generate the appropriate Css. This will cause the image to be rotated for Html, Dompdf, and Mpdf; Tcpdf does not recognize the rotation Css.
This commit is contained in:
oleibman
2026-05-14 21:54:49 -07:00
parent 7cc6e4e580
commit a2a6ccdeef
3 changed files with 56 additions and 8 deletions
+13 -8
View File
@@ -2,6 +2,7 @@
namespace PhpOffice\PhpSpreadsheet\Reader;
use Composer\Pcre\Preg;
use DOMAttr;
use DOMDocument;
use DOMElement;
@@ -180,7 +181,7 @@ class Html extends BaseReader
return false;
}
$beginning = preg_replace(self::STARTS_WITH_BOM, '', $this->readBeginning()) ?? '';
$beginning = Preg::replace(self::STARTS_WITH_BOM, '', $this->readBeginning());
$startWithTag = self::startsWithTag($beginning);
$containsTags = self::containsTags($beginning);
@@ -457,7 +458,7 @@ class Html extends BaseReader
}
if (isset($attributeArray['style'])) {
$alignStyle = $attributeArray['style'];
if (preg_match('/\btext-align:\s*(left|right|center|justify)\b/', (string) $alignStyle, $matches) === 1) {
if (Preg::isMatch('/\btext-align:\s*(left|right|center|justify)\b/', (string) $alignStyle, $matches)) {
$sheet->getComment($column . $row)->setAlignment($matches[1]);
}
}
@@ -772,7 +773,7 @@ class Html extends BaseReader
{
foreach ($element->childNodes as $child) {
if ($child instanceof DOMText) {
$domText = (string) preg_replace('/\s+/', ' ', trim($child->nodeValue ?? ''));
$domText = Preg::replace('/\s+/', ' ', trim($child->nodeValue ?? ''));
if ($domText === "\u{a0}") {
$domText = '';
}
@@ -883,7 +884,7 @@ class Html extends BaseReader
break;
default:
if (preg_match('/^custom[.](bool|date|float|int|string)[.](.+)$/', $metaName, $matches) === 1) {
if (Preg::isMatch('/^custom[.](bool|date|float|int|string)[.](.+)$/', $metaName, $matches)) {
match ($matches[1]) {
'bool' => $properties->setCustomProperty($matches[2], (bool) $metaContent, Properties::PROPERTY_TYPE_BOOLEAN),
'float' => $properties->setCustomProperty($matches[2], (float) $metaContent, Properties::PROPERTY_TYPE_FLOAT),
@@ -910,13 +911,12 @@ class Html extends BaseReader
/** @internal */
protected static function replaceNonAsciiIfNeeded(string $convert): ?string
{
if (preg_match(self::STARTS_WITH_BOM, $convert) !== 1 && preg_match(self::DECLARES_CHARSET, $convert) !== 1) {
if (!Preg::isMatch(self::STARTS_WITH_BOM, $convert) && !Preg::isMatch(self::DECLARES_CHARSET, $convert)) {
$lowend = "\u{80}";
$highend = "\u{10ffff}";
$regexp = "/[$lowend-$highend]/u";
/** @var callable $callback */
$callback = [self::class, 'replaceNonAscii'];
$convert = preg_replace_callback($regexp, $callback, $convert);
// use native preg because of "u" modifier
$convert = preg_replace_callback($regexp, self::replaceNonAscii(...), $convert);
}
return $convert;
@@ -1274,6 +1274,11 @@ class Html extends BaseReader
$drawing->setOpacity((int) ($opacity * 100000));
}
}
/** @var string */
$transform = $styleArray['transform'] ?? '';
if (Preg::isMatch('/rotate[(](-?\d{1,3})deg[)]$/', $transform, $matches)) {
$drawing->setRotation((int) $matches[1]);
}
}
/**
+5
View File
@@ -758,6 +758,11 @@ class Html extends BaseWriter
$opacity = "opacity:$opacityValue; ";
}
}
$rotationValue = $drawing->getRotation();
if ($rotationValue !== 0) {
$rotation = "transform: rotate({$rotationValue}deg); ";
$opacity .= $rotation;
}
$filedesc = $drawing->getDescription();
$filedesc = $filedesc ? htmlspecialchars($filedesc, ENT_QUOTES) : 'Embedded image';
if ($drawing instanceof Drawing && $drawing->getPath() !== '') {
@@ -0,0 +1,38 @@
<?php
declare(strict_types=1);
namespace PhpOffice\PhpSpreadsheetTests\Writer\Html;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Worksheet\Drawing;
use PhpOffice\PhpSpreadsheetTests\Functional\AbstractFunctional;
class ImageRotateTest extends AbstractFunctional
{
public function testImageCopyXls(): void
{
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
$drawing = new Drawing();
$drawing->setName('Blue Square');
$drawing->setDescription('Blue_Square');
$drawing->setPath('samples/images/blue_square.png');
$drawing->setCoordinates('C5');
$drawing->setRotation(45);
$drawing->setWorksheet($sheet);
$reloadedSpreadsheet = $this->writeAndReload($spreadsheet, 'Html');
$spreadsheet->disconnectWorksheets();
$rsheet = $reloadedSpreadsheet->getActiveSheet();
$drawings = $rsheet->getDrawingCollection();
self::assertCount(1, $drawings);
$drawing = $drawings[0];
self::assertNotNull($drawing);
self::assertSame(45, $drawing->getRotation());
$reloadedSpreadsheet->disconnectWorksheets();
}
}