Skip to content

llm1998/PDFMake

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

1 Commit
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

LaravelPDF

Modern PDF generation library for Laravel with multiple digital signatures support. Built on PHP 8.3+ with a fluent API on top of TCPDF.

✨ Features

  • 🎯 Fluent PHP 8.3+ API β€” Typed properties, enums, readonly classes, named arguments
  • ✍️ Multiple Digital Signatures β€” Sign PDFs with two or more certificates (certification + approval)
  • πŸ“„ Full PDF Generation β€” Text, tables, images, HTML, barcodes, QR codes
  • 🎨 HTML to PDF β€” Convert HTML/CSS content including tables and styles
  • πŸ”§ Laravel Integration β€” Service Provider, Facade, config, Blade views
  • πŸ“Š Barcodes β€” QR Code, Code 128, EAN-13, DataMatrix, PDF417
  • πŸ”’ Document Security β€” Encryption, password protection, permissions
  • 🌐 Unicode β€” Full Unicode support (UTF-8, RTL, CJK)
  • πŸ“ Certificate Support β€” PFX/P12, PEM, and file-based certificates

πŸ“¦ Installation

composer require lmoreli/laravel-pdf

Laravel Auto-Discovery

The service provider and facade are auto-registered via Laravel's package discovery.

Publish Config (Optional)

php artisan vendor:publish --tag=laravelpdf-config

πŸš€ Quick Start

Basic PDF

use LMoreli\LaravelPDF\PDFDocument;

$pdf = PDFDocument::create()
    ->setTitle('My Document')
    ->addPage()
    ->setFont('helvetica', 'B', 16)
    ->cell(0, 10, 'Hello World!', 0, 1, 'C')
    ->lineBreak(10)
    ->setFont('helvetica', '', 12)
    ->multiCell(0, 0, 'This is a paragraph of text.', align: 'J')
    ->save('/path/to/output.pdf');

From HTML

$pdf = PDFDocument::fromHTML('<h1>Hello</h1><p>World!</p>')
    ->save('/path/to/output.pdf');

Using Laravel Facade

use LMoreli\LaravelPDF\Facades\PDF;

// In a controller:
public function downloadReport()
{
    $pdf = PDF::fromView('pdfs.report', ['data' => $reportData]);
    return $pdf->response('report.pdf');
}

From Blade View

$pdf = PDFDocument::fromView('pdfs.invoice', [
    'invoice' => $invoice,
    'items' => $items,
]);

return $pdf->response('invoice.pdf', inline: false); // Force download

✍️ Digital Signatures

Single Signature

use LMoreli\LaravelPDF\Support\Certificate;
use LMoreli\LaravelPDF\Signature\SignatureAppearance;

$certificate = Certificate::fromPFX('/path/to/certificate.pfx', 'password');

$pdf = PDFDocument::create()
    ->addPage()
    ->writeHTML('<h1>Signed Document</h1>')
    ->addCertificationSignature(
        certificate: $certificate,
        appearance: SignatureAppearance::at(x: 15, y: 250, width: 80, height: 20, page: 1),
        info: [
            'Name' => 'John Doe',
            'Reason' => 'Document certification',
            'Location' => 'SΓ£o Paulo, Brazil',
        ],
    )
    ->save('/path/to/signed.pdf');

Dual Signatures (Key Feature! πŸ”‘)

use LMoreli\LaravelPDF\Support\Certificate;
use LMoreli\LaravelPDF\Signature\SignatureAppearance;
use LMoreli\LaravelPDF\Enums\CertificatePermission;

// Load two different certificates
$cert1 = Certificate::fromPFX('/path/to/cert1.pfx', 'pass1');
$cert2 = Certificate::fromPFX('/path/to/cert2.pfx', 'pass2');

$pdf = PDFDocument::create()
    ->addPage()
    ->writeHTML($contractHTML)
    ->addDualSignatures(
        firstCertificate: $cert1,
        secondCertificate: $cert2,
        firstAppearance: SignatureAppearance::at(
            x: 15, y: 250, width: 80, height: 20,
            page: 1, name: 'Signature - Company',
        ),
        secondAppearance: SignatureAppearance::at(
            x: 105, y: 250, width: 80, height: 20,
            page: 1, name: 'Signature - Director',
        ),
        firstInfo: [
            'Name' => 'Company ABC',
            'Reason' => 'Contract certification',
        ],
        secondInfo: [
            'Name' => 'John Director',
            'Reason' => 'Contract approval',
        ],
    )
    ->save('/path/to/dual-signed.pdf');

Signatures with Config Profiles

In config/laravelpdf.php:

'signatures' => [
    'certificates' => [
        'company' => [
            'type' => 'pfx',
            'path' => storage_path('certs/company.pfx'),
            'password' => env('PDF_CERT_COMPANY_PASSWORD'),
        ],
        'director' => [
            'type' => 'pfx',
            'path' => storage_path('certs/director.pfx'),
            'password' => env('PDF_CERT_DIRECTOR_PASSWORD'),
        ],
    ],
],

Usage:

$certManager = app('laravelpdf.certificates');
$companyCert = $certManager->get('company');
$directorCert = $certManager->get('director');

$pdf->addDualSignatures($companyCert, $directorCert);

πŸ“Š Barcodes & QR Codes

// QR Code
$pdf->qrCode('https://example.com', x: 10, y: 50, size: 40);

// Code 128
$pdf->code128('ABC-12345', x: 10, y: 100, w: 60, h: 15);

// EAN-13
$pdf->ean13('7891234567890', x: 10, y: 130);

// DataMatrix
$pdf->dataMatrix('Hello World', x: 10, y: 160, size: 30);

// PDF417
$pdf->pdf417('Large data content...', x: 10, y: 200);

πŸ“‹ Tables

Programmatic Table

$pdf->table(
    headers: ['Name', 'Email', 'Role'],
    rows: [
        ['Alice', 'alice@example.com', 'Admin'],
        ['Bob', 'bob@example.com', 'User'],
        ['Carol', 'carol@example.com', 'Editor'],
    ],
    widths: [50, 80, 50],
    options: [
        'headerBg' => [41, 86, 219],
        'headerColor' => [255, 255, 255],
        'altRowBg' => [240, 245, 255],
    ],
);

HTML Table

$pdf->htmlTable('
    <table border="1" cellpadding="4">
        <tr><th>Col 1</th><th>Col 2</th></tr>
        <tr><td>A</td><td>B</td></tr>
    </table>
');

🎨 Drawing & Graphics

// Lines
$pdf->line(10, 50, 200, 50);

// Rectangles
$pdf->rect(10, 60, 50, 30, 'DF');  // Draw + Fill

// Rounded rectangles
$pdf->roundedRect(10, 100, 80, 40, 5);

// Circles
$pdf->circle(50, 160, 20);

// Transparency
$pdf->setAlpha(0.5);

πŸ“„ Headers & Footers

Callback-Based

$pdf->setHeaderCallback(function ($pdf) {
    $pdf->setFont('helvetica', 'B', 10)
        ->setTextColorHex('#666666')
        ->cell(0, 10, 'My Company - Confidential', 'B', 1, 'C');
});

$pdf->setFooterCallback(function ($pdf) {
    $pdf->setFont('helvetica', 'I', 8)
        ->cell(0, 10, 'Page ' . $pdf->getPage(), 'T', 0, 'C');
});

Simple Header

$pdf->setSimpleHeader('Company Name', 'Document Title', '/path/to/logo.png', 30);

πŸ”’ Document Protection

$pdf->setProtection(
    permissions: ['print', 'copy'],
    userPass: 'user123',
    ownerPass: 'owner456',
    mode: 2,  // AES-128
);

πŸ”§ Configuration

Certificate Types

// From PFX/P12 file
$cert = Certificate::fromPFX('/path/to/cert.pfx', 'password');

// From PEM files
$cert = Certificate::fromFiles(
    certPath: '/path/to/cert.pem',
    keyPath: '/path/to/key.pem',
    password: 'key-password',
);

// From PEM strings
$cert = Certificate::fromPEM($certPEM, $keyPEM, $password);

// Validate
$cert->validate(); // Throws on invalid
echo $cert->getSubjectName();
echo $cert->isValid() ? 'Valid' : 'Expired';

Output Options

// Save to file
$pdf->save('/path/to/output.pdf');

// Get as string
$content = $pdf->toString();

// Get as base64
$base64 = $pdf->toBase64();

// Stream inline to browser
$pdf->stream('document.pdf');

// Force download
$pdf->download('document.pdf');

// Laravel HTTP response
return $pdf->response('document.pdf', inline: true);

πŸ“ Project Structure

LaravelPDF/
β”œβ”€β”€ composer.json
β”œβ”€β”€ config/
β”‚   └── laravelpdf.php           # Laravel config
β”œβ”€β”€ src/
β”‚   β”œβ”€β”€ PDFDocument.php          # Main class (fluent API)
β”‚   β”œβ”€β”€ LaravelPDFServiceProvider.php
β”‚   β”œβ”€β”€ Enums/
β”‚   β”‚   β”œβ”€β”€ CertificatePermission.php
β”‚   β”‚   β”œβ”€β”€ OutputDestination.php
β”‚   β”‚   β”œβ”€β”€ PageFormat.php
β”‚   β”‚   β”œβ”€β”€ PageOrientation.php
β”‚   β”‚   β”œβ”€β”€ SignatureType.php
β”‚   β”‚   └── Unit.php
β”‚   β”œβ”€β”€ Exceptions/
β”‚   β”‚   β”œβ”€β”€ PDFException.php
β”‚   β”‚   └── SignatureException.php
β”‚   β”œβ”€β”€ Facades/
β”‚   β”‚   └── PDF.php
β”‚   β”œβ”€β”€ Signature/
β”‚   β”‚   β”œβ”€β”€ DigitalSignature.php       # Signature config DTO
β”‚   β”‚   β”œβ”€β”€ IncrementalPDFSigner.php   # Core signing engine
β”‚   β”‚   β”œβ”€β”€ MultiSignatureManager.php  # Multi-sig orchestrator
β”‚   β”‚   └── SignatureAppearance.php    # Visual positioning
β”‚   β”œβ”€β”€ Support/
β”‚   β”‚   β”œβ”€β”€ Certificate.php            # Certificate wrapper
β”‚   β”‚   β”œβ”€β”€ CertificateManager.php     # Config-based profiles
β”‚   β”‚   β”œβ”€β”€ ExtendedTCPDF.php          # TCPDF bridge
β”‚   β”‚   └── TimestampAuthority.php     # RFC 3161 TSA
β”‚   └── Traits/
β”‚       β”œβ”€β”€ ManagesBarcodes.php
β”‚       β”œβ”€β”€ ManagesContent.php
β”‚       β”œβ”€β”€ ManagesFonts.php
β”‚       β”œβ”€β”€ ManagesHeaders.php
β”‚       β”œβ”€β”€ ManagesHTML.php
β”‚       β”œβ”€β”€ ManagesImages.php
β”‚       β”œβ”€β”€ ManagesMetadata.php
β”‚       β”œβ”€β”€ ManagesPages.php
β”‚       β”œβ”€β”€ ManagesSignatures.php
β”‚       └── ManagesTables.php
β”œβ”€β”€ examples/
β”‚   β”œβ”€β”€ 01_basic.php
β”‚   β”œβ”€β”€ 02_html_to_pdf.php
β”‚   β”œβ”€β”€ 03_dual_signatures.php
β”‚   └── 04_laravel_controller.php
└── tests/

πŸ”¬ How Multi-Signature Works

LaravelPDF implements multiple digital signatures using PDF Incremental Updates, the standard mechanism defined in the PDF specification (ISO 32000):

  1. First Signature (Certification): The initial PDF is generated and signed normally using PKCS#7 detached signatures. This sets the DocMDP permissions.

  2. Second Signature (Approval): The already-signed PDF is appended with an incremental update containing:

    • A new signature field (widget annotation)
    • A new signature dictionary with ByteRange placeholder
    • Updated AcroForm fields array
    • Updated page annotations
    • New xref table and trailer pointing to the previous one
  3. Integrity: Each signature covers all bytes of the PDF up to that point (except its own signature placeholder), so previous signatures remain valid.

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Original PDF Content   β”‚ ← Signed by Certificate 1
β”‚ %%EOF                  β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ Incremental Update     β”‚ ← Signed by Certificate 2
β”‚ (new sig field, xref)  β”‚
β”‚ %%EOF                  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

πŸ“‹ Requirements

  • PHP 8.3 or higher
  • OpenSSL extension
  • Laravel 10, 11, or 12 (for Laravel integration)

πŸ“„ License

MIT License. See LICENSE for details.

🀝 Credits

  • Built on top of TCPDF
  • Created by Lucas Moreli

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors