Skip to content

feat: table api#46

Open
ephraimduncan wants to merge 2 commits intoLibPDF-js:mainfrom
ephraimduncan:feat/table-api
Open

feat: table api#46
ephraimduncan wants to merge 2 commits intoLibPDF-js:mainfrom
ephraimduncan:feat/table-api

Conversation

@ephraimduncan
Copy link

@ephraimduncan ephraimduncan commented Mar 7, 2026

Summary

Adds a declarative table layout and rendering API to @libpdf/core. Define columns, headers, body rows, and footers — the library handles measurement, text wrapping, pagination, and drawing.

Features

  • Column sizing — fixed widths, "auto" (fit content), and "*" (fill remaining space)
  • Multi-page pagination — tables that exceed the page bounds automatically flow onto new pages
  • Header/footer repeat — headers repeat on every page; footers appear on the last page (configurable)
  • Text wrapping"word" and "break-word" overflow modes with automatic line breaking
  • Cell styling — per-section, per-column, and per-cell font, color, fill, alignment, and padding
  • Alternating row fills — built-in alternateRowStyle option
  • Sparse rows — footer/summary rows that only populate specific columns (e.g., totals)
  • Borders — independent inner/outer border width and color

Usage

import { PDF } from "@libpdf/core";
import { grayscale } from "@libpdf/core/helpers/colors";

const pdf = PDF.create();
const page = pdf.addPage({ size: "letter" });

const result = pdf.drawTable(
  page,
  {
    columns: [
      { key: "name", width: 200 },
      { key: "value", width: "*" },
    ],
    head: [["Name", "Value"]],
    body: [
      ["Alpha", "100"],
      ["Beta", "200"],
      ["Gamma", "300"],
    ],
  },
  {
    bounds: { x: 48, y: 72, width: 516, height: 640 },
    style: { fontSize: 11, lineHeight: 14, padding: 6 },
    headStyle: { font: "Helvetica-Bold", fillColor: grayscale(0.85) },
  },
);

const bytes = await pdf.save();

Rendered demos

Simple table

simple-table

simple-table.pdf

Multi-page invoice (page 1)

invoice-multi-page-p1

invoice-multi-page.pdf

Multi-page invoice (last page)

invoice-multi-page-last

Borderless with alternating row fills

borderless-alternating

borderless-alternating.pdf

Right-aligned numeric columns

right-aligned-numeric

right-aligned-numeric.pdf

Word wrapping (break-word)

break-word

break-word.pdf

Sparse footer rows

sparse-footer

sparse-footer.pdf

Auto + star column widths

auto-star-widths

auto-star-widths.pdf

Architecture

src/tables/
  types.ts      — TableDefinition, DrawTableOptions, result types
  normalize.ts  — resolve cascading styles, sparse rows → dense cells
  measure.ts    — text measurement, line breaking, column width solving
  layout.ts     — pagination into PageFragments with head/foot repeat
  render.ts     — draw cells, borders, fills onto PDFPage objects

Test plan

  • Single-page table renders and survives save/reload round-trip
  • 50-row invoice paginates across multiple pages with correct row counts
  • Footer rows (subtotal/tax/total) only appear on the last page
  • Borderless table has no stroke operators in content stream
  • Alternating row fills produce correct rg operators
  • Right-aligned columns have matching right edges
  • break-word wrapping splits long text mid-word
  • Sparse footer rows leave empty cells for missing keys
  • auto + * column widths compute correctly
  • Content drawn after table starts below cursorY

Add PDF.drawTable() for creating tables with automatic pagination,
repeated headers/footers, flexible column widths (fixed/auto/star),
text wrapping with break-word support, and a style cascade system.

Internal architecture splits into normalize → measure → layout → render
pipeline under src/tables/, keeping layout computation pure and testable
independent of PDF drawing.
@vercel
Copy link
Contributor

vercel bot commented Mar 7, 2026

@ephraimduncan is attempting to deploy a commit to the mythie's projects Team on Vercel.

A member of the Team first needs to authorize it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant