diff --git a/pdf.js b/pdf.js index 8ec57f7..ac792fb 100644 --- a/pdf.js +++ b/pdf.js @@ -30,12 +30,18 @@ } // Format the raw schedule string to match the official PDF style: - // comma-separate day codes (M W -> M,W) and trim duplicated room suffixes. + // comma-separate day codes, trim duplicated room suffixes, and split + // multiple meeting segments onto separate lines. function formatSchedule(schedule) { let s = String(schedule || '').replace(/\s+/g, ' ').trim(); if (!s) return s; + // Collapse duplicated TC anywhere in the string. + while (/TC\s+TC/.test(s)) s = s.replace(/TC\s+TC/g, 'TC'); + // Join adjacent day codes with commas. s = s.replace(/\b(M|Tu|T|W|Th|F|Sa|Su)\s+(M|Tu|T|W|Th|F|Sa|Su)\b/g, '$1,$2'); - s = s.replace(/TC(?:\s+TC)+$/g, 'TC'); + // Split concatenated meeting segments (room ending in TC followed by a + // day code) onto separate lines. + s = s.replace(/(TC)\s+(?=(?:Su|Sa|Th|Tu|M|W|F|T)\b)/g, '$1\n'); return s; } @@ -211,7 +217,7 @@ if (c.key === 'description') { lines = wrapLines(cells.description, font, cellSize, c.w - 6); const sched = String(cells.schedule || ''); - if (sched) lines.push(...wrapText(sched, font, cellSize, c.w - 6)); + if (sched) lines.push(...wrapLines(sched, font, cellSize, c.w - 6)); } else { lines = wrapLines(cells[c.key], font, cellSize, c.w - 6); } @@ -256,8 +262,10 @@ } // Data rows: fixed 24.5pt baseline-to-baseline spacing. Description + - // schedule stack with 12pt line spacing. - const rowHeight = 24.5; + // schedule stack with 12pt line spacing. Expand the row height when + // the schedule or wrapped description needs more lines. + const maxLines = Math.max(...linesPerCol.map((l) => l.length)); + const rowHeight = Math.max(24.5, maxLines * lineH + 0.5); if (y - rowHeight < 50) { page = doc.addPage([612, 792]); y = 792 - 10; diff --git a/test/pdf.test.js b/test/pdf.test.js index e2ff615..222a0e2 100644 --- a/test/pdf.test.js +++ b/test/pdf.test.js @@ -65,6 +65,14 @@ test('formatSchedule normalizes schedule text', () => { assert.strictEqual(formatSchedule('M W 07:30 AM - 10:00 AM LB447TC TC'), 'M,W 07:30 AM - 10:00 AM LB447TC'); assert.strictEqual(formatSchedule('T Th 09:00 AM - 10:30 AM LB443TC TC'), 'T,Th 09:00 AM - 10:30 AM LB443TC'); assert.strictEqual(formatSchedule('M,W 03:00 PM - 05:30 PM LB443TC'), 'M,W 03:00 PM - 05:30 PM LB443TC'); + assert.strictEqual( + formatSchedule('T 07:30 AM - 10:30 AM LB448TC TC Th 07:30 AM - 10:30 AM LB448TC'), + 'T 07:30 AM - 10:30 AM LB448TC\nTh 07:30 AM - 10:30 AM LB448TC' + ); + assert.strictEqual( + formatSchedule('W 01:30 PM - 04:30 PM LB448TC TC M 01:30 PM - 04:30 PM LB448TC'), + 'W 01:30 PM - 04:30 PM LB448TC\nM 01:30 PM - 04:30 PM LB448TC' + ); }); test('formatFullName reorders first-last names to last-first', () => {