Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CSS background-size properties "contain" and "cover" #793

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
21 changes: 21 additions & 0 deletions src/CssConverter.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
*
Expand Down
106 changes: 70 additions & 36 deletions src/Html2Pdf.php
Original file line number Diff line number Diff line change
Expand Up @@ -1798,6 +1798,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;
Expand All @@ -1806,93 +1807,126 @@ 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;
$resize = false;
if ($iSize && $iSize != 'auto') {
$containerIsLandscape = $bW > $bH;
$imageIsLandscape = $imageWidth > $imageHeight;

if ($iSize == 'contain') {
if (($containerIsLandscape && $imageIsLandscape) || (!$containerIsLandscape && !$imageIsLandscape)) {
// Scale to fit width or height, maintaining aspect ratio
$scale = min($bW / $imageWidth, $bH / $imageHeight);
} else {
// 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)) {
// Scale to cover width or height, maintaining aspect ratio
$scale = max($bW / $imageWidth, $bH / $imageHeight);
} else {
// Scale to cover height or width, maintaining aspect ratio
$scale = max($bH / $imageHeight, $bW / $imageWidth);
}
$imageWidth *= $scale;
$imageHeight *= $scale;
}

$resize = true;
$fitbox = 'CM';
}

// prepare the position of the backgroung
// prepare the position of the background
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, '', '');
$this->pdf->Image($iName, $iX, $iY, $cW, $cH, '', '', '', $resize, 300, '', false, false, 0, $fitbox);
}
}

Expand Down
10 changes: 8 additions & 2 deletions src/Parsing/Css.php
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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'),
Expand Down Expand Up @@ -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;
Expand Down