From fcddf4006a5bf7e0dd2741de503617553fe82cde Mon Sep 17 00:00:00 2001
From: Siebe Verschaeve
Date: Tue, 29 Apr 2025 14:33:49 +0200
Subject: [PATCH 1/2] feat: add LocalStorageService for managing local storage
operations
- Implemented LocalStorageService class to handle saving, retrieving, adding, updating, and deleting items in localStorage.
- Added methods for managing arrays of objects with error handling for localStorage operations.
chore: initialize TypeScript configuration
- Created tsconfig.json with settings for ES2020, module resolution, and linting options.
- Configured TypeScript to include the src directory for compilation.
---
.gitignore | 2 +
builddate.js | 1 -
compile | 13 -
css/about.css | 4 +-
eendraadschema.js | 10935 ----------------
index.html | 11 +-
jsPDF/jspdf.umd.min.js | 398 -
jsPDF/jspdf.umd.min.js.map | 1 -
jsPDF/print.js | 267 -
package-lock.json | 3553 +++++
package.json | 22 +-
pako/pako.min.js | 2 -
prop/prop_scripts.js | 10 -
prop/prop_scripts.ts | 82 -
src/EventManager.ts | 2 +-
src/Hierarchical_List.ts | 80 +-
src/List_Item/Aansluiting.ts | 9 +-
src/List_Item/Aansluitpunt.ts | 6 +-
src/List_Item/Aftakdoos.ts | 6 +-
src/List_Item/Batterij.ts | 6 +-
src/List_Item/Bel.ts | 6 +-
src/List_Item/Boiler.ts | 6 +-
src/List_Item/Bord.ts | 83 +-
src/List_Item/Contactdoos.ts | 6 +-
src/List_Item/Diepvriezer.ts | 6 +-
src/List_Item/Domotica.ts | 6 +-
.../Domotica_gestuurde_verbruiker.ts | 12 +-
src/List_Item/Domotica_verticaal.ts | 6 +-
src/List_Item/Droogkast.ts | 6 +-
src/List_Item/Drukknop.ts | 7 +-
src/List_Item/EV_lader.ts | 6 +-
src/List_Item/Electro_Item.ts | 11 +-
src/List_Item/Elektriciteitsmeter.ts | 6 +-
src/List_Item/Elektrische_oven.ts | 6 +-
src/List_Item/Ketel.ts | 7 +-
src/List_Item/Koelkast.ts | 6 +-
src/List_Item/Kookfornuis.ts | 6 +-
src/List_Item/Kring.ts | 8 +-
src/List_Item/Leiding.ts | 6 +-
src/List_Item/Lichtpunt.ts | 7 +-
src/List_Item/List_Item.ts | 9 +-
src/List_Item/Media.ts | 7 +-
src/List_Item/Meerdere_verbruikers.ts | 6 +-
src/List_Item/Microgolfoven.ts | 6 +-
src/List_Item/Motor.ts | 6 +-
src/List_Item/Omvormer.ts | 6 +-
src/List_Item/Overspanningsbeveiliging.ts | 6 +-
src/List_Item/Schakelaars/Lichtcircuit.ts | 21 +-
src/List_Item/Schakelaars/Schakelaar.ts | 5 +-
src/List_Item/Schakelaars/Schakelaars.ts | 7 +-
src/List_Item/Splitsing.ts | 8 +-
src/List_Item/Stoomoven.ts | 6 +-
src/List_Item/Transformator.ts | 7 +-
src/List_Item/USB_lader.ts | 7 +-
src/List_Item/Vaatwasmachine.ts | 6 +-
src/List_Item/Ventilator.ts | 6 +-
src/List_Item/Verbruiker.ts | 6 +-
src/List_Item/Verlenging.ts | 5 +-
src/List_Item/Verwarmingstoestel.ts | 6 +-
src/List_Item/Vrije_ruimte.ts | 5 +-
src/List_Item/Vrije_tekst.ts | 6 +-
src/List_Item/Warmtepomp.ts | 7 +-
src/List_Item/Wasmachine.ts | 6 +-
src/List_Item/Zekering.ts | 7 +-
src/List_Item/Zeldzame_symbolen.ts | 6 +-
src/List_Item/Zonnepaneel.ts | 7 +-
src/Properties.ts | 2 +-
src/SVGSymbols.ts | 2 +-
src/SVGelement.ts | 2 +-
src/Session.ts | 38 -
src/TopMenu.ts | 4 +-
src/config.ts | 18 +-
src/documentation/HelperTip.ts | 4 +-
src/documentation/documentation.ts | 4 +-
src/general.ts | 27 +-
src/importExport.ts | 670 -
src/main.ts | 1766 ++-
src/print/MarkerList.ts | 2 +-
src/print/Page_Info.ts | 2 +-
src/print/Print_Table.ts | 15 +-
src/services/localstorage.service.ts | 57 +
src/sitplan/ContextMenu.ts | 2 +-
src/sitplan/ElectroItemZoeker.ts | 14 +-
src/sitplan/GeometricFunctions.ts | 6 +-
src/sitplan/MouseDrag.ts | 6 +-
src/sitplan/SituationPlan.ts | 111 +-
src/sitplan/SituationPlanElement.ts | 45 +-
src/sitplan/SituationPlanView.ts | 353 +-
...ituationPlanView_ElementPropertiesPopup.ts | 31 +-
src/sitplan/SituationPlanView_SideBar.ts | 2 +-
src/storage/MultiLevelStorage.ts | 4 +-
src/tsconfig.json | 33 -
src/undoRedo.ts | 205 -
tsconfig.json | 25 +
94 files changed, 6046 insertions(+), 13207 deletions(-)
create mode 100644 .gitignore
delete mode 100644 builddate.js
delete mode 100755 compile
delete mode 100644 eendraadschema.js
delete mode 100644 jsPDF/jspdf.umd.min.js
delete mode 100644 jsPDF/jspdf.umd.min.js.map
delete mode 100644 jsPDF/print.js
create mode 100644 package-lock.json
delete mode 100755 pako/pako.min.js
delete mode 100644 prop/prop_scripts.js
delete mode 100644 prop/prop_scripts.ts
delete mode 100644 src/Session.ts
delete mode 100644 src/importExport.ts
create mode 100644 src/services/localstorage.service.ts
delete mode 100644 src/tsconfig.json
create mode 100644 tsconfig.json
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..8f9d799
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+node_modules/*
+dist/*
\ No newline at end of file
diff --git a/builddate.js b/builddate.js
deleted file mode 100644
index 784550d..0000000
--- a/builddate.js
+++ /dev/null
@@ -1 +0,0 @@
-var CONF_builddate="20250317-222603"
diff --git a/compile b/compile
deleted file mode 100755
index 690ab80..0000000
--- a/compile
+++ /dev/null
@@ -1,13 +0,0 @@
-#!/bin/bash
-
-cd src
-
-tsc # Uses the tsconfig.json file
-
-cat GPL3.js swap.js > eendraadschema.js
-mv eendraadschema.js ..
-rm swap.js
-
-cd ..
-
-echo "var CONF_builddate=\""`date +%Y%m%d-%H%M%S`"\"" > builddate.js
diff --git a/css/about.css b/css/about.css
index 554035c..ac381ad 100644
--- a/css/about.css
+++ b/css/about.css
@@ -1,9 +1,9 @@
body {
-# background-color: white;
+/* background-color: white; */
}
html {
- # background: url(bg.jpg) no-repeat center center fixed;
+ /* background: url(bg.jpg) no-repeat center center fixed; */
-webkit-background-size: cover;
-moz-background-size: cover;
-o-background-size: cover;
diff --git a/eendraadschema.js b/eendraadschema.js
deleted file mode 100644
index 87c6cad..0000000
--- a/eendraadschema.js
+++ /dev/null
@@ -1,10935 +0,0 @@
-/*** Eendraadschema ***
-
-=== Community edition ===
-
-Copyright (C) 2019-2025 Ivan Goethals GPLv3
-
-This program is free software: you can redistribute it and/or modify it
-under the terms of the GNU General Public License as published by the
-Free Software Foundation, either version 3 of the License, or (at your option)
-any later version.
-
-This program is distributed in the hope that it will be useful, but
-WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
-more details.
-
-You should have received a copy of the GNU General Public License
-along with this program. If not, see http://www.gnu.org/licenses/.
-
-The source code of this community edition is hosted on Github at
-https://github.com/igoethal/eendraadschema.
-
-=== Exclusive license ===
-
-Notwithstanding the above, the original and sole author of this edition,
-Ivan Goethals, reserves the exclusive right to create derivative works of
-this software and distribute them under different licensing terms, including
-but not limited to proprietary licenses. This includes the ability to develop
-and offer a hosted edition with additional features not available in the
-community edition. This right does not extend to derivative works produced by
-others based on this community edition.
-
-=== Embedded content ===
-
-== Pako.js ==
-
-This program uses the Pako.js entropy coding library. Pako is released under
-an MIT license by Andrey Tupitsin and Vitaly Puzrin. For more information on
-Pako and the full license text, please visit https://github.com/nodeca/pako
-
-== Zlib ==
-
-Pako implements ZLib in javascript. Zlib is released under the ZLIB License.
-See https://www.zlib.net/zlib_license.html
-
-== jsPDF ==
-
-This program uses the jsPDF library to transform SVG images into PDF files.
-The jsPDF license is as follows
-
-Copyright
-(c) 2010-2021 James Hall, https://github.com/MrRio/jsPDF
-(c) 2015-2021 yWorks GmbH, https://www.yworks.com/
-
-Permission is hereby granted, free of charge, to any person obtaining
-a copy of this software and associated documentation files (the
-"Software"), to deal in the Software without restriction, including
-without limitation the rights to use, copy, modify, merge, publish,
-distribute, sublicense, and/or sell copies of the Software, and to
-permit persons to whom the Software is furnished to do so, subject to
-the following conditions:
-
-The above copyright notice and this permission notice shall be
-included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-*/
-
-var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
- return new (P || (P = Promise))(function (resolve, reject) {
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
- step((generator = generator.apply(thisArg, _arguments || [])).next());
- });
-};
-var __generator = (this && this.__generator) || function (thisArg, body) {
- var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
- return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
- function verb(n) { return function (v) { return step([n, v]); }; }
- function step(op) {
- if (f) throw new TypeError("Generator is already executing.");
- while (g && (g = 0, op[0] && (_ = 0)), _) try {
- if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
- if (y = 0, t) op = [op[0] & 2, t.value];
- switch (op[0]) {
- case 0: case 1: t = op; break;
- case 4: _.label++; return { value: op[1], done: false };
- case 5: _.label++; y = op[1]; op = [0]; continue;
- case 7: op = _.ops.pop(); _.trys.pop(); continue;
- default:
- if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
- if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
- if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
- if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
- if (t[2]) _.ops.pop();
- _.trys.pop(); continue;
- }
- op = body.call(thisArg, _);
- } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
- if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
- }
-};
-var __assign = (this && this.__assign) || function () {
- __assign = Object.assign || function(t) {
- for (var s, i = 1, n = arguments.length; i < n; i++) {
- s = arguments[i];
- for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
- t[p] = s[p];
- }
- return t;
- };
- return __assign.apply(this, arguments);
-};
-var __extends = (this && this.__extends) || (function () {
- var extendStatics = function (d, b) {
- extendStatics = Object.setPrototypeOf ||
- ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
- function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
- return extendStatics(d, b);
- };
- return function (d, b) {
- if (typeof b !== "function" && b !== null)
- throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
- extendStatics(d, b);
- function __() { this.constructor = d; }
- d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
- };
-})();
-var __rest = (this && this.__rest) || function (s, e) {
- var t = {};
- for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
- t[p] = s[p];
- if (s != null && typeof Object.getOwnPropertySymbols === "function")
- for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
- if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
- t[p[i]] = s[p[i]];
- }
- return t;
-};
-function deepClone(obj) {
- var _out = new obj.constructor;
- var getType = function (n) {
- return Object.prototype.toString.call(n).slice(8, -1);
- };
- for (var _key in obj) {
- if (obj.hasOwnProperty(_key)) {
- _out[_key] = getType(obj[_key]) === 'Object' || getType(obj[_key]) === 'Array' ? deepClone(obj[_key]) : obj[_key];
- }
- }
- return _out;
-}
-/**
- * Returns true if the current mode is a development mode.
- * This is determined by the presence of a 'dev' parameter in the URL.
- *
- * @returns {boolean} True if this is a development mode, false otherwise.
- */
-function isDevMode() {
- try {
- var urlParams = new URLSearchParams(window.location.search);
- return urlParams.has('dev');
- }
- catch (error) {
- console.error('Error checking for dev mode:', error);
- return false;
- }
-}
-// Function for length of a string in 8 bit bytes
-var byteSize = function (str) { return new Blob([str]).size; };
-function contains(a, obj) {
- for (var i = 0; i < a.length; i++) {
- if (a[i] === obj) {
- return true;
- }
- }
- return false;
-}
-function isInt(value) {
- return !isNaN(value) &&
- parseInt(value) == value &&
- !isNaN(parseInt(value, 10));
-}
-function svgTextWidth(input, fontsize, options) {
- if (fontsize === void 0) { fontsize = 10; }
- if (options === void 0) { options = ''; }
- var div = document.createElement('div');
- div.innerHTML = '';
- var tryoutdiv = document.body;
- /*if (document.getElementById("configsection").style.display === 'block') {
- tryoutdiv = document.getElementById("configsection") as HTMLElement;
- } else if (document.getElementById("outerdiv").style.display === 'block') {
- tryoutdiv = document.getElementById("outerdiv") as HTMLElement;
- } else {
- tryoutdiv = document.getElementById("right_col_inner") as HTMLElement;
- }*/
- tryoutdiv.appendChild(div);
- var width = div.children[0].children[0].getBBox().width;
- tryoutdiv.removeChild(div);
- return (Math.ceil(width));
-}
-function flattenSVG(SVGstruct, shiftx, shifty, node, overflowright) {
- if (overflowright === void 0) { overflowright = 0; }
- if (node == 0)
- structure.print_table.pagemarkers.clear();
- var str = "";
- var X = new XMLSerializer();
- var parser = new DOMParser();
- var outstruct = SVGstruct;
- if (SVGstruct.localName == "svg") {
- if (outstruct.attributes.getNamedItem("x")) { // make SVG a 0,0 element
- shiftx += parseFloat(outstruct.attributes.getNamedItem("x").nodeValue);
- outstruct.attributes.getNamedItem("x").nodeValue = 0;
- }
- if (outstruct.attributes.getNamedItem("y")) { // make SVG a 0,0 element
- shifty += parseFloat(outstruct.attributes.getNamedItem("y").nodeValue);
- outstruct.attributes.getNamedItem("y").nodeValue = 0;
- }
- for (var i = 0; i < SVGstruct.children.length; i++) {
- str = str.concat(flattenSVG(SVGstruct.children[i], shiftx, shifty, node + 1), "\n");
- }
- if (node <= 0) {
- if (outstruct.attributes.getNamedItem("width")) { // make SVG a 0,0 element
- str = '';
- }
- else {
- str = '';
- }
- }
- }
- else {
- if (SVGstruct.localName == "line") {
- if (shiftx != 0) {
- outstruct.attributes.getNamedItem("x1").nodeValue = parseFloat(outstruct.attributes.getNamedItem("x1").nodeValue) + shiftx;
- outstruct.attributes.getNamedItem("x2").nodeValue = parseFloat(outstruct.attributes.getNamedItem("x2").nodeValue) + shiftx;
- }
- if (shifty != 0) {
- outstruct.attributes.getNamedItem("y1").nodeValue = parseFloat(outstruct.attributes.getNamedItem("y1").nodeValue) + shifty;
- outstruct.attributes.getNamedItem("y2").nodeValue = parseFloat(outstruct.attributes.getNamedItem("y2").nodeValue) + shifty;
- }
- }
- if (SVGstruct.localName == "use") {
- if (shiftx != 0) {
- outstruct.attributes.getNamedItem("x").nodeValue = parseFloat(outstruct.attributes.getNamedItem("x").nodeValue) + shiftx;
- }
- if (shifty != 0) {
- outstruct.attributes.getNamedItem("y").nodeValue = parseFloat(outstruct.attributes.getNamedItem("y").nodeValue) + shifty;
- }
- }
- if (SVGstruct.localName == "rect") {
- if (shiftx != 0) {
- outstruct.attributes.getNamedItem("x").nodeValue = parseFloat(outstruct.attributes.getNamedItem("x").nodeValue) + shiftx;
- }
- if (shifty != 0) {
- outstruct.attributes.getNamedItem("y").nodeValue = parseFloat(outstruct.attributes.getNamedItem("y").nodeValue) + shifty;
- }
- }
- if (SVGstruct.localName == "circle") {
- if (shiftx != 0) {
- outstruct.attributes.getNamedItem("cx").nodeValue = parseFloat(outstruct.attributes.getNamedItem("cx").nodeValue) + shiftx;
- }
- if (shifty != 0) {
- outstruct.attributes.getNamedItem("cy").nodeValue = parseFloat(outstruct.attributes.getNamedItem("cy").nodeValue) + shifty;
- }
- }
- if (SVGstruct.localName == "text") {
- outstruct.attributes.getNamedItem("x").nodeValue = parseFloat(outstruct.attributes.getNamedItem("x").nodeValue) + shiftx;
- outstruct.attributes.getNamedItem("y").nodeValue = parseFloat(outstruct.attributes.getNamedItem("y").nodeValue) + shifty;
- if (outstruct.attributes.getNamedItem("transform")) {
- if (outstruct.attributes.getNamedItem("transform").value.includes('rotate')) {
- outstruct.attributes.getNamedItem("transform").value = "rotate(-90 " +
- outstruct.attributes.getNamedItem("x").nodeValue + "," +
- outstruct.attributes.getNamedItem("y").nodeValue + ")";
- }
- else {
- outstruct.attributes.getNamedItem("transform").value = "scale(-1,1) translate(-" +
- outstruct.attributes.getNamedItem("x").nodeValue * 2 + ",0)";
- }
- }
- }
- if (SVGstruct.localName == "polygon") {
- var polystr_out = "";
- var polystr_in = outstruct.attributes.getNamedItem("points").nodeValue;
- var splitted_in = polystr_in.split(" ");
- for (var countstr = 0; countstr < splitted_in.length; countstr++) {
- var points_in = splitted_in[countstr].split(",");
- polystr_out += (points_in[0] * 1 + shiftx) + ',' + (points_in[1] * 1 + shifty);
- if (countstr < splitted_in.length - 1) {
- polystr_out += ' ';
- }
- }
- outstruct.attributes.getNamedItem("points").nodeValue = polystr_out;
- }
- str = X.serializeToString(outstruct);
- //remove all the xmlns tags
- var regex = /xmlns="[^"]+"/g;
- str = str.replace(regex, '');
- }
- structure.print_table.pagemarkers.addMarker(node, shiftx);
- return str;
-}
-function flattenSVGfromString(xmlstr, overflowright) {
- if (overflowright === void 0) { overflowright = 0; }
- var str = "";
- var parser = new DOMParser();
- var xmlDoc = parser.parseFromString(xmlstr, "text/xml"); //important to use "text/xml"
- str = flattenSVG(xmlDoc.childNodes[0], 0, 0, 0, overflowright);
- return str;
-}
-function htmlspecialchars(my_input) {
- var returnstr;
- if (typeof (my_input) == 'undefined')
- returnstr = "";
- else
- returnstr = my_input.toString();
- var map = {
- '&': '&',
- '<': '<',
- '>': '>',
- '"': '"',
- "'": '''
- };
- return returnstr.replace(/[&<>"']/g, function (m) { return map[m]; });
-}
-function browser_ie_detected() {
- var ua = window.navigator.userAgent;
- var msie = ua.indexOf("MSIE ");
- var trident = ua.indexOf('Trident/');
- if ((msie > 0) || (trident > 0))
- return true;
- else
- return false;
-}
-var randomId = (function () {
- var counters = {};
- return function (prefix) {
- if (prefix === void 0) { prefix = "Rnd_"; }
- if (!(prefix in counters)) {
- counters[prefix] = 0;
- }
- var value = counters[prefix];
- counters[prefix]++;
- return "".concat(prefix).concat(value.toString());
- };
-})();
-var Session = /** @class */ (function () {
- function Session() {
- this.sessionKey = 'SessionJS';
- this.newUser = false;
- var storedSessionId = localStorage.getItem(this.sessionKey);
- if (storedSessionId) {
- this.sessionId = storedSessionId;
- }
- else {
- this.sessionId = this.generateRandomBase64String(22);
- localStorage.setItem(this.sessionKey, this.sessionId);
- this.newUser = true;
- }
- }
- Session.prototype.generateRandomBase64String = function (length) {
- var characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
- var result = '';
- var charactersLength = characters.length;
- for (var i = 0; i < length; i++) {
- var randomIndex = Math.floor(Math.random() * charactersLength);
- result += characters[randomIndex];
- }
- return result;
- };
- Session.prototype.getSessionId = function () {
- return this.sessionId;
- };
- Session.prototype.isNewUser = function () {
- return this.newUser;
- };
- return Session;
-}());
-var SVGelement = /** @class */ (function () {
- function SVGelement() {
- this.data = "";
- this.xleft = 0;
- this.xright = 0;
- this.xrightmin = 0;
- this.yup = 0;
- this.ydown = 0;
- }
- return SVGelement;
-}());
-/**
- * Manages the addition and removal of event listeners on HTML elements.
- */
-var EventManager = /** @class */ (function () {
- function EventManager() {
- this.listeners = [];
- }
- /**
- * Adds an event listener to a specified HTML element. If a listener of the same
- * type already exists on the element, it is removed before adding the new one.
- *
- * @param element - The HTML element to attach the event listener to.
- * @param type - The type of the event.
- * @param listener - The event listener function or object.
- */
- EventManager.prototype.addEventListener = function (element, type, listener) {
- var existingListenerIndex = this.listeners.findIndex(function (l) { return l.element === element && l.type === type; });
- if (existingListenerIndex !== -1) {
- var existingListener = this.listeners[existingListenerIndex];
- element.removeEventListener(type, existingListener.listener);
- this.listeners.splice(existingListenerIndex, 1);
- }
- this.listeners.push({ element: element, type: type, listener: listener });
- element.addEventListener(type, listener);
- this.cleanup(); // Before we proceed, remove all listeners for elements that no longer exist
- };
- /**
- * Removes all event listeners managed by this EventManager instance.
- */
- EventManager.prototype.removeAllEventListeners = function () {
- this.listeners.forEach(function (_a) {
- var element = _a.element, type = _a.type, listener = _a.listener;
- element.removeEventListener(type, listener);
- });
- this.listeners = [];
- };
- /**
- * Disposes of the EventManager by removing all event listeners.
- */
- EventManager.prototype.dispose = function () {
- this.removeAllEventListeners();
- };
- /**
- * Removes all listeners for which the HTML element no longer exists.
- */
- EventManager.prototype.cleanup = function () {
- this.listeners = this.listeners.filter(function (_a) {
- var element = _a.element, type = _a.type, listener = _a.listener;
- if (!document.contains(element)) {
- element.removeEventListener(type, listener);
- return false;
- }
- return true;
- });
- };
- return EventManager;
-}());
-/**
- * A class to handle a list of potential page breaks or markers in the form {depth,xpos} with depth how nested
- * they are (lower depth is better to include a page break) and xpos the location in pixels in the SVG where
- * the break could be added
- */
-var MarkerList = /** @class */ (function () {
- function MarkerList() {
- this.markers = [];
- }
- /**
- * Clear the list of markers
- */
- MarkerList.prototype.clear = function () {
- this.markers = [];
- };
- /**
- * Add a marker to the markerlist.
- * If the same marker already exists, no new one will be added.
- * @param depth - The depth of the marker, or how nested it is (lower depth is better for page breaks).
- * @param xpos - The x-coordinate position of the marker.
- */
- MarkerList.prototype.addMarker = function (depth, xpos) {
- // Check if the marker already exists
- var exists = this.markers.some(function (marker) { return marker.depth === depth && marker.xpos === xpos; });
- if (!exists) {
- this.markers.push({ depth: depth, xpos: xpos });
- }
- };
- /**
- * Sorts the markers by their x-coordinate position in ascending order.
- */
- MarkerList.prototype.sort = function () {
- this.markers.sort(function (a, b) { return a.xpos - b.xpos; });
- };
- /**
- * Looks for the marker in the half-open internal (minx, maxx] with the lowest possible depth.
- * If multiple markers exist with the same depth, the one with the highest xpos is returned.
- * If no suitable marker is found, a dummy {depth=null, xpos=maxx} is returned.
- * @param minx - Minimal x for any markers that will be considered.
- * @param maxx - Maximal x for any markers that will be considered.
- * @returns The marker with the lowest depth (and highest xpos if multiple exist) or a dummy marker if none are found.
- */
- MarkerList.prototype.findMinDepth = function (minx, maxx) {
- // Filter markers within the range
- var filteredMarkers = this.markers.filter(function (marker) { return marker.xpos > minx && marker.xpos <= maxx; });
- if (filteredMarkers.length === 0) {
- return { depth: null, xpos: maxx }; // No markers in the specified range so we just take the maximum
- }
- // Find the marker with the lowest depth, if multiple exist with the same depth, take the one with the highest xpos
- return filteredMarkers.reduce(function (minDepthMarker, marker) {
- if (marker.depth < minDepthMarker.depth ||
- (marker.depth === minDepthMarker.depth && marker.xpos > minDepthMarker.xpos)) {
- return marker;
- }
- return minDepthMarker;
- }, filteredMarkers[0]);
- };
- return MarkerList;
-}());
-/**
- * Store information on what part of the SVG lands on one specific page
- * The height is the final height in number of pixels
- * The start and stop are the x-locations in pixels of the part of the total SVG that needs to land on this particular page
- */
-var Page_Info = /** @class */ (function () {
- function Page_Info() {
- this.height = 0;
- this.start = 0;
- this.stop = 0;
- }
- return Page_Info;
-}());
-/**
- * Stores all information about pagination and how pages will be printed.
- * Can perform automatic pagination or ask the user to paginate.
- *
- * We don't use private variables in this class as we want to serialize it (JSON)
- */
-var Print_Table = /** @class */ (function () {
- /**
- * Initialize list of pages (foresee at least 1 page) and pagemarkers
- */
- function Print_Table() {
- this.height = 0; //How high is the SVG that will be printed in pixels
- this.maxwidth = 0; //What is the width of the SVG that will be printed in pixels and therefore the maximum printing width
- this.displaypage = 0;
- this.enableAutopage = true; //Flag to indicate if automatic pagination is used or not
- this.pages = new Array();
- this.pages.push(new Page_Info());
- this.pagemarkers = new MarkerList;
- }
- /**
- * Set papersize to either "A4" or "A3"
- * @param papersize - A string, if it is neither "A4" or "A3", the papersize will default to "A4".
- */
- Print_Table.prototype.setPaperSize = function (papersize) {
- this.papersize = (papersize === "A3" ? "A3" : "A4");
- };
- /**
- * Get papersize. If papersize was not yet defined, it is forced to "A4"
- * @returns The papersize, either "A3" or "A4"
- */
- Print_Table.prototype.getPaperSize = function () {
- if (!this.papersize)
- this.papersize = "A4";
- return (this.papersize);
- };
- /**
- * Set displayheight of all pages to height
- * @param height - Height in pixels
- */
- Print_Table.prototype.setHeight = function (height) {
- this.height = height;
- this.pages.forEach(function (page) { page.height = height; });
- };
- /**
- * Get displayheight
- * @returns Height in pixels
- */
- Print_Table.prototype.getHeight = function () {
- return (this.height);
- };
- /**
- * Set modevertical to either "alles" (meaning we show the full height of the page) or "kies" meaning the user can choose
- * @param more - Either "alles" or "kies"
- */
- Print_Table.prototype.setModeVertical = function (mode) {
- this.modevertical = (mode === "kies" ? "kies" : "alles");
- this.forceCorrectFigures();
- };
- /**
- * Get modevertical
- * @returns either "alles" or "kies"
- */
- Print_Table.prototype.getModeVertical = function () {
- this.forceCorrectFigures();
- return (this.modevertical);
- };
- /**
- * Checks that all start and stop position of pages are valid
- * For instance, the startx position should never be higher than the stopx.
- * In addition, the SVG always goes from left to right over the pages so the startx
- * of a new page cannot be lower than the stopx of the page before.
- */
- Print_Table.prototype.forceCorrectFigures = function () {
- var _this = this;
- if (!this.modevertical) {
- this.modevertical = "alles";
- }
- switch (this.modevertical) {
- case "kies":
- this.starty = Math.min(Math.max(0, this.starty), this.height);
- this.stopy = Math.min(Math.max(this.starty, this.stopy), this.height);
- break;
- default:
- this.starty = 0;
- this.stopy = this.height;
- }
- this.pages[this.pages.length - 1].stop = this.maxwidth;
- this.pages.forEach(function (page, index) {
- if (page.stop < 0)
- page.stop = 0;
- if (page.start < 0)
- page.start = 0;
- if (index > 0) {
- page.start = _this.pages[index - 1].stop;
- }
- if (page.stop > _this.maxwidth) {
- _this.pages[_this.pages.length - 1].stop = _this.maxwidth;
- }
- if (page.start > page.stop) {
- page.start = page.stop;
- }
- });
- };
- /**
- * Sets the maximum width of the SVG to be displayed.
- * As a general rule this equals the width of the SVG itself in pixels
- * @param maxwidth
- */
- Print_Table.prototype.setMaxWidth = function (maxwidth) {
- this.maxwidth = maxwidth;
- this.forceCorrectFigures();
- };
- /**
- * Gets the maximum width that can be displayed or printed
- * @returns maxwidth, as a general rule this equals the width of the SVG itsef in pixels
- */
- Print_Table.prototype.getMaxWidth = function () {
- return (this.maxwidth);
- };
- /**
- * Returns the starty position of the page that will be displayed or printed
- * @returns starty
- */
- Print_Table.prototype.getstarty = function () {
- this.forceCorrectFigures();
- return (this.starty);
- };
- /**
- * Returns the stopy position of the page that will be displayed or printed
- * @returns stopy
- */
- Print_Table.prototype.getstopy = function () {
- this.forceCorrectFigures();
- return (this.stopy);
- };
- /**
- * Sets the starty position of the page that will be displayed or printed
- * @param starty
- */
- Print_Table.prototype.setstarty = function (starty) {
- this.starty = starty;
- this.forceCorrectFigures;
- };
- /**
- * Sets the stopy position of the page that will be displayed or printed
- * @param starty
- */
- Print_Table.prototype.setstopy = function (stopy) {
- this.stopy = stopy;
- this.forceCorrectFigures;
- };
- /**
- * Sets the stopx position of one specific page to a desired value.
- * The function calls forceCorrectFigures() afterwards to ensure the natural flow of pages (left to right)
- * is respected. Note that stopx in the underlying Page_Info object is called stop and we cannot change that
- * anymore as the classes are used for serialization.
- * @param page - page number for which we want to set the stopx (starts counting at zero)
- * @param stop - stopx position to set
- */
- Print_Table.prototype.setStop = function (page, stop) {
- if (page > 0) {
- if (stop < this.pages[page - 1].stop)
- stop = this.pages[page - 1].stop;
- }
- if (page < this.pages.length - 1) {
- if (stop > this.pages[page + 1].stop)
- stop = this.pages[page + 1].stop;
- }
- if (stop > this.maxwidth)
- stop = this.maxwidth;
- this.pages[page].stop = stop;
- this.forceCorrectFigures();
- };
- /**
- * Automatically create pages based on pagemarkers
- */
- Print_Table.prototype.autopage = function () {
- /* Autopage uses some ratio's determined by the useful SVG drawing size on the PDF. This depends on the margins configured in print.js
- At present all of this is still hard-coded. Should become a function of print.js
-
- A4
-
- Height: 210-20-30-5-5 --> 150
- Width: 297-20 --> 277
- Ratio: 1.8467
-
- A3
-
- Height: 297-20-30-5-5 --> 237
- Width: 420-20 --> 400
- Ratio: 1.6878
- */
- var _this = this;
- //First set all pages to maximum to avoid that we bump into boundaries
- this.pages.forEach(function (page, index) {
- page.stop = _this.maxwidth;
- });
- var height = this.getstopy() - this.getstarty();
- var maxsvgwidth = height * (this.getPaperSize() == "A3" ? 1.6878 : 1.8467);
- var minsvgwidth = 3 / 4 * maxsvgwidth;
- var page = 0;
- var pos = 0;
- if (maxsvgwidth > 0) {
- while ((this.maxwidth - pos) > maxsvgwidth) { // The undivided part still does not fit on a page
- pos = this.pagemarkers.findMinDepth(pos + minsvgwidth, pos + maxsvgwidth).xpos;
- while (this.pages.length < page + 2)
- this.addPage();
- this.setStop(page, pos);
- page++;
- }
- }
- // The last page stops at the maximum size of the SVG
- this.setStop(page, this.maxwidth);
- // Delete unneeded pages at the end
- for (var i = this.pages.length - 1; i > page; i--) {
- this.deletePage(i);
- }
- };
- /**
- * Add a page
- */
- Print_Table.prototype.addPage = function () {
- var page_info;
- page_info = new Page_Info();
- page_info.height = this.height;
- page_info.start = this.pages[this.pages.length - 1].stop;
- page_info.stop = this.maxwidth;
- this.pages.push(page_info);
- };
- /**
- * Remove a page
- * @param page - number of the page to be removed, starting at 0
- */
- Print_Table.prototype.deletePage = function (page) {
- if (page == 0) {
- this.pages[1].start = 0;
- }
- else {
- this.pages[page - 1].stop = this.pages[page].stop;
- }
- this.pages.splice(page, 1);
- };
- /**
- * Display a Select box to choose papersize (A3 or A4)
- * The table is displayed in the HTMLElement div that is given as a parameter to the function.
- * If any manipulation is done by the user that would require redrawing the print preview, the redrawCallBack function is executed
- * from within this function
- * @param div - Existing HTMLElement where the table will be inserted
- * @param redrawCallBack - Callback function that ensures everything that needs to be redrawn is redrawn
- */
- Print_Table.prototype.insertHTMLselectPaperSize = function (div, redrawCallBack) {
- var _this = this;
- var select = document.createElement('select');
- select.id = 'select_papersize_input';
- var optionA4 = document.createElement('option');
- optionA4.value = 'A4';
- optionA4.textContent = 'A4';
- var optionA3 = document.createElement('option');
- optionA3.value = 'A3';
- optionA3.textContent = 'A3';
- if (this.papersize == "A3")
- optionA3.selected = true;
- else
- optionA4.selected = true;
- select.appendChild(optionA4);
- select.appendChild(optionA3);
- select.onchange = function (event) {
- _this.setPaperSize(event.target.value);
- redrawCallBack();
- };
- div.appendChild(select);
- };
- /**
- * Display a Select box to choose dpi (300 or 600)
- * The table is displayed in the HTMLElement div that is given as a parameter to the function.
- * If any manipulation is done by the user that would require redrawing the print preview, the redrawCallBack function is executed
- * from within this function
- * @param div - Existing HTMLElement where the table will be inserted
- * @param redrawCallBack - Callback function that ensures everything that needs to be redrawn is redrawn
- */
- Print_Table.prototype.insertHTMLselectdpi = function (div, redrawCallBack) {
- var select = document.createElement('select');
- select.id = "select_dpi_input";
- var option300 = document.createElement('option');
- option300.value = '300';
- option300.textContent = '300dpi (standaard)';
- var option600 = document.createElement('option');
- option600.value = '600';
- option600.textContent = '600dpi (beter maar trager)';
- if (typeof (structure.properties.dpi) == 'undefined')
- structure.properties.dpi = 300;
- if (structure.properties.dpi == 600)
- option600.selected = true;
- else
- option300.selected = true;
- select.appendChild(option300);
- select.appendChild(option600);
- select.onchange = function (event) {
- structure.properties.dpi = parseInt(event.target.value, 0);
- };
- div.appendChild(select);
- };
- /**
- * Display a Check box to decide if one wants to use autopage or not.
- * If autopage is enabled, we also recalculate the page boundaries
- * The checkbox is displayed in the HTMLElement div that is given as a parameter to the function.
- * If any manipulation is done by the user that would require redrawing the print preview, the redrawCallBack function is executed
- * from within this function
- * @param div - Existing HTMLElement where the table will be inserted
- * @param redrawCallBack - Callback function that ensures everything that needs to be redrawn is redrawn
- */
- Print_Table.prototype.insertHTMLcheckAutopage = function (div, redrawCallBack) {
- var _this = this;
- var checkbox = document.createElement('input');
- checkbox.type = 'checkbox';
- checkbox.id = 'autopage';
- checkbox.name = 'autopage';
- var label = document.createElement('label');
- label.htmlFor = 'autopage';
- label.textContent = "Handmatig over pagina's verdelen";
- if (this.enableAutopage) {
- this.setModeVertical("alles");
- this.autopage();
- }
- else {
- checkbox.checked = true;
- }
- div.append(checkbox);
- div.append(label);
- checkbox.onchange = function (event) {
- _this.enableAutopage = !event.target.checked;
- redrawCallBack();
- };
- };
- /**
- * Display a select box to choose the vertical mode.
- * If vertical mode is "kies", we also display input boxes to choose the starty and stopy positions.
- * The checkbox is displayed in the HTMLElement div that is given as a parameter to the function.
- * If any manipulation is done by the user that would require redrawing the print preview, the redrawCallBack function is executed
- * from within this function
- * @param div - Existing HTMLElement where the table will be inserted
- * @param redrawCallBack - Callback function that ensures everything that needs to be redrawn is redrawn
- */
- Print_Table.prototype.insertHTMLchooseVerticals = function (div, redrawCallBack) {
- var _this = this;
- var outstr = "";
- switch (this.modevertical) {
- case "kies":
- outstr += 'Hoogte ';
- outstr += ' StartY ';
- outstr += '';
- outstr += ' StopY ';
- outstr += '';
- break;
- case "alles":
- default:
- outstr += 'Hoogte ';
- }
- div.insertAdjacentHTML('beforeend', outstr);
- document.getElementById('select_modeVertical').onchange = function (event) {
- _this.setModeVertical(event.target.value);
- redrawCallBack();
- };
- if (this.modevertical == "kies") {
- document.getElementById('input_starty').onchange = function (event) {
- var starty = parseInt(event.target.value);
- if (isNaN(starty))
- starty = 0;
- _this.setstarty(starty);
- _this.forceCorrectFigures();
- redrawCallBack();
- };
- document.getElementById('input_stopy').onchange = function (event) {
- var stopy = parseInt(event.target.value);
- if (isNaN(stopy))
- stopy = _this.getHeight();
- ;
- _this.setstopy(stopy);
- _this.forceCorrectFigures();
- redrawCallBack();
- };
- }
- };
- /**
- * Display a button to force auto-pagination even when in manual mode
- * @param div - Existing HTMLElement where the table will be inserted
- * @param redrawCallBack - Callback function that ensures everything that needs to be redrawn is redrawn
- */
- Print_Table.prototype.insertHTMLsuggestXposButton = function (div, redrawCallBack) {
- var _this = this;
- var button = document.createElement('button');
- button.innerText = 'Suggereer X-posities';
- div.append(button);
- button.onclick = function () {
- _this.autopage();
- redrawCallBack();
- };
- };
- /**
- * Display a table where the user can choose start and stop positions for the x-coordinates in the SVG of each individual page
- * The table is displayed in the HTMLElement div that is given as a parameter to the function.
- * If any manipulation is done by the user that would require redrawing the print preview, the redrawCallBack function is executed
- * from within this function
- * @param div - Existing HTMLElement where the table will be inserted
- * @param redrawCallBack - Callback function that ensures everything that needs to be redrawn is redrawn
- */
- Print_Table.prototype.insertHTMLposxTable = function (div, redrawCallBack) {
- var _this = this;
- if (structure.print_table.enableAutopage)
- this.autopage();
- var outstr = "";
- var pagenum;
- outstr += '
";
- div.insertAdjacentHTML('beforeend', outstr);
- document.getElementById('Btn_Addpage').onclick = function () {
- _this.addPage();
- redrawCallBack();
- };
- document.querySelectorAll('button[id^="Btn_Deletepage_"]').forEach(function (button) {
- var match = button.id.match(/Btn_Deletepage_(\d+)/);
- if (match) {
- var page_1 = parseInt(match[1]);
- button.onclick = function () {
- _this.deletePage(page_1);
- redrawCallBack();
- };
- }
- });
- document.querySelectorAll('input[id^="input_stop_"]').forEach(function (input) {
- input.addEventListener('change', function (event) {
- var match = event.target.id.match(/input_stop_(\d+)/);
- if (match) {
- var page = parseInt(match[1]);
- var stop_1 = parseInt(event.target.value);
- _this.setStop(page, stop_1);
- redrawCallBack();
- }
- });
- });
- };
- return Print_Table;
-}());
-function HLDisplayPage() {
- structure.print_table.displaypage = parseInt(document.getElementById("id_select_page").value) - 1;
- printsvg();
-}
-function dosvgdownload() {
- var prtContent = document.getElementById("printsvgarea").innerHTML;
- var filename = document.getElementById("dosvgname").value;
- download_by_blob(prtContent, filename, 'data:image/svg+xml;charset=utf-8'); //Was text/plain
-}
-function getPrintSVGWithoutAddress(outSVG, page) {
- if (page === void 0) { page = structure.print_table.displaypage; }
- var scale = 1;
- var startx = structure.print_table.pages[page].start;
- var width = structure.print_table.pages[page].stop - startx;
- var starty = structure.print_table.getstarty();
- var height = structure.print_table.getstopy() - starty;
- var viewbox = '' + startx + ' ' + starty + ' ' + width + ' ' + height;
- var outstr = '';
- return (outstr);
-}
-function printsvg() {
- function generatePdf() {
- if (typeof (structure.properties.dpi) == 'undefined')
- structure.properties.dpi = 300;
- var svg = flattenSVGfromString(structure.toSVG(0, "horizontal").data);
- var pages = Array.from({ length: structure.print_table.pages.length }, function (_, i) { return i + 1; });
- var sitplanprint = structure.sitplan.toSitPlanPrint();
- printPDF(svg, structure.print_table, structure.properties, pages, document.getElementById("dopdfname").value, //filename
- document.getElementById("progress_pdf"), //HTML element where callback status can be given
- sitplanprint);
- }
- function renderPrintSVG(outSVG) {
- document.getElementById("printarea").innerHTML = '
' +
- getPrintSVGWithoutAddress(outSVG) +
- '
';
- }
- // First we generate an SVG image. We do this first because we need the size
- // We will display it at the end of this function
- var outSVG = new SVGelement();
- outSVG = structure.toSVG(0, "horizontal");
- var height = outSVG.yup + outSVG.ydown;
- var width = outSVG.xleft + outSVG.xright;
- structure.print_table.setHeight(height);
- structure.print_table.setMaxWidth(width + 10);
- // Then we display all the print options
- var outstr = "";
- var strleft = "";
- document.getElementById("configsection").innerHTML
- = '
'
- + ' ' // Generate PDF button comes here
- + ' ' // Selector to choose "A3" and "A4" comes here
- + ' ' // Selector for dpi 300 or 600 comes here
- + ' ' // Input box for filename of pdf document
- + ' ' // Area where status of pdf generation can be displayed
- + '
'
- + ' ' // Checkbox to choose if we want to auto paginate or not comes here
- + ' ' // An optional area to choose what part of the y-space of the image is shown
- + ' ' // A button to force auto pagination comes here
- + '
'
- + ' ' // Table with all startx and stopx comes here
- + '
'
- + '
'
- + '
Klik op de groene pijl om het schema over meerdere pagina\'s te printen en kies voor elke pagina de start- en stop-positie in het schema (in pixels).
'
- + '
Onderaan kan je bekijken welk deel van het schema op welke pagina belandt.
'
- + '
'
- + '
'
- + '
'
- + ' ';
- document.getElementById("configsection").insertAdjacentHTML('beforeend', outstr);
- structure.print_table.insertHTMLposxTable(document.getElementById('id_print_table'), printsvg);
- }
- strleft += '';
- strleft += 'Printvoorbeeld: Pagina (Enkel tekening, kies "Genereer PDF" om ook de tekstuele gegevens te zien)';
- strleft += '
';
- strleft += '
Sla tekening hieronder op als SVG en converteer met een ander programma naar PDF (bvb Inkscape).
';
- strleft += displayButtonPrintToPdf(); // This is only for the online version
- strleft += '';
- document.getElementById("configsection").insertAdjacentHTML('beforeend', strleft);
- // Finally we show the actual SVG
- renderPrintSVG(outSVG);
- toggleAppView('config');
-}
-var importExportUsingFileAPI = /** @class */ (function () {
- function importExportUsingFileAPI() {
- this.clear();
- //this.updateButtons();
- }
- importExportUsingFileAPI.prototype.clear = function () {
- this.saveNeeded = false;
- this.fileHandle = null;
- this.filename = null;
- };
- importExportUsingFileAPI.prototype.updateLastSaved = function () {
- var currentdate = new Date();
- this.lastsaved = currentdate.getHours().toString().padStart(2, '0') + ":" +
- currentdate.getMinutes().toString().padStart(2, '0') + ":" +
- currentdate.getSeconds().toString().padStart(2, '0');
- ;
- //If there is an object on the screen that needs updating, do it
- if (document.getElementById('exportscreen')) {
- showFilePage(); // Update the export screen if we are actually on the export screen
- }
- };
- importExportUsingFileAPI.prototype.setSaveNeeded = function (input) {
- var lastSaveNeeded = this.saveNeeded;
- this.saveNeeded = input;
- //if (input !== lastSaveNeeded) this.updateButtons();
- };
- importExportUsingFileAPI.prototype.readFile = function () {
- return __awaiter(this, void 0, void 0, function () {
- var _a, file, contents;
- return __generator(this, function (_b) {
- switch (_b.label) {
- case 0:
- _a = this;
- return [4 /*yield*/, window.showOpenFilePicker({
- types: [{
- description: 'Eendraadschema (.eds)',
- accept: { 'application/eds': ['.eds'] },
- }],
- })];
- case 1:
- _a.fileHandle = (_b.sent())[0];
- return [4 /*yield*/, this.fileHandle.getFile()];
- case 2:
- file = _b.sent();
- return [4 /*yield*/, file.text()];
- case 3:
- contents = _b.sent();
- this.filename = file.name;
- structure.properties.filename = file.name;
- this.setSaveNeeded(false);
- this.updateLastSaved(); // Needed because EDStoStructure whipes everything
- return [2 /*return*/, contents];
- }
- });
- });
- };
- importExportUsingFileAPI.prototype.saveAs = function (content) {
- return __awaiter(this, void 0, void 0, function () {
- var options, _a;
- return __generator(this, function (_b) {
- switch (_b.label) {
- case 0:
- options = {
- suggestedName: structure.properties.filename,
- types: [{
- description: 'Eendraadschema (.eds)',
- accept: { 'application/eds': ['.eds'] },
- }],
- startIn: 'documents' // Suggests the Documents folder
- };
- _a = this;
- return [4 /*yield*/, window.showSaveFilePicker(options)];
- case 1:
- _a.fileHandle = _b.sent();
- return [4 /*yield*/, this.saveFile(content, this.fileHandle)];
- case 2:
- _b.sent();
- return [2 /*return*/];
- }
- });
- });
- };
- ;
- importExportUsingFileAPI.prototype.saveFile = function (content, handle) {
- return __awaiter(this, void 0, void 0, function () {
- var writable;
- return __generator(this, function (_a) {
- switch (_a.label) {
- case 0: return [4 /*yield*/, handle.createWritable()];
- case 1:
- writable = _a.sent();
- return [4 /*yield*/, writable.write(content)];
- case 2:
- _a.sent();
- return [4 /*yield*/, writable.close()];
- case 3:
- _a.sent();
- this.filename = handle.name;
- structure.properties.filename = handle.name;
- this.setSaveNeeded(false);
- this.updateLastSaved();
- return [2 /*return*/];
- }
- });
- });
- };
- ;
- importExportUsingFileAPI.prototype.save = function (content) {
- return __awaiter(this, void 0, void 0, function () {
- return __generator(this, function (_a) {
- switch (_a.label) {
- case 0: return [4 /*yield*/, this.saveFile(content, this.fileHandle)];
- case 1:
- _a.sent();
- return [2 /*return*/];
- }
- });
- });
- };
- ;
- return importExportUsingFileAPI;
-}());
-var fileAPIobj = new importExportUsingFileAPI();
-/* FUNCTION importjson
-
- This is the callback function for the legacy filepicker if the file API is not available in the browser */
-var importjson = function (event) {
- var input = event.target;
- var reader = new FileReader();
- var text = "";
- reader.onload = function () {
- EDStoStructure(reader.result.toString());
- };
- reader.readAsText(input.files[0]);
-};
-var appendjson = function (event) {
- var input = event.target;
- var reader = new FileReader();
- var text = "";
- reader.onload = function () {
- importToAppend(reader.result.toString());
- };
- reader.readAsText(input.files[0]);
-};
-/* FUNCTION loadClicked()
-
- Gets called when a user wants to open a file. Checks if the fileAPI is available in the browser.
- If so, the fileAPI is used. If not, the legacy function importjson is called */
-function loadClicked() {
- return __awaiter(this, void 0, void 0, function () {
- var data;
- return __generator(this, function (_a) {
- switch (_a.label) {
- case 0:
- if (!window.showOpenFilePicker) return [3 /*break*/, 2];
- return [4 /*yield*/, fileAPIobj.readFile()];
- case 1:
- data = _a.sent();
- EDStoStructure(data);
- return [3 /*break*/, 3];
- case 2:
- document.getElementById('importfile').click();
- document.getElementById('importfile').value = "";
- _a.label = 3;
- case 3: return [2 /*return*/];
- }
- });
- });
-}
-/**
- * function importToAppendClicked()
- *
- * Vraagt om een EDS bestand op de machine te kiezen en voegt de inhoud toe aan het reeds geopende schema.
- * We gebruiken hier bewust niet de fileAPI aangezien die reeds gebruikt wordt voor het reeds geopende schema.
- */
-function importToAppendClicked() {
- return __awaiter(this, void 0, void 0, function () {
- return __generator(this, function (_a) {
- document.getElementById('appendfile').click();
- document.getElementById('appendfile').value = "";
- return [2 /*return*/];
- });
- });
-}
-/* FUNCTION upgrade_version
-
- Takes a structure, usually imported from json into javascript object, and performs a version upgrade if needed.
- as mystructure is passed by reference, all upgrades are done in-line.
-
-*/
-function upgrade_version(mystructure, version) {
- // At a certain moment (2023-01-11 to 2023-01-13) there was a bug in the systen so that files where accidentally outputed with props, without keys, but with version 1.
- // We correct for this below. If there are props and not keys but it still reads version 1, it should be interpreted as version 3.
- if ((version == 1) && (mystructure.length > 0) && (typeof (mystructure.data[0].keys) == 'undefined') && (typeof (mystructure.data[0].props) != 'undefined')) {
- version = 3;
- }
- /* Indien versie 1 moeten we vrije tekst elementen die niet leeg zijn 30 pixels breder maken.
- * Merk ook op dat versie 1 nog een key based systeem had met keys[0][2] het type
- * en keys[16][2] die aangeeft of vrije tekst al dan niet een kader bevat (verbruiker) of niet (zonder kader)
- */
- if (version < 2) {
- for (var i = 0; i < mystructure.length; i++) {
- // Breedte van Vrije tekst velden zonder kader met 30 verhogen sinds 16/12/2023
- if ((mystructure.data[i].keys[0][2] === "Vrije tekst") && (mystructure.data[i].keys[16][2] != "verbruiker")) {
- if (Number(mystructure.data[i].keys[22][2]) > 0)
- mystructure.data[i].keys[22][2] = String(Number(mystructure.data[i].keys[22][2]) + 30);
- else
- mystructure.data[i].keys[18][2] = "automatisch";
- if (mystructure.data[i].keys[16][2] != "zonder kader")
- mystructure.data[i].keys[16][2] = "verbruiker";
- }
- }
- }
- // In versie 2 heetten Contactdozen altijd nog Stopcontacten
- if (version < 3) {
- for (var i = 0; i < mystructure.length; i++) {
- if (mystructure.data[i].keys[0][2] === "Stopcontact")
- mystructure.data[i].keys[0][2] = "Contactdoos";
- if (mystructure.data[i].keys[0][2] === "Leeg")
- mystructure.data[i].keys[0][2] = "Aansluitpunt";
- }
- }
- // In versie 3 heetten Contactdozen ook soms nog Stopcontacten, maar niet altijd
- if (version == 3) {
- for (var i = 0; i < mystructure.length; i++) {
- if (mystructure.data[i].props.type === "Stopcontact")
- mystructure.data[i].props.type = "Contactdoos";
- }
- }
- //Vanaf versie 4 staan niet automatisch meer haakjes <> rond de benaming van borden. Indien kleiner dan versie 4 moeten we deze toevoegen
- if (version < 4) {
- if (version < 3) {
- for (var i = 0; i < mystructure.length; i++) {
- if ((mystructure.data[i].keys[0][2] === "Bord") && (mystructure.data[i].keys[10][2] !== ""))
- mystructure.data[i].keys[10][2] = '<' + mystructure.data[i].keys[10][2] + '>';
- }
- }
- else {
- for (var i = 0; i < mystructure.length; i++) {
- if ((mystructure.data[i].props.type === "Bord") && (mystructure.data[i].props.naam !== ""))
- mystructure.data[i].props.naam = '<' + mystructure.data[i].props.naam + '>';
- }
- }
- }
- //Algemene upgrade voor versies 3 tot en met 4
- if ((version >= 3) && (version <= 4)) {
- for (var i = 0; i < mystructure.length; i++) {
- if (mystructure.data[i].props.type === "Leeg")
- mystructure.data[i].props.type = "Aansluitpunt";
- }
- }
-}
-/* FUNCTION json_to_structure
-
- Takes a string in pure json and puts the content in a hierarchical list that is returned.
- The function can take an old structure that is to be cleaned as input (optional)
-
- Will perform a version upgrade in case the json is from an earlier version of the eendraadschema tool but this version upgrade will not be performed
- if version is set to 0. If version is not set to 0 it should be set to the verson of the json.
-
-*/
-function json_to_structure(text, oldstruct, version) {
- if (oldstruct === void 0) { oldstruct = null; }
- if (version === void 0) { version = 0; }
- // If a structure exists, clear it
- if (oldstruct != null)
- oldstruct.dispose(); // Clear the structure
- /* Read all data from disk in a javascript structure mystructure.
- * Afterwards we will gradually copy elements from this one into the official outstruct
- */
- var mystructure = JSON.parse(text);
- // upgrade if needed
- if (version != 0)
- upgrade_version(mystructure, version);
- /* We starten met het kopieren van data naar de eigenlijke outstruct.
- * Ook hier houden we er rekening mee dat in oude saves mogelijk niet alle info voorhanden was */
- var outstruct = new Hierarchical_List();
- // Kopieren van hoofd-eigenschappen
- if (typeof mystructure.properties != 'undefined') {
- if (typeof mystructure.properties.filename != "undefined")
- outstruct.properties.filename = mystructure.properties.filename;
- if (typeof mystructure.properties.owner != "undefined")
- outstruct.properties.owner = mystructure.properties.owner;
- if (typeof mystructure.properties.control != "undefined")
- outstruct.properties.control = mystructure.properties.control;
- if (typeof mystructure.properties.installer != "undefined")
- outstruct.properties.installer = mystructure.properties.installer;
- if (typeof mystructure.properties.info != "undefined")
- outstruct.properties.info = mystructure.properties.info;
- if (typeof mystructure.properties.info != "undefined")
- outstruct.properties.dpi = mystructure.properties.dpi;
- if (typeof mystructure.properties.currentView != "undefined")
- outstruct.properties.currentView = mystructure.properties.currentView;
- }
- // Kopieren van de paginatie voor printen
- if (typeof mystructure.print_table != "undefined") {
- outstruct.print_table.setHeight(mystructure.print_table.height);
- outstruct.print_table.setMaxWidth(mystructure.print_table.maxwidth);
- outstruct.print_table.setPaperSize(mystructure.print_table.papersize);
- outstruct.print_table.setModeVertical(mystructure.print_table.modevertical);
- outstruct.print_table.setstarty(mystructure.print_table.starty);
- outstruct.print_table.setstopy(mystructure.print_table.stopy);
- if (typeof mystructure.print_table.enableAutopage != "undefined") {
- outstruct.print_table.enableAutopage = mystructure.print_table.enableAutopage;
- }
- else {
- outstruct.print_table.enableAutopage = false;
- }
- for (var i = 0; i < mystructure.print_table.pages.length; i++) {
- if (i != 0)
- outstruct.print_table.addPage();
- outstruct.print_table.pages[i].height = mystructure.print_table.pages[i].height;
- outstruct.print_table.pages[i].start = mystructure.print_table.pages[i].start;
- outstruct.print_table.pages[i].stop = mystructure.print_table.pages[i].stop;
- }
- }
- // Kopieren van de situatieplannen
- if (typeof mystructure.sitplanjson != "undefined") {
- outstruct.sitplan = new SituationPlan();
- outstruct.sitplan.fromJsonObject(mystructure.sitplanjson);
- }
- /* Kopieren van de eigenschappen van elk element.
- * Keys voor versies 1 en 2 en props voor versie 3
- */
- for (var i = 0; i < mystructure.length; i++) {
- if ((version != 0) && (version < 3)) {
- outstruct.addItem(mystructure.data[i].keys[0][2]);
- outstruct.data[i].convertLegacyKeys(mystructure.data[i].keys);
- }
- else {
- outstruct.addItem(mystructure.data[i].props.type);
- Object.assign(outstruct.data[i].props, mystructure.data[i].props);
- }
- outstruct.data[i].parent = mystructure.data[i].parent;
- outstruct.active[i] = mystructure.active[i];
- outstruct.id[i] = mystructure.id[i];
- outstruct.data[i].id = mystructure.data[i].id;
- outstruct.data[i].indent = mystructure.data[i].indent;
- outstruct.data[i].collapsed = mystructure.data[i].collapsed;
- }
- // As we re-read the structure and it might be shorter then it once was (due to deletions) but we might still have the old high ID's, always take over the curid from the file
- outstruct.curid = mystructure.curid;
- // Sort the entire new structure
- outstruct.reSort();
- // Return the result
- return outstruct;
-}
-function loadFromText(text, version, redraw) {
- if (redraw === void 0) { redraw = true; }
- structure = json_to_structure(text, structure, version);
- if (redraw == true)
- topMenu.selectMenuItemByName('Eéndraadschema'); // Ga naar het bewerken scherm, dat zal automatisch voor hertekenen zorgen.
-}
-/**
- * Converteert een string in EDS formaat naar een json string.
- * De string kan eventueel eerst entropy gecodeerd en base64 encoded zijn.
- * De string kan ook een header hebben met een versie en een identificatie.
- *
- * @param {string} mystring - De string die uit een bestand of een json string is geladen.
- * @returns {Object} - Een object met twee attributen: text en version. Text is de json string en version is de versie van de string.
- */
-function EDStoJson(mystring) {
- var text = "";
- var version;
- /* If first 3 bytes read "EDS", it is an entropy coded file
- * The first 3 bytes are EDS, the next 3 bytes indicate the version
- * The next 4 bytes are decimal zeroes "0000"
- * thereafter is a base64 encoded data-structure
- *
- * If the first 3 bytes read "TXT", it is not entropy coded, nor base64
- * The next 7 bytes are the same as above.
- *
- * If there is no identifier, it is treated as a version 1 TXT
- * */
- if ((mystring.charCodeAt(0) == 69) && (mystring.charCodeAt(1) == 68) && (mystring.charCodeAt(2) == 83)) { //recognize as EDS
- /* Determine versioning
- * < 16/12/2023: Version 1, original key based implementation
- * 16/12/2023: Version 2, Introductie van automatische breedte voor bepaalde SVG-tekst
- * Vrije tekst van Version 1 moet 30 pixels groter gemaakt worden om nog mooi in het schema te passen
- * XX/01/2024: Version 3, Overgang van key based implementation naar props based implementation
- * functies convertLegacyKeys ingevoerd om oude files nog te lezen.
- */
- version = Number(mystring.substring(3, 6));
- if (isNaN(version))
- version = 1; // Hele oude files bevatten geen versie, ze proberen ze te lezen als versie 1
- mystring = atob(mystring.substring(10, mystring.length));
- var buffer = new Uint8Array(mystring.length);
- for (var i = 0; i < mystring.length; i++) {
- buffer[i - 0] = mystring.charCodeAt(i);
- }
- try { //See if the text decoder works, if not, we will do it manually (slower)
- var decoder = new TextDecoder("utf-8");
- text = decoder.decode(pako.inflate(buffer));
- }
- catch (error) { //Continue without the text decoder (old browsers)
- var inflated = pako.inflate(buffer);
- text = "";
- for (var i = 0; i < inflated.length; i++) {
- text += String.fromCharCode(inflated[i]);
- }
- }
- }
- else if ((mystring.charCodeAt(0) == 84) && (mystring.charCodeAt(1) == 88) && (mystring.charCodeAt(2) == 84)) { //recognize as TXT
- version = Number(mystring.substring(3, 6));
- if (isNaN(version))
- version = 3;
- text = mystring.substring(10, mystring.length);
- }
- else { // Very old file without header
- text = mystring;
- version = 1;
- }
- //Return an object with the text and the version
- return { text: text, version: version };
-}
-/* FUNCTION EDStoStructure
-
- Starts from a string that can be loaded from disk or from a file and is in EDS-format.
- puts the content in the javascript structure called "structure".
- Will redraw everything if the redraw flag is set.
-
-*/
-function EDStoStructure(mystring, redraw) {
- if (redraw === void 0) { redraw = true; }
- var JSONdata = EDStoJson(mystring);
- // Dump the json in into the structure and redraw if needed
- loadFromText(JSONdata.text, JSONdata.version, redraw);
- // Clear the undo stack and push this one on top
- undostruct.clear();
- undostruct.store();
- // Scroll to top left for the SVG and HTML, this can only be done at the end because "right col" has to actually be visible
- var leftelem = document.getElementById("left_col");
- if (leftelem != null) {
- leftelem.scrollTop = 0;
- leftelem.scrollLeft = 0;
- }
- var rightelem = document.getElementById("right_col");
- if (rightelem != null) {
- rightelem.scrollTop = 0;
- rightelem.scrollLeft = 0;
- }
-}
-function importToAppend(mystring, redraw) {
- if (redraw === void 0) { redraw = true; }
- var JSONdata = EDStoJson(mystring);
- var structureToAppend = json_to_structure(JSONdata.text, null, JSONdata.version);
- //get the Maximal ID in array structure.id and call it maxID
- var maxID = 0;
- for (var i = 0; i < structure.id.length; i++) {
- if (structure.id[i] > maxID)
- maxID = structure.id[i];
- }
- //then increase the ID's in structureToAppend accordingly
- for (var i = 0; i < structureToAppend.id.length; i++) {
- structureToAppend.id[i] += maxID;
- structureToAppend.data[i].id += maxID;
- if (structureToAppend.data[i].parent != 0) {
- structureToAppend.data[i].parent += maxID;
- }
- }
- structure.curid += structureToAppend.curid;
- //then merge information for the eendraadschema
- structure.length = structure.length + structureToAppend.length;
- structure.active = structure.active.concat(structureToAppend.active);
- structure.id = structure.id.concat(structureToAppend.id);
- structure.data = structure.data.concat(structureToAppend.data);
- //update the sourcelist
- structure.data.forEach(function (item) {
- item.sourcelist = structure;
- });
- //then set the printer to autopage
- structure.print_table.enableAutopage = true;
- //then merge the situation plans but only if both exist
- if (structure.sitplan != null) {
- if (structureToAppend.sitplan != null) {
- // Eerst oude situationplanview leeg maken, anders blijven oude div's hangen
- if (structure.sitplanview != null)
- structure.sitplanview.dispose();
- // dan nieuw situationplan maken en bij openen van het schema zal automatisch een nieuw situationplanview gecreëerd wordne
- structure.sitplanjson = structure.sitplan.toJsonObject();
- structureToAppend.sitplanjson = structureToAppend.sitplan.toJsonObject();
- for (var i = 0; i < structureToAppend.sitplanjson.elements.length; i++) {
- if (structureToAppend.sitplanjson.elements[i].electroItemId != null)
- structureToAppend.sitplanjson.elements[i].electroItemId += maxID;
- structureToAppend.sitplanjson.elements[i].page += structure.sitplanjson.numPages;
- }
- if ((structure.sitplanjson != null) && (structureToAppend.sitplanjson != null)) {
- structure.sitplanjson.numPages += structureToAppend.sitplanjson.numPages;
- structure.sitplanjson.elements = structure.sitplanjson.elements.concat(structureToAppend.sitplanjson.elements);
- }
- structure.sitplan.fromJsonObject(structure.sitplanjson);
- structure.sitplanjson = null;
- }
- }
- structure.reSort();
- undostruct.store();
- //then remove the pointer from structureToAppend and let the garbage collector do its work
- structureToAppend = null;
- //redraw if needed
- if (redraw)
- topMenu.selectMenuItemByName('Eéndraadschema');
-}
-function structure_to_json() {
- // Remove some unneeded data members that would only inflate the size of the output file
- for (var _i = 0, _a = structure.data; _i < _a.length; _i++) {
- var listitem = _a[_i];
- listitem.sourcelist = null;
- }
- var swap = structure.print_table.pagemarkers;
- var swap2 = structure.sitplan;
- var swap3 = structure.sitplanview;
- structure.print_table.pagemarkers = null;
- if (structure.sitplan != null)
- structure.sitplanjson = structure.sitplan.toJsonObject();
- structure.sitplan = null;
- structure.sitplanview = null;
- // Create the output structure in uncompressed form
- var text = JSON.stringify(structure);
- // Put the removed data members back
- for (var _b = 0, _c = structure.data; _b < _c.length; _b++) {
- var listitem = _c[_b];
- listitem.sourcelist = structure;
- }
- structure.print_table.pagemarkers = swap;
- structure.sitplan = swap2;
- structure.sitplanview = swap3;
- // Remove sitplanjson again
- structure.sitplanjson = null;
- return (text);
-}
-/** FUNCTION download_by_blob
- *
- * Downloads an EDS file to the user's PC
- *
- */
-function download_by_blob(text, filename, mimeType) {
- var element = document.createElement('a');
- if (navigator.msSaveBlob) {
- navigator.msSaveBlob(new Blob([text], {
- type: mimeType
- }), filename);
- }
- else if (URL && 'download' in element) {
- var uriContent = URL.createObjectURL(new Blob([text], { type: mimeType }));
- element.setAttribute('href', uriContent);
- element.setAttribute('download', filename);
- element.style.display = 'none';
- document.body.appendChild(element);
- element.click();
- setTimeout(function () { return document.body.removeChild(element); }, 1000); // 1-second delay
- }
- else {
- this.location.go("".concat(mimeType, ",").concat(encodeURIComponent(text)));
- }
-}
-/* FUNCTION showFilePage
-
- Shows the File-Page. It will look different depending on whether the browser supports the file API or not
-
-*/
-function showFilePage() {
- var strleft = ''; //We need the id to check elsewhere that the screen is open
- strleft += "\n
\n
\n
\n Openen\n
\n
\n
\n
\n
\n
\n
\n \n
\n
\n Click op \"openen\" en selecteer een eerder opgeslagen EDS bestand.\n
\n
\n
\n
\n
\n
\n
\n
\n
\n Opslaan\n
\n
\n
\n
\n ";
- if (window.showOpenFilePicker) { // Use fileAPI
- strleft += '
';
- strleft += 'U kan het schema opslaan op uw lokale harde schijf voor later gebruik. De standaard-naam is eendraadschema.eds. U kan deze wijzigen door links op "wijzigen" te klikken. ';
- strleft += 'Klik vervolgens op "opslaan" en volg de instructies van uw browser. ';
- strleft += 'In de meeste gevallen zal uw browser het bestand automatisch plaatsen in de Downloads-folder tenzij u uw browser instelde dat die eerst een locatie moet vragen.
';
- strleft += 'Eens opgeslagen kan het schema later opnieuw geladen worden door in het menu "openen" te kiezen en vervolgens het bestand op uw harde schijf te selecteren.';
- strleft += '
';
- strleft += PROP_GDPR(); //Function returns empty for GIT version, returns GDPR notice when used online.
- strleft += '
';
- }
- strleft += "\n
\n
\n
\n ";
- strleft += "\n
\n
\n
\n Samenvoegen\n
\n
\n
\n
\n
\n
\n
\n \n
\n
\n Open een tweede EDS bestand en voeg de inhoud toe aan het huidige EDS bestand. \n Voegt de \u00E9\u00E9ndraadschema's samen en voegt eveneens pagina's toe aan het situatieschema als dat nodig is.
\n Opgelet! Het is aanbevolen uw werk op te slaan alvorens deze functie te gebruiken!\n
\n Een volledige handleiding is beschikbaar in PDF formaat.\n Klik links om deze in een ander venster te openen.\n \n Het programma is in volle ontwikkeling dus delen van de handleiding zijn\n mogelijk ietwat verouderd. \n
\n
\n
\n
\n \n
\n
\n Specifiek voor het werken met het situatieschema werd een ander korter document opgesteld.\n Klik link om deze in een ander venster te openen.\n
\n
\n
\n
\n
\n
";
- document.getElementById("configsection").innerHTML = strleft;
- toggleAppView('config');
- document.getElementById('Btn_downloadManual').onclick = function () { window.open('Documentation/edsdoc.pdf', '_blank'); };
- document.getElementById('Btn_downloadSitPlanManual').onclick = function () { window.open('Documentation/sitplandoc.pdf', '_blank'); };
-}
-var ContextMenu = /** @class */ (function () {
- /**
- * Constructor voor het initialiseren van het contextmenu-element.
- * @param div - Het HTML-element waaraan het contextmenu wordt toegevoegd.
- */
- function ContextMenu(div) {
- if (div === void 0) { div = document.body; }
- this.menuItems = [];
- this.menuElement = null;
- this.menuElement = document.createElement('div');
- this.menuElement.className = 'context-menu';
- div.appendChild(this.menuElement);
- }
- /**
- * Wis alle menu-items.
- */
- ContextMenu.prototype.clearMenu = function () {
- this.menuItems = [];
- if (this.menuElement) {
- this.menuElement.innerHTML = '';
- }
- };
- /**
- * Voeg een menu-item toe met een optionele sneltoets.
- * @param label - De tekstlabel van het menu-item.
- * @param callback - De functie die wordt aangeroepen bij het klikken op het menu-item.
- * @param shortcut - De optionele sneltoets voor het menu-item.
- */
- ContextMenu.prototype.addMenuItem = function (label, callback, shortcut) {
- this.menuItems.push({ label: label, shortcut: shortcut, callback: callback });
- };
- /**
- * Voeg een scheidingslijn toe aan het menu.
- */
- ContextMenu.prototype.addLine = function () {
- this.menuItems.push({ label: 'separator', callback: function () { } });
- };
- /**
- * Render de menu-items.
- */
- ContextMenu.prototype.renderMenu = function () {
- var _this = this;
- if (this.menuElement) {
- this.menuElement.innerHTML = '';
- this.menuItems.forEach(function (item, index) {
- if (item.label === 'separator') {
- var separator = document.createElement('hr');
- separator.className = 'context-menu-separator';
- _this.menuElement.appendChild(separator);
- }
- else {
- var menuItem = document.createElement('div');
- menuItem.className = 'context-menu-item';
- var labelElement = document.createElement('span');
- labelElement.textContent = item.label;
- var shortcutElement = document.createElement('span');
- shortcutElement.textContent = item.shortcut || '';
- shortcutElement.className = 'context-menu-shortcut';
- menuItem.appendChild(labelElement);
- menuItem.appendChild(shortcutElement);
- menuItem.addEventListener('click', function () {
- item.callback();
- _this.hide();
- });
- _this.menuElement.appendChild(menuItem);
- }
- });
- }
- };
- /**
- * Toon het contextmenu op de locatie van de muisklik.
- * @param event - Het muisgebeurtenisobject.
- */
- ContextMenu.prototype.show = function (event) {
- if (this.menuElement) {
- this.renderMenu();
- this.menuElement.style.display = 'block';
- var clientX = event.clientX, clientY = event.clientY;
- var innerWidth_1 = window.innerWidth, innerHeight_1 = window.innerHeight;
- var _a = this.menuElement, offsetWidth = _a.offsetWidth, offsetHeight = _a.offsetHeight;
- var left = clientX;
- var top_1 = clientY;
- if (left + offsetWidth > innerWidth_1) {
- left = innerWidth_1 - offsetWidth;
- }
- if (top_1 + offsetHeight > innerHeight_1) {
- top_1 = innerHeight_1 - offsetHeight;
- }
- this.menuElement.style.left = "".concat(left, "px");
- this.menuElement.style.top = "".concat(top_1, "px");
- }
- };
- /**
- * Verberg het contextmenu.
- */
- ContextMenu.prototype.hide = function () {
- if (this.menuElement) {
- this.menuElement.style.display = 'none';
- }
- };
- return ContextMenu;
-}());
-/**
- * Class gebruikt in SituationPlanView om te zoeken naar electroitems op basis van de kringnaam.
- * Dit laat toe items to selecteren uit het volledige eendraadschema en ze te plaatsen op het situatieschema.
- *
- * Deze class refereert naar de volgende globale variabelen:
- * - structure
- */
-var ElectroItemZoeker = /** @class */ (function () {
- /**
- * Constructor van de ElectroItemZoeker.
- *
- * Initialiseert de lijst van alle toegestane ElectroItems in het situatieplan.
- */
- function ElectroItemZoeker() {
- this.excludedTypes = ['Aansluiting', 'Bord', 'Kring', 'Domotica', 'Domotica module (verticaal)',
- 'Domotica gestuurde verbruiker', 'Leiding', 'Splitsing', 'Verlenging',
- 'Vrije ruimte', 'Meerdere verbruikers'];
- this.data = [];
- this.borden = [];
- this.reCalculate();
- }
- /**
- * Geeft de lijst van alle toegestane ElectroItems in het situatieplan retour.
- * @returns {Object[]} een lijst van objecten met de volgende structuur:
- * {id: number, kringnaam: string, adres: string, type: string}
- */
- ElectroItemZoeker.prototype.getData = function () {
- return this.data;
- };
- /**
- * Geeft de lijst van alle Borden in het eendraadschema.
- * @returns {Object[]} een lijst van objecten met de volgende structuur:
- * {id: number, naam: string}
- *
- * Indien de originele naam null is of enkel uit spaties bestaat wordt als naam "Bord" meegegeven
- */
- ElectroItemZoeker.prototype.getBorden = function () {
- return this.borden;
- };
- /**
- * Geeft een lijst van alle unieke kringnamen retour uit de lijst van ElectroItems.
- * @returns {string[]} een lijst van unieke kringnamen.
- */
- ElectroItemZoeker.prototype.getUniqueKringnaam = function () {
- return Array.from(new Set(this.data.map(function (x) { return x.kringnaam; })));
- };
- /**
- * Geeft een lijst van alle ElectroItems retour die behoren tot de kring met de naam 'kringnaam'.
- * @param {string} kringnaam - de naam van de kring.
- * @returns {Object[]} een lijst van objecten met de volgende structuur:
- * {id: number, adres: string, type: string}
- */
- ElectroItemZoeker.prototype.getElectroItemsByKring = function (kringnaam) {
- return this.data.filter(function (x) { return x.kringnaam === kringnaam; }).map(function (x) { return ({ id: x.id, adres: x.adres, type: x.type }); });
- };
- /**
- * Rekent de lijst van alle toegestane ElectroItems opnieuw uit.
- *
- * Deze methode wordt gebruikt om de lijst van ElectroItems te vullen die in het situatieplan gebruikt mogen worden.
- * De lijst wordt opnieuw uitgerekend door de volgende stappen:
- * 1. Doorlopen alle actieve ElectroItems in de structuur.
- * 2. Voor elke ElectroItem worden de kringnaam en het type bepaald.
- * 3. Als de kringnaam niet leeg is en het type niet voorkomt in de lijst van uitgesloten types, dan wordt de ElectroItem toegevoegd aan de lijst.
- * 4. De ElectroItem wordt toegevoegd met de volgende structuur: {id: number, kringnaam: string, adres: string, type: string}
- *
- * Er wordt eveneens een lijst van borden gemaakt.
- */
- ElectroItemZoeker.prototype.reCalculate = function () {
- this.data = [];
- this.borden = [];
- for (var i = 0; i < structure.length; i++) {
- if (structure.active[i]) {
- var id = structure.id[i];
- var electroItem = structure.data[i];
- if (electroItem == null)
- continue;
- var type = electroItem.getType();
- if (type == 'Bord') {
- var myName = electroItem.props.naam;
- if ((myName == null) || (myName.trim() == ''))
- myName = 'Bord';
- this.borden.push({ id: id, naam: myName });
- }
- else {
- var kringnaam = structure.findKringName(id).trim();
- if (kringnaam != '') {
- if ((type != null) && (this.excludedTypes.indexOf(type) === -1)) {
- var adres = electroItem.getReadableAdres();
- this.data.push({ id: id, kringnaam: kringnaam, adres: adres, type: type });
- }
- }
- }
- }
- }
- };
- return ElectroItemZoeker;
-}());
-/**
- * Functie die de breedte en hoogte van een rechthoek als invoer neemt, evenals een rotatie rond het midden van de rechthoek.
- * De functie retourneert de breedte en hoogte van de kleinste rechthoek die de geroteerde rechthoek omsluit met zijden langs de X- en Y-assen.
- */
-function getRotatedRectangleSize(width, height, rotation) {
- var rotationInRadians = rotation * Math.PI / 180;
- var cos = Math.cos(rotationInRadians);
- var sin = Math.sin(rotationInRadians);
- var rotatedWidth = Math.abs(width * cos) + Math.abs(height * sin);
- var rotatedHeight = Math.abs(width * sin) + Math.abs(height * cos);
- return { width: rotatedWidth, height: rotatedHeight };
-}
-/**
- * Functie die de breedte en hoogte van een rechthoek als invoer neemt, evenals een rotatie rond het midden van de rechthoek.
- * De functie retourneert de breedte en hoogte van de rechthoek die voldoet aan de volgende eigenschappen:
- * - De zijden zijn parallel aan de X-as en de Y-as.
- * - De rechthoek snijdt de X-as en Y-as in dezelfde punten als de originele geroteerde rechthoek
- *
- * Deze functie kan gebruikt worden om de locatie van labels te bepalen.
- */
-function getXYRectangleSize(width, height, rotation) {
- rotation = Math.abs(rotation) % 180;
- if (rotation > 90)
- rotation = 180 - rotation;
- var rotationInRadians = rotation * Math.PI / 180;
- var cos = Math.cos(rotationInRadians);
- var sin = Math.sin(rotationInRadians);
- return { width: Math.min(width / cos, height / sin), height: Math.min(width / sin, height / cos) };
-}
-/**
- * Cache het resultaat van getPixelsPerMillimeter() om de overhead van het maken en verwijderen van een DOM-element bij elke oproep te voorkomen.
- */
-var cachedPixelsPerMillimeter = null;
-/**
- * Berekent het aantal pixels in een millimeter op het huidige scherm.
- * Maakt gebruik van een cache om de overhead van het maken en verwijderen van een DOM-element bij elke oproep te voorkomen.
- * @returns {number} Het aantal pixels in een millimeter.
- */
-function getPixelsPerMillimeter() {
- if (cachedPixelsPerMillimeter === null) {
- var div = document.createElement('div');
- div.style.width = '10mm';
- div.style.position = 'absolute';
- document.body.appendChild(div);
- var widthInPixels = div.offsetWidth;
- document.body.removeChild(div);
- cachedPixelsPerMillimeter = widthInPixels / 10;
- }
- return cachedPixelsPerMillimeter;
-}
-/**
- * Class that helps with dragging a box on the situation plan view.
- * It keeps track of the start position of the drag and the zoomfactor.
- */
-var MouseDrag = /** @class */ (function () {
- function MouseDrag() {
- this.startOffsetLeft = 0;
- this.startOffsetTop = 0;
- this.hassMoved = false;
- }
- /**
- * Start the drag.
- * @param mouseX The x position of the mouse when the drag starts.
- * @param mouseY The y position of the mouse when the drag starts.
- * @param startOffsetLeft The left position of the box when the drag starts.
- * @param startOffsetTop The top position of the box when the drag starts.
- */
- MouseDrag.prototype.startDrag = function (mouseX, mouseY, startOffsetLeft, startOffsetTop) {
- if (mouseX === void 0) { mouseX = 0; }
- if (mouseY === void 0) { mouseY = 0; }
- if (startOffsetLeft === void 0) { startOffsetLeft = 0; }
- if (startOffsetTop === void 0) { startOffsetTop = 0; }
- this.startOffsetLeft = startOffsetLeft;
- this.startOffsetTop = startOffsetTop;
- this.hassMoved = false;
- var menuHeight = parseFloat(getComputedStyle(document.documentElement).getPropertyValue('--menu-height'));
- var ribbonHeight = parseFloat(getComputedStyle(document.documentElement).getPropertyValue('--ribbon-height'));
- var sideBarWidth = parseFloat(getComputedStyle(document.documentElement).getPropertyValue('--sideBarWidth'));
- this.startPaperPos = structure.sitplanview.canvasPosToPaperPos(mouseX - sideBarWidth, mouseY - menuHeight - ribbonHeight);
- };
- /**
- * Return the new left and top position of the box based on the current mouse position.
- * @param mouseX The current x position of the mouse.
- * @param mouseY The current y position of the mouse.
- * @returns An object with the new left and top position of the box.
- */
- MouseDrag.prototype.returnNewLeftTop = function (mousex, mousey) {
- if (mousex === void 0) { mousex = 0; }
- if (mousey === void 0) { mousey = 0; }
- var menuHeight = parseFloat(getComputedStyle(document.documentElement).getPropertyValue('--menu-height'));
- var ribbonHeight = parseFloat(getComputedStyle(document.documentElement).getPropertyValue('--ribbon-height'));
- var sideBarWidth = parseFloat(getComputedStyle(document.documentElement).getPropertyValue('--sideBarWidth'));
- var stopPaperPos = structure.sitplanview.canvasPosToPaperPos(mousex - sideBarWidth, mousey - menuHeight - ribbonHeight);
- if (stopPaperPos.x != this.startPaperPos.x || stopPaperPos.y != this.startPaperPos.y)
- this.hassMoved = true;
- return ({
- left: (stopPaperPos.x - this.startPaperPos.x) + this.startOffsetLeft,
- top: (stopPaperPos.y - this.startPaperPos.y) + this.startOffsetTop
- });
- };
- return MouseDrag;
-}());
-/**
- * Volledig overzicht van een situatieplan.
- * Werd gebouwd voor gebruik in de browser maar is redelijk browser-agnostic.
- * De effectieve code om te interageren met de browser zelf zit in class SituationPlanView.
- *
- * Deze class refereert naar de volgende globale variabelen:
- * - structure
- */
-var SituationPlan = /** @class */ (function () {
- function SituationPlan() {
- this.activePage = 1; // We houden deze bij in situationplan zodat ook wijzigingen van pagina's worden opgeslagen
- this.numPages = 1;
- this.elements = [];
- this.defaults = {
- fontsize: 11,
- scale: SITPLANVIEW_DEFAULT_SCALE,
- rotate: 0
- };
- }
- /**
- * Reset alle waarden van het situatieplan naar hun default waarde.
- * Gebruik deze functie om alle data te wissen en het situatieplan opnieuw te beginnen.
- */
- SituationPlan.prototype.dispose = function () {
- this.elements = [];
- this.numPages = 1;
- this.activePage = 1;
- this.defaults = {
- fontsize: 11,
- scale: SITPLANVIEW_DEFAULT_SCALE,
- rotate: 0
- };
- };
- /**
- * Workaround om de private variabele elements te kunnen gebruiken in friend classs
- * @returns {SituationPlanElement[]} De elementen van het situatieplan
- */
- SituationPlan.prototype.getElements = function () {
- return this.elements;
- };
- /**
- * SituationPlanElement toevoegen aan het situatieplan
- * @param element
- * @returns {void}
- */
- SituationPlan.prototype.addElement = function (element) {
- this.elements.push(element);
- };
- /**
- * Laad een SVG-inhoud vanuit een bestand en voegt deze toe aan het situatieplan.
- * De SVG werd geselecteerd in een fileInput HTML element en komt binnen via het event in de functie header.
- *
- * @param {any} event Het event dat aangaf dat er een bestand was gekozen om te uploaden.
- * @param {number} page Het pagina-nummer van het element in het situatieplan.
- * @param {number} posx De x-coordinaat van het element in het situatieplan.
- * @param {number} posy De y-coordinaat van het element in het situatieplan.
- * @param {() => void} callback Een callback-functie die wordt aangeroepen wanneer het element is toegevoegd.
- * @returns {SituationPlanElement} Het element dat is toegevoegd.
- */
- SituationPlan.prototype.addElementFromFile = function (event, page, posx, posy, callback) {
- var element = new SituationPlanElement();
- element.setVars({ page: page, posx: posx, posy: posy, labelfontsize: this.defaults.fontsize, scale: this.defaults.scale, rotate: this.defaults.rotate });
- element.importFromFile(event, callback);
- this.elements.push(element);
- return element;
- };
- /**
- * Creëer een nieuw element in het situatieplan dat gelinkt is aan een Electro_Item
- *
- * @param {number} electroItemId Het ID van de Electro_Item.
- * @param {number} page Het pagina-nummer van het element in het situatieplan.
- * @param {number} posx De x-coordinaat van het element in het situatieplan.
- * @param {number} posy De y-coordinaat van het element in het situatieplan.
- * @param {AdresType} adrestype Het type van het adres, bijvoorbeeld 'manueel'.
- * AdresType wordt definieerd in SituationPlanElement.ts
- * @param {string} adres Het adres.
- * @param {AdresLocation} adreslocation De locatie van het adres in het label ('boven' of 'onder' of 'rechts' of 'links').
- * AdresLocation wordt definieerd in SituationPlanElement.ts
- * @param {number} labelfontsize De lettergrootte van het label.
- * @param {number} scale De schaal van het element.
- * @param {number} rotate De rotatie van het element.
- * @returns {SituationPlanElement} Het element dat is toegevoegd.
- */
- SituationPlan.prototype.addElementFromElectroItem = function (electroItemId, page, posx, posy, adrestype, adres, adreslocation, labelfontsize, scale, rotate) {
- var electroItem = structure.getElectroItemById(electroItemId);
- if (!electroItem)
- return null;
- var element = electroItem.toSituationPlanElement();
- Object.assign(element, { page: page, posx: posx, posy: posy, labelfontsize: labelfontsize, scale: scale, rotate: rotate });
- element.setElectroItemId(electroItemId);
- element.setAdres(adrestype, adres, adreslocation);
- this.elements.push(element);
- return element;
- };
- /**
- * Verwijder een element van het situatieplan.
- * In principe is er altijd maar één maar de functie gebruikt recursie in het geval er meerdere zouden zijn
- * om ze allemaal te verwijderen.
- *
- * @param {SituationPlanElement} element Het element dat verwijderd moet worden.
- * @returns {void}
- */
- SituationPlan.prototype.removeElement = function (element) {
- var index = this.elements.indexOf(element);
- if (index === -1)
- return;
- this.elements.splice(index, 1);
- if (element.boxref != null)
- element.boxref.remove();
- if (element.boxlabelref != null)
- element.boxlabelref.remove();
- this.removeElement(element); // Recurse in het geval er meerdere zouden zijn maar dit zou niet mogen gebeuren
- };
- /**
- * Zorgt ervoor dat alle elementen in het situatieplan een link hebben naar
- * het eendraadschema.
- *
- * Als een element in het situatieplan verwijst naar een symbool dat niet langer in
- * het eendraadschema zit, wordt het element verwijderd uit het situatieplan.
- */
- SituationPlan.prototype.syncToEendraadSchema = function () {
- for (var _i = 0, _a = this.elements; _i < _a.length; _i++) {
- var element = _a[_i];
- //Indien een symbool niet langer in het eendraadschema zit moet het ook uit het situatieplan verwijderd worden
- //We kunnen hier niet de functie isEendraadSchemaSymbool of getElectroItemById gebruiken want die zorgen
- //ervoor dat onderstaande altijd false geeft als de symbolen niet langer in het eendraadschema zitten waardoor
- //de cleanup die nodig is niet gebeurd.
- if ((element.electroItemId != null) && (structure.getElectroItemById(element.getElectroItemId()) == null)) {
- this.removeElement(element);
- this.syncToEendraadSchema();
- return; // Start opnieuw en stop na recursie
- }
- }
- };
- /**
- * Sorteer de elementen in het situatieplan op basis van de z-index van hun boxref elementen in de DOM.
- * Elementen met een `null` `boxref` worden naar het einde van de lijst verplaatst.
- *
- * Het sorteren is nodig om ervoor te zorgen dat bij het printen wanneer lineair door de elementen wordt gegaan
- * de elementen in de juiste volgorde worden gestacked.
- *
- * @returns {void}
- */
- SituationPlan.prototype.orderByZIndex = function () {
- //if (structure.sitplanview == null) return;
- this.elements.sort(function (a, b) {
- var asort = (((a.boxref == null) || (a.boxref.style.zIndex === "")) ? 0 : parseInt(a.boxref.style.zIndex));
- var bsort = (((b.boxref == null) || (b.boxref.style.zIndex === "")) ? 0 : parseInt(b.boxref.style.zIndex));
- return asort - bsort;
- });
- };
- /**
- * Initialiseer het situatieplan vanuit een json-object.
- *
- * @param {Object} json Het json-object dat het situatieplan bevat.
- * Het object bevat een 'numPages' property dat het aantal pagina's in het situatieplan aangeeft.
- * Verder bevat het een 'elements' property dat een array is van json-objecten die elk een element in het situatieplan vertegenwoordigen.
- * Elke json-object in de array bevat de properties van een SituationPlanElement.
- * @returns {void}
- */
- SituationPlan.prototype.fromJsonObject = function (json) {
- this.dispose();
- if (json.numPages !== undefined) {
- this.numPages = json.numPages;
- }
- else {
- this.numPages = 1;
- }
- if (json.activePage !== undefined) {
- this.activePage = json.activePage;
- }
- else {
- this.activePage = 1;
- }
- if (json.defaults !== undefined) {
- Object.assign(this.defaults, json.defaults);
- }
- if (Array.isArray(json.elements)) {
- this.elements = json.elements.map(function (element) {
- var newElement = new SituationPlanElement();
- newElement.fromJsonObject(element);
- return newElement;
- });
- }
- else {
- this.elements = [];
- }
- };
- /**
- * Converteer het situatieplan naar een JSON-object dat gebruikt kan worden
- * voor opslaan in lokale storage of voor versturen naar de server.
- *
- * @returns {any} Het JSON-object dat het situatieplan bevat.
- */
- SituationPlan.prototype.toJsonObject = function () {
- this.orderByZIndex();
- var elements = [];
- for (var _i = 0, _a = this.elements; _i < _a.length; _i++) {
- var element = _a[_i];
- elements.push(element.toJsonObject());
- }
- return { numPages: this.numPages, activePage: this.activePage, defaults: this.defaults, elements: elements };
- };
- /**
- * Converteer het situatieplan naar een formaat dat gebruikt kan worden voor printen.
- *
- * @param {boolean} fitToPage Indien `true` dan wordt de pagina automatisch aangepast om alle elementen te laten passen.
- * Als `false` dan wordt de pagina in de originele grootte gebruikt.
- * @returns {any} Het formaat van het situatieplan dat gebruikt kan worden voor printen.
- * Dit is een javascript object met structuur
- * {
- * numpages: number,
- * pages: [
- * {
- * svg: string,
- * minx: number,
- * miny: number,
- * maxx: number,
- * maxy: number
- * }
- * ]
- * }
- */
- SituationPlan.prototype.toSitPlanPrint = function (fitToPage) {
- if (fitToPage === void 0) { fitToPage = false; }
- this.syncToEendraadSchema(); // Om zeker te zijn dat we geen onbestaande elementen meer hebben
- this.orderByZIndex(); // Sorteer de elementen op basis van de z-index zodat ze in de juiste volgorde worden geprint
- var outstruct = {};
- outstruct.numpages = (this.elements.length > 0 ? structure.sitplan.numPages : 0);
- outstruct.pages = [];
- for (var i = 0; i < outstruct.numpages; i++) {
- var svgstr = '';
- var maxx = getPixelsPerMillimeter() * 277;
- var maxy = getPixelsPerMillimeter() * 150;
- var minx = 0;
- var miny = 0;
- for (var _i = 0, _a = this.elements; _i < _a.length; _i++) {
- var element = _a[_i];
- if (element.page == (i + 1)) {
- var fontsize = (element.labelfontsize != null) ? element.labelfontsize : 11;
- svgstr += element.getScaledSVG(true);
- if (fitToPage) {
- var boundingbox = getRotatedRectangleSize(element.sizex * element.getscale(), element.sizey * element.getscale(), element.rotate);
- maxx = Math.max(maxx, element.posx + boundingbox.width / 2);
- maxy = Math.max(maxy, element.posy + boundingbox.height / 2);
- minx = Math.min(minx, element.posx - boundingbox.width / 2);
- miny = Math.min(miny, element.posy - boundingbox.height / 2);
- }
- var str = element.getAdres();
- svgstr += "").concat(htmlspecialchars(str), "");
- }
- }
- svgstr = "");
- outstruct.pages.push({ sizex: maxx - minx, sizey: maxy - miny, svg: svgstr });
- }
- return outstruct;
- };
- return SituationPlan;
-}());
-/**
- * Class SituationPlanElement
- *
- * Deze class refereert naar de volgende globale variabelen:
- * - structure
- * - SITPLANVIEW_DEFAULT_SCALE
- */
-var SituationPlanElement = /** @class */ (function () {
- /**
- * Constructor
- */
- function SituationPlanElement() {
- this.electroItemId = null; // Referentie naar het electro-element in de datastructuur indien van toepassing
- // -- Basis eigenschappen van het element zelf --
- this.boxref = null; // Referentie naar het DIV document in de browser waar het element wordt afgebeeld
- this.svg = ''; /* SVG content van het element indien van toepassing
- Indien electroItemId == null dan is dit de SVG content van het element zelf
- Indien electroItemId != null dan is dit de laatste geladen SVG content van het electro-element
- Altijd te verifiëren dat deze niet leeg is en evenueel een update forceren */
- // -- Basis eigenschappen van het label --
- // adres gegevens zijn private gedefinieerd omwille van consistentie (bvb adres kan leeg gelaten worden als het type auto is)
- this.boxlabelref = null; // Referentie naar het DIV document in de browser waar het label wordt afgebeeld
- this.adrestype = null;
- this.adres = null;
- this.adreslocation = 'rechts';
- // -- Positionering van het element zelf --
- this.page = 1; //pagina waarop het element zich bevindt
- this.posx = 0; //center positie-x in het schema
- this.posy = 0; //center positie-y in het schema
- this.sizex = 0; //breedte
- this.sizey = 0; //hoogte
- this.rotate = 0;
- this.scale = SITPLANVIEW_DEFAULT_SCALE;
- // -- Positionering van het label --
- this.labelposx = 0;
- this.labelposy = 0;
- this.labelfontsize = 11;
- // -- Een vlag om de situationplanview te laten weten dat de box content moet geupdated worden
- this.needsViewUpdate = false;
- // -- Een vlag voor verplaatsbaarheid
- this.movable = true;
- this.id = randomId("SP_");
- }
- SituationPlanElement.prototype.setscale = function (scale) {
- this.scale = scale;
- this.needsViewUpdate = true;
- };
- SituationPlanElement.prototype.getscale = function () {
- return this.scale;
- };
- /**
- * isEendraadschemaSymbool
- *
- * Controleer of het element een geldig electro-element is
- * Hiervoor is een geldige electroItemId nodig en moet het element in de datastructuur voorkomen.
- *
- * @returns boolean
- */
- SituationPlanElement.prototype.isEendraadschemaSymbool = function () {
- if (this.electroItemId != null) {
- return (structure.getElectroItemById(this.electroItemId) != null);
- }
- return false;
- };
- SituationPlanElement.prototype.isEDSSymbolAndRotates360degrees = function () {
- if (this.isEendraadschemaSymbool()) {
- var electroElement = structure.getElectroItemById(this.electroItemId);
- if (electroElement != null) {
- var type = electroElement.getType();
- return SituationPlanElement.ROTATES_360_DEGREES_TYPES.has(type);
- }
- return false;
- }
- return false;
- };
- /**
- * setAdres
- *
- * @param adrestype string : 'auto' of 'manueel'
- * @param adres string : Indien manueel, het adres. Indien auto wordt deze genegeerd en this.adres altijd op null gezet.
- * @param adreslocation string: 'rechts' of 'links' of 'boven' of 'onder'
- */
- SituationPlanElement.prototype.setAdres = function (adrestype, adres, adreslocation) {
- this.adrestype = adrestype;
- this.adreslocation = adreslocation;
- if (this.adrestype === 'manueel')
- this.adres = adres;
- else
- this.adres = null;
- };
- /**
- * setAdresLocation
- *
- * @param adreslocation string: 'rechts' of 'links' of 'boven' of 'onder'
- */
- SituationPlanElement.prototype.setAdresLocation = function (adreslocation) {
- this.adreslocation = adreslocation;
- };
- /**
- * getAdresType
- *
- * @returns string : 'auto' of 'manueel'
- */
- SituationPlanElement.prototype.getAdresType = function () {
- return this.adrestype;
- };
- /**
- * getAdres
- *
- * @returns string : adres
- */
- SituationPlanElement.prototype.getAdres = function () {
- var _a;
- if (!this.isEendraadschemaSymbool())
- return ''; // Geen adres voor niet-elektro-elementen
- var element = structure.getElectroItemById(this.electroItemId);
- if (element == null)
- return ''; // zou redundant moeten zijn want we controleerden al in isEendraadschemaSymbool
- if (this.adrestype === 'auto') {
- return element.getReadableAdres();
- }
- else {
- return (_a = this.adres) !== null && _a !== void 0 ? _a : '';
- }
- };
- /**
- * getAdresLocation
- *
- * @returns 'rechts'|'links'|'boven'|'onder'
- */
- SituationPlanElement.prototype.getAdresLocation = function () {
- return this.adreslocation;
- };
- /**
- * setElectroItemId
- *
- * @param electroItemId number : id van het electroitem in structure
- *
- * TODO: zou beter een private functie zijn en niet worden aangeroepen vanuit SituationPlan en SituationPlanView
- */
- SituationPlanElement.prototype.setElectroItemId = function (electroItemId) {
- this.electroItemId = electroItemId;
- };
- /**
- * getElectroItemId
- *
- * @returns number : id van het electroitem in structure
- */
- SituationPlanElement.prototype.getElectroItemId = function () {
- if (this.isEendraadschemaSymbool())
- return this.electroItemId;
- else
- return null;
- };
- /**
- * updateElectroItemSVG
- *
- * @param svg string : nieuwe gegenereerde SVG door het electro-element
- * @param width number : breedte van de SVG zonder schaling en rotatie, indien niet gegeven wordt de breedte gezocht met functie getSizeFromString
- * @param height number : hoogte van de SVG zonder schaling en rotatie
- */
- SituationPlanElement.prototype.updateElectroItemSVG = function (svg, width, height) {
- if (width === void 0) { width = undefined; }
- if (height === void 0) { height = undefined; }
- if (this.isEendraadschemaSymbool()) {
- if (this.svg !== svg) { // This works because when saving to a file, svg is set to '' so an update will be triggered here
- this.needsViewUpdate = true;
- this.svg = svg;
- if (width != null)
- this.sizex = width;
- if (height != null)
- this.sizey = height;
- if (width == null || height == null)
- this.getSizeFromString();
- }
- }
- };
- /**
- * getUnscaledSVGifNotElectroItem
- *
- * @returns string : SVG content van het element op voorwaarde dat het geen electro-element is
- */
- SituationPlanElement.prototype.getUnscaledSVGifNotElectroItem = function () {
- // cleanSVG(): Deze functie is nodig omdat gebleken is dat de print SVG commando's in jsPDF niet overweg kunnen met SVG's die niet dadelijk beginnen met '
';
- output += '';
- output += '
Vergeet niet regelmatig uw werk op te slaan in het "Bestand"-menu.
");
- };
- // -- Functie om de tree links te tekenen te starten by node met id = myParent --
- Hierarchical_List.prototype.toHTMLinner = function (ordinal) {
- if (this.data[ordinal].collapsed) {
- return ("
"));
- }
- };
- Hierarchical_List.prototype.updateHTMLinner = function (id) {
- var ordinal = this.getOrdinalById(id);
- document.getElementById('id_elem_' + id).innerHTML = this.toHTMLinner(ordinal);
- };
- Hierarchical_List.prototype.toHTML = function (myParent) {
- if (myParent == 0)
- this.updateRibbon();
- var output = "";
- var numberDrawn = 0;
- // Teken het volledige schema in HTML
- for (var i = 0; i < this.length; i++) {
- if (this.active[i] && (this.data[i].parent == myParent)) {
- numberDrawn++;
- output += '
';
- output += this.toHTMLinner(i);
- output += "
";
- }
- }
- if ((myParent == 0) && (numberDrawn < 1)) {
- output += " "; //no need for the add button if we have items
- }
- return (output);
- };
- /** Functie om de naam van een kring te vinden waartoe een element behoord
- *
- */
- Hierarchical_List.prototype.findKringName = function (my_id) {
- var myOrdinal = this.getOrdinalById(my_id);
- var myParent = this.data[myOrdinal].parent;
- if (myParent == 0) {
- return ("");
- }
- else {
- var myParentOrdinal = this.getOrdinalById(myParent);
- if (myParentOrdinal == null)
- return ("");
- if (this.data[myParentOrdinal].getType() == "Kring") {
- var kringnaam = this.data[myParentOrdinal].props.naam;
- if (kringnaam.trim() != "") {
- return (this.data[myParentOrdinal].props.naam);
- }
- else {
- return (this.findKringName(myParent));
- }
- }
- else {
- return (this.findKringName(myParent));
- }
- }
- };
- // -- Functie om de tree links te tekenen te starten by node met id = myParent --
- Hierarchical_List.prototype.toSVG = function (myParent, stack, minxleft, includeparent) {
- if (minxleft === void 0) { minxleft = 0; }
- if (includeparent === void 0) { includeparent = false; }
- // Eerst creëren we een array van SVGelements met alle kinderen van myParent en eventueel myParent zelf
- var inSVG = new Array(); //Results from nested calls will be added here
- var elementCounter = 0;
- // Dan vullen we de array door doorheen de lijst van kinderen te gaan en een tekening van elk kind op te slaan
- for (var i = 0; i < this.length; i++) {
- if (this.active[i] && ((this.data[i].parent == myParent) || ((includeparent == true) && (this.id[i] == myParent)))) {
- var mytype = this.data[i].getType();
- /*if (typeof(this.data[i].keys) != 'undefined') mytype = this.data[i].keys[0][2]; else*/ mytype = this.data[i].props.type;
- switch (mytype) {
- case "":
- inSVG[elementCounter] = new SVGelement();
- break;
- case "Bord":
- inSVG[elementCounter] = this.data[i].toSVG();
- break;
- case "Splitsing":
- inSVG[elementCounter] = this.data[i].toSVG();
- break;
- case "Domotica":
- inSVG[elementCounter] = this.data[i].toSVG(); //Maak de tekening van Domotica
- this.tekenVerticaleLijnIndienKindVanKring(this.data[i], inSVG[elementCounter]);
- break;
- case "Meerdere verbruikers":
- inSVG[elementCounter] = this.data[i].toSVG(); //Maak de tekening van meerdere verbruikers
- this.tekenVerticaleLijnIndienKindVanKring(this.data[i], inSVG[elementCounter]);
- break;
- case "Vrije ruimte":
- inSVG[elementCounter] = this.data[i].toSVG(); //Maak de tekening
- break;
- case "Domotica module (verticaal)":
- inSVG[elementCounter] = this.data[i].toSVG(); //Maak de tekening
- this.tekenVerticaleLijnIndienKindVanKring(this.data[i], inSVG[elementCounter]);
- break;
- case "Domotica gestuurde verbruiker":
- inSVG[elementCounter] = this.data[i].toSVG(); //Maak de tekening
- this.tekenVerticaleLijnIndienKindVanKring(this.data[i], inSVG[elementCounter]);
- break;
- case "Kring":
- inSVG[elementCounter] = this.data[i].toSVG(); //Maak de tekening
- break;
- case "Aansluiting":
- inSVG[elementCounter] = this.data[i].toSVG(); //Maak de tekening
- this.tekenVerticaleLijnIndienKindVanKring(this.data[i], inSVG[elementCounter]);
- break;
- default:
- if ((myParent != 0) &&
- (this.data[this.getOrdinalById(myParent)].getType() == "Meerdere verbruikers"))
- inSVG[elementCounter] = this.data[i].toSVG();
- else if (stack == "vertical")
- inSVG[elementCounter] = this.toSVG(this.id[i], "horizontal", 0, true); //if we are still in vertical mode, switch to horizontal and take childs with us
- else { //we are in horizontal mode and can start drawing
- if (this.id[i] == myParent) { // Element is de parent en tekent zichzelf
- inSVG[elementCounter] = this.data[i].toSVG();
- }
- else { // Element is niet de parent, we tekenen het element en al zijn kinderen
- inSVG[elementCounter] = this.toSVG(this.id[i], "horizontal", 0, true); //if we are still in vertical mode, switch to horizontal and take childs with us
- }
- }
- this.tekenVerticaleLijnIndienKindVanKring(this.data[i], inSVG[elementCounter]);
- break;
- }
- elementCounter++;
- }
- }
- // If there are no elements, make at least an empty one to avoid problems here below ---
- if (elementCounter == 0) {
- inSVG[0] = new SVGelement();
- }
- // Now create the output element ---
- var outSVG = new SVGelement;
- outSVG.xleft = 0;
- outSVG.xright = 0;
- outSVG.yup = 0;
- outSVG.ydown = 0;
- outSVG.data = "";
- var width = 0; //How wide is the structure?
- var height = 0; //How high is the structure?
- switch (stack) {
- case "horizontal":
- var max_yup = 0; //What is the maximal distance above the horizontal line?
- var max_ydown = 0; //What is the maximal distance below the horizontal line?
- // analyse the size of the structure. Build horizontally
- for (var i = 0; i < elementCounter; i++) {
- width = width + inSVG[i].xleft + inSVG[i].xright;
- max_yup = Math.max(max_yup, inSVG[i].yup);
- max_ydown = Math.max(max_ydown, inSVG[i].ydown);
- }
- height = max_yup + max_ydown;
- // decide on the output structure
- if (elementCounter > 0) {
- outSVG.xleft = inSVG[0].xleft; //Leave space of the first element at the left
- outSVG.xright = width - outSVG.xleft;
- outSVG.xrightmin = outSVG.xright - inSVG[elementCounter - 1].xright;
- }
- else {
- outSVG.xleft = 0;
- outSVG.xright = 0;
- outSVG.xrightmin = 0;
- }
- ;
- outSVG.yup = max_yup;
- outSVG.ydown = max_ydown;
- // Create the output data
- var xpos = 0;
- for (var i = 0; i < elementCounter; i++) {
- outSVG.data += '';
- outSVG.data += inSVG[i].data;
- outSVG.data += '';
- xpos += inSVG[i].xleft + inSVG[i].xright;
- }
- break;
- case "vertical":
- var max_xleft = 0; //What is the maximal distance left of the vertical line?
- var max_xright = 0; //What is the maximal distance right of the vertical line?
- // analyse the size of the structure. Build vertically
- for (var i = 0; i < elementCounter; i++) {
- height = height + inSVG[i].yup + inSVG[i].ydown;
- max_xleft = Math.max(max_xleft, inSVG[i].xleft);
- max_xright = Math.max(max_xright, inSVG[i].xright);
- }
- max_xleft = Math.max(minxleft, max_xleft);
- width = max_xleft + max_xright;
- // decide on the output structure
- outSVG.yup = height; //As a general rule, there is no ydown, but to be confirmed
- outSVG.ydown = 0;
- //outSVG.xleft = Math.max(max_xleft,35); // foresee at least 35 for text at the left
- outSVG.xleft = max_xleft;
- if (this.data[this.getOrdinalById(myParent)].getType() == "Kring")
- max_xright += 10; // Altijd 10 extra rechts voorzien voor uitstekende tekst/adressen
- outSVG.xright = Math.max(max_xright, 25); // foresee at least 25 at the right
- // create the output data
- var ypos = 0;
- for (var i = elementCounter - 1; i >= 0; i--) {
- outSVG.data += '';
- outSVG.data += inSVG[i].data;
- outSVG.data += '';
- ypos += inSVG[i].yup + inSVG[i].ydown;
- }
- break;
- }
- outSVG.data += "\n";
- if (myParent == 0) { //We will always foresee a 20 pixel horizontal and 5 pixel vertical margin
- var header = "";
- header += SVGSymbols.outputSVGSymbols();
- var footer = "";
- outSVG.data = header + outSVG.data + footer;
- }
- return (outSVG);
- };
- return Hierarchical_List;
-}());
-var SITPLANVIEW_SELECT_PADDING = parseInt(getComputedStyle(document.documentElement).getPropertyValue('--selectPadding').trim());
-var SITPLANVIEW_ZOOMINTERVAL = { MIN: 0.1, MAX: 1000 };
-var SITPLANVIEW_DEFAULT_SCALE = 0.7;
-var CONFIGPAGE_LEFT = "\n
\n
\n Welkom op \u00E9\u00E9ndraadschema\n
\n
\n Kies \u00E9\u00E9n van onderstaande voorbeelden om van te starten of start van een leeg schema (optie 3).\n
\n \n \n Tip: Om de mogelijkheden van het programma te leren kennen is het vaak beter eerst een voorbeeldschema te\n bekijken alvorens van een leeg schema te vertrekken.\n \n \n
\n \n
\n
\n
\n Voorbeeld 1\n
\n
\n Voorbeeld 2\n
\n
\n Leeg schema\n
\n
\n Openen\n
\n
\n
\n
\n \n
\n Eenvoudig schema, enkel contactdozen en lichtpunten.\n
\n
\n
\n \n
\n Iets complexer schema met teleruptoren, verbruikers achter contactdozen en gesplitste kringen.\n
\n
\n
\n \n
\n";
-var CONFIGPAGE_RIGHT = "\n
\n
\n
\n \n
\n Open een schema dat u eerder heeft opgeslagen op uw computer (EDS-bestand). Enkel bestanden aangemaakt na 12 juli 2019 worden herkend.\n
\r\n * Default is \"a4\". If you want to use your own format just pass instead of one of the above predefined formats the size as an number-array, e.g. [595.28, 841.89]\r\n * @param orientation {string} Orientation of the new page. Possible values are \"portrait\" or \"landscape\" (or shortcuts \"p\" (Default), \"l\").\r\n * @function\r\n * @instance\r\n * @returns {jsPDF}\r\n *\r\n * @memberof jsPDF#\r\n * @name addPage\r\n */\r\n API.addPage = function() {\r\n _addPage.apply(this, arguments);\r\n return this;\r\n };\r\n /**\r\n * Adds (and transfers the focus to) new page to the PDF document.\r\n * @function\r\n * @instance\r\n * @returns {jsPDF}\r\n *\r\n * @memberof jsPDF#\r\n * @name setPage\r\n * @param {number} page Switch the active page to the page number specified (indexed starting at 1).\r\n * @example\r\n * doc = jsPDF()\r\n * doc.addPage()\r\n * doc.addPage()\r\n * doc.text('I am on page 3', 10, 10)\r\n * doc.setPage(1)\r\n * doc.text('I am on page 1', 10, 10)\r\n */\r\n API.setPage = function() {\r\n _setPage.apply(this, arguments);\r\n setOutputDestination.call(this, pages[currentPage]);\r\n return this;\r\n };\r\n\r\n /**\r\n * @name insertPage\r\n * @memberof jsPDF#\r\n *\r\n * @function\r\n * @instance\r\n * @param {Object} beforePage\r\n * @returns {jsPDF}\r\n */\r\n API.insertPage = function(beforePage) {\r\n this.addPage();\r\n this.movePage(currentPage, beforePage);\r\n return this;\r\n };\r\n\r\n /**\r\n * @name movePage\r\n * @memberof jsPDF#\r\n * @function\r\n * @instance\r\n * @param {number} targetPage\r\n * @param {number} beforePage\r\n * @returns {jsPDF}\r\n */\r\n API.movePage = function(targetPage, beforePage) {\r\n var tmpPages, tmpPagesContext;\r\n if (targetPage > beforePage) {\r\n tmpPages = pages[targetPage];\r\n tmpPagesContext = pagesContext[targetPage];\r\n for (var i = targetPage; i > beforePage; i--) {\r\n pages[i] = pages[i - 1];\r\n pagesContext[i] = pagesContext[i - 1];\r\n }\r\n pages[beforePage] = tmpPages;\r\n pagesContext[beforePage] = tmpPagesContext;\r\n this.setPage(beforePage);\r\n } else if (targetPage < beforePage) {\r\n tmpPages = pages[targetPage];\r\n tmpPagesContext = pagesContext[targetPage];\r\n for (var j = targetPage; j < beforePage; j++) {\r\n pages[j] = pages[j + 1];\r\n pagesContext[j] = pagesContext[j + 1];\r\n }\r\n pages[beforePage] = tmpPages;\r\n pagesContext[beforePage] = tmpPagesContext;\r\n this.setPage(beforePage);\r\n }\r\n return this;\r\n };\r\n\r\n /**\r\n * Deletes a page from the PDF.\r\n * @name deletePage\r\n * @memberof jsPDF#\r\n * @function\r\n * @param {number} targetPage\r\n * @instance\r\n * @returns {jsPDF}\r\n */\r\n API.deletePage = function() {\r\n _deletePage.apply(this, arguments);\r\n return this;\r\n };\r\n\r\n /**\r\n * Adds text to page. Supports adding multiline text when 'text' argument is an Array of Strings.\r\n *\r\n * @function\r\n * @instance\r\n * @param {String|Array} text String or array of strings to be added to the page. Each line is shifted one line down per font, spacing settings declared before this call.\r\n * @param {number} x Coordinate (in units declared at inception of PDF document) against left edge of the page.\r\n * @param {number} y Coordinate (in units declared at inception of PDF document) against upper edge of the page.\r\n * @param {Object} [options] - Collection of settings signaling how the text must be encoded.\r\n * @param {string} [options.align=left] - The alignment of the text, possible values: left, center, right, justify.\r\n * @param {string} [options.baseline=alphabetic] - Sets text baseline used when drawing the text, possible values: alphabetic, ideographic, bottom, top, middle, hanging\r\n * @param {number|Matrix} [options.angle=0] - Rotate the text clockwise or counterclockwise. Expects the angle in degree.\r\n * @param {number} [options.rotationDirection=1] - Direction of the rotation. 0 = clockwise, 1 = counterclockwise.\r\n * @param {number} [options.charSpace=0] - The space between each letter.\r\n * @param {number} [options.horizontalScale=1] - Horizontal scale of the text as a factor of the regular size.\r\n * @param {number} [options.lineHeightFactor=1.15] - The lineheight of each line.\r\n * @param {Object} [options.flags] - Flags for to8bitStream.\r\n * @param {boolean} [options.flags.noBOM=true] - Don't add BOM to Unicode-text.\r\n * @param {boolean} [options.flags.autoencode=true] - Autoencode the Text.\r\n * @param {number} [options.maxWidth=0] - Split the text by given width, 0 = no split.\r\n * @param {string} [options.renderingMode=fill] - Set how the text should be rendered, possible values: fill, stroke, fillThenStroke, invisible, fillAndAddForClipping, strokeAndAddPathForClipping, fillThenStrokeAndAddToPathForClipping, addToPathForClipping.\r\n * @param {boolean} [options.isInputVisual] - Option for the BidiEngine\r\n * @param {boolean} [options.isOutputVisual] - Option for the BidiEngine\r\n * @param {boolean} [options.isInputRtl] - Option for the BidiEngine\r\n * @param {boolean} [options.isOutputRtl] - Option for the BidiEngine\r\n * @param {boolean} [options.isSymmetricSwapping] - Option for the BidiEngine\r\n * @param {number|Matrix} transform If transform is a number the text will be rotated by this value around the anchor set by x and y.\r\n *\r\n * If it is a Matrix, this matrix gets directly applied to the text, which allows shearing\r\n * effects etc.; the x and y offsets are then applied AFTER the coordinate system has been established by this\r\n * matrix. This means passing a rotation matrix that is equivalent to some rotation angle will in general yield a\r\n * DIFFERENT result. A matrix is only allowed in \"advanced\" API mode.\r\n * @returns {jsPDF}\r\n * @memberof jsPDF#\r\n * @name text\r\n */\r\n API.__private__.text = API.text = function(text, x, y, options, transform) {\r\n /*\r\n * Inserts something like this into PDF\r\n * BT\r\n * /F1 16 Tf % Font name + size\r\n * 16 TL % How many units down for next line in multiline text\r\n * 0 g % color\r\n * 28.35 813.54 Td % position\r\n * (line one) Tj\r\n * T* (line two) Tj\r\n * T* (line three) Tj\r\n * ET\r\n */\r\n options = options || {};\r\n var scope = options.scope || this;\r\n var payload, da, angle, align, charSpace, maxWidth, flags, horizontalScale;\r\n\r\n // Pre-August-2012 the order of arguments was function(x, y, text, flags)\r\n // in effort to make all calls have similar signature like\r\n // function(data, coordinates... , miscellaneous)\r\n // this method had its args flipped.\r\n // code below allows backward compatibility with old arg order.\r\n if (\r\n typeof text === \"number\" &&\r\n typeof x === \"number\" &&\r\n (typeof y === \"string\" || Array.isArray(y))\r\n ) {\r\n var tmp = y;\r\n y = x;\r\n x = text;\r\n text = tmp;\r\n }\r\n\r\n var transformationMatrix;\r\n\r\n if (arguments[3] instanceof Matrix === false) {\r\n flags = arguments[3];\r\n angle = arguments[4];\r\n align = arguments[5];\r\n\r\n if (typeof flags !== \"object\" || flags === null) {\r\n if (typeof angle === \"string\") {\r\n align = angle;\r\n angle = null;\r\n }\r\n if (typeof flags === \"string\") {\r\n align = flags;\r\n flags = null;\r\n }\r\n if (typeof flags === \"number\") {\r\n angle = flags;\r\n flags = null;\r\n }\r\n options = {\r\n flags: flags,\r\n angle: angle,\r\n align: align\r\n };\r\n }\r\n } else {\r\n advancedApiModeTrap(\r\n \"The transform parameter of text() with a Matrix value\"\r\n );\r\n transformationMatrix = transform;\r\n }\r\n\r\n if (isNaN(x) || isNaN(y) || typeof text === \"undefined\" || text === null) {\r\n throw new Error(\"Invalid arguments passed to jsPDF.text\");\r\n }\r\n\r\n if (text.length === 0) {\r\n return scope;\r\n }\r\n\r\n var xtra = \"\";\r\n var isHex = false;\r\n var lineHeight =\r\n typeof options.lineHeightFactor === \"number\"\r\n ? options.lineHeightFactor\r\n : lineHeightFactor;\r\n var scaleFactor = scope.internal.scaleFactor;\r\n\r\n function ESC(s) {\r\n s = s.split(\"\\t\").join(Array(options.TabLen || 9).join(\" \"));\r\n return pdfEscape(s, flags);\r\n }\r\n\r\n function transformTextToSpecialArray(text) {\r\n //we don't want to destroy original text array, so cloning it\r\n var sa = text.concat();\r\n var da = [];\r\n var len = sa.length;\r\n var curDa;\r\n //we do array.join('text that must not be PDFescaped\")\r\n //thus, pdfEscape each component separately\r\n while (len--) {\r\n curDa = sa.shift();\r\n if (typeof curDa === \"string\") {\r\n da.push(curDa);\r\n } else {\r\n if (\r\n Array.isArray(text) &&\r\n (curDa.length === 1 ||\r\n (curDa[1] === undefined && curDa[2] === undefined))\r\n ) {\r\n da.push(curDa[0]);\r\n } else {\r\n da.push([curDa[0], curDa[1], curDa[2]]);\r\n }\r\n }\r\n }\r\n return da;\r\n }\r\n\r\n function processTextByFunction(text, processingFunction) {\r\n var result;\r\n if (typeof text === \"string\") {\r\n result = processingFunction(text)[0];\r\n } else if (Array.isArray(text)) {\r\n //we don't want to destroy original text array, so cloning it\r\n var sa = text.concat();\r\n var da = [];\r\n var len = sa.length;\r\n var curDa;\r\n var tmpResult;\r\n //we do array.join('text that must not be PDFescaped\")\r\n //thus, pdfEscape each component separately\r\n while (len--) {\r\n curDa = sa.shift();\r\n if (typeof curDa === \"string\") {\r\n da.push(processingFunction(curDa)[0]);\r\n } else if (Array.isArray(curDa) && typeof curDa[0] === \"string\") {\r\n tmpResult = processingFunction(curDa[0], curDa[1], curDa[2]);\r\n da.push([tmpResult[0], tmpResult[1], tmpResult[2]]);\r\n }\r\n }\r\n result = da;\r\n }\r\n return result;\r\n }\r\n\r\n //Check if text is of type String\r\n var textIsOfTypeString = false;\r\n var tmpTextIsOfTypeString = true;\r\n\r\n if (typeof text === \"string\") {\r\n textIsOfTypeString = true;\r\n } else if (Array.isArray(text)) {\r\n //we don't want to destroy original text array, so cloning it\r\n var sa = text.concat();\r\n da = [];\r\n var len = sa.length;\r\n var curDa;\r\n //we do array.join('text that must not be PDFescaped\")\r\n //thus, pdfEscape each component separately\r\n while (len--) {\r\n curDa = sa.shift();\r\n if (\r\n typeof curDa !== \"string\" ||\r\n (Array.isArray(curDa) && typeof curDa[0] !== \"string\")\r\n ) {\r\n tmpTextIsOfTypeString = false;\r\n }\r\n }\r\n textIsOfTypeString = tmpTextIsOfTypeString;\r\n }\r\n if (textIsOfTypeString === false) {\r\n throw new Error(\r\n 'Type of text must be string or Array. \"' +\r\n text +\r\n '\" is not recognized.'\r\n );\r\n }\r\n\r\n //If there are any newlines in text, we assume\r\n //the user wanted to print multiple lines, so break the\r\n //text up into an array. If the text is already an array,\r\n //we assume the user knows what they are doing.\r\n //Convert text into an array anyway to simplify\r\n //later code.\r\n\r\n if (typeof text === \"string\") {\r\n if (text.match(/[\\r?\\n]/)) {\r\n text = text.split(/\\r\\n|\\r|\\n/g);\r\n } else {\r\n text = [text];\r\n }\r\n }\r\n\r\n //baseline\r\n var height = activeFontSize / scope.internal.scaleFactor;\r\n var descent = height * (lineHeight - 1);\r\n\r\n switch (options.baseline) {\r\n case \"bottom\":\r\n y -= descent;\r\n break;\r\n case \"top\":\r\n y += height - descent;\r\n break;\r\n case \"hanging\":\r\n y += height - 2 * descent;\r\n break;\r\n case \"middle\":\r\n y += height / 2 - descent;\r\n break;\r\n case \"ideographic\":\r\n case \"alphabetic\":\r\n default:\r\n // do nothing, everything is fine\r\n break;\r\n }\r\n\r\n //multiline\r\n maxWidth = options.maxWidth || 0;\r\n\r\n if (maxWidth > 0) {\r\n if (typeof text === \"string\") {\r\n text = scope.splitTextToSize(text, maxWidth);\r\n } else if (Object.prototype.toString.call(text) === \"[object Array]\") {\r\n text = text.reduce(function(acc, textLine) {\r\n return acc.concat(scope.splitTextToSize(textLine, maxWidth));\r\n }, []);\r\n }\r\n }\r\n\r\n //creating Payload-Object to make text byRef\r\n payload = {\r\n text: text,\r\n x: x,\r\n y: y,\r\n options: options,\r\n mutex: {\r\n pdfEscape: pdfEscape,\r\n activeFontKey: activeFontKey,\r\n fonts: fonts,\r\n activeFontSize: activeFontSize\r\n }\r\n };\r\n events.publish(\"preProcessText\", payload);\r\n\r\n text = payload.text;\r\n options = payload.options;\r\n\r\n //angle\r\n angle = options.angle;\r\n\r\n if (\r\n transformationMatrix instanceof Matrix === false &&\r\n angle &&\r\n typeof angle === \"number\"\r\n ) {\r\n angle *= Math.PI / 180;\r\n\r\n if (options.rotationDirection === 0) {\r\n angle = -angle;\r\n }\r\n\r\n if (apiMode === ApiMode.ADVANCED) {\r\n angle = -angle;\r\n }\r\n\r\n var c = Math.cos(angle);\r\n var s = Math.sin(angle);\r\n transformationMatrix = new Matrix(c, s, -s, c, 0, 0);\r\n } else if (angle && angle instanceof Matrix) {\r\n transformationMatrix = angle;\r\n }\r\n\r\n if (apiMode === ApiMode.ADVANCED && !transformationMatrix) {\r\n transformationMatrix = identityMatrix;\r\n }\r\n\r\n //charSpace\r\n\r\n charSpace = options.charSpace || activeCharSpace;\r\n\r\n if (typeof charSpace !== \"undefined\") {\r\n xtra += hpf(scale(charSpace)) + \" Tc\\n\";\r\n this.setCharSpace(this.getCharSpace() || 0);\r\n }\r\n\r\n horizontalScale = options.horizontalScale;\r\n if (typeof horizontalScale !== \"undefined\") {\r\n xtra += hpf(horizontalScale * 100) + \" Tz\\n\";\r\n }\r\n\r\n //lang\r\n\r\n var lang = options.lang;\r\n\r\n if (lang) {\r\n // xtra += \"/Lang (\" + lang +\")\\n\";\r\n }\r\n\r\n //renderingMode\r\n var renderingMode = -1;\r\n var parmRenderingMode =\r\n typeof options.renderingMode !== \"undefined\"\r\n ? options.renderingMode\r\n : options.stroke;\r\n var pageContext = scope.internal.getCurrentPageInfo().pageContext;\r\n\r\n switch (parmRenderingMode) {\r\n case 0:\r\n case false:\r\n case \"fill\":\r\n renderingMode = 0;\r\n break;\r\n case 1:\r\n case true:\r\n case \"stroke\":\r\n renderingMode = 1;\r\n break;\r\n case 2:\r\n case \"fillThenStroke\":\r\n renderingMode = 2;\r\n break;\r\n case 3:\r\n case \"invisible\":\r\n renderingMode = 3;\r\n break;\r\n case 4:\r\n case \"fillAndAddForClipping\":\r\n renderingMode = 4;\r\n break;\r\n case 5:\r\n case \"strokeAndAddPathForClipping\":\r\n renderingMode = 5;\r\n break;\r\n case 6:\r\n case \"fillThenStrokeAndAddToPathForClipping\":\r\n renderingMode = 6;\r\n break;\r\n case 7:\r\n case \"addToPathForClipping\":\r\n renderingMode = 7;\r\n break;\r\n }\r\n\r\n var usedRenderingMode =\r\n typeof pageContext.usedRenderingMode !== \"undefined\"\r\n ? pageContext.usedRenderingMode\r\n : -1;\r\n\r\n //if the coder wrote it explicitly to use a specific\r\n //renderingMode, then use it\r\n if (renderingMode !== -1) {\r\n xtra += renderingMode + \" Tr\\n\";\r\n //otherwise check if we used the rendering Mode already\r\n //if so then set the rendering Mode...\r\n } else if (usedRenderingMode !== -1) {\r\n xtra += \"0 Tr\\n\";\r\n }\r\n\r\n if (renderingMode !== -1) {\r\n pageContext.usedRenderingMode = renderingMode;\r\n }\r\n\r\n //align\r\n align = options.align || \"left\";\r\n var leading = activeFontSize * lineHeight;\r\n var pageWidth = scope.internal.pageSize.getWidth();\r\n var activeFont = fonts[activeFontKey];\r\n charSpace = options.charSpace || activeCharSpace;\r\n maxWidth = options.maxWidth || 0;\r\n\r\n var lineWidths;\r\n flags = Object.assign({ autoencode: true, noBOM: true }, options.flags);\r\n\r\n var wordSpacingPerLine = [];\r\n var findWidth = function(v) {\r\n return (\r\n (scope.getStringUnitWidth(v, {\r\n font: activeFont,\r\n charSpace: charSpace,\r\n fontSize: activeFontSize,\r\n doKerning: false\r\n }) *\r\n activeFontSize) /\r\n scaleFactor\r\n );\r\n };\r\n if (Object.prototype.toString.call(text) === \"[object Array]\") {\r\n da = transformTextToSpecialArray(text);\r\n var newY;\r\n if (align !== \"left\") {\r\n lineWidths = da.map(findWidth);\r\n }\r\n //The first line uses the \"main\" Td setting,\r\n //and the subsequent lines are offset by the\r\n //previous line's x coordinate.\r\n var prevWidth = 0;\r\n var newX;\r\n if (align === \"right\") {\r\n //The passed in x coordinate defines the\r\n //rightmost point of the text.\r\n x -= lineWidths[0];\r\n text = [];\r\n len = da.length;\r\n for (var i = 0; i < len; i++) {\r\n if (i === 0) {\r\n newX = getHorizontalCoordinate(x);\r\n newY = getVerticalCoordinate(y);\r\n } else {\r\n newX = scale(prevWidth - lineWidths[i]);\r\n newY = -leading;\r\n }\r\n text.push([da[i], newX, newY]);\r\n prevWidth = lineWidths[i];\r\n }\r\n } else if (align === \"center\") {\r\n //The passed in x coordinate defines\r\n //the center point.\r\n x -= lineWidths[0] / 2;\r\n text = [];\r\n len = da.length;\r\n for (var j = 0; j < len; j++) {\r\n if (j === 0) {\r\n newX = getHorizontalCoordinate(x);\r\n newY = getVerticalCoordinate(y);\r\n } else {\r\n newX = scale((prevWidth - lineWidths[j]) / 2);\r\n newY = -leading;\r\n }\r\n text.push([da[j], newX, newY]);\r\n prevWidth = lineWidths[j];\r\n }\r\n } else if (align === \"left\") {\r\n text = [];\r\n len = da.length;\r\n for (var h = 0; h < len; h++) {\r\n text.push(da[h]);\r\n }\r\n } else if (align === \"justify\" && activeFont.encoding === \"Identity-H\") {\r\n // when using unicode fonts, wordSpacePerLine does not apply\r\n text = [];\r\n len = da.length;\r\n maxWidth = maxWidth !== 0 ? maxWidth : pageWidth;\r\n let backToStartX = 0;\r\n for (var l = 0; l < len; l++) {\r\n newY = l === 0 ? getVerticalCoordinate(y) : -leading;\r\n newX = l === 0 ? getHorizontalCoordinate(x) : backToStartX;\r\n if (l < len - 1) {\r\n let spacing = scale(\r\n (maxWidth - lineWidths[l]) / (da[l].split(\" \").length - 1)\r\n );\r\n let words = da[l].split(\" \");\r\n text.push([words[0] + \" \", newX, newY]);\r\n backToStartX = 0; // distance to reset back to the left\r\n for (let i = 1; i < words.length; i++) {\r\n let shiftAmount =\r\n (findWidth(words[i - 1] + \" \" + words[i]) -\r\n findWidth(words[i])) *\r\n scaleFactor +\r\n spacing;\r\n if (i == words.length - 1) text.push([words[i], shiftAmount, 0]);\r\n else text.push([words[i] + \" \", shiftAmount, 0]);\r\n backToStartX -= shiftAmount;\r\n }\r\n } else {\r\n text.push([da[l], newX, newY]);\r\n }\r\n }\r\n text.push([\"\", backToStartX, 0]);\r\n } else if (align === \"justify\") {\r\n text = [];\r\n len = da.length;\r\n maxWidth = maxWidth !== 0 ? maxWidth : pageWidth;\r\n for (var l = 0; l < len; l++) {\r\n newY = l === 0 ? getVerticalCoordinate(y) : -leading;\r\n newX = l === 0 ? getHorizontalCoordinate(x) : 0;\r\n if (l < len - 1) {\r\n wordSpacingPerLine.push(\r\n hpf(\r\n scale(\r\n (maxWidth - lineWidths[l]) / (da[l].split(\" \").length - 1)\r\n )\r\n )\r\n );\r\n } else {\r\n wordSpacingPerLine.push(0);\r\n }\r\n text.push([da[l], newX, newY]);\r\n }\r\n } else {\r\n throw new Error(\r\n 'Unrecognized alignment option, use \"left\", \"center\", \"right\" or \"justify\".'\r\n );\r\n }\r\n }\r\n\r\n //R2L\r\n var doReversing = typeof options.R2L === \"boolean\" ? options.R2L : R2L;\r\n if (doReversing === true) {\r\n text = processTextByFunction(text, function(text, posX, posY) {\r\n return [\r\n text\r\n .split(\"\")\r\n .reverse()\r\n .join(\"\"),\r\n posX,\r\n posY\r\n ];\r\n });\r\n }\r\n\r\n //creating Payload-Object to make text byRef\r\n payload = {\r\n text: text,\r\n x: x,\r\n y: y,\r\n options: options,\r\n mutex: {\r\n pdfEscape: pdfEscape,\r\n activeFontKey: activeFontKey,\r\n fonts: fonts,\r\n activeFontSize: activeFontSize\r\n }\r\n };\r\n events.publish(\"postProcessText\", payload);\r\n\r\n text = payload.text;\r\n isHex = payload.mutex.isHex || false;\r\n\r\n //Escaping\r\n var activeFontEncoding = fonts[activeFontKey].encoding;\r\n\r\n if (\r\n activeFontEncoding === \"WinAnsiEncoding\" ||\r\n activeFontEncoding === \"StandardEncoding\"\r\n ) {\r\n text = processTextByFunction(text, function(text, posX, posY) {\r\n return [ESC(text), posX, posY];\r\n });\r\n }\r\n\r\n da = transformTextToSpecialArray(text);\r\n\r\n text = [];\r\n var STRING = 0;\r\n var ARRAY = 1;\r\n var variant = Array.isArray(da[0]) ? ARRAY : STRING;\r\n var posX;\r\n var posY;\r\n var content;\r\n var wordSpacing = \"\";\r\n\r\n var generatePosition = function(\r\n parmPosX,\r\n parmPosY,\r\n parmTransformationMatrix\r\n ) {\r\n var position = \"\";\r\n if (parmTransformationMatrix instanceof Matrix) {\r\n // It is kind of more intuitive to apply a plain rotation around the text anchor set by x and y\r\n // but when the user supplies an arbitrary transformation matrix, the x and y offsets should be applied\r\n // in the coordinate system established by this matrix\r\n if (typeof options.angle === \"number\") {\r\n parmTransformationMatrix = matrixMult(\r\n parmTransformationMatrix,\r\n new Matrix(1, 0, 0, 1, parmPosX, parmPosY)\r\n );\r\n } else {\r\n parmTransformationMatrix = matrixMult(\r\n new Matrix(1, 0, 0, 1, parmPosX, parmPosY),\r\n parmTransformationMatrix\r\n );\r\n }\r\n\r\n if (apiMode === ApiMode.ADVANCED) {\r\n parmTransformationMatrix = matrixMult(\r\n new Matrix(1, 0, 0, -1, 0, 0),\r\n parmTransformationMatrix\r\n );\r\n }\r\n\r\n position = parmTransformationMatrix.join(\" \") + \" Tm\\n\";\r\n } else {\r\n position = hpf(parmPosX) + \" \" + hpf(parmPosY) + \" Td\\n\";\r\n }\r\n return position;\r\n };\r\n\r\n for (var lineIndex = 0; lineIndex < da.length; lineIndex++) {\r\n wordSpacing = \"\";\r\n\r\n switch (variant) {\r\n case ARRAY:\r\n content =\r\n (isHex ? \"<\" : \"(\") + da[lineIndex][0] + (isHex ? \">\" : \")\");\r\n posX = parseFloat(da[lineIndex][1]);\r\n posY = parseFloat(da[lineIndex][2]);\r\n break;\r\n case STRING:\r\n content = (isHex ? \"<\" : \"(\") + da[lineIndex] + (isHex ? \">\" : \")\");\r\n posX = getHorizontalCoordinate(x);\r\n posY = getVerticalCoordinate(y);\r\n break;\r\n }\r\n\r\n if (\r\n typeof wordSpacingPerLine !== \"undefined\" &&\r\n typeof wordSpacingPerLine[lineIndex] !== \"undefined\"\r\n ) {\r\n wordSpacing = wordSpacingPerLine[lineIndex] + \" Tw\\n\";\r\n }\r\n\r\n if (lineIndex === 0) {\r\n text.push(\r\n wordSpacing +\r\n generatePosition(posX, posY, transformationMatrix) +\r\n content\r\n );\r\n } else if (variant === STRING) {\r\n text.push(wordSpacing + content);\r\n } else if (variant === ARRAY) {\r\n text.push(\r\n wordSpacing +\r\n generatePosition(posX, posY, transformationMatrix) +\r\n content\r\n );\r\n }\r\n }\r\n\r\n text = variant === STRING ? text.join(\" Tj\\nT* \") : text.join(\" Tj\\n\");\r\n text += \" Tj\\n\";\r\n\r\n var result = \"BT\\n/\";\r\n result += activeFontKey + \" \" + activeFontSize + \" Tf\\n\"; // font face, style, size\r\n result += hpf(activeFontSize * lineHeight) + \" TL\\n\"; // line spacing\r\n result += textColor + \"\\n\";\r\n result += xtra;\r\n result += text;\r\n result += \"ET\";\r\n\r\n out(result);\r\n usedFonts[activeFontKey] = true;\r\n return scope;\r\n };\r\n\r\n // PDF supports these path painting and clip path operators:\r\n //\r\n // S - stroke\r\n // s - close/stroke\r\n // f (F) - fill non-zero\r\n // f* - fill evenodd\r\n // B - fill stroke nonzero\r\n // B* - fill stroke evenodd\r\n // b - close fill stroke nonzero\r\n // b* - close fill stroke evenodd\r\n // n - nothing (consume path)\r\n // W - clip nonzero\r\n // W* - clip evenodd\r\n //\r\n // In order to keep the API small, we omit the close-and-fill/stroke operators and provide a separate close()\r\n // method.\r\n /**\r\n *\r\n * @name clip\r\n * @function\r\n * @instance\r\n * @param {string} rule Only possible value is 'evenodd'\r\n * @returns {jsPDF}\r\n * @memberof jsPDF#\r\n * @description All .clip() after calling drawing ops with a style argument of null.\r\n */\r\n var clip = (API.__private__.clip = API.clip = function(rule) {\r\n // Call .clip() after calling drawing ops with a style argument of null\r\n // W is the PDF clipping op\r\n if (\"evenodd\" === rule) {\r\n out(\"W*\");\r\n } else {\r\n out(\"W\");\r\n }\r\n return this;\r\n });\r\n\r\n /**\r\n * @name clipEvenOdd\r\n * @function\r\n * @instance\r\n * @returns {jsPDF}\r\n * @memberof jsPDF#\r\n * @description Modify the current clip path by intersecting it with the current path using the even-odd rule. Note\r\n * that this will NOT consume the current path. In order to only use this path for clipping call\r\n * {@link API.discardPath} afterwards.\r\n */\r\n API.clipEvenOdd = function() {\r\n return clip(\"evenodd\");\r\n };\r\n\r\n /**\r\n * Consumes the current path without any effect. Mainly used in combination with {@link clip} or\r\n * {@link clipEvenOdd}. The PDF \"n\" operator.\r\n * @name discardPath\r\n * @function\r\n * @instance\r\n * @returns {jsPDF}\r\n * @memberof jsPDF#\r\n */\r\n API.__private__.discardPath = API.discardPath = function() {\r\n out(\"n\");\r\n return this;\r\n };\r\n\r\n var isValidStyle = (API.__private__.isValidStyle = function(style) {\r\n var validStyleVariants = [\r\n undefined,\r\n null,\r\n \"S\",\r\n \"D\",\r\n \"F\",\r\n \"DF\",\r\n \"FD\",\r\n \"f\",\r\n \"f*\",\r\n \"B\",\r\n \"B*\",\r\n \"n\"\r\n ];\r\n var result = false;\r\n if (validStyleVariants.indexOf(style) !== -1) {\r\n result = true;\r\n }\r\n return result;\r\n });\r\n\r\n API.__private__.setDefaultPathOperation = API.setDefaultPathOperation = function(\r\n operator\r\n ) {\r\n if (isValidStyle(operator)) {\r\n defaultPathOperation = operator;\r\n }\r\n return this;\r\n };\r\n\r\n var getStyle = (API.__private__.getStyle = API.getStyle = function(style) {\r\n // see path-painting operators in PDF spec\r\n var op = defaultPathOperation; // stroke\r\n\r\n switch (style) {\r\n case \"D\":\r\n case \"S\":\r\n op = \"S\"; // stroke\r\n break;\r\n case \"F\":\r\n op = \"f\"; // fill\r\n break;\r\n case \"FD\":\r\n case \"DF\":\r\n op = \"B\";\r\n break;\r\n case \"f\":\r\n case \"f*\":\r\n case \"B\":\r\n case \"B*\":\r\n /*\r\n Allow direct use of these PDF path-painting operators:\r\n - f fill using nonzero winding number rule\r\n - f* fill using even-odd rule\r\n - B fill then stroke with fill using non-zero winding number rule\r\n - B* fill then stroke with fill using even-odd rule\r\n */\r\n op = style;\r\n break;\r\n }\r\n return op;\r\n });\r\n\r\n /**\r\n * Close the current path. The PDF \"h\" operator.\r\n * @name close\r\n * @function\r\n * @instance\r\n * @returns {jsPDF}\r\n * @memberof jsPDF#\r\n */\r\n var close = (API.close = function() {\r\n out(\"h\");\r\n return this;\r\n });\r\n\r\n /**\r\n * Stroke the path. The PDF \"S\" operator.\r\n * @name stroke\r\n * @function\r\n * @instance\r\n * @returns {jsPDF}\r\n * @memberof jsPDF#\r\n */\r\n API.stroke = function() {\r\n out(\"S\");\r\n return this;\r\n };\r\n\r\n /**\r\n * Fill the current path using the nonzero winding number rule. If a pattern is provided, the path will be filled\r\n * with this pattern, otherwise with the current fill color. Equivalent to the PDF \"f\" operator.\r\n * @name fill\r\n * @function\r\n * @instance\r\n * @param {PatternData=} pattern If provided the path will be filled with this pattern\r\n * @returns {jsPDF}\r\n * @memberof jsPDF#\r\n */\r\n API.fill = function(pattern) {\r\n fillWithOptionalPattern(\"f\", pattern);\r\n return this;\r\n };\r\n\r\n /**\r\n * Fill the current path using the even-odd rule. The PDF f* operator.\r\n * @see API.fill\r\n * @name fillEvenOdd\r\n * @function\r\n * @instance\r\n * @param {PatternData=} pattern If provided the path will be filled with this pattern\r\n * @returns {jsPDF}\r\n * @memberof jsPDF#\r\n */\r\n API.fillEvenOdd = function(pattern) {\r\n fillWithOptionalPattern(\"f*\", pattern);\r\n return this;\r\n };\r\n\r\n /**\r\n * Fill using the nonzero winding number rule and then stroke the current Path. The PDF \"B\" operator.\r\n * @see API.fill\r\n * @name fillStroke\r\n * @function\r\n * @instance\r\n * @param {PatternData=} pattern If provided the path will be stroked with this pattern\r\n * @returns {jsPDF}\r\n * @memberof jsPDF#\r\n */\r\n API.fillStroke = function(pattern) {\r\n fillWithOptionalPattern(\"B\", pattern);\r\n return this;\r\n };\r\n\r\n /**\r\n * Fill using the even-odd rule and then stroke the current Path. The PDF \"B\" operator.\r\n * @see API.fill\r\n * @name fillStrokeEvenOdd\r\n * @function\r\n * @instance\r\n * @param {PatternData=} pattern If provided the path will be fill-stroked with this pattern\r\n * @returns {jsPDF}\r\n * @memberof jsPDF#\r\n */\r\n API.fillStrokeEvenOdd = function(pattern) {\r\n fillWithOptionalPattern(\"B*\", pattern);\r\n return this;\r\n };\r\n\r\n var fillWithOptionalPattern = function(style, pattern) {\r\n if (typeof pattern === \"object\") {\r\n fillWithPattern(pattern, style);\r\n } else {\r\n out(style);\r\n }\r\n };\r\n\r\n var putStyle = function(style) {\r\n if (\r\n style === null ||\r\n (apiMode === ApiMode.ADVANCED && style === undefined)\r\n ) {\r\n return;\r\n }\r\n\r\n style = getStyle(style);\r\n\r\n // stroking / filling / both the path\r\n out(style);\r\n };\r\n\r\n function cloneTilingPattern(patternKey, boundingBox, xStep, yStep, matrix) {\r\n var clone = new TilingPattern(\r\n boundingBox || this.boundingBox,\r\n xStep || this.xStep,\r\n yStep || this.yStep,\r\n this.gState,\r\n matrix || this.matrix\r\n );\r\n clone.stream = this.stream;\r\n var key = patternKey + \"$$\" + this.cloneIndex++ + \"$$\";\r\n addPattern(key, clone);\r\n return clone;\r\n }\r\n\r\n var fillWithPattern = function(patternData, style) {\r\n var patternId = patternMap[patternData.key];\r\n var pattern = patterns[patternId];\r\n\r\n if (pattern instanceof ShadingPattern) {\r\n out(\"q\");\r\n\r\n out(clipRuleFromStyle(style));\r\n\r\n if (pattern.gState) {\r\n API.setGState(pattern.gState);\r\n }\r\n out(patternData.matrix.toString() + \" cm\");\r\n out(\"/\" + patternId + \" sh\");\r\n out(\"Q\");\r\n } else if (pattern instanceof TilingPattern) {\r\n // pdf draws patterns starting at the bottom left corner and they are not affected by the global transformation,\r\n // so we must flip them\r\n var matrix = new Matrix(1, 0, 0, -1, 0, getPageHeight());\r\n\r\n if (patternData.matrix) {\r\n matrix = matrix.multiply(patternData.matrix || identityMatrix);\r\n // we cannot apply a matrix to the pattern on use so we must abuse the pattern matrix and create new instances\r\n // for each use\r\n patternId = cloneTilingPattern.call(\r\n pattern,\r\n patternData.key,\r\n patternData.boundingBox,\r\n patternData.xStep,\r\n patternData.yStep,\r\n matrix\r\n ).id;\r\n }\r\n\r\n out(\"q\");\r\n out(\"/Pattern cs\");\r\n out(\"/\" + patternId + \" scn\");\r\n\r\n if (pattern.gState) {\r\n API.setGState(pattern.gState);\r\n }\r\n\r\n out(style);\r\n out(\"Q\");\r\n }\r\n };\r\n\r\n var clipRuleFromStyle = function(style) {\r\n switch (style) {\r\n case \"f\":\r\n case \"F\":\r\n return \"W n\";\r\n case \"f*\":\r\n return \"W* n\";\r\n case \"B\":\r\n return \"W S\";\r\n case \"B*\":\r\n return \"W* S\";\r\n\r\n // these two are for compatibility reasons (in the past, calling any primitive method with a shading pattern\r\n // and \"n\"/\"S\" as style would still fill/fill and stroke the path)\r\n case \"S\":\r\n return \"W S\";\r\n case \"n\":\r\n return \"W n\";\r\n }\r\n };\r\n\r\n /**\r\n * Begin a new subpath by moving the current point to coordinates (x, y). The PDF \"m\" operator.\r\n * @param {number} x\r\n * @param {number} y\r\n * @name moveTo\r\n * @function\r\n * @instance\r\n * @memberof jsPDF#\r\n * @returns {jsPDF}\r\n */\r\n var moveTo = (API.moveTo = function(x, y) {\r\n out(hpf(scale(x)) + \" \" + hpf(transformScaleY(y)) + \" m\");\r\n return this;\r\n });\r\n\r\n /**\r\n * Append a straight line segment from the current point to the point (x, y). The PDF \"l\" operator.\r\n * @param {number} x\r\n * @param {number} y\r\n * @memberof jsPDF#\r\n * @name lineTo\r\n * @function\r\n * @instance\r\n * @memberof jsPDF#\r\n * @returns {jsPDF}\r\n */\r\n var lineTo = (API.lineTo = function(x, y) {\r\n out(hpf(scale(x)) + \" \" + hpf(transformScaleY(y)) + \" l\");\r\n return this;\r\n });\r\n\r\n /**\r\n * Append a cubic Bézier curve to the current path. The curve shall extend from the current point to the point\r\n * (x3, y3), using (x1, y1) and (x2, y2) as Bézier control points. The new current point shall be (x3, x3).\r\n * @param {number} x1\r\n * @param {number} y1\r\n * @param {number} x2\r\n * @param {number} y2\r\n * @param {number} x3\r\n * @param {number} y3\r\n * @memberof jsPDF#\r\n * @name curveTo\r\n * @function\r\n * @instance\r\n * @memberof jsPDF#\r\n * @returns {jsPDF}\r\n */\r\n var curveTo = (API.curveTo = function(x1, y1, x2, y2, x3, y3) {\r\n out(\r\n [\r\n hpf(scale(x1)),\r\n hpf(transformScaleY(y1)),\r\n hpf(scale(x2)),\r\n hpf(transformScaleY(y2)),\r\n hpf(scale(x3)),\r\n hpf(transformScaleY(y3)),\r\n \"c\"\r\n ].join(\" \")\r\n );\r\n return this;\r\n });\r\n\r\n /**\r\n * Draw a line on the current page.\r\n *\r\n * @name line\r\n * @function\r\n * @instance\r\n * @param {number} x1\r\n * @param {number} y1\r\n * @param {number} x2\r\n * @param {number} y2\r\n * @param {string} style A string specifying the painting style or null. Valid styles include: 'S' [default] - stroke, 'F' - fill, and 'DF' (or 'FD') - fill then stroke. A null value postpones setting the style so that a shape may be composed using multiple method calls. The last drawing method call used to define the shape should not have a null style argument. default: 'S'\r\n * @returns {jsPDF}\r\n * @memberof jsPDF#\r\n */\r\n API.__private__.line = API.line = function(x1, y1, x2, y2, style) {\r\n if (\r\n isNaN(x1) ||\r\n isNaN(y1) ||\r\n isNaN(x2) ||\r\n isNaN(y2) ||\r\n !isValidStyle(style)\r\n ) {\r\n throw new Error(\"Invalid arguments passed to jsPDF.line\");\r\n }\r\n if (apiMode === ApiMode.COMPAT) {\r\n return this.lines([[x2 - x1, y2 - y1]], x1, y1, [1, 1], style || \"S\");\r\n } else {\r\n return this.lines([[x2 - x1, y2 - y1]], x1, y1, [1, 1]).stroke();\r\n }\r\n };\r\n\r\n /**\r\n * @typedef {Object} PatternData\r\n * {Matrix|undefined} matrix\r\n * {Number|undefined} xStep\r\n * {Number|undefined} yStep\r\n * {Array.|undefined} boundingBox\r\n */\r\n\r\n /**\r\n * Adds series of curves (straight lines or cubic bezier curves) to canvas, starting at `x`, `y` coordinates.\r\n * All data points in `lines` are relative to last line origin.\r\n * `x`, `y` become x1,y1 for first line / curve in the set.\r\n * For lines you only need to specify [x2, y2] - (ending point) vector against x1, y1 starting point.\r\n * For bezier curves you need to specify [x2,y2,x3,y3,x4,y4] - vectors to control points 1, 2, ending point. All vectors are against the start of the curve - x1,y1.\r\n *\r\n * @example .lines([[2,2],[-2,2],[1,1,2,2,3,3],[2,1]], 212,110, [1,1], 'F', false) // line, line, bezier curve, line\r\n * @param {Array} lines Array of *vector* shifts as pairs (lines) or sextets (cubic bezier curves).\r\n * @param {number} x Coordinate (in units declared at inception of PDF document) against left edge of the page\r\n * @param {number} y Coordinate (in units declared at inception of PDF document) against upper edge of the page\r\n * @param {number} scale (Defaults to [1.0,1.0]) x,y Scaling factor for all vectors. Elements can be any floating number Sub-one makes drawing smaller. Over-one grows the drawing. Negative flips the direction.\r\n * @param {string=} style A string specifying the painting style or null. Valid styles include:\r\n * 'S' [default] - stroke,\r\n * 'F' - fill,\r\n * and 'DF' (or 'FD') - fill then stroke.\r\n * In \"compat\" API mode, a null value postpones setting the style so that a shape may be composed using multiple\r\n * method calls. The last drawing method call used to define the shape should not have a null style argument.\r\n *\r\n * In \"advanced\" API mode this parameter is deprecated.\r\n * @param {Boolean=} closed If true, the path is closed with a straight line from the end of the last curve to the starting point.\r\n * @function\r\n * @instance\r\n * @returns {jsPDF}\r\n * @memberof jsPDF#\r\n * @name lines\r\n */\r\n API.__private__.lines = API.lines = function(\r\n lines,\r\n x,\r\n y,\r\n scale,\r\n style,\r\n closed\r\n ) {\r\n var scalex, scaley, i, l, leg, x2, y2, x3, y3, x4, y4, tmp;\r\n\r\n // Pre-August-2012 the order of arguments was function(x, y, lines, scale, style)\r\n // in effort to make all calls have similar signature like\r\n // function(content, coordinateX, coordinateY , miscellaneous)\r\n // this method had its args flipped.\r\n // code below allows backward compatibility with old arg order.\r\n if (typeof lines === \"number\") {\r\n tmp = y;\r\n y = x;\r\n x = lines;\r\n lines = tmp;\r\n }\r\n\r\n scale = scale || [1, 1];\r\n closed = closed || false;\r\n\r\n if (\r\n isNaN(x) ||\r\n isNaN(y) ||\r\n !Array.isArray(lines) ||\r\n !Array.isArray(scale) ||\r\n !isValidStyle(style) ||\r\n typeof closed !== \"boolean\"\r\n ) {\r\n throw new Error(\"Invalid arguments passed to jsPDF.lines\");\r\n }\r\n\r\n // starting point\r\n moveTo(x, y);\r\n\r\n scalex = scale[0];\r\n scaley = scale[1];\r\n l = lines.length;\r\n //, x2, y2 // bezier only. In page default measurement \"units\", *after* scaling\r\n //, x3, y3 // bezier only. In page default measurement \"units\", *after* scaling\r\n // ending point for all, lines and bezier. . In page default measurement \"units\", *after* scaling\r\n x4 = x; // last / ending point = starting point for first item.\r\n y4 = y; // last / ending point = starting point for first item.\r\n\r\n for (i = 0; i < l; i++) {\r\n leg = lines[i];\r\n if (leg.length === 2) {\r\n // simple line\r\n x4 = leg[0] * scalex + x4; // here last x4 was prior ending point\r\n y4 = leg[1] * scaley + y4; // here last y4 was prior ending point\r\n lineTo(x4, y4);\r\n } else {\r\n // bezier curve\r\n x2 = leg[0] * scalex + x4; // here last x4 is prior ending point\r\n y2 = leg[1] * scaley + y4; // here last y4 is prior ending point\r\n x3 = leg[2] * scalex + x4; // here last x4 is prior ending point\r\n y3 = leg[3] * scaley + y4; // here last y4 is prior ending point\r\n x4 = leg[4] * scalex + x4; // here last x4 was prior ending point\r\n y4 = leg[5] * scaley + y4; // here last y4 was prior ending point\r\n curveTo(x2, y2, x3, y3, x4, y4);\r\n }\r\n }\r\n\r\n if (closed) {\r\n close();\r\n }\r\n\r\n putStyle(style);\r\n return this;\r\n };\r\n\r\n /**\r\n * Similar to {@link API.lines} but all coordinates are interpreted as absolute coordinates instead of relative.\r\n * @param {Array>\" });\n\n scope.internal.acroformPlugin.xForms.push(appearance);\n }\n\n // Assume AppearanceStreamContent is a Array with N,R,D (at least\n // one of them!)\n if (fieldObject.appearanceStreamContent) {\n var appearanceStreamString = \"\";\n // Iterate over N,R and D\n for (var k in fieldObject.appearanceStreamContent) {\n if (fieldObject.appearanceStreamContent.hasOwnProperty(k)) {\n var value = fieldObject.appearanceStreamContent[k];\n appearanceStreamString += \"/\" + k + \" \";\n appearanceStreamString += \"<<\";\n if (Object.keys(value).length >= 1 || Array.isArray(value)) {\n // appearanceStream is an Array or Object!\n for (var i in value) {\n if (value.hasOwnProperty(i)) {\n var obj = value[i];\n if (typeof obj === \"function\") {\n // if Function is referenced, call it in order\n // to get the FormXObject\n obj = obj.call(scope, fieldObject);\n }\n appearanceStreamString += \"/\" + i + \" \" + obj + \" \";\n\n // In case the XForm is already used, e.g. OffState\n // of CheckBoxes, don't add it\n if (!(scope.internal.acroformPlugin.xForms.indexOf(obj) >= 0))\n scope.internal.acroformPlugin.xForms.push(obj);\n }\n }\n } else {\n obj = value;\n if (typeof obj === \"function\") {\n // if Function is referenced, call it in order to\n // get the FormXObject\n obj = obj.call(scope, fieldObject);\n }\n appearanceStreamString += \"/\" + i + \" \" + obj;\n if (!(scope.internal.acroformPlugin.xForms.indexOf(obj) >= 0))\n scope.internal.acroformPlugin.xForms.push(obj);\n }\n appearanceStreamString += \">>\";\n }\n }\n\n // appearance stream is a normal Object..\n keyValueList.push({\n key: \"AP\",\n value: \"<<\\n\" + appearanceStreamString + \">>\"\n });\n }\n\n scope.internal.putStream({\n additionalKeyValues: keyValueList,\n objectId: fieldObject.objId\n });\n\n scope.internal.out(\"endobj\");\n }\n }\n if (standardFields) {\n createXFormObjectCallback(scope.internal.acroformPlugin.xForms, scope);\n }\n};\n\nvar createXFormObjectCallback = function(fieldArray, scope) {\n for (var i in fieldArray) {\n if (fieldArray.hasOwnProperty(i)) {\n var key = i;\n var fieldObject = fieldArray[i];\n // Start Writing the Object\n scope.internal.newObjectDeferredBegin(fieldObject.objId, true);\n\n if (\n typeof fieldObject === \"object\" &&\n typeof fieldObject.putStream === \"function\"\n ) {\n fieldObject.putStream();\n }\n delete fieldArray[key];\n }\n }\n};\n\nvar initializeAcroForm = function(scope, formObject) {\n formObject.scope = scope;\n if (\n scope.internal !== undefined &&\n (scope.internal.acroformPlugin === undefined ||\n scope.internal.acroformPlugin.isInitialized === false)\n ) {\n AcroFormField.FieldNum = 0;\n scope.internal.acroformPlugin = JSON.parse(\n JSON.stringify(acroformPluginTemplate)\n );\n if (scope.internal.acroformPlugin.acroFormDictionaryRoot) {\n throw new Error(\"Exception while creating AcroformDictionary\");\n }\n scaleFactor = scope.internal.scaleFactor;\n // The Object Number of the AcroForm Dictionary\n scope.internal.acroformPlugin.acroFormDictionaryRoot = new AcroFormDictionary();\n scope.internal.acroformPlugin.acroFormDictionaryRoot.scope = scope;\n\n // add Callback for creating the AcroForm Dictionary\n scope.internal.acroformPlugin.acroFormDictionaryRoot._eventID = scope.internal.events.subscribe(\n \"postPutResources\",\n function() {\n AcroFormDictionaryCallback(scope);\n }\n );\n\n scope.internal.events.subscribe(\"buildDocument\", function() {\n annotReferenceCallback(scope);\n }); // buildDocument\n\n // Register event, that is triggered when the DocumentCatalog is\n // written, in order to add /AcroForm\n\n scope.internal.events.subscribe(\"putCatalog\", function() {\n putCatalogCallback(scope);\n });\n\n // Register event, that creates all Fields\n scope.internal.events.subscribe(\"postPutPages\", function(fieldArray) {\n createFieldCallback(fieldArray, scope);\n });\n\n scope.internal.acroformPlugin.isInitialized = true;\n }\n};\n\n//PDF 32000-1:2008, page 26, 7.3.6\nvar arrayToPdfArray = (jsPDFAPI.__acroform__.arrayToPdfArray = function(\n array,\n objId,\n scope\n) {\n var encryptor = function(data) {\n return data;\n };\n if (Array.isArray(array)) {\n var content = \"[\";\n for (var i = 0; i < array.length; i++) {\n if (i !== 0) {\n content += \" \";\n }\n switch (typeof array[i]) {\n case \"boolean\":\n case \"number\":\n case \"object\":\n content += array[i].toString();\n break;\n case \"string\":\n if (array[i].substr(0, 1) !== \"/\") {\n if (typeof objId !== \"undefined\" && scope)\n encryptor = scope.internal.getEncryptor(objId);\n content += \"(\" + pdfEscape(encryptor(array[i].toString())) + \")\";\n } else {\n content += array[i].toString();\n }\n break;\n }\n }\n content += \"]\";\n return content;\n }\n throw new Error(\n \"Invalid argument passed to jsPDF.__acroform__.arrayToPdfArray\"\n );\n});\nfunction getMatches(string, regex, index) {\n index || (index = 1); // default to the first capturing group\n var matches = [];\n var match;\n while ((match = regex.exec(string))) {\n matches.push(match[index]);\n }\n return matches;\n}\nvar pdfArrayToStringArray = function(array) {\n var result = [];\n if (typeof array === \"string\") {\n result = getMatches(array, /\\((.*?)\\)/g);\n }\n return result;\n};\n\nvar toPdfString = function(string, objId, scope) {\n var encryptor = function(data) {\n return data;\n };\n if (typeof objId !== \"undefined\" && scope)\n encryptor = scope.internal.getEncryptor(objId);\n string = string || \"\";\n string.toString();\n string = \"(\" + pdfEscape(encryptor(string)) + \")\";\n return string;\n};\n\n// ##########################\n// Classes\n// ##########################\n\n/**\n * @class AcroFormPDFObject\n * @classdesc A AcroFormPDFObject\n */\nvar AcroFormPDFObject = function() {\n this._objId = undefined;\n this._scope = undefined;\n\n /**\n * @name AcroFormPDFObject#objId\n * @type {any}\n */\n Object.defineProperty(this, \"objId\", {\n get: function() {\n if (typeof this._objId === \"undefined\") {\n if (typeof this.scope === \"undefined\") {\n return undefined;\n }\n this._objId = this.scope.internal.newObjectDeferred();\n }\n return this._objId;\n },\n set: function(value) {\n this._objId = value;\n }\n });\n Object.defineProperty(this, \"scope\", {\n value: this._scope,\n writable: true\n });\n};\n\n/**\n * @function AcroFormPDFObject.toString\n */\nAcroFormPDFObject.prototype.toString = function() {\n return this.objId + \" 0 R\";\n};\n\nAcroFormPDFObject.prototype.putStream = function() {\n var keyValueList = this.getKeyValueListForStream();\n this.scope.internal.putStream({\n data: this.stream,\n additionalKeyValues: keyValueList,\n objectId: this.objId\n });\n this.scope.internal.out(\"endobj\");\n};\n\n/**\n * Returns an key-value-List of all non-configurable Variables from the Object\n *\n * @name getKeyValueListForStream\n * @returns {string}\n */\nAcroFormPDFObject.prototype.getKeyValueListForStream = function() {\n var keyValueList = [];\n var keys = Object.getOwnPropertyNames(this).filter(function(key) {\n return (\n key != \"content\" &&\n key != \"appearanceStreamContent\" &&\n key != \"scope\" &&\n key != \"objId\" &&\n key.substring(0, 1) != \"_\"\n );\n });\n\n for (var i in keys) {\n if (Object.getOwnPropertyDescriptor(this, keys[i]).configurable === false) {\n var key = keys[i];\n var value = this[key];\n\n if (value) {\n if (Array.isArray(value)) {\n keyValueList.push({\n key: key,\n value: arrayToPdfArray(value, this.objId, this.scope)\n });\n } else if (value instanceof AcroFormPDFObject) {\n // In case it is a reference to another PDFObject,\n // take the reference number\n value.scope = this.scope;\n keyValueList.push({ key: key, value: value.objId + \" 0 R\" });\n } else if (typeof value !== \"function\") {\n keyValueList.push({ key: key, value: value });\n }\n }\n }\n }\n return keyValueList;\n};\n\nvar AcroFormXObject = function() {\n AcroFormPDFObject.call(this);\n\n Object.defineProperty(this, \"Type\", {\n value: \"/XObject\",\n configurable: false,\n writable: true\n });\n\n Object.defineProperty(this, \"Subtype\", {\n value: \"/Form\",\n configurable: false,\n writable: true\n });\n\n Object.defineProperty(this, \"FormType\", {\n value: 1,\n configurable: false,\n writable: true\n });\n\n var _BBox = [];\n Object.defineProperty(this, \"BBox\", {\n configurable: false,\n get: function() {\n return _BBox;\n },\n set: function(value) {\n _BBox = value;\n }\n });\n\n Object.defineProperty(this, \"Resources\", {\n value: \"2 0 R\",\n configurable: false,\n writable: true\n });\n\n var _stream;\n Object.defineProperty(this, \"stream\", {\n enumerable: false,\n configurable: true,\n set: function(value) {\n _stream = value.trim();\n },\n get: function() {\n if (_stream) {\n return _stream;\n } else {\n return null;\n }\n }\n });\n};\n\ninherit(AcroFormXObject, AcroFormPDFObject);\n\nvar AcroFormDictionary = function() {\n AcroFormPDFObject.call(this);\n\n var _Kids = [];\n\n Object.defineProperty(this, \"Kids\", {\n enumerable: false,\n configurable: true,\n get: function() {\n if (_Kids.length > 0) {\n return _Kids;\n } else {\n return undefined;\n }\n }\n });\n Object.defineProperty(this, \"Fields\", {\n enumerable: false,\n configurable: false,\n get: function() {\n return _Kids;\n }\n });\n\n // Default Appearance\n var _DA;\n Object.defineProperty(this, \"DA\", {\n enumerable: false,\n configurable: false,\n get: function() {\n if (!_DA) {\n return undefined;\n }\n var encryptor = function(data) {\n return data;\n };\n if (this.scope) encryptor = this.scope.internal.getEncryptor(this.objId);\n return \"(\" + pdfEscape(encryptor(_DA)) + \")\";\n },\n set: function(value) {\n _DA = value;\n }\n });\n};\n\ninherit(AcroFormDictionary, AcroFormPDFObject);\n\n/**\n * The Field Object contains the Variables, that every Field needs\n *\n * @class AcroFormField\n * @classdesc An AcroForm FieldObject\n */\nvar AcroFormField = function() {\n AcroFormPDFObject.call(this);\n\n //Annotation-Flag See Table 165\n var _F = 4;\n Object.defineProperty(this, \"F\", {\n enumerable: false,\n configurable: false,\n get: function() {\n return _F;\n },\n set: function(value) {\n if (!isNaN(value)) {\n _F = value;\n } else {\n throw new Error(\n 'Invalid value \"' + value + '\" for attribute F supplied.'\n );\n }\n }\n });\n\n /**\n * (PDF 1.2) If set, print the annotation when the page is printed. If clear, never print the annotation, regardless of wether is is displayed on the screen.\n * NOTE 2 This can be useful for annotations representing interactive pushbuttons, which would serve no meaningful purpose on the printed page.\n *\n * @name AcroFormField#showWhenPrinted\n * @default true\n * @type {boolean}\n */\n Object.defineProperty(this, \"showWhenPrinted\", {\n enumerable: true,\n configurable: true,\n get: function() {\n return Boolean(getBitForPdf(_F, 3));\n },\n set: function(value) {\n if (Boolean(value) === true) {\n this.F = setBitForPdf(_F, 3);\n } else {\n this.F = clearBitForPdf(_F, 3);\n }\n }\n });\n\n var _Ff = 0;\n Object.defineProperty(this, \"Ff\", {\n enumerable: false,\n configurable: false,\n get: function() {\n return _Ff;\n },\n set: function(value) {\n if (!isNaN(value)) {\n _Ff = value;\n } else {\n throw new Error(\n 'Invalid value \"' + value + '\" for attribute Ff supplied.'\n );\n }\n }\n });\n\n var _Rect = [];\n Object.defineProperty(this, \"Rect\", {\n enumerable: false,\n configurable: false,\n get: function() {\n if (_Rect.length === 0) {\n return undefined;\n }\n return _Rect;\n },\n set: function(value) {\n if (typeof value !== \"undefined\") {\n _Rect = value;\n } else {\n _Rect = [];\n }\n }\n });\n\n /**\n * The x-position of the field.\n *\n * @name AcroFormField#x\n * @default null\n * @type {number}\n */\n Object.defineProperty(this, \"x\", {\n enumerable: true,\n configurable: true,\n get: function() {\n if (!_Rect || isNaN(_Rect[0])) {\n return 0;\n }\n return _Rect[0];\n },\n set: function(value) {\n _Rect[0] = value;\n }\n });\n\n /**\n * The y-position of the field.\n *\n * @name AcroFormField#y\n * @default null\n * @type {number}\n */\n Object.defineProperty(this, \"y\", {\n enumerable: true,\n configurable: true,\n get: function() {\n if (!_Rect || isNaN(_Rect[1])) {\n return 0;\n }\n return _Rect[1];\n },\n set: function(value) {\n _Rect[1] = value;\n }\n });\n\n /**\n * The width of the field.\n *\n * @name AcroFormField#width\n * @default null\n * @type {number}\n */\n Object.defineProperty(this, \"width\", {\n enumerable: true,\n configurable: true,\n get: function() {\n if (!_Rect || isNaN(_Rect[2])) {\n return 0;\n }\n return _Rect[2];\n },\n set: function(value) {\n _Rect[2] = value;\n }\n });\n\n /**\n * The height of the field.\n *\n * @name AcroFormField#height\n * @default null\n * @type {number}\n */\n Object.defineProperty(this, \"height\", {\n enumerable: true,\n configurable: true,\n get: function() {\n if (!_Rect || isNaN(_Rect[3])) {\n return 0;\n }\n return _Rect[3];\n },\n set: function(value) {\n _Rect[3] = value;\n }\n });\n\n var _FT = \"\";\n Object.defineProperty(this, \"FT\", {\n enumerable: true,\n configurable: false,\n get: function() {\n return _FT;\n },\n set: function(value) {\n switch (value) {\n case \"/Btn\":\n case \"/Tx\":\n case \"/Ch\":\n case \"/Sig\":\n _FT = value;\n break;\n default:\n throw new Error(\n 'Invalid value \"' + value + '\" for attribute FT supplied.'\n );\n }\n }\n });\n\n var _T = null;\n\n Object.defineProperty(this, \"T\", {\n enumerable: true,\n configurable: false,\n get: function() {\n if (!_T || _T.length < 1) {\n // In case of a Child from a Radio´Group, you don't need a FieldName\n if (this instanceof AcroFormChildClass) {\n return undefined;\n }\n _T = \"FieldObject\" + AcroFormField.FieldNum++;\n }\n var encryptor = function(data) {\n return data;\n };\n if (this.scope) encryptor = this.scope.internal.getEncryptor(this.objId);\n return \"(\" + pdfEscape(encryptor(_T)) + \")\";\n },\n set: function(value) {\n _T = value.toString();\n }\n });\n\n /**\n * (Optional) The partial field name (see 12.7.3.2, “Field Names”).\n *\n * @name AcroFormField#fieldName\n * @default null\n * @type {string}\n */\n Object.defineProperty(this, \"fieldName\", {\n configurable: true,\n enumerable: true,\n get: function() {\n return _T;\n },\n set: function(value) {\n _T = value;\n }\n });\n\n var _fontName = \"helvetica\";\n /**\n * The fontName of the font to be used.\n *\n * @name AcroFormField#fontName\n * @default 'helvetica'\n * @type {string}\n */\n Object.defineProperty(this, \"fontName\", {\n enumerable: true,\n configurable: true,\n get: function() {\n return _fontName;\n },\n set: function(value) {\n _fontName = value;\n }\n });\n\n var _fontStyle = \"normal\";\n /**\n * The fontStyle of the font to be used.\n *\n * @name AcroFormField#fontStyle\n * @default 'normal'\n * @type {string}\n */\n Object.defineProperty(this, \"fontStyle\", {\n enumerable: true,\n configurable: true,\n get: function() {\n return _fontStyle;\n },\n set: function(value) {\n _fontStyle = value;\n }\n });\n\n var _fontSize = 0;\n /**\n * The fontSize of the font to be used.\n *\n * @name AcroFormField#fontSize\n * @default 0 (for auto)\n * @type {number}\n */\n Object.defineProperty(this, \"fontSize\", {\n enumerable: true,\n configurable: true,\n get: function() {\n return _fontSize;\n },\n set: function(value) {\n _fontSize = value;\n }\n });\n\n var _maxFontSize = undefined;\n /**\n * The maximum fontSize of the font to be used.\n *\n * @name AcroFormField#maxFontSize\n * @default 0 (for auto)\n * @type {number}\n */\n Object.defineProperty(this, \"maxFontSize\", {\n enumerable: true,\n configurable: true,\n get: function() {\n if (_maxFontSize === undefined) {\n // use the old default value here - the value is some kind of random as it depends on the scaleFactor (user unit)\n // (\"50\" is transformed to the \"user space\" but then used in \"pdf space\")\n return 50 / scaleFactor;\n } else {\n return _maxFontSize;\n }\n },\n set: function(value) {\n _maxFontSize = value;\n }\n });\n\n var _color = \"black\";\n /**\n * The color of the text\n *\n * @name AcroFormField#color\n * @default 'black'\n * @type {string|rgba}\n */\n Object.defineProperty(this, \"color\", {\n enumerable: true,\n configurable: true,\n get: function() {\n return _color;\n },\n set: function(value) {\n _color = value;\n }\n });\n\n var _DA = \"/F1 0 Tf 0 g\";\n // Defines the default appearance (Needed for variable Text)\n Object.defineProperty(this, \"DA\", {\n enumerable: true,\n configurable: false,\n get: function() {\n if (\n !_DA ||\n this instanceof AcroFormChildClass ||\n this instanceof AcroFormTextField\n ) {\n return undefined;\n }\n return toPdfString(_DA, this.objId, this.scope);\n },\n set: function(value) {\n value = value.toString();\n _DA = value;\n }\n });\n\n var _DV = null;\n Object.defineProperty(this, \"DV\", {\n enumerable: false,\n configurable: false,\n get: function() {\n if (!_DV) {\n return undefined;\n }\n if (this instanceof AcroFormButton === false) {\n return toPdfString(_DV, this.objId, this.scope);\n }\n return _DV;\n },\n set: function(value) {\n value = value.toString();\n if (this instanceof AcroFormButton === false) {\n if (value.substr(0, 1) === \"(\") {\n _DV = pdfUnescape(value.substr(1, value.length - 2));\n } else {\n _DV = pdfUnescape(value);\n }\n } else {\n _DV = value;\n }\n }\n });\n\n /**\n * (Optional; inheritable) The default value to which the field reverts when a reset-form action is executed (see 12.7.5.3, “Reset-Form Action”). The format of this value is the same as that of value.\n *\n * @name AcroFormField#defaultValue\n * @default null\n * @type {any}\n */\n Object.defineProperty(this, \"defaultValue\", {\n enumerable: true,\n configurable: true,\n get: function() {\n if (this instanceof AcroFormButton === true) {\n return pdfUnescape(_DV.substr(1, _DV.length - 1));\n } else {\n return _DV;\n }\n },\n set: function(value) {\n value = value.toString();\n if (this instanceof AcroFormButton === true) {\n _DV = \"/\" + value;\n } else {\n _DV = value;\n }\n }\n });\n\n var _V = null;\n Object.defineProperty(this, \"_V\", {\n enumerable: false,\n configurable: false,\n get: function() {\n if (!_V) {\n return undefined;\n }\n return _V;\n },\n set: function(value) {\n this.V = value;\n }\n });\n Object.defineProperty(this, \"V\", {\n enumerable: false,\n configurable: false,\n get: function() {\n if (!_V) {\n return undefined;\n }\n if (this instanceof AcroFormButton === false) {\n return toPdfString(_V, this.objId, this.scope);\n }\n return _V;\n },\n set: function(value) {\n value = value.toString();\n if (this instanceof AcroFormButton === false) {\n if (value.substr(0, 1) === \"(\") {\n _V = pdfUnescape(value.substr(1, value.length - 2));\n } else {\n _V = pdfUnescape(value);\n }\n } else {\n _V = value;\n }\n }\n });\n\n /**\n * (Optional; inheritable) The field’s value, whose format varies depending on the field type. See the descriptions of individual field types for further information.\n *\n * @name AcroFormField#value\n * @default null\n * @type {any}\n */\n Object.defineProperty(this, \"value\", {\n enumerable: true,\n configurable: true,\n get: function() {\n if (this instanceof AcroFormButton === true) {\n return pdfUnescape(_V.substr(1, _V.length - 1));\n } else {\n return _V;\n }\n },\n set: function(value) {\n value = value.toString();\n if (this instanceof AcroFormButton === true) {\n _V = \"/\" + value;\n } else {\n _V = value;\n }\n }\n });\n\n /**\n * Check if field has annotations\n *\n * @name AcroFormField#hasAnnotation\n * @readonly\n * @type {boolean}\n */\n Object.defineProperty(this, \"hasAnnotation\", {\n enumerable: true,\n configurable: true,\n get: function() {\n return this.Rect;\n }\n });\n\n Object.defineProperty(this, \"Type\", {\n enumerable: true,\n configurable: false,\n get: function() {\n return this.hasAnnotation ? \"/Annot\" : null;\n }\n });\n\n Object.defineProperty(this, \"Subtype\", {\n enumerable: true,\n configurable: false,\n get: function() {\n return this.hasAnnotation ? \"/Widget\" : null;\n }\n });\n\n var _hasAppearanceStream = false;\n /**\n * true if field has an appearanceStream\n *\n * @name AcroFormField#hasAppearanceStream\n * @readonly\n * @type {boolean}\n */\n Object.defineProperty(this, \"hasAppearanceStream\", {\n enumerable: true,\n configurable: true,\n get: function() {\n return _hasAppearanceStream;\n },\n set: function(value) {\n value = Boolean(value);\n _hasAppearanceStream = value;\n }\n });\n\n /**\n * The page on which the AcroFormField is placed\n *\n * @name AcroFormField#page\n * @type {number}\n */\n var _page;\n Object.defineProperty(this, \"page\", {\n enumerable: true,\n configurable: true,\n get: function() {\n if (!_page) {\n return undefined;\n }\n return _page;\n },\n set: function(value) {\n _page = value;\n }\n });\n\n /**\n * If set, the user may not change the value of the field. Any associated widget annotations will not interact with the user; that is, they will not respond to mouse clicks or change their appearance in response to mouse motions. This flag is useful for fields whose values are computed or imported from a database.\n *\n * @name AcroFormField#readOnly\n * @default false\n * @type {boolean}\n */\n Object.defineProperty(this, \"readOnly\", {\n enumerable: true,\n configurable: true,\n get: function() {\n return Boolean(getBitForPdf(this.Ff, 1));\n },\n set: function(value) {\n if (Boolean(value) === true) {\n this.Ff = setBitForPdf(this.Ff, 1);\n } else {\n this.Ff = clearBitForPdf(this.Ff, 1);\n }\n }\n });\n\n /**\n * If set, the field shall have a value at the time it is exported by a submitform action (see 12.7.5.2, “Submit-Form Action”).\n *\n * @name AcroFormField#required\n * @default false\n * @type {boolean}\n */\n Object.defineProperty(this, \"required\", {\n enumerable: true,\n configurable: true,\n get: function() {\n return Boolean(getBitForPdf(this.Ff, 2));\n },\n set: function(value) {\n if (Boolean(value) === true) {\n this.Ff = setBitForPdf(this.Ff, 2);\n } else {\n this.Ff = clearBitForPdf(this.Ff, 2);\n }\n }\n });\n\n /**\n * If set, the field shall not be exported by a submit-form action (see 12.7.5.2, “Submit-Form Action”)\n *\n * @name AcroFormField#noExport\n * @default false\n * @type {boolean}\n */\n Object.defineProperty(this, \"noExport\", {\n enumerable: true,\n configurable: true,\n get: function() {\n return Boolean(getBitForPdf(this.Ff, 3));\n },\n set: function(value) {\n if (Boolean(value) === true) {\n this.Ff = setBitForPdf(this.Ff, 3);\n } else {\n this.Ff = clearBitForPdf(this.Ff, 3);\n }\n }\n });\n\n var _Q = null;\n Object.defineProperty(this, \"Q\", {\n enumerable: true,\n configurable: false,\n get: function() {\n if (_Q === null) {\n return undefined;\n }\n return _Q;\n },\n set: function(value) {\n if ([0, 1, 2].indexOf(value) !== -1) {\n _Q = value;\n } else {\n throw new Error(\n 'Invalid value \"' + value + '\" for attribute Q supplied.'\n );\n }\n }\n });\n\n /**\n * (Optional; inheritable) A code specifying the form of quadding (justification) that shall be used in displaying the text:\n * 'left', 'center', 'right'\n *\n * @name AcroFormField#textAlign\n * @default 'left'\n * @type {string}\n */\n Object.defineProperty(this, \"textAlign\", {\n get: function() {\n var result;\n switch (_Q) {\n case 0:\n default:\n result = \"left\";\n break;\n case 1:\n result = \"center\";\n break;\n case 2:\n result = \"right\";\n break;\n }\n return result;\n },\n configurable: true,\n enumerable: true,\n set: function(value) {\n switch (value) {\n case \"right\":\n case 2:\n _Q = 2;\n break;\n case \"center\":\n case 1:\n _Q = 1;\n break;\n case \"left\":\n case 0:\n default:\n _Q = 0;\n }\n }\n });\n};\n\ninherit(AcroFormField, AcroFormPDFObject);\n\n/**\n * @class AcroFormChoiceField\n * @extends AcroFormField\n */\nvar AcroFormChoiceField = function() {\n AcroFormField.call(this);\n // Field Type = Choice Field\n this.FT = \"/Ch\";\n // options\n this.V = \"()\";\n\n this.fontName = \"zapfdingbats\";\n // Top Index\n var _TI = 0;\n\n Object.defineProperty(this, \"TI\", {\n enumerable: true,\n configurable: false,\n get: function() {\n return _TI;\n },\n set: function(value) {\n _TI = value;\n }\n });\n\n /**\n * (Optional) For scrollable list boxes, the top index (the index in the Opt array of the first option visible in the list). Default value: 0.\n *\n * @name AcroFormChoiceField#topIndex\n * @default 0\n * @type {number}\n */\n Object.defineProperty(this, \"topIndex\", {\n enumerable: true,\n configurable: true,\n get: function() {\n return _TI;\n },\n set: function(value) {\n _TI = value;\n }\n });\n\n var _Opt = [];\n Object.defineProperty(this, \"Opt\", {\n enumerable: true,\n configurable: false,\n get: function() {\n return arrayToPdfArray(_Opt, this.objId, this.scope);\n },\n set: function(value) {\n _Opt = pdfArrayToStringArray(value);\n }\n });\n\n /**\n * @memberof AcroFormChoiceField\n * @name getOptions\n * @function\n * @instance\n * @returns {array} array of Options\n */\n this.getOptions = function() {\n return _Opt;\n };\n\n /**\n * @memberof AcroFormChoiceField\n * @name setOptions\n * @function\n * @instance\n * @param {array} value\n */\n this.setOptions = function(value) {\n _Opt = value;\n if (this.sort) {\n _Opt.sort();\n }\n };\n\n /**\n * @memberof AcroFormChoiceField\n * @name addOption\n * @function\n * @instance\n * @param {string} value\n */\n this.addOption = function(value) {\n value = value || \"\";\n value = value.toString();\n _Opt.push(value);\n if (this.sort) {\n _Opt.sort();\n }\n };\n\n /**\n * @memberof AcroFormChoiceField\n * @name removeOption\n * @function\n * @instance\n * @param {string} value\n * @param {boolean} allEntries (default: false)\n */\n this.removeOption = function(value, allEntries) {\n allEntries = allEntries || false;\n value = value || \"\";\n value = value.toString();\n\n while (_Opt.indexOf(value) !== -1) {\n _Opt.splice(_Opt.indexOf(value), 1);\n if (allEntries === false) {\n break;\n }\n }\n };\n\n /**\n * If set, the field is a combo box; if clear, the field is a list box.\n *\n * @name AcroFormChoiceField#combo\n * @default false\n * @type {boolean}\n */\n Object.defineProperty(this, \"combo\", {\n enumerable: true,\n configurable: true,\n get: function() {\n return Boolean(getBitForPdf(this.Ff, 18));\n },\n set: function(value) {\n if (Boolean(value) === true) {\n this.Ff = setBitForPdf(this.Ff, 18);\n } else {\n this.Ff = clearBitForPdf(this.Ff, 18);\n }\n }\n });\n\n /**\n * If set, the combo box shall include an editable text box as well as a drop-down list; if clear, it shall include only a drop-down list. This flag shall be used only if the Combo flag is set.\n *\n * @name AcroFormChoiceField#edit\n * @default false\n * @type {boolean}\n */\n Object.defineProperty(this, \"edit\", {\n enumerable: true,\n configurable: true,\n get: function() {\n return Boolean(getBitForPdf(this.Ff, 19));\n },\n set: function(value) {\n //PDF 32000-1:2008, page 444\n if (this.combo === true) {\n if (Boolean(value) === true) {\n this.Ff = setBitForPdf(this.Ff, 19);\n } else {\n this.Ff = clearBitForPdf(this.Ff, 19);\n }\n }\n }\n });\n\n /**\n * If set, the field’s option items shall be sorted alphabetically. This flag is intended for use by writers, not by readers. Conforming readers shall display the options in the order in which they occur in the Opt array (see Table 231).\n *\n * @name AcroFormChoiceField#sort\n * @default false\n * @type {boolean}\n */\n Object.defineProperty(this, \"sort\", {\n enumerable: true,\n configurable: true,\n get: function() {\n return Boolean(getBitForPdf(this.Ff, 20));\n },\n set: function(value) {\n if (Boolean(value) === true) {\n this.Ff = setBitForPdf(this.Ff, 20);\n _Opt.sort();\n } else {\n this.Ff = clearBitForPdf(this.Ff, 20);\n }\n }\n });\n\n /**\n * (PDF 1.4) If set, more than one of the field’s option items may be selected simultaneously; if clear, at most one item shall be selected\n *\n * @name AcroFormChoiceField#multiSelect\n * @default false\n * @type {boolean}\n */\n Object.defineProperty(this, \"multiSelect\", {\n enumerable: true,\n configurable: true,\n get: function() {\n return Boolean(getBitForPdf(this.Ff, 22));\n },\n set: function(value) {\n if (Boolean(value) === true) {\n this.Ff = setBitForPdf(this.Ff, 22);\n } else {\n this.Ff = clearBitForPdf(this.Ff, 22);\n }\n }\n });\n\n /**\n * (PDF 1.4) If set, text entered in the field shall not be spellchecked. This flag shall not be used unless the Combo and Edit flags are both set.\n *\n * @name AcroFormChoiceField#doNotSpellCheck\n * @default false\n * @type {boolean}\n */\n Object.defineProperty(this, \"doNotSpellCheck\", {\n enumerable: true,\n configurable: true,\n get: function() {\n return Boolean(getBitForPdf(this.Ff, 23));\n },\n set: function(value) {\n if (Boolean(value) === true) {\n this.Ff = setBitForPdf(this.Ff, 23);\n } else {\n this.Ff = clearBitForPdf(this.Ff, 23);\n }\n }\n });\n\n /**\n * (PDF 1.5) If set, the new value shall be committed as soon as a selection is made (commonly with the pointing device). In this case, supplying a value for a field involves three actions: selecting the field for fill-in, selecting a choice for the fill-in value, and leaving that field, which finalizes or “commits” the data choice and triggers any actions associated with the entry or changing of this data. If this flag is on, then processing does not wait for leaving the field action to occur, but immediately proceeds to the third step.\n * This option enables applications to perform an action once a selection is made, without requiring the user to exit the field. If clear, the new value is not committed until the user exits the field.\n *\n * @name AcroFormChoiceField#commitOnSelChange\n * @default false\n * @type {boolean}\n */\n Object.defineProperty(this, \"commitOnSelChange\", {\n enumerable: true,\n configurable: true,\n get: function() {\n return Boolean(getBitForPdf(this.Ff, 27));\n },\n set: function(value) {\n if (Boolean(value) === true) {\n this.Ff = setBitForPdf(this.Ff, 27);\n } else {\n this.Ff = clearBitForPdf(this.Ff, 27);\n }\n }\n });\n\n this.hasAppearanceStream = false;\n};\ninherit(AcroFormChoiceField, AcroFormField);\n\n/**\n * @class AcroFormListBox\n * @extends AcroFormChoiceField\n * @extends AcroFormField\n */\nvar AcroFormListBox = function() {\n AcroFormChoiceField.call(this);\n this.fontName = \"helvetica\";\n\n //PDF 32000-1:2008, page 444\n this.combo = false;\n};\ninherit(AcroFormListBox, AcroFormChoiceField);\n\n/**\n * @class AcroFormComboBox\n * @extends AcroFormListBox\n * @extends AcroFormChoiceField\n * @extends AcroFormField\n */\nvar AcroFormComboBox = function() {\n AcroFormListBox.call(this);\n this.combo = true;\n};\ninherit(AcroFormComboBox, AcroFormListBox);\n\n/**\n * @class AcroFormEditBox\n * @extends AcroFormComboBox\n * @extends AcroFormListBox\n * @extends AcroFormChoiceField\n * @extends AcroFormField\n */\nvar AcroFormEditBox = function() {\n AcroFormComboBox.call(this);\n this.edit = true;\n};\ninherit(AcroFormEditBox, AcroFormComboBox);\n\n/**\n * @class AcroFormButton\n * @extends AcroFormField\n */\nvar AcroFormButton = function() {\n AcroFormField.call(this);\n this.FT = \"/Btn\";\n\n /**\n * (Radio buttons only) If set, exactly one radio button shall be selected at all times; selecting the currently selected button has no effect. If clear, clicking the selected button deselects it, leaving no button selected.\n *\n * @name AcroFormButton#noToggleToOff\n * @type {boolean}\n */\n Object.defineProperty(this, \"noToggleToOff\", {\n enumerable: true,\n configurable: true,\n get: function() {\n return Boolean(getBitForPdf(this.Ff, 15));\n },\n set: function(value) {\n if (Boolean(value) === true) {\n this.Ff = setBitForPdf(this.Ff, 15);\n } else {\n this.Ff = clearBitForPdf(this.Ff, 15);\n }\n }\n });\n\n /**\n * If set, the field is a set of radio buttons; if clear, the field is a checkbox. This flag may be set only if the Pushbutton flag is clear.\n *\n * @name AcroFormButton#radio\n * @type {boolean}\n */\n Object.defineProperty(this, \"radio\", {\n enumerable: true,\n configurable: true,\n get: function() {\n return Boolean(getBitForPdf(this.Ff, 16));\n },\n set: function(value) {\n if (Boolean(value) === true) {\n this.Ff = setBitForPdf(this.Ff, 16);\n } else {\n this.Ff = clearBitForPdf(this.Ff, 16);\n }\n }\n });\n\n /**\n * If set, the field is a pushbutton that does not retain a permanent value.\n *\n * @name AcroFormButton#pushButton\n * @type {boolean}\n */\n Object.defineProperty(this, \"pushButton\", {\n enumerable: true,\n configurable: true,\n get: function() {\n return Boolean(getBitForPdf(this.Ff, 17));\n },\n set: function(value) {\n if (Boolean(value) === true) {\n this.Ff = setBitForPdf(this.Ff, 17);\n } else {\n this.Ff = clearBitForPdf(this.Ff, 17);\n }\n }\n });\n\n /**\n * (PDF 1.5) If set, a group of radio buttons within a radio button field that use the same value for the on state will turn on and off in unison; that is if one is checked, they are all checked. If clear, the buttons are mutually exclusive (the same behavior as HTML radio buttons).\n *\n * @name AcroFormButton#radioIsUnison\n * @type {boolean}\n */\n Object.defineProperty(this, \"radioIsUnison\", {\n enumerable: true,\n configurable: true,\n get: function() {\n return Boolean(getBitForPdf(this.Ff, 26));\n },\n set: function(value) {\n if (Boolean(value) === true) {\n this.Ff = setBitForPdf(this.Ff, 26);\n } else {\n this.Ff = clearBitForPdf(this.Ff, 26);\n }\n }\n });\n\n var _MK = {};\n Object.defineProperty(this, \"MK\", {\n enumerable: false,\n configurable: false,\n get: function() {\n var encryptor = function(data) {\n return data;\n };\n if (this.scope) encryptor = this.scope.internal.getEncryptor(this.objId);\n if (Object.keys(_MK).length !== 0) {\n var result = [];\n result.push(\"<<\");\n var key;\n for (key in _MK) {\n result.push(\"/\" + key + \" (\" + pdfEscape(encryptor(_MK[key])) + \")\");\n }\n result.push(\">>\");\n return result.join(\"\\n\");\n }\n return undefined;\n },\n set: function(value) {\n if (typeof value === \"object\") {\n _MK = value;\n }\n }\n });\n\n /**\n * From the PDF reference:\n * (Optional, button fields only) The widget annotation's normal caption which shall be displayed when it is not interacting with the user.\n * Unlike the remaining entries listed in this Table which apply only to widget annotations associated with pushbutton fields (see Pushbuttons in 12.7.4.2, \"Button Fields\"), the CA entry may be used with any type of button field, including check boxes (see Check Boxes in 12.7.4.2, \"Button Fields\") and radio buttons (Radio Buttons in 12.7.4.2, \"Button Fields\").\n *\n * - '8' = Cross,\n * - 'l' = Circle,\n * - '' = nothing\n * @name AcroFormButton#caption\n * @type {string}\n */\n Object.defineProperty(this, \"caption\", {\n enumerable: true,\n configurable: true,\n get: function() {\n return _MK.CA || \"\";\n },\n set: function(value) {\n if (typeof value === \"string\") {\n _MK.CA = value;\n }\n }\n });\n\n var _AS;\n Object.defineProperty(this, \"AS\", {\n enumerable: false,\n configurable: false,\n get: function() {\n return _AS;\n },\n set: function(value) {\n _AS = value;\n }\n });\n\n /**\n * (Required if the appearance dictionary AP contains one or more subdictionaries; PDF 1.2) The annotation's appearance state, which selects the applicable appearance stream from an appearance subdictionary (see Section 12.5.5, \"Appearance Streams\")\n *\n * @name AcroFormButton#appearanceState\n * @type {any}\n */\n Object.defineProperty(this, \"appearanceState\", {\n enumerable: true,\n configurable: true,\n get: function() {\n return _AS.substr(1, _AS.length - 1);\n },\n set: function(value) {\n _AS = \"/\" + value;\n }\n });\n};\ninherit(AcroFormButton, AcroFormField);\n\n/**\n * @class AcroFormPushButton\n * @extends AcroFormButton\n * @extends AcroFormField\n */\nvar AcroFormPushButton = function() {\n AcroFormButton.call(this);\n this.pushButton = true;\n};\ninherit(AcroFormPushButton, AcroFormButton);\n\n/**\n * @class AcroFormRadioButton\n * @extends AcroFormButton\n * @extends AcroFormField\n */\nvar AcroFormRadioButton = function() {\n AcroFormButton.call(this);\n this.radio = true;\n this.pushButton = false;\n\n var _Kids = [];\n Object.defineProperty(this, \"Kids\", {\n enumerable: true,\n configurable: false,\n get: function() {\n return _Kids;\n },\n set: function(value) {\n if (typeof value !== \"undefined\") {\n _Kids = value;\n } else {\n _Kids = [];\n }\n }\n });\n};\ninherit(AcroFormRadioButton, AcroFormButton);\n\n/**\n * The Child class of a RadioButton (the radioGroup) -> The single Buttons\n *\n * @class AcroFormChildClass\n * @extends AcroFormField\n * @ignore\n */\nvar AcroFormChildClass = function() {\n AcroFormField.call(this);\n\n var _parent;\n Object.defineProperty(this, \"Parent\", {\n enumerable: false,\n configurable: false,\n get: function() {\n return _parent;\n },\n set: function(value) {\n _parent = value;\n }\n });\n\n var _optionName;\n Object.defineProperty(this, \"optionName\", {\n enumerable: false,\n configurable: true,\n get: function() {\n return _optionName;\n },\n set: function(value) {\n _optionName = value;\n }\n });\n\n var _MK = {};\n Object.defineProperty(this, \"MK\", {\n enumerable: false,\n configurable: false,\n get: function() {\n var encryptor = function(data) {\n return data;\n };\n if (this.scope) encryptor = this.scope.internal.getEncryptor(this.objId);\n var result = [];\n result.push(\"<<\");\n var key;\n for (key in _MK) {\n result.push(\"/\" + key + \" (\" + pdfEscape(encryptor(_MK[key])) + \")\");\n }\n result.push(\">>\");\n return result.join(\"\\n\");\n },\n set: function(value) {\n if (typeof value === \"object\") {\n _MK = value;\n }\n }\n });\n\n /**\n * From the PDF reference:\n * (Optional, button fields only) The widget annotation's normal caption which shall be displayed when it is not interacting with the user.\n * Unlike the remaining entries listed in this Table which apply only to widget annotations associated with pushbutton fields (see Pushbuttons in 12.7.4.2, \"Button Fields\"), the CA entry may be used with any type of button field, including check boxes (see Check Boxes in 12.7.4.2, \"Button Fields\") and radio buttons (Radio Buttons in 12.7.4.2, \"Button Fields\").\n *\n * - '8' = Cross,\n * - 'l' = Circle,\n * - '' = nothing\n * @name AcroFormButton#caption\n * @type {string}\n */\n Object.defineProperty(this, \"caption\", {\n enumerable: true,\n configurable: true,\n get: function() {\n return _MK.CA || \"\";\n },\n set: function(value) {\n if (typeof value === \"string\") {\n _MK.CA = value;\n }\n }\n });\n\n var _AS;\n Object.defineProperty(this, \"AS\", {\n enumerable: false,\n configurable: false,\n get: function() {\n return _AS;\n },\n set: function(value) {\n _AS = value;\n }\n });\n\n /**\n * (Required if the appearance dictionary AP contains one or more subdictionaries; PDF 1.2) The annotation's appearance state, which selects the applicable appearance stream from an appearance subdictionary (see Section 12.5.5, \"Appearance Streams\")\n *\n * @name AcroFormButton#appearanceState\n * @type {any}\n */\n Object.defineProperty(this, \"appearanceState\", {\n enumerable: true,\n configurable: true,\n get: function() {\n return _AS.substr(1, _AS.length - 1);\n },\n set: function(value) {\n _AS = \"/\" + value;\n }\n });\n this.caption = \"l\";\n this.appearanceState = \"Off\";\n // todo: set AppearanceType as variable that can be set from the\n // outside...\n this._AppearanceType = AcroFormAppearance.RadioButton.Circle;\n // The Default appearanceType is the Circle\n this.appearanceStreamContent = this._AppearanceType.createAppearanceStream(\n this.optionName\n );\n};\ninherit(AcroFormChildClass, AcroFormField);\n\nAcroFormRadioButton.prototype.setAppearance = function(appearance) {\n if (!(\"createAppearanceStream\" in appearance && \"getCA\" in appearance)) {\n throw new Error(\n \"Couldn't assign Appearance to RadioButton. Appearance was Invalid!\"\n );\n }\n for (var objId in this.Kids) {\n if (this.Kids.hasOwnProperty(objId)) {\n var child = this.Kids[objId];\n child.appearanceStreamContent = appearance.createAppearanceStream(\n child.optionName\n );\n child.caption = appearance.getCA();\n }\n }\n};\n\nAcroFormRadioButton.prototype.createOption = function(name) {\n // Create new Child for RadioGroup\n var child = new AcroFormChildClass();\n child.Parent = this;\n child.optionName = name;\n // Add to Parent\n this.Kids.push(child);\n\n addField.call(this.scope, child);\n\n return child;\n};\n\n/**\n * @class AcroFormCheckBox\n * @extends AcroFormButton\n * @extends AcroFormField\n */\nvar AcroFormCheckBox = function() {\n AcroFormButton.call(this);\n\n this.fontName = \"zapfdingbats\";\n this.caption = \"3\";\n this.appearanceState = \"On\";\n this.value = \"On\";\n this.textAlign = \"center\";\n this.appearanceStreamContent = AcroFormAppearance.CheckBox.createAppearanceStream();\n};\ninherit(AcroFormCheckBox, AcroFormButton);\n\n/**\n * @class AcroFormTextField\n * @extends AcroFormField\n */\nvar AcroFormTextField = function() {\n AcroFormField.call(this);\n this.FT = \"/Tx\";\n\n /**\n * If set, the field may contain multiple lines of text; if clear, the field’s text shall be restricted to a single line.\n *\n * @name AcroFormTextField#multiline\n * @type {boolean}\n */\n Object.defineProperty(this, \"multiline\", {\n enumerable: true,\n configurable: true,\n get: function() {\n return Boolean(getBitForPdf(this.Ff, 13));\n },\n set: function(value) {\n if (Boolean(value) === true) {\n this.Ff = setBitForPdf(this.Ff, 13);\n } else {\n this.Ff = clearBitForPdf(this.Ff, 13);\n }\n }\n });\n\n /**\n * (PDF 1.4) If set, the text entered in the field represents the pathname of a file whose contents shall be submitted as the value of the field.\n *\n * @name AcroFormTextField#fileSelect\n * @type {boolean}\n */\n Object.defineProperty(this, \"fileSelect\", {\n enumerable: true,\n configurable: true,\n get: function() {\n return Boolean(getBitForPdf(this.Ff, 21));\n },\n set: function(value) {\n if (Boolean(value) === true) {\n this.Ff = setBitForPdf(this.Ff, 21);\n } else {\n this.Ff = clearBitForPdf(this.Ff, 21);\n }\n }\n });\n\n /**\n * (PDF 1.4) If set, text entered in the field shall not be spell-checked.\n *\n * @name AcroFormTextField#doNotSpellCheck\n * @type {boolean}\n */\n Object.defineProperty(this, \"doNotSpellCheck\", {\n enumerable: true,\n configurable: true,\n get: function() {\n return Boolean(getBitForPdf(this.Ff, 23));\n },\n set: function(value) {\n if (Boolean(value) === true) {\n this.Ff = setBitForPdf(this.Ff, 23);\n } else {\n this.Ff = clearBitForPdf(this.Ff, 23);\n }\n }\n });\n\n /**\n * (PDF 1.4) If set, the field shall not scroll (horizontally for single-line fields, vertically for multiple-line fields) to accommodate more text than fits within its annotation rectangle. Once the field is full, no further text shall be accepted for interactive form filling; for noninteractive form filling, the filler should take care not to add more character than will visibly fit in the defined area.\n *\n * @name AcroFormTextField#doNotScroll\n * @type {boolean}\n */\n Object.defineProperty(this, \"doNotScroll\", {\n enumerable: true,\n configurable: true,\n get: function() {\n return Boolean(getBitForPdf(this.Ff, 24));\n },\n set: function(value) {\n if (Boolean(value) === true) {\n this.Ff = setBitForPdf(this.Ff, 24);\n } else {\n this.Ff = clearBitForPdf(this.Ff, 24);\n }\n }\n });\n\n /**\n * (PDF 1.5) May be set only if the MaxLen entry is present in the text field dictionary (see Table 229) and if the Multiline, Password, and FileSelect flags are clear. If set, the field shall be automatically divided into as many equally spaced positions, or combs, as the value of MaxLen, and the text is laid out into those combs.\n *\n * @name AcroFormTextField#comb\n * @type {boolean}\n */\n Object.defineProperty(this, \"comb\", {\n enumerable: true,\n configurable: true,\n get: function() {\n return Boolean(getBitForPdf(this.Ff, 25));\n },\n set: function(value) {\n if (Boolean(value) === true) {\n this.Ff = setBitForPdf(this.Ff, 25);\n } else {\n this.Ff = clearBitForPdf(this.Ff, 25);\n }\n }\n });\n\n /**\n * (PDF 1.5) If set, the value of this field shall be a rich text string (see 12.7.3.4, “Rich Text Strings”). If the field has a value, the RV entry of the field dictionary (Table 222) shall specify the rich text string.\n *\n * @name AcroFormTextField#richText\n * @type {boolean}\n */\n Object.defineProperty(this, \"richText\", {\n enumerable: true,\n configurable: true,\n get: function() {\n return Boolean(getBitForPdf(this.Ff, 26));\n },\n set: function(value) {\n if (Boolean(value) === true) {\n this.Ff = setBitForPdf(this.Ff, 26);\n } else {\n this.Ff = clearBitForPdf(this.Ff, 26);\n }\n }\n });\n\n var _MaxLen = null;\n Object.defineProperty(this, \"MaxLen\", {\n enumerable: true,\n configurable: false,\n get: function() {\n return _MaxLen;\n },\n set: function(value) {\n _MaxLen = value;\n }\n });\n\n /**\n * (Optional; inheritable) The maximum length of the field’s text, in characters.\n *\n * @name AcroFormTextField#maxLength\n * @type {number}\n */\n Object.defineProperty(this, \"maxLength\", {\n enumerable: true,\n configurable: true,\n get: function() {\n return _MaxLen;\n },\n set: function(value) {\n if (Number.isInteger(value)) {\n _MaxLen = value;\n }\n }\n });\n\n Object.defineProperty(this, \"hasAppearanceStream\", {\n enumerable: true,\n configurable: true,\n get: function() {\n return this.V || this.DV;\n }\n });\n};\ninherit(AcroFormTextField, AcroFormField);\n\n/**\n * @class AcroFormPasswordField\n * @extends AcroFormTextField\n * @extends AcroFormField\n */\nvar AcroFormPasswordField = function() {\n AcroFormTextField.call(this);\n\n /**\n * If set, the field is intended for entering a secure password that should not be echoed visibly to the screen. Characters typed from the keyboard shall instead be echoed in some unreadable form, such as asterisks or bullet characters.\n * NOTE To protect password confidentiality, readers should never store the value of the text field in the PDF file if this flag is set.\n *\n * @name AcroFormTextField#password\n * @type {boolean}\n */\n Object.defineProperty(this, \"password\", {\n enumerable: true,\n configurable: true,\n get: function() {\n return Boolean(getBitForPdf(this.Ff, 14));\n },\n set: function(value) {\n if (Boolean(value) === true) {\n this.Ff = setBitForPdf(this.Ff, 14);\n } else {\n this.Ff = clearBitForPdf(this.Ff, 14);\n }\n }\n });\n this.password = true;\n};\ninherit(AcroFormPasswordField, AcroFormTextField);\n\n// Contains Methods for creating standard appearances\nvar AcroFormAppearance = {\n CheckBox: {\n createAppearanceStream: function() {\n var appearance = {\n N: {\n On: AcroFormAppearance.CheckBox.YesNormal\n },\n D: {\n On: AcroFormAppearance.CheckBox.YesPushDown,\n Off: AcroFormAppearance.CheckBox.OffPushDown\n }\n };\n\n return appearance;\n },\n /**\n * Returns the standard On Appearance for a CheckBox\n *\n * @returns {AcroFormXObject}\n */\n YesPushDown: function(formObject) {\n var xobj = createFormXObject(formObject);\n xobj.scope = formObject.scope;\n var stream = [];\n var fontKey = formObject.scope.internal.getFont(\n formObject.fontName,\n formObject.fontStyle\n ).id;\n var encodedColor = formObject.scope.__private__.encodeColorString(\n formObject.color\n );\n var calcRes = calculateX(formObject, formObject.caption);\n stream.push(\"0.749023 g\");\n stream.push(\n \"0 0 \" +\n f2(AcroFormAppearance.internal.getWidth(formObject)) +\n \" \" +\n f2(AcroFormAppearance.internal.getHeight(formObject)) +\n \" re\"\n );\n stream.push(\"f\");\n stream.push(\"BMC\");\n stream.push(\"q\");\n stream.push(\"0 0 1 rg\");\n stream.push(\n \"/\" + fontKey + \" \" + f2(calcRes.fontSize) + \" Tf \" + encodedColor\n );\n stream.push(\"BT\");\n stream.push(calcRes.text);\n stream.push(\"ET\");\n stream.push(\"Q\");\n stream.push(\"EMC\");\n xobj.stream = stream.join(\"\\n\");\n return xobj;\n },\n\n YesNormal: function(formObject) {\n var xobj = createFormXObject(formObject);\n xobj.scope = formObject.scope;\n var fontKey = formObject.scope.internal.getFont(\n formObject.fontName,\n formObject.fontStyle\n ).id;\n var encodedColor = formObject.scope.__private__.encodeColorString(\n formObject.color\n );\n var stream = [];\n var height = AcroFormAppearance.internal.getHeight(formObject);\n var width = AcroFormAppearance.internal.getWidth(formObject);\n var calcRes = calculateX(formObject, formObject.caption);\n stream.push(\"1 g\");\n stream.push(\"0 0 \" + f2(width) + \" \" + f2(height) + \" re\");\n stream.push(\"f\");\n stream.push(\"q\");\n stream.push(\"0 0 1 rg\");\n stream.push(\"0 0 \" + f2(width - 1) + \" \" + f2(height - 1) + \" re\");\n stream.push(\"W\");\n stream.push(\"n\");\n stream.push(\"0 g\");\n stream.push(\"BT\");\n stream.push(\n \"/\" + fontKey + \" \" + f2(calcRes.fontSize) + \" Tf \" + encodedColor\n );\n stream.push(calcRes.text);\n stream.push(\"ET\");\n stream.push(\"Q\");\n xobj.stream = stream.join(\"\\n\");\n return xobj;\n },\n\n /**\n * Returns the standard Off Appearance for a CheckBox\n *\n * @returns {AcroFormXObject}\n */\n OffPushDown: function(formObject) {\n var xobj = createFormXObject(formObject);\n xobj.scope = formObject.scope;\n var stream = [];\n stream.push(\"0.749023 g\");\n stream.push(\n \"0 0 \" +\n f2(AcroFormAppearance.internal.getWidth(formObject)) +\n \" \" +\n f2(AcroFormAppearance.internal.getHeight(formObject)) +\n \" re\"\n );\n stream.push(\"f\");\n xobj.stream = stream.join(\"\\n\");\n return xobj;\n }\n },\n\n RadioButton: {\n Circle: {\n createAppearanceStream: function(name) {\n var appearanceStreamContent = {\n D: {\n Off: AcroFormAppearance.RadioButton.Circle.OffPushDown\n },\n N: {}\n };\n appearanceStreamContent.N[name] =\n AcroFormAppearance.RadioButton.Circle.YesNormal;\n appearanceStreamContent.D[name] =\n AcroFormAppearance.RadioButton.Circle.YesPushDown;\n return appearanceStreamContent;\n },\n getCA: function() {\n return \"l\";\n },\n\n YesNormal: function(formObject) {\n var xobj = createFormXObject(formObject);\n xobj.scope = formObject.scope;\n var stream = [];\n // Make the Radius of the Circle relative to min(height, width) of formObject\n var DotRadius =\n AcroFormAppearance.internal.getWidth(formObject) <=\n AcroFormAppearance.internal.getHeight(formObject)\n ? AcroFormAppearance.internal.getWidth(formObject) / 4\n : AcroFormAppearance.internal.getHeight(formObject) / 4;\n // The Borderpadding...\n DotRadius = Number((DotRadius * 0.9).toFixed(5));\n var c = AcroFormAppearance.internal.Bezier_C;\n var DotRadiusBezier = Number((DotRadius * c).toFixed(5));\n /*\n * The Following is a Circle created with Bezier-Curves.\n */\n stream.push(\"q\");\n stream.push(\n \"1 0 0 1 \" +\n f5(AcroFormAppearance.internal.getWidth(formObject) / 2) +\n \" \" +\n f5(AcroFormAppearance.internal.getHeight(formObject) / 2) +\n \" cm\"\n );\n stream.push(DotRadius + \" 0 m\");\n stream.push(\n DotRadius +\n \" \" +\n DotRadiusBezier +\n \" \" +\n DotRadiusBezier +\n \" \" +\n DotRadius +\n \" 0 \" +\n DotRadius +\n \" c\"\n );\n stream.push(\n \"-\" +\n DotRadiusBezier +\n \" \" +\n DotRadius +\n \" -\" +\n DotRadius +\n \" \" +\n DotRadiusBezier +\n \" -\" +\n DotRadius +\n \" 0 c\"\n );\n stream.push(\n \"-\" +\n DotRadius +\n \" -\" +\n DotRadiusBezier +\n \" -\" +\n DotRadiusBezier +\n \" -\" +\n DotRadius +\n \" 0 -\" +\n DotRadius +\n \" c\"\n );\n stream.push(\n DotRadiusBezier +\n \" -\" +\n DotRadius +\n \" \" +\n DotRadius +\n \" -\" +\n DotRadiusBezier +\n \" \" +\n DotRadius +\n \" 0 c\"\n );\n stream.push(\"f\");\n stream.push(\"Q\");\n xobj.stream = stream.join(\"\\n\");\n return xobj;\n },\n YesPushDown: function(formObject) {\n var xobj = createFormXObject(formObject);\n xobj.scope = formObject.scope;\n var stream = [];\n var DotRadius =\n AcroFormAppearance.internal.getWidth(formObject) <=\n AcroFormAppearance.internal.getHeight(formObject)\n ? AcroFormAppearance.internal.getWidth(formObject) / 4\n : AcroFormAppearance.internal.getHeight(formObject) / 4;\n // The Borderpadding...\n DotRadius = Number((DotRadius * 0.9).toFixed(5));\n // Save results for later use; no need to waste\n // processor ticks on doing math\n var k = Number((DotRadius * 2).toFixed(5));\n var kc = Number((k * AcroFormAppearance.internal.Bezier_C).toFixed(5));\n var dc = Number(\n (DotRadius * AcroFormAppearance.internal.Bezier_C).toFixed(5)\n );\n\n stream.push(\"0.749023 g\");\n stream.push(\"q\");\n stream.push(\n \"1 0 0 1 \" +\n f5(AcroFormAppearance.internal.getWidth(formObject) / 2) +\n \" \" +\n f5(AcroFormAppearance.internal.getHeight(formObject) / 2) +\n \" cm\"\n );\n stream.push(k + \" 0 m\");\n stream.push(k + \" \" + kc + \" \" + kc + \" \" + k + \" 0 \" + k + \" c\");\n stream.push(\n \"-\" + kc + \" \" + k + \" -\" + k + \" \" + kc + \" -\" + k + \" 0 c\"\n );\n stream.push(\n \"-\" + k + \" -\" + kc + \" -\" + kc + \" -\" + k + \" 0 -\" + k + \" c\"\n );\n stream.push(kc + \" -\" + k + \" \" + k + \" -\" + kc + \" \" + k + \" 0 c\");\n stream.push(\"f\");\n stream.push(\"Q\");\n stream.push(\"0 g\");\n stream.push(\"q\");\n stream.push(\n \"1 0 0 1 \" +\n f5(AcroFormAppearance.internal.getWidth(formObject) / 2) +\n \" \" +\n f5(AcroFormAppearance.internal.getHeight(formObject) / 2) +\n \" cm\"\n );\n stream.push(DotRadius + \" 0 m\");\n stream.push(\n \"\" +\n DotRadius +\n \" \" +\n dc +\n \" \" +\n dc +\n \" \" +\n DotRadius +\n \" 0 \" +\n DotRadius +\n \" c\"\n );\n stream.push(\n \"-\" +\n dc +\n \" \" +\n DotRadius +\n \" -\" +\n DotRadius +\n \" \" +\n dc +\n \" -\" +\n DotRadius +\n \" 0 c\"\n );\n stream.push(\n \"-\" +\n DotRadius +\n \" -\" +\n dc +\n \" -\" +\n dc +\n \" -\" +\n DotRadius +\n \" 0 -\" +\n DotRadius +\n \" c\"\n );\n stream.push(\n dc +\n \" -\" +\n DotRadius +\n \" \" +\n DotRadius +\n \" -\" +\n dc +\n \" \" +\n DotRadius +\n \" 0 c\"\n );\n stream.push(\"f\");\n stream.push(\"Q\");\n xobj.stream = stream.join(\"\\n\");\n return xobj;\n },\n OffPushDown: function(formObject) {\n var xobj = createFormXObject(formObject);\n xobj.scope = formObject.scope;\n var stream = [];\n var DotRadius =\n AcroFormAppearance.internal.getWidth(formObject) <=\n AcroFormAppearance.internal.getHeight(formObject)\n ? AcroFormAppearance.internal.getWidth(formObject) / 4\n : AcroFormAppearance.internal.getHeight(formObject) / 4;\n // The Borderpadding...\n DotRadius = Number((DotRadius * 0.9).toFixed(5));\n // Save results for later use; no need to waste\n // processor ticks on doing math\n var k = Number((DotRadius * 2).toFixed(5));\n var kc = Number((k * AcroFormAppearance.internal.Bezier_C).toFixed(5));\n\n stream.push(\"0.749023 g\");\n stream.push(\"q\");\n stream.push(\n \"1 0 0 1 \" +\n f5(AcroFormAppearance.internal.getWidth(formObject) / 2) +\n \" \" +\n f5(AcroFormAppearance.internal.getHeight(formObject) / 2) +\n \" cm\"\n );\n stream.push(k + \" 0 m\");\n stream.push(k + \" \" + kc + \" \" + kc + \" \" + k + \" 0 \" + k + \" c\");\n stream.push(\n \"-\" + kc + \" \" + k + \" -\" + k + \" \" + kc + \" -\" + k + \" 0 c\"\n );\n stream.push(\n \"-\" + k + \" -\" + kc + \" -\" + kc + \" -\" + k + \" 0 -\" + k + \" c\"\n );\n stream.push(kc + \" -\" + k + \" \" + k + \" -\" + kc + \" \" + k + \" 0 c\");\n stream.push(\"f\");\n stream.push(\"Q\");\n xobj.stream = stream.join(\"\\n\");\n return xobj;\n }\n },\n\n Cross: {\n /**\n * Creates the Actual AppearanceDictionary-References\n *\n * @param {string} name\n * @returns {Object}\n * @ignore\n */\n createAppearanceStream: function(name) {\n var appearanceStreamContent = {\n D: {\n Off: AcroFormAppearance.RadioButton.Cross.OffPushDown\n },\n N: {}\n };\n appearanceStreamContent.N[name] =\n AcroFormAppearance.RadioButton.Cross.YesNormal;\n appearanceStreamContent.D[name] =\n AcroFormAppearance.RadioButton.Cross.YesPushDown;\n return appearanceStreamContent;\n },\n getCA: function() {\n return \"8\";\n },\n\n YesNormal: function(formObject) {\n var xobj = createFormXObject(formObject);\n xobj.scope = formObject.scope;\n var stream = [];\n var cross = AcroFormAppearance.internal.calculateCross(formObject);\n stream.push(\"q\");\n stream.push(\n \"1 1 \" +\n f2(AcroFormAppearance.internal.getWidth(formObject) - 2) +\n \" \" +\n f2(AcroFormAppearance.internal.getHeight(formObject) - 2) +\n \" re\"\n );\n stream.push(\"W\");\n stream.push(\"n\");\n stream.push(f2(cross.x1.x) + \" \" + f2(cross.x1.y) + \" m\");\n stream.push(f2(cross.x2.x) + \" \" + f2(cross.x2.y) + \" l\");\n stream.push(f2(cross.x4.x) + \" \" + f2(cross.x4.y) + \" m\");\n stream.push(f2(cross.x3.x) + \" \" + f2(cross.x3.y) + \" l\");\n stream.push(\"s\");\n stream.push(\"Q\");\n xobj.stream = stream.join(\"\\n\");\n return xobj;\n },\n YesPushDown: function(formObject) {\n var xobj = createFormXObject(formObject);\n xobj.scope = formObject.scope;\n var cross = AcroFormAppearance.internal.calculateCross(formObject);\n var stream = [];\n stream.push(\"0.749023 g\");\n stream.push(\n \"0 0 \" +\n f2(AcroFormAppearance.internal.getWidth(formObject)) +\n \" \" +\n f2(AcroFormAppearance.internal.getHeight(formObject)) +\n \" re\"\n );\n stream.push(\"f\");\n stream.push(\"q\");\n stream.push(\n \"1 1 \" +\n f2(AcroFormAppearance.internal.getWidth(formObject) - 2) +\n \" \" +\n f2(AcroFormAppearance.internal.getHeight(formObject) - 2) +\n \" re\"\n );\n stream.push(\"W\");\n stream.push(\"n\");\n stream.push(f2(cross.x1.x) + \" \" + f2(cross.x1.y) + \" m\");\n stream.push(f2(cross.x2.x) + \" \" + f2(cross.x2.y) + \" l\");\n stream.push(f2(cross.x4.x) + \" \" + f2(cross.x4.y) + \" m\");\n stream.push(f2(cross.x3.x) + \" \" + f2(cross.x3.y) + \" l\");\n stream.push(\"s\");\n stream.push(\"Q\");\n xobj.stream = stream.join(\"\\n\");\n return xobj;\n },\n OffPushDown: function(formObject) {\n var xobj = createFormXObject(formObject);\n xobj.scope = formObject.scope;\n var stream = [];\n stream.push(\"0.749023 g\");\n stream.push(\n \"0 0 \" +\n f2(AcroFormAppearance.internal.getWidth(formObject)) +\n \" \" +\n f2(AcroFormAppearance.internal.getHeight(formObject)) +\n \" re\"\n );\n stream.push(\"f\");\n xobj.stream = stream.join(\"\\n\");\n return xobj;\n }\n }\n },\n\n /**\n * Returns the standard Appearance\n *\n * @returns {AcroFormXObject}\n */\n createDefaultAppearanceStream: function(formObject) {\n // Set Helvetica to Standard Font (size: auto)\n // Color: Black\n var fontKey = formObject.scope.internal.getFont(\n formObject.fontName,\n formObject.fontStyle\n ).id;\n var encodedColor = formObject.scope.__private__.encodeColorString(\n formObject.color\n );\n var fontSize = formObject.fontSize;\n var result = \"/\" + fontKey + \" \" + fontSize + \" Tf \" + encodedColor;\n return result;\n }\n};\n\nAcroFormAppearance.internal = {\n Bezier_C: 0.551915024494,\n\n calculateCross: function(formObject) {\n var width = AcroFormAppearance.internal.getWidth(formObject);\n var height = AcroFormAppearance.internal.getHeight(formObject);\n var a = Math.min(width, height);\n\n var cross = {\n x1: {\n // upperLeft\n x: (width - a) / 2,\n y: (height - a) / 2 + a // height - borderPadding\n },\n x2: {\n // lowerRight\n x: (width - a) / 2 + a,\n y: (height - a) / 2 // borderPadding\n },\n x3: {\n // lowerLeft\n x: (width - a) / 2,\n y: (height - a) / 2 // borderPadding\n },\n x4: {\n // upperRight\n x: (width - a) / 2 + a,\n y: (height - a) / 2 + a // height - borderPadding\n }\n };\n\n return cross;\n }\n};\nAcroFormAppearance.internal.getWidth = function(formObject) {\n var result = 0;\n if (typeof formObject === \"object\") {\n result = scale(formObject.Rect[2]);\n }\n return result;\n};\nAcroFormAppearance.internal.getHeight = function(formObject) {\n var result = 0;\n if (typeof formObject === \"object\") {\n result = scale(formObject.Rect[3]);\n }\n return result;\n};\n\n// Public:\n\n/**\n * Add an AcroForm-Field to the jsPDF-instance\n *\n * @name addField\n * @function\n * @instance\n * @param {Object} fieldObject\n * @returns {jsPDF}\n */\nvar addField = (jsPDFAPI.addField = function(fieldObject) {\n initializeAcroForm(this, fieldObject);\n\n if (fieldObject instanceof AcroFormField) {\n putForm(fieldObject);\n } else {\n throw new Error(\"Invalid argument passed to jsPDF.addField.\");\n }\n fieldObject.page = fieldObject.scope.internal.getCurrentPageInfo().pageNumber;\n return this;\n});\n\njsPDFAPI.AcroFormChoiceField = AcroFormChoiceField;\njsPDFAPI.AcroFormListBox = AcroFormListBox;\njsPDFAPI.AcroFormComboBox = AcroFormComboBox;\njsPDFAPI.AcroFormEditBox = AcroFormEditBox;\njsPDFAPI.AcroFormButton = AcroFormButton;\njsPDFAPI.AcroFormPushButton = AcroFormPushButton;\njsPDFAPI.AcroFormRadioButton = AcroFormRadioButton;\njsPDFAPI.AcroFormCheckBox = AcroFormCheckBox;\njsPDFAPI.AcroFormTextField = AcroFormTextField;\njsPDFAPI.AcroFormPasswordField = AcroFormPasswordField;\njsPDFAPI.AcroFormAppearance = AcroFormAppearance;\n\njsPDFAPI.AcroForm = {\n ChoiceField: AcroFormChoiceField,\n ListBox: AcroFormListBox,\n ComboBox: AcroFormComboBox,\n EditBox: AcroFormEditBox,\n Button: AcroFormButton,\n PushButton: AcroFormPushButton,\n RadioButton: AcroFormRadioButton,\n CheckBox: AcroFormCheckBox,\n TextField: AcroFormTextField,\n PasswordField: AcroFormPasswordField,\n Appearance: AcroFormAppearance\n};\n\njsPDF.AcroForm = {\n ChoiceField: AcroFormChoiceField,\n ListBox: AcroFormListBox,\n ComboBox: AcroFormComboBox,\n EditBox: AcroFormEditBox,\n Button: AcroFormButton,\n PushButton: AcroFormPushButton,\n RadioButton: AcroFormRadioButton,\n CheckBox: AcroFormCheckBox,\n TextField: AcroFormTextField,\n PasswordField: AcroFormPasswordField,\n Appearance: AcroFormAppearance\n};\n\nvar AcroForm = jsPDF.AcroForm;\n\nexport {\n AcroForm,\n AcroFormChoiceField,\n AcroFormListBox,\n AcroFormComboBox,\n AcroFormEditBox,\n AcroFormButton,\n AcroFormPushButton,\n AcroFormRadioButton,\n AcroFormCheckBox,\n AcroFormTextField,\n AcroFormPasswordField,\n AcroFormAppearance\n};\n","function toLookup(arr) {\n return arr.reduce(function(lookup, name, index) {\n lookup[name] = index;\n\n return lookup;\n }, {});\n}\n\nvar fontStyleOrder = {\n italic: [\"italic\", \"oblique\", \"normal\"],\n oblique: [\"oblique\", \"italic\", \"normal\"],\n normal: [\"normal\", \"oblique\", \"italic\"]\n};\n\nvar fontStretchOrder = [\n \"ultra-condensed\",\n \"extra-condensed\",\n \"condensed\",\n \"semi-condensed\",\n \"normal\",\n \"semi-expanded\",\n \"expanded\",\n \"extra-expanded\",\n \"ultra-expanded\"\n];\n\n// For a given font-stretch value, we need to know where to start our search\n// from in the fontStretchOrder list.\nvar fontStretchLookup = toLookup(fontStretchOrder);\n\nvar fontWeights = [100, 200, 300, 400, 500, 600, 700, 800, 900];\nvar fontWeightsLookup = toLookup(fontWeights);\n\nfunction normalizeFontStretch(stretch) {\n stretch = stretch || \"normal\";\n\n return typeof fontStretchLookup[stretch] === \"number\" ? stretch : \"normal\";\n}\n\nfunction normalizeFontStyle(style) {\n style = style || \"normal\";\n\n return fontStyleOrder[style] ? style : \"normal\";\n}\n\nfunction normalizeFontWeight(weight) {\n if (!weight) {\n return 400;\n }\n\n if (typeof weight === \"number\") {\n // Ignore values which aren't valid font-weights.\n return weight >= 100 && weight <= 900 && weight % 100 === 0 ? weight : 400;\n }\n\n if (/^\\d00$/.test(weight)) {\n return parseInt(weight);\n }\n\n switch (weight) {\n case \"bold\":\n return 700;\n\n case \"normal\":\n default:\n return 400;\n }\n}\n\nexport function normalizeFontFace(fontFace) {\n var family = fontFace.family.replace(/\"|'/g, \"\").toLowerCase();\n\n var style = normalizeFontStyle(fontFace.style);\n var weight = normalizeFontWeight(fontFace.weight);\n var stretch = normalizeFontStretch(fontFace.stretch);\n\n return {\n family: family,\n style: style,\n weight: weight,\n stretch: stretch,\n src: fontFace.src || [],\n\n // The ref property maps this font-face to the font\n // added by the .addFont() method.\n ref: fontFace.ref || {\n name: family,\n style: [stretch, style, weight].join(\" \")\n }\n };\n}\n\n/**\n * Turns a list of font-faces into a map, for easier lookup when resolving\n * fonts.\n * @private\n */\nexport function buildFontFaceMap(fontFaces) {\n var map = {};\n\n for (var i = 0; i < fontFaces.length; ++i) {\n var normalized = normalizeFontFace(fontFaces[i]);\n\n var name = normalized.family;\n var stretch = normalized.stretch;\n var style = normalized.style;\n var weight = normalized.weight;\n\n map[name] = map[name] || {};\n\n map[name][stretch] = map[name][stretch] || {};\n map[name][stretch][style] = map[name][stretch][style] || {};\n map[name][stretch][style][weight] = normalized;\n }\n\n return map;\n}\n\n/**\n * Searches a map of stretches, weights, etc. in the given direction and\n * then, if no match has been found, in the opposite directions.\n *\n * @param {Object.} matchingSet A map of the various font variations.\n * @param {any[]} order The order of the different variations\n * @param {number} pivot The starting point of the search in the order list.\n * @param {number} dir The initial direction of the search (desc = -1, asc = 1)\n * @private\n */\n\nfunction searchFromPivot(matchingSet, order, pivot, dir) {\n var i;\n\n for (i = pivot; i >= 0 && i < order.length; i += dir) {\n if (matchingSet[order[i]]) {\n return matchingSet[order[i]];\n }\n }\n\n for (i = pivot; i >= 0 && i < order.length; i -= dir) {\n if (matchingSet[order[i]]) {\n return matchingSet[order[i]];\n }\n }\n}\n\nfunction resolveFontStretch(stretch, matchingSet) {\n if (matchingSet[stretch]) {\n return matchingSet[stretch];\n }\n\n var pivot = fontStretchLookup[stretch];\n\n // If the font-stretch value is normal or more condensed, we want to\n // start with a descending search, otherwise we should do ascending.\n var dir = pivot <= fontStretchLookup[\"normal\"] ? -1 : 1;\n var match = searchFromPivot(matchingSet, fontStretchOrder, pivot, dir);\n\n if (!match) {\n // Since a font-family cannot exist without having at least one stretch value\n // we should never reach this point.\n throw new Error(\n \"Could not find a matching font-stretch value for \" + stretch\n );\n }\n\n return match;\n}\n\nfunction resolveFontStyle(fontStyle, matchingSet) {\n if (matchingSet[fontStyle]) {\n return matchingSet[fontStyle];\n }\n\n var ordering = fontStyleOrder[fontStyle];\n\n for (var i = 0; i < ordering.length; ++i) {\n if (matchingSet[ordering[i]]) {\n return matchingSet[ordering[i]];\n }\n }\n\n // Since a font-family cannot exist without having at least one style value\n // we should never reach this point.\n throw new Error(\"Could not find a matching font-style for \" + fontStyle);\n}\n\nfunction resolveFontWeight(weight, matchingSet) {\n if (matchingSet[weight]) {\n return matchingSet[weight];\n }\n\n if (weight === 400 && matchingSet[500]) {\n return matchingSet[500];\n }\n\n if (weight === 500 && matchingSet[400]) {\n return matchingSet[400];\n }\n\n var pivot = fontWeightsLookup[weight];\n\n // If the font-stretch value is normal or more condensed, we want to\n // start with a descending search, otherwise we should do ascending.\n var dir = weight < 400 ? -1 : 1;\n var match = searchFromPivot(matchingSet, fontWeights, pivot, dir);\n\n if (!match) {\n // Since a font-family cannot exist without having at least one stretch value\n // we should never reach this point.\n throw new Error(\n \"Could not find a matching font-weight for value \" + weight\n );\n }\n\n return match;\n}\n\nvar defaultGenericFontFamilies = {\n \"sans-serif\": \"helvetica\",\n fixed: \"courier\",\n monospace: \"courier\",\n terminal: \"courier\",\n cursive: \"times\",\n fantasy: \"times\",\n serif: \"times\"\n};\n\nvar systemFonts = {\n caption: \"times\",\n icon: \"times\",\n menu: \"times\",\n \"message-box\": \"times\",\n \"small-caption\": \"times\",\n \"status-bar\": \"times\"\n};\n\nfunction ruleToString(rule) {\n return [rule.stretch, rule.style, rule.weight, rule.family].join(\" \");\n}\n\nexport function resolveFontFace(fontFaceMap, rules, opts) {\n opts = opts || {};\n\n var defaultFontFamily = opts.defaultFontFamily || \"times\";\n var genericFontFamilies = Object.assign(\n {},\n defaultGenericFontFamilies,\n opts.genericFontFamilies || {}\n );\n\n var rule = null;\n var matches = null;\n\n for (var i = 0; i < rules.length; ++i) {\n rule = normalizeFontFace(rules[i]);\n\n if (genericFontFamilies[rule.family]) {\n rule.family = genericFontFamilies[rule.family];\n }\n\n if (fontFaceMap.hasOwnProperty(rule.family)) {\n matches = fontFaceMap[rule.family];\n\n break;\n }\n }\n\n // Always fallback to a known font family.\n matches = matches || fontFaceMap[defaultFontFamily];\n\n if (!matches) {\n // At this point we should definitiely have a font family, but if we\n // don't there is something wrong with our configuration\n throw new Error(\n \"Could not find a font-family for the rule '\" +\n ruleToString(rule) +\n \"' and default family '\" +\n defaultFontFamily +\n \"'.\"\n );\n }\n\n matches = resolveFontStretch(rule.stretch, matches);\n matches = resolveFontStyle(rule.style, matches);\n matches = resolveFontWeight(rule.weight, matches);\n\n if (!matches) {\n // We should've fount\n throw new Error(\n \"Failed to resolve a font for the rule '\" + ruleToString(rule) + \"'.\"\n );\n }\n\n return matches;\n}\n\n/**\n * Builds a style id for use with the addFont() method.\n * @param {FontFace} font\n * @private\n */\nexport function toStyleName(font) {\n return [font.weight, font.style, font.stretch].join(\" \");\n}\n\nfunction eatWhiteSpace(input) {\n return input.trimLeft();\n}\n\nfunction parseQuotedFontFamily(input, quote) {\n var index = 0;\n\n while (index < input.length) {\n var current = input.charAt(index);\n\n if (current === quote) {\n return [input.substring(0, index), input.substring(index + 1)];\n }\n\n index += 1;\n }\n\n // Unexpected end of input\n return null;\n}\n\nfunction parseNonQuotedFontFamily(input) {\n // It implements part of the identifier parser here: https://www.w3.org/TR/CSS21/syndata.html#value-def-identifier\n //\n // NOTE: This parser pretty much ignores escaped identifiers and that there is a thing called unicode.\n //\n // Breakdown of regexp:\n // -[a-z_] - when identifier starts with a hyphen, you're not allowed to have another hyphen or a digit\n // [a-z_] - allow a-z and underscore at beginning of input\n // [a-z0-9_-]* - after that, anything goes\n var match = input.match(/^(-[a-z_]|[a-z_])[a-z0-9_-]*/i);\n\n // non quoted value contains illegal characters\n if (match === null) {\n return null;\n }\n\n return [match[0], input.substring(match[0].length)];\n}\n\nvar defaultFont = [\"times\"];\n\nexport function parseFontFamily(input) {\n var result = [];\n var ch, parsed;\n var remaining = input.trim();\n\n if (remaining === \"\") {\n return defaultFont;\n }\n\n if (remaining in systemFonts) {\n return [systemFonts[remaining]];\n }\n\n while (remaining !== \"\") {\n parsed = null;\n remaining = eatWhiteSpace(remaining);\n ch = remaining.charAt(0);\n\n switch (ch) {\n case '\"':\n case \"'\":\n parsed = parseQuotedFontFamily(remaining.substring(1), ch);\n break;\n\n default:\n parsed = parseNonQuotedFontFamily(remaining);\n break;\n }\n\n if (parsed === null) {\n return defaultFont;\n }\n\n result.push(parsed[0]);\n\n remaining = eatWhiteSpace(parsed[1]);\n\n // We expect end of input or a comma separator here\n if (remaining !== \"\" && remaining.charAt(0) !== \",\") {\n return defaultFont;\n }\n\n remaining = remaining.replace(/^,/, \"\");\n }\n\n return result;\n}\n","/** @license\n * jsPDF addImage plugin\n * Copyright (c) 2012 Jason Siefken, https://github.com/siefkenj/\n * 2013 Chris Dowling, https://github.com/gingerchris\n * 2013 Trinh Ho, https://github.com/ineedfat\n * 2013 Edwin Alejandro Perez, https://github.com/eaparango\n * 2013 Norah Smith, https://github.com/burnburnrocket\n * 2014 Diego Casorran, https://github.com/diegocr\n * 2014 James Robb, https://github.com/jamesbrobb\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n/**\n * @name addImage\n * @module\n */\n\nimport { jsPDF } from \"../jspdf.js\";\nimport { atob, btoa } from \"../libs/AtobBtoa.js\";\n\n(function(jsPDFAPI) {\n \"use strict\";\n\n var namespace = \"addImage_\";\n jsPDFAPI.__addimage__ = {};\n\n var UNKNOWN = \"UNKNOWN\";\n\n // Heuristic selection of a good batch for large array .apply. Not limiting make the call overflow.\n // With too small batch iteration will be slow as more calls are made,\n // higher values cause larger and slower garbage collection.\n var ARRAY_APPLY_BATCH = 8192;\n\n var imageFileTypeHeaders = {\n PNG: [[0x89, 0x50, 0x4e, 0x47]],\n TIFF: [\n [0x4d, 0x4d, 0x00, 0x2a], //Motorola\n [0x49, 0x49, 0x2a, 0x00] //Intel\n ],\n JPEG: [\n [\n 0xff,\n 0xd8,\n 0xff,\n 0xe0,\n undefined,\n undefined,\n 0x4a,\n 0x46,\n 0x49,\n 0x46,\n 0x00\n ], //JFIF\n [\n 0xff,\n 0xd8,\n 0xff,\n 0xe1,\n undefined,\n undefined,\n 0x45,\n 0x78,\n 0x69,\n 0x66,\n 0x00,\n 0x00\n ], //Exif\n [0xff, 0xd8, 0xff, 0xdb], //JPEG RAW\n [0xff, 0xd8, 0xff, 0xee] //EXIF RAW\n ],\n JPEG2000: [[0x00, 0x00, 0x00, 0x0c, 0x6a, 0x50, 0x20, 0x20]],\n GIF87a: [[0x47, 0x49, 0x46, 0x38, 0x37, 0x61]],\n GIF89a: [[0x47, 0x49, 0x46, 0x38, 0x39, 0x61]],\n WEBP: [\n [\n 0x52,\n 0x49,\n 0x46,\n 0x46,\n undefined,\n undefined,\n undefined,\n undefined,\n 0x57,\n 0x45,\n 0x42,\n 0x50\n ]\n ],\n BMP: [\n [0x42, 0x4d], //BM - Windows 3.1x, 95, NT, ... etc.\n [0x42, 0x41], //BA - OS/2 struct bitmap array\n [0x43, 0x49], //CI - OS/2 struct color icon\n [0x43, 0x50], //CP - OS/2 const color pointer\n [0x49, 0x43], //IC - OS/2 struct icon\n [0x50, 0x54] //PT - OS/2 pointer\n ]\n };\n\n /**\n * Recognize filetype of Image by magic-bytes\n *\n * https://en.wikipedia.org/wiki/List_of_file_signatures\n *\n * @name getImageFileTypeByImageData\n * @public\n * @function\n * @param {string|arraybuffer} imageData imageData as binary String or arraybuffer\n * @param {string} format format of file if filetype-recognition fails, e.g. 'JPEG'\n *\n * @returns {string} filetype of Image\n */\n var getImageFileTypeByImageData = (jsPDFAPI.__addimage__.getImageFileTypeByImageData = function(\n imageData,\n fallbackFormat\n ) {\n fallbackFormat = fallbackFormat || UNKNOWN;\n var i;\n var j;\n var result = UNKNOWN;\n var headerSchemata;\n var compareResult;\n var fileType;\n\n if (\n fallbackFormat === \"RGBA\" ||\n (imageData.data !== undefined &&\n imageData.data instanceof Uint8ClampedArray &&\n \"height\" in imageData &&\n \"width\" in imageData)\n ) {\n return \"RGBA\";\n }\n\n if (isArrayBufferView(imageData)) {\n for (fileType in imageFileTypeHeaders) {\n headerSchemata = imageFileTypeHeaders[fileType];\n for (i = 0; i < headerSchemata.length; i += 1) {\n compareResult = true;\n for (j = 0; j < headerSchemata[i].length; j += 1) {\n if (headerSchemata[i][j] === undefined) {\n continue;\n }\n if (headerSchemata[i][j] !== imageData[j]) {\n compareResult = false;\n break;\n }\n }\n if (compareResult === true) {\n result = fileType;\n break;\n }\n }\n }\n } else {\n for (fileType in imageFileTypeHeaders) {\n headerSchemata = imageFileTypeHeaders[fileType];\n for (i = 0; i < headerSchemata.length; i += 1) {\n compareResult = true;\n for (j = 0; j < headerSchemata[i].length; j += 1) {\n if (headerSchemata[i][j] === undefined) {\n continue;\n }\n if (headerSchemata[i][j] !== imageData.charCodeAt(j)) {\n compareResult = false;\n break;\n }\n }\n if (compareResult === true) {\n result = fileType;\n break;\n }\n }\n }\n }\n\n if (result === UNKNOWN && fallbackFormat !== UNKNOWN) {\n result = fallbackFormat;\n }\n return result;\n });\n\n // Image functionality ported from pdf.js\n var putImage = function(image) {\n var out = this.internal.write;\n var putStream = this.internal.putStream;\n var getFilters = this.internal.getFilters;\n\n var filter = getFilters();\n while (filter.indexOf(\"FlateEncode\") !== -1) {\n filter.splice(filter.indexOf(\"FlateEncode\"), 1);\n }\n\n image.objectId = this.internal.newObject();\n\n var additionalKeyValues = [];\n additionalKeyValues.push({ key: \"Type\", value: \"/XObject\" });\n additionalKeyValues.push({ key: \"Subtype\", value: \"/Image\" });\n additionalKeyValues.push({ key: \"Width\", value: image.width });\n additionalKeyValues.push({ key: \"Height\", value: image.height });\n\n if (image.colorSpace === color_spaces.INDEXED) {\n additionalKeyValues.push({\n key: \"ColorSpace\",\n value:\n \"[/Indexed /DeviceRGB \" +\n // if an indexed png defines more than one colour with transparency, we've created a sMask\n (image.palette.length / 3 - 1) +\n \" \" +\n (\"sMask\" in image && typeof image.sMask !== \"undefined\"\n ? image.objectId + 2\n : image.objectId + 1) +\n \" 0 R]\"\n });\n } else {\n additionalKeyValues.push({\n key: \"ColorSpace\",\n value: \"/\" + image.colorSpace\n });\n if (image.colorSpace === color_spaces.DEVICE_CMYK) {\n additionalKeyValues.push({ key: \"Decode\", value: \"[1 0 1 0 1 0 1 0]\" });\n }\n }\n additionalKeyValues.push({\n key: \"BitsPerComponent\",\n value: image.bitsPerComponent\n });\n if (\n \"decodeParameters\" in image &&\n typeof image.decodeParameters !== \"undefined\"\n ) {\n additionalKeyValues.push({\n key: \"DecodeParms\",\n value: \"<<\" + image.decodeParameters + \">>\"\n });\n }\n if (\"transparency\" in image && Array.isArray(image.transparency)) {\n var transparency = \"\",\n i = 0,\n len = image.transparency.length;\n for (; i < len; i++)\n transparency +=\n image.transparency[i] + \" \" + image.transparency[i] + \" \";\n\n additionalKeyValues.push({\n key: \"Mask\",\n value: \"[\" + transparency + \"]\"\n });\n }\n if (typeof image.sMask !== \"undefined\") {\n additionalKeyValues.push({\n key: \"SMask\",\n value: image.objectId + 1 + \" 0 R\"\n });\n }\n\n var alreadyAppliedFilters =\n typeof image.filter !== \"undefined\" ? [\"/\" + image.filter] : undefined;\n\n putStream({\n data: image.data,\n additionalKeyValues: additionalKeyValues,\n alreadyAppliedFilters: alreadyAppliedFilters,\n objectId: image.objectId\n });\n\n out(\"endobj\");\n\n // Soft mask\n if (\"sMask\" in image && typeof image.sMask !== \"undefined\") {\n var decodeParameters =\n \"/Predictor \" +\n image.predictor +\n \" /Colors 1 /BitsPerComponent \" +\n image.bitsPerComponent +\n \" /Columns \" +\n image.width;\n var sMask = {\n width: image.width,\n height: image.height,\n colorSpace: \"DeviceGray\",\n bitsPerComponent: image.bitsPerComponent,\n decodeParameters: decodeParameters,\n data: image.sMask\n };\n if (\"filter\" in image) {\n sMask.filter = image.filter;\n }\n putImage.call(this, sMask);\n }\n\n //Palette\n if (image.colorSpace === color_spaces.INDEXED) {\n var objId = this.internal.newObject();\n //out('<< /Filter / ' + img['f'] +' /Length ' + img['pal'].length + '>>');\n //putStream(zlib.compress(img['pal']));\n putStream({\n data: arrayBufferToBinaryString(new Uint8Array(image.palette)),\n objectId: objId\n });\n out(\"endobj\");\n }\n };\n var putResourcesCallback = function() {\n var images = this.internal.collections[namespace + \"images\"];\n for (var i in images) {\n putImage.call(this, images[i]);\n }\n };\n var putXObjectsDictCallback = function() {\n var images = this.internal.collections[namespace + \"images\"],\n out = this.internal.write,\n image;\n for (var i in images) {\n image = images[i];\n out(\"/I\" + image.index, image.objectId, \"0\", \"R\");\n }\n };\n\n var checkCompressValue = function(value) {\n if (value && typeof value === \"string\") value = value.toUpperCase();\n return value in jsPDFAPI.image_compression ? value : image_compression.NONE;\n };\n\n var initialize = function() {\n if (!this.internal.collections[namespace + \"images\"]) {\n this.internal.collections[namespace + \"images\"] = {};\n this.internal.events.subscribe(\"putResources\", putResourcesCallback);\n this.internal.events.subscribe(\"putXobjectDict\", putXObjectsDictCallback);\n }\n };\n\n var getImages = function() {\n var images = this.internal.collections[namespace + \"images\"];\n initialize.call(this);\n return images;\n };\n var getImageIndex = function() {\n return Object.keys(this.internal.collections[namespace + \"images\"]).length;\n };\n var notDefined = function(value) {\n return typeof value === \"undefined\" || value === null || value.length === 0;\n };\n var generateAliasFromImageData = function(imageData) {\n if (typeof imageData === \"string\" || isArrayBufferView(imageData)) {\n return sHashCode(imageData);\n } else if (isArrayBufferView(imageData.data)) {\n return sHashCode(imageData.data);\n }\n\n return null;\n };\n\n var isImageTypeSupported = function(type) {\n return typeof jsPDFAPI[\"process\" + type.toUpperCase()] === \"function\";\n };\n\n var isDOMElement = function(object) {\n return typeof object === \"object\" && object.nodeType === 1;\n };\n\n var getImageDataFromElement = function(element, format) {\n //if element is an image which uses data url definition, just return the dataurl\n if (element.nodeName === \"IMG\" && element.hasAttribute(\"src\")) {\n var src = \"\" + element.getAttribute(\"src\");\n\n //is base64 encoded dataUrl, directly process it\n if (src.indexOf(\"data:image/\") === 0) {\n return atob(\n unescape(src)\n .split(\"base64,\")\n .pop()\n );\n }\n\n //it is probably an url, try to load it\n var tmpImageData = jsPDFAPI.loadFile(src, true);\n if (tmpImageData !== undefined) {\n return tmpImageData;\n }\n }\n\n if (element.nodeName === \"CANVAS\") {\n if (element.width === 0 || element.height === 0) {\n throw new Error(\n \"Given canvas must have data. Canvas width: \" +\n element.width +\n \", height: \" +\n element.height\n );\n }\n var mimeType;\n switch (format) {\n case \"PNG\":\n mimeType = \"image/png\";\n break;\n case \"WEBP\":\n mimeType = \"image/webp\";\n break;\n case \"JPEG\":\n case \"JPG\":\n default:\n mimeType = \"image/jpeg\";\n break;\n }\n return atob(\n element\n .toDataURL(mimeType, 1.0)\n .split(\"base64,\")\n .pop()\n );\n }\n };\n\n var checkImagesForAlias = function(alias) {\n var images = this.internal.collections[namespace + \"images\"];\n if (images) {\n for (var e in images) {\n if (alias === images[e].alias) {\n return images[e];\n }\n }\n }\n };\n\n var determineWidthAndHeight = function(width, height, image) {\n if (!width && !height) {\n width = -96;\n height = -96;\n }\n if (width < 0) {\n width = (-1 * image.width * 72) / width / this.internal.scaleFactor;\n }\n if (height < 0) {\n height = (-1 * image.height * 72) / height / this.internal.scaleFactor;\n }\n if (width === 0) {\n width = (height * image.width) / image.height;\n }\n if (height === 0) {\n height = (width * image.height) / image.width;\n }\n\n return [width, height];\n };\n\n var writeImageToPDF = function(x, y, width, height, image, rotation) {\n var dims = determineWidthAndHeight.call(this, width, height, image),\n coord = this.internal.getCoordinateString,\n vcoord = this.internal.getVerticalCoordinateString;\n\n var images = getImages.call(this);\n\n width = dims[0];\n height = dims[1];\n images[image.index] = image;\n\n if (rotation) {\n rotation *= Math.PI / 180;\n var c = Math.cos(rotation);\n var s = Math.sin(rotation);\n //like in pdf Reference do it 4 digits instead of 2\n var f4 = function(number) {\n return number.toFixed(4);\n };\n var rotationTransformationMatrix = [\n f4(c),\n f4(s),\n f4(s * -1),\n f4(c),\n 0,\n 0,\n \"cm\"\n ];\n }\n this.internal.write(\"q\"); //Save graphics state\n if (rotation) {\n this.internal.write(\n [1, \"0\", \"0\", 1, coord(x), vcoord(y + height), \"cm\"].join(\" \")\n ); //Translate\n this.internal.write(rotationTransformationMatrix.join(\" \")); //Rotate\n this.internal.write(\n [coord(width), \"0\", \"0\", coord(height), \"0\", \"0\", \"cm\"].join(\" \")\n ); //Scale\n } else {\n this.internal.write(\n [\n coord(width),\n \"0\",\n \"0\",\n coord(height),\n coord(x),\n vcoord(y + height),\n \"cm\"\n ].join(\" \")\n ); //Translate and Scale\n }\n\n if (this.isAdvancedAPI()) {\n // draw image bottom up when in \"advanced\" API mode\n this.internal.write([1, 0, 0, -1, 0, 0, \"cm\"].join(\" \"));\n }\n\n this.internal.write(\"/I\" + image.index + \" Do\"); //Paint Image\n this.internal.write(\"Q\"); //Restore graphics state\n };\n\n /**\n * COLOR SPACES\n */\n var color_spaces = (jsPDFAPI.color_spaces = {\n DEVICE_RGB: \"DeviceRGB\",\n DEVICE_GRAY: \"DeviceGray\",\n DEVICE_CMYK: \"DeviceCMYK\",\n CAL_GREY: \"CalGray\",\n CAL_RGB: \"CalRGB\",\n LAB: \"Lab\",\n ICC_BASED: \"ICCBased\",\n INDEXED: \"Indexed\",\n PATTERN: \"Pattern\",\n SEPARATION: \"Separation\",\n DEVICE_N: \"DeviceN\"\n });\n\n /**\n * DECODE METHODS\n */\n jsPDFAPI.decode = {\n DCT_DECODE: \"DCTDecode\",\n FLATE_DECODE: \"FlateDecode\",\n LZW_DECODE: \"LZWDecode\",\n JPX_DECODE: \"JPXDecode\",\n JBIG2_DECODE: \"JBIG2Decode\",\n ASCII85_DECODE: \"ASCII85Decode\",\n ASCII_HEX_DECODE: \"ASCIIHexDecode\",\n RUN_LENGTH_DECODE: \"RunLengthDecode\",\n CCITT_FAX_DECODE: \"CCITTFaxDecode\"\n };\n\n /**\n * IMAGE COMPRESSION TYPES\n */\n var image_compression = (jsPDFAPI.image_compression = {\n NONE: \"NONE\",\n FAST: \"FAST\",\n MEDIUM: \"MEDIUM\",\n SLOW: \"SLOW\"\n });\n\n /**\n * @name sHashCode\n * @function\n * @param {string} data\n * @returns {string}\n */\n var sHashCode = (jsPDFAPI.__addimage__.sHashCode = function(data) {\n var hash = 0,\n i,\n len;\n\n if (typeof data === \"string\") {\n len = data.length;\n for (i = 0; i < len; i++) {\n hash = (hash << 5) - hash + data.charCodeAt(i);\n hash |= 0; // Convert to 32bit integer\n }\n } else if (isArrayBufferView(data)) {\n len = data.byteLength / 2;\n for (i = 0; i < len; i++) {\n hash = (hash << 5) - hash + data[i];\n hash |= 0; // Convert to 32bit integer\n }\n }\n return hash;\n });\n\n /**\n * Validates if given String is a valid Base64-String\n *\n * @name validateStringAsBase64\n * @public\n * @function\n * @param {String} possible Base64-String\n *\n * @returns {boolean}\n */\n var validateStringAsBase64 = (jsPDFAPI.__addimage__.validateStringAsBase64 = function(\n possibleBase64String\n ) {\n possibleBase64String = possibleBase64String || \"\";\n possibleBase64String.toString().trim();\n\n var result = true;\n\n if (possibleBase64String.length === 0) {\n result = false;\n }\n\n if (possibleBase64String.length % 4 !== 0) {\n result = false;\n }\n\n if (\n /^[A-Za-z0-9+/]+$/.test(\n possibleBase64String.substr(0, possibleBase64String.length - 2)\n ) === false\n ) {\n result = false;\n }\n\n if (\n /^[A-Za-z0-9/][A-Za-z0-9+/]|[A-Za-z0-9+/]=|==$/.test(\n possibleBase64String.substr(-2)\n ) === false\n ) {\n result = false;\n }\n return result;\n });\n\n /**\n * Strips out and returns info from a valid base64 data URI\n *\n * @name extractImageFromDataUrl\n * @function\n * @param {string} dataUrl a valid data URI of format 'data:[][;base64],'\n * @returns {Array}an Array containing the following\n * [0] the complete data URI\n * [1] \n * [2] format - the second part of the mime-type i.e 'png' in 'image/png'\n * [4] \n */\n var extractImageFromDataUrl = (jsPDFAPI.__addimage__.extractImageFromDataUrl = function(\n dataUrl\n ) {\n dataUrl = dataUrl || \"\";\n var dataUrlParts = dataUrl.split(\"base64,\");\n var result = null;\n\n if (dataUrlParts.length === 2) {\n var extractedInfo = /^data:(\\w*\\/\\w*);*(charset=(?!charset=)[\\w=-]*)*;*$/.exec(\n dataUrlParts[0]\n );\n if (Array.isArray(extractedInfo)) {\n result = {\n mimeType: extractedInfo[1],\n charset: extractedInfo[2],\n data: dataUrlParts[1]\n };\n }\n }\n return result;\n });\n\n /**\n * Check to see if ArrayBuffer is supported\n *\n * @name supportsArrayBuffer\n * @function\n * @returns {boolean}\n */\n var supportsArrayBuffer = (jsPDFAPI.__addimage__.supportsArrayBuffer = function() {\n return (\n typeof ArrayBuffer !== \"undefined\" && typeof Uint8Array !== \"undefined\"\n );\n });\n\n /**\n * Tests supplied object to determine if ArrayBuffer\n *\n * @name isArrayBuffer\n * @function\n * @param {Object} object an Object\n *\n * @returns {boolean}\n */\n jsPDFAPI.__addimage__.isArrayBuffer = function(object) {\n return supportsArrayBuffer() && object instanceof ArrayBuffer;\n };\n\n /**\n * Tests supplied object to determine if it implements the ArrayBufferView (TypedArray) interface\n *\n * @name isArrayBufferView\n * @function\n * @param {Object} object an Object\n * @returns {boolean}\n */\n var isArrayBufferView = (jsPDFAPI.__addimage__.isArrayBufferView = function(\n object\n ) {\n return (\n supportsArrayBuffer() &&\n typeof Uint32Array !== \"undefined\" &&\n (object instanceof Int8Array ||\n object instanceof Uint8Array ||\n (typeof Uint8ClampedArray !== \"undefined\" &&\n object instanceof Uint8ClampedArray) ||\n object instanceof Int16Array ||\n object instanceof Uint16Array ||\n object instanceof Int32Array ||\n object instanceof Uint32Array ||\n object instanceof Float32Array ||\n object instanceof Float64Array)\n );\n });\n\n /**\n * Convert Binary String to ArrayBuffer\n *\n * @name binaryStringToUint8Array\n * @public\n * @function\n * @param {string} BinaryString with ImageData\n * @returns {Uint8Array}\n */\n var binaryStringToUint8Array = (jsPDFAPI.__addimage__.binaryStringToUint8Array = function(\n binary_string\n ) {\n var len = binary_string.length;\n var bytes = new Uint8Array(len);\n for (var i = 0; i < len; i++) {\n bytes[i] = binary_string.charCodeAt(i);\n }\n return bytes;\n });\n\n /**\n * Convert the Buffer to a Binary String\n *\n * @name arrayBufferToBinaryString\n * @public\n * @function\n * @param {ArrayBuffer|ArrayBufferView} ArrayBuffer buffer or bufferView with ImageData\n *\n * @returns {String}\n */\n var arrayBufferToBinaryString = (jsPDFAPI.__addimage__.arrayBufferToBinaryString = function(\n buffer\n ) {\n var out = \"\";\n // There are calls with both ArrayBuffer and already converted Uint8Array or other BufferView.\n // Do not copy the array if input is already an array.\n var buf = isArrayBufferView(buffer) ? buffer : new Uint8Array(buffer);\n for (var i = 0; i < buf.length; i += ARRAY_APPLY_BATCH) {\n // Limit the amount of characters being parsed to prevent overflow.\n // Note that while TextDecoder would be faster, it does not have the same\n // functionality as fromCharCode with any provided encodings as of 3/2021.\n out += String.fromCharCode.apply(\n null,\n buf.subarray(i, i + ARRAY_APPLY_BATCH)\n );\n }\n return out;\n });\n\n /**\n * Possible parameter for addImage, an RGBA buffer with size.\n *\n * @typedef {Object} RGBAData\n * @property {Uint8ClampedArray} data - Single dimensional array of RGBA values. For example from canvas getImageData.\n * @property {number} width - Image width as the data does not carry this information in itself.\n * @property {number} height - Image height as the data does not carry this information in itself.\n */\n\n /**\n * Adds an Image to the PDF.\n *\n * @name addImage\n * @public\n * @function\n * @param {string|HTMLImageElement|HTMLCanvasElement|Uint8Array|RGBAData} imageData imageData as base64 encoded DataUrl or Image-HTMLElement or Canvas-HTMLElement or object containing RGBA array (like output from canvas.getImageData).\n * @param {string} format format of file if filetype-recognition fails or in case of a Canvas-Element needs to be specified (default for Canvas is JPEG), e.g. 'JPEG', 'PNG', 'WEBP'\n * @param {number} x x Coordinate (in units declared at inception of PDF document) against left edge of the page\n * @param {number} y y Coordinate (in units declared at inception of PDF document) against upper edge of the page\n * @param {number} width width of the image (in units declared at inception of PDF document)\n * @param {number} height height of the Image (in units declared at inception of PDF document)\n * @param {string} alias alias of the image (if used multiple times)\n * @param {string} compression compression of the generated JPEG, can have the values 'NONE', 'FAST', 'MEDIUM' and 'SLOW'\n * @param {number} rotation rotation of the image in degrees (0-359)\n *\n * @returns jsPDF\n */\n jsPDFAPI.addImage = function() {\n var imageData, format, x, y, w, h, alias, compression, rotation;\n\n imageData = arguments[0];\n if (typeof arguments[1] === \"number\") {\n format = UNKNOWN;\n x = arguments[1];\n y = arguments[2];\n w = arguments[3];\n h = arguments[4];\n alias = arguments[5];\n compression = arguments[6];\n rotation = arguments[7];\n } else {\n format = arguments[1];\n x = arguments[2];\n y = arguments[3];\n w = arguments[4];\n h = arguments[5];\n alias = arguments[6];\n compression = arguments[7];\n rotation = arguments[8];\n }\n\n if (\n typeof imageData === \"object\" &&\n !isDOMElement(imageData) &&\n \"imageData\" in imageData\n ) {\n var options = imageData;\n\n imageData = options.imageData;\n format = options.format || format || UNKNOWN;\n x = options.x || x || 0;\n y = options.y || y || 0;\n w = options.w || options.width || w;\n h = options.h || options.height || h;\n alias = options.alias || alias;\n compression = options.compression || compression;\n rotation = options.rotation || options.angle || rotation;\n }\n\n //If compression is not explicitly set, determine if we should use compression\n var filter = this.internal.getFilters();\n if (compression === undefined && filter.indexOf(\"FlateEncode\") !== -1) {\n compression = \"SLOW\";\n }\n\n if (isNaN(x) || isNaN(y)) {\n throw new Error(\"Invalid coordinates passed to jsPDF.addImage\");\n }\n\n initialize.call(this);\n\n var image = processImageData.call(\n this,\n imageData,\n format,\n alias,\n compression\n );\n\n writeImageToPDF.call(this, x, y, w, h, image, rotation);\n\n return this;\n };\n\n var processImageData = function(imageData, format, alias, compression) {\n var result, dataAsBinaryString;\n\n if (\n typeof imageData === \"string\" &&\n getImageFileTypeByImageData(imageData) === UNKNOWN\n ) {\n imageData = unescape(imageData);\n var tmpImageData = convertBase64ToBinaryString(imageData, false);\n\n if (tmpImageData !== \"\") {\n imageData = tmpImageData;\n } else {\n tmpImageData = jsPDFAPI.loadFile(imageData, true);\n if (tmpImageData !== undefined) {\n imageData = tmpImageData;\n }\n }\n }\n\n if (isDOMElement(imageData)) {\n imageData = getImageDataFromElement(imageData, format);\n }\n\n format = getImageFileTypeByImageData(imageData, format);\n if (!isImageTypeSupported(format)) {\n throw new Error(\n \"addImage does not support files of type '\" +\n format +\n \"', please ensure that a plugin for '\" +\n format +\n \"' support is added.\"\n );\n }\n\n // now do the heavy lifting\n\n if (notDefined(alias)) {\n alias = generateAliasFromImageData(imageData);\n }\n result = checkImagesForAlias.call(this, alias);\n\n if (!result) {\n if (supportsArrayBuffer()) {\n // no need to convert if imageData is already uint8array\n if (!(imageData instanceof Uint8Array) && format !== \"RGBA\") {\n dataAsBinaryString = imageData;\n imageData = binaryStringToUint8Array(imageData);\n }\n }\n\n result = this[\"process\" + format.toUpperCase()](\n imageData,\n getImageIndex.call(this),\n alias,\n checkCompressValue(compression),\n dataAsBinaryString\n );\n }\n\n if (!result) {\n throw new Error(\"An unknown error occurred whilst processing the image.\");\n }\n return result;\n };\n\n /**\n * @name convertBase64ToBinaryString\n * @function\n * @param {string} stringData\n * @returns {string} binary string\n */\n var convertBase64ToBinaryString = (jsPDFAPI.__addimage__.convertBase64ToBinaryString = function(\n stringData,\n throwError\n ) {\n throwError = typeof throwError === \"boolean\" ? throwError : true;\n var base64Info;\n var imageData = \"\";\n var rawData;\n\n if (typeof stringData === \"string\") {\n base64Info = extractImageFromDataUrl(stringData);\n rawData = base64Info !== null ? base64Info.data : stringData;\n\n try {\n imageData = atob(rawData);\n } catch (e) {\n if (throwError) {\n if (!validateStringAsBase64(rawData)) {\n throw new Error(\n \"Supplied Data is not a valid base64-String jsPDF.convertBase64ToBinaryString \"\n );\n } else {\n throw new Error(\n \"atob-Error in jsPDF.convertBase64ToBinaryString \" + e.message\n );\n }\n }\n }\n }\n return imageData;\n });\n\n /**\n * @name getImageProperties\n * @function\n * @param {Object} imageData\n * @returns {Object}\n */\n jsPDFAPI.getImageProperties = function(imageData) {\n var image;\n var tmpImageData = \"\";\n var format;\n\n if (isDOMElement(imageData)) {\n imageData = getImageDataFromElement(imageData);\n }\n\n if (\n typeof imageData === \"string\" &&\n getImageFileTypeByImageData(imageData) === UNKNOWN\n ) {\n tmpImageData = convertBase64ToBinaryString(imageData, false);\n\n if (tmpImageData === \"\") {\n tmpImageData = jsPDFAPI.loadFile(imageData) || \"\";\n }\n imageData = tmpImageData;\n }\n\n format = getImageFileTypeByImageData(imageData);\n if (!isImageTypeSupported(format)) {\n throw new Error(\n \"addImage does not support files of type '\" +\n format +\n \"', please ensure that a plugin for '\" +\n format +\n \"' support is added.\"\n );\n }\n\n if (supportsArrayBuffer() && !(imageData instanceof Uint8Array)) {\n imageData = binaryStringToUint8Array(imageData);\n }\n\n image = this[\"process\" + format.toUpperCase()](imageData);\n\n if (!image) {\n throw new Error(\"An unknown error occurred whilst processing the image\");\n }\n\n image.fileType = format;\n\n return image;\n };\n})(jsPDF.API);\n","/**\n * @license\n * Copyright (c) 2014 Steven Spungin (TwelveTone LLC) steven@twelvetone.tv\n *\n * Licensed under the MIT License.\n * http://opensource.org/licenses/mit-license\n */\n\n/**\n * jsPDF Annotations PlugIn\n *\n * There are many types of annotations in a PDF document. Annotations are placed\n * on a page at a particular location. They are not 'attached' to an object.\n * \n * This plugin current supports \n *
Goto Page (set pageNumber and top in options)\n *
Goto Name (set name and top in options)\n *
Goto URL (set url in options)\n *
\n * \tThe destination magnification factor can also be specified when goto is a page number or a named destination. (see documentation below)\n * (set magFactor in options). XYZ is the default.\n *
\n *
\n * Links, Text, Popup, and FreeText are supported.\n *
\n *
\n * Options In PDF spec Not Implemented Yet\n *
link border\n *
named target\n *
page coordinates\n *
destination page scaling and layout\n *
actions other than URL and GotoPage\n *
background / hover actions\n * \n * @name annotations\n * @module\n */\n\n/*\n Destination Magnification Factors\n See PDF 1.3 Page 386 for meanings and options\n\n [supported]\n\tXYZ (options; left top zoom)\n\tFit (no options)\n\tFitH (options: top)\n\tFitV (options: left)\n\n\t[not supported]\n\tFitR\n\tFitB\n\tFitBH\n\tFitBV\n */\n\nimport { jsPDF } from \"../jspdf.js\";\n\n(function(jsPDFAPI) {\n \"use strict\";\n\n var notEmpty = function(obj) {\n if (typeof obj != \"undefined\") {\n if (obj != \"\") {\n return true;\n }\n }\n };\n\n jsPDF.API.events.push([\n \"addPage\",\n function(addPageData) {\n var pageInfo = this.internal.getPageInfo(addPageData.pageNumber);\n pageInfo.pageContext.annotations = [];\n }\n ]);\n\n jsPDFAPI.events.push([\n \"putPage\",\n function(putPageData) {\n var getHorizontalCoordinateString = this.internal.getCoordinateString;\n var getVerticalCoordinateString = this.internal\n .getVerticalCoordinateString;\n var pageInfo = this.internal.getPageInfoByObjId(putPageData.objId);\n var pageAnnos = putPageData.pageContext.annotations;\n\n var anno, rect, line;\n var found = false;\n for (var a = 0; a < pageAnnos.length && !found; a++) {\n anno = pageAnnos[a];\n switch (anno.type) {\n case \"link\":\n if (\n notEmpty(anno.options.url) ||\n notEmpty(anno.options.pageNumber)\n ) {\n found = true;\n }\n break;\n case \"reference\":\n case \"text\":\n case \"freetext\":\n found = true;\n break;\n }\n }\n if (found == false) {\n return;\n }\n\n this.internal.write(\"/Annots [\");\n for (var i = 0; i < pageAnnos.length; i++) {\n anno = pageAnnos[i];\n var escape = this.internal.pdfEscape;\n var encryptor = this.internal.getEncryptor(putPageData.objId);\n\n switch (anno.type) {\n case \"reference\":\n // References to Widget Annotations (for AcroForm Fields)\n this.internal.write(\" \" + anno.object.objId + \" 0 R \");\n break;\n case \"text\":\n // Create a an object for both the text and the popup\n var objText = this.internal.newAdditionalObject();\n var objPopup = this.internal.newAdditionalObject();\n var encryptorText = this.internal.getEncryptor(objText.objId);\n\n var title = anno.title || \"Note\";\n rect =\n \"/Rect [\" +\n getHorizontalCoordinateString(anno.bounds.x) +\n \" \" +\n getVerticalCoordinateString(anno.bounds.y + anno.bounds.h) +\n \" \" +\n getHorizontalCoordinateString(anno.bounds.x + anno.bounds.w) +\n \" \" +\n getVerticalCoordinateString(anno.bounds.y) +\n \"] \";\n\n line =\n \"<>\";\n objText.content = line;\n\n var parent = objText.objId + \" 0 R\";\n var popoff = 30;\n rect =\n \"/Rect [\" +\n getHorizontalCoordinateString(anno.bounds.x + popoff) +\n \" \" +\n getVerticalCoordinateString(anno.bounds.y + anno.bounds.h) +\n \" \" +\n getHorizontalCoordinateString(\n anno.bounds.x + anno.bounds.w + popoff\n ) +\n \" \" +\n getVerticalCoordinateString(anno.bounds.y) +\n \"] \";\n line =\n \"<>\";\n objPopup.content = line;\n\n this.internal.write(objText.objId, \"0 R\", objPopup.objId, \"0 R\");\n\n break;\n case \"freetext\":\n rect =\n \"/Rect [\" +\n getHorizontalCoordinateString(anno.bounds.x) +\n \" \" +\n getVerticalCoordinateString(anno.bounds.y) +\n \" \" +\n getHorizontalCoordinateString(anno.bounds.x + anno.bounds.w) +\n \" \" +\n getVerticalCoordinateString(anno.bounds.y + anno.bounds.h) +\n \"] \";\n var color = anno.color || \"#000000\";\n line =\n \"<>\";\n this.internal.write(line);\n break;\n case \"link\":\n if (anno.options.name) {\n var loc = this.annotations._nameMap[anno.options.name];\n anno.options.pageNumber = loc.page;\n anno.options.top = loc.y;\n } else {\n if (!anno.options.top) {\n anno.options.top = 0;\n }\n }\n\n rect =\n \"/Rect [\" +\n anno.finalBounds.x +\n \" \" +\n anno.finalBounds.y +\n \" \" +\n anno.finalBounds.w +\n \" \" +\n anno.finalBounds.h +\n \"] \";\n\n line = \"\";\n if (anno.options.url) {\n line =\n \"<>\";\n } else if (anno.options.pageNumber) {\n // first page is 0\n var info = this.internal.getPageInfo(anno.options.pageNumber);\n line =\n \"<>\";\n this.internal.write(line);\n }\n break;\n }\n }\n this.internal.write(\"]\");\n }\n ]);\n\n /**\n * @name createAnnotation\n * @function\n * @param {Object} options\n */\n jsPDFAPI.createAnnotation = function(options) {\n var pageInfo = this.internal.getCurrentPageInfo();\n switch (options.type) {\n case \"link\":\n this.link(\n options.bounds.x,\n options.bounds.y,\n options.bounds.w,\n options.bounds.h,\n options\n );\n break;\n case \"text\":\n case \"freetext\":\n pageInfo.pageContext.annotations.push(options);\n break;\n }\n };\n\n /**\n * Create a link\n *\n * valid options\n *
pageNumber or url [required]\n *
If pageNumber is specified, top and zoom may also be specified
\n * @name link\n * @function\n * @param {number} x\n * @param {number} y\n * @param {number} w\n * @param {number} h\n * @param {Object} options\n */\n jsPDFAPI.link = function(x, y, w, h, options) {\n var pageInfo = this.internal.getCurrentPageInfo();\n var getHorizontalCoordinateString = this.internal.getCoordinateString;\n var getVerticalCoordinateString = this.internal.getVerticalCoordinateString;\n\n pageInfo.pageContext.annotations.push({\n finalBounds: {\n x: getHorizontalCoordinateString(x),\n y: getVerticalCoordinateString(y),\n w: getHorizontalCoordinateString(x + w),\n h: getVerticalCoordinateString(y + h)\n },\n options: options,\n type: \"link\"\n });\n };\n\n /**\n * Currently only supports single line text.\n * Returns the width of the text/link\n *\n * @name textWithLink\n * @function\n * @param {string} text\n * @param {number} x\n * @param {number} y\n * @param {Object} options\n * @returns {number} width the width of the text/link\n */\n jsPDFAPI.textWithLink = function(text, x, y, options) {\n var totalLineWidth = this.getTextWidth(text);\n var lineHeight = this.internal.getLineHeight() / this.internal.scaleFactor;\n var linkHeight, linkWidth;\n\n // Checking if maxWidth option is passed to determine lineWidth and number of lines for each line\n if (options.maxWidth !== undefined) {\n var { maxWidth } = options;\n linkWidth = maxWidth;\n var numOfLines = this.splitTextToSize(text, linkWidth).length;\n linkHeight = Math.ceil(lineHeight * numOfLines);\n } else {\n linkWidth = totalLineWidth;\n linkHeight = lineHeight;\n }\n\n this.text(text, x, y, options);\n\n //TODO We really need the text baseline height to do this correctly.\n // Or ability to draw text on top, bottom, center, or baseline.\n y += lineHeight * 0.2;\n //handle x position based on the align option\n if (options.align === \"center\") {\n x = x - totalLineWidth / 2; //since starting from center move the x position by half of text width\n }\n if (options.align === \"right\") {\n x = x - totalLineWidth;\n }\n this.link(x, y - lineHeight, linkWidth, linkHeight, options);\n return totalLineWidth;\n };\n\n //TODO move into external library\n /**\n * @name getTextWidth\n * @function\n * @param {string} text\n * @returns {number} txtWidth\n */\n jsPDFAPI.getTextWidth = function(text) {\n var fontSize = this.internal.getFontSize();\n var txtWidth =\n (this.getStringUnitWidth(text) * fontSize) / this.internal.scaleFactor;\n return txtWidth;\n };\n\n return this;\n})(jsPDF.API);\n","/**\n * @license\n * Copyright (c) 2017 Aras Abbasi\n *\n * Licensed under the MIT License.\n * http://opensource.org/licenses/mit-license\n */\n\nimport { jsPDF } from \"../jspdf.js\";\n\n/**\n * jsPDF arabic parser PlugIn\n *\n * @name arabic\n * @module\n */\n(function(jsPDFAPI) {\n \"use strict\";\n\n /**\n * Arabic shape substitutions: char code => (isolated, final, initial, medial).\n * Arabic Substition A\n */\n var arabicSubstitionA = {\n 0x0621: [0xfe80], // ARABIC LETTER HAMZA\n 0x0622: [0xfe81, 0xfe82], // ARABIC LETTER ALEF WITH MADDA ABOVE\n 0x0623: [0xfe83, 0xfe84], // ARABIC LETTER ALEF WITH HAMZA ABOVE\n 0x0624: [0xfe85, 0xfe86], // ARABIC LETTER WAW WITH HAMZA ABOVE\n 0x0625: [0xfe87, 0xfe88], // ARABIC LETTER ALEF WITH HAMZA BELOW\n 0x0626: [0xfe89, 0xfe8a, 0xfe8b, 0xfe8c], // ARABIC LETTER YEH WITH HAMZA ABOVE\n 0x0627: [0xfe8d, 0xfe8e], // ARABIC LETTER ALEF\n 0x0628: [0xfe8f, 0xfe90, 0xfe91, 0xfe92], // ARABIC LETTER BEH\n 0x0629: [0xfe93, 0xfe94], // ARABIC LETTER TEH MARBUTA\n 0x062a: [0xfe95, 0xfe96, 0xfe97, 0xfe98], // ARABIC LETTER TEH\n 0x062b: [0xfe99, 0xfe9a, 0xfe9b, 0xfe9c], // ARABIC LETTER THEH\n 0x062c: [0xfe9d, 0xfe9e, 0xfe9f, 0xfea0], // ARABIC LETTER JEEM\n 0x062d: [0xfea1, 0xfea2, 0xfea3, 0xfea4], // ARABIC LETTER HAH\n 0x062e: [0xfea5, 0xfea6, 0xfea7, 0xfea8], // ARABIC LETTER KHAH\n 0x062f: [0xfea9, 0xfeaa], // ARABIC LETTER DAL\n 0x0630: [0xfeab, 0xfeac], // ARABIC LETTER THAL\n 0x0631: [0xfead, 0xfeae], // ARABIC LETTER REH\n 0x0632: [0xfeaf, 0xfeb0], // ARABIC LETTER ZAIN\n 0x0633: [0xfeb1, 0xfeb2, 0xfeb3, 0xfeb4], // ARABIC LETTER SEEN\n 0x0634: [0xfeb5, 0xfeb6, 0xfeb7, 0xfeb8], // ARABIC LETTER SHEEN\n 0x0635: [0xfeb9, 0xfeba, 0xfebb, 0xfebc], // ARABIC LETTER SAD\n 0x0636: [0xfebd, 0xfebe, 0xfebf, 0xfec0], // ARABIC LETTER DAD\n 0x0637: [0xfec1, 0xfec2, 0xfec3, 0xfec4], // ARABIC LETTER TAH\n 0x0638: [0xfec5, 0xfec6, 0xfec7, 0xfec8], // ARABIC LETTER ZAH\n 0x0639: [0xfec9, 0xfeca, 0xfecb, 0xfecc], // ARABIC LETTER AIN\n 0x063a: [0xfecd, 0xfece, 0xfecf, 0xfed0], // ARABIC LETTER GHAIN\n 0x0641: [0xfed1, 0xfed2, 0xfed3, 0xfed4], // ARABIC LETTER FEH\n 0x0642: [0xfed5, 0xfed6, 0xfed7, 0xfed8], // ARABIC LETTER QAF\n 0x0643: [0xfed9, 0xfeda, 0xfedb, 0xfedc], // ARABIC LETTER KAF\n 0x0644: [0xfedd, 0xfede, 0xfedf, 0xfee0], // ARABIC LETTER LAM\n 0x0645: [0xfee1, 0xfee2, 0xfee3, 0xfee4], // ARABIC LETTER MEEM\n 0x0646: [0xfee5, 0xfee6, 0xfee7, 0xfee8], // ARABIC LETTER NOON\n 0x0647: [0xfee9, 0xfeea, 0xfeeb, 0xfeec], // ARABIC LETTER HEH\n 0x0648: [0xfeed, 0xfeee], // ARABIC LETTER WAW\n 0x0649: [0xfeef, 0xfef0, 64488, 64489], // ARABIC LETTER ALEF MAKSURA\n 0x064a: [0xfef1, 0xfef2, 0xfef3, 0xfef4], // ARABIC LETTER YEH\n 0x0671: [0xfb50, 0xfb51], // ARABIC LETTER ALEF WASLA\n 0x0677: [0xfbdd], // ARABIC LETTER U WITH HAMZA ABOVE\n 0x0679: [0xfb66, 0xfb67, 0xfb68, 0xfb69], // ARABIC LETTER TTEH\n 0x067a: [0xfb5e, 0xfb5f, 0xfb60, 0xfb61], // ARABIC LETTER TTEHEH\n 0x067b: [0xfb52, 0xfb53, 0xfb54, 0xfb55], // ARABIC LETTER BEEH\n 0x067e: [0xfb56, 0xfb57, 0xfb58, 0xfb59], // ARABIC LETTER PEH\n 0x067f: [0xfb62, 0xfb63, 0xfb64, 0xfb65], // ARABIC LETTER TEHEH\n 0x0680: [0xfb5a, 0xfb5b, 0xfb5c, 0xfb5d], // ARABIC LETTER BEHEH\n 0x0683: [0xfb76, 0xfb77, 0xfb78, 0xfb79], // ARABIC LETTER NYEH\n 0x0684: [0xfb72, 0xfb73, 0xfb74, 0xfb75], // ARABIC LETTER DYEH\n 0x0686: [0xfb7a, 0xfb7b, 0xfb7c, 0xfb7d], // ARABIC LETTER TCHEH\n 0x0687: [0xfb7e, 0xfb7f, 0xfb80, 0xfb81], // ARABIC LETTER TCHEHEH\n 0x0688: [0xfb88, 0xfb89], // ARABIC LETTER DDAL\n 0x068c: [0xfb84, 0xfb85], // ARABIC LETTER DAHAL\n 0x068d: [0xfb82, 0xfb83], // ARABIC LETTER DDAHAL\n 0x068e: [0xfb86, 0xfb87], // ARABIC LETTER DUL\n 0x0691: [0xfb8c, 0xfb8d], // ARABIC LETTER RREH\n 0x0698: [0xfb8a, 0xfb8b], // ARABIC LETTER JEH\n 0x06a4: [0xfb6a, 0xfb6b, 0xfb6c, 0xfb6d], // ARABIC LETTER VEH\n 0x06a6: [0xfb6e, 0xfb6f, 0xfb70, 0xfb71], // ARABIC LETTER PEHEH\n 0x06a9: [0xfb8e, 0xfb8f, 0xfb90, 0xfb91], // ARABIC LETTER KEHEH\n 0x06ad: [0xfbd3, 0xfbd4, 0xfbd5, 0xfbd6], // ARABIC LETTER NG\n 0x06af: [0xfb92, 0xfb93, 0xfb94, 0xfb95], // ARABIC LETTER GAF\n 0x06b1: [0xfb9a, 0xfb9b, 0xfb9c, 0xfb9d], // ARABIC LETTER NGOEH\n 0x06b3: [0xfb96, 0xfb97, 0xfb98, 0xfb99], // ARABIC LETTER GUEH\n 0x06ba: [0xfb9e, 0xfb9f], // ARABIC LETTER NOON GHUNNA\n 0x06bb: [0xfba0, 0xfba1, 0xfba2, 0xfba3], // ARABIC LETTER RNOON\n 0x06be: [0xfbaa, 0xfbab, 0xfbac, 0xfbad], // ARABIC LETTER HEH DOACHASHMEE\n 0x06c0: [0xfba4, 0xfba5], // ARABIC LETTER HEH WITH YEH ABOVE\n 0x06c1: [0xfba6, 0xfba7, 0xfba8, 0xfba9], // ARABIC LETTER HEH GOAL\n 0x06c5: [0xfbe0, 0xfbe1], // ARABIC LETTER KIRGHIZ OE\n 0x06c6: [0xfbd9, 0xfbda], // ARABIC LETTER OE\n 0x06c7: [0xfbd7, 0xfbd8], // ARABIC LETTER U\n 0x06c8: [0xfbdb, 0xfbdc], // ARABIC LETTER YU\n 0x06c9: [0xfbe2, 0xfbe3], // ARABIC LETTER KIRGHIZ YU\n 0x06cb: [0xfbde, 0xfbdf], // ARABIC LETTER VE\n 0x06cc: [0xfbfc, 0xfbfd, 0xfbfe, 0xfbff], // ARABIC LETTER FARSI YEH\n 0x06d0: [0xfbe4, 0xfbe5, 0xfbe6, 0xfbe7], //ARABIC LETTER E\n 0x06d2: [0xfbae, 0xfbaf], // ARABIC LETTER YEH BARREE\n 0x06d3: [0xfbb0, 0xfbb1] // ARABIC LETTER YEH BARREE WITH HAMZA ABOVE\n };\n\n /*\n var ligaturesSubstitutionA = {\n 0xFBEA: []// ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH ALEF ISOLATED FORM\n };\n */\n\n var ligatures = {\n 0xfedf: {\n 0xfe82: 0xfef5, // ARABIC LIGATURE LAM WITH ALEF WITH MADDA ABOVE ISOLATED FORM\n 0xfe84: 0xfef7, // ARABIC LIGATURE LAM WITH ALEF WITH HAMZA ABOVE ISOLATED FORM\n 0xfe88: 0xfef9, // ARABIC LIGATURE LAM WITH ALEF WITH HAMZA BELOW ISOLATED FORM\n 0xfe8e: 0xfefb // ARABIC LIGATURE LAM WITH ALEF ISOLATED FORM\n },\n 0xfee0: {\n 0xfe82: 0xfef6, // ARABIC LIGATURE LAM WITH ALEF WITH MADDA ABOVE FINAL FORM\n 0xfe84: 0xfef8, // ARABIC LIGATURE LAM WITH ALEF WITH HAMZA ABOVE FINAL FORM\n 0xfe88: 0xfefa, // ARABIC LIGATURE LAM WITH ALEF WITH HAMZA BELOW FINAL FORM\n 0xfe8e: 0xfefc // ARABIC LIGATURE LAM WITH ALEF FINAL FORM\n },\n 0xfe8d: { 0xfedf: { 0xfee0: { 0xfeea: 0xfdf2 } } }, // ALLAH\n 0x0651: {\n 0x064c: 0xfc5e, // Shadda + Dammatan\n 0x064d: 0xfc5f, // Shadda + Kasratan\n 0x064e: 0xfc60, // Shadda + Fatha\n 0x064f: 0xfc61, // Shadda + Damma\n 0x0650: 0xfc62 // Shadda + Kasra\n }\n };\n\n var arabic_diacritics = {\n 1612: 64606, // Shadda + Dammatan\n 1613: 64607, // Shadda + Kasratan\n 1614: 64608, // Shadda + Fatha\n 1615: 64609, // Shadda + Damma\n 1616: 64610 // Shadda + Kasra\n };\n\n var alfletter = [1570, 1571, 1573, 1575];\n\n var noChangeInForm = -1;\n var isolatedForm = 0;\n var finalForm = 1;\n var initialForm = 2;\n var medialForm = 3;\n\n jsPDFAPI.__arabicParser__ = {};\n\n //private\n var isInArabicSubstitutionA = (jsPDFAPI.__arabicParser__.isInArabicSubstitutionA = function(\n letter\n ) {\n return typeof arabicSubstitionA[letter.charCodeAt(0)] !== \"undefined\";\n });\n\n var isArabicLetter = (jsPDFAPI.__arabicParser__.isArabicLetter = function(\n letter\n ) {\n return (\n typeof letter === \"string\" &&\n /^[\\u0600-\\u06FF\\u0750-\\u077F\\u08A0-\\u08FF\\uFB50-\\uFDFF\\uFE70-\\uFEFF]+$/.test(\n letter\n )\n );\n });\n\n var isArabicEndLetter = (jsPDFAPI.__arabicParser__.isArabicEndLetter = function(\n letter\n ) {\n return (\n isArabicLetter(letter) &&\n isInArabicSubstitutionA(letter) &&\n arabicSubstitionA[letter.charCodeAt(0)].length <= 2\n );\n });\n\n var isArabicAlfLetter = (jsPDFAPI.__arabicParser__.isArabicAlfLetter = function(\n letter\n ) {\n return (\n isArabicLetter(letter) && alfletter.indexOf(letter.charCodeAt(0)) >= 0\n );\n });\n\n jsPDFAPI.__arabicParser__.arabicLetterHasIsolatedForm = function(letter) {\n return (\n isArabicLetter(letter) &&\n isInArabicSubstitutionA(letter) &&\n arabicSubstitionA[letter.charCodeAt(0)].length >= 1\n );\n };\n\n var arabicLetterHasFinalForm = (jsPDFAPI.__arabicParser__.arabicLetterHasFinalForm = function(\n letter\n ) {\n return (\n isArabicLetter(letter) &&\n isInArabicSubstitutionA(letter) &&\n arabicSubstitionA[letter.charCodeAt(0)].length >= 2\n );\n });\n\n jsPDFAPI.__arabicParser__.arabicLetterHasInitialForm = function(letter) {\n return (\n isArabicLetter(letter) &&\n isInArabicSubstitutionA(letter) &&\n arabicSubstitionA[letter.charCodeAt(0)].length >= 3\n );\n };\n\n var arabicLetterHasMedialForm = (jsPDFAPI.__arabicParser__.arabicLetterHasMedialForm = function(\n letter\n ) {\n return (\n isArabicLetter(letter) &&\n isInArabicSubstitutionA(letter) &&\n arabicSubstitionA[letter.charCodeAt(0)].length == 4\n );\n });\n\n var resolveLigatures = (jsPDFAPI.__arabicParser__.resolveLigatures = function(\n letters\n ) {\n var i = 0;\n var tmpLigatures = ligatures;\n var result = \"\";\n var effectedLetters = 0;\n\n for (i = 0; i < letters.length; i += 1) {\n if (typeof tmpLigatures[letters.charCodeAt(i)] !== \"undefined\") {\n effectedLetters++;\n tmpLigatures = tmpLigatures[letters.charCodeAt(i)];\n\n if (typeof tmpLigatures === \"number\") {\n result += String.fromCharCode(tmpLigatures);\n tmpLigatures = ligatures;\n effectedLetters = 0;\n }\n if (i === letters.length - 1) {\n tmpLigatures = ligatures;\n result += letters.charAt(i - (effectedLetters - 1));\n i = i - (effectedLetters - 1);\n effectedLetters = 0;\n }\n } else {\n tmpLigatures = ligatures;\n result += letters.charAt(i - effectedLetters);\n i = i - effectedLetters;\n effectedLetters = 0;\n }\n }\n\n return result;\n });\n\n jsPDFAPI.__arabicParser__.isArabicDiacritic = function(letter) {\n return (\n letter !== undefined &&\n arabic_diacritics[letter.charCodeAt(0)] !== undefined\n );\n };\n\n var getCorrectForm = (jsPDFAPI.__arabicParser__.getCorrectForm = function(\n currentChar,\n beforeChar,\n nextChar\n ) {\n if (!isArabicLetter(currentChar)) {\n return -1;\n }\n\n if (isInArabicSubstitutionA(currentChar) === false) {\n return noChangeInForm;\n }\n if (\n !arabicLetterHasFinalForm(currentChar) ||\n (!isArabicLetter(beforeChar) && !isArabicLetter(nextChar)) ||\n (!isArabicLetter(nextChar) && isArabicEndLetter(beforeChar)) ||\n (isArabicEndLetter(currentChar) && !isArabicLetter(beforeChar)) ||\n (isArabicEndLetter(currentChar) && isArabicAlfLetter(beforeChar)) ||\n (isArabicEndLetter(currentChar) && isArabicEndLetter(beforeChar))\n ) {\n return isolatedForm;\n }\n\n if (\n arabicLetterHasMedialForm(currentChar) &&\n isArabicLetter(beforeChar) &&\n !isArabicEndLetter(beforeChar) &&\n isArabicLetter(nextChar) &&\n arabicLetterHasFinalForm(nextChar)\n ) {\n return medialForm;\n }\n\n if (isArabicEndLetter(currentChar) || !isArabicLetter(nextChar)) {\n return finalForm;\n }\n return initialForm;\n });\n\n /**\n * @name processArabic\n * @function\n * @param {string} text\n * @returns {string}\n */\n var parseArabic = function(text) {\n text = text || \"\";\n\n var result = \"\";\n var i = 0;\n var j = 0;\n var position = 0;\n var currentLetter = \"\";\n var prevLetter = \"\";\n var nextLetter = \"\";\n\n var words = text.split(\"\\\\s+\");\n var newWords = [];\n for (i = 0; i < words.length; i += 1) {\n newWords.push(\"\");\n for (j = 0; j < words[i].length; j += 1) {\n currentLetter = words[i][j];\n prevLetter = words[i][j - 1];\n nextLetter = words[i][j + 1];\n if (isArabicLetter(currentLetter)) {\n position = getCorrectForm(currentLetter, prevLetter, nextLetter);\n if (position !== -1) {\n newWords[i] += String.fromCharCode(\n arabicSubstitionA[currentLetter.charCodeAt(0)][position]\n );\n } else {\n newWords[i] += currentLetter;\n }\n } else {\n newWords[i] += currentLetter;\n }\n }\n\n newWords[i] = resolveLigatures(newWords[i]);\n }\n result = newWords.join(\" \");\n\n return result;\n };\n\n var processArabic = (jsPDFAPI.__arabicParser__.processArabic = jsPDFAPI.processArabic = function() {\n var text =\n typeof arguments[0] === \"string\" ? arguments[0] : arguments[0].text;\n var tmpText = [];\n var result;\n\n if (Array.isArray(text)) {\n var i = 0;\n tmpText = [];\n for (i = 0; i < text.length; i += 1) {\n if (Array.isArray(text[i])) {\n tmpText.push([parseArabic(text[i][0]), text[i][1], text[i][2]]);\n } else {\n tmpText.push([parseArabic(text[i])]);\n }\n }\n result = tmpText;\n } else {\n result = parseArabic(text);\n }\n if (typeof arguments[0] === \"string\") {\n return result;\n } else {\n arguments[0].text = result;\n return arguments[0];\n }\n });\n\n jsPDFAPI.events.push([\"preProcessText\", processArabic]);\n})(jsPDF.API);\n","/** @license\n * jsPDF Autoprint Plugin\n *\n * Licensed under the MIT License.\n * http://opensource.org/licenses/mit-license\n */\n\nimport { jsPDF } from \"../jspdf.js\";\n\n/**\n * @name autoprint\n * @module\n */\n(function(jsPDFAPI) {\n \"use strict\";\n\n /**\n * Makes the PDF automatically open the print-Dialog when opened in a PDF-viewer.\n *\n * @name autoPrint\n * @function\n * @param {Object} options (optional) Set the attribute variant to 'non-conform' (default) or 'javascript' to activate different methods of automatic printing when opening in a PDF-viewer .\n * @returns {jsPDF}\n * @example\n * var doc = new jsPDF();\n * doc.text(10, 10, 'This is a test');\n * doc.autoPrint({variant: 'non-conform'});\n * doc.save('autoprint.pdf');\n */\n jsPDFAPI.autoPrint = function(options) {\n \"use strict\";\n var refAutoPrintTag;\n options = options || {};\n options.variant = options.variant || \"non-conform\";\n\n switch (options.variant) {\n case \"javascript\":\n //https://github.com/Rob--W/pdf.js/commit/c676ecb5a0f54677b9f3340c3ef2cf42225453bb\n this.addJS(\"print({});\");\n break;\n case \"non-conform\":\n default:\n this.internal.events.subscribe(\"postPutResources\", function() {\n refAutoPrintTag = this.internal.newObject();\n this.internal.out(\"<<\");\n this.internal.out(\"/S /Named\");\n this.internal.out(\"/Type /Action\");\n this.internal.out(\"/N /Print\");\n this.internal.out(\">>\");\n this.internal.out(\"endobj\");\n });\n\n this.internal.events.subscribe(\"putCatalog\", function() {\n this.internal.out(\"/OpenAction \" + refAutoPrintTag + \" 0 R\");\n });\n break;\n }\n return this;\n };\n})(jsPDF.API);\n","/**\n * @license\n * Copyright (c) 2014 Steven Spungin (TwelveTone LLC) steven@twelvetone.tv\n *\n * Licensed under the MIT License.\n * http://opensource.org/licenses/mit-license\n */\n\nimport { jsPDF } from \"../jspdf.js\";\n\n/**\n * jsPDF Canvas PlugIn\n * This plugin mimics the HTML5 Canvas\n *\n * The goal is to provide a way for current canvas users to print directly to a PDF.\n * @name canvas\n * @module\n */\n(function(jsPDFAPI) {\n \"use strict\";\n\n /**\n * @class Canvas\n * @classdesc A Canvas Wrapper for jsPDF\n */\n var Canvas = function() {\n var jsPdfInstance = undefined;\n Object.defineProperty(this, \"pdf\", {\n get: function() {\n return jsPdfInstance;\n },\n set: function(value) {\n jsPdfInstance = value;\n }\n });\n\n var _width = 150;\n /**\n * The height property is a positive integer reflecting the height HTML attribute of the