Skip to content

Commit

Permalink
fix: fetch assets inside dynamic content (#1369, #1587, #1630, #1936, #…
Browse files Browse the repository at this point in the history
  • Loading branch information
mskec committed Feb 26, 2024
1 parent fab09cc commit ffb096b
Show file tree
Hide file tree
Showing 4 changed files with 230 additions and 34 deletions.
70 changes: 43 additions & 27 deletions packages/layout/src/steps/resolvePagination.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
/* eslint-disable prefer-destructuring */

import * as P from '@react-pdf/primitives';
import { isNil, omit, compose } from '@react-pdf/fns';
import { isNil, omit, asyncCompose } from '@react-pdf/fns';

import isFixed from '../node/isFixed';
import splitText from '../text/splitText';
Expand All @@ -17,6 +17,7 @@ import resolveTextLayout from './resolveTextLayout';
import resolveInheritance from './resolveInheritance';
import { resolvePageDimensions } from './resolveDimensions';
import { resolvePageStyles } from './resolveStyles';
import resolveAssets from './resolveAssets';

const isText = (node) => node.type === P.Text;

Expand All @@ -32,7 +33,8 @@ const allFixed = (nodes) => nodes.every(isFixed);

const isDynamic = (node) => !isNil(node.props?.render);

const relayoutPage = compose(
const relayoutPage = asyncCompose(
resolveAssets,
resolveTextLayout,
resolvePageDimensions,
resolveInheritance,
Expand Down Expand Up @@ -175,19 +177,20 @@ const resolveDynamicNodes = (props, node) => {
return Object.assign({}, node, { box, lines, children });
};

const resolveDynamicPage = (props, page, fontStore, yoga) => {
const resolveDynamicPage = async (props, page, fontStore, yoga) => {
if (shouldResolveDynamicNodes(page)) {
const resolvedPage = resolveDynamicNodes(props, page);
return relayoutPage(resolvedPage, fontStore, yoga);
const relayoutedPage = await relayoutPage(resolvedPage, fontStore, yoga);
return relayoutedPage;
}

return page;
};

const splitPage = (page, pageNumber, fontStore, yoga) => {
const splitPage = async (page, pageNumber, fontStore, yoga) => {
const wrapArea = getWrapArea(page);
const contentArea = getContentArea(page);
const dynamicPage = resolveDynamicPage({ pageNumber }, page, fontStore, yoga);
const dynamicPage = await resolveDynamicPage({ pageNumber }, page, fontStore, yoga);
const height = page.style.height;

const [currentChilds, nextChilds] = splitNodes(
Expand All @@ -196,10 +199,10 @@ const splitPage = (page, pageNumber, fontStore, yoga) => {
dynamicPage.children,
);

const relayout = (node) => relayoutPage(node, fontStore, yoga);
const relayout = async node => relayoutPage(node, fontStore, yoga);

const currentBox = { ...page.box, height };
const currentPage = relayout(
const currentPage = await relayout(
Object.assign({}, page, { box: currentBox, children: currentChilds }),
);

Expand All @@ -209,7 +212,7 @@ const splitPage = (page, pageNumber, fontStore, yoga) => {
const nextBox = omit('height', page.box);
const nextProps = omit('bookmark', page.props);

const nextPage = relayout(
const nextPage = await relayout(
Object.assign({}, page, {
props: nextProps,
box: nextBox,
Expand All @@ -220,7 +223,7 @@ const splitPage = (page, pageNumber, fontStore, yoga) => {
return [currentPage, nextPage];
};

const resolvePageIndices = (fontStore, yoga, page, pageNumber, pages) => {
const resolvePageIndices = async (fontStore, yoga, page, pageNumber, pages) => {
const totalPages = pages.length;

const props = {
Expand All @@ -233,24 +236,25 @@ const resolvePageIndices = (fontStore, yoga, page, pageNumber, pages) => {
return resolveDynamicPage(props, page, fontStore, yoga);
};

const assocSubPageData = (subpages) => {
const assocSubPageData = (subpages, pageIndex) => {
return subpages.map((page, i) => ({
...page,
pageIndex,
subPageNumber: i,
subPageTotalPages: subpages.length,
}));
};

const dissocSubPageData = (page) => {
return omit(['subPageNumber', 'subPageTotalPages'], page);
const dissocSubPageData = page => {
return omit(['pageIndex', 'subPageNumber', 'subPageTotalPages'], page);
};

const paginate = (page, pageNumber, fontStore, yoga) => {
const paginate = async (page, pageNumber, fontStore, yoga) => {
if (!page) return [];

if (page.props?.wrap === false) return [page];

let splittedPage = splitPage(page, pageNumber, fontStore, yoga);
let splittedPage = await splitPage(page, pageNumber, fontStore, yoga);

const pages = [splittedPage[0]];
let nextPage = splittedPage[1];
Expand All @@ -271,28 +275,40 @@ const paginate = (page, pageNumber, fontStore, yoga) => {
};

/**
* Performs pagination. This is the step responsible of breaking the whole document
* into pages following pagiation rules, such as `fixed`, `break` and dynamic nodes.
* Performs pagination. This is the step responsible for breaking the whole document
* into pages following pagination rules, such as `fixed`, `break` and dynamic nodes.
*
* @param {Object} doc node
* @param {Object} fontStore font store
* @returns {Object} layout node
*/
const resolvePagination = (doc, fontStore) => {
const resolvePagination = async (doc, fontStore) => {
let pages = [];
let pageNumber = 1;

for (let i = 0; i < doc.children.length; i += 1) {
const page = doc.children[i];
let subpages = paginate(page, pageNumber, fontStore, doc.yoga);
await Promise.all(
doc.children.map(async (page, pageIndex) => {
let subpages = await paginate(page, pageNumber, fontStore, doc.yoga);

subpages = assocSubPageData(subpages);
pageNumber += subpages.length;
pages = pages.concat(subpages);
}
subpages = assocSubPageData(subpages, pageIndex);
pageNumber += subpages.length;
pages.push(...subpages);
}),
);

// because the subpages are pushed into the array according to the speed they are paginated,
// we sort them by their initial index, while keeping the subpages order.
pages.sort((a, b) => {
if (a.pageIndex !== b.pageIndex) {
return a.pageIndex - b.pageIndex;
}
return a.subPageNumber - b.subPageNumber;
});

pages = pages.map((...args) =>
dissocSubPageData(resolvePageIndices(fontStore, doc.yoga, ...args)),
pages = await Promise.all(
pages.map(async (...args) =>
dissocSubPageData(await resolvePageIndices(fontStore, doc.yoga, ...args)),
),
);

return assingChildren(pages, doc);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { describe, expect, test } from 'vitest';

import { loadYoga } from '../../src/yoga';
import { Text } from '@react-pdf/primitives';

import resolvePagination from '../../src/steps/resolvePagination';
import resolveDimensions from '../../src/steps/resolveDimensions';
import { loadYoga } from '../../src/yoga';

// dimensions is required by pagination step and them are calculated here
const calcLayout = (node) => resolvePagination(resolveDimensions(node));
Expand Down Expand Up @@ -53,7 +53,7 @@ describe('pagination step', () => {
],
};

const layout = calcLayout(root);
const layout = await calcLayout(root);

const page = layout.children[0];
const view = layout.children[0].children[0];
Expand Down Expand Up @@ -103,7 +103,7 @@ describe('pagination step', () => {
],
};

const layout = calcLayout(root);
const layout = await calcLayout(root);

const view1 = layout.children[0].children[0];
const view2 = layout.children[1].children[0];
Expand Down Expand Up @@ -140,7 +140,7 @@ describe('pagination step', () => {
],
};

const layout = calcLayout(root);
const layout = await calcLayout(root);

const view1 = layout.children[0].children[0];
const view2 = layout.children[1].children[0];
Expand All @@ -151,6 +151,158 @@ describe('pagination step', () => {
expect(view3.box.height).toBe(10);
});

test('should calculate height and keep the page order', async () => {
const root = {
type: 'DOCUMENT',
children: [
{
type: 'PAGE',
box: {},
style: {
width: 5,
height: 60,
},
children: [
{
type: 'VIEW',
box: {},
style: { height: 18 },
props: { fixed: true },
children: [],
},
{
type: 'VIEW',
box: {},
style: { height: 30 },
props: {},
children: [],
},
{
type: 'VIEW',
box: {},
style: { height: 57 },
props: {},
children: [],
},
{
type: 'VIEW',
box: {},
style: { height: 15 },
props: {},
children: [],
},
],
},
{
type: 'PAGE',
box: {},
style: {
height: 50,
},
children: [
{
type: 'VIEW',
box: {},
style: {},
props: {
fixed: true,
render: () => <Text style={{ height: 10 }}>rear window</Text>,
},
children: [],
},
{
type: 'VIEW',
box: {},
style: { height: 22 },
props: {},
children: [],
},
],
},
{
type: 'PAGE',
box: {},
style: {
height: 40,
},
children: [
{
type: 'VIEW',
box: {},
style: { height: 12 },
props: {},
children: [],
},
],
},
{
type: 'PAGE',
box: {},
style: {
height: 30,
},
children: [
{
type: 'VIEW',
box: {},
style: {},
props: {},
children: [],
},
],
},
],
};

const layout = await calcLayout(root);

const page1 = layout.children[0];
const [view1, view2, view3] = page1.children;

const page2 = layout.children[1];
const [view4, view5] = page2.children;

const page3 = layout.children[2];
const [view6, view7, view8] = page3.children;

const page4 = layout.children[3];
const [view9, view10] = page4.children;

const page5 = layout.children[4];
const [view11] = page5.children;

const page6 = layout.children[5];

// page 1
expect(view1.box.height).toBe(18); // fixed header
expect(view2.box.height).toBe(30);
expect(view3.box.height).toBe(12);
expect(page1.box.height).toBe(60);

// page 2
expect(view4.box.height).toBe(18); // fixed header
expect(view5.box.height).toBe(42);
expect(page2.box.height).toBe(60);

// page 3
expect(view6.box.height).toBe(18); // fixed header
expect(view7.box.height).toBe(3);
expect(view8.box.height).toBe(15);
expect(page3.box.height).toBe(60);

// page 4
expect(view9.box.height).toBe(10);
expect(view10.box.height).toBe(22);
expect(page4.box.height).toBe(50);

// page 5
expect(page5.box.height).toBe(40);
expect(view11.box.height).toBe(12);

// page 6
expect(page6.box.height).toBe(30);
});

test('should not wrap page with false wrap prop', async () => {
const yoga = await loadYoga();

Expand Down Expand Up @@ -181,7 +333,7 @@ describe('pagination step', () => {
],
};

const layout = calcLayout(root);
const layout = await calcLayout(root);

expect(layout.children.length).toBe(1);
});
Expand Down Expand Up @@ -238,7 +390,7 @@ describe('pagination step', () => {
],
};

const layout = calcLayout(root);
const layout = await calcLayout(root);

const page1 = layout.children[0];
const page2 = layout.children[1];
Expand Down
Loading

0 comments on commit ffb096b

Please sign in to comment.