@pdfme/common 4.3.2-dev.2 → 4.3.2-dev.3
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/dist/cjs/__tests__/helper.test.js +135 -198
- package/dist/cjs/__tests__/helper.test.js.map +1 -1
- package/dist/cjs/src/helper.js +222 -69
- package/dist/cjs/src/helper.js.map +1 -1
- package/dist/cjs/src/index.js +2 -1
- package/dist/cjs/src/index.js.map +1 -1
- package/dist/esm/__tests__/helper.test.js +137 -200
- package/dist/esm/__tests__/helper.test.js.map +1 -1
- package/dist/esm/src/helper.js +220 -66
- package/dist/esm/src/helper.js.map +1 -1
- package/dist/esm/src/index.js +2 -2
- package/dist/esm/src/index.js.map +1 -1
- package/dist/types/src/helper.d.ts +3 -10
- package/dist/types/src/index.d.ts +2 -2
- package/package.json +1 -1
- package/src/helper.ts +236 -79
- package/src/index.ts +2 -0
package/src/helper.ts
CHANGED
@@ -19,6 +19,8 @@ import {
|
|
19
19
|
DEFAULT_FONT_VALUE,
|
20
20
|
} from './constants.js';
|
21
21
|
|
22
|
+
export const cloneDeep = <T>(value: T): T => JSON.parse(JSON.stringify(value));
|
23
|
+
|
22
24
|
const uniq = <T>(array: Array<T>) => Array.from(new Set(array));
|
23
25
|
|
24
26
|
export const getFallbackFontName = (font: Font) => {
|
@@ -228,112 +230,267 @@ interface ModifyTemplateForDynamicTableArg {
|
|
228
230
|
input: Record<string, string>;
|
229
231
|
_cache: Map<any, any>;
|
230
232
|
options: CommonOptions;
|
231
|
-
|
232
|
-
template: Template;
|
233
|
-
input: Record<string, string>;
|
234
|
-
_cache: Map<any, any>;
|
235
|
-
options: CommonOptions;
|
236
|
-
}) => Promise<Template>;
|
237
|
-
getDynamicHeight: (
|
233
|
+
getDynamicHeights: (
|
238
234
|
value: string,
|
239
235
|
args: { schema: Schema; basePdf: BasePdf; options: CommonOptions; _cache: Map<any, any> }
|
240
|
-
) => Promise<number>;
|
236
|
+
) => Promise<number[]>;
|
241
237
|
}
|
242
238
|
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
239
|
+
class Node {
|
240
|
+
index = 0;
|
241
|
+
|
242
|
+
key?: string;
|
243
|
+
schema?: Schema;
|
244
|
+
|
245
|
+
children: Node[] = [];
|
246
|
+
|
247
|
+
width = 0;
|
248
|
+
height = 0;
|
249
|
+
padding: [number, number, number, number] = [0, 0, 0, 0];
|
250
|
+
position: { x: number; y: number } = { x: 0, y: 0 };
|
251
|
+
|
252
|
+
constructor({ width = 0, height = 0 } = {}) {
|
253
|
+
this.width = width;
|
254
|
+
this.height = height;
|
249
255
|
}
|
250
256
|
|
251
|
-
|
257
|
+
setIndex(index: number): void {
|
258
|
+
this.index = index;
|
259
|
+
}
|
252
260
|
|
253
|
-
|
261
|
+
setKeyAndSchema(key: string, schema: Schema): void {
|
262
|
+
this.key = key;
|
263
|
+
this.schema = schema;
|
264
|
+
}
|
254
265
|
|
255
|
-
|
256
|
-
|
266
|
+
setWidth(width: number): void {
|
267
|
+
this.width = width;
|
268
|
+
}
|
257
269
|
|
258
|
-
|
259
|
-
|
260
|
-
const basePdf = template.basePdf;
|
261
|
-
const tmpDiffMap = new Map<number, number>();
|
262
|
-
if (!isBlankPdf(basePdf)) {
|
263
|
-
return tmpDiffMap;
|
270
|
+
setHeight(height: number): void {
|
271
|
+
this.height = height;
|
264
272
|
}
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
for (const [key, schema] of Object.entries(schemaObj)) {
|
269
|
-
const dynamicHeight = await getDynamicHeight(input?.[key] || '', {
|
270
|
-
schema,
|
271
|
-
basePdf,
|
272
|
-
options,
|
273
|
-
_cache,
|
274
|
-
});
|
275
|
-
if (schema.height !== dynamicHeight) {
|
276
|
-
tmpDiffMap.set(
|
277
|
-
schema.position.y + schema.height + pageHeight * pageIndex,
|
278
|
-
dynamicHeight - schema.height
|
279
|
-
);
|
280
|
-
}
|
281
|
-
}
|
282
|
-
pageIndex++;
|
273
|
+
|
274
|
+
setPadding(padding: [number, number, number, number]): void {
|
275
|
+
this.padding = padding;
|
283
276
|
}
|
284
277
|
|
285
|
-
|
286
|
-
|
287
|
-
|
278
|
+
setPosition(position: { x: number; y: number }): void {
|
279
|
+
this.position = position;
|
280
|
+
}
|
288
281
|
|
289
|
-
|
290
|
-
const
|
291
|
-
|
292
|
-
|
293
|
-
additionalHeight += newValue;
|
282
|
+
insertChild(child: Node): void {
|
283
|
+
const index = this.getChildCount();
|
284
|
+
child.setIndex(index);
|
285
|
+
this.children.splice(index, 0, child);
|
294
286
|
}
|
295
287
|
|
296
|
-
|
297
|
-
|
288
|
+
getChildCount(): number {
|
289
|
+
return this.children.length;
|
290
|
+
}
|
298
291
|
|
299
|
-
|
300
|
-
|
301
|
-
diffMap: Map<number, number>
|
302
|
-
): Template => {
|
303
|
-
if (!isBlankPdf(template.basePdf) || diffMap.size === 0) {
|
304
|
-
return template;
|
292
|
+
getChild(index: number): Node {
|
293
|
+
return this.children[index];
|
305
294
|
}
|
295
|
+
}
|
306
296
|
|
307
|
-
|
308
|
-
const
|
309
|
-
|
310
|
-
|
311
|
-
|
297
|
+
function createPage(basePdf: BlankPdf) {
|
298
|
+
const page = new Node({ ...basePdf });
|
299
|
+
page.setPadding(basePdf.padding);
|
300
|
+
return page;
|
301
|
+
}
|
312
302
|
|
313
|
-
|
314
|
-
|
315
|
-
|
303
|
+
function createNode(arg: {
|
304
|
+
key: string;
|
305
|
+
schema: Schema;
|
306
|
+
position: { x: number; y: number };
|
307
|
+
width: number;
|
308
|
+
height: number;
|
309
|
+
}) {
|
310
|
+
const { position, width, height, key, schema } = arg;
|
311
|
+
const node = new Node({ width, height });
|
312
|
+
node.setPosition(position);
|
313
|
+
node.setKeyAndSchema(key, schema);
|
314
|
+
return node;
|
315
|
+
}
|
316
316
|
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
317
|
+
function resortChildren(page: Node, orderMap: Map<string, number>): void {
|
318
|
+
page.children = page.children
|
319
|
+
.sort((a, b) => {
|
320
|
+
const orderA = orderMap.get(a.key!);
|
321
|
+
const orderB = orderMap.get(b.key!);
|
322
|
+
if (orderA === undefined || orderB === undefined) {
|
323
|
+
throw new Error('[@pdfme/common] order is not defined');
|
324
|
+
}
|
325
|
+
return orderA - orderB;
|
326
|
+
})
|
327
|
+
.map((child, index) => {
|
328
|
+
child.setIndex(index);
|
329
|
+
return child;
|
330
|
+
});
|
331
|
+
}
|
321
332
|
|
322
|
-
|
323
|
-
|
324
|
-
|
333
|
+
async function createOnePage(
|
334
|
+
arg: {
|
335
|
+
basePdf: BlankPdf;
|
336
|
+
schemaObj: Record<string, Schema>;
|
337
|
+
orderMap: Map<string, number>;
|
338
|
+
} & Omit<ModifyTemplateForDynamicTableArg, 'template'>
|
339
|
+
): Promise<Node> {
|
340
|
+
const { basePdf, schemaObj, orderMap, input, options, _cache, getDynamicHeights } = arg;
|
341
|
+
const page = createPage(basePdf);
|
342
|
+
|
343
|
+
const schemaPositions: number[] = [];
|
344
|
+
const sortedSchemaEntries = Object.entries(schemaObj).sort(
|
345
|
+
(a, b) => a[1].position.y - b[1].position.y
|
346
|
+
);
|
347
|
+
const diffMap = new Map();
|
348
|
+
for (const [key, schema] of sortedSchemaEntries) {
|
349
|
+
const { position, width } = schema;
|
350
|
+
|
351
|
+
const opt = { schema, basePdf, options, _cache };
|
352
|
+
const heights = await getDynamicHeights(input?.[key] || '', opt);
|
353
|
+
|
354
|
+
const heightsSum = heights.reduce((acc, cur) => acc + cur, 0);
|
355
|
+
const originalHeight = schema.height;
|
356
|
+
if (heightsSum !== originalHeight) {
|
357
|
+
diffMap.set(position.y + originalHeight, heightsSum - originalHeight);
|
358
|
+
}
|
359
|
+
heights.forEach((height, index) => {
|
360
|
+
let y = schema.position.y + heights.reduce((acc, cur, i) => (i < index ? acc + cur : acc), 0);
|
361
|
+
for (const [diffY, diff] of diffMap.entries()) {
|
362
|
+
if (diffY <= schema.position.y) {
|
363
|
+
y += diff;
|
325
364
|
}
|
326
365
|
}
|
366
|
+
const node = createNode({ key, schema, position: { ...position, y }, width, height });
|
367
|
+
|
368
|
+
schemaPositions.push(y + height + basePdf.padding[2]);
|
369
|
+
page.insertChild(node);
|
370
|
+
});
|
371
|
+
}
|
372
|
+
|
373
|
+
const pageHeight = Math.max(...schemaPositions, basePdf.height - basePdf.padding[2]);
|
374
|
+
page.setHeight(pageHeight);
|
375
|
+
|
376
|
+
resortChildren(page, orderMap);
|
327
377
|
|
328
|
-
|
329
|
-
|
330
|
-
|
378
|
+
return page;
|
379
|
+
}
|
380
|
+
|
381
|
+
function breakIntoPages(arg: {
|
382
|
+
longPage: Node;
|
383
|
+
orderMap: Map<string, number>;
|
384
|
+
basePdf: BlankPdf;
|
385
|
+
}): Node[] {
|
386
|
+
const { longPage, orderMap, basePdf } = arg;
|
387
|
+
const pages: Node[] = [createPage(basePdf)];
|
388
|
+
const [paddingTop, , paddingBottom] = basePdf.padding;
|
389
|
+
const yAdjustments: { page: number; value: number }[] = [];
|
390
|
+
|
391
|
+
const getPageHeight = (pageIndex: number) =>
|
392
|
+
basePdf.height - paddingBottom - (pageIndex > 0 ? paddingTop : 0);
|
393
|
+
|
394
|
+
const calculateNewY = (y: number, pageIndex: number) => {
|
395
|
+
const newY = y - pageIndex * (basePdf.height - paddingTop - paddingBottom);
|
396
|
+
|
397
|
+
while (pages.length <= pageIndex) {
|
398
|
+
if (!pages[pageIndex]) {
|
399
|
+
pages.push(createPage(basePdf));
|
400
|
+
yAdjustments.push({ page: pageIndex, value: (newY - paddingTop) * -1 });
|
331
401
|
}
|
402
|
+
}
|
403
|
+
return newY + (yAdjustments.find((adj) => adj.page === pageIndex)?.value || 0);
|
404
|
+
};
|
405
|
+
|
406
|
+
const children = longPage.children.sort((a, b) => a.position.y - b.position.y);
|
407
|
+
for (let i = 0; i < children.length; i++) {
|
408
|
+
const { key, schema, position, height, width } = children[i];
|
409
|
+
const { y, x } = position;
|
410
|
+
|
411
|
+
let targetPageIndex = Math.floor(y / getPageHeight(pages.length - 1));
|
412
|
+
let newY = calculateNewY(y, targetPageIndex);
|
332
413
|
|
333
|
-
|
334
|
-
|
414
|
+
if (newY + height > basePdf.height - paddingBottom) {
|
415
|
+
targetPageIndex++;
|
416
|
+
newY = calculateNewY(y, targetPageIndex);
|
335
417
|
}
|
418
|
+
|
419
|
+
if (!key || !schema) throw new Error('[@pdfme/common] key or schema is undefined');
|
420
|
+
|
421
|
+
const clonedElement = createNode({ key, schema, position: { x, y: newY }, width, height });
|
422
|
+
pages[targetPageIndex].insertChild(clonedElement);
|
423
|
+
}
|
424
|
+
|
425
|
+
pages.forEach((page) => resortChildren(page, orderMap));
|
426
|
+
|
427
|
+
return pages;
|
428
|
+
}
|
429
|
+
|
430
|
+
function createNewTemplate(pages: Node[], basePdf: BlankPdf): Template {
|
431
|
+
const newTemplate: Template = {
|
432
|
+
schemas: Array.from({ length: pages.length }, () => ({} as Record<string, Schema>)),
|
433
|
+
basePdf: basePdf,
|
434
|
+
};
|
435
|
+
|
436
|
+
const keyToSchemas = new Map<string, Node[]>();
|
437
|
+
|
438
|
+
cloneDeep(pages).forEach((page, pageIndex) => {
|
439
|
+
page.children.forEach((child) => {
|
440
|
+
const { key, schema } = child;
|
441
|
+
if (!key || !schema) throw new Error('[@pdfme/common] key or schema is undefined');
|
442
|
+
|
443
|
+
if (!keyToSchemas.has(key)) {
|
444
|
+
keyToSchemas.set(key, []);
|
445
|
+
}
|
446
|
+
keyToSchemas.get(key)!.push(child);
|
447
|
+
|
448
|
+
const sameKeySchemas = page.children.filter((c) => c.key === key);
|
449
|
+
const start = keyToSchemas.get(key)!.length - sameKeySchemas.length;
|
450
|
+
|
451
|
+
if (sameKeySchemas.length > 0) {
|
452
|
+
if (!sameKeySchemas[0].schema) {
|
453
|
+
throw new Error('[@pdfme/common] schema is undefined');
|
454
|
+
}
|
455
|
+
|
456
|
+
// Use the first schema to get the schema and position
|
457
|
+
const schema = sameKeySchemas[0].schema;
|
458
|
+
const height = sameKeySchemas.reduce((acc, cur) => acc + cur.height, 0);
|
459
|
+
const position = sameKeySchemas[0].position;
|
460
|
+
|
461
|
+
// Currently, __bodyRange exists for table schemas, but if we make it more abstract,
|
462
|
+
// it could be used for other schemas as well to render schemas that have been split by page breaks, starting from the middle.
|
463
|
+
schema.__bodyRange = {
|
464
|
+
start: Math.max(start - 1, 0),
|
465
|
+
end: start + sameKeySchemas.length - 1,
|
466
|
+
};
|
467
|
+
|
468
|
+
newTemplate.schemas[pageIndex][key] = Object.assign({}, schema, { position, height });
|
469
|
+
}
|
470
|
+
});
|
471
|
+
});
|
472
|
+
|
473
|
+
return newTemplate;
|
474
|
+
}
|
475
|
+
|
476
|
+
export const getDynamicTemplate = async (
|
477
|
+
arg: ModifyTemplateForDynamicTableArg
|
478
|
+
): Promise<Template> => {
|
479
|
+
const { template } = arg;
|
480
|
+
if (!isBlankPdf(template.basePdf)) {
|
481
|
+
return template;
|
482
|
+
}
|
483
|
+
|
484
|
+
const basePdf = template.basePdf as BlankPdf;
|
485
|
+
const pages: Node[] = [];
|
486
|
+
|
487
|
+
for (const schemaObj of template.schemas) {
|
488
|
+
const orderMap = new Map(Object.keys(schemaObj).map((key, index) => [key, index]));
|
489
|
+
|
490
|
+
const longPage = await createOnePage({ basePdf, schemaObj, orderMap, ...arg });
|
491
|
+
const brokenPages = breakIntoPages({ longPage, basePdf, orderMap });
|
492
|
+
pages.push(...brokenPages);
|
336
493
|
}
|
337
494
|
|
338
|
-
return
|
495
|
+
return createNewTemplate(pages, template.basePdf);
|
339
496
|
};
|
package/src/index.ts
CHANGED
@@ -37,6 +37,7 @@ import type {
|
|
37
37
|
DesignerProps,
|
38
38
|
} from './types.js';
|
39
39
|
import {
|
40
|
+
cloneDeep,
|
40
41
|
getFallbackFontName,
|
41
42
|
getDefaultFont,
|
42
43
|
getB64BasePdf,
|
@@ -67,6 +68,7 @@ export {
|
|
67
68
|
BLANK_PDF,
|
68
69
|
ZOOM,
|
69
70
|
DEFAULT_FONT_NAME,
|
71
|
+
cloneDeep,
|
70
72
|
getFallbackFontName,
|
71
73
|
getDefaultFont,
|
72
74
|
getB64BasePdf,
|