UNPKG

@pdfme/common

Version:

TypeScript base PDF generator and React base UI. Open source, developed by the community, and completely free to use under the MIT license!

239 lines 9.32 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.getDynamicTemplate = void 0; const helper_js_1 = require("./helper.js"); class LayoutNode { constructor({ width = 0, height = 0 } = {}) { Object.defineProperty(this, "index", { enumerable: true, configurable: true, writable: true, value: 0 }); Object.defineProperty(this, "schema", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "children", { enumerable: true, configurable: true, writable: true, value: [] }); Object.defineProperty(this, "width", { enumerable: true, configurable: true, writable: true, value: 0 }); Object.defineProperty(this, "height", { enumerable: true, configurable: true, writable: true, value: 0 }); Object.defineProperty(this, "padding", { enumerable: true, configurable: true, writable: true, value: [0, 0, 0, 0] }); Object.defineProperty(this, "position", { enumerable: true, configurable: true, writable: true, value: { x: 0, y: 0 } }); this.width = width; this.height = height; } setIndex(index) { this.index = index; } setSchema(schema) { this.schema = schema; } setWidth(width) { this.width = width; } setHeight(height) { this.height = height; } setPadding(padding) { this.padding = padding; } setPosition(position) { this.position = position; } insertChild(child) { const index = this.getChildCount(); child.setIndex(index); this.children.splice(index, 0, child); } getChildCount() { return this.children.length; } getChild(index) { return this.children[index]; } } function createPage(basePdf) { const page = new LayoutNode({ ...basePdf }); page.setPadding(basePdf.padding); return page; } function createNode(arg) { const { position, width, height, schema } = arg; const node = new LayoutNode({ width, height }); node.setPosition(position); node.setSchema(schema); return node; } function resortChildren(page, orderMap) { page.children = page.children .sort((a, b) => { const orderA = orderMap.get(a.schema?.name ?? ''); const orderB = orderMap.get(b.schema?.name ?? ''); if (orderA === undefined || orderB === undefined) { throw new Error('[@pdfme/common] order is not defined'); } return orderA - orderB; }) .map((child, index) => { child.setIndex(index); return child; }); } async function createOnePage(arg) { const { basePdf, schemaPage, orderMap, input, options, _cache, getDynamicHeights } = arg; const page = createPage(basePdf); const schemaPositions = []; const sortedSchemaEntries = (0, helper_js_1.cloneDeep)(schemaPage).sort((a, b) => a.position.y - b.position.y); const diffMap = new Map(); for (const schema of sortedSchemaEntries) { const { position, width } = schema; const opt = { schema, basePdf, options, _cache }; const value = (schema.readOnly ? schema.content : input?.[schema.name]) || ''; const heights = await getDynamicHeights(value, opt); const heightsSum = heights.reduce((acc, cur) => acc + cur, 0); const originalHeight = schema.height; if (heightsSum !== originalHeight) { diffMap.set(position.y + originalHeight, heightsSum - originalHeight); } heights.forEach((height, index) => { let y = schema.position.y + heights.reduce((acc, cur, i) => (i < index ? acc + cur : acc), 0); for (const [diffY, diff] of diffMap.entries()) { if (diffY <= schema.position.y) { y += diff; } } const node = createNode({ schema, position: { ...position, y }, width, height }); schemaPositions.push(y + height + basePdf.padding[2]); page.insertChild(node); }); } const pageHeight = Math.max(...schemaPositions, basePdf.height - basePdf.padding[2]); page.setHeight(pageHeight); resortChildren(page, orderMap); return page; } function breakIntoPages(arg) { const { longPage, orderMap, basePdf } = arg; const pages = [createPage(basePdf)]; const [paddingTop, , paddingBottom] = basePdf.padding; const yAdjustments = []; const getPageHeight = (pageIndex) => basePdf.height - paddingBottom - (pageIndex > 0 ? paddingTop : 0); const calculateNewY = (y, pageIndex) => { const newY = y - pageIndex * (basePdf.height - paddingTop - paddingBottom); while (pages.length <= pageIndex) { if (!pages[pageIndex]) { pages.push(createPage(basePdf)); yAdjustments.push({ page: pageIndex, value: (newY - paddingTop) * -1 }); } } return newY + (yAdjustments.find((adj) => adj.page === pageIndex)?.value || 0); }; const children = longPage.children.sort((a, b) => a.position.y - b.position.y); for (let i = 0; i < children.length; i++) { const { schema, position, height, width } = children[i]; const { y, x } = position; let targetPageIndex = Math.floor(y / getPageHeight(pages.length - 1)); let newY = calculateNewY(y, targetPageIndex); if (newY + height > basePdf.height - paddingBottom) { targetPageIndex++; newY = calculateNewY(y, targetPageIndex); } if (!schema) throw new Error('[@pdfme/common] schema is undefined'); const clonedElement = createNode({ schema, position: { x, y: newY }, width, height }); pages[targetPageIndex].insertChild(clonedElement); } pages.forEach((page) => resortChildren(page, orderMap)); return pages; } function createNewTemplate(pages, basePdf) { const newTemplate = { schemas: Array.from({ length: pages.length }, () => []), basePdf: basePdf, }; const nameToSchemas = new Map(); (0, helper_js_1.cloneDeep)(pages).forEach((page, pageIndex) => { page.children.forEach((child) => { const { schema } = child; if (!schema) throw new Error('[@pdfme/common] schema is undefined'); const name = schema.name; if (!nameToSchemas.has(name)) { nameToSchemas.set(name, []); } nameToSchemas.get(name).push(child); const sameNameSchemas = page.children.filter((c) => c.schema?.name === name); const start = nameToSchemas.get(name).length - sameNameSchemas.length; if (sameNameSchemas.length > 0) { if (!sameNameSchemas[0].schema) { throw new Error('[@pdfme/common] schema is undefined'); } // Use the first schema to get the schema and position const schema = sameNameSchemas[0].schema; const height = sameNameSchemas.reduce((acc, cur) => acc + cur.height, 0); const position = sameNameSchemas[0].position; // Currently, __bodyRange exists for table schemas, but if we make it more abstract, // it could be used for other schemas as well to render schemas that have been split by page breaks, starting from the middle. schema.__bodyRange = { start: Math.max(start - 1, 0), end: start + sameNameSchemas.length - 1, }; // Currently, this is used to determine whether to display the header when a table is split. schema.__isSplit = start > 0; const newSchema = Object.assign({}, schema, { position, height }); const index = newTemplate.schemas[pageIndex].findIndex((s) => s.name === name); if (index !== -1) { newTemplate.schemas[pageIndex][index] = newSchema; } else { newTemplate.schemas[pageIndex].push(newSchema); } } }); }); return newTemplate; } const getDynamicTemplate = async (arg) => { const { template } = arg; if (!(0, helper_js_1.isBlankPdf)(template.basePdf)) { return template; } const basePdf = template.basePdf; const pages = []; for (const schemaPage of template.schemas) { const orderMap = new Map(schemaPage.map((schema, index) => [schema.name, index])); const longPage = await createOnePage({ basePdf, schemaPage, orderMap, ...arg }); const brokenPages = breakIntoPages({ longPage, basePdf, orderMap }); pages.push(...brokenPages); } return createNewTemplate(pages, template.basePdf); }; exports.getDynamicTemplate = getDynamicTemplate; //# sourceMappingURL=dynamicTemplate.js.map