2

我正在尝试使用 Ghostscript.Net 构建 PostScript 到 PDF 转换器。GetArgs 返回的参数是我通常用来调用 gswin32c.exe 的参数,它们工作正常。

但是每次我调用 Process 时,我都会收到一条错误消息,提示“调用 'gsapi_init_with_args' 时发生错误:-100”。谷歌搜索该错误并没有带来任何问题,所以我想我可以在这里问。

直接使用 Ghostscript.net 调用 .dll 时是否需要考虑不同的参数?还是我在其他地方犯了错误?

这是我的课:

public class PdfConverter
{
    #region Private Fields
    private List<GhostscriptVersionInfo> _Versions = GhostscriptVersionInfo.GetInstalledVersions(GhostscriptLicense.GPL | GhostscriptLicense.AFPL | GhostscriptLicense.Artifex);
    #endregion


    #region Private Properties
    private GhostscriptVersionInfo Version { get; set; }
    #endregion


    #region Construction
    public PdfConverter()
    {
        Version = GhostscriptVersionInfo.GetLastInstalledVersion(); 
    }
    #endregion


    #region Public Members
    public bool ConvertToPdf(DirectoryInfo dir)
    {
        var d = dir;
        if(!d.Exists)
            return false;

        var postScriptFiles = d.GetFiles("*.ps");
        var pdfFiles        = postScriptFiles.Select(psf => new FileInfo(Path.ChangeExtension(psf.FullName, ".pdf")));

        foreach(var file in postScriptFiles) {
            //ThreadPool.QueueUserWorkItem(new WaitCallback((o) => { 
                Process(file, new FileInfo(Path.ChangeExtension(file.FullName, ".pdf")));
            //}));
        }

        pdfFiles.ForEach(pdf => pdf?.Refresh());
        return pdfFiles.All(pdf => pdf.Exists);
    }
    #endregion


    #region Private Helpers
    private void Process(FileInfo inputFile, FileInfo outputFile)
    {
        Console.WriteLine($"Converting {inputFile} to {outputFile}");
        var proc = new GhostscriptProcessor(Version, true);
        proc.Process(GetArgs(inputFile, outputFile).ToArray(), new ConsoleStdIO(true, true, true));
    }


    private IEnumerable<string> GetArgs(FileInfo inputFile, FileInfo outputFile)
    {
        return new [] {
            $"-q ",
            $"-sDEVICE=pdfwrite",
            $"-dSAFER",
            $"-dNOPAUSE",
            $"-dBATCH",
            $"-sPAPERSIZE=a4",
            $"-dEmbedAllFonts=true",
            $"-dAutoRotatePages=/None",
            $"-sOutputFile=\"{outputFile.FullName}\"",
            $"-dCompatibilityLevel#1.4",
            $"-c .setpdfwrite",
            $"-f \"{inputFile.FullName}\""
        };
    }
    #endregion
}

编辑:我忘了提:要实现它,我必须创建自己的 GhostsdcriptStdIO 类。我承认我不完全确定我是否做对了。尽管它确实毫无例外地被实例化,并覆盖了 StdOut(...) get 的调用,并且输出按预期写入控制台。override void StdError(...) get 也被调用。并且还写到控制台上。顺便说一句,错误的输出是:

"**** 无法打开文件 "c:\temp\test.pdf"" "**** 无法打开初始设备,正在退出。"

这是我的 ConsoleStdIO 类:

public class ConsoleStdIO : Ghostscript.NET.GhostscriptStdIO
{
    #region Construction
    public ConsoleStdIO(bool handleStdIn, bool handleStdOut, bool handleStdError) : base(handleStdIn, handleStdOut, handleStdError) { }  
    #endregion


    #region Overrides
    public override void StdError(string error)
    {
        var foo    = Encoding.Default.GetBytes(error);
        var lenght = foo.Length;

        using (var err = Console.OpenStandardError()) {
            if(err.CanWrite)
                err.Write(foo, 0, lenght);
        }
    }

    public override void StdIn(out string input, int count)
    {
        byte[] bytes = new byte[0];
        using(var stdInput = Console.OpenStandardInput()) {
            stdInput.Read(bytes, 0, count);
        }
        input = Encoding.Default.GetString(bytes);
    }

    public override void StdOut(string output)
    {
        var foo    = Encoding.Default.GetBytes(output);
        var lenght = foo.Length;

        using (var err = Console.OpenStandardError()) {
            if(err.CanWrite)
                err.Write(foo, 0, lenght);
        }           
    }
    #endregion
}

同样:使用 gswin32c.exe 对完全相同的文件和参数执行相同的操作可以正常工作。快乐黑客

4

2 回答 2

3

错误 -100 是 gs_error_Fatal,这意味着“发生灾难性错误”。它表明程序未能正确启动,我们无法说出原因。反向通道可能包含更多信息。

确实,后台通道会告诉您出了什么问题:

**** 无法打开文件“c:\temp\test.pdf **** 无法打开初始设备,正在退出。

Ghostscript 无法打开输出文件,这意味着它无法打开 pdfwrite 设备(因为它需要输出文件),因此它中止了操作。

Ghostscript 无法打开输出文件的原因可能有很多。我要做的第一件事是减少参数的数量;

当您尝试调试问题时,您不想要-q(安静),您想要获得的所有信息。

-dSAFER至少要从一开始就删除,因为这会阻止 Ghostscript 访问当前工作目录之外的目录和某些“特殊”目录。它很可能会阻止您访问临时目录。

EmbedAllFonts当它的值与默认值相同时,您不需要设置。

您可以删除CompatibilityLevel(并注意您在此处使用了 # 而不是 =)开关,并AutoRotatePages同时使其工作。

" -c .setpdfwrite -f" 字符串多年来一直毫无意义,但人们仍在继续使用它。这些天所做的就是减慢处理的开始,放弃它。

最后,您可以尝试将反斜杠 ('\') 字符更改为正斜杠 ('/'),​​以防您的字符串处理搞砸了,或者使用双反斜杠(我自己会使用正斜杠)。

您还应该检查 c:\test\temp.pdf 是否不存在,或者它是否存在不是只读的或已经在不同的应用程序中打开。

于 2017-12-14T10:51:14.897 回答
1

所以我解决了这个问题......
在接受 KenS 的建议后,我可以在没有 Ghostscript(不是 Ghostscript.NET)的情况下运行应用程序给我任何错误。但它没有生成实际的 PDF 文件。

因此,KenS 的回答并没有完全解决问题,但由于“少即是多”,并且由于他花时间在 IRC 上与我交谈以验证我的 args 本身是否正确,因此我仍然会给出答案点。

真正解决了我的问题如下:
这里是我原来的 GetArgs(...)

    private IEnumerable<string> GetArgs(FileInfo inputFile, FileInfo outputFile)
    {
        return new [] {
            $"-sDEVICE=pdfwrite",
            $"-dNOPAUSE",
            $"-dBATCH",
            $"-sPAPERSIZE=a4",
            @"-sFONTPATH=" + System.Environment.GetFolderPath(System.Environment.SpecialFolder.Fonts),
            $"-sOutputFile={outputFile.FullName}",
            $"{inputFile.FullName}",
        };
    }

#csharp 中有人向我指出,在 c 中,第一个参数始终是命令的名称。所以他建议把“gs”作为第一个参数(作为一个假人)并尝试......这就是真正解决我的问题的原因。

所以这就是工作 GetArgs(...) 的样子:

    private IEnumerable<string> GetArgs(FileInfo inputFile, FileInfo outputFile)
    {
        return new [] {
            $"gs",
            $"-sDEVICE=pdfwrite",
            $"-dNOPAUSE",
            $"-dBATCH",
            $"-sPAPERSIZE=a4",
            @"-sFONTPATH=" + System.Environment.GetFolderPath(System.Environment.SpecialFolder.Fonts),
            $"-sOutputFile={outputFile.FullName}",
            $"{inputFile.FullName}",
        };
    }
于 2017-12-14T13:28:26.050 回答