Skip to content

Commit 59f66f1

Browse files
hchevalierwojtekmaj
authored andcommitted
Allow overriding of imageResourcesPath (wojtekmaj#728)
* Allow overriding of imageResourcesPath * Add tests * Update README.md
1 parent b45f9dd commit 59f66f1

File tree

6 files changed

+69
-2
lines changed

6 files changed

+69
-2
lines changed

README.md

+2
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,7 @@ Loads a document passed using `file` prop.
222222
|error|What the component should display in case of an error.|`"Failed to load PDF file."`|<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>|
223223
|externalLinkTarget|Link target for external links rendered in annotations.|unset, which means that default behavior will be used|One of valid [values for `target` attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a#Attributes).<ul><li>`"_self"`</li><li>`"_blank"`</li><li>`"_parent"`</li><li>`"_top"`</li></ul>
224224
|file|What PDF should be displayed.<br />Its value can be an URL, a file (imported using `import ... from ...` or from file input form element), or an object with parameters (`url` - URL; `data` - data, preferably Uint8Array; `range` - PDFDataRangeTransport; `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`).<br />**Warning**: Since equality check (`===`) is used to determine if `file` object has changed, it must be memoized by setting it in component's state, `useMemo` or other similar technique.|n/a|<ul><li>URL:<br />`"http://example.com/sample.pdf"`</li><li>File:<br />`import sample from '../static/sample.pdf'` and then<br />`sample`</li><li>Parameter object:<br />`{ url: 'http://example.com/sample.pdf', httpHeaders: { 'X-CustomHeader': '40359820958024350238508234' }, withCredentials: true }`</ul>|
225+
|imageResourcesPath|The path used to prefix the src attributes of annotation SVGs.|n/a (pdf.js will fallback to an empty string)|`"/public/images/"`|
225226
|inputRef|A prop that behaves like [ref](https://reactjs.org/docs/refs-and-the-dom.html), but it's passed to main `<div>` rendered by `<Document>` component.|n/a|<ul><li>Function:<br />`(ref) => { this.myDocument = 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>|
226227
|loading|What the component should display while loading.|`"Loading PDF…"`|<ul><li>String:<br />`"Please wait!"`</li><li>React element:<br />`<div>Please wait!</div>`</li><li>Function:<br />`this.renderLoader`</li></ul>|
227228
|noData|What the component should display in case of no data.|`"No PDF file specified."`|<ul><li>String:<br />`"Please select a file."`</li><li>React element:<br />`<div>Please select a file.</div>`</li><li>Function:<br />`this.renderNoData`</li></ul>|
@@ -248,6 +249,7 @@ Displays a page. Should be placed inside `<Document />`. Alternatively, it can h
248249
|customTextRenderer|A function that customizes how a text layer is rendered. Passes itext item and index for item.|n/a|`({ str, itemIndex }) => { return (<mark>{str}</mark>) }`|
249250
|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>|
250251
|height|Page height. If neither `height` nor `width` are defined, page will be rendered at the size defined in PDF. If you define `width` and `height` at the same time, `height` will be ignored. If you define `height` and `scale` at the same time, the height will be multiplied by a given factor.|Page's default height|`300`|
252+
|imageResourcesPath|The path used to prefix the src attributes of annotation SVGs.|n/a (pdf.js will fallback to an empty string)|`"/public/images/"`|
251253
|inputRef|A prop that behaves like [ref](https://reactjs.org/docs/refs-and-the-dom.html), but it's passed to main `<div>` rendered by `<Page>` component.|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>|
252254
|loading|What the component should display while loading.|`"Loading page…"`|<ul><li>String:<br />`"Please wait!"`</li><li>React element:<br />`<div>Please wait!</div>`</li><li>Function:<br />`this.renderLoader`</li></ul>|
253255
|noData|What the component should display in case of no data.|`"No page specified."`|<ul><li>String:<br />`"Please select a page."`</li><li>React element:<br />`<div>Please select a page.</div>`</li><li>Function:<br />`this.renderNoData`</li></ul>|

__mocks__/_pdf3.pdf

17.7 KB
Binary file not shown.

src/Document.jsx

+3-1
Original file line numberDiff line numberDiff line change
@@ -149,10 +149,11 @@ export default class Document extends PureComponent {
149149

150150
get childContext() {
151151
const { linkService, registerPage, unregisterPage } = this;
152-
const { renderMode, rotate } = this.props;
152+
const { imageResourcesPath, renderMode, rotate } = this.props;
153153
const { pdf } = this.state;
154154

155155
return {
156+
imageResourcesPath,
156157
linkService,
157158
pdf,
158159
registerPage,
@@ -382,6 +383,7 @@ Document.propTypes = {
382383
className: isClassName,
383384
error: isFunctionOrNode,
384385
file: isFile,
386+
imageResourcesPath: PropTypes.string,
385387
inputRef: isRef,
386388
loading: isFunctionOrNode,
387389
noData: isFunctionOrNode,

src/Page.jsx

+1
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,7 @@ PageInternal.propTypes = {
401401
customTextRenderer: PropTypes.func,
402402
error: isFunctionOrNode,
403403
height: PropTypes.number,
404+
imageResourcesPath: PropTypes.string,
404405
inputRef: isRef,
405406
loading: isFunctionOrNode,
406407
noData: isFunctionOrNode,

src/Page/AnnotationLayer.jsx

+9-1
Original file line numberDiff line numberDiff line change
@@ -103,12 +103,19 @@ export class AnnotationLayerInternal extends PureComponent {
103103
return;
104104
}
105105

106-
const { linkService, page, renderInteractiveForms } = this.props;
106+
const {
107+
imageResourcesPath,
108+
linkService,
109+
page,
110+
renderInteractiveForms,
111+
} = this.props;
112+
107113
const viewport = this.viewport.clone({ dontFlip: true });
108114

109115
const parameters = {
110116
annotations,
111117
div: this.annotationLayer,
118+
imageResourcesPath,
112119
linkService,
113120
page,
114121
renderInteractiveForms,
@@ -138,6 +145,7 @@ export class AnnotationLayerInternal extends PureComponent {
138145
}
139146

140147
AnnotationLayerInternal.propTypes = {
148+
imageResourcesPath: PropTypes.string,
141149
linkService: isLinkService.isRequired,
142150
onGetAnnotationsError: PropTypes.func,
143151
onGetAnnotationsSuccess: PropTypes.func,

src/Page/AnnotationLayer.spec.jsx

+54
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {
1414
} from '../../test-utils';
1515

1616
const pdfFile = loadPDF('./__mocks__/_pdf.pdf');
17+
const annotatedPdfFile = loadPDF('./__mocks__/_pdf3.pdf');
1718

1819
describe('AnnotationLayer', () => {
1920
const linkService = new LinkService({ eventBus });
@@ -219,5 +220,58 @@ describe('AnnotationLayer', () => {
219220
expect(viewport.scale).toEqual(scale);
220221
});
221222
});
223+
224+
it('renders annotations with the default imageResourcesPath given no imageResourcesPath', async () => {
225+
const pdf = await pdfjs.getDocument({ data: annotatedPdfFile.arrayBuffer }).promise;
226+
const annotatedPage = await pdf.getPage(1);
227+
228+
const {
229+
func: onRenderAnnotationLayerSuccess, promise: onRenderAnnotationLayerSuccessPromise,
230+
} = makeAsyncCallback();
231+
const imageResourcesPath = '';
232+
const desiredImageTagRegExp = new RegExp(`<img[^>]+src="${imageResourcesPath}annotation-note.svg"`);
233+
234+
const component = mount(
235+
<AnnotationLayer
236+
linkService={linkService}
237+
onRenderAnnotationLayerSuccess={onRenderAnnotationLayerSuccess}
238+
page={annotatedPage}
239+
/>,
240+
);
241+
242+
expect.assertions(1);
243+
return onRenderAnnotationLayerSuccessPromise.then(() => {
244+
component.update();
245+
const stringifiedAnnotationLayerNode = component.html();
246+
expect(stringifiedAnnotationLayerNode).toMatch(desiredImageTagRegExp);
247+
});
248+
});
249+
250+
it('renders annotations with the specified imageResourcesPath given imageResourcesPath', async () => {
251+
const pdf = await pdfjs.getDocument({ data: annotatedPdfFile.arrayBuffer }).promise;
252+
const annotatedPage = await pdf.getPage(1);
253+
254+
const {
255+
func: onRenderAnnotationLayerSuccess, promise: onRenderAnnotationLayerSuccessPromise,
256+
} = makeAsyncCallback();
257+
const imageResourcesPath = '/public/images/';
258+
const desiredImageTagRegExp = new RegExp(`<img[^>]+src="${imageResourcesPath}annotation-note.svg"`);
259+
260+
const component = mount(
261+
<AnnotationLayer
262+
imageResourcesPath={imageResourcesPath}
263+
linkService={linkService}
264+
onRenderAnnotationLayerSuccess={onRenderAnnotationLayerSuccess}
265+
page={annotatedPage}
266+
/>,
267+
);
268+
269+
expect.assertions(1);
270+
return onRenderAnnotationLayerSuccessPromise.then(() => {
271+
component.update();
272+
const stringifiedAnnotationLayerNode = component.html();
273+
expect(stringifiedAnnotationLayerNode).toMatch(desiredImageTagRegExp);
274+
});
275+
});
222276
});
223277
});

0 commit comments

Comments
 (0)