Skip to content

Lay out horizontally scaled text in page units - #1812

Open
youdie006 wants to merge 1 commit into
foliojs:masterfrom
youdie006:hscale-layout
Open

youdie006 wants to merge 1 commit into
foliojs:masterfrom
youdie006:hscale-layout

Conversation

@youdie006

@youdie006 youdie006 commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor

What kind of change does this PR introduce?

Bug fix. LineWrapper multiplies width, indent and columnGap by horizontalScaling / 100, but widthOfString() already returns scaled widths, so the two cancel out. Text wraps at the same words as at 100%, overflows the box above 100% and leaves it part empty below, and right and center alignment are off by the same factor. Glyph boxes from pdftotext -bbox, width: 200 at x 50 (right edge 250):

master this PR
paragraph, horizontalScaling: 150, rightmost glyph 331.7 239.4
paragraph, horizontalScaling: 50, lines 4 (right edge 143.9) 2 (245.3)
'Hello big world', width: 400, align: 'right', horizontalScaling: 80, right margin 80.00 0.00
same with horizontalScaling: 120 -80.00 0.00
align: 'center', horizontalScaling: 120, left / right margin 192.75 / 112.75 152.75 / 152.75
indent: 20, horizontalScaling: 120, first line starts at 74.0 70.0

The wrapper now keeps those three in page units; character and word spacing stay scaled, as Tz scales them. align: 'justify' computed the gap in page units and then let Tz scale it again, so with only the wrapper fixed, justified lines ended at about 259 (120%) or 245 (80%) instead of 250. That gap is now divided by the scaling. The word spacing in the decoration width and in boundsOfString() is scaled as well; for wordSpacing: 6, horizontalScaling: 80 the bounds width is now 72.60, the glyph width, instead of 75.00.

With horizontalScaling left at 100 nothing changes. The new tests in tests/unit/text.spec.js fail on master (8 of them), and reverting either file or any one of the five scaled terms fails at least one. prettier --check, lint and npm test (61 files, 538 tests) pass on Node 22; I did not run Node 20 or 24.

Checklist:

  • Unit Tests
  • Documentation N/A
  • Update CHANGELOG.md
  • Ready to be merged

Written with AI assistance (Claude); I have reviewed the change.

The line wrapper scaled the width, indent and column gap by
horizontalScaling, while widthOfString already returns scaled widths,
so the scaling cancelled out: text wrapped at the same words as at
100% and ran past the right edge above 100% (or stopped short below
it), and right and center alignment were off by the same factor.
Justified spacing and the word spacing in decorations and
boundsOfString were also computed at 100% and then scaled by Tz.
@blikblum

blikblum commented Oct 7, 2026

Copy link
Copy Markdown
Member

Is there a code snippet that demonstrate the issue visually?

@youdie006

Copy link
Copy Markdown
Contributor Author

Yes, this draws a 200pt-wide box and fills it at 50%, 100% and 150%, plus a right-aligned line at 120%:

const PDFDocument = require('pdfkit');
const fs = require('fs');

const doc = new PDFDocument();
doc.pipe(fs.createWriteStream('hscale.pdf'));
const text = 'The quick brown fox jumps over the lazy dog while the five boxing wizards jump quickly';
[50, 100, 150].forEach((horizontalScaling, i) => {
  const y = 50 + i * 120;
  doc.rect(50, y, 200, 100).stroke();
  doc.text(text, 50, y, { width: 200, horizontalScaling });
});
doc.rect(50, 420, 200, 20).stroke();
doc.text('right aligned', 50, 420, { width: 200, align: 'right', horizontalScaling: 120 });
doc.end();

On master the 150% text runs out of the box on every line and the 50% text only fills the left half, because each case breaks at the same words as 100%; the right-aligned line ends 40pt past the box. Rightmost glyph edges from pdftotext -bbox-layout (box right edge at 250):

master this PR
50% 143.9 (3 lines) 245.3 (2 lines)
100% 237.8 237.8
150% 331.7 (3 lines) 239.4 (4 lines)
right, 120% 290.0 250.0

This branch has not been deployed

No deployments
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