Ghostscript 打印机在特定页数处停止打印
Ghostscript printer stops printing at a specific number of pages
我配置了一个 PDF 打印机,它使用 Ghostscript 将文档转换为 PDF,然后由我的 Java 桌面应用程序处理和使用。它通过 RedMon 端口重定向打印机数据。对于我打印的大多数文档,它工作正常并按预期生成 PDF 文件。然而,对于具有一定页数的文档,该过程只是冻结:没有抛出错误,该过程只是保持。它似乎与文件大小或打印机属性无关(尽管后者似乎会影响打印的页数)。
停止Java应用程序后,我留下了一个固定页数的文档(通常是265页,但也有以263页或247页结尾的)。倒数第二页不完整(如部分打印的表格和文本),而最后一页打印为错误:
ERROR: syntaxerror
OFFENDING COMMAND: --nostringval--
STACK:
/[NUMBER]
其中 [NUMBER] 是任何给定的一位数。
这是我的 Ghostscript 集成器 class:
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"
我有第二台运行完美的虚拟打印机,它们之间似乎没有显着差异:相同的驱动程序、相同的端口参数、相同的设置、非常相似的代码。然而,它并没有在一定数量的页面后冻结,并且输出文件符合预期。
是什么导致我的打印机停止响应?
事实证明你的打印机没有问题,而是你的代码有问题。更具体地说,你如何[不]处理运行时流。您的流程缺少的是 StreamGobbler.
A StreamGobbler is an InputStream that uses an internal worker thread to constantly consume input from another InputStream. It uses a buffer to store the consumed data. The buffer size is automatically adjusted, if needed.
您的进程挂起,因为它无法完全读取输入流。以下文章对发生这种情况的原因以及解决方法提供了非常深入的解释:
When Runtime.exec() won't - Part 1
When Runtime.exec() won't - Part 2
但引用文章本身(反过来引用 JDK Javadoc):
Because some native platforms only provide limited buffer size for standard input and output streams, failure to promptly write the input stream or read the output stream of the subprocess may cause the subprocess to block, and even deadlock.
解决方案是通过实现 StreamGobbler class:
来简单地耗尽进程中的每个输入流
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);
}
}
}
我配置了一个 PDF 打印机,它使用 Ghostscript 将文档转换为 PDF,然后由我的 Java 桌面应用程序处理和使用。它通过 RedMon 端口重定向打印机数据。对于我打印的大多数文档,它工作正常并按预期生成 PDF 文件。然而,对于具有一定页数的文档,该过程只是冻结:没有抛出错误,该过程只是保持。它似乎与文件大小或打印机属性无关(尽管后者似乎会影响打印的页数)。
停止Java应用程序后,我留下了一个固定页数的文档(通常是265页,但也有以263页或247页结尾的)。倒数第二页不完整(如部分打印的表格和文本),而最后一页打印为错误:
ERROR: syntaxerror
OFFENDING COMMAND: --nostringval--
STACK:
/[NUMBER]
其中 [NUMBER] 是任何给定的一位数。
这是我的 Ghostscript 集成器 class:
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"
我有第二台运行完美的虚拟打印机,它们之间似乎没有显着差异:相同的驱动程序、相同的端口参数、相同的设置、非常相似的代码。然而,它并没有在一定数量的页面后冻结,并且输出文件符合预期。
是什么导致我的打印机停止响应?
事实证明你的打印机没有问题,而是你的代码有问题。更具体地说,你如何[不]处理运行时流。您的流程缺少的是 StreamGobbler.
A StreamGobbler is an InputStream that uses an internal worker thread to constantly consume input from another InputStream. It uses a buffer to store the consumed data. The buffer size is automatically adjusted, if needed.
您的进程挂起,因为它无法完全读取输入流。以下文章对发生这种情况的原因以及解决方法提供了非常深入的解释:
When Runtime.exec() won't - Part 1
When Runtime.exec() won't - Part 2
但引用文章本身(反过来引用 JDK Javadoc):
Because some native platforms only provide limited buffer size for standard input and output streams, failure to promptly write the input stream or read the output stream of the subprocess may cause the subprocess to block, and even deadlock.
解决方案是通过实现 StreamGobbler class:
来简单地耗尽进程中的每个输入流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);
}
}
}