@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
JavaScript
;
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