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

Pdf Tools Laravel Package

itul/pdf-tools

itul/pdf-tools is a Laravel/PHP package offering practical PDF utilities for common document workflows, giving developers a simple, framework-friendly way to generate, manipulate, and manage PDF files with reusable helpers and clean integration.

View on GitHub
Deep Wiki
Context7

Getting Started

itul/pdf-tools is a Bitbucket-hosted PDF utility package with no public documentation and no Packagist presence, so treat onboarding as source-reading rather than doc-reading.

  1. Confirm repository access first. Because it lives on Bitbucket, Composer needs credentials before require will resolve. Add the VCS entry and authenticate:

    {
      "repositories": [
        { "type": "vcs", "url": "https://bitbucket.org/bmooreitul/pdf-tools" }
      ],
      "require": { "itul/pdf-tools": "dev-master" }
    }
    

    Use a Bitbucket app password / OAuth consumer in auth.json (or CI secrets) so both local dev and pipelines can clone it.

  2. Discover the API from the package itself. Open these in order:

    • composer.json — reveals whether it wraps a binary (Ghostscript/qpdf/pdftk), a PHP library (FPDI, mPDF, Dompdf), or is bespoke.
    • src/ — the service provider and the main entry class tell you the public surface.
    • config/ — anything publishable (php artisan vendor:publish).
    • tests/ and README — the only usage examples you are likely to get.
  3. Publish and inspect config (if a config file ships):

    php artisan vendor:publish --provider="Itul\\PdfTools\\PdfToolsServiceProvider"
    

    Pay attention to binary paths (gs, qpdf, pdftk) and temp directories — these are the usual first-run failures.

  4. First use case: a low-risk round-trip. Pick an operation you can verify byte-for-byte, e.g. splitting a small known PDF into pages and asserting page count, before touching HTML-to-PDF conversion or merging. This isolates install/config problems from logic problems.

Given the package has zero dependents and zero stars, assume no semver discipline and pin to a commit hash (dev-master#<sha>) rather than a floating branch.

Implementation Patterns

Wrap it, don't sprinkle it. Introduce a single application-level service that owns the package, so a future replacement (or a fork of this abandoned-looking repo) is one file's worth of work:

final class PdfService
{
    public function __construct(private PdfTools $pdf) {}

    public function merge(array $paths, string $out): string
    {
        // package-specific calls isolated here
    }
}

Bind this in a service provider and inject it — never resolve the package directly in controllers or jobs.

Push heavy operations onto the queue. Merging, compression, OCR, and HTML-to-PDF are CPU- and IO-bound and will blow through web request timeouts. Model each PDF operation as a job that writes to a disk and emits an event when done:

final class MergePdfs implements ShouldQueue
{
    public function handle(PdfService $pdf): void
    {
        $out = "pdfs/{$this->uuid}.pdf";
        $pdf->merge($this->inputs, Storage::path($out));
        PdfMerged::dispatch($this->uuid);
    }
}

Use a pipeline for multi-step workflows. A typical chain is upload → validate → normalize → transform → store → stream download. Keep each step a small method on your wrapper so you can test them independently and skip steps conditionally.

Separate storage from streaming. Write outputs to a dedicated disk (pdfs/), then return a response()->streamDownload() or a signed temporary URL. Avoid echoing PDF bytes from inside the package call — you lose the ability to retry or cache.

Test against fixture PDFs. Commit a couple of small PDFs under tests/Fixtures/. Assert on page count, file existence, and file size rather than binary equality. If the package exposes page-count or metadata helpers, use them to verify results instead of pulling in a second parser.

Validate and sanitize paths. Any filename or path coming from a user should be resolved against a known base directory and checked with realpath() before being handed to the package, which likely passes it to a shell binary.

Gotchas and Tips

  • Private repo auth breaks CI silently. If composer install works locally but not in your pipeline, it is almost always a missing Bitbucket credential in auth.json or the CI secret store. Bake this into your Docker image build, not just a local script.
  • Pin to a commit. With no tags and no known release cadence, dev-master can shift under you. Lock the hash and update deliberately.
  • Binary dependencies are the #1 runtime failure. If the package shells out to Ghostscript, qpdf, or ImageMagick, those must exist in every environment — including the queue worker container. Add a one-line health check to your deploy (e.g. which gs qpdf) so a missing binary surfaces at deploy time, not on a user's first merge.
  • CLI wrappers often fail quietly. Many PDF toolsets return exit code 0 with empty output, or write a truncated file on error. After every call, assert that the output file exists and is non-zero. Wrap calls in try/finally so temp files are unlinked even on exception — otherwise your storage/app/pdf/tmp directory grows forever.
  • Memory and time limits. Large PDFs plus libraries like FPDI can exhaust PHP's default memory_limit. Set memory_limit and set_time_limit per job, or pass the work to a binary that runs out-of-process.
  • Prefer constructor injection over the facade. Facades are awkward to mock and this package likely has no interface to bind against. Wrapping it (as above) gives you a seam without needing the package to cooperate.
  • Config caching quirks. If you publish a config file that reads env() values, remember php artisan config:cache in production will freeze those at build time — set binary paths and temp directories via config, and drive only the config values from env.
  • Non-ASCII filenames. Passing Unicode paths to shell binaries is a common corruption source. Normalize to ASCII-safe temp names internally, and restore friendly filenames only at download time via the Content-Disposition header.
  • Extension points to look for. Published config (binary paths, temp dir, defaults), any interface in src/Contracts you can bind your own implementation to, and any events the package dispatches. If none exist, that is a signal to keep your integration thin — this package gives you little to hook into and may not be maintained.
  • Health/debug command. Write a small artisan command (pdf:doctor) that reports resolved binary paths, versions, and a successful round-trip on a bundled fixture. It will save hours the first time a fresh environment misbehaves.
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.
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
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
cgv/localized-resource