PHPackages                             pdfparse/flatpdf - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [PDF &amp; Document Generation](/categories/documents)
4. /
5. pdfparse/flatpdf

ActiveLibrary[PDF &amp; Document Generation](/categories/documents)

pdfparse/flatpdf
================

A flat, zero-dependency PHP PDF writer built for speed and large documents.

v0.1.5(3mo ago)09MITPHPPHP ^8.2CI passing

Since Feb 11Pushed 3mo agoCompare

[ Source](https://github.com/pdfparse/flatpdf)[ Packagist](https://packagist.org/packages/pdfparse/flatpdf)[ Docs](https://flatpdf.pdfparse.co/)[ RSS](/packages/pdfparse-flatpdf/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (4)Dependencies (2)Versions (7)Used By (0)

FlatPdf
=======

[](#flatpdf)

A flat, zero-dependency PHP PDF writer built for speed and large documents.

Built by [PdfParse](https://pdfparse.co). [Documentation](https://flatpdf.pdfparse.co/)

Why FlatPdf?
------------

[](#why-flatpdf)

DOMPDF and mPDF are great for converting HTML/CSS to PDF, but they choke on large table-heavy documents — slow rendering, high memory usage, and unpredictable page-break behavior inside tables.

FlatPdf writes PDF operators directly. No HTML parsing, no CSS rendering engine, no external binaries. Just PHP generating PDF bytes.

**Good at:**

- Long reports with hundreds/thousands of table rows
- Consistent, predictable table rendering across pages
- Auto-sizing columns based on content
- Repeated headers on page breaks
- JPEG images with Laravel Storage/S3 support
- Stream compression (FlateDecode) — 60-70% smaller files
- Low memory usage — streams page-by-page
- Fast — generates a 12-page financial report in milliseconds

**Not for:**

- Pixel-perfect HTML/CSS reproduction
- Complex layouts (floats, grids, absolute positioning)
- Custom font embedding (uses the 14 standard PDF fonts)
- PNG/GIF images (JPEG only)

Installation
------------

[](#installation)

```
composer require pdfparse/flatpdf
```

Requires PHP 8.2+.

Quick Start
-----------

[](#quick-start)

```
use PdfParse\FlatPdf\FlatPdf;
use PdfParse\FlatPdf\Style;

$pdf = FlatPdf::make();

$pdf->h1('Monthly Report');
$pdf->text('Generated on ' . date('Y-m-d'));

$pdf->table(
    headers: ['Product', 'Revenue', 'Margin'],
    rows: [
        ['Widget A', '$12,400', '34%'],
        ['Widget B', '$8,200', '41%'],
        ['Widget C', '$15,800', '28%'],
    ],
    options: [
        'columnAligns' => ['left', 'right', 'right'],
    ]
);

$pdf->save('report.pdf');
```

Style Presets
-------------

[](#style-presets)

```
// Dense layout — maximum data per page
$pdf = FlatPdf::make(Style::compact());

// A4 paper
$pdf = FlatPdf::make(Style::a4());

// Landscape — great for wide tables
$pdf = FlatPdf::make(Style::landscape());

// Landscape + compact — maximum columns
$pdf = FlatPdf::make(Style::landscapeCompact());

// Custom style
$pdf = FlatPdf::make(Style::make(
    headerText: 'Acme Corp — Confidential',
    footerText: 'Internal Use Only',
    tableHeaderBg: [0.18, 0.33, 0.59],
    tableFontSize: 7,
    showPageNumbers: true,
));
```

Tables
------

[](#tables)

### Basic table

[](#basic-table)

```
$pdf->table(
    ['Name', 'Department', 'Salary'],
    [
        ['Alice', 'Engineering', '$150,000'],
        ['Bob', 'Sales', '$120,000'],
    ],
    [
        'columnAligns' => ['left', 'left', 'right'],
        'striped' => true,
        'repeatHeader' => true,
    ]
);
```

### From associative arrays (Eloquent-friendly)

[](#from-associative-arrays-eloquent-friendly)

```
$users = User::all()->toArray();

$pdf->dataTable($users, [
    'columns' => ['name', 'email', 'created_at', 'subscription_amount'],
    'columnLabels' => [
        'name' => 'Full Name',
        'created_at' => 'Joined',
        'subscription_amount' => 'MRR',
    ],
    'formatters' => [
        'created_at' => fn($v) => date('M j, Y', strtotime($v)),
        'subscription_amount' => fn($v) => '$' . number_format($v / 100, 2),
    ],
    'columnAligns' => ['left', 'left', 'center', 'right'],
]);
```

### Table options

[](#table-options)

OptionTypeDescription`columnWidths``float[]`Explicit column widths in points`columnAligns``string[]``'left'`, `'right'`, or `'center'` per column`columnMinWidths``float[]`Minimum widths per column`maxColumnWidth``float`Cap any single column width`fontSize``float`Override table font size`striped``bool`Alternating row background colors`repeatHeader``bool`Repeat header row after page breaksText &amp; Headings
-------------------

[](#text--headings)

```
$pdf->h1('Section Title');        // Large heading with underline
$pdf->h2('Subsection');           // Medium heading
$pdf->h3('Sub-subsection');       // Small heading

$pdf->text('Regular paragraph text with automatic word-wrapping.');
$pdf->bold('Bold text.');
$pdf->italic('Italic text.');
$pdf->code('Monospaced text.');

$pdf->hr();                       // Horizontal rule
$pdf->space(20);                  // Vertical space (points)
$pdf->pageBreak();                // Force new page
```

Images
------

[](#images)

JPEG images are embedded directly as DCTDecode streams — no re-encoding, no GD dependency.

### From a file path

[](#from-a-file-path)

```
$pdf->image('/path/to/photo.jpg', width: 300);
```

### From raw bytes

[](#from-raw-bytes)

```
$jpeg = file_get_contents('https://example.com/photo.jpg');
$pdf->imageFromString($jpeg, width: 250);
```

### From a Laravel filesystem disk (S3, etc.)

[](#from-a-laravel-filesystem-disk-s3-etc)

```
$pdf->imageFromDisk(Storage::disk('s3'), 'reports/chart.jpg', width: 400);

// Also works with any disk: local, GCS, etc.
$pdf->imageFromDisk(Storage::disk('public'), 'images/logo.jpg', width: 150);
```

`imageFromDisk()` accepts any object with a `get(string $path): ?string` method — fully compatible with Laravel's `Storage::disk()` without requiring Laravel as a dependency.

### Sizing and alignment

[](#sizing-and-alignment)

```
// Specify width — height auto-calculated to preserve aspect ratio
$pdf->image('photo.jpg', width: 200);

// Specify height — width auto-calculated
$pdf->image('photo.jpg', height: 150);

// Specify both (may distort)
$pdf->image('photo.jpg', width: 200, height: 100);

// No dimensions — renders at natural size (1 pixel = 1 point at 72 DPI)
$pdf->image('photo.jpg');

// Alignment
$pdf->image('photo.jpg', width: 200, options: ['align' => 'center']);
$pdf->image('photo.jpg', width: 200, options: ['align' => 'right']);

// Custom DPI (default 72)
$pdf->image('photo.jpg', options: ['dpi' => 150]);
```

Images auto-clamp to the content area and trigger page breaks when they don't fit.

Page Control
------------

[](#page-control)

```
// Check remaining space before adding content
if ($pdf->getRemainingSpace() < 100) {
    $pdf->pageBreak();
}

// Get current page number
echo $pdf->getCurrentPage();
```

Saving to S3 / Laravel Filesystems
----------------------------------

[](#saving-to-s3--laravel-filesystems)

`output()` returns the raw PDF bytes — pass them directly to any filesystem or HTTP response:

```
// Save to S3
Storage::disk('s3')->put('reports/monthly.pdf', $pdf->output());

// Save to any disk
Storage::disk('gcs')->put('exports/report.pdf', $pdf->output());

// Return as HTTP download
return response($pdf->output())
    ->header('Content-Type', 'application/pdf')
    ->header('Content-Disposition', 'attachment; filename="report.pdf"');

// Inline browser preview
return response($pdf->output())
    ->header('Content-Type', 'application/pdf')
    ->header('Content-Disposition', 'inline; filename="report.pdf"');

// Save locally and to S3
$pdf->save(storage_path('app/report.pdf'));
Storage::disk('s3')->put('reports/report.pdf', $pdf->output());
```

### Full Laravel example

[](#full-laravel-example)

```
use PdfParse\FlatPdf\FlatPdf;
use Illuminate\Support\Facades\Storage;

public function generateReport()
{
    $pdf = FlatPdf::make();

    // Load images from S3
    $pdf->imageFromDisk(Storage::disk('s3'), 'assets/logo.jpg', width: 150);

    $pdf->h1('Monthly Report');
    $pdf->dataTable(Order::all()->toArray(), [
        'columns' => ['id', 'customer', 'total', 'created_at'],
        'formatters' => [
            'total' => fn($v) => '$' . number_format($v / 100, 2),
        ],
    ]);

    // Save back to S3
    Storage::disk('s3')->put('reports/monthly.pdf', $pdf->output());

    return response($pdf->output())
        ->header('Content-Type', 'application/pdf');
}
```

Style Reference
---------------

[](#style-reference)

All `Style` properties with defaults:

```
Style::make(
    // Page dimensions (points: 72 points = 1 inch)
    pageWidth: 612,          // Letter width (A4: 595.28)
    pageHeight: 792,         // Letter height (A4: 841.89)
    marginTop: 60,
    marginBottom: 60,
    marginLeft: 50,
    marginRight: 50,

    // Body text
    fontFamily: 'Helvetica', // Also: 'Times-Roman', 'Courier'
    fontSize: 9,
    lineHeight: 1.4,
    textColor: [0.2, 0.2, 0.2],

    // Headings
    h1Size: 20,
    h2Size: 15,
    h3Size: 12,
    headingSpaceBefore: 16,
    headingSpaceAfter: 6,
    headingColor: [0.1, 0.1, 0.1],

    // Tables
    tableFontSize: 8,
    tableCellPadding: 5,
    tableLineWidth: 0.5,
    tableHeaderBg: [0.22, 0.40, 0.65],
    tableHeaderColor: [1.0, 1.0, 1.0],
    tableHeaderFont: 'Helvetica-Bold',
    tableRowBg: [1.0, 1.0, 1.0],
    tableAltRowBg: [0.95, 0.96, 0.98],
    tableBorderColor: [0.78, 0.80, 0.83],
    tableStriped: true,
    tableRepeatHeaderOnNewPage: true,

    // Header / Footer
    showPageNumbers: true,
    pageNumberFormat: 'Page {page} of {pages}',
    headerFooterFontSize: 7,
    headerFooterColor: [0.5, 0.5, 0.5],
    headerText: '',
    footerText: '',

    // Spacing
    paragraphSpacing: 8,

    // Compression
    compress: true,           // FlateDecode stream compression (default: on)
);
```

Colors are RGB arrays with values from 0.0 to 1.0.

Compression
-----------

[](#compression)

Stream compression is enabled by default. Content streams are compressed with `gzcompress()` (zlib deflate), which typically reduces file size by 60-70% on table-heavy documents.

```
// Compression is on by default — nothing to do
$pdf = FlatPdf::make();

// Disable if you need readable PDF internals for debugging
$pdf = FlatPdf::make(Style::make(compress: false));
```

JPEG image streams are never double-compressed — they use DCTDecode only.

Available Fonts
---------------

[](#available-fonts)

FlatPdf uses the 14 standard PDF fonts (no embedding required, supported by every PDF reader):

- **Helvetica** (+ Bold, Oblique, BoldOblique)
- **Times-Roman** (+ Bold, Italic, BoldItalic)
- **Courier** (+ Bold, Oblique, BoldOblique)

Aliases work too: `'arial'`, `'sans-serif'`, `'serif'`, `'monospace'`.

Testing &amp; Static Analysis
-----------------------------

[](#testing--static-analysis)

FlatPdf uses [Pest](https://pestphp.com) for testing and [PHPStan](https://phpstan.org) at max level for static analysis.

```
# Install dev dependencies
composer install

# Run the test suite
composer test

# Run tests with coverage
composer test:coverage

# Run PHPStan static analysis
composer analyse

# Run both tests and analysis
composer qa
```

Releasing a New Version
-----------------------

[](#releasing-a-new-version)

1. Update your code and merge to `main`
2. Tag the release with a semantic version: ```
    git tag v1.0.0
    git push origin v1.0.0
    ```
3. The GitHub Actions workflow will automatically notify Packagist
4. The new version will be available via `composer require pdfparse/flatpdf` within minutes

### Version Guidelines

[](#version-guidelines)

- **Patch** (`v1.0.1`) — Bug fixes, no breaking changes
- **Minor** (`v1.1.0`) — New features, backwards compatible
- **Major** (`v2.0.0`) — Breaking changes to the public API

License
-------

[](#license)

MIT

###  Health Score

34

—

LowBetter than 77% of packages

Maintenance82

Actively maintained with recent releases

Popularity4

Limited adoption so far

Community2

Small or concentrated contributor base

Maturity41

Maturing project, gaining track record

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~0 days

Total

6

Last Release

93d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/1dde185ef7df86d8d7098c703cd210705237013dd04a2e301e8828a4308a90b8?d=identicon)[wbridgett](/maintainers/wbridgett)

---

Tags

pdfPDF GENERATORtablesreportsno-dependenciespdf-writer

###  Code Quality

TestsPest

Static AnalysisPHPStan

Type Coverage Yes

### Embed Badge

![Health badge](/badges/pdfparse-flatpdf/health.svg)

```
[![Health](https://phpackages.com/badges/pdfparse-flatpdf/health.svg)](https://phpackages.com/packages/pdfparse-flatpdf)
```

###  Alternatives

[geekcom/phpjasper

A PHP report generator

493258.4k1](/packages/geekcom-phpjasper)[robregonm/yii2-pdf

Yii 2 PDF Response Formatter

4647.5k1](/packages/robregonm-yii2-pdf)[geekcom/phpjasper-laravel

A PHP report generator

3341.9k](/packages/geekcom-phpjasper-laravel)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
