简洁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
}
如何运作
- 移动可见的 DOM 文本并记录行矩形
Range,包括:::before编号 :::after生成带有字串的文字content。 。 。 . - 抓取每个页面作为光栅图像,以显示视觉忠贞度(页被行走) 并同时抓获——抓获占据了墙上时间.
- 发现符合条件
@font-face规则并获取/解码 WOFF2 网页字体 SFNT/OTF/TTF字节. - 解决一个 Noto Sans JP 对 CJK 文本的回落需求 甲板自己的字体 不能覆盖(参见字体行为).
- 释放带有光栅页面图像的 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软件包.
许可证
麻省理工学院