Skip to content

Commit

Permalink
Rewrite PageCanvas to React Hooks
Browse files Browse the repository at this point in the history
  • Loading branch information
wojtekmaj committed Mar 8, 2023
1 parent 2d9fe50 commit a8a5eba
Showing 1 changed file with 96 additions and 104 deletions.
200 changes: 96 additions & 104 deletions src/Page/PageCanvas.jsx
Original file line number Diff line number Diff line change
@@ -1,114 +1,84 @@
import React, { createRef, PureComponent } from 'react';
import React, { createRef, useCallback, useEffect, useMemo } from 'react';
import PropTypes from 'prop-types';
import mergeRefs from 'merge-refs';
import warning from 'tiny-warning';
import * as pdfjs from 'pdfjs-dist/build/pdf';

import PageContext from '../PageContext';

import { getDevicePixelRatio, isCancelException, makePageCallback } from '../shared/utils';
import {
cancelRunningTask,
getDevicePixelRatio,
isCancelException,
makePageCallback,
} from '../shared/utils';

import { isPage, isRef, isRotate } from '../shared/propTypes';

const ANNOTATION_MODE = pdfjs.AnnotationMode;

export class PageCanvasInternal extends PureComponent {
canvasElement = createRef();

componentDidMount() {
this.drawPageOnCanvas();
}

componentDidUpdate(prevProps) {
const { canvasBackground, devicePixelRatio, page, renderForms } = this.props;
if (
canvasBackground !== prevProps.canvasBackground ||
devicePixelRatio !== prevProps.devicePixelRatio ||
renderForms !== prevProps.renderForms
) {
// Ensures the canvas will be re-rendered from scratch. Otherwise all form data will stay.
page.cleanup();
this.drawPageOnCanvas();
}
}

componentWillUnmount() {
this.cancelRenderingTask();

const { current: canvas } = this.canvasElement;

/**
* Zeroing the width and height cause most browsers to release graphics
* resources immediately, which can greatly reduce memory consumption.
*/
if (canvas) {
canvas.width = 0;
canvas.height = 0;
}
}

cancelRenderingTask() {
if (this.renderer) {
this.renderer.cancel();
this.renderer = null;
}
}
export function PageCanvasInternal({
canvasBackground,
canvasRef,
devicePixelRatio: devicePixelRatioProps,
onRenderError: onRenderErrorProps,
onRenderSuccess: onRenderSuccessProps,
page,
renderForms,
rotate: rotateProps,
scale,
}) {
const canvasElement = createRef();

const devicePixelRatio = devicePixelRatioProps || getDevicePixelRatio();

/**
* Called when a page is rendered successfully.
*/
onRenderSuccess = () => {
this.renderer = null;

const { onRenderSuccess, page, scale } = this.props;

if (onRenderSuccess) onRenderSuccess(makePageCallback(page, scale));
};
const onRenderSuccess = useCallback(() => {
if (onRenderSuccessProps) {
onRenderSuccessProps(makePageCallback(page, scale));
}
}, [onRenderSuccessProps, page, scale]);

/**
* Called when a page fails to render.
*/
onRenderError = (error) => {
if (isCancelException(error)) {
return;
}

warning(false, error);

const { onRenderError } = this.props;

if (onRenderError) onRenderError(error);
};

get devicePixelRatio() {
const { devicePixelRatio } = this.props;

return devicePixelRatio || getDevicePixelRatio();
}

get renderViewport() {
const { devicePixelRatio } = this;
const { page, rotate, scale } = this.props;
const onRenderError = useCallback(
(error) => {
if (isCancelException(error)) {
return;
}

warning(false, error);

if (onRenderErrorProps) {
onRenderErrorProps(error);
}
},
[onRenderErrorProps],
);

return page.getViewport({ scale: scale * devicePixelRatio, rotation: rotate });
}
const renderViewport = useMemo(
() => page.getViewport({ scale: scale * devicePixelRatio, rotation: rotateProps }),
[page, rotateProps, scale, devicePixelRatio],
);

get viewport() {
const { page, rotate, scale } = this.props;
const viewport = useMemo(
() => page.getViewport({ scale, rotation: rotateProps }),
[page, rotateProps, scale],
);

return page.getViewport({ scale, rotation: rotate });
}
function drawPageOnCanvas() {
// Ensures the canvas will be re-rendered from scratch. Otherwise all form data will stay.
page.cleanup();

drawPageOnCanvas = () => {
const { current: canvas } = this.canvasElement;
const { current: canvas } = canvasElement;

if (!canvas) {
return null;
}

const { renderViewport, viewport } = this;
const { canvasBackground, page, renderForms } = this.props;

canvas.width = renderViewport.width;
canvas.height = renderViewport.height;

Expand All @@ -126,30 +96,52 @@ export class PageCanvasInternal extends PureComponent {
renderContext.background = canvasBackground;
}

// If another render is in progress, let's cancel it
this.cancelRenderingTask();

const cancellable = page.render(renderContext);
this.renderer = cancellable;

return cancellable.promise.then(this.onRenderSuccess).catch(this.onRenderError);
};

render() {
const { canvasRef } = this.props;

return (
<canvas
className="react-pdf__Page__canvas"
dir="ltr"
ref={mergeRefs(canvasRef, this.canvasElement)}
style={{
display: 'block',
userSelect: 'none',
}}
/>
);
const runningTask = cancellable;

cancellable.promise.then(onRenderSuccess).catch(onRenderError);

return () => cancelRunningTask(runningTask);
}

useEffect(drawPageOnCanvas, [
canvasBackground,
canvasElement,
devicePixelRatio,
onRenderError,
onRenderSuccess,
page,
renderForms,
renderViewport,
viewport,
]);

const cleanup = useCallback(() => {
const { current: canvas } = canvasElement;

/**
* Zeroing the width and height cause most browsers to release graphics
* resources immediately, which can greatly reduce memory consumption.
*/
if (canvas) {
canvas.width = 0;
canvas.height = 0;
}
}, [canvasElement]);

useEffect(() => cleanup, [cleanup]);

return (
<canvas
className="react-pdf__Page__canvas"
dir="ltr"
ref={mergeRefs(canvasRef, canvasElement)}
style={{
display: 'block',
userSelect: 'none',
}}
/>
);
}

PageCanvasInternal.propTypes = {
Expand Down

0 comments on commit a8a5eba

Please sign in to comment.