GhostScript光栅化器内存不足异常
本文关键字:内存不足 异常 GhostScript | 更新日期: 2023-09-27 18:19:26
我正在使用一个作为后台处理器运行的32位控制台应用程序。我正在处理的部分使用GhostScript对PDF执行OCR。PDF的每一页都被呈现为临时文件夹中的PNG图像,然后OCR阅读器读取该图像。OCR文本将保存到数据库中,然后删除临时文件夹中的文件。
问题是GhostScriptRasterizer对象占用了处理器的所有可用内存。当我调用GhostScriptRasterizer.GetPage(dpi,dpi,pageNumber)方法时,我会得到OutOfMemory异常或System.ArgumentException with Message";参数无效";。我对第二个例外的研究告诉我,这确实是第一个例外的症状。方法调用占用了所有可飞行的内存。
GetPage方法正在创建一个System.Drawing.Bitmap图像,该图像需要连续的无分段内存。问题代码从这里开始。
try
{
img = rasterizer.GetPage(dpi, dpi, pageNumber);
}
catch (OutOfMemoryException ex)
{
img = GetImage(rasterizer, dpi, pageNumber, ms);
}
catch (System.ArgumentException ex)
{
img = GetImage(rasterizer, dpi, pageNumber, ms);
}
我编写的GetImage方法如下所示。
public Image GetImage(GhostscriptRasterizer rasterizer, int dpi, int pageNumber, MemoryStream ms)
{
rasterizer.Close();
rasterizer.Dispose();
rasterizer = new GhostscriptRasterizer();
rasterizer.Open(ms);
dpi = dpi - 50;
Image image = null;
if (dpi > 0)
{
try
{
image = rasterizer.GetPage(dpi, dpi, pageNumber);
}
catch (OutOfMemoryException ex)
{
image = GetImage(rasterizer, dpi, pageNumber, ms);
}
catch (System.ArgumentException ex)
{
image = GetImage(rasterizer, dpi, pageNumber, ms);
}
}
return image;
}
我开始使用的dpi是300,在我们对该系统的第一次测试中,它对95%的文档都有效。然而,对于某些页面,300 dpi显然太高了,因为我得到了内存外的例外。看起来有些页面大约是35 X 59英寸。我无法控制这件事。对我来说,解决方案是不断尝试越来越低的dpi,直到我有了一个不会吃掉所有内存的东西。然而,所有的内存都保留在光栅化器对象中,所以我需要以某种方式处理它。调用rasterizer.Close()会出现以下错误。
托管调试助手"FatalExecutionEngineError"在"F:''Development''bin''Debug''Processor.Run.vshost.exe"中检测到问题。
附加信息:运行时遇到致命错误。错误的地址位于线程0x3e90上的0x7331e8c6。错误代码为0xc0000005。此错误可能是CLR或用户代码的不安全或不可验证部分中的错误。此错误的常见来源包括COM互操作或PInvoke的用户封送处理错误,这些错误可能会损坏堆栈。
删除Close()调用并调用rasterizer.Dispose()给了我:
Ghostscript.NET.dll 中发生类型为"System.AccessViolationException"的未处理异常
附加信息:试图读取或写入受保护的内存。这通常表示其他内存已损坏。
我甚至只是试图在遇到异常时中断并返回文件列表,但这仍然要求我不要对光栅化器使用using声明,因为我在使用结束时得到了相同的异常,因为它当然是在尝试处理对象。垃圾收集器似乎稍后会收集到内存,但这并不能解决我的问题。我仍然无法在同一个作业中光栅化页面。
我能想到的唯一解决方案是以某种方式提前调整pdf的大小,但我希望有人知道如何处理内存并以更低的dpi重新光栅化。
当PDF请求大介质时,您可以编写更改介质大小的PostScript。但这需要一些PostScript编程知识。
然而,我相信实际的问题不在于Ghostscript,因为当超过内存限制时,Ghostscript将切换到显示列表模型,在该模型中,它将页面分波段输出到磁盘(运行显示列表的次数与要输出的波段的次数一样多)。如果你真的有一个磁盘,而且有足够的内存容纳一条光栅线,那么它将(最终在每行一个波段的情况下)输出整个东西。
这向我表明,实际的问题是你使用的C++或C#包装器,而不是Ghostscript tself。
我怀疑您的包装器试图在内存中创建一个巨大的位图,以在将渲染输出写入磁盘之前保存它。这不是必须的。
试着直接从命令行使用一个失败的文件运行Ghostscript,如果这有效,那么你可以简单地使用Ghostscript,它完全能够生成PNG文件作为输出。我使用Ghostscript以600 dpi输出这种大小或更大的媒体。
我也有类似的问题,在发生异常后处理内存时,我会得到"尝试读取或写入受保护的内存"。当我试图转换受密码保护的PDF时,就会发生这种情况——即使在捕捉到异常之后,也会发生上述访问违规并导致程序崩溃。
我使用的解决方案:
我还在程序中使用iTextSharp。因此,我使用iTextSharp编写了一个方法来检查PDF文件是否首先受到密码保护,使用此线程的帮助:https://stackoverflow.com/questions/11298651/checking-if-pdf-is-password-protected-using-itextsharp#=
所以现在我在遇到这个问题之前先检查一下。这是我找到的解决这个问题的唯一方法——我认为Ghostscript.NET包装器不再更新或维护了。
我在调用GhostScript
方法的方法上使用了HandleProcessCorruptedStateExceptionsAttribute
和SecurityCritical
属性。
这个问题为我解决了。我不再有这个例外。