ci: use Gitea API for releases #2

Closed
adrianbonpin wants to merge 56 commits from feat/ismis-helper into prod
7 changed files with 67 additions and 14 deletions
Showing only changes of commit b3b4495427 - Show all commits
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -6,7 +6,7 @@
"permissions": ["storage", "downloads"], "permissions": ["storage", "downloads"],
"host_permissions": ["https://ismis.usc.edu.ph/*"], "host_permissions": ["https://ismis.usc.edu.ph/*"],
"background": { "background": {
"scripts": ["libs/pdf-lib.min.js", "pdf.js", "background.js"] "scripts": ["libs/fontkit.min.js", "libs/pdf-lib.min.js", "pdf.js", "background.js"]
}, },
"content_scripts": [ "content_scripts": [
{ {
+65 -13
View File
@@ -94,6 +94,58 @@
return ''; return '';
} }
// Fetch a bundled asset in the extension background page. Returns null in
// Node (tests) or if the asset is unavailable.
async function loadAssetBytes(asset) {
const ext = (typeof browser !== 'undefined' && browser.runtime && browser.runtime.getURL);
try {
const url = ext ? browser.runtime.getURL(asset) : null;
if (!url) return null;
const resp = await fetch(url);
if (!resp.ok) return null;
return new Uint8Array(await resp.arrayBuffer());
} catch (e) {
return null;
}
}
// Embed the bundled Segoe UI fonts (regular/bold/italic). Fontkit is
// required by pdf-lib to embed custom TTF fonts. Falls back to Helvetica
// (standard fonts) when the assets are unavailable (e.g. Node tests).
async function loadFonts(doc, PDFLib) {
const { StandardFonts } = PDFLib;
const fontkit = (typeof self !== 'undefined' && self.fontkit) ||
(typeof globalThis !== 'undefined' && globalThis.fontkit) ||
null;
const segoe = {
regular: 'assets/fonts/Segoe-UI.ttf',
bold: 'assets/fonts/Segoe-UI-Bold.ttf',
italic: 'assets/fonts/Segoe-UI-Italic.ttf'
};
try {
if (fontkit) {
doc.registerFontkit(fontkit);
const regular = await loadAssetBytes(segoe.regular);
const bold = await loadAssetBytes(segoe.bold);
const italic = await loadAssetBytes(segoe.italic);
if (regular && bold && italic) {
return {
font: await doc.embedFont(regular),
bold: await doc.embedFont(bold),
italic: await doc.embedFont(italic),
isCustom: true
};
}
}
} catch (e) { /* fall through to Helvetica */ }
return {
font: doc.embedStandardFont(StandardFonts.Helvetica),
bold: doc.embedStandardFont(StandardFonts.HelveticaBold),
italic: doc.embedStandardFont(StandardFonts.HelveticaOblique),
isCustom: false
};
}
// Load the bundled header logo. Prefer the transparent PNG (matches the // Load the bundled header logo. Prefer the transparent PNG (matches the
// official PDF, which masks the JPEG's baked-in background) for a clean // official PDF, which masks the JPEG's baked-in background) for a clean
// look on white; fall back to the JPEG in Node (tests) or if unavailable. // look on white; fall back to the JPEG in Node (tests) or if unavailable.
@@ -128,9 +180,8 @@
const doc = await PDFDocument.create(); const doc = await PDFDocument.create();
let page = doc.addPage([612, 792]); // Letter let page = doc.addPage([612, 792]); // Letter
const font = doc.embedStandardFont(StandardFonts.Helvetica); const fonts = await loadFonts(doc, PDFLib);
const bold = doc.embedStandardFont(StandardFonts.HelveticaBold); const { font, bold, italic } = fonts;
const italic = doc.embedStandardFont(StandardFonts.HelveticaOblique);
const margin = 36; const margin = 36;
const contentWidth = 539.3; // matches reference right edge 575.3 const contentWidth = 539.3; // matches reference right edge 575.3
let y = 792 - 10; // near the very top, matching the reference logo position let y = 792 - 10; // near the very top, matching the reference logo position
@@ -168,13 +219,14 @@
const logoW = 292.5; const logoW = 292.5;
const logoH = logoW * (logoImg.height / logoImg.width); const logoH = logoW * (logoImg.height / logoImg.width);
page.drawImage(logoImg, { x: (612 - logoW) / 2, y: y - logoH, width: logoW, height: logoH }); page.drawImage(logoImg, { x: (612 - logoW) / 2, y: y - logoH, width: logoW, height: logoH });
y -= logoH + 8; y -= logoH + 8.79;
} }
center("STUDENT'S STUDY LOAD", 9.5, bold, 14); // Baseline advances match the reference (Segoe UI) header layout.
if (header.semester) center(String(header.semester).toUpperCase(), 9, font, 13.5); center("STUDENT'S STUDY LOAD", 9.5, bold, 13.84);
if (header.semester) center(String(header.semester).toUpperCase(), 9, font, 13.76);
const dateRange = header.dateRange || deriveDateRange(header.semester); const dateRange = header.dateRange || deriveDateRange(header.semester);
if (dateRange) center(dateRange, 9, font, 13.5); if (dateRange) center(dateRange, 9, font, 11.75);
if (header.dateEnrolled) left('Date Enrolled: ' + header.dateEnrolled, 8, italic, margin, 13.5); if (header.dateEnrolled) left('Date Enrolled: ' + header.dateEnrolled, 8, italic, margin, 14.53);
// Student info row, laid out in fixed zones across the page (matching the // Student info row, laid out in fixed zones across the page (matching the
// reference): ID (bold) left, name at ~100, program at ~338, and // reference): ID (bold) left, name at ~100, program at ~338, and
@@ -188,7 +240,7 @@
// Right-aligned to the content edge, nudged left ~6pt (half of 1rem). // Right-aligned to the content edge, nudged left ~6pt (half of 1rem).
page.drawText(yearLevelText, { x: margin + contentWidth - w - 6, y, size: 9, font, color: black }); page.drawText(yearLevelText, { x: margin + contentWidth - w - 6, y, size: 9, font, color: black });
} }
if (header.studentId || header.fullName || header.program || yearLevelText) y -= 14.5; if (header.studentId || header.fullName || header.program || yearLevelText) y -= 13.59;
// ---- Table: GROUP, COURSE, COURSE DESCRIPTION, TEACHER, UNITS, NOTE ---- // ---- Table: GROUP, COURSE, COURSE DESCRIPTION, TEACHER, UNITS, NOTE ----
// Column boundaries tuned to the reference PDF (left edge 36, right edge 575.3). // Column boundaries tuned to the reference PDF (left edge 36, right edge 575.3).
@@ -204,8 +256,8 @@
const headerSize = 8; const headerSize = 8;
const lineH = 12; const lineH = 12;
// Header label positions tuned for Helvetica: left/center aligned within // Header label positions tuned for Segoe UI (matches the reference):
// each column so labels never overlap. // left/center aligned within each column so labels never overlap.
function headerLabelXFor() { function headerLabelXFor() {
const widths = {}; const widths = {};
for (const c of cols) { for (const c of cols) {
@@ -213,8 +265,8 @@
widths[key] = font.widthOfTextAtSize(c.label, headerSize); widths[key] = font.widthOfTextAtSize(c.label, headerSize);
} }
return { return {
GROUP: cols[0].x, GROUP: cols[0].x + 1.3,
COURSE: cols[1].x + 5, COURSE: cols[1].x + 9,
COURSE_DESCRIPTION: cols[2].x + (cols[2].w - widths.COURSE_DESCRIPTION) / 2, COURSE_DESCRIPTION: cols[2].x + (cols[2].w - widths.COURSE_DESCRIPTION) / 2,
TEACHER: cols[3].x + (cols[3].w - widths.TEACHER) / 2, TEACHER: cols[3].x + (cols[3].w - widths.TEACHER) / 2,
UNITS: cols[4].x + (cols[4].w - widths.UNITS) / 2, UNITS: cols[4].x + (cols[4].w - widths.UNITS) / 2,