From 822922df5cd1d1d5f065e89f2fe47a82c07669cb Mon Sep 17 00:00:00 2001 From: Pierre Goiffon Date: Thu, 23 Feb 2023 11:45:29 +0100 Subject: [PATCH] =?UTF-8?q?N=C2=B05588=20-=20Improve=20PDF=20export=20robu?= =?UTF-8?q?stness=20when=20AttributeImage=20dimensions=20cannot=20be=20det?= =?UTF-8?q?ermined=20(#350)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Can happen for example on SVG images Now the export won't crash anymore, and we'll get a log (export channel, warning level) with the object and attribute causing a problem as context Co-authored-by: Molkobain --- core/log.class.inc.php | 6 ++++ core/pdfbulkexport.class.inc.php | 54 +++++++++++++++++++++++--------- 2 files changed, 46 insertions(+), 14 deletions(-) diff --git a/core/log.class.inc.php b/core/log.class.inc.php index df525d863..6596ffa91 100644 --- a/core/log.class.inc.php +++ b/core/log.class.inc.php @@ -549,6 +549,12 @@ class LogChannels const DEADLOCK = 'DeadLock'; + /** + * @var string + * @since 2.7.9 + */ + const EXPORT = 'export'; + const INLINE_IMAGE = 'InlineImage'; /** diff --git a/core/pdfbulkexport.class.inc.php b/core/pdfbulkexport.class.inc.php index 629c1fd59..eb4aa15f4 100644 --- a/core/pdfbulkexport.class.inc.php +++ b/core/pdfbulkexport.class.inc.php @@ -216,28 +216,33 @@ EOF // As sample data will be displayed in the web browser, AttributeImage needs to be rendered with a regular HTML format, meaning its "src" looking like "data:image/png;base64,iVBORw0KGgoAAAANSUh..." // Whereas for the PDF generation it needs to be rendered with a TCPPDF-compatible format, meaning its "src" looking like "@iVBORw0KGgoAAAANSUh..." if ($oAttDef instanceof AttributeImage) { - return $this->GetAttributeImageValue($oAttDef, $oObj->Get($sAttCode), static::ENUM_OUTPUT_TYPE_SAMPLE); + return $this->GetAttributeImageValue($oObj, $sAttCode, static::ENUM_OUTPUT_TYPE_SAMPLE); } } return parent::GetSampleData($oObj, $sAttCode); } + /** + * @param \DBObject $oObj + * @param string $sAttCode + * + * @return int|string + * @throws \Exception + */ protected function GetValue($oObj, $sAttCode) { - switch($sAttCode) - { + switch ($sAttCode) { case 'id': $sRet = parent::GetValue($oObj, $sAttCode); break; default: $value = $oObj->Get($sAttCode); - if ($value instanceof ormDocument) - { + if ($value instanceof ormDocument) { $oAttDef = MetaModel::GetAttributeDef(get_class($oObj), $sAttCode); if ($oAttDef instanceof AttributeImage) { - $sRet = $this->GetAttributeImageValue($oAttDef, $value, static::ENUM_OUTPUT_TYPE_REAL); + $sRet = $this->GetAttributeImageValue($oObj, $sAttCode, static::ENUM_OUTPUT_TYPE_REAL); } else { @@ -268,15 +273,22 @@ EOF } /** - * @param \AttributeImage $oAttDef Instance of image attribute - * @param \ormDocument $oValue Value of image attribute + * @param \DBObject $oObj + * @param string $sAttCode * @param string $sOutputType {@see \PDFBulkExport::ENUM_OUTPUT_TYPE_SAMPLE}, {@see \PDFBulkExport::ENUM_OUTPUT_TYPE_REAL} * * @return string Rendered value of $oAttDef / $oValue according to the desired $sOutputType - * @since 2.7.8 + * @throws \ArchivedObjectException + * @throws \CoreException + * + * @since 2.7.8 N°2244 method creation + * @since 2.7.9 N°5588 signature change to get the object so that we can log all the needed information */ - protected function GetAttributeImageValue(AttributeImage $oAttDef, ormDocument $oValue, string $sOutputType) + protected function GetAttributeImageValue(DBObject $oObj, string $sAttCode, string $sOutputType) { + $oValue = $oObj->Get($sAttCode); + $oAttDef = MetaModel::GetAttributeDef(get_class($oObj), $sAttCode); + // To limit the image size in the PDF output, we have to enforce the size as height/width because max-width/max-height have no effect // $iDefaultMaxWidthPx = 48; @@ -287,13 +299,27 @@ EOF $sUrl = $oAttDef->Get('default_image'); } else { - list($iWidth, $iHeight) = utils::GetImageSize($oValue->GetData()); $iMaxWidthPx = min($iDefaultMaxWidthPx, $oAttDef->Get('display_max_width')); $iMaxHeightPx = min($iDefaultMaxHeightPx, $oAttDef->Get('display_max_height')); - $fScale = min($iMaxWidthPx / $iWidth, $iMaxHeightPx / $iHeight); - $iNewWidth = $iWidth * $fScale; - $iNewHeight = $iHeight * $fScale; + list($iWidth, $iHeight) = utils::GetImageSize($oValue->GetData()); + if ((is_null($iWidth)) || (is_null($iHeight)) || ($iWidth === 0) || ($iHeight === 0)) { + // Avoid division by zero exception (SVGs, corrupted images, ...) + $iNewWidth = $iDefaultMaxWidthPx; + $iNewHeight = $iDefaultMaxHeightPx; + + $sAttCode = $oAttDef->GetCode(); + IssueLog::Warning('AttributeImage: Cannot read image size', LogChannels::EXPORT, [ + 'ObjClass' => get_class($oObj), + 'ObjKey' => $oObj->GetKey(), + 'ObjFriendlyName' => $oObj->GetName(), + 'AttCode' => $sAttCode, + ]); + } else { + $fScale = min($iMaxWidthPx / $iWidth, $iMaxHeightPx / $iHeight); + $iNewWidth = $iWidth * $fScale; + $iNewHeight = $iHeight * $fScale; + } $sValueAsBase64 = base64_encode($oValue->GetData()); switch ($sOutputType) {