invoiceninja/invoiceninja

Selfhost - Modern PDF attachments silently fail to embed due to missing PdfParse module

Open

#12,004 opened on 2026年6月3日

GitHub で見る
 (2 comments) (0 reactions) (0 assignees)PHP (2,635 forks)batch import
feature requesthelp wanted

Repository metrics

Stars
 (9,732 stars)
PR merge metrics
 (平均マージ 8d 15h) (30d で 21 merged PRs)

説明

Bug description

When attaching a modern PDF (version 1.5+) to an invoice in the self-hosted version, the PDF silently fails to embed in the generated invoice PDF, even if "Embed Images/Documents" is enabled.

This occurs because FPDI natively throws a PdfParserException when encountering PDFs > v1.4. In the App\Services\PdfMaker\PdfMerge class, the exception is caught, and the code attempts to downgrade the PDF by calling \Modules\Admin\Services\PdfParse::downgrade($file). However, this module class is not present in the self-hosted version, causing the class_exists() check to fail. As a result, the document embedding is completely skipped without any error logs.

Steps to reproduce

  1. Enable "Include attached images/pdfs in the invoice" in Settings -> Invoice Design.
  2. Attach a PDF saved in format > 1.4 (e.g., a standard export from modern tools like Chrome or Word) to an invoice.
  3. Click "View PDF" or send the invoice.
  4. The attached PDF is completely omitted from the final document, and no error is thrown in laravel.log.

Expected behavior

The application should either natively attempt to downgrade the PDF using Ghostscript (gs) via a fallback if the premium module is missing, or at least log an error explaining why the attachment was skipped.

Environment

  • Invoice Ninja v5 (Self-hosted docker invoiceninja-debian:latest)

Code context

In app/Services/PdfMaker/PdfMerge.php:

            } catch (\setasign\Fpdi\PdfParser\PdfParserException $e) {
                // If FPDI fails, try downgrading the PDF

                if (class_exists(\Modules\Admin\Services\PdfParse::class)) {
                    $downgradedPdf = \Modules\Admin\Services\PdfParse::downgrade($file);
                    $pageCount = $pdf->setSourceFile(StreamReader::createByString($downgradedPdf));
                }
            }

If gs is installed on the host, a simple shell_exec fallback could be implemented here for self-hosted users.

コントリビューターガイド