From 84949e4c8fffdbd9775a77807374b21457d341c4 Mon Sep 17 00:00:00 2001 From: Kevin Glier Date: Mon, 4 Jul 2022 22:22:23 +0200 Subject: [PATCH 1/5] Add support for CSS property "background-size" with values "contain" and "cover" --- src/CssConverter.php | 21 +++++++++++++++++++++ src/Html2Pdf.php | 38 ++++++++++++++++++++++++++++++++++---- src/Parsing/Css.php | 10 ++++++++-- 3 files changed, 63 insertions(+), 6 deletions(-) diff --git a/src/CssConverter.php b/src/CssConverter.php index ed393998..b1cd0128 100644 --- a/src/CssConverter.php +++ b/src/CssConverter.php @@ -307,6 +307,27 @@ public function convertBackgroundImage($css) return null; } + /** + * Parse a background size + * + * @param string $css + * + * @return string|null $value + */ + public function convertBackgroundSize($css) + { + if ($css === 'auto') { + return null; + } + + $available = ['contain', 'cover']; + if (in_array($css, $available)) { + return $css; + } + + return null; + } + /** * Parse a background position * diff --git a/src/Html2Pdf.php b/src/Html2Pdf.php index 6e05a18c..27c945c7 100755 --- a/src/Html2Pdf.php +++ b/src/Html2Pdf.php @@ -1793,6 +1793,7 @@ protected function _drawRectangle($x, $y, $w, $h, $border, $padding, $margin, $b $iName = $background['image']; $iPosition = $background['position'] !== null ? $background['position'] : array(0, 0); $iRepeat = $background['repeat'] !== null ? $background['repeat'] : array(true, true); + $iSize = $background['size'] !== null ? $background['size'] : 'auto'; // size of the background without the borders $bX = $x; @@ -1828,10 +1829,39 @@ protected function _drawRectangle($x, $y, $w, $h, $border, $padding, $margin, $b } } else { // convert the size of the image from pixel to the unit of the PDF - $imageWidth = 72./96.*$imageInfos[0]/$this->pdf->getK(); - $imageHeight = 72./96.*$imageInfos[1]/$this->pdf->getK(); + $imageWidth = 72./96.*$imageInfos[0]/$this->pdf->getK(); + $imageHeight = 72./96.*$imageInfos[1]/$this->pdf->getK(); - // prepare the position of the backgroung + + // prepare for image size "contain" and "cover" + $fitbox = false; + $resize = false; + if ($iSize && $iSize != 'auto') { + $containerIsLandscape = $bW > $bH; + $imageIsLandscape = $imageWidth > $imageHeight; + + if ($iSize == 'contain') { + $imageWidth = $bW; + $imageHeight = $bH; + } + else if ($iSize == 'cover') { + if ((!$containerIsLandscape && $imageIsLandscape) || ($containerIsLandscape && !$imageIsLandscape)) { + $aspectRatio = $imageWidth / $imageHeight; + $imageWidth = $imageWidth * $aspectRatio; + $imageHeight = $bH; + } + else { + $aspectRatio = $imageHeight / $imageWidth; + $imageWidth = $bW; + $imageHeight = $imageHeight * $aspectRatio; + } + } + + $resize = true; + $fitbox = 'CM'; + } + + // prepare the position of the background if ($iRepeat[0]) { $iPosition[0] = $bX; } elseif (preg_match('/^([-]?[0-9\.]+)%/isU', $iPosition[0], $match)) { @@ -1887,7 +1917,7 @@ protected function _drawRectangle($x, $y, $w, $h, $border, $padding, $margin, $b $cW = $imageXmax-$iX; } - $this->pdf->Image($iName, $iX, $iY, $imageWidth, $imageHeight, '', ''); + $this->pdf->Image($iName, $iX, $iY, $imageWidth, $imageHeight, '', '', '', $resize, 300, '', false, false, 0, $fitbox); } } diff --git a/src/Parsing/Css.php b/src/Parsing/Css.php index 661a72b0..1cb1c2e6 100644 --- a/src/Parsing/Css.php +++ b/src/Parsing/Css.php @@ -172,7 +172,8 @@ public function initStyle() 'color' => null, 'image' => null, 'position' => null, - 'repeat' => null + 'repeat' => null, + 'size' => null ); $this->value['border'] = array(); $this->value['padding'] = array(); @@ -225,7 +226,7 @@ public function resetStyle($tagName = '') $this->value['display'] = null; $this->value['rotate'] = null; $this->value['overflow'] = 'visible'; - $this->value['background'] = array('color' => null, 'image' => null, 'position' => null, 'repeat' => null); + $this->value['background'] = array('color' => null, 'image' => null, 'position' => null, 'repeat' => null, 'size' => null); $this->value['border'] = array( 't' => $this->readBorder('none'), 'r' => $this->readBorder('none'), @@ -1141,6 +1142,11 @@ public function analyse($tagName, &$param, $legacy = null) $this->value['background']['position'] = $this->cssConverter->convertBackgroundPosition($val, $res); break; + case 'background-size': + $res = null; + $this->value['background']['size'] = $this->cssConverter->convertBackgroundSize($val, $res); + break; + case 'background-repeat': $this->value['background']['repeat'] = $this->cssConverter->convertBackgroundRepeat($val); break; From f102b55d1261cb521dd5a1213083c450a4f8fae1 Mon Sep 17 00:00:00 2001 From: Kevin Glier Date: Tue, 9 May 2023 16:04:41 +0200 Subject: [PATCH 2/5] Fix deprecated notice "Creation of dynamic property" --- src/MyPdf.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/MyPdf.php b/src/MyPdf.php index 384842d9..f750e20d 100644 --- a/src/MyPdf.php +++ b/src/MyPdf.php @@ -26,6 +26,10 @@ class MyPdf extends \TCPDF // nb of segment to build a arc with bezier curv const ARC_NB_SEGMENT = 8; + /** + * @var float|mixed + */ + private $ws; /** * class constructor From 83d8a03d6943a0afc52f7af8fe833ecbb8e60033 Mon Sep 17 00:00:00 2001 From: Kevin Glier Date: Tue, 9 May 2023 16:14:02 +0200 Subject: [PATCH 3/5] Fix deprecated notice "Creation of dynamic property" --- src/MyPdf.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/MyPdf.php b/src/MyPdf.php index a2e8ee6b..14d83c7e 100644 --- a/src/MyPdf.php +++ b/src/MyPdf.php @@ -27,10 +27,6 @@ class MyPdf extends TCPDF // nb of segment to build an arc with bezier curv const ARC_NB_SEGMENT = 8; - /** - * @var float|mixed - */ - private $ws; /** * @var float From 9a2417bf6069afdc3bc3d48e19149284b125afb9 Mon Sep 17 00:00:00 2001 From: Kevin Glier Date: Thu, 25 Jul 2024 08:47:14 +0200 Subject: [PATCH 4/5] Fix background-size calculation for "contain" and "cover" --- src/Html2Pdf.php | 92 ++++++++++++++++++++++++------------------------ 1 file changed, 46 insertions(+), 46 deletions(-) diff --git a/src/Html2Pdf.php b/src/Html2Pdf.php index 22171ca7..0c436385 100755 --- a/src/Html2Pdf.php +++ b/src/Html2Pdf.php @@ -1807,36 +1807,35 @@ protected function _drawRectangle($x, $y, $w, $h, $border, $padding, $margin, $b $bH = $h; if ($border['b']['width']) { - $bH-= $border['b']['width']; + $bH -= $border['b']['width']; } if ($border['l']['width']) { - $bW-= $border['l']['width']; - $bX+= $border['l']['width']; + $bW -= $border['l']['width']; + $bX += $border['l']['width']; } if ($border['t']['width']) { - $bH-= $border['t']['width']; - $bY+= $border['t']['width']; + $bH -= $border['t']['width']; + $bY += $border['t']['width']; } if ($border['r']['width']) { - $bW-= $border['r']['width']; + $bW -= $border['r']['width']; } // get the size of the image - // WARNING : if URL, "allow_url_fopen" must turned to "on" in php.ini - $imageInfos=@getimagesize($iName); + // WARNING : if URL, "allow_url_fopen" must be turned to "on" in php.ini + $imageInfos = @getimagesize($iName); - // if the image can not be loaded - if (!is_array($imageInfos) || count($imageInfos)<2) { + // if the image cannot be loaded + if (!is_array($imageInfos) || count($imageInfos) < 2) { if ($this->_testIsImage) { - $e = new ImageException('Unable to get the size of the image ['.$iName.']'); + $e = new ImageException('Unable to get the size of the image [' . $iName . ']'); $e->setImage($iName); throw $e; } } else { - // convert the size of the image from pixel to the unit of the PDF - $imageWidth = 72./96.*$imageInfos[0]/$this->pdf->getK(); - $imageHeight = 72./96.*$imageInfos[1]/$this->pdf->getK(); - + // convert the size of the image from pixels to the unit of the PDF + $imageWidth = 72. / 96. * $imageInfos[0] / $this->pdf->getK(); + $imageHeight = 72. / 96. * $imageInfos[1] / $this->pdf->getK(); // prepare for image size "contain" and "cover" $fitbox = false; @@ -1846,19 +1845,20 @@ protected function _drawRectangle($x, $y, $w, $h, $border, $padding, $margin, $b $imageIsLandscape = $imageWidth > $imageHeight; if ($iSize == 'contain') { - $imageWidth = $bW; - $imageHeight = $bH; - } - else if ($iSize == 'cover') { - if ((!$containerIsLandscape && $imageIsLandscape) || ($containerIsLandscape && !$imageIsLandscape)) { - $aspectRatio = $imageWidth / $imageHeight; - $imageWidth = $imageWidth * $aspectRatio; + if (($containerIsLandscape && !$imageIsLandscape) || (!$containerIsLandscape && $imageIsLandscape)) { $imageHeight = $bH; + $imageWidth = $bH * ($imageInfos[0] / $imageInfos[1]); + } else { + $imageWidth = $bW; + $imageHeight = $bW * ($imageInfos[1] / $imageInfos[0]); } - else { - $aspectRatio = $imageHeight / $imageWidth; + } else if ($iSize == 'cover') { + if (($containerIsLandscape && !$imageIsLandscape) || (!$containerIsLandscape && $imageIsLandscape)) { $imageWidth = $bW; - $imageHeight = $imageHeight * $aspectRatio; + $imageHeight = $bW * ($imageInfos[1] / $imageInfos[0]); + } else { + $imageHeight = $bH; + $imageWidth = $bH * ($imageInfos[0] / $imageInfos[1]); } } @@ -1870,59 +1870,59 @@ protected function _drawRectangle($x, $y, $w, $h, $border, $padding, $margin, $b if ($iRepeat[0]) { $iPosition[0] = $bX; } elseif (preg_match('/^([-]?[0-9\.]+)%/isU', $iPosition[0], $match)) { - $iPosition[0] = $bX + $match[1]*($bW-$imageWidth)/100; + $iPosition[0] = $bX + $match[1] * ($bW - $imageWidth) / 100; } else { - $iPosition[0] = $bX+$iPosition[0]; + $iPosition[0] = $bX + $iPosition[0]; } if ($iRepeat[1]) { $iPosition[1] = $bY; } elseif (preg_match('/^([-]?[0-9\.]+)%/isU', $iPosition[1], $match)) { - $iPosition[1] = $bY + $match[1]*($bH-$imageHeight)/100; + $iPosition[1] = $bY + $match[1] * ($bH - $imageHeight) / 100; } else { - $iPosition[1] = $bY+$iPosition[1]; + $iPosition[1] = $bY + $iPosition[1]; } $imageXmin = $bX; - $imageXmax = $bX+$bW; + $imageXmax = $bX + $bW; $imageYmin = $bY; - $imageYmax = $bY+$bH; + $imageYmax = $bY + $bH; if (!$iRepeat[0] && !$iRepeat[1]) { - $imageXmin = $iPosition[0]; - $imageXmax = $iPosition[0]+$imageWidth; - $imageYmin = $iPosition[1]; - $imageYmax = $iPosition[1]+$imageHeight; + $imageXmin = $iPosition[0]; + $imageXmax = $iPosition[0] + $imageWidth; + $imageYmin = $iPosition[1]; + $imageYmax = $iPosition[1] + $imageHeight; } elseif ($iRepeat[0] && !$iRepeat[1]) { - $imageYmin = $iPosition[1]; - $imageYmax = $iPosition[1]+$imageHeight; + $imageYmin = $iPosition[1]; + $imageYmax = $iPosition[1] + $imageHeight; } elseif (!$iRepeat[0] && $iRepeat[1]) { - $imageXmin = $iPosition[0]; - $imageXmax = $iPosition[0]+$imageWidth; + $imageXmin = $iPosition[0]; + $imageXmax = $iPosition[0] + $imageWidth; } // build the path to display the image (because of radius) $this->pdf->clippingPathStart($bX, $bY, $bW, $bH, $inTL, $inTR, $inBL, $inBR); // repeat the image - for ($iY=$imageYmin; $iY<$imageYmax; $iY+=$imageHeight) { - for ($iX=$imageXmin; $iX<$imageXmax; $iX+=$imageWidth) { + for ($iY = $imageYmin; $iY < $imageYmax; $iY += $imageHeight) { + for ($iX = $imageXmin; $iX < $imageXmax; $iX += $imageWidth) { $cX = null; $cY = null; $cW = $imageWidth; $cH = $imageHeight; - if ($imageYmax-$iY<$imageHeight) { + if ($imageYmax - $iY < $imageHeight) { $cX = $iX; $cY = $iY; - $cH = $imageYmax-$iY; + $cH = $imageYmax - $iY; } - if ($imageXmax-$iX<$imageWidth) { + if ($imageXmax - $iX < $imageWidth) { $cX = $iX; $cY = $iY; - $cW = $imageXmax-$iX; + $cW = $imageXmax - $iX; } - $this->pdf->Image($iName, $iX, $iY, $imageWidth, $imageHeight, '', '', '', $resize, 300, '', false, false, 0, $fitbox); + $this->pdf->Image($iName, $iX, $iY, $cW, $cH, '', '', '', $resize, 300, '', false, false, 0, $fitbox); } } From 720416a1c806009186f606f0db67edc54831c469 Mon Sep 17 00:00:00 2001 From: Kevin Glier Date: Thu, 25 Jul 2024 09:43:31 +0200 Subject: [PATCH 5/5] Fix background-size calculation for "contain" and "cover" respecting the orientation --- src/Html2Pdf.php | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/src/Html2Pdf.php b/src/Html2Pdf.php index 0c436385..1792c46c 100755 --- a/src/Html2Pdf.php +++ b/src/Html2Pdf.php @@ -1845,21 +1845,25 @@ protected function _drawRectangle($x, $y, $w, $h, $border, $padding, $margin, $b $imageIsLandscape = $imageWidth > $imageHeight; if ($iSize == 'contain') { - if (($containerIsLandscape && !$imageIsLandscape) || (!$containerIsLandscape && $imageIsLandscape)) { - $imageHeight = $bH; - $imageWidth = $bH * ($imageInfos[0] / $imageInfos[1]); + if (($containerIsLandscape && $imageIsLandscape) || (!$containerIsLandscape && !$imageIsLandscape)) { + // Scale to fit width or height, maintaining aspect ratio + $scale = min($bW / $imageWidth, $bH / $imageHeight); } else { - $imageWidth = $bW; - $imageHeight = $bW * ($imageInfos[1] / $imageInfos[0]); + // Scale to fit height or width, maintaining aspect ratio + $scale = min($bH / $imageHeight, $bW / $imageWidth); } + $imageWidth *= $scale; + $imageHeight *= $scale; } else if ($iSize == 'cover') { - if (($containerIsLandscape && !$imageIsLandscape) || (!$containerIsLandscape && $imageIsLandscape)) { - $imageWidth = $bW; - $imageHeight = $bW * ($imageInfos[1] / $imageInfos[0]); + if (($containerIsLandscape && $imageIsLandscape) || (!$containerIsLandscape && !$imageIsLandscape)) { + // Scale to cover width or height, maintaining aspect ratio + $scale = max($bW / $imageWidth, $bH / $imageHeight); } else { - $imageHeight = $bH; - $imageWidth = $bH * ($imageInfos[0] / $imageInfos[1]); + // Scale to cover height or width, maintaining aspect ratio + $scale = max($bH / $imageHeight, $bW / $imageWidth); } + $imageWidth *= $scale; + $imageHeight *= $scale; } $resize = true;