Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/lemon-dolphins-hunt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@react-pdf/layout": patch
---

Fix yoga error Invalid array length at Array.push(<anonymous>)
Fix infinite loop while wrapping pages
5 changes: 4 additions & 1 deletion packages/layout/src/node/shouldBreak.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { SafeNode } from '../types';
import getWrap from './getWrap';
import isFixed from './isFixed';

const getBreak = (node: SafeNode) =>
'break' in node.props ? node.props.break : false;
Expand Down Expand Up @@ -31,6 +32,7 @@ const shouldBreak = (
child: SafeNode,
futureElements: SafeNode[],
height: number,
previousElements: SafeNode[],
) => {
if ('fixed' in child.props) return false;

Expand All @@ -42,7 +44,8 @@ const shouldBreak = (

// If the child is already at the top of the page, breaking won't improve its presence
// (as long as react-pdf does not support breaking into differently sized containers)
const breakingImprovesPresence = child.box.top > child.box.marginTop;
const breakingImprovesPresence =
previousElements.filter((node: SafeNode) => !isFixed(node)).length > 0;

return (
getBreak(child) ||
Expand Down
7 changes: 6 additions & 1 deletion packages/layout/src/steps/resolvePagination.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,12 @@ const splitNodes = (height: number, contentArea: number, nodes: SafeNode[]) => {
const nodeTop = getTop(child);
const nodeHeight = child.box.height;
const isOutside = height <= nodeTop;
const shouldBreak = shouldNodeBreak(child, futureNodes, height);
const shouldBreak = shouldNodeBreak(
child,
futureNodes,
height,
currentChildren,
);
const shouldSplit = height + SAFETY_THRESHOLD < nodeTop + nodeHeight;
const canWrap = canNodeWrap(child);
const fitsInsidePage = nodeHeight <= contentArea;
Expand Down
2 changes: 1 addition & 1 deletion packages/layout/src/text/measureText.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ const measureText =
if (widthMode === Yoga.MeasureMode.Exactly) {
if (!node.lines) node.lines = layoutText(node, width, height, fontStore);

return { height: linesHeight(node) };
return { height: linesHeight(node), width };
}

if (widthMode === Yoga.MeasureMode.AtMost) {
Expand Down
116 changes: 115 additions & 1 deletion packages/layout/tests/node/shouldBreak.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ describe('node shouldBreak', () => {
},
[],
1000,
[],
);

expect(result).toEqual(false);
Expand All @@ -31,6 +32,7 @@ describe('node shouldBreak', () => {
},
[],
1000,
[],
);

expect(result).toEqual(true);
Expand All @@ -54,6 +56,7 @@ describe('node shouldBreak', () => {
},
[],
1000,
[],
);

expect(result).toEqual(false);
Expand All @@ -77,6 +80,7 @@ describe('node shouldBreak', () => {
},
[],
1000,
[],
);

expect(result).toEqual(true);
Expand All @@ -100,6 +104,7 @@ describe('node shouldBreak', () => {
},
[],
1000,
[],
);

expect(result).toEqual(true);
Expand Down Expand Up @@ -142,6 +147,24 @@ describe('node shouldBreak', () => {
},
],
1000,
[
{
type: 'VIEW',
props: {},
style: {},
children: [],
box: {
top: 900,
right: 0,
bottom: 0,
left: 0,
height: 200,
width: 200,
marginTop: 0,
marginBottom: 0,
},
},
],
);

expect(result).toEqual(true);
Expand Down Expand Up @@ -184,6 +207,24 @@ describe('node shouldBreak', () => {
},
],
1000,
[
{
type: 'VIEW',
props: {},
style: {},
children: [],
box: {
top: 900,
right: 0,
bottom: 0,
left: 0,
height: 200,
width: 200,
marginTop: 0,
marginBottom: 0,
},
},
],
);

expect(result).toEqual(true);
Expand Down Expand Up @@ -226,6 +267,7 @@ describe('node shouldBreak', () => {
},
],
1000,
[],
);

expect(result).toEqual(false);
Expand Down Expand Up @@ -268,6 +310,7 @@ describe('node shouldBreak', () => {
},
],
1000,
[],
);

expect(result).toEqual(false);
Expand Down Expand Up @@ -310,6 +353,7 @@ describe('node shouldBreak', () => {
},
],
1000,
[],
);

expect(result).toEqual(false);
Expand Down Expand Up @@ -352,12 +396,56 @@ describe('node shouldBreak', () => {
},
],
1000,
[],
);

expect(result).toEqual(false);
});

test('should not break due to minPresenceAhead when breaking does not improve presence because the node is already the first non-fixed node on the page, to avoid infinite loops', () => {
const result = shouldBreak(
{
type: 'VIEW',
props: { minPresenceAhead: 400 },
style: {},
children: [],
box: {
top: 500,
right: 0,
bottom: 0,
left: 0,
height: 400,
width: 200,
marginTop: 500,
marginBottom: 0,
},
},
[
{
type: 'VIEW',
props: {},
style: {},
children: [],
box: {
top: 900,
right: 0,
bottom: 0,
left: 0,
height: 200,
width: 200,
marginTop: 0,
marginBottom: 0,
},
},
],
1000,
[],
);

expect(result).toEqual(false);
});

test('should not break due to minPresenceAhead when breaking does not improve presence, to avoid infinite loops', () => {
test('should not break due to minPresenceAhead even when there are some previous fixed nodes on the page, to avoid infinite loops', () => {
const result = shouldBreak(
{
type: 'VIEW',
Expand Down Expand Up @@ -394,6 +482,26 @@ describe('node shouldBreak', () => {
},
],
1000,
[
{
type: 'VIEW',
props: {
fixed: true,
},
style: {},
children: [],
box: {
top: 900,
right: 0,
bottom: 0,
left: 0,
height: 200,
width: 200,
marginTop: 0,
marginBottom: 0,
},
},
],
);

expect(result).toEqual(false);
Expand Down Expand Up @@ -436,6 +544,7 @@ describe('node shouldBreak', () => {
},
],
1000,
[],
);

expect(result).toEqual(false);
Expand Down Expand Up @@ -478,6 +587,7 @@ describe('node shouldBreak', () => {
},
],
1000,
[],
);

expect(result).toEqual(false);
Expand Down Expand Up @@ -536,6 +646,7 @@ describe('node shouldBreak', () => {
},
],
811.89,
[],
);

expect(result).toEqual(false);
Expand Down Expand Up @@ -594,6 +705,7 @@ describe('node shouldBreak', () => {
},
],
811.89,
[],
);

expect(result).toEqual(false);
Expand Down Expand Up @@ -721,6 +833,7 @@ describe('node shouldBreak', () => {
},
],
781.89,
[],
);

expect(result).toEqual(false);
Expand Down Expand Up @@ -763,6 +876,7 @@ describe('node shouldBreak', () => {
},
],
776.89,
[],
);

expect(result).toEqual(false);
Expand Down
63 changes: 63 additions & 0 deletions packages/layout/tests/steps/resolvePagination.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -276,4 +276,67 @@ describe('pagination step', () => {
// If calcLayout returns then we did not hit an infinite loop
expect(true).toBe(true);
});

test('should take padding into account when splitting pages', async () => {
const yoga = await loadYoga();

const root = {
type: 'DOCUMENT' as const,
yoga,
props: {},
style: {},
children: [
{
type: 'PAGE' as const,
box: {
width: 612,
height: 792,
top: 0,
left: 0,
right: 612,
bottom: 792,
},
style: {
paddingTop: 30,
width: 612,
height: 792,
},
props: { wrap: true },
children: [
{
type: 'VIEW' as const,
box: {
width: 612,
height: 761,
top: 0,
left: 0,
right: 612,
bottom: 761,
},
style: { height: 761, marginBottom: 24 },
props: { wrap: true, break: false },
},
{
type: 'VIEW' as const,
box: {
width: 612,
height: 80,
top: 761,
left: 0,
right: 612,
bottom: 841,
},
style: { height: 80 },
props: { wrap: true, break: false },
},
],
},
],
};

calcLayout(root);

// If calcLayout returns then we did not hit an infinite loop
expect(true).toBe(true);
});
});
Loading