Skip to content

About

Lightweight, cross-platform .NET renderer that turns HTML + CSS into paged PDF and PNG — no headless browser. hand-written block-flow layout. From HTML to any document.

Topics

Resources

Stars

7 stars

Watchers

0 watching

Forks

Repository files navigation

PaperSharp

面向 .NET 的高性能文档渲染引擎 —— 纯托管管线把 HTML/CSS 直接编译为矢量 PDF 与高清图片,无需浏览器,无需 headless Chrome。

HTML + CSS → PDF · PNG · JPEG · WebP · ICO · SVG

.NET Platform No headless browser License NuGet

为什么选它

  • 零浏览器依赖 —— 不装 Chromium、不起子进程、不开调试端口;一个类库嵌进你的 .NET 进程即可渲染
  • 服务端效率 —— 单页 PDF 中位约 30ms,单线程吞吐约 40 页/秒;线程安全,多请求可并发进同一进程;常驻仅一个类库的体量,没有 V8/渲染树的内存包袱
  • 中文原生排版 —— CJK 逐字折行、用字级字体子集嵌入(纯文字单页 PDF 约 50–120KB,文字可选中可搜索)、中文数字页码
  • 打印级语义 —— 天生面向分页媒体:@page 页边距、页眉页脚边距盒、自动跨页回流、表格跨页续 + <thead> 重复、break-inside / orphans / widows
  • 确定性输出 —— 默认离线渲染,同一输入恒定产物,适合发票、报表、合同等财务级文档

30 秒上手

using PaperSharp;

byte[] pdf = Renderer.RenderPdf(html, css);                        // 分页矢量 PDF(自动注入书签、链接注释、元数据)
byte[] png = Renderer.RenderPng(html, css, width: 794, scale: 2);  // 2× 高清(~192 DPI)

字体:包里内嵌的是 SIL OFL 授权的 Noto Sans SC 子集(regular/bold,随 dll 内嵌资源走,不依赖宿主目录)。 CSS 里写 "Microsoft YaHei" 而本机装了雅黑时会自动用系统字面;想让引擎吃自己烤的子集 (体积与跨平台一致都要靠它),在首次渲染前设 FontManager.BundledFontDirectory。

示例集锦

每个场景一行调用 / 一段 CSS 搞定;文末给出仓内对应的完整验收样例。

① 一行代码出 PDF

byte[] pdf = Renderer.RenderPdf(
    "<h1>发票</h1><p>合计:¥1,234.00</p>",
    "h1 { color: #2c3e50 }  p { font-size: 14px }");

② 打印文档:页边距 + 页眉页脚页码,全自动

@page {
  size: A4;  margin: 25mm 20mm;
  @top-center    { content: "季度财务报表"; font-size: 9pt; color: #888 }
  @bottom-center { content: "第 " counter(page) " 页 / 共 " counter(pages) " 页" }
}

内容超一页自动回流,边距盒每页重绘。→ 完整样例 assets/report.html

③ 400 行长表:跨页续 + 每页重复表头 + 中文页码

table { border-collapse: collapse; width: 100% }
thead { display: table-header-group }   /* 每页自动重放表头 */
tr    { break-inside: avoid }           /* 单行不跨页折断 */
td    { padding: 6px 10px }             /* 支持 % 列宽、<col>、colspan/rowspan */
@page { @bottom-right { content: counter(page, cjk-ideographic) " / " counter(pages) " 页" } }

跨页列宽共享、rowspan 续格自动补边框。→ assets/m1.html(400 行明细发票)

④ 图卡输出:一个模板,五种格式

using SkiaSharp;

byte[] png = Renderer.RenderPng(html, css, width: 1200, scale: 2);   // OG 图(1600px 宽 2× 高清)
byte[] jpg = Renderer.RenderImage(html, css, 1200, SKEncodedImageFormat.Jpeg, quality: 82);
byte[] svg = Renderer.RenderSvg(html, css, 1200);                    // 矢量:渐变/裁剪映射为 SVG 原生
byte[] ico = Renderer.RenderIco(html, css, size: 64, width: 512);    // favicon,多尺寸

⑤ 视觉全开:conic 渐变 + squircle + 遮罩 + 滤镜,纯 CSS

.hero {
  border-radius: 32px;  corner-shape: squircle;
  background: conic-gradient(from 45deg, #f6d365, #fd6589, #84fab0, #f6d365);
  box-shadow: 0 12px 40px rgba(0, 0, 0, .25);
  mask-image: linear-gradient(to bottom, black 60%, transparent);
  filter: blur(0px) drop-shadow(0 4px 12px rgba(255, 0, 128, .4));
  mix-blend-mode: screen;
}

→ assets/m3.html(OG 图卡风整页)

⑥ 中文图文混排:真 inline 行盒

<p>本月营收 <strong class="up">↑ 128%</strong>(含税口径),
   扫码或点击 <a href="https://example.com/report">完整报表</a>
   <img src="imgs/qr.png" width="16" /> 查看明细。</p>

混排按行盒折行、基线对齐、CJK 逐字断行;渲进 PDF 后 <a> 变成可点击的链接注释,精确到行内片段矩形。→ assets/inline.html

⑦ 书籍级 PDF:书签目录自动生成

byte[] pdf = Renderer.RenderPdf(bookHtml, css);
// h1–h6 → PDF 阅读器侧边栏书签层级;<title>/<meta name=author|description> → 文档元数据

零配置,Skia 不提供此 API——书签/注释由自研 PdfEnhancer 增量尾部注入。→ assets/m5.html

⑧ 字体与主题:一行拉云端字体,var/calc 全支持

css = GoogleFonts.Fetch("Noto Serif SC:wght@400;700") + css;   // 联网一次,之后永久离线复用
body  { --brand: #1a73e8; --gap: 12px }
.btn  { background: var(--brand, #333); padding: calc(var(--gap) / 2) calc(var(--gap) * 2) }
.col  { width: calc(100% / 3 - var(--gap)) }

→ assets/m4.html(var/calc 全谱 + 多字体)

⑨ 多文种混排:阿拉伯连写 + RTL 双向 + emoji

.letter { direction: rtl; text-align: start }   /* RTL 段落:折行、对齐、行内盒序全部镜像 */

阿拉伯文经 HarfBuzz 连写成形;RTL 段里的拉丁词/数字按 bidi run 排布;emoji(含 ZWJ 家庭序列)与缺字按字形级回退,不再出豆腐块。→ assets/m7.html(阿拉伯/希伯来/越南/韩中混排整页)

架构

  • AngleSharp + AngleSharp.Css — HTML 解析、选择器匹配、CSS 层叠与继承
  • SkiaSharp (+HarfBuzz) — 字体读取、文本 shaping(中文/CJK/阿拉伯连写)、光栅化、PDF 矢量输出
  • 自研 — block/flex/grid/float/table 布局、inline 折行、块级分页、绘制遍历、var()/calc()/% 补全、PDF 增量尾部(书签/注释)

一句话:AngleSharp 做 CSS 语义层,SkiaSharp 做视觉几何层,中间那层 block-flow 布局是自己写的。

能力速览(每项都有验收样例,src/PaperSharpSamples/assets/,m1–m5、m7–m9 对应里程碑)

  • 分页:块级跨页回流;长表跨页续 + 共享列宽 + 重复 <thead>;break-before/after: page、break-inside: avoid、orphans/widows;@page size/margin + 页眉页脚边距盒 + counter(page/pages)(含中文数字页码)
  • 布局:flexbox 全家(含 wrap/reverse/gap/嵌套)、Grid 子集、float + 图文环绕、relative/absolute + z-index 盖顶、% 单位按包含块布局期解析、box-sizing、min/max
  • 溢出裁剪:overflow / overflow-x / overflow-y 的 hidden·clip·auto·scroll·overlay 一律裁剪到 padding box(定格输出没有滚动位置,也不画滚动条);一轴非 visible 即两轴同裁(规范折算);祖先带 border-radius 时按圆角矩形裁;脱流(position:absolute)子盒同样按树祖先链裁剪,不逃逸
  • 选择器与伪元素::is()/:where()(AngleSharp 原生)、::before/::after 生成内容(字符串 / attr() / open-quote·close-quote / url() 图 / counter()·counters() 自动编号,含中文数字),块级与行内成盒
  • 文本:inline 混排真行盒(span/a/br/inline-block/行内 img,基线对齐 + vertical-align)、CJK 逐字 / Latin 逐词折行、white-space/word-break/text-overflow: ellipsis/letter-spacing/text-transform/text-decoration、text-wrap: balance、text-fit
  • 视觉:圆角(含 squircle)/阴影/linear·radial·conic 渐变/opacity/mix-blend-mode/clip-path/mask-image/filter: blur|drop-shadow 链/background-clip: text(渐变文字)/backdrop-filter(毛玻璃,仅位图路径)/内联 <svg>
  • 字体:构建期 fonttools 子集化 + 随 dll 内嵌(纯文字单页 PDF 从 ~2.5MB 降到 50–120KB)、@font-face(多 src 回退/woff2)、GoogleFonts.Fetch() 下载+缓存+离线复用、RegisterFontDirectory、BundledFontDirectory(宿主自带子集)、跨平台系统回退
  • 复杂文字:HarfBuzz shaping(阿拉伯连写)、direction: rtl + 轻量 bidi 混排、扩展字素安全折行(emoji ZWJ/组合符不拆散)、字形级字体回退(emoji/小语种不再出豆腐块)
  • 动画(确定性取帧):解析 @keyframes + animation 全套(timing/iteration/direction/fill),渲染传 animationTime(秒)即取该时刻的一帧,对 opacity/颜色/长度/圆角/字号/定位偏移按帧插值;输出仍是静态单帧、逐字节确定。Tailwind animate-pulse 已映射(详见边界)
  • 输出:矢量 PDF(h1–h6 书签 + <a> 链接注释 + metadata,经自研 PdfEnhancer 增量尾部注入)/ PNG(scale 高清)/ JPEG / WebP / ICO / SVG
  • 边界:@media/transform/fixed/sticky/网络图未做;overflow 已交付裁剪但不建立 BFC(overflow:hidden 仍不隔离浮动与父子 margin 坍缩,display:flow-root 同);transform 未实现 → 依赖它的动画(spin/ping/bounce)关键帧会被解析但不生效(不做假变换),仅 opacity/颜色/长度类动画可取帧;backdrop-filter 只在位图(PNG/JPEG/WebP)路径生效,PDF/SVG 矢量路径跳过;嵌套双向、国旗 emoji、shaped run 的 letter-spacing 是复杂文字的已知简化——全量清单见 ROADMAP.md

目录结构

papersharp/
├── PaperSharp.slnx
├── src/
│   ├── PaperSharp/                 # 纯渲染库(net8.0 类库)
│   │   ├── Renderer.cs             # 顶层 API:RenderPdf / RenderPng / RenderImage / RenderIco / RenderSvg
│   │   ├── Css/                    # StyleResolver(AngleSharp 桥+UA 表+兜底) / ComputedStyle / CssSize /
│   │   │                           # CssValueParser / Visual / FontFaceParser / GoogleFonts / PageSpec(@page)
│   │   ├── Layout/                 # LayoutBuilder / BlockBox / InlineLayout / FloatContext / Paginator /
│   │   │                           # TextMeasurer / ImageLoader / FontManager
│   │   ├── Pdf/                    # PdfEnhancer(书签+注释+xref 重写) / PdfSemantic(从盒树收书签与链接矩形)
│   │   └── Paint/                  # Painter(单页长图) / PagePainter(分页) / BoxPainter / TextPainter /
│   │                               # SvgWriter(绘制期录 SVG) / MarginBoxRenderer
│   ├── PaperSharp.Wasm/            # browser-wasm 宿主(JSExport:Render / InstallSubsetFont / RegisterFont)
│   └── PaperSharpSamples/          # 验收样例 + 基准宿主(Exe,引用 PaperSharp)
│       ├── Program.cs / Benchmark.cs / run.bat
│       ├── tools/subset-font.py    # 一次性裁字体子集(fonttools)
│       └── assets/                 # invoice/long/flex/img/report/pct/polish/inline/table + m1–m5(html+css)、imgs/、fonts/
├── js/                             # papersharp npm 包(JSX/HTML + Tailwind 子集 → 同一套 wasm 引擎)
├── extension/                      # MV3 浏览器插件:任意网页右上角导出 PDF/PNG/JPEG/WebP/SVG/ICO
└── tests/PaperSharp.Tests/         # xUnit + PdfPig + PDFtoImage,goldens/ 提交基准

跑起来

cd src/PaperSharpSamples
python tools/subset-font.py   :: 一次性,需 pip install fonttools;产出会自动同步进库工程(只同步 OFL 的 Noto),跳过则 PDF 回退嵌整份系统雅黑 ~20MB
dotnet run

产物在 bin/Debug/net10.0/output/。也可直接双击 run.bat。

浏览器插件(任意网页 → 六种格式)

extension/ 是一个 MV3 插件:在任意页面右上角加一个「导出」按钮,把整页导出成引擎支持的 PDF / PNG / JPEG / WebP / SVG / ICO——渲染全在本机 .NET WASM 里,不截图、不上传、无服务端。

node extension/build.mjs --publish         :: 产出 wasm + 中文字体子集
:: 浏览器 → 扩展页 → 开发者模式 → 加载已解压的扩展程序 → 选 extension 目录
node extension/test/e2e-browser.mjs --diag :: 端到端实测(Edge 无头装载扩展跑六格式)

快照走「计算样式展平」而不是把网页 CSS 原样交给引擎:浏览器负责解析级联 / @layer / 媒体查询 / 阴影 DOM,插件只下发每个元素最终生效的扁平规则(签名去重后通常 200–300 KB),正好落在引擎的 CSS 子集上;overflow:hidden 用已实现的 clip-path 近似、background-image: url() 合成成绝对定位 <img>、图片一律内联成 data URI。真实网站的错位主要靠「钉死」解决——块级盒直接按浏览器算好的矩形 position:absolute 摆位(长图整页钉,PDF 只钉 fixed/sticky/absolute 与 flex/grid 子女以保住分页回流, 表格只钉位置)。导出侧再把快照标记成 precomputed,让引擎跳过「声明值」那一轮层叠: Bootstrap 控制台样张的引擎段从 10.9 s 降到 3.0 s。取舍与已知限制见 extension/README.md。

支持的 CSS 子集

  • 选择器:type / #id / .class / 复合 / 后代 / > / 属性 / :is() / :where() —— AngleSharp.Css 原生
  • 伪元素与计数器:::before / ::after 生成内容 —— 字符串、attr()、open-quote/close-quote、url() 图片、counter()/counters() 自动编号(counter-reset/counter-increment,counter-style 含十进制/字母/罗马/cjk-ideographic/trad-chinese-informal);行内与块级伪元素各自成盒,多级列表嵌套编号
  • 盒模型:width/height/min/max(px/pt/em/rem/%)、margin/padding(1–4 值简写 + longhand,负值、%)、border-*-width/color、box-sizing: border-box(块/flex/img/单元格一致)。margin/padding 的 % 一律相对包含块宽;height% 在父高 auto 时退化为 auto
  • Flexbox:direction(含 reverse)/ justify-content 6 种 / align-items·align-self(含 stretch)/ grow·shrink·basis(含 %·calc)/ wrap / 嵌套;align-content 暂简化为 flex-start
  • Grid 子集:template-columns/rows(长度/%/fr/auto/minmax()/repeat()/auto-fill)、gap、grid-column/row: span N、跨轨道 stretch
  • 定位:relative / absolute(shrink-to-fit、相对最近定位祖先 padding box、left+right 拉伸、负偏移)+ z-index 排序盖顶;fixed/sticky 按 static 处理
  • 视觉:border-radius(含 squircle)/ box-shadow / linear·radial·conic-gradient / opacity / mix-blend-mode / clip-path(inset·circle·polygon) / mask-image(线性渐变) / filter: blur()·drop-shadow() 链 / background-clip: text(渐变文字:背景铺进字形)/ backdrop-filter(毛玻璃,读取盒背后已绘像素再模糊,仅位图路径)
  • 文本与 inline:display:inline(span/a/em/strong/…UA 已补)与 inline-block 真行盒(基线、text-align、vertical-align、balance、浮动绕排);white-space(normal/nowrap/pre/pre-wrap/pre-line)、word-break、ellipsis、letter-spacing、text-transform、text-decoration、line-height(无单位/%)
  • 表格:table 全家族 display + colspan/rowspan/caption/<col>;collapse/separate 两种边框模型;列宽自动分配(单跨 max-content 基准、显式 width/%/ 优先、超限等比缩放);跨页续表
  • 图片:<img> 文件路径(相对/绝对)与 data: URI、内联 <svg>;宽高三种模式(都给/单给等比/自然尺寸);网络 URL 不抓(离线 + 确定性输出)
  • 字体:font-family/size/weight/line-height、@font-face(src 多候选按序回退、字重就近)、GoogleFonts.Fetch("Roboto:wght@400;700");未做 format() 校验与 local()
  • 动画(确定性取帧):@keyframes(from/to、百分比、多选择器帧)+ animation 简写(duration/delay/timing 含 cubic-bezier·steps/iteration-count/direction/fill-mode);渲染传 animationTime(秒)按该时刻求值插值,覆盖 opacity / color / background-color / font-size / letter-spacing / width / height / top·right·bottom·left / border-radius。transform 未实现 → 依赖它的动画停在基线态(不造假)。Tailwind animate-pulse 已映射为 opacity 关键帧
  • var() / calc():自研(AngleSharp 处理不全)——var 作用域链 + 回退 + 嵌套;calc 线性式(px/pt/em/rem/%、嵌套括号),布局期按包含块落地
  • @page 分页媒体:size(A2–A5/Letter/Legal/两长度/landscape)、margin(mm/cm/in/pt/px)、@top-*/@bottom-* 边距盒(content 字面串 + counter(page)/counter(pages),counter-style 含 cjk-ideographic/trad-chinese-informal)
  • PDF 语义:书签 outline(h1–h6)、链接注释(<a href>,行内精确矩形)、元数据(<title>/<meta name=author|description|keywords>)

测试

tests/PaperSharp.Tests,三层守门:

层 守什么
结构(PdfStructureTests) PDF 可解析(兜住 PdfEnhancer 手写 xref)、页数、A4 尺寸、<6MB 体积哨兵
语义(PdfSemanticTests) 书签树 / 链接注释 / 元数据、中文字可抽取(ToUnicode)、跨页有内容
像素(PngGolden + PdfRaster) PNG 字节级 golden;PDF 经 PDFium 读回栅格 diff ≤0.1%——验证"别人打开长什么样"
dotnet test tests/PaperSharp.Tests                        :: 全量 ~30s
dotnet test tests/PaperSharp.Tests --filter "Category!=Slow"   :: 跳过 PDF 栅格
UPDATE_GOLDENS=1 dotnet test tests/PaperSharp.Tests       :: 有意变更后重生成基准(评审 diff 再提交)

约定:永远不比 PDF 字节(Skia 嵌时间戳);golden 依赖本机字体栈,当前仅 Windows 钉基准;测试并行执行——库已线程安全(RenderScope 每请求隔离),全套并行跑本身就是并发回归哨兵,另有 ConcurrencyTests 专测(16 线程字节一致 / 文档间无污染 / BaseDir 不串台)。

性能

详见 PERF_REPORT.md(§11 字体子集化、§12 跨引擎赛道定位与 shaping 刀、§13 PDF 体积两刀、§14 合并态分段归因;§3–§7 是 2026-09-22/24 换 PNG 编码器前后的 bench 50,留作历史对照)。要点:

  • 单页 PDF:纯文字样例中位 27.5–64.8ms(invoice 31.0ms、report 两页 64.8ms),单线程吞吐 invoice 42.4 页/秒(§11 的字体子集化把 5.1 页/秒抬上来的:速度与体积是同一个根因,Skia 的 PDF 后端完全不子集化字体)。
  • 体积:纯文字单页 94 → 47.5 KB(§13.3 cmap 收口 + name 瘦身),m7 那种含彩色字体的页 −92.6%(§13.6 COLR v1 图层闭包),逐像素不变。
  • 多页成本模型:跨引擎语料上 warm 拟合 ≈ 165 ms + 50 ms/声明页(系统字体宿主),shaping run 缓存把边际压到 36.3 ms/页(−27%)、产物逐字节相同(§12.9)。
  • 布局(measure+paginate)<2%,层叠(AngleSharp GetComputedStyle)才是元素上千时的第一瓶颈——已用 EagerStyleCollection 把 O(n².5) 消回近线性(下两条)。
  • buildDoc(AngleSharp 逐元素层叠)曾随元素数超线性 ~O(n^2.5):table 2000 行 74.4s / flexlist 2000 条 21.3s,而同规模自研布局仅 76/162ms。2026-09-24 已定位并消掉:根因不是层叠算法贵,是 AngleSharp 每层叠一个元素、且对其每个祖先,都把整份样式表集合(未物化的 LINQ)重新枚举一遍 → 每元素 O(深度 × 文档大小)。改成「整篇枚举一次、之后吃这份列表」(Css/EagerStyleCollection.cs,~40 行,特异度/继承/!important/var() 仍全部由 AngleSharp 判定)后 table 2000 行 74.4s → 5.29s、净层叠同进程对拍 19–25×,规模档回到近线性;逐属性 57,058 次比对 0 差异(PERF_REPORT §5.5)。ROADMAP M6 的「自研级联」因此降级为可选的依赖收敛项。
  • 「谁是第一瓶颈」有两个区间:产物像素数主导时是编码,元素数上千时是层叠。2026-09-24 用自研并行 PNG 编码器把编码段压掉 14×(22 个真实页面样例合计 4752 → 339ms,端到端 2.74×、逐像素不变)之后,数百元素的常见页面(m1 501 盒:层叠 306ms vs 编码 46ms)也已转为层叠第一;随后上面的层叠改动把该语料端到端再压到 2,091ms(−15%,语料最大 501 盒,所以只有一两成);同日第二刀(层叠结果按元素缓存 + ReadStyle 双轮并一轮,语义仍零替换)在此之上把 22 样例 buildDoc 再压 1.76×、端到端 2,893 → 2,001ms(1.45×),门是 1,274,452 次逐属性比对 0 不一致(PERF_REPORT §5.6)。层叠这条路的性能理由至此用尽。

AngleSharp 坑位备忘(均已绕过,改这块代码前必读)

  • computed 把 % 按 1024 视口折算成 px(height:100%→1024px),calc() 同理被预解析 → 需要原始值的属性走 GetDeclarations() 取声明值(Val()/Spec()),% 与模板串交给 CssSize 布局期解析
  • computed 不序列化 margin/padding 简写(返回空串)→ 一律逐边读 longhand,否则静默丢 0
  • GetComputedStyle 每次调用都重走全文档收集样式表,而且对元素的每个祖先各走一次(GetStyleCollection() 内部是未物化的 Concat LINQ)→ 绝对不要在层叠循环里现取集合:整篇文档要 new EagerStyleCollection(window.GetStyleCollection()) 物化一次、DocContext.Styles 复用,否则每元素 O(深度 × 文档大小)、整篇 O(n²)(实测 2000 行表格 25.9s vs 物化后 1.04s,PERF_REPORT §5.5)。顺带:把 GetStyleCollection() 提到循环外并不治(探针 B 行 ≈ A 行),必须物化。且物化只消掉重枚举——AngleSharp 仍为每个祖先各跑一次全表匹配、结果反复重算,要用 EagerStyleCollection 自带的按元素缓存(递推 R(e)=ComputeCascadedStyle(e, parent:R(最近祖先)))才吃得到那 5–8×(§5.6)。
  • computed 对「未声明的属性」直接返回空串,不补初始值 → 别指望「先看 computed、空则跳过」能省掉第二轮 GetDeclarations():Raw()/Spec() 的兜底必然再问一次,实测闸门只放行 3/505 元素时 GetDeclarations 仍被调 505 次、耗时不变(PERF_REPORT §6.4)
  • computed 折 em 用的是渲染设备基线 16px,不是元素自己的 font-size → margin/padding 的 em 不能读 computed:UA 的 ul,ol{margin:1em 0} 作用在 .note{font-size:10pt} 上会被折成 16px 而非 13.333px,每条列表项凭空下坠 2.667px。钉在单测 AuthorMarginCascadeTests.UaMarginInEm_usesElementFontSize_notDeviceBase,别把 ReadStyle.MarginVal「简化」成只读 computed
  • radial-gradient 让整元素 GetComputedStyle 抛 NRE;repeating-* 渐变的 CssText 必抛 → Get() try/catch 兜底;background/background-image 一律不读 computed
  • text-overflow 根本不实现 → ScanUnsupported 从原始 CSS 建 class/id/tag 索引,Lookup() 兜底;AngleSharp 缺啥往 UnsupportedProps 加
  • overflow 只认 hidden/auto/scroll/visible:clip、overlay、两值简写 overflow: visible hidden 在 computed 和 GetDeclarations() 里都是空串(解析阶段即判非法丢弃)→ 三种写法靠 UnsupportedProps 的原文兜底捞回;兜底正则必须带 (?<![\w-]) 左边界,否则 overflow 会命中同一条规则里的 text-overflow:clip,把文本截断值当成盒级裁剪值
  • em/rem/% 需要非空 render device → BuildDocument 里 WithRenderDevice(new DefaultRenderDevice{ FontSize=16, ... })
  • AngleSharp 不内置 UA display → UaStylesheet 显式补 span/a/em/…{display:inline} 与表格家族;<br> 只进 inline 片段不进 Children(否则误判成块级)
  • 伪元素 UA 默认注入 display:block(computed 与 declared 都是,作者没写也判块)→ 不能据此定行内/块级;ScanPseudoDisplay 从原始 CSS 扫作者显式声明的 display,缺省按 inline
  • counters(a,',') 的逗号被重序列化吃掉(computed/declared 都变 counters(a ,)),且按 token 兜底会串味(li 命中别的选择器的 li)→ content 串仍走 computed(选择器门控正确),但解析器 FuncArgs 兼容「逗号或空白」两种分隔
  • counter-reset/counter-increment computed 恒返回 "none" → 挡住 Lookup 兜底;CleanCounter 把 "none"/空当缺失,强制走原始 CSS 声明值
  • background-clip computed 恒返回 border-box(即便写了 text)→ Lookup 传空 current 强制读原始声明值才能认出 text
  • animation/@keyframes 完全不实现 → 走 UnsupportedProps 原始 CSS 兜底 + 自研 Animation 解析与取帧;简写解析里 forwards/backwards 以 s 结尾,须先认关键字再认时间(否则误当秒数)
  • 版本号会漂:restore 失败跑 dotnet add package AngleSharp.Css --prerelease(AngleSharp 主库需匹配)

历史缺陷(已修,动到对应模块前先读代码注释):表格定位 O(行²)→线性、inline 片段合并漏推进 curW、单元格建列宽前未 ResolveBoxSizes 致逐字折行、ParseSize 负值当 auto。

其它环境问题:Linux 缺中文字体装 fonts-noto-cjk;monospace 回退是纯拉丁系,中文等宽需显式配 CJK 字体;dotnet run --no-build 在构建失败时会静默跑旧 dll——回归前确认 build 真成功。

扩展方向

见 ROADMAP.md(差距矩阵 + M5 剩余 / M6 自研级联 + 明确不做;M7 复杂文字与并发安全化已交付)。

About

Lightweight, cross-platform .NET renderer that turns HTML + CSS into paged PDF and PNG — no headless browser. hand-written block-flow layout. From HTML to any document.

Topics

Resources

Stars

7 stars

Watchers

0 watching

Forks

Packages

Contributors

Languages