64 lines
2 KiB
JavaScript
64 lines
2 KiB
JavaScript
/**
|
|
* Minimal PDF builder for retrieval receipts (no native deps).
|
|
* @module lib/receiptPdf
|
|
*/
|
|
|
|
/**
|
|
* @param {string} s
|
|
* @returns {string}
|
|
*/
|
|
function pdfEscape(s) {
|
|
return String(s).replace(/\\/g, '\\\\').replace(/\(/g, '\\(').replace(/\)/g, '\\)');
|
|
}
|
|
|
|
/**
|
|
* @param {object} receipt
|
|
* @returns {Buffer}
|
|
*/
|
|
export function buildReceiptPdf(receipt) {
|
|
const lines = [
|
|
'Verae Time — certified retrieval receipt',
|
|
`Type: ${receipt.type}`,
|
|
`Job ID: ${receipt.jobId}`,
|
|
`SHA256: ${receipt.sha256 ?? ''}`,
|
|
`Original timestamp: ${receipt.timestamp ?? ''}`,
|
|
`Retrieved at: ${receipt.extraSeal?.retrievedAt ?? ''}`,
|
|
`Tenant: ${receipt.extraSeal?.tenantId ?? ''}`,
|
|
`Seal event: ${receipt.extraSeal?.event ?? ''}`,
|
|
`Certificate: ${String(receipt.certificate ?? '').slice(0, 80)}`,
|
|
];
|
|
|
|
const commands = lines
|
|
.map((line, i) => {
|
|
const y = 720 - i * 18;
|
|
return `BT /F1 11 Tf 50 ${y} Td (${pdfEscape(line)}) Tj ET`;
|
|
})
|
|
.join('\n');
|
|
|
|
const stream = `${commands}\n`;
|
|
const objects = [
|
|
'1 0 obj << /Type /Catalog /Pages 2 0 R >> endobj',
|
|
'2 0 obj << /Type /Pages /Kids [3 0 R] /Count 1 >> endobj',
|
|
'3 0 obj << /Type /Page /Parent 2 0 R /MediaBox [0 0 612 792] /Contents 4 0 R /Resources << /Font << /F1 5 0 R >> >> >> endobj',
|
|
`4 0 obj << /Length ${Buffer.byteLength(stream)} >> stream\n${stream}endstream endobj`,
|
|
'5 0 obj << /Type /Font /Subtype /Type1 /BaseFont /Helvetica >> endobj',
|
|
];
|
|
|
|
let offset = '%PDF-1.4\n'.length;
|
|
const xref = [0];
|
|
let body = '%PDF-1.4\n';
|
|
for (const obj of objects) {
|
|
xref.push(offset);
|
|
const chunk = `${obj}\n`;
|
|
body += chunk;
|
|
offset += Buffer.byteLength(chunk);
|
|
}
|
|
const xrefStart = offset;
|
|
let xrefTable = `xref\n0 ${objects.length + 1}\n0000000000 65535 f \n`;
|
|
for (let i = 1; i < xref.length; i += 1) {
|
|
xrefTable += `${String(xref[i]).padStart(10, '0')} 00000 n \n`;
|
|
}
|
|
body += xrefTable;
|
|
body += `trailer << /Size ${objects.length + 1} /Root 1 0 R >>\nstartxref\n${xrefStart}\n%%EOF\n`;
|
|
return Buffer.from(body, 'utf8');
|
|
}
|