fix: use pdf-lib embedFont with proper pagination for CJK PDF
This commit is contained in:
@@ -4,11 +4,9 @@ import { PDFDocument, rgb, StandardFonts, PageSizes } from 'pdf-lib';
|
||||
|
||||
const FONT_SEARCH_PATHS = [
|
||||
path.join(__dirname, '..', '..', '..', 'assets', 'fonts', 'NotoSansSC-VF.ttf'),
|
||||
'C:/Windows/Fonts/msyh.ttc',
|
||||
'C:/Windows/Fonts/msyhbd.ttc',
|
||||
'C:/Windows/Fonts/simsun.ttc',
|
||||
'C:/Windows/Fonts/msyh.ttf',
|
||||
'C:/Windows/Fonts/simsun.ttf',
|
||||
'/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc',
|
||||
'/usr/share/fonts/truetype/noto/NotoSansCJK-Regular.ttc',
|
||||
];
|
||||
|
||||
let cachedFontBytes: Buffer | null = null;
|
||||
@@ -61,59 +59,39 @@ export async function generateAssessmentPdf(options: PdfReportOptions): Promise<
|
||||
const sectionSize = 13;
|
||||
const lineHeight = fontSize * 1.6;
|
||||
|
||||
function drawPage(title: string, titleSize: number, sections: PdfSection[]): void {
|
||||
const page = doc.addPage([pageWidth, pageHeight]);
|
||||
let y = pageHeight - margin;
|
||||
let page = doc.addPage([pageWidth, pageHeight]);
|
||||
let y = pageHeight - margin;
|
||||
|
||||
page.drawText(title, { x: margin, y, size: titleSize, font, color: rgb(0, 0, 0) });
|
||||
y -= titleSize * 1.8;
|
||||
|
||||
if (options.subtitle) {
|
||||
page.drawText(options.subtitle, { x: margin, y, size: 9, font, color: rgb(0.4, 0.4, 0.4) });
|
||||
y -= 16;
|
||||
}
|
||||
|
||||
for (const section of sections) {
|
||||
y -= 8;
|
||||
if (y < margin + sectionSize * 2) {
|
||||
y = pageHeight - margin;
|
||||
}
|
||||
page.drawText(section.title, { x: margin, y, size: sectionSize, font, color: rgb(0.1, 0.1, 0.1) });
|
||||
y -= sectionSize * 1.8;
|
||||
|
||||
for (const line of section.lines) {
|
||||
const chunks = wrapLine(line, font, fontSize, pageWidth - margin * 2);
|
||||
for (const chunk of chunks) {
|
||||
if (y < margin + lineHeight) {
|
||||
y = pageHeight - margin;
|
||||
}
|
||||
page.drawText(chunk, { x: margin, y, size: fontSize, font, color: rgb(0.2, 0.2, 0.2) });
|
||||
y -= lineHeight;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
page.drawText('--- End of Report ---', { x: margin, y: margin, size: 8, font, color: rgb(0.6, 0.6, 0.6) });
|
||||
function newPage() {
|
||||
page = doc.addPage([pageWidth, pageHeight]);
|
||||
y = pageHeight - margin;
|
||||
}
|
||||
|
||||
drawPage(options.title, titleSize, options.sections);
|
||||
function drawText(text: string, size: number, color: any, offsetY: number) {
|
||||
if (y < margin + offsetY) newPage();
|
||||
page.drawText(text, { x: margin, y, size, font, color });
|
||||
y -= offsetY;
|
||||
}
|
||||
|
||||
drawText(options.title, titleSize, rgb(0, 0, 0), titleSize * 1.8);
|
||||
|
||||
if (options.subtitle) {
|
||||
drawText(options.subtitle, 9, rgb(0.4, 0.4, 0.4), 16);
|
||||
}
|
||||
|
||||
for (const section of options.sections) {
|
||||
y -= 8;
|
||||
drawText(section.title, sectionSize, rgb(0.1, 0.1, 0.1), sectionSize * 1.8);
|
||||
|
||||
for (const line of section.lines) {
|
||||
if (!line) continue;
|
||||
for (const chunk of line.split('\n')) {
|
||||
drawText(chunk || ' ', fontSize, rgb(0.2, 0.2, 0.2), lineHeight);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
drawText('--- End of Report ---', 8, rgb(0.6, 0.6, 0.6), 20);
|
||||
|
||||
return Buffer.from(await doc.save());
|
||||
}
|
||||
|
||||
function wrapLine(text: string, _font: any, fontSize: number, maxWidth: number): string[] {
|
||||
if (!text || text.length === 0) return [''];
|
||||
const chunks: string[] = [];
|
||||
const lines = text.split('\n');
|
||||
for (const line of lines) {
|
||||
if (line.length === 0) {
|
||||
chunks.push('');
|
||||
continue;
|
||||
}
|
||||
const estimatedCharsPerLine = Math.max(1, Math.floor(maxWidth / (fontSize * 0.6)));
|
||||
for (let i = 0; i < line.length; i += estimatedCharsPerLine) {
|
||||
chunks.push(line.substring(i, i + estimatedCharsPerLine));
|
||||
}
|
||||
}
|
||||
return chunks.length > 0 ? chunks : [text];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user