frontbacked-svg 5.0.0
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.
- package/LICENSE +191 -0
- package/README.md +41 -0
- package/dist/main.cjs +3785 -0
- package/dist/main.d.cts +316 -0
- package/dist/main.d.ts +316 -0
- package/dist/main.mjs +3681 -0
- package/package.json +103 -0
- package/src/base64Image.ts +101 -0
- package/src/cssParser.ts +59 -0
- package/src/filters.ts +226 -0
- package/src/font-utils.ts +204 -0
- package/src/get-default-fields-value.ts +64 -0
- package/src/getSvg.ts +284 -0
- package/src/imageHelper.ts +293 -0
- package/src/imagePassportUtils.ts +718 -0
- package/src/images-processor.ts +186 -0
- package/src/main.ts +1354 -0
- package/src/polyfills/DOMParser.ts +28 -0
- package/src/polyfills/File.ts +20 -0
- package/src/polyfills/Image.ts +90 -0
- package/src/svgScaler.ts +179 -0
- package/src/textGenCodeParser.ts +319 -0
- package/src/time.ts +147 -0
- package/src/toolsFunc.ts +79 -0
- package/src/types.ts +177 -0
- package/src/utils.ts +758 -0
- package/src/watermaker.ts +190 -0
package/src/main.ts
ADDED
|
@@ -0,0 +1,1354 @@
|
|
|
1
|
+
|
|
2
|
+
import { INode, parse, stringify } from "svgson"
|
|
3
|
+
|
|
4
|
+
import "./polyfills/Image.ts"
|
|
5
|
+
import "./polyfills/DOMParser.ts"
|
|
6
|
+
import "./polyfills/File.ts"
|
|
7
|
+
import {
|
|
8
|
+
isVoid, nullOrEmpty, base64ToSvg, genId, base64ToFile, orderOptions, orderByName,
|
|
9
|
+
mergeTemplateData, isUploadedFileUrl, isImageDataUrl, getImage, getImageMask, cleanFilename,
|
|
10
|
+
convertPngBase64ImagesToJpeg, convertPngBase64ImagesWithUPNG, buildImageSelectName, buildImageSelectNameReverse,
|
|
11
|
+
breakName, showField, splitSvgElementId, useDataForRandSeed, valueOfParseValue, actionsStorageKey, fileFieldStorageKey,
|
|
12
|
+
fileDocStorageKey, fileCollectionStorageKey, isBrowser, saveFileFieldFile, getFileFieldFile, moveFileFieldFile,
|
|
13
|
+
deleteFileFieldFile, deleteDataWithKeyPrefix, arrayToMap, isTheSameUrl, expandImagesMap, setR2Host,
|
|
14
|
+
FileFieldsFiles
|
|
15
|
+
} from "./utils.ts"
|
|
16
|
+
import {
|
|
17
|
+
fetchImageForBase64, getUrlUpdatesCount,
|
|
18
|
+
removeUpdateQueryString, compareBase64, removeQueryString, incrementUpdateQueryString,
|
|
19
|
+
mergeImages, generateWatermark
|
|
20
|
+
} from "./images-processor.ts"
|
|
21
|
+
import {
|
|
22
|
+
base64ToImage, analyzeImage
|
|
23
|
+
} from "./imagePassportUtils.ts"
|
|
24
|
+
import {
|
|
25
|
+
AnalyzeResult
|
|
26
|
+
} from "./imagePassportUtils.ts"
|
|
27
|
+
import FILTERS from "./filters.ts"
|
|
28
|
+
import { responsiveFontSize, responsivePathD, responsiveTransform, responsiveViewBox } from "./svgScaler.ts"
|
|
29
|
+
import { getResponsiveX, getResponsiveY } from "./toolsFunc.ts"
|
|
30
|
+
import { OBSCURE_PCT, obscureText, watermark } from "./watermaker.ts"
|
|
31
|
+
import { textGenCodeParser } from "./textGenCodeParser.ts"
|
|
32
|
+
import { getIdentifier, parseAndModifyCSS, parseValueUnit } from "./cssParser.ts"
|
|
33
|
+
import {
|
|
34
|
+
getFontFormat, getFontFamiliesFromSVG, generateFontMap, collectFontFamilies,
|
|
35
|
+
extractFontFamiliesFromCSS, getFontId, createFontThumbnail
|
|
36
|
+
} from "./font-utils.ts"
|
|
37
|
+
|
|
38
|
+
import { downloadSvgAsImage } from "./getSvg.ts"
|
|
39
|
+
import getDefaultFieldsValue from "./get-default-fields-value.ts"
|
|
40
|
+
import {
|
|
41
|
+
timestampToDate, dateToTimestamp, joinTimeSegments, secondsToTimeSegments,
|
|
42
|
+
parsePaymentWindow, timestampToGmt, gmtTimestampToLocal
|
|
43
|
+
} from "./time.ts"
|
|
44
|
+
|
|
45
|
+
import {
|
|
46
|
+
getImageColor, getImageDimension, imageToStamp, isBlankImage, cropImage, reduceBase64Image
|
|
47
|
+
} from "./imageHelper.ts"
|
|
48
|
+
|
|
49
|
+
import {
|
|
50
|
+
Doc, FileMap, FileImage, FilterArgs, Filters, Declaration, CssDeclarations,
|
|
51
|
+
CssAction, CssActions, Mask, Filter, TextSelectSettings, MaskMap, MapWithName, FieldsData, Fields, Field,
|
|
52
|
+
Template, TemplateData, Templates, TemplatesResults, Font, FontsMap, ImageUploadMaskInfo
|
|
53
|
+
} from "./types.ts"
|
|
54
|
+
import { getImageDimensions, resizeBase64Image } from "./base64Image.ts"
|
|
55
|
+
/*
|
|
56
|
+
import { writeFileSync } from "fs"
|
|
57
|
+
import path from "path"
|
|
58
|
+
import { cwd } from "process"
|
|
59
|
+
*/
|
|
60
|
+
|
|
61
|
+
export {
|
|
62
|
+
downloadSvgAsImage,
|
|
63
|
+
getDefaultFieldsValue,
|
|
64
|
+
getFontFormat, getFontFamiliesFromSVG, generateFontMap,
|
|
65
|
+
getIdentifier, parseValueUnit, parseAndModifyCSS,
|
|
66
|
+
collectFontFamilies, extractFontFamiliesFromCSS, getFontId, createFontThumbnail,
|
|
67
|
+
timestampToDate, dateToTimestamp, joinTimeSegments, secondsToTimeSegments,
|
|
68
|
+
parsePaymentWindow, timestampToGmt, gmtTimestampToLocal,
|
|
69
|
+
arrayToMap,
|
|
70
|
+
FILTERS
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export {
|
|
74
|
+
fetchImageForBase64, getUrlUpdatesCount,
|
|
75
|
+
removeUpdateQueryString, compareBase64, removeQueryString, incrementUpdateQueryString,
|
|
76
|
+
mergeImages, generateWatermark
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export {
|
|
80
|
+
isVoid, nullOrEmpty, base64ToSvg, genId, base64ToFile, orderOptions, orderByName,
|
|
81
|
+
mergeTemplateData, isUploadedFileUrl, isImageDataUrl, getImage, getImageMask, cleanFilename,
|
|
82
|
+
convertPngBase64ImagesToJpeg, convertPngBase64ImagesWithUPNG, buildImageSelectName, buildImageSelectNameReverse,
|
|
83
|
+
breakName, showField, splitSvgElementId, useDataForRandSeed, valueOfParseValue, actionsStorageKey, fileFieldStorageKey,
|
|
84
|
+
fileDocStorageKey, fileCollectionStorageKey, isBrowser, saveFileFieldFile, getFileFieldFile, moveFileFieldFile,
|
|
85
|
+
deleteFileFieldFile, deleteDataWithKeyPrefix
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export {
|
|
89
|
+
getImageColor, getImageDimension, imageToStamp, isBlankImage, cropImage, reduceBase64Image
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export {
|
|
93
|
+
base64ToImage, analyzeImage, isTheSameUrl, expandImagesMap, setR2Host
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export type {
|
|
97
|
+
Doc, FileMap, FileImage, FilterArgs, Filters, Declaration, CssDeclarations,
|
|
98
|
+
CssAction, CssActions, Mask, Filter, TextSelectSettings, MaskMap, MapWithName, FieldsData, Fields, Field,
|
|
99
|
+
Template, TemplateData, Templates, TemplatesResults, Font, FontsMap, ImageUploadMaskInfo, AnalyzeResult
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const getFieldId = (id: string) => {
|
|
103
|
+
const [ name, ext ] = id.split(".")
|
|
104
|
+
return `${name}.${(ext || "").split("_")[0].trim()}`.trim()
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const applyFiltersRecursively = async (base64Image: string, filters: (Mask | null)[], excludedFilters?: string[]) => {
|
|
108
|
+
////console.log("transformImageByTemplate:0", filters[0])
|
|
109
|
+
const result = !filters[0] || (excludedFilters && excludedFilters.includes(filters[0].filter_id))? base64Image : await FILTERS[filters[0].filter_id].filter(base64Image, filters[0].args)
|
|
110
|
+
filters.shift()
|
|
111
|
+
if(filters.length == 0) {
|
|
112
|
+
return result;
|
|
113
|
+
|
|
114
|
+
} else {
|
|
115
|
+
return applyFiltersRecursively(result, filters, excludedFilters)
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const applyFilters = (base64Image: string, filters: (Mask | null)[], excludedFilters?: string[]): Promise<string> => {
|
|
120
|
+
return new Promise(async (resolve, reject) => {
|
|
121
|
+
try {
|
|
122
|
+
|
|
123
|
+
//console.log("applyFilters.1", base64Image.substring(0, 10))
|
|
124
|
+
const base64 = await applyFiltersRecursively(base64Image, filters, excludedFilters)
|
|
125
|
+
|
|
126
|
+
//console.log("applyFilters.2", base64.substring(0, 10))
|
|
127
|
+
resolve(base64)
|
|
128
|
+
|
|
129
|
+
} catch(e: any) {
|
|
130
|
+
//console.log("applyFilters.error", e?.message)
|
|
131
|
+
reject(e)
|
|
132
|
+
}
|
|
133
|
+
})
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const imagesToBase64 = async (
|
|
137
|
+
storageHost: string,
|
|
138
|
+
images: FileMap,
|
|
139
|
+
data: FieldsData,
|
|
140
|
+
templateData: TemplateData,
|
|
141
|
+
deviceWidth?: number | null,
|
|
142
|
+
maxRetries: number = 5,
|
|
143
|
+
fileFieldTypesDefaultFiles?: FileFieldsFiles
|
|
144
|
+
): Promise<FileMap> => {
|
|
145
|
+
const entries = Object.entries(images);
|
|
146
|
+
const successful: FileMap = {};
|
|
147
|
+
let remaining = entries;
|
|
148
|
+
|
|
149
|
+
for (let attempt = 1; attempt <= maxRetries && remaining.length > 0; attempt++) {
|
|
150
|
+
console.log(`🌀 Attempt ${attempt} - ${remaining.length} image(s) remaining`);
|
|
151
|
+
|
|
152
|
+
const failed: typeof remaining = [];
|
|
153
|
+
|
|
154
|
+
const promises = remaining.map(async ([id, url]) => {
|
|
155
|
+
try {
|
|
156
|
+
const fieldId = getFieldId(id);
|
|
157
|
+
let base64: string | null = null;
|
|
158
|
+
|
|
159
|
+
if (["image_upload", "faceshot", "sign"].includes(templateData.fields[fieldId]?.type)) {
|
|
160
|
+
const img = data[fieldId] && isImageDataUrl(data[fieldId])
|
|
161
|
+
? data[fieldId]
|
|
162
|
+
: getFileFieldFile("other_tools_data", data.id, fieldId, fileFieldTypesDefaultFiles);
|
|
163
|
+
base64 = img;
|
|
164
|
+
} else if (!id.endsWith("_thumbnail")) {
|
|
165
|
+
const base64Url = getImage(id, images, deviceWidth ? `_${deviceWidth}` : null);
|
|
166
|
+
if (base64Url) {
|
|
167
|
+
base64 = await fetchImageForBase64(storageHost, base64Url);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if(base64 && isImageDataUrl(base64) && base64.startsWith('data:img/')) {
|
|
172
|
+
base64 = `data:image/${base64.substring('data:img/'.length)}`
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (base64 && isImageDataUrl(base64) && templateData.masks) {
|
|
176
|
+
const maskId = templateData.fields[fieldId]?.type === "image_select" ? fieldId : id;
|
|
177
|
+
const mask = templateData.masks[maskId];
|
|
178
|
+
if (mask) {
|
|
179
|
+
const filters = Object.values(mask);
|
|
180
|
+
if (["sign", "image_upload"].includes(templateData.fields[fieldId]?.type)) {
|
|
181
|
+
base64 = await applyFilters(base64, filters, ["ImageTransform"]);
|
|
182
|
+
} else {
|
|
183
|
+
base64 = await applyFilters(base64, filters);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
successful[id] = base64 || "";
|
|
189
|
+
return;
|
|
190
|
+
} catch (err: any) {
|
|
191
|
+
console.warn(`⚠️ Failed to fetch image for "${id}" on attempt ${attempt}:`, err.message);
|
|
192
|
+
failed.push([id, url]);
|
|
193
|
+
}
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
await Promise.all(promises);
|
|
197
|
+
remaining = failed;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
if (remaining.length > 0) {
|
|
201
|
+
throw new Error(`❌ Failed to fetch ${remaining.length} image(s) after ${maxRetries} attempts.`);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
return successful;
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
export function rotateTransformValue(degree: number, x: number, y: number, width: number, height: number) {
|
|
208
|
+
const cx = x + width / 2;
|
|
209
|
+
const cy = y + height / 2;
|
|
210
|
+
return `rotate(${degree} ${cx} ${cy})`;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const FRACTION_DIGITS = 3
|
|
214
|
+
const scaleSvg = (
|
|
215
|
+
child: INode,
|
|
216
|
+
preferedWidth: number,
|
|
217
|
+
preferedHeight: number,
|
|
218
|
+
templateWidth: number,
|
|
219
|
+
templateHeight: number,
|
|
220
|
+
textContent?: string | null,
|
|
221
|
+
maxTextBeforeScaleDown?: number | number
|
|
222
|
+
) => {
|
|
223
|
+
|
|
224
|
+
if(child.attributes.width) {
|
|
225
|
+
child.attributes.width = `${getResponsiveX(Number(child.attributes.width), preferedWidth, templateWidth, FRACTION_DIGITS)}`
|
|
226
|
+
}
|
|
227
|
+
if(child.attributes.height) {
|
|
228
|
+
child.attributes.height = `${getResponsiveY(Number(child.attributes.height), preferedHeight, templateHeight, FRACTION_DIGITS)}`
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
if(child.attributes.x) {
|
|
232
|
+
child.attributes.x = `${getResponsiveX(Number(child.attributes.x), preferedWidth, templateWidth, FRACTION_DIGITS)}`
|
|
233
|
+
}
|
|
234
|
+
if(child.attributes.y) {
|
|
235
|
+
child.attributes.y = `${getResponsiveY(Number(child.attributes.y), preferedHeight, templateHeight, FRACTION_DIGITS)}`
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
if(child.attributes.transform) {
|
|
239
|
+
child.attributes.transform = responsiveTransform(
|
|
240
|
+
child.attributes.transform,
|
|
241
|
+
(v) => getResponsiveX(v, preferedWidth, templateWidth, FRACTION_DIGITS),
|
|
242
|
+
(v) => getResponsiveY(v, preferedHeight, templateHeight, FRACTION_DIGITS),
|
|
243
|
+
textContent, maxTextBeforeScaleDown
|
|
244
|
+
)
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
if(child.name == "path" && child.attributes.d) {
|
|
248
|
+
child.attributes.d = responsivePathD(
|
|
249
|
+
child.attributes.d,
|
|
250
|
+
(v) => getResponsiveX(v, preferedWidth, templateWidth, FRACTION_DIGITS),
|
|
251
|
+
(v) => getResponsiveY(v, preferedHeight, templateHeight, FRACTION_DIGITS)
|
|
252
|
+
)
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
if(child.attributes.dy) {
|
|
256
|
+
child.attributes.dy = `${getResponsiveY(Number(child.attributes.dy), preferedHeight, templateHeight, FRACTION_DIGITS)}`
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
if(child.children.length > 0) {
|
|
260
|
+
for(var i = 0; i < child.children.length; i++) {
|
|
261
|
+
scaleSvg(child.children[i], preferedWidth, preferedHeight, templateWidth, templateHeight, textContent, maxTextBeforeScaleDown)
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
export const getTextAreaMaxLength = (
|
|
267
|
+
textAreaNode: INode,
|
|
268
|
+
textInput?: string,
|
|
269
|
+
tag?: string,
|
|
270
|
+
maxCharsPerLine?: number,
|
|
271
|
+
showWatermark?: boolean | null
|
|
272
|
+
): {
|
|
273
|
+
maxLines: number,
|
|
274
|
+
defaultInputLines: string[]
|
|
275
|
+
} => {
|
|
276
|
+
let maxLines = 0;
|
|
277
|
+
const textInputLines = textInput ? textInput.split("\n") : [];
|
|
278
|
+
const totalTextInputLines = textInputLines.length;
|
|
279
|
+
const defaultInputLines = [];
|
|
280
|
+
|
|
281
|
+
const breakLineSafely = (line: string, maxLength: number): string[] => {
|
|
282
|
+
const result = [];
|
|
283
|
+
let currentLine = "";
|
|
284
|
+
|
|
285
|
+
for (const word of line.split(" ")) {
|
|
286
|
+
if ((currentLine + word).length > maxLength) {
|
|
287
|
+
if (currentLine.length > 0) {
|
|
288
|
+
result.push(currentLine.trim());
|
|
289
|
+
}
|
|
290
|
+
currentLine = word + " ";
|
|
291
|
+
} else {
|
|
292
|
+
currentLine += word + " ";
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
if (currentLine.trim().length > 0) {
|
|
297
|
+
result.push(currentLine.trim());
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
return result;
|
|
301
|
+
};
|
|
302
|
+
|
|
303
|
+
for (let i = 0; i < textAreaNode.children.length; i++) {
|
|
304
|
+
if (textAreaNode.children[i].value.length > 0) {
|
|
305
|
+
maxLines++;
|
|
306
|
+
defaultInputLines.push(textAreaNode.children[i].value);
|
|
307
|
+
|
|
308
|
+
if (textInputLines.length > 0) {
|
|
309
|
+
let modifiedText = textInputLines[0];
|
|
310
|
+
|
|
311
|
+
if (maxCharsPerLine && modifiedText.length > maxCharsPerLine) {
|
|
312
|
+
const brokenLines = breakLineSafely(modifiedText, maxCharsPerLine);
|
|
313
|
+
modifiedText = brokenLines.shift() || "";
|
|
314
|
+
textInputLines[0] = brokenLines.join("\n");
|
|
315
|
+
} else {
|
|
316
|
+
textInputLines.shift();
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// Apply text obfuscation before setting the value
|
|
320
|
+
if(showWatermark) {
|
|
321
|
+
textAreaNode.children[i].value = obscureText(modifiedText, OBSCURE_PCT);
|
|
322
|
+
|
|
323
|
+
} else {
|
|
324
|
+
textAreaNode.children[i].value = modifiedText
|
|
325
|
+
}
|
|
326
|
+
} else if (totalTextInputLines > 0) {
|
|
327
|
+
textAreaNode.children[i].value = "";
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
if (textAreaNode.children[i].children.length > 0) {
|
|
332
|
+
for (let j = 0; j < textAreaNode.children[i].children.length; j++) {
|
|
333
|
+
if (textAreaNode.children[i].children[j].value.length > 0) {
|
|
334
|
+
maxLines++;
|
|
335
|
+
defaultInputLines.push(textAreaNode.children[i].children[j].value);
|
|
336
|
+
|
|
337
|
+
if (textInputLines.length > 0) {
|
|
338
|
+
let modifiedText = textInputLines[0];
|
|
339
|
+
|
|
340
|
+
if (maxCharsPerLine && modifiedText.length > maxCharsPerLine) {
|
|
341
|
+
const brokenLines = breakLineSafely(modifiedText, maxCharsPerLine);
|
|
342
|
+
modifiedText = brokenLines.shift() || "";
|
|
343
|
+
textInputLines[0] = brokenLines.join("\n");
|
|
344
|
+
} else {
|
|
345
|
+
textInputLines.shift();
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// Apply text obfuscation before setting the value
|
|
349
|
+
if(showWatermark) {
|
|
350
|
+
textAreaNode.children[i].children[j].value = obscureText(modifiedText, OBSCURE_PCT);
|
|
351
|
+
|
|
352
|
+
} else {
|
|
353
|
+
textAreaNode.children[i].children[j].value = modifiedText
|
|
354
|
+
}
|
|
355
|
+
} else if (totalTextInputLines > 0) {
|
|
356
|
+
textAreaNode.children[i].children[j].value = "";
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
return {
|
|
364
|
+
maxLines,
|
|
365
|
+
defaultInputLines
|
|
366
|
+
};
|
|
367
|
+
};
|
|
368
|
+
|
|
369
|
+
const mutateParsedSvg = (
|
|
370
|
+
fields: Fields,
|
|
371
|
+
parsedSvg: INode, id: string, childIndex: number, field: Field,
|
|
372
|
+
images: FileMap, data: FieldsData,
|
|
373
|
+
templateWidth?: number | null, templateHeight?: number | null, width?: number | null,
|
|
374
|
+
showWatermark?: boolean | null,
|
|
375
|
+
mask?: Filter | null
|
|
376
|
+
): void => {
|
|
377
|
+
|
|
378
|
+
if(!showField(field, fields, data)) {
|
|
379
|
+
parsedSvg.children[childIndex].attributes["class"] = "hide"
|
|
380
|
+
return
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
if(parsedSvg.children[childIndex].name == "image" && !field?.type) {
|
|
384
|
+
|
|
385
|
+
|
|
386
|
+
////console.log("parsedSvg.mutateParsedSvg ", width, templateWidth, templateHeight, " _SUFFIX_ ", width? `_${width}` : null)
|
|
387
|
+
|
|
388
|
+
const img = getImage(id, images, width? `_${width}` : null)
|
|
389
|
+
parsedSvg.children[childIndex].attributes["xlink:href"] = img
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
|
|
393
|
+
const addFilter = () => {
|
|
394
|
+
////console.log("addFilter:", mask, mask?.ImageTransform?.filter_id, mask?.ImageTransform?.args)
|
|
395
|
+
if(mask && mask?.ImageTransform?.filter_id && mask?.ImageTransform?.args) {
|
|
396
|
+
const node = parsedSvg.children[childIndex]
|
|
397
|
+
const x = parseFloat(node.attributes["x"]);
|
|
398
|
+
const y = parseFloat(node.attributes["y"]);
|
|
399
|
+
const width = parseFloat(node.attributes["width"]);
|
|
400
|
+
const height = parseFloat(node.attributes["height"]);
|
|
401
|
+
|
|
402
|
+
parsedSvg.children[childIndex].attributes['transform'] = rotateTransformValue(
|
|
403
|
+
mask?.ImageTransform.args?.rotationAngle,
|
|
404
|
+
x, y, width, height
|
|
405
|
+
)
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
|
|
410
|
+
if(parsedSvg.children[childIndex].name != "defs") {
|
|
411
|
+
parsedSvg.children[childIndex].attributes["data-svg-id"] = parsedSvg.children[childIndex].attributes["id"]
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
if(field?.type == "image_select") {
|
|
415
|
+
if(data[field.id]) {
|
|
416
|
+
const imageIndex = data[field.id]
|
|
417
|
+
const img = getImage(imageIndex, images, width? `_${width}` : null)
|
|
418
|
+
parsedSvg.children[childIndex].attributes["xlink:href"] = img
|
|
419
|
+
//console.log("parsedSvg.image_select ", field.type, width, templateWidth, templateHeight, " _WITH_SUFFIX_ ", width? `${id}_${width}` : null, !img? "no_image" : img.substring(0, 10))
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
} else if(["image_upload", "sign", "faceshot"].includes(field?.type)) {
|
|
423
|
+
if(images[id]) {
|
|
424
|
+
parsedSvg.children[childIndex].attributes["xlink:href"] = images[id]
|
|
425
|
+
if(field?.type == "sign") {
|
|
426
|
+
//addFilter()
|
|
427
|
+
////console.log("parsedSvg.image_upload/sign", id, mask?.ImageTransform)
|
|
428
|
+
|
|
429
|
+
} else if(field?.type == "faceshot") {
|
|
430
|
+
////console.log("parsedSvg.image_upload/faceshot", id, mask?.ImageTransform)
|
|
431
|
+
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
} else if(field?.type == "checkbox") {
|
|
436
|
+
if(images[id] && data[field.id]) {
|
|
437
|
+
if(parsedSvg.children[childIndex].name == "image") {
|
|
438
|
+
parsedSvg.children[childIndex].attributes["xlink:href"] = getImage(id, images, width? `_${width}` : null)
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
} else if(field?.type == "qrcode") {
|
|
443
|
+
if(images[id]) {
|
|
444
|
+
if(parsedSvg.children[childIndex].name == "image") {
|
|
445
|
+
parsedSvg.children[childIndex].attributes["xlink:href"] = getImage(id, images, width? `_${width}` : null)
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
} else if(["text", "defgen"].includes(field?.type)) {
|
|
450
|
+
var child = parsedSvg.children[childIndex].children[0]
|
|
451
|
+
var prevChild = null
|
|
452
|
+
while (child.children && child.children.length > 0) {
|
|
453
|
+
prevChild = child
|
|
454
|
+
child = child.children[0]
|
|
455
|
+
}
|
|
456
|
+
//child.value = data[field.id] || `No ${field.name} entered`
|
|
457
|
+
if(data[field.id]) {
|
|
458
|
+
if(showWatermark) {
|
|
459
|
+
child.value = obscureText(data[field.id], OBSCURE_PCT);//escapeHtmlEntities(data[field.id])//
|
|
460
|
+
|
|
461
|
+
} else {
|
|
462
|
+
child.value = data[field.id];//escapeHtmlEntities(data[field.id])//
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
} else if(["textarea"].includes(field?.type)) {
|
|
467
|
+
getTextAreaMaxLength(parsedSvg.children[childIndex], data[field.id], field.id, field?.maxCharsPerLine, showWatermark)
|
|
468
|
+
|
|
469
|
+
} else if(["gen", "date"].includes(field?.type)) {
|
|
470
|
+
var child = parsedSvg.children[childIndex].children[0]
|
|
471
|
+
var prevChild = null
|
|
472
|
+
while (child.children && child.children.length > 0) {
|
|
473
|
+
prevChild = child
|
|
474
|
+
child = child.children[0]
|
|
475
|
+
}
|
|
476
|
+
////console.log("textGenCodeParser.1", data, field)
|
|
477
|
+
child.value = textGenCodeParser(field.code, data, useDataForRandSeed, (dataKey, dataAsKey) => {
|
|
478
|
+
////console.log("textGenCodeParser.2", data?.id, dataKey)
|
|
479
|
+
if(fields[dataKey]?.type == "text_select") {
|
|
480
|
+
return ((fields[dataKey].selections || {})[dataAsKey]?.value || "").trim()
|
|
481
|
+
}
|
|
482
|
+
return dataAsKey
|
|
483
|
+
})
|
|
484
|
+
|
|
485
|
+
} else if(["text_select"].includes(field?.type)) {
|
|
486
|
+
var child = parsedSvg.children[childIndex].children[0]
|
|
487
|
+
var prevChild = null
|
|
488
|
+
while (child.children && child.children.length > 0) {
|
|
489
|
+
prevChild = child
|
|
490
|
+
child = child.children[0]
|
|
491
|
+
}
|
|
492
|
+
if(data[field.id]) {
|
|
493
|
+
child.value = ((field.selections || {})[data[field.id]]?.value || "").trim()
|
|
494
|
+
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
const CUSTOMER_CSS_VALUE_PROCESSORS: { [x: string]: (value: string) => string } = {
|
|
501
|
+
"letter-spacing": (propertyValue: string) => {
|
|
502
|
+
const { value, unit } = parseValueUnit(propertyValue)
|
|
503
|
+
let updatedValue;
|
|
504
|
+
if(unit == "psd") {
|
|
505
|
+
//Convert psd value to em
|
|
506
|
+
updatedValue = `${parseFloat(value) / 1000}em`
|
|
507
|
+
|
|
508
|
+
} else {
|
|
509
|
+
updatedValue = propertyValue
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
return updatedValue
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
const updateCustomCssValues = (cssAction: CssAction) => {
|
|
516
|
+
const updatedCssAction = { ...cssAction }
|
|
517
|
+
for(const [key, value] of Object.entries(cssAction)) {
|
|
518
|
+
const updatedDeclarations = [ ...value.declarations ]
|
|
519
|
+
for(var i = 0; i < updatedDeclarations.length; i++) {
|
|
520
|
+
const updatedValueFunc = CUSTOMER_CSS_VALUE_PROCESSORS[value.declarations[i].property]
|
|
521
|
+
if(updatedValueFunc) {
|
|
522
|
+
updatedDeclarations[i] = {
|
|
523
|
+
...updatedDeclarations[i],
|
|
524
|
+
value: updatedValueFunc(value.declarations[i].value)
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
updatedCssAction[key].declarations = updatedDeclarations
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
return updatedCssAction;
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
const updateCustomCssPropertiesValuesToCssValues = (cssActions?: CssActions | null) => {
|
|
535
|
+
if(!cssActions) return null
|
|
536
|
+
const updatedCssActions = { ...cssActions }
|
|
537
|
+
if(updatedCssActions.if_selector) {
|
|
538
|
+
updatedCssActions.if_selector = updateCustomCssValues(updatedCssActions.if_selector)
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
if(updatedCssActions.if_property) {
|
|
542
|
+
updatedCssActions.if_property = updateCustomCssValues(updatedCssActions.if_property)
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
if(updatedCssActions.if_property_and_value) {
|
|
546
|
+
updatedCssActions.if_property_and_value = updateCustomCssValues(updatedCssActions.if_property_and_value)
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
return updatedCssActions
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
function getOcclusionId(a: Record<string, string>, b: Record<string, string>, tag?: string): string | null {
|
|
553
|
+
// Calculate the boundaries of both objects
|
|
554
|
+
const aLeft = Number(a.x);
|
|
555
|
+
const aRight = aLeft + Number(a.width);
|
|
556
|
+
const aTop = Number(a.y);
|
|
557
|
+
const aBottom = aTop + Number(a.height);
|
|
558
|
+
|
|
559
|
+
const pct = 5;
|
|
560
|
+
|
|
561
|
+
var bLeft = Number(b.x);
|
|
562
|
+
var bRight = bLeft + Number(b.width);
|
|
563
|
+
var bTop = Number(b.y);
|
|
564
|
+
var bBottom = bTop + Number(b.height);
|
|
565
|
+
|
|
566
|
+
var bLeftPct = (bLeft * pct) / 100;
|
|
567
|
+
var bRightPct = (bRight * pct) / 100;
|
|
568
|
+
var bTopPct = (bTop * pct) / 100;
|
|
569
|
+
var bBottomPct = (bBottom * pct) / 100;
|
|
570
|
+
|
|
571
|
+
// Check if object 'b' is completely behind object 'a'
|
|
572
|
+
const isOccluded = (
|
|
573
|
+
bLeft + bLeftPct >= aLeft && bRight - bRightPct <= aRight && // 'b' horizontally inside 'a'
|
|
574
|
+
bTop + bTopPct >= aTop && bBottom - bBottomPct <= aBottom // 'b' vertically inside 'a'
|
|
575
|
+
);
|
|
576
|
+
|
|
577
|
+
/*
|
|
578
|
+
//console.log("getOcclusionId5:", tag, `
|
|
579
|
+
aLeft: ${aLeft} | aRight: ${aRight} | aTop: ${aTop} | aBottom: ${aBottom} |
|
|
580
|
+
bLeft: ${bLeft} | bRight: ${bRight} | bTop: ${bTop} | bBottom: ${bBottom} |
|
|
581
|
+
`)*/
|
|
582
|
+
|
|
583
|
+
// If 'b' is occluded by 'a', return the id of 'b'. Otherwise, return false.
|
|
584
|
+
return isOccluded ? b.id : null;
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
const calculateElementId = (element: INode, elementIndex: number, allElements: INode[]): string[] => {
|
|
588
|
+
const occlusionIdList = [element.attributes.id]
|
|
589
|
+
for(var i = allElements.length - 1; i >= 0; i--) {
|
|
590
|
+
if(elementIndex != i && allElements[i].name == "image" && allElements[i].attributes) {
|
|
591
|
+
const occlusionId = getOcclusionId(element.attributes, allElements[i].attributes, `${element.attributes.id}:${allElements[i].attributes.id}`)
|
|
592
|
+
if(occlusionId) occlusionIdList.push(occlusionId)
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
return occlusionIdList
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
export const getSvg = (
|
|
599
|
+
storageHost: string,
|
|
600
|
+
data: FieldsData, templateData: TemplateData, fonts?: FontsMap | null, showWatermark?: boolean | null,
|
|
601
|
+
width?: number | "max" | null,
|
|
602
|
+
tempMask?: MaskMap | null,
|
|
603
|
+
fileFieldTypesDefaultFiles?: FileFieldsFiles
|
|
604
|
+
): Promise<string> => {
|
|
605
|
+
////console.log("formatDate:3.w", width)
|
|
606
|
+
|
|
607
|
+
return new Promise(async (resolve, reject) => {
|
|
608
|
+
|
|
609
|
+
parse(templateData.svg)
|
|
610
|
+
.then(async parsedSvg => {
|
|
611
|
+
const fieldsKeys = Object.keys(templateData.fields)
|
|
612
|
+
const templateWidth = Number(`${parsedSvg.attributes.width || 0}`)
|
|
613
|
+
const templateHeight = Number(`${parsedSvg.attributes.height || 0}`)
|
|
614
|
+
|
|
615
|
+
var preferedWidth = !width || width == "max"? templateWidth : width
|
|
616
|
+
//if(preferedWidth && preferedWidth > 728 && !isHighQuality) preferedWidth = 728
|
|
617
|
+
|
|
618
|
+
const preferedHeight = ((preferedWidth / templateWidth) * templateHeight)
|
|
619
|
+
let images = await imagesToBase64(storageHost, templateData.images, data, templateData, preferedWidth, 5, fileFieldTypesDefaultFiles)
|
|
620
|
+
//writeFileSync(path.join(cwd(), "./images.json"), JSON.stringify(images, null, "\t"))
|
|
621
|
+
//console.log("parsedSvg.images", images)
|
|
622
|
+
//resolve(stringify(parsedSvg))/*
|
|
623
|
+
|
|
624
|
+
let maskImageWidth = 0, maskImageHeight = 0, maskImageX = 0, maskImageY = 0;
|
|
625
|
+
var defIndex = -1
|
|
626
|
+
|
|
627
|
+
//Scale down the SVG
|
|
628
|
+
parsedSvg.attributes.width = `${preferedWidth}`
|
|
629
|
+
parsedSvg.attributes.height = `${preferedHeight}`
|
|
630
|
+
|
|
631
|
+
if(parsedSvg.attributes.viewBox) {
|
|
632
|
+
parsedSvg.attributes.viewBox = responsiveViewBox(
|
|
633
|
+
parsedSvg.attributes.viewBox,
|
|
634
|
+
(v) => getResponsiveX(v, preferedWidth, templateWidth, FRACTION_DIGITS),
|
|
635
|
+
(v) => getResponsiveY(v, preferedHeight, templateHeight, FRACTION_DIGITS)
|
|
636
|
+
)
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
for(var i = parsedSvg.children.length - 1; i >= 0; i--) {
|
|
640
|
+
const child = parsedSvg.children[i]
|
|
641
|
+
|
|
642
|
+
const { name, type, selectIndex, typeAndSelectIndex } = splitSvgElementId(child.attributes.id)
|
|
643
|
+
const key = `${name}.${type}`
|
|
644
|
+
|
|
645
|
+
//Scale down the SVG child
|
|
646
|
+
const field = templateData.fields[key]
|
|
647
|
+
if(child.attributes.id == "Company_Name.text") {
|
|
648
|
+
////console.log("valueArray:1", key, child.attributes.id, field?.id, field, data[field?.id || ""], data)
|
|
649
|
+
}
|
|
650
|
+
scaleSvg(child, preferedWidth, preferedHeight, templateWidth, templateHeight, data[field?.id || ""], field?.maxTextBeforeScaleDown)
|
|
651
|
+
|
|
652
|
+
//console.info("parsedSvg.otherz.0", key, child.attributes.x, child.attributes.y)
|
|
653
|
+
if(child.name == "defs") {
|
|
654
|
+
defIndex = i
|
|
655
|
+
|
|
656
|
+
} else if(fieldsKeys.includes(key) || child.name == "image") {
|
|
657
|
+
|
|
658
|
+
var fieldId = getFieldId(child.attributes.id)//id.split("_")[0]
|
|
659
|
+
const imageId = type == "image_select"? fieldId : child.attributes.id
|
|
660
|
+
|
|
661
|
+
mutateParsedSvg(templateData.fields,
|
|
662
|
+
parsedSvg, child.attributes.id, i, templateData.fields[key], images, data,
|
|
663
|
+
templateWidth, templateHeight, preferedWidth, showWatermark,
|
|
664
|
+
(tempMask || {})[imageId] || (templateData?.masks || {})[imageId]
|
|
665
|
+
)
|
|
666
|
+
|
|
667
|
+
////console.log("parsedSvg.otherz!!", imageId, tempMask, (tempMask || {})[imageId], ((tempMask || {})[imageId] || {}).ImageTransform)
|
|
668
|
+
if(((tempMask || {})[imageId] || {}).ImageTransform) {
|
|
669
|
+
////console.log("parsedSvg.otherz!", key, child.attributes.x, child.attributes.y)
|
|
670
|
+
maskImageWidth = parseFloat(child.attributes.width)
|
|
671
|
+
maskImageHeight = parseFloat(child.attributes.height)
|
|
672
|
+
maskImageX = parseFloat(child.attributes.x)
|
|
673
|
+
maskImageY = parseFloat(child.attributes.y)
|
|
674
|
+
////console.log("parsedSvg.otherz!.2", key, maskImageX, maskImageY, tempMask)
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
var calulatedIdList: string[] = []
|
|
680
|
+
for(var i = parsedSvg.children.length - 1; i >= 0; i--) {
|
|
681
|
+
const child = parsedSvg.children[i]
|
|
682
|
+
if(child.name == "image") {
|
|
683
|
+
// Add a custom data attribute to identify the clickable element
|
|
684
|
+
const calculatedId = calculateElementId(child, i, parsedSvg.children).filter(id => !calulatedIdList.includes(id));
|
|
685
|
+
calulatedIdList = calulatedIdList.concat(calculatedId)
|
|
686
|
+
parsedSvg.children[i].attributes['data-click-element-id'] =
|
|
687
|
+
calculatedId.length == 0?
|
|
688
|
+
parsedSvg.children[i].attributes.id : calculatedId.join();
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
//Add fonts and widgets styles
|
|
693
|
+
if(defIndex == -1) {
|
|
694
|
+
//defIndex = 0
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
if(defIndex > -1) {
|
|
698
|
+
const el = parsedSvg.children[defIndex]
|
|
699
|
+
////console.log("parsedSvg:defs", el)
|
|
700
|
+
// Check for <defs> and <style> elements
|
|
701
|
+
if (el.children) {
|
|
702
|
+
el.children.forEach(child => {
|
|
703
|
+
if (child.name === 'style' && child.children && child.children.length > 0) {
|
|
704
|
+
const cssContent = child.children[0].value;
|
|
705
|
+
|
|
706
|
+
const updatedCssContent = parseAndModifyCSS(cssContent, (selector, declarations) => {
|
|
707
|
+
|
|
708
|
+
const css = updateCustomCssPropertiesValuesToCssValues(templateData.cssActions)
|
|
709
|
+
const ifSelector = css?.if_selector || {}
|
|
710
|
+
const ifProperty = css?.if_property || {}
|
|
711
|
+
const ifpropertyAndValue = css?.if_property_and_value || {}
|
|
712
|
+
|
|
713
|
+
var updatedDeclarations = [ ...declarations ]
|
|
714
|
+
|
|
715
|
+
//Update the selector actions first, then the property action, then the property and value.
|
|
716
|
+
//This will allow property and value action to have precedence over the property action, and the
|
|
717
|
+
//property action over the selector action
|
|
718
|
+
|
|
719
|
+
const ifSelectorActionAll = ifSelector["*"]
|
|
720
|
+
const ifSelectorAction = ifSelector[getIdentifier(selector)]
|
|
721
|
+
|
|
722
|
+
if( ifSelectorActionAll ) {
|
|
723
|
+
if(ifSelectorActionAll.shouldReplace) {
|
|
724
|
+
updatedDeclarations = ifSelectorActionAll.declarations
|
|
725
|
+
|
|
726
|
+
} else {
|
|
727
|
+
updatedDeclarations = [ ...updatedDeclarations, ...ifSelectorActionAll.declarations ]
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
if( ifSelectorAction ) {
|
|
732
|
+
if(ifSelectorAction.shouldReplace) {
|
|
733
|
+
updatedDeclarations = ifSelectorAction.declarations
|
|
734
|
+
|
|
735
|
+
} else {
|
|
736
|
+
updatedDeclarations = [ ...updatedDeclarations, ...ifSelectorAction.declarations ]
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
for(var i = 0; i < declarations.length; i++) {
|
|
741
|
+
const decl = declarations[i]
|
|
742
|
+
const ifPropertyActionAll = ifProperty["*"]
|
|
743
|
+
const ifPropertyAction = ifProperty[getIdentifier(decl.property)]
|
|
744
|
+
|
|
745
|
+
const ifPropertyAndValueActionAll = ifpropertyAndValue["*"]
|
|
746
|
+
const ifPropertyAndValueAction = ifpropertyAndValue[getIdentifier(decl.property, decl.value)]
|
|
747
|
+
|
|
748
|
+
if( ifPropertyActionAll ) {
|
|
749
|
+
if(ifPropertyActionAll.shouldReplace) {
|
|
750
|
+
updatedDeclarations = ifPropertyActionAll.declarations
|
|
751
|
+
|
|
752
|
+
} else {
|
|
753
|
+
updatedDeclarations = [ ...updatedDeclarations, ...ifPropertyActionAll.declarations ]
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
if( ifPropertyAction ) {
|
|
758
|
+
if(ifPropertyAction.shouldReplace) {
|
|
759
|
+
updatedDeclarations = ifPropertyAction.declarations
|
|
760
|
+
|
|
761
|
+
} else {
|
|
762
|
+
updatedDeclarations = [ ...updatedDeclarations, ...ifPropertyAction.declarations ]
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
if( ifPropertyAndValueActionAll ) {
|
|
767
|
+
if(ifPropertyAndValueActionAll.shouldReplace) {
|
|
768
|
+
updatedDeclarations = ifPropertyAndValueActionAll.declarations
|
|
769
|
+
|
|
770
|
+
} else {
|
|
771
|
+
updatedDeclarations = [ ...updatedDeclarations, ...ifPropertyAndValueActionAll.declarations ]
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
if( ifPropertyAndValueAction ) {
|
|
776
|
+
if(ifPropertyAndValueAction.shouldReplace) {
|
|
777
|
+
updatedDeclarations = ifPropertyAndValueAction.declarations
|
|
778
|
+
|
|
779
|
+
} else {
|
|
780
|
+
updatedDeclarations = [ ...updatedDeclarations, ...ifPropertyAndValueAction.declarations ]
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
if (decl.property === 'font-size') {
|
|
785
|
+
updatedDeclarations.push(
|
|
786
|
+
{
|
|
787
|
+
property: 'font-size',
|
|
788
|
+
value: responsiveFontSize(
|
|
789
|
+
decl.value,
|
|
790
|
+
templateWidth,
|
|
791
|
+
templateHeight,
|
|
792
|
+
preferedWidth,
|
|
793
|
+
preferedHeight
|
|
794
|
+
)
|
|
795
|
+
}
|
|
796
|
+
)
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
return updatedDeclarations;
|
|
801
|
+
})
|
|
802
|
+
child.children[0].value = updatedCssContent;
|
|
803
|
+
////console.log("parsedSvg:defs.css.init ", cssContent)
|
|
804
|
+
////console.log("parsedSvg:defs.css.final ", updatedCssContent)
|
|
805
|
+
}
|
|
806
|
+
});
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
//Sets styles
|
|
810
|
+
const styles = []
|
|
811
|
+
//Sets style for hiding elements
|
|
812
|
+
styles.push({
|
|
813
|
+
"name": "style",
|
|
814
|
+
"type": "element",
|
|
815
|
+
"value": "",
|
|
816
|
+
"attributes": {},
|
|
817
|
+
"children": [
|
|
818
|
+
{
|
|
819
|
+
"name": "",
|
|
820
|
+
"type": "text",
|
|
821
|
+
"value": `.hide { opacity: 0 !important;}`,
|
|
822
|
+
"attributes": {},
|
|
823
|
+
"children": []
|
|
824
|
+
}
|
|
825
|
+
]
|
|
826
|
+
} as INode)
|
|
827
|
+
//Sets styles for filter widgets
|
|
828
|
+
styles.push({
|
|
829
|
+
"name": "style",
|
|
830
|
+
"type": "element",
|
|
831
|
+
"value": "",
|
|
832
|
+
"attributes": {},
|
|
833
|
+
"children": [
|
|
834
|
+
{
|
|
835
|
+
"name": "",
|
|
836
|
+
"type": "text",
|
|
837
|
+
"value": `#softbaker-slider-handle, #softbaker-button-background, #softbaker-button-text, #softbaker-cancel-background, #softbaker-cancel-text, #softbaker-slider-track { cursor: pointer !important;}`,
|
|
838
|
+
"attributes": {},
|
|
839
|
+
"children": []
|
|
840
|
+
}
|
|
841
|
+
]
|
|
842
|
+
} as INode)
|
|
843
|
+
parsedSvg.children[defIndex].children = styles.concat(parsedSvg.children[defIndex].children)
|
|
844
|
+
|
|
845
|
+
if(fonts) {
|
|
846
|
+
//Add fonts
|
|
847
|
+
const fontsElements = []
|
|
848
|
+
|
|
849
|
+
for(const font of Object.values(fonts)) {
|
|
850
|
+
if(font.dataUrl && font.ext) {
|
|
851
|
+
fontsElements.push({
|
|
852
|
+
"name": "style",
|
|
853
|
+
"type": "element",
|
|
854
|
+
"value": "",
|
|
855
|
+
"attributes": {},
|
|
856
|
+
"children": [
|
|
857
|
+
{
|
|
858
|
+
"name": "",
|
|
859
|
+
"type": "text",
|
|
860
|
+
"value": `\n @font-face {\n font-family: "${font.name}";\n src: url(${font.dataUrl}) format(${getFontFormat(font.ext)});\n }`,
|
|
861
|
+
"attributes": {},
|
|
862
|
+
"children": []
|
|
863
|
+
}
|
|
864
|
+
]
|
|
865
|
+
} as INode)
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
if(fontsElements.length > 0) {
|
|
869
|
+
parsedSvg.children[defIndex].children = fontsElements.concat(parsedSvg.children[defIndex].children)
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
//Add Rotate widgets
|
|
876
|
+
if(tempMask) {
|
|
877
|
+
|
|
878
|
+
const maskKey = Object.keys(tempMask)[0]
|
|
879
|
+
const maskValue = ((tempMask || {})[maskKey] || {}).ImageTransform
|
|
880
|
+
|
|
881
|
+
////console.log("parsedSvg.otherz.tempMask", tempMask, "maskValue:", maskValue, maskImageX, maskImageY, maskImageWidth, maskImageHeight)
|
|
882
|
+
|
|
883
|
+
const colorAccent = "#dd6b20"
|
|
884
|
+
//Rect for slider
|
|
885
|
+
const rect = {
|
|
886
|
+
"name": "rect",
|
|
887
|
+
"type": "element",
|
|
888
|
+
"value": "",
|
|
889
|
+
"attributes": {
|
|
890
|
+
"id": "softbaker-slider-track",
|
|
891
|
+
"x": maskImageX + "",
|
|
892
|
+
"y": maskImageY + maskImageHeight + 10 + "",
|
|
893
|
+
"width": maskImageWidth + "",
|
|
894
|
+
"height": "10",
|
|
895
|
+
"fill": "#2a2a2a"
|
|
896
|
+
},
|
|
897
|
+
"children": []
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
//Circle for slider
|
|
901
|
+
const degree = maskValue?.args?.rotationAngle || 0//(newX / rect.width) * 360;
|
|
902
|
+
const cx = (degree * parseFloat(rect.attributes.width)) / 360
|
|
903
|
+
const circle = {
|
|
904
|
+
"name": "circle",
|
|
905
|
+
"type": "element",
|
|
906
|
+
"value": "",
|
|
907
|
+
"attributes": {
|
|
908
|
+
"id": "softbaker-slider-handle",
|
|
909
|
+
"cx": maskImageX + cx + "",
|
|
910
|
+
"cy": parseFloat(rect.attributes.y) + (parseFloat(rect.attributes.height) / 2) + "",
|
|
911
|
+
"r": parseFloat(rect.attributes.height) + "",
|
|
912
|
+
"fill": colorAccent
|
|
913
|
+
},
|
|
914
|
+
"children": []
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
//Background for submit
|
|
918
|
+
const bg = {
|
|
919
|
+
"name": "rect",
|
|
920
|
+
"type": "element",
|
|
921
|
+
"value": "",
|
|
922
|
+
"attributes": {
|
|
923
|
+
"id": "softbaker-button-background",
|
|
924
|
+
"x": maskImageX + "",
|
|
925
|
+
"y": parseFloat(rect.attributes.y) + parseFloat(rect.attributes.height) + 10 + "",
|
|
926
|
+
"width": "40",
|
|
927
|
+
"height": "20",
|
|
928
|
+
"rx": "10",
|
|
929
|
+
"ry": "10",
|
|
930
|
+
"fill": colorAccent
|
|
931
|
+
},
|
|
932
|
+
"children": []
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
//Text for submit
|
|
936
|
+
const text = {
|
|
937
|
+
"name": "text",
|
|
938
|
+
"type": "element",
|
|
939
|
+
"value": "",
|
|
940
|
+
"attributes": {
|
|
941
|
+
"id": "softbaker-button-text",
|
|
942
|
+
"x": maskImageX + (parseFloat(bg.attributes.width) / 2) + "",
|
|
943
|
+
"y": parseFloat(bg.attributes.y) + (parseFloat(bg.attributes.height) / 2) + 5 + "",
|
|
944
|
+
"font-size": "14",
|
|
945
|
+
"text-anchor": "middle",
|
|
946
|
+
"fill": "#FFFFFF"
|
|
947
|
+
},
|
|
948
|
+
"children": [
|
|
949
|
+
{
|
|
950
|
+
"name": "",
|
|
951
|
+
"type": "text",
|
|
952
|
+
"value": "✔",
|
|
953
|
+
"parent": null,
|
|
954
|
+
"attributes": {},
|
|
955
|
+
"children": []
|
|
956
|
+
}
|
|
957
|
+
]
|
|
958
|
+
}
|
|
959
|
+
|
|
960
|
+
//Background for cancel
|
|
961
|
+
const submitCancelSpace = 50
|
|
962
|
+
const cancelBg = {
|
|
963
|
+
"name": "rect",
|
|
964
|
+
"type": "element",
|
|
965
|
+
"value": "",
|
|
966
|
+
"attributes": {
|
|
967
|
+
"id": "softbaker-cancel-background",
|
|
968
|
+
"x": maskImageX + submitCancelSpace + "",
|
|
969
|
+
"y": parseFloat(rect.attributes.y) + parseFloat(rect.attributes.height) + 10 + "",
|
|
970
|
+
"width": "40",
|
|
971
|
+
"height": "20",
|
|
972
|
+
"rx": "10",
|
|
973
|
+
"ry": "10",
|
|
974
|
+
"stroke": colorAccent
|
|
975
|
+
},
|
|
976
|
+
"children": []
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
//Text for cancel
|
|
980
|
+
const cancelText = {
|
|
981
|
+
"name": "text",
|
|
982
|
+
"type": "element",
|
|
983
|
+
"value": "",
|
|
984
|
+
"attributes": {
|
|
985
|
+
"id": "softbaker-cancel-text",
|
|
986
|
+
"x": maskImageX + submitCancelSpace + (parseFloat(bg.attributes.width) / 2) + "",
|
|
987
|
+
"y": parseFloat(bg.attributes.y) + (parseFloat(bg.attributes.height) / 2) + 5 + "",
|
|
988
|
+
"font-size": "14",
|
|
989
|
+
"text-anchor": "middle",
|
|
990
|
+
"fill": "#FFFFFF"
|
|
991
|
+
},
|
|
992
|
+
"children": [
|
|
993
|
+
{
|
|
994
|
+
"name": "",
|
|
995
|
+
"type": "text",
|
|
996
|
+
"value": "❌",
|
|
997
|
+
"parent": null,
|
|
998
|
+
"attributes": {},
|
|
999
|
+
"children": []
|
|
1000
|
+
}
|
|
1001
|
+
]
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
parsedSvg.children = parsedSvg.children.concat(
|
|
1005
|
+
[rect, circle, bg, text, cancelBg, cancelText]
|
|
1006
|
+
)
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
//if(width == 1024) //console.log("formatDate:3.1", parsedSvg)
|
|
1010
|
+
//if(width == 1024) //console.log("formatDate:3.2", stringify(parsedSvg))
|
|
1011
|
+
//Add watermark
|
|
1012
|
+
////console.log("DataIsFreemium:", data.is_freemium)
|
|
1013
|
+
if(showWatermark) {
|
|
1014
|
+
watermark(stringify(parsedSvg), templateWidth, templateHeight)
|
|
1015
|
+
.then(watermark => {
|
|
1016
|
+
parsedSvg.children.push({
|
|
1017
|
+
"name": "image",
|
|
1018
|
+
"type": "element",
|
|
1019
|
+
"value": "",
|
|
1020
|
+
"attributes": {
|
|
1021
|
+
"id": "watermark-" + genId(8),
|
|
1022
|
+
"width": templateWidth + "",
|
|
1023
|
+
"height": templateHeight + "",
|
|
1024
|
+
"xlink:href": watermark
|
|
1025
|
+
},
|
|
1026
|
+
"children": []
|
|
1027
|
+
})
|
|
1028
|
+
//if(width == 1024) //console.log("formatDate:3.3", stringify(parsedSvg))
|
|
1029
|
+
resolve(stringify(parsedSvg))
|
|
1030
|
+
})
|
|
1031
|
+
.catch((e) => {
|
|
1032
|
+
//if(width == 1024) //console.log("formatDate:3.e", e.message)
|
|
1033
|
+
reject(e)
|
|
1034
|
+
})
|
|
1035
|
+
|
|
1036
|
+
} else {
|
|
1037
|
+
//if(width == 1024) //console.log("formatDate:3.4", stringify(parsedSvg))
|
|
1038
|
+
resolve(stringify(parsedSvg))
|
|
1039
|
+
}//*/
|
|
1040
|
+
|
|
1041
|
+
})
|
|
1042
|
+
.catch(e => {
|
|
1043
|
+
//if(width == 1024) //console.log("formatDate:4", e.message)
|
|
1044
|
+
reject(e)
|
|
1045
|
+
})
|
|
1046
|
+
})
|
|
1047
|
+
}
|
|
1048
|
+
|
|
1049
|
+
// Utility function to remove underscore(_) at the ends of an id which is the photoshop version of white spaces at the ends
|
|
1050
|
+
export const trimId = (name: string) => {
|
|
1051
|
+
return name.replace(/^_+|_+$/g, '');
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1054
|
+
export const splitElementNameWithDirective = (nameWithDirective: string) => {
|
|
1055
|
+
const [ name, directive ] = nameWithDirective.split("@")
|
|
1056
|
+
|
|
1057
|
+
return { name, directive }
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1060
|
+
const getPlaceholder = (element: INode) => {
|
|
1061
|
+
var child = element.children[0]
|
|
1062
|
+
var prevChild = null
|
|
1063
|
+
while (child.children && child.children.length > 0) {
|
|
1064
|
+
prevChild = child
|
|
1065
|
+
child = child.children[0]
|
|
1066
|
+
}
|
|
1067
|
+
const placeholder = child.value
|
|
1068
|
+
return nullOrEmpty(placeholder)? null : placeholder
|
|
1069
|
+
}
|
|
1070
|
+
const updateFields = (fields: Fields, id: string, element: INode) => {
|
|
1071
|
+
const elementName = element.name
|
|
1072
|
+
const { name, type, selectIndex, typeAndSelectIndex } = splitSvgElementId(id)
|
|
1073
|
+
|
|
1074
|
+
if(!typeAndSelectIndex || !type) return { field: null, updatedFields: fields }
|
|
1075
|
+
|
|
1076
|
+
const field: Field = { } as Field
|
|
1077
|
+
switch (type) {
|
|
1078
|
+
case "select"://Covers image select, text select, textarea select,...
|
|
1079
|
+
field.id = `${name}.${type}`
|
|
1080
|
+
field.name = breakName(name)
|
|
1081
|
+
field.type = `${elementName}_${type}`
|
|
1082
|
+
if(elementName == "image") {
|
|
1083
|
+
const option = {
|
|
1084
|
+
name: `${breakName(name)} ${selectIndex}`,
|
|
1085
|
+
id: id,
|
|
1086
|
+
index: selectIndex || 0,
|
|
1087
|
+
type: elementName
|
|
1088
|
+
}
|
|
1089
|
+
field.options = orderOptions({...(fields[field.id]?.options || {}), [option.id]: option})
|
|
1090
|
+
}
|
|
1091
|
+
break;
|
|
1092
|
+
case "upload":
|
|
1093
|
+
field.id = `${name}.${type}`
|
|
1094
|
+
field.name = breakName(name)
|
|
1095
|
+
field.type = `${elementName}_${type}`
|
|
1096
|
+
break;
|
|
1097
|
+
case "faceshot":
|
|
1098
|
+
field.id = `${name}.${type}`
|
|
1099
|
+
field.name = breakName(name)
|
|
1100
|
+
field.type = type
|
|
1101
|
+
break;
|
|
1102
|
+
case "text":
|
|
1103
|
+
case "gen":
|
|
1104
|
+
case "defgen":
|
|
1105
|
+
case "date":
|
|
1106
|
+
field.id = `${name}.${type}`
|
|
1107
|
+
field.name = breakName(name)
|
|
1108
|
+
field.type = type
|
|
1109
|
+
field.placeholder = getPlaceholder(element) || field.name
|
|
1110
|
+
break;
|
|
1111
|
+
case "textarea":
|
|
1112
|
+
field.id = `${name}.${type}`
|
|
1113
|
+
field.name = breakName(name)
|
|
1114
|
+
field.type = type
|
|
1115
|
+
field.placeholder = getTextAreaMaxLength(element).defaultInputLines.join("\n")
|
|
1116
|
+
break;
|
|
1117
|
+
default://Covers text, textarea, and others
|
|
1118
|
+
field.id = `${name}.${type}`
|
|
1119
|
+
field.name = breakName(name)
|
|
1120
|
+
field.type = type
|
|
1121
|
+
break;
|
|
1122
|
+
}
|
|
1123
|
+
if(Object.keys(field).length > 0) fields[field.id] = field
|
|
1124
|
+
|
|
1125
|
+
return {
|
|
1126
|
+
field,
|
|
1127
|
+
updatedFields: fields
|
|
1128
|
+
}
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1131
|
+
// Function to convert PSD to SVG and placeholders
|
|
1132
|
+
export const buildTemplateDataFromSvg = (base64?: string | null): Promise<TemplateData | null> => {
|
|
1133
|
+
return new Promise(async (resolve, reject) => {
|
|
1134
|
+
try {
|
|
1135
|
+
|
|
1136
|
+
if(base64) {
|
|
1137
|
+
parse(base64ToSvg(base64))
|
|
1138
|
+
.then(async parsedSvg => {
|
|
1139
|
+
const annotationErrors: string[] = []
|
|
1140
|
+
const images: FileMap = { }
|
|
1141
|
+
var imagesArray: string[] = []
|
|
1142
|
+
var imagesIdArray: string[] = []
|
|
1143
|
+
const idListOfImagesWithThumbnails: string[] = []
|
|
1144
|
+
var fields: Fields = { }
|
|
1145
|
+
const children = []
|
|
1146
|
+
const processedChoiceFields: string[] = []
|
|
1147
|
+
const masks: MaskMap = {}
|
|
1148
|
+
//Used to check if the title and description directives are declared
|
|
1149
|
+
var title, desc
|
|
1150
|
+
const imageAnalysisPromises: (Promise<ImageUploadMaskInfo>)[] = []
|
|
1151
|
+
for(const el of parsedSvg.children) {
|
|
1152
|
+
const id = trimId(el.attributes["data-name"] || el.attributes.id || genId(8))
|
|
1153
|
+
el.attributes.id = id
|
|
1154
|
+
const { field, updatedFields } = updateFields(fields, id, el)
|
|
1155
|
+
fields = updatedFields
|
|
1156
|
+
//console.log("parsedSvg2:ID", el.attributes["data-name"], id, field?.type)
|
|
1157
|
+
|
|
1158
|
+
if(el.name == "image") {
|
|
1159
|
+
if(!field?.type || !["image_upload", "sign", "faceshot"].includes((field?.type || ""))) {
|
|
1160
|
+
//If the image is a copy of an image already added,
|
|
1161
|
+
if(imagesArray.includes(el.attributes["xlink:href"])) {
|
|
1162
|
+
//set the image as a reference to the already added copy
|
|
1163
|
+
const imageIndex = imagesArray.indexOf(el.attributes["xlink:href"])
|
|
1164
|
+
images[id] = imagesIdArray[imageIndex]
|
|
1165
|
+
|
|
1166
|
+
} else {
|
|
1167
|
+
//add the image
|
|
1168
|
+
images[id] = el.attributes["xlink:href"]
|
|
1169
|
+
imagesArray.push(el.attributes["xlink:href"])
|
|
1170
|
+
imagesIdArray.push(id)
|
|
1171
|
+
}
|
|
1172
|
+
|
|
1173
|
+
} else {
|
|
1174
|
+
//Since the user will be the one to provide it, then no need to upload the placeholder image
|
|
1175
|
+
//Just asign an empty value so the image can be detected has an image to be processed
|
|
1176
|
+
//especially during the application of filter on the image.
|
|
1177
|
+
/*
|
|
1178
|
+
const zRotation = await getZRotationDegree(el.attributes["xlink:href"])
|
|
1179
|
+
if(zRotation != 0) {
|
|
1180
|
+
masks[id] = {filter_id: "Rotate", args: { degree: zRotation }}
|
|
1181
|
+
}*/
|
|
1182
|
+
|
|
1183
|
+
imageAnalysisPromises.push(new Promise(async (resolve, reject) => {
|
|
1184
|
+
const imageEl = await base64ToImage(el.attributes["xlink:href"]);
|
|
1185
|
+
try {
|
|
1186
|
+
const analysisResult = await analyzeImage(imageEl, true);
|
|
1187
|
+
//rotationAngle is way off on analysis done on signatures, and most signature angles are close to 0
|
|
1188
|
+
//if(field?.type == "sign") analysisResult.rotationAngle = 0
|
|
1189
|
+
resolve({
|
|
1190
|
+
imageId: id,
|
|
1191
|
+
filterId: "ImageTransform",
|
|
1192
|
+
mask: {filter_id: "ImageTransform", args: analysisResult }
|
|
1193
|
+
})
|
|
1194
|
+
|
|
1195
|
+
} catch(e: any) {
|
|
1196
|
+
//console.log("annotationErrors:e", e.message)
|
|
1197
|
+
annotationErrors.push(`${el.attributes["id"]} has image annotation error: ${e.message}`)
|
|
1198
|
+
reject(e)
|
|
1199
|
+
}
|
|
1200
|
+
}))
|
|
1201
|
+
images[id] = ""
|
|
1202
|
+
}
|
|
1203
|
+
|
|
1204
|
+
el.attributes["xlink:href"] = ""
|
|
1205
|
+
if(field && field.type == "image_select") {
|
|
1206
|
+
idListOfImagesWithThumbnails.push(id)
|
|
1207
|
+
}
|
|
1208
|
+
}
|
|
1209
|
+
|
|
1210
|
+
if(field) {
|
|
1211
|
+
if(["image_select", "text_select", "textarea_select"].includes(field?.type)) {
|
|
1212
|
+
el.attributes.id = field.id
|
|
1213
|
+
if(!processedChoiceFields.includes(el.attributes.id)) {
|
|
1214
|
+
children.push(el)
|
|
1215
|
+
processedChoiceFields.push(el.attributes.id)
|
|
1216
|
+
}
|
|
1217
|
+
|
|
1218
|
+
} else {
|
|
1219
|
+
const { name } = splitSvgElementId(id)
|
|
1220
|
+
const { directive } = splitElementNameWithDirective(name)
|
|
1221
|
+
if(directive && directive == "name") {
|
|
1222
|
+
title = id
|
|
1223
|
+
|
|
1224
|
+
} else if(directive && directive == "desc") {
|
|
1225
|
+
desc = id
|
|
1226
|
+
|
|
1227
|
+
}
|
|
1228
|
+
children.push(el)
|
|
1229
|
+
//processedChoiceFields.push(field.type)
|
|
1230
|
+
}
|
|
1231
|
+
|
|
1232
|
+
} else {
|
|
1233
|
+
children.push(el)
|
|
1234
|
+
}
|
|
1235
|
+
|
|
1236
|
+
}
|
|
1237
|
+
|
|
1238
|
+
let imageAnalysisPromisesError
|
|
1239
|
+
if(imageAnalysisPromises.length > 0) {
|
|
1240
|
+
try {
|
|
1241
|
+
const all = await Promise.all(imageAnalysisPromises)
|
|
1242
|
+
for(const analysisResult of all) {
|
|
1243
|
+
masks[analysisResult.imageId] = {
|
|
1244
|
+
[analysisResult.filterId]: analysisResult.mask
|
|
1245
|
+
}
|
|
1246
|
+
}
|
|
1247
|
+
|
|
1248
|
+
} catch(e) {
|
|
1249
|
+
imageAnalysisPromisesError = e
|
|
1250
|
+
}
|
|
1251
|
+
}
|
|
1252
|
+
|
|
1253
|
+
if(annotationErrors.length > 0) {
|
|
1254
|
+
return reject(new Error(annotationErrors.join(" ")))
|
|
1255
|
+
}
|
|
1256
|
+
|
|
1257
|
+
if(imageAnalysisPromisesError) return (imageAnalysisPromisesError)
|
|
1258
|
+
|
|
1259
|
+
if(!title || !desc) {
|
|
1260
|
+
let error
|
|
1261
|
+
if(!title && !desc) {
|
|
1262
|
+
error = "Make sure that at least @name and @desc is declared on some text fields. @small is optional."
|
|
1263
|
+
|
|
1264
|
+
} else if(!title) {
|
|
1265
|
+
error = "Make sure that at least @name is declared on a text field. @small is optional."
|
|
1266
|
+
|
|
1267
|
+
} else {
|
|
1268
|
+
error = "Make sure that at least @desc is declared on a text field. @small is optional."
|
|
1269
|
+
|
|
1270
|
+
}
|
|
1271
|
+
return reject(new Error(error))
|
|
1272
|
+
}
|
|
1273
|
+
|
|
1274
|
+
imagesArray = []
|
|
1275
|
+
imagesIdArray = []
|
|
1276
|
+
parsedSvg.children = children
|
|
1277
|
+
|
|
1278
|
+
//console.info("parsedSvg: ", parsedSvg)
|
|
1279
|
+
//console.info("parsedSvg.fields ", fields)
|
|
1280
|
+
//console.info("parsedSvg:images ", "images")
|
|
1281
|
+
// Resolve with SVG content and placeholders description
|
|
1282
|
+
|
|
1283
|
+
const expandedImages = await expandImagesMap(images, async (id, image) => {
|
|
1284
|
+
const { width } = await getImageDimensions(image)
|
|
1285
|
+
var returnMap: FileMap = { }
|
|
1286
|
+
//console.info("parsedSvg:baseWidth2: ", width)
|
|
1287
|
+
returnMap = {
|
|
1288
|
+
[`${id}`]: image
|
|
1289
|
+
}
|
|
1290
|
+
|
|
1291
|
+
if(width >= 728) {
|
|
1292
|
+
returnMap = {
|
|
1293
|
+
...returnMap,
|
|
1294
|
+
[`${id}`]: image,
|
|
1295
|
+
[`${id}_512`]: await resizeBase64Image(image, 512)
|
|
1296
|
+
}
|
|
1297
|
+
}
|
|
1298
|
+
|
|
1299
|
+
if(width >= 1024) {
|
|
1300
|
+
returnMap = {
|
|
1301
|
+
...returnMap,
|
|
1302
|
+
[`${id}`]: image,
|
|
1303
|
+
[`${id}_728`]: await resizeBase64Image(image, 728)
|
|
1304
|
+
}
|
|
1305
|
+
}
|
|
1306
|
+
|
|
1307
|
+
if(width >= 2047) {
|
|
1308
|
+
returnMap = {
|
|
1309
|
+
...returnMap,
|
|
1310
|
+
[`${id}`]: image,
|
|
1311
|
+
[`${id}_1024`]: await resizeBase64Image(image, 1024)
|
|
1312
|
+
}
|
|
1313
|
+
}
|
|
1314
|
+
|
|
1315
|
+
if(idListOfImagesWithThumbnails.includes(id)) {
|
|
1316
|
+
returnMap[`${id}_thumbnail`] = await resizeBase64Image(image, 70)
|
|
1317
|
+
}
|
|
1318
|
+
if(Object.keys(returnMap).length > 0) {
|
|
1319
|
+
return returnMap
|
|
1320
|
+
}
|
|
1321
|
+
|
|
1322
|
+
return null
|
|
1323
|
+
})
|
|
1324
|
+
|
|
1325
|
+
//console.log("parsedSvg2:expandedImages ", expandedImages)
|
|
1326
|
+
|
|
1327
|
+
resolve({
|
|
1328
|
+
svg: stringify(parsedSvg),
|
|
1329
|
+
fields: fields,
|
|
1330
|
+
images,//: await convertPngBase64ImagesWithUPNG(images),
|
|
1331
|
+
masks,
|
|
1332
|
+
cssActions: {
|
|
1333
|
+
if_selector: {
|
|
1334
|
+
"*": {
|
|
1335
|
+
declarations: [{
|
|
1336
|
+
property: "white-space", value: "pre"
|
|
1337
|
+
}],
|
|
1338
|
+
}
|
|
1339
|
+
}
|
|
1340
|
+
}
|
|
1341
|
+
});
|
|
1342
|
+
})
|
|
1343
|
+
.catch(e => {
|
|
1344
|
+
reject(e)
|
|
1345
|
+
})
|
|
1346
|
+
|
|
1347
|
+
} else {
|
|
1348
|
+
resolve(null)
|
|
1349
|
+
}
|
|
1350
|
+
} catch (e) {
|
|
1351
|
+
reject(e);
|
|
1352
|
+
}
|
|
1353
|
+
});
|
|
1354
|
+
};
|