diff --git a/.changeset/smart-maps-pay.md b/.changeset/smart-maps-pay.md new file mode 100644 index 000000000..2efa024fc --- /dev/null +++ b/.changeset/smart-maps-pay.md @@ -0,0 +1,5 @@ +--- +'@react-pdf/textkit': patch +--- + +Improve text line slicing performance for long paragraphs. diff --git a/packages/textkit/src/engines/linebreaker/index.ts b/packages/textkit/src/engines/linebreaker/index.ts index db55a1c6a..57f15b39a 100644 --- a/packages/textkit/src/engines/linebreaker/index.ts +++ b/packages/textkit/src/engines/linebreaker/index.ts @@ -32,12 +32,14 @@ const breakLines = ( let start = 0; let end = null; - const lines: AttributedString[] = breaks.reduce((acc, breakPoint) => { + const lines: AttributedString[] = []; + + for (const breakPoint of breaks) { const node = nodes[breakPoint]; const prevNode = nodes[breakPoint - 1]; // Last breakpoint corresponds to K&P mandatory final glue - if (breakPoint === nodes.length - 1) return acc; + if (breakPoint === nodes.length - 1) continue; let line: AttributedString; if (node.type === 'penalty') { @@ -54,8 +56,8 @@ const breakLines = ( start = end; - return [...acc, line]; - }, []); + lines.push(line); + } lines.push(slice(start, attributedString.string.length, attributedString)); diff --git a/packages/textkit/src/run/glyphIndexAt.ts b/packages/textkit/src/run/glyphIndexAt.ts index a64d09967..d49bfd8f0 100644 --- a/packages/textkit/src/run/glyphIndexAt.ts +++ b/packages/textkit/src/run/glyphIndexAt.ts @@ -23,12 +23,18 @@ const glyphIndexAt = (index: number, run: Run) => { return glyphIndices.length; } + let low = 0; + let high = glyphIndices.length - 1; let result = index; - for (let i = glyphIndices.length - 1; i >= 0; i -= 1) { - if (glyphIndices[i] <= index) { - result = i; - break; + while (low <= high) { + const mid = (low + high) >> 1; + + if (glyphIndices[mid] <= index) { + result = mid; + low = mid + 1; + } else { + high = mid - 1; } } diff --git a/packages/textkit/src/run/offset.ts b/packages/textkit/src/run/offset.ts index 289d97c77..d7ca95351 100644 --- a/packages/textkit/src/run/offset.ts +++ b/packages/textkit/src/run/offset.ts @@ -19,7 +19,15 @@ const offset = (index: number, run: Run) => { const stringIndices = run.stringIndices || []; const value = stringIndices[index]; - return stringIndices.slice(0, index).filter((i) => i === value).length; + if (value === undefined) return 0; + + let result = 0; + + for (let i = index - 1; i >= 0 && stringIndices[i] === value; i -= 1) { + result += 1; + } + + return result; }; export default offset;