Topics

无障碍诊断服务的证书和报告现已支持PDF格式!

  • column

Liberogic 的无障碍诊断服务之前以 HTML 格式提供证书和报告,但考虑到「希望以 PDF 格式提供证书」的反馈,现在可以发行 PDF 格式的证书和报告

PDF 网络无障碍检查证明书

PDF 易访问性符合报告(VPAT / ACR)

为了实现这一需求,我们构建了一个系统,在构建过程中输出HTML的同时自动生成PDF。

🛠️ PDF 转换基础:Puppeteer 和环境配置

作为PDF生成工具,我们采用了Puppeteer来实现对Chromium浏览器的无头操作。由于HTML原本就是用Astro构建的,所以Puppeteer的集成相对顺利。

1. 确保本地服务器的渲染环境

Puppeteer 利用 Chromium 的打印功能,但直接读取本地 HTML 文件(file://)时,会出现布局错乱的问题。

为了避免这个问题,我构建了以下环境。

  • 临时 Web 服务器:将构建后的 dist 目录作为临时本地 Web 服务器(http-server)启动,作为 Node.js 的子进程运行。
  • 稳定的环境:通过让 Puppeteer 经由服务器(http://localhost:8080)访问,确保了与浏览器相同的稳定渲染环境。

环境构建和服务器启动代码(摘录)

// プロジェクト定数からドメインを取得し、未設定なら 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. 应用日文字体

用于生成 PDF 的本地服务器环境中没有日文字体,因此会出现嵌入在 PDF 中的字体显示异常的问题。

  • 字体解决方案:在脚本中,在生成 PDF 之前动态地将 Web 字体的引用和应用样式插入到 DOM 中。这样就可以用日文字体输出 PDF。HTML 端优先考虑速度,不使用 Web 字体,因此通过这种动态插入方式只对 PDF 应用字体。

字体动态插入代码(摘录)

// 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);

🚨 最大的挑战:PDF 内链接的 URL 引用问题

最困难的部分是 PDF 之间的链接。嵌入在 PDF 中的链接在部署后仍然引用本地开发环境的 URL(http://localhost:8080)。这是因为 Chromium 在加载 HTML 时会保留基础 URI 作为 PDF 链接的基点。

强制转换链接为绝对路径

为了解决这个问题,我通过以下步骤强制将链接转换为部署后的完整绝对路径。

  1. 获取部署目标域名: 从项目常数(SITE_URL)中获取部署目标的域名(例:https://example.com)。
  2. 构建并替换绝对URL: 使用Node.js获取HTML内容,将原始链接(/accessibility_report/top/)替换为以获取的域名为基础的完整URL(例:https://example.com/accessibility_report/pdf/acr-top.pdf)。
  3. 重新应用到DOM: 将替换后的HTML重新应用到Chromium(page.setContent()),以确保嵌入PDF中的链接被固定为预期的部署后域名

链接替换代码片段(摘录)

// 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
});

🎉 总结

这次因为有PDF之间相互链接的特殊需求,花了一些功夫,但Puppeteer本身的集成过程相当顺利。构建速度也很快,这确实是一个非常便利的工具!

本次处理的要点如下:

  • 通过本地服务器渲染确保稳定性
  • 通过动态Web字体注入实现日语支持
  • 通过链接绝对路径化实现PDF间的链接

通过PDF支持,服务的易用性得到了显著提升。我们将继续追求为客户提供有价值的无障碍诊断服务质量!

本文作者

从DTP跨越到Web世界,不知不觉中已掌握标签、前端开发、创意指导、无障碍设计等各项技能的"技术高手"。从Liberogic创业初期就活跃至今,如今是公司内部的"活百科"。最近沉迷于"能否在无障碍适配上更多依赖AI?"这样的思考,正在探索借助AI提示词提高效率的方法。技术实力和思维方式都还在不断进化中

Futa

IAAP 认证网络无障碍专家 (WAS) / 标记语言工程师 / 前端工程师 / 网络总监

查看本员工的文章

安心的团队体制和迅速的反应能力是我们的优势

Liberogic 拥有经验丰富的员工团队,积极推进项目,因此获得了客户的高度评价。
我们会妥善分配项目经理和总监,确保整个项目顺利进行。 通过避免不必要的全职投入导致的成本增加,并采用适当配置人力资源的方式,从把握业务内容到估价的制作和提交速度都赢得了良好的口碑。

* 本公司不积极开展SES驻场工作等业务,敬请谅解。

Slack、Teams、Redmine、Backlog、Asana、Jira、Notion、Google Workspace、Zoom、Webex 等,您可以使用几乎所有主要的项目管理工具和沟通协作工具。

您是否在网页无障碍对应方面遇到困难?

案例分析