Enhance document size and template management with padding options
This commit introduces new padding options for document sizes, allowing for customizable print padding and specific padding values (left, right, top, bottom). The document size schema has been updated to include these new fields, and corresponding route handlers have been modified to handle padding data during document size creation and editing. Additionally, the template manager has been updated to apply padding when rendering templates, ensuring that the layout respects the specified padding values. Tests have been added to verify the correct application of padding in both document sizes and templates, improving the overall document formatting capabilities.
This commit is contained in:
parent
e8c9a307ab
commit
ff60d6cf09
@ -51,6 +51,14 @@ export const hasPermission = async (user, objectType, action) => {
|
|||||||
|
|
||||||
export const checkPermissions = (objectType, action) => async (req, res, next) => {
|
export const checkPermissions = (objectType, action) => async (req, res, next) => {
|
||||||
try {
|
try {
|
||||||
|
if (req.user?._objectType == 'host') {
|
||||||
|
if (
|
||||||
|
(objectType === 'documentTemplate' && action === 'design') ||
|
||||||
|
(objectType === 'file' && action === 'download')
|
||||||
|
) {
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
}
|
||||||
const allowed = await hasPermission(req.user, objectType, action);
|
const allowed = await hasPermission(req.user, objectType, action);
|
||||||
if (!allowed) {
|
if (!allowed) {
|
||||||
return res.status(403).json({ error: 'Forbidden', code: 'FORBIDDEN' });
|
return res.status(403).json({ error: 'Forbidden', code: 'FORBIDDEN' });
|
||||||
|
|||||||
@ -25,6 +25,31 @@ const documentSizeSchema = new Schema(
|
|||||||
required: true,
|
required: true,
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
|
printPadding: {
|
||||||
|
type: Boolean,
|
||||||
|
required: true,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
paddingLeft: {
|
||||||
|
type: Number,
|
||||||
|
required: true,
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
|
paddingRight: {
|
||||||
|
type: Number,
|
||||||
|
required: true,
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
|
paddingTop: {
|
||||||
|
type: Number,
|
||||||
|
required: true,
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
|
paddingBottom: {
|
||||||
|
type: Number,
|
||||||
|
required: true,
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{ timestamps: true }
|
{ timestamps: true }
|
||||||
);
|
);
|
||||||
|
|||||||
@ -63,7 +63,7 @@ const isAuthenticated = async (req, res, next) => {
|
|||||||
try {
|
try {
|
||||||
const session = await getSession(token);
|
const session = await getSession(token);
|
||||||
if (session && session.expiresAt > Date.now()) {
|
if (session && session.expiresAt > Date.now()) {
|
||||||
req.user = session.user;
|
req.user = { ...session.user, _objectType: 'user' };
|
||||||
req.session = session;
|
req.session = session;
|
||||||
return next();
|
return next();
|
||||||
}
|
}
|
||||||
@ -71,7 +71,7 @@ const isAuthenticated = async (req, res, next) => {
|
|||||||
// Try email-render JWT (short-lived token for Puppeteer email notifications)
|
// Try email-render JWT (short-lived token for Puppeteer email notifications)
|
||||||
const user = await lookupUserByToken(token);
|
const user = await lookupUserByToken(token);
|
||||||
if (user) {
|
if (user) {
|
||||||
req.user = user;
|
req.user = { ...user, _objectType: 'user' };
|
||||||
req.session = { user };
|
req.session = { user };
|
||||||
return next();
|
return next();
|
||||||
}
|
}
|
||||||
@ -85,6 +85,7 @@ const isAuthenticated = async (req, res, next) => {
|
|||||||
if (hostId && authCode) {
|
if (hostId && authCode) {
|
||||||
const host = await getObject({ model: hostModel, id: hostId });
|
const host = await getObject({ model: hostModel, id: hostId });
|
||||||
if (host && host.authCode === authCode) {
|
if (host && host.authCode === authCode) {
|
||||||
|
req.user = { ...host, _objectType: 'host' };
|
||||||
return next();
|
return next();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,8 +5,32 @@ import { getFilter, convertPropertiesString, getSort } from '../../utils.js';
|
|||||||
|
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
|
|
||||||
const listAllowedFilters = ['name', 'width', 'height', 'createdAt', 'updatedAt', '_reference'];
|
const listAllowedFilters = [
|
||||||
const listAllowedSorters = ['name', 'width', 'height', 'infiniteHeight', 'createdAt', 'updatedAt'];
|
'name',
|
||||||
|
'width',
|
||||||
|
'height',
|
||||||
|
'printPadding',
|
||||||
|
'paddingLeft',
|
||||||
|
'paddingRight',
|
||||||
|
'paddingTop',
|
||||||
|
'paddingBottom',
|
||||||
|
'createdAt',
|
||||||
|
'updatedAt',
|
||||||
|
'_reference',
|
||||||
|
];
|
||||||
|
const listAllowedSorters = [
|
||||||
|
'name',
|
||||||
|
'width',
|
||||||
|
'height',
|
||||||
|
'infiniteHeight',
|
||||||
|
'printPadding',
|
||||||
|
'paddingLeft',
|
||||||
|
'paddingRight',
|
||||||
|
'paddingTop',
|
||||||
|
'paddingBottom',
|
||||||
|
'createdAt',
|
||||||
|
'updatedAt',
|
||||||
|
];
|
||||||
const propertiesAllowedFilters = [];
|
const propertiesAllowedFilters = [];
|
||||||
import {
|
import {
|
||||||
listDocumentSizesRouteHandler,
|
listDocumentSizesRouteHandler,
|
||||||
|
|||||||
@ -77,13 +77,34 @@ describe('Document Size Service Route Handlers', () => {
|
|||||||
|
|
||||||
describe('newDocumentSizeRouteHandler', () => {
|
describe('newDocumentSizeRouteHandler', () => {
|
||||||
it('should create a new document size', async () => {
|
it('should create a new document size', async () => {
|
||||||
req.body = { name: 'Letter', width: 216, height: 279 };
|
req.body = {
|
||||||
|
name: 'Letter',
|
||||||
|
width: 216,
|
||||||
|
height: 279,
|
||||||
|
infiniteHeight: false,
|
||||||
|
printPadding: false,
|
||||||
|
paddingLeft: 0,
|
||||||
|
paddingRight: 0,
|
||||||
|
paddingTop: 0,
|
||||||
|
paddingBottom: 0,
|
||||||
|
};
|
||||||
const mockSize = { _id: '456', ...req.body };
|
const mockSize = { _id: '456', ...req.body };
|
||||||
newObject.mockResolvedValue(mockSize);
|
newObject.mockResolvedValue(mockSize);
|
||||||
|
|
||||||
await newDocumentSizeRouteHandler(req, res);
|
await newDocumentSizeRouteHandler(req, res);
|
||||||
|
|
||||||
expect(newObject).toHaveBeenCalled();
|
expect(newObject).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
newData: expect.objectContaining({
|
||||||
|
name: 'Letter',
|
||||||
|
printPadding: false,
|
||||||
|
paddingLeft: 0,
|
||||||
|
paddingRight: 0,
|
||||||
|
paddingTop: 0,
|
||||||
|
paddingBottom: 0,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
);
|
||||||
expect(res.send).toHaveBeenCalledWith(mockSize);
|
expect(res.send).toHaveBeenCalledWith(mockSize);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -113,7 +113,7 @@ describe('Document Template Service Route Handlers', () => {
|
|||||||
'<Text>Hello</Text>',
|
'<Text>Hello</Text>',
|
||||||
{ name: 'Test' },
|
{ name: 'Test' },
|
||||||
1,
|
1,
|
||||||
{},
|
{ padding: true },
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
expect(res.send).toHaveBeenCalledWith(mockPreview);
|
expect(res.send).toHaveBeenCalledWith(mockPreview);
|
||||||
@ -155,12 +155,34 @@ describe('Document Template Service Route Handlers', () => {
|
|||||||
'<Text>Hello</Text>',
|
'<Text>Hello</Text>',
|
||||||
{ name: 'Test' },
|
{ name: 'Test' },
|
||||||
'pdf',
|
'pdf',
|
||||||
{}
|
{ padding: true }
|
||||||
);
|
);
|
||||||
expect(res.set).toHaveBeenCalledWith('Content-Type', 'application/pdf');
|
expect(res.set).toHaveBeenCalledWith('Content-Type', 'application/pdf');
|
||||||
expect(res.send).toHaveBeenCalledWith(pdf);
|
expect(res.send).toHaveBeenCalledWith(pdf);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('passes padding false when requested by the caller', async () => {
|
||||||
|
req.params.id = 'template-1';
|
||||||
|
req.query = { type: 'pdf', padding: 'false' };
|
||||||
|
req.body = { object: { name: 'Test' } };
|
||||||
|
templateManager.renderDownload.mockResolvedValue({
|
||||||
|
type: 'pdf',
|
||||||
|
mime: 'application/pdf',
|
||||||
|
extension: 'pdf',
|
||||||
|
buffers: [Buffer.from('pdf-data')],
|
||||||
|
});
|
||||||
|
|
||||||
|
await downloadDocumentTemplateRouteHandler(req, res);
|
||||||
|
|
||||||
|
expect(templateManager.renderDownload).toHaveBeenCalledWith(
|
||||||
|
'template-1',
|
||||||
|
undefined,
|
||||||
|
{ name: 'Test' },
|
||||||
|
'pdf',
|
||||||
|
{ padding: false }
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it('should return base64 images when multiple pages are rendered', async () => {
|
it('should return base64 images when multiple pages are rendered', async () => {
|
||||||
req.params.id = 'template-1';
|
req.params.id = 'template-1';
|
||||||
req.query = { type: 'png' };
|
req.query = { type: 'png' };
|
||||||
|
|||||||
@ -116,6 +116,11 @@ export const editDocumentSizeRouteHandler = async (req, res) => {
|
|||||||
width: req.body.width,
|
width: req.body.width,
|
||||||
height: req.body.height,
|
height: req.body.height,
|
||||||
infiniteHeight: req.body.infiniteHeight,
|
infiniteHeight: req.body.infiniteHeight,
|
||||||
|
printPadding: req.body.printPadding,
|
||||||
|
paddingLeft: req.body.paddingLeft,
|
||||||
|
paddingRight: req.body.paddingRight,
|
||||||
|
paddingTop: req.body.paddingTop,
|
||||||
|
paddingBottom: req.body.paddingBottom,
|
||||||
};
|
};
|
||||||
// Create audit log before updating
|
// Create audit log before updating
|
||||||
const result = await editObject({
|
const result = await editObject({
|
||||||
@ -143,6 +148,11 @@ export const newDocumentSizeRouteHandler = async (req, res) => {
|
|||||||
width: req.body.width,
|
width: req.body.width,
|
||||||
height: req.body.height,
|
height: req.body.height,
|
||||||
infiniteHeight: req.body.infiniteHeight,
|
infiniteHeight: req.body.infiniteHeight,
|
||||||
|
printPadding: req.body.printPadding,
|
||||||
|
paddingLeft: req.body.paddingLeft,
|
||||||
|
paddingRight: req.body.paddingRight,
|
||||||
|
paddingTop: req.body.paddingTop,
|
||||||
|
paddingBottom: req.body.paddingBottom,
|
||||||
};
|
};
|
||||||
const result = await newObject({
|
const result = await newObject({
|
||||||
model: documentSizeModel,
|
model: documentSizeModel,
|
||||||
|
|||||||
@ -20,6 +20,27 @@ import { formatTemplateContent } from '../../templates/templateformatter.js';
|
|||||||
const logger = log4js.getLogger('Document Templates');
|
const logger = log4js.getLogger('Document Templates');
|
||||||
logger.level = config.server.logLevel;
|
logger.level = config.server.logLevel;
|
||||||
|
|
||||||
|
function parseOptionalBoolean(value) {
|
||||||
|
if (value === undefined || value === null || value === '') {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
if (value === true || value === false) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
const lower = String(value).trim().toLowerCase();
|
||||||
|
if (lower === 'true' || lower === '1') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (lower === 'false' || lower === '0') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPaddingOption(req, defaultValue = true) {
|
||||||
|
return parseOptionalBoolean(req.body?.padding ?? req.query?.padding) ?? defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
export const listDocumentTemplatesRouteHandler = async (
|
export const listDocumentTemplatesRouteHandler = async (
|
||||||
req,
|
req,
|
||||||
res,
|
res,
|
||||||
@ -293,6 +314,7 @@ export const previewDocumentTemplateRouteHandler = async (req, res) => {
|
|||||||
const content = req.body?.content;
|
const content = req.body?.content;
|
||||||
const testObject = req.body?.testObject || req.body?.object || {};
|
const testObject = req.body?.testObject || req.body?.object || {};
|
||||||
const scale = req.body?.scale ?? 1;
|
const scale = req.body?.scale ?? 1;
|
||||||
|
const padding = getPaddingOption(req, true);
|
||||||
|
|
||||||
logger.debug(`Previewing document template with ID: ${id}`);
|
logger.debug(`Previewing document template with ID: ${id}`);
|
||||||
|
|
||||||
@ -301,7 +323,7 @@ export const previewDocumentTemplateRouteHandler = async (req, res) => {
|
|||||||
content,
|
content,
|
||||||
testObject,
|
testObject,
|
||||||
scale,
|
scale,
|
||||||
{},
|
{ padding },
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -320,7 +342,9 @@ export const downloadDocumentTemplateRouteHandler = async (req, res) => {
|
|||||||
const data = req.body?.object || req.body?.testObject || {};
|
const data = req.body?.object || req.body?.testObject || {};
|
||||||
const width = req.body?.width || req.query.width;
|
const width = req.body?.width || req.query.width;
|
||||||
const filename = req.body?.filename || req.query.filename || 'document';
|
const filename = req.body?.filename || req.query.filename || 'document';
|
||||||
const options = {};
|
const options = {
|
||||||
|
padding: getPaddingOption(req, true),
|
||||||
|
};
|
||||||
if (width) {
|
if (width) {
|
||||||
options.width = Number(width);
|
options.width = Number(width);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -91,11 +91,51 @@ jest.unstable_mockModule('../../config.js', () => ({
|
|||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const { TemplateManager } = await import('../templatemanager.js');
|
const { TemplateManager, resolveDocumentPadding } = await import('../templatemanager.js');
|
||||||
const { getObject } = await import('../../database/database.js');
|
const { getObject } = await import('../../database/database.js');
|
||||||
const ejs = (await import('ejs')).default;
|
const ejs = (await import('ejs')).default;
|
||||||
const { generatePDF } = await import('../pdffactory.js');
|
const { generatePDF } = await import('../pdffactory.js');
|
||||||
|
|
||||||
|
describe('resolveDocumentPadding', () => {
|
||||||
|
const paddedSize = {
|
||||||
|
printPadding: false,
|
||||||
|
paddingLeft: 10,
|
||||||
|
paddingRight: 8,
|
||||||
|
paddingTop: 6,
|
||||||
|
paddingBottom: 4,
|
||||||
|
};
|
||||||
|
|
||||||
|
it('does not apply padding unless requested or printPadding is true', () => {
|
||||||
|
expect(resolveDocumentPadding(paddedSize, false)).toEqual({
|
||||||
|
applyPadding: false,
|
||||||
|
paddingLeft: 0,
|
||||||
|
paddingRight: 0,
|
||||||
|
paddingTop: 0,
|
||||||
|
paddingBottom: 0,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('applies padding when requested', () => {
|
||||||
|
expect(resolveDocumentPadding(paddedSize, true)).toEqual({
|
||||||
|
applyPadding: true,
|
||||||
|
paddingLeft: 10,
|
||||||
|
paddingRight: 8,
|
||||||
|
paddingTop: 6,
|
||||||
|
paddingBottom: 4,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('applies padding when printPadding is true even if not requested', () => {
|
||||||
|
expect(resolveDocumentPadding({ ...paddedSize, printPadding: true }, false)).toEqual({
|
||||||
|
applyPadding: true,
|
||||||
|
paddingLeft: 10,
|
||||||
|
paddingRight: 8,
|
||||||
|
paddingTop: 6,
|
||||||
|
paddingBottom: 4,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('TemplateManager', () => {
|
describe('TemplateManager', () => {
|
||||||
let templateManager;
|
let templateManager;
|
||||||
|
|
||||||
@ -161,6 +201,87 @@ describe('TemplateManager', () => {
|
|||||||
expect(generatePDF).toHaveBeenCalled();
|
expect(generatePDF).toHaveBeenCalled();
|
||||||
expect(result.pdf).toBeDefined();
|
expect(result.pdf).toBeDefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('adds padding to page size when padding is requested', async () => {
|
||||||
|
getObject.mockResolvedValue({
|
||||||
|
documentSize: {
|
||||||
|
width: 100,
|
||||||
|
height: 50,
|
||||||
|
infiniteHeight: false,
|
||||||
|
printPadding: false,
|
||||||
|
paddingLeft: 10,
|
||||||
|
paddingRight: 10,
|
||||||
|
paddingTop: 5,
|
||||||
|
paddingBottom: 5,
|
||||||
|
},
|
||||||
|
global: false,
|
||||||
|
objectType: 'printer',
|
||||||
|
});
|
||||||
|
|
||||||
|
await templateManager.renderPDF('temp-id', 'content', {}, { padding: true });
|
||||||
|
|
||||||
|
expect(generatePDF).toHaveBeenCalledWith(
|
||||||
|
expect.any(String),
|
||||||
|
expect.objectContaining({
|
||||||
|
width: 120,
|
||||||
|
height: 60,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('adds padding when printPadding is true even if padding is false', async () => {
|
||||||
|
getObject.mockResolvedValue({
|
||||||
|
documentSize: {
|
||||||
|
width: 100,
|
||||||
|
height: 50,
|
||||||
|
infiniteHeight: false,
|
||||||
|
printPadding: true,
|
||||||
|
paddingLeft: 10,
|
||||||
|
paddingRight: 10,
|
||||||
|
paddingTop: 5,
|
||||||
|
paddingBottom: 5,
|
||||||
|
},
|
||||||
|
global: false,
|
||||||
|
objectType: 'printer',
|
||||||
|
});
|
||||||
|
|
||||||
|
await templateManager.renderPDF('temp-id', 'content', {}, { padding: false });
|
||||||
|
|
||||||
|
expect(generatePDF).toHaveBeenCalledWith(
|
||||||
|
expect.any(String),
|
||||||
|
expect.objectContaining({
|
||||||
|
width: 120,
|
||||||
|
height: 60,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('keeps the original page size when padding is not applied', async () => {
|
||||||
|
getObject.mockResolvedValue({
|
||||||
|
documentSize: {
|
||||||
|
width: 100,
|
||||||
|
height: 50,
|
||||||
|
infiniteHeight: false,
|
||||||
|
printPadding: false,
|
||||||
|
paddingLeft: 10,
|
||||||
|
paddingRight: 10,
|
||||||
|
paddingTop: 5,
|
||||||
|
paddingBottom: 5,
|
||||||
|
},
|
||||||
|
global: false,
|
||||||
|
objectType: 'printer',
|
||||||
|
});
|
||||||
|
|
||||||
|
await templateManager.renderPDF('temp-id', 'content', {}, { padding: false });
|
||||||
|
|
||||||
|
expect(generatePDF).toHaveBeenCalledWith(
|
||||||
|
expect.any(String),
|
||||||
|
expect.objectContaining({
|
||||||
|
width: 100,
|
||||||
|
height: 50,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('renderDownload', () => {
|
describe('renderDownload', () => {
|
||||||
|
|||||||
@ -18,18 +18,22 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.previewWrapper {
|
.previewWrapper {
|
||||||
width: <%= (scaledWidth) || '50mm' %>;
|
width: <%= (scaledPageWidth) || (scaledWidth) || '50mm' %>;
|
||||||
height: <%= (scaledHeight) || '50mm' %>;
|
height: <%= (scaledPageHeight) || (scaledHeight) || '50mm' %>;
|
||||||
}
|
}
|
||||||
.previewDocument {
|
.previewDocument {
|
||||||
width: <%= (width) || '50mm' %>;
|
width: <%= (width) || '50mm' %>;
|
||||||
height: <%= (height) || '50mm' %>;
|
height: <%= (height) || '50mm' %>;
|
||||||
|
padding: <%= (paddingTop) || '0mm' %> <%= (paddingRight) || '0mm' %> <%= (paddingBottom) || '0mm' %> <%= (paddingLeft) || '0mm' %>;
|
||||||
|
box-sizing: content-box;
|
||||||
transform: scale(<%= scale || '1' %>);
|
transform: scale(<%= scale || '1' %>);
|
||||||
transform-origin: top left;
|
transform-origin: top left;
|
||||||
}
|
}
|
||||||
.renderDocument {
|
.renderDocument {
|
||||||
width: <%= (scaledWidth) || '50mm' %>;
|
width: <%= (width) || '50mm' %>;
|
||||||
height: <%= (scaledHeight) || '50mm' %>;
|
height: <%= (height) || '50mm' %>;
|
||||||
|
padding: <%= (paddingTop) || '0mm' %> <%= (paddingRight) || '0mm' %> <%= (paddingBottom) || '0mm' %> <%= (paddingLeft) || '0mm' %>;
|
||||||
|
box-sizing: content-box;
|
||||||
transform: scale(<%= scale || '1' %>);
|
transform: scale(<%= scale || '1' %>);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -25,6 +25,11 @@ body {
|
|||||||
transform-origin: top left;
|
transform-origin: top left;
|
||||||
position: relative;
|
position: relative;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
box-sizing: content-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.renderDocument {
|
||||||
|
box-sizing: content-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
.documentText {
|
.documentText {
|
||||||
|
|||||||
@ -357,6 +357,32 @@ function imageToSvg(pngBuffer, widthPx, heightPx, documentWidthMm, documentHeigh
|
|||||||
</svg>`;
|
</svg>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function toPaddingMm(value) {
|
||||||
|
const numeric = Number(value);
|
||||||
|
return Number.isFinite(numeric) && numeric > 0 ? numeric : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function resolveDocumentPadding(documentSize = {}, paddingRequested = false) {
|
||||||
|
const applyPadding = documentSize.printPadding == true || paddingRequested == true;
|
||||||
|
if (!applyPadding) {
|
||||||
|
return {
|
||||||
|
applyPadding: false,
|
||||||
|
paddingLeft: 0,
|
||||||
|
paddingRight: 0,
|
||||||
|
paddingTop: 0,
|
||||||
|
paddingBottom: 0,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
applyPadding: true,
|
||||||
|
paddingLeft: toPaddingMm(documentSize.paddingLeft),
|
||||||
|
paddingRight: toPaddingMm(documentSize.paddingRight),
|
||||||
|
paddingTop: toPaddingMm(documentSize.paddingTop),
|
||||||
|
paddingBottom: toPaddingMm(documentSize.paddingBottom),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export class TemplateManager {
|
export class TemplateManager {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.fc = {
|
this.fc = {
|
||||||
@ -368,9 +394,10 @@ export class TemplateManager {
|
|||||||
|
|
||||||
async renderTemplate(id, content, data = {}, scale = 1, options = {}, preview = true) {
|
async renderTemplate(id, content, data = {}, scale = 1, options = {}, preview = true) {
|
||||||
try {
|
try {
|
||||||
|
const { padding: paddingRequested = false, ...ejsOptions } = options;
|
||||||
const defaultOptions = {
|
const defaultOptions = {
|
||||||
async: true,
|
async: true,
|
||||||
...options,
|
...ejsOptions,
|
||||||
};
|
};
|
||||||
logger.debug('Rendering template:', id);
|
logger.debug('Rendering template:', id);
|
||||||
|
|
||||||
@ -477,6 +504,11 @@ export class TemplateManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const infiniteHeight = documentSize.infiniteHeight == true;
|
const infiniteHeight = documentSize.infiniteHeight == true;
|
||||||
|
const padding = resolveDocumentPadding(documentSize, paddingRequested == true);
|
||||||
|
const pageWidth = (documentSize.width || 0) + padding.paddingLeft + padding.paddingRight;
|
||||||
|
const pageHeight = infiniteHeight
|
||||||
|
? null
|
||||||
|
: (documentSize.height || 0) + padding.paddingTop + padding.paddingBottom;
|
||||||
|
|
||||||
const baseHtml = await ejs.render(
|
const baseHtml = await ejs.render(
|
||||||
baseTemplate,
|
baseTemplate,
|
||||||
@ -486,6 +518,12 @@ export class TemplateManager {
|
|||||||
height: infiniteHeight ? 'fit-content' : `${documentSize.height}mm`,
|
height: infiniteHeight ? 'fit-content' : `${documentSize.height}mm`,
|
||||||
scaledWidth: `${documentSize.width * scale}mm`,
|
scaledWidth: `${documentSize.width * scale}mm`,
|
||||||
scaledHeight: infiniteHeight ? 'auto' : `${documentSize.height * scale}mm`,
|
scaledHeight: infiniteHeight ? 'auto' : `${documentSize.height * scale}mm`,
|
||||||
|
scaledPageWidth: `${pageWidth * scale}mm`,
|
||||||
|
scaledPageHeight: infiniteHeight ? 'auto' : `${pageHeight * scale}mm`,
|
||||||
|
paddingTop: `${padding.paddingTop}mm`,
|
||||||
|
paddingRight: `${padding.paddingRight}mm`,
|
||||||
|
paddingBottom: `${padding.paddingBottom}mm`,
|
||||||
|
paddingLeft: `${padding.paddingLeft}mm`,
|
||||||
scale: `${scale}`,
|
scale: `${scale}`,
|
||||||
baseCSS: baseCSS,
|
baseCSS: baseCSS,
|
||||||
previewPaginationScript: preview ? previewPaginationScript : '',
|
previewPaginationScript: preview ? previewPaginationScript : '',
|
||||||
@ -495,9 +533,14 @@ export class TemplateManager {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
html: baseHtml,
|
html: baseHtml,
|
||||||
width: documentSize.width,
|
width: pageWidth,
|
||||||
height: infiniteHeight ? 'auto' : documentSize.height,
|
height: infiniteHeight ? 'auto' : pageHeight,
|
||||||
infiniteHeight: infiniteHeight,
|
infiniteHeight: infiniteHeight,
|
||||||
|
printPadding: documentSize.printPadding == true,
|
||||||
|
paddingLeft: padding.paddingLeft,
|
||||||
|
paddingRight: padding.paddingRight,
|
||||||
|
paddingTop: padding.paddingTop,
|
||||||
|
paddingBottom: padding.paddingBottom,
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
@ -548,12 +591,13 @@ export class TemplateManager {
|
|||||||
try {
|
try {
|
||||||
logger.debug(`Rendering ${format} for template:`, id);
|
logger.debug(`Rendering ${format} for template:`, id);
|
||||||
|
|
||||||
|
const { padding: _padding, ...imageOptions } = options;
|
||||||
const pdfResult = await this.renderPDF(id, content, data, options);
|
const pdfResult = await this.renderPDF(id, content, data, options);
|
||||||
if (pdfResult.error != undefined) {
|
if (pdfResult.error != undefined) {
|
||||||
return { error: pdfResult.error, code: pdfResult.code };
|
return { error: pdfResult.error, code: pdfResult.code };
|
||||||
}
|
}
|
||||||
|
|
||||||
const images = await convertPDFToImage(pdfResult.pdf, options);
|
const images = await convertPDFToImage(pdfResult.pdf, imageOptions);
|
||||||
const converted = [];
|
const converted = [];
|
||||||
for (const image of images) {
|
for (const image of images) {
|
||||||
if (format === 'png') {
|
if (format === 'png') {
|
||||||
@ -573,12 +617,13 @@ export class TemplateManager {
|
|||||||
try {
|
try {
|
||||||
logger.debug('Rendering SVG for template:', id);
|
logger.debug('Rendering SVG for template:', id);
|
||||||
|
|
||||||
|
const { padding: _padding, ...imageOptions } = options;
|
||||||
const pdfResult = await this.renderPDF(id, content, data, options);
|
const pdfResult = await this.renderPDF(id, content, data, options);
|
||||||
if (pdfResult.error != undefined) {
|
if (pdfResult.error != undefined) {
|
||||||
return { error: pdfResult.error, code: pdfResult.code };
|
return { error: pdfResult.error, code: pdfResult.code };
|
||||||
}
|
}
|
||||||
|
|
||||||
const images = await convertPDFToImage(pdfResult.pdf, options);
|
const images = await convertPDFToImage(pdfResult.pdf, imageOptions);
|
||||||
const svgs = [];
|
const svgs = [];
|
||||||
for (const image of images) {
|
for (const image of images) {
|
||||||
const png = await sharp(image).png().toBuffer();
|
const png = await sharp(image).png().toBuffer();
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user