├── .travis.yml
├── test
├── test.pdf
├── index.html
├── test.less
├── webpack.config.js
├── package.json
└── test.jsx
├── .eslintignore
├── sample
├── sample.pdf
├── index.html
├── sample.less
├── webpack.config.js
├── package.json
└── sample.jsx
├── .gitignore
├── src
├── react-pdf.entry.js
├── react-pdf.entry.noworker.js
└── react-pdf.jsx
├── .babelrc
├── .eslintrc
├── .codeclimate.yml
├── .gitattributes
├── README.md
├── LICENSE
└── package.json
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js: "node"
3 |
--------------------------------------------------------------------------------
/test/test.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nnarhinen/react-pdf/HEAD/test/test.pdf
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | # Ignore compiled files
2 | build/*
3 | sample/build/*
4 | test/build/*
5 |
--------------------------------------------------------------------------------
/sample/sample.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nnarhinen/react-pdf/HEAD/sample/sample.pdf
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .vscode
2 | sample/build
3 | test/build
4 | node_modules
5 | npm-debug.log
6 | package-lock.json
7 | stats*.json
8 |
--------------------------------------------------------------------------------
/src/react-pdf.entry.js:
--------------------------------------------------------------------------------
1 | const ReactPDF = require('./react-pdf');
2 |
3 | require('pdfjs-dist/webpack');
4 | require('pdfjs-dist/web/compatibility');
5 |
6 | module.exports = ReactPDF;
7 |
--------------------------------------------------------------------------------
/src/react-pdf.entry.noworker.js:
--------------------------------------------------------------------------------
1 | const ReactPDF = require('./react-pdf');
2 |
3 | const pdfjs = require('pdfjs-dist');
4 | require('pdfjs-dist/web/compatibility');
5 |
6 | pdfjs.PDFJS.disableWorker = true;
7 |
8 | module.exports = ReactPDF;
9 |
--------------------------------------------------------------------------------
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | [
4 | "es2015",
5 | {
6 | "modules": false
7 | }
8 | ],
9 | "stage-2",
10 | "react"
11 | ],
12 | "plugins": [
13 | "transform-class-properties"
14 | ]
15 | }
16 |
--------------------------------------------------------------------------------
/sample/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | react-pdf sample page
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/test/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | react-pdf test page
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "airbnb",
3 | "env": {
4 | "browser": true,
5 | "es6": true
6 | },
7 | "globals": {
8 | "PDFJS": true
9 | },
10 | "parser": "babel-eslint",
11 | "parserOptions": {
12 | "ecmaFeatures": {
13 | "experimentalObjectRestSpread": true
14 | }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/.codeclimate.yml:
--------------------------------------------------------------------------------
1 | engines:
2 | duplication:
3 | enabled: true
4 | exclude_paths:
5 | - "webpack.config.js"
6 | - "test/webpack.config.js"
7 | - "sample/webpack.config.js"
8 | config:
9 | languages:
10 | - javascript
11 | eslint:
12 | enabled: true
13 | fixme:
14 | enabled: true
15 | ratings:
16 | paths:
17 | - "**.js"
18 | - "**.jsx"
19 | exclude_paths:
20 | - build/
21 | - test/build/
22 | - sample/build/
23 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
4 | # Custom for Visual Studio
5 | *.cs diff=csharp
6 |
7 | # Standard to msysgit
8 | *.doc diff=astextplain
9 | *.DOC diff=astextplain
10 | *.docx diff=astextplain
11 | *.DOCX diff=astextplain
12 | *.dot diff=astextplain
13 | *.DOT diff=astextplain
14 | *.pdf diff=astextplain
15 | *.PDF diff=astextplain
16 | *.rtf diff=astextplain
17 | *.RTF diff=astextplain
18 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # React-PDF
2 | Easily display PDF files in your React application.
3 |
4 | # We've moved
5 |
6 | This GitHub repository is no longer maintained. See [wojtekmaj/react-pdf](https://github.com/wojtekmaj/react-pdf) to report bugs, contribute and follow existing and upcoming releases.
7 |
8 | Don't worry, react-pdf on NPM is fully up-to-date with [wojtekmaj/react-pdf](https://github.com/wojtekmaj/react-pdf). You don't need to do any changes within your package.json file!
9 |
--------------------------------------------------------------------------------
/sample/sample.less:
--------------------------------------------------------------------------------
1 | .Example {
2 | font-family: Segoe UI, Tahoma, sans-serif;
3 |
4 | input, button {
5 | font: inherit;
6 | }
7 |
8 | &__container {
9 | display: flex;
10 | flex-direction: column;
11 | align-items: center;
12 |
13 | &__load {
14 | }
15 |
16 | &__preview {
17 | border: 1px solid darkgray;
18 | margin: 1em 0;
19 | }
20 |
21 | &__controls {
22 | width: 300px;
23 | display: flex;
24 |
25 | span {
26 | flex-grow: 1;
27 | margin: 0 1em;
28 | text-align: center;
29 | }
30 |
31 | button {
32 | width: 80px;
33 | }
34 | }
35 | }
36 | }
--------------------------------------------------------------------------------
/test/test.less:
--------------------------------------------------------------------------------
1 | .Example {
2 | font-family: Segoe UI, Tahoma, sans-serif;
3 |
4 | input, button {
5 | font: inherit;
6 | }
7 |
8 | &__container {
9 | display: flex;
10 | flex-direction: row;
11 | align-items: flex-start;
12 |
13 | &__load {
14 | width: 420px;
15 | }
16 |
17 | &__preview {
18 | display: flex;
19 | flex-direction: column;
20 | align-items: center;
21 | width: 420px;
22 |
23 | &__out {
24 | border: 1px solid darkgray;
25 | margin: 1em 0;
26 | }
27 |
28 | &__controls {
29 | width: 420px;
30 | display: flex;
31 |
32 | span {
33 | flex-grow: 1;
34 | margin: 0 1em;
35 | text-align: center;
36 | }
37 |
38 | button {
39 | width: 80px;
40 | }
41 | }
42 | }
43 | }
44 | }
--------------------------------------------------------------------------------
/test/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const CopyWebpackPlugin = require('copy-webpack-plugin');
3 |
4 | module.exports = {
5 | context: __dirname,
6 | devtool: 'source-map',
7 | entry: './test',
8 | output: {
9 | path: path.join(__dirname, 'build'),
10 | filename: '[name].bundle.js',
11 | },
12 | resolve: {
13 | extensions: ['.js', '.jsx'],
14 | },
15 | module: {
16 | rules: [
17 | {
18 | test: /\.pdf$/,
19 | use: 'url-loader',
20 | },
21 | {
22 | test: /\.less$/,
23 | use: [
24 | 'style-loader',
25 | 'css-loader',
26 | 'less-loader',
27 | ],
28 | },
29 | {
30 | test: /\.jsx?$/,
31 | exclude: /node_modules/,
32 | use: 'babel-loader',
33 | },
34 | ],
35 | },
36 | plugins: [
37 | new CopyWebpackPlugin([
38 | { from: './index.html' },
39 | { from: './test.pdf' },
40 | ]),
41 | ],
42 | };
43 |
--------------------------------------------------------------------------------
/sample/webpack.config.js:
--------------------------------------------------------------------------------
1 | const webpack = require('webpack');
2 | const path = require('path');
3 | const CopyWebpackPlugin = require('copy-webpack-plugin');
4 |
5 | module.exports = {
6 | context: __dirname,
7 | entry: './sample',
8 | output: {
9 | path: path.join(__dirname, 'build'),
10 | filename: '[name].bundle.js',
11 | },
12 | resolve: {
13 | extensions: ['.js', '.jsx'],
14 | },
15 | module: {
16 | rules: [
17 | {
18 | test: /\.less$/,
19 | use: [
20 | 'style-loader',
21 | 'css-loader',
22 | 'less-loader',
23 | ],
24 | },
25 | {
26 | test: /\.jsx?$/,
27 | exclude: /node_modules/,
28 | use: 'babel-loader',
29 | },
30 | ],
31 | },
32 | plugins: [
33 | new webpack.DefinePlugin({
34 | 'process.env': {
35 | NODE_ENV: JSON.stringify('production'),
36 | },
37 | }),
38 | new webpack.optimize.UglifyJsPlugin(),
39 | new CopyWebpackPlugin([
40 | { from: './index.html' },
41 | { from: './sample.pdf' },
42 | ]),
43 | ],
44 | };
45 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Wojciech Maj
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/test/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-pdf-test-page",
3 | "version": "1.7.0",
4 | "description": "A test page for React-PDF.",
5 | "scripts": {
6 | "build": "webpack"
7 | },
8 | "author": {
9 | "name": "Wojciech Maj",
10 | "email": "kontakt@wojtekmaj.pl"
11 | },
12 | "license": "MIT",
13 | "dependencies": {
14 | "react": ">=15.5",
15 | "react-dom": ">=15.5",
16 | "react-pdf": "../"
17 | },
18 | "devDependencies": {
19 | "babel-core": "^6.25.0",
20 | "babel-eslint": "^7.2.3",
21 | "babel-loader": "^7.1.1",
22 | "babel-plugin-transform-class-properties": "^6.24.1",
23 | "babel-preset-es2015": "^6.24.1",
24 | "babel-preset-react": "^6.24.1",
25 | "babel-preset-stage-2": "^6.24.1",
26 | "copy-webpack-plugin": "^4.0.1",
27 | "css-loader": "latest",
28 | "eslint": "^3.19.0",
29 | "eslint-config-airbnb": "^15.0.2",
30 | "eslint-plugin-class-property": "^1.0.6",
31 | "eslint-plugin-import": "^2.7.0",
32 | "eslint-plugin-jsx-a11y": "^5.1.1",
33 | "eslint-plugin-react": "^7.1.0",
34 | "file-loader": "latest",
35 | "less": "^2.7.2",
36 | "less-loader": "latest",
37 | "style-loader": "latest",
38 | "url-loader": "latest",
39 | "webpack": "^2.7.0"
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/sample/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-pdf-sample-page",
3 | "version": "1.7.0",
4 | "description": "A sample page for React-PDF.",
5 | "scripts": {
6 | "build": "webpack"
7 | },
8 | "author": {
9 | "name": "Wojciech Maj",
10 | "email": "kontakt@wojtekmaj.pl"
11 | },
12 | "license": "MIT",
13 | "dependencies": {
14 | "react": ">=15.5",
15 | "react-dom": ">=15.5",
16 | "react-pdf": "../"
17 | },
18 | "devDependencies": {
19 | "babel-core": "^6.25.0",
20 | "babel-eslint": "^7.2.3",
21 | "babel-loader": "^7.1.1",
22 | "babel-plugin-transform-class-properties": "^6.24.1",
23 | "babel-preset-es2015": "^6.24.1",
24 | "babel-preset-react": "^6.24.1",
25 | "babel-preset-stage-2": "^6.24.1",
26 | "copy-webpack-plugin": "^4.0.1",
27 | "css-loader": "latest",
28 | "eslint": "^3.19.0",
29 | "eslint-config-airbnb": "^15.0.2",
30 | "eslint-plugin-class-property": "^1.0.6",
31 | "eslint-plugin-import": "^2.7.0",
32 | "eslint-plugin-jsx-a11y": "^5.1.1",
33 | "eslint-plugin-react": "^7.1.0",
34 | "file-loader": "latest",
35 | "less": "^2.7.2",
36 | "less-loader": "latest",
37 | "style-loader": "latest",
38 | "url-loader": "latest",
39 | "webpack": "^2.7.0"
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-pdf",
3 | "version": "1.7.0",
4 | "description": "Easily display PDF files in your React application.",
5 | "main": "build/react-pdf.entry.js",
6 | "es6": "src/react-pdf.entry.js",
7 | "scripts": {
8 | "build": "babel src -d build",
9 | "eslint": "eslint ./src",
10 | "prepublishOnly": "npm run build",
11 | "test": "npm run eslint"
12 | },
13 | "keywords": [
14 | "pdf",
15 | "pdf-viewer",
16 | "react"
17 | ],
18 | "author": {
19 | "name": "Wojciech Maj",
20 | "email": "kontakt@wojtekmaj.pl"
21 | },
22 | "contributors": [
23 | {
24 | "name": "Niklas Närhinen",
25 | "email": "niklas@narhinen.net"
26 | },
27 | {
28 | "name": "Bart Van Houtte",
29 | "email": "bart.van.houtte@ading.be"
30 | }
31 | ],
32 | "license": "MIT",
33 | "dependencies": {
34 | "pdfjs-dist": "^1.8.532",
35 | "prop-types": ">=15.5",
36 | "react": ">=15.5",
37 | "react-dom": ">=15.5"
38 | },
39 | "devDependencies": {
40 | "babel-cli": "^6.24.1",
41 | "babel-core": "^6.25.0",
42 | "babel-eslint": "^7.2.3",
43 | "babel-plugin-transform-class-properties": "^6.24.1",
44 | "babel-preset-es2015": "^6.24.1",
45 | "babel-preset-react": "^6.24.1",
46 | "babel-preset-stage-2": "^6.24.1",
47 | "eslint": "^3.19.0",
48 | "eslint-config-airbnb": "^15.0.2",
49 | "eslint-plugin-class-property": "^1.0.6",
50 | "eslint-plugin-import": "^2.7.0",
51 | "eslint-plugin-jsx-a11y": "^5.1.1",
52 | "eslint-plugin-react": "^7.1.0"
53 | },
54 | "repository": {
55 | "type": "git",
56 | "url": "https://github.com/wojtekmaj/react-pdf.git"
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/sample/sample.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { render } from 'react-dom';
3 | import ReactPDF from 'react-pdf';
4 |
5 | import './sample.less';
6 |
7 | class Example extends Component {
8 | state = {
9 | file: './sample.pdf',
10 | pageIndex: null,
11 | pageNumber: null,
12 | total: null,
13 | }
14 |
15 | onFileChange = (event) => {
16 | this.setState({
17 | file: event.target.files[0],
18 | });
19 | }
20 |
21 | onDocumentLoad = ({ total }) => {
22 | this.setState({ total });
23 | }
24 |
25 | onPageLoad = ({ pageIndex, pageNumber }) => {
26 | this.setState({ pageIndex, pageNumber });
27 | }
28 |
29 | changePage(by) {
30 | this.setState(prevState => ({
31 | pageIndex: prevState.pageIndex + by,
32 | }));
33 | }
34 |
35 | render() {
36 | const { file, pageIndex, pageNumber, total } = this.state;
37 |
38 | return (
39 |
40 |
react-pdf sample page
41 |
42 |
43 |
44 |
48 |
49 |
50 |
57 |
58 |
59 |
65 | Page {pageNumber || '--'} of {total || '--'}
66 |
72 |
73 |
74 |
75 | );
76 | }
77 | }
78 |
79 | render(, document.getElementById('react-container'));
80 |
--------------------------------------------------------------------------------
/test/test.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { render } from 'react-dom';
3 | import ReactPDF from 'react-pdf';
4 |
5 | import './test.less';
6 |
7 | import samplePDF from './test.pdf';
8 |
9 | let componentRenderCount = 0;
10 |
11 | class WrappedReactPDF extends ReactPDF {
12 | componentDidMount() {
13 | super.componentDidMount();
14 | }
15 |
16 | componentWillReceiveProps(nextProps) {
17 | super.componentWillReceiveProps(nextProps);
18 | }
19 |
20 | componentWillUpdate() {
21 | componentRenderCount += 1;
22 | }
23 | }
24 |
25 | WrappedReactPDF.propTypes = ReactPDF.propTypes;
26 |
27 | class Test extends Component {
28 | state = {
29 | file: null,
30 | pageIndex: null,
31 | pageNumber: null,
32 | passObj: false,
33 | pageRenderCount: 0,
34 | pageWidth: 300,
35 | total: null,
36 | }
37 |
38 | onFileChange = (event) => {
39 | this.setState({
40 | file: event.target.files[0],
41 | });
42 | }
43 |
44 | onFileUintChange = (event) => {
45 | const reader = new FileReader();
46 |
47 | reader.onloadend = () => {
48 | this.setState({
49 | file: reader.result,
50 | });
51 | };
52 |
53 | reader.readAsArrayBuffer(event.target.files[0]);
54 | }
55 |
56 | onURLChange = (event) => {
57 | event.preventDefault();
58 |
59 | const url = event.target.querySelector('input').value;
60 |
61 | if (!url) {
62 | return;
63 | }
64 |
65 | this.setState({
66 | file: url,
67 | });
68 | }
69 |
70 | onRequestChange = (event) => {
71 | event.preventDefault();
72 |
73 | const url = event.target.querySelector('input').value;
74 |
75 | if (!url) {
76 | return;
77 | }
78 |
79 | fetch(url).then(response => response.blob()).then((blob) => {
80 | this.setState({
81 | file: blob,
82 | });
83 | });
84 | }
85 |
86 | onUseImported = () => {
87 | this.setState({
88 | file: samplePDF,
89 | });
90 | }
91 |
92 | onPassObjChange = (event) => {
93 | this.setState({ passObj: event.target.checked });
94 | }
95 |
96 | onPageWidthChange = (event) => {
97 | const width = event.target.value;
98 |
99 | if (!width) {
100 | return;
101 | }
102 |
103 | this.setState({
104 | pageWidth: parseInt(width, 10),
105 | });
106 | }
107 |
108 | onDocumentLoad = ({ total }) => {
109 | this.setState({ total });
110 | }
111 |
112 | onDocumentError = ({ message }) => {
113 | // eslint-disable-next-line no-console
114 | console.error(message);
115 | }
116 |
117 | onPageLoad = ({ pageIndex, pageNumber }) => {
118 | this.setState({ pageIndex, pageNumber });
119 | }
120 |
121 | onPageRender = () => {
122 | this.setState({ pageRenderCount: (this.state.pageRenderCount + 1) });
123 | }
124 |
125 | get transformedFile() {
126 | if (!this.state.passObj) {
127 | return this.state.file;
128 | }
129 |
130 | const result = {};
131 | if (typeof this.state.file === 'string') {
132 | result.url = this.state.file;
133 | } else {
134 | return this.state.file;
135 | }
136 | return result;
137 | }
138 |
139 | changePage(by) {
140 | this.setState(prevState => ({
141 | pageIndex: prevState.pageIndex + by,
142 | }));
143 | }
144 |
145 | render() {
146 | const { pageIndex, pageNumber, pageRenderCount, pageWidth, total } = this.state;
147 |
148 | return (
149 |
150 |
react-pdf test page
151 |
152 |
153 |
154 |
158 |
159 |
160 |
164 |
165 |
170 |
171 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
191 |
192 |
193 |
194 |
195 |
203 |
204 |
205 |
211 | Page {pageNumber || '--'} of {total || '--'}
212 |
218 |
219 |
220 | Page render count: {pageRenderCount}
221 | Component render count: {componentRenderCount}
222 |
223 |
224 |
225 |
226 | );
227 | }
228 | }
229 |
230 | render(, document.getElementById('react-container'));
231 |
--------------------------------------------------------------------------------
/src/react-pdf.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import PropTypes from 'prop-types';
3 |
4 | export default class ReactPDF extends Component {
5 | state = {
6 | pdf: null,
7 | page: null,
8 | }
9 |
10 | componentDidMount() {
11 | this.handleFileLoad();
12 | }
13 |
14 | componentWillReceiveProps(nextProps) {
15 | if (this.isParameterObject(nextProps.file)) {
16 | // File is a parameter object
17 | if (
18 | (nextProps.file && !this.props.file) ||
19 | nextProps.file.data !== this.props.file.data ||
20 | nextProps.file.range !== this.props.file.range ||
21 | nextProps.file.url !== this.props.file.url
22 | ) {
23 | this.handleFileLoad(nextProps);
24 | return;
25 | }
26 | } else if (nextProps.file && nextProps.file !== this.props.file) {
27 | // File is a normal object or not an object at all
28 | this.handleFileLoad(nextProps);
29 | return;
30 | }
31 |
32 | if (
33 | this.state.pdf &&
34 | typeof nextProps.pageIndex !== 'undefined' &&
35 | nextProps.pageIndex !== this.props.pageIndex
36 | ) {
37 | this.loadPage(nextProps.pageIndex);
38 | }
39 | }
40 |
41 | shouldComponentUpdate(nextProps, nextState) {
42 | return (
43 | nextState.pdf !== this.state.pdf ||
44 | nextState.page !== this.state.page ||
45 | nextProps.width !== this.props.width ||
46 | nextProps.scale !== this.props.scale
47 | );
48 | }
49 |
50 | /**
51 | * Called when a document is loaded successfully.
52 | */
53 | onDocumentLoad = (pdf) => {
54 | this.callIfDefined(
55 | this.props.onDocumentLoad,
56 | {
57 | total: pdf.numPages,
58 | },
59 | );
60 |
61 | this.setState({ pdf });
62 |
63 | this.loadPage(this.props.pageIndex);
64 | }
65 |
66 | /**
67 | * Called when a document fails to load.
68 | */
69 | onDocumentError = (error) => {
70 | this.callIfDefined(
71 | this.props.onDocumentError,
72 | error,
73 | );
74 |
75 | this.setState({ pdf: false });
76 | }
77 |
78 | /**
79 | * Called when a page is loaded successfully.
80 | */
81 | onPageLoad = (page) => {
82 | const scale = this.getPageScale(page);
83 |
84 | this.callIfDefined(
85 | this.props.onPageLoad,
86 | {
87 | pageIndex: page.pageIndex,
88 | pageNumber: page.pageNumber,
89 | get width() { return page.view[2] * scale; },
90 | get height() { return page.view[3] * scale; },
91 | scale,
92 | get originalWidth() { return page.view[2]; },
93 | get originalHeight() { return page.view[3]; },
94 | },
95 | );
96 |
97 | this.setState({ page });
98 | }
99 |
100 | /**
101 | * Called when a page is rendered successfully.
102 | */
103 | onPageRender = () => {
104 | this.renderer = null;
105 |
106 | this.callIfDefined(this.props.onPageRender);
107 | }
108 |
109 | /**
110 | * Called when a page fails to load or render.
111 | */
112 | onPageError = (error) => {
113 | this.callIfDefined(
114 | this.props.onPageError,
115 | error,
116 | );
117 |
118 | this.setState({ page: false });
119 | }
120 |
121 | getPageScale(page = this.state.page) {
122 | const { scale, width } = this.props;
123 |
124 | // Be default, we'll render page at 100% * scale width.
125 | let pageScale = 1;
126 |
127 | // If width is defined, calculate the scale of the page so it could be of desired width.
128 | if (width) {
129 | pageScale = width / page.getViewport(scale).width;
130 | }
131 |
132 | return scale * pageScale;
133 | }
134 |
135 | callIfDefined = (fn, args) => {
136 | if (fn && typeof fn === 'function') {
137 | fn(args);
138 | }
139 | }
140 |
141 | displayCORSWarning = () => {
142 | // eslint-disable-next-line no-console
143 | console.warn('Loading PDF as base64 strings/URLs might not work on protocols other than HTTP/HTTPS. On Google Chrome, you can use --allow-file-access-from-files flag for debugging purposes.');
144 | }
145 |
146 | isParameterObject = object =>
147 | object &&
148 | typeof object === 'object' &&
149 | ['file', 'range', 'url'].some(key => Object.keys(object).includes(key))
150 |
151 | isDataURI = str => /^data:/.test(str)
152 |
153 | dataURItoBlob = (dataURI) => {
154 | let byteString;
155 | if (dataURI.split(',')[0].indexOf('base64') >= 0) {
156 | byteString = atob(dataURI.split(',')[1]);
157 | } else {
158 | byteString = unescape(dataURI.split(',')[1]);
159 | }
160 |
161 | const mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0];
162 |
163 | const ia = new Uint8Array(byteString.length);
164 | for (let i = 0; i < byteString.length; i += 1) {
165 | ia[i] = byteString.charCodeAt(i);
166 | }
167 |
168 | return new Blob([ia], { type: mimeString });
169 | }
170 |
171 | handleFileLoad(props = this.props) {
172 | let { file } = props;
173 |
174 | if (
175 | !file ||
176 | (
177 | this.isParameterObject(file) &&
178 | !file.data && !file.range && !file.url
179 | )
180 | ) {
181 | return null;
182 | }
183 |
184 | this.setState({
185 | page: null,
186 | pdf: null,
187 | });
188 |
189 | // File is a string
190 | if (typeof file === 'string') {
191 | // File is not data URI
192 | if (!this.isDataURI(file)) {
193 | if (window.location.protocol === 'file:') {
194 | this.displayCORSWarning();
195 | }
196 |
197 | return this.loadDocument(file);
198 | }
199 |
200 | // File is data URI
201 | file = this.dataURItoBlob(file);
202 |
203 | // Fall through to "File is a blob"
204 | }
205 |
206 | // File is a Blob
207 | if (file instanceof Blob) {
208 | file = URL.createObjectURL(file);
209 |
210 | return this.loadDocument(file);
211 | }
212 |
213 | // File is a File
214 | if (file instanceof File) {
215 | const reader = new FileReader();
216 |
217 | reader.onloadend = () => {
218 | this.loadDocument(new Uint8Array(reader.result));
219 | };
220 |
221 | return reader.readAsArrayBuffer(file);
222 | }
223 |
224 | // File is an ArrayBuffer
225 | if (file instanceof ArrayBuffer) {
226 | return this.loadDocument(file);
227 | }
228 |
229 | // File is a parameter object
230 | if (this.isParameterObject(file)) {
231 | if (
232 | file.url &&
233 | window.location.protocol === 'file:'
234 | ) {
235 | this.displayCORSWarning();
236 | }
237 |
238 | // Prevent from modifying props
239 | file = Object.assign({}, file);
240 |
241 | // File is data URI
242 | if (file.url && this.isDataURI(file.url)) {
243 | file = URL.createObjectURL(this.dataURItoBlob(file.url));
244 | }
245 |
246 | return this.loadDocument(file);
247 | }
248 |
249 | throw new Error('Unrecognized input type.');
250 | }
251 |
252 | loadDocument(...args) {
253 | PDFJS.getDocument(...args)
254 | .then(this.onDocumentLoad)
255 | .catch(this.onDocumentError);
256 | }
257 |
258 | loadPage(pageIndex) {
259 | const { pdf } = this.state;
260 |
261 | if (!pdf) {
262 | throw new Error('Unexpected call to getPage() before the document has been loaded.');
263 | }
264 |
265 | let pageNumber = pageIndex + 1;
266 |
267 | if (!pageIndex || pageNumber < 1) {
268 | pageNumber = 1;
269 | } else if (pageNumber >= pdf.numPages) {
270 | pageNumber = pdf.numPages;
271 | }
272 |
273 | pdf.getPage(pageNumber)
274 | .then(this.onPageLoad)
275 | .catch(this.onPageError);
276 | }
277 |
278 | renderNoData() {
279 | return (
280 | {this.props.noData}
281 | );
282 | }
283 |
284 | renderError() {
285 | return (
286 | {this.props.error}
287 | );
288 | }
289 |
290 | renderLoader() {
291 | return (
292 | {this.props.loading}
293 | );
294 | }
295 |
296 | render() {
297 | const { file } = this.props;
298 | const { pdf, page } = this.state;
299 |
300 | if (!file) {
301 | return this.renderNoData();
302 | }
303 |
304 | if (pdf === false || page === false) {
305 | return this.renderError();
306 | }
307 |
308 | if (pdf === null || page === null) {
309 | return this.renderLoader();
310 | }
311 |
312 | return (
313 |