├── .gitignore
├── README.md
├── config-overrides.js
├── package-lock.json
├── package.json
├── public
└── index.html
└── src
├── App.css
├── App.js
├── Editor.js
├── Viewer.js
├── assets
├── fonts
│ ├── Roboto-Italic.ttf
│ ├── Roboto-Medium.ttf
│ ├── Roboto-MediumItalic.ttf
│ └── Roboto-Regular.ttf
└── images
│ └── bee.png
├── index.js
├── makePDF.js
└── register-files.js
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # pdfkit-react-example
2 | This is a simple example of using [PdfKit](https://pdfkit.org/) with React (bootstrapped with [Create React App](https://github.com/facebook/create-react-app)).
3 |
4 | It is based on [`pdfkit-webpack-example`](https://github.com/blikblum/pdfkit-webpack-example).
5 |
6 | [Live preview here.](https://sturtevant.github.io/pdfkit-react-example/)
7 |
8 | ## Notes
9 |
10 | - [`react-ace`](https://github.com/securingsincity/react-ace) is used for the in-browser code editting.
11 | - [`customize-cra`](https://github.com/arackaf/customize-cra) is used to modify the webpack configuration so that assets like fonts and images can be imported in all the ways found in `pdfkit-webpack-example`. These customizations can be found in `config-overrides.js`.
12 | - The initial PDF content code (`makePDF` function) has been moved into its own file (`src/makePDF.js`) so that it can be loaded separately and avoid having its formatting disrupted by Webpack/Babel.
13 |
14 | ## To Use
15 |
16 | The first time: in the project directory, run `npm install`.
17 |
18 | Then run `npm start` to start the app in development mode.
19 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
20 |
21 | The page will reload if you make edits.
22 | You will also see any lint errors in the console.
23 |
--------------------------------------------------------------------------------
/config-overrides.js:
--------------------------------------------------------------------------------
1 | const { override, addWebpackAlias, addWebpackModuleRule } = require('customize-cra')
2 |
3 | const path = require('path')
4 |
5 | module.exports = override(
6 | addWebpackAlias({fs: 'pdfkit/js/virtual-fs.js'}),
7 | addWebpackModuleRule({ enforce: 'post', test: /fontkit[/\\]index.js$/, loader: "transform-loader?brfs" }),
8 | addWebpackModuleRule({ enforce: 'post', test: /unicode-properties[/\\]index.js$/, loader: "transform-loader?brfs" }),
9 | addWebpackModuleRule({ enforce: 'post', test: /linebreak[/\\]src[/\\]linebreaker.js/, loader: "transform-loader?brfs" }),
10 | addWebpackModuleRule({ test: /src[/\\]assets/, loader: 'arraybuffer-loader' }),
11 | addWebpackModuleRule({ test: /\.afm$/, loader: 'raw-loader' }),
12 | addWebpackModuleRule({ test: /src\/makePDF\.js/, loader: 'raw-loader' })
13 | )
14 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "pdfkit-react-example",
3 | "version": "0.1.0",
4 | "private": true,
5 | "homepage": "http://sturtevant.github.io/pdfkit-react-example",
6 | "dependencies": {
7 | "@testing-library/jest-dom": "^4.2.4",
8 | "@testing-library/react": "^9.5.0",
9 | "@testing-library/user-event": "^7.2.1",
10 | "ace-builds": "^1.4.12",
11 | "blob-stream": "^0.1.3",
12 | "brace": "^0.11.1",
13 | "node-sass": "^4.14.1",
14 | "pdfkit": "^0.10.0",
15 | "react": "^16.13.1",
16 | "react-ace": "^9.1.1",
17 | "react-dom": "^16.13.1",
18 | "react-scripts": "3.4.1"
19 | },
20 | "devDependencies": {
21 | "arraybuffer-loader": "^1.0.8",
22 | "brfs": "^2.0.2",
23 | "customize-cra": "^1.0.0",
24 | "gh-pages": "^3.1.0",
25 | "html-webpack-plugin": "^3.2.0",
26 | "raw-loader": "^3.1.0",
27 | "react-app-rewired": "^2.1.6",
28 | "string-replace-webpack-plugin": "^0.1.3",
29 | "transform-loader": "^0.2.4",
30 | "webpack": "^4.41.2",
31 | "webpack-cli": "^3.3.10"
32 | },
33 | "scripts": {
34 | "start": "react-app-rewired start",
35 | "build": "react-app-rewired build",
36 | "test": "react-app-rewired test",
37 | "predeploy": "npm run build",
38 | "deploy": "gh-pages -d build"
39 | },
40 | "eslintConfig": {
41 | "extends": "react-app"
42 | },
43 | "browserslist": {
44 | "production": [
45 | ">0.2%",
46 | "not dead",
47 | "not op_mini all"
48 | ],
49 | "development": [
50 | "last 1 chrome version",
51 | "last 1 firefox version",
52 | "last 1 safari version"
53 | ]
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | PDFKit Browser Demo
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/src/App.css:
--------------------------------------------------------------------------------
1 | body {
2 | width: 1230px;
3 | margin: 20px auto;
4 | font-family: Georgia;
5 | }
6 |
7 | h1 {
8 | margin: 0;
9 | }
10 |
11 | a {
12 | color: blue;
13 | }
14 |
15 | #editor {
16 | width: 600px !important;
17 | height: 775px !important;
18 | margin-right: 20px;
19 | display: inline-block;
20 | }
21 |
22 | iframe {
23 | border: 1px solid black;
24 | }
--------------------------------------------------------------------------------
/src/App.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 | import './App.css';
3 | import Editor from './Editor';
4 | import Viewer from './Viewer';
5 | import makePDF from './makePDF';
6 |
7 | const defaultValue = makePDF
8 | .toString()
9 | .split('\n')
10 | .slice(1, -1)
11 | .join('\n')
12 | .replace(/^ {2}/gm, '');
13 |
14 | function App() {
15 | const [value, setValue] = useState(defaultValue);
16 |
17 | const onChange = (newValue) => {
18 | setValue(newValue);
19 | }
20 |
21 | return (
22 | <>
23 | PDFKit Browser Demo (built with create-react-app)
24 | Website | Github
25 |
26 |
27 | >
28 | );
29 | }
30 |
31 | export default App;
--------------------------------------------------------------------------------
/src/Editor.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import AceEditor from 'react-ace'
3 |
4 | import 'ace-builds'
5 | import 'ace-builds/webpack-resolver'
6 | import 'ace-builds/src-noconflict/mode-javascript'
7 | import 'ace-builds/src-noconflict/theme-monokai'
8 |
9 | const Editor = ({value, onChange}) => {
10 | return ()
18 | }
19 |
20 | export default Editor
--------------------------------------------------------------------------------
/src/Viewer.js:
--------------------------------------------------------------------------------
1 | import React, { useRef, useEffect } from 'react'
2 | import './register-files'
3 |
4 | var PDFDocument = require('pdfkit').default;
5 | var blobStream = require('blob-stream');
6 |
7 | var lorem =
8 | 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam in suscipit purus. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Vivamus nec hendrerit felis. Morbi aliquam facilisis risus eu lacinia. Sed eu leo in turpis fringilla hendrerit. Ut nec accumsan nisl. Suspendisse rhoncus nisl posuere tortor tempus et dapibus elit porta. Cras leo neque, elementum a rhoncus ut, vestibulum non nibh. Phasellus pretium justo turpis. Etiam vulputate, odio vitae tincidunt ultricies, eros odio dapibus nisi, ut tincidunt lacus arcu eu elit. Aenean velit erat, vehicula eget lacinia ut, dignissim non tellus. Aliquam nec lacus mi, sed vestibulum nunc. Suspendisse potenti. Curabitur vitae sem turpis. Vestibulum sed neque eget dolor dapibus porttitor at sit amet sem. Fusce a turpis lorem. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae;\nMauris at ante tellus. Vestibulum a metus lectus. Praesent tempor purus a lacus blandit eget gravida ante hendrerit. Cras et eros metus. Sed commodo malesuada eros, vitae interdum augue semper quis. Fusce id magna nunc. Curabitur sollicitudin placerat semper. Cras et mi neque, a dignissim risus. Nulla venenatis porta lacus, vel rhoncus lectus tempor vitae. Duis sagittis venenatis rutrum. Curabitur tempor massa tortor.';
9 |
10 | const Viewer = ({ value }) => {
11 | const iframe = useRef();
12 |
13 | useEffect(() => {
14 | try {
15 | // eslint-disable-next-line no-new-func
16 | var fn = new Function(
17 | 'PDFDocument',
18 | 'blobStream',
19 | 'lorem',
20 | 'iframe',
21 | value
22 | );
23 | fn(PDFDocument, blobStream, lorem, iframe.current);
24 | } catch (e) {
25 | console.log(e);
26 | }
27 | }, [value, iframe]);
28 |
29 | return ;
30 | }
31 |
32 | export default Viewer
--------------------------------------------------------------------------------
/src/assets/fonts/Roboto-Italic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sturtevant/pdfkit-react-example/15869303f4a383d0a88860c63e638ff79830b3e5/src/assets/fonts/Roboto-Italic.ttf
--------------------------------------------------------------------------------
/src/assets/fonts/Roboto-Medium.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sturtevant/pdfkit-react-example/15869303f4a383d0a88860c63e638ff79830b3e5/src/assets/fonts/Roboto-Medium.ttf
--------------------------------------------------------------------------------
/src/assets/fonts/Roboto-MediumItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sturtevant/pdfkit-react-example/15869303f4a383d0a88860c63e638ff79830b3e5/src/assets/fonts/Roboto-MediumItalic.ttf
--------------------------------------------------------------------------------
/src/assets/fonts/Roboto-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sturtevant/pdfkit-react-example/15869303f4a383d0a88860c63e638ff79830b3e5/src/assets/fonts/Roboto-Regular.ttf
--------------------------------------------------------------------------------
/src/assets/images/bee.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sturtevant/pdfkit-react-example/15869303f4a383d0a88860c63e638ff79830b3e5/src/assets/images/bee.png
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 |
5 | ReactDOM.render(
6 |
7 |
8 | ,
9 | document.getElementById('root')
10 | );
--------------------------------------------------------------------------------
/src/makePDF.js:
--------------------------------------------------------------------------------
1 | module.exports = (PDFDocument, blobStream, lorem, iframe) => {
2 | // create a document and pipe to a blob
3 | var doc = new PDFDocument();
4 | var stream = doc.pipe(blobStream());
5 |
6 | doc.registerFont('Roboto', 'fonts/Roboto-Regular.ttf')
7 |
8 | // draw some text
9 | doc.fontSize(25).text('Here is some vector graphics...', 100, 80);
10 |
11 | // some vector graphics
12 | doc
13 | .save()
14 | .moveTo(100, 150)
15 | .lineTo(100, 250)
16 | .lineTo(200, 250)
17 | .fill('#FF3300');
18 |
19 | doc.circle(280, 200, 50).fill('#6600FF');
20 |
21 | // an SVG path
22 | doc
23 | .scale(0.6)
24 | .translate(470, 130)
25 | .path('M 250,75 L 323,301 131,161 369,161 177,301 z')
26 | .fill('red', 'even-odd')
27 | .restore();
28 |
29 | // and some justified text wrapped into columns
30 | doc
31 | .font('Roboto')
32 | .text('And here is some wrapped text...', 100, 300)
33 | .fontSize(13)
34 | .moveDown()
35 | .text(lorem, {
36 | width: 412,
37 | align: 'justify',
38 | indent: 30,
39 | columns: 2,
40 | height: 300,
41 | ellipsis: true
42 | });
43 |
44 | doc.addPage();
45 |
46 | doc
47 | .fontSize(25)
48 | .font('Courier')
49 | .text('And an image...')
50 | .image('images/bee.png')
51 |
52 | doc
53 | .font('Courier-Bold')
54 | .text('Finish...')
55 |
56 | // end and display the document in the iframe to the right
57 | doc.end();
58 | stream.on('finish', function () {
59 | iframe.src = stream.toBlobURL('application/pdf');
60 | });
61 | }
--------------------------------------------------------------------------------
/src/register-files.js:
--------------------------------------------------------------------------------
1 | import fs from 'fs'
2 | // use raw-loader explicitly
3 | // eslint-disable-next-line import/no-webpack-loader-syntax
4 | import Courier from '!!raw-loader!pdfkit/js/data/Courier.afm'
5 | // use raw-loader implicitly (webpack is configured to load *.afm files using raw loader)
6 | import CourierBold from 'pdfkit/js/data/Courier-Bold.afm'
7 |
8 | function registerBinaryFiles(ctx) {
9 | ctx.keys().forEach(key => {
10 | // extracts "./" from beginning of the key
11 | fs.writeFileSync(key.substring(2), ctx(key))
12 | });
13 | }
14 |
15 | function registerAFMFonts(ctx) {
16 | ctx.keys().forEach(key => {
17 | const match = key.match(/([^/]*\.afm$)/)
18 | if (match) {
19 | // afm files must be stored on data path
20 | fs.writeFileSync(`data/${match[0]}`, ctx(key).default)
21 | }
22 | });
23 | }
24 |
25 | // register all files found in assets folder (relative to src)
26 | registerBinaryFiles(require.context('./assets', true))
27 |
28 | // register AFM fonts distributed with pdfkit
29 | // is good practice to register only required fonts to avoid the bundle size increase
30 | registerAFMFonts(require.context('pdfkit/js/data', false, /Helvetica.*\.afm$/))
31 |
32 | // register files imported directly
33 | fs.writeFileSync('data/Courier.afm', Courier)
34 | fs.writeFileSync('data/Courier-Bold.afm', CourierBold)
35 |
--------------------------------------------------------------------------------