From 7fcf890dd23a88903fbeae075a853b645e611e50 Mon Sep 17 00:00:00 2001 From: Dan Krieger Date: Tue, 27 Oct 2026 23:12:51 -0700 Subject: [PATCH] docs: refresh status for 0.2, and credit php-pdf and php-docx - READMEs and the site said v0.1 and listed SVG, encryption and bookmarks as missing; all three have shipped. The status list now names only what is still unsupported, and the root feature list covers what 0.2 added. - "No HTML parser" no longer holds now that Markup::html() reads HTML fragments; the README says templates aren't parsed as HTML instead. - The CHANGELOG's SVG entry no longer waits on a php-pdf release, and the multi-format spec marks HTML input and bookmarks as done. - A "Built on" section in the root README, credits in each package README, the site home and guide intro, and a php-docx entry in NOTICE.md give Denis Skripchenko's php-pdf and php-docx their due. Co-Authored-By: Claude Opus 5.5 --- CHANGELOG.md | 2 +- NOTICE.md | 7 ++++ README.md | 34 +++++++++++++++---- .../2026-10-19-multi-format-export-design.md | 4 +-- packages/core/README.md | 17 +++++++--- packages/docx/README.md | 3 ++ packages/laravel/README.md | 7 +++- site/src/README.md | 6 +++- site/src/guide/README.md | 6 +++- 9 files changed, 68 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d6f096e..18697c5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -43,7 +43,7 @@ Migration from v0.1: - Laravel: `->docx()` on a `PendingDocument` (needs `papercut/docx`, suggested; throws `MissingExporter` without it), and preview routes accept `?format=docx`. - PDF templates: `Page::template(PdfPage::file($path))` makes a page the size of a page of an existing PDF and draws that page behind everything else on it, for certificates and letterheads. `PdfPage::file()` and `PdfPage::bytes()` take a page number and a password, and also draw a PDF page anywhere in the content, sized like an image. The page is drawn as it is, with its text and vector artwork intact, stored once however many pages use it, and encrypted with the document. A missing file, bytes that aren't a PDF, a wrong password or a missing page throw `PdfUnreadable`. Backends opt in through a new `PdfPageLoader` interface. In Blade, `` (or `template-data`, with `template-page` and `template-password`) and ``. HTML exports leave PDF pages out and report them as dropped. - Placed content: `Page::overlay(Place::at($x, $y, ...))` puts content at a point on the page, over everything else, on every page. `x` and `y` are lengths from the top-left corner or percentages of the page, `anchor()` picks the point of the content placed there, and `width()` takes a length or a percentage of the page width. In Blade, `` in the page's `overlay` slot. HTML exports position places with CSS. -- SVG: `Svg::file($path)`, `Svg::fromString($markup)` and `` (or markup inside the tag) draw an SVG as vector paths through php-pdf's SVG renderer, sized like an image from its width, height or view box, placed by `preserveAspectRatio` and clipped to its box. `fill-rule="evenodd"` keeps the holes in rings, letters and stars. SVG `` is set in the document's embedded fonts, never a base-14 one. Untrusted SVG is checked first: no external files, DTDs or entity declarations, scripts and event handlers removed, and sources over 10 MB or over a million units of drawing work (about a second and a half) refused. Elements not drawn yet, like `` and ``, are named in a warning. HTML exports show an `` with a `data:` URI and the SVG's `` as alt text. Needs the php-pdf release with even-odd fill and the SVG group and gradient fixes. +- SVG: `Svg::file($path)`, `Svg::fromString($markup)` and `<x-doc::svg src|data>` (or markup inside the tag) draw an SVG as vector paths through php-pdf's SVG renderer, sized like an image from its width, height or view box, placed by `preserveAspectRatio` and clipped to its box. `fill-rule="evenodd"` keeps the holes in rings, letters and stars. SVG `<text>` is set in the document's embedded fonts, never a base-14 one. Untrusted SVG is checked first: no external files, DTDs or entity declarations, scripts and event handlers removed, and sources over 10 MB or over a million units of drawing work (about a second and a half) refused. Elements not drawn yet, like `<clipPath>` and `<filter>`, are named in a warning. HTML exports show an `<img>` with a `data:` URI and the SVG's `<title>` as alt text. Uses the even-odd fill and the SVG group and gradient fixes in php-pdf 1.26. - QR codes: `QrCode::make($value)` and `<x-doc::qr-code value="…">` draw a QR code as vector squares in the text color, square, 25mm by default, with error-correction level and quiet zone. Encoded with php-pdf's encoder (up to 213 bytes at Medium) behind a `QrEncoder` interface, or from your own grid with `QrCode::fromModules()`. HTML exports draw an inline SVG. - Styled QR codes: `shape()`, `eyes()`, `eyeColor()` and `gradient()` on `QrCode`, and `shape`, `eyes`, `eye-color`, `gradient` and `gradient-type` on `<x-doc::qr-code>`, give a code dots or rounded modules, dot or pointy eyes, a separate eye color and a linear or radial gradient. Dots and rounded modules raise the error correction to Quartile, unless it is High, so they scan reliably. Papercut encodes the modules and the optional `bacon/bacon-qr-code` package draws them, through the SVG support; styling without it throws `MissingPackage` with the command to install it. `BaconQrEncoder` uses Bacon's encoder for text up to 2,953 bytes. HTML exports show a styled code as an SVG image. - Encryption: `PdfOptions(encryption: Encryption::password('secret'))` and Laravel's `->encrypt()` write AES-256 PDFs that open only with the password. `Encryption` sets print, copy, modify, annotate and assemble permissions; a missing owner password is generated at random so the permissions hold, and screen-reader access is always allowed. diff --git a/NOTICE.md b/NOTICE.md index d540d04..642b30c 100644 --- a/NOTICE.md +++ b/NOTICE.md @@ -25,3 +25,10 @@ Tailwind CSS v3.4.17 default palette by `packages/core/tools/generate-palette.ph object writing, font subsetting and text shaping. It is used only through `Papercut\Backend\PhpPdf\*`. - License: MIT (distributed via Composer, not bundled in this repository) + +## dskripchenko/php-docx + +`papercut/docx` depends on [`dskripchenko/php-docx`](https://github.com/dskripchenko/php-docx) (`^1.17`) to write Word files. It is used only through +`Papercut\Docx\*`. + +- License: MIT (distributed via Composer, not bundled in this repository) diff --git a/README.md b/README.md index 01a6092..030ad38 100644 --- a/README.md +++ b/README.md @@ -12,12 +12,12 @@ Blade components that compile to a real layout engine, not HTML. Papercut is a PDF library for modern PHP. You write `<x-doc::*>` tags in Blade; they become a tree of typed, immutable components with flexbox layout and page-aware pagination, and a pure-PHP engine turns -that tree into a PDF. There is no browser, no HTML parser and no CSS cascade. The tag vocabulary is small, -strict and ours, and a typo in an attribute is an error that names the tag, not a silently wrong page. +that tree into a PDF, written by Denis Skripchenko's [php-pdf](https://github.com/dskripchenko/php-pdf) (see [Built on](#built-on)). There is no browser and no CSS cascade: templates are not parsed as HTML. The tag vocabulary +is small, strict and ours, and a typo in an attribute is an error that names the tag, not a silently wrong page. **[Documentation](https://one-learning-community.github.io/papercut/)** · [Guide](https://one-learning-community.github.io/papercut/guide/) · [Laravel](https://one-learning-community.github.io/papercut/laravel/) · [Reference](https://one-learning-community.github.io/papercut/reference/blade-tags.html) · [Examples](https://one-learning-community.github.io/papercut/examples.html) -> **Status: v0.1, pre-release.** Not yet published to Packagist. See [Status](#status) below. +> **Status: 0.2, pre-release.** Not yet published to Packagist. See [Status](#status) below. ## A Blade document @@ -61,6 +61,11 @@ return $invoice->docx()->download('invoice.docx'); // Word (experimental), with - Keep-together and keep-with-next, so a heading is not stranded at the bottom of a page. - Page numbers with a page count: `Page {page} of {total}`. - Real fonts: every document embeds fonts (Inter is bundled; register your own families), with subsetting and fallback families. The 14 standard PDF fonts are not used. +- Lists, links, images (PNG and JPEG; WebP, GIF, BMP and AVIF with `ext-gd`), SVG drawn as vectors, and QR codes, plain or styled. +- Rich text from your data: `<x-doc::markup>` turns an HTML fragment or plain text (say, from a rich-text editor) into Papercut nodes. +- PDF templates and placed content: draw a page of an existing PDF behind yours and place text at a point on it, for certificates and letterheads. +- Bookmarks built from headings, and AES-256 encryption with permissions. +- HTML export from the same template, and Word export (experimental) with `papercut/docx`. - Decorators: ready-made card, callout, blockquote, section, field, choice list, key-value list and badge, restyled from the theme (`<x-doc::callout type="warning">`). - Composition: your own Blade components can contain `<x-doc::*>` tags (see `examples/laravel/views/components`). - Strict, readable errors for unknown attributes, bad values and invalid structure. @@ -77,6 +82,19 @@ return $invoice->docx()->download('invoice.docx'); // Word (experimental), with 4. A thin backend adapter turns the display list into PDF objects via [`dskripchenko/php-pdf`](https://github.com/dskripchenko/php-pdf). +## Built on + +Papercut stands on two libraries by [Denis Skripchenko](https://github.com/dskripchenko), and wouldn't exist without +them. + +- **[`dskripchenko/php-pdf`](https://github.com/dskripchenko/php-pdf)** writes every PDF Papercut makes. Papercut decides where things go on the page; php-pdf turns + that into a PDF. It parses, subsets and embeds the fonts and applies their kerning, embeds the images, draws SVG, + encodes QR codes, encrypts, and reads existing PDFs for page templates. It can also sign and merge the result. + When Papercut needed more, such as even-odd fills in SVG and faster font loading, the maintainer added it upstream. +- **[`dskripchenko/php-docx`](https://github.com/dskripchenko/php-docx)** writes Papercut's Word files. + +Both are pure PHP and MIT licensed. If Papercut is useful to you, they deserve a star too. + ## Not using Laravel? `papercut/core` is framework-agnostic. Build the same documents with a fluent, immutable PHP API; see @@ -99,12 +117,13 @@ The guide lives at [one-learning-community.github.io/papercut](https://one-learn ## Status -v0.1, pre-release, not yet published to Packagist. No performance or compatibility benchmarks have been +0.2, pre-release, not yet published to Packagist. No performance or compatibility benchmarks have been published; do not assume any. Requires PHP 8.4+ and Laravel 12 or 13 (CI tests against both). -Not in v0.1: HarfBuzz shaping (the backend exposes no glyph-level drawing API; its built-in shaping covers kerning and ligatures within runs), right-to-left and bidirectional paragraph layout, hyphenation, -Knuth-Plass line breaking, multi-column text flow, SVG, AcroForms, digital signatures, PDF/A, encryption, -editing or merging existing PDFs, table of contents and bookmarks. Bookmarks are first in line for v0.2. +Not supported yet: HarfBuzz shaping (the backend exposes no glyph-level drawing API; its built-in shaping covers kerning +and ligatures within runs), right-to-left and bidirectional paragraph layout, hyphenation, Knuth-Plass line breaking, +multi-column text flow, form filling (AcroForms), PDF/A and tables of contents. Signing and merging aren't built in, but +php-pdf can do both to a Papercut PDF; see [Passwords, signatures and merging](https://one-learning-community.github.io/papercut/guide/passwords-and-signatures.html). ## Licenses @@ -112,3 +131,4 @@ editing or merging existing PDFs, table of contents and bookmarks. Bookmarks are - Bundled Inter font: SIL Open Font License 1.1 (`packages/core/resources/fonts/Inter/LICENSE.txt`). - Color palette derived from Tailwind CSS: MIT. - PDF backend [`dskripchenko/php-pdf`](https://github.com/dskripchenko/php-pdf): MIT. +- Word backend [`dskripchenko/php-docx`](https://github.com/dskripchenko/php-docx): MIT. diff --git a/docs/superpowers/specs/2026-10-19-multi-format-export-design.md b/docs/superpowers/specs/2026-10-19-multi-format-export-design.md index afd4165..a1402f8 100644 --- a/docs/superpowers/specs/2026-10-19-multi-format-export-design.md +++ b/docs/superpowers/specs/2026-10-19-multi-format-export-design.md @@ -381,8 +381,8 @@ v0.1 was never published to Packagist, so there are no deprecation shims. The ch ## 12. Future work (the design leaves room) - *Done:* font sources (Google Fonts and others in HTML export), see `2026-10-19-html-font-sources-design.md`. -- **Raw HTML input** (`<x-doc::html>`): convert HTML into Papercut nodes once, when the tree is built, using php-pdf's `HtmlParser` and CSS cascade as the front end plus a translator from its elements (`Paragraph`, `Run`, `Table`, `ListNode`) to `Text`/`Span`/`Table`. The result works in every format. It reverses v0.1's "no HTML parser" stance, so it needs its own spec. It would likely also bring a list node. +- *Done as `Markup::html()` and `<x-doc::markup>`, see `2026-10-19-html-input-design.md`:* **Raw HTML input** (`<x-doc::html>`): convert HTML into Papercut nodes once, when the tree is built, using php-pdf's `HtmlParser` and CSS cascade as the front end plus a translator from its elements (`Paragraph`, `Run`, `Table`, `ListNode`) to `Text`/`Span`/`Table`. The result works in every format. It reverses v0.1's "no HTML parser" stance, so it needs its own spec. It would likely also bring a list node. - **Font embedding in DOCX** (upstream). - **An editable DOCX mode** (fewer layout tables, more native paragraphs), as a contribution. - **Page background in DOCX** as an anchored shape behind the text. -- **Bookmarks** (moved from v0.2 to v0.3), mapping naturally to PDF outlines, DOCX heading styles and HTML anchors. +- *Done in 0.2 for PDF (DOCX and HTML anchors not yet):* **Bookmarks** (moved from v0.2 to v0.3), mapping naturally to PDF outlines, DOCX heading styles and HTML anchors. diff --git a/packages/core/README.md b/packages/core/README.md index 1a4c906..a7e5861 100644 --- a/packages/core/README.md +++ b/packages/core/README.md @@ -7,11 +7,11 @@ Component-based, page-aware PDF layout engine for modern PHP. Build documents from typed, immutable components with flexbox-style layout and page-aware pagination; a pure-PHP engine turns the tree into a PDF. -There is no browser, no HTML parser and no CSS cascade. +There is no browser and no CSS cascade. **Documentation:** [one-learning-community.github.io/papercut](https://one-learning-community.github.io/papercut/guide/) -> **Status: v0.1, pre-release.** Not yet published to Packagist. Using Laravel? See +> **Status: 0.2, pre-release.** Not yet published to Packagist. Using Laravel? See > [`papercut/laravel`](../laravel) for the Blade front end of this same engine. ## Requirements @@ -518,8 +518,8 @@ $options = new PdfOptions(fonts: $fonts); ``` The backend shapes and reorders text within a single drawn run (kerning, ligatures). Right-to-left and bidirectional -paragraph layout (direction, reordering across line breaks, direction-aware alignment) is unsupported and untested -in v0.1, and so is HarfBuzz shaping. +paragraph layout (direction, reordering across line breaks, direction-aware alignment) is not supported yet, and +neither is HarfBuzz shaping. ## Fonts in HTML @@ -634,7 +634,14 @@ $document = Document::make(Page::a4()->margin(mm(20))->body(Column::make($text)- Runnable scripts in [`examples/core`](../../examples/core): an invoice, a multi-page transcript and a report. +## Credits + +Every PDF Papercut makes is written by [`dskripchenko/php-pdf`](https://github.com/dskripchenko/php-pdf), by Denis Skripchenko. Papercut lays out and paginates the +document; php-pdf parses, subsets and embeds the fonts, applies kerning, embeds images, draws SVG, encodes QR codes, +encrypts, and reads the PDFs used as page templates. When Papercut needed more, the maintainer added it upstream. +Thank you. + ## License MIT. Bundled Inter font: SIL Open Font License 1.1 (`resources/fonts/Inter/LICENSE.txt`). -Color palette derived from Tailwind CSS (MIT). See [`NOTICE.md`](../../NOTICE.md). +Color palette derived from Tailwind CSS (MIT). php-pdf: MIT. See [`NOTICE.md`](../../NOTICE.md). diff --git a/packages/docx/README.md b/packages/docx/README.md index ceaa62e..f8bf86f 100644 --- a/packages/docx/README.md +++ b/packages/docx/README.md @@ -8,6 +8,9 @@ Word (.docx) export for [Papercut](../../README.md) documents: the same document you render as a PDF, as a file people can open and edit in Word, LibreOffice or Google Docs. +The Word file is written by [`dskripchenko/php-docx`](https://github.com/dskripchenko/php-docx), by Denis Skripchenko. Papercut maps its layout onto +Word's paragraphs and tables; php-docx writes the file. Thank you. + **Documentation:** [Word export](https://one-learning-community.github.io/papercut/guide/word-export.html) > **Status: experimental, pre-release. Not yet published to Packagist.** Word, LibreOffice and Google Docs lay out diff --git a/packages/laravel/README.md b/packages/laravel/README.md index f0fd777..d9cd5f1 100644 --- a/packages/laravel/README.md +++ b/packages/laravel/README.md @@ -9,7 +9,7 @@ Blade components for [Papercut](../../README.md): `<x-doc::*>` tags compile into **Documentation:** [one-learning-community.github.io/papercut/laravel](https://one-learning-community.github.io/papercut/laravel/) -> **Status: v0.1, pre-release. Not yet published to Packagist.** +> **Status: 0.2, pre-release. Not yet published to Packagist.** ## Install @@ -474,3 +474,8 @@ Set `PAPERCUT_PREVIEW=true`. Only in the `local` environment, `GET /_papercut` l - `Papercut\Exception\LayoutOverflow`: a block cannot fit on a page even after every permitted split. Controlled by `overflow`: throw (default in `local`/`testing`) or clip. Blade wraps exceptions thrown while rendering a view in `Illuminate\View\ViewException`; the original is its `getPrevious()`. + +## Credits + +The Blade tags compile to [`papercut/core`](../core), and every PDF is written by [`dskripchenko/php-pdf`](https://github.com/dskripchenko/php-pdf), by Denis +Skripchenko. Word output uses his [`dskripchenko/php-docx`](https://github.com/dskripchenko/php-docx). Thank you to both projects. diff --git a/site/src/README.md b/site/src/README.md index 58a6a8c..1d32947 100644 --- a/site/src/README.md +++ b/site/src/README.md @@ -27,7 +27,7 @@ features: details: Render the same document as a PDF to download, or as HTML to show in the browser. - title: No headless browser details: Pure PHP from start to finish. There's no Chrome or wkhtmltopdf to install on your servers. -footer: MIT licensed. Inter is bundled under the SIL Open Font License. +footer: MIT licensed. PDFs are written by php-pdf and Word files by php-docx, both by Denis Skripchenko. Inter is bundled under the SIL Open Font License. --- Papercut builds PDFs from Blade or plain PHP, with page breaks that just work. Here's a transcript as a Blade view: @@ -62,3 +62,7 @@ return Papercut::view('pdf.transcript', compact('student', 'courses'))->pdf()->d ``` If the course list runs to ten pages, the header row repeats at the top of each one and no row gets cut in half at a page break. + +## Built on php-pdf and php-docx + +Papercut decides where everything goes on the page. Writing the file is the work of two libraries by [Denis Skripchenko](https://github.com/dskripchenko): [php-pdf](https://github.com/dskripchenko/php-pdf) writes every PDF, with its fonts, images, SVG, QR codes and encryption, and [php-docx](https://github.com/dskripchenko/php-docx) writes the Word files. Both are pure PHP and MIT licensed, and Papercut wouldn't exist without them. diff --git a/site/src/guide/README.md b/site/src/guide/README.md index 9672e56..2c4461e 100644 --- a/site/src/guide/README.md +++ b/site/src/guide/README.md @@ -26,9 +26,13 @@ Papercut sits in between. Its layout model is a small subset of flexbox, so it s The HTML exporter works from the same tree, which is how one document can produce both formats. +## Built on php-pdf + +Papercut does the layout; [php-pdf](https://github.com/dskripchenko/php-pdf), by Denis Skripchenko, writes the PDF. It parses, subsets and embeds the fonts and applies their kerning, embeds the images, draws SVG, encodes QR codes, encrypts, and reads existing PDFs for page templates. Its sibling [php-docx](https://github.com/dskripchenko/php-docx) writes Papercut's Word files. When Papercut needed more from php-pdf, such as even-odd fills in SVG and faster font loading, the maintainer added it upstream. + ## Current limitations -Papercut is at v0.1, and a few things aren't supported yet: +Papercut is at 0.2, and a few things aren't supported yet: - complex text shaping (HarfBuzz), right-to-left and bidirectional text, and hyphenation - multi-column text flow