fix: widen NOTE column and add pagination to study load PDF
This commit is contained in:
@@ -36,7 +36,7 @@
|
|||||||
const courses = data.courses || [];
|
const courses = data.courses || [];
|
||||||
|
|
||||||
const doc = await PDFDocument.create();
|
const doc = await PDFDocument.create();
|
||||||
const page = doc.addPage([612, 792]); // Letter
|
let page = doc.addPage([612, 792]); // Letter
|
||||||
const font = doc.embedStandardFont(StandardFonts.Helvetica);
|
const font = doc.embedStandardFont(StandardFonts.Helvetica);
|
||||||
const bold = doc.embedStandardFont(StandardFonts.HelveticaBold);
|
const bold = doc.embedStandardFont(StandardFonts.HelveticaBold);
|
||||||
const margin = 50;
|
const margin = 50;
|
||||||
@@ -70,12 +70,12 @@
|
|||||||
|
|
||||||
// Table columns: NOTE, UNITS, TEACHER, COURSE DESCRIPTION, COURSE, GROUP
|
// Table columns: NOTE, UNITS, TEACHER, COURSE DESCRIPTION, COURSE, GROUP
|
||||||
const cols = [
|
const cols = [
|
||||||
{ key: 'status', label: 'NOTE', x: margin, w: 40 },
|
{ key: 'status', label: 'NOTE', x: margin, w: 50 },
|
||||||
{ key: 'units', label: 'UNITS', x: margin + 40, w: 45 },
|
{ key: 'units', label: 'UNITS', x: margin + 50, w: 45 },
|
||||||
{ key: 'teacher', label: 'TEACHER', x: margin + 85, w: 110 },
|
{ key: 'teacher', label: 'TEACHER', x: margin + 95, w: 110 },
|
||||||
{ key: 'description', label: 'COURSE DESCRIPTION', x: margin + 195, w: 150 },
|
{ key: 'description', label: 'COURSE DESCRIPTION', x: margin + 205, w: 150 },
|
||||||
{ key: 'courseCode', label: 'COURSE', x: margin + 345, w: 70 },
|
{ key: 'courseCode', label: 'COURSE', x: margin + 355, w: 70 },
|
||||||
{ key: 'groupSchedule', label: 'GROUP', x: margin + 415, w: 97 }
|
{ key: 'groupSchedule', label: 'GROUP', x: margin + 425, w: 87 }
|
||||||
];
|
];
|
||||||
const cellSize = 9;
|
const cellSize = 9;
|
||||||
const headerSize = 9;
|
const headerSize = 9;
|
||||||
@@ -84,10 +84,12 @@
|
|||||||
const f = isHeader ? bold : font;
|
const f = isHeader ? bold : font;
|
||||||
const size = isHeader ? headerSize : cellSize;
|
const size = isHeader ? headerSize : cellSize;
|
||||||
const linesPerCol = cols.map((c) => wrapText(cells[c.key] || '', f, size, c.w - 6));
|
const linesPerCol = cols.map((c) => wrapText(cells[c.key] || '', f, size, c.w - 6));
|
||||||
const rowHeight = Math.max.apply(null, linesPerCol.map((l) => l.length)) * 11 + 8;
|
const rowHeight = Math.max(...linesPerCol.map((l) => l.length)) * 11 + 8;
|
||||||
if (y - rowHeight < 50) {
|
if (!isHeader && y - rowHeight < 50) {
|
||||||
page.drawText('', { x: margin, y: y, size: 1, font, color: black });
|
// Row doesn't fit on the current page: start a new page and re-draw the header.
|
||||||
return false;
|
page = doc.addPage([612, 792]);
|
||||||
|
y = 792 - 50;
|
||||||
|
drawHeaderRow();
|
||||||
}
|
}
|
||||||
if (isHeader) {
|
if (isHeader) {
|
||||||
page.drawRectangle({ x: margin, y: y - rowHeight, width: contentWidth, height: rowHeight, color: rgb(0.85, 0.9, 0.95) });
|
page.drawRectangle({ x: margin, y: y - rowHeight, width: contentWidth, height: rowHeight, color: rgb(0.85, 0.9, 0.95) });
|
||||||
@@ -109,9 +111,13 @@
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function drawHeaderRow() {
|
||||||
const headerCells = {};
|
const headerCells = {};
|
||||||
cols.forEach((c) => { headerCells[c.key] = c.label; });
|
cols.forEach((c) => { headerCells[c.key] = c.label; });
|
||||||
drawRow(headerCells, true);
|
drawRow(headerCells, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
drawHeaderRow();
|
||||||
|
|
||||||
for (const course of courses) {
|
for (const course of courses) {
|
||||||
const cells = {
|
const cells = {
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
const { test } = require('node:test');
|
const { test } = require('node:test');
|
||||||
const assert = require('node:assert');
|
const assert = require('node:assert');
|
||||||
|
const fs = require('node:fs');
|
||||||
|
const os = require('node:os');
|
||||||
|
const path = require('node:path');
|
||||||
|
const { execFileSync } = require('node:child_process');
|
||||||
global.PDFLib = require('pdf-lib');
|
global.PDFLib = require('pdf-lib');
|
||||||
const { buildStudyLoadPdf } = require('../pdf.js');
|
const { buildStudyLoadPdf } = require('../pdf.js');
|
||||||
|
|
||||||
@@ -20,6 +24,20 @@ const sampleData = {
|
|||||||
totalUnits: '6.00'
|
totalUnits: '6.00'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function extractPdfText(bytes) {
|
||||||
|
const tmp = path.join(os.tmpdir(), 'study-load-test.pdf');
|
||||||
|
fs.writeFileSync(tmp, Buffer.from(bytes));
|
||||||
|
const script = [
|
||||||
|
'import fitz',
|
||||||
|
`doc = fitz.open('${tmp}')`,
|
||||||
|
'text = ""',
|
||||||
|
'for page in doc:',
|
||||||
|
' text += page.get_text()',
|
||||||
|
'print(text)'
|
||||||
|
].join('\n');
|
||||||
|
return execFileSync('python3', ['-c', script], { encoding: 'utf8' });
|
||||||
|
}
|
||||||
|
|
||||||
test('buildStudyLoadPdf returns valid PDF bytes', async () => {
|
test('buildStudyLoadPdf returns valid PDF bytes', async () => {
|
||||||
const bytes = await buildStudyLoadPdf(sampleData);
|
const bytes = await buildStudyLoadPdf(sampleData);
|
||||||
assert.ok(bytes instanceof Uint8Array);
|
assert.ok(bytes instanceof Uint8Array);
|
||||||
@@ -27,4 +45,8 @@ test('buildStudyLoadPdf returns valid PDF bytes', async () => {
|
|||||||
assert.strictEqual(head, '%PDF-');
|
assert.strictEqual(head, '%PDF-');
|
||||||
const doc = await PDFLib.PDFDocument.load(bytes);
|
const doc = await PDFLib.PDFDocument.load(bytes);
|
||||||
assert.strictEqual(doc.getPageCount(), 1);
|
assert.strictEqual(doc.getPageCount(), 1);
|
||||||
|
|
||||||
|
const text = extractPdfText(bytes);
|
||||||
|
assert.ok(text.includes('CIS 2101'), 'PDF text should contain course code CIS 2101');
|
||||||
|
assert.ok(text.includes("STUDENT'S STUDY LOAD"), 'PDF text should contain the title');
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user