面向 .NET 的高性能pdf/image渲染引擎 —— 纯托管代码把 HTML/CSS 直接编译为矢量 PDF 与高清图片,无需浏览器,无需 headless Chrome,纯c#自绘。
HTML + CSS→PDF · PNG · JPEG · WebP · ICO · SVG
https://papersharp.netlify.app
- 零浏览器依赖 —— 不装 Chromium、不起子进程、不开调试端口;一个类库嵌进你的 .NET 进程即可渲染
- 服务端效率 —— 单页 PDF 中位约 30ms,单线程吞吐约 40 页/秒;线程安全,多请求可并发进同一进程;常驻仅一个类库的体量,没有 V8/渲染树的内存包袱
- 中文原生排版 —— CJK 逐字折行、用字级字体子集嵌入(纯文字单页 PDF 约 50–120KB,文字可选中可搜索)、中文数字页码
- 打印级语义 —— 天生面向分页媒体:
@page页边距、页眉页脚边距盒、自动跨页回流、表格跨页续 +<thead>重复、break-inside/orphans/widows - 确定性输出 —— 默认离线渲染,同一输入恒定产物,适合发票、报表、合同等财务级文档
- 需要创建html所见所得模板 实现用户前端使用html实时编辑查看pdf模板,保存时将html保存到后端数据库,当需要生成pdf的时候,后端替换pdf中的占位符,最后将替换后的html交给
PaperSharp生成pdf,如果pdf库不支持html,则工作量极大。
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。Linux 需安装
fonts-noto-cjk才有中文回退;monospace回退是纯拉丁系,中文等宽要显式配 CJK 字体。
每个场景一行调用 / 一段 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(阿拉伯/希伯来/越南/韩中混排整页)
- 分页:块级跨页回流;长表跨页续 + 共享列宽 + 重复
<thead>;break-before/after: page、break-inside: avoid、orphans/widows;@pagesize/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/颜色/长度/圆角/字号/定位偏移按帧插值;输出仍是静态单帧、逐字节确定。Tailwindanimate-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 是复杂文字的已知简化