Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 3 additions & 1 deletion packages/react-pdf/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -606,6 +606,7 @@ Displays a page. Should be placed inside `<Document />`. Alternatively, it can h

| Prop name | Description | Default value | Example values |
| ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| annotationMode | Controls which annotations are rendered on the page canvas. Takes precedence over `renderForms`. | `AnnotationMode.ENABLE` | `AnnotationMode.ENABLE_FORMS` |
| canvasBackground | Canvas background color. Any valid `canvas.fillStyle` can be used. | n/a | `"transparent"` |
| canvasRef | A prop that behaves like [ref](https://reactjs.org/docs/refs-and-the-dom.html), but it's passed to `<canvas>` rendered by `<Canvas>` component. | n/a | <ul><li>Function:<br />`(ref) => { this.myCanvas = ref; }`</li><li>Ref created using `createRef`:<br />`this.ref = createRef();`<br />…<br />`inputRef={this.ref}`</li><li>Ref created using `useRef`:<br />`const ref = useRef();`<br />…<br />`inputRef={ref}`</li></ul> |
| className | Class name(s) that will be added to rendered element along with the default `react-pdf__Page`. | n/a | <ul><li>String:<br />`"custom-class-name-1 custom-class-name-2"`</li><li>Array of strings:<br />`["custom-class-name-1", "custom-class-name-2"]`</li></ul> |
Expand Down Expand Up @@ -638,7 +639,7 @@ Displays a page. Should be placed inside `<Document />`. Alternatively, it can h
| pageNumber | Which page from PDF file should be displayed, by page number. If provided, `pageIndex` prop will be ignored. | `1` | `2` |
| pdf | pdf object obtained from `<Document />`'s `onLoadSuccess` callback function. | (automatically obtained from parent `<Document />`) | `pdf` |
| renderAnnotationLayer | Whether annotations (e.g. links) should be rendered. | `true` | `false` |
| renderForms | Whether forms should be rendered. `renderAnnotationLayer` prop must be set to `true`. | `false` | `true` |
| renderForms | Whether forms should be rendered. `renderAnnotationLayer` prop must be set to `true`. Ignored when `annotationMode` is defined. | `false` | `true` |
| renderMode | Rendering mode of the document. Can be `"canvas"`, `"custom"` or `"none"`. If set to `"custom"`, `customRenderer` must also be provided. | `"canvas"` | `"custom"` |
| renderTextLayer | Whether a text layer should be rendered. | `true` | `false` |
| rotate | Rotation of the page in degrees. `90` = rotated to the right, `180` = upside down, `270` = rotated to the left. | Page's default setting, usually `0` | `90` |
Expand Down Expand Up @@ -667,6 +668,7 @@ Displays a thumbnail of a page. Does not render the annotation layer or the text

Props are the same as in `<Page />` component, but certain annotation layer and text layer-related props are not available:

- annotationMode
- customTextRenderer
- onGetAnnotationsError
- onGetAnnotationsSuccess
Expand Down
5 changes: 5 additions & 0 deletions packages/react-pdf/src/AnnotationMode.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import * as pdfjs from 'pdfjs-dist';

const AnnotationMode: typeof pdfjs.AnnotationMode = pdfjs.AnnotationMode;

export default AnnotationMode;
54 changes: 54 additions & 0 deletions packages/react-pdf/src/Page.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { page, userEvent } from 'vitest/browser';
import { render } from 'vitest-browser-react';
import { createRef } from 'react';

import AnnotationMode from './AnnotationMode.js';
import DocumentContext from './DocumentContext.js';
import { pdfjs } from './index.test.js';
import LinkService from './LinkService.js';
Expand Down Expand Up @@ -797,6 +798,59 @@ describe('Page', () => {
expect(textWidgetAnnotation).toBeFalsy();
});

it('requests page to be rendered with forms given annotationMode = AnnotationMode.ENABLE_FORMS', async () => {
const { func: onRenderAnnotationLayerSuccess, promise: onRenderAnnotationLayerSuccessPromise } =
makeAsyncCallback();

const { container } = await renderWithContext(
<Page
annotationMode={AnnotationMode.ENABLE_FORMS}
onRenderAnnotationLayerSuccess={onRenderAnnotationLayerSuccess}
pageIndex={0}
renderMode="none"
/>,
{
linkService,
pdf: pdf4,
},
);

expect.assertions(1);

await onRenderAnnotationLayerSuccessPromise;

const textWidgetAnnotation = container.querySelector('.textWidgetAnnotation');

expect(textWidgetAnnotation).toBeTruthy();
});

it('prefers annotationMode over renderForms', async () => {
const { func: onRenderAnnotationLayerSuccess, promise: onRenderAnnotationLayerSuccessPromise } =
makeAsyncCallback();

const { container } = await renderWithContext(
<Page
annotationMode={AnnotationMode.ENABLE}
onRenderAnnotationLayerSuccess={onRenderAnnotationLayerSuccess}
pageIndex={0}
renderForms
renderMode="none"
/>,
{
linkService,
pdf: pdf4,
},
);

expect.assertions(1);

await onRenderAnnotationLayerSuccessPromise;

const textWidgetAnnotation = container.querySelector('.textWidgetAnnotation');

expect(textWidgetAnnotation).toBeFalsy();
});

it('requests page to be rendered with forms given renderForms = true', async () => {
const { func: onRenderAnnotationLayerSuccess, promise: onRenderAnnotationLayerSuccessPromise } =
makeAsyncCallback();
Expand Down
18 changes: 17 additions & 1 deletion packages/react-pdf/src/Page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import mergeRefs from 'merge-refs';
import invariant from 'tiny-invariant';
import warning from 'warning';

import AnnotationMode from './AnnotationMode.js';
import Message from './Message.js';
import AnnotationLayer from './Page/AnnotationLayer.js';
import Canvas from './Page/Canvas.js';
Expand Down Expand Up @@ -53,6 +54,13 @@ const defaultScale = 1;
export type PageProps = {
_className?: string;
_enableRegisterUnregisterPage?: boolean;
/**
* Controls which annotations are rendered on the page canvas. When defined, this prop takes precedence over `renderForms`.
*
* @default AnnotationMode.ENABLE
* @example AnnotationMode.ENABLE_FORMS
*/
annotationMode?: (typeof AnnotationMode)[keyof typeof AnnotationMode];
/**
* Canvas background color. Any valid `canvas.fillStyle` can be used.
*
Expand Down Expand Up @@ -322,6 +330,7 @@ export default function Page(props: PageProps): React.ReactElement {
const {
_className = 'react-pdf__Page',
_enableRegisterUnregisterPage = true,
annotationMode: annotationModeProps,
canvasBackground,
canvasRef,
children,
Expand Down Expand Up @@ -355,7 +364,7 @@ export default function Page(props: PageProps): React.ReactElement {
pdf,
registerPage,
renderAnnotationLayer: renderAnnotationLayerProps = true,
renderForms = false,
renderForms: renderFormsProps = false,
renderMode = 'canvas',
renderTextLayer: renderTextLayerProps = true,
rotate: rotateProps,
Expand All @@ -378,6 +387,11 @@ export default function Page(props: PageProps): React.ReactElement {

const pageNumber = pageNumberProps ?? (isProvided(pageIndexProps) ? pageIndexProps + 1 : null);

const annotationMode =
annotationModeProps ?? (renderFormsProps ? AnnotationMode.ENABLE_FORMS : AnnotationMode.ENABLE);

const renderForms = annotationMode === AnnotationMode.ENABLE_FORMS;

const rotate = rotateProps ?? (page ? page.rotate : null);

const scale = useMemo(() => {
Expand Down Expand Up @@ -510,6 +524,7 @@ export default function Page(props: PageProps): React.ReactElement {
isProvided(pageIndex) && pageNumber && isProvided(rotate) && isProvided(scale)
? {
_className,
annotationMode,
canvasBackground,
customTextRenderer,
devicePixelRatio,
Expand Down Expand Up @@ -538,6 +553,7 @@ export default function Page(props: PageProps): React.ReactElement {
: null,
[
_className,
annotationMode,
canvasBackground,
customTextRenderer,
devicePixelRatio,
Expand Down
40 changes: 38 additions & 2 deletions packages/react-pdf/src/Page/Canvas.spec.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { beforeAll, describe, expect, it, vi } from 'vitest';
import { render } from 'vitest-browser-react';

import AnnotationMode from '../AnnotationMode.js';
import { pdfjs } from '../index.test.js';
import PageContext from '../PageContext.js';
import Canvas from './Canvas.js';
Expand Down Expand Up @@ -44,12 +45,12 @@ describe('Canvas', () => {
page = await pdf.getPage(1);

pageWithRendererMocked = Object.assign(page, {
render: () => ({
render: vi.fn(() => ({
promise: new Promise<void>((resolve) => resolve()),
cancel: () => {
// Intentionally empty
},
}),
})),
});
});

Expand Down Expand Up @@ -92,6 +93,41 @@ describe('Canvas', () => {
});

describe('rendering', () => {
it('passes annotationMode to the PDF.js renderer', async () => {
const { func: onRenderSuccess, promise: onRenderSuccessPromise } = makeAsyncCallback();

await renderWithContext(<Canvas />, {
annotationMode: AnnotationMode.DISABLE,
onRenderSuccess,
page: pageWithRendererMocked,
renderForms: true,
scale: 1,
});

await onRenderSuccessPromise;

expect(pageWithRendererMocked.render).toHaveBeenCalledWith(
expect.objectContaining({ annotationMode: AnnotationMode.DISABLE }),
);
});

it('maps the legacy renderForms prop to AnnotationMode.ENABLE_FORMS', async () => {
const { func: onRenderSuccess, promise: onRenderSuccessPromise } = makeAsyncCallback();

await renderWithContext(<Canvas />, {
onRenderSuccess,
page: pageWithRendererMocked,
renderForms: true,
scale: 1,
});

await onRenderSuccessPromise;

expect(pageWithRendererMocked.render).toHaveBeenCalledWith(
expect.objectContaining({ annotationMode: AnnotationMode.ENABLE_FORMS }),
);
});

it('passes canvas element to canvasRef properly', async () => {
const canvasRef = vi.fn();

Expand Down
10 changes: 5 additions & 5 deletions packages/react-pdf/src/Page/Canvas.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@

import { useCallback, useEffect, useMemo, useRef } from 'react';
import mergeRefs from 'merge-refs';
import * as pdfjs from 'pdfjs-dist';
import invariant from 'tiny-invariant';
import warning from 'warning';

import AnnotationMode from '../AnnotationMode.js';
import StructTree from '../StructTree.js';

import usePageContext from '../shared/hooks/usePageContext.js';
Expand All @@ -19,8 +19,6 @@ import {

import type { RenderParameters } from 'pdfjs-dist/types/src/display/api.js';

const ANNOTATION_MODE = pdfjs.AnnotationMode;

type CanvasProps = {
canvasRef?: React.Ref<HTMLCanvasElement>;
};
Expand All @@ -33,6 +31,7 @@ export default function Canvas(props: CanvasProps): React.ReactElement {
const mergedProps = { ...pageContext, ...props };
const {
_className,
annotationMode,
canvasBackground,
devicePixelRatio = getDevicePixelRatio(),
onRenderError: onRenderErrorProps,
Expand Down Expand Up @@ -113,7 +112,8 @@ export default function Canvas(props: CanvasProps): React.ReactElement {
canvas.style.visibility = 'hidden';

const renderContext: RenderParameters = {
annotationMode: renderForms ? ANNOTATION_MODE.ENABLE_FORMS : ANNOTATION_MODE.ENABLE,
annotationMode:
annotationMode ?? (renderForms ? AnnotationMode.ENABLE_FORMS : AnnotationMode.ENABLE),
canvas,
canvasContext: canvas.getContext('2d', { alpha: false }) as CanvasRenderingContext2D,
pageColors,
Expand All @@ -136,7 +136,7 @@ export default function Canvas(props: CanvasProps): React.ReactElement {

return () => cancelRunningTask(runningTask);
},
[canvasBackground, page, pageColors, renderForms, renderViewport, viewport],
[annotationMode, canvasBackground, page, pageColors, renderForms, renderViewport, viewport],
);

const cleanup = useCallback(() => {
Expand Down
1 change: 1 addition & 0 deletions packages/react-pdf/src/Thumbnail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import type { ClassName, OnItemClickArgs } from './shared/types.js';

export type ThumbnailProps = Omit<
PageProps,
| 'annotationMode'
| 'className'
| 'customTextRenderer'
| 'onGetAnnotationsError'
Expand Down
2 changes: 2 additions & 0 deletions packages/react-pdf/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import * as pdfjs from 'pdfjs-dist';

import AnnotationMode from './AnnotationMode.js';
import Document from './Document.js';
import Outline from './Outline.js';
import Page from './Page.js';
Expand Down Expand Up @@ -32,6 +33,7 @@ displayWorkerWarning();
pdfjs.GlobalWorkerOptions.workerSrc = 'pdf.worker.mjs';

export {
AnnotationMode,
Document,
Outline,
Page,
Expand Down
2 changes: 2 additions & 0 deletions packages/react-pdf/src/shared/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import type {
TextMarkedContent,
TypedArray,
} from 'pdfjs-dist/types/src/display/api.js';
import type AnnotationMode from '../AnnotationMode.js';
import type LinkService from '../LinkService.js';

export type { PasswordResponses, StructTreeNode, TextContent, TextItem, TextMarkedContent };
Expand Down Expand Up @@ -154,6 +155,7 @@ export type DocumentContextType = {

export type PageContextType = {
_className?: string;
annotationMode: (typeof AnnotationMode)[keyof typeof AnnotationMode];
canvasBackground?: string;
customTextRenderer?: CustomTextRenderer;
devicePixelRatio?: number;
Expand Down
6 changes: 3 additions & 3 deletions sample/next-app/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -630,13 +630,13 @@ __metadata:
linkType: hard

"postcss@npm:^8.5.10":
version: 8.5.22
resolution: "postcss@npm:8.5.22"
version: 8.5.25
resolution: "postcss@npm:8.5.25"
dependencies:
nanoid: "npm:^3.3.16"
picocolors: "npm:^1.1.1"
source-map-js: "npm:^1.2.1"
checksum: 10c0/9e143ee457988049d5f187116fd37f2750ae9d8d71cc99eae690b776e549e134b34086cbaa2f0360ecd1729b15d918227bd0fc3a2ffbe341f7212d0827080793
checksum: 10c0/0a12c1e74b456c57122e81f684e02fd98ff4d57526f794d10c996df1147158808f5ae373ac82b988c8de6cbbaa82dbd7b13803b14f5dd3cf7cc6a42ccad5c9f2
languageName: node
linkType: hard

Expand Down
Loading