Skip to content
Snippets Groups Projects
Unverified Commit 486422df authored by Bekerle, Patrizio's avatar Bekerle, Patrizio :fire:
Browse files

Allow annotating PDFs before signing them (#37, not final)

parent 9b95a69b
Branches
No related tags found
No related merge requests found
Pipeline #17973 passed
......@@ -14,6 +14,6 @@
"de": "Erlaubt das Hochladen von PDF-Dokumenten, um sie mit einer Amtssignatur zu versehen",
"en": "Allows upload of PDF-documents to officially sign them"
},
"subscribe": "lang,entry-point-url,nextcloud-web-app-password-url,nextcloud-webdav-url,nextcloud-name,nextcloud-file-url,show-nextcloud-file-picker,auth",
"subscribe": "lang,entry-point-url,nextcloud-web-app-password-url,nextcloud-webdav-url,nextcloud-name,nextcloud-file-url,show-nextcloud-file-picker,auth,allow-annotating",
"required_roles": ["ROLE_SCOPE_OFFICIAL-SIGNATURE"]
}
......@@ -14,5 +14,5 @@
"de": "Erlaubt das Hochladen von PDF-Dokumenten, um sie mit einer persönlichen elektronischen Signatur zu versehen",
"en": "Allows upload of PDF-documents to personally sign them"
},
"subscribe": "lang,entry-point-url,nextcloud-web-app-password-url,nextcloud-webdav-url,nextcloud-name,nextcloud-file-url,show-nextcloud-file-picker,auth"
"subscribe": "lang,entry-point-url,nextcloud-web-app-password-url,nextcloud-webdav-url,nextcloud-name,nextcloud-file-url,show-nextcloud-file-picker,auth,allow-annotating"
}
......@@ -58,6 +58,7 @@
show-nextcloud-file-picker
<%= buildInfo.env !== 'production' ? 'allow-signature-rotation' : '' %>
<%= buildInfo.env !== 'production' ? 'show-clipboard' : '' %>
<%= buildInfo.env !== 'production' ? 'allow-annotating' : '' %>
nextcloud-web-app-password-url="<%= nextcloudWebAppPasswordURL %>"
nextcloud-webdav-url="<%= nextcloudWebDavURL %>"
nextcloud-name="<%= nextcloudName %>"
......
......@@ -47,8 +47,7 @@ class OfficialSignaturePdfUpload extends ScopedElementsMixin(DBPSignatureLitElem
this.queuedFilesPlacementModes = [];
this.queuedFilesNeedsPlacement = new Map();
this.currentPreviewQueueKey = '';
this.allowAnnotating = false;
}
static get scopedElements() {
......@@ -89,6 +88,7 @@ class OfficialSignaturePdfUpload extends ScopedElementsMixin(DBPSignatureLitElem
signaturePlacementInProgress: { type: Boolean, attribute: false },
withSigBlock: { type: Boolean, attribute: false },
isSignaturePlacement: { type: Boolean, attribute: false },
allowAnnotating: { type: Boolean, attribute: 'allow-annotating' }
};
}
......@@ -670,7 +670,12 @@ class OfficialSignaturePdfUpload extends ScopedElementsMixin(DBPSignatureLitElem
<dbp-icon name="trash"></dbp-icon></button>
</div>
<div class="bottom-line">
<div></div>
<div class="${classMap({hidden: this.allowAnnotating})}"></div>
<button class="button ${classMap({hidden: !this.allowAnnotating})}"
?disabled="${this.signingProcessEnabled}"
title="${i18n.t('official-pdf-upload.add-annotation-title')}"
@click="${() => { this.addAnnotation(id, i18n); }}">
<dbp-icon name="plus"></dbp-icon></button>
<button class="button"
?disabled="${this.signingProcessEnabled}"
@click="${() => { this.showPreview(id); }}">${i18n.t('official-pdf-upload.show-preview')}</button>
......
......@@ -48,6 +48,7 @@ class QualifiedSignaturePdfUpload extends ScopedElementsMixin(DBPSignatureLitEle
this.queuedFilesPlacementModes = [];
this.queuedFilesNeedsPlacement = new Map();
this.currentPreviewQueueKey = '';
this.allowAnnotating = false;
this._onReceiveIframeMessage = this.onReceiveIframeMessage.bind(this);
this._onReceiveBeforeUnload = this.onReceiveBeforeUnload.bind(this);
......@@ -92,6 +93,7 @@ class QualifiedSignaturePdfUpload extends ScopedElementsMixin(DBPSignatureLitEle
signaturePlacementInProgress: { type: Boolean, attribute: false },
withSigBlock: { type: Boolean, attribute: false },
isSignaturePlacement: { type: Boolean, attribute: false },
allowAnnotating: { type: Boolean, attribute: 'allow-annotating' }
};
}
......
......@@ -113,6 +113,39 @@ export default class DBPSignatureLitElement extends DBPSignatureBaseLitElement {
return file;
}
/**
* Add an annotation to a file on the queue
*
* @param key
*/
async addAnnotation(key, i18n) {
const annotationKey = prompt("Please enter a key");
if (annotationKey === null || annotationKey === "") {
return;
}
const annotationValue = prompt("Please enter a value");
if (annotationValue === null || annotationValue === "") {
return;
}
let file = this.queuedFiles[key];
// console.log("file before annotation", file);
// annotate the pwd with the key and value
file = await utils.addKeyValuePdfAnnotation(file, i18n, 'AppName', this.auth['user-full-name'], annotationKey, annotationValue);
// console.log("file after annotation", file);
// overwrite the current pdf
this.queuedFiles[key] = file;
return file;
}
uploadOneQueuedFile() {
const file = this.takeFileFromQueue();
......
......@@ -32,7 +32,8 @@
"signature-placement-label": "Signatur platzieren",
"positioning": "Positionierung",
"confirm-page-leave": "Sind Sie sicher, dass Sie die Seite verlassen wollen? Es stehen signierte Dokumente zum Download bereit.",
"file-picker-context": "PDF-Dokumente zum Signieren auswählen"
"file-picker-context": "PDF-Dokumente zum Signieren auswählen",
"add-annotation-title": "Dem PDF eine Kennzahl hinzufügen"
},
"qualified-pdf-upload": {
"upload-field-label": "Dokument persönlich signieren",
......@@ -67,7 +68,8 @@
"signature-placement-label": "Signatur platzieren",
"positioning": "Positionierung",
"confirm-page-leave": "Sind Sie sicher, dass Sie die Seite verlassen wollen? Es stehen signierte Dokumente zum Download bereit.",
"file-picker-context": "PDF-Dokumente zum Signieren auswählen"
"file-picker-context": "PDF-Dokumente zum Signieren auswählen",
"add-annotation-title": "Dem PDF eine Kennzahl hinzufügen"
},
"signature-verification": {
"upload-field-label": "PDF-Dokumente zum Überprüfen der Signaturen hochladen",
......@@ -123,5 +125,6 @@
"error-cancel-message": "Der Signaturprozess wurde manuell abgebrochen.",
"error-rights-message": "Abbruch auf Grund mangelnder Rechte Ihres Accounts.",
"label-manual-positioning-missing": "Bestehende Signatur vorhanden, bitte wählen Sie ihre Positionierung manuell.",
"error-manual-positioning-missing": "Ein oder mehrere Signaturen in der Warteschlange müssen manuell positioniert werden."
"error-manual-positioning-missing": "Ein oder mehrere Signaturen in der Warteschlange müssen manuell positioniert werden.",
"annotation-author": "({{appName}}) im Namen von {{personName}}"
}
......@@ -31,7 +31,8 @@
"signature-placement-label": "Place signature",
"positioning": "Positioning",
"confirm-page-leave": "Are you sure you want to leave this page? There are still signed documents ready to be downloaded.",
"file-picker-context": "Upload PDF-documents to sign"
"file-picker-context": "Upload PDF-documents to sign",
"add-annotation-title": "Add annotation to PDF"
},
"qualified-pdf-upload": {
"upload-field-label": "Personally Sign Document",
......@@ -66,7 +67,8 @@
"signature-placement-label": "Place signature",
"positioning": "Positioning",
"confirm-page-leave": "Are you sure you want to leave this page? There are still signed documents ready to be downloaded.",
"file-picker-context": "Upload PDF-documents to sign"
"file-picker-context": "Upload PDF-documents to sign",
"add-annotation-title": "Add annotation to PDF"
},
"signature-verification": {
"upload-field-label": "Verify signed documents",
......@@ -123,5 +125,6 @@
"error-cancel-message": "The signature process was manually aborted.",
"error-rights-message": "Abort due to insufficient rights of your account.",
"label-manual-positioning-missing": "Existing signature present, please select its positioning manually.",
"error-manual-positioning-missing": "One or more signatures in the queue must be positioned manually."
"error-manual-positioning-missing": "One or more signatures in the queue must be positioned manually.",
"annotation-author": "({{appName}}) in the name of {{personName}}"
}
import {AnnotationFactory} from '@digital-blueprint/annotpdf/_bundles/pdfAnnotate.js';
/**
* Finds an object in a JSON result by identifier
*
......@@ -100,6 +102,25 @@ export const readBinaryFileContent = async (file) => {
});
};
/**
* Returns the content of the file as array buffer
*
* @param {File} file The file to read
* @returns {string} The content
*/
export const readArrayBufferFileContent = async (file) => {
return new Promise((resolve, reject) => {
let reader = new FileReader();
reader.onload = () => {
resolve(reader.result);
};
reader.onerror = () => {
reject(reader.error);
};
reader.readAsArrayBuffer(file);
});
};
/**
* Given a PDF file returns the amount of signatures found in it.
*
......@@ -120,3 +141,34 @@ export const getPDFSignatureCount = async (file) => {
}
return matches;
};
export const addKeyValuePdfAnnotation = async (file, i18n, appName, personName, key, value) => {
const data = await readArrayBufferFileContent(file);
let pdfFactory = new AnnotationFactory(data);
// console.log("pdfFactory.getAnnotations() before", pdfFactory.getAnnotations());
const page = 0;
const rect = [1, 1, 1, 1];
const author = i18n.t('annotation-author', {
appName: appName,
personName: personName
});
const contents = 'DBP-SIGNATURE-' + key + ': ' + value;
// pdfFactory.checkRect(4, rect);
// Create single free text annotation with print flag and 0 font size
let annot = Object.assign(pdfFactory.createBaseAnnotation(page, rect, contents, author), {
annotation_flag: 4, // enable print to be PDF/A conform
color: {r: 1, g: 1, b: 1}, // white to (maybe) hide it better
opacity: 0.001, // we can't set to 0 because of "if (opacity) {"
defaultAppearance: "/Invalid_font 0 Tf" // font size 0 to (maybe) hide it better
});
annot.type = "/FreeText";
pdfFactory.annotations.push(annot);
const blob = pdfFactory.write();
return new File([blob], file.name, { type: file.type });
};
......@@ -895,6 +895,16 @@
lodash "^4.17.19"
to-fast-properties "^2.0.0"
"@digital-blueprint/annotpdf@^1.0.13a":
version "1.0.13-a"
resolved "https://registry.npmjs.org/@digital-blueprint/annotpdf/-/annotpdf-1.0.13-a.tgz#f588188a6e745dabcafa7756ca2c8b24a74e43f6"
integrity sha512-Nr/YcOYswe5XZG9NnjBVOa+tz5uNcQD1vg41pirbW7/zsnu0W48xjcCIpf46PkWsjAf+NxBRQB+i8gx/n0iQIg==
dependencies:
"@types/crypto-js" "^4.0.1"
"@types/pako" "^1.0.1"
crypto-js "^4.0.0"
pako "^1.0.10"
"@eslint/eslintrc@^0.2.1":
version "0.2.2"
resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.2.2.tgz#d01fc791e2fc33e88a29d6f3dc7e93d0cd784b76"
......@@ -1122,6 +1132,11 @@
resolved "https://registry.yarnpkg.com/@types/cors/-/cors-2.8.10.tgz#61cc8469849e5bcdd0c7044122265c39cec10cf4"
integrity sha512-C7srjHiVG3Ey1nR6d511dtDkCEjxuN9W1HWAEjGq8kpcwmNM6JJkpC0xvabM7BXTG2wDq8Eu33iH9aQKa7IvLQ==
"@types/crypto-js@^4.0.1":
version "4.0.1"
resolved "https://registry.npmjs.org/@types/crypto-js/-/crypto-js-4.0.1.tgz#3a4bd24518b0e6c5940da4e2659eeb2ef0806963"
integrity sha512-6+OPzqhKX/cx5xh+yO8Cqg3u3alrkhoxhE5ZOdSEv0DOzJ13lwJ6laqGU0Kv6+XDMFmlnGId04LtY22PsFLQUw==
"@types/ejs@^3.0.4":
version "3.0.5"
resolved "https://registry.yarnpkg.com/@types/ejs/-/ejs-3.0.5.tgz#95a3a1c3d9603eba80fe67ff56da1ba275ef2eda"
......@@ -1172,6 +1187,11 @@
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.28.tgz#cade4b64f8438f588951a6b35843ce536853f25b"
integrity sha512-lg55ArB+ZiHHbBBttLpzD07akz0QPrZgUODNakeC09i62dnrywr9mFErHuaPlB6I7z+sEbK+IYmplahvplCj2g==
"@types/pako@^1.0.1":
version "1.0.1"
resolved "https://registry.npmjs.org/@types/pako/-/pako-1.0.1.tgz#33b237f3c9aff44d0f82fe63acffa4a365ef4a61"
integrity sha512-GdZbRSJ3Cv5fiwT6I0SQ3ckeN2PWNqxd26W9Z2fCK1tGrrasGy4puvNFtnddqH9UJFMQYXxEuuB7B8UK+LLwSg==
"@types/resolve@1.17.1":
version "1.17.1"
resolved "https://registry.yarnpkg.com/@types/resolve/-/resolve-1.17.1.tgz#3afd6ad8967c77e4376c598a82ddd58f46ec45d6"
......@@ -1454,12 +1474,12 @@ astral-regex@^2.0.0:
resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31"
integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==
async-mutex@^0.2.4:
version "0.2.4"
resolved "https://registry.yarnpkg.com/async-mutex/-/async-mutex-0.2.4.tgz#f6ea5f9cc73147f395f86fa573a2af039fe63082"
integrity sha512-fcQKOXUKMQc57JlmjBCHtkKNrfGpHyR7vu18RfuLfeTAf4hK9PgOadPR5cDrBQ682zasrLUhJFe7EKAHJOduDg==
async-mutex@^0.3.0:
version "0.3.1"
resolved "https://registry.npmjs.org/async-mutex/-/async-mutex-0.3.1.tgz#7033af665f1c7cebed8b878267a43ba9e77c5f67"
integrity sha512-vRfQwcqBnJTLzVQo72Sf7KIUbcSUP5hNchx6udI1U6LuPQpfePgdjJzlCe76yFZ8pxlLjn9lwcl/Ya0TSOv0Tw==
dependencies:
tslib "^2.0.0"
tslib "^2.1.0"
async@0.9.x:
version "0.9.2"
......@@ -2019,6 +2039,11 @@ cross-spawn@^7.0.2:
shebang-command "^2.0.0"
which "^2.0.1"
crypto-js@^4.0.0:
version "4.0.0"
resolved "https://registry.npmjs.org/crypto-js/-/crypto-js-4.0.0.tgz#2904ab2677a9d042856a2ea2ef80de92e4a36dcc"
integrity sha512-bzHZN8Pn+gS7DQA6n+iUmBfl0hO5DJq++QP3U6uTucDtk/0iGpXd/Gg7CGR0p8tJhofJyaKoWBuJI4eAO00BBg==
cssom@^0.4.1:
version "0.4.4"
resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.4.4.tgz#5a66cf93d2d0b661d80bf6a44fb65f5c2e4e0a10"
......@@ -2425,19 +2450,6 @@ escodegen@^1.11.1:
optionalDependencies:
source-map "~0.6.1"
eslint-plugin-jsdoc@^31.0.0:
version "31.6.1"
resolved "https://registry.yarnpkg.com/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-31.6.1.tgz#98040c801500572fff71c984a097d89946f1e450"
integrity sha512-5hCV3u+1VSEUMyfdTl+dpWsioD7tqQr2ILQw+KbXrF42AVxCLO8gnNLR6zDCDjqGGpt79V1sgY0RRchCWuCigg==
dependencies:
comment-parser "1.1.2"
debug "^4.3.1"
jsdoctypeparser "^9.0.0"
lodash "^4.17.20"
regextras "^0.7.1"
semver "^7.3.4"
spdx-expression-parse "^3.0.1"
eslint-plugin-jsdoc@^32.0.0:
version "32.2.0"
resolved "https://registry.yarnpkg.com/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-32.2.0.tgz#d848ea8475a9be63d8d261bd7fa01cf2a2bb32d5"
......@@ -4484,7 +4496,7 @@ package-name-regex@1.0.9:
resolved "https://registry.yarnpkg.com/package-name-regex/-/package-name-regex-1.0.9.tgz#d7d33ff3f2ef43a28fbb02aa4fcf48fc1ab26588"
integrity sha512-+U2oQCfEz2IlGqws8gmfKzdMDbSd6+RZp6UIFdKo+GAw3+o+kfnsgXkWtJ1JMoKhpP2kEvuYyTy1lXOEQEe0ZA==
pako@~1.0.2:
pako@^1.0.10, pako@~1.0.2:
version "1.0.11"
resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf"
integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==
......@@ -5747,7 +5759,7 @@ ts-simple-type@~1.0.5:
resolved "https://registry.npmjs.org/ts-simple-type/-/ts-simple-type-1.0.7.tgz#03930af557528dd40eaa121913c7035a0baaacf8"
integrity sha512-zKmsCQs4dZaeSKjEA7pLFDv7FHHqAFLPd0Mr//OIJvu8M+4p4bgSFJwZSEBEg3ec9W7RzRz1vi8giiX0+mheBQ==
tslib@2.1.0, tslib@^2.0.3:
tslib@2.1.0, tslib@^2.0.3, tslib@^2.1.0:
version "2.1.0"
resolved "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz#da60860f1c2ecaa5703ab7d39bc05b6bf988b97a"
integrity sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==
......@@ -5757,11 +5769,6 @@ tslib@^1.9.3:
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
tslib@^2.0.0:
version "2.0.3"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.0.3.tgz#8e0741ac45fc0c226e58a17bfc3e64b9bc6ca61c"
integrity sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ==
tunnel-agent@^0.6.0:
version "0.6.0"
resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd"
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment