Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Calculating the devicePixelRatio for the pdf to display with less pixels than the limit of the platform #25721

Merged
merged 22 commits into from
Aug 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
e0708ec
Calculating the devicePixelRatio for the pdf to display with less pix…
ShogunFire Aug 22, 2023
ea8bc98
I forgot to send the prop devicePixelRatio to the page
ShogunFire Aug 22, 2023
342688f
We also need to make sure the height doesn't surpass the height limit
ShogunFire Aug 22, 2023
57e6499
If the ratio we calculated is bigger than the defaut ratio, we don't …
ShogunFire Aug 23, 2023
7d34e6a
Adding ratioWidth and removing variables that don't need to be in the…
ShogunFire Aug 24, 2023
5c90a87
I forgot to remove this
ShogunFire Aug 24, 2023
7e837eb
Adding canvas-size to package-lock.json
ShogunFire Aug 24, 2023
012fa2a
Adding the canvas limits to onyx to not have to calculate them everyt…
ShogunFire Aug 25, 2023
217e448
Merge remote-tracking branch 'origin/main' into fixPdfBrokenMaxCanvas…
ShogunFire Aug 25, 2023
7c33146
Fix lint errors and move the onyx merges in a new action file
ShogunFire Aug 25, 2023
145f15f
Wrong name for ONYX keys width
ShogunFire Aug 25, 2023
5fedae8
Merge remote-tracking branch 'origin/main' into fixPdfBrokenMaxCanvas…
ShogunFire Aug 25, 2023
a8c3bfe
Change imports order and remove unused
ShogunFire Aug 25, 2023
609c856
Add comment and after npm run prettier
ShogunFire Aug 25, 2023
bebb637
Adding the last space
ShogunFire Aug 25, 2023
ea4e5e8
Adding comments, improving code and memoizing getDevicePixelRatio to …
ShogunFire Aug 28, 2023
5e8851b
Update src/ONYXKEYS.ts comment for max area
ShogunFire Aug 28, 2023
7dbd94d
Update src/ONYXKEYS.ts comment for max height
ShogunFire Aug 28, 2023
3400f2d
Update src/ONYXKEYS.ts comment for max width
ShogunFire Aug 28, 2023
20fa08e
Changing methods order for lint
ShogunFire Aug 28, 2023
b80abb3
Merge remote-tracking branch 'origin/fixPdfBrokenMaxCanvasSize' into …
ShogunFire Aug 28, 2023
9565272
Putting getDevicePixelRatio above other methods for lint
ShogunFire Aug 28, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@
"awesome-phonenumber": "^5.4.0",
"babel-plugin-transform-remove-console": "^6.9.4",
"babel-polyfill": "^6.26.0",
"canvas-size": "^1.2.6",
"core-js": "^3.32.0",
"date-fns": "^2.30.0",
"date-fns-tz": "^2.0.0",
Expand Down
12 changes: 12 additions & 0 deletions src/ONYXKEYS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,15 @@ const ONYXKEYS = {
// Information on any active demos being run
DEMO_INFO: 'demoInfo',

// Max area supported for HTML <canvas> element
MAX_CANVAS_AREA: 'maxCanvasArea',

// Max height supported for HTML <canvas> element
MAX_CANVAS_HEIGHT: 'maxCanvasHeight',

// Max width supported for HTML <canvas> element
MAX_CANVAS_WIDTH: 'maxCanvasWidth',

/** Collection Keys */
COLLECTION: {
DOWNLOAD: 'download_',
Expand Down Expand Up @@ -352,6 +361,9 @@ type OnyxValues = {
[ONYXKEYS.MAPBOX_ACCESS_TOKEN]: OnyxTypes.MapboxAccessToken;
[ONYXKEYS.ONYX_UPDATES_FROM_SERVER]: number;
[ONYXKEYS.ONYX_UPDATES_LAST_UPDATE_ID_APPLIED_TO_CLIENT]: number;
[ONYXKEYS.MAX_CANVAS_AREA]: number;
[ONYXKEYS.MAX_CANVAS_HEIGHT]: number;
[ONYXKEYS.MAX_CANVAS_WIDTH]: number;

// Collections
[ONYXKEYS.COLLECTION.DOWNLOAD]: OnyxTypes.Download;
Expand Down
58 changes: 57 additions & 1 deletion src/components/PDFView/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import 'core-js/features/array/at';
import {Document, Page, pdfjs} from 'react-pdf/dist/esm/entry.webpack';
import pdfWorkerSource from 'pdfjs-dist/legacy/build/pdf.worker';
import {VariableSizeList as List} from 'react-window';
import {withOnyx} from 'react-native-onyx';
import * as CanvasSize from '../../libs/actions/CanvasSize';
import FullScreenLoadingIndicator from '../FullscreenLoadingIndicator';
import styles from '../../styles/styles';
import variables from '../../styles/variables';
Expand All @@ -17,6 +19,7 @@ import Text from '../Text';
import compose from '../../libs/compose';
import PressableWithoutFeedback from '../Pressable/PressableWithoutFeedback';
import Log from '../../libs/Log';
import ONYXKEYS from '../../ONYXKEYS';

/**
* Each page has a default border. The app should take this size into account
Expand Down Expand Up @@ -48,10 +51,12 @@ class PDFView extends Component {
this.calculatePageHeight = this.calculatePageHeight.bind(this);
this.calculatePageWidth = this.calculatePageWidth.bind(this);
this.renderPage = this.renderPage.bind(this);
this.getDevicePixelRatio = _.memoize(this.getDevicePixelRatio);
this.setListAttributes = this.setListAttributes.bind(this);

const workerBlob = new Blob([pdfWorkerSource], {type: 'text/javascript'});
pdfjs.GlobalWorkerOptions.workerSrc = URL.createObjectURL(workerBlob);
this.retrieveCanvasLimits();
}

componentDidUpdate(prevProps) {
Expand Down Expand Up @@ -114,6 +119,23 @@ class PDFView extends Component {
ref.tabIndex = -1;
}

/**
* Calculate the devicePixelRatio the page should be rendered with
* Each platform has a different default devicePixelRatio and different canvas limits, we need to verify that
* with the default devicePixelRatio it will be able to diplay the pdf correctly, if not we must change the devicePixelRatio.
* @param {Number} width of the page
* @param {Number} height of the page
* @returns {Number} devicePixelRatio for this page on this platform
*/
getDevicePixelRatio(width, height) {
const nbPixels = width * height;
const ratioHeight = this.props.maxCanvasHeight / height;
const ratioWidth = this.props.maxCanvasWidth / width;
const ratioArea = Math.sqrt(this.props.maxCanvasArea / nbPixels);
const ratio = Math.min(ratioHeight, ratioArea, ratioWidth);
return ratio > window.devicePixelRatio ? undefined : ratio;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What would happen if we pass undefined to the page here?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What would happen if we pass undefined to the page here?

@pecanoro, the default devicePixelRatio would be used, same as before
By default, it matches screen device pixel ratio, so for example 3 on most iPhones, 1 on standard resolution office monitor
This does return undefined for most PDFs, only the ones that won't fit require a custom devicePixelRatio

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the explanation, just making sure I understand the code properly 😄

}

/**
* Calculates a proper page height. The method should be called only when there are page viewports.
* It is based on a ratio between the specific page viewport width and provided page width.
Expand Down Expand Up @@ -194,6 +216,23 @@ class PDFView extends Component {
this.props.onToggleKeyboard(isKeyboardOpen);
}

/**
* Verify that the canvas limits have been calculated already, if not calculate them and put them in Onyx
*/
retrieveCanvasLimits() {
if (!this.props.maxCanvasArea) {
CanvasSize.retrieveMaxCanvasArea();
}

if (!this.props.maxCanvasHeight) {
CanvasSize.retrieveMaxCanvasHeight();
}

if (!this.props.maxCanvasWidth) {
CanvasSize.retrieveMaxCanvasWidth();
}
}

/**
* Render a specific page based on its index.
* The method includes a wrapper to apply virtualized styles.
Expand All @@ -204,6 +243,8 @@ class PDFView extends Component {
*/
renderPage({index, style}) {
const pageWidth = this.calculatePageWidth();
const pageHeight = this.calculatePageHeight(index);
const devicePixelRatio = this.getDevicePixelRatio(pageWidth, pageHeight);

return (
<View style={style}>
Expand All @@ -214,6 +255,7 @@ class PDFView extends Component {
// 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>
);
Expand Down Expand Up @@ -299,4 +341,18 @@ class PDFView extends Component {
PDFView.propTypes = pdfViewPropTypes.propTypes;
PDFView.defaultProps = pdfViewPropTypes.defaultProps;

export default compose(withLocalize, withWindowDimensions)(PDFView);
export default compose(
withLocalize,
withWindowDimensions,
withOnyx({
maxCanvasArea: {
key: ONYXKEYS.MAX_CANVAS_AREA,
},
maxCanvasHeight: {
key: ONYXKEYS.MAX_CANVAS_HEIGHT,
},
maxCanvasWidth: {
key: ONYXKEYS.MAX_CANVAS_WIDTH,
},
}),
)(PDFView);
43 changes: 43 additions & 0 deletions src/libs/actions/CanvasSize.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import Onyx from 'react-native-onyx';
import canvasSize from 'canvas-size';
import ONYXKEYS from '../../ONYXKEYS';

/**
* Calculate the max area of canvas on this specific platform and save it in onyx
*/
function retrieveMaxCanvasArea() {
canvasSize.maxArea({
onSuccess: (width, height) => {
Onyx.merge(ONYXKEYS.MAX_CANVAS_AREA, width * height);
},
});
}

/**
* Calculate the max height of canvas on this specific platform and save it in onyx
*/
function retrieveMaxCanvasHeight() {
canvasSize.maxHeight({
onSuccess: (width, height) => {
Onyx.merge(ONYXKEYS.MAX_CANVAS_HEIGHT, height);
},
});
}

/**
* Calculate the max width of canvas on this specific platform and save it in onyx
*/
function retrieveMaxCanvasWidth() {
canvasSize.maxWidth({
onSuccess: (width) => {
Onyx.merge(ONYXKEYS.MAX_CANVAS_WIDTH, width);
},
});
}

export {
// eslint-disable-next-line import/prefer-default-export
retrieveMaxCanvasArea,
retrieveMaxCanvasHeight,
retrieveMaxCanvasWidth,
};