diff --git a/CHANGELOG.md b/CHANGELOG.md index daca229d..4527a6a7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ### Unreleased +- Fix `boundsOfString()` with `lineBreak: false` leaving out `wordSpacing`, so its width now matches the rendered text and its underline - Fix the underline, strike, link and goTo of wrapped text, and `boundsOfString()` with a `width`, counting `characterSpacing` and `wordSpacing` twice, so they no longer run past the text and match the same text drawn with `lineBreak: false` - Fix the Node build throwing once a bundler inlines it into an application bundle, `Cannot find module '#standard-fonts/Helvetica'` on the first document from an ESM bundle and `Invalid URL` while importing a CommonJS one, because the standard font metrics and the PDF/A ICC profile were resolved relative to pdfkit's own package directory at import time. Both are now resolved on first use, a standard font that cannot be loaded names itself and the fix in its error, and `registerStdFonts` is exported from the Node build as it already was from the browser build, so a bundle can register the fonts it uses from `pdfkit/standard-fonts/`. The Node build now requires the standard fonts by a path relative to the built file, which replaces the `#standard-fonts/*` import mapping and lets an ESM bundle load them from a copy of `js/standard-fonts` next to it. Fixes #1801 - Fix `doc.list()` drawing the label of a `numbered` or `lettered` item with the line wrapper's own options object, which threw `unsupported number: NaN` for `align` `center` and `right`, applied a word spacing derived from the previous line to the label for `align` `justify`, and sized the underline, strike, link and goTo of every label after the first from the previous item's text diff --git a/lib/mixins/text.js b/lib/mixins/text.js index 7c42b5e3..78c872d7 100644 --- a/lib/mixins/text.js +++ b/lib/mixins/text.js @@ -184,7 +184,13 @@ export default { } else { // render paragraphs as single lines for (let line of string.split('\n')) { - const lineWidth = this.widthOfString(line, options); + let lineWidth = this.widthOfString(line, options); + if (options.wordSpacing) { + // whitespace runs were collapsed above, so each gap is one character + const trimmed = line.trim(); + const space = /\s/g; + while (space.test(trimmed)) lineWidth += options.wordSpacing; + } this.y += lineHeight; contentWidth = Math.max(contentWidth, lineWidth); } diff --git a/tests/unit/text.spec.js b/tests/unit/text.spec.js index d883bc5a..bb7a572f 100644 --- a/tests/unit/text.spec.js +++ b/tests/unit/text.spec.js @@ -242,6 +242,27 @@ Q new RegExp(`50 [\\d.]+ m\n${end.replace('.', '\\.')}\\d* [\\d.]+ l`), ]); }); + + test('bounds of text with lineBreak false and wordSpacing', () => { + const docData = logData(document); + const options = { lineBreak: false, underline: true, wordSpacing: 5 }; + const bounds = document.boundsOfString( + 'Hello big world', + 50, + 50, + options, + ); + + document.text('Hello big world', 50, 50, options); + document.end(); + + // as wide as the underline, which includes the word spacing + expect(bounds.width.toFixed(2)).toBe('88.74'); + expect(docData).toContainChunk([ + 'stream', + /50 [\d.]+ m\n138\.74\d* [\d.]+ l/, + ]); + }); }); describe('text with structure parent links', () => {