Skip to content
KoalaFactsPublic

About

A browser-free HTML and CSS to PDF engine!

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

FactsPDF

Generate searchable business-report PDFs from HTML and CSS, without a browser.

Rounded commercial report cards (source-only)

border-radius:12pt 9pt 6pt 3pt now paints four independent PDF corner curves for ordinary report panels (one-to-four physical shorthand values). The existing single-radius output remains byte-identical. Text, pagination, border protection and unsupported-value errors remain scoped. The 54-card single-radius report, 50-card four-corner report and independent physical-unit oracle are scoped, not browser CSS conformance. See precise value and failure rules. No new installable preview, human reference approval or customer grant is implied.

FactsPDF is a KoalaFacts rendering engine for applications and command-line jobs that control their report templates. It supports English and Chinese text, explicit font styles, transparent PNGs, tables and pagination through a documented HTML/CSS subset. Fonts and images are supplied by the caller.

Current stage: unreleased 0.1.0-preview.1; source-bound private candidate retained for maintainer evaluation. Merged-main CI 40561057964 at 47bc97910843ad6f72f11a285ec5b74e13862623 passed managed/API and actual Linux/Windows x64 Native AOT checks; six other applicable main workflows also passed. The owner separately verified and privately retained the matching NuGet, managed CLI, Linux native CLI, examples and source components, and executed the Linux native first-use and restricted-font recovery paths. No public NuGet/CLI release, customer software delivery, stable API or general evaluation grant exists. Human approval of new PDF pages and legal distribution rights remain separate. See the evaluation guide and licensing; private engineering checks do not grant usage rights.

Quick start · C# API · Compatibility lookup · Examples · Licensing

Have a business report template to assess? Request a template-fit check with a public, non-confidential description of the layout and runtime. Maintainers can compare it with the documented support boundaries and discuss evaluation rights. This is not a trial grant, a package release or a promise of production suitability.

Generate your first PDF

You need Git and a .NET 10 SDK compatible with global.json. Check the Community or Commercial licensing requirements before building or running the engine; there is no general free trial for ineligible organizations.

git clone https://github.com/KoalaFacts/FactsPDF.git
cd FactsPDF
dotnet run --project src/FactsPDF.Cli -c Release -- examples/quick-start.html quick-start.pdf

Open quick-start.pdf: a one-page service report with a styled table, using ASCII text and built-in Courier. This example needs no external fonts or images.

To add a header, footer and current page number:

dotnet run --project src/FactsPDF.Cli -c Release -- examples/quick-start.html numbered-report.pdf --header-text "Service report" --footer-text "Internal" --page-numbers

The footer reads Internal | 1. Existing output files require --overwrite; conversion failures preserve the existing file. Diagnostics go to stderr, and failure returns a nonzero exit code. Use --help for the CLI options.

To identify the actual built CLI during deployment or a support request, run dotnet run --project src/FactsPDF.Cli -c Release -- --version from a source checkout, or FactsPDF.Cli --version for an installed executable. This reads no report input or resources and prints the assembly's informational version, including the source commit suffix when stamped by the build. It does not attest toolchain signatures or imply that a public package is released.

Missing fonts, glyphs, style roles, embedding permission or images? The current source CLI keeps the original error code and adds an actionable hint. Follow the first-use recovery recipes; resources remain explicit and a failed conversion does not become a partially successful PDF.

Use the .NET API

For a source-based evaluation, create a console app beside the checkout and reference the library project. Run these commands from the FactsPDF directory:

dotnet new console --framework net10.0 --output ../FactsPdfDemo
dotnet add ../FactsPdfDemo/FactsPdfDemo.csproj reference src/FactsPDF/FactsPDF.csproj

Replace ../FactsPdfDemo/Program.cs with:

using FactsPDF;

using var pdf = new MemoryStream();
var result = PdfConverter.Convert(
    "<h1>Service report</h1><p>Prepared for Example Client.</p>",
    pdf,
    new PdfOptions
    {
        HeaderText = "Service report",
        FooterText = "Internal",
        PageNumbers = true
    });

File.WriteAllBytes("report.pdf", pdf.ToArray());
Console.WriteLine($"Created {result.PageCount} page(s), {result.BytesWritten} bytes.");
dotnet run --project ../FactsPdfDemo -c Release

This writes report.pdf in the current directory after conversion succeeds. The API also accepts page geometry, font/image resources, resource limits and a cancellation token. See the API guide and the local-package consumer recipe for integration without a source-project reference.

Current-source multiline report notes

For controlled invoices, inspections and customer notes, explicit paragraphs and headings can use white-space:pre-line to retain authored line breaks from LF, CRLF, CR and form-feed while continuing to collapse ordinary spaces/tabs. Lines still soft-wrap to the measured PDF content width and continue across pages; the final source segment break is discarded. An explicit <br> remains a break. Scoped stylesheet/inline CSS, cascade and explicit p-in-table-cell width work, but container/span declarations, break-spaces, RTL/shaping and unrestricted CSS Text do not. For unwrapped source lines, use the separate bounded <pre> or white-space:pre report mode. Use the separate bounded pre-wrap mode when an authored report must retain visible U+0020 space advances.

The 72-record report has an independently authored explicit-break oracle. Source and Native AOT PDF text, position, pagination and output restoration are checked by WhiteSpacePreLineTests and verify_native_feature_paths.py. This source feature is not a released package or evaluation grant.

Current-source preserved-space multiline report notes

Explicit business-report paragraphs and headings can now use white-space:pre-wrap to keep every authored ASCII space (including leading, repeated and trailing spaces) and CRLF/CR/LF/form-feed line breaks. The spaces are measured PDF glyphs: they affect word positions, automatic wrapping, paragraph alignment, table-cell line occupancy and pagination.

<p style="white-space:pre-wrap">REF001  ASSET   CHECK
  OWNER:  AB</p>

The source-only boundary is deliberate: no tab stops or other control whitespace (explicit FPDF1305), no break-spaces or span/container white-space declarations, RTL or full CSS Text. The separate pre mode preserves these spaces and authored breaks but forbids all automatic wrapping. An unbreakable word or single unfit space errors with FPDF1302, preserving an existing PDF. CSS cascade, !important and CSS-wide reset values use the same paragraph-style path as pre-line; ordinary normal and nowrap PDFs remain unchanged.

See the 72-record authored report, independently authored collapsed-space control and scoped contract. Source NUnit and Linux/Windows Native AOT/Poppler evidence check the actual glyph positions, line breaks, pagination and failed-output restoration. No released binary, NuGet package or customer grant follows from this change.

Current-source preformatted report audit blocks

For inspection logs, reconciliations and aligned audit notes, an explicitly closed <pre>…</pre> block or white-space:pre on an existing p/heading preserves every authored U+0020 space and CRLF/CR/LF/form-feed newline, without soft-wrapping a line. This is real PDF glyph positioning and pagination, not an HTML tag accepted without rendering. An explicitly styled inline span may colour part of a line; <br> remains a manual break. The engine uses the caller's chosen font chain (or built-in Courier when no font is supplied) and does not silently substitute a browser-style monospace face.

<pre>REF001   OK
REF002   NEEDS REVIEW
TOTAL    002</pre>

Long lines that do not fit the resolved content width fail FPDF1302 rather than wrapping, shrinking or clipping. Tabs and other control whitespace fail FPDF1305; no tab stops, general CSS Text, nested blocks, or pre within lists/tables are implemented. <pre> is an ordinary standalone block, not a full HTML5 preformatted/semantic renderer. Stylesheets, !important, inherit/initial/unset, page breaks, true font metrics and protected output still use the established paragraph pipeline. This is source code only, not a new installable binary or distribution grant.

See the 72-row audit report, independent pre-wrap PDF oracle, CSS/HTML contract, and source/Native AOT tests.

Current-source dashed and dotted report borders

Business report panels and existing decorated text, image and table boxes may use border:2pt dashed #28557a, border:2pt dotted #a3452e or individual border-*-style values. Dashed edges use bounded PDF dash strokes; dotted edges use round-cap PDF strokes. Width, colour, padding, containing width and page continuation use the existing box model; a side's dash pattern restarts per page fragment, while only the first and last fragments paint their top and bottom edges. Unstyled and solid-border reports retain their legacy PDF bytes. This is not arbitrary/multi-corner rounded or double borders or full CSS Borders support. One bounded border-radius is separately documented below.

See the 76-record real report, scope and failure contract, and Native AOT geometry/recovery evidence. No public binary or customer distribution rights follow from this source change.

What works today

Area Implemented capability Details
Text and fonts English/Chinese text; explicit normal, bold, italic and bold-italic font chains; ordered character fallback; optional font subsetting Fonts, styles, subsetting
Document layout Headings, paragraphs, nested blocks, lists up to two levels, page breaks and heading keeps; fixed widths, padding, backgrounds and solid borders that continue across pages Layout scope, box fragmentation
Stylesheets Inline CSS and embedded <style> rules; documented selectors and cascade; print-length units mm/cm/in for supported lengths, parent-relative font-size:125%/1.125em, root-relative font-size:1.2rem, computed line-height:150%/1.5em/1.5rem/physical leading, four-sided paragraph/heading margin shorthand with scoped root-relative rem and element-relative em lengths, horizontal auto centring for explicit-width boxes, opt-in box-sizing:border-box report panel sizing, bounded real min-width/max-width constraints and actual horizontal margin:0 auto centring of div/section/article panels (bounded nonnegative vertical margins outside painted borders) plus computed-root rem and own-font em widths/padding for ordinary report blocks, bounded paragraph/heading white-space:nowrap, white-space:pre-line, white-space:pre-wrap or unwrapped white-space:pre with real authored breaks and spaces and opt-in overflow-wrap:break-word for long report identifiers, first-line paragraph/heading text-indent with real wrapping, bounded physical letter-spacing for report labels and inline text, measured soft-wrapped text-align:justify using PDF word gaps, ASCII-only text-transform:uppercase/lowercase/capitalize on report text, colour-only background and measured underline/overline/strike-through decorations with scoped opaque inks and solid/dashed/dotted strokes CSS and spacing boundaries
Images Explicit baseline JPEG and supported static, non-interlaced PNG, including transparency; standalone images and direct image items in table cells Images and resource binding
Tables Explicit fixed columns, colspan, flat paragraph/image cells, repeated single-row headers, oversized body-row continuation and scoped whole-row vertical-align:top/middle/bottom for cell content Table rules and limits
Headers, footers and page numbers Legacy centered literal text and current page number; development-source PageFurniture adds styled left/center/right regions, {page}/{pages} labels and whole first-page replacement Legacy page text, structured page furniture
Integration .NET library and CLI; Linux/Windows x64 Native AOT execution checks Build and platform verification

For inspection findings, revised quotes and status notes, text-decoration:overline dashed blue or text-decoration:underline dotted #ff0000 paints a real clipped vector line at the font-measured text position. You can also set text-decoration-color and text-decoration-style individually; their CSS-wide cascade and currentcolor follow the existing styling rules. The unstyled and legacy solid paths preserve existing bytes. One line only; no double, wavy, multi-line, alpha/transparency, variable thickness or browser-wide propagation. See the source example, PDF implementation tests, and bounded CSS contract. This is current source, not a newly distributed binary or customer licence.

For business tables with multiline descriptions, td/th now accept vertical-align:top, middle or bottom to position a shorter cell's text or image within the complete painted row. Whole rows and intact repeated headers work across pages. Rows needing independent cell-by-cell continuation reject middle/bottom instead of pretending to align; inline baseline alignment, rowspan and general CSS table layout remain unsupported. See the 40-row report, physical-padding oracle and table contract.

Headers and footers: current limits

HeaderText and FooterText repeat on every page as centered, normal black 10pt text. PageNumbers = true appends | N to a nonempty footer, or shows the number alone. Text is literal: {page}, {pages} and HTML are not expanded. It must fit on one line in the existing margins; overflow is an error.

The current source API also supports total-page labels, left/center/right regions, explicit styles and whole first-page replacement through PdfOptions.PageFurniture. These additions are implemented in the development source and have not been released as a package. See the page-furniture CLI guide for configuration and limits.

Controlled div, section and article report panels can now be centred using bounded horizontal margin:0 auto, including constrained max-width, nested decorated boxes and page fragments. Vertical panel margins remain zero-only; ordinary paragraphs/headings retain their separate vertical-margin contract. See the panel-margin CSS scope, 50-entry example and independent point oracle. This is source-only functionality, not a new installable candidate or public release.

Current source typography: report templates may use numeric font-weight in exact hundred steps from 100 to 900. Values 100–500 select the caller-supplied normal font chain and 600–900 the caller-supplied bold chain, including inline labels, CSS selectors and cascade. Real font advances affect PDF line wrapping and pagination. Missing bold resources fail explicitly; FactsPDF does not synthesize variable weights. See the 64-entry example and font role contract. This source addition is not a new public NuGet binary or stable CSS Fonts implementation.

Current-source business-report quotations

The standard <blockquote>…</blockquote> element now creates a real inset PDF report panel, with default outside margins of 8pt vertically and 24pt horizontally. Quoted paragraphs and inline emphasis inherit the existing font styles, while measured text, optional solid borders, backgrounds, padding and page continuation use the same renderer as div report panels. For example:

<blockquote style="border-left:2pt solid #28557a;padding:7pt">
  <p>Inspection evidence: all nominated checks were completed.</p>
</blockquote>

This is a bounded quote layout feature, not citation metadata, Tagged PDF or browser-complete blockquote semantics. cite attributes, nesting inside lists/tables/inline elements, negative or percentage vertical margins, margin collapse, CSS height, arbitrary/per-corner rounded geometry and arbitrary HTML are unsupported and explicitly rejected. The quote is source-only: it does not update the retained preview binaries or grant customer distribution rights. Real 44-item quote report, independently authored div oracle and exact CSS/HTML contract.

Current-source ASCII capitalized report labels

text-transform:capitalize now formats English service-register labels as actual PDF glyphs before font selection, wrapping and pagination. Only the first lowercase ASCII letter in each ASCII letters/digits token is capitalized; punctuation starts another token while styled inline spans do not. Mixed-case remainder stays unchanged. Non-ASCII cased Unicode text fails explicitly rather than being silently transformed. See the 54-entry report, independently authored literal-case PDF oracle and bounded CSS contract. This source capability is not a rebuilt binary or a public NuGet release.

Current-source selectable report-list markers

Business reports may set list-style-type on an existing ol, ul or individual li to render actual numbered or unnumbered PDF lists. For example:

<style>
  ol.inspections { list-style-type: lower-alpha; }
  ol.actions { list-style-type: upper-roman; }
</style>
<ol class="inspections"><li>Inspect equipment</li><li>Record outcome</li></ol>
<ol class="actions"><li>Assign owner</li><li>Schedule follow-up</li></ol>

Supported kinds: disc, circle, square, decimal, lower-alpha, upper-alpha, lower-roman, upper-roman, none. Ordered lists normally remain decimal and unordered lists remain vector discs, preserving old PDF bytes. circle paints a transparent-centre stroked ring; square paints a filled vector square in the item colour. Both honour the measured list gutter, first-line marker placement, page continuation and display-command limits. Alphabetic labels progress a…z…aa and Roman labels use standard subtractive notation through 3999. Text markers use caller-supplied fonts, measured per-list gutters, and one marker on the first line even across pages. none removes the marker, not the item's text; it retains the existing list layout indentation. CSS cascade, !important, and inherit/initial/unset apply; nested lists retain their own defaults unless explicitly overridden. Unsupported marker kinds, lists over two levels, placement on non-list elements and unavailable marker glyphs fail explicitly, preserving the existing PDF destination.

74-entry paginated facilities report, 80-entry circle/square multi-page report, bounded CSS contract, full list mechanics. This is a source-only capability: no preview binary, NuGet publication, commercial grant or human PDF approval follows from this change.

Current-source report list continuation and numbering

Ordered business report actions can continue from <ol start="21">, count down via <ol reversed>, or reset an item with <li value="43"> inside ol. A one-token shorthand such as list-style:upper-roman or list-style:square uses the same real PDF text/vector marker, CSS cascade, font measurement and pagination as list-style-type. Unstyled lists retain byte-identical PDF output.

This is a bounded subset: only ASCII positive ordinals 1–999999, only direct ol > li value overrides, and one existing marker kind or CSS-wide keyword per list-style. No arbitrary CSS Lists semantics, inside/position/images/counters, HTML type, negative ordinals or list-item value on ul; invalid inputs fail without overwriting a destination. See the 77-entry pagination example and exact list contract. This is current source, not a new retained binary, public NuGet or customer distribution grant.

Current-source print-only report sections (bounded display)

Use display:none to omit a draft, internal note, or unused ordinary report block from the generated PDF without leaving blank space; display:block preserves the ordinary block layout. Embedded stylesheets, inline rules, specificity, source order and !important participate in the same CSS cascade.

<style>.internal-only { display: none; }</style>
<section class="internal-only"><p>Draft for internal approval</p></section>
<section><p>Customer-facing report</p></section>

Supported targets are p/h1–h6, div/section/article and blockquote outside lists/tables. A hidden block and its complete validated subtree never reach PDF glyph layout, margin/padding geometry, pagination or painted backgrounds/borders. Invalid HTML/CSS, absent resources and input budgets are still checked inside hidden markup; this is not a validator bypass. The HTML hidden attribute, CSS-wide display keywords, inline/flex/grid layout, image/hr/table/list suppression, JavaScript and media-query display switches are not supported.

See exact source contract, 40-entry example, and visible-only PDF oracle. Source implementation is not a published NuGet binary or customer licence.

Current-source logical report spacing (LTR only)

Controlled left-to-right business reports can now use margin-inline, margin-block, padding-inline and padding-block with one or two values, as alternatives to the corresponding physical longhands. For example, margin-inline:auto centres an explicitly sized panel, while padding-inline:8pt 12pt adds measurable left/right insets and margin-block:6pt 9pt creates real vertical gaps. The same ordinary report boxes also support eight individual LTR logical side longhands: margin-inline-start/margin-inline-end, margin-block-start/margin-block-end, padding-inline-start/padding-inline-end, and padding-block-start/padding-block-end. For example, margin-inline-start:auto;margin-inline-end:auto centres a fixed-width report card; padding-inline-start:6pt changes the actual first glyph X; and margin-block-end:8pt adds real space below a paragraph or card. Each longhand takes exactly one value and participates independently in normal physical-side cascade, !important, inherit/initial/unset, print and font-relative lengths, box painting, page continuation and strict failure restoration. A 40-reference paginated report has a separately written physical-side PDF oracle for the logical-side example.

This is LTR normal-flow mapping only: inline-start = left, inline-end = right, block-start = top, block-end = bottom. direction, RTL/vertical writing modes, negative values, CSS margin collapse, other logical properties, calc() and unsupported elements remain outside scope. Body/list/table/ image/inline margins remain rejected, as with their physical longhands. See the CSS contract and real report + physical-longhand oracle. This source change does not update the historical private binary candidate.

Current-source horizontal report dividers

A standalone HTML <hr> now paints a real 1pt current-text-colour rule with 8pt non-collapsing outside spacing above and below. <hr /> is equivalent. Templates may customise the separator through the existing bounded width, min-width, max-width, margin (nonnegative vertical lengths and horizontal auto), padding, box-sizing, color, background and solid border declarations. The rule follows its containing width and moves intact to the next page when needed. font-size affects only supported rule-local em box lengths.

This is a void block separator, not unrestricted HTML or CSS: no content, table/paragraph/list/inline nesting, arbitrary height, pseudo-elements, arbitrary/patterned rounded borders, margin collapse or resource URLs. Unsupported declarations fail explicitly and preserve an existing output. See the scoped CSS contract, 44-entry report, and the independently authored painted-box oracle. This source feature is not a public NuGet/CLI release or a new private binary.

Current-source LTR text alignment for report labels

Common report templates can now write text-align:start or text-align:end on paragraphs, headings and inherited ordinary containers. In the engine's left-to-right horizontal text model, these correspond respectively to left and right: the actual PDF glyph X coordinates, font measurement, first-line indentation, wrapping and cross-page continuation follow the existing physical-alignment renderer.

This applies through embedded stylesheets, inline declarations, normal cascade and inheritance; it does not add RTL, direction, HTML dir, vertical writing, inline-span start/end alignment or full CSS Text behavior. Unsupported values and directions fail without replacing an existing PDF. Compare the 46-entry printable report with its physical alignment oracle and the exact CSS contract. This is current source only, not an updated package or distribution grant.

Compatibility lookup

The Can I Render site provides searchable support records for HTML, CSS, text, PDF behavior and runtime integration. Filter by category or status, open a feature's precise scope and limitations, and follow its source/test evidence. It supports Chinese/English interface labels, shareable feature links and JSON/CSV export.

This is a code-audit snapshot, not a released-version comparison matrix. Each record carries its own audit commit; unknown introduction versions remain unknown. A supported subfeature does not make its whole parent standard supported.

To browse locally, use Node 24 and npm, starting from the repository root:

cd site
npm ci
npm run dev

Open the local URL printed by Vite, normally http://127.0.0.1:5173/FactsPDF/.

Source-built business report templates can scale text spacing with typography: text-indent:0.4em, letter-spacing:0.1em and word-spacing:0.15rem. Each length uses its element's final font or the computed html root, then changes actual glyph positioning, first-line wrapping and pagination. Letter/word advances remain bounded to 0–24pt after computation; percentage letter/word spacing, negative values, inline text indentation and general browser CSS remain unsupported. See the contract and 72-entry example. This source feature does not update retained private installable software.

For report explanatory paragraphs and headings, opt-in text-indent:18pt, text-indent:0.25in or text-indent:15% insets only the first logical line within the actual content width; later wrapped lines, explicit <br> lines and subsequent pages are not re-indented. The paragraph box/background keeps its original position. Percentages resolve against its actual content width. Nonnegative pt/px/mm/cm/in/% or zero are supported; overlarge insets or first words that cannot fit reject with FPDF1302 without overwriting the destination. Inline spans, containers and table-cell paragraphs cannot set it; negative/hanging indents and calc() are unsupported. This is a scoped PDF text layout capability, not full CSS Text conformance.

Report headings, labels and inline text can use a bounded, nonnegative letter-spacing:0.75pt or equivalent px/mm/cm/in print length. This changes real PDF glyph advances, line breaks, horizontal alignment and text-decoration widths, including with explicitly bound Chinese TrueType glyphs. normal and zero retain legacy PDF bytes. Negative spacing, percentages, calc() and spacing over 24pt are rejected, rather than silently dropped. See the scoped tracking contract.

Controlled report prose can opt into text-align:justify to distribute the measured remaining width among collapsed U+0020 spaces on automatically wrapped lines. The final line, explicit <br> lines, single-line/nowrap text and lines without retained word spaces remain ragged; no RTL, CJK inter-character or full CSS Text claim is made. Both Courier and explicit TrueType paths use real PDF TJ displacement. See bounded justification.

Controlled report prose can opt into word-spacing:0.01in (equivalent to 0.72pt) at collapsed word spaces. This changes actual PDF word positions, line wrapping, alignment and pagination; the normal/zero path preserves legacy PDF bytes. The renderer uses PDF TJ displacement for Courier and embedded CID TrueType, not byte-specific Tw. Only nonnegative print lengths up to 24pt are supported. See word-spacing boundaries.

Long unbroken invoice and approval identifiers can use overflow-wrap:break-word on explicit report paragraphs/headings, including flat paragraphs inside fixed-column table cells. The measured PDF layout keeps ordinary words whole when they fit a fresh line, but splits an oversized token at supported glyph boundaries and continues across pages. white-space:nowrap takes precedence; unsupported anywhere/inline declarations and an unfit individual glyph reject without modifying a previous PDF. This bounded source-only feature is not full browser CSS Text or automatic hyphenation. See the contract, 32-entry report, and independent manual-break oracle.

Controlled identifiers or approval labels can use white-space:nowrap on <p> and headings; multiline inspection notes can use white-space:pre-line instead. Spaces still collapse, explicit <br> works, and content too wide for the measured line fails with FPDF1302 without replacing the output. normal is byte-identical to the default; pre is separately supported as a bounded unwrapped preservation mode, while break-spaces and arbitrary inline white-space spans remain unsupported. See bounded white-space and multiline notes.

Controlled report typography can use line-height:150%, line-height:1.5em, line-height:18pt or print units such as 0.25in. Percent/em compute an absolute line height against the element's own computed font size; that fixed height is inherited unchanged even when nested text changes font size. Unitless line-height:1.5 remains a multiplier inherited by children. These values affect real line boxes and pagination and reject invalid input before replacing output. See line-height contract.

On current source, business report templates can opt into font-size:1.2rem and line-height:1.5rem. rem uses the <html> element's computed font size, or PdfOptions.FontSize when no html element exists. On html itself, font-size:rem resolves against the initial configured font size to avoid a self-reference; line-height:rem uses its newly computed root size. Font sizes resolve to 1–144pt; fixed line heights resolve to 0.1–1440pt and inherit as points. This feeds actual glyph layout, wrapping and pagination (not merely syntax acceptance). This typography feature is separate from the newer scoped rem lengths for ordinary report box width/min-max/padding; rem margins are now limited to paragraphs/headings outside table cells; borders and image/table/column sizing still reject rem; only bounded text-indent/letter-spacing/word-spacing now accept it. See the root-relative contract and runnable multi-page example; these source changes are not the previously retained installable candidate.

Controlled report templates can use opaque rgb(35, 68, 107), rgb(35 68 107) and rgba(35,68,107,1) for text, backgrounds and solid borders. These produce the same painted PDF as matching hex colours; non-opaque alpha and gradients still fail explicitly. See bounded CSS colours and the print report example. For a fontless case-formatting example with a preserved proper-name span, see the report case labels.

For source-built business report boxes, width:13rem, max-width:14rem, min-width:10rem, and padding:0.5rem now resolve against the computed html root font size, or PdfOptions.FontSize if there is no html element. The resulting point lengths drive actual box/content widths, text wrap, centring, borders/backgrounds and page continuation. rem is limited to ordinary blocks, paragraphs and headings for these properties; image/table/column lengths and border rem still reject; bounded text-indent/letter-spacing/word-spacing and paragraph/heading margins accept rem. Unsupported values and unsafe geometry preserve previous output bytes. The separate element-relative em layout is also available in source-built controlled reports: width:12em, min-width:8em, max-width:14em, padding:0.25em and paragraph/heading margin:0.2em 0.5em resolve against the element's own computed font-size after cascade, unlike rem (the html root). They change real PDF line wrapping, painted boxes and page breaks; negative or overwide geometry rejects before replacing output. Inline/table/image/col box lengths, table-cell em margins, border/text-spacing em, calc() and general container margins remain unsupported. The 32-entry em report has a separately authored point-value oracle and targeted tests; this is not part of the retained installable candidate. See the root-relative box contract and 69-record report with its independent absolute-point oracle. This feature is source-only and not part of the retained installable preview.

For printed business reports, div, section and article now support nonnegative top/bottom margins alongside horizontal margin:0 auto. For example, margin:12pt auto 7pt centres a painted card and leaves 12pt above and 7pt below its outside border, including nested/multipage PDF content without CSS margin collapsing. See the 50-entry sample, independent fixed-spacer oracle and bounded CSS contract. Body, lists, tables, images and inline margins remain unsupported. No new installable candidate or human PDF approval is implied.

Scoped report paragraphs/headings also accept margin:0.25rem 0.5rem, margin-top:0.5rem and 1–4 side shorthand values. The computed html root (or PdfOptions.FontSize fallback) drives real PDF vertical gaps, horizontal text/paint position, wrapping and pagination. Horizontal auto still combines with supported lengths. Negative margins, vertical %/auto, calc(), body/list/table/image/inline margins and table-cell rem/em margins remain rejected; div/section/article panels have a separate bounded exception. See the scoped margin contract, the 32-entry report and its separately authored fixed-point oracle. Unfit output preserves the prior destination. This is source-only.

For source-built business reports, min-width and max-width now constrain real used block widths, including width:auto panels and horizontal auto margins. For example, max-width:146mm caps a report panel, while min-width:82mm;max-width:90mm bounds an invoice-reference box. box-sizing:border-box applies constraints to the padded/bordered outer box; the default content-box constrains the content width instead. Supported lengths are nonnegative pt/px/mm/cm/in/% or zero; min-width:auto and max-width:none remove the constraint. Actual over-constrained geometry is rejected without replacing prior output. This is only ordinary report blocks/paragraphs/headings; image, table and inline sizing stay separate. See CSS scope and the runnable report.

Check your template against these boundaries

  • Tables need explicit column widths. Short tables and rows that fit a fresh page stay whole; only eligible oversized body rows continue at whole text-line or image boundaries. Headers and images remain indivisible. No automatic column widths, rowspan or nested tables.
  • PNG support is bounded: no interlaced/16-bit PNG, APNG or SVG. Images are explicit resources, not URLs or files automatically loaded from HTML.
  • Fonts are explicit static TrueType files. There is no system-font discovery, CSS font-family matching, synthetic bold/italic, full complex-script shaping or RTL layout.
  • General flex/grid, external stylesheets, CSS Paged Media and arbitrary webpage conversion are outside the supported subset. Unsupported input produces explicit diagnostics.

Fonts and images are explicit inputs

For Chinese or other supported non-ASCII text, provide appropriately licensed font files. The paths below are placeholders for files you supply:

dotnet run --project src/FactsPDF.Cli -c Release -- examples/stylesheet-document.html bilingual.pdf --font fonts/Latin.ttf --font fonts/Chinese.ttf --subset-fonts

Repeated --font options define fallback order. Bold, italic and bold-italic content need their own --font-bold, --font-italic and --font-bold-italic chains. Fonts are not bundled; full embedding is the default. Subsetting is opt-in and respects the supplied font's embedding restrictions.

An HTML image such as <img src="logo"> names a resource. Bind it using --image logo=path/to/logo.png, or PdfOptions.Images in the API. To try the branded example with a generated synthetic logo, use Python 3 for the fixture generation step, then the same CLI:

python3 scripts/png_fixtures.py --output artifacts/readme-images
dotnet run --project src/FactsPDF.Cli -c Release -- examples/branded-business-report.html branded-report.pdf --image logo=artifacts/readme-images/brand-mark.png --header-text "Service report" --footer-text "Internal" --page-numbers

Choose a report example

Example What it demonstrates Inputs beyond HTML
Quick start A one-page service report with a styled table None; ASCII/Courier
Print spacing report Print units (mm/cm/in), real computed line heights and four-sided paragraph margins affecting text, page layout and painted panels None; ASCII/Courier
Long invoice identifiers 32 unbroken references, actual measured emergency wrapping, and independent manual-break oracle None; ASCII/Courier
Root-relative margins 32-entry multi-page service report with computed-root rem margins, compared against a separate point-value oracle None; ASCII/Courier
Element-relative report layout 32-entry service report with em box widths, padding and paragraph/heading margins based on each element's computed font; compared to a separate fixed-point oracle None; ASCII/Courier
Branded report Transparent logo beside company details and line items The explicit logo binding above
Detailed service report Column spans and ordered paragraphs/images inside cells The same logo binding
Long service report A long description continues across pages; short values occur once and totals follow The same logo binding
Combined reports English/Chinese text, four font roles, lists, image, table and page text together Four explicit font-role chains, JPEG binding and page options; complete command in the guide

The evaluation guide helps match a real customer template and deployment environment to the current scope.

Development and delivery status

CI runs managed tests on Windows, Linux and macOS, and executes actual Linux x64 and Windows x64 Native AOT CLIs. Independent PDF checks cover content, geometry, fonts, images, pagination and error recovery. The merged native feature-path gate (#58) executes structured page furniture and oversized-row continuation on both Linux and Windows Native AOT; the private candidate PR (#60) additionally checks native software-ZIP extraction and actual CLI execution. These checks apply to their exact tested revisions, not to a released product.

The Node subprocess and browser WASM integrations are experimental probes, not published SDKs. Performance results apply to their documented workloads and environments; there is no general speed or resource guarantee.

Structured page furniture and the locked 0.1.0-preview.1 candidate toolchain are implemented in the development source (#57, #60). The owner privately retained all five components built for merged main 47bc97910843ad6f72f11a285ec5b74e13862623, with source-bound checksums and real offline Linux Native AOT first-use and FPDF1505 font-permission recovery checks. Actual main CI 40561057964 passed after a targeted retry of a Windows-native dependency-mirror failure that occurred before compilation; six other applicable workflow groups also passed. CI validation and owner-only retention do not mean public distribution, independent signature attestation or customer acceptance.

At earlier engine source 1a81f69a4beaf92a8710e5b79ce355440efb00b0, real managed-CLI replay matched all 52 original PDFs / 311 pages byte for byte. A separate real Linux Native AOT replay at e1dfe3b8ea0b5a0cc1cbd612abf22dd8a59748ea also matched 52/52. Those are scoped historical executions, not a fresh 47bc9791 replay. The original Unicode 10/30/100-record report inputs and positive C1 four-distinct-Chinese-font-style evidence were inspected in PR #71's actual API/managed/native acceptance artifact; the original reports exercise Chinese regular/bold, while C1 separately exercises Chinese regular/bold/italic/bold-italic. Engineering evidence does not approve new PDF pages on the user's behalf. New-page human sign-off, the fixed-environment performance budget, distribution authority and customer evaluation terms remain open. Strict local qpdf checking has not been asserted complete. There is no public NuGet/CLI, Release or customer delivery; follow issue #51.

Task Documentation
Build, test, package locally or inspect Native AOT output Development guide
Check exact support and acceptance evidence Engineering support matrix
Inspect CSS parsing and differential verification CSS syntax, quality gates
Contribute code or review licensing obligations Contribution policy, licensing guide

Licensing

FactsPDF is source-available, not MIT or OSI-approved open source. It offers Community and separately agreed Commercial licensing paths.

  • The Community License 1.1 covers qualifying personal use, entities with Group annual gross revenue at most USD 1,000,000 (including proprietary commercial use), and qualifying charitable/open-source-project uses within their defined scope. The full license controls eligibility.
  • Uses outside Community eligibility need written evaluation or commercial authorization before building or running the engine. Commercial licences are available by arrangement.
  • Covered engine changes and corresponding build materials must be published under Community License 1.1 before distribution or operational use, including internal use and SaaS, subject to the license's controlled-development/testing exception. An independent application's code, customer data, templates and generated PDFs do not become public merely by calling FactsPDF.
  • Paying for a Commercial License does not automatically permit private engine modifications or include support, an SLA, updates or unlimited deployments.

See licensing examples, commercial terms and enquiry process and release requirements. Enquiries are welcome. Maintainers must complete the repository's release and authorization checks before publishing software packages or accepting paid license orders. Request a private channel before sharing customer templates or confidential business details. Receiving an ordinary generated PDF does not itself require an engine license.

About

A browser-free HTML and CSS to PDF engine!

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages