0

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);
    }
}
deceze
  • 510,633
  • 85
  • 743
  • 889
schlompf
  • 1
  • 2
  • SHow more of the script so we have some context of what you are doing please – RiggsFolly Aug 10 '23 at 11:39
  • 2
    "*a pdf which can not be opened by viewers*" - Then open it with a text-editor, e.g. notepad or notepad++ to see if any errors occured – DarkBee Aug 10 '23 at 11:39
  • 1
    Are you sure you _have_ two images in your data? `count($imagePaths) > 0` would also be true if you had only _one_ item in $imagePaths, yet you would nevertheless try to access the non-existing `$imagePaths[1]` then ... – CBroe Aug 10 '23 at 11:41
  • 3
    It is nice that you solved the question. Bravo. It's even nicer that you let the readers of your question know about it. However, you added the solution to your question in the question itself. That's not how Stack Overflow is supposed to work. Can you please add the solution as an **answer**, just copy it, and delete it from the question? – KIKO Software Aug 17 '23 at 09:48

0 Answers0