2

我正在使用作为后台处理器运行的 32 位控制台应用程序。我正在处理的部分使用 GhostScript 在 PDF 上执行 OCR。PDF 的每一页都呈现为临时文件夹中的 PNG 图像,然后 OCR 阅读器读取该图像。OCR 文本保存到数据库中,然后删除临时文件夹中的文件。

问题在于 GhostScriptRasterizer 对象占用了处理器可用的所有内存。当我调用 GhostScriptRasterizer.GetPage(dpi, dpi, pageNumber) 方法时,我得到一个 OutOfMemory 异常或 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.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 声明,因为我在 using 结束时遇到了相同的异常,因为它当然正在尝试处置对象。看起来垃圾收集器后来收集了该内存,但这并没有以任何方式解决我的问题。我仍然无法在同一个作业中对页面进行光栅化。

我能想到的唯一解决方案是以某种方式提前调整 pdf 的大小,但我希望有人知道一种处理该内存并以新的较低 dpi 重新光栅化的方法。

4

3 回答 3

1

您可以编写 PostScript 以在 PDF 请求大媒体时更改媒体大小。但这需要一些 PostScript 编程知识。

然而,我相信实际的问题不是 Ghostscript,因为当超出内存限制时,Ghostscript 将切换到显示列表模型,在该模型中,它将页面以波段输出到磁盘(运行显示列表的次数与要输出的波段一样多)。如果您确实有一个磁盘,并且您清楚地这样做了,并且有足够的内存用于一条光栅线,那么它将(最终在每条线一个波段的情况下)输出整个内容。

这向我表明实际问题出在您使用的 C++ 或 C# 包装器上,而不是 Ghostscript 本身。

我怀疑您的包装器正在尝试在内存中创建一个巨大的位图来保存渲染输出,然后再将其写入磁盘。这不是必需的。

尝试使用您的失败文件之一直接从命令行运行 Ghostscript,如果可行,那么您可以简单地使用 Ghostscript,它完全能够生成 PNG 文件作为输出。对于它的价值,我使用 Ghostscript 以 600 dpi 输出该尺寸和更大的媒体。

于 2015-10-28T08:44:08.963 回答
0

我 在调用方法的方法之上使用了HandleProcessCorruptedStateExceptionsAttribute和属性。SecurityCriticalGhostScript

这为我解决了问题。我不再得到这个例外。

于 2018-04-06T08:16:56.263 回答
0

我有一个类似的问题,在发生异常后处理内存时,我得到“尝试读取或写入受保护的内存”。当我尝试转换受密码保护的 PDF 时会发生这种情况 - 即使在捕获异常之后,也会发生上述访问冲突并使程序崩溃。

我使用的解决方案:

我也在我的程序中使用 iTextSharp。因此,我使用 iTextSharp 编写了一个方法来检查 PDF 文件是否首先受密码保护,使用此线程的帮助:https ://stackoverflow.com/questions/11298651/checking-if-pdf-is-password-protected-using- itextsharp# =

所以现在我在遇到问题之前检查问题。这是我发现解决此问题的唯一方法 - 我认为 Ghostscript.NET 包装器不再更新或维护。

于 2016-04-08T19:18:07.820 回答