Skip to content

Commit 2c88d84

Browse files
Merge pull request #1 from rushabh-atharva/blank-pages
fix: handle edge case causing blank pages
2 parents 4aad0f9 + 4e935dd commit 2c88d84

6 files changed

Lines changed: 151 additions & 34 deletions

File tree

packages/layout/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
2-
"name": "@react-pdf/layout",
3-
"version": "4.4.1",
2+
"name": "ats-pdf-layout-4",
3+
"version": "4.4.2",
44
"license": "MIT",
55
"description": "Resolve document component's layout",
66
"author": "Diego Muracciole <diegomuracciole@gmail.com>",

packages/layout/src/node/getWrap.ts

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,38 @@
11
import * as P from '@react-pdf/primitives';
22
import { SafeNode } from '../types';
33

4-
const NON_WRAP_TYPES = [P.Svg, P.Note, P.Image, P.Canvas];
4+
export const NON_WRAP_TYPES = [P.Svg, P.Note, P.Image, P.Canvas];
55

6-
const getWrap = (node: SafeNode) => {
6+
export const canCauseBlankSpace = (node, prevNode, currentChildren) => {
7+
if (!('preventBlankSpace' in node.props)) return false;
8+
9+
const prevNodeHasHeightOne = prevNode?.box?.height === 1;
10+
const childrenIsEmpty = currentChildren?.length === 0;
11+
12+
// padding/margin case
13+
if (prevNodeHasHeightOne === true && childrenIsEmpty === false) {
14+
return true;
15+
}
16+
17+
// gap between content case
18+
if (node.box.height > 680 && childrenIsEmpty === true) {
19+
return true;
20+
}
21+
22+
return false;
23+
};
24+
25+
const getWrap = (
26+
node: SafeNode,
27+
prevNode: SafeNode,
28+
currentChildren: SafeNode[],
29+
) => {
730
if (NON_WRAP_TYPES.includes(node.type)) return false;
831

932
if (!node.props) return true;
1033

34+
if (canCauseBlankSpace(node, prevNode, currentChildren)) return true;
35+
1136
return 'wrap' in node.props ? node.props.wrap : true;
1237
};
1338

packages/layout/src/node/shouldBreak.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ const shouldBreak = (
3737
if ('fixed' in child.props) return false;
3838

3939
const shouldSplit = height < child.box.top + child.box.height;
40-
const canWrap = getWrap(child);
40+
const canWrap = getWrap(child, child, []);
4141

4242
// Calculate the y coordinate where the desired presence of the child ends
4343
const endOfPresence = getEndOfPresence(child, futureElements);

packages/layout/src/steps/resolvePagination.ts

Lines changed: 34 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import FontStore from '@react-pdf/font';
55
import isFixed from '../node/isFixed';
66
import splitText from '../text/splitText';
77
import splitNode from '../node/splitNode';
8-
import canNodeWrap from '../node/getWrap';
8+
import canNodeWrap, { NON_WRAP_TYPES } from '../node/getWrap';
99
import getWrapArea from '../page/getWrapArea';
1010
import getContentArea from '../page/getContentArea';
1111
import createInstances from '../node/createInstances';
@@ -50,6 +50,7 @@ const relayoutPage = compose(
5050
);
5151

5252
const warnUnavailableSpace = (node: SafeNode) => {
53+
// eslint-disable-next-line no-console
5354
console.warn(
5455
`Node of type ${node.type} can't wrap between pages and it's bigger than available page height`,
5556
);
@@ -73,8 +74,9 @@ const splitNodes = (height: number, contentArea: number, nodes: SafeNode[]) => {
7374
height,
7475
currentChildren,
7576
);
77+
const prevChild = nodes.length > 0 && i > 0 ? nodes[i - 1] : undefined;
7678
const shouldSplit = height + SAFETY_THRESHOLD < nodeTop + nodeHeight;
77-
const canWrap = canNodeWrap(child);
79+
const canWrap = canNodeWrap(child, prevChild, currentChildren);
7880
const fitsInsidePage = nodeHeight <= contentArea;
7981

8082
if (isFixed(child)) {
@@ -91,9 +93,24 @@ const splitNodes = (height: number, contentArea: number, nodes: SafeNode[]) => {
9193
}
9294

9395
if (!fitsInsidePage && !canWrap) {
94-
currentChildren.push(child);
95-
nextChildren.push(...futureNodes);
96-
warnUnavailableSpace(child);
96+
if (NON_WRAP_TYPES.includes(child.type)) {
97+
currentChildren.push(child);
98+
nextChildren.push(...futureNodes);
99+
warnUnavailableSpace(child);
100+
} else {
101+
// We don't want to break non wrapable nodes, so we just let them be.
102+
const box = Object.assign({}, child.box, {
103+
top: child.box.top - height,
104+
});
105+
const props = Object.assign({}, child.props, {
106+
wrap: true,
107+
break: false,
108+
});
109+
const next = Object.assign({}, child, { box, props });
110+
111+
currentChildren.push(...futureFixedNodes);
112+
nextChildren.push(next, ...futureNodes);
113+
}
97114
break;
98115
}
99116

@@ -116,18 +133,18 @@ const splitNodes = (height: number, contentArea: number, nodes: SafeNode[]) => {
116133
// All children are moved to the next page, it doesn't make sense to show the parent on the current page
117134
if (child.children.length > 0 && currentChild.children.length === 0) {
118135
// But if the current page is empty then we can just include the parent on the current page
119-
if (currentChildren.length === 0) {
120-
currentChildren.push(child, ...futureFixedNodes);
121-
nextChildren.push(...futureNodes);
122-
} else {
123-
const box = Object.assign({}, child.box, {
124-
top: child.box.top - height,
125-
});
126-
const next = Object.assign({}, child, { box });
127-
128-
currentChildren.push(...futureFixedNodes);
129-
nextChildren.push(next, ...futureNodes);
130-
}
136+
// if (currentChildren.length === 0) {
137+
// currentChildren.push(child, ...futureFixedNodes);
138+
// nextChildren.push(...futureNodes);
139+
// } else {
140+
const box = Object.assign({}, child.box, {
141+
top: child.box.top - height,
142+
});
143+
const next = Object.assign({}, child, { box });
144+
145+
currentChildren.push(...futureFixedNodes);
146+
nextChildren.push(next, ...futureNodes);
147+
// }
131148
break;
132149
}
133150

packages/renderer/package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
2-
"name": "@react-pdf/renderer",
3-
"version": "4.3.1",
2+
"name": "ats-pdf-renderer-4",
3+
"version": "4.3.2",
44
"license": "MIT",
55
"description": "Create PDF files on the browser and server",
66
"author": "Diego Muracciole <diegomuracciole@gmail.com>",
@@ -26,7 +26,7 @@
2626
"@babel/runtime": "^7.20.13",
2727
"@react-pdf/fns": "3.1.2",
2828
"@react-pdf/font": "^4.0.3",
29-
"@react-pdf/layout": "^4.4.1",
29+
"ats-pdf-layout-4": "^4.4.2",
3030
"@react-pdf/pdfkit": "^4.0.4",
3131
"@react-pdf/primitives": "^4.1.1",
3232
"@react-pdf/reconciler": "^1.1.4",

yarn.lock

Lines changed: 84 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -752,8 +752,14 @@
752752
dependencies:
753753
"@babel/helper-plugin-utils" "^7.18.6"
754754

755-
"@babel/plugin-transform-react-jsx-development@^7.18.6", "@babel/plugin-transform-react-jsx-self@^7.23.3":
756-
name "@babel/plugin-transform-react-jsx-development"
755+
"@babel/plugin-transform-react-jsx-development@^7.18.6":
756+
version "7.23.3"
757+
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.23.3.tgz#ed3e7dadde046cce761a8e3cf003a13d1a7972d9"
758+
integrity sha512-qXRvbeKDSfwnlJnanVRp0SfuWE5DQhwQr5xtLBzp56Wabyo+4CMosF6Kfp+eOD/4FYpql64XVJ2W0pVLlJZxOQ==
759+
dependencies:
760+
"@babel/helper-plugin-utils" "^7.22.5"
761+
762+
"@babel/plugin-transform-react-jsx-self@^7.23.3":
757763
version "7.23.3"
758764
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.23.3.tgz#ed3e7dadde046cce761a8e3cf003a13d1a7972d9"
759765
integrity sha512-qXRvbeKDSfwnlJnanVRp0SfuWE5DQhwQr5xtLBzp56Wabyo+4CMosF6Kfp+eOD/4FYpql64XVJ2W0pVLlJZxOQ==
@@ -2180,6 +2186,40 @@
21802186
unbzip2-stream "1.4.3"
21812187
yargs "17.7.2"
21822188

2189+
"@react-pdf/layout@^4.4.1":
2190+
version "4.4.1"
2191+
resolved "https://registry.yarnpkg.com/@react-pdf/layout/-/layout-4.4.1.tgz#50c95084f703cf2a1395e8b2e48300b57b3a74fe"
2192+
integrity sha512-GVzdlWoZWldRDzlWj3SttRXmVDxg7YfraAohwy+o9gb9hrbDJaaAV6jV3pc630Evd3K46OAzk8EFu8EgPDuVuA==
2193+
dependencies:
2194+
"@react-pdf/fns" "3.1.2"
2195+
"@react-pdf/image" "^3.0.3"
2196+
"@react-pdf/primitives" "^4.1.1"
2197+
"@react-pdf/stylesheet" "^6.1.1"
2198+
"@react-pdf/textkit" "^6.0.0"
2199+
"@react-pdf/types" "^2.9.1"
2200+
emoji-regex-xs "^1.0.0"
2201+
queue "^6.0.1"
2202+
yoga-layout "^3.2.1"
2203+
2204+
"@react-pdf/renderer@^4.3.1":
2205+
version "4.3.1"
2206+
resolved "https://registry.yarnpkg.com/@react-pdf/renderer/-/renderer-4.3.1.tgz#0fdc86a0c5d7f92565cda3bef393ef5d374a7d5f"
2207+
integrity sha512-dPKHiwGTaOsKqNWCHPYYrx8CDfAGsUnV4tvRsEu0VPGxuot1AOq/M+YgfN/Pb+MeXCTe2/lv6NvA8haUtj3tsA==
2208+
dependencies:
2209+
"@babel/runtime" "^7.20.13"
2210+
"@react-pdf/fns" "3.1.2"
2211+
"@react-pdf/font" "^4.0.3"
2212+
"@react-pdf/layout" "^4.4.1"
2213+
"@react-pdf/pdfkit" "^4.0.4"
2214+
"@react-pdf/primitives" "^4.1.1"
2215+
"@react-pdf/reconciler" "^1.1.4"
2216+
"@react-pdf/render" "^4.3.1"
2217+
"@react-pdf/types" "^2.9.1"
2218+
events "^3.3.0"
2219+
object-assign "^4.1.1"
2220+
prop-types "^15.6.2"
2221+
queue "^6.0.1"
2222+
21832223
"@rollup/plugin-alias@^5.1.0":
21842224
version "5.1.0"
21852225
resolved "https://registry.yarnpkg.com/@rollup/plugin-alias/-/plugin-alias-5.1.0.tgz#99a94accc4ff9a3483be5baeedd5d7da3b597e93"
@@ -8959,8 +8999,7 @@ randombytes@^2.1.0:
89598999
loose-envify "^1.1.0"
89609000
object-assign "^4.1.1"
89619001

8962-
"react-19@npm:react@19.0.0-rc-66855b96-20241106", react@19.0.0-rc-66855b96-20241106:
8963-
name react-19
9002+
"react-19@npm:react@19.0.0-rc-66855b96-20241106":
89649003
version "19.0.0-rc-66855b96-20241106"
89659004
resolved "https://registry.yarnpkg.com/react/-/react-19.0.0-rc-66855b96-20241106.tgz#f04d7283454a32bdd8e9757db4308b75b9739e56"
89669005
integrity sha512-klH7xkT71SxRCx4hb1hly5FJB21Hz0ACyxbXYAECEqssUjtJeFUAaI2U1DgJAzkGEnvEm3DkxuBchMC/9K4ipg==
@@ -8984,8 +9023,14 @@ randombytes@^2.1.0:
89849023
object-assign "^4.1.1"
89859024
scheduler "^0.20.2"
89869025

8987-
"react-dom-19@npm:react-dom@19.0.0-rc-66855b96-20241106", react-dom@19.0.0-rc-66855b96-20241106:
8988-
name react-dom-19
9026+
"react-dom-19@npm:react-dom@19.0.0-rc-66855b96-20241106":
9027+
version "19.0.0-rc-66855b96-20241106"
9028+
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-19.0.0-rc-66855b96-20241106.tgz#beba73decfd1b9365a3c83673a298623b15acb0b"
9029+
integrity sha512-D25vdaytZ1wFIRiwNU98NPQ/upS2P8Co4/oNoa02PzHbh8deWdepjm5qwZM/46OdSiGv4WSWwxP55RO9obqJEQ==
9030+
dependencies:
9031+
scheduler "0.25.0-rc-66855b96-20241106"
9032+
9033+
react-dom@19.0.0-rc-66855b96-20241106:
89899034
version "19.0.0-rc-66855b96-20241106"
89909035
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-19.0.0-rc-66855b96-20241106.tgz#beba73decfd1b9365a3c83673a298623b15acb0b"
89919036
integrity sha512-D25vdaytZ1wFIRiwNU98NPQ/upS2P8Co4/oNoa02PzHbh8deWdepjm5qwZM/46OdSiGv4WSWwxP55RO9obqJEQ==
@@ -9037,6 +9082,11 @@ react-refresh@^0.14.0:
90379082
resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.14.0.tgz#4e02825378a5f227079554d4284889354e5f553e"
90389083
integrity sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ==
90399084

9085+
react@19.0.0-rc-66855b96-20241106:
9086+
version "19.0.0-rc-66855b96-20241106"
9087+
resolved "https://registry.yarnpkg.com/react/-/react-19.0.0-rc-66855b96-20241106.tgz#f04d7283454a32bdd8e9757db4308b75b9739e56"
9088+
integrity sha512-klH7xkT71SxRCx4hb1hly5FJB21Hz0ACyxbXYAECEqssUjtJeFUAaI2U1DgJAzkGEnvEm3DkxuBchMC/9K4ipg==
9089+
90409090
react@^18, react@^18.2.0:
90419091
version "18.3.1"
90429092
resolved "https://registry.yarnpkg.com/react/-/react-18.3.1.tgz#49ab892009c53933625bd16b2533fc754cab2891"
@@ -10095,7 +10145,7 @@ string-argv@0.3.1:
1009510145
resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.3.1.tgz#95e2fbec0427ae19184935f816d74aaa4c5c19da"
1009610146
integrity sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==
1009710147

10098-
"string-width-cjs@npm:string-width@^4.2.0", "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
10148+
"string-width-cjs@npm:string-width@^4.2.0":
1009910149
version "4.2.3"
1010010150
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
1010110151
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
@@ -10113,6 +10163,15 @@ string-width@^1.0.1:
1011310163
is-fullwidth-code-point "^1.0.0"
1011410164
strip-ansi "^3.0.0"
1011510165

10166+
"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
10167+
version "4.2.3"
10168+
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
10169+
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
10170+
dependencies:
10171+
emoji-regex "^8.0.0"
10172+
is-fullwidth-code-point "^3.0.0"
10173+
strip-ansi "^6.0.1"
10174+
1011610175
string-width@^5.0.1, string-width@^5.1.2:
1011710176
version "5.1.2"
1011810177
resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794"
@@ -10231,7 +10290,7 @@ stringify-object@^3.3.0:
1023110290
is-obj "^1.0.1"
1023210291
is-regexp "^1.0.0"
1023310292

10234-
"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1:
10293+
"strip-ansi-cjs@npm:strip-ansi@^6.0.1":
1023510294
version "6.0.1"
1023610295
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
1023710296
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
@@ -10245,6 +10304,13 @@ strip-ansi@^3.0.0, strip-ansi@^3.0.1:
1024510304
dependencies:
1024610305
ansi-regex "^2.0.0"
1024710306

10307+
strip-ansi@^6.0.0, strip-ansi@^6.0.1:
10308+
version "6.0.1"
10309+
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
10310+
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
10311+
dependencies:
10312+
ansi-regex "^5.0.1"
10313+
1024810314
strip-ansi@^7.0.1:
1024910315
version "7.1.0"
1025010316
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45"
@@ -11291,7 +11357,7 @@ wordwrap@^1.0.0:
1129111357
resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb"
1129211358
integrity sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=
1129311359

11294-
"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0:
11360+
"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0":
1129511361
version "7.0.0"
1129611362
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
1129711363
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
@@ -11309,6 +11375,15 @@ wrap-ansi@^6.0.1, wrap-ansi@^6.2.0:
1130911375
string-width "^4.1.0"
1131011376
strip-ansi "^6.0.0"
1131111377

11378+
wrap-ansi@^7.0.0:
11379+
version "7.0.0"
11380+
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
11381+
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
11382+
dependencies:
11383+
ansi-styles "^4.0.0"
11384+
string-width "^4.1.0"
11385+
strip-ansi "^6.0.0"
11386+
1131211387
wrap-ansi@^8.1.0:
1131311388
version "8.1.0"
1131411389
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214"

0 commit comments

Comments
 (0)