Use actual font files in PDF generation and fix footer display
Update pdf-renderer.ts to map specific font families to their corresponding files, and add the font files to the assets directory. Adjust footer height calculation to ensure proper display.
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -145,6 +145,16 @@ const FONT_FILES = {
|
||||
arabicSansBold: "NotoSansArabic-Bold.ttf",
|
||||
latinRegular: "DejaVuSans.ttf",
|
||||
latinBold: "DejaVuSans-Bold.ttf",
|
||||
dinNextRegular: "DINNextLTArabic-Regular.ttf",
|
||||
dinNextBold: "DINNextLTArabic-Bold.ttf",
|
||||
tajawalRegular: "Tajawal-Regular.ttf",
|
||||
tajawalBold: "Tajawal-Bold.ttf",
|
||||
helvNeueLTArRegular: "HelveticaNeueLTArabic-Regular.ttf",
|
||||
helvNeueLTArBold: "HelveticaNeueLTArabic-Bold.ttf",
|
||||
helvNeueRegular: "HelveticaNeue-Regular.ttf",
|
||||
helvNeueBold: "HelveticaNeue-Bold.ttf",
|
||||
majallaRegular: "Majalla-Regular.ttf",
|
||||
majallaBold: "Majalla-Bold.ttf",
|
||||
} as const;
|
||||
|
||||
type FontFileKey = keyof typeof FONT_FILES;
|
||||
@@ -163,15 +173,9 @@ type FamilyMapping = {
|
||||
latinBold: FontFileKey;
|
||||
};
|
||||
|
||||
// Web fonts (DIN Next LT Arabic, Tajawal, Helvetica Neue LT Arabic,
|
||||
// Helvetica Neue, Majalla) are not bundled into the PDF renderer
|
||||
// because we do not ship their font files server-side. Instead we map
|
||||
// each web family to the closest visual stand-in already bundled here:
|
||||
// sans-serif Arabic families render with NotoSansArabic; serif/Naskh
|
||||
// stand-ins use NotoNaskhArabic; pure-Latin families (Helvetica Neue)
|
||||
// fall back to the Naskh pair so Arabic glyphs in mixed strings still
|
||||
// render correctly. This keeps PDFs readable without ballooning the
|
||||
// server bundle.
|
||||
// Every user-facing font family now maps to its actual bundled font
|
||||
// files. The user uploaded all font assets so the PDF renders with the
|
||||
// exact typeface chosen in settings — no more Noto stand-ins.
|
||||
const FAMILY_MAP: Record<string, FamilyMapping> = {
|
||||
system: {
|
||||
arabicRegular: "naskhRegular",
|
||||
@@ -180,34 +184,34 @@ const FAMILY_MAP: Record<string, FamilyMapping> = {
|
||||
latinBold: "latinBold",
|
||||
},
|
||||
"DIN Next LT Arabic": {
|
||||
arabicRegular: "arabicSansRegular",
|
||||
arabicBold: "arabicSansBold",
|
||||
latinRegular: "latinRegular",
|
||||
latinBold: "latinBold",
|
||||
arabicRegular: "dinNextRegular",
|
||||
arabicBold: "dinNextBold",
|
||||
latinRegular: "dinNextRegular",
|
||||
latinBold: "dinNextBold",
|
||||
},
|
||||
Tajawal: {
|
||||
arabicRegular: "arabicSansRegular",
|
||||
arabicBold: "arabicSansBold",
|
||||
latinRegular: "latinRegular",
|
||||
latinBold: "latinBold",
|
||||
arabicRegular: "tajawalRegular",
|
||||
arabicBold: "tajawalBold",
|
||||
latinRegular: "tajawalRegular",
|
||||
latinBold: "tajawalBold",
|
||||
},
|
||||
"Helvetica Neue LT Arabic": {
|
||||
arabicRegular: "arabicSansRegular",
|
||||
arabicBold: "arabicSansBold",
|
||||
latinRegular: "latinRegular",
|
||||
latinBold: "latinBold",
|
||||
arabicRegular: "helvNeueLTArRegular",
|
||||
arabicBold: "helvNeueLTArBold",
|
||||
latinRegular: "helvNeueLTArRegular",
|
||||
latinBold: "helvNeueLTArBold",
|
||||
},
|
||||
"Helvetica Neue": {
|
||||
arabicRegular: "naskhRegular",
|
||||
arabicBold: "naskhBold",
|
||||
latinRegular: "latinRegular",
|
||||
latinBold: "latinBold",
|
||||
latinRegular: "helvNeueRegular",
|
||||
latinBold: "helvNeueBold",
|
||||
},
|
||||
Majalla: {
|
||||
arabicRegular: "naskhRegular",
|
||||
arabicBold: "naskhBold",
|
||||
latinRegular: "latinRegular",
|
||||
latinBold: "latinBold",
|
||||
arabicRegular: "majallaRegular",
|
||||
arabicBold: "majallaBold",
|
||||
latinRegular: "majallaRegular",
|
||||
latinBold: "majallaBold",
|
||||
},
|
||||
};
|
||||
|
||||
@@ -658,36 +662,46 @@ export async function renderSchedulePdf(input: RenderPdfInput): Promise<Buffer>
|
||||
|
||||
function drawFooter(): void {
|
||||
const longDate = formatLongDate(input.date);
|
||||
const labelSize = Math.max(11, Math.round(input.font.fontSize * 0.95));
|
||||
const dateSize = Math.max(11, Math.round(input.font.fontSize * 0.95));
|
||||
const footerHeight = labelSize * 1.4 + dateSize * 1.4;
|
||||
const sz = Math.max(11, Math.round(input.font.fontSize * 0.95));
|
||||
const lineStep = sz * 1.5;
|
||||
const footerHeight = lineStep * 2 + 4;
|
||||
const footerWidth =
|
||||
doc.page.width - doc.page.margins.left - doc.page.margins.right;
|
||||
const footerY = doc.page.height - doc.page.margins.bottom - footerHeight;
|
||||
const footerAlign: "left" | "right" = baseDir === "rtl" ? "right" : "left";
|
||||
drawMixedLine(doc, input.labels.recordedBy, {
|
||||
x: doc.page.margins.left,
|
||||
y: footerY,
|
||||
|
||||
// Temporarily zero-out the bottom margin AND save/restore doc.y so
|
||||
// PDFKit never auto-paginates when we render near the page bottom.
|
||||
const savedBottom = doc.page.margins.bottom;
|
||||
const savedY = doc.y;
|
||||
doc.page.margins.bottom = 0;
|
||||
|
||||
// --- Label line ("مقيد" / "Recorded by") ---
|
||||
const labelIsArabic = [...input.labels.recordedBy].filter(isArabicChar).length * 2 > input.labels.recordedBy.length;
|
||||
const labelFont = fontKeyFor(labelIsArabic, "bold", mapping);
|
||||
const features = featuresFor(labelIsArabic);
|
||||
doc.font(labelFont).fontSize(sz).fillColor("black");
|
||||
doc.text(input.labels.recordedBy, doc.page.margins.left, footerY, {
|
||||
width: footerWidth,
|
||||
fontSize: labelSize,
|
||||
weight: "bold",
|
||||
align: footerAlign,
|
||||
baseDirection: baseDir,
|
||||
mapping,
|
||||
lineBreak: false,
|
||||
...(features ? { features } : {}),
|
||||
});
|
||||
drawMixedLine(doc, longDate, {
|
||||
x: doc.page.margins.left,
|
||||
y: footerY + labelSize * 1.4,
|
||||
|
||||
// --- Date line ("3 May 2026") ---
|
||||
// Reset doc.y BEFORE the second call so PDFKit doesn't think
|
||||
// we've exceeded the page boundary.
|
||||
doc.y = footerY;
|
||||
const dateFont = fontKeyFor(false, "regular", mapping);
|
||||
doc.font(dateFont).fontSize(sz).fillColor("black");
|
||||
doc.text(longDate, doc.page.margins.left, footerY + lineStep, {
|
||||
width: footerWidth,
|
||||
fontSize: dateSize,
|
||||
weight: "regular",
|
||||
align: footerAlign,
|
||||
// Force LTR for the date — the long-form date is always Latin
|
||||
// ("3 May 2026"), so an RTL base direction would visually reorder
|
||||
// the runs ("May 2026 3"). Alignment still mirrors the locale.
|
||||
baseDirection: "ltr",
|
||||
mapping,
|
||||
lineBreak: false,
|
||||
});
|
||||
|
||||
doc.page.margins.bottom = savedBottom;
|
||||
doc.y = savedY;
|
||||
}
|
||||
|
||||
// ---- Table layout ----
|
||||
|
||||
Reference in New Issue
Block a user