├── .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 | --------------------------------------------------------------------------------