├── .babelrc ├── .github └── FUNDING.yml ├── .gitignore ├── .npmignore ├── ISSUE_TEMPLATE.md ├── LICENSE ├── README.md ├── package-lock.json ├── package.json └── src ├── CMapReaderFactory.js ├── annotationLayer.css ├── buffer-loader.js ├── componentFactory.js ├── pdfjsWrapper.js ├── vuePdfNoSss.vue ├── vuePdfNoSssNoWorker.vue └── vuePdfSss.vue /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": ["syntax-dynamic-import"] 3 | } -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry 13 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | 11 | # Directory for instrumented libs generated by jscoverage/JSCover 12 | lib-cov 13 | 14 | # Coverage directory used by tools like istanbul 15 | coverage 16 | 17 | # nyc test coverage 18 | .nyc_output 19 | 20 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 21 | .grunt 22 | 23 | # node-waf configuration 24 | .lock-wscript 25 | 26 | # Compiled binary addons (http://nodejs.org/api/addons.html) 27 | build/Release 28 | 29 | # Dependency directories 30 | node_modules 31 | jspm_packages 32 | 33 | # Optional npm cache directory 34 | .npm 35 | 36 | # Optional REPL history 37 | .node_repl_history 38 | 39 | # Misc 40 | tests 41 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | 11 | # Directory for instrumented libs generated by jscoverage/JSCover 12 | lib-cov 13 | 14 | # Coverage directory used by tools like istanbul 15 | coverage 16 | 17 | # nyc test coverage 18 | .nyc_output 19 | 20 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 21 | .grunt 22 | 23 | # node-waf configuration 24 | .lock-wscript 25 | 26 | # Compiled binary addons (http://nodejs.org/api/addons.html) 27 | build/Release 28 | 29 | # Dependency directories 30 | node_modules 31 | jspm_packages 32 | 33 | # Optional npm cache directory 34 | .npm 35 | 36 | # Optional REPL history 37 | .node_repl_history 38 | 39 | # Misc 40 | tests 41 | -------------------------------------------------------------------------------- /ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 17 | 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Franck Freiburger 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vue-pdf 2 | vue.js pdf viewer is a package for Vue that enables you to display and view PDF's easily via vue components. 3 | 4 | ## Install via NPM/Yarn 5 | ```bash 6 | npm install vue-pdf 7 | ``` 8 | 9 | ```bash 10 | yarn add vue-pdf 11 | ``` 12 | 13 | ## Example - basic 14 | ```vue 15 | 18 | 19 | 134 | ``` 135 | 136 | 137 | ##### Example - display multiple pages of the same pdf document 138 | ```vue 139 | 150 | 151 | 177 | ``` 178 | 179 | 180 | ##### Example - print all pages 181 | ```vue 182 | 186 | ``` 187 | 188 | 189 | ##### Example - print multiple pages 190 | ```vue 191 | 195 | ``` 196 | 197 | 198 | ##### Example - get text content 199 | ```vue 200 | 213 | 214 | 239 | ``` 240 | 241 | 242 | ##### Example - complete 243 | ```vue 244 | 260 | 298 | ``` 299 | 300 | ## Credits 301 | [ Franck Freiburger](https://www.franck-freiburger.com) 302 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-pdf", 3 | "version": "4.2.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@types/json-schema": { 8 | "version": "7.0.4", 9 | "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.4.tgz", 10 | "integrity": "sha512-8+KAKzEvSUdeo+kmqnKrqgeE+LcA0tjYWFY7RPProVYwnqDjukzO+3b6dLD56rYX5TdWejnEOLJYOIeh4CXKuA==" 11 | }, 12 | "babel-plugin-syntax-dynamic-import": { 13 | "version": "6.18.0", 14 | "resolved": "https://registry.npmjs.org/babel-plugin-syntax-dynamic-import/-/babel-plugin-syntax-dynamic-import-6.18.0.tgz", 15 | "integrity": "sha1-jWomIpyDdFqZgqRBBRVyyqF5sdo=" 16 | }, 17 | "big.js": { 18 | "version": "5.2.2", 19 | "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", 20 | "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==" 21 | }, 22 | "emojis-list": { 23 | "version": "3.0.0", 24 | "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", 25 | "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==" 26 | }, 27 | "fast-json-stable-stringify": { 28 | "version": "2.0.0", 29 | "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", 30 | "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" 31 | }, 32 | "json-schema-traverse": { 33 | "version": "0.4.1", 34 | "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", 35 | "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" 36 | }, 37 | "json5": { 38 | "version": "1.0.1", 39 | "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", 40 | "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", 41 | "requires": { 42 | "minimist": "^1.2.0" 43 | } 44 | }, 45 | "loader-utils": { 46 | "version": "1.4.0", 47 | "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", 48 | "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", 49 | "requires": { 50 | "big.js": "^5.2.2", 51 | "emojis-list": "^3.0.0", 52 | "json5": "^1.0.1" 53 | } 54 | }, 55 | "minimist": { 56 | "version": "1.2.5", 57 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", 58 | "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" 59 | }, 60 | "pdfjs-dist": { 61 | "version": "2.5.207", 62 | "resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-2.5.207.tgz", 63 | "integrity": "sha512-xGDUhnCYPfHy+unMXCLCJtlpZaaZ17Ew3WIL0tnSgKFUZXHAPD49GO9xScyszSsQMoutNDgRb+rfBXIaX/lJbw==" 64 | }, 65 | "punycode": { 66 | "version": "2.1.1", 67 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", 68 | "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" 69 | }, 70 | "raw-loader": { 71 | "version": "4.0.1", 72 | "resolved": "https://registry.npmjs.org/raw-loader/-/raw-loader-4.0.1.tgz", 73 | "integrity": "sha512-baolhQBSi3iNh1cglJjA0mYzga+wePk7vdEX//1dTFd+v4TsQlQE0jitJSNF1OIP82rdYulH7otaVmdlDaJ64A==", 74 | "requires": { 75 | "loader-utils": "^2.0.0", 76 | "schema-utils": "^2.6.5" 77 | }, 78 | "dependencies": { 79 | "ajv": { 80 | "version": "6.12.2", 81 | "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.2.tgz", 82 | "integrity": "sha512-k+V+hzjm5q/Mr8ef/1Y9goCmlsK4I6Sm74teeyGvFk1XrOsbsKLjEdrvny42CZ+a8sXbk8KWpY/bDwS+FLL2UQ==", 83 | "requires": { 84 | "fast-deep-equal": "^3.1.1", 85 | "fast-json-stable-stringify": "^2.0.0", 86 | "json-schema-traverse": "^0.4.1", 87 | "uri-js": "^4.2.2" 88 | } 89 | }, 90 | "ajv-keywords": { 91 | "version": "3.4.1", 92 | "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.4.1.tgz", 93 | "integrity": "sha512-RO1ibKvd27e6FEShVFfPALuHI3WjSVNeK5FIsmme/LYRNxjKuNj+Dt7bucLa6NdSv3JcVTyMlm9kGR84z1XpaQ==" 94 | }, 95 | "emojis-list": { 96 | "version": "3.0.0", 97 | "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", 98 | "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==" 99 | }, 100 | "fast-deep-equal": { 101 | "version": "3.1.1", 102 | "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz", 103 | "integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==" 104 | }, 105 | "json5": { 106 | "version": "2.1.3", 107 | "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz", 108 | "integrity": "sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==", 109 | "requires": { 110 | "minimist": "^1.2.5" 111 | } 112 | }, 113 | "loader-utils": { 114 | "version": "2.0.0", 115 | "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz", 116 | "integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==", 117 | "requires": { 118 | "big.js": "^5.2.2", 119 | "emojis-list": "^3.0.0", 120 | "json5": "^2.1.2" 121 | } 122 | }, 123 | "minimist": { 124 | "version": "1.2.5", 125 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", 126 | "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" 127 | }, 128 | "schema-utils": { 129 | "version": "2.7.0", 130 | "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz", 131 | "integrity": "sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==", 132 | "requires": { 133 | "@types/json-schema": "^7.0.4", 134 | "ajv": "^6.12.2", 135 | "ajv-keywords": "^3.4.1" 136 | } 137 | } 138 | } 139 | }, 140 | "uri-js": { 141 | "version": "4.2.2", 142 | "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", 143 | "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", 144 | "requires": { 145 | "punycode": "^2.1.0" 146 | } 147 | }, 148 | "vue-resize-sensor": { 149 | "version": "2.0.0", 150 | "resolved": "https://registry.npmjs.org/vue-resize-sensor/-/vue-resize-sensor-2.0.0.tgz", 151 | "integrity": "sha512-W+y2EAI/BxS4Vlcca9scQv8ifeBFck56DRtSwWJ2H4Cw1GLNUYxiZxUHHkuzuI5JPW/cYtL1bPO5xPyEXx4LmQ==" 152 | } 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-pdf", 3 | "version": "4.2.0", 4 | "description": "vue.js pdf viewer", 5 | "main": "src/vuePdfNoSss.vue", 6 | "scripts": {}, 7 | "repository": { 8 | "type": "git", 9 | "url": "git+https://github.com/FranckFreiburger/vue-pdf.git" 10 | }, 11 | "keywords": [ 12 | "vue.js", 13 | "pdf", 14 | "viewer" 15 | ], 16 | "author": "Franck FREIBURGER", 17 | "license": "MIT", 18 | "bugs": { 19 | "url": "https://github.com/FranckFreiburger/vue-pdf/issues" 20 | }, 21 | "homepage": "https://github.com/FranckFreiburger/vue-pdf#readme", 22 | "dependencies": { 23 | "babel-plugin-syntax-dynamic-import": "^6.18.0", 24 | "loader-utils": "^1.4.0", 25 | "raw-loader": "^4.0.1", 26 | "worker-loader": "^2.0.0", 27 | "pdfjs-dist": "^2.5.207 <2.8.0", 28 | "vue-resize-sensor": "^2.0.0" 29 | }, 30 | "peerDependencies": {} 31 | } 32 | -------------------------------------------------------------------------------- /src/CMapReaderFactory.js: -------------------------------------------------------------------------------- 1 | import { CMapCompressionType } from 'pdfjs-dist/es5/build/pdf.js' 2 | 3 | // see https://github.com/mozilla/pdf.js/blob/628e70fbb5dea3b9066aa5c34cca70aaafef8db2/src/display/dom_utils.js#L64 4 | 5 | export default function() { 6 | 7 | this.fetch = function(query) { 8 | 9 | return import('./buffer-loader!pdfjs-dist/cmaps/'+query.name+'.bcmap' /* webpackChunkName: "noprefetch-[request]" */) 10 | .then(function(bcmap) { 11 | 12 | return { 13 | cMapData: bcmap.default, 14 | compressionType: CMapCompressionType.BINARY, 15 | }; 16 | }); 17 | } 18 | }; 19 | -------------------------------------------------------------------------------- /src/annotationLayer.css: -------------------------------------------------------------------------------- 1 | /* see https://github.com/mozilla/pdf.js/blob/55a853b6678cf3d05681ffbb521e5228e607b5d2/test/annotation_layer_test.css */ 2 | 3 | .annotationLayer { 4 | position: absolute; 5 | left: 0; 6 | top: 0; 7 | right: 0; 8 | bottom: 0; 9 | } 10 | 11 | .annotationLayer section { 12 | position: absolute; 13 | } 14 | 15 | .annotationLayer .linkAnnotation > a { 16 | position: absolute; 17 | font-size: 1em; 18 | top: 0; 19 | left: 0; 20 | width: 100%; 21 | height: 100%; 22 | } 23 | 24 | .annotationLayer .linkAnnotation > a /* -ms-a */ { 25 | background: url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7") 0 0 repeat; 26 | } 27 | 28 | .annotationLayer .linkAnnotation > a:hover { 29 | opacity: 0.2; 30 | background: #ff0; 31 | box-shadow: 0px 2px 10px #ff0; 32 | } 33 | 34 | .annotationLayer .textAnnotation img { 35 | position: absolute; 36 | cursor: pointer; 37 | } 38 | 39 | .annotationLayer .textWidgetAnnotation input, 40 | .annotationLayer .textWidgetAnnotation textarea, 41 | .annotationLayer .choiceWidgetAnnotation select, 42 | .annotationLayer .buttonWidgetAnnotation.checkBox input, 43 | .annotationLayer .buttonWidgetAnnotation.radioButton input { 44 | background-color: rgba(0, 54, 255, 0.13); 45 | border: 1px solid transparent; 46 | box-sizing: border-box; 47 | font-size: 9px; 48 | height: 100%; 49 | padding: 0 3px; 50 | vertical-align: top; 51 | width: 100%; 52 | } 53 | 54 | .annotationLayer .textWidgetAnnotation textarea { 55 | font: message-box; 56 | font-size: 9px; 57 | resize: none; 58 | } 59 | 60 | .annotationLayer .textWidgetAnnotation input[disabled], 61 | .annotationLayer .textWidgetAnnotation textarea[disabled], 62 | .annotationLayer .choiceWidgetAnnotation select[disabled], 63 | .annotationLayer .buttonWidgetAnnotation.checkBox input[disabled], 64 | .annotationLayer .buttonWidgetAnnotation.radioButton input[disabled] { 65 | background: none; 66 | border: 1px solid transparent; 67 | cursor: not-allowed; 68 | } 69 | 70 | .annotationLayer .textWidgetAnnotation input:hover, 71 | .annotationLayer .textWidgetAnnotation textarea:hover, 72 | .annotationLayer .choiceWidgetAnnotation select:hover, 73 | .annotationLayer .buttonWidgetAnnotation.checkBox input:hover, 74 | .annotationLayer .buttonWidgetAnnotation.radioButton input:hover { 75 | border: 1px solid #000; 76 | } 77 | 78 | .annotationLayer .textWidgetAnnotation input:focus, 79 | .annotationLayer .textWidgetAnnotation textarea:focus, 80 | .annotationLayer .choiceWidgetAnnotation select:focus { 81 | background: none; 82 | border: 1px solid transparent; 83 | } 84 | 85 | .annotationLayer .textWidgetAnnotation input.comb { 86 | font-family: monospace; 87 | padding-left: 2px; 88 | padding-right: 0; 89 | } 90 | 91 | .annotationLayer .textWidgetAnnotation input.comb:focus { 92 | /* 93 | * Letter spacing is placed on the right side of each character. Hence, the 94 | * letter spacing of the last character may be placed outside the visible 95 | * area, causing horizontal scrolling. We avoid this by extending the width 96 | * when the element has focus and revert this when it loses focus. 97 | */ 98 | width: 115%; 99 | } 100 | 101 | .annotationLayer .buttonWidgetAnnotation.checkBox input, 102 | .annotationLayer .buttonWidgetAnnotation.radioButton input { 103 | -webkit-appearance: none; 104 | -moz-appearance: none; 105 | -ms-appearance: none; 106 | appearance: none; 107 | } 108 | 109 | .annotationLayer .popupWrapper { 110 | position: absolute; 111 | width: 20em; 112 | } 113 | 114 | .annotationLayer .popup { 115 | position: absolute; 116 | z-index: 200; 117 | max-width: 20em; 118 | background-color: #FFFF99; 119 | box-shadow: 0px 2px 5px #333; 120 | border-radius: 2px; 121 | padding: 0.6em; 122 | margin-left: 5px; 123 | cursor: pointer; 124 | word-wrap: break-word; 125 | } 126 | 127 | .annotationLayer .popup h1 { 128 | font-size: 1em; 129 | border-bottom: 1px solid #000000; 130 | padding-bottom: 0.2em; 131 | } 132 | 133 | .annotationLayer .popup p { 134 | padding-top: 0.2em; 135 | } 136 | 137 | .annotationLayer .highlightAnnotation, 138 | .annotationLayer .underlineAnnotation, 139 | .annotationLayer .squigglyAnnotation, 140 | .annotationLayer .strikeoutAnnotation, 141 | .annotationLayer .lineAnnotation svg line, 142 | .annotationLayer .fileAttachmentAnnotation { 143 | cursor: pointer; 144 | } 145 | -------------------------------------------------------------------------------- /src/buffer-loader.js: -------------------------------------------------------------------------------- 1 | var loaderUtils = require('loader-utils'); 2 | 3 | module.exports = function(content) { 4 | 5 | var options = loaderUtils.getOptions(this); 6 | 7 | var data; 8 | if ( content instanceof Buffer ) 9 | data = content; 10 | else 11 | data = Buffer.from(content); 12 | 13 | return 'export default Buffer.from("'+data.toString('base64')+'", "base64")'; 14 | } 15 | 16 | module.exports.raw = true; 17 | -------------------------------------------------------------------------------- /src/componentFactory.js: -------------------------------------------------------------------------------- 1 | import resizeSensor from 'vue-resize-sensor' 2 | 3 | export default function(pdfjsWrapper) { 4 | 5 | var createLoadingTask = pdfjsWrapper.createLoadingTask; 6 | var PDFJSWrapper = pdfjsWrapper.PDFJSWrapper; 7 | 8 | return { 9 | createLoadingTask: createLoadingTask, 10 | render: function(h) { 11 | return h('span', { 12 | attrs: { 13 | style: 'position: relative; display: block' 14 | } 15 | }, [ 16 | h('canvas', { 17 | attrs: { 18 | style: 'display: inline-block; width: 100%; height: 100%; vertical-align: top', 19 | }, 20 | ref:'canvas' 21 | }), 22 | h('span', { 23 | style: 'display: inline-block; width: 100%; height: 100%', 24 | class: 'annotationLayer', 25 | ref:'annotationLayer' 26 | }), 27 | h(resizeSensor, { 28 | props: { 29 | initial: true 30 | }, 31 | on: { 32 | resize: this.resize 33 | }, 34 | }) 35 | ]) 36 | }, 37 | props: { 38 | src: { 39 | type: [String, Object, Uint8Array], 40 | default: '', 41 | }, 42 | page: { 43 | type: Number, 44 | default: 1, 45 | }, 46 | rotate: { 47 | type: Number, 48 | }, 49 | }, 50 | watch: { 51 | src: function() { 52 | 53 | this.pdf.loadDocument(this.src); 54 | }, 55 | page: function() { 56 | 57 | this.pdf.loadPage(this.page, this.rotate); 58 | }, 59 | rotate: function() { 60 | this.pdf.renderPage(this.rotate); 61 | }, 62 | }, 63 | methods: { 64 | resize: function(size) { 65 | 66 | // check if the element is attached to the dom tree || resizeSensor being destroyed 67 | if ( this.$el.parentNode === null || (size.width === 0 && size.height === 0) ) 68 | return; 69 | 70 | // on IE10- canvas height must be set 71 | this.$refs.canvas.style.height = this.$refs.canvas.offsetWidth * (this.$refs.canvas.height / this.$refs.canvas.width) + 'px'; 72 | // update the page when the resolution is too poor 73 | var resolutionScale = this.pdf.getResolutionScale(); 74 | 75 | if ( resolutionScale < 0.85 || resolutionScale > 1.15 ) 76 | this.pdf.renderPage(this.rotate); 77 | 78 | // this.$refs.annotationLayer.style.transform = 'scale('+resolutionScale+')'; 79 | }, 80 | print: function(dpi, pageList) { 81 | 82 | this.pdf.printPage(dpi, pageList); 83 | } 84 | }, 85 | 86 | // doc: mounted hook is not called during server-side rendering. 87 | mounted: function() { 88 | 89 | this.pdf = new PDFJSWrapper(this.$refs.canvas, this.$refs.annotationLayer, this.$emit.bind(this)); 90 | 91 | this.$on('loaded', function() { 92 | 93 | this.pdf.loadPage(this.page, this.rotate); 94 | }); 95 | 96 | this.$on('page-size', function(width, height) { 97 | 98 | this.$refs.canvas.style.height = this.$refs.canvas.offsetWidth * (height / width) + 'px'; 99 | }); 100 | 101 | this.pdf.loadDocument(this.src); 102 | }, 103 | 104 | // doc: destroyed hook is not called during server-side rendering. 105 | destroyed: function() { 106 | 107 | this.pdf.destroy(); 108 | } 109 | } 110 | 111 | } 112 | -------------------------------------------------------------------------------- /src/pdfjsWrapper.js: -------------------------------------------------------------------------------- 1 | import { PDFLinkService } from 'pdfjs-dist/es5/web/pdf_viewer'; 2 | 3 | var pendingOperation = Promise.resolve(); 4 | 5 | export default function(PDFJS) { 6 | 7 | function isPDFDocumentLoadingTask(obj) { 8 | 9 | return typeof(obj) === 'object' && obj !== null && obj.__PDFDocumentLoadingTask === true; 10 | // or: return obj.constructor.name === 'PDFDocumentLoadingTask'; 11 | } 12 | 13 | function createLoadingTask(src, options) { 14 | 15 | var source; 16 | if ( typeof(src) === 'string' ) 17 | source = { url: src }; 18 | else if ( src instanceof Uint8Array ) 19 | source = { data: src }; 20 | else if ( typeof(src) === 'object' && src !== null ) 21 | source = Object.assign({}, src); 22 | else 23 | throw new TypeError('invalid src type'); 24 | 25 | // source.verbosity = PDFJS.VerbosityLevel.INFOS; 26 | // source.pdfBug = true; 27 | // source.stopAtErrors = true; 28 | 29 | if ( options && options.withCredentials ) 30 | source.withCredentials = options.withCredentials; 31 | 32 | var loadingTask = PDFJS.getDocument(source); 33 | loadingTask.__PDFDocumentLoadingTask = true; // since PDFDocumentLoadingTask is not public 34 | 35 | if ( options && options.onPassword ) 36 | loadingTask.onPassword = options.onPassword; 37 | 38 | if ( options && options.onProgress ) 39 | loadingTask.onProgress = options.onProgress; 40 | 41 | return loadingTask; 42 | } 43 | 44 | 45 | function PDFJSWrapper(canvasElt, annotationLayerElt, emitEvent) { 46 | 47 | var pdfDoc = null; 48 | var pdfPage = null; 49 | var pdfRender = null; 50 | var canceling = false; 51 | 52 | canvasElt.getContext('2d').save(); 53 | 54 | function clearCanvas() { 55 | 56 | canvasElt.getContext('2d').clearRect(0, 0, canvasElt.width, canvasElt.height); 57 | } 58 | 59 | function clearAnnotations() { 60 | 61 | while ( annotationLayerElt.firstChild ) 62 | annotationLayerElt.removeChild(annotationLayerElt.firstChild); 63 | } 64 | 65 | this.destroy = function() { 66 | 67 | if ( pdfDoc === null ) 68 | return; 69 | 70 | // Aborts all network requests and destroys worker. 71 | pendingOperation = pdfDoc.destroy(); 72 | pdfDoc = null; 73 | } 74 | 75 | this.getResolutionScale = function() { 76 | 77 | return canvasElt.offsetWidth / canvasElt.width; 78 | } 79 | 80 | this.printPage = function(dpi, pageNumberOnly) { 81 | 82 | if ( pdfPage === null ) 83 | return; 84 | 85 | // 1in == 72pt 86 | // 1in == 96px 87 | var PRINT_RESOLUTION = dpi === undefined ? 150 : dpi; 88 | var PRINT_UNITS = PRINT_RESOLUTION / 72.0; 89 | var CSS_UNITS = 96.0 / 72.0; 90 | 91 | var iframeElt = document.createElement('iframe'); 92 | 93 | function removeIframe() { 94 | 95 | iframeElt.parentNode.removeChild(iframeElt); 96 | } 97 | 98 | new Promise(function(resolve, reject) { 99 | 100 | iframeElt.frameBorder = '0'; 101 | iframeElt.scrolling = 'no'; 102 | iframeElt.width = '0px;' 103 | iframeElt.height = '0px;' 104 | iframeElt.style.cssText = 'position: absolute; top: 0; left: 0'; 105 | 106 | iframeElt.onload = function() { 107 | 108 | resolve(this.contentWindow); 109 | } 110 | 111 | window.document.body.appendChild(iframeElt); 112 | }) 113 | .then(function(win) { 114 | 115 | win.document.title = ''; 116 | 117 | return pdfDoc.getPage(1) 118 | .then(function(page) { 119 | 120 | var viewport = page.getViewport({ scale: 1 }); 121 | win.document.head.appendChild(win.document.createElement('style')).textContent = 122 | '@supports ((size:A4) and (size:1pt 1pt)) {' + 123 | '@page { margin: 1pt; size: ' + ((viewport.width * PRINT_UNITS) / CSS_UNITS) + 'pt ' + ((viewport.height * PRINT_UNITS) / CSS_UNITS) + 'pt; }' + 124 | '}' + 125 | 126 | '@media print {' + 127 | 'body { margin: 0 }' + 128 | 'canvas { page-break-before: avoid; page-break-after: always; page-break-inside: avoid }' + 129 | '}'+ 130 | 131 | '@media screen {' + 132 | 'body { margin: 0 }' + 133 | '}'+ 134 | 135 | '' 136 | return win; 137 | }) 138 | }) 139 | .then(function(win) { 140 | 141 | var allPages = []; 142 | 143 | for ( var pageNumber = 1; pageNumber <= pdfDoc.numPages; ++pageNumber ) { 144 | 145 | if ( pageNumberOnly !== undefined && pageNumberOnly.indexOf(pageNumber) === -1 ) 146 | continue; 147 | 148 | allPages.push( 149 | pdfDoc.getPage(pageNumber) 150 | .then(function(page) { 151 | 152 | var viewport = page.getViewport({ scale: 1 }); 153 | 154 | var printCanvasElt = win.document.body.appendChild(win.document.createElement('canvas')); 155 | printCanvasElt.width = (viewport.width * PRINT_UNITS); 156 | printCanvasElt.height = (viewport.height * PRINT_UNITS); 157 | 158 | return page.render({ 159 | canvasContext: printCanvasElt.getContext('2d'), 160 | transform: [ // Additional transform, applied just before viewport transform. 161 | PRINT_UNITS, 0, 0, 162 | PRINT_UNITS, 0, 0 163 | ], 164 | viewport: viewport, 165 | intent: 'print' 166 | }).promise; 167 | }) 168 | ); 169 | } 170 | 171 | Promise.all(allPages) 172 | .then(function() { 173 | 174 | win.focus(); // Required for IE 175 | if (win.document.queryCommandSupported('print')) { 176 | win.document.execCommand('print', false, null); 177 | } else { 178 | win.print(); 179 | } 180 | removeIframe(); 181 | }) 182 | .catch(function(err) { 183 | 184 | removeIframe(); 185 | emitEvent('error', err); 186 | }) 187 | }) 188 | } 189 | 190 | this.renderPage = function(rotate) { 191 | if ( pdfRender !== null ) { 192 | 193 | if ( canceling ) 194 | return; 195 | canceling = true; 196 | pdfRender.cancel().catch(function(err) { 197 | emitEvent('error', err); 198 | }); 199 | return; 200 | } 201 | 202 | if ( pdfPage === null ) 203 | return; 204 | 205 | var pageRotate = (pdfPage.rotate === undefined ? 0 : pdfPage.rotate) + (rotate === undefined ? 0 : rotate); 206 | 207 | var scale = canvasElt.offsetWidth / pdfPage.getViewport({ scale: 1 }).width * (window.devicePixelRatio || 1); 208 | var viewport = pdfPage.getViewport({ scale: scale, rotation:pageRotate }); 209 | 210 | emitEvent('page-size', viewport.width, viewport.height, scale); 211 | 212 | canvasElt.width = viewport.width; 213 | canvasElt.height = viewport.height; 214 | 215 | pdfRender = pdfPage.render({ 216 | canvasContext: canvasElt.getContext('2d'), 217 | viewport: viewport 218 | }); 219 | 220 | annotationLayerElt.style.visibility = 'hidden'; 221 | clearAnnotations(); 222 | 223 | var viewer = { 224 | scrollPageIntoView: function(params) { 225 | emitEvent('link-clicked', params.pageNumber) 226 | }, 227 | }; 228 | 229 | var linkService = new PDFLinkService(); 230 | linkService.setDocument(pdfDoc); 231 | linkService.setViewer(viewer); 232 | 233 | pendingOperation = pendingOperation.then(function() { 234 | 235 | var getAnnotationsOperation = 236 | pdfPage.getAnnotations({ intent: 'display' }) 237 | .then(function(annotations) { 238 | 239 | PDFJS.AnnotationLayer.render({ 240 | viewport: viewport.clone({ dontFlip: true }), 241 | div: annotationLayerElt, 242 | annotations: annotations, 243 | page: pdfPage, 244 | linkService: linkService, 245 | renderInteractiveForms: false 246 | }); 247 | }); 248 | 249 | var pdfRenderOperation = 250 | pdfRender.promise 251 | .then(function() { 252 | 253 | annotationLayerElt.style.visibility = ''; 254 | canceling = false; 255 | pdfRender = null; 256 | }) 257 | .catch(function(err) { 258 | 259 | pdfRender = null; 260 | if ( err instanceof PDFJS.RenderingCancelledException ) { 261 | 262 | canceling = false; 263 | this.renderPage(rotate); 264 | return; 265 | } 266 | emitEvent('error', err); 267 | }.bind(this)) 268 | 269 | return Promise.all([getAnnotationsOperation, pdfRenderOperation]); 270 | }.bind(this)); 271 | } 272 | 273 | 274 | this.forEachPage = function(pageCallback) { 275 | 276 | var numPages = pdfDoc.numPages; 277 | 278 | (function next(pageNum) { 279 | 280 | pdfDoc.getPage(pageNum) 281 | .then(pageCallback) 282 | .then(function() { 283 | 284 | if ( ++pageNum <= numPages ) 285 | next(pageNum); 286 | }) 287 | })(1); 288 | } 289 | 290 | 291 | this.loadPage = function(pageNumber, rotate) { 292 | 293 | pdfPage = null; 294 | 295 | if ( pdfDoc === null ) 296 | return; 297 | 298 | pendingOperation = pendingOperation.then(function() { 299 | 300 | return pdfDoc.getPage(pageNumber); 301 | }) 302 | .then(function(page) { 303 | 304 | pdfPage = page; 305 | this.renderPage(rotate); 306 | emitEvent('page-loaded', page.pageNumber); 307 | }.bind(this)) 308 | .catch(function(err) { 309 | 310 | clearCanvas(); 311 | clearAnnotations(); 312 | emitEvent('error', err); 313 | }); 314 | } 315 | 316 | this.loadDocument = function(src) { 317 | 318 | pdfDoc = null; 319 | pdfPage = null; 320 | 321 | emitEvent('num-pages', undefined); 322 | 323 | if ( !src ) { 324 | 325 | canvasElt.removeAttribute('width'); 326 | canvasElt.removeAttribute('height'); 327 | clearAnnotations(); 328 | return; 329 | } 330 | 331 | // wait for pending operation ends 332 | pendingOperation = pendingOperation.then(function() { 333 | 334 | var loadingTask; 335 | if ( isPDFDocumentLoadingTask(src) ) { 336 | 337 | if ( src.destroyed ) { 338 | 339 | emitEvent('error', new Error('loadingTask has been destroyed')); 340 | return 341 | } 342 | 343 | loadingTask = src; 344 | } else { 345 | 346 | loadingTask = createLoadingTask(src, { 347 | onPassword: function(updatePassword, reason) { 348 | 349 | var reasonStr; 350 | switch (reason) { 351 | case PDFJS.PasswordResponses.NEED_PASSWORD: 352 | reasonStr = 'NEED_PASSWORD'; 353 | break; 354 | case PDFJS.PasswordResponses.INCORRECT_PASSWORD: 355 | reasonStr = 'INCORRECT_PASSWORD'; 356 | break; 357 | } 358 | emitEvent('password', updatePassword, reasonStr); 359 | }, 360 | onProgress: function(status) { 361 | 362 | var ratio = status.loaded / status.total; 363 | emitEvent('progress', Math.min(ratio, 1)); 364 | } 365 | }); 366 | } 367 | 368 | return loadingTask.promise; 369 | }) 370 | .then(function(pdf) { 371 | 372 | pdfDoc = pdf; 373 | emitEvent('num-pages', pdf.numPages); 374 | emitEvent('loaded'); 375 | }) 376 | .catch(function(err) { 377 | 378 | clearCanvas(); 379 | clearAnnotations(); 380 | emitEvent('error', err); 381 | }) 382 | } 383 | 384 | annotationLayerElt.style.transformOrigin = '0 0'; 385 | } 386 | 387 | return { 388 | createLoadingTask: createLoadingTask, 389 | PDFJSWrapper: PDFJSWrapper, 390 | } 391 | } 392 | -------------------------------------------------------------------------------- /src/vuePdfNoSss.vue: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/vuePdfNoSssNoWorker.vue: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/vuePdfSss.vue: -------------------------------------------------------------------------------- 1 | 2 | --------------------------------------------------------------------------------