Support for line breaking on hyphens (#451)

* support for line breaking on hyphens

* update test pdfs
This commit is contained in:
Peter Ward
2024-03-22 00:16:53 +00:00
committed by GitHub
parent 4fbc7d9f1e
commit 3a35e7e800
5 changed files with 36 additions and 10 deletions

View File

Binary file not shown.

View File

@@ -74,23 +74,29 @@ describe('getSplitPosition test with mocked font width calculations', () => {
expect(getSplittedLines('aaaa', mockCalcValues)).toEqual(['aaaa']);
});
it('splits a line to the nearest previous space', () => {
it('splits a line to the nearest previous breakable char', () => {
expect(getSplittedLines('aaa bbb', mockCalcValues)).toEqual(['aaa', 'bbb']);
expect(getSplittedLines('top-hat', mockCalcValues)).toEqual(['top-', 'hat']);
expect(getSplittedLines('top—hat', mockCalcValues)).toEqual(['top—', 'hat']); // em dash
expect(getSplittedLines('tophat', mockCalcValues)).toEqual(['top', 'hat']); // en dash
});
it('splits a line where the split point is on a space', () => {
it('splits a line where the split point is on a breakable char', () => {
expect(getSplittedLines('aaaaa bbbbb', mockCalcValues)).toEqual(['aaaaa', 'bbbbb']);
expect(getSplittedLines('left-hand', mockCalcValues)).toEqual(['left-', 'hand']);
});
it('splits a long line in the middle of a word if too long', () => {
expect(getSplittedLines('aaaaaa bbb', mockCalcValues)).toEqual(['aaaaa', 'a bbb']);
expect(getSplittedLines('aaaaaa-a b', mockCalcValues)).toEqual(['aaaaa', 'a-a b']);
expect(getSplittedLines('aaaaa-aa b', mockCalcValues)).toEqual(['aaaaa', '-aa b']);
});
it('splits a long line without spaces at exactly 5 chars', () => {
it('splits a long line without breakable chars at exactly 5 chars', () => {
expect(getSplittedLines('abcdef', mockCalcValues)).toEqual(['abcde', 'f']);
});
it('splits a very long line without spaces at exactly 5 chars', () => {
it('splits a very long line without breakable chars at exactly 5 chars', () => {
expect(getSplittedLines('abcdefghijklmn', mockCalcValues)).toEqual(['abcde', 'fghij', 'klmn']);
});

View File

@@ -152,21 +152,41 @@ const getOverPosition = (textLine: string, calcValues: FontWidthCalcValues) => {
return null;
};
/**
* Line breakable chars depend on the language and writing system.
* Western writing systems typically use spaces and hyphens as line breakable chars.
* Other writing systems often break on word boundaries so the following
* does not negatively impact them.
* However, this might need to be revisited for broader language support.
*/
const isLineBreakableChar = (char: string) => {
const lineBreakableChars = [' ', '-', "\u2014", "\u2013"];
return lineBreakableChars.includes(char);
}
/**
* Gets the position of the split. Splits the exceeding line at
* the last whitespace prior to it exceeding the bounding box width.
* the last breakable char prior to it exceeding the bounding box width.
*/
const getSplitPosition = (textLine: string, calcValues: FontWidthCalcValues) => {
const overPos = getOverPosition(textLine, calcValues);
if (overPos === null) return textLine.length; // input line is shorter than the available space
let overPosTmp = overPos;
while (textLine[overPosTmp] !== ' ' && overPosTmp >= 0) {
if (textLine[overPos] === ' ') {
// if the character immediately beyond the boundary is a space, split
return overPos;
}
let overPosTmp = overPos - 1;
while (overPosTmp >= 0) {
if (isLineBreakableChar(textLine[overPosTmp])) {
return overPosTmp+1;
}
overPosTmp--;
}
// For very long lines with no whitespace use the original overPos
return overPosTmp > 0 ? overPosTmp : overPos;
// For very long lines with no breakable chars use the original overPos
return overPos;
};
/**
@@ -175,7 +195,7 @@ const getSplitPosition = (textLine: string, calcValues: FontWidthCalcValues) =>
*/
export const getSplittedLines = (textLine: string, calcValues: FontWidthCalcValues): string[] => {
const splitPos = getSplitPosition(textLine, calcValues);
const splittedLine = textLine.substring(0, splitPos);
const splittedLine = textLine.substring(0, splitPos).trimEnd();
const rest = textLine.substring(splitPos).trimStart();
if (rest === textLine) {