Topics

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

  • column

Liberogic的无障碍诊断服务此前以HTML格式提供证书和报告,但在收到“希望提供PDF格式证书”的反馈后,现已可以发放PDF格式的证书和报告!

PDF格式的网页无障碍检测证书

PDF格式的无障碍合规报告(VPAT / ACR)

为实现此项功能,我们构建了一套系统,在构建过程中输出HTML的同时自动输出PDF。

🛠️ PDF生成基础:Puppeteer与环境搭建

作为PDF生成工具,我们采用了可以以无头模式操作Chromium浏览器的Puppeteer。由于HTML原本就是使用Astro构建的,因此引入Puppeteer相对顺利。

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

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

为避免这一问题,我们搭建了以下环境。

  • 临时Web服务器:以Node.js子进程的方式启动一个临时本地Web服务器(http-server),用于托管构建后的dist目录。
  • 稳定的运行环境:让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 呢?”技术与思考仍在不断进化中。

二俣 歩

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

查看此员工的文章

案例分析