Skip to content

Commit

Permalink
Add support for custom renderMode
Browse files Browse the repository at this point in the history
  • Loading branch information
wojtekmaj committed Jul 17, 2023
1 parent 67eae55 commit db9701e
Show file tree
Hide file tree
Showing 10 changed files with 182 additions and 14 deletions.
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -408,7 +408,7 @@ Loads a document passed using `file` prop.
| onSourceError | Function called in case of an error while retrieving document source from `file` prop. | n/a | `(error) => alert('Error while retrieving document source! ' + error.message)` |
| onSourceSuccess | Function called when document source is successfully retrieved from `file` prop. | n/a | `() => alert('Document source retrieved!')` |
| options | An object in which additional parameters to be passed to PDF.js can be defined. Most notably: `cMapUrl`, `cMapPacked` - required for CMap support; `httpHeaders` - custom request headers, e.g. for authorization); `withCredentials` - a boolean to indicate whether or not to include cookies in the request (defaults to `false`). For a full list of possible parameters, check [PDF.js documentation on DocumentInitParameters](https://mozilla.github.io/pdf.js/api/draft/module-pdfjsLib.html#~DocumentInitParameters). | n/a | `{ cMapUrl: 'cmaps/', cMapPacked: true }` |
| renderMode | Rendering mode of the document. Can be `"canvas"`, `"svg"` or `"none"`.<br />**Warning**: SVG render mode is no longer maintained and may be removed in the future. | `"canvas"` | `"svg"` |
| renderMode | Rendering mode of the document. Can be `"canvas"`, `"custom"`, `"none"` or `"svg"`. If set to `"custom"`, `customRenderer` must also be provided.<br />**Warning**: SVG render mode is no longer maintained and may be removed in the future. | `"canvas"` | `"svg"` |
| rotate | Rotation of the document in degrees. If provided, will change rotation globally, even for the pages which were given `rotate` prop of their own. `90` = rotated to the right, `180` = upside down, `270` = rotated to the left. | n/a | `90` |

### Page
Expand All @@ -422,6 +422,7 @@ Displays a page. Should be placed inside `<Document />`. Alternatively, it can h
| canvasBackground | Canvas background color. Any valid `canvas.fillStyle` can be used. If you set `renderMode` to `"svg"` this prop will be ignored. | 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 `<PageCanvas>` component. If you set `renderMode` to `"svg"` this prop will be ignored. | n/a | <ul><li>Function:<br />`(ref) => { this.myPage = ref; }`</li><li>Ref created using `React.createRef`:<br />`this.ref = React.createRef();`<br />…<br />`inputRef={this.ref}`</li><li>Ref created using `React.useRef`:<br />`const ref = React.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> |
| customRenderer | Function that customizes how a page is rendered. You must set `renderMode` to `"custom"` to use this prop. | n/a | `MyCustomRenderer` |
| customTextRenderer | Function that customizes how a text layer is rendered. | n/a | `` ({ str, itemIndex }) => str.replace(/ipsum/g, value => `<mark>${value}</mark>`) `` |
| devicePixelRatio | The ratio between physical pixels and device-independent pixels (DIPs) on the current device. | `window.devicePixelRatio` | `1` |
| error | What the component should display in case of an error. | `"Failed to load the page."` | <ul><li>String:<br />`"An error occurred!"`</li><li>React element:<br />`<div>An error occurred!</div>`</li><li>Function:<br />`this.renderError`</li></ul> |
Expand All @@ -446,7 +447,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` |
| 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` |
| renderMode | Rendering mode of the document. Can be `"canvas"`, `"svg"` or `"none"`. | `"canvas"` | `"svg"` |
| renderMode | Rendering mode of the document. Can be `"canvas"`, `"custom"`, `"none"` or `"svg"`. If set to `"custom"`, `customRenderer` must also be provided.<br />**Warning**: SVG render mode is no longer maintained and may be removed in the future. | `"canvas"` | `"svg"` |
| 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` |
| scale | Page scale. | `1.0` | `0.5` |
Expand Down
53 changes: 53 additions & 0 deletions src/Page.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -448,6 +448,32 @@ describe('Page', () => {
expect(pageCanvas).toBeInTheDocument();
});

it('requests page to be rendered in custom mode when given renderMode = "custom"', async () => {
const { func: onLoadSuccess, promise: onLoadSuccessPromise } = makeAsyncCallback();

function CustomRenderer() {
return <div className="custom-renderer" />;
}

const { container } = renderWithContext(
<Page
customRenderer={CustomRenderer}
onLoadSuccess={onLoadSuccess}
pageIndex={0}
renderMode="custom"
/>,
{ pdf },
);

expect.assertions(1);

await onLoadSuccessPromise;

const customRenderer = container.querySelector('.custom-renderer');

expect(customRenderer).toBeInTheDocument();
});

it('requests page to be rendered in SVG mode when given renderMode = "svg"', async () => {
const { func: onLoadSuccess, promise: onLoadSuccessPromise } = makeAsyncCallback();

Expand Down Expand Up @@ -533,6 +559,33 @@ describe('Page', () => {
expect(textLayer).toBeInTheDocument();
});

it('renders TextLayer when given renderMode = "custom"', async () => {
const { func: onLoadSuccess, promise: onLoadSuccessPromise } = makeAsyncCallback();

function CustomRenderer() {
return <div className="custom-renderer" />;
}

const { container } = renderWithContext(
<Page
customRenderer={CustomRenderer}
onLoadSuccess={onLoadSuccess}
pageIndex={0}
renderMode="custom"
renderTextLayer
/>,
{ pdf },
);

expect.assertions(1);

await onLoadSuccessPromise;

const textLayer = container.querySelector('.react-pdf__Page__textContent');

expect(textLayer).toBeInTheDocument();
});

it('renders TextLayer when given renderMode = "svg"', async () => {
const { func: onLoadSuccess, promise: onLoadSuccessPromise } = makeAsyncCallback();

Expand Down
12 changes: 12 additions & 0 deletions src/Page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import {
import type { PDFDocumentProxy, PDFPageProxy } from 'pdfjs-dist';
import type { EventProps } from 'make-event-props';
import type {
CustomRenderer,
CustomTextRenderer,
NodeOrRenderer,
OnGetAnnotationsError,
Expand Down Expand Up @@ -62,6 +63,7 @@ export type PageProps = {
canvasRef?: React.Ref<HTMLCanvasElement>;
children?: React.ReactNode;
className?: string;
customRenderer?: CustomRenderer;
customTextRenderer?: CustomTextRenderer;
devicePixelRatio?: number;
error?: NodeOrRenderer;
Expand Down Expand Up @@ -114,6 +116,7 @@ export default function Page(props: PageProps) {
canvasRef,
children,
className,
customRenderer: CustomRenderer,
customTextRenderer,
devicePixelRatio,
error = 'Failed to load the page.',
Expand Down Expand Up @@ -356,6 +359,14 @@ export default function Page(props: PageProps) {

function renderMainLayer() {
switch (renderMode) {
case 'custom': {
invariant(
CustomRenderer,
`renderMode was set to "custom", but no customRenderer was passed.`,
);

return <CustomRenderer key={`${pageKey}_custom`} />;
}
case 'none':
return null;
case 'svg':
Expand Down Expand Up @@ -442,6 +453,7 @@ Page.propTypes = {
canvasRef: isRef,
children: PropTypes.node,
className: isClassName,
customRenderer: PropTypes.func,
customTextRenderer: PropTypes.func,
devicePixelRatio: PropTypes.number,
error: isFunctionOrNode,
Expand Down
26 changes: 26 additions & 0 deletions src/Thumbnail.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -431,6 +431,32 @@ describe('Thumbnail', () => {
expect(pageCanvas).toBeInTheDocument();
});

it('requests page to be rendered in custom mode when given renderMode = "custom"', async () => {
const { func: onLoadSuccess, promise: onLoadSuccessPromise } = makeAsyncCallback();

function CustomRenderer() {
return <div className="custom-renderer" />;
}

const { container } = renderWithContext(
<Thumbnail
customRenderer={CustomRenderer}
onLoadSuccess={onLoadSuccess}
pageIndex={0}
renderMode="custom"
/>,
{ pdf },
);

expect.assertions(1);

await onLoadSuccessPromise;

const customRenderer = container.querySelector('.custom-renderer');

expect(customRenderer).toBeInTheDocument();
});

it('requests page to be rendered in SVG mode when given renderMode = "svg"', async () => {
const { func: onLoadSuccess, promise: onLoadSuccessPromise } = makeAsyncCallback();

Expand Down
2 changes: 1 addition & 1 deletion src/shared/propTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,6 @@ export const isRef = PropTypes.oneOfType([
}),
]);

export const isRenderMode = PropTypes.oneOf(['canvas', 'none', 'svg']);
export const isRenderMode = PropTypes.oneOf(['canvas', 'custom', 'none', 'svg']);

export const isRotate = PropTypes.oneOf([0, 90, 180, 270]);
Loading

0 comments on commit db9701e

Please sign in to comment.