feat: add option to truncate long course descriptions to single line (default on, matches official PDF)
This commit is contained in:
@@ -64,6 +64,22 @@
|
||||
return out.length ? out : [''];
|
||||
}
|
||||
|
||||
// Truncate a string to the longest prefix that fits within maxWidth at the
|
||||
// given size, hard-cutting mid-word with no ellipsis. Mirrors the official
|
||||
// PDF which cuts off course descriptions that exceed the column width.
|
||||
function truncateToWidth(text, font, size, maxWidth) {
|
||||
const s = String(text == null ? '' : text).trim();
|
||||
if (!s) return '';
|
||||
if (font.widthOfTextAtSize(s, size) <= maxWidth) return s;
|
||||
let lo = 0, hi = s.length;
|
||||
while (lo < hi) {
|
||||
const mid = Math.ceil((lo + hi) / 2);
|
||||
if (font.widthOfTextAtSize(s.slice(0, mid), size) <= maxWidth) lo = mid;
|
||||
else hi = mid - 1;
|
||||
}
|
||||
return s.slice(0, lo);
|
||||
}
|
||||
|
||||
// Compact a degree name for display:
|
||||
// "BACHELOR OF SCIENCE IN INFORMATION TECHNOLOGY" -> "BS IT".
|
||||
// Multi-word fields become initials; single-word fields stay title-cased.
|
||||
@@ -177,6 +193,9 @@
|
||||
const { PDFDocument, StandardFonts, rgb } = PDFLib;
|
||||
const header = data.header || {};
|
||||
const courses = data.courses || [];
|
||||
// Truncate long course descriptions to a single line matching the official
|
||||
// PDF (default ON). Disabling allows descriptions to wrap.
|
||||
const truncateDescription = data.truncateDescription !== false;
|
||||
|
||||
const doc = await PDFDocument.create();
|
||||
let page = doc.addPage([612, 792]); // Letter
|
||||
@@ -278,7 +297,12 @@
|
||||
return cols.map((c) => {
|
||||
let lines;
|
||||
if (c.key === 'description') {
|
||||
lines = wrapLines(cells.description, font, cellSize, c.w - 6);
|
||||
if (truncateDescription) {
|
||||
// Single line, hard-truncated to fit the column (official PDF style).
|
||||
lines = [truncateToWidth(cells.description, font, cellSize, c.w - 3)];
|
||||
} else {
|
||||
lines = wrapLines(cells.description, font, cellSize, c.w - 6);
|
||||
}
|
||||
const sched = String(cells.schedule || '');
|
||||
if (sched) lines.push(...wrapLines(sched, font, cellSize, c.w - 6));
|
||||
} else {
|
||||
@@ -408,5 +432,5 @@
|
||||
return doc.save(); // returns Promise<Uint8Array>
|
||||
}
|
||||
|
||||
return { buildStudyLoadPdf, compactProgram, deriveDateRange, formatSchedule, formatFullName };
|
||||
return { buildStudyLoadPdf, compactProgram, deriveDateRange, formatSchedule, formatFullName, truncateToWidth };
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user