@astlide/crispdf

中文文档
0.0.3发布于 2026/11/02MITTypeScriptES Module构建来源已验证近 30 天下载 4090

DOM → 在浏览器中高质量的PDF:光栅背景+向量文本覆盖. 可选择、可搜索、可复制文本.

中文使用文档
文档目录10 节
  1. 安装
  2. 使用量
  3. API 密码
  4. 如何运作
  5. 字体行为
  6. 验证
  7. 自我检查
  8. 浏览器要求
  9. 限制
  10. 许可证

简洁f

浏览器- side DOM 到 PDF 有高视力的忠诚度和可选择的文本 .

crispdf 使每个页面成为光栅背景,然后覆盖真实的 PDF 上方为文本。 该结果保留了复杂的 HTML/CSS 图像完整,同时保留 查找、选择和复制/粘贴可映射到PDF字体的文本.

安装

pnpm add @astlide/crispdf
# npm i @astlide/crispdf
# yarn add @astlide/crispdf

使用量

import { domToPdf } from '@astlide/crispdf'

const result = await domToPdf({
  pages: document.querySelectorAll<HTMLElement>('[data-page]'),
  source: { width: 800, height: 600 },
  output: { width: 800, height: 600, unit: 'pt' },
  rasterFormat: 'jpeg',
  jpegQuality: 0.85,
  onProgress: (pageIndex, totalPages) => {
    console.log(`page ${pageIndex}/${totalPages}`)
  },
  onTiming: (event) => {
    console.log(event)
  },
})

if (result.warnings.length > 0) {
  console.warn(result.warnings)
}

const url = URL.createObjectURL(result.blob)

API 密码

function domToPdf(opts: DomToPdfOptions): Promise<DomToPdfResult>

interface DomToPdfOptions {
  pages: ArrayLike<HTMLElement>
  source: { width: number; height: number }
  output: { width: number; height: number; unit: 'pt' }
  rasterFormat?: 'jpeg' | 'png' // default 'jpeg'
  jpegQuality?: number // default 0.85
  onProgress?: (pageIndex: number, totalPages: number) => void
  onTiming?: (event: TimingEvent) => void
  /** Opt-in visual self-check; off by default. Requires `pdfjs-dist`. */
  selfCheck?: { enabled: boolean; threshold?: number }
}

type TimingEvent =
  | { stage: 'walk'; page: number; durationMs: number }
  | { stage: 'capture'; page: number; durationMs: number }
  | { stage: 'fonts'; durationMs: number }
  | { stage: 'emit'; durationMs: number }
  | { stage: 'selfCheck'; durationMs: number }

interface DomToPdfResult {
  blob: Blob
  warnings: string[]
  /** Present only when `selfCheck.enabled` was set. */
  selfCheck?: SelfCheckPageResult[]
}

interface SelfCheckPageResult {
  page: number // 1-indexed
  diff: number // visual difference, 0..1
  exceeded: boolean // diff > threshold
}

如何运作

  1. 移动可见的 DOM 文本并记录行矩形 Range,包括: ::before 编号 : ::after 生成带有字串的文字 content。 。 。 .
  2. 抓取每个页面作为光栅图像,以显示视觉忠贞度(页被行走) 并同时抓获——抓获占据了墙上时间.
  3. 发现符合条件 @font-face 规则并获取/解码 WOFF2 网页字体 SFNT/OTF/TTF字节.
  4. 解决一个 Noto Sans JP 对 CJK 文本的回落需求 甲板自己的字体 不能覆盖(参见字体行为).
  5. 释放带有光栅页面图像的 PDF 加上可选文本层.

PDF 写入器将网页字体嵌入到带有身份-H编码的 CID 密钥字体和 /ToUnicode 地图。 原始的WOFF2容器从未被嵌入.

每个失败模式都设计为 可见, 不沉默: 文本向量 层层无法复制 仍然显示在光栅 validate() (以前)加 selfCheck 而不是悄悄地降低内容.

字体行为

  • 拉丁文可以回落到PDF标准字体: Helvetica, Times, Courier,, 及其粗体/斜体变体.
  • Google字体网络字体在从CSSOM可发现时被嵌入.
  • unicode-range 子集被过滤到 文档,然后平行获取;每个字符选择路线 到覆盖它的字体.
  • 捷克 (kana / kanji / CJK puntuation / fullwidth 形式),没有嵌入式或 标准字体封面触发点名 Noto Sans JP 子集通过 谷歌字体 text= API — 只使用字符( 有几个 KB) , 而非完整 多MB脸. 下行时,文字会留入光栅并有警告.
  • 没有被可嵌入字体覆盖的字符仍然可见于光栅 层,但可以从可选择的层中丢出,并带有警告.

验证

validate(pages) 是静态飞行前检查——在飞行前运行 domToPdf (单位:千美元) CI 或运行时捕获会丢失可选择文本的内容 .

import { validate } from '@astlide/crispdf'

const { ok, errors, warnings } = validate(pages)
if (!ok) {
  // errors mean text will be lost from the selectable layer
  console.error(errors)
}
  • 错误 (单位:千美元)ok: false: <canvas> 编号 : <video> 光栅元素 文本永远无法到达可选择的层.
  • 警告数字 : mix-blend-mode, (中文(简体) ), filter, (中文(简体) ), backdrop-filter, (中文(简体) ), position: sticky,3D变形 ——它们变形但光泽,不会 与矢量文本覆盖相混合,所以输出可能与屏幕不同.
function validate(pages: ArrayLike<HTMLElement>): ValidationResult

interface ValidationResult {
  ok: boolean
  errors: ValidationIssue[]
  warnings: ValidationIssue[]
}

interface ValidationIssue {
  rule: string
  message: string
  element: HTMLElement
  selector: string
}

自我检查

与 selfCheck.enabled,生成的 PDF 将返回到像素中 pdf.js和每一页都是用地上真光栅显示的 包含文本(与文本压缩分开) 嵌入光栅, 所以一个丢失文本 diffs 的页面 较高,而不是更低。 页 次 不同之处 threshold (默认) 0.1(单位:千美元) result.warnings 财务报告和审定财务报表 result.selfCheck。这变成沉默,使漂移——a 没有嵌入的字体,一个不支持的 CSS 特性——被嵌入可探测的警告.

Diff是一个整页正比, 直截了当: 它捕获毛 失败,而不是微妙的一闪一闪 允许它添加第二个抓取 每页加上pdf.js渲染,所以是选入.

pdfjs-dist 是一个 可选同伴依赖: 仅在启用时安装 自我检查。 它通过动态导入被装入, 所以它不会输入您的捆绑 否则.

浏览器要求

软件包以浏览器运行. 这需要DOM API, Blob, (中文(简体) ), fetch, (中文(简体) ), document.fonts, (中文(简体) ), Range.getClientRects(),并使用 html-to-image. 自查额外用途 OffscreenCanvas, (中文(简体) ), createImageBitmap,以及 pdfjs-dist。 。 。 .

限制

  • Web 字体嵌入仅限于 Google 字体主机 (单位:千美元)fonts.gstatic.com 编号 : fonts.googleapis.com),在当前发行.
  • 无法通过 CSSOM 检查的跨源样式表被跳过 .
  • 生成的字串以外的内容 content (对面, attr(), (中文(简体) ), url() 图像,引文关键词)只停留在光栅层.
  • 影子DOM和 Iframe 内容没有被移动.
  • 点播的CJK倒计时涵盖日语(Noto Sans JP),重量为400;其他 脚本(韩文,汉语特写,泰文,. )尚未自动取用.
  • 色彩 emoji 支持取决于可嵌入的字体; 否则 emoji 是光栅 只有这个.
  • API足够稳定,可以尝试,但这仍然是 0.0.x 软件包.

许可证

麻省理工学院

版本与发布记录

@astlide/crispdf 共发布 2 个版本,首次发布于 2026/05/30;过去 12 个月发布了 2 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
0.0.3latest2026/11/025 天前
0.0.12026/05/305 个月前

关于 @astlide/crispdf 的常见问题

@astlide/crispdf 是什么?

DOM → 在浏览器中高质量的PDF:光栅背景+向量文本覆盖. 可选择、可搜索、可复制文本.

如何安装 @astlide/crispdf?

在项目目录运行 npm install @astlide/crispdf,或使用 pnpm add / yarn add / bun add。

@astlide/crispdf 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@astlide/crispdf 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@astlide/crispdf 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=22.12.0。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@astlide/crispdf 可以商用吗?

@astlide/crispdf 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@astlide/crispdf 的下载量有多少?

根据 npm 官方下载统计,@astlide/crispdf 在 2026/10/07 – 2026/11/05 这 30 天内被下载 4,090 次,日均约 136 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@astlide/crispdf 还在维护吗?

最新版本 0.0.3 发布于 2026/11/02(5 天前);至今共发布 2 个版本,其中过去 12 个月发布了 2 个;首次发布于 2026/05/30。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。