Replies: 65 comments
-
@jesusgp22 You probably want to use some kind of virtualization library for displaying PDF's with a lot of pages, like react-virtualized for instance. Maybe this is useful to you. |
Beta Was this translation helpful? Give feedback.
-
Hey, thank you so much for your answer, I'll def check this out You might want to add a note about this on react-pdf documentation to help others with the same performance issues or even in the future add this as a core feature for large docs. |
Beta Was this translation helpful? Give feedback.
-
Following up on this @michaeldzjap I am watching some presentations on react-virtualized and it will break text search feature, is this a trade off that I can't get around? |
Beta Was this translation helpful? Give feedback.
-
I am not familiar with the text search feature I have to admit. But I suspect that it relies on the text layer for each page to be rendered in order to be able to find all the relevant results for a specific search query (e.g. a word that could be located anywhere in the document). The whole point of virtualizing a collection of elements ( I don't think there is an easy way around this unfortunately. A solution could be to keep a virtual representation of a text layer of each page in memory (like how React does this for HTML elements) and search through that instead. Might be possible. |
Beta Was this translation helpful? Give feedback.
-
That's an interesting approach, I am guessing this will most likely break browser text search feature anyway, in any case I think it is ok to implement this using just a regular search box element. Now the questions are:
|
Beta Was this translation helpful? Give feedback.
-
I think you would need to dig into pdf.js for this, relying on the react-pdf api probably is not enough. You can get the text content for a page using this apparently: page.getTextContent().then(function(textContent) { ... });
Yes, that is a tricky one... You'd know the page number. Maybe it should be a 2 step operation or something. 1 - Search through the virtual text layers for a query. Keep a result of all pages that match. 2 - For each page in the result of step 1 see if it is rendered, if it is you can probably find the word rather easily, because I think each word is rendered as a separate HTML element in a text layer. If the page is not rendered yet, scroll to it with react-virtualized so that it will be rendered and then again find the HTML element that contains the first occurrence of the word/query in the text layer element tree. Something like the above. I might think too simplistic about this, I haven't actually tried it myself. But this is how I would approach things initially I think. |
Beta Was this translation helpful? Give feedback.
-
I was wondering if the biggest performance issue was rendering the text layer or the canvas, in case rendering the canvas is an issue, it might be possible to ask pdf.js to only render the text layer? |
Beta Was this translation helpful? Give feedback.
-
@jesusgp22 Nope, you can toggle text content and annotations on/off, but canvas is currently not behind a flag. I don't see a good reason against having it toggleable, though :) |
Beta Was this translation helpful? Give feedback.
-
@michaeldzjap Any reason for this? If you use |
Beta Was this translation helpful? Give feedback.
-
@wojtekmaj Yes, my wording was rather poor. What I meant is that
Yes. This is exactly what I do to cache all document page widths and height on initial load when using react-pdf together with react-virtualized. |
Beta Was this translation helpful? Give feedback.
-
Thank you both for this amazing discussion 👍 |
Beta Was this translation helpful? Give feedback.
-
We are also having trouble loading long PDFs. We are loading a 17mb PDFs and the application crashes, and since we have customers with 100mb+ PDFs, crashing is not an option. This example which is also a react wrapper to PDF.js seem to work for us. It tricks PDF.js to load only the current visible page and the ten previous pages. It looks like it has something to do with the wrapper div's styles, because when you change some of the styles it loses it lazy loading behaviour. I couldnt reproduce this trick to your lib. But we liked react-pdf so much that we are still trying to adapt this lazy load trick to it. We like the fact that your lib has no default toolbox and that it has mapped its props to pdf.js handlers/configs, so we can develop our customized toolbox. So we would be glad to see it working better with long pdfs, maybe using this trick that yurydelendik/pdfjs-react uses (thats a shame that I couldnt reproduce it with your lib! ) |
Beta Was this translation helpful? Give feedback.
-
@MarcoNicolodi I found that react-virtualized worked really bad with react-pdf I implemented the aproach to only render a few pages but to make things work you have to render a div that has the dimensions of the pages you don't render you can 100% integrate this with react-pdf using the document object that is returned by react-pdf and use getPage and page.getViewport methods to get the page dimensions I built my own algorithm to detect what pages are visible and I run it everytime the user scrolls or if a resize event happens. |
Beta Was this translation helpful? Give feedback.
-
Hey everyone, There is some good news too, though. If I can suggest something, import React, { Component } from 'react';
import { Document, Page } from 'react-pdf/build/entry.webpack';
import './Sample.less';
export default class Sample extends Component {
state = {
file: './test.pdf',
numPages: null,
pagesRendered: null,
}
onDocumentLoadSuccess = ({ numPages }) =>
this.setState({
numPages,
pagesRendered: 0,
});
onRenderSuccess = () =>
this.setState(prevState => ({
pagesRendered: prevState.pagesRendered + 1,
}));
render() {
const { file, numPages, pagesRendered } = this.state;
/**
* The amount of pages we want to render now. Always 1 more than already rendered,
* no more than total amount of pages in the document.
*/
const pagesRenderedPlusOne = Math.min(pagesRendered + 1, numPages);
return (
<div className="Example">
<header>
<h1>react-pdf sample page</h1>
</header>
<div className="Example__container">
<div className="Example__container__document">
<Document
file={file}
onLoadSuccess={this.onDocumentLoadSuccess}
>
{
Array.from(
new Array(pagesRenderedPlusOne),
(el, index) => {
const isCurrentlyRendering = pagesRenderedPlusOne === index + 1;
const isLastPage = numPages === index + 1;
const needsCallbackToRenderNextPage = isCurrentlyRendering && !isLastPage;
return (
<Page
key={`page_${index + 1}`}
onRenderSuccess={
needsCallbackToRenderNextPage ? this.onRenderSuccess : null
}
pageNumber={index + 1}
/>
);
},
)
}
</Document>
</div>
</div>
</div>
);
}
} Of course you can do much more - add placeholders, check on scroll which pages need rendering, keep info on whether all pages so far were rendered... I believe in your creativity ;) And if I can be of any help regarding API, please let me know! |
Beta Was this translation helpful? Give feedback.
-
Hey, may you share this example? |
Beta Was this translation helpful? Give feedback.
-
this is a demo for large pdfs: https://github.com/zhoumy96/react-pdf-large-files |
Beta Was this translation helpful? Give feedback.
-
"without performance optimization" is the key here. React-PDF is NOT a PDF viewer - it is only a tool to build one. If you want to browse 100 page PDFs, you need to take similar precautions as if you were trying to open 100 images at once, or 100 videos, or whatever. You wouldn't open them all at once, would you?
You can, as long as Range header is supported by the server you're serving the content from. |
Beta Was this translation helpful? Give feedback.
-
@zhoumy96 Good example. Rendering pages only when they are actually needed is a key for performant PDF viewer. |
Beta Was this translation helpful? Give feedback.
-
Here's my take on hooking React-PDF to React-Window. https://codesandbox.io/s/react-pdf-react-window-x3xzzg |
Beta Was this translation helpful? Give feedback.
-
Would you mind giving an example of how to do this? I tried setting My PDF files are hosted in an AWS S3 bucket which does support range requests to my knowledge. |
Beta Was this translation helpful? Give feedback.
-
Hmm, not sure about that. I'm pretty sure PDF.js will request only as much data as needed, if it's possible, e.g. when you only want to display Page 1. If it's not happening, it's on PDF.js side. There may be something else that I don't know about that might prevent partial download from happening, e.g. PDF built in a specific way or something. |
Beta Was this translation helpful? Give feedback.
-
@wojtekmaj Thank you for providing the great demo. When I try to implement it in my application and load a large pdf file I notice the memory is keep increasing when I keep loading following pages or switch between pages. And the memory are not released until I close the browser tab.. Any idea how can we optimize it? Thank you! |
Beta Was this translation helpful? Give feedback.
-
Ok, I figured out what the problem here was: The PDF files have to be "linearized", which means that they are saved in a way so that the file can be requested in chunks. On a Mac, I just opened the PDF in Preview, reordered a page and put it back (otherwise it doesn't save if no changes are made), and hit File -> Save. It should save linearized by default. On Windows you will probably have to find a third party app to do it (like Acrobat). I hope that helps anyone that was having the same issue. |
Beta Was this translation helpful? Give feedback.
-
This is working perfectly. Thankyou so much EDIT: Any help to make the navigation button work in these codes? EDIT #2: So the solution to going to specific page is given by react-window documents React-window . Previous Page and next page can also be done similarly |
Beta Was this translation helpful? Give feedback.
-
I think still load the full PDF if you pass in <Document... onLoadProgress={onDocumentLoadProgress}
Logs will display the total load |
Beta Was this translation helpful? Give feedback.
-
@wojtekmaj @EricLiu0614 Any update on this. Even I had a similar problem, when i play around with scroll on huge pdfs, there seems to be a memory leak, which is causing page crash. |
Beta Was this translation helpful? Give feedback.
-
The browser does not expose Accept-Ranges and Content-Range by default. These two headers will cause pdf.js to mistakenly think that the server does not support range requests, and then directly request the entire file. My CORS configuration for s3 bucket.
Expose these header to let react-pdf known about the headers that it need to stream |
Beta Was this translation helpful? Give feedback.
-
The memory leak seems to be due to code sandbox and not react-pdf + react-window example. Did you try to run the example locally? |
Beta Was this translation helpful? Give feedback.
This comment was marked as spam.
This comment was marked as spam.
-
I have published the following library for rendering large PDFs. It's not yet in 1.0 but it's useful for rendering very large PDFs and lazily loading each pdf. https://github.com/jkgenser/react-pdf-headless This is also published to NPM: https://www.npmjs.com/package/react-pdf-headless The DEMO app is a full example of this library being used: https://github.com/jkgenser/react-pdf-headless/blob/main/demo/App.tsx It only depends on react-pdf and @tanstack/virtual |
Beta Was this translation helpful? Give feedback.
-
This might be a good question for pdf.js community itself but how does rendering large PDFs can be better handled with react-pdf?
pdf.js suggests not rendering more than 25 pages at a time:
https://github.com/mozilla/pdf.js/wiki/Frequently-Asked-Questions#allthepages
I even had to add this to my component to keep react from trying re-create the virtual DOM of the Document:
The problem is that I also need to dynamically set the width of the document on user interacting so I can't save myself from re-creating the virtual DOM after width changes, any way I can achieve this with your lib?
Beta Was this translation helpful? Give feedback.
All reactions