ebta-workflow-prod 1.1.2 → 4.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/.eslintrc.js +7 -0
  2. package/.vscode/extensions.json +3 -0
  3. package/.vscode/settings.json +3 -0
  4. package/README.md +26 -0
  5. package/build/2983859c0d77824ec8db.svg +1 -0
  6. package/build/4168d659d55fd94368bc.svg +1 -0
  7. package/build/641b2c17e92b0e885b9f.svg +1 -0
  8. package/build/882445a26a4f3964786f.svg +1 -0
  9. package/build/activitypack.json +1 -1
  10. package/build/b280e967312a55101032.svg +1 -0
  11. package/build/main.js +2 -2
  12. package/build/main.js.LICENSE.txt +99 -0
  13. package/certs/localhost+2-key.pem +28 -0
  14. package/certs/localhost+2.pem +26 -0
  15. package/index.html +58 -0
  16. package/package.json +43 -7
  17. package/public/img/background.svg +3 -3
  18. package/public/img/compass.svg +3 -3
  19. package/public/img/grab.svg +3 -3
  20. package/public/img/grabbing.svg +3 -3
  21. package/public/img/sprites.svg +14 -14
  22. package/src/activities/AppendObjects.ts +40 -0
  23. package/src/activities/Base64ToFile.ts +56 -0
  24. package/src/activities/CreateGraphicLayer.ts +85 -0
  25. package/src/activities/DrapGeometry.ts +99 -0
  26. package/src/activities/GetElementByDataLayoutID.ts +58 -0
  27. package/src/activities/GetSesionStorage.ts +36 -0
  28. package/src/activities/GetTaskStatus.ts +56 -0
  29. package/src/activities/GetUserToken.ts +102 -0
  30. package/src/activities/GetvGISDetails copy.ts +110 -0
  31. package/src/activities/GetvGISDetails.ts +110 -0
  32. package/src/activities/HTMLextractor.ts +188 -0
  33. package/src/activities/LegendCreator.ts +155 -0
  34. package/src/activities/MergeObjects.ts +61 -0
  35. package/src/activities/PDFMapGenerator.ts +729 -0
  36. package/src/activities/RequestChecker.ts +94 -0
  37. package/src/activities/Sample.ts +41 -0
  38. package/src/activities/SetSessionStorage.ts +57 -0
  39. package/src/activities/SliceLine.ts +61 -0
  40. package/src/elements/AttributeFilter.tsx +1867 -0
  41. package/src/elements/CircularSlider.tsx +67 -0
  42. package/src/elements/CsColorPicker.tsx +140 -0
  43. package/src/elements/DropDownMenu.tsx +282 -0
  44. package/src/elements/HeaderwithHelp.tsx +178 -0
  45. package/src/elements/ItemSearcher.tsx +1035 -0
  46. package/src/elements/ItemSearcher_v2.tsx +1071 -0
  47. package/src/elements/LineChartMui.tsx +50 -0
  48. package/src/elements/MenueBar.tsx +118 -0
  49. package/src/elements/MultipleAutoCompleteCS.tsx +145 -0
  50. package/src/elements/SingleAutoCompleteCS.tsx +133 -0
  51. package/src/elements/TableRadioGoup.tsx +163 -0
  52. package/src/elements/Timeline.tsx +104 -0
  53. package/src/elements/ToggleButtonGroup.tsx +129 -0
  54. package/src/elements/Viewer360.tsx +71 -0
  55. package/src/elements/modules/AttributeFilterEasyComp.tsx +253 -0
  56. package/src/index.ts +41 -0
  57. package/src/main.ts +15 -0
  58. package/src/styles/imageSlider.css +34 -0
  59. package/tsconfig.json +12 -0
  60. package/uuid.js +7 -0
@@ -0,0 +1,729 @@
1
+
2
+ /* eslint-disable @typescript-eslint/no-unused-vars */
3
+ /* eslint-disable @typescript-eslint/no-unsafe-argument */
4
+ /* eslint-disable prefer-const */
5
+ import type { IActivityHandler } from "@vertigis/workflow";
6
+ import { PDFDocument, degrees, rgb, PDFName, PDFString, TextAlignment, PDFArray, StandardFonts,PDFRef } from 'pdf-lib';
7
+ import axios from 'axios';
8
+
9
+
10
+ /** An interface that defines the inputs of the activity. */
11
+ interface PDFMapGeneratorInputs {
12
+ /**
13
+ * @displayName img Base64 Array
14
+ * @description img Base64 Array
15
+ * @required
16
+ */
17
+ imgBase64: string[];
18
+ /**
19
+ * @displayName PDF Link
20
+ * @description The link of blank PDF Template.
21
+ */
22
+ pdfLink: string;
23
+
24
+ /**
25
+ * @displayName Legend img Base64
26
+ * @description Legend img Base64 created by Legend creator
27
+ * @required
28
+ */
29
+ LegendImgBase64: string;
30
+ /**
31
+ * @displayName Scale Array
32
+ * @description Array contains the scale numbers catched from each extent
33
+ * @required
34
+ */
35
+ scaleArray: number[];
36
+ /**
37
+ * @displayName Map Title
38
+ * @description Map Title
39
+ */
40
+ Title: string;
41
+ /**
42
+ * @displayName Revision Description
43
+ * @description Revision Description
44
+ */
45
+ Descr: string;
46
+ /**
47
+ * @displayName Note
48
+ * @description Note
49
+ */
50
+ Note: string;
51
+ /**
52
+ * @displayName Revision
53
+ * @description Revision
54
+ */
55
+ Rev: string;
56
+ /**
57
+ * @displayName Printer Name
58
+ * @description Printer Name
59
+ */
60
+ PrintUser: string;
61
+ /**
62
+ * @displayName Rotation angle
63
+ * @description Rotation angle for compass
64
+ */
65
+ rotation: number[];
66
+ /**
67
+ * @displayName QR Code Url
68
+ * @description QR Code Url
69
+ */
70
+ QrUrl?: string[];
71
+ /**
72
+ * @displayName link inside the button
73
+ * @description Display button lead to QR URLs rather than QRCodes
74
+ */
75
+ buttonLink?: string[];
76
+ /**
77
+ * @displayName QR Code image array (url ending with .jpg etc)
78
+ * @description Put QR image url so it will be put in pdf. If this value given, QR URL and button parameters will be ignored.
79
+ */
80
+ QrCodeImage?: string[];
81
+
82
+
83
+ /**
84
+ * @displayName Field View Id
85
+ * @description Field View Id
86
+ */
87
+ FiewViewId: string[] | number[];
88
+ /**
89
+ * @displayName Array of geocoded addresses
90
+ * @description Array of geocoded addresses
91
+ */
92
+ addressList: string[];
93
+ }
94
+ /** An interface that defines the outputs of the activity. */
95
+ interface PDFMapGeneratorOutputs {
96
+ /**
97
+ * @description The result of the activity.
98
+ */
99
+ result: string;
100
+ }
101
+ /**
102
+ * @displayName PDF Map Generator
103
+ * @category EBTA Utilities
104
+ * @description An activity which generate the Map PDF using map and legend screenshot
105
+ */
106
+ export default class PDFMapGeneratorActivity implements IActivityHandler {
107
+ private async fetchData(url: string): Promise<ArrayBuffer> {
108
+ try {
109
+ console.log('EBTA PDFMapGenerator version 1.1')
110
+ if (typeof fetch !== 'undefined') {
111
+ const response = await fetch(url, {
112
+ mode: 'cors',
113
+ credentials: 'omit'
114
+ });
115
+ if (!response.ok) {
116
+ throw new Error(`HTTP error! status: ${response.status}`);
117
+ }
118
+ return await response.arrayBuffer();
119
+ } else {
120
+ const response = await axios.get(url, {
121
+ responseType: 'arraybuffer',
122
+ withCredentials: false
123
+ });
124
+ return response.data;
125
+ }
126
+ } catch (error) {
127
+ console.error(`Failed to fetch data from ${url}:`, error);
128
+ throw new Error(`Failed to fetch data from ${url}`);
129
+ }
130
+ }
131
+
132
+ private async reencodeImageToArrayBuffer(arrayBuffer: ArrayBuffer): Promise<ArrayBuffer> {
133
+ const blob = new Blob([arrayBuffer], { type: 'image/jpeg' });
134
+ const img = new Image();
135
+
136
+ // Convert the array buffer to a blob URL
137
+ img.src = URL.createObjectURL(blob);
138
+
139
+ return new Promise<ArrayBuffer>((resolve, reject) => {
140
+ img.onload = () => {
141
+ const canvas = document.createElement('canvas');
142
+ canvas.width = img.width;
143
+ canvas.height = img.height;
144
+
145
+ const ctx = canvas.getContext('2d');
146
+ if (!ctx) return reject(new Error('Canvas context could not be created'));
147
+
148
+ ctx.drawImage(img, 0, 0);
149
+
150
+ canvas.toBlob((blob) => {
151
+ if (!blob) return reject(new Error('Failed to convert canvas to Blob'));
152
+
153
+ const reader = new FileReader();
154
+ reader.onloadend = () => {
155
+ if (reader.result instanceof ArrayBuffer) {
156
+ resolve(reader.result);
157
+ } else {
158
+ reject(new Error('Failed to read Blob as ArrayBuffer'));
159
+ }
160
+ };
161
+ reader.readAsArrayBuffer(blob);
162
+ }, 'image/jpeg');
163
+ };
164
+
165
+ img.onerror = (err) => reject(new Error('Image load error: '));
166
+ });
167
+ }
168
+
169
+
170
+ private arrayBufferToBase64(buffer: ArrayBuffer): string {
171
+ let binary = '';
172
+ const bytes = new Uint8Array(buffer);
173
+ const len = bytes.byteLength;
174
+ for (let i = 0; i < len; i++) {
175
+ binary += String.fromCharCode(bytes[i]);
176
+ }
177
+ return btoa(binary);
178
+ }
179
+ private createDownloadUrl(base64String: string): string {
180
+ return `data:application/pdf;base64,${base64String}`;
181
+ }
182
+ private base64ToArrayBuffer(base64: string): ArrayBuffer {
183
+ const binaryString = atob(base64.split(',')[1]);
184
+ const len = binaryString.length;
185
+ const bytes = new Uint8Array(len);
186
+ for (let i = 0; i < len; i++) {
187
+ bytes[i] = binaryString.charCodeAt(i);
188
+ }
189
+ return bytes.buffer;
190
+ }
191
+ private isValidImage(base64: string): boolean {
192
+ return base64.startsWith('data:image/png;base64,') ||
193
+ base64.startsWith('data:image/jpg;base64,') ||
194
+ base64.startsWith('data:image/jpeg;base64,');
195
+ }
196
+ private nearestNiceInteger(num) {
197
+ const changeLimit = num * 0.2;
198
+ const bigInterger = [10000, 5000, 2000, 1000, 500, 200, 100, 50, 20, 10, 5, 2];
199
+ for (const multiple of bigInterger) {
200
+ const rounded = Math.round(num / multiple) * multiple;
201
+ if (Math.abs(rounded - num) <= changeLimit) {
202
+ return rounded;
203
+ }
204
+ }
205
+ // If no suitable "nice" integer is found within the 20% range, return the original number
206
+ return num;
207
+ }
208
+
209
+
210
+
211
+ /** Perform the execution logic of the activity. */
212
+ async execute(inputs: PDFMapGeneratorInputs): Promise<PDFMapGeneratorOutputs> {
213
+ const defaulPDF = 'https://cdn.jsdelivr.net/npm/ebta-resources/resources/prints/A3Layout_Landscape_General_Form.pdf'
214
+ const pdfURL = inputs.pdfLink ? inputs.pdfLink : defaulPDF
215
+ const imgBase64Array = inputs.imgBase64;
216
+
217
+ // eslint-disable-next-line @typescript-eslint/unbound-method
218
+ if (!imgBase64Array || imgBase64Array.length === 0 || !imgBase64Array.every(this.isValidImage)) {
219
+ return {
220
+ result: 'Invalid image input',
221
+ };
222
+ }
223
+ try {
224
+ const pdfBuffer = await this.fetchData(pdfURL);
225
+ console.log('PDF fetched successfully');
226
+ const originalPdfDoc = await PDFDocument.load(pdfBuffer);
227
+ const newPdfDoc = await PDFDocument.create();
228
+ const [originalPage] = await newPdfDoc.copyPages(originalPdfDoc, [0]);
229
+ const { width, height } = originalPage.getSize();
230
+ const pixel2MeterOnPaper = 1 / (71.42857) * 0.0254
231
+
232
+ const image1url = 'https://cdn.jsdelivr.net/npm/ebta-resources/resources/prints/caution.png'
233
+ const image2url = 'https://cdn.jsdelivr.net/npm/ebta-resources/resources/prints/warning.png'
234
+ const image3url = 'https://cdn.jsdelivr.net/npm/ebta-resources/resources/prints/dbyd.png'
235
+ const imageBarurl = 'https://cdn.jsdelivr.net/npm/ebta-resources/resources/prints/Scalebar.png'
236
+ const imageNorthUrl = 'https://cdn-icons-png.flaticon.com/512/3236/3236586.png'
237
+ const image1Buffer = await this.fetchData(image1url);
238
+ console.log('Image1 fetched successfully');
239
+ const image2Buffer = await this.fetchData(image2url);
240
+ console.log('Image2 fetched successfully');
241
+ const image3Buffer = await this.fetchData(image3url);
242
+ console.log('Image3 fetched successfully');
243
+ const imageBarBuffer = await this.fetchData(imageBarurl);
244
+ console.log('Image scale bar fetched successfully');
245
+ const imageNorthBuffer = await this.fetchData(imageNorthUrl);
246
+ console.log('Image North Arrow fetched successfully');
247
+ const legendBuffer = this.base64ToArrayBuffer(inputs.LegendImgBase64);
248
+ const filledPDFs: Uint8Array[] = [];
249
+ for (let i = 0; i < imgBase64Array.length; i++) {
250
+ let doc = await PDFDocument.load(pdfBuffer);
251
+ let form = doc.getForm();
252
+ let newPage = doc.getPage(0)
253
+ let imgBase64 = imgBase64Array[i]
254
+ let imageBuffer = this.base64ToArrayBuffer(imgBase64);
255
+ console.log(`Image ${i} data processed successfully`);
256
+ let image;
257
+ if (imgBase64.startsWith('data:image/png;base64,')) {
258
+ image = await doc.embedPng(imageBuffer);
259
+ } else if (imgBase64.startsWith('data:image/jpeg;base64,') || imgBase64.startsWith('data:image/jpg;base64,')) {
260
+ image = await doc.embedJpg(imageBuffer);
261
+ }
262
+ const image1 = await doc.embedPng(image1Buffer);
263
+ const image2 = await doc.embedPng(image2Buffer);
264
+ const image3 = await doc.embedPng(image3Buffer);
265
+ const imageBar = await doc.embedPng(imageBarBuffer);
266
+ const imageNorth = await doc.embedPng(imageNorthBuffer);
267
+ let legendImage
268
+ if (inputs.LegendImgBase64.startsWith('data:image/png;base64,')) {
269
+ legendImage = await doc.embedPng(legendBuffer);
270
+ } else if (inputs.LegendImgBase64.startsWith('data:image/jpeg;base64,') || inputs.LegendImgBase64.startsWith('data:image/jpg;base64,')) {
271
+ legendImage = await doc.embedJpg(legendBuffer);
272
+ }
273
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
274
+ newPage.drawImage(image, {
275
+ x: width * 0.0205,
276
+ y: height * (0.1695),
277
+ width: width * 0.8113,
278
+ height: height * 0.8029,
279
+ });
280
+ //Define position & size of legend screeshot, depent on width/height ratio
281
+ if (legendImage.width / legendImage.height > 0.259) {
282
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
283
+ newPage.drawImage(legendImage, {
284
+ x: width * 0.83796,
285
+ y: height * (0.94611) - width * 0.14 * (legendImage.height / legendImage.width),
286
+ width: width * 0.14,
287
+ height: width * 0.14 * (legendImage.height / legendImage.width),
288
+ });
289
+ }
290
+ else {
291
+ if (legendImage.width / legendImage.height < 0.259 / 1.6) {
292
+ if (legendImage.width / legendImage.height < 0.259 / 3.95) {
293
+ if (legendImage.width / legendImage.height < 0.259 / 5.5) {
294
+ console.log("split legend into 3 columns");
295
+ const fullLegendimg = new Image();
296
+ fullLegendimg.src = inputs.LegendImgBase64;
297
+ fullLegendimg.onload = async () => {
298
+ const neckHeight = Math.floor(legendImage.height * 0.33); // Approximate neck position
299
+ const waistHeight = Math.floor(legendImage.height * 0.66); // Approximate waist position
300
+ // Neck to Top
301
+ const neckUpCanvas = document.createElement('canvas');
302
+ neckUpCanvas.width = legendImage.width;
303
+ neckUpCanvas.height = neckHeight;
304
+ const neckUpCtx = neckUpCanvas.getContext('2d');
305
+ neckUpCtx!.drawImage(fullLegendimg, 0, 0, legendImage.width, neckHeight, 0, 0, legendImage.width, neckHeight);
306
+ // Waist to Neck
307
+ const waistUpCanvas = document.createElement('canvas');
308
+ waistUpCanvas.width = legendImage.width;
309
+ waistUpCanvas.height = waistHeight - neckHeight;
310
+ const waistUpCtx = waistUpCanvas.getContext('2d');
311
+ waistUpCtx!.drawImage(fullLegendimg, 0, neckHeight, legendImage.width, waistHeight - neckHeight, 0, 0, legendImage.width, waistHeight - neckHeight);
312
+ // Bottom to Waist
313
+ const waistDownCanvas = document.createElement('canvas');
314
+ waistDownCanvas.width = legendImage.width;
315
+ waistDownCanvas.height = legendImage.height - waistHeight;
316
+ const waistDownCtx = waistDownCanvas.getContext('2d');
317
+ waistDownCtx!.drawImage(fullLegendimg, 0, waistHeight, legendImage.width, legendImage.height - waistHeight, 0, 0, legendImage.width, legendImage.height - waistHeight);
318
+ const neckUpBase64 = neckUpCanvas.toDataURL('image/jpeg');
319
+ const upBuffer1 = this.base64ToArrayBuffer(neckUpBase64);
320
+ const waistUpBase64 = waistUpCanvas.toDataURL('image/jpeg');
321
+ const upBuffer2 = this.base64ToArrayBuffer(waistUpBase64);
322
+ const waistDownBase64 = waistDownCanvas.toDataURL('image/jpeg');
323
+ const downBuffer = this.base64ToArrayBuffer(waistDownBase64);
324
+ const LegendNeckUp = await doc.embedJpg(upBuffer1);
325
+ const LegendWaistUp = await doc.embedJpg(upBuffer2);
326
+ const LegendWaistDown = await doc.embedJpg(downBuffer);
327
+ newPage.drawImage(LegendNeckUp, {
328
+ x: width * 0.83796,
329
+ y: height * 0.94611 - width * 0.14 * 0.33 * (LegendNeckUp.height / LegendNeckUp.width),
330
+ width: width * 0.14 * 0.33,
331
+ height: width * 0.14 * 0.33 * (LegendNeckUp.height / LegendNeckUp.width),
332
+ });
333
+ newPage.drawImage(LegendWaistUp, {
334
+ x: width * 0.83796 + width * 0.14 * 0.33,
335
+ y: height * 0.94611 - width * 0.14 * 0.33 * (LegendWaistUp.height / LegendWaistUp.width),
336
+ width: width * 0.14 * 0.33,
337
+ height: width * 0.14 * 0.33 * (LegendWaistUp.height / LegendWaistUp.width),
338
+ });
339
+ newPage.drawImage(LegendWaistDown, {
340
+ x: width * 0.83796 + width * 0.14 * .66,
341
+ y: height * 0.94611 - width * 0.14 * 0.33 * (LegendWaistDown.height / LegendWaistDown.width),
342
+ width: width * 0.14 * 0.33,
343
+ height: width * 0.14 * 0.33 * (LegendWaistDown.height / LegendWaistDown.width)
344
+ });
345
+ }
346
+ }
347
+ else {
348
+ //split into 2 columns & strectch
349
+ console.log("split legend into 2 columns & stretch ")
350
+ const fullLegendimg = new Image();
351
+ fullLegendimg.src = inputs.LegendImgBase64
352
+ fullLegendimg.onload = async () => {
353
+ const halfHeight = Math.floor(legendImage.height / 2)
354
+ const waistUpCanvas = document.createElement('canvas');
355
+ waistUpCanvas.width = legendImage.width;
356
+ waistUpCanvas.height = halfHeight;
357
+ const waistUpCtx = waistUpCanvas.getContext('2d');
358
+ waistUpCtx!.drawImage(fullLegendimg, 0, 0, legendImage.width, halfHeight, 0, 0, legendImage.width, halfHeight);
359
+ const waistDownCanvas = document.createElement('canvas');
360
+ waistDownCanvas.width = legendImage.width;
361
+ waistDownCanvas.height = halfHeight;
362
+ const waistDownCtx = waistDownCanvas.getContext('2d');
363
+ waistDownCtx!.drawImage(fullLegendimg, 0, halfHeight, legendImage.width, halfHeight, 0, 0, legendImage.width, halfHeight);
364
+ const waistUpBase64 = waistUpCanvas.toDataURL('image/jpeg');
365
+ const upBuffer = this.base64ToArrayBuffer(waistUpBase64);
366
+ const waistDownBase64 = waistDownCanvas.toDataURL('image/jpeg');
367
+ const downBuffer = this.base64ToArrayBuffer(waistDownBase64);
368
+ const LegendUp = await doc.embedJpg(upBuffer);
369
+ const LegendDown = await doc.embedJpg(downBuffer);
370
+
371
+ newPage.drawImage(LegendUp, {
372
+ x: width * 0.83796,
373
+ y: height * (0.94611 - 0.77),
374
+ width: height * 0.77 * (LegendUp.width / LegendUp.height),
375
+ height: height * 0.77,
376
+ });
377
+ newPage.drawImage(LegendDown, {
378
+ x: width * 0.83796 + height * 0.77 * (LegendDown.width / LegendDown.height),
379
+ y: height * (0.94611 - 0.77),
380
+ width: height * 0.77 * (LegendDown.width / LegendDown.height),
381
+ height: height * 0.77,
382
+ })
383
+ }
384
+ }
385
+ }
386
+ else {
387
+ //split into 2 columns & no strectch
388
+ console.log("split legend into 2 columns & no strectch")
389
+ const fullLegendimg = new Image();
390
+ fullLegendimg.src = inputs.LegendImgBase64
391
+ fullLegendimg.onload = async () => {
392
+ const halfHeight = Math.floor(legendImage.height / 2)
393
+ const waistUpCanvas = document.createElement('canvas');
394
+ waistUpCanvas.width = legendImage.width;
395
+ waistUpCanvas.height = halfHeight;
396
+ const waistUpCtx = waistUpCanvas.getContext('2d');
397
+ waistUpCtx!.drawImage(fullLegendimg, 0, 0, legendImage.width, halfHeight, 0, 0, legendImage.width, halfHeight);
398
+ const waistDownCanvas = document.createElement('canvas');
399
+ waistDownCanvas.width = legendImage.width;
400
+ waistDownCanvas.height = halfHeight;
401
+ const waistDownCtx = waistDownCanvas.getContext('2d');
402
+ waistDownCtx!.drawImage(fullLegendimg, 0, halfHeight, legendImage.width, halfHeight, 0, 0, legendImage.width, halfHeight);
403
+ const waistUpBase64 = waistUpCanvas.toDataURL('image/jpeg');
404
+ const upBuffer = this.base64ToArrayBuffer(waistUpBase64);
405
+ const waistDownBase64 = waistDownCanvas.toDataURL('image/jpeg');
406
+ const downBuffer = this.base64ToArrayBuffer(waistDownBase64);
407
+ const LegendUp = await doc.embedJpg(upBuffer);
408
+ const LegendDown = await doc.embedJpg(downBuffer);
409
+ newPage.drawImage(LegendUp, {
410
+ x: width * 0.83796,
411
+ y: height * (0.94611) - width * 0.14 * 0.5 * (LegendUp.height / LegendUp.width),
412
+ width: width * 0.14 * 0.5,
413
+ height: width * 0.14 * 0.5 * (LegendUp.height / LegendUp.width),
414
+ });
415
+ newPage.drawImage(LegendDown, {
416
+ x: width * 0.83796 + width * 0.14 * 0.505,
417
+ y: height * (0.94611) - width * 0.14 * 0.5 * (LegendDown.height / LegendDown.width),
418
+ width: width * 0.14 * 0.5,
419
+ height: width * 0.14 * 0.5 * (LegendDown.height / LegendDown.width)
420
+ })
421
+ }
422
+ }
423
+ }
424
+ else {
425
+ // A little bit slim, just stretch
426
+ newPage.drawImage(legendImage, {
427
+ x: width * 0.83796,
428
+ y: height * (0.94611 - 0.77),
429
+ width: height * 0.77 * (legendImage.width / legendImage.height),
430
+ height: height * 0.77,
431
+ });
432
+ }
433
+ }
434
+ const lableSize = 100
435
+ newPage.drawImage(image1, {
436
+ x: 28,
437
+ y: 145,
438
+ width: lableSize,
439
+ height: lableSize * (image1.height / image1.width),
440
+ });
441
+ newPage.drawImage(image2, {
442
+ x: 28 + lableSize + 6,
443
+ y: 145,
444
+ width: lableSize,
445
+ height: lableSize * (image2.height / image2.width),
446
+ });
447
+ newPage.drawImage(image3, {
448
+ x: 28 + 2 * lableSize + 6 * 2,
449
+ y: 145,
450
+ width: lableSize * .8,
451
+ height: lableSize * .8 * (image3.height / image3.width),
452
+ });
453
+ console.log(`success in page. ${i} `)
454
+ // north arrow
455
+ const centerX = 40;
456
+ const centerY = 110;
457
+ const halfWidth = 10;
458
+ const halfHeight = 10;
459
+ const angle = inputs.rotation[i]
460
+ const newX = centerX - halfWidth * 1.22 * Math.cos((angle - 35) / 180 * Math.PI);
461
+ const newY = centerY + halfHeight * 1.22 * Math.sin((angle - 35) / 180 * Math.PI);
462
+ newPage.drawImage(imageNorth, {
463
+ x: newX,
464
+ y: newY,
465
+ width: 20,
466
+ height: 20,
467
+ rotate: degrees(-angle),
468
+ });
469
+ // scale bar
470
+ const barScale = 0.5
471
+ const barMaxDistance_nochange = imageBar.width * pixel2MeterOnPaper * inputs.scaleArray[i] * 0.544797456 * barScale
472
+ const niceBar = this.nearestNiceInteger(barMaxDistance_nochange)
473
+ const newBarPixel = imageBar.width * (niceBar / barMaxDistance_nochange) * barScale
474
+ const scalebarX = 59
475
+ newPage.drawImage(imageBar, {
476
+ x: scalebarX,
477
+ y: 93,
478
+ width: newBarPixel,
479
+ height: imageBar.height * barScale,
480
+ });
481
+ // 0
482
+ newPage.drawText('0', {
483
+ x: scalebarX,
484
+ y: 113,
485
+ size: 8
486
+ })
487
+ // half scale bar number
488
+ newPage.drawText(Math.ceil(niceBar / 2).toString(), {
489
+ x: scalebarX + newBarPixel / 2,//dynamic
490
+ y: 113,
491
+ size: 8
492
+ })
493
+ // 100% scale bar number
494
+ newPage.drawText(Math.ceil(niceBar).toString(), {
495
+ x: scalebarX + newBarPixel,//dynamic
496
+ y: 113,
497
+ size: 8
498
+ })
499
+ const font = await doc.embedFont(StandardFonts.Helvetica);
500
+ //title
501
+ const titleField = form.getTextField('Title.' + i)
502
+ titleField.setText(inputs.Title)
503
+ titleField.setAlignment(TextAlignment.Center)
504
+ titleField.updateAppearances(font)
505
+ //printer
506
+ const printerField = form.getTextField('Printer.' + i)
507
+ printerField.setText(inputs.PrintUser)
508
+ printerField.updateAppearances(font)
509
+ //description
510
+ const descrField = form.getTextField('RevDescr.' + i)
511
+ descrField.setText(inputs.Descr)
512
+ descrField.updateAppearances(font)
513
+ //address
514
+ const addrField = form.getTextField('Address.' + i)
515
+ addrField.setText(inputs.addressList[i])
516
+ addrField.updateAppearances(font)
517
+ //scale ratio
518
+ const scaletext = '1:' + Math.ceil(inputs.scaleArray[i] * 0.544797456);
519
+ const scaleNumber = form.getTextField('Scale.' + i)
520
+ scaleNumber.setText(scaletext)
521
+ scaleNumber.enableReadOnly()
522
+ scaleNumber.updateAppearances(font)
523
+ //revision
524
+ const revField = form.getTextField('Rev.' + i)
525
+ revField.setText(inputs.Rev)
526
+ revField.updateAppearances(font)
527
+ //date
528
+ const today = new Date();
529
+ const formattedDate = today.toLocaleDateString('en-AU');
530
+ const dateField1 = form.getTextField('DateA.' + i);
531
+ dateField1.setText(formattedDate);
532
+ dateField1.updateAppearances(font)
533
+ const dateField2 = form.getTextField('DateB.' + i);
534
+ dateField2.setText(formattedDate);
535
+ dateField2.updateAppearances(font)
536
+ //Note, will wrap to new line]
537
+ const noteField = form.getTextField('Note.' + i);
538
+ noteField.setText(inputs.Note);
539
+ noteField.updateAppearances(font)
540
+ console.log('Give values Successful')
541
+ let FieldNames = ['Title', 'Printer', 'RevDescr', 'Address', 'Rev', 'Scale', 'DateA', 'DateB', 'Note']
542
+ //Remove those not used in current page
543
+ for (let k = 0; k < 10; k++) {
544
+ if (k != i) {
545
+ for (let field of FieldNames) {
546
+ let tmp = form.getTextField(field + '.' + k)
547
+ const widget0 = tmp.acroField.getWidgets()[0];
548
+ const AP = widget0.ensureAP();
549
+ AP.set(PDFName.of('N'), PDFRef.of(0, 0));
550
+ form.removeField(tmp);
551
+ }
552
+ }
553
+ }
554
+ console.log('Text Fields removal successful')
555
+ //draw a QR Code image only when Qr URL is given && QR image not given
556
+
557
+ if (inputs.buttonLink && inputs.buttonLink.length>0) {
558
+ const buttonX=width * 0.74
559
+ const buttonY=130
560
+ const buttonW=90
561
+ const buttonH=50
562
+ const buttonBackgroundUrl = 'https://cdn.jsdelivr.net/npm/ebta-resources/resources/prints/gis-button.png'
563
+ const buttonBackgroundBuffer = await this.fetchData(buttonBackgroundUrl);
564
+ const buttonEmbed = await doc.embedPng(buttonBackgroundBuffer);
565
+ newPage.drawImage(buttonEmbed, {
566
+ x: buttonX,
567
+ y: buttonY,
568
+ width: buttonW,
569
+ height: buttonH,
570
+ });
571
+ const boldFont = await doc.embedFont(StandardFonts.HelveticaBold);
572
+ newPage.drawText('Go to EBTA GIS', {
573
+ x: buttonX+5,
574
+ y: buttonY+22,
575
+ size: 10,
576
+ color: rgb(1, 1, 1), // White color
577
+ font: boldFont,
578
+ });
579
+
580
+ const existingAnnots = newPage.node.lookup(PDFName.of('Annots'));
581
+ let annotationsArray = existingAnnots instanceof PDFArray ? existingAnnots : doc.context.obj([]);
582
+
583
+
584
+ const linkAnnotation = doc.context.register(
585
+ doc.context.obj({
586
+ Type: 'Annot',
587
+ Subtype: 'Link',
588
+ Rect: [buttonX, buttonY, buttonX+buttonW,buttonY+buttonH],
589
+ Border: [0, 0, 0],
590
+ A: {
591
+ Type: 'Action',
592
+ S: 'URI',
593
+ URI: PDFString.of((i <= (inputs.buttonLink.length - 1)) ? inputs.buttonLink[i] : inputs.buttonLink[inputs.buttonLink.length - 1]),
594
+ },
595
+ })
596
+ );
597
+ // Append the new annotation to the existing ones
598
+ annotationsArray.push(linkAnnotation);
599
+ // Set the updated annotations array back to the page
600
+ newPage.node.set(PDFName.of('Annots'), annotationsArray);
601
+ }
602
+
603
+ if (inputs.QrUrl && inputs.QrUrl.length > 0 && (inputs.QrCodeImage == undefined)) {
604
+ //QR Code
605
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
606
+ const QRCode = require('qrcode');
607
+ const qrDataUrl = (i <= (inputs.QrUrl.length - 1)) ? await QRCode.toDataURL(inputs.QrUrl[i]) : await QRCode.toDataURL(inputs.QrUrl[inputs.QrUrl.length - 1]);
608
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
609
+ const qrImageBytes = await fetch(qrDataUrl).then(res => res.arrayBuffer());
610
+ const qrImage = await doc.embedPng(qrImageBytes);
611
+ newPage.drawImage(qrImage, {
612
+ x: 210,
613
+ y: 93,
614
+ width: 40,
615
+ height: 40,
616
+ });
617
+
618
+ const existingAnnots = newPage.node.lookup(PDFName.of('Annots'));
619
+ let annotationsArray = existingAnnots instanceof PDFArray ? existingAnnots : doc.context.obj([]);
620
+ const linkAnnotation = doc.context.register(
621
+ doc.context.obj({
622
+ Type: 'Annot',
623
+ Subtype: 'Link',
624
+ Rect: [210, 93, 250, 93+40],
625
+ Border: [0, 0, 0],
626
+ A: {
627
+ Type: 'Action',
628
+ S: 'URI',
629
+ URI: PDFString.of((i <= (inputs.QrUrl.length - 1)) ? inputs.QrUrl[i] : inputs.QrUrl[inputs.QrUrl.length - 1]),
630
+ },
631
+ })
632
+ );
633
+ // Append the new annotation to the existing ones
634
+ annotationsArray.push(linkAnnotation);
635
+ // Set the updated annotations array back to the page
636
+ newPage.node.set(PDFName.of('Annots'), annotationsArray);
637
+ }
638
+
639
+
640
+ if (inputs.QrCodeImage && inputs.QrCodeImage.length > 0) {
641
+ const inputQrImageUrl = (i <= (inputs.QrCodeImage.length - 1)) ? inputs.QrCodeImage[i] : inputs.QrCodeImage[inputs.QrCodeImage.length - 1]
642
+ const inputQrBuffer = await this.fetchData(inputQrImageUrl);
643
+ console.log('input QR Code image fetched successfully');
644
+ let inputQRembed
645
+ console.log(inputQrImageUrl);
646
+ console.log(inputQrImageUrl.toLowerCase().includes(".png"));
647
+ console.log(inputQrImageUrl.toLowerCase().includes(".jpg")||inputQrImageUrl.toLowerCase().includes(".jpeg"));
648
+ if (inputQrImageUrl.toLowerCase().includes(".png")){
649
+ inputQRembed = await doc.embedPng(inputQrBuffer);
650
+ console.log('embed QR Code image as png');
651
+ }
652
+
653
+ else if (inputQrImageUrl.toLowerCase().includes(".jpg")||inputQrImageUrl.toLowerCase().includes(".jpeg")){
654
+ console.log(inputQrBuffer)
655
+ const dataView = new DataView(inputQrBuffer);
656
+ console.log(dataView)
657
+ // Read the first two bytes (SOI marker)
658
+ const soiMarker = dataView.getUint16(0);
659
+ console.log(soiMarker)
660
+ // Check if the SOI marker is correct
661
+ if (soiMarker === 0xFFD8) {
662
+ console.log('SOI marker found: 0xFFD8, keep going');
663
+ inputQRembed = await doc.embedJpg(inputQrBuffer);
664
+ } else {
665
+ console.log('SOI marker not found but forcing to be correct.');
666
+ let fixedArrayBuffer = await this.reencodeImageToArrayBuffer(inputQrBuffer)
667
+ inputQRembed = await doc.embedJpg(fixedArrayBuffer);
668
+ }
669
+ console.log('embed QR Code image as jpg successful');
670
+ }
671
+
672
+
673
+ newPage.drawImage(inputQRembed, {
674
+ x: 210,
675
+ y: 93,
676
+ width: 40,
677
+ height: 40,
678
+ });
679
+ console.log('draw input QR Code image finished');
680
+ }
681
+
682
+
683
+ // field view ID, only happens when has value
684
+ if (inputs.FiewViewId && inputs.FiewViewId.length > 0) {
685
+ const fieldViewIdField = form.createTextField('FieldViewId' + i);
686
+ fieldViewIdField.setText((i <= (inputs.FiewViewId.length - 1)) ? inputs.FiewViewId[i].toString() : inputs.FiewViewId[inputs.FiewViewId.length - 1].toString());
687
+ fieldViewIdField.addToPage(newPage, {
688
+ x: 213,
689
+ y: 78,
690
+ height: 12,
691
+ width: 40,
692
+ borderWidth: 0,
693
+ textColor: rgb(0, 0, 0),
694
+ backgroundColor: rgb(1, 1, 1),
695
+ borderColor: rgb(1, 1, 1),
696
+ });
697
+ console.log('field View ID text added.');
698
+ }
699
+ filledPDFs.push(await doc.save());
700
+
701
+ }
702
+
703
+ const finalPDF = await PDFDocument.create();
704
+
705
+ // Copy pages from all filled PDFs into the final document
706
+ for (const pdfBytes of filledPDFs) {
707
+ const pdf = await PDFDocument.load(pdfBytes);
708
+ const copiedPages = await finalPDF.copyPages(pdf, pdf.getPageIndices());
709
+ copiedPages.forEach(page => finalPDF.addPage(page));
710
+ }
711
+
712
+ const pdfBytes = await finalPDF.save();
713
+ const base64String = this.arrayBufferToBase64(pdfBytes);
714
+ const downloadUrl = this.createDownloadUrl(base64String);
715
+ console.log('pdf file created and ready to download');
716
+ return {
717
+ /**
718
+ * @description The result pdf created for printing.
719
+ */
720
+ result: downloadUrl,
721
+ };
722
+ } catch (error) {
723
+ console.error('Error in PDF Map Generator:', error);
724
+ return {
725
+ result: 'ERROR',
726
+ };
727
+ }
728
+ }
729
+ }