Skip to content

Commit

Permalink
[breaking] Refactor TextLayer to use pdfjs.renderTextLayer (#944)
Browse files Browse the repository at this point in the history
- Text layer alignment significantly improved
- New prop: enhanceTextSelection
- New prop: onRenderTextLayerError
- New prop: onRenderTextLayerSuccess

Breaking changes:
- onGetTextSuccess is now called with an object containing items and styles
- TextLayer.css must be imported for TextLayer to work properly
  • Loading branch information
wojtekmaj committed Apr 20, 2022
1 parent 8bdb7c1 commit be758b5
Show file tree
Hide file tree
Showing 17 changed files with 293 additions and 307 deletions.
10 changes: 9 additions & 1 deletion README.md
Expand Up @@ -168,6 +168,14 @@ If you want to use annotations (e.g. links) in PDFs rendered by React-PDF, then
import 'react-pdf/dist/esm/Page/AnnotationLayer.css';
```

### Support for text layer

If you want to use text layer in PDFs rendered by React-PDF, then you would need to include stylesheet necessary for text layer to be correctly displayed like so:

```js
import 'react-pdf/dist/esm/Page/TextLayer.css';
```

### Support for non-latin characters

If you want to ensure that PDFs with non-latin characters will render perfectly, or you have encountered the following warning:
Expand Down Expand Up @@ -403,7 +411,7 @@ Displays a page. Should be placed inside `<Document />`. Alternatively, it can h
| onRenderSuccess | Function called when the page is successfully rendered on the screen. | n/a | `() => alert('Rendered the page!')` |
| onGetAnnotationsSuccess | Function called when annotations are successfully loaded. | n/a | `(annotations) => alert('Now displaying ' + annotations.length + ' annotations!')` |
| onGetAnnotationsError | Function called in case of an error while loading annotations. | n/a | `(error) => alert('Error while loading annotations! ' + error.message)` |
| onGetTextSuccess | Function called when text layer items are successfully loaded. | n/a | `(items) => alert('Now displaying ' + items.length + ' text layer items!')` |
| onGetTextSuccess | Function called when text layer items are successfully loaded. | n/a | `({ items, styles }) => alert('Now displaying ' + items.length + ' text layer items!')` |
| onGetTextError | Function called in case of an error while loading text layer items. | n/a | `(error) => alert('Error while loading text layer items! ' + error.message)` |
| pageIndex | Which page from PDF file should be displayed, by page index. | `0` | `1` |
| pageNumber | Which page from PDF file should be displayed, by page number. If provided, `pageIndex` prop will be ignored. | `1` | `2` |
Expand Down
16 changes: 16 additions & 0 deletions copy-styles.js
Expand Up @@ -15,3 +15,19 @@ fs.copyFile('src/Page/AnnotationLayer.css', 'dist/umd/Page/AnnotationLayer.css',
// eslint-disable-next-line no-console
console.log('AnnotationLayer.css copied successfully to UMD build.');
});

fs.copyFile('src/Page/TextLayer.css', 'dist/esm/Page/TextLayer.css', (error) => {
if (error) {
throw error;
}
// eslint-disable-next-line no-console
console.log('TextLayer.css copied successfully to ESM build.');
});

fs.copyFile('src/Page/TextLayer.css', 'dist/umd/Page/TextLayer.css', (error) => {
if (error) {
throw error;
}
// eslint-disable-next-line no-console
console.log('TextLayer.css copied successfully to UMD build.');
});
2 changes: 1 addition & 1 deletion jest.config.json
Expand Up @@ -9,6 +9,6 @@
"\\.(css|less)$": "<rootDir>/__mocks__/styleMock.js",
"pdfjs-dist/(.*)": "pdfjs-dist/legacy/$1"
},
"setupFiles": ["<rootDir>/jest.setup.js"],
"setupFiles": ["jest-canvas-mock", "<rootDir>/jest.setup.js"],
"testEnvironment": "<rootDir>/jest.env.js"
}
1 change: 1 addition & 0 deletions package.json
Expand Up @@ -94,6 +94,7 @@
"eslint-config-wojtekmaj": "^0.6.5",
"husky": "^7.0.0",
"jest": "^27.0.0",
"jest-canvas-mock": "^2.3.1",
"prettier": "^2.5.0",
"pretty-quick": "^3.1.0",
"react": "^17.0.0",
Expand Down
1 change: 1 addition & 0 deletions sample/create-react-app-5/src/Sample.jsx
@@ -1,6 +1,7 @@
import React, { useState } from 'react';
import { Document, Page } from 'react-pdf';
import 'react-pdf/dist/esm/Page/AnnotationLayer.css';
import 'react-pdf/dist/esm/Page/TextLayer.css';

import './Sample.css';

Expand Down
1 change: 1 addition & 0 deletions sample/parcel/Sample.jsx
@@ -1,6 +1,7 @@
import React, { useState } from 'react';
import { Document, Page } from 'react-pdf/dist/esm/entry.parcel';
import 'react-pdf/dist/esm/Page/AnnotationLayer.css';
import 'react-pdf/dist/esm/Page/TextLayer.css';

import './Sample.less';

Expand Down
1 change: 1 addition & 0 deletions sample/parcel2/Sample.jsx
@@ -1,6 +1,7 @@
import React, { useState } from 'react';
import { Document, Page } from 'react-pdf/dist/esm/entry.parcel2';
import 'react-pdf/dist/esm/Page/AnnotationLayer.css';
import 'react-pdf/dist/esm/Page/TextLayer.css';

import './Sample.less';

Expand Down
1 change: 1 addition & 0 deletions sample/webpack4/Sample.jsx
@@ -1,6 +1,7 @@
import React, { useState } from 'react';
import { Document, Page } from 'react-pdf/dist/esm/entry.webpack';
import 'react-pdf/dist/esm/Page/AnnotationLayer.css';
import 'react-pdf/dist/esm/Page/TextLayer.css';

import './Sample.less';

Expand Down
1 change: 1 addition & 0 deletions sample/webpack5/Sample.jsx
@@ -1,6 +1,7 @@
import React, { useState } from 'react';
import { Document, Page } from 'react-pdf/dist/esm/entry.webpack5';
import 'react-pdf/dist/esm/Page/AnnotationLayer.css';
import 'react-pdf/dist/esm/Page/TextLayer.css';

import './Sample.less';

Expand Down
9 changes: 9 additions & 0 deletions src/Page.jsx
Expand Up @@ -79,6 +79,7 @@ export class PageInternal extends PureComponent {
const {
canvasBackground,
customTextRenderer,
enhanceTextSelection,
onGetAnnotationsError,
onGetAnnotationsSuccess,
onGetTextError,
Expand All @@ -87,13 +88,16 @@ export class PageInternal extends PureComponent {
onRenderAnnotationLayerSuccess,
onRenderError,
onRenderSuccess,
onRenderTextLayerError,
onRenderTextLayerSuccess,
renderForms,
renderInteractiveForms,
} = this.props;

return {
canvasBackground,
customTextRenderer,
enhanceTextSelection,
onGetAnnotationsError,
onGetAnnotationsSuccess,
onGetTextError,
Expand All @@ -102,6 +106,8 @@ export class PageInternal extends PureComponent {
onRenderAnnotationLayerSuccess,
onRenderError,
onRenderSuccess,
onRenderTextLayerError,
onRenderTextLayerSuccess,
page,
renderForms: renderForms ?? renderInteractiveForms, // For backward compatibility
rotate: this.rotate,
Expand Down Expand Up @@ -375,6 +381,7 @@ PageInternal.propTypes = {
children: PropTypes.node,
className: isClassName,
customTextRenderer: PropTypes.func,
enhanceTextSelection: PropTypes.bool,
error: isFunctionOrNode,
height: PropTypes.number,
imageResourcesPath: PropTypes.string,
Expand All @@ -387,6 +394,8 @@ PageInternal.propTypes = {
onLoadSuccess: PropTypes.func,
onRenderError: PropTypes.func,
onRenderSuccess: PropTypes.func,
onRenderTextLayerError: PropTypes.func,
onRenderTextLayerSuccess: PropTypes.func,
pageIndex: isPageIndex,
pageNumber: isPageNumber,
pdf: isPdf,
Expand Down
90 changes: 90 additions & 0 deletions src/Page/TextLayer.css
@@ -0,0 +1,90 @@
/* Copyright 2014 Mozilla Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

.textLayer {
position: absolute;
text-align: initial;
left: 0;
top: 0;
right: 0;
bottom: 0;
overflow: hidden;
line-height: 1;
text-size-adjust: none;
}

.textLayer span,
.textLayer br {
color: transparent;
position: absolute;
white-space: pre;
cursor: text;
transform-origin: 0% 0%;
}

/* Only necessary in Google Chrome, see issue 14205, and most unfortunately
* the problem doesn't show up in "text" reference tests. */
.textLayer span.markedContent {
top: 0;
height: 0;
}

.textLayer .highlight {
margin: -1px;
padding: 1px;
background-color: rgba(180, 0, 170, 1);
border-radius: 4px;
}

.textLayer .highlight.appended {
position: initial;
}

.textLayer .highlight.begin {
border-radius: 4px 0 0 4px;
}

.textLayer .highlight.end {
border-radius: 0 4px 4px 0;
}

.textLayer .highlight.middle {
border-radius: 0;
}

.textLayer .highlight.selected {
background-color: rgba(0, 100, 0, 1);
}

/* Avoids https://github.com/mozilla/pdf.js/issues/13840 in Chrome */
.textLayer br::selection {
background: transparent;
}

.textLayer .endOfContent {
display: block;
position: absolute;
left: 0;
top: 100%;
right: 0;
bottom: 0;
z-index: -1;
cursor: default;
user-select: none;
}

.textLayer .endOfContent.active {
top: 0;
}

0 comments on commit be758b5

Please sign in to comment.