Skip to content

Commit d9c8c95

Browse files
committedOct 31, 2021
Tweak code for multiline text layout
1 parent 739dc59 commit d9c8c95

File tree

2 files changed

+52
-7
lines changed

2 files changed

+52
-7
lines changed
 

‎src/api/text/layout.ts

+19-7
Original file line numberDiff line numberDiff line change
@@ -41,27 +41,39 @@ const computeFontSize = (
4141
while (fontSize < MAX_FONT_SIZE) {
4242
let linesUsed = 0;
4343

44-
for (let idx = 0, len = lines.length; idx < len; idx++) {
45-
const line = lines[idx];
44+
for (
45+
let lineIdx = 0, lineLen = lines.length;
46+
lineIdx < lineLen;
47+
lineIdx++
48+
) {
49+
linesUsed += 1;
50+
51+
const line = lines[lineIdx];
52+
const words = line.split(' ');
53+
54+
// Layout the words using the current `fontSize`, line wrapping
55+
// whenever we reach the end of the current line.
4656
let spaceInLineRemaining = bounds.width;
47-
linesUsed += line.split(' ').reduce((used, word, i, words) => {
48-
word = i === words.length - 1 ? word : word + ' ';
57+
for (let idx = 0, len = words.length; idx < len; idx++) {
58+
const isLastWord = idx === len - 1;
59+
const word = isLastWord ? words[idx] : words[idx] + ' ';
4960
const widthOfWord = font.widthOfTextAtSize(word, fontSize);
5061
spaceInLineRemaining -= widthOfWord;
5162
if (spaceInLineRemaining <= 0) {
52-
used++;
63+
linesUsed += 1;
5364
spaceInLineRemaining = bounds.width - widthOfWord;
5465
}
55-
return used;
56-
}, 1);
66+
}
5767
}
5868

69+
// Return if we exceeded the allowed width
5970
if (!multiline && linesUsed > lines.length) return fontSize - 1;
6071

6172
const height = font.heightAtSize(fontSize);
6273
const lineHeight = height + height * 0.2;
6374
const totalHeight = lineHeight * linesUsed;
6475

76+
// Return if we exceeded the allowed height
6577
if (totalHeight > Math.abs(bounds.height)) return fontSize - 1;
6678

6779
fontSize += 1;

‎tests/api/text/layout.spec.ts

+33
Original file line numberDiff line numberDiff line change
@@ -130,4 +130,37 @@ describe(`layoutMultilineText`, () => {
130130
expect(multilineTextLayout.lines.length).toStrictEqual(1);
131131
expect(multilineTextLayout.fontSize).toStrictEqual(MAX_FONT_SIZE);
132132
});
133+
134+
it('should respect empty lines', async () => {
135+
const pdfDoc = await PDFDocument.create();
136+
const font = await pdfDoc.embedFont(StandardFonts.Helvetica);
137+
const alignment = TextAlignment.Left;
138+
const padding = 0;
139+
const borderWidth = 0;
140+
const lines = ['Super Mario Bros.', '', 'Boop'];
141+
const text = lines.join('\n');
142+
143+
for (let fontSize = MIN_FONT_SIZE; fontSize <= MAX_FONT_SIZE; fontSize++) {
144+
const height = font.heightAtSize(fontSize) - (borderWidth + padding) * 2;
145+
146+
const width =
147+
font.widthOfTextAtSize(lines[0], fontSize) -
148+
(borderWidth + padding) * 2;
149+
150+
const bounds = {
151+
x: borderWidth + padding,
152+
y: borderWidth + padding,
153+
width,
154+
height,
155+
};
156+
157+
const multilineTextLayout = layoutMultilineText(text, {
158+
alignment,
159+
bounds,
160+
font,
161+
});
162+
163+
expect(multilineTextLayout.lines.length).toStrictEqual(3);
164+
}
165+
});
133166
});

0 commit comments

Comments
 (0)
Please sign in to comment.