51 lines
3.2 KiB
JavaScript
51 lines
3.2 KiB
JavaScript
// Run with Electron: electron --ozone-platform=x11 export.cjs
|
|
const { app, BrowserWindow, session } = require('electron');
|
|
const fs = require('node:fs/promises');
|
|
const path = require('node:path');
|
|
const assert = require('node:assert/strict');
|
|
|
|
app.disableHardwareAcceleration();
|
|
const timeout = setTimeout(() => { console.error('PDF export timed out after 60 seconds'); app.exit(1); }, 60000);
|
|
app.whenReady().then(async () => {
|
|
console.log('Chromium ready');
|
|
const failures = [];
|
|
session.defaultSession.webRequest.onBeforeRequest((details, callback) => {
|
|
const remote = /^https?:/.test(details.url);
|
|
if (remote) failures.push(`External request: ${details.url}`);
|
|
callback({ cancel: remote });
|
|
});
|
|
session.defaultSession.webRequest.onErrorOccurred(details => failures.push(`${details.error}: ${details.url}`));
|
|
const win = new BrowserWindow({ show: false, width: 1000, height: 1200,
|
|
webPreferences: { nodeIntegration: false, contextIsolation: true, sandbox: true, backgroundThrottling: false } });
|
|
await win.loadFile(path.join(__dirname, 'index.html'));
|
|
console.log('HTML loaded');
|
|
const result = await win.webContents.executeJavaScript(`(async () => {
|
|
await document.fonts.ready;
|
|
await Promise.all([...document.images].map(image => image.decode()));
|
|
const pages = [...document.querySelectorAll('.page')];
|
|
const overflow = pages.flatMap((page, index) => {
|
|
const footer = page.querySelector('.folio').getBoundingClientRect();
|
|
return [...page.children].filter(child => !child.classList.contains('folio'))
|
|
.filter(child => child.getBoundingClientRect().bottom > footer.top - 8)
|
|
.map(child => ({ page: index + 1, element: child.tagName, text: child.textContent.slice(0, 50) }));
|
|
});
|
|
return { blocks: pages.map(page => page.id), pages: pages.length, overflow, familiesLoaded: ['Podkova', 'Oswald', 'Noto'].every(family => [...document.fonts].some(face => face.family === family && face.status === 'loaded')), fonts: ['14px Noto', '700 14px Noto', '600 48px Oswald', '600 32px Podkova'].every(font => document.fonts.check(font)),
|
|
images: [...document.images].length, unloaded: [...document.images].filter(image => !image.naturalWidth).length };
|
|
})()`);
|
|
assert.equal(result.pages, 4, 'Expected four pages across three brandbook blocks');
|
|
assert.deepEqual(result.blocks, ['logo', 'logo-mono', 'fonts', 'colors']);
|
|
assert.deepEqual(result.overflow, [], 'Content reaches the page footer');
|
|
assert.equal(result.fonts, true, 'Local fonts must load');
|
|
assert.equal(result.familiesLoaded, true, 'All three bundled font families must load');
|
|
assert.equal(result.unloaded, 0, 'All images must load');
|
|
assert.deepEqual(failures, [], 'All resources must load locally');
|
|
console.log('Layout and local resources checked');
|
|
const pdf = await win.webContents.printToPDF({ printBackground: true, preferCSSPageSize: true,
|
|
pageSize: 'A4', margins: { top: 0, bottom: 0, left: 0, right: 0 }, generateTaggedPDF: true });
|
|
await fs.writeFile(path.join(__dirname, 'brandbook.pdf'), pdf);
|
|
console.log(JSON.stringify({ ...result, bytes: pdf.length, output: 'brandbook.pdf' }, null, 2));
|
|
win.destroy();
|
|
clearTimeout(timeout);
|
|
app.quit();
|
|
}).catch(error => { console.error(error); app.exit(1); });
|