Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Tcpdf Bundle Laravel Package

qipsius/tcpdf-bundle

Symfony bundle that integrates TCPDF into your project, providing a configured service for generating PDF documents from HTML, with manageable settings for fonts, page formats, and output.

View on GitHub
Deep Wiki
Context7

Getting Started

composer require qipsius/tcpdf-bundle

The bundle pulls in tecnickcom/tcpdf, so you do not need to install TCPDF separately. There is no Flex recipe, so register it manually:

// config/bundles.php
return [
    // ...
    Qipsius\TCPDFBundle\QipsiusTCPDFBundle::class => ['all' => true],
];

Confirm the service id and its constructor arguments before writing any code — the bundle registers a pre-configured TCPDF instance (conventionally qipsius.tcpdf), but the exact id/alias varies by version:

bin/console debug:container tcpdf
bin/console config:dump-reference qipsius_tcpdf

Minimal configuration (paths are the only thing you normally need to touch):

# config/packages/qipsius_tcpdf.yaml
qipsius_tcpdf:
    tcpdf:
        k_path_images: '%kernel.project_dir%/public/images/'
        k_path_cache: '%kernel.cache_dir%/tcpdf'
        k_path_fonts: '%kernel.project_dir%/vendor/tecnickcom/tcpdf/fonts'

First use case — a one-page PDF from a controller:

public function invoice(TCPDF $tcpdf): Response
{
    $pdf = clone $tcpdf;              // see Gotchas: the service is a shared prototype
    $pdf->SetFont('helvetica', '', 11);
    $pdf->AddPage();
    $pdf->writeHTML('<h1>Invoice #1042</h1><p>Thanks for your business.</p>');

    return new Response($pdf->Output('invoice.pdf', 'S'), 200, [
        'Content-Type' => 'application/pdf',
        'Content-Disposition' => 'inline; filename="invoice.pdf"',
    ]);
}

That's the whole loop: inject, clone, draw, Output(..., 'S'), wrap in a Response. Everything else is TCPDF API knowledge, not bundle knowledge.

Implementation Patterns

Wrap the shared service in a factory. The bundled service is a singleton prototype. For anything beyond a single PDF per request, expose your own service that clones the prototype or constructs a fresh instance:

final class PdfFactory
{
    public function __construct(private \TCPDF $prototype) {}

    public function create(string $orientation = 'P', string $format = 'A4'): \TCPDF
    {
        $pdf = clone $this->prototype;
        $pdf->setPrintHeader(false);
        $pdf->setPrintFooter(false);
        return $pdf;
    }
}

Twig-rendered PDFs. The most productive pattern is HTML in Twig, rendered to a string, then writeHTML(). Keep templates table-based with inline styles and no flexbox/grid — TCPDF's HTML engine is roughly HTML4 + a CSS subset:

$html = $this->renderView('pdf/invoice.html.twig', ['invoice' => $invoice]);
$pdf->writeHTML($html, true, false, true, false, '');

Shared header/footer. Subclass TCPDF, override Header()/Footer(), and register your subclass as the service by overriding the bundle's definition in config/services.yaml (same service id, class: pointing at yours). This is the cleanest extension point the bundle gives you.

Output modes. Pick deliberately:

  • 'I' — inline stream (browser viewer)
  • 'D' — force download
  • 'S' — return the bytes as a string (use this for storage, mail attachments, tests)
  • 'F' — write to a path on disk

Email attachments. Generate to a temp file and hand the path to Symfony Mailer:

$path = tempnam(sys_get_temp_dir(), 'pdf') . '.pdf';
$pdf->Output($path, 'F');
$email->attachFromPath($path, 'invoice.pdf', 'application/pdf');

Async generation. TCPDF objects are stateful and non-serializable — never put them in a Messenger payload. Instead dispatch a message carrying only the entity id, and build the PDF inside the handler via your factory.

Testing. Assert on %PDF- prefix and Content-Type rather than byte-for-byte output; TCPDF output is not deterministic across versions.

Gotchas and Tips

  • The service is shared and stateful. Injecting it and calling AddPage() twice in one request yields a two-page PDF the second time. Always clone or build fresh. This is by far the most common bug with this bundle.
  • Config is read at container compile time, not per PDF. Changing k_path_* values mid-request does nothing; TCPDF also define()s K_PATH_* constants at class load, so a second differing configuration in the same process is silently ignored.
  • The cache/font directory must exist and be writable. Point k_path_cache at %kernel.cache_dir% and make sure deploy scripts don't wipe permissions. A non-writable cache is a classic source of blank or truncated output.
  • Image paths resolve against K_PATH_IMAGES. Relative <img src="logo.png"> fails silently (a red box, or nothing) if the path is wrong. Prefer absolute paths in dev, or set k_path_images to a real directory.
  • Headers/footers must be configured before AddPage(); afterwards SetHeaderData/SetFooterMargin calls won't apply to already-emitted pages.
  • Debugging blank pages: check $pdf->getErrors() — TCPDF accumulates errors rather than throwing — and verify writeHTML() received non-empty content. Suppress the E_DEPRECATED noise TCPDF emits on modern PHP in narrow scopes rather than globally.
  • UTF-8 and fonts: the default helvetica core font does not cover non-Latin scripts. Switch to dejavusans (bundled) for accented/Cyrillic text; expect larger files and slower generation.
  • Memory and time: large tables blow past memory_limit; raise it in the handler/command, not in php.ini globally. Bulk jobs should generate one PDF per process iteration and unset() between runs.
  • Check the version matrix before upgrading. With a small dependent base (0 dependents), minor releases aren't field-tested by many users; pin ^2/^3 explicitly and read the changelog on Symfony major upgrades.
  • Extension points are plain Symfony: override the service definition, decorate it, or alias your own class to the bundle's id. There is no bundle-specific plugin system, so treat config/services.yaml as the customization surface.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle
glavweb/extra-bundle
glavweb/api-bundle
glavweb/admin-bundle
adachsoft/api-version-bundle
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager