Skip to content

fix(text): reset continued state when an explicit position is given (#1641)#1748

Open
JamBalaya56562 wants to merge 1 commit into
foliojs:masterfrom
JamBalaya56562:fix/1641-continued-reset-on-explicit-position
Open

fix(text): reset continued state when an explicit position is given (#1641)#1748
JamBalaya56562 wants to merge 1 commit into
foliojs:masterfrom
JamBalaya56562:fix/1641-continued-reset-on-explicit-position

Conversation

@JamBalaya56562

Copy link
Copy Markdown

Summary

Fixes #1641.

When a text() call passes an explicit x/y coordinate and is not itself continued, it now discards any wrapping state left behind by a previous continued: true run and draws at the absolute coordinate you pass — instead of being offset by the previous run's inline position.

The problem

continued is a forward-looking flag: the state is saved based on the current call's flag and reused by the next call (lib/mixins/text.js#L91-L92), and the next _text reuses this._wrapper unconditionally at L84. On continuation the saved continuedX is added to the current x in the wrapper's firstLine handler (line_wrapper.js#L48-L49, accumulated at L352). So a coordinate passed to the call immediately after a continued run is offset, not absolute:

for (const t of ['AAA ', 'BBB ', 'CCC ']) doc.text(t, { continued: true });
doc.text('FRESH', 400);   // requested x = 400
x of FRESH
Before 484.02 (= 400 + accumulated inline width)
After 400

Previously the only way to avoid this was to set continued: false on the last segment of the run, which is awkward inside loops and when delegating text to helper functions.

The fix

A guard at the top of _text: if a positional x or y is given and the call isn't itself continued, clear this._wrapper / this._textOptions before initializing options. continuedX lives on the wrapper instance, so dropping the wrapper resets it to 0 naturally.

Why this is safe (not a breaking change)

The canonical rich-text idiom continues to work unchanged, because continuing segments pass no coordinates:

doc.fillColor('green').text(a, { width: 465, continued: true })
   .fillColor('red').text(b);   // no x/y -> still continues inline

Verified against the rest of the codebase:

  • Existing continued unit/visual tests and the docs/text.md examples pass no coordinates on the continuing call → unaffected.
  • heightOfString already nulls this state today → behavior identical.
  • boundsOfString uses a local wrapper and never touches this._wrapper → unaffected.
  • list uses its own wrapper via _initOptions → unaffected.
  • table renders each cell at explicit coordinates and cells don't continue → unaffected (and now protected from state bleeding into a cell).

Tests & docs

  • Added two regression tests in tests/unit/text.spec.js: explicit position resets after a continued run; a continued run without coordinates still flows inline.
  • Documented in docs/text.md that continued is forward-looking, how to end a run, and that an explicit position starts a call fresh.
  • CHANGELOG entry under Unreleased.

Full tests/unit + tests/visual suites pass (the one pdfmake/tables failure is a pre-existing default-5s-timeout flake that also fails on master and passes with a longer timeout — unrelated to this change).

🤖 Generated with Claude Code

…oliojs#1641)

A `text` call that passes an explicit x or y coordinate and is not itself
`continued` now discards any wrapping state left behind by a previous
`continued: true` run, so the coordinate is honored as absolute instead of
being offset by the previous run's inline position.

Rich-text sequences (whose continuing segments pass no coordinates) are
unaffected. Adds regression tests and documents the forward-looking nature
of the `continued` flag.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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.

specifying x-coord does not position in absolute space

1 participant