Skip to content

fix(pdf): long notes, tokens, rows and tax names break instead of overlapping - #119

Merged
TomiMalamud merged 4 commits into
mainfrom
claude/sdk-pdf-generation-i7gy4m
Oct 9, 2026
Merged

TomiMalamud merged 4 commits into
mainfrom
claude/sdk-pdf-generation-i7gy4m

Conversation

@TomiMalamud

@TomiMalamud TomiMalamud commented Oct 9, 2026 •

Copy link
Copy Markdown
Collaborator

Follow-up to #117, from three review passes over @facturas/pdf. I reproduced each finding in a rendered PDF before fixing it. Each has a test that fails without its fix.

Fixes

  1. Long notes broke the layout. The closing block (totals, legends, notes, fiscal footer) could not split. With long notes, the totals overlapped the lines and the notes ran over the CAE and off the page.
    • Notes moved: they now sit after the receiver and before the lines, like Stripe's memo, and break across sheets freely.
    • Closing block: it no longer holds free text. The total, legends and fiscal footer still move together, so the CAE is never alone on a sheet.
    • Tried first: minPresenceAhead and an absolutely positioned footer. Neither held the footer to the last content sheet.
  2. Tokens wider than the description column overran the price columns. A token longer than 20 characters, such as a URL, may now break anywhere. Words still wrap whole.
  3. Rows taller than a sheet were clipped. Every row was unbreakable. A row whose description is over 600 characters may now continue on the next sheet.
  4. Long tax names ran into their amount. "Percepción de Ingresos Brutos Provincia de Buenos Aires" is wider than the totals column. The label now wraps in its own column, and the amount keeps its width.
  5. A long tax breakdown outgrew the sheet. With 15 wrapped tax rows, react-pdf squeezed the closing block's lines to about 3.3 pt apart (review comment).
    • Usual breakdown: it stays in one block with the total and the CAE, with <VoucherAside> beside it.
    • Long breakdown: when its estimated height passes 250 pt, its rows flow and may continue on the next sheet. Only "Importe total" stays with the legends and the fiscal footer.

Visible change

<VoucherNotes> and the notes option now print before the lines instead of after the totals. The guide, the changeset and the doc comments say so. <VoucherAside> moves with the totals and the CAE, so the guide asks to keep it short.

Tests

  • Sheet edge: one test sweeps 20 to 32 lines across the first sheet's edge and checks that the totals always share the CAE's sheet.
  • One per finding: long notes (70 clauses, all printed, above the totals), an oversized token, an oversized row, a long tax name, and 15 long tax rows (line spacing kept on every sheet, total with the CAE).

Validation

  • pnpm check
  • pnpm typecheck
  • pnpm test
  • pnpm typecheck:examples
  • pnpm check:docs
  • pnpm --filter @facturas/pdf build

🤖 Generated with Claude Code

https://claude.ai/code/session_01PWf3KVPSbVbfiQgseVoyNW

claude added 2 commits October 8, 2026 22:46
Review findings, each reproduced in a rendered PDF first:

- A token wider than the description column, such as a URL, overran the
  quantity and price columns. Tokens longer than 20 characters may now break
  anywhere; words still wrap whole.
- A row taller than a sheet was clipped, because every row was
  unbreakable. A row with a description over 600 characters may now
  continue on the next sheet.
- Long notes broke the layout: the rigid closing block could not split,
  so totals overlapped the lines and notes ran over the CAE and off the
  page. Notes now sit after the receiver and before the lines, as Stripe's
  memo, and break across sheets freely. The closing block keeps only the
  totals, legends and fiscal footer, a bounded height, so they still move
  together and the CAE is never alone. minPresenceAhead was tried and
  does not hold the footer, which sits on an auto margin.

Tests cover each case and fail without their fix. One test sweeps the line
count across the sheet edge and checks the totals stay with the CAE.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PWf3KVPSbVbfiQgseVoyNW
…e amount

"Percepción de Ingresos Brutos Provincia de Buenos Aires" is about 250 pt at
9 pt, wider than the 240 pt totals column, so it ran into "$ 3,00". The
label now takes the room left of the amount and wraps; the amount keeps
its width. A test checks that the name breaks before the amount's column,
and it fails without the fix.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PWf3KVPSbVbfiQgseVoyNW
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

@TomiMalamud TomiMalamud left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One reproduced PDF pagination regression; details inline.

Comment thread packages/pdf/src/theme.ts
…th the CAE

Wrapping long tax names made the closing block's height unbounded: with 15
wrapped tax rows it outgrew the sheet, and react-pdf squeezed its lines to
about 3.3 pt apart, overlapping the labels and the total.

A usual breakdown still stays in one block with the total and the CAE. When
its estimated height passes 250 pt, its rows flow and may continue on the
next sheet, and only "Importe total" stays in the block with the legends and
the fiscal footer. The estimate is conservative, 28 characters per label
line. Two layouts keep the aside beside the breakdown in the usual case.

A test renders 15 long tax rows, checks every sheet keeps its line spacing,
all rows print and the total sits with the CAE. It fails without the fix.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PWf3KVPSbVbfiQgseVoyNW

@TomiMalamud TomiMalamud left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Encontré una regresión de paginación en 32fb004: el aside de pagos y saldo se separa del importe total cuando el desglose de impuestos se pagina. Reproducción y propuesta en el comentario inline.

Comment thread packages/pdf/src/voucher.tsx Outdated
With a long tax breakdown, the rows flow and the total moves to the
CAE's block, but <VoucherAside> stayed with the rows. Payments and balance
printed on sheet 1 while the total and the CAE sat on sheet 2.

When the breakdown breaks, the aside now goes into the closing block,
beside "Importe total", so it stays with the total and the CAE as the
guide promises. A test renders 15 long tax rows with a two-line aside and
checks both share the CAE's sheet, side by side. It fails on 32fb004.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PWf3KVPSbVbfiQgseVoyNW
@TomiMalamud
TomiMalamud merged commit 40e3f8c into main Oct 9, 2026
3 checks passed
@TomiMalamud
TomiMalamud deleted the claude/sdk-pdf-generation-i7gy4m branch October 9, 2026 01:31
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.

2 participants