-
Notifications
You must be signed in to change notification settings - Fork 3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #33083 from artus9033/proposal/32502
fix: upgrade to newer react-pdf and pdf.js to render high-resolution images properly on the web
- Loading branch information
Showing
9 changed files
with
410 additions
and
4,289 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
diff --git a/node_modules/react-pdf/dist/esm/Document.js b/node_modules/react-pdf/dist/esm/Document.js | ||
index ac7ca31..56bc766 100644 | ||
--- a/node_modules/react-pdf/dist/esm/Document.js | ||
+++ b/node_modules/react-pdf/dist/esm/Document.js | ||
@@ -240,6 +240,7 @@ const Document = forwardRef(function Document(_a, ref) { | ||
pdfDispatch({ type: 'REJECT', error }); | ||
}); | ||
return () => { | ||
+ loadingTask._worker.destroy(); | ||
loadingTask.destroy(); | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
import 'core-js/features/array/at'; | ||
import PropTypes from 'prop-types'; | ||
import React, {memo, useCallback} from 'react'; | ||
import {Document} from 'react-pdf'; | ||
import {VariableSizeList as List} from 'react-window'; | ||
import _ from 'underscore'; | ||
import FullScreenLoadingIndicator from '@components/FullscreenLoadingIndicator'; | ||
import Text from '@components/Text'; | ||
import stylePropTypes from '@styles/stylePropTypes'; | ||
import CONST from '@src/CONST'; | ||
import PageRenderer from './WebPDFPageRenderer'; | ||
|
||
const propTypes = { | ||
/** Index of the PDF page to be displayed passed by VariableSizeList */ | ||
errorLabelStyles: stylePropTypes, | ||
/** Returns translated string for given locale and phrase */ | ||
translate: PropTypes.func.isRequired, | ||
/** The source URL from which to load PDF file to be displayed */ | ||
sourceURL: PropTypes.string.isRequired, | ||
/** Callback invoked when the PDF document is loaded successfully */ | ||
onDocumentLoadSuccess: PropTypes.func.isRequired, | ||
/** Viewport info of all PDF pages */ | ||
pageViewportsLength: PropTypes.number.isRequired, | ||
/** Sets attributes to list container */ | ||
setListAttributes: PropTypes.func.isRequired, | ||
/** Indicates, whether the screen is of small width */ | ||
isSmallScreenWidth: PropTypes.bool.isRequired, | ||
/** Height of PDF document container view */ | ||
containerHeight: PropTypes.number.isRequired, | ||
/** Width of PDF document container view */ | ||
containerWidth: PropTypes.number.isRequired, | ||
/** The number of pages of the PDF file to be rendered */ | ||
numPages: PropTypes.number, | ||
/** Function that calculates the height of a page of the PDF document */ | ||
calculatePageHeight: PropTypes.func.isRequired, | ||
/** Function that calculates the devicePixelRatio the page should be rendered with */ | ||
getDevicePixelRatio: PropTypes.func.isRequired, | ||
/** The estimated height of a single PDF page for virtualized rendering purposes */ | ||
estimatedItemSize: PropTypes.number.isRequired, | ||
/** The width of a page in the PDF file */ | ||
pageWidth: PropTypes.number.isRequired, | ||
/** The style applied to the list component */ | ||
listStyle: stylePropTypes, | ||
/** Function that should initiate that the user should be prompted for password to the PDF file */ | ||
initiatePasswordChallenge: PropTypes.func.isRequired, | ||
/** Either: | ||
* - `string` - the password provided by the user to unlock the PDF file | ||
* - `undefined` if password isn't needed to view the PDF file | ||
* - `null` if the password is required but hasn't been provided yet */ | ||
password: PropTypes.string, | ||
}; | ||
|
||
const defaultProps = { | ||
errorLabelStyles: [], | ||
numPages: null, | ||
listStyle: undefined, | ||
password: undefined, | ||
}; | ||
|
||
const WebPDFDocument = memo( | ||
({ | ||
errorLabelStyles, | ||
translate, | ||
sourceURL, | ||
onDocumentLoadSuccess, | ||
pageViewportsLength, | ||
setListAttributes, | ||
isSmallScreenWidth, | ||
containerHeight, | ||
containerWidth, | ||
numPages, | ||
calculatePageHeight, | ||
getDevicePixelRatio, | ||
estimatedItemSize, | ||
pageWidth, | ||
listStyle, | ||
initiatePasswordChallenge, | ||
password, | ||
}) => { | ||
const onPassword = useCallback( | ||
(callback, reason) => { | ||
if (reason === CONST.PDF_PASSWORD_FORM.REACT_PDF_PASSWORD_RESPONSES.NEED_PASSWORD) { | ||
if (password) { | ||
callback(password); | ||
} else { | ||
initiatePasswordChallenge(reason); | ||
} | ||
} else if (reason === CONST.PDF_PASSWORD_FORM.REACT_PDF_PASSWORD_RESPONSES.INCORRECT_PASSWORD) { | ||
initiatePasswordChallenge(reason); | ||
} | ||
}, | ||
[password, initiatePasswordChallenge], | ||
); | ||
|
||
return ( | ||
<Document | ||
loading={<FullScreenLoadingIndicator />} | ||
error={<Text style={errorLabelStyles}>{translate('attachmentView.failedToLoadPDF')}</Text>} | ||
file={sourceURL} | ||
options={{ | ||
cMapUrl: 'cmaps/', | ||
cMapPacked: true, | ||
}} | ||
externalLinkTarget="_blank" | ||
onLoadSuccess={onDocumentLoadSuccess} | ||
onPassword={onPassword} | ||
> | ||
{!!pageViewportsLength && ( | ||
<List | ||
outerRef={setListAttributes} | ||
style={listStyle} | ||
width={isSmallScreenWidth ? pageWidth : containerWidth} | ||
height={containerHeight} | ||
estimatedItemSize={estimatedItemSize} | ||
itemCount={numPages} | ||
itemSize={calculatePageHeight} | ||
itemData={{pageWidth, calculatePageHeight, getDevicePixelRatio, estimatedItemSize}} | ||
> | ||
{PageRenderer} | ||
</List> | ||
)} | ||
</Document> | ||
); | ||
}, | ||
(prevProps, nextProps) => _.isEqual(prevProps, nextProps), | ||
); | ||
|
||
WebPDFDocument.displayName = 'WebPDFDocument'; | ||
WebPDFDocument.propTypes = propTypes; | ||
WebPDFDocument.defaultProps = defaultProps; | ||
|
||
export default WebPDFDocument; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
import PropTypes from 'prop-types'; | ||
import React, {memo} from 'react'; | ||
import {View} from 'react-native'; | ||
import {Page} from 'react-pdf'; | ||
import _ from 'underscore'; | ||
import stylePropTypes from '@styles/stylePropTypes'; | ||
import PDFViewConstants from './constants'; | ||
|
||
const propTypes = { | ||
/** Index of the PDF page to be displayed passed by VariableSizeList */ | ||
index: PropTypes.number.isRequired, | ||
|
||
/** Page extra data passed by VariableSizeList's data prop */ | ||
data: PropTypes.shape({ | ||
/** Width of a single page in the document */ | ||
pageWidth: PropTypes.number.isRequired, | ||
/** Function that calculates the height of a page given its index */ | ||
calculatePageHeight: PropTypes.func.isRequired, | ||
/** Function that calculates the pixel ratio for a page given its calculated width and height */ | ||
getDevicePixelRatio: PropTypes.func.isRequired, | ||
/** The estimated height of a single page in the document */ | ||
estimatedItemSize: PropTypes.number.isRequired, | ||
}).isRequired, | ||
|
||
/** Additional style props passed by VariableSizeList */ | ||
style: stylePropTypes.isRequired, | ||
}; | ||
|
||
const WebPDFPageRenderer = memo( | ||
({index: pageIndex, data, style}) => { | ||
const {pageWidth, calculatePageHeight, getDevicePixelRatio, estimatedItemSize} = data; | ||
|
||
const pageHeight = calculatePageHeight(pageIndex); | ||
const devicePixelRatio = getDevicePixelRatio(pageWidth, pageHeight); | ||
|
||
return ( | ||
<View style={{...style, top: `${parseFloat(style.top) + PDFViewConstants.PAGE_BORDER}px`}}> | ||
<Page | ||
key={`page_${pageIndex}`} | ||
width={pageWidth} | ||
height={pageHeight || estimatedItemSize} | ||
pageIndex={pageIndex} | ||
// This needs to be empty to avoid multiple loading texts which show per page and look ugly | ||
// See https://github.com/Expensify/App/issues/14358 for more details | ||
loading="" | ||
devicePixelRatio={devicePixelRatio} | ||
/> | ||
</View> | ||
); | ||
}, | ||
(prevProps, nextProps) => _.isEqual(prevProps, nextProps), | ||
); | ||
|
||
WebPDFPageRenderer.displayName = 'WebPDFPageRenderer'; | ||
WebPDFPageRenderer.propTypes = propTypes; | ||
|
||
export default WebPDFPageRenderer; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
/** | ||
* Each page has a default border. The app should take this size into account | ||
* when calculates the page width and height. | ||
*/ | ||
const PAGE_BORDER = 9; | ||
|
||
/** | ||
* Pages should be more narrow than the container on large screens. The app should take this size into account | ||
* when calculates the page width. | ||
*/ | ||
const LARGE_SCREEN_SIDE_SPACING = 40; | ||
|
||
const REQUIRED_PASSWORD_MISSING = null; | ||
|
||
export default {PAGE_BORDER, LARGE_SCREEN_SIDE_SPACING, REQUIRED_PASSWORD_MISSING}; |
Oops, something went wrong.