我正在使用作为后台处理器运行的32位控制台应用程序。我正在处理的部分使用GhostScript在PDF上执行OCR。 PDF的每个页面都呈现为临时文件夹中的PNG图像,然后OCR读取器读取该文件夹。 OCR文本将保存到数据库中,然后删除临时文件夹中的文件。
问题在于GhostScriptRasterizer对象占用了处理器可用的所有内存。当我调用GhostScriptRasterizer.GetPage(dpi,dpi,pageNumber)方法时,我得到一个OutOfMemory Exception或一个System.ArgumentException,消息“参数无效”。我对第二个例外的研究告诉我它实际上是第一个的症状。方法调用会占用所有可用内存。
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显然太高,因为我得到了Outofmemory异常。看起来有些页面大约是35 X 59英寸。我无法控制这一点。对我来说,解决方案是继续尝试更低和更低的dpi,直到我有一些不吃掉所有内存的东西。但是,所有内存都保留在rasterizer对象中,所以我需要以某种方式处理它。调用rasterizer.Close()会给我以下错误。
托管调试助手'FatalExecutionEngineError'在'F:\ Development \ bin \ Debug \ Processor.Run.vshost.exe'中检测到问题。
其他信息:运行时遇到致命错误。错误的地址在0x7331e8c6,在线程0x3e90上。错误代码是0xc0000005。此错误可能是CLR中的错误,也可能是用户代码的不安全或不可验证部分中的错误。此错误的常见来源包括COM-interop或PInvoke的用户封送错误,这可能会破坏堆栈。
删除Close()调用并调用rasterizer.Dispose()会给我:
Ghostscript.NET.dll中出现未处理的“System.AccessViolationException”类型异常
附加信息:尝试读取或写入受保护的内存。这通常表明其他内存已损坏。
我甚至只是尝试破解,如果我遇到异常并返回文件列表,这仍然要求我不使用rasterizer的using声明,因为我在使用结束时得到了相同的异常,因为它当然是正在尝试处理对象。看起来垃圾收集器后来接收了该内存,但这并不能解决我的问题。我仍无法在同一作业中栅格化页面。
我能想到的唯一解决方案是以某种方式提前调整pdf的大小,但我希望有人知道处理内存并以新的较低dpi重新光栅化的方法。
答案 0 :(得分:1)
当PDF请求大型媒体时,您可以编写改变媒体大小的PostScript。但这需要一些PostScript编程知识。
我认为实际的问题不是Ghostscript,因为当超出内存限制时,Ghostscript将切换到显示列表模型,它将页面输出到磁带中(运行显示列表的次数与输出的带数一样多) )。如果你实际上有一个你明确做的磁盘,并且有足够的内存用于一个栅格线,那么它(最终在每行一个频带的情况下)输出整个东西。
对我来说,实际问题是您正在使用的C ++或C#包装器,而不是Ghostscript本身。
我怀疑你的包装器试图在内存中创建一个巨大的位图来保存渲染的输出,然后再将其写入磁盘。这不是必需的。
尝试使用您的一个失败文件直接从命令行运行Ghostscript,如果可行,那么您可以简单地使用Ghostscript,它完全能够生成PNG文件作为输出。为了它的价值,我使用Ghostscript以600 dpi输出那个尺寸和更大尺寸的媒体。
答案 1 :(得分:0)
我有类似的问题,在发生异常后处理内存时,我得到“尝试读取或写入受保护的内存”。当我尝试转换受密码保护的PDF时会发生这种情况 - 即使在捕获异常后,也会发生上述访问冲突并导致程序崩溃。
我使用的解决方案:
我也在我的程序中使用iTextSharp。所以我使用iTextSharp编写了一个方法来检查PDF文件是否首先受密码保护,使用此线程的帮助:https://stackoverflow.com/questions/11298651/checking-if-pdf-is-password-protected-using-itextsharp#=
所以现在我在遇到问题之前检查问题。这是我发现这个问题的唯一方法 - 我不认为Ghostscript.NET包装器正在被更新或维护。
答案 2 :(得分:0)
我在调用HandleProcessCorruptedStateExceptionsAttribute
方法的方法上使用了SecurityCritical
和GhostScript
属性。
这个问题已经解决了。我不再得到这个例外。