Files
LabelChange-server/.trae/documents/pdf_to_bitmap_rendering_plan.md
2026-06-01 16:30:29 +08:00

8.0 KiB
Raw Blame History

PDF页面渲染方案 - ConvertPdfFirstPageToBitmap改造

问题诊断

当前ConvertPdfFirstPageToBitmap方法只生成纯白色Bitmap未实现PDF页面的实际渲染导致条码识别时无法识别到PDF中的内容包括条码

关键需求更新

  1. 充分利用现有能力项目代码中已有将PDF URL转换成字节流的功能此方案应与之集成
  2. 确保缓存完整性只要验证了PDF有效性不管是否成功提取条码都必须将PDF字节流缓存到数据库
  3. 保障业务连续性:缓存的目的是确保现场可以正常进行面单打印,条码识别是增强功能非必须功能

现有资源确认

  • 项目已集成DinkToPdf库(通过PdfConverterSingleton
  • 项目已有System.DrawingSystem.Drawing.Imaging
  • 项目已有PDF URL→字节流的转换能力在下载/上传流程中)
  • 完整的CORE SDK支持

改进后的解决方案

核心逻辑调整

原始流程(问题):

下载PDF → ConvertPdfFirstPageToBitmap(纯白) → 条码识别(失败) → 缓存PDF

改进流程(推荐):

下载PDF已是字节流
    ↓
验证PDF有效性 ✅ (关键点:必须执行)
    ├─ 有效 → 立即缓存PDF字节流到数据库 ⭐ (优先级:高)
    └─ 无效 → 标记缓存失败,不阻塞业务
    ↓
条码识别(可选功能)
    ├─ ConvertPdfFirstPageToBitmap渲染
    ├─ RecognizeBarcodeAsync识别
    └─ 识别成功 → 更新缓存的条码字段(选择性)
    └─ 识别失败 → 不影响已缓存的PDF数据 ⭐

关键设计原则

原则1缓存优先保障业务

  • 验证通过即缓存PDF验证成功→立即缓存PDF字节流
  • 条码是增强功能条码识别失败不影响PDF缓存
  • 现场打印保障确保缓存的PDF数据始终可用于打印

原则2充分利用现有能力

  • 复用字节流处理项目中已有的PDF URL→字节流转换逻辑
  • 整合现有DinkToPdf如使用DinkToPdf转换复用已有的PdfConverterSingleton
  • 集成Ghostscript如采用Ghostscript方案将PDF字节流直接传入

原则3非阻塞设计

  • PDF缓存同步:验证+缓存为关键路径,必须快速完成
  • 条码识别异步:条码识别为后续异步操作,失败不影响主流程

实施方案详细设计

方案A基于现有DinkToPdf最小化改动 推荐短期

优点

  • 项目已集成DinkToPdf
  • 与现有代码体系一致
  • 改动最小

流程

PDF字节流已有
    ↓
验证PDF + 缓存PDF字节流
    ↓
使用DinkToPdf或项目已有能力进行渲染
    ↓
条码识别(非必须)

方案B集成Ghostscript生产级方案 推荐长期

优点

  • 渲染效果最佳
  • 专业PDF处理
  • 性能和质量兼优

流程

PDF字节流已有
    ↓
验证PDF + 缓存PDF字节流
    ↓
使用Ghostscript渲染第一页→Bitmap
    ↓
条码识别(非必须)

推荐实施流程

第一阶段:保障业务连续性(高优先级)

  1. 改造缓存逻辑确保PDF验证通过→立即缓存

    • ProcessSingleCacheTask中调整顺序
    • 先保存PDF字节流再进行条码识别
    • 条码识别失败不回滚缓存
  2. 缓存表结构确认确保能存储完整PDF数据

    • PdfBytes字段存储PDF二进制数据 (已有)
    • Status字段标记缓存状态 (已有)
    • 条码字段:可选,识别成功时更新 (已有)

第二阶段改造ConvertPdfFirstPageToBitmap方法中优先级

选项A1使用DinkToPdf现有能力

输入byte[] pdfBytes已有字节流
处理流程:
1. 使用PdfConverterSingleton进行转换
2. 或复用项目中TagGenerationService的PDF处理逻辑
3. 生成Bitmap用于条码识别
输出Bitmap对象

选项B1集成Ghostscript.NET

dotnet add package Ghostscript.NET
输入byte[] pdfBytes已有字节流
处理流程:
1. 保存PDF字节流到临时文件
2. 使用GhostscriptRasterizer初始化
3. 设置DPI为200
4. 渲染第一页→Image
5. 转换为Bitmap
6. 清理临时文件
输出Bitmap对象

第三阶段:异常处理和性能优化(低优先级)

  • 渲染超时处理3-5秒
  • 大文件内存管理
  • 缓存渲染结果

实现要点详解

关键修改1缓存优先原则

在ProcessSingleCacheTask中

原始下载PDF → 条码识别 → 成功则保存 → 失败则标记失败
改进下载PDF → 验证PDF → 立即保存PDF → 异步进行条码识别

新增逻辑

// 1. 获取PDF字节流现有能力
byte[] labelBytes = // ... 现有转换逻辑

// 2. 验证PDF有效性
int pageCount = GetPdfPageCount(labelBytes);  // 验证通过

// 3. 立即保存PDF到缓存同步
await SaveCacheAsync(
    waybillNumber, 
    labelBytes, 
    pageCount, 
    labelBytes.Length,
    finalMileTrackingNumber, 
    customerId
    // 暂不传入条码信息
);

// 4. 异步进行条码识别(不阻塞,失败不影响缓存)
_ = Task.Run(async () =>
{
    try
    {
        var (barcodeNumber, barcodeType, confidence) = 
            await ExtractBarcodeFromPdfAsync(labelBytes);
        
        if (!string.IsNullOrEmpty(barcodeNumber))
        {
            // 条码识别成功,更新缓存中的条码字段
            await UpdateCacheWithBarcodeAsync(waybillNumber, barcodeNumber, barcodeType, confidence);
        }
    }
    catch { /* 静默失败不影响已缓存的PDF */ }
});

关键修改2PDF字节流复用

充分利用现有能力

现有代码中的PDF URL → 字节流转换
↓
直接传给ConvertPdfFirstPageToBitmap
↓
直接传给SaveCacheAsync

好处

  • 无需重复转换
  • 一次转换多次使用
  • 性能最优

风险评估与对策

风险1缓存失败导致无备份

风险如果缓存失败可能没有PDF数据用于打印 对策

  • 重试机制缓存失败重试3次
  • 业务降级缓存失败时保留URL现场可实时下载
  • 监控告警记录所有缓存失败的case

风险2条码识别阻塞缓存

风险:条码识别耗时影响业务 对策

  • 使用异步Task已设计
  • 设置超时控制
  • 失败自动降级

风险3Ghostscript部署依赖

风险Ghostscript需要系统配置 对策

  • 选择包含预编译二进制的NuGet版本
  • 统一部署文档和脚本
  • 开发环境提前测试

验证方案

验收标准

  • 业务保障无论条码识别成否PDF都被成功缓存
  • 调试可见SaveDebugImage输出显示真实PDF内容非空白
  • 条码增强:条码识别成功率>80%(失败不影响业务)
  • 性能指标
    • PDF验证+缓存:<1秒
    • PDF渲染<3秒
    • 条码识别:<2秒
  • 异常处理:所有异常都被捕获,不影响主流程

测试场景

  1. 正常PDF→成功缓存+条码识别成功
  2. 正常PDF→成功缓存+条码识别失败验收PDF仍被缓存
  3. 包含二维码的PDF→成功缓存+二维码识别
  4. 包含一维码的PDF→成功缓存+一维码识别
  5. 损坏的PDF→缓存失败异常处理业务降级
  6. 超大PDF→性能和内存测试

实施优先级

  1. 高优先级:修改缓存逻辑,确保验证通过→立即缓存
  2. 高优先级改造ConvertPdfFirstPageToBitmap进行真实渲染
  3. 中优先级:条码识别异步化,失败自动降级
  4. 低优先级:性能优化和缓存结果

预期效果

完成此方案后:

  • 业务连续性保障缓存的PDF数据可用于现场打印
  • 渲染效果提升调试图像显示真实PDF内容
  • 条码识别增强:成功识别条码但不强制依赖
  • 系统鲁棒性:异常不影响核心缓存功能
  • 现有资源复用:充分利用已有的字节流转换能力