├── .gitignore ├── README.md ├── example ├── .gitignore ├── README.md ├── babel.config.js ├── package-lock.json ├── package.json ├── public │ ├── favicon.ico │ ├── index.html │ └── static │ │ ├── output.pdf │ │ └── test.pdf ├── src │ ├── App.vue │ ├── README.md │ └── main.js └── yarn.lock ├── package.json └── src ├── PDFJSAnnotate.js ├── UI ├── arrow.js ├── circle.js ├── edit.js ├── eraser.js ├── event.js ├── index.js ├── page.js ├── pen.js ├── point.js ├── rect.js ├── text.js └── utils.js ├── a11y ├── createScreenReaderOnly.js ├── initEventHandlers.js ├── insertElementWithinChildren.js ├── insertElementWithinElement.js ├── insertScreenReaderComment.js ├── insertScreenReaderHint.js ├── renderScreenReaderComments.js └── renderScreenReaderHints.js ├── adapter ├── LocalStoreAdapter.js ├── LocalUserStoreAdapter.js └── StoreAdapter.js ├── config.js ├── css ├── cmaps │ ├── 78-EUC-H.bcmap │ ├── 78-EUC-V.bcmap │ ├── 78-H.bcmap │ ├── 78-RKSJ-H.bcmap │ ├── 78-RKSJ-V.bcmap │ ├── 78-V.bcmap │ ├── 78ms-RKSJ-H.bcmap │ ├── 78ms-RKSJ-V.bcmap │ ├── 83pv-RKSJ-H.bcmap │ ├── 90ms-RKSJ-H.bcmap │ ├── 90ms-RKSJ-V.bcmap │ ├── 90msp-RKSJ-H.bcmap │ ├── 90msp-RKSJ-V.bcmap │ ├── 90pv-RKSJ-H.bcmap │ ├── 90pv-RKSJ-V.bcmap │ ├── Add-H.bcmap │ ├── Add-RKSJ-H.bcmap │ ├── Add-RKSJ-V.bcmap │ ├── Add-V.bcmap │ ├── Adobe-CNS1-0.bcmap │ ├── Adobe-CNS1-1.bcmap │ ├── Adobe-CNS1-2.bcmap │ ├── Adobe-CNS1-3.bcmap │ ├── Adobe-CNS1-4.bcmap │ ├── Adobe-CNS1-5.bcmap │ ├── Adobe-CNS1-6.bcmap │ ├── Adobe-CNS1-UCS2.bcmap │ ├── Adobe-GB1-0.bcmap │ ├── Adobe-GB1-1.bcmap │ ├── Adobe-GB1-2.bcmap │ ├── Adobe-GB1-3.bcmap │ ├── Adobe-GB1-4.bcmap │ ├── Adobe-GB1-5.bcmap │ ├── Adobe-GB1-UCS2.bcmap │ ├── Adobe-Japan1-0.bcmap │ ├── Adobe-Japan1-1.bcmap │ ├── Adobe-Japan1-2.bcmap │ ├── Adobe-Japan1-3.bcmap │ ├── Adobe-Japan1-4.bcmap │ ├── Adobe-Japan1-5.bcmap │ ├── Adobe-Japan1-6.bcmap │ ├── Adobe-Japan1-UCS2.bcmap │ ├── Adobe-Korea1-0.bcmap │ ├── Adobe-Korea1-1.bcmap │ ├── Adobe-Korea1-2.bcmap │ ├── Adobe-Korea1-UCS2.bcmap │ ├── B5-H.bcmap │ ├── B5-V.bcmap │ ├── B5pc-H.bcmap │ ├── B5pc-V.bcmap │ ├── CNS-EUC-H.bcmap │ ├── CNS-EUC-V.bcmap │ ├── CNS1-H.bcmap │ ├── CNS1-V.bcmap │ ├── CNS2-H.bcmap │ ├── CNS2-V.bcmap │ ├── ETHK-B5-H.bcmap │ ├── ETHK-B5-V.bcmap │ ├── ETen-B5-H.bcmap │ ├── ETen-B5-V.bcmap │ ├── ETenms-B5-H.bcmap │ ├── ETenms-B5-V.bcmap │ ├── EUC-H.bcmap │ ├── EUC-V.bcmap │ ├── Ext-H.bcmap │ ├── Ext-RKSJ-H.bcmap │ ├── Ext-RKSJ-V.bcmap │ ├── Ext-V.bcmap │ ├── GB-EUC-H.bcmap │ ├── GB-EUC-V.bcmap │ ├── GB-H.bcmap │ ├── GB-V.bcmap │ ├── GBK-EUC-H.bcmap │ ├── GBK-EUC-V.bcmap │ ├── GBK2K-H.bcmap │ ├── GBK2K-V.bcmap │ ├── GBKp-EUC-H.bcmap │ ├── GBKp-EUC-V.bcmap │ ├── GBT-EUC-H.bcmap │ ├── GBT-EUC-V.bcmap │ ├── GBT-H.bcmap │ ├── GBT-V.bcmap │ ├── GBTpc-EUC-H.bcmap │ ├── GBTpc-EUC-V.bcmap │ ├── GBpc-EUC-H.bcmap │ ├── GBpc-EUC-V.bcmap │ ├── H.bcmap │ ├── HKdla-B5-H.bcmap │ ├── HKdla-B5-V.bcmap │ ├── HKdlb-B5-H.bcmap │ ├── HKdlb-B5-V.bcmap │ ├── HKgccs-B5-H.bcmap │ ├── HKgccs-B5-V.bcmap │ ├── HKm314-B5-H.bcmap │ ├── HKm314-B5-V.bcmap │ ├── HKm471-B5-H.bcmap │ ├── HKm471-B5-V.bcmap │ ├── HKscs-B5-H.bcmap │ ├── HKscs-B5-V.bcmap │ ├── Hankaku.bcmap │ ├── Hiragana.bcmap │ ├── KSC-EUC-H.bcmap │ ├── KSC-EUC-V.bcmap │ ├── KSC-H.bcmap │ ├── KSC-Johab-H.bcmap │ ├── KSC-Johab-V.bcmap │ ├── KSC-V.bcmap │ ├── KSCms-UHC-H.bcmap │ ├── KSCms-UHC-HW-H.bcmap │ ├── KSCms-UHC-HW-V.bcmap │ ├── KSCms-UHC-V.bcmap │ ├── KSCpc-EUC-H.bcmap │ ├── KSCpc-EUC-V.bcmap │ ├── Katakana.bcmap │ ├── LICENSE │ ├── NWP-H.bcmap │ ├── NWP-V.bcmap │ ├── RKSJ-H.bcmap │ ├── RKSJ-V.bcmap │ ├── Roman.bcmap │ ├── UniCNS-UCS2-H.bcmap │ ├── UniCNS-UCS2-V.bcmap │ ├── UniCNS-UTF16-H.bcmap │ ├── UniCNS-UTF16-V.bcmap │ ├── UniCNS-UTF32-H.bcmap │ ├── UniCNS-UTF32-V.bcmap │ ├── UniCNS-UTF8-H.bcmap │ ├── UniCNS-UTF8-V.bcmap │ ├── UniGB-UCS2-H.bcmap │ ├── UniGB-UCS2-V.bcmap │ ├── UniGB-UTF16-H.bcmap │ ├── UniGB-UTF16-V.bcmap │ ├── UniGB-UTF32-H.bcmap │ ├── UniGB-UTF32-V.bcmap │ ├── UniGB-UTF8-H.bcmap │ ├── UniGB-UTF8-V.bcmap │ ├── UniJIS-UCS2-H.bcmap │ ├── UniJIS-UCS2-HW-H.bcmap │ ├── UniJIS-UCS2-HW-V.bcmap │ ├── UniJIS-UCS2-V.bcmap │ ├── UniJIS-UTF16-H.bcmap │ ├── UniJIS-UTF16-V.bcmap │ ├── UniJIS-UTF32-H.bcmap │ ├── UniJIS-UTF32-V.bcmap │ ├── UniJIS-UTF8-H.bcmap │ ├── UniJIS-UTF8-V.bcmap │ ├── UniJIS2004-UTF16-H.bcmap │ ├── UniJIS2004-UTF16-V.bcmap │ ├── UniJIS2004-UTF32-H.bcmap │ ├── UniJIS2004-UTF32-V.bcmap │ ├── UniJIS2004-UTF8-H.bcmap │ ├── UniJIS2004-UTF8-V.bcmap │ ├── UniJISPro-UCS2-HW-V.bcmap │ ├── UniJISPro-UCS2-V.bcmap │ ├── UniJISPro-UTF8-V.bcmap │ ├── UniJISX0213-UTF32-H.bcmap │ ├── UniJISX0213-UTF32-V.bcmap │ ├── UniJISX02132004-UTF32-H.bcmap │ ├── UniJISX02132004-UTF32-V.bcmap │ ├── UniKS-UCS2-H.bcmap │ ├── UniKS-UCS2-V.bcmap │ ├── UniKS-UTF16-H.bcmap │ ├── UniKS-UTF16-V.bcmap │ ├── UniKS-UTF32-H.bcmap │ ├── UniKS-UTF32-V.bcmap │ ├── UniKS-UTF8-H.bcmap │ ├── UniKS-UTF8-V.bcmap │ ├── V.bcmap │ └── WP-Symbol.bcmap ├── images │ ├── annotation-check.svg │ ├── annotation-comment.svg │ ├── annotation-help.svg │ ├── annotation-insert.svg │ ├── annotation-key.svg │ ├── annotation-newparagraph.svg │ ├── annotation-noicon.svg │ ├── annotation-note.svg │ ├── annotation-paragraph.svg │ ├── findbarButton-next-rtl.png │ ├── findbarButton-next-rtl@2x.png │ ├── findbarButton-next.png │ ├── findbarButton-next@2x.png │ ├── findbarButton-previous-rtl.png │ ├── findbarButton-previous-rtl@2x.png │ ├── findbarButton-previous.png │ ├── findbarButton-previous@2x.png │ ├── grab.cur │ ├── grabbing.cur │ ├── loading-icon.gif │ ├── loading-small.png │ ├── loading-small@2x.png │ ├── secondaryToolbarButton-documentProperties.png │ ├── secondaryToolbarButton-documentProperties@2x.png │ ├── secondaryToolbarButton-firstPage.png │ ├── secondaryToolbarButton-firstPage@2x.png │ ├── secondaryToolbarButton-handTool.png │ ├── secondaryToolbarButton-handTool@2x.png │ ├── secondaryToolbarButton-lastPage.png │ ├── secondaryToolbarButton-lastPage@2x.png │ ├── secondaryToolbarButton-rotateCcw.png │ ├── secondaryToolbarButton-rotateCcw@2x.png │ ├── secondaryToolbarButton-rotateCw.png │ ├── secondaryToolbarButton-rotateCw@2x.png │ ├── shadow.png │ ├── texture.png │ ├── toolbarButton-bookmark.png │ ├── toolbarButton-bookmark@2x.png │ ├── toolbarButton-download.png │ ├── toolbarButton-download@2x.png │ ├── toolbarButton-menuArrows.png │ ├── toolbarButton-menuArrows@2x.png │ ├── toolbarButton-openFile.png │ ├── toolbarButton-openFile@2x.png │ ├── toolbarButton-pageDown-rtl.png │ ├── toolbarButton-pageDown-rtl@2x.png │ ├── toolbarButton-pageDown.png │ ├── toolbarButton-pageDown@2x.png │ ├── toolbarButton-pageUp-rtl.png │ ├── toolbarButton-pageUp-rtl@2x.png │ ├── toolbarButton-pageUp.png │ ├── toolbarButton-pageUp@2x.png │ ├── toolbarButton-presentationMode.png │ ├── toolbarButton-presentationMode@2x.png │ ├── toolbarButton-print.png │ ├── toolbarButton-print@2x.png │ ├── toolbarButton-search.png │ ├── toolbarButton-search@2x.png │ ├── toolbarButton-secondaryToolbarToggle-rtl.png │ ├── toolbarButton-secondaryToolbarToggle-rtl@2x.png │ ├── toolbarButton-secondaryToolbarToggle.png │ ├── toolbarButton-secondaryToolbarToggle@2x.png │ ├── toolbarButton-sidebarToggle-rtl.png │ ├── toolbarButton-sidebarToggle-rtl@2x.png │ ├── toolbarButton-sidebarToggle.png │ ├── toolbarButton-sidebarToggle@2x.png │ ├── toolbarButton-viewAttachments.png │ ├── toolbarButton-viewAttachments@2x.png │ ├── toolbarButton-viewOutline-rtl.png │ ├── toolbarButton-viewOutline-rtl@2x.png │ ├── toolbarButton-viewOutline.png │ ├── toolbarButton-viewOutline@2x.png │ ├── toolbarButton-viewThumbnail.png │ ├── toolbarButton-viewThumbnail@2x.png │ ├── toolbarButton-zoomIn.png │ ├── toolbarButton-zoomIn@2x.png │ ├── toolbarButton-zoomOut.png │ └── toolbarButton-zoomOut@2x.png ├── pdf_viewer.css └── toolbar.css ├── initColorPicker.js ├── pdfAnnotate.vue ├── render ├── appendChild.js ├── index.js ├── renderArrow.js ├── renderCircle.js ├── renderLine.js ├── renderPath.js ├── renderPoint.js ├── renderRect.js └── renderText.js └── utils ├── abstractFunction.js ├── mathUtils.js ├── normalizeColor.js ├── setAttributes.js └── uuid.js /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | # local env files 6 | .env.local 7 | .env.*.local 8 | 9 | # Log files 10 | npm-debug.log* 11 | yarn-debug.log* 12 | yarn-error.log* 13 | 14 | # Editor directories and files 15 | .idea 16 | .vscode 17 | *.suo 18 | *.ntvs* 19 | *.njsproj 20 | *.sln 21 | *.sw? 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## pdf-annotate-vue 2 | 3 | > Annotation layer for pdf.js in vue 4 | 5 | 6 | ## Install 7 | 8 | ``` 9 | npm install pdf-annotate-vue -S 10 | or 11 | yarn add pdf-annotate-vue -S 12 | ``` 13 | 14 | ## Example 15 | 16 | ```vue 17 | 23 | 24 | 44 | 45 | ``` 46 | 47 | 48 | ## run example 49 | 50 | ```bash 51 | # Clone Project 52 | git clone https://github.com/AaronLeong/pdf-annotate-vue.git 53 | 54 | cd pdf-annotate-vue/example 55 | # Project setup --> install node_modules 56 | yarn install 57 | # Compiles and hot-reloads for development 58 | yarn run serve 59 | 60 | ``` -------------------------------------------------------------------------------- /example/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | # local env files 6 | .env.local 7 | .env.*.local 8 | 9 | # Log files 10 | npm-debug.log* 11 | yarn-debug.log* 12 | yarn-error.log* 13 | 14 | # Editor directories and files 15 | .idea 16 | .vscode 17 | *.suo 18 | *.ntvs* 19 | *.njsproj 20 | *.sln 21 | *.sw? 22 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | # pdf-anno-vue 2 | 3 | ## Project setup 4 | ``` 5 | yarn install 6 | ``` 7 | 8 | ### Compiles and hot-reloads for development 9 | ``` 10 | yarn run serve 11 | ``` 12 | 13 | ### Compiles and minifies for production 14 | ``` 15 | yarn run build 16 | ``` 17 | 18 | ### Run your tests 19 | ``` 20 | yarn run test 21 | ``` 22 | 23 | ### Lints and fixes files 24 | ``` 25 | yarn run lint 26 | ``` 27 | 28 | ### Customize configuration 29 | See [Configuration Reference](https://cli.vuejs.org/config/). 30 | -------------------------------------------------------------------------------- /example/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/app' 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pdf-anno-vue", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "serve": "vue-cli-service serve", 7 | "build": "vue-cli-service build", 8 | "lint": "vue-cli-service lint" 9 | }, 10 | "dependencies": { 11 | "core-js": "^2.6.5", 12 | "create-stylesheet": "^0.3.0", 13 | "pdf-annotate-vue": "^1.0.1", 14 | "pdfjs-dist": "^2.0.943", 15 | "vue": "^2.6.10" 16 | }, 17 | "devDependencies": { 18 | "@vue/cli-plugin-babel": "^3.8.0", 19 | "@vue/cli-plugin-eslint": "^3.8.0", 20 | "@vue/cli-service": "^3.8.0", 21 | "babel-eslint": "^10.0.1", 22 | "eslint": "^5.16.0", 23 | "eslint-plugin-vue": "^5.0.0", 24 | "vue-template-compiler": "^2.6.10" 25 | }, 26 | "eslintConfig": { 27 | "root": true, 28 | "env": { 29 | "node": true 30 | }, 31 | "extends": [ 32 | "plugin:vue/essential", 33 | "eslint:recommended" 34 | ], 35 | "rules": {}, 36 | "parserOptions": { 37 | "parser": "babel-eslint" 38 | } 39 | }, 40 | "postcss": { 41 | "plugins": { 42 | "autoprefixer": {} 43 | } 44 | }, 45 | "browserslist": [ 46 | "> 1%", 47 | "last 2 versions" 48 | ], 49 | "description": "## Project setup ``` yarn install ```", 50 | "main": "babel.config.js", 51 | "author": "", 52 | "license": "ISC" 53 | } 54 | -------------------------------------------------------------------------------- /example/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/example/public/favicon.ico -------------------------------------------------------------------------------- /example/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | pdf-anno-vue 9 | 10 | 11 | 14 |
15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /example/public/static/output.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/example/public/static/output.pdf -------------------------------------------------------------------------------- /example/public/static/test.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/example/public/static/test.pdf -------------------------------------------------------------------------------- /example/src/App.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /example/src/README.md: -------------------------------------------------------------------------------- 1 | README.md -------------------------------------------------------------------------------- /example/src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import App from './App.vue' 3 | 4 | Vue.config.productionTip = false 5 | 6 | new Vue({ 7 | render: h => h(App), 8 | }).$mount('#app') 9 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pdf-annotate-vue", 3 | "version": "1.0.2", 4 | "description": "Annotation layer for pdf.js in vue", 5 | "main": "./src/pdfAnnotate.vue", 6 | "homepage": "https://github.com/AaronLeong/pdf-annotate-vue", 7 | "bugs": { 8 | "url": "https://github.com/AaronLeong/pdf-annotate-vue/issues", 9 | "email": "mc2liang@gmail.com" 10 | }, 11 | "repository": "github:AaronLeong/pdf-annotate-vue", 12 | "scripts": { 13 | "test": "echo \"Error: no test specified\" && exit 1" 14 | }, 15 | "keywords": [ 16 | "pdf", 17 | "annotation", 18 | "pdf.js", 19 | "pdfjs", 20 | "vue" 21 | ], 22 | "author": "Liang Shihua", 23 | "license": "MIT", 24 | "dependencies": { 25 | "create-stylesheet": "^0.3.0", 26 | "pdfjs-dist": "^2.0.943" 27 | }, 28 | "devDependencies": { 29 | "@vue/cli-plugin-babel": "^3.8.0", 30 | "@vue/cli-plugin-eslint": "^3.8.0", 31 | "@vue/cli-service": "^3.8.0", 32 | "babel-eslint": "^10.0.1", 33 | "eslint": "^5.16.0", 34 | "eslint-plugin-vue": "^5.0.0", 35 | "vue-template-compiler": "^2.6.10" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/PDFJSAnnotate.js: -------------------------------------------------------------------------------- 1 | import StoreAdapter from './adapter/StoreAdapter'; 2 | import LocalStoreAdapter from './adapter/LocalStoreAdapter'; 3 | import LocalUserStoreAdapter from './adapter/LocalUserStoreAdapter'; 4 | import render from './render'; 5 | import UI from './UI'; 6 | import config from './config'; 7 | import uuid from './utils/uuid'; 8 | import { 9 | findAnnotationAtPoint, 10 | findSVGContainer, 11 | convertToScreenPoint 12 | } from './UI/utils'; 13 | 14 | export default { 15 | findAnnotationAtPoint, 16 | findSVGContainer, 17 | convertToScreenPoint, 18 | 19 | /** 20 | * Abstract class that needs to be defined so PDFJSAnnotate 21 | * knows how to communicate with your server. 22 | */ 23 | StoreAdapter, 24 | 25 | /** 26 | * Implementation of StoreAdapter that stores annotation data to localStorage. 27 | */ 28 | LocalStoreAdapter, 29 | 30 | /** 31 | * Implementation of StoreAdapter that stores annotation data to localStorage particular 32 | * to a specific user 33 | */ 34 | LocalUserStoreAdapter, 35 | 36 | /** 37 | * Abstract instance of StoreAdapter 38 | */ 39 | __storeAdapter: new StoreAdapter(), 40 | 41 | /** 42 | * Getter for the underlying StoreAdapter property 43 | * 44 | * @return {StoreAdapter} 45 | */ 46 | getStoreAdapter() { 47 | return this.__storeAdapter; 48 | }, 49 | 50 | /** 51 | * Setter for the underlying StoreAdapter property 52 | * 53 | * @param {StoreAdapter} adapter The StoreAdapter implementation to be used. 54 | */ 55 | setStoreAdapter(adapter) { 56 | // TODO this throws an error when bundled 57 | // if (!(adapter instanceof StoreAdapter)) { 58 | // throw new Error('adapter must be an instance of StoreAdapter'); 59 | // } 60 | 61 | this.__storeAdapter = adapter; 62 | }, 63 | 64 | /** 65 | * UI is a helper for instrumenting UI interactions for creating, 66 | * editing, and deleting annotations in the browser. 67 | */ 68 | UI, 69 | 70 | /** 71 | * Render the annotations for a page in the PDF Document 72 | * 73 | * @param {SVGElement} svg The SVG element that annotations should be rendered to 74 | * @param {PageViewport} viewport The PDFPage.getViewport data 75 | * @param {Object} data The StoreAdapter.getAnnotations data 76 | * @return {Promise} 77 | */ 78 | render, 79 | 80 | /** 81 | * Convenience method for getting annotation data 82 | * 83 | * @alias StoreAdapter.getAnnotations 84 | * @param {String} documentId The ID of the document 85 | * @param {String} pageNumber The page number 86 | * @return {Promise} 87 | */ 88 | getAnnotations(documentId, pageNumber) { 89 | return this.getStoreAdapter().getAnnotations(...arguments); 90 | }, 91 | 92 | config, 93 | 94 | uuid 95 | }; 96 | -------------------------------------------------------------------------------- /src/UI/arrow.js: -------------------------------------------------------------------------------- 1 | import PDFJSAnnotate from '../PDFJSAnnotate'; 2 | import { appendChild } from '../render/appendChild'; 3 | import { 4 | disableUserSelect, 5 | enableUserSelect, 6 | findSVGAtPoint, 7 | findSVGContainer, 8 | getMetadata, 9 | convertToSvgPoint, 10 | convertToScreenPoint, 11 | findAnnotationAtPoint 12 | } from './utils'; 13 | 14 | let _enabled = false; 15 | let _penSize; 16 | let _penColor; 17 | let path; 18 | let lines; 19 | let originY; 20 | let originX; 21 | 22 | /** 23 | * Handle document.mousedown event 24 | */ 25 | function handleDocumentMousedown(e) { 26 | let target = findAnnotationAtPoint(e.clientX, e.clientY); 27 | if (target === null) { 28 | return; 29 | } 30 | 31 | let type = target.getAttribute('data-pdf-annotate-type'); 32 | if (type !== 'circle' && type !== 'fillcircle' && type !== 'emptycircle') { 33 | return; 34 | } 35 | 36 | let svg = findSVGContainer(target); 37 | let { documentId } = getMetadata(svg); 38 | let annotationId = target.getAttribute('data-pdf-annotate-id'); 39 | 40 | PDFJSAnnotate.getStoreAdapter().getAnnotation(documentId, annotationId).then((annotation) => { 41 | if (annotation) { 42 | path = null; 43 | lines = []; 44 | 45 | let point = convertToScreenPoint([ 46 | annotation.cx, 47 | annotation.cy 48 | ], svg); 49 | 50 | let rect = svg.getBoundingClientRect(); 51 | 52 | originX = point[0] + rect.left; 53 | originY = point[1] + rect.top; 54 | 55 | document.addEventListener('mousemove', handleDocumentMousemove); 56 | document.addEventListener('mouseup', handleDocumentMouseup); 57 | } 58 | }); 59 | } 60 | 61 | /** 62 | * Handle document.mouseup event 63 | * 64 | * @param {Event} e The DOM event to be handled 65 | */ 66 | function handleDocumentMouseup(e) { 67 | let svg; 68 | if (lines.length > 1 && (svg = findSVGAtPoint(e.clientX, e.clientY))) { 69 | let { documentId, pageNumber } = getMetadata(svg); 70 | 71 | PDFJSAnnotate.getStoreAdapter().addAnnotation(documentId, pageNumber, { 72 | type: 'arrow', 73 | width: _penSize, 74 | color: _penColor, 75 | lines 76 | }).then((annotation) => { 77 | if (path) { 78 | svg.removeChild(path); 79 | } 80 | 81 | appendChild(svg, annotation); 82 | }); 83 | } 84 | 85 | document.removeEventListener('mousemove', handleDocumentMousemove); 86 | document.removeEventListener('mouseup', handleDocumentMouseup); 87 | } 88 | 89 | /** 90 | * Handle document.mousemove event 91 | * 92 | * @param {Event} e The DOM event to be handled 93 | */ 94 | function handleDocumentMousemove(e) { 95 | let x = lines.length === 0 ? originX : e.clientX; 96 | let y = lines.length === 0 ? originY : e.clientY; 97 | 98 | savePoint(x, y); 99 | } 100 | 101 | /** 102 | * Handle document.keyup event 103 | * 104 | * @param {Event} e The DOM event to be handled 105 | */ 106 | function handleDocumentKeyup(e) { 107 | // Cancel rect if Esc is pressed 108 | if (e.keyCode === 27) { 109 | lines = null; 110 | path.parentNode.removeChild(path); 111 | document.removeEventListener('mousemove', handleDocumentMousemove); 112 | document.removeEventListener('mouseup', handleDocumentMouseup); 113 | } 114 | } 115 | 116 | /** 117 | * Save a point to the line being drawn. 118 | * 119 | * @param {Number} x The x coordinate of the point 120 | * @param {Number} y The y coordinate of the point 121 | */ 122 | function savePoint(x, y) { 123 | let svg = findSVGAtPoint(x, y); 124 | if (!svg) { 125 | return; 126 | } 127 | 128 | let rect = svg.getBoundingClientRect(); 129 | let point = convertToSvgPoint([ 130 | x - rect.left, 131 | y - rect.top 132 | ], svg); 133 | 134 | if (lines.length < 2) { 135 | lines.push(point); 136 | return; 137 | } 138 | else { 139 | lines[1] = point; // update end point 140 | } 141 | 142 | if (path) { 143 | svg.removeChild(path); 144 | } 145 | 146 | path = appendChild(svg, { 147 | type: 'arrow', 148 | color: _penColor, 149 | width: _penSize, 150 | lines 151 | }); 152 | } 153 | 154 | /** 155 | * Set the attributes of the pen. 156 | * 157 | * @param {Number} penSize The size of the lines drawn by the pen 158 | * @param {String} penColor The color of the lines drawn by the pen 159 | */ 160 | export function setArrow(penSize = 10, penColor = '0000FF') { 161 | _penSize = parseInt(penSize, 10); 162 | _penColor = penColor; 163 | } 164 | 165 | /** 166 | * Enable the pen behavior 167 | */ 168 | export function enableArrow() { 169 | if (_enabled) { return; } 170 | 171 | _enabled = true; 172 | document.addEventListener('mousedown', handleDocumentMousedown); 173 | document.addEventListener('keyup', handleDocumentKeyup); 174 | disableUserSelect(); 175 | } 176 | 177 | /** 178 | * Disable the pen behavior 179 | */ 180 | export function disableArrow() { 181 | if (!_enabled) { return; } 182 | 183 | _enabled = false; 184 | document.removeEventListener('mousedown', handleDocumentMousedown); 185 | document.removeEventListener('keyup', handleDocumentKeyup); 186 | enableUserSelect(); 187 | } 188 | 189 | -------------------------------------------------------------------------------- /src/UI/circle.js: -------------------------------------------------------------------------------- 1 | import PDFJSAnnotate from '../PDFJSAnnotate'; 2 | import { appendChild } from '../render/appendChild'; 3 | import { 4 | findSVGAtPoint, 5 | getMetadata, 6 | convertToSvgPoint 7 | } from './utils'; 8 | 9 | let _enabled = false; 10 | let _type; 11 | let _circleRadius = 10; 12 | let _circleColor = '0000FF'; 13 | 14 | /** 15 | * Set the attributes of the pen. 16 | * 17 | * @param {Number} circleRadius The radius of the circle 18 | * @param {String} circleColor The color of the circle 19 | */ 20 | export function setCircle(circleRadius = 10, circleColor = '0000FF') { 21 | _circleRadius = parseInt(circleRadius, 10); 22 | _circleColor = circleColor; 23 | } 24 | 25 | /** 26 | * Handle document.mouseup event 27 | * 28 | * @param {Event} e The DOM event to handle 29 | */ 30 | function handleDocumentMouseup(e) { 31 | let svg = findSVGAtPoint(e.clientX, e.clientY); 32 | if (!svg) { 33 | return; 34 | } 35 | let rect = svg.getBoundingClientRect(); 36 | saveCircle(svg, _type, { 37 | x: e.clientX - rect.left, 38 | y: e.clientY - rect.top 39 | }, _circleRadius, _circleColor); 40 | } 41 | 42 | /** 43 | * Save a circle annotation 44 | * 45 | * @param {SVGElement} svg 46 | * @param {String} type The type of circle (circle, emptycircle, fillcircle) 47 | * @param {Object} pt The point to use for annotation 48 | * @param {float} radius 49 | * @param {String} color The color of the rects 50 | */ 51 | function saveCircle(svg, type, pt, radius, color) { 52 | // Initialize the annotation 53 | let svg_pt = convertToSvgPoint([ pt.x, pt.y ], svg); 54 | let annotation = { 55 | type, 56 | color, 57 | cx: svg_pt[0], 58 | cy: svg_pt[1], 59 | r: radius 60 | }; 61 | 62 | let { documentId, pageNumber } = getMetadata(svg); 63 | 64 | // Add the annotation 65 | PDFJSAnnotate.getStoreAdapter().addAnnotation(documentId, pageNumber, annotation) 66 | .then((annotation) => { 67 | appendChild(svg, annotation); 68 | }); 69 | } 70 | 71 | /** 72 | * Enable circle behavior 73 | */ 74 | export function enableCircle(type) { 75 | _type = type; 76 | 77 | if (_enabled) { return; } 78 | 79 | _enabled = true; 80 | document.addEventListener('mouseup', handleDocumentMouseup); 81 | } 82 | 83 | /** 84 | * Disable circle behavior 85 | */ 86 | export function disableCircle() { 87 | if (!_enabled) { return; } 88 | 89 | _enabled = false; 90 | document.removeEventListener('mouseup', handleDocumentMouseup); 91 | } 92 | 93 | export function addCircle(type, e) { 94 | let oldType = _type; 95 | _type = type; 96 | handleDocumentMouseup(e); 97 | _type = oldType; 98 | } 99 | -------------------------------------------------------------------------------- /src/UI/eraser.js: -------------------------------------------------------------------------------- 1 | import PDFJSAnnotate from '../PDFJSAnnotate'; 2 | import { 3 | findAnnotationAtPoint, 4 | getMetadata 5 | } from './utils'; 6 | 7 | let _canerase = false; 8 | let previousPoint = null; 9 | 10 | /** 11 | * 12 | * @param {PointerEvent} e DOM event to handle 13 | */ 14 | function handleDocumentDown(e) { 15 | _canerase = true; 16 | previousPoint = [e.clientX, e.clientY]; 17 | } 18 | 19 | /** 20 | * 21 | * @param {PointerEvent} e DOM event to handle 22 | */ 23 | function handleDocumentUp(e) { 24 | _canerase = false; 25 | erase(findAnnotationAtPoint(e.clientX, e.clientY)); 26 | } 27 | 28 | /** 29 | * 30 | * @param {PointerEvent} e DOM event to handle 31 | */ 32 | function handleDocumentMouseMove(e) { 33 | if (!_canerase) { 34 | return; 35 | } 36 | 37 | // This algorithm attempts to get the various points between the last 38 | // PointerEvent and this one 39 | let check = []; 40 | let diffX = Math.abs(previousPoint[0] - e.clientX); 41 | let diffY = Math.abs(previousPoint[1] - e.clientY); 42 | if (diffX >= 1 || diffY >= 1) { 43 | let maxSteps = Math.round(Math.max(diffX, diffY)); 44 | let subStepSize = Math.min(diffX, diffY) / maxSteps; 45 | let smallerTest = diffX < diffY; 46 | let startPoint = [ 47 | Math.min(previousPoint[0], e.clientX), 48 | Math.min(previousPoint[1], e.clientY) 49 | ]; 50 | for (let i = 0; i < maxSteps; i++) { 51 | if (smallerTest) { 52 | check.push([Math.round(startPoint[0] + (subStepSize * i)), Math.round(startPoint[1] + i)]); 53 | } 54 | else { 55 | check.push([Math.round(startPoint[0] + i), Math.round(startPoint[1] + (subStepSize * i))]); 56 | } 57 | } 58 | } 59 | for (let point of check) { 60 | erase(findAnnotationAtPoint(point[0], point[1])); 61 | } 62 | previousPoint = [e.clientX, e.clientY]; 63 | } 64 | 65 | function erase(target) { 66 | if (!_canerase) { 67 | return; 68 | } 69 | 70 | if (target) { 71 | let { documentId } = getMetadata(target.parentElement); 72 | let annotationId = target.getAttribute('data-pdf-annotate-id'); 73 | PDFJSAnnotate.getStoreAdapter().deleteAnnotation(documentId, annotationId).then(() => { 74 | let nodes = document.querySelectorAll(`[data-pdf-annotate-id="${annotationId}"]`); 75 | [...nodes].forEach((n) => { 76 | n.parentNode.removeChild(n); 77 | }); 78 | }); 79 | } 80 | } 81 | 82 | export function enableEraser() { 83 | document.addEventListener('pointermove', handleDocumentMouseMove); 84 | document.addEventListener('pointerdown', handleDocumentDown); 85 | document.addEventListener('pointerup', handleDocumentUp); 86 | } 87 | 88 | export function disableEraser() { 89 | document.removeEventListener('pointermove', handleDocumentMouseMove); 90 | document.removeEventListener('pointerdown', handleDocumentDown); 91 | document.removeEventListener('pointerup', handleDocumentUp); 92 | } 93 | -------------------------------------------------------------------------------- /src/UI/event.js: -------------------------------------------------------------------------------- 1 | import EventEmitter from 'events'; 2 | import { 3 | findAnnotationAtPoint, 4 | findSVGAtPoint 5 | } from './utils'; 6 | 7 | const emitter = new EventEmitter(); 8 | 9 | let clickNode; 10 | 11 | /** 12 | * Handle document.click event 13 | * 14 | * @param {Event} e The DOM event to be handled 15 | */ 16 | document.addEventListener('click', function handleDocumentClick(e) { 17 | if (!findSVGAtPoint(e.clientX, e.clientY)) { 18 | return; 19 | } 20 | 21 | let target = findAnnotationAtPoint(e.clientX, e.clientY); 22 | 23 | // Emit annotation:blur if clickNode is no longer clicked 24 | if (clickNode && clickNode !== target) { 25 | emitter.emit('annotation:blur', clickNode); 26 | } 27 | 28 | // Emit annotation:click if target was clicked 29 | if (target) { 30 | emitter.emit('annotation:click', target); 31 | } 32 | 33 | clickNode = target; 34 | }); 35 | 36 | // let mouseOverNode; 37 | // document.addEventListener('mousemove', function handleDocumentMousemove(e) { 38 | // let target = findAnnotationAtPoint(e.clientX, e.clientY); 39 | // 40 | // // Emit annotation:mouseout if target was mouseout'd 41 | // if (mouseOverNode && !target) { 42 | // emitter.emit('annotation:mouseout', mouseOverNode); 43 | // } 44 | // 45 | // // Emit annotation:mouseover if target was mouseover'd 46 | // if (target && mouseOverNode !== target) { 47 | // emitter.emit('annotation:mouseover', target); 48 | // } 49 | // 50 | // mouseOverNode = target; 51 | // }); 52 | 53 | export function fireEvent() { emitter.emit(...arguments); }; 54 | export function addEventListener() { emitter.on(...arguments); }; 55 | export function removeEventListener() { emitter.removeListener(...arguments); }; 56 | -------------------------------------------------------------------------------- /src/UI/index.js: -------------------------------------------------------------------------------- 1 | import { addEventListener, removeEventListener, fireEvent } from './event'; 2 | import { disableEdit, enableEdit } from './edit'; 3 | import { disablePen, enablePen, setPen } from './pen'; 4 | import { disableArrow, enableArrow, setArrow } from './arrow'; 5 | import { disableEraser, enableEraser } from './eraser'; 6 | import { disablePoint, enablePoint } from './point'; 7 | import { disableRect, enableRect } from './rect'; 8 | import { disableCircle, enableCircle, setCircle, addCircle } from './circle'; 9 | import { disableText, enableText, setText } from './text'; 10 | import { createPage, renderPage } from './page'; 11 | 12 | export default { 13 | addEventListener, 14 | removeEventListener, 15 | fireEvent, 16 | 17 | disableEdit, 18 | enableEdit, 19 | 20 | disablePen, 21 | enablePen, 22 | setPen, 23 | 24 | disablePoint, 25 | enablePoint, 26 | 27 | disableRect, 28 | enableRect, 29 | 30 | disableCircle, 31 | enableCircle, 32 | setCircle, 33 | addCircle, 34 | 35 | disableArrow, 36 | enableArrow, 37 | setArrow, 38 | 39 | disableEraser, 40 | enableEraser, 41 | 42 | disableText, 43 | enableText, 44 | setText, 45 | 46 | createPage, 47 | renderPage 48 | }; 49 | -------------------------------------------------------------------------------- /src/UI/page.js: -------------------------------------------------------------------------------- 1 | import PDFJSAnnotate from '../PDFJSAnnotate'; 2 | import config from '../config'; 3 | import renderScreenReaderHints from '../a11y/renderScreenReaderHints'; 4 | import { DefaultTextLayerFactory } from 'pdfjs-dist/web/pdf_viewer.js'; 5 | 6 | // Template for creating a new page 7 | const PAGE_TEMPLATE = ` 8 | 15 | `; 16 | 17 | /** 18 | * Create a new page to be appended to the DOM. 19 | * 20 | * @param {Number} pageNumber The page number that is being created 21 | * @return {HTMLElement} 22 | */ 23 | export function createPage(pageNumber) { 24 | let temp = document.createElement('div'); 25 | temp.innerHTML = PAGE_TEMPLATE; 26 | 27 | let page = temp.children[0]; 28 | let canvas = page.querySelector('canvas'); 29 | 30 | page.setAttribute('id', `pageContainer${pageNumber}`); 31 | page.setAttribute('data-page-number', pageNumber); 32 | 33 | canvas.mozOpaque = true; 34 | canvas.setAttribute('id', `page${pageNumber}`); 35 | 36 | return page; 37 | } 38 | 39 | /** 40 | * Render a page that has already been created. 41 | * 42 | * @param {Number} pageNumber The page number to be rendered 43 | * @param {Object} renderOptions The options for rendering 44 | * @return {Promise} Settled once rendering has completed 45 | * A settled Promise will be either: 46 | * - fulfilled: [pdfPage, annotations] 47 | * - rejected: Error 48 | */ 49 | export function renderPage(pageNumber, renderOptions) { 50 | let { 51 | documentId, 52 | pdfDocument, 53 | scale, 54 | rotate 55 | } = renderOptions; 56 | 57 | // Load the page and annotations 58 | return Promise.all([ 59 | pdfDocument.getPage(pageNumber), 60 | PDFJSAnnotate.getAnnotations(documentId, pageNumber) 61 | ]).then(([pdfPage, annotations]) => { 62 | let page = document.getElementById(`pageContainer${pageNumber}`); 63 | let svg = page.querySelector(config.annotationClassQuery()); 64 | let canvas = page.querySelector('.canvasWrapper canvas'); 65 | let canvasContext = canvas.getContext('2d', {alpha: false}); 66 | let totalRotation = (rotate + pdfPage.rotate) % 360; 67 | let viewport = pdfPage.getViewport(scale, totalRotation); 68 | let transform = scalePage(pageNumber, viewport, canvasContext); 69 | 70 | // Render the page 71 | return Promise.all([ 72 | pdfPage.render({ canvasContext, viewport, transform }), 73 | PDFJSAnnotate.render(svg, viewport, annotations) 74 | ]).then(() => { 75 | // Text content is needed for a11y, but is also necessary for creating 76 | // highlight and strikeout annotations which require selecting text. 77 | return pdfPage.getTextContent({normalizeWhitespace: true}).then((textContent) => { 78 | return new Promise((resolve, reject) => { 79 | // Render text layer for a11y of text content 80 | let textLayer = page.querySelector(config.textClassQuery()); 81 | let textLayerFactory = new DefaultTextLayerFactory(); 82 | let textLayerBuilder = textLayerFactory.createTextLayerBuilder(textLayer, pageNumber - 1, viewport); 83 | textLayerBuilder.setTextContent(textContent); 84 | textLayerBuilder.render(); 85 | 86 | // Enable a11y for annotations 87 | // Timeout is needed to wait for `textLayerBuilder.render` 88 | setTimeout(() => { 89 | try { 90 | renderScreenReaderHints(annotations.annotations); 91 | resolve(); 92 | } 93 | catch (e) { 94 | reject(e); 95 | } 96 | }); 97 | }); 98 | }); 99 | }).then(() => { 100 | // Indicate that the page was loaded 101 | page.setAttribute('data-loaded', 'true'); 102 | 103 | return [pdfPage, annotations]; 104 | }); 105 | }); 106 | } 107 | 108 | /** 109 | * Scale the elements of a page. 110 | * 111 | * @param {Number} pageNumber The page number to be scaled 112 | * @param {Object} viewport The viewport of the PDF page (see pdfPage.getViewport(scale, rotate)) 113 | * @param {Object} context The canvas context that the PDF page is rendered to 114 | * @return {Array} The transform data for rendering the PDF page 115 | */ 116 | function scalePage(pageNumber, viewport, context) { 117 | let page = document.getElementById(`pageContainer${pageNumber}`); 118 | let canvas = page.querySelector('.canvasWrapper canvas'); 119 | let svg = page.querySelector(config.annotationClassQuery()); 120 | let wrapper = page.querySelector('.canvasWrapper'); 121 | let textLayer = page.querySelector(config.textClassQuery()); 122 | let outputScale = getOutputScale(context); 123 | let transform = !outputScale.scaled ? null : [outputScale.sx, 0, 0, outputScale.sy, 0, 0]; 124 | let sfx = approximateFraction(outputScale.sx); 125 | let sfy = approximateFraction(outputScale.sy); 126 | 127 | // Adjust width/height for scale 128 | page.style.visibility = ''; 129 | canvas.width = roundToDivide(viewport.width * outputScale.sx, sfx[0]); 130 | canvas.height = roundToDivide(viewport.height * outputScale.sy, sfy[0]); 131 | canvas.style.width = roundToDivide(viewport.width, sfx[1]) + 'px'; 132 | canvas.style.height = roundToDivide(viewport.height, sfx[1]) + 'px'; 133 | svg.setAttribute('width', viewport.width); 134 | svg.setAttribute('height', viewport.height); 135 | svg.style.width = `${viewport.width}px`; 136 | svg.style.height = `${viewport.height}px`; 137 | page.style.width = `${viewport.width}px`; 138 | page.style.height = `${viewport.height}px`; 139 | wrapper.style.width = `${viewport.width}px`; 140 | wrapper.style.height = `${viewport.height}px`; 141 | textLayer.style.width = `${viewport.width}px`; 142 | textLayer.style.height = `${viewport.height}px`; 143 | 144 | return transform; 145 | } 146 | 147 | /** 148 | * Approximates a float number as a fraction using Farey sequence (max order of 8). 149 | * 150 | * @param {Number} x Positive float number 151 | * @return {Array} Estimated fraction: the first array item is a numerator, 152 | * the second one is a denominator. 153 | */ 154 | function approximateFraction(x) { 155 | // Fast path for int numbers or their inversions. 156 | if (Math.floor(x) === x) { 157 | return [x, 1]; 158 | } 159 | 160 | const xinv = 1 / x; 161 | const limit = 8; 162 | if (xinv > limit) { 163 | return [1, limit]; 164 | } 165 | else if (Math.floor(xinv) === xinv) { 166 | return [1, xinv]; 167 | } 168 | 169 | const x_ = x > 1 ? xinv : x; 170 | 171 | // a/b and c/d are neighbours in Farey sequence. 172 | let a = 0; let b = 1; let c = 1; let d = 1; 173 | 174 | // Limit search to order 8. 175 | while (true) { 176 | // Generating next term in sequence (order of q). 177 | let p = a + c; let q = b + d; 178 | if (q > limit) { 179 | break; 180 | } 181 | if (x_ <= p / q) { 182 | c = p; d = q; 183 | } 184 | else { 185 | a = p; b = q; 186 | } 187 | } 188 | 189 | // Select closest of neighbours to x. 190 | if (x_ - a / b < c / d - x_) { 191 | return x_ === x ? [a, b] : [b, a]; 192 | } 193 | else { 194 | return x_ === x ? [c, d] : [d, c]; 195 | } 196 | } 197 | 198 | function getOutputScale(ctx) { 199 | let devicePixelRatio = window.devicePixelRatio || 1; 200 | let backingStoreRatio = ctx.webkitBackingStorePixelRatio || 201 | ctx.mozBackingStorePixelRatio || 202 | ctx.msBackingStorePixelRatio || 203 | ctx.oBackingStorePixelRatio || 204 | ctx.backingStorePixelRatio || 1; 205 | let pixelRatio = devicePixelRatio / backingStoreRatio; 206 | return { 207 | sx: pixelRatio, 208 | sy: pixelRatio, 209 | scaled: pixelRatio !== 1 210 | }; 211 | } 212 | 213 | function roundToDivide(x, div) { 214 | let r = x % div; 215 | return r === 0 ? x : Math.round(x - r + div); 216 | } 217 | -------------------------------------------------------------------------------- /src/UI/pen.js: -------------------------------------------------------------------------------- 1 | import PDFJSAnnotate from '../PDFJSAnnotate'; 2 | import { appendChild } from '../render/appendChild'; 3 | import { 4 | disableUserSelect, 5 | enableUserSelect, 6 | findSVGAtPoint, 7 | getMetadata, 8 | convertToSvgPoint 9 | } from './utils'; 10 | 11 | let _enabled = false; 12 | let _candraw = false; 13 | let _penSize; 14 | let _penColor; 15 | let path; 16 | let lines = []; 17 | 18 | /** 19 | * Handle document.touchdown or document.pointerdown event 20 | * @param {PointerEvent} e The DOM event to be handled 21 | */ 22 | function handleDocumentPointerdown(e) { 23 | e.preventDefault(); 24 | path = null; 25 | lines = []; 26 | _candraw = true; 27 | } 28 | 29 | /** 30 | * Handle document.pointerup event 31 | * 32 | * @param {PointerEvent} e The DOM event to be handled 33 | */ 34 | function handleDocumentPointerup(e) { 35 | saveToStorage(e.clientX, e.clientY); 36 | } 37 | 38 | function saveToStorage(x, y) { 39 | _candraw = false; 40 | let svg; 41 | if (lines.length > 1 && (svg = findSVGAtPoint(x, y))) { 42 | let { documentId, pageNumber } = getMetadata(svg); 43 | PDFJSAnnotate.getStoreAdapter().addAnnotation(documentId, pageNumber, { 44 | type: 'drawing', 45 | width: _penSize, 46 | color: _penColor, 47 | lines 48 | }).then((annotation) => { 49 | if (path) { 50 | svg.removeChild(path); 51 | } 52 | 53 | appendChild(svg, annotation); 54 | }); 55 | } 56 | } 57 | 58 | /** 59 | * Handle document.mousemove event 60 | * 61 | * @param {PointerEvent} e The DOM event to be handled 62 | */ 63 | function handleDocumentPointermove(e) { 64 | if (!e.srcElement.classList.contains('annotationLayer')) { 65 | return; 66 | } 67 | if (_candraw) { 68 | savePoint(e.clientX, e.clientY); 69 | } 70 | } 71 | 72 | /** 73 | * Handle document.keyup event 74 | * 75 | * @param {KeyboardEvent} e The DOM event to be handled 76 | * } e The DOM event to be handled 77 | */ 78 | function handleDocumentKeyup(e) { 79 | // Cancel rect if Esc is pressed 80 | if (e.keyCode === 27) { 81 | lines = null; 82 | path.parentNode.removeChild(path); 83 | document.removeEventListener('pointermove', handleDocumentPointermove); 84 | document.removeEventListener('pointerup', handleDocumentPointerup); 85 | } 86 | } 87 | 88 | /** 89 | * Save a point to the line being drawn. 90 | * 91 | * @param {Number} x The x coordinate of the point 92 | * @param {Number} y The y coordinate of the point 93 | */ 94 | function savePoint(x, y) { 95 | let svg = findSVGAtPoint(x, y); 96 | if (!svg) { 97 | return; 98 | } 99 | 100 | let rect = svg.getBoundingClientRect(); 101 | let point = convertToSvgPoint([ 102 | x - rect.left, 103 | y - rect.top 104 | ], svg); 105 | point[0] = point[0].toFixed(2); 106 | point[1] = point[1].toFixed(2); 107 | lines.push(point); 108 | 109 | if (lines.length <= 1) { 110 | return; 111 | } 112 | 113 | if (path) { 114 | svg.removeChild(path); 115 | } 116 | 117 | path = appendChild(svg, { 118 | type: 'drawing', 119 | color: _penColor, 120 | width: _penSize, 121 | lines 122 | }); 123 | } 124 | 125 | /** 126 | * Set the attributes of the pen. 127 | * 128 | * @param {Number} penSize The size of the lines drawn by the pen 129 | * @param {String} penColor The color of the lines drawn by the pen 130 | */ 131 | export function setPen(penSize = 1, penColor = '000000') { 132 | _penSize = parseInt(penSize, 10); 133 | _penColor = penColor; 134 | } 135 | 136 | /** 137 | * Enable the pen behavior 138 | */ 139 | export function enablePen() { 140 | if (_enabled) { 141 | return; 142 | } 143 | 144 | _enabled = true; 145 | // Chrome and Firefox has different behaviors with how pen works, so we need different events. 146 | document.addEventListener('pointerdown', handleDocumentPointerdown); 147 | document.addEventListener('pointermove', handleDocumentPointermove); 148 | document.addEventListener('pointerup', handleDocumentPointerup); 149 | 150 | document.addEventListener('keyup', handleDocumentKeyup); 151 | disableUserSelect(); 152 | } 153 | 154 | /** 155 | * Disable the pen behavior 156 | */ 157 | export function disablePen() { 158 | if (!_enabled) { 159 | return; 160 | } 161 | 162 | _enabled = false; 163 | document.removeEventListener('pointerdown', handleDocumentPointerdown); 164 | document.removeEventListener('pointermove', handleDocumentPointermove); 165 | document.removeEventListener('pointerup', handleDocumentPointerup); 166 | 167 | document.removeEventListener('keyup', handleDocumentKeyup); 168 | enableUserSelect(); 169 | } 170 | 171 | -------------------------------------------------------------------------------- /src/UI/point.js: -------------------------------------------------------------------------------- 1 | import PDFJSAnnotate from '../PDFJSAnnotate'; 2 | import { appendChild } from '../render/appendChild'; 3 | import { 4 | BORDER_COLOR, 5 | findSVGAtPoint, 6 | getMetadata, 7 | scaleDown 8 | } from './utils'; 9 | 10 | let _enabled = false; 11 | let input; 12 | 13 | /** 14 | * Handle document.mouseup event 15 | * 16 | * @param {Event} The DOM event to be handled 17 | */ 18 | function handleDocumentMouseup(e) { 19 | if (input || !findSVGAtPoint(e.clientX, e.clientY)) { 20 | return; 21 | } 22 | 23 | input = document.createElement('input'); 24 | input.setAttribute('id', 'pdf-annotate-point-input'); 25 | input.setAttribute('placeholder', 'Enter comment'); 26 | input.style.border = `3px solid ${BORDER_COLOR}`; 27 | input.style.borderRadius = '3px'; 28 | input.style.position = 'absolute'; 29 | input.style.top = `${e.clientY}px`; 30 | input.style.left = `${e.clientX}px`; 31 | 32 | input.addEventListener('blur', handleInputBlur); 33 | input.addEventListener('keyup', handleInputKeyup); 34 | 35 | document.body.appendChild(input); 36 | input.focus(); 37 | } 38 | 39 | /** 40 | * Handle input.blur event 41 | */ 42 | function handleInputBlur() { 43 | savePoint(); 44 | } 45 | 46 | /** 47 | * Handle input.keyup event 48 | * 49 | * @param {Event} e The DOM event to handle 50 | */ 51 | function handleInputKeyup(e) { 52 | if (e.keyCode === 27) { 53 | closeInput(); 54 | } 55 | else if (e.keyCode === 13) { 56 | savePoint(); 57 | } 58 | } 59 | 60 | /** 61 | * Save a new point annotation from input 62 | */ 63 | function savePoint() { 64 | if (input.value.trim().length > 0) { 65 | let clientX = parseInt(input.style.left, 10); 66 | let clientY = parseInt(input.style.top, 10); 67 | let content = input.value.trim(); 68 | let svg = findSVGAtPoint(clientX, clientY); 69 | if (!svg) { 70 | return; 71 | } 72 | 73 | let rect = svg.getBoundingClientRect(); 74 | let { documentId, pageNumber } = getMetadata(svg); 75 | let annotation = Object.assign({ 76 | type: 'point' 77 | }, scaleDown(svg, { 78 | x: clientX - rect.left, 79 | y: clientY - rect.top 80 | })); 81 | 82 | PDFJSAnnotate.getStoreAdapter().addAnnotation(documentId, pageNumber, annotation) 83 | .then((annotation) => { 84 | PDFJSAnnotate.getStoreAdapter().addComment( 85 | documentId, 86 | annotation.uuid, 87 | content 88 | ); 89 | 90 | appendChild(svg, annotation); 91 | }); 92 | } 93 | 94 | closeInput(); 95 | } 96 | 97 | /** 98 | * Close the input element 99 | */ 100 | function closeInput() { 101 | input.removeEventListener('blur', handleInputBlur); 102 | input.removeEventListener('keyup', handleInputKeyup); 103 | document.body.removeChild(input); 104 | input = null; 105 | } 106 | 107 | /** 108 | * Enable point annotation behavior 109 | */ 110 | export function enablePoint() { 111 | if (_enabled) { return; } 112 | 113 | _enabled = true; 114 | document.addEventListener('mouseup', handleDocumentMouseup); 115 | } 116 | 117 | /** 118 | * Disable point annotation behavior 119 | */ 120 | export function disablePoint() { 121 | if (!_enabled) { return; } 122 | 123 | _enabled = false; 124 | document.removeEventListener('mouseup', handleDocumentMouseup); 125 | } 126 | 127 | -------------------------------------------------------------------------------- /src/UI/rect.js: -------------------------------------------------------------------------------- 1 | import PDFJSAnnotate from '../PDFJSAnnotate'; 2 | import config from '../config'; 3 | import { appendChild } from '../render/appendChild'; 4 | import { 5 | BORDER_COLOR, 6 | disableUserSelect, 7 | enableUserSelect, 8 | findSVGAtPoint, 9 | getMetadata, 10 | convertToSvgRect 11 | } from './utils'; 12 | 13 | let _enabled = false; 14 | let _type; 15 | let overlay; 16 | let originY; 17 | let originX; 18 | 19 | /** 20 | * Get the current window selection as rects 21 | * 22 | * @return {Array} An Array of rects 23 | */ 24 | function getSelectionRects() { 25 | try { 26 | let selection = window.getSelection(); 27 | let range = selection.getRangeAt(0); 28 | let rects = range.getClientRects(); 29 | 30 | if (rects.length > 0 && 31 | rects[0].width > 0 && 32 | rects[0].height > 0) { 33 | return rects; 34 | } 35 | } 36 | catch (e) {} 37 | 38 | return null; 39 | } 40 | 41 | /** 42 | * Handle document.mousedown event 43 | * 44 | * @param {Event} e The DOM event to handle 45 | */ 46 | function handleDocumentMousedown(e) { 47 | let svg; 48 | if (_type !== 'area' || !(svg = findSVGAtPoint(e.clientX, e.clientY))) { 49 | return; 50 | } 51 | 52 | let rect = svg.getBoundingClientRect(); 53 | originY = e.clientY; 54 | originX = e.clientX; 55 | 56 | overlay = document.createElement('div'); 57 | overlay.style.position = 'absolute'; 58 | overlay.style.top = `${originY - rect.top}px`; 59 | overlay.style.left = `${originX - rect.left}px`; 60 | overlay.style.border = `3px solid ${BORDER_COLOR}`; 61 | overlay.style.borderRadius = '3px'; 62 | svg.parentNode.appendChild(overlay); 63 | 64 | document.addEventListener('mousemove', handleDocumentMousemove); 65 | disableUserSelect(); 66 | } 67 | 68 | /** 69 | * Handle document.mousemove event 70 | * 71 | * @param {Event} e The DOM event to handle 72 | */ 73 | function handleDocumentMousemove(e) { 74 | let svg = overlay.parentNode.querySelector(config.annotationSvgQuery()); 75 | let rect = svg.getBoundingClientRect(); 76 | 77 | if (originX + (e.clientX - originX) < rect.right) { 78 | overlay.style.width = `${e.clientX - originX}px`; 79 | } 80 | 81 | if (originY + (e.clientY - originY) < rect.bottom) { 82 | overlay.style.height = `${e.clientY - originY}px`; 83 | } 84 | } 85 | 86 | /** 87 | * Handle document.mouseup event 88 | * 89 | * @param {Event} e The DOM event to handle 90 | */ 91 | function handleDocumentMouseup(e) { 92 | let rects; 93 | if (_type !== 'area' && (rects = getSelectionRects())) { 94 | saveRect(_type, [...rects].map((r) => { 95 | return { 96 | top: r.top, 97 | left: r.left, 98 | width: r.width, 99 | height: r.height 100 | }; 101 | })); 102 | } 103 | else if (_type === 'area' && overlay) { 104 | let svg = overlay.parentNode.querySelector(config.annotationSvgQuery()); 105 | let rect = svg.getBoundingClientRect(); 106 | saveRect(_type, [{ 107 | top: parseInt(overlay.style.top, 10) + rect.top, 108 | left: parseInt(overlay.style.left, 10) + rect.left, 109 | width: parseInt(overlay.style.width, 10), 110 | height: parseInt(overlay.style.height, 10) 111 | }]); 112 | 113 | overlay.parentNode.removeChild(overlay); 114 | overlay = null; 115 | 116 | document.removeEventListener('mousemove', handleDocumentMousemove); 117 | enableUserSelect(); 118 | } 119 | } 120 | 121 | /** 122 | * Handle document.keyup event 123 | * 124 | * @param {Event} e The DOM event to handle 125 | */ 126 | function handleDocumentKeyup(e) { 127 | // Cancel rect if Esc is pressed 128 | if (e.keyCode === 27) { 129 | let selection = window.getSelection(); 130 | selection.removeAllRanges(); 131 | if (overlay && overlay.parentNode) { 132 | overlay.parentNode.removeChild(overlay); 133 | overlay = null; 134 | document.removeEventListener('mousemove', handleDocumentMousemove); 135 | } 136 | } 137 | } 138 | 139 | /** 140 | * Save a rect annotation 141 | * 142 | * @param {String} type The type of rect (area, highlight, strikeout) 143 | * @param {Array} rects The rects to use for annotation 144 | * @param {String} color The color of the rects 145 | */ 146 | function saveRect(type, rects, color) { 147 | let svg = findSVGAtPoint(rects[0].left, rects[0].top); 148 | let annotation; 149 | 150 | if (!svg) { 151 | return; 152 | } 153 | 154 | let boundingRect = svg.getBoundingClientRect(); 155 | 156 | console.log('saveRect', type) 157 | if (!color) { 158 | if (type === 'highlight') { 159 | color = 'FFFF00'; 160 | } 161 | else if (type === 'strikeout') { 162 | color = 'FF0000'; 163 | } 164 | } 165 | 166 | // Initialize the annotation 167 | annotation = { 168 | type, 169 | color, 170 | rectangles: [...rects].map((r) => { 171 | let offset = 0; 172 | 173 | if (type === 'strikeout') { 174 | offset = r.height / 2; 175 | } 176 | 177 | return convertToSvgRect({ 178 | y: (r.top + offset) - boundingRect.top, 179 | x: r.left - boundingRect.left, 180 | width: r.width, 181 | height: r.height 182 | }, svg); 183 | }).filter((r) => r.width > 0 && r.height > 0 && r.x > -1 && r.y > -1) 184 | }; 185 | 186 | // Short circuit if no rectangles exist 187 | if (annotation.rectangles.length === 0) { 188 | return; 189 | } 190 | 191 | // Special treatment for area as it only supports a single rect 192 | if (type === 'area') { 193 | let rect = annotation.rectangles[0]; 194 | delete annotation.rectangles; 195 | annotation.x = rect.x; 196 | annotation.y = rect.y; 197 | annotation.width = rect.width; 198 | annotation.height = rect.height; 199 | } 200 | 201 | let { documentId, pageNumber } = getMetadata(svg); 202 | 203 | // Add the annotation 204 | console.log('annotation',annotation) 205 | PDFJSAnnotate.getStoreAdapter().addAnnotation(documentId, pageNumber, annotation) 206 | .then((annotation) => { 207 | appendChild(svg, annotation); 208 | 209 | }); 210 | 211 | PDFJSAnnotate.getStoreAdapter().getAnnotations('#viewer',1).then(function(data){ 212 | console.log(data) 213 | }) 214 | 215 | } 216 | 217 | /** 218 | * Enable rect behavior 219 | */ 220 | export function enableRect(type) { 221 | _type = type; 222 | 223 | if (_enabled) { return; } 224 | 225 | _enabled = true; 226 | document.addEventListener('mouseup', handleDocumentMouseup); 227 | document.addEventListener('mousedown', handleDocumentMousedown); 228 | document.addEventListener('keyup', handleDocumentKeyup); 229 | } 230 | 231 | /** 232 | * Disable rect behavior 233 | */ 234 | export function disableRect() { 235 | if (!_enabled) { return; } 236 | 237 | _enabled = false; 238 | document.removeEventListener('mouseup', handleDocumentMouseup); 239 | document.removeEventListener('mousedown', handleDocumentMousedown); 240 | document.removeEventListener('keyup', handleDocumentKeyup); 241 | } 242 | 243 | -------------------------------------------------------------------------------- /src/UI/text.js: -------------------------------------------------------------------------------- 1 | import PDFJSAnnotate from '../PDFJSAnnotate'; 2 | import { appendChild } from '../render/appendChild'; 3 | import { 4 | BORDER_COLOR, 5 | findSVGAtPoint, 6 | getMetadata, 7 | convertToSvgPoint 8 | } from './utils'; 9 | 10 | let _enabled = false; 11 | let input; 12 | let _textSize; 13 | let _textColor; 14 | 15 | /** 16 | * Handle document.mouseup event 17 | * 18 | * @param {Event} e The DOM event to handle 19 | */ 20 | function handleDocumentMouseup(e) { 21 | if (input || !findSVGAtPoint(e.clientX, e.clientY)) { 22 | return; 23 | } 24 | if (!e.srcElement.classList.contains('annotationLayer')) { 25 | return; 26 | } 27 | input = document.createElement('input'); 28 | input.setAttribute('id', 'pdf-annotate-text-input'); 29 | input.setAttribute('placeholder', 'Enter text'); 30 | input.style.border = `3px solid ${BORDER_COLOR}`; 31 | input.style.borderRadius = '3px'; 32 | input.style.position = 'absolute'; 33 | input.style.top = `${e.clientY}px`; 34 | input.style.left = `${e.clientX}px`; 35 | input.style.fontSize = `${_textSize}px`; 36 | input.style.zIndex = '41'; 37 | input.addEventListener('blur', handleInputBlur); 38 | input.addEventListener('keyup', handleInputKeyup); 39 | 40 | document.body.appendChild(input); 41 | input.focus(); 42 | } 43 | 44 | /** 45 | * Handle input.blur event 46 | */ 47 | function handleInputBlur() { 48 | saveText(); 49 | } 50 | 51 | /** 52 | * Handle input.keyup event 53 | * 54 | * @param {Event} e The DOM event to handle 55 | */ 56 | function handleInputKeyup(e) { 57 | if (e.keyCode === 27) { 58 | closeInput(); 59 | } 60 | else if (e.keyCode === 13) { 61 | saveText(); 62 | } 63 | } 64 | 65 | /** 66 | * Save a text annotation from input 67 | */ 68 | function saveText() { 69 | let value = (input.value) ? input.value.replace(/ +$/, '') : ''; 70 | if (value.length > 0) { 71 | let clientX = parseInt(input.style.left, 10); 72 | let clientY = parseInt(input.style.top, 10); 73 | let svg = findSVGAtPoint(clientX, clientY); 74 | if (!svg) { 75 | return; 76 | } 77 | let height = _textSize; 78 | let { documentId, pageNumber, viewport } = getMetadata(svg); 79 | let scale = 1 / viewport.scale; 80 | let rect = svg.getBoundingClientRect(); 81 | let pt = convertToSvgPoint([ 82 | clientX - rect.left, 83 | clientY - rect.top + height], svg, viewport); 84 | let annotation = { 85 | type: 'textbox', 86 | size: _textSize * scale, 87 | color: _textColor, 88 | content: value, 89 | x: pt[0], 90 | y: pt[1], 91 | rotation: -viewport.rotation 92 | }; 93 | 94 | PDFJSAnnotate.getStoreAdapter().addAnnotation(documentId, pageNumber, annotation) 95 | .then((annotation) => { 96 | appendChild(svg, annotation); 97 | }); 98 | } 99 | 100 | closeInput(); 101 | } 102 | 103 | /** 104 | * Close the input 105 | */ 106 | function closeInput() { 107 | if (input) { 108 | input.removeEventListener('blur', handleInputBlur); 109 | input.removeEventListener('keyup', handleInputKeyup); 110 | document.body.removeChild(input); 111 | input = null; 112 | } 113 | } 114 | 115 | /** 116 | * Set the text attributes 117 | * 118 | * @param {Number} textSize The size of the text 119 | * @param {String} textColor The color of the text 120 | */ 121 | export function setText(textSize = 12, textColor = '000000') { 122 | _textSize = parseInt(textSize, 10); 123 | _textColor = textColor; 124 | } 125 | 126 | /** 127 | * Enable text behavior 128 | */ 129 | export function enableText() { 130 | if (_enabled) { 131 | return; 132 | } 133 | 134 | _enabled = true; 135 | document.addEventListener('mouseup', handleDocumentMouseup); 136 | } 137 | 138 | /** 139 | * Disable text behavior 140 | */ 141 | export function disableText() { 142 | if (!_enabled) { return; } 143 | 144 | _enabled = false; 145 | document.removeEventListener('mouseup', handleDocumentMouseup); 146 | } 147 | 148 | -------------------------------------------------------------------------------- /src/UI/utils.js: -------------------------------------------------------------------------------- 1 | import createStyleSheet from 'create-stylesheet'; 2 | import { getTranslation } from '../render/appendChild'; 3 | import { 4 | applyTransform, 5 | applyInverseTransform, 6 | translate, 7 | rotate, 8 | scale 9 | } from '../utils/mathUtils'; 10 | 11 | export const BORDER_COLOR = '#00BFFF'; 12 | 13 | const userSelectStyleSheet = createStyleSheet({ 14 | body: { 15 | '-webkit-user-select': 'none', 16 | '-moz-user-select': 'none', 17 | '-ms-user-select': 'none', 18 | 'user-select': 'none' 19 | } 20 | }); 21 | userSelectStyleSheet.setAttribute('data-pdf-annotate-user-select', 'true'); 22 | 23 | /** 24 | * Find the SVGElement that contains all the annotations for a page 25 | * 26 | * @param {Element} node An annotation within that container 27 | * @return {SVGElement} The container SVG or null if it can't be found 28 | */ 29 | export function findSVGContainer(node) { 30 | let parentNode = node; 31 | 32 | while ((parentNode = parentNode.parentNode) && 33 | parentNode !== document) { 34 | if (parentNode.nodeName.toUpperCase() === 'SVG' && 35 | parentNode.getAttribute('data-pdf-annotate-container') === 'true') { 36 | return parentNode; 37 | } 38 | } 39 | 40 | return null; 41 | } 42 | 43 | /** 44 | * Find an SVGElement container at a given point 45 | * 46 | * @param {Number} x The x coordinate of the point 47 | * @param {Number} y The y coordinate of the point 48 | * @return {SVGElement} The container SVG or null if one can't be found 49 | */ 50 | export function findSVGAtPoint(x, y) { 51 | let elements = document.querySelectorAll('svg[data-pdf-annotate-container="true"]'); 52 | 53 | for (let i = 0, l = elements.length; i < l; i++) { 54 | let el = elements[i]; 55 | let rect = el.getBoundingClientRect(); 56 | 57 | if (pointIntersectsRect(x, y, rect)) { 58 | return el; 59 | } 60 | } 61 | 62 | return null; 63 | } 64 | 65 | /** 66 | * Find an Element that represents an annotation at a given point. 67 | * 68 | * IMPORTANT: Requires the annotation layer to be the top most element so 69 | * either use z-ordering or make it the leaf container. 70 | * 71 | * @param {Number} x The x coordinate of the point 72 | * @param {Number} y The y coordinate of the point 73 | * @return {Element} The annotation element or null if one can't be found 74 | */ 75 | export function findAnnotationAtPoint(x, y) { 76 | let el = null; 77 | let candidate = document.elementFromPoint(x, y); 78 | while (!el && candidate && candidate !== document) { 79 | let type = candidate.getAttribute('data-pdf-annotate-type'); 80 | if (type) { 81 | el = candidate; 82 | } 83 | candidate = candidate.parentNode; 84 | } 85 | return el; 86 | } 87 | 88 | /** 89 | * Determine if a point intersects a rect 90 | * 91 | * @param {Number} x The x coordinate of the point 92 | * @param {Number} y The y coordinate of the point 93 | * @param {Object} rect The points of a rect (likely from getBoundingClientRect) 94 | * @return {Boolean} True if a collision occurs, otherwise false 95 | */ 96 | export function pointIntersectsRect(x, y, rect) { 97 | return y >= rect.top && y <= rect.bottom && x >= rect.left && x <= rect.right; 98 | } 99 | 100 | /** 101 | * Get the rect of an annotation element accounting for offset. 102 | * 103 | * @param {Element} el The element to get the rect of 104 | * @return {Object} The dimensions of the element 105 | */ 106 | export function getOffsetAnnotationRect(el) { 107 | let rect = el.getBoundingClientRect(); 108 | let { width, height } = rect; 109 | let extraOffsetWidth = 0; 110 | let extraOffsetHeight = 0; 111 | if (['line', 'path'].indexOf(el.tagName.toLowerCase()) > -1 && el.getBBox) { 112 | let bbox = el.getBBox(); 113 | extraOffsetWidth = (rect.width - bbox.width) / 2; 114 | extraOffsetHeight = (rect.height - bbox.height) / 2; 115 | width = bbox.width; 116 | height = bbox.height; 117 | } 118 | let { offsetLeft, offsetTop } = getOffset(el); 119 | return { 120 | top: rect.top - offsetTop + extraOffsetHeight, 121 | left: rect.left - offsetLeft + extraOffsetWidth, 122 | bottom: rect.bottom - offsetTop - extraOffsetHeight, 123 | right: rect.right - offsetLeft - extraOffsetWidth, 124 | width: width, 125 | height: height 126 | }; 127 | } 128 | 129 | /** 130 | * Adjust scale from normalized scale (100%) to rendered scale. 131 | * 132 | * @param {SVGElement} svg The SVG to gather metadata from 133 | * @param {Object} rect A map of numeric values to scale 134 | * @return {Object} A copy of `rect` with values scaled up 135 | */ 136 | export function scaleUp(svg, rect) { 137 | let result = {}; 138 | let { viewport } = getMetadata(svg); 139 | 140 | Object.keys(rect).forEach((key) => { 141 | result[key] = rect[key] * viewport.scale; 142 | }); 143 | 144 | return result; 145 | } 146 | 147 | export function convertToSvgRect(rect, svg, viewport) { 148 | let pt1 = [rect.x, rect.y]; 149 | let pt2 = [rect.x + rect.width, rect.y + rect.height]; 150 | 151 | pt1 = convertToSvgPoint(pt1, svg, viewport); 152 | pt2 = convertToSvgPoint(pt2, svg, viewport); 153 | 154 | return { 155 | x: Math.min(pt1[0], pt2[0]), 156 | y: Math.min(pt1[1], pt2[1]), 157 | width: Math.abs(pt2[0] - pt1[0]), 158 | height: Math.abs(pt2[1] - pt1[1]) 159 | }; 160 | } 161 | 162 | export function convertToSvgPoint(pt, svg, viewport) { 163 | viewport = viewport || getMetadata(svg).viewport; 164 | 165 | let xform = [ 1, 0, 0, 1, 0, 0 ]; 166 | xform = scale(xform, viewport.scale, viewport.scale); 167 | xform = rotate(xform, viewport.rotation); 168 | 169 | let offset = getTranslation(viewport); 170 | xform = translate(xform, offset.x, offset.y); 171 | 172 | return applyInverseTransform(pt, xform); 173 | } 174 | 175 | export function convertToScreenPoint(pt, svg, viewport) { 176 | viewport = viewport || getMetadata(svg).viewport; 177 | 178 | let xform = [ 1, 0, 0, 1, 0, 0 ]; 179 | xform = scale(xform, viewport.scale, viewport.scale); 180 | xform = rotate(xform, viewport.rotation); 181 | 182 | let offset = getTranslation(viewport); 183 | xform = translate(xform, offset.x, offset.y); 184 | 185 | return applyTransform(pt, xform); 186 | } 187 | 188 | /** 189 | * Adjust scale from rendered scale to a normalized scale (100%). 190 | * 191 | * @param {SVGElement} svg The SVG to gather metadata from 192 | * @param {Object} rect A map of numeric values to scale 193 | * @return {Object} A copy of `rect` with values scaled down 194 | */ 195 | export function scaleDown(svg, rect) { 196 | let result = {}; 197 | let { viewport } = getMetadata(svg); 198 | 199 | Object.keys(rect).forEach((key) => { 200 | result[key] = rect[key] / viewport.scale; 201 | }); 202 | 203 | return result; 204 | } 205 | 206 | /** 207 | * Get the scroll position of an element, accounting for parent elements 208 | * 209 | * @param {Element} el The element to get the scroll position for 210 | * @return {Object} The scrollTop and scrollLeft position 211 | */ 212 | export function getScroll(el) { 213 | let scrollTop = 0; 214 | let scrollLeft = 0; 215 | let parentNode = el; 216 | 217 | while ((parentNode = parentNode.parentNode) && 218 | parentNode !== document) { 219 | scrollTop += parentNode.scrollTop; 220 | scrollLeft += parentNode.scrollLeft; 221 | } 222 | 223 | return { scrollTop, scrollLeft }; 224 | } 225 | 226 | /** 227 | * Get the offset position of an element, accounting for parent elements 228 | * 229 | * @param {Element} el The element to get the offset position for 230 | * @return {Object} The offsetTop and offsetLeft position 231 | */ 232 | export function getOffset(el) { 233 | let parentNode = el; 234 | 235 | while ((parentNode = parentNode.parentNode) && 236 | parentNode !== document) { 237 | if (parentNode.nodeName.toUpperCase() === 'SVG') { 238 | break; 239 | } 240 | } 241 | 242 | let rect = parentNode.getBoundingClientRect(); 243 | 244 | return { offsetLeft: rect.left, offsetTop: rect.top }; 245 | } 246 | 247 | /** 248 | * Disable user ability to select text on page 249 | */ 250 | export function disableUserSelect() { 251 | if (!userSelectStyleSheet.parentNode) { 252 | document.head.appendChild(userSelectStyleSheet); 253 | } 254 | } 255 | 256 | /** 257 | * Enable user ability to select text on page 258 | */ 259 | export function enableUserSelect() { 260 | if (userSelectStyleSheet.parentNode) { 261 | userSelectStyleSheet.parentNode.removeChild(userSelectStyleSheet); 262 | } 263 | } 264 | 265 | /** 266 | * Get the metadata for a SVG container 267 | * 268 | * @param {SVGElement} svg The SVG container to get metadata for 269 | */ 270 | export function getMetadata(svg) { 271 | return { 272 | documentId: svg.getAttribute('data-pdf-annotate-document'), 273 | pageNumber: parseInt(svg.getAttribute('data-pdf-annotate-page'), 10), 274 | viewport: JSON.parse(svg.getAttribute('data-pdf-annotate-viewport')) 275 | }; 276 | } 277 | -------------------------------------------------------------------------------- /src/a11y/createScreenReaderOnly.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Create a node that is only visible to screen readers 3 | * 4 | * @param {String} content The text content that should be read by screen reader 5 | * @param {String} [annotationId] The ID of the annotation assocaited 6 | * @return {Element} An Element that is only visible to screen readers 7 | */ 8 | export default function createScreenReaderOnly(content, annotationId) { 9 | let node = document.createElement('div'); 10 | let text = document.createTextNode(content); 11 | node.appendChild(text); 12 | node.setAttribute('id', `pdf-annotate-screenreader-${annotationId}`); 13 | node.style.position = 'absolute'; 14 | node.style.left = '-10000px'; 15 | node.style.top = 'auto'; 16 | node.style.width = '1px'; 17 | node.style.height = '1px'; 18 | node.style.overflow = 'hidden'; 19 | return node; 20 | } 21 | -------------------------------------------------------------------------------- /src/a11y/initEventHandlers.js: -------------------------------------------------------------------------------- 1 | import renderScreenReaderHints from './renderScreenReaderHints'; 2 | import insertScreenReaderComment from './insertScreenReaderComment'; 3 | import renderScreenReaderComments from './renderScreenReaderComments'; 4 | import { addEventListener } from '../UI/event'; 5 | import PDFJSAnnotate from '../PDFJSAnnotate'; 6 | 7 | /** 8 | * Initialize the event handlers for keeping screen reader hints synced with data 9 | */ 10 | export default function initEventHandlers() { 11 | addEventListener('annotation:add', (documentId, pageNumber, annotation) => { 12 | reorderAnnotationsByType(documentId, pageNumber, annotation.type); 13 | }); 14 | addEventListener('annotation:edit', (documentId, annotationId, annotation) => { 15 | reorderAnnotationsByType(documentId, annotation.page, annotation.type); 16 | }); 17 | addEventListener('annotation:delete', removeAnnotation); 18 | addEventListener('comment:add', insertComment); 19 | addEventListener('comment:delete', removeComment); 20 | } 21 | 22 | /** 23 | * Reorder the annotation numbers by annotation type 24 | * 25 | * @param {String} documentId The ID of the document 26 | * @param {Number} pageNumber The page number of the annotations 27 | * @param {Strig} type The annotation type 28 | */ 29 | function reorderAnnotationsByType(documentId, pageNumber, type) { 30 | PDFJSAnnotate.getStoreAdapter().getAnnotations(documentId, pageNumber) 31 | .then((annotations) => { 32 | return annotations.annotations.filter((a) => { 33 | return a.type === type; 34 | }); 35 | }) 36 | .then((annotations) => { 37 | annotations.forEach((a) => { 38 | removeAnnotation(documentId, a.uuid); 39 | }); 40 | 41 | return annotations; 42 | }) 43 | .then(renderScreenReaderHints); 44 | } 45 | 46 | /** 47 | * Remove the screen reader hint for an annotation 48 | * 49 | * @param {String} documentId The ID of the document 50 | * @param {String} annotationId The Id of the annotation 51 | */ 52 | function removeAnnotation(documentId, annotationId) { 53 | removeElementById(`pdf-annotate-screenreader-${annotationId}`); 54 | removeElementById(`pdf-annotate-screenreader-${annotationId}-end`); 55 | } 56 | 57 | /** 58 | * Insert a screen reader hint for a comment 59 | * 60 | * @param {String} documentId The ID of the document 61 | * @param {String} annotationId The ID of tha assocated annotation 62 | * @param {Object} comment The comment to insert a hint for 63 | */ 64 | function insertComment(documentId, annotationId, comment) { 65 | let list = document.querySelector(`pdf-annotate-screenreader-comment-list-${annotationId}`); 66 | let promise; 67 | 68 | if (!list) { 69 | promise = renderScreenReaderComments(documentId, annotationId, []).then(() => { 70 | list = document.querySelector(`pdf-annotate-screenreader-comment-list-${annotationId}`); 71 | return true; 72 | }); 73 | } 74 | else { 75 | promise = Promise.resolve(true); 76 | } 77 | 78 | promise.then(() => { 79 | insertScreenReaderComment(comment); 80 | }); 81 | } 82 | 83 | /** 84 | * Remove a screen reader hint for a comment 85 | * 86 | * @param {String} documentId The ID of the document 87 | * @param {String} commentId The ID of the comment 88 | */ 89 | function removeComment(documentId, commentId) { 90 | removeElementById(`pdf-annotate-screenreader-comment-${commentId}`); 91 | } 92 | 93 | /** 94 | * Remove an element from the DOM by it's ID if it exists 95 | * 96 | * @param {String} elementId The ID of the element to be removed 97 | */ 98 | function removeElementById(elementId) { 99 | let el = document.getElementById(elementId); 100 | if (el) { 101 | el.parentNode.removeChild(el); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/a11y/insertElementWithinChildren.js: -------------------------------------------------------------------------------- 1 | import config from '../config'; 2 | import insertElementWithinElement from './insertElementWithinElement'; 3 | import { pointIntersectsRect, scaleUp } from '../UI/utils'; 4 | 5 | /** 6 | * Insert an element at a point within the document. 7 | * This algorithm will try to insert between elements if possible. 8 | * It will however use `insertElementWithinElement` if it is more accurate. 9 | * 10 | * @param {Element} el The element to be inserted 11 | * @param {Number} x The x coordinate of the point 12 | * @param {Number} y The y coordinate of the point 13 | * @param {Number} pageNumber The page number to limit elements to 14 | * @return {Boolean} True if element was able to be inserted, otherwise false 15 | */ 16 | export default function insertElementWithinChildren(el, x, y, pageNumber) { 17 | // Try and use most accurate method of inserting within an element 18 | if (insertElementWithinElement(el, x, y, pageNumber, true)) { 19 | return true; 20 | } 21 | 22 | // Fall back to inserting between elements 23 | let svg = document.querySelector(`svg[data-pdf-annotate-page="${pageNumber}"]`); 24 | let rect = svg.getBoundingClientRect(); 25 | let nodes = [...svg.parentNode.querySelectorAll(config.textClassQuery() + ' > div')]; 26 | 27 | y = scaleUp(svg, {y}).y + rect.top; 28 | x = scaleUp(svg, {x}).x + rect.left; 29 | 30 | // Find the best node to insert before 31 | for (let i = 0, l = nodes.length; i < l; i++) { 32 | let n = nodes[i]; 33 | let r = n.getBoundingClientRect(); 34 | if (y <= r.top) { 35 | n.parentNode.insertBefore(el, n); 36 | return true; 37 | } 38 | } 39 | 40 | // If all else fails try to append to the bottom 41 | let textLayer = svg.parentNode.querySelector(config.textClassQuery()); 42 | if (textLayer) { 43 | let textRect = textLayer.getBoundingClientRect(); 44 | if (pointIntersectsRect(x, y, textRect)) { 45 | textLayer.appendChild(el); 46 | return true; 47 | } 48 | } 49 | 50 | return false; 51 | } 52 | -------------------------------------------------------------------------------- /src/a11y/insertElementWithinElement.js: -------------------------------------------------------------------------------- 1 | import config from '../config'; 2 | import { 3 | pointIntersectsRect, 4 | scaleUp, 5 | scaleDown 6 | } from '../UI/utils'; 7 | 8 | /** 9 | * Insert an element at a point within the document. 10 | * This algorithm will only insert within an element amidst it's text content. 11 | * 12 | * @param {Element} el The element to be inserted 13 | * @param {Number} x The x coordinate of the point 14 | * @param {Number} y The y coordinate of the point 15 | * @param {Number} pageNumber The page number to limit elements to 16 | * @param {Boolean} insertBefore Whether the element is to be inserted before or after x 17 | * @return {Boolean} True if element was able to be inserted, otherwise false 18 | */ 19 | export default function insertElementWithinElement(el, x, y, pageNumber, insertBefore) { 20 | const OFFSET_ADJUST = 2; 21 | 22 | // If inserting before adjust `x` by looking for element a few px to the right 23 | // Otherwise adjust a few px to the left 24 | // This is to allow a little tolerance by searching within the box, instead 25 | // of getting a false negative by testing right on the border. 26 | x = Math.max(x + (OFFSET_ADJUST * (insertBefore ? 1 : -1)), 0); 27 | 28 | let node = textLayerElementFromPoint(x, y + OFFSET_ADJUST, pageNumber); 29 | if (!node) { 30 | return false; 31 | } 32 | 33 | // Now that node has been found inverse the adjustment for `x`. 34 | // This is done to accomodate tolerance by cutting off on the outside of the 35 | // text boundary, instead of missing a character by cutting off within. 36 | x = x + (OFFSET_ADJUST * (insertBefore ? -1 : 1)); 37 | 38 | let svg = document.querySelector(`svg[data-pdf-annotate-page="${pageNumber}"]`); 39 | let left = scaleDown(svg, {left: node.getBoundingClientRect().left}).left - svg.getBoundingClientRect().left; 40 | let temp = node.cloneNode(true); 41 | let head = temp.innerHTML.split(''); 42 | let tail = []; 43 | 44 | // Insert temp off screen 45 | temp.style.position = 'absolute'; 46 | temp.style.top = '-10000px'; 47 | temp.style.left = '-10000px'; 48 | document.body.appendChild(temp); 49 | 50 | while (head.length) { 51 | // Don't insert within HTML tags 52 | if (head[head.length - 1] === '>') { 53 | while (head.length) { 54 | tail.unshift(head.pop()); 55 | if (tail[0] === '<') { 56 | break; 57 | } 58 | } 59 | } 60 | 61 | // Check if width of temp based on current head value satisfies x 62 | temp.innerHTML = head.join(''); 63 | let width = scaleDown(svg, {width: temp.getBoundingClientRect().width}).width; 64 | if (left + width <= x) { 65 | break; 66 | } 67 | tail.unshift(head.pop()); 68 | } 69 | 70 | // Update original node with new markup, including element to be inserted 71 | node.innerHTML = head.join('') + el.outerHTML + tail.join(''); 72 | temp.parentNode.removeChild(temp); 73 | 74 | return true; 75 | } 76 | 77 | /** 78 | * Get a text layer element at a given point on a page 79 | * 80 | * @param {Number} x The x coordinate of the point 81 | * @param {Number} y The y coordinate of the point 82 | * @param {Number} pageNumber The page to limit elements to 83 | * @return {Element} First text layer element found at the point 84 | */ 85 | function textLayerElementFromPoint(x, y, pageNumber) { 86 | let svg = document.querySelector(`svg[data-pdf-annotate-page="${pageNumber}"]`); 87 | let rect = svg.getBoundingClientRect(); 88 | y = scaleUp(svg, {y}).y + rect.top; 89 | x = scaleUp(svg, {x}).x + rect.left; 90 | return [...svg.parentNode.querySelectorAll(config.textClassQuery() + ' [data-canvas-width]')].filter((el) => { 91 | return pointIntersectsRect(x, y, el.getBoundingClientRect()); 92 | })[0]; 93 | } 94 | -------------------------------------------------------------------------------- /src/a11y/insertScreenReaderComment.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Insert a comment into the DOM to be available by screen reader 3 | * 4 | * @param {Object} comment The comment to be inserted 5 | */ 6 | export default function insertScreenReaderComment(comment) { 7 | if (!comment) { 8 | return; 9 | } 10 | 11 | let list = document.querySelector(`#pdf-annotate-screenreader-${comment.annotation} ol`); 12 | if (list) { 13 | let item = document.createElement('li'); 14 | item.setAttribute('id', `pdf-annotate-screenreader-comment-${comment.uuid}`); 15 | item.appendChild(document.createTextNode(`${comment.content}`)); 16 | list.appendChild(item); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/a11y/insertScreenReaderHint.js: -------------------------------------------------------------------------------- 1 | import createScreenReaderOnly from './createScreenReaderOnly'; 2 | import insertElementWithinChildren from './insertElementWithinChildren'; 3 | import insertElementWithinElement from './insertElementWithinElement'; 4 | import renderScreenReaderComments from './renderScreenReaderComments'; 5 | 6 | // Annotation types that support comments 7 | const COMMENT_TYPES = ['highlight', 'point', 'area', 'circle', 'emptycircle', 'fillcircle']; 8 | 9 | /** 10 | * Insert a hint into the DOM for screen readers for a specific annotation. 11 | * 12 | * @param {Object} annotation The annotation to insert a hint for 13 | * @param {Number} num The number of the annotation out of all annotations of the same type 14 | */ 15 | export default function insertScreenReaderHint(annotation, num = 0) { 16 | switch (annotation.type) { 17 | case 'highlight': 18 | case 'strikeout': 19 | let rects = annotation.rectangles; 20 | let first = rects[0]; 21 | let last = rects[rects.length - 1]; 22 | 23 | insertElementWithinElement( 24 | createScreenReaderOnly(`Begin ${annotation.type} annotation ${num}`, annotation.uuid), 25 | first.x, first.y, annotation.page, true 26 | ); 27 | 28 | insertElementWithinElement( 29 | createScreenReaderOnly(`End ${annotation.type} annotation ${num}`, `${annotation.uuid}-end`), 30 | last.x + last.width, last.y, annotation.page, false 31 | ); 32 | break; 33 | 34 | case 'textbox': 35 | case 'point': 36 | let text = annotation.type === 'textbox' ? ` (content: ${annotation.content})` : ''; 37 | 38 | insertElementWithinChildren( 39 | createScreenReaderOnly(`${annotation.type} annotation ${num}${text}`, annotation.uuid), 40 | annotation.x, annotation.y, annotation.page 41 | ); 42 | break; 43 | 44 | case 'drawing': 45 | case 'area': 46 | let x = typeof annotation.x !== 'undefined' ? annotation.x : annotation.lines[0][0]; 47 | let y = typeof annotation.y !== 'undefined' ? annotation.y : annotation.lines[0][1]; 48 | 49 | insertElementWithinChildren( 50 | createScreenReaderOnly(`Unlabeled drawing`, annotation.uuid), 51 | x, y, annotation.page 52 | ); 53 | break; 54 | 55 | case 'circle': 56 | case 'fillcircle': 57 | case 'emptycircle': 58 | let x2 = typeof annotation.cx !== 'undefined' ? annotation.cx : annotation.lines[0][0]; 59 | let y2 = typeof annotation.cy !== 'undefined' ? annotation.cy : annotation.lines[0][1]; 60 | 61 | insertElementWithinChildren( 62 | createScreenReaderOnly(`Unlabeled drawing`, annotation.uuid), 63 | x2, y2, annotation.page 64 | ); 65 | break; 66 | } 67 | 68 | // Include comments in screen reader hint 69 | if (COMMENT_TYPES.includes(annotation.type)) { 70 | renderScreenReaderComments(annotation.documentId, annotation.uuid); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/a11y/renderScreenReaderComments.js: -------------------------------------------------------------------------------- 1 | import PDFJSAnnotate from '../PDFJSAnnotate'; 2 | import insertScreenReaderComment from './insertScreenReaderComment'; 3 | 4 | /** 5 | * Insert the comments into the DOM to be available by screen reader 6 | * 7 | * Example output: 8 | *
9 | *
Begin highlight 1
10 | *
    11 | *
  1. Foo
  2. 12 | *
  3. Bar
  4. 13 | *
  5. Baz
  6. 14 | *
  7. Qux
  8. 15 | *
16 | *
17 | *
Some highlighted text goes here...
18 | *
End highlight 1
19 | * 20 | * NOTE: `screenReaderOnly` is not a real class, just used for brevity 21 | * 22 | * @param {String} documentId The ID of the document 23 | * @param {String} annotationId The ID of the annotation 24 | * @param {Array} [comments] Optionally preloaded comments to be rendered 25 | * @return {Promise} Promise that once has comments, render them for screen reader 26 | */ 27 | export default function renderScreenReaderComments(documentId, annotationId, comments) { 28 | let promise; 29 | 30 | if (Array.isArray(comments)) { 31 | promise = Promise.resolve(comments); 32 | } 33 | else { 34 | promise = PDFJSAnnotate.getStoreAdapter().getComments(documentId, annotationId); 35 | } 36 | 37 | return promise.then((comments) => { 38 | // Node needs to be found by querying DOM as it may have been inserted as innerHTML 39 | // leaving `screenReaderNode` as an invalid reference (see `insertElementWithinElement`). 40 | let node = document.getElementById(`pdf-annotate-screenreader-${annotationId}`); 41 | if (node) { 42 | let list = document.createElement('ol'); 43 | list.setAttribute('id', `pdf-annotate-screenreader-comment-list-${annotationId}`); 44 | list.setAttribute('aria-label', 'Comments'); 45 | node.appendChild(list); 46 | comments.forEach(insertScreenReaderComment); 47 | } 48 | }); 49 | } 50 | -------------------------------------------------------------------------------- /src/a11y/renderScreenReaderHints.js: -------------------------------------------------------------------------------- 1 | import insertScreenReaderHint from './insertScreenReaderHint'; 2 | import initEventHandlers from './initEventHandlers'; 3 | 4 | // TODO This is not the right place for this to live 5 | initEventHandlers(); 6 | 7 | /** 8 | * Insert hints into the DOM for screen readers. 9 | * 10 | * @param {Array} annotations The annotations that hints are inserted for 11 | */ 12 | export default function renderScreenReaderHints(annotations) { 13 | annotations = Array.isArray(annotations) ? annotations : []; 14 | 15 | // Insert hints for each type 16 | Object.keys(SORT_TYPES).forEach((type) => { 17 | let sortBy = SORT_TYPES[type]; 18 | annotations 19 | .filter((a) => a.type === type) 20 | .sort(sortBy) 21 | .forEach((a, i) => insertScreenReaderHint(a, i + 1)); 22 | }); 23 | } 24 | 25 | // Sort annotations first by y, then by x. 26 | // This allows hints to be injected in the order they appear, 27 | // which makes numbering them easier. 28 | function sortByPoint(a, b) { 29 | if (a.y < b.y) { 30 | return a.x - b.x; 31 | } 32 | else { 33 | return 1; 34 | } 35 | } 36 | 37 | // Sort annotation by it's first rectangle 38 | function sortByRectPoint(a, b) { 39 | return sortByPoint(a.rectangles[0], b.rectangles[0]); 40 | } 41 | 42 | // Sort annotation by it's first line 43 | function sortByLinePoint(a, b) { 44 | let lineA = a.lines[0]; 45 | let lineB = b.lines[0]; 46 | return sortByPoint( 47 | {x: lineA[0], y: lineA[1]}, 48 | {x: lineB[0], y: lineB[1]} 49 | ); 50 | } 51 | 52 | // Arrange supported types and associated sort methods 53 | const SORT_TYPES = { 54 | 'highlight': sortByRectPoint, 55 | 'strikeout': sortByRectPoint, 56 | 'drawing': sortByLinePoint, 57 | 'textbox': sortByPoint, 58 | 'point': sortByPoint, 59 | 'area': sortByPoint 60 | }; 61 | 62 | -------------------------------------------------------------------------------- /src/adapter/LocalStoreAdapter.js: -------------------------------------------------------------------------------- 1 | import uuid from '../utils/uuid'; 2 | import StoreAdapter from './StoreAdapter'; 3 | 4 | // StoreAdapter for working with localStorage 5 | // This is ideal for testing, examples, and prototyping 6 | export default class LocalStoreAdapter extends StoreAdapter { 7 | constructor() { 8 | super({ 9 | getAnnotations(documentId, pageNumber) { 10 | return new Promise((resolve, reject) => { 11 | let annotations = getAnnotations(documentId).filter((i) => { 12 | return i.page === pageNumber && i.class === 'Annotation'; 13 | }); 14 | 15 | resolve({ 16 | documentId, 17 | pageNumber, 18 | annotations 19 | }); 20 | }); 21 | } 22 | }); 23 | 24 | this.getAnnotation = (documentId, annotationId) => { 25 | return Promise.resolve(getAnnotations(documentId)[findAnnotation(documentId, annotationId)]); 26 | }; 27 | 28 | this.addAnnotation = (documentId, pageNumber, annotation) => { 29 | return new Promise((resolve, reject) => { 30 | annotation.class = 'Annotation'; 31 | annotation.uuid = uuid(); 32 | annotation.page = pageNumber; 33 | 34 | let annotations = getAnnotations(documentId); 35 | annotations.push(annotation); 36 | updateAnnotations(documentId, annotations); 37 | 38 | resolve(annotation); 39 | }); 40 | }; 41 | 42 | this.editAnnotation = (documentId, annotationId, annotation) => { 43 | return new Promise((resolve, reject) => { 44 | let annotations = getAnnotations(documentId); 45 | annotations[findAnnotation(documentId, annotationId)] = annotation; 46 | updateAnnotations(documentId, annotations); 47 | resolve(annotation); 48 | }); 49 | }; 50 | 51 | this.deleteAnnotation = (documentId, annotationId) => { 52 | return new Promise((resolve, reject) => { 53 | let annotation = getAnnotations(documentId).filter(i => i.uuid === annotationId)[0] || {}; 54 | if (!annotation) { 55 | return reject('Could not find annotation'); 56 | } 57 | let index = findAnnotation(documentId, annotationId); 58 | if (index > -1) { 59 | let annotations = getAnnotations(documentId); 60 | annotations.splice(index, 1); 61 | updateAnnotations(documentId, annotations); 62 | } 63 | 64 | resolve(true); 65 | }); 66 | }; 67 | 68 | this.getComments = (documentId, annotationId) => { 69 | return new Promise((resolve, reject) => { 70 | resolve(getAnnotations(documentId).filter((i) => { 71 | return i.class === 'Comment' && i.annotation === annotationId; 72 | })); 73 | }); 74 | }; 75 | 76 | this.addComment = (documentId, annotationId, content) => { 77 | return new Promise((resolve, reject) => { 78 | let comment = { 79 | class: 'Comment', 80 | uuid: uuid(), 81 | annotation: annotationId, 82 | content: content 83 | }; 84 | 85 | let annotations = getAnnotations(documentId); 86 | annotations.push(comment); 87 | updateAnnotations(documentId, annotations); 88 | 89 | resolve(comment); 90 | }); 91 | }; 92 | 93 | this.deleteComment = (documentId, commentId) => { 94 | return new Promise((resolve, reject) => { 95 | let comment = getAnnotations(documentId).filter(i => i.uuid === commentId)[0] || {}; 96 | if (!comment) { 97 | return reject('Could not find annotation'); 98 | } 99 | let index = -1; 100 | let annotations = getAnnotations(documentId); 101 | for (let i = 0, l = annotations.length; i < l; i++) { 102 | if (annotations[i].uuid === commentId) { 103 | index = i; 104 | break; 105 | } 106 | } 107 | 108 | if (index > -1) { 109 | annotations.splice(index, 1); 110 | updateAnnotations(documentId, annotations); 111 | } 112 | 113 | resolve(true); 114 | }); 115 | }; 116 | } 117 | } 118 | 119 | function getAnnotations(documentId) { 120 | return JSON.parse(localStorage.getItem(`${documentId}/annotations`)) || []; 121 | } 122 | 123 | function updateAnnotations(documentId, annotations) { 124 | localStorage.setItem(`${documentId}/annotations`, JSON.stringify(annotations)); 125 | } 126 | /** 127 | * 128 | * @param {String} documentId Document id of the annotation 129 | * @param {String} annotationId The id of the annotation 130 | * 131 | * This function finds all the annotation made by one user. 132 | * 133 | * @return {int} The index of the annotation in localstorage 134 | */ 135 | function findAnnotation(documentId, annotationId) { 136 | let index = -1; 137 | let annotations = getAnnotations(documentId); 138 | for (let i = 0, l = annotations.length; i < l; i++) { 139 | if (annotations[i].uuid === annotationId) { 140 | index = i; 141 | break; 142 | } 143 | } 144 | return index; 145 | } 146 | -------------------------------------------------------------------------------- /src/adapter/LocalUserStoreAdapter.js: -------------------------------------------------------------------------------- 1 | import uuid from '../utils/uuid'; 2 | import StoreAdapter from './StoreAdapter'; 3 | 4 | // StoreAdapter for working with localStorage and associated user id 5 | // This is ideal for testing, examples, and prototyping 6 | export default class LocalUserStoreAdapter extends StoreAdapter { 7 | constructor(userId = 'user', globalEdit = false) { 8 | super({ 9 | getAnnotations(documentId, pageNumber) { 10 | return new Promise((resolve, reject) => { 11 | let annotations = getAllAnnotations(documentId).filter((i) => { 12 | return i.page === pageNumber && i.class === 'Annotation'; 13 | }); 14 | 15 | resolve({ 16 | documentId, 17 | pageNumber, 18 | annotations 19 | }); 20 | }); 21 | } 22 | }); 23 | 24 | this._userId = userId; 25 | this._globalEdit = globalEdit; 26 | 27 | this.getAnnotation = (documentId, annotationId) => { 28 | return Promise.resolve(getAnnotations(documentId, this._userId)[findAnnotation(documentId, this._userId, annotationId)]); 29 | }; 30 | 31 | this.addAnnotation = (documentId, pageNumber, annotation) => { 32 | return new Promise((resolve, reject) => { 33 | annotation.class = 'Annotation'; 34 | annotation.uuid = uuid(); 35 | annotation.page = pageNumber; 36 | annotation.userId = this._userId; 37 | 38 | let annotations = getAnnotations(documentId, this._userId); 39 | annotations.push(annotation); 40 | updateAnnotations(documentId, this._userId, annotations); 41 | 42 | resolve(annotation); 43 | }); 44 | }; 45 | 46 | this.editAnnotation = (documentId, annotationId, annotation) => { 47 | return new Promise((resolve, reject) => { 48 | if (!this._globalEdit && annotation.userId && annotation.userId !== this._userId) { 49 | reject('Non-matching userId'); 50 | } 51 | let annotations = getAnnotations(documentId, annotation.userId); 52 | annotations[findAnnotation(documentId, annotation.userId, annotationId)] = annotation; 53 | updateAnnotations(documentId, annotation.userId, annotations); 54 | resolve(annotation); 55 | }); 56 | }; 57 | 58 | this.deleteAnnotation = (documentId, annotationId) => { 59 | return new Promise((resolve, reject) => { 60 | let annotation = getAllAnnotations(documentId).filter(i => i.uuid === annotationId)[0] || {}; 61 | if (!annotation) { 62 | return reject('Could not find annotation'); 63 | } 64 | else if (!this._globalEdit && annotation.userId && annotation.userId !== this._userId) { 65 | return reject('Non-matching userId'); 66 | } 67 | let index = findAnnotation(documentId, annotation.userId, annotationId); 68 | if (index > -1) { 69 | let annotations = getAnnotations(documentId, annotation.userId); 70 | annotations.splice(index, 1); 71 | updateAnnotations(documentId, annotation.userId, annotations); 72 | } 73 | 74 | resolve(true); 75 | }); 76 | }; 77 | 78 | this.getComments = (documentId, annotationId) => { 79 | return new Promise((resolve, reject) => { 80 | resolve(getAnnotations(documentId, this._userId).filter((i) => { 81 | return i.class === 'Comment' && i.annotation === annotationId; 82 | })); 83 | }); 84 | }; 85 | 86 | this.addComment = (documentId, annotationId, content) => { 87 | return new Promise((resolve, reject) => { 88 | let comment = { 89 | class: 'Comment', 90 | uuid: uuid(), 91 | annotation: annotationId, 92 | content: content, 93 | userId: this._userId 94 | }; 95 | 96 | let annotations = getAnnotations(documentId, this._userId); 97 | annotations.push(comment); 98 | updateAnnotations(documentId, this._userId, annotations); 99 | 100 | resolve(comment); 101 | }); 102 | }; 103 | 104 | this.deleteComment = (documentId, commentId) => { 105 | return new Promise((resolve, reject) => { 106 | let comment = getAllAnnotations(documentId).filter(i => i.uuid === commentId)[0] || {}; 107 | if (!comment) { 108 | return reject('Could not find annotation'); 109 | } 110 | else if (!this._globalEdit && comment.userId && comment.userId !== this._userId) { 111 | return reject('Non-matching userId'); 112 | } 113 | let index = -1; 114 | let annotations = getAnnotations(documentId, comment.userId); 115 | for (let i = 0, l = annotations.length; i < l; i++) { 116 | if (annotations[i].uuid === commentId) { 117 | index = i; 118 | break; 119 | } 120 | } 121 | 122 | if (index > -1) { 123 | annotations.splice(index, 1); 124 | updateAnnotations(documentId, comment.userId, annotations); 125 | } 126 | 127 | resolve(true); 128 | }); 129 | }; 130 | } 131 | 132 | get userId() { 133 | return this._userId; 134 | } 135 | } 136 | 137 | function getAllAnnotations(documentId) { 138 | let all_annotations = []; 139 | let re = new RegExp(`${documentId}/(.*)/annotations`); 140 | for (let i = 0; i < localStorage.length; i++) { 141 | if (localStorage.key(i).search(re) > -1) { 142 | all_annotations.push(...JSON.parse(localStorage.getItem(localStorage.key(i)))); 143 | } 144 | } 145 | return all_annotations; 146 | } 147 | 148 | function getAnnotations(documentId, userId) { 149 | return JSON.parse(localStorage.getItem(`${documentId}/${userId}/annotations`)) || []; 150 | } 151 | 152 | function updateAnnotations(documentId, userId, annotations) { 153 | localStorage.setItem(`${documentId}/${userId}/annotations`, JSON.stringify(annotations)); 154 | } 155 | /** 156 | * 157 | * @param {String} documentId Document id of the annotation 158 | * @param {String} userId User id of the annotation 159 | * @param {String} annotationId The id of the annotation 160 | * 161 | * This function finds all the annotation made by one user. 162 | * 163 | * @return {int} The index of the annotation in localstorage 164 | */ 165 | function findAnnotation(documentId, userId, annotationId) { 166 | let index = -1; 167 | let annotations = getAnnotations(documentId, userId); 168 | for (let i = 0, l = annotations.length; i < l; i++) { 169 | if (annotations[i].uuid === annotationId) { 170 | index = i; 171 | break; 172 | } 173 | } 174 | return index; 175 | } 176 | -------------------------------------------------------------------------------- /src/adapter/StoreAdapter.js: -------------------------------------------------------------------------------- 1 | // Disable JSDoc as it cannot really deal with the odd way that the functions are defined 2 | /* eslint valid-jsdoc: 0 */ 3 | 4 | import abstractFunction from '../utils/abstractFunction'; 5 | import { fireEvent } from '../UI/event'; 6 | 7 | // Adapter should never be invoked publicly 8 | export default class StoreAdapter { 9 | /** 10 | * Create a new StoreAdapter instance 11 | * 12 | * @param {Object} [definition] The definition to use for overriding abstract methods 13 | */ 14 | constructor(definition = {}) { 15 | // Copy each function from definition if it is a function we know about 16 | Object.keys(definition).forEach((key) => { 17 | if (typeof definition[key] === 'function' && 18 | typeof this[key] === 'function') { 19 | this[key] = definition[key]; 20 | } 21 | }); 22 | } 23 | 24 | /** 25 | * Get all the annotations for a given document and page number. 26 | * 27 | * @param {String} documentId The ID for the document the annotations belong to 28 | * @param {Number} pageNumber The number of the page the annotations belong to 29 | * @return {Promise} Promise that returns with list of annotations for document and page 30 | */ 31 | __getAnnotations(documentId, pageNumber) { abstractFunction('getAnnotations'); } 32 | get getAnnotations() { return this.__getAnnotations; } 33 | set getAnnotations(fn) { 34 | this.__getAnnotations = function getAnnotations(documentId, pageNumber) { 35 | return fn(...arguments).then((annotations) => { 36 | // TODO may be best to have this happen on the server 37 | if (annotations.annotations) { 38 | annotations.annotations.forEach((a) => { 39 | a.documentId = documentId; 40 | }); 41 | } 42 | return annotations; 43 | }); 44 | }; 45 | } 46 | 47 | /** 48 | * Get the definition for a specific annotation. 49 | * 50 | * @param {String} documentId The ID for the document the annotation belongs to 51 | * @param {String} annotationId The ID for the annotation 52 | * @return {Promise} Promise that returns the requested annotation 53 | */ 54 | getAnnotation(documentId, annotationId) { abstractFunction('getAnnotation'); } 55 | 56 | /** 57 | * Add an annotation 58 | * 59 | * @param {String} documentId The ID for the document to add the annotation to 60 | * @param {String} pageNumber The page number to add the annotation to 61 | * @param {Object} annotation The definition for the new annotation 62 | * @return {Promise} Promise that returns with the added annotation 63 | */ 64 | __addAnnotation(documentId, pageNumber, annotation) { abstractFunction('addAnnotation'); } 65 | get addAnnotation() { return this.__addAnnotation; } 66 | set addAnnotation(fn) { 67 | this.__addAnnotation = function addAnnotation(documentId, pageNumber, annotation) { 68 | return fn(...arguments).then((annotation) => { 69 | fireEvent('annotation:add', documentId, pageNumber, annotation); 70 | return annotation; 71 | }); 72 | }; 73 | } 74 | 75 | /** 76 | * Edit an annotation 77 | * 78 | * @param {String} documentId The ID for the document 79 | * @param {String} pageNumber the page number of the annotation 80 | * @param {Object} annotation The definition of the modified annotation 81 | * @return {Promise} Promise that returns with the edited annotation 82 | */ 83 | __editAnnotation(documentId, pageNumber, annotation) { abstractFunction('editAnnotation'); } 84 | get editAnnotation() { return this.__editAnnotation; } 85 | set editAnnotation(fn) { 86 | this.__editAnnotation = function editAnnotation(documentId, annotationId, annotation) { 87 | return fn(...arguments).then((annotation) => { 88 | fireEvent('annotation:edit', documentId, annotationId, annotation); 89 | return annotation; 90 | }); 91 | }; 92 | } 93 | 94 | /** 95 | * Delete an annotation 96 | * 97 | * @param {String} documentId The ID for the document 98 | * @param {String} annotationId The ID for the annotation 99 | * @return {Promise} Promise that returns with boolean if annotation was deleted 100 | */ 101 | __deleteAnnotation(documentId, annotationId) { abstractFunction('deleteAnnotation'); } 102 | get deleteAnnotation() { return this.__deleteAnnotation; } 103 | set deleteAnnotation(fn) { 104 | this.__deleteAnnotation = function deleteAnnotation(documentId, annotationId) { 105 | return fn(...arguments).then((success) => { 106 | if (success) { 107 | fireEvent('annotation:delete', documentId, annotationId); 108 | } 109 | return success; 110 | }); 111 | }; 112 | } 113 | 114 | /** 115 | * Get all the comments for an annotation 116 | * 117 | * @param {String} documentId The ID for the document 118 | * @param {String} annotationId The ID for the annotation 119 | * @return {Promise} Promise that returns with comments for annotation 120 | */ 121 | getComments(documentId, annotationId) { abstractFunction('getComments'); } 122 | 123 | /** 124 | * Add a new comment 125 | * 126 | * @param {String} documentId The ID for the document 127 | * @param {String} annotationId The ID for the annotation 128 | * @param {Object} content The definition of the comment 129 | * @return {Promise} Promise that returns with the added comment 130 | */ 131 | __addComment(documentId, annotationId, content) { abstractFunction('addComment'); } 132 | get addComment() { return this.__addComment; } 133 | set addComment(fn) { 134 | this.__addComment = function addComment(documentId, annotationId, content) { 135 | return fn(...arguments).then((comment) => { 136 | fireEvent('comment:add', documentId, annotationId, comment); 137 | return comment; 138 | }); 139 | }; 140 | } 141 | 142 | /** 143 | * Delete a comment 144 | * 145 | * @param {String} documentId The ID for the document 146 | * @param {String} commentId The ID for the comment 147 | * @return {Promise} Promise that returns with boolean if comment was deleted 148 | */ 149 | __deleteComment(documentId, commentId) { abstractFunction('deleteComment'); } 150 | get deleteComment() { return this.__deleteComment; } 151 | set deleteComment(fn) { 152 | this.__deleteComment = function deleteComment(documentId, commentId) { 153 | return fn(...arguments).then((success) => { 154 | if (success) { 155 | fireEvent('comment:delete', documentId, commentId); 156 | } 157 | return success; 158 | }); 159 | }; 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /src/config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | annotationLayerName: 'annotationLayer', 3 | textLayerName: 'textLayer', 4 | annotationSvgQuery: function() { 5 | return 'svg.' + this.annotationLayerName; 6 | }, 7 | annotationClassQuery: function() { 8 | return '.' + this.annotationLayerName; 9 | }, 10 | textClassQuery: function() { 11 | return '.' + this.textLayerName; 12 | } 13 | }; 14 | -------------------------------------------------------------------------------- /src/css/cmaps/78-EUC-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/78-EUC-H.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/78-EUC-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/78-EUC-V.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/78-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/78-H.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/78-RKSJ-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/78-RKSJ-H.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/78-RKSJ-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/78-RKSJ-V.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/78-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/78-V.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/78ms-RKSJ-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/78ms-RKSJ-H.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/78ms-RKSJ-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/78ms-RKSJ-V.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/83pv-RKSJ-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/83pv-RKSJ-H.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/90ms-RKSJ-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/90ms-RKSJ-H.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/90ms-RKSJ-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/90ms-RKSJ-V.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/90msp-RKSJ-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/90msp-RKSJ-H.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/90msp-RKSJ-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/90msp-RKSJ-V.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/90pv-RKSJ-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/90pv-RKSJ-H.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/90pv-RKSJ-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/90pv-RKSJ-V.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/Add-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/Add-H.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/Add-RKSJ-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/Add-RKSJ-H.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/Add-RKSJ-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/Add-RKSJ-V.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/Add-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/Add-V.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/Adobe-CNS1-0.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/Adobe-CNS1-0.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/Adobe-CNS1-1.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/Adobe-CNS1-1.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/Adobe-CNS1-2.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/Adobe-CNS1-2.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/Adobe-CNS1-3.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/Adobe-CNS1-3.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/Adobe-CNS1-4.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/Adobe-CNS1-4.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/Adobe-CNS1-5.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/Adobe-CNS1-5.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/Adobe-CNS1-6.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/Adobe-CNS1-6.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/Adobe-CNS1-UCS2.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/Adobe-CNS1-UCS2.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/Adobe-GB1-0.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/Adobe-GB1-0.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/Adobe-GB1-1.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/Adobe-GB1-1.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/Adobe-GB1-2.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/Adobe-GB1-2.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/Adobe-GB1-3.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/Adobe-GB1-3.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/Adobe-GB1-4.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/Adobe-GB1-4.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/Adobe-GB1-5.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/Adobe-GB1-5.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/Adobe-GB1-UCS2.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/Adobe-GB1-UCS2.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/Adobe-Japan1-0.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/Adobe-Japan1-0.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/Adobe-Japan1-1.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/Adobe-Japan1-1.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/Adobe-Japan1-2.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/Adobe-Japan1-2.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/Adobe-Japan1-3.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/Adobe-Japan1-3.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/Adobe-Japan1-4.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/Adobe-Japan1-4.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/Adobe-Japan1-5.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/Adobe-Japan1-5.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/Adobe-Japan1-6.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/Adobe-Japan1-6.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/Adobe-Japan1-UCS2.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/Adobe-Japan1-UCS2.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/Adobe-Korea1-0.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/Adobe-Korea1-0.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/Adobe-Korea1-1.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/Adobe-Korea1-1.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/Adobe-Korea1-2.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/Adobe-Korea1-2.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/Adobe-Korea1-UCS2.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/Adobe-Korea1-UCS2.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/B5-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/B5-H.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/B5-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/B5-V.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/B5pc-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/B5pc-H.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/B5pc-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/B5pc-V.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/CNS-EUC-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/CNS-EUC-H.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/CNS-EUC-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/CNS-EUC-V.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/CNS1-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/CNS1-H.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/CNS1-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/CNS1-V.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/CNS2-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/CNS2-H.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/CNS2-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/CNS2-V.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/ETHK-B5-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/ETHK-B5-H.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/ETHK-B5-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/ETHK-B5-V.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/ETen-B5-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/ETen-B5-H.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/ETen-B5-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/ETen-B5-V.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/ETenms-B5-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/ETenms-B5-H.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/ETenms-B5-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/ETenms-B5-V.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/EUC-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/EUC-H.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/EUC-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/EUC-V.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/Ext-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/Ext-H.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/Ext-RKSJ-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/Ext-RKSJ-H.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/Ext-RKSJ-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/Ext-RKSJ-V.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/Ext-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/Ext-V.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/GB-EUC-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/GB-EUC-H.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/GB-EUC-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/GB-EUC-V.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/GB-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/GB-H.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/GB-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/GB-V.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/GBK-EUC-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/GBK-EUC-H.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/GBK-EUC-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/GBK-EUC-V.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/GBK2K-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/GBK2K-H.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/GBK2K-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/GBK2K-V.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/GBKp-EUC-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/GBKp-EUC-H.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/GBKp-EUC-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/GBKp-EUC-V.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/GBT-EUC-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/GBT-EUC-H.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/GBT-EUC-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/GBT-EUC-V.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/GBT-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/GBT-H.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/GBT-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/GBT-V.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/GBTpc-EUC-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/GBTpc-EUC-H.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/GBTpc-EUC-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/GBTpc-EUC-V.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/GBpc-EUC-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/GBpc-EUC-H.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/GBpc-EUC-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/GBpc-EUC-V.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/H.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/HKdla-B5-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/HKdla-B5-H.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/HKdla-B5-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/HKdla-B5-V.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/HKdlb-B5-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/HKdlb-B5-H.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/HKdlb-B5-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/HKdlb-B5-V.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/HKgccs-B5-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/HKgccs-B5-H.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/HKgccs-B5-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/HKgccs-B5-V.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/HKm314-B5-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/HKm314-B5-H.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/HKm314-B5-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/HKm314-B5-V.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/HKm471-B5-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/HKm471-B5-H.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/HKm471-B5-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/HKm471-B5-V.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/HKscs-B5-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/HKscs-B5-H.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/HKscs-B5-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/HKscs-B5-V.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/Hankaku.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/Hankaku.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/Hiragana.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/Hiragana.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/KSC-EUC-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/KSC-EUC-H.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/KSC-EUC-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/KSC-EUC-V.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/KSC-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/KSC-H.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/KSC-Johab-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/KSC-Johab-H.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/KSC-Johab-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/KSC-Johab-V.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/KSC-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/KSC-V.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/KSCms-UHC-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/KSCms-UHC-H.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/KSCms-UHC-HW-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/KSCms-UHC-HW-H.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/KSCms-UHC-HW-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/KSCms-UHC-HW-V.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/KSCms-UHC-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/KSCms-UHC-V.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/KSCpc-EUC-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/KSCpc-EUC-H.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/KSCpc-EUC-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/KSCpc-EUC-V.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/Katakana.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/Katakana.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/LICENSE: -------------------------------------------------------------------------------- 1 | %%Copyright: ----------------------------------------------------------- 2 | %%Copyright: Copyright 1990-2009 Adobe Systems Incorporated. 3 | %%Copyright: All rights reserved. 4 | %%Copyright: 5 | %%Copyright: Redistribution and use in source and binary forms, with or 6 | %%Copyright: without modification, are permitted provided that the 7 | %%Copyright: following conditions are met: 8 | %%Copyright: 9 | %%Copyright: Redistributions of source code must retain the above 10 | %%Copyright: copyright notice, this list of conditions and the following 11 | %%Copyright: disclaimer. 12 | %%Copyright: 13 | %%Copyright: Redistributions in binary form must reproduce the above 14 | %%Copyright: copyright notice, this list of conditions and the following 15 | %%Copyright: disclaimer in the documentation and/or other materials 16 | %%Copyright: provided with the distribution. 17 | %%Copyright: 18 | %%Copyright: Neither the name of Adobe Systems Incorporated nor the names 19 | %%Copyright: of its contributors may be used to endorse or promote 20 | %%Copyright: products derived from this software without specific prior 21 | %%Copyright: written permission. 22 | %%Copyright: 23 | %%Copyright: THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 24 | %%Copyright: CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 25 | %%Copyright: INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 26 | %%Copyright: MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 27 | %%Copyright: DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 28 | %%Copyright: CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 29 | %%Copyright: SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 30 | %%Copyright: NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 31 | %%Copyright: LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 32 | %%Copyright: HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 33 | %%Copyright: CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 34 | %%Copyright: OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 35 | %%Copyright: SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 36 | %%Copyright: ----------------------------------------------------------- 37 | -------------------------------------------------------------------------------- /src/css/cmaps/NWP-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/NWP-H.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/NWP-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/NWP-V.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/RKSJ-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/RKSJ-H.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/RKSJ-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/RKSJ-V.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/Roman.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/Roman.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/UniCNS-UCS2-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/UniCNS-UCS2-H.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/UniCNS-UCS2-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/UniCNS-UCS2-V.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/UniCNS-UTF16-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/UniCNS-UTF16-H.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/UniCNS-UTF16-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/UniCNS-UTF16-V.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/UniCNS-UTF32-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/UniCNS-UTF32-H.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/UniCNS-UTF32-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/UniCNS-UTF32-V.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/UniCNS-UTF8-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/UniCNS-UTF8-H.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/UniCNS-UTF8-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/UniCNS-UTF8-V.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/UniGB-UCS2-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/UniGB-UCS2-H.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/UniGB-UCS2-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/UniGB-UCS2-V.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/UniGB-UTF16-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/UniGB-UTF16-H.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/UniGB-UTF16-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/UniGB-UTF16-V.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/UniGB-UTF32-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/UniGB-UTF32-H.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/UniGB-UTF32-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/UniGB-UTF32-V.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/UniGB-UTF8-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/UniGB-UTF8-H.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/UniGB-UTF8-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/UniGB-UTF8-V.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/UniJIS-UCS2-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/UniJIS-UCS2-H.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/UniJIS-UCS2-HW-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/UniJIS-UCS2-HW-H.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/UniJIS-UCS2-HW-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/UniJIS-UCS2-HW-V.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/UniJIS-UCS2-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/UniJIS-UCS2-V.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/UniJIS-UTF16-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/UniJIS-UTF16-H.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/UniJIS-UTF16-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/UniJIS-UTF16-V.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/UniJIS-UTF32-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/UniJIS-UTF32-H.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/UniJIS-UTF32-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/UniJIS-UTF32-V.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/UniJIS-UTF8-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/UniJIS-UTF8-H.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/UniJIS-UTF8-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/UniJIS-UTF8-V.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/UniJIS2004-UTF16-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/UniJIS2004-UTF16-H.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/UniJIS2004-UTF16-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/UniJIS2004-UTF16-V.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/UniJIS2004-UTF32-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/UniJIS2004-UTF32-H.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/UniJIS2004-UTF32-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/UniJIS2004-UTF32-V.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/UniJIS2004-UTF8-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/UniJIS2004-UTF8-H.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/UniJIS2004-UTF8-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/UniJIS2004-UTF8-V.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/UniJISPro-UCS2-HW-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/UniJISPro-UCS2-HW-V.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/UniJISPro-UCS2-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/UniJISPro-UCS2-V.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/UniJISPro-UTF8-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/UniJISPro-UTF8-V.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/UniJISX0213-UTF32-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/UniJISX0213-UTF32-H.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/UniJISX0213-UTF32-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/UniJISX0213-UTF32-V.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/UniJISX02132004-UTF32-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/UniJISX02132004-UTF32-H.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/UniJISX02132004-UTF32-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/UniJISX02132004-UTF32-V.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/UniKS-UCS2-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/UniKS-UCS2-H.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/UniKS-UCS2-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/UniKS-UCS2-V.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/UniKS-UTF16-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/UniKS-UTF16-H.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/UniKS-UTF16-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/UniKS-UTF16-V.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/UniKS-UTF32-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/UniKS-UTF32-H.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/UniKS-UTF32-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/UniKS-UTF32-V.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/UniKS-UTF8-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/UniKS-UTF8-H.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/UniKS-UTF8-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/UniKS-UTF8-V.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/V.bcmap -------------------------------------------------------------------------------- /src/css/cmaps/WP-Symbol.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/cmaps/WP-Symbol.bcmap -------------------------------------------------------------------------------- /src/css/images/annotation-check.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 11 | 12 | -------------------------------------------------------------------------------- /src/css/images/annotation-comment.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 13 | 16 | 17 | -------------------------------------------------------------------------------- /src/css/images/annotation-help.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 16 | 18 | 21 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /src/css/images/annotation-insert.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /src/css/images/annotation-key.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 11 | 12 | -------------------------------------------------------------------------------- /src/css/images/annotation-newparagraph.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 11 | 12 | -------------------------------------------------------------------------------- /src/css/images/annotation-noicon.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /src/css/images/annotation-note.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 14 | 21 | 28 | 35 | 42 | 43 | -------------------------------------------------------------------------------- /src/css/images/annotation-paragraph.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 13 | 16 | 17 | -------------------------------------------------------------------------------- /src/css/images/findbarButton-next-rtl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/images/findbarButton-next-rtl.png -------------------------------------------------------------------------------- /src/css/images/findbarButton-next-rtl@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/images/findbarButton-next-rtl@2x.png -------------------------------------------------------------------------------- /src/css/images/findbarButton-next.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/images/findbarButton-next.png -------------------------------------------------------------------------------- /src/css/images/findbarButton-next@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/images/findbarButton-next@2x.png -------------------------------------------------------------------------------- /src/css/images/findbarButton-previous-rtl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/images/findbarButton-previous-rtl.png -------------------------------------------------------------------------------- /src/css/images/findbarButton-previous-rtl@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/images/findbarButton-previous-rtl@2x.png -------------------------------------------------------------------------------- /src/css/images/findbarButton-previous.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/images/findbarButton-previous.png -------------------------------------------------------------------------------- /src/css/images/findbarButton-previous@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/images/findbarButton-previous@2x.png -------------------------------------------------------------------------------- /src/css/images/grab.cur: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/images/grab.cur -------------------------------------------------------------------------------- /src/css/images/grabbing.cur: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/images/grabbing.cur -------------------------------------------------------------------------------- /src/css/images/loading-icon.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/images/loading-icon.gif -------------------------------------------------------------------------------- /src/css/images/loading-small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/images/loading-small.png -------------------------------------------------------------------------------- /src/css/images/loading-small@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/images/loading-small@2x.png -------------------------------------------------------------------------------- /src/css/images/secondaryToolbarButton-documentProperties.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/images/secondaryToolbarButton-documentProperties.png -------------------------------------------------------------------------------- /src/css/images/secondaryToolbarButton-documentProperties@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/images/secondaryToolbarButton-documentProperties@2x.png -------------------------------------------------------------------------------- /src/css/images/secondaryToolbarButton-firstPage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/images/secondaryToolbarButton-firstPage.png -------------------------------------------------------------------------------- /src/css/images/secondaryToolbarButton-firstPage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/images/secondaryToolbarButton-firstPage@2x.png -------------------------------------------------------------------------------- /src/css/images/secondaryToolbarButton-handTool.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/images/secondaryToolbarButton-handTool.png -------------------------------------------------------------------------------- /src/css/images/secondaryToolbarButton-handTool@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/images/secondaryToolbarButton-handTool@2x.png -------------------------------------------------------------------------------- /src/css/images/secondaryToolbarButton-lastPage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/images/secondaryToolbarButton-lastPage.png -------------------------------------------------------------------------------- /src/css/images/secondaryToolbarButton-lastPage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/images/secondaryToolbarButton-lastPage@2x.png -------------------------------------------------------------------------------- /src/css/images/secondaryToolbarButton-rotateCcw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/images/secondaryToolbarButton-rotateCcw.png -------------------------------------------------------------------------------- /src/css/images/secondaryToolbarButton-rotateCcw@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/images/secondaryToolbarButton-rotateCcw@2x.png -------------------------------------------------------------------------------- /src/css/images/secondaryToolbarButton-rotateCw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/images/secondaryToolbarButton-rotateCw.png -------------------------------------------------------------------------------- /src/css/images/secondaryToolbarButton-rotateCw@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/images/secondaryToolbarButton-rotateCw@2x.png -------------------------------------------------------------------------------- /src/css/images/shadow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/images/shadow.png -------------------------------------------------------------------------------- /src/css/images/texture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/images/texture.png -------------------------------------------------------------------------------- /src/css/images/toolbarButton-bookmark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/images/toolbarButton-bookmark.png -------------------------------------------------------------------------------- /src/css/images/toolbarButton-bookmark@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/images/toolbarButton-bookmark@2x.png -------------------------------------------------------------------------------- /src/css/images/toolbarButton-download.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/images/toolbarButton-download.png -------------------------------------------------------------------------------- /src/css/images/toolbarButton-download@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/images/toolbarButton-download@2x.png -------------------------------------------------------------------------------- /src/css/images/toolbarButton-menuArrows.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/images/toolbarButton-menuArrows.png -------------------------------------------------------------------------------- /src/css/images/toolbarButton-menuArrows@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/images/toolbarButton-menuArrows@2x.png -------------------------------------------------------------------------------- /src/css/images/toolbarButton-openFile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/images/toolbarButton-openFile.png -------------------------------------------------------------------------------- /src/css/images/toolbarButton-openFile@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/images/toolbarButton-openFile@2x.png -------------------------------------------------------------------------------- /src/css/images/toolbarButton-pageDown-rtl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/images/toolbarButton-pageDown-rtl.png -------------------------------------------------------------------------------- /src/css/images/toolbarButton-pageDown-rtl@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/images/toolbarButton-pageDown-rtl@2x.png -------------------------------------------------------------------------------- /src/css/images/toolbarButton-pageDown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/images/toolbarButton-pageDown.png -------------------------------------------------------------------------------- /src/css/images/toolbarButton-pageDown@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/images/toolbarButton-pageDown@2x.png -------------------------------------------------------------------------------- /src/css/images/toolbarButton-pageUp-rtl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/images/toolbarButton-pageUp-rtl.png -------------------------------------------------------------------------------- /src/css/images/toolbarButton-pageUp-rtl@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/images/toolbarButton-pageUp-rtl@2x.png -------------------------------------------------------------------------------- /src/css/images/toolbarButton-pageUp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/images/toolbarButton-pageUp.png -------------------------------------------------------------------------------- /src/css/images/toolbarButton-pageUp@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/images/toolbarButton-pageUp@2x.png -------------------------------------------------------------------------------- /src/css/images/toolbarButton-presentationMode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/images/toolbarButton-presentationMode.png -------------------------------------------------------------------------------- /src/css/images/toolbarButton-presentationMode@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/images/toolbarButton-presentationMode@2x.png -------------------------------------------------------------------------------- /src/css/images/toolbarButton-print.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/images/toolbarButton-print.png -------------------------------------------------------------------------------- /src/css/images/toolbarButton-print@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/images/toolbarButton-print@2x.png -------------------------------------------------------------------------------- /src/css/images/toolbarButton-search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/images/toolbarButton-search.png -------------------------------------------------------------------------------- /src/css/images/toolbarButton-search@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/images/toolbarButton-search@2x.png -------------------------------------------------------------------------------- /src/css/images/toolbarButton-secondaryToolbarToggle-rtl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/images/toolbarButton-secondaryToolbarToggle-rtl.png -------------------------------------------------------------------------------- /src/css/images/toolbarButton-secondaryToolbarToggle-rtl@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/images/toolbarButton-secondaryToolbarToggle-rtl@2x.png -------------------------------------------------------------------------------- /src/css/images/toolbarButton-secondaryToolbarToggle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/images/toolbarButton-secondaryToolbarToggle.png -------------------------------------------------------------------------------- /src/css/images/toolbarButton-secondaryToolbarToggle@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/images/toolbarButton-secondaryToolbarToggle@2x.png -------------------------------------------------------------------------------- /src/css/images/toolbarButton-sidebarToggle-rtl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/images/toolbarButton-sidebarToggle-rtl.png -------------------------------------------------------------------------------- /src/css/images/toolbarButton-sidebarToggle-rtl@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/images/toolbarButton-sidebarToggle-rtl@2x.png -------------------------------------------------------------------------------- /src/css/images/toolbarButton-sidebarToggle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/images/toolbarButton-sidebarToggle.png -------------------------------------------------------------------------------- /src/css/images/toolbarButton-sidebarToggle@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/images/toolbarButton-sidebarToggle@2x.png -------------------------------------------------------------------------------- /src/css/images/toolbarButton-viewAttachments.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/images/toolbarButton-viewAttachments.png -------------------------------------------------------------------------------- /src/css/images/toolbarButton-viewAttachments@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/images/toolbarButton-viewAttachments@2x.png -------------------------------------------------------------------------------- /src/css/images/toolbarButton-viewOutline-rtl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/images/toolbarButton-viewOutline-rtl.png -------------------------------------------------------------------------------- /src/css/images/toolbarButton-viewOutline-rtl@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/images/toolbarButton-viewOutline-rtl@2x.png -------------------------------------------------------------------------------- /src/css/images/toolbarButton-viewOutline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/images/toolbarButton-viewOutline.png -------------------------------------------------------------------------------- /src/css/images/toolbarButton-viewOutline@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/images/toolbarButton-viewOutline@2x.png -------------------------------------------------------------------------------- /src/css/images/toolbarButton-viewThumbnail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/images/toolbarButton-viewThumbnail.png -------------------------------------------------------------------------------- /src/css/images/toolbarButton-viewThumbnail@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/images/toolbarButton-viewThumbnail@2x.png -------------------------------------------------------------------------------- /src/css/images/toolbarButton-zoomIn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/images/toolbarButton-zoomIn.png -------------------------------------------------------------------------------- /src/css/images/toolbarButton-zoomIn@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/images/toolbarButton-zoomIn@2x.png -------------------------------------------------------------------------------- /src/css/images/toolbarButton-zoomOut.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/images/toolbarButton-zoomOut.png -------------------------------------------------------------------------------- /src/css/images/toolbarButton-zoomOut@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxhxliang/pdf-annotate-vue/400f1e5f5c9eff7f82a042b765ce243411cfd912/src/css/images/toolbarButton-zoomOut@2x.png -------------------------------------------------------------------------------- /src/css/pdf_viewer.css: -------------------------------------------------------------------------------- 1 | /* Copyright 2014 Mozilla Foundation 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | .textLayer { 17 | position: absolute; 18 | left: 0; 19 | top: 0; 20 | right: 0; 21 | bottom: 0; 22 | overflow: hidden; 23 | opacity: 0.2; 24 | line-height: 1.0; 25 | } 26 | 27 | .textLayer > div { 28 | color: transparent; 29 | position: absolute; 30 | white-space: pre; 31 | cursor: text; 32 | -webkit-transform-origin: 0% 0%; 33 | transform-origin: 0% 0%; 34 | } 35 | 36 | .textLayer .highlight { 37 | margin: -1px; 38 | padding: 1px; 39 | 40 | background-color: rgb(180, 0, 170); 41 | border-radius: 4px; 42 | } 43 | 44 | .textLayer .highlight.begin { 45 | border-radius: 4px 0px 0px 4px; 46 | } 47 | 48 | .textLayer .highlight.end { 49 | border-radius: 0px 4px 4px 0px; 50 | } 51 | 52 | .textLayer .highlight.middle { 53 | border-radius: 0px; 54 | } 55 | 56 | .textLayer .highlight.selected { 57 | background-color: rgb(0, 100, 0); 58 | } 59 | 60 | .textLayer ::-moz-selection { background: rgb(0,0,255); } 61 | 62 | .textLayer ::selection { background: rgb(0,0,255); } 63 | 64 | .textLayer .endOfContent { 65 | display: block; 66 | position: absolute; 67 | left: 0px; 68 | top: 100%; 69 | right: 0px; 70 | bottom: 0px; 71 | z-index: -1; 72 | cursor: default; 73 | -webkit-user-select: none; 74 | -moz-user-select: none; 75 | -ms-user-select: none; 76 | user-select: none; 77 | } 78 | 79 | .textLayer .endOfContent.active { 80 | top: 0px; 81 | } 82 | 83 | 84 | .annotationLayer section { 85 | position: absolute; 86 | } 87 | 88 | .annotationLayer .linkAnnotation > a, 89 | .annotationLayer .buttonWidgetAnnotation.pushButton > a { 90 | position: absolute; 91 | font-size: 1em; 92 | top: 0; 93 | left: 0; 94 | width: 100%; 95 | height: 100%; 96 | } 97 | 98 | .annotationLayer .linkAnnotation > a:hover, 99 | .annotationLayer .buttonWidgetAnnotation.pushButton > a:hover { 100 | opacity: 0.2; 101 | background: #ff0; 102 | box-shadow: 0px 2px 10px #ff0; 103 | } 104 | 105 | .annotationLayer .textAnnotation img { 106 | position: absolute; 107 | cursor: pointer; 108 | } 109 | 110 | .annotationLayer .textWidgetAnnotation input, 111 | .annotationLayer .textWidgetAnnotation textarea, 112 | .annotationLayer .choiceWidgetAnnotation select, 113 | .annotationLayer .buttonWidgetAnnotation.checkBox input, 114 | .annotationLayer .buttonWidgetAnnotation.radioButton input { 115 | background-color: rgba(0, 54, 255, 0.13); 116 | border: 1px solid transparent; 117 | box-sizing: border-box; 118 | font-size: 9px; 119 | height: 100%; 120 | margin: 0; 121 | padding: 0 3px; 122 | vertical-align: top; 123 | width: 100%; 124 | } 125 | 126 | .annotationLayer .choiceWidgetAnnotation select option { 127 | padding: 0; 128 | } 129 | 130 | .annotationLayer .buttonWidgetAnnotation.radioButton input { 131 | border-radius: 50%; 132 | } 133 | 134 | .annotationLayer .textWidgetAnnotation textarea { 135 | font: message-box; 136 | font-size: 9px; 137 | resize: none; 138 | } 139 | 140 | .annotationLayer .textWidgetAnnotation input[disabled], 141 | .annotationLayer .textWidgetAnnotation textarea[disabled], 142 | .annotationLayer .choiceWidgetAnnotation select[disabled], 143 | .annotationLayer .buttonWidgetAnnotation.checkBox input[disabled], 144 | .annotationLayer .buttonWidgetAnnotation.radioButton input[disabled] { 145 | background: none; 146 | border: 1px solid transparent; 147 | cursor: not-allowed; 148 | } 149 | 150 | .annotationLayer .textWidgetAnnotation input:hover, 151 | .annotationLayer .textWidgetAnnotation textarea:hover, 152 | .annotationLayer .choiceWidgetAnnotation select:hover, 153 | .annotationLayer .buttonWidgetAnnotation.checkBox input:hover, 154 | .annotationLayer .buttonWidgetAnnotation.radioButton input:hover { 155 | border: 1px solid #000; 156 | } 157 | 158 | .annotationLayer .textWidgetAnnotation input:focus, 159 | .annotationLayer .textWidgetAnnotation textarea:focus, 160 | .annotationLayer .choiceWidgetAnnotation select:focus { 161 | background: none; 162 | border: 1px solid transparent; 163 | } 164 | 165 | .annotationLayer .buttonWidgetAnnotation.checkBox input:checked:before, 166 | .annotationLayer .buttonWidgetAnnotation.checkBox input:checked:after, 167 | .annotationLayer .buttonWidgetAnnotation.radioButton input:checked:before { 168 | background-color: #000; 169 | content: ''; 170 | display: block; 171 | position: absolute; 172 | } 173 | 174 | .annotationLayer .buttonWidgetAnnotation.checkBox input:checked:before, 175 | .annotationLayer .buttonWidgetAnnotation.checkBox input:checked:after { 176 | height: 80%; 177 | left: 45%; 178 | width: 1px; 179 | } 180 | 181 | .annotationLayer .buttonWidgetAnnotation.checkBox input:checked:before { 182 | -webkit-transform: rotate(45deg); 183 | transform: rotate(45deg); 184 | } 185 | 186 | .annotationLayer .buttonWidgetAnnotation.checkBox input:checked:after { 187 | -webkit-transform: rotate(-45deg); 188 | transform: rotate(-45deg); 189 | } 190 | 191 | .annotationLayer .buttonWidgetAnnotation.radioButton input:checked:before { 192 | border-radius: 50%; 193 | height: 50%; 194 | left: 30%; 195 | top: 20%; 196 | width: 50%; 197 | } 198 | 199 | .annotationLayer .textWidgetAnnotation input.comb { 200 | font-family: monospace; 201 | padding-left: 2px; 202 | padding-right: 0; 203 | } 204 | 205 | .annotationLayer .textWidgetAnnotation input.comb:focus { 206 | /* 207 | * Letter spacing is placed on the right side of each character. Hence, the 208 | * letter spacing of the last character may be placed outside the visible 209 | * area, causing horizontal scrolling. We avoid this by extending the width 210 | * when the element has focus and revert this when it loses focus. 211 | */ 212 | width: 115%; 213 | } 214 | 215 | .annotationLayer .buttonWidgetAnnotation.checkBox input, 216 | .annotationLayer .buttonWidgetAnnotation.radioButton input { 217 | -webkit-appearance: none; 218 | -moz-appearance: none; 219 | appearance: none; 220 | padding: 0; 221 | } 222 | 223 | .annotationLayer .popupWrapper { 224 | position: absolute; 225 | width: 20em; 226 | } 227 | 228 | .annotationLayer .popup { 229 | position: absolute; 230 | z-index: 200; 231 | max-width: 20em; 232 | background-color: #FFFF99; 233 | box-shadow: 0px 2px 5px #333; 234 | border-radius: 2px; 235 | padding: 0.6em; 236 | margin-left: 5px; 237 | cursor: pointer; 238 | font: message-box; 239 | word-wrap: break-word; 240 | } 241 | 242 | .annotationLayer .popup h1 { 243 | font-size: 1em; 244 | border-bottom: 1px solid #000000; 245 | margin: 0; 246 | padding-bottom: 0.2em; 247 | } 248 | 249 | .annotationLayer .popup p { 250 | margin: 0; 251 | padding-top: 0.2em; 252 | } 253 | 254 | .annotationLayer .highlightAnnotation, 255 | .annotationLayer .underlineAnnotation, 256 | .annotationLayer .squigglyAnnotation, 257 | .annotationLayer .strikeoutAnnotation, 258 | .annotationLayer .lineAnnotation svg line, 259 | .annotationLayer .squareAnnotation svg rect, 260 | .annotationLayer .circleAnnotation svg ellipse, 261 | .annotationLayer .polylineAnnotation svg polyline, 262 | .annotationLayer .polygonAnnotation svg polygon, 263 | .annotationLayer .inkAnnotation svg polyline, 264 | .annotationLayer .stampAnnotation, 265 | .annotationLayer .fileAttachmentAnnotation { 266 | cursor: pointer; 267 | } 268 | 269 | 270 | .page .annotationLayer { 271 | position: absolute; 272 | left: 0; 273 | top: 0; 274 | right: 0; 275 | bottom: 0; 276 | overflow: hidden; 277 | opacity: 0.2; 278 | line-height: 1.0; 279 | } 280 | 281 | 282 | .pdfViewer .canvasWrapper { 283 | overflow: hidden; 284 | } 285 | 286 | /* .pdfViewer .page { 287 | direction: ltr; 288 | width: 816px; 289 | height: 1056px; 290 | margin: 1px auto -8px auto; 291 | position: relative; 292 | overflow: visible; 293 | border: 9px solid transparent; 294 | background-clip: content-box; 295 | -o-border-image: url(images/shadow.png) 9 9 repeat; 296 | border-image: url(images/shadow.png) 9 9 repeat; 297 | background-color: white; 298 | } */ 299 | 300 | .pdfViewer.removePageBorders .page { 301 | margin: 0px auto 10px auto; 302 | border: none; 303 | } 304 | 305 | .pdfViewer.singlePageView { 306 | display: inline-block; 307 | } 308 | 309 | .pdfViewer.singlePageView .page { 310 | margin: 0; 311 | border: none; 312 | } 313 | 314 | .pdfViewer.scrollHorizontal, .pdfViewer.scrollWrapped, .spread { 315 | margin-left: 3.5px; 316 | margin-right: 3.5px; 317 | text-align: center; 318 | } 319 | 320 | .pdfViewer.scrollHorizontal, .spread { 321 | white-space: nowrap; 322 | } 323 | 324 | .pdfViewer.removePageBorders, 325 | .pdfViewer.scrollHorizontal .spread, 326 | .pdfViewer.scrollWrapped .spread { 327 | margin-left: 0; 328 | margin-right: 0; 329 | } 330 | 331 | .spread .page, 332 | .pdfViewer.scrollHorizontal .page, 333 | .pdfViewer.scrollWrapped .page, 334 | .pdfViewer.scrollHorizontal .spread, 335 | .pdfViewer.scrollWrapped .spread { 336 | display: inline-block; 337 | vertical-align: middle; 338 | } 339 | 340 | .spread .page, 341 | .pdfViewer.scrollHorizontal .page, 342 | .pdfViewer.scrollWrapped .page { 343 | margin-left: -3.5px; 344 | margin-right: -3.5px; 345 | } 346 | 347 | .pdfViewer.removePageBorders .spread .page, 348 | .pdfViewer.removePageBorders.scrollHorizontal .page, 349 | .pdfViewer.removePageBorders.scrollWrapped .page { 350 | margin-left: 5px; 351 | margin-right: 5px; 352 | } 353 | 354 | .pdfViewer .page canvas { 355 | margin: 0; 356 | display: block; 357 | } 358 | 359 | .pdfViewer .page canvas[hidden] { 360 | display: none; 361 | } 362 | 363 | .pdfViewer .page .loadingIcon { 364 | position: absolute; 365 | display: block; 366 | left: 0; 367 | top: 0; 368 | right: 0; 369 | bottom: 0; 370 | background: url('images/loading-icon.gif') center no-repeat; 371 | } 372 | 373 | .pdfPresentationMode .pdfViewer { 374 | margin-left: 0; 375 | margin-right: 0; 376 | } 377 | 378 | .pdfPresentationMode .pdfViewer .page, 379 | .pdfPresentationMode .pdfViewer .spread { 380 | display: block; 381 | } 382 | 383 | .pdfPresentationMode .pdfViewer .page, 384 | .pdfPresentationMode .pdfViewer.removePageBorders .page { 385 | margin-left: auto; 386 | margin-right: auto; 387 | } 388 | 389 | .pdfPresentationMode:-ms-fullscreen .pdfViewer .page { 390 | margin-bottom: 100% !important; 391 | } 392 | 393 | .pdfPresentationMode:-webkit-full-screen .pdfViewer .page { 394 | margin-bottom: 100%; 395 | border: 0; 396 | } 397 | 398 | .pdfPresentationMode:-moz-full-screen .pdfViewer .page { 399 | margin-bottom: 100%; 400 | border: 0; 401 | } 402 | 403 | .pdfPresentationMode:fullscreen .pdfViewer .page { 404 | margin-bottom: 100%; 405 | border: 0; 406 | } 407 | -------------------------------------------------------------------------------- /src/css/toolbar.css: -------------------------------------------------------------------------------- 1 | * { 2 | box-sizing: border-box; 3 | } 4 | 5 | body { 6 | font-family: sans-serif; 7 | margin: 40px 0; 8 | } 9 | 10 | .toolbar { 11 | background-color: #eaeaea; 12 | border-bottom: 1px solid #d0d0d0; 13 | height: 35px; 14 | position: fixed; 15 | top: 0; 16 | left: 0; 17 | right: 0; 18 | padding: 0 5px; 19 | text-shadow: 1px 1px 0 #fff; 20 | z-index: 100; 21 | -webkit-box-shadow: inset 0px 1px 1px 0px rgba(255,255,255,1); 22 | -moz-box-shadow: inset 0px 1px 1px 0px rgba(255,255,255,1); 23 | box-shadow: inset 0px 1px 1px 0px rgba(255,255,255,1); 24 | } 25 | .toolbar .spacer { 26 | display: inline-block; 27 | border-left: 1px solid #c1c1c1; 28 | height: 34px; 29 | margin: 0 5px -11px; 30 | } 31 | 32 | .toolbar a { 33 | color: inherit; 34 | text-decoration: none; 35 | } 36 | 37 | .toolbar button { 38 | background-color: #eaeaea; 39 | border: 1px solid transparent; 40 | border-radius: 3px; 41 | font-size: 15px; 42 | padding: 3px; 43 | margin: 0; 44 | text-align: center; 45 | text-shadow: 1px 1px 0 #fff; 46 | position: relative; 47 | min-width: 27px; 48 | min-height: 27px; 49 | } 50 | .toolbar button:after { 51 | content: ' '; 52 | display: block; 53 | } 54 | .toolbar button.active { 55 | border-color: #bababa; 56 | -webkit-box-shadow: inset 0 0 1px rgba(0,0,0,0.25); 57 | -moz-box-shadow: inset 0 0 1px rgba(0,0,0,0.25); 58 | box-shadow: inset 0 0 1px rgba(0,0,0,0.25); 59 | } 60 | .toolbar button:hover, 61 | .toolbar button:focus { 62 | border-color: #bababa; 63 | } 64 | 65 | .toolbar button.pen { 66 | font-size: 16px; 67 | } 68 | 69 | .toolbar button.comment { 70 | font-size: 10px; 71 | color: #ffffff; 72 | text-shadow: 0 0 0 #000000; 73 | } 74 | 75 | .toolbar button.rectangle:after { 76 | width: 15px; 77 | height: 15px; 78 | border: 1px solid red; 79 | position: absolute; 80 | top: 4px; 81 | left: 4px; 82 | } 83 | 84 | .toolbar button.circle:after { 85 | width: 15px; 86 | height: 15px; 87 | border: 0px solid red; 88 | position: absolute; 89 | top: 4px; 90 | left: 4px; 91 | } 92 | 93 | .toolbar button.highlight:after { 94 | width: 17px; 95 | height: 17px; 96 | background: yellow; 97 | position: absolute; 98 | top: 4px; 99 | left: 4px; 100 | } 101 | 102 | .toolbar button.strikeout:after { 103 | width: 15px; 104 | height: 1px; 105 | background: red; 106 | position: absolute; 107 | top: 12px; 108 | left: 4px; 109 | } 110 | 111 | .toolbar button.text:after { 112 | content: 'T'; 113 | font-family: 'Times New Roman'; 114 | } 115 | 116 | .color { 117 | display: inline-block; 118 | width: 25px; 119 | height: 25px; 120 | border: 1px solid #000; 121 | vertical-align: middle; 122 | } 123 | .color-selected { 124 | border: 3px solid #666; 125 | width: 30px; 126 | height: 30px; 127 | margin-top: -1px; 128 | margin-left: -2px; 129 | margin-right: -2px; 130 | } 131 | .text-color, .pen-color, .hotspot-color { 132 | display: inline-block; 133 | } 134 | -------------------------------------------------------------------------------- /src/initColorPicker.js: -------------------------------------------------------------------------------- 1 | // Color picker component 2 | const COLORS = [ 3 | {hex: '#000000', name: 'Black'}, 4 | {hex: '#EF4437', name: 'Red'}, 5 | {hex: '#E71F63', name: 'Pink'}, 6 | {hex: '#8F3E97', name: 'Purple'}, 7 | {hex: '#65499D', name: 'Deep Purple'}, 8 | {hex: '#4554A4', name: 'Indigo'}, 9 | {hex: '#2083C5', name: 'Blue'}, 10 | {hex: '#35A4DC', name: 'Light Blue'}, 11 | {hex: '#09BCD3', name: 'Cyan'}, 12 | {hex: '#009688', name: 'Teal'}, 13 | {hex: '#43A047', name: 'Green'}, 14 | {hex: '#8BC34A', name: 'Light Green'}, 15 | {hex: '#FDC010', name: 'Yellow'}, 16 | {hex: '#F8971C', name: 'Orange'}, 17 | {hex: '#F0592B', name: 'Deep Orange'}, 18 | {hex: '#F06291', name: 'Light Pink'} 19 | ]; 20 | 21 | export default function initColorPicker(el, value, onChange) { 22 | function getNameFromHex(hex) { 23 | for (let color of COLORS) { 24 | if (color.hex === hex) { 25 | return color.name; 26 | } 27 | } 28 | } 29 | 30 | function setColor(hex, name, fireOnChange = true) { 31 | a.setAttribute('data-color', hex); 32 | a.setAttribute('data-name', name); 33 | a.setAttribute('title', name); 34 | a.style.background = hex; 35 | if (fireOnChange && typeof onChange === 'function') { 36 | onChange(hex); 37 | } 38 | closePicker(); 39 | } 40 | 41 | function togglePicker() { 42 | if (isPickerOpen) { 43 | closePicker(); 44 | } 45 | else { 46 | openPicker(); 47 | } 48 | } 49 | 50 | function closePicker() { 51 | document.removeEventListener('keyup', handleDocumentKeyup); 52 | if (picker && picker.parentNode) { 53 | picker.parentNode.removeChild(picker); 54 | } 55 | isPickerOpen = false; 56 | a.focus(); 57 | } 58 | 59 | function openPicker() { 60 | if (!picker) { 61 | picker = document.createElement('div'); 62 | picker.style.background = '#fff'; 63 | picker.style.border = '1px solid #ccc'; 64 | picker.style.padding = '2px'; 65 | picker.style.position = 'absolute'; 66 | picker.style.width = '122px'; 67 | el.style.position = 'relative'; 68 | 69 | COLORS.map(createColorOption).forEach((c) => { 70 | c.style.margin = '2px'; 71 | c.onclick = function() { 72 | setColor(c.getAttribute('data-color'), c.getAttribute('data-name')); 73 | }; 74 | picker.appendChild(c); 75 | }); 76 | } 77 | 78 | document.addEventListener('keyup', handleDocumentKeyup); 79 | el.appendChild(picker); 80 | isPickerOpen = true; 81 | } 82 | 83 | function createColorOption(color) { 84 | let e = document.createElement('a'); 85 | e.className = 'color'; 86 | e.setAttribute('href', 'javascript://'); 87 | e.setAttribute('title', color.name); 88 | e.setAttribute('data-name', color.name); 89 | e.setAttribute('data-color', color.hex); 90 | e.style.background = color.hex; 91 | return e; 92 | } 93 | 94 | function handleDocumentKeyup(e) { 95 | if (e.keyCode === 27) { 96 | closePicker(); 97 | } 98 | } 99 | 100 | let picker; 101 | let isPickerOpen = false; 102 | let name = getNameFromHex(value); 103 | let a = createColorOption({hex: value, name: name}); 104 | a.onclick = togglePicker; 105 | el.appendChild(a); 106 | setColor(value, name, false); 107 | } 108 | -------------------------------------------------------------------------------- /src/render/appendChild.js: -------------------------------------------------------------------------------- 1 | import objectAssign from 'object-assign'; 2 | import renderLine from './renderLine'; 3 | import renderPath from './renderPath'; 4 | import renderPoint from './renderPoint'; 5 | import renderRect from './renderRect'; 6 | import renderText from './renderText'; 7 | import renderCircle from './renderCircle'; 8 | import renderArrow from './renderArrow'; 9 | 10 | const isFirefox = /firefox/i.test(navigator.userAgent); 11 | 12 | /** 13 | * Get the x/y translation to be used for transforming the annotations 14 | * based on the rotation of the viewport. 15 | * 16 | * @param {Object} viewport The viewport data from the page 17 | * @return {Object} 18 | */ 19 | export function getTranslation(viewport) { 20 | let x; 21 | let y; 22 | 23 | // Modulus 360 on the rotation so that we only 24 | // have to worry about four possible values. 25 | switch (viewport.rotation % 360) { 26 | case 0: 27 | x = y = 0; 28 | break; 29 | case 90: 30 | x = 0; 31 | y = (viewport.width / viewport.scale) * -1; 32 | break; 33 | case 180: 34 | x = (viewport.width / viewport.scale) * -1; 35 | y = (viewport.height / viewport.scale) * -1; 36 | break; 37 | case 270: 38 | x = (viewport.height / viewport.scale) * -1; 39 | y = 0; 40 | break; 41 | } 42 | 43 | return { x, y }; 44 | } 45 | 46 | /** 47 | * Transform the rotation and scale of a node using SVG's native transform attribute. 48 | * 49 | * @param {Node} node The node to be transformed 50 | * @param {Object} viewport The page's viewport data 51 | * @return {Node} 52 | */ 53 | function transform(node, viewport) { 54 | let trans = getTranslation(viewport); 55 | 56 | // Let SVG natively transform the element 57 | node.setAttribute('transform', `scale(${viewport.scale}) rotate(${viewport.rotation}) translate(${trans.x}, ${trans.y})`); 58 | 59 | // Manually adjust x/y for nested SVG nodes 60 | if (!isFirefox && node.nodeName.toLowerCase() === 'svg') { 61 | node.setAttribute('x', parseInt(node.getAttribute('x'), 10) * viewport.scale); 62 | node.setAttribute('y', parseInt(node.getAttribute('y'), 10) * viewport.scale); 63 | 64 | let x = parseInt(node.getAttribute('x', 10)); 65 | let y = parseInt(node.getAttribute('y', 10)); 66 | let width = parseInt(node.getAttribute('width'), 10); 67 | let height = parseInt(node.getAttribute('height'), 10); 68 | let path = node.querySelector('path'); 69 | let svg = path.parentNode; 70 | 71 | // Scale width/height 72 | [node, svg, path, node.querySelector('rect')].forEach((n) => { 73 | n.setAttribute('width', parseInt(n.getAttribute('width'), 10) * viewport.scale); 74 | n.setAttribute('height', parseInt(n.getAttribute('height'), 10) * viewport.scale); 75 | }); 76 | 77 | // Transform path but keep scale at 100% since it will be handled natively 78 | transform(path, objectAssign({}, viewport, { scale: 1 })); 79 | 80 | switch (viewport.rotation % 360) { 81 | case 90: 82 | node.setAttribute('x', viewport.width - y - width); 83 | node.setAttribute('y', x); 84 | svg.setAttribute('x', 1); 85 | svg.setAttribute('y', 0); 86 | break; 87 | case 180: 88 | node.setAttribute('x', viewport.width - x - width); 89 | node.setAttribute('y', viewport.height - y - height); 90 | svg.setAttribute('y', 2); 91 | break; 92 | case 270: 93 | node.setAttribute('x', y); 94 | node.setAttribute('y', viewport.height - x - height); 95 | svg.setAttribute('x', -1); 96 | svg.setAttribute('y', 0); 97 | break; 98 | } 99 | } 100 | 101 | return node; 102 | } 103 | 104 | /** 105 | * Append an annotation as a child of an SVG. 106 | * 107 | * @param {SVGElement} svg The SVG element to append the annotation to 108 | * @param {Object} annotation The annotation definition to render and append 109 | * @param {Object} viewport The page's viewport data 110 | * @return {SVGElement} A node that was created and appended by this function 111 | */ 112 | export function appendChild(svg, annotation, viewport) { 113 | if (!viewport) { 114 | viewport = JSON.parse(svg.getAttribute('data-pdf-annotate-viewport')); 115 | } 116 | 117 | let child; 118 | switch (annotation.type) { 119 | case 'area': 120 | case 'highlight': 121 | child = renderRect(annotation); 122 | break; 123 | case 'circle': 124 | case 'fillcircle': 125 | case 'emptycircle': 126 | child = renderCircle(annotation); 127 | break; 128 | case 'strikeout': 129 | child = renderLine(annotation); 130 | break; 131 | case 'point': 132 | child = renderPoint(annotation); 133 | break; 134 | case 'textbox': 135 | child = renderText(annotation); 136 | break; 137 | case 'drawing': 138 | child = renderPath(annotation); 139 | break; 140 | case 'arrow': 141 | child = renderArrow(annotation); 142 | break; 143 | } 144 | 145 | // If no type was provided for an annotation it will result in node being null. 146 | // Skip appending/transforming if node doesn't exist. 147 | if (child) { 148 | // Set attributes 149 | child.setAttribute('data-pdf-annotate-id', annotation.uuid); 150 | child.setAttribute('aria-hidden', true); 151 | 152 | // Dynamically set any other attributes associated with annotation that is not related to drawing it 153 | Object.keys(annotation).filter((key) => { 154 | return ['color', 'x', 'y', 'cx', 'cy', 'color', 'documentId', 'lines', 'page', 155 | 'width', 'class', 'content', 'size', 'rotation', 'r'].indexOf(key) === -1; 156 | }).forEach((key) => { 157 | child.setAttribute(`data-pdf-annotate-${key}`, annotation[key]); 158 | }); 159 | 160 | svg.appendChild(transform(child, viewport)); 161 | } 162 | 163 | return child; 164 | } 165 | 166 | /** 167 | * Transform a child annotation of an SVG. 168 | * 169 | * @param {SVGElement} svg The SVG element with the child annotation 170 | * @param {Object} child The SVG child to transform 171 | * @param {Object} viewport The page's viewport data 172 | * @return {SVGElement} A node that was transformed by this function 173 | */ 174 | export function transformChild(svg, child, viewport) { 175 | if (!viewport) { 176 | viewport = JSON.parse(svg.getAttribute('data-pdf-annotate-viewport')); 177 | } 178 | 179 | // If no type was provided for an annotation it will result in node being null. 180 | // Skip transforming if node doesn't exist. 181 | if (child) { 182 | child = transform(child, viewport); 183 | } 184 | 185 | return child; 186 | } 187 | 188 | export default { 189 | /** 190 | * Get the x/y translation to be used for transforming the annotations 191 | * based on the rotation of the viewport. 192 | */ 193 | getTranslation, 194 | 195 | /** 196 | * Append an SVG child for an annotation 197 | */ 198 | appendChild, 199 | 200 | /** 201 | * Transform an existing SVG child 202 | */ 203 | transformChild 204 | }; 205 | -------------------------------------------------------------------------------- /src/render/index.js: -------------------------------------------------------------------------------- 1 | import { appendChild, transformChild } from './appendChild'; 2 | 3 | /** 4 | * Render the response from PDFJSAnnotate.getStoreAdapter().getAnnotations to SVG 5 | * 6 | * @param {SVGElement} svg The SVG element to render the annotations to 7 | * @param {Object} viewport The page viewport data 8 | * @param {Object} data The response from PDFJSAnnotate.getStoreAdapter().getAnnotations 9 | * @return {Promise} Settled once rendering has completed 10 | * A settled Promise will be either: 11 | * - fulfilled: SVGElement 12 | * - rejected: Error 13 | */ 14 | export default function render(svg, viewport, data) { 15 | return new Promise((resolve, reject) => { 16 | // Reset the content of the SVG 17 | svg.setAttribute('data-pdf-annotate-container', true); 18 | svg.setAttribute('data-pdf-annotate-viewport', JSON.stringify(viewport)); 19 | svg.removeAttribute('data-pdf-annotate-document'); 20 | svg.removeAttribute('data-pdf-annotate-page'); 21 | 22 | // If there's no data nothing can be done 23 | if (!data) { 24 | svg.innerHTML = ''; 25 | return resolve(svg); 26 | } 27 | 28 | svg.setAttribute('data-pdf-annotate-document', data.documentId); 29 | svg.setAttribute('data-pdf-annotate-page', data.pageNumber); 30 | 31 | // Make sure annotations is an array 32 | if (!Array.isArray(data.annotations) || data.annotations.length === 0) { 33 | return resolve(svg); 34 | } 35 | 36 | // Append or transform annotation to svg 37 | data.annotations.forEach((a) => { 38 | let node = svg.querySelector('[data-pdf-annotate-id="' + a.uuid + '"]'); 39 | if (node) { 40 | transformChild(svg, node, viewport); 41 | } 42 | else { 43 | appendChild(svg, a, viewport); 44 | } 45 | }); 46 | 47 | resolve(svg); 48 | }); 49 | } 50 | -------------------------------------------------------------------------------- /src/render/renderArrow.js: -------------------------------------------------------------------------------- 1 | import setAttributes from '../utils/setAttributes'; 2 | import normalizeColor from '../utils/normalizeColor'; 3 | import { 4 | makePoint, makeVector, makeVectorFromPoints, 5 | magnitude, unitVector, crossProduct, 6 | addVector, multiplyVector, negateVector 7 | } from '../utils/mathUtils'; 8 | 9 | /** 10 | * Create SVGPathElement from an annotation definition. 11 | * This is used for anntations of type `drawing`. 12 | * 13 | * @param {Object} a The annotation definition 14 | * @return {SVGPathElement} The path to be rendered 15 | */ 16 | export default function renderArrow(a) { 17 | let arrow = document.createElementNS('http://www.w3.org/2000/svg', 'polygon'); 18 | 19 | if (a.lines.length === 2) { 20 | let p1 = a.lines[0]; 21 | let p2 = a.lines[a.lines.length - 1]; 22 | 23 | let arrowLength = 40; 24 | let pt0 = makePoint(p1[0], p1[1], 0); 25 | let pt1 = makePoint(p2[0], p2[1], 0); 26 | let x = makeVectorFromPoints(pt0, pt1); 27 | let unitX = unitVector(x); 28 | pt1 = addVector(pt0, multiplyVector(unitX, arrowLength)); 29 | x = makeVectorFromPoints(pt0, pt1); 30 | let unitZ = makeVector(0, 0, 1); 31 | let unitY = unitVector(crossProduct(unitX, unitZ)); 32 | let thickness = a.width || 10; 33 | 34 | let A = addVector(pt0, multiplyVector(unitY, thickness * 0.5)); 35 | let B = addVector(A, multiplyVector(unitX, magnitude(x) - thickness * 2.0)); 36 | let C = addVector(B, multiplyVector(unitY, thickness)); 37 | let D = pt1; 38 | let G = addVector(pt0, multiplyVector(negateVector(unitY), thickness * 0.5)); 39 | let F = addVector(G, multiplyVector(unitX, magnitude(x) - thickness * 2.0)); 40 | let E = addVector(F, multiplyVector(negateVector(unitY), thickness)); 41 | 42 | let points = '' + 43 | A.x + ',' + A.y + ' ' + 44 | B.x + ',' + B.y + ' ' + 45 | C.x + ',' + C.y + ' ' + 46 | D.x + ',' + D.y + ' ' + 47 | E.x + ',' + E.y + ' ' + 48 | F.x + ',' + F.y + ' ' + 49 | G.x + ',' + G.y; 50 | 51 | setAttributes(arrow, { 52 | points: points, 53 | stroke: normalizeColor(a.color || '#000'), 54 | fill: normalizeColor(a.color || '#000') 55 | }); 56 | } 57 | 58 | return arrow; 59 | } 60 | -------------------------------------------------------------------------------- /src/render/renderCircle.js: -------------------------------------------------------------------------------- 1 | import setAttributes from '../utils/setAttributes'; 2 | import normalizeColor from '../utils/normalizeColor'; 3 | 4 | /** 5 | * Create an SVGCircleElement from an annotation definition. 6 | * This is used for annotations of type `circle`. 7 | * 8 | * @param {Object} a The annotation definition 9 | * @return {SVGGElement|SVGCircleElement} A circle to be rendered 10 | */ 11 | export default function renderCircle(a) { 12 | let circle = createCircle(a); 13 | let color = normalizeColor(a.color || '#f00'); 14 | 15 | if (a.type === 'circle') { 16 | setAttributes(circle, { 17 | stroke: color, 18 | fill: 'none', 19 | 'stroke-width': 5 20 | }); 21 | } 22 | if (a.type === 'emptycircle') { 23 | setAttributes(circle, { 24 | stroke: color, 25 | fill: 'none', 26 | 'stroke-width': 2 27 | }); 28 | } 29 | 30 | if (a.type === 'fillcircle') { 31 | setAttributes(circle, { 32 | stroke: color, 33 | fill: color, 34 | 'stroke-width': 5 35 | }); 36 | } 37 | 38 | return circle; 39 | } 40 | 41 | function createCircle(a) { 42 | let circle = document.createElementNS('http://www.w3.org/2000/svg', 'circle'); 43 | setAttributes(circle, { 44 | cx: a.cx, 45 | cy: a.cy, 46 | r: a.r 47 | }); 48 | 49 | return circle; 50 | } 51 | -------------------------------------------------------------------------------- /src/render/renderLine.js: -------------------------------------------------------------------------------- 1 | import setAttributes from '../utils/setAttributes'; 2 | import normalizeColor from '../utils/normalizeColor'; 3 | 4 | /** 5 | * Create SVGLineElements from an annotation definition. 6 | * This is used for anntations of type `strikeout`. 7 | * 8 | * @param {Object} a The annotation definition 9 | * @return {SVGGElement} A group of all lines to be rendered 10 | */ 11 | export default function renderLine(a) { 12 | let group = document.createElementNS('http://www.w3.org/2000/svg', 'g'); 13 | setAttributes(group, { 14 | stroke: normalizeColor(a.color || '#f00'), 15 | strokeWidth: 1 16 | }); 17 | 18 | a.rectangles.forEach((r) => { 19 | let line = document.createElementNS('http://www.w3.org/2000/svg', 'line'); 20 | 21 | setAttributes(line, { 22 | x1: r.x, 23 | y1: r.y, 24 | x2: r.x + r.width, 25 | y2: r.y 26 | }); 27 | 28 | group.appendChild(line); 29 | }); 30 | 31 | return group; 32 | } 33 | -------------------------------------------------------------------------------- /src/render/renderPath.js: -------------------------------------------------------------------------------- 1 | import setAttributes from '../utils/setAttributes'; 2 | import normalizeColor from '../utils/normalizeColor'; 3 | 4 | /** 5 | * Create SVGPathElement from an annotation definition. 6 | * This is used for anntations of type `drawing`. 7 | * 8 | * @param {Object} a The annotation definition 9 | * @return {SVGPathElement} The path to be rendered 10 | */ 11 | export default function renderPath(a) { 12 | let d = []; 13 | let path = document.createElementNS('http://www.w3.org/2000/svg', 'path'); 14 | 15 | if (a.lines.length > 0) { 16 | d.push(`M${a.lines[0][0]} ${a.lines[0][1]}`); 17 | for (let i = 1, l = a.lines.length; i < l; i++) { 18 | let p1 = a.lines[i]; 19 | let p2 = a.lines[i + 1]; 20 | if (p2) { 21 | d.push(`L${p1[0]} ${p1[1]}`); 22 | } 23 | } 24 | } 25 | 26 | /* 27 | 28 | if(a.lines.length>2) { 29 | var p1 = a.lines[0]; 30 | var p2 = a.lines[a.lines.length-1]; 31 | 32 | var p3 = []; //arrow 33 | var p4 = []; 34 | var p0 = []; //arrow intersection 35 | 36 | if (p2) { 37 | var k = -(p2[0]-p1[0])/(p2[1]-p1[1]); 38 | 39 | var deltaX = 3; 40 | p0[0] = p1[0]+0.8*(p2[0]-p1[0]); 41 | p0[1] = p1[1]+0.8*(p2[1]-p1[1]); 42 | 43 | p3[0] = p0[0] + deltaX; 44 | p3[1] = p0[1] + k*deltaX; 45 | 46 | p4[0] = p0[0] - deltaX; 47 | p4[1] = p0[1] - k*deltaX; 48 | 49 | if(Math.abs(p2[1]-p1[1]) < 20) { 50 | 51 | p3[0] = p0[0] ; 52 | p3[1] = p0[1] + deltaX*1; 53 | 54 | p4[0] = p0[0] ; 55 | p4[1] = p0[1] - deltaX*1; 56 | 57 | } 58 | 59 | d.push(`M${p1[0]} ${p1[1]} ${p2[0]} ${p2[1]}`); 60 | //d.push(`M${p1[0]} ${p1[1]} ${p2[0]} ${p2[1]}`); 61 | d.push(`M${p2[0]} ${p2[1]} ${p3[0]} ${p3[1]}`); 62 | d.push(`M${p3[0]} ${p3[1]} ${p4[0]} ${p4[1]}`); 63 | d.push(`M${p4[0]} ${p4[1]} ${p2[0]} ${p2[1]}`); 64 | } 65 | } */ 66 | 67 | setAttributes(path, { 68 | d: `${d.join(' ')}`, // `${d.join(' ')}Z`, 69 | stroke: normalizeColor(a.color || '#000'), 70 | strokeWidth: a.width || 1, 71 | fill: 'none' 72 | }); 73 | 74 | return path; 75 | } 76 | -------------------------------------------------------------------------------- /src/render/renderPoint.js: -------------------------------------------------------------------------------- 1 | import setAttributes from '../utils/setAttributes'; 2 | 3 | const SIZE = 25; 4 | const D = 'M499.968 214.336q-113.832 0 -212.877 38.781t-157.356 104.625 -58.311 142.29q0 62.496 39.897 119.133t112.437 97.929l48.546 27.9 -15.066 53.568q-13.392 50.778 -39.06 95.976 84.816 -35.154 153.45 -95.418l23.994 -21.204 31.806 3.348q38.502 4.464 72.54 4.464 113.832 0 212.877 -38.781t157.356 -104.625 58.311 -142.29 -58.311 -142.29 -157.356 -104.625 -212.877 -38.781z'; 5 | 6 | /** 7 | * Create SVGElement from an annotation definition. 8 | * This is used for anntations of type `comment`. 9 | * 10 | * @param {Object} a The annotation definition 11 | * @return {SVGElement} A svg to be rendered 12 | */ 13 | export default function renderPoint(a) { 14 | let outerSVG = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); 15 | let innerSVG = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); 16 | let rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect'); 17 | let path = document.createElementNS('http://www.w3.org/2000/svg', 'path'); 18 | 19 | setAttributes(outerSVG, { 20 | width: SIZE, 21 | height: SIZE, 22 | x: a.x, 23 | y: a.y 24 | }); 25 | 26 | setAttributes(innerSVG, { 27 | width: SIZE, 28 | height: SIZE, 29 | x: 0, 30 | y: (SIZE * 0.05) * -1, 31 | viewBox: '0 0 1000 1000' 32 | }); 33 | 34 | setAttributes(rect, { 35 | width: SIZE, 36 | height: SIZE, 37 | stroke: '#000', 38 | fill: '#ff0' 39 | }); 40 | 41 | setAttributes(path, { 42 | d: D, 43 | strokeWidth: 50, 44 | stroke: '#000', 45 | fill: '#fff' 46 | }); 47 | 48 | innerSVG.appendChild(path); 49 | outerSVG.appendChild(rect); 50 | outerSVG.appendChild(innerSVG); 51 | 52 | return outerSVG; 53 | } 54 | -------------------------------------------------------------------------------- /src/render/renderRect.js: -------------------------------------------------------------------------------- 1 | import setAttributes from '../utils/setAttributes'; 2 | import normalizeColor from '../utils/normalizeColor'; 3 | 4 | /** 5 | * Create SVGRectElements from an annotation definition. 6 | * This is used for anntations of type `area` and `highlight`. 7 | * 8 | * @param {Object} a The annotation definition 9 | * @return {SVGGElement|SVGRectElement} A group of all rects to be rendered 10 | */ 11 | export default function renderRect(a) { 12 | if (a.type === 'highlight') { 13 | let group = document.createElementNS('http://www.w3.org/2000/svg', 'g'); 14 | setAttributes(group, { 15 | fill: normalizeColor(a.color || '#ff0'), 16 | fillOpacity: 0.2 17 | }); 18 | 19 | a.rectangles.forEach((r) => { 20 | group.appendChild(createRect(r)); 21 | }); 22 | 23 | return group; 24 | } 25 | else { 26 | let rect = createRect(a); 27 | setAttributes(rect, { 28 | stroke: normalizeColor(a.color || '#f00'), 29 | fill: 'none' 30 | }); 31 | 32 | return rect; 33 | } 34 | } 35 | 36 | function createRect(r) { 37 | let rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect'); 38 | 39 | setAttributes(rect, { 40 | x: r.x, 41 | y: r.y, 42 | width: r.width, 43 | height: r.height 44 | }); 45 | 46 | return rect; 47 | } 48 | -------------------------------------------------------------------------------- /src/render/renderText.js: -------------------------------------------------------------------------------- 1 | import setAttributes from '../utils/setAttributes'; 2 | import normalizeColor from '../utils/normalizeColor'; 3 | 4 | /** 5 | * Create SVGTextElement from an annotation definition. 6 | * This is used for anntations of type `textbox`. 7 | * 8 | * @param {Object} a The annotation definition 9 | * @return {SVGTextElement} A text to be rendered 10 | */ 11 | export default function renderText(a) { 12 | // Text should be rendered at 0 degrees relative to 13 | // document rotation 14 | let text = document.createElementNS('http://www.w3.org/2000/svg', 'text'); 15 | setAttributes(text, { 16 | x: a.x, 17 | y: a.y, 18 | fill: normalizeColor(a.color || '#000'), 19 | fontSize: a.size, 20 | transform: `rotate(${a.rotation})`, 21 | style: 'white-space: pre' 22 | }); 23 | text.innerHTML = a.content; 24 | 25 | let g = document.createElementNS('http://www.w3.org/2000/svg', 'g'); 26 | g.appendChild(text); 27 | 28 | return g; 29 | } 30 | -------------------------------------------------------------------------------- /src/utils/abstractFunction.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Throw an Error for an abstract function that hasn't been implemented. 3 | * 4 | * @param {String} name The name of the abstract function 5 | */ 6 | export default function abstractFunction(name) { 7 | throw new Error(name + ' is not implemented'); 8 | } 9 | -------------------------------------------------------------------------------- /src/utils/mathUtils.js: -------------------------------------------------------------------------------- 1 | // Transform point by matrix 2 | // 3 | export function applyTransform(p, m) { 4 | let xt = p[0] * m[0] + p[1] * m[2] + m[4]; 5 | let yt = p[0] * m[1] + p[1] * m[3] + m[5]; 6 | return [xt, yt]; 7 | }; 8 | 9 | // Transform point by matrix inverse 10 | // 11 | export function applyInverseTransform(p, m) { 12 | let d = m[0] * m[3] - m[1] * m[2]; 13 | let xt = (p[0] * m[3] - p[1] * m[2] + m[2] * m[5] - m[4] * m[3]) / d; 14 | let yt = (-p[0] * m[1] + p[1] * m[0] + m[4] * m[1] - m[5] * m[0]) / d; 15 | return [xt, yt]; 16 | }; 17 | 18 | // Concatenates two transformation matrices together and returns the result. 19 | export function transform(m1, m2) { 20 | return [ 21 | m1[0] * m2[0] + m1[2] * m2[1], 22 | m1[1] * m2[0] + m1[3] * m2[1], 23 | m1[0] * m2[2] + m1[2] * m2[3], 24 | m1[1] * m2[2] + m1[3] * m2[3], 25 | m1[0] * m2[4] + m1[2] * m2[5] + m1[4], 26 | m1[1] * m2[4] + m1[3] * m2[5] + m1[5] 27 | ]; 28 | }; 29 | 30 | export function translate(m, x, y) { 31 | return [ 32 | m[0], 33 | m[1], 34 | m[2], 35 | m[3], 36 | m[0] * x + m[2] * y + m[4], 37 | m[1] * x + m[3] * y + m[5] 38 | ]; 39 | }; 40 | 41 | export function rotate(m, angle) { 42 | angle = angle * Math.PI / 180; 43 | 44 | let cosValue = Math.cos(angle); 45 | let sinValue = Math.sin(angle); 46 | 47 | return [ 48 | m[0] * cosValue + m[2] * sinValue, 49 | m[1] * cosValue + m[3] * sinValue, 50 | m[0] * (-sinValue) + m[2] * cosValue, 51 | m[1] * (-sinValue) + m[3] * cosValue, 52 | m[4], 53 | m[5] 54 | ]; 55 | }; 56 | 57 | export function scale(m, x, y) { 58 | return [ 59 | m[0] * x, 60 | m[1] * x, 61 | m[2] * y, 62 | m[3] * y, 63 | m[4], 64 | m[5] 65 | ]; 66 | }; 67 | 68 | export function makePoint(x, y, z) { 69 | return { x: x, y: y, z: z }; 70 | }; 71 | 72 | export function makeVector(xcoord, ycoord, zcoord) { 73 | return { xcoord: xcoord, ycoord: ycoord, zcoord: zcoord }; 74 | }; 75 | 76 | export function makeVectorFromPoints(pt1, pt2) { 77 | let xcoord = pt2.x - pt1.x; 78 | let ycoord = pt2.y - pt1.y; 79 | let zcoord = pt2.z - pt1.z; 80 | return makeVector(xcoord, ycoord, zcoord); 81 | }; 82 | 83 | export function addVector(pt, v) { 84 | return makePoint(pt.x + v.xcoord, pt.y + v.ycoord, pt.z + v.zcoord); 85 | }; 86 | 87 | export function multiplyVector(v, scalar) { 88 | return makeVector(v.xcoord * scalar, v.ycoord * scalar, v.zcoord * scalar); 89 | }; 90 | 91 | export function magnitude(v) { 92 | return Math.sqrt( 93 | Math.pow(v.xcoord, 2) + Math.pow(v.ycoord, 2) + Math.pow(v.zcoord, 2) 94 | ); 95 | }; 96 | 97 | export function negateVector(v) { 98 | return multiplyVector(v, -1); 99 | }; 100 | 101 | export function unitVector(v) { 102 | let mag = magnitude(v); 103 | let xcoord = v.xcoord / mag; 104 | let ycoord = v.ycoord / mag; 105 | let zcoord = v.zcoord / mag; 106 | return makeVector(xcoord, ycoord, zcoord); 107 | }; 108 | 109 | export function crossProduct(u, v) { 110 | // 111 | // u X v = < u2*v3 - u3*v2, 112 | // u3*v1 - u1*v3, 113 | // u1*v2 - u2*v1 > 114 | let xcoord = u.ycoord * v.zcoord - u.zcoord * v.ycoord; 115 | let ycoord = u.zcoord * v.xcoord - u.xcoord * v.zcoord; 116 | let zcoord = u.xcoord * v.ycoord - u.ycoord * v.xcoord; 117 | return makeVector(xcoord, ycoord, zcoord); 118 | }; 119 | -------------------------------------------------------------------------------- /src/utils/normalizeColor.js: -------------------------------------------------------------------------------- 1 | const REGEX_HASHLESS_HEX = /^([a-f0-9]{6}|[a-f0-9]{3})$/i; 2 | 3 | /** 4 | * Normalize a color value 5 | * 6 | * @param {String} color The color to normalize 7 | * @return {String} 8 | */ 9 | export default function normalizeColor(color) { 10 | if (REGEX_HASHLESS_HEX.test(color)) { 11 | color = `#${color}`; 12 | } 13 | return color; 14 | } 15 | -------------------------------------------------------------------------------- /src/utils/setAttributes.js: -------------------------------------------------------------------------------- 1 | const UPPER_REGEX = /[A-Z]/g; 2 | 3 | // Don't convert these attributes from camelCase to hyphenated-attributes 4 | const BLACKLIST = [ 5 | 'viewBox' 6 | ]; 7 | 8 | let keyCase = (key) => { 9 | if (BLACKLIST.indexOf(key) === -1) { 10 | key = key.replace(UPPER_REGEX, match => '-' + match.toLowerCase()); 11 | } 12 | return key; 13 | }; 14 | 15 | /** 16 | * Set attributes for a node from a map 17 | * 18 | * @param {Node} node The node to set attributes on 19 | * @param {Object} attributes The map of key/value pairs to use for attributes 20 | */ 21 | export default function setAttributes(node, attributes) { 22 | Object.keys(attributes).forEach((key) => { 23 | node.setAttribute(keyCase(key), attributes[key]); 24 | }); 25 | } 26 | -------------------------------------------------------------------------------- /src/utils/uuid.js: -------------------------------------------------------------------------------- 1 | const REGEXP = /[xy]/g; 2 | const PATTERN = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'; 3 | 4 | function replacement(c) { 5 | let r = Math.random() * 16 | 0; 6 | let v = c === 'x' ? r : (r & 0x3 | 0x8); 7 | return v.toString(16); 8 | } 9 | 10 | /** 11 | * Generate a univierally unique identifier 12 | * 13 | * @return {String} A UUID 14 | */ 15 | export default function uuid() { 16 | return PATTERN.replace(REGEXP, replacement); 17 | } 18 | --------------------------------------------------------------------------------