I pull pictures from a database and use TCPDF to create a PDF.
If I just use one picture, everything is fine if I use two I get a broken PDF.
One picture and everything is fine:
if (isset($imagePaths) && count($imagePaths) > 0) {
$pdf->Image($imagePaths[0], '', '', 50, 0, '', '', '', true, 300, '', false, false, 1, false, false, false);
}
But if I use two
if (isset($imagePaths) && count($imagePaths) > 0) {
$pdf->Image($imagePaths[0], '', '', 50, 0, '', '', '', true, 300, '', false, false, 1, false, false, false);
$pdf->Image($imagePaths[1], '', '', 50, 0, '', '', '', true, 300, '', false, false, 1, false, false, false);
}
I get a pdf which can not be opened by viewers because it is corrupt. The preview of the Contao CMS lets me see the pdf and everything looks fine. The pictures are there and they have the size they are supposed to have but the pdf is broken.
I used:
$pdf = new TCPDF('P', 'mm', 'A4', true, 'UTF-8', false);
$pdf->setPDFVersion('1.7'); // and all other versions
$pdf->setPrintHeader(false);
$pdf->setPrintFooter(false);
What might be the problem?
@RiggsFolly: Here is the rest of the code:
use Contao\Database;
use Contao\FrontendUser;
use TCPDF;
$member = FrontendUser::getInstance();
if ($member->id) {
$pdf = new TCPDF('P', 'mm', 'A4', true, 'UTF-8', false);
$pdf->setPDFVersion('1.7'); // or any Version
$pdf->SetCreator(PDF_CREATOR);
$pdf->setPrintHeader(false);
$pdf->setPrintFooter(false);
$pdf->AddPage();
$memberData = Database::getInstance()->prepare("SELECT * FROM tl_member WHERE id=?")
->execute($member->id);
$imageUuids = \StringUtil::deserialize($memberData->Fahrzeugbilder, true);
$imagePaths = [];
foreach ($imageUuids as $uuid) {
$fileModel = \FilesModel::findByUuid($uuid);
if ($fileModel) {
$imagePaths[] = $fileModel->path;
}
}
// simple Test
if (isset($imagePaths) && count($imagePaths) > 0) {
$pdf->Image($imagePaths[0], '', '', 50, 0, '', '', '', true, 300, '', false, false, 1, false, false, false);
$pdf->Ln();
$pdf->Image($imagePaths[1], '', '', 50, 0, '', '', '', true, 300, '', false, false, 1, false, false, false);
}
$pdfPath = 'files/Unfallmeldungen/' . $member->lastname . '_' . rand(1000, 9999) . '.pdf';
$pdf->Output(TL_ROOT . '/' . $pdfPath, 'F');
}
?>
@DarkBee: Well if I open the PDF in vscode it is indeed interesting to look at the data. It seems that the broken files lack an %%EOF and some lines before.
working:
0000042554 00000 n
trailer
<< /Size 11 /Root 10 0 R /Info 8 0 R /ID [ <700cba70ce5dbefffc3e8d8df177581f> <700cba70ce5dbefffc3e8d8df177581f> ] >>
startxref
42762
%%EOF
broken:
0000081249 00000 n
trailer
<< /Size 12 /Root 11 0 R /Info 9 0 R /ID [ <d4535d28154be4804d3268484cd
And I can put 3 images in the PDF it will again only lack the last 3-4 lines.
@CBroe: Yes I am sure. The internal viewer of the Contao CMS does not care so much about the file integrity and shows it anyway and there I can see, that it looks exactly as it should. With both images. (And I of course checked with only one image (which works) and tried each, [0] and [1]. )
SOLUTION:
Turned out that some pictures did not produce this (broken) outcome. And some who did not work, worked after saving them again in a photo editor. It is kind of strange that two pictures which work alone, do not work together. And although the pictures are resized which means in my imagination that the are decoded, resized and then saved as a new image, therefore are encoded as a completely new image, I thougt more recoding might do the trick. So the following function converts the images.
// Convert images to prevent corrupted PDF
function optimizeAndResizeImage($imagePath, $targetWidth) {
// Retrieve image details
$imageInfo = getimagesize($imagePath);
// Load the image based on its type
switch ($imageInfo[2]) {
case IMAGETYPE_JPEG:
$sourceImage = imagecreatefromjpeg($imagePath);
break;
case IMAGETYPE_PNG:
$sourceImage = imagecreatefrompng($imagePath);
break;
case IMAGETYPE_GIF:
$sourceImage = imagecreatefromgif($imagePath);
break;
default:
return false;
}
// Calculate scaling ratio
$ratio = $targetWidth / $imageInfo[0];
$newWidth = (int) $targetWidth;
$newHeight = (int) ($imageInfo[1] * $ratio);
// Create a new image with the calculated dimensions
$newImage = imagecreatetruecolor($newWidth, $newHeight);
// Copy and scale the old image into the new image
imagecopyresampled($newImage, $sourceImage, 0, 0, 0, 0, $newWidth, $newHeight, $imageInfo[0], $imageInfo[1]);
// Set the temporary path for the optimized image
$tempPath = sys_get_temp_dir() . '/' . uniqid() . '.jpg';
// Save the new image
imagejpeg($newImage, $tempPath, 90); // 90 is the quality
// Release resources
imagedestroy($sourceImage);
imagedestroy($newImage);
return $tempPath;
}
Now this works:
foreach ($imagePaths as $imagePath) {
$optimizedImagePath = optimizeAndResizeImage($imagePath, 50 * 2.83465);
if ($optimizedImagePath) {
$pdf->Image($optimizedImagePath, '', '', 50, 0, '', '', '', true, 300, '', false, false, 1, false, false, false);
$pdf->Ln(); // Line break after the image
$pdf->Ln(5); // Empty line of 5mm
// Delete the temporary optimized image
unlink($optimizedImagePath);
}
}