squiggle with signature-pad and smoother lines + blossom #258
20
package-lock.json
generated
20
package-lock.json
generated
@ -48,7 +48,6 @@
|
|||||||
"react-toastify": "10.0.4",
|
"react-toastify": "10.0.4",
|
||||||
"redux": "5.0.1",
|
"redux": "5.0.1",
|
||||||
"signature_pad": "^5.0.4",
|
"signature_pad": "^5.0.4",
|
||||||
"svgo": "^3.3.2",
|
|
||||||
"tseep": "1.2.1"
|
"tseep": "1.2.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@ -2215,6 +2214,7 @@
|
|||||||
"version": "0.2.0",
|
"version": "0.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz",
|
||||||
"integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==",
|
"integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==",
|
||||||
|
"dev": true,
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=10.13.0"
|
"node": ">=10.13.0"
|
||||||
@ -2936,6 +2936,7 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
|
||||||
"integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==",
|
"integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==",
|
||||||
|
"dev": true,
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
"node_modules/brace-expansion": {
|
"node_modules/brace-expansion": {
|
||||||
@ -3635,6 +3636,7 @@
|
|||||||
"version": "5.1.0",
|
"version": "5.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz",
|
||||||
"integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==",
|
"integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==",
|
||||||
|
"dev": true,
|
||||||
"license": "BSD-2-Clause",
|
"license": "BSD-2-Clause",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"boolbase": "^1.0.0",
|
"boolbase": "^1.0.0",
|
||||||
@ -3651,6 +3653,7 @@
|
|||||||
"version": "2.3.1",
|
"version": "2.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz",
|
||||||
"integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==",
|
"integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==",
|
||||||
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"mdn-data": "2.0.30",
|
"mdn-data": "2.0.30",
|
||||||
@ -3664,6 +3667,7 @@
|
|||||||
"version": "6.1.0",
|
"version": "6.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz",
|
||||||
"integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==",
|
"integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==",
|
||||||
|
"dev": true,
|
||||||
"license": "BSD-2-Clause",
|
"license": "BSD-2-Clause",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 6"
|
"node": ">= 6"
|
||||||
@ -3676,6 +3680,7 @@
|
|||||||
"version": "5.0.5",
|
"version": "5.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/csso/-/csso-5.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/csso/-/csso-5.0.5.tgz",
|
||||||
"integrity": "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==",
|
"integrity": "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==",
|
||||||
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"css-tree": "~2.2.0"
|
"css-tree": "~2.2.0"
|
||||||
@ -3689,6 +3694,7 @@
|
|||||||
"version": "2.2.1",
|
"version": "2.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.2.1.tgz",
|
||||||
"integrity": "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==",
|
"integrity": "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==",
|
||||||
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"mdn-data": "2.0.28",
|
"mdn-data": "2.0.28",
|
||||||
@ -3703,6 +3709,7 @@
|
|||||||
"version": "2.0.28",
|
"version": "2.0.28",
|
||||||
"resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.28.tgz",
|
"resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.28.tgz",
|
||||||
"integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==",
|
"integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==",
|
||||||
|
"dev": true,
|
||||||
"license": "CC0-1.0"
|
"license": "CC0-1.0"
|
||||||
},
|
},
|
||||||
"node_modules/csstype": {
|
"node_modules/csstype": {
|
||||||
@ -3950,6 +3957,7 @@
|
|||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz",
|
||||||
"integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==",
|
"integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==",
|
||||||
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"domelementtype": "^2.3.0",
|
"domelementtype": "^2.3.0",
|
||||||
@ -3977,6 +3985,7 @@
|
|||||||
"version": "2.3.0",
|
"version": "2.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz",
|
||||||
"integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==",
|
"integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==",
|
||||||
|
"dev": true,
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"type": "github",
|
"type": "github",
|
||||||
@ -3989,6 +3998,7 @@
|
|||||||
"version": "5.0.3",
|
"version": "5.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz",
|
||||||
"integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==",
|
"integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==",
|
||||||
|
"dev": true,
|
||||||
"license": "BSD-2-Clause",
|
"license": "BSD-2-Clause",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"domelementtype": "^2.3.0"
|
"domelementtype": "^2.3.0"
|
||||||
@ -4004,6 +4014,7 @@
|
|||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz",
|
||||||
"integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==",
|
"integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==",
|
||||||
|
"dev": true,
|
||||||
"license": "BSD-2-Clause",
|
"license": "BSD-2-Clause",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"dom-serializer": "^2.0.0",
|
"dom-serializer": "^2.0.0",
|
||||||
@ -4053,6 +4064,7 @@
|
|||||||
"version": "4.5.0",
|
"version": "4.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
|
||||||
"integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
|
"integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
|
||||||
|
"dev": true,
|
||||||
"license": "BSD-2-Clause",
|
"license": "BSD-2-Clause",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.12"
|
"node": ">=0.12"
|
||||||
@ -5980,6 +5992,7 @@
|
|||||||
"version": "2.0.30",
|
"version": "2.0.30",
|
||||||
"resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz",
|
"resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz",
|
||||||
"integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==",
|
"integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==",
|
||||||
|
"dev": true,
|
||||||
"license": "CC0-1.0"
|
"license": "CC0-1.0"
|
||||||
},
|
},
|
||||||
"node_modules/merge-stream": {
|
"node_modules/merge-stream": {
|
||||||
@ -6561,6 +6574,7 @@
|
|||||||
"version": "2.1.1",
|
"version": "2.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz",
|
||||||
"integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==",
|
"integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==",
|
||||||
|
"dev": true,
|
||||||
"license": "BSD-2-Clause",
|
"license": "BSD-2-Clause",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"boolbase": "^1.0.0"
|
"boolbase": "^1.0.0"
|
||||||
@ -6920,6 +6934,7 @@
|
|||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz",
|
||||||
"integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==",
|
"integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==",
|
||||||
|
"dev": true,
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
"node_modules/picomatch": {
|
"node_modules/picomatch": {
|
||||||
@ -7978,6 +7993,7 @@
|
|||||||
"version": "1.2.1",
|
"version": "1.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
|
||||||
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
|
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
|
||||||
|
"dev": true,
|
||||||
"license": "BSD-3-Clause",
|
"license": "BSD-3-Clause",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
@ -8204,6 +8220,7 @@
|
|||||||
"version": "3.3.2",
|
"version": "3.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/svgo/-/svgo-3.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/svgo/-/svgo-3.3.2.tgz",
|
||||||
"integrity": "sha512-OoohrmuUlBs8B8o6MB2Aevn+pRIH9zDALSR+6hhqVfa6fRwG/Qw9VUMSMW9VNg2CFc/MTIfabtdOVl9ODIJjpw==",
|
"integrity": "sha512-OoohrmuUlBs8B8o6MB2Aevn+pRIH9zDALSR+6hhqVfa6fRwG/Qw9VUMSMW9VNg2CFc/MTIfabtdOVl9ODIJjpw==",
|
||||||
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@trysound/sax": "0.2.0",
|
"@trysound/sax": "0.2.0",
|
||||||
@ -8229,6 +8246,7 @@
|
|||||||
"version": "7.2.0",
|
"version": "7.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
|
||||||
"integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",
|
"integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",
|
||||||
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 10"
|
"node": ">= 10"
|
||||||
|
@ -58,7 +58,6 @@
|
|||||||
"react-toastify": "10.0.4",
|
"react-toastify": "10.0.4",
|
||||||
"redux": "5.0.1",
|
"redux": "5.0.1",
|
||||||
"signature_pad": "^5.0.4",
|
"signature_pad": "^5.0.4",
|
||||||
"svgo": "^3.3.2",
|
|
||||||
"tseep": "1.2.1"
|
"tseep": "1.2.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
@ -6,6 +6,7 @@ import { useEffect, useRef } from 'react'
|
|||||||
import pdfViewStyles from './style.module.scss'
|
import pdfViewStyles from './style.module.scss'
|
||||||
import { FONT_SIZE, FONT_TYPE, inPx } from '../../utils/pdf.ts'
|
import { FONT_SIZE, FONT_TYPE, inPx } from '../../utils/pdf.ts'
|
||||||
import { useScale } from '../../hooks/useScale.tsx'
|
import { useScale } from '../../hooks/useScale.tsx'
|
||||||
|
import { MARK_TYPE_CONFIG } from '../getMarkComponents.tsx'
|
||||||
interface PdfPageProps {
|
interface PdfPageProps {
|
||||||
fileName: string
|
fileName: string
|
||||||
pageIndex: number
|
pageIndex: number
|
||||||
@ -60,6 +61,7 @@ const PdfPageItem = ({
|
|||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
{otherUserMarks.map((m, i) => {
|
{otherUserMarks.map((m, i) => {
|
||||||
|
const { render: MarkRenderComponent } = MARK_TYPE_CONFIG[m.type] || {}
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={i}
|
key={i}
|
||||||
@ -73,7 +75,9 @@ const PdfPageItem = ({
|
|||||||
fontSize: inPx(from(page.width, FONT_SIZE))
|
fontSize: inPx(from(page.width, FONT_SIZE))
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{m.value}
|
{typeof MarkRenderComponent !== 'undefined' && (
|
||||||
|
<MarkRenderComponent value={m.value} mark={m} />
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
|
@ -1,8 +1,17 @@
|
|||||||
|
import { toast } from 'react-toastify'
|
||||||
import { MarkType } from '../types/drawing'
|
import { MarkType } from '../types/drawing'
|
||||||
import { MarkConfigs } from '../types/mark'
|
import { MarkConfigs } from '../types/mark'
|
||||||
|
import {
|
||||||
|
decryptArrayBuffer,
|
||||||
|
encryptArrayBuffer,
|
||||||
|
getHash,
|
||||||
|
isOnline,
|
||||||
|
uploadToFileStorage
|
||||||
|
} from '../utils'
|
||||||
import { MarkInputSignature } from './MarkInputs/Signature'
|
import { MarkInputSignature } from './MarkInputs/Signature'
|
||||||
import { MarkInputText } from './MarkInputs/Text'
|
import { MarkInputText } from './MarkInputs/Text'
|
||||||
import { MarkRenderSignature } from './MarkRender/Signature'
|
import { MarkRenderSignature } from './MarkRender/Signature'
|
||||||
|
import axios from 'axios'
|
||||||
|
|
||||||
export const MARK_TYPE_CONFIG: MarkConfigs = {
|
export const MARK_TYPE_CONFIG: MarkConfigs = {
|
||||||
[MarkType.TEXT]: {
|
[MarkType.TEXT]: {
|
||||||
@ -11,6 +20,73 @@ export const MARK_TYPE_CONFIG: MarkConfigs = {
|
|||||||
},
|
},
|
||||||
[MarkType.SIGNATURE]: {
|
[MarkType.SIGNATURE]: {
|
||||||
input: MarkInputSignature,
|
input: MarkInputSignature,
|
||||||
render: MarkRenderSignature
|
render: MarkRenderSignature,
|
||||||
|
encryptAndUpload: async (value, encryptionKey) => {
|
||||||
|
// Value is the stringified signature object
|
||||||
|
// Encode it as text to the arrayBuffer
|
||||||
|
const encoder = new TextEncoder()
|
||||||
|
const uint8Array = encoder.encode(value)
|
||||||
|
const hash = await getHash(uint8Array)
|
||||||
|
|
||||||
|
if (!hash) {
|
||||||
|
throw new Error("Can't get file hash.")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!encryptionKey) {
|
||||||
|
throw new Error('Signature requires an encryption key')
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encrypt the file contents with the same encryption key from the create signature
|
||||||
|
const encryptedArrayBuffer = await encryptArrayBuffer(
|
||||||
|
uint8Array,
|
||||||
|
encryptionKey
|
||||||
|
)
|
||||||
|
|
||||||
|
// Create the encrypted json file from array buffer and hash
|
||||||
|
const file = new File([encryptedArrayBuffer], `${hash}.json`)
|
||||||
|
|
||||||
|
if (await isOnline()) {
|
||||||
|
try {
|
||||||
|
const url = await uploadToFileStorage(file)
|
||||||
|
toast.success('files.zip uploaded to file storage')
|
||||||
|
return url
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof Error) {
|
||||||
|
toast.error(error.message || 'Error occurred in uploading file')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Handle offline?
|
||||||
|
}
|
||||||
|
|
||||||
|
return value
|
||||||
|
},
|
||||||
|
fetchAndDecrypt: async (value, encryptionKey) => {
|
||||||
|
if (!encryptionKey) {
|
||||||
|
throw new Error('Signature requires an encryption key')
|
||||||
|
}
|
||||||
|
|
||||||
|
const encryptedArrayBuffer = await axios.get(value, {
|
||||||
|
responseType: 'arraybuffer'
|
||||||
|
})
|
||||||
|
|
||||||
|
const arrayBuffer = await decryptArrayBuffer(
|
||||||
|
encryptedArrayBuffer.data,
|
||||||
|
encryptionKey
|
||||||
|
).catch((err) => {
|
||||||
|
console.log('err in decryption:>> ', err)
|
||||||
|
return null
|
||||||
|
})
|
||||||
|
|
||||||
|
if (arrayBuffer) {
|
||||||
|
// decode json
|
||||||
|
const decoder = new TextDecoder()
|
||||||
|
const value = decoder.decode(arrayBuffer)
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle offline?
|
||||||
|
return value
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,7 @@ import { Event } from 'nostr-tools'
|
|||||||
import store from '../store/store'
|
import store from '../store/store'
|
||||||
import { NostrController } from '../controllers'
|
import { NostrController } from '../controllers'
|
||||||
import { MetaParseError } from '../types/errors/MetaParseError'
|
import { MetaParseError } from '../types/errors/MetaParseError'
|
||||||
|
import { MARK_TYPE_CONFIG } from '../components/getMarkComponents'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Flattened interface that combines properties `Meta`, `CreateSignatureEventContent`,
|
* Flattened interface that combines properties `Meta`, `CreateSignatureEventContent`,
|
||||||
@ -142,6 +143,7 @@ export const useSigitMeta = (meta: Meta): FlatMeta => {
|
|||||||
setMarkConfig(markConfig)
|
setMarkConfig(markConfig)
|
||||||
setZipUrl(zipUrl)
|
setZipUrl(zipUrl)
|
||||||
|
|
||||||
|
let encryptionKey: string | null = null
|
||||||
if (meta.keys) {
|
if (meta.keys) {
|
||||||
const { sender, keys } = meta.keys
|
const { sender, keys } = meta.keys
|
||||||
// Retrieve the user's public key from the state
|
// Retrieve the user's public key from the state
|
||||||
@ -162,6 +164,7 @@ export const useSigitMeta = (meta: Meta): FlatMeta => {
|
|||||||
return null
|
return null
|
||||||
})
|
})
|
||||||
|
|
||||||
|
encryptionKey = decrypted
|
||||||
setEncryptionKey(decrypted)
|
setEncryptionKey(decrypted)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -206,13 +209,40 @@ export const useSigitMeta = (meta: Meta): FlatMeta => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
parsedSignatureEventsMap.forEach((event, npub) => {
|
for (const [npub, event] of parsedSignatureEventsMap) {
|
||||||
const isValidSignature = verifyEvent(event)
|
const isValidSignature = verifyEvent(event)
|
||||||
if (isValidSignature) {
|
if (isValidSignature) {
|
||||||
// get the signature of prev signer from the content of current signers signedEvent
|
// get the signature of prev signer from the content of current signers signedEvent
|
||||||
const prevSignersSig = getPrevSignerSig(npub)
|
const prevSignersSig = getPrevSignerSig(npub)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const obj: SignedEventContent = JSON.parse(event.content)
|
const obj: SignedEventContent = JSON.parse(event.content)
|
||||||
|
|
||||||
|
// Signature object can include values that need to be fetched and decrypted
|
||||||
|
for (let i = 0; i < obj.marks.length; i++) {
|
||||||
|
const m = obj.marks[i]
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { fetchAndDecrypt } = MARK_TYPE_CONFIG[m.type] || {}
|
||||||
|
if (
|
||||||
|
typeof fetchAndDecrypt === 'function' &&
|
||||||
|
m.value &&
|
||||||
|
encryptionKey
|
||||||
|
) {
|
||||||
|
const decrypted = await fetchAndDecrypt(
|
||||||
|
m.value,
|
||||||
|
encryptionKey
|
||||||
|
)
|
||||||
|
obj.marks[i].value = decrypted
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(
|
||||||
|
`Error during mark fetchAndDecrypt phase`,
|
||||||
|
error
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
parsedSignatureEventsMap.set(npub, {
|
parsedSignatureEventsMap.set(npub, {
|
||||||
...event,
|
...event,
|
||||||
parsedContent: obj
|
parsedContent: obj
|
||||||
@ -228,7 +258,7 @@ export const useSigitMeta = (meta: Meta): FlatMeta => {
|
|||||||
signerStatusMap.set(npub as `npub1${string}`, SignStatus.Invalid)
|
signerStatusMap.set(npub as `npub1${string}`, SignStatus.Invalid)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
|
|
||||||
signers
|
signers
|
||||||
.filter((s) => !parsedSignatureEventsMap.has(s))
|
.filter((s) => !parsedSignatureEventsMap.has(s))
|
||||||
|
@ -33,7 +33,8 @@ import {
|
|||||||
signEventForMetaFile,
|
signEventForMetaFile,
|
||||||
updateUsersAppData,
|
updateUsersAppData,
|
||||||
findOtherUserMarks,
|
findOtherUserMarks,
|
||||||
timeout
|
timeout,
|
||||||
|
processMarks
|
||||||
} from '../../utils'
|
} from '../../utils'
|
||||||
import { Container } from '../../components/Container'
|
import { Container } from '../../components/Container'
|
||||||
import { DisplayMeta } from './internal/displayMeta'
|
import { DisplayMeta } from './internal/displayMeta'
|
||||||
@ -54,6 +55,7 @@ import {
|
|||||||
} from '../../utils/file.ts'
|
} from '../../utils/file.ts'
|
||||||
import { ARRAY_BUFFER, DEFLATE } from '../../utils/const.ts'
|
import { ARRAY_BUFFER, DEFLATE } from '../../utils/const.ts'
|
||||||
import { generateTimestamp } from '../../utils/opentimestamps.ts'
|
import { generateTimestamp } from '../../utils/opentimestamps.ts'
|
||||||
|
import { MARK_TYPE_CONFIG } from '../../components/getMarkComponents.tsx'
|
||||||
|
|
||||||
enum SignedStatus {
|
enum SignedStatus {
|
||||||
Fully_Signed,
|
Fully_Signed,
|
||||||
@ -237,6 +239,43 @@ export const SignPage = () => {
|
|||||||
const signedMarks = extractMarksFromSignedMeta(meta)
|
const signedMarks = extractMarksFromSignedMeta(meta)
|
||||||
const currentUserMarks = getCurrentUserMarks(metaMarks, signedMarks)
|
const currentUserMarks = getCurrentUserMarks(metaMarks, signedMarks)
|
||||||
const otherUserMarks = findOtherUserMarks(signedMarks, usersPubkey!)
|
const otherUserMarks = findOtherUserMarks(signedMarks, usersPubkey!)
|
||||||
|
|
||||||
|
if (meta.keys) {
|
||||||
|
for (let i = 0; i < otherUserMarks.length; i++) {
|
||||||
|
const m = otherUserMarks[i]
|
||||||
|
const { sender, keys } = meta.keys
|
||||||
|
const usersNpub = hexToNpub(usersPubkey)
|
||||||
|
if (usersNpub in keys) {
|
||||||
|
const encryptionKey = await nostrController
|
||||||
|
.nip04Decrypt(sender, keys[usersNpub])
|
||||||
|
.catch((err) => {
|
||||||
|
console.log(
|
||||||
|
'An error occurred in decrypting encryption key',
|
||||||
|
err
|
||||||
|
)
|
||||||
|
return null
|
||||||
|
})
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { fetchAndDecrypt } = MARK_TYPE_CONFIG[m.type] || {}
|
||||||
|
if (
|
||||||
|
typeof fetchAndDecrypt === 'function' &&
|
||||||
|
m.value &&
|
||||||
|
encryptionKey
|
||||||
|
) {
|
||||||
|
const decrypted = await fetchAndDecrypt(
|
||||||
|
m.value,
|
||||||
|
encryptionKey
|
||||||
|
)
|
||||||
|
otherUserMarks[i].value = decrypted
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Error during mark fetchAndDecrypt phase`, error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
setOtherUserMarks(otherUserMarks)
|
setOtherUserMarks(otherUserMarks)
|
||||||
setCurrentUserMarks(currentUserMarks)
|
setCurrentUserMarks(currentUserMarks)
|
||||||
setIsMarksCompleted(isCurrentUserMarksComplete(currentUserMarks))
|
setIsMarksCompleted(isCurrentUserMarksComplete(currentUserMarks))
|
||||||
@ -248,6 +287,7 @@ export const SignPage = () => {
|
|||||||
if (meta) {
|
if (meta) {
|
||||||
handleUpdatedMeta(meta)
|
handleUpdatedMeta(meta)
|
||||||
}
|
}
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [meta, usersPubkey])
|
}, [meta, usersPubkey])
|
||||||
|
|
||||||
const handleDownload = async () => {
|
const handleDownload = async () => {
|
||||||
@ -552,8 +592,8 @@ export const SignPage = () => {
|
|||||||
setIsLoading(true)
|
setIsLoading(true)
|
||||||
|
|
||||||
setLoadingSpinnerDesc('Signing nostr event')
|
setLoadingSpinnerDesc('Signing nostr event')
|
||||||
|
const usersNpub = hexToNpub(usersPubkey!)
|
||||||
const prevSig = getPrevSignersSig(hexToNpub(usersPubkey!))
|
const prevSig = getPrevSignersSig(usersNpub)
|
||||||
if (!prevSig) {
|
if (!prevSig) {
|
||||||
setIsLoading(false)
|
setIsLoading(false)
|
||||||
toast.error('Previous signature is invalid')
|
toast.error('Previous signature is invalid')
|
||||||
@ -562,7 +602,26 @@ export const SignPage = () => {
|
|||||||
|
|
||||||
const marks = getSignerMarksForMeta() || []
|
const marks = getSignerMarksForMeta() || []
|
||||||
|
|
||||||
const signedEvent = await signEventForMeta({ prevSig, marks })
|
let encryptionKey: string | undefined
|
||||||
|
if (meta.keys) {
|
||||||
|
const { sender, keys } = meta.keys
|
||||||
|
encryptionKey = await nostrController
|
||||||
|
.nip04Decrypt(sender, keys[usersNpub])
|
||||||
|
.catch((err) => {
|
||||||
|
// Log and display an error message if decryption fails
|
||||||
|
console.log('An error occurred in decrypting encryption key', err)
|
||||||
|
toast.error('An error occurred in decrypting encryption key')
|
||||||
|
return undefined
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const processedMarks = await processMarks(marks, encryptionKey)
|
||||||
|
|
||||||
|
const signedEvent = await signEventForMeta({
|
||||||
|
prevSig,
|
||||||
|
marks: processedMarks
|
||||||
|
})
|
||||||
|
|
||||||
if (!signedEvent) return
|
if (!signedEvent) return
|
||||||
|
|
||||||
const updatedMeta = updateMetaSignatures(meta, signedEvent)
|
const updatedMeta = updateMetaSignatures(meta, signedEvent)
|
||||||
|
@ -367,82 +367,77 @@ export const VerifyPage = () => {
|
|||||||
setIsLoading(true)
|
setIsLoading(true)
|
||||||
|
|
||||||
setLoadingSpinnerDesc('Fetching file from file server')
|
setLoadingSpinnerDesc('Fetching file from file server')
|
||||||
axios
|
try {
|
||||||
.get(zipUrl, {
|
const res = await axios.get(zipUrl, {
|
||||||
responseType: 'arraybuffer'
|
responseType: 'arraybuffer'
|
||||||
})
|
})
|
||||||
.then(async (res) => {
|
|
||||||
const fileName = zipUrl.split('/').pop()
|
|
||||||
const file = new File([res.data], fileName!)
|
|
||||||
|
|
||||||
const encryptedArrayBuffer = await file.arrayBuffer()
|
const fileName = zipUrl.split('/').pop()
|
||||||
const arrayBuffer = await decryptArrayBuffer(
|
const file = new File([res.data], fileName!)
|
||||||
encryptedArrayBuffer,
|
|
||||||
encryptionKey
|
const encryptedArrayBuffer = await file.arrayBuffer()
|
||||||
).catch((err) => {
|
const arrayBuffer = await decryptArrayBuffer(
|
||||||
console.log('err in decryption:>> ', err)
|
encryptedArrayBuffer,
|
||||||
|
encryptionKey
|
||||||
|
).catch((err) => {
|
||||||
|
console.log('err in decryption:>> ', err)
|
||||||
|
toast.error(err.message || 'An error occurred in decrypting file.')
|
||||||
|
return null
|
||||||
|
})
|
||||||
|
|
||||||
|
if (arrayBuffer) {
|
||||||
|
const zip = await JSZip.loadAsync(arrayBuffer).catch((err) => {
|
||||||
|
console.log('err in loading zip file :>> ', err)
|
||||||
toast.error(
|
toast.error(
|
||||||
err.message || 'An error occurred in decrypting file.'
|
err.message || 'An error occurred in loading zip file.'
|
||||||
)
|
)
|
||||||
return null
|
return null
|
||||||
})
|
})
|
||||||
|
|
||||||
if (arrayBuffer) {
|
if (!zip) return
|
||||||
const zip = await JSZip.loadAsync(arrayBuffer).catch((err) => {
|
|
||||||
console.log('err in loading zip file :>> ', err)
|
|
||||||
toast.error(
|
|
||||||
err.message || 'An error occurred in loading zip file.'
|
|
||||||
)
|
|
||||||
return null
|
|
||||||
})
|
|
||||||
|
|
||||||
if (!zip) return
|
const files: { [fileName: string]: SigitFile } = {}
|
||||||
|
const fileHashes: { [key: string]: string | null } = {}
|
||||||
|
const fileNames = Object.values(zip.files).map(
|
||||||
|
(entry) => entry.name
|
||||||
|
)
|
||||||
|
|
||||||
const files: { [fileName: string]: SigitFile } = {}
|
// generate hashes for all entries in files folder of zipArchive
|
||||||
const fileHashes: { [key: string]: string | null } = {}
|
// these hashes can be used to verify the originality of files
|
||||||
const fileNames = Object.values(zip.files).map(
|
for (const fileName of fileNames) {
|
||||||
(entry) => entry.name
|
const arrayBuffer = await readContentOfZipEntry(
|
||||||
|
zip,
|
||||||
|
fileName,
|
||||||
|
'arraybuffer'
|
||||||
)
|
)
|
||||||
|
|
||||||
// generate hashes for all entries in files folder of zipArchive
|
if (arrayBuffer) {
|
||||||
// these hashes can be used to verify the originality of files
|
files[fileName] = await convertToSigitFile(
|
||||||
for (const fileName of fileNames) {
|
arrayBuffer,
|
||||||
const arrayBuffer = await readContentOfZipEntry(
|
fileName!
|
||||||
zip,
|
|
||||||
fileName,
|
|
||||||
'arraybuffer'
|
|
||||||
)
|
)
|
||||||
|
const hash = await getHash(arrayBuffer)
|
||||||
|
|
||||||
if (arrayBuffer) {
|
if (hash) {
|
||||||
files[fileName] = await convertToSigitFile(
|
fileHashes[fileName.replace(/^files\//, '')] = hash
|
||||||
arrayBuffer,
|
|
||||||
fileName!
|
|
||||||
)
|
|
||||||
const hash = await getHash(arrayBuffer)
|
|
||||||
|
|
||||||
if (hash) {
|
|
||||||
fileHashes[fileName.replace(/^files\//, '')] = hash
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
fileHashes[fileName.replace(/^files\//, '')] = null
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
fileHashes[fileName.replace(/^files\//, '')] = null
|
||||||
}
|
}
|
||||||
|
|
||||||
setCurrentFileHashes(fileHashes)
|
|
||||||
setFiles(files)
|
|
||||||
|
|
||||||
setIsLoading(false)
|
|
||||||
}
|
}
|
||||||
})
|
|
||||||
.catch((err) => {
|
setCurrentFileHashes(fileHashes)
|
||||||
console.error(`error occurred in getting file from ${zipUrl}`, err)
|
setFiles(files)
|
||||||
toast.error(
|
|
||||||
err.message || `error occurred in getting file from ${zipUrl}`
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
setIsLoading(false)
|
setIsLoading(false)
|
||||||
})
|
}
|
||||||
|
} catch (err) {
|
||||||
|
const message = `error occurred in getting file from ${zipUrl}`
|
||||||
|
console.error(message, err)
|
||||||
|
if (err instanceof Error) toast.error(err.message)
|
||||||
|
else toast.error(message)
|
||||||
|
} finally {
|
||||||
|
setIsLoading(false)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
processSigit()
|
processSigit()
|
||||||
|
@ -43,7 +43,9 @@ export interface MarkRenderProps {
|
|||||||
|
|
||||||
export interface MarkConfig {
|
export interface MarkConfig {
|
||||||
input: React.FC<MarkInputProps>
|
input: React.FC<MarkInputProps>
|
||||||
render?: React.FC<MarkRenderProps>
|
render: React.FC<MarkRenderProps>
|
||||||
|
encryptAndUpload?: (value: string, key?: string) => Promise<string>
|
||||||
|
fetchAndDecrypt?: (value: string, key?: string) => Promise<string>
|
||||||
}
|
}
|
||||||
|
|
||||||
export type MarkConfigs = {
|
export type MarkConfigs = {
|
||||||
|
@ -1,7 +1,11 @@
|
|||||||
|
import { MARK_TYPE_CONFIG } from '../components/getMarkComponents.tsx'
|
||||||
|
import { NostrController } from '../controllers/NostrController.ts'
|
||||||
|
import store from '../store/store.ts'
|
||||||
import { Meta } from '../types'
|
import { Meta } from '../types'
|
||||||
import { PdfPage } from '../types/drawing.ts'
|
import { PdfPage } from '../types/drawing.ts'
|
||||||
import { MOST_COMMON_MEDIA_TYPES } from './const.ts'
|
import { MOST_COMMON_MEDIA_TYPES } from './const.ts'
|
||||||
import { extractMarksFromSignedMeta } from './mark.ts'
|
import { extractMarksFromSignedMeta } from './mark.ts'
|
||||||
|
import { hexToNpub } from './nostr.ts'
|
||||||
import {
|
import {
|
||||||
addMarks,
|
addMarks,
|
||||||
groupMarksByFileNamePage,
|
groupMarksByFileNamePage,
|
||||||
@ -21,7 +25,45 @@ export const getZipWithFiles = async (
|
|||||||
for (const [fileName, file] of Object.entries(files)) {
|
for (const [fileName, file] of Object.entries(files)) {
|
||||||
// Handle PDF Files, add marks
|
// Handle PDF Files, add marks
|
||||||
if (file.isPdf && fileName in marksByFileNamePage) {
|
if (file.isPdf && fileName in marksByFileNamePage) {
|
||||||
const blob = await addMarks(file, marksByFileNamePage[fileName])
|
const marksToAdd = marksByFileNamePage[fileName]
|
||||||
|
if (meta.keys) {
|
||||||
|
for (let i = 0; i < marks.length; i++) {
|
||||||
|
const m = marks[i]
|
||||||
|
const { sender, keys } = meta.keys
|
||||||
|
const usersPubkey = store.getState().auth.usersPubkey!
|
||||||
|
const usersNpub = hexToNpub(usersPubkey)
|
||||||
|
if (usersNpub in keys) {
|
||||||
|
const encryptionKey = await NostrController.getInstance()
|
||||||
|
.nip04Decrypt(sender, keys[usersNpub])
|
||||||
|
.catch((err) => {
|
||||||
|
console.log(
|
||||||
|
'An error occurred in decrypting encryption key',
|
||||||
|
err
|
||||||
|
)
|
||||||
|
return null
|
||||||
|
})
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { fetchAndDecrypt } = MARK_TYPE_CONFIG[m.type] || {}
|
||||||
|
if (
|
||||||
|
typeof fetchAndDecrypt === 'function' &&
|
||||||
|
m.value &&
|
||||||
|
encryptionKey
|
||||||
|
) {
|
||||||
|
// TODO
|
||||||
|
// extract draw with proper values
|
||||||
|
// save both pdf with marking and original hash files for signature
|
||||||
|
// ...
|
||||||
|
const decrypted = await fetchAndDecrypt(m.value, encryptionKey)
|
||||||
|
marks[i].value = decrypted
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Error during mark fetchAndDecrypt phase`, error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const blob = await addMarks(file, marksToAdd)
|
||||||
zip.file(`marked/${fileName}`, blob)
|
zip.file(`marked/${fileName}`, blob)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,6 +24,7 @@ import {
|
|||||||
faStamp,
|
faStamp,
|
||||||
faTableCellsLarge
|
faTableCellsLarge
|
||||||
} from '@fortawesome/free-solid-svg-icons'
|
} from '@fortawesome/free-solid-svg-icons'
|
||||||
|
import { MARK_TYPE_CONFIG } from '../components/getMarkComponents.tsx'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Takes in an array of Marks already filtered by User.
|
* Takes in an array of Marks already filtered by User.
|
||||||
@ -279,6 +280,26 @@ export const getOptimizedPathsWithStrokeWidth = (svgString: string) => {
|
|||||||
return tuples
|
return tuples
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const processMarks = async (marks: Mark[], encryptionKey?: string) => {
|
||||||
|
const _marks = [...marks]
|
||||||
|
for (let i = 0; i < _marks.length; i++) {
|
||||||
|
const mark = _marks[i]
|
||||||
|
const hasProcess =
|
||||||
|
mark.type in MARK_TYPE_CONFIG &&
|
||||||
|
typeof MARK_TYPE_CONFIG[mark.type]?.encryptAndUpload === 'function'
|
||||||
|
|
||||||
|
if (hasProcess) {
|
||||||
|
const value = mark.value!
|
||||||
|
const processFn = MARK_TYPE_CONFIG[mark.type]?.encryptAndUpload
|
||||||
|
if (processFn) {
|
||||||
|
mark.value = await processFn(value, encryptionKey)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return _marks
|
||||||
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
getCurrentUserMarks,
|
getCurrentUserMarks,
|
||||||
filterMarksByPubkey,
|
filterMarksByPubkey,
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { MarkType, PdfPage } from '../types/drawing.ts'
|
import { MarkType, PdfPage } from '../types/drawing.ts'
|
||||||
import { LineCapStyle, PDFDocument, PDFFont, PDFPage, rgb } from 'pdf-lib'
|
import { PDFDocument, PDFFont, PDFPage, rgb } from 'pdf-lib'
|
||||||
import { Mark } from '../types/mark.ts'
|
import { Mark } from '../types/mark.ts'
|
||||||
import * as PDFJS from 'pdfjs-dist'
|
import * as PDFJS from 'pdfjs-dist'
|
||||||
import PDFJSWorker from 'pdfjs-dist/build/pdf.worker.min.mjs?worker'
|
import PDFJSWorker from 'pdfjs-dist/build/pdf.worker.min.mjs?worker'
|
||||||
@ -11,6 +11,9 @@ if (!PDFJS.GlobalWorkerOptions.workerPort) {
|
|||||||
|
|
||||||
import fontkit from '@pdf-lib/fontkit'
|
import fontkit from '@pdf-lib/fontkit'
|
||||||
import defaultFont from '../assets/fonts/roboto-regular.ttf'
|
import defaultFont from '../assets/fonts/roboto-regular.ttf'
|
||||||
|
import { BasicPoint } from 'signature_pad/dist/types/point'
|
||||||
|
import SignaturePad from 'signature_pad'
|
||||||
|
import { SIGNATURE_PAD_OPTIONS, SIGNATURE_PAD_SIZE } from './const.ts'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defined font size used when generating a PDF. Currently it is difficult to fully
|
* Defined font size used when generating a PDF. Currently it is difficult to fully
|
||||||
@ -136,7 +139,7 @@ export const addMarks = async (
|
|||||||
const mark = marksPerPage[i][j]
|
const mark = marksPerPage[i][j]
|
||||||
switch (mark.type) {
|
switch (mark.type) {
|
||||||
case MarkType.SIGNATURE:
|
case MarkType.SIGNATURE:
|
||||||
drawSignatureText(mark, pages[i])
|
await embedSignaturePng(mark, pages[i], pdf)
|
||||||
break
|
break
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@ -255,23 +258,41 @@ async function embedFont(pdf: PDFDocument) {
|
|||||||
return embeddedFont
|
return embeddedFont
|
||||||
}
|
}
|
||||||
|
|
||||||
const drawSignatureText = (mark: Mark, page: PDFPage) => {
|
const embedSignaturePng = async (
|
||||||
|
mark: Mark,
|
||||||
|
page: PDFPage,
|
||||||
|
pdf: PDFDocument
|
||||||
|
) => {
|
||||||
const { location } = mark
|
const { location } = mark
|
||||||
const { height } = page.getSize()
|
const { height } = page.getSize()
|
||||||
|
|
||||||
// Convert the mark location origin (top, left) to PDF origin (bottom, left)
|
|
||||||
const x = location.left
|
|
||||||
const y = height - location.top
|
|
||||||
|
|
||||||
if (hasValue(mark)) {
|
if (hasValue(mark)) {
|
||||||
const segments: string[][] = JSON.parse(mark.value!)
|
const data = JSON.parse(mark.value!).map((p: BasicPoint[]) => ({
|
||||||
segments.forEach(([d, w]) => {
|
points: p
|
||||||
page.drawSvgPath(d, {
|
}))
|
||||||
x,
|
const canvas = document.createElement('canvas')
|
||||||
y,
|
canvas.width = SIGNATURE_PAD_SIZE.width
|
||||||
borderWidth: parseFloat(w),
|
canvas.height = SIGNATURE_PAD_SIZE.height
|
||||||
borderLineCap: LineCapStyle.Round
|
const pad = new SignaturePad(canvas, SIGNATURE_PAD_OPTIONS)
|
||||||
})
|
pad.fromData(data)
|
||||||
|
const signatureImage = await pdf.embedPng(pad.toDataURL())
|
||||||
|
|
||||||
|
const scaled = signatureImage.scaleToFit(location.width, location.height)
|
||||||
|
|
||||||
|
// Convert the mark location origin (top, left) to PDF origin (bottom, left)
|
||||||
|
// and center the image
|
||||||
|
const x = location.left + (location.width - scaled.width) / 2
|
||||||
|
const y =
|
||||||
|
height -
|
||||||
|
location.top -
|
||||||
|
location.height +
|
||||||
|
(location.height - scaled.height) / 2
|
||||||
|
|
||||||
|
page.drawImage(signatureImage, {
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
width: scaled.width,
|
||||||
|
height: scaled.height
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user