0

我配置了一个 PDF 打印机,它使用 Ghostscript 将文档转换为 PDF,然后由我的 Java 桌面应用程序处理和使用。它通过 RedMon 端口重定向打印机数据。对于我打印的大多数文档,它都可以正常工作并按预期生成 PDF 文件。但是,对于具有一定页数的文档,该过程只是冻结:没有抛出错误,该过程只是保持。它似乎与文件大小或打印机属性无关(尽管后者似乎会影响打印的页数)。

停止 Java 应用程序后,我留下了一个具有固定页数的文档(通常为 265 页,但也恰好以 263 页或 247 页结束)。倒数第二页不完整(如部分打印的表格和文本),而最后一页打印为错误:

ERROR: syntaxerror
OFFENDING COMMAND: --nostringval--

STACK:

/[NUMBER]

其中 [NUMBER] 是任何给定的一位数。

这是我的 Ghostscript 集成器类:

public class GhostScriptIntegrator {
    public static void createPDF(String[] args, String filename) {
        if (args.length > 0) {
            try {
                Process process = Runtime.getRuntime().exec(
                        args[0] + " -sOutputFile=\"" + filename
                        + "\" -c save pop -f -");

                OutputStream os = process.getOutputStream();
                BufferedReader sc = null;
                try (PrintWriter writer = new PrintWriter(os)) {
                    sc = new BufferedReader(new InputStreamReader(System.in));
                    String line;
                    while ((line = sc.readLine()) != null) {
                        writer.println(line);
                    }
                    writer.flush();
                } catch (Exception ex) {
                    Logger.getLogger(GhostScriptIntegrator.class.getName()).log(Level.SEVERE, null, ex);
                } finally {
                    if (sc != null) {
                        sc.close();
                    }
                }

                process.waitFor();
            } catch (InterruptedException | IOException ex) {
                Logger.getLogger(GhostScriptIntegrator.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }
}

args参数由我的虚拟打印机处理(类似于我在上一篇文章中的呈现方式):

完整的论点:

-jar "C:\Program Files (x86)\Impressora SPE\ImpressoraSPE.jar" "C:\Program Files (x86)\gs\gs9.21\bin\gswin32c -I\"C:\Program Files (x86)\gs\gs9.21\lib\" -dSAFER -dBATCH -dNOPAUSE -sDEVICE=pdfwrite -sPAPERSIZE=a4 -q -dPDFA=2 -dPDFACompatibilityPolicy=1 -dSimulateOverprint=true -dCompatibilityLevel=1.3 -dPDFSETTINGS=/screen -dEmbedAllFonts=true -dSubsetFonts=true -dAutoRotatePages=/None -dColorImageDownsampleType=/Bicubic -dColorImageResolution=150"

我有第二台可以完美运行的虚拟打印机,它们之间似乎没有显着差异:相同的驱动程序、相同的端口参数、相同的设置、非常相似的代码。但是,它不会在一定数量的页面后冻结,并且输出文件符合预期。

是什么导致我的打印机停止响应?

4

1 回答 1

0

事实证明,您的打印机没有问题,而是您的代码有问题。更具体地说,您如何 [不] 处理运行时流。您的流程缺少的是StreamGobbler

StreamGobbler 是一个 InputStream,它使用内部工作线程不断地使用来自另一个 InputStream 的输入。它使用缓冲区来存储消耗的数据。如果需要,缓冲区大小会自动调整。

您的进程挂起,因为它无法完全读取输入流。以下文章对它发生的原因以及如何解决它提供了非常深入的解释:

当 Runtime.exec() 不会 - 第 1 部分
当 Runtime.exec() 不会 - 第 2 部分

但是引用文章本身(反过来,引用 JDK Javadoc):

由于部分原生平台只为标准输入输出流提供有限的缓冲区大小,未能及时写入子进程的输入流或读取输出流可能会导致子进程阻塞,甚至死锁。

解决方案是通过实现 StreamGobbler 类来简单地耗尽进程中的每个输入流:

public class GhostScriptIntegrator {
    public static void createPDF(String[] args, String filename) throws FileNotFoundException {
        if (args.length > 0) {
            try {
                Process process = Runtime.getRuntime().exec(
                        args[0] + " -sOutputFile=\"" + filename
                        + "\" -c save pop -f -");

                OutputStream os = process.getOutputStream();
                BufferedReader sc = null;
                InputStreamReader ir = new InputStreamReader(System.in);
                try (PrintWriter writer = new PrintWriter(os)) {

                    StreamGobbler errorGobbler = new StreamGobbler(
                            process.getErrorStream(), "ERROR");
                    StreamGobbler outputGobbler = new StreamGobbler(
                            process.getInputStream(), "OUTPUT");

                    errorGobbler.start();
                    outputGobbler.start();

                    sc = new BufferedReader(ir);
                    String line;
                    while ((line = sc.readLine()) != null) {
                        writer.println(line);
                        writer.flush();
                    }
                } catch (IOException ex) {
                    Logger.getLogger(GhostScriptIntegrator.class.getName()).log(Level.SEVERE, null, ex);
                } finally {
                    if (sc != null) {
                        sc.close();
                    }
                    ir.close();
                    if (os != null) {
                        os.close();
                    }
                }

                process.waitFor();
            } catch (InterruptedException | IOException ex) {
                Logger.getLogger(GhostScriptIntegrator.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }
}

class StreamGobbler extends Thread {

    InputStream is;
    String type;

    StreamGobbler(InputStream is, String type) {
        this.is = is;
        this.type = type;
    }

    public void run() {
        try {
            InputStreamReader isr = new InputStreamReader(is);
            BufferedReader br = new BufferedReader(isr);
            long contador = 0;
            while (br.readLine() != null) {
                //Do nothing
            }

        } catch (IOException ex) {
            Logger.getLogger(StreamGobbler.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
}
于 2019-02-21T13:00:14.147 回答