Liberogic's accessibility diagnostic service previously provided certificates and reports in HTML format. In response to requests for PDF versions, we now offer PDF format certificates and reports!
PDF Web Accessibility Inspection Certificate
PDF Accessibility Conformance Report (VPAT / ACR)
To implement this, we built a system that automatically generates PDF files simultaneously with HTML output during the build process.
🛠️ PDF Foundation: Puppeteer and Environment Setup
We adopted Puppeteer, which enables headless control of the Chromium browser, as our PDF generation tool. Since the HTML was already built with Astro, integrating Puppeteer was relatively straightforward.
1. Ensuring Rendering Environment with Local Server
Puppeteer uses Chromium's print function, but directly loading local HTML files (file://) causes a layout breakdown issue.
To work around this, we set up the following environment.
- Temporary web server: We launch a temporary local web server (
http-server) as a Node.js child process to host thedistdirectory after the build. - Stable environment: By having Puppeteer access the server via (
http://localhost:8080), we ensure a consistent rendering environment identical to the browser.
Environment setup and server startup code (excerpt)
// プロジェクト定数からドメインを取得し、未設定なら localhost:8080 を使用
const HOST_DOMAIN = SITE_URL || `http://localhost:${PORT}`;
const BUILD_ROOT = path.join(__dirname, 'dist');
const PORT = 8080;
// --- サーバーの起動ロジック ---
let serverProcess = exec(`npx http-server ${BUILD_ROOT} -p ${PORT} -s --silent`);
// サーバーが起動するまで待機
await new Promise((resolve) => setTimeout(resolve, 2000));
// --- Puppeteerのページアクセス ---
// Puppeteerは localhost:8080 にアクセスし、レンダリングを開始します
const serverUrl = `http://localhost:${PORT}/${REPORT_DIR}/${urlPath}`;
await page.goto(serverUrl, { waitUntil: 'networkidle0' });
2. Applying Japanese fonts
The local server environment for PDF generation lacks Japanese fonts, which causes issues with fonts embedded in the PDF.
- Font workaround: In the script, we dynamically inject web font references and applied styles into the DOM just before PDF generation. This enables PDF output in Japanese fonts. Since the HTML side prioritizes speed and does not use web fonts, this dynamic injection ensures fonts are applied only to the PDF.
Font dynamic injection code (excerpt)
// Webフォントの動的挿入ロジック (page.evaluateでブラウザ側で実行)
await page.evaluate((fontUrl) => {
// <link>タグを生成してDOMに挿入
const link = document.createElement('link');
link.rel = 'stylesheet';
link.href = fontUrl;
document.head.appendChild(link);
// @media print スタイルを強制的に挿入し、フォントを適用
const printStyle = document.createElement('style');
printStyle.textContent = `
@media print {
html, body {
font-family: 'Noto Sans JP', sans-serif !important;
}
}
`;
document.head.appendChild(printStyle);
}, WEBFONTS_URL);
🚨 The biggest challenge: URL reference issues in PDF links
The most troublesome issue was links between PDFs. Links embedded in the PDF continued to reference the local development environment URL (http://localhost:8080) even after deployment. This occurs because Chromium retains the base URI from when it loaded the HTML as the foundation for PDF links.
Forcing absolute paths for links
To resolve this, we forcibly rewrote links to complete absolute paths after deployment using the following procedure.
- Using the deployment destination domain: We retrieved the deployment destination domain (for example,
https://example.com) from the project constant (SITE_URL). - Building and replacing absolute URLs: We used Node.js to fetch the HTML content and rewrote the original link (
/accessibility_report/top/) to a complete URL (for example,https://example.com/accessibility_report/pdf/acr-top.pdf) based on the retrieved domain. - Reapplying to the DOM: By reapplying the rewritten HTML to Chromium (using
page.setContent()), we ensured that links embedded in the PDF were fixed to the intended post-deployment domain.
Link rewriting code (excerpt)
// 1. 無効化したいリンク(.link-ignore-pdf)を物理的に削除
const ignoreLinkRegex = new RegExp(`(<a\\\\s+[^>]*class=["'][^"']*${ignoreClass}[^"']*["'][^>]*>)(.*?)(<\\/a\\\\s*>)`, 'gi');
content = content.replace(ignoreLinkRegex, '$2'); // <a>タグ全体を中身のテキストに置換
// 2. 詳細ページへのリンクを絶対URLに書き換え
const detailLinkRegex = new RegExp(`href="${reportDirRootLink}([^/]+)\\/"`, 'g');
const detailPdfUrl = `${pdfAbsoluteUrl}/acr-top.pdf`; // 例
content = content.replace(detailLinkRegex, (match, slug) => {
// リンクを <http://localhost>... ではなく、<https://example.com/>... に強制置換
return `href="${pdfAbsoluteUrl}/acr-${slug}.pdf"`;
});
// 3. 最終的なコンテンツをブラウザに再適用し、PDF出力へ
await page.setContent(content, {
waitUntil: 'domcontentloaded',
baseURL: baseUrlForContent
});
🎉 Summary
While we had some difficulty due to the somewhat unique requirement of linking across PDFs, the Puppeteer implementation itself went quite smoothly. The build is fast, and we found it to be a very useful tool!
The key points of this implementation are as follows:
- Ensuring stability through rendering via a local server
- Japanese support through dynamic web font injection
- Absolute paths for links between PDFs
PDF support has significantly improved the convenience of our services. We remain committed to delivering high-quality web accessibility consulting services that provide genuine value to our clients!
A "master of technique" who jumped from DTP into the web world and, before he knew it, mastered markup, frontend, direction, and accessibility. Active across multiple domains since Liberogic's early days, he's now a walking encyclopedia within the company. Recently, he's been diving deep into prompt-driven efficiency optimization, wondering "Can we rely more on AI for accessibility compliance?" Both his technology and thinking continue to evolve.
Futa
IAAP Certified Web Accessibility Specialist (WAS) / Markup Engineer / Frontend Engineer / Web Director