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
2 changes: 2 additions & 0 deletions packages/react-pdf/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -489,6 +489,7 @@ Displays a page. Should be placed inside `<Document />`. Alternatively, it can h

| Prop name | Description | Default value | Example values |
| ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| annotationMode | Annotation mode. | `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 @@ -548,6 +549,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;
27 changes: 27 additions & 0 deletions packages/react-pdf/src/Page.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import { pdfjs } from './index.test.js';

import Page from './Page.js';
import AnnotationMode from './AnnotationMode.js';
import LinkService from './LinkService.js';

import failingPdf from '../../../__mocks__/_failing_pdf.js';
Expand Down Expand Up @@ -752,7 +753,7 @@
);

expect.assertions(1);

Check failure on line 756 in packages/react-pdf/src/Page.spec.tsx

View workflow job for this annotation

GitHub Actions / Unit tests (React 19)

src/Page.spec.tsx > Page > requests page to be rendered with forms when given annotationMode = AnnotationMode.ENABLE_FORMS

AssertionError: expected null to be truthy - Expected: true + Received: null ❯ src/Page.spec.tsx:756:34

Check failure on line 756 in packages/react-pdf/src/Page.spec.tsx

View workflow job for this annotation

GitHub Actions / Unit tests (React 18)

src/Page.spec.tsx > Page > requests page to be rendered with forms when given annotationMode = AnnotationMode.ENABLE_FORMS

AssertionError: expected null to be truthy - Expected: true + Received: null ❯ src/Page.spec.tsx:756:34

Check failure on line 756 in packages/react-pdf/src/Page.spec.tsx

View workflow job for this annotation

GitHub Actions / Unit tests (React 19)

src/Page.spec.tsx > Page > requests page to be rendered with forms when given annotationMode = AnnotationMode.ENABLE_FORMS

AssertionError: expected null to be truthy - Expected: true + Received: null ❯ src/Page.spec.tsx:756:34

Check failure on line 756 in packages/react-pdf/src/Page.spec.tsx

View workflow job for this annotation

GitHub Actions / Unit tests (React 18)

src/Page.spec.tsx > Page > requests page to be rendered with forms when given annotationMode = AnnotationMode.ENABLE_FORMS

AssertionError: expected null to be truthy - Expected: true + Received: null ❯ src/Page.spec.tsx:756:34
await onRenderAnnotationLayerSuccessPromise;

const textWidgetAnnotation = container.querySelector('.textWidgetAnnotation');
Expand All @@ -760,6 +761,32 @@
expect(textWidgetAnnotation).toBeFalsy();
});

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

const { container } = 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');

Check failure on line 786 in packages/react-pdf/src/Page.spec.tsx

View workflow job for this annotation

GitHub Actions / Unit tests (React 19)

src/Page.spec.tsx > Page > requests page to be rendered with forms given renderForms = true

AssertionError: expected null to be truthy - Expected: true + Received: null ❯ src/Page.spec.tsx:786:34

Check failure on line 786 in packages/react-pdf/src/Page.spec.tsx

View workflow job for this annotation

GitHub Actions / Unit tests (React 18)

src/Page.spec.tsx > Page > requests page to be rendered with forms given renderForms = true

AssertionError: expected null to be truthy - Expected: true + Received: null ❯ src/Page.spec.tsx:786:34

Check failure on line 786 in packages/react-pdf/src/Page.spec.tsx

View workflow job for this annotation

GitHub Actions / Unit tests (React 19)

src/Page.spec.tsx > Page > requests page to be rendered with forms given renderForms = true

AssertionError: expected null to be truthy - Expected: true + Received: null ❯ src/Page.spec.tsx:786:34

Check failure on line 786 in packages/react-pdf/src/Page.spec.tsx

View workflow job for this annotation

GitHub Actions / Unit tests (React 18)

src/Page.spec.tsx > Page > requests page to be rendered with forms given renderForms = true

AssertionError: expected null to be truthy - Expected: true + Received: null ❯ src/Page.spec.tsx:786:34
expect(textWidgetAnnotation).toBeTruthy();
});

it('requests page to be rendered with forms given renderForms = true', async () => {
const { func: onRenderAnnotationLayerSuccess, promise: onRenderAnnotationLayerSuccessPromise } =
makeAsyncCallback();
Expand Down
11 changes: 11 additions & 0 deletions 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 PageContext from './PageContext.js';

import Message from './Message.js';
Expand Down Expand Up @@ -50,6 +51,13 @@ const defaultScale = 1;
export type PageProps = {
_className?: string;
_enableRegisterUnregisterPage?: boolean;
/**
* Annotation mode.
*
* @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 @@ -307,6 +315,7 @@ export default function Page(props: PageProps): React.ReactElement {
const {
_className = 'react-pdf__Page',
_enableRegisterUnregisterPage = true,
annotationMode = AnnotationMode.ENABLE,
canvasBackground,
canvasRef,
children,
Expand Down Expand Up @@ -493,6 +502,7 @@ export default function Page(props: PageProps): React.ReactElement {
page && isProvided(pageIndex) && pageNumber && isProvided(rotate) && isProvided(scale)
? {
_className,
annotationMode,
canvasBackground,
customTextRenderer,
devicePixelRatio,
Expand All @@ -519,6 +529,7 @@ export default function Page(props: PageProps): React.ReactElement {
: null,
[
_className,
annotationMode,
canvasBackground,
customTextRenderer,
devicePixelRatio,
Expand Down
16 changes: 14 additions & 2 deletions packages/react-pdf/src/Page/AnnotationLayer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import invariant from 'tiny-invariant';
import warning from 'warning';
import * as pdfjs from 'pdfjs-dist';

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

import useDocumentContext from '../shared/hooks/useDocumentContext.js';
import usePageContext from '../shared/hooks/usePageContext.js';
import useResolver from '../shared/hooks/useResolver.js';
Expand All @@ -22,6 +24,7 @@ export default function AnnotationLayer(): React.ReactElement {

const mergedProps = { ...documentContext, ...pageContext };
const {
annotationMode,
imageResourcesPath,
linkService,
onGetAnnotationsError: onGetAnnotationsErrorProps,
Expand Down Expand Up @@ -176,7 +179,7 @@ export default function AnnotationLayer(): React.ReactElement {
imageResourcesPath,
linkService,
page,
renderForms,
renderForms: annotationMode === AnnotationMode.ENABLE_FORMS ? renderForms : false,
viewport: clonedViewport,
};

Expand All @@ -195,7 +198,16 @@ export default function AnnotationLayer(): React.ReactElement {
// TODO: Cancel running task?
};
},
[annotations, imageResourcesPath, linkService, page, pdf, renderForms, viewport],
[
annotationMode,
annotations,
imageResourcesPath,
linkService,
page,
pdf,
renderForms,
viewport,
],
);

return (
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 @@ -4,8 +4,8 @@ import { useCallback, useEffect, useMemo, useRef } from 'react';
import mergeRefs from 'merge-refs';
import invariant from 'tiny-invariant';
import warning from 'warning';
import * as pdfjs from 'pdfjs-dist';

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

import usePageContext from '../shared/hooks/usePageContext.js';
Expand All @@ -18,8 +18,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 @@ -32,6 +30,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 @@ -111,7 +110,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),
canvasContext: canvas.getContext('2d', { alpha: false }) as CanvasRenderingContext2D,
viewport: renderViewport,
};
Expand All @@ -132,7 +132,7 @@ export default function Canvas(props: CanvasProps): React.ReactElement {

return () => cancelRunningTask(runningTask);
},
[canvasBackground, page, renderForms, renderViewport, viewport],
[annotationMode, canvasBackground, page, 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
Expand Up @@ -9,6 +9,7 @@ import useDocumentContext from './shared/hooks/useDocumentContext.js';
import useOutlineContext from './shared/hooks/useOutlineContext.js';
import usePageContext from './shared/hooks/usePageContext.js';

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

export type { DocumentProps } from './Document.js';
Expand All @@ -31,5 +32,6 @@ export {
useDocumentContext,
useOutlineContext,
usePageContext,
AnnotationMode,
PasswordResponses,
};
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,
} from 'pdfjs-dist/types/src/display/api.js';
import type { AnnotationLayerParameters } from 'pdfjs-dist/types/src/display/annotation_layer.js';
import type AnnotationMode from '../AnnotationMode.js';
import type LinkService from '../LinkService.js';

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

export type PageContextType = {
_className?: string;
annotationMode?: (typeof AnnotationMode)[keyof typeof AnnotationMode];
canvasBackground?: string;
customTextRenderer?: CustomTextRenderer;
devicePixelRatio?: number;
Expand Down
2 changes: 1 addition & 1 deletion test/AnnotationOptions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export default function AnnotationOptions({
<fieldset>
<legend>Annotation options</legend>

<label htmlFor={targetUnsetId}>External link target</label>
<label htmlFor={targetUnsetId}>External link target:</label>
<div>
<input
checked={externalLinkTarget === undefined}
Expand Down
55 changes: 55 additions & 0 deletions test/LayerOptions.tsx
Original file line number Diff line number Diff line change
@@ -1,31 +1,44 @@
import { useId } from 'react';

import type { AnnotationMode } from 'react-pdf';

type LayerOptionsProps = {
annotationMode?: keyof typeof AnnotationMode;
renderAnnotationLayer: boolean;
renderForms: boolean;
renderTextLayer: boolean;
useCustomTextRenderer: boolean;
setAnnotationMode: (value: keyof typeof AnnotationMode | undefined) => void;
setRenderAnnotationLayer: (value: boolean) => void;
setRenderForms: (value: boolean) => void;
setRenderTextLayer: (value: boolean) => void;
setUseCustomTextRenderer: (value: boolean) => void;
};

export default function LayerOptions({
annotationMode,
renderAnnotationLayer,
renderForms,
renderTextLayer,
useCustomTextRenderer,
setAnnotationMode,
setRenderAnnotationLayer,
setRenderForms,
setRenderTextLayer,
setUseCustomTextRenderer,
}: LayerOptionsProps) {
const annotationModeId = useId();
const renderTextLayerId = useId();
const useCustomTextRendererId = useId();
const renderAnnotationLayerId = useId();
const renderFormsId = useId();

function onAnnotationModeChange(event: React.ChangeEvent<HTMLInputElement>) {
const { value } = event.target;

setAnnotationMode(value as keyof typeof AnnotationMode);
}

function onRenderAnnotationLayerChange(event: React.ChangeEvent<HTMLInputElement>) {
setRenderAnnotationLayer(event.target.checked);
}
Expand All @@ -46,6 +59,48 @@ export default function LayerOptions({
<fieldset>
<legend>Layer options</legend>

<label htmlFor={annotationModeId}>Annotation mode:</label>
<div>
<input
checked={annotationMode === 'DISABLE'}
id={annotationModeId}
onChange={onAnnotationModeChange}
type="radio"
value="DISABLE"
/>
<label htmlFor={annotationModeId}>DISABLE</label>
</div>
<div>
<input
checked={annotationMode === 'ENABLE'}
id={annotationModeId}
onChange={onAnnotationModeChange}
type="radio"
value="ENABLE"
/>
<label htmlFor={annotationModeId}>ENABLE</label>
</div>
<div>
<input
checked={annotationMode === 'ENABLE_FORMS'}
id={annotationModeId}
onChange={onAnnotationModeChange}
type="radio"
value="ENABLE_FORMS"
/>
<label htmlFor={annotationModeId}>ENABLE_FORMS</label>
</div>
<div>
<input
checked={annotationMode === 'ENABLE_STORAGE'}
id={annotationModeId}
onChange={onAnnotationModeChange}
type="radio"
value="EDIT"
/>
<label htmlFor={annotationModeId}>EDIT</label>
</div>

<div>
<input
checked={renderTextLayer}
Expand Down
9 changes: 5 additions & 4 deletions test/Test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import CustomRenderer from './CustomRenderer.js';

import { isArrayBuffer, isBlob, isBrowser, loadFromFile, dataURItoBlob } from './shared/utils.js';

import type { AnnotationMode } from 'react-pdf';
import type { PDFDocumentProxy, PDFPageProxy } from 'pdfjs-dist';
import type { ExternalLinkTarget, File, PassMethod, RenderMode } from './shared/types.js';

Expand Down Expand Up @@ -69,6 +70,7 @@ export function readAsDataURL(file: Blob): Promise<string> {
}

export default function Test() {
const [annotationMode, setAnnotationMode] = useState<keyof typeof AnnotationMode>();
const [canvasBackground, setCanvasBackground] = useState<string>();
const [devicePixelRatio, setDevicePixelRatio] = useState<number>();
const [displayAll, setDisplayAll] = useState(false);
Expand All @@ -83,7 +85,6 @@ export default function Test() {
const [passMethod, setPassMethod] = useState<PassMethod>();
const [render, setRender] = useState(true);
const [renderAnnotationLayer, setRenderAnnotationLayer] = useState(true);
const [renderForms, setRenderForms] = useState(true);
const [renderMode, setRenderMode] = useState<RenderMode | undefined>('canvas');
const [renderTextLayer, setRenderTextLayer] = useState(true);
const [useCustomTextRenderer, setUseCustomTextRenderer] = useState(true);
Expand Down Expand Up @@ -214,6 +215,7 @@ export default function Test() {
};

const pageProps = {
annotationMode,
canvasBackground,
className: 'custom-classname-page',
customRenderer: CustomRenderer,
Expand All @@ -223,7 +225,6 @@ export default function Test() {
onClick: onPageClick,
onRenderSuccess: onPageRenderSuccess,
renderAnnotationLayer,
renderForms,
renderMode,
renderTextLayer,
scale: pageScale,
Expand All @@ -240,12 +241,12 @@ export default function Test() {
<LoadingOptions file={file} setFile={setFile} setRender={setRender} />
<PassingOptions file={file} passMethod={passMethod} setPassMethod={setPassMethod} />
<LayerOptions
annotationMode={annotationMode}
renderAnnotationLayer={renderAnnotationLayer}
renderForms={renderForms}
renderTextLayer={renderTextLayer}
useCustomTextRenderer={useCustomTextRenderer}
setAnnotationMode={setAnnotationMode}
setRenderAnnotationLayer={setRenderAnnotationLayer}
setRenderForms={setRenderForms}
setRenderTextLayer={setRenderTextLayer}
setUseCustomTextRenderer={setUseCustomTextRenderer}
/>
Expand Down
Loading