You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用PDFiumSharp转大PDF为单图时Image.Save到MemoryStream失败如何解决

错误原因分析
  • 首先是JPEG格式标准硬限制:JPEG单帧图像的最大边长不得超过65535像素。按你当前200DPI的设置,单张A4 PDF(高度为842点)转换后高度约为2339像素,28页左右总高度就会超出65535的限制,保存时必然触发编码错误。
  • 其次是GDI+ Bitmap的限制:GDI+的Bitmap内存分配在系统GDI共享内存池,而非.NET托管堆,对单张图像的总像素、最大高度都有环境相关的隐性限制,大尺寸图像容易出现分配失败、保存失败的问题。
  • 你遇到的A generic error occurred in GDI+和MemoryStream没有关系,本质是上述两个限制共同导致的。
不拆分页面的解决方案

方案1:下调DPI(改动最小,优先推荐)

只要将DPI下调到130及以下,40页A4 PDF拼接后的总高度就会低于65535像素,完全符合JPEG格式限制,无需修改其他逻辑:

// 原DPI=200改为130即可适配40页A4的需求
private readonly int DPI = 130;

如果需要更高清晰度,可根据实际最大页数计算DPI上限:最大DPI = 65535 * 72 / (单页高度点数 * 最大页数),A4单页高度为842点,40页的话计算下来最大DPI约为139。

方案2:更换图像编码API(无需降DPI)

弃用GDI+的Image.Save方法,改用WPF成像体系的JpegBitmapEncoder或者System.Drawing.Common的更新版本编码,这类API不受GDI内存池限制,对大尺寸图像的支持更好。如果边长必须超过65535,建议更换输出格式为PNG,PNG没有65535的边长限制。

方案3:优化像素拷贝逻辑(降低内存占用)

现有代码中每页需要经过PDFiumBitmap -> 流 -> Image -> 绘制到总Bitmap的流程,中间存在多次内存拷贝,大文件下容易触发内存不足。可以直接将PDFiumBitmap的像素数据拷贝到总Bitmap的内存缓冲区,减少开销:

// 总Bitmap创建后锁定像素缓冲区
var bitmapData = img.LockBits(new Rectangle(0, 0, maxWidth, totalHeight), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
var scan0 = bitmapData.Scan0;
var stride = bitmapData.Stride;

// 每页渲染后直接拷贝像素
foreach (var page in doc.Pages)
{
    int pageWidth = PointsToPixels(page.Width);
    int pageHeight = PointsToPixels(page.Height);
    using (var pdfBitmap = new PDFiumBitmap(pageWidth, pageHeight, true))
    {
        pdfBitmap.Fill(new FPDF_COLOR(255,255,255,255));
        page.Render(pdfBitmap);
        int widthOffset = (maxWidth - pageWidth) / 2;
        // 逐行拷贝像素到总缓冲区对应位置
        for (int y = 0; y < pageHeight; y++)
        {
            var sourcePtr = pdfBitmap.Buffer + y * pdfBitmap.Stride;
            var destPtr = scan0 + (heightOffset + y) * stride + widthOffset * 4;
            Buffer.MemoryCopy(sourcePtr.ToPointer(), destPtr.ToPointer(), pageWidth *4, pageWidth *4);
        }
        heightOffset += pageHeight;
    }
}
// 解锁缓冲区后再保存
img.UnlockBits(bitmapData);
其他代码优化建议
  • 图像保存后记得重置outputStream.Position = 0,否则调用方直接读取流会拿到空数据。
  • 可以给Graphics对象添加高质量渲染配置,提升输出画质:
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
g.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality;

内容的提问来源于stack exchange,提问作者MasterOfTwo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.29 06:36:03