php - DOMPDF image rendering issue - Stack Overflow

admin2025-04-19  1

As stated in the title i'm currently using Laravel DOMPDF that extends from DOMPDF library to generate PDF from HTML.

Issue : Images are displayed in the PDF as a black square with a cross inside

Context :

I'm retrieving from my DB an HTML file that was created using a text editor.

$template = Template::where('Title', $request->templateTitle)->firstOrFail();

This HTML contains placeholders (in the code dynamic-values) for words and images. Still from my DB i'm retrieving data to replace these placeholders

// Retrieve trainer & company data for dynamic replacements.
$trainer = Trainer::findOrFail($request->trainerId);
$company = Company::findOrFail($trainer->companies_id);

// Define the dynamic values. (Keep these keys in sync with your template)
$dynamicValues = [
    'company_name'                => $company->company_name,
    'street_number'               => $company->street_number,
    'street_type'                 => $company->street_type,
    'street_name'                 => $company->street_name,
    'postal_code'                 => $company->postal_code,
    'city'                        => $company->city,
    'activity_declaration_number' => $company->activity_declaration_number,
    'acquisition_region'          => $company->acquisition_region,
    'registration_number'         => $company->registration_number,
    'contact_first_name'          => $company->contact_first_name,
    'contact_last_name'           => $company->contact_last_name,
    'trainer_first_name'          => $trainer->first_name,
    'trainer_last_name'           => $trainer->last_name,
    'trainer_address'             => $trainer->address,
    'trainer_zip_code'            => $trainer->zip_code,
    'logo_path'                   => $company->logo_path, 
];

// Get the initial content from the template.
$content = $template->Content;
// Replace all dynamic text placeholders.
foreach ($dynamicValues as $key => $value) {
    $placeholder = '<span class="dynamic-value">' . $key . '</span>';
    $content = str_replace($placeholder, $value, $content);
}

Then to replace placeholder images for a PDF i call :

$content = $this->processDynamicImages($content, $dynamicValues, 'pdf');

Which is just a function that remove images with empty src, retrieve the images marked as 'dynamic-image', replace the placeholder src by it's value and returns the full html:

private function processDynamicImages($html, array $dynamicValues, $format)
    {
        libxml_use_internal_errors(true);
        $dom = new \DOMDocument();
        $dom->loadHTML(mb_convert_encoding($html, 'HTML-ENTITIES', 'UTF-8'));
        $images = $dom->getElementsByTagName('img');
        foreach ($images as $img) {
            if (!($img instanceof \DOMElement)) {
                continue;
            }
            if (empty($img->getAttribute('src'))) {
                $img->parentNode->removeChild($img);
                continue;
            }
            $class = $img->getAttribute('class');
            if (strpos($class, 'dynamic-image') !== false) {
                $src = $img->getAttribute('src');
                foreach ($dynamicValues as $key => $value) {
                    if (stripos($src, $key) !== false) {
                        $newPath = ltrim($value, '/'); // e.g. "storage/companies/logos/..."
                        // Ensure forward slashes (they work with chroot)
                        $newPath = str_replace('\\', '/', $newPath);
                        $img->setAttribute('src', $newPath);
                        break;
                    }
                }
            }
        }
        $result = $dom->saveHTML();
        libxml_clear_errors();
        return $result;
    }

Then we finish the pdf Creation by defining the chroot (the folder in which DOMPDF will be authorized to look for images)

$chroot = public_path();
$pdf = \Barryvdh\DomPDF\Facade\Pdf::loadHTML($content)->setOptions(['chroot' => $chroot, 'isRemoteEnabled'=>true]);

And defining the filename, filepath for storage and create the pdf :

$filename = $pdfFilename;
$filePath = storage_path('app/public/Trainers/contracts/' . $filename);
$pdf->save($filePath);

What i've found :

There is a file called Cache.php in a folder called Image in the vendor/dompdf/src library

In this file there is a function called resolve_url which, as stated at the top of the function is used to Resolve and fetch an image for use

After several tests with this function, i found the issue was coming from this foreach :

foreach ($allowed_protocols[$protocol]["rules"] as $rule) {
                [$result, $message] = $rule($full_url);
                if (!$result) {
                    throw new ImageException("Error loading $url: $message", E_WARNING);
                }
            }

Where $allowed_protocols = $options->getAllowedProtocols() and getAllowedProtocols() returns

private $allowedProtocols = [
        "data://" => ["rules" => []],
        "file://" => ["rules" => []],
        "http://" => ["rules" => []],
        "https://" => ["rules" => []]
    ];

and [$result,$message] would return :

array:2 [▼ // vendor\dompdf\dompdf\src\Image\Cache.php:86
  0 => false
  1 => "Permission denied. The file could not be found under the paths specified by Options::chroot."
]

So i removed the foreach, just to find that i had to enable the extension php_gd in my php_ini and restart my computer and it worked.

Question:

Why does this foreach triggers an error ?

EDIT ON SWITI ANSWER

Thank you for your answer Switi

But as i stated in the body of my question, i've already installed php_gd by exactly doing what you are telling in your answer

Something caught my attention though, i didn't try the asset() for the src. So following up on your advice i replaced in my controller method processDynamicImages() , in if($format === 'pdf') this :

$newPath = str_replace('\\', '/', $newPath);

by that :

$newPath = str_replace('\\', '/', asset($newPath));

Now, two things changed.

  • First the loading time for the answer coming from the server is way longer, i even had to add ini_set('max_execution_time', 90); to the beginning of my Controller to avoid a Maximum Execution Time error
  • Second, in the Image/Cache.php in DOMPDF library, the foreach that checks if the image follows security rules is now returning :

array (0 => true,1 => NULL) instead of false and Permission denied before returning :

Dompdf\Exception\ImageException {#4598 ▼ // vendor\dompdf\dompdf\src\Image\Cache.php:191
  #message: "Image not found"
  #code: 2
  #file: "
D:\Graphy Scope\CRM MASTERFORMA\Code\masterforma\vendor
\dompdf\dompdf\
src\Image\Cache.php
"
  #line: 117
  trace: {▶}
}

We're getting somewhere !

If i dd($full_url); i get http://localhost:8000/storage/companies/logos/QYMcuLqYwlhqZIXst1j13w79iI41wctYNenXVuf6.png which is the right path to the image, so i don't understand the image not found

转载请注明原文地址:http://conceptsofalgorithm.com/Algorithm/1745021481a280408.html

最新回复(0)