PowGrade.FileConverters.DocxToPdf
0.1.0
dotnet add package PowGrade.FileConverters.DocxToPdf --version 0.1.0
NuGet\Install-Package PowGrade.FileConverters.DocxToPdf -Version 0.1.0
<PackageReference Include="PowGrade.FileConverters.DocxToPdf" Version="0.1.0" />
<PackageVersion Include="PowGrade.FileConverters.DocxToPdf" Version="0.1.0" />
<PackageReference Include="PowGrade.FileConverters.DocxToPdf" />
paket add PowGrade.FileConverters.DocxToPdf --version 0.1.0
#r "nuget: PowGrade.FileConverters.DocxToPdf, 0.1.0"
#:package PowGrade.FileConverters.DocxToPdf@0.1.0
#addin nuget:?package=PowGrade.FileConverters.DocxToPdf&version=0.1.0
#tool nuget:?package=PowGrade.FileConverters.DocxToPdf&version=0.1.0
DocxToPdf(DOCX 转 PDF)
高性能、可扩展的 .NET 10 文档转换库与 CLI,用自研 PDF 引擎(SkiaSharp 驱动)将 DOCX 高保真转换为 PDF,支持 PDF/A、数字签名、目录页、页眉页脚、脚注尾注、多栏排版、字段解析等企业级能力。
功能概览
- 文档元素:段落、列表、表格、图片、超链接、书签、页眉/页脚、脚注/尾注、页面边框、多栏、横竖排、首字下沉
- 高级样式:完整样式继承、系统/嵌入/CJK 字体、主题色、VML 形状、DrawingML 文本提取与格式保留
- 图形图表:柱/条、折线、饼图、面积、散点、雷达、圆环、股票、气泡、曲面、组合图;SmartArt 栅格化渲染、未知布局基于 layout hint 的自适应 generic placement/connector/text-box sizing/shape routing(已覆盖 relationship、balance、converging、diverging、target、alternating、stacked、timeline、funnel 等弱分类族),并尽量保留可提取文本
- 数学公式:OMML 渲染;分数、上下标、根号、矩阵等布局
- 字段系统:PAGE/NUMPAGES(双遍延迟)、DATE/TIME、AUTHOR/TITLE/SUBJECT、FILENAME/FILEPATH、DOCVARIABLE、MERGEFIELD、HYPERLINK、REF/PAGEREF、TOC、IF/COMPARE、LISTNUM、SYMBOL、SECTION/SECTIONPAGES、EQ、SET/ASK/FILLIN、USERNAME/USERINITIALS、MERGEREC/MERGESEQ、BARCODE、AUTOTEXT
- PDF 能力:加密与权限、PDF/A(1a/1b/2a/2b/2u/3a/3b/3u/4/4e/4f)、ICC/XMP 元数据、数字签名(PKCS#7、PAdES B‑B/BT/BLT/BLTA)、时间戳
- 专业特性:水印(文本/图片、位置/旋转、支持平铺、常量透明度与透明 PNG alpha 保留)、修订跟踪与汇总页、目录页、行号(页面/节/连续重启)、性能模式、转换进度回调、结构化文档标签(SDT)、文档复杂度分析
新增 DrawingML 矢量/渐变能力(近期)
- 线性渐变:支持 tileRect、rotWithShape、scaled=false 等关键参数组合
- 预设矢量形状扩展:diamond、parallelogram、trapezoid、chevron、rightTriangle、teardrop、octagon、star4/6/8/10/12、ribbon、wave、doubleWave,以及 leftArrow/rightArrow/upArrow/downArrow/leftRightArrow/upDownArrow 等箭头(含别名)
完成度评估
- 测试通过:644 个单元测试全部通过(本地运行验证)
- 建议按 3 个口径理解当前完成度:
- 日常生产可用度:约 95% - 97%
- README 宣称能力的兑现度:约 91% - 94%
- 对 Word 像素级一致性/审计级标准的满足度:约 82% - 86%
| 能力域 | 评估 | 状态 | 说明 |
|---|---|---|---|
| 核心正文/段落/列表/表格/图片 | 94% - 97% | 稳定 | 主转换链成熟,基础版式和常见文档结构覆盖充分;paragraph/tab/float/top-bottom/directional-wrap 已连续补强。 |
| 样式继承/主题色/字体选择 | 90% - 94% | 稳定 | 样式继承、主题色、常见中英文字体路径较完整。 |
| 字段系统与延迟回填 | 94% - 97% | 强项 | PAGE/NUMPAGES、SECTION、REF/PAGEREF、IF、DOCVARIABLE、BARCODE、TOC 等覆盖深。 |
| 页眉页脚/脚注尾注/目录/行号 | 88% - 92% | 稳定 | 多节继承、TOC 双遍回填、行号与分页字段已打通。 |
| DrawingML/VML/图表 | 89% - 95% | 可用但有降级 | 常见形状、图表类型和文本提取已实现;tight/through 多边形与 raster/vector mask 环绕、multi-contour wrapPolygon、wrapText side、字符/列/行级锚点主链已打通,复杂 /main shape 的降级栅格路径已从 placeholder 提升为真实 DrawingML 渲染,grouped/multi-image 复合形状现在不仅支持 picture-only live image layers,也支持多层 live vector background + live image layers + searchable text,并补上 picture rotation、srcRect crop、flip 与更深一层 nested group transform 语义;本地 file-backed、相对 source DOCX 目录的 external workbook target,以及 ms-excel:ofe|u|... 包装路径都已能直接抽取真实数据而不是退回 placeholder。 |
| SmartArt/复杂图形 | 82% - 89% | 部分降级 | 仍以栅格化主链为主,但优化布局覆盖更广;未知布局已从单一 generic fallback 继续推进到 relationship/balance/converging/diverging/target/alternating/stacked/timeline/funnel 等 layout-hint-driven placement,并补上更细的 connector、文本盒尺寸、shape routing 与 theme accent palette 语义,可提取文本在可解析时已尽量保留为 searchable text。 |
| PDF/A/签名/校验/CLI | 88% - 94% | 稳定 | PDF/A、签名、校验报告、CLI 诊断链路完整。 |
| 批注/修订/协作语义 | 70% - 80% | 部分实现 | 汇总页和基础修订支持较好,但 Word 协作语义并非完全等价。 |
| 水印 | 88% - 92% | 主链稳定 | 主转换链与后处理盖章链均支持文本/图片水印、位置、旋转、平铺、常量透明度和透明 PNG alpha 保留。 |
当前已验证的实现亮点:
- 30+ 字段类型的嵌套解析与运行时页码/分节信息注入
- 多节页面设置与页眉页脚继承、脚注/尾注内容输出
- PDF/A 元数据与输出意图、合规性验证可选
- 数字签名(包含可见签名外观,含 TSA 时间戳)
- AltChunk 扁平化(HTML/MHTML/XML/WordprocessingML)
- DrawingML/VML 文本与常见形状处理,混排图文的合理降级
ImageConverter已补上隔离回归覆盖:页眉部件作用域CreateScoped(...)、共享浮动锚点 marker 传递、以及混合 textbox+image anchored drawing 的双通道保留StructuredDocumentTagHelper已补上隔离回归覆盖:文本、日期、下拉/组合框、复选框,以及无显式类型时的 plain text fallback 与 display value 契约RevisionHandler已补上隔离回归覆盖:插入/删除识别、tracked-change 元数据提取、样式着色、修订收集/统计、显示文本,以及修订摘要页;同时修正为基于 OpenXML 实际的InsertedRun/DeletedRunwrapper 解算修订信息VmlHelper已补上隔离回归覆盖:WordArttextpath文本提取、VMLshape/rect基础形状映射,并修正 WordArt 样式中的文本色color会真正进入输出字体FieldCodeHelper已补上隔离回归覆盖:LISTNUM 编号格式、SHAPEDOG 开关解析、SYMBOL 十进制/十六进制/补充平面字符解码;Phase 0 里计划的关键 helper harness 已基本补齐- 浮动布局主链已覆盖:tab 对齐、same-page exclusion 持久化、TopAndBottom 统一路径、
distT/distB/distL/distR、even-odd 多 contourwrapPolygon、基于区间中点 inside 判定的更稳健 polygon scanline、按 contour 顶点自适应切片的 polygon exclusion band、wrapTextside、raster/vector Tight/Through 多段 mask 行扫描、避免过度合并 row-slice exclusion、同一行多 exclusion 时的最大可用间隙选择、跨页与代理画布共享的 marker 锚点上下文、浮动对象缓存最近一次已解析 marker 位置、竖排character水平锚点的 marker/cached-anchor 复用,以及正文竖排、页眉页脚竖排、自然分页 continuation paragraph anchored textbox(横排 + 竖排)、section 级竖排表格单元格、页眉页脚表格路径下的竖排 anchored textbox direction + marker binding、rightMargin/topMargin/bottomMargin等 margin-edge offset/alignment anchors,以及跨页嵌套表格 textbox 的 marker-relative 回归;正文与页眉页脚中BehindText/InFrontOfText也已按relativeHeight更接近 Word 的分层排序 - 复杂
/mainDrawingML shape 在无法继续走向量/结构化路径时,现已优先使用真实DrawingMLRenderer栅格输出替代通用 placeholder PNG;genericwps:wsp文本体和多图片组合在降级路径中的视觉保留显著更好 - grouped/multi-image DrawingML 在经过 DOCX 落盘重开后,已可保留 picture-only live image layers;对多层 vector background + image layers 的混合对象,也能继续输出 live background + live images + searchable text,并补上 picture rotation、
srcRectcrop、flip 与 nested group transform 语义,而不再统一退化成复合 raster 背景;无文本的 grouped preset/custom geometry 也已能直接走 live vector path,而不是因为多spPr统一退回栅格 - inline SmartArt 未知布局已从单一 process fallback 提升到基于 layout hint 的自适应 generic placement;relationship/balance/converging/diverging/target/alternating/stacked/timeline/funnel 等弱分类布局会继续套用更贴近模板意图的 connector、文本盒尺寸、shape routing 与 theme accent palette 策略,并在可解析时保留 searchable text
已知限制与规划
- DrawingML 矢量化:常见几何子集已支持;复杂路径/公式、部分线端样式、复杂渐变仍回退栅格
- 非图片 Tight/Through:矢量内容在无
wrapPolygon时已可按 alpha mask 逐行避让并保留单行内多段 opaque run;显式wrapPolygon现已支持 even-odd 多 contour/孔洞场景,但极端自交路径仍未完全等价 wrapPolygon:当前已支持 even-odd 多 contour exclusion,并改为基于唯一交点分段后的中点 inside 判定与 contour 顶点自适应 band 切片;reopened/nested point container 也能继续还原 contour,但极端自交、多层嵌套和与 Word 专有几何规则完全一致的边界仍待继续补齐character/line相对定位:已接入 marker 驱动的字符/行级锚点解算,并补上跨页与代理画布的锚点上下文共享与最近一次解析结果缓存;段落跨页后再延迟落位的复杂组合仍有边缘差异- 极端嵌套锚点:文本框/表格/多栏中的多级浮动对象,在跨页与避让叠加场景仍有边缘差异
- SmartArt:已优化多种布局的栅格化排版;未知布局现已采用 layout-hint-driven generic placement,并补上 relationship/balance/converging/diverging/target/alternating/stacked/timeline/funnel 等连接线、文本盒尺寸与 shape routing 策略,但仍未达到 Microsoft 各模板的完整 template-specific 语义
- 图表数据源:支持嵌入式工作簿、缓存引用,以及本地 file-backed、相对 source DOCX 目录与
ms-excel:ofe|u|...包装的外部工作簿路径;需远程/不可解析外部工作簿实时计算的场景将以占位呈现 - 复杂 grouped/multi-image DrawingML shape 中,picture-only 与多层 vector background + image layers 的复合对象都已可输出 live 结构;picture 级旋转、
srcRect裁剪、flip 与 nested group transform 也已接入,但更深层层级、混合自定义几何与更复杂组合矩阵仍可能退回复合栅格背景,尚未达到 Word 级对象语义等价 - AltChunk:复杂外部格式的深度展开有限,已保证常见 HTML/MIME 场景可用
- 超大文档(1000+ 页):规划进一步性能优化(分块流式、粗排版预估等)
离 100% 还差多远(当前判断):
- 核心正文主链大致还差 3% - 5%,剩余主要集中在极端浮动几何、多轮廓/孔洞 wrap、跨页与多级嵌套避让,而不是普通段落/列表/表格/图片。
- README 宣称能力兑现度大致还差 6% - 9%,主要差在复杂 DrawingML/SmartArt、协作语义、以及部分审计级 PDF/版式一致性场景。
- 如果目标是“和 Word 像素级一致”,仍有约 14% - 18% 的差距,主要不是功能缺失,而是复杂布局细节和降级路径的一致性问题。
改进计划(节选):
- 修复并减少现有构建告警(空引用、未用变量、过时 API)
- DrawingML 矢量路径与渐变继续覆盖更多预设与自定义子集,并把现有 live composite 从当前的 nested group/flip/多层 vector background 语义继续推进到更复杂的层级矩阵、混合几何与 Word 特有组合规则
- SmartArt 未知/弱分类布局继续从当前的 layout-hint-driven generic placement + shape routing 推进到更多模板族的 connector path、node family 与文本盒尺寸策略
- Tight/Through 真实轮廓避让继续补齐带孔洞/多轮廓/自交几何在跨页、多层嵌套和 Word 专有边界规则下的等价性,并继续压缩剩余边界差异
- 图表文本布局使用新版 Skia API 提升标注清晰度
- 将中文字体选择(当前默认 SimSun)外置为配置项并支持字体优先级
- 将
character/line相对锚点继续推进到跨页、多级嵌套、文本框/表格组合场景下更接近 Word 的定位 - AltChunk 的表格/列表再细化边界保留与空白折叠策略
快速开始
前置条件
- .NET 10 SDK
构建
git clone <repository-url>
cd PowGrade.FileConverters.DocxToPdf/src
dotnet build
命令行
CLI 位于 src/PowGrade.FileConverters.DocxToPdf.Cli:
cd PowGrade.FileConverters.DocxToPdf/src/PowGrade.FileConverters.DocxToPdf.Cli
dotnet run -- convert <input.docx> <output.pdf>
dotnet run -- <input.docx> <output.pdf>
dotnet run -- convert <input.docx> <output.pdf> --engine-report-json
dotnet run -- convert <input.docx> <output.pdf> --engine-root path/to/powgrade-pdf-runtime --engine-report-json
dotnet run -- convert <input.docx> <output.pdf> --engine-report-out engine-report.json --require-extracted-engine
dotnet run -- inspect-engine --json --require-extracted-engine
不带参数时会打印 usage 并以非 0 退出。
DOCX 预检/验证:
dotnet run -- preflight <input.docx|directory> --json
dotnet run -- preflight <input.docx|directory> --json --summary-only
dotnet run -- validate <input.docx|directory> --perfect --json
dotnet run -- convert <input.docx> <output.pdf> --summary-json
dotnet run -- convert <input.docx> <output.pdf> --summary-out report.json
preflight 与 validate 只报告静态验证信号,不执行实际转换,因此不会采集 runtime degradation 或 placeholder 事件。
需要查看运行时降级时,使用 convert --summary-json 或 convert --summary-out,并读取 summary.runtimeDegradations。
当 preflight / validate 使用 --summary-only --json 时,输出会显式标记 runtimeDegradationsCaptured: false;而 convert --summary-json / --summary-out 会在 summary 中标记 runtimeDegradationsCaptured: true。
校验已生成 PDF:
dotnet run -- validate <input.pdf> --pdfa PdfA2b
dotnet run -- validate <input.pdf> --signature
dotnet run -- validate <input.pdf> --pdfa PdfA2b --signature --json
dotnet run -- validate <input.pdf> --pdfa PdfA2b --json --out report.json
--json 输出机器可读报告;--out 同时写入报告文件。
--require-extracted-engine 可强制转换时使用已剥离的 PDF engine 边界。
--engine-root 或环境变量 POWGRADE_PDF_ENGINE_ROOT 可显式指定外置 PowGrade.Pdf.* runtime 目录。
inspect-engine 可在不执行转换时检查当前 runtime 的发现来源、命中目录与边界状态。
作为库使用
using PowGrade.FileConverters.DocxToPdf;
using PowGrade.FileConverters.DocxToPdf.Models;
using var input = File.OpenRead("input.docx");
using var output = File.Create("output.pdf");
var converter = new DocxToPdfConverter(new ConvertOptions());
converter.Convert(input, output);
var runtimeReport = converter.ConvertWithRuntimeReport("input.docx", "output.pdf");
Console.WriteLine($"Extracted boundary: {runtimeReport.EngineRuntimeBoundary?.IsExtractedBoundary}");
依赖注入:
services.AddDocxToPdf();
// 或(直接声明外置 engine runtime 目录)
services.AddDocxToPdf("/opt/powgrade-pdf-runtime");
// 或(带配置)
services.AddDocxToPdf(options =>
{
options.EngineRootDirectory = "/opt/powgrade-pdf-runtime";
options.RequireExtractedEngineBoundary = true;
});
// 或(从配置绑定)
services.AddDocxToPdf(configuration, "DocxToPdf");
// 或(同时接收 runtime 边界报告)
services.AddDocxToPdf(
"/opt/powgrade-pdf-runtime",
new Progress<EngineRuntimeBoundaryReport>(report =>
{
Console.WriteLine(report.IsExtractedBoundary);
}));
// 或(从配置绑定并同时接收 runtime 边界报告)
services.AddDocxToPdf(configuration, new Progress<EngineRuntimeBoundaryReport>(report =>
{
Console.WriteLine(report.EngineRootSource);
}));
var converter = app.Services.GetRequiredService<DocxToPdfConverter>();
// 或
var fileConverter = app.Services.GetRequiredService<IFileConverter>();
{
"DocxToPdf": {
"EngineRootDirectory": "/opt/powgrade-pdf-runtime",
"RequireExtractedEngineBoundary": true,
"AnalyzeDocumentBeforeConversion": true
}
}
EngineRootDirectory 的解析优先级是:显式配置/参数 > 环境变量 POWGRADE_PDF_ENGINE_ROOT > 自动发现。
自动发现当前会优先检查运行目录本身,其次检查约定子目录(如 powgrade-pdf-runtime、PowGrade.Pdf.Runtime、runtimes/powgrade-pdf),必要时也会检查源文档所在目录旁的约定 runtime 目录。
当需要确认最终命中了哪条路径时,优先运行 inspect-engine 或读取 engineRuntime.engineRootSource。
高级配置示例
var options = new ConvertOptions
{
// 进度与性能
EnablePerformanceMonitoring = true, // 输出阶段耗时与计数器
ProgressReporter = new Progress<ConvertProgress>(p =>
{
// p.Stage, p.CompletedItems/p.TotalItems, p.Message, p.CurrentItem
// 例如:页级进度 "生成新页面"、"TOC Page N"
}),
// 图片下采样(大文档友好)
DownscaleLargeImages = true,
TargetImageDpi = 150,
MaxImageMegapixels = 8.0,
DownscaleJpegQuality = 85,
// 中文字体优先级(按顺序尝试),未找到时回退到 DefaultFontName
PreferredChineseFonts = new List<string>
{
"Microsoft YaHei", "SimSun", "NSimSun",
"SimHei", "KaiTi", "FangSong",
"PingFang SC", "Hiragino Sans GB", "WenQuanYi Micro Hei",
"STSong-Light"
},
PageSize = Rectangle.A4,
MarginLeft = 72f,
MarginRight = 72f,
MarginTop = 72f,
MarginBottom = 72f,
RenderHeadersFooters = true,
GenerateTableOfContents = true,
AddCommentsSummaryPage = true,
Watermark = new WatermarkOptions
{
Text = "CONFIDENTIAL",
FontSize = 48f,
Opacity = 0.3f,
Rotation = -45f,
Tiled = true
},
Encryption = new PdfEncryptionOptions
{
UserPassword = "user123",
OwnerPassword = "owner123",
AllowPrint = true,
AllowCopyContent = false
},
PdfACompliance = PdfAComplianceLevel.PdfA2b,
SignatureOptions = new SignatureOptions
{
CertificatePath = "certificate.pfx",
CertificatePassword = "password",
SignatureType = SignatureType.PadesBT,
Reason = "Document Approval",
Location = "Office"
}
};
var converter = new DocxToPdfConverter(options);
converter.Convert("input.docx", "output.pdf");
项目结构
src/
├── PowGrade.FileConverters.DocxToPdf/
│ ├── DocxToPdfConverter.cs
│ ├── ServiceCollectionExtensions.cs
│ ├── Converters/
│ ├── Rendering/
│ ├── Rasterization/
│ ├── Helpers/
│ ├── PdfEngine/
│ └── Models/
├── PowGrade.FileConverters.DocxToPdf.Cli/
└── PowGrade.FileConverters.DocxToPdf.Tests/
tests/
├── fidelity-corpus/
└── fixtures/
tools/
└── DocxFixtureGenerator/
测试
cd src/PowGrade.FileConverters.DocxToPdf.Tests
dotnet test
已跟踪的基线夹具位于 tests/fidelity-corpus/ 和 tests/fixtures/paragraph-converter/。
tests/fidelity-corpus/*.json sidecar 现可为样本声明页数、允许的 perfect-conversion warning 片段,以及 page-level PDF content assertions(containsText / doesNotContainText / matchesPattern / doesNotMatchPattern / containsTextsInOrder)和文本级 scorecard(pdfTextCountAssertions / pdfTextPageAssertions),便于把 placeholder/raster fallback、关键文本存在性、基础阅读顺序和跨页分布逐步纳入 scorecard。
tests/fidelity-corpus/*.pdf 是仓库内的回归参考 PDF,不是 Microsoft Word 打开 DOCX 时的权威视觉真值。当需要按 Word 实际打开态逐页比对时,先用下面的脚本导出 Word 页面截图,再让 .codex-out/sample1/compare-viewer.html 右侧优先加载这些截图:
powershell -ExecutionPolicy Bypass -File .\tools\ExportWordPageScreenshots.ps1 `
-DocxPath .\tests\fidelity-corpus\sample1.docx `
-OutputDirectory .\.codex-out\sample1\word-baseline
导出脚本会通过 Word 对象模型把每页导出为 Enhanced Metafile 再栅格化成 PNG,不依赖屏幕截图;生成完毕后,compare viewer 会优先使用 .codex-out/sample1/word-baseline/page-{n}.png,仅在截图缺失时回退到 tests/fidelity-corpus/sample1.pdf。
当需要重新生成这些样本时,运行:
dotnet run --project tools/DocxFixtureGenerator/DocxFixtureGenerator.csproj
覆盖范围:段落/表格/列表/图片、样式继承、字段解析、DrawingML/SmartArt、图表、OMML、页眉页脚、页面边框、PDF 输出校验、CLI 命令等。
许可证
MIT License(见根目录 LICENSE)。
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net8.0
- BouncyCastle.Cryptography (>= 2.6.2)
- DocumentFormat.OpenXml (>= 3.5.1)
- Microsoft.Extensions.Configuration.Abstractions (>= 8.0.0)
- Microsoft.Extensions.Configuration.Binder (>= 8.0.2)
- PowGrade.FileConverters.Core (>= 0.1.0)
- PowGrade.Pdf (>= 0.1.0)
- SkiaSharp (>= 3.119.2)
- System.Security.Cryptography.Pkcs (>= 8.0.1)
- System.Text.Json (>= 8.0.6)
NuGet packages (1)
Showing the top 1 NuGet packages that depend on PowGrade.FileConverters.DocxToPdf:
| Package | Downloads |
|---|---|
|
PowGrade.FileConverters.DocxToPdf.Cli
Package Description |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 0.1.0 | 170 | 4/30/2026 |