React-pdf: How can I find out if an element is at the end of the page in order to wrap it to the next page if it doesn't fit?

Created on 15 May 2019  路  3Comments  路  Source: diegomura/react-pdf

I have bunch of tables in the PDF file. If a table reaches the end of the pages, I want to render table headers in the next page along with the rest of the data instead of wrapping it. Is it possible with React PDF? The generated content is all dynamic (fetched from server); so there is no way for me to know the location of the object beforehand. I want to implement this dynamically.

Most helpful comment

@DavidMich1 I do not think this would work. As @tommmyy suggested, it is impossible to calculate the exact height of each element based on number of items. Because sometimes the text can wrap to multiple lines and this will be impossible to calculate properly.

Due to the limitations of React PDF, I have simplified my requirement for what I wanted to achieve and utilized wrap property of views to move content to the next page. By grouping several elements in a single group, I was able to move the whole group to the next page, which worked fine even if there is no way to properly isolate components as different parts of the UI must be combined together to be wrapped properly. What I mean by the last word is best explained by some snippets:

I have created two components -- Section and Table. I needed to wrap the content to the next page in the following way: If Section Title, Table's Header, and Table's first Row do not fit to the page, wrap it to the next page. This is currently impossible (at least what is there based on docs) to achieve because the View components are all combined inside my custom components. In order to resolve my issue, I moved Section Title into Table component and split table data's first row and created something like this:

// Table.js
<>
   <View wrap={false}>
      <SectionTitle title={title} />
      <TableHeader columns={columns} />
      <TableContent data={[firstRow]} />
   </View>
   <TableContent data={restOfTheRows} />
</>

I have a suggested solution to this issue. What if we have a component with a ref or better, what if we have a hook that we can attach to these components that will automatically group the components for wrapping. Something like the following:

const Doc = () => {
  const wrapRef = useWrapper();

  return (
    <>
       ...
       <SectionTitle wrap={wrapRef} title="Title" />
       <Table wrap={wrapRef} data={[...]} />
     </>
  );
}

Then, the Table component can forward the wrap reference to its internal components. As a result, wrapping will be calculated based on components in isolation, which will improve developer experience because it would be much easier to create custom components and wrap parts of each component while maintaining code reusability.

---

Regarding the issue of knowing what to do if a component is wrapped, I think it would also be useful to add a behavior to `render` prop to use wrapping for some custom functionality. For example,

{movedToNextPage &&

}
Render the component

```

This way, we can render additional components if we know for sure that the element is moved to the next page due to lack of space. These types of things are useful when displaying tables in PDF. For example, if the table has data that wraps to multiple pages, it might be useful to show table headers at the beginning of each page.

All 3 comments

A way to do it would be to addition the table's height to a total of pixel (or whatever the measure you want to use) which would represent the maximum in which your table can fit into a page. You can do the same with the rows of a table. Example: An A4 page's height is 842 pixels, subtract this by some padding so let's say it's now 800 pixels. Each row of a table is 25 pixels. By now you can guess how to do the math to check if it exceed the limit. If it does, close the Page tag and start a new one.

But what if the height of the table's row is not always the same?

Above that, there is none of white-space, word-break or word-wrap CSS properties implemented.

Another problem: what if the table starts somewhere in the middle of a page?

@DavidMich1 I do not think this would work. As @tommmyy suggested, it is impossible to calculate the exact height of each element based on number of items. Because sometimes the text can wrap to multiple lines and this will be impossible to calculate properly.

Due to the limitations of React PDF, I have simplified my requirement for what I wanted to achieve and utilized wrap property of views to move content to the next page. By grouping several elements in a single group, I was able to move the whole group to the next page, which worked fine even if there is no way to properly isolate components as different parts of the UI must be combined together to be wrapped properly. What I mean by the last word is best explained by some snippets:

I have created two components -- Section and Table. I needed to wrap the content to the next page in the following way: If Section Title, Table's Header, and Table's first Row do not fit to the page, wrap it to the next page. This is currently impossible (at least what is there based on docs) to achieve because the View components are all combined inside my custom components. In order to resolve my issue, I moved Section Title into Table component and split table data's first row and created something like this:

// Table.js
<>
   <View wrap={false}>
      <SectionTitle title={title} />
      <TableHeader columns={columns} />
      <TableContent data={[firstRow]} />
   </View>
   <TableContent data={restOfTheRows} />
</>

I have a suggested solution to this issue. What if we have a component with a ref or better, what if we have a hook that we can attach to these components that will automatically group the components for wrapping. Something like the following:

const Doc = () => {
  const wrapRef = useWrapper();

  return (
    <>
       ...
       <SectionTitle wrap={wrapRef} title="Title" />
       <Table wrap={wrapRef} data={[...]} />
     </>
  );
}

Then, the Table component can forward the wrap reference to its internal components. As a result, wrapping will be calculated based on components in isolation, which will improve developer experience because it would be much easier to create custom components and wrap parts of each component while maintaining code reusability.

---

Regarding the issue of knowing what to do if a component is wrapped, I think it would also be useful to add a behavior to `render` prop to use wrapping for some custom functionality. For example,

{movedToNextPage &&

}
Render the component

```

This way, we can render additional components if we know for sure that the element is moved to the next page due to lack of space. These types of things are useful when displaying tables in PDF. For example, if the table has data that wraps to multiple pages, it might be useful to show table headers at the beginning of each page.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

idrisadetunmbi picture idrisadetunmbi  路  3Comments

emt1803 picture emt1803  路  3Comments

benbenedek picture benbenedek  路  3Comments

pavle-lekic-htec picture pavle-lekic-htec  路  4Comments

cheald picture cheald  路  3Comments