diff --git a/pom.xml b/pom.xml index 3028c75..3472bdc 100644 --- a/pom.xml +++ b/pom.xml @@ -1,327 +1,318 @@ - - - 4.0.0 - - org.ghost4j - ghost4j - jar - 1.0.5 - - Ghost4J - Java wrapper for Ghostscript API. Official ${project.name} build of the - ${project.version} release - - http://www.ghost4j.org - - - - ossrh - https://oss.sonatype.org/content/repositories/snapshots - - - ossrh - https://oss.sonatype.org/service/local/staging/deploy/maven2/ - - - - - - GNU LESSER GENERAL PUBLIC LICENSE - http://www.gnu.org/licenses/lgpl-3.0-standalone.html - - - - - scm:git:https://github.com/zippy1978/ghost4j.git - scm:git:https://github.com/zippy1978/ghost4j.git - http://www.github.com/zippy1978/ghost4j - HEAD - - - - GitHub - https://github.com/zippy1978/ghost4j/issues - - - - UTF-8 - - - - - Gilles Grousset - gi.grousset@gmail.com - http://zippy1978.tumblr.com - - - - - - Dave Smith - dave.smith@candata.com - CANdata Systems - http://www.candata.com - - - Michael Sliwak - msliwak@googlemail.com - - - squallssck - http://techfee.com - squallssck@gmail.com - - - BusyBusinessCat - https://github.com/BusyBusinessCat - - - O.J. Sousa Rodrigues - osoriojaques@gmail.com - - developer - contributor - - +1 - - - - - - junit - junit - 4.11 - test - - - net.java.dev.jna - jna - 4.1.0 - - - org.slf4j - slf4j-api - 1.7.32 - - - commons-beanutils - commons-beanutils - 1.9.4 - - - org.apache.xmlgraphics - xmlgraphics-commons - 2.3 - - - com.lowagie - itext - 2.1.7 - - - bcmail-jdk14 - bouncycastle - - - bcmail-jdk14 - org.bouncycastle - - - bcprov-jdk14 - bouncycastle - - - bcprov-jdk14 - org.bouncycastle - - - bctsp-jdk14 - org.bouncycastle - - - - - - - - - org.apache.maven.plugins - maven-project-info-reports-plugin - 2.4 - - - - dependencies - project-team - license - scm - - - - - - org.apache.maven.plugins - maven-javadoc-plugin - 2.10.3 - - maven - -Xdoclint:none - - - - - - - - - - org.apache.maven.wagon - wagon-ssh - 2.2 - - - - - - - org.apache.maven.plugins - maven-compiler-plugin - 3.0 - - 1.7 - 1.7 - - - - - org.apache.maven.plugins - maven-source-plugin - 2.1.2 - - - verify - - jar-no-fork - - - - - - - org.apache.maven.plugins - maven-javadoc-plugin - 2.8.1 - - maven - -Xdoclint:none - - - - verify - - jar - - - - - - - maven-assembly-plugin - - - src/main/assembly/dist.xml - - - - - - com.github.github - site-maven-plugin - 0.12 - - Official ${project.name} build - github.com - ${project.artifactId} - zippy1978 - - - - - site - - site - - - - - - org.apache.maven.plugins - maven-site-plugin - 3.0 - - - com.anasoft.os - m2st-doxia-macros - 2.0 - - - org.apache.maven.doxia - doxia-module-markdown - 1.7 - - - - UTF-8 - UTF-8 - - - org.apache.maven.plugins - maven-project-info-reports-plugin - 2.5 - - - - dependencies - project-team - issue-tracking - license - scm - - - - - - org.apache.maven.plugins - maven-javadoc-plugin - 2.7 - - - org.apache.maven.plugins - maven-surefire-report-plugin - 2.6 - - - - - - - org.apache.maven.plugins - maven-gpg-plugin - 1.6 - - - sign-artifacts - verify - - sign - - - - - - - - - - + + + 4.0.0 + + org.ghost4j + org.ghost4j + jar + 1.0.5 + + Ghost4J + Java wrapper for Ghostscript API. Official ${project.name} build of the ${project.version} release + http://www.ghost4j.org + + + + ossrh + https://oss.sonatype.org/content/repositories/snapshots + + + ossrh + https://oss.sonatype.org/service/local/staging/deploy/maven2/ + + + + + + GNU LESSER GENERAL PUBLIC LICENSE + http://www.gnu.org/licenses/lgpl-3.0-standalone.html + + + + + scm:git:https://github.com/zippy1978/ghost4j.git + scm:git:https://github.com/zippy1978/ghost4j.git + http://www.github.com/zippy1978/ghost4j + HEAD + + + + GitHub + https://github.com/zippy1978/ghost4j/issues + + + + UTF-8 + + + + + Gilles Grousset + gi.grousset@gmail.com + http://zippy1978.tumblr.com + + + + + + Dave Smith + dave.smith@candata.com + CANdata Systems + http://www.candata.com + + + Michael Sliwak + msliwak@googlemail.com + + + squallssck + http://techfee.com + squallssck@gmail.com + + + BusyBusinessCat + https://github.com/BusyBusinessCat + + + O.J. Sousa Rodrigues + osoriojaques@gmail.com + + developer + contributor + + +1 + + + + + + junit + junit + 4.11 + test + + + org.slf4j + slf4j-jdk14 + 2.0.6 + test + + + net.java.dev.jna + jna + 5.13.0 + + + org.slf4j + slf4j-api + 2.0.6 + + + commons-beanutils + commons-beanutils + 1.9.4 + + + org.apache.xmlgraphics + xmlgraphics-commons + 2.8 + + + com.lowagie + itext + 2.1.7 + + + bcmail-jdk14 + bouncycastle + + + bcmail-jdk14 + org.bouncycastle + + + bcprov-jdk14 + bouncycastle + + + bcprov-jdk14 + org.bouncycastle + + + bctsp-jdk14 + org.bouncycastle + + + + + + + + + org.apache.maven.plugins + maven-project-info-reports-plugin + 2.4 + + + + dependencies + project-team + license + scm + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + 2.10.3 + + maven + -Xdoclint:none + + + + + + + + + org.apache.maven.wagon + wagon-ssh + 2.2 + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.0 + + 17 + 17 + + + + org.apache.maven.plugins + maven-source-plugin + 2.1.2 + + + verify + + jar-no-fork + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + 2.8.1 + + maven + -Xdoclint:none + + + + verify + + jar + + + + + + maven-assembly-plugin + + + src/main/assembly/dist.xml + + + + + com.github.github + site-maven-plugin + 0.12 + + Official ${project.name} build + github.com + ${project.artifactId} + zippy1978 + + + + site + + site + + + + + + org.apache.maven.plugins + maven-site-plugin + 3.0 + + + com.anasoft.os + m2st-doxia-macros + 2.0 + + + org.apache.maven.doxia + doxia-module-markdown + 1.7 + + + + UTF-8 + UTF-8 + + + org.apache.maven.plugins + maven-project-info-reports-plugin + 2.5 + + + + dependencies + project-team + issue-tracking + license + scm + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + 2.7 + + + org.apache.maven.plugins + maven-surefire-report-plugin + 2.6 + + + + + + org.apache.maven.plugins + maven-gpg-plugin + 1.6 + + + sign-artifacts + verify + + sign + + + + + + + \ No newline at end of file diff --git a/src/main/java/org/ghost4j/AbstractComponent.java b/src/main/java/org/ghost4j/AbstractComponent.java index ae5dd44..a304949 100644 --- a/src/main/java/org/ghost4j/AbstractComponent.java +++ b/src/main/java/org/ghost4j/AbstractComponent.java @@ -25,139 +25,138 @@ */ public abstract class AbstractComponent implements Component { - /** - * Holds available device names of the Ghostscript interperter. - */ - private static final List AVAILABLE_DEVICE_NAMES = new ArrayList(); - - /** - * Classes of Document supported by the converter. - */ - protected Class[] supportedDocumentClasses; - - /** - * Assert a given document instance is supported by the converter - * - * @param document - * @throws DocumentException - * When document is not supported - */ - protected void assertDocumentSupported(Document document) - throws DocumentException { - - if (supportedDocumentClasses != null) { - - for (Class clazz : supportedDocumentClasses) { - if (clazz.getName().equals(document.getClass().getName())) { - // supported - return; + /** + * Holds available device names of the Ghostscript interperter. + */ + private static final List AVAILABLE_DEVICE_NAMES = new ArrayList(); + + /** + * Classes of Document supported by the converter. + */ + protected Class[] supportedDocumentClasses; + + /** + * Assert a given document instance is supported by the converter + * + * @param document + * @throws DocumentException + * When document is not supported + */ + protected void assertDocumentSupported(Document document) + throws DocumentException { + + if (supportedDocumentClasses != null) { + + for (Class clazz : supportedDocumentClasses) { + if (clazz.getName().equals(document.getClass().getName())) { + // supported + return; + } + } + + // document not supported + throw new DocumentException("Documents of class " + + document.getClass().getName() + + " are not supported by the component"); } - } - - // document not supported - throw new DocumentException("Documents of class " - + document.getClass().getName() - + " are not supported by the component"); - } - } - - public void copySettings(Map settings) - throws IllegalAccessException, InvocationTargetException { - - if (settings.get("maxProcessCount") != null) { - settings.remove("maxProcessCount"); } - BeanUtils.populate(this, settings); + public void copySettings(Map settings) + throws IllegalAccessException, InvocationTargetException { - } - - @SuppressWarnings("unchecked") - public Map extractSettings() throws IllegalAccessException, - InvocationTargetException, NoSuchMethodException { + if (settings.get("maxProcessCount") != null) { + settings.remove("maxProcessCount"); + } - Map result = PropertyUtils.describe(this); + BeanUtils.populate(this, settings); - if (result.get("maxProcessCount") != null) { - result.remove("maxProcessCount"); } - return result; - } - - /** - * Checks if a given device is supported by the current Ghostscript version. - * - * @param deviceName - * Device name - * @return true/false - * @throws GhostscriptException - */ - protected synchronized boolean isDeviceSupported(String deviceName) - throws GhostscriptException { - - // if no device names know yet : query the interpreter - if (AVAILABLE_DEVICE_NAMES.size() == 0) { + public Map extractSettings() throws IllegalAccessException, + InvocationTargetException, NoSuchMethodException { - // get Ghostscript instance - Ghostscript gs = Ghostscript.getInstance(); + Map result = PropertyUtils.describe(this); - // retrieve available devices - try { - - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - - String[] gsArgs = { "-dQUIET", "-dNOPAUSE", "-dBATCH", - "-dNODISPLAY" }; - - synchronized (gs) { - gs.setStdOut(baos); - gs.initialize(gsArgs); - gs.runString("devicenames =="); - gs.exit(); + if (result.get("maxProcessCount") != null) { + result.remove("maxProcessCount"); } - // result string - String result = new String(baos.toByteArray()); - String[] lines = result.split("\n"); - int i = 0; - while (!lines[i].startsWith("[")) { - i++; - } - String[] deviceNames = lines[i].substring(1, - lines[i].length() - 2).split("/"); - for (String string : deviceNames) { - AVAILABLE_DEVICE_NAMES.add(string.trim()); + return result; + } + + /** + * Checks if a given device is supported by the current Ghostscript version. + * + * @param deviceName + * Device name + * @return true/false + * @throws GhostscriptException + */ + protected synchronized boolean isDeviceSupported(String deviceName) + throws GhostscriptException { + + // if no device names know yet : query the interpreter + if (AVAILABLE_DEVICE_NAMES.size() == 0) { + + // get Ghostscript instance + Ghostscript gs = Ghostscript.getInstance(); + + // retrieve available devices + try { + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + + String[] gsArgs = { "-dQUIET", "-dNOPAUSE", "-dBATCH", + "-dNODISPLAY" }; + + synchronized (gs) { + gs.setStdOut(baos); + gs.initialize(gsArgs); + gs.runString("devicenames =="); + gs.exit(); + } + + // result string + String result = new String(baos.toByteArray()); + String[] lines = result.split("\n"); + int i = 0; + while (!lines[i].startsWith("[")) { + i++; + } + String[] deviceNames = lines[i].substring(1, + lines[i].length() - 2).split("/"); + for (String string : deviceNames) { + AVAILABLE_DEVICE_NAMES.add(string.trim()); + } + + } catch (GhostscriptException e) { + throw e; + } finally { + Ghostscript.deleteInstance(); + } } - } catch (GhostscriptException e) { - throw e; - } finally { - Ghostscript.deleteInstance(); - } + return AVAILABLE_DEVICE_NAMES.contains(deviceName); } - return AVAILABLE_DEVICE_NAMES.contains(deviceName); - } - - /** - * Asserts a given device is supported by the current Ghostscript version. - * - * @param deviceName - * Device name - * @throws GhostscriptException - * Thrown is device is not supported, or call to the interpreter - * fails - */ - protected void assertDeviceSupported(String deviceName) - throws GhostscriptException { - - if (!this.isDeviceSupported(deviceName)) { - throw new GhostscriptException( - "device " - + deviceName - + " is not supported by the current Ghostscript interpreter."); + /** + * Asserts a given device is supported by the current Ghostscript version. + * + * @param deviceName + * Device name + * @throws GhostscriptException + * Thrown is device is not supported, or call to the interpreter + * fails + */ + protected void assertDeviceSupported(String deviceName) + throws GhostscriptException { + + if (!this.isDeviceSupported(deviceName)) { + throw new GhostscriptException( + "device " + + deviceName + + " is not supported by the current Ghostscript interpreter."); + } } - } } diff --git a/src/main/java/org/ghost4j/AbstractRemoteComponent.java b/src/main/java/org/ghost4j/AbstractRemoteComponent.java index df4c56e..2198027 100644 --- a/src/main/java/org/ghost4j/AbstractRemoteComponent.java +++ b/src/main/java/org/ghost4j/AbstractRemoteComponent.java @@ -25,125 +25,125 @@ */ public abstract class AbstractRemoteComponent extends AbstractComponent { - /** - * Logger used to log messages. - */ - private Logger logger = LoggerFactory.getLogger(AbstractRemoteComponent.class.getName()); - - /** - * Maximum number of parallel processes allowed for the converter. - */ - protected int maxProcessCount = 0; - /** - * Number of parallel processes running. - */ - protected int processCount = 0; - - /** - * Wait for a process to get free. - */ - public void waitForFreeProcess() { - - while (processCount >= maxProcessCount) { - try { - Thread.sleep(1000); - } catch (Exception e) { - // nothing - } + /** + * Logger used to log messages. + */ + private Logger logger = LoggerFactory.getLogger(AbstractRemoteComponent.class.getName()); + + /** + * Maximum number of parallel processes allowed for the converter. + */ + protected int maxProcessCount = 0; + /** + * Number of parallel processes running. + */ + protected int processCount = 0; + + /** + * Wait for a process to get free. + */ + public void waitForFreeProcess() { + + while (processCount >= maxProcessCount) { + try { + Thread.sleep(1000); + } catch (Exception e) { + // nothing + } + } } - } - - /** - * Checks if the current class has a proper 'main' method declared. - * - * @return true id 'main' method was found - */ - public boolean isStandAloneModeSupported() { - - try { - this.getClass().getMethod("main", String[].class); - return true; - } catch (Exception ex) { - return false; + + /** + * Checks if the current class has a proper 'main' method declared. + * + * @return true id 'main' method was found + */ + public boolean isStandAloneModeSupported() { + + try { + this.getClass().getMethod("main", String[].class); + return true; + } catch (Exception ex) { + return false; + } + } + + /** + * Start a remote component server on a Javafork object. + * + * @param fork + * JavaFork used to run the server + * @return Port number used by the server + * @throws IOException + */ + protected synchronized int startRemoteServer(JavaFork fork) + throws IOException { + + // get free TCP port to run Cajo server on + int cajoPort = NetworkUtil.findAvailablePort("127.0.0.1", 5000, 6000); + if (cajoPort == 0) { + throw new IOException("No port available to start remote component"); + } + logger.debug(Thread.currentThread() + " uses " + cajoPort + + " as server port"); + + // add extra environment variables to JVM + Map environment = new HashMap(); + // Cajo port + environment.put("cajo.port", String.valueOf(cajoPort)); + fork.setEnvironment(environment); + + // start new JVM with current converter + fork.start(); + + // wait for the remote JVM to start + NetworkUtil.waitUntilPortListening("127.0.0.1", cajoPort, 10000); + + return cajoPort; + } + + /** + * Get a client proxy of a remote component + * + * @param serverPort + * Server port + * @param clazz + * Interface of the proxy + * @return The proxy object + * @throws Exception + */ + protected synchronized Object getRemoteComponent(int serverPort, + Class clazz) throws Exception { + + return Remote.getItem("//127.0.0.1:" + serverPort + "/" + + clazz.getCanonicalName()); + } - } - - /** - * Start a remote component server on a Javafork object. - * - * @param fork - * JavaFork used to run the server - * @return Port number used by the server - * @throws IOException - */ - protected synchronized int startRemoteServer(JavaFork fork) - throws IOException { - - // get free TCP port to run Cajo server on - int cajoPort = NetworkUtil.findAvailablePort("127.0.0.1", 5000, 6000); - if (cajoPort == 0) { - throw new IOException("No port available to start remote component"); + + /** + * Create and return a new JavaFork for remote processing. + * + * @return A JavaFork + */ + protected JavaFork buildJavaFork() { + + JavaFork fork = new JavaFork(); + fork.setRedirectStreams(true); + fork.setWaitBeforeExiting(false); + fork.setStartClass(this.getClass()); + + return fork; + } + + public int getMaxProcessCount() { + return maxProcessCount; + } + + public void setMaxProcessCount(int maxProcessCount) { + this.maxProcessCount = maxProcessCount; + } + + public int getProcessCount() { + return processCount; } - logger.debug(Thread.currentThread() + " uses " + cajoPort - + " as server port"); - - // add extra environment variables to JVM - Map environment = new HashMap(); - // Cajo port - environment.put("cajo.port", String.valueOf(cajoPort)); - fork.setEnvironment(environment); - - // start new JVM with current converter - fork.start(); - - // wait for the remote JVM to start - NetworkUtil.waitUntilPortListening("127.0.0.1", cajoPort, 10000); - - return cajoPort; - } - - /** - * Get a client proxy of a remote component - * - * @param serverPort - * Server port - * @param clazz - * Interface of the proxy - * @return The proxy object - * @throws Exception - */ - protected synchronized Object getRemoteComponent(int serverPort, - Class clazz) throws Exception { - - return Remote.getItem("//127.0.0.1:" + serverPort + "/" - + clazz.getCanonicalName()); - - } - - /** - * Create and return a new JavaFork for remote processing. - * - * @return A JavaFork - */ - protected JavaFork buildJavaFork() { - - JavaFork fork = new JavaFork(); - fork.setRedirectStreams(true); - fork.setWaitBeforeExiting(false); - fork.setStartClass(this.getClass()); - - return fork; - } - - public int getMaxProcessCount() { - return maxProcessCount; - } - - public void setMaxProcessCount(int maxProcessCount) { - this.maxProcessCount = maxProcessCount; - } - - public int getProcessCount() { - return processCount; - } } diff --git a/src/main/java/org/ghost4j/Component.java b/src/main/java/org/ghost4j/Component.java index c541cc3..23a30d7 100644 --- a/src/main/java/org/ghost4j/Component.java +++ b/src/main/java/org/ghost4j/Component.java @@ -17,26 +17,26 @@ */ public interface Component { - /** - * Copy settings (object properties except for property 'maxProcessCount') - * to the current component - * - * @param settings - * @throws InvocationTargetException - * @throws IllegalAccessException - */ - public void copySettings(Map settings) - throws IllegalAccessException, InvocationTargetException; + /** + * Copy settings (object properties except for property 'maxProcessCount') + * to the current component + * + * @param settings + * @throws InvocationTargetException + * @throws IllegalAccessException + */ + public void copySettings(Map settings) + throws IllegalAccessException, InvocationTargetException; - /** - * Extract settings (object properties except for property - * 'maxProcessCount') of the current component - * - * @return a Map of settings - * @throws NoSuchMethodException - * @throws InvocationTargetException - * @throws IllegalAccessException - */ - public Map extractSettings() throws IllegalAccessException, - InvocationTargetException, NoSuchMethodException; + /** + * Extract settings (object properties except for property + * 'maxProcessCount') of the current component + * + * @return a Map of settings + * @throws NoSuchMethodException + * @throws InvocationTargetException + * @throws IllegalAccessException + */ + public Map extractSettings() throws IllegalAccessException, + InvocationTargetException, NoSuchMethodException; } diff --git a/src/main/java/org/ghost4j/Ghostscript.java b/src/main/java/org/ghost4j/Ghostscript.java index de43342..cbcf202 100644 --- a/src/main/java/org/ghost4j/Ghostscript.java +++ b/src/main/java/org/ghost4j/Ghostscript.java @@ -9,14 +9,17 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.text.ParseException; -import java.text.SimpleDateFormat; - +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; import org.ghost4j.display.DisplayCallback; import org.ghost4j.display.DisplayData; +import com.sun.jna.Native; import com.sun.jna.Pointer; import com.sun.jna.ptr.IntByReference; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.slf4j.event.Level; /** @@ -26,639 +29,664 @@ */ public class Ghostscript { - /** - * Name of the system property used to set the encoding to use for stdin. - */ - public static final String PROPERTY_NAME_ENCODING = "ghost4j.encoding"; - /** - * Holds Ghostscript interpreter native instance (C pointer). - */ - private static GhostscriptLibrary.gs_main_instance.ByReference nativeInstanceByRef; - /** - * Holds singleton instance. - */ - private static Ghostscript instance; - /** - * Standard input stream. - */ - private static InputStream stdIn; - /** - * Standard output stream. - */ - private static OutputStream stdOut; - /** - * Error output stream. - */ - private static OutputStream stdErr; - /** - * Display callback used to handle display. - */ - private static DisplayCallback displayCallback; - /** - * Stores display data when working with display callback. - */ - private static DisplayData displayData; - /** - * Holds the native display callback. - */ - private static GhostscriptLibrary.display_callback_s nativeDisplayCallback; - - /** - * Singleton access method. - * - * @return The singleton instance. - */ - public static synchronized Ghostscript getInstance() { - - if (instance == null) { - - // new instance - instance = new Ghostscript(); + /** + * Name of the system property used to set the encoding to use for stdin. + */ + public static final String PROPERTY_NAME_ENCODING = "ghost4j.encoding"; + /** + * Logger name. + */ + private static final String LOGGER_NAME = Ghostscript.class.getName(); + /** + * Holds Ghostscript interpreter native instance (C pointer). + */ + private static GhostscriptLibrary.gs_main_instance.ByReference nativeInstanceByRef; + /** + * Holds singleton instance. + */ + private static Ghostscript instance; + /** + * Standard input stream. + */ + private static InputStream stdIn; + /** + * Standard output stream. + */ + private static OutputStream stdOut; + /** + * Error output stream. + */ + private static OutputStream stdErr; + /** + * Display callback used to handle display. + */ + private static DisplayCallback displayCallback; + /** + * Stores display data when working with display callback. + */ + private static DisplayData displayData; + /** + * Holds the native display callback. + */ + private static GhostscriptLibrary.display_callback_s nativeDisplayCallback; + + /** + * Singleton access method. + * + * @return The singleton instance. + */ + public static synchronized Ghostscript getInstance() { + + if (instance == null) { + + // new instance + instance = new Ghostscript(); - } + } - return instance; - } - - /** - * Gets the display callback set on the Ghostscript interpreter (may be null - * if not set). - * - * @return The DisplayCallback or null - */ - public synchronized DisplayCallback getDisplayCallback() { - return displayCallback; - } - - /** - * Sets a display callback for the Ghostscript interpreter. - * - * @param displayCallback - * DisplayCallback object - */ - public synchronized void setDisplayCallback(DisplayCallback displayCallback) { - this.displayCallback = displayCallback; - } - - /** - * Gets the error output stream of the Ghostscript interpreter (may be null - * if not set). - * - * @return The OutputStream or null - */ - public synchronized OutputStream getStdErr() { - return stdErr; - } - - /** - * Sets the error output stream of the Ghostscript interpreter. - * - * @param stdErr - * OutputStream object - */ - public synchronized void setStdErr(OutputStream stdErr) { - this.stdErr = stdErr; - } - - /** - * Gets the standard output stream of the Ghostscript interpreter (may be - * null if not set). - * - * @return The OutputStream or null - */ - public synchronized OutputStream getStdOut() { - return stdOut; - } - - /** - * Sets the standard output stream of the Ghostscript interpreter. - * - * @param stdOut - * OutputStream object - */ - public synchronized void setStdOut(OutputStream stdOut) { - this.stdOut = stdOut; - } - - /** - * Gets the standard input stream of the Ghostscript interpreter (may be - * null if not set). - * - * @return The InputStream or null - */ - public synchronized InputStream getStdIn() { - return stdIn; - } - - /** - * Sets the standard input stream of the Ghostscript interpreter. - * - * @param stdIn - * InputStream object - */ - public synchronized void setStdIn(InputStream stdIn) { - this.stdIn = stdIn; - } - - /** - * Private constructor. - */ - private Ghostscript() { - } - - /** - * Singleton factory method for getting a Ghostscript,interpreter instance. - * Only called from class itself. - * - * @return Ghostscript instance. - * @throws org.ghost4j.GhostscriptException - */ - private synchronized GhostscriptLibrary.gs_main_instance.ByReference getNativeInstanceByRef() - throws GhostscriptException { - - if (nativeInstanceByRef == null) { - - // prepare instance - nativeInstanceByRef = new GhostscriptLibrary.gs_main_instance.ByReference(); - // create instance - int result = GhostscriptLibrary.instance.gsapi_new_instance( - nativeInstanceByRef.getPointer(), null); - - // test result - if (result != 0) { - // failure - nativeInstanceByRef = null; - throw new GhostscriptException( - "Cannot get Ghostscript interpreter instance. Error code is " - + result); - } + return instance; } - return nativeInstanceByRef; - } + /** + * Gets the display callback set on the Ghostscript interpreter (may be null + * if not set). + * + * @return The DisplayCallback or null + */ + public synchronized DisplayCallback getDisplayCallback() { + return displayCallback; + } - private synchronized DisplayData getDisplayData() { + /** + * Sets a display callback for the Ghostscript interpreter. + * + * @param displayCallback + * DisplayCallback object + */ + public synchronized void setDisplayCallback(DisplayCallback displayCallback) { + this.displayCallback = displayCallback; + } - if (displayData == null) { - displayData = new DisplayData(); + /** + * Gets the error output stream of the Ghostscript interpreter (may be null + * if not set). + * + * @return The OutputStream or null + */ + public synchronized OutputStream getStdErr() { + return stdErr; } - return displayData; - } - - /** - * Gets Ghostscript revision data. - * - * @return Revision data. - */ - public static GhostscriptRevision getRevision() { - - // prepare revision structure and call revision function - GhostscriptLibrary.gsapi_revision_s revision = new GhostscriptLibrary.gsapi_revision_s(); - GhostscriptLibrary.instance.gsapi_revision(revision, revision.size()); - - GhostscriptRevision result = new GhostscriptRevision(); - result.setProduct(revision.product); - result.setCopyright(revision.copyright); - result.setNumber(new Float(revision.revision.floatValue() / 100) - .toString()); - // parse revision date - try { - SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd"); - result.setRevisionDate(sdf.parse(revision.revisiondate.toString())); - } catch (ParseException e) { - result.setRevisionDate(null); + /** + * Sets the error output stream of the Ghostscript interpreter. + * + * @param stdErr + * OutputStream object + */ + public synchronized void setStdErr(OutputStream stdErr) { + this.stdErr = stdErr; } - return result; - - } - - /** - * Initializes Ghostscript interpreter. - * - * @param args - * Interpreter parameters. Use the same as Ghostscript command - * line arguments. - * @throws org.ghost4j.GhostscriptException - */ - public void initialize(String[] args) throws GhostscriptException { - - int result = 0; - - // stdin callback - GhostscriptLibrary.stdin_fn stdinCallback = null; - if (getStdIn() != null) { - stdinCallback = new GhostscriptLibrary.stdin_fn() { - - public int callback(Pointer caller_handle, Pointer buf, int len) { - - // retrieve encoding, if no ghost4j encoding defined = use - // JVM default - String encoding = System.getProperty( - PROPERTY_NAME_ENCODING, - System.getProperty("file.encoding")); - - try { - byte[] buffer = new byte[1000]; - int read = getStdIn().read(buffer); - if (read != -1) { - buf.setString(0, new String(buffer, 0, read, - encoding)); - buffer = null; - return read; - } - } catch (Exception e) { - // an error occurs: do nothing - } + /** + * Gets the standard output stream of the Ghostscript interpreter (may be + * null if not set). + * + * @return The OutputStream or null + */ + public synchronized OutputStream getStdOut() { + return stdOut; + } - return 0; - } - }; + /** + * Sets the standard output stream of the Ghostscript interpreter. + * + * @param stdOut + * OutputStream object + */ + public synchronized void setStdOut(OutputStream stdOut) { + this.stdOut = stdOut; } - // stdout callback, if no stdout explicitly defined, use a - // GhostscriptLoggerOutputStream to log messages - GhostscriptLibrary.stdout_fn stdoutCallback = null; - if (getStdOut() == null) { - setStdOut(new GhostscriptLoggerOutputStream(Level.INFO)); + /** + * Gets the standard input stream of the Ghostscript interpreter (may be + * null if not set). + * + * @return The InputStream or null + */ + public synchronized InputStream getStdIn() { + return stdIn; } - stdoutCallback = new GhostscriptLibrary.stdout_fn() { + /** + * Sets the standard input stream of the Ghostscript interpreter. + * + * @param stdIn + * InputStream object + */ + public synchronized void setStdIn(InputStream stdIn) { + this.stdIn = stdIn; + } - public int callback(Pointer caller_handle, String str, int len) { + /** + * Private constructor. + */ + private Ghostscript() { + } - try { - getStdOut().write(str.getBytes(), 0, len); - } catch (IOException ex) { - // do nothing + /** + * Singleton factory method for getting a Ghostscript,interpreter instance. + * Only called from class itself. + * + * @return Ghostscript instance. + * @throws org.ghost4j.GhostscriptException + */ + private synchronized GhostscriptLibrary.gs_main_instance.ByReference getNativeInstanceByRef() + throws GhostscriptException { + + if (nativeInstanceByRef == null) { + + // prepare instance + nativeInstanceByRef = new GhostscriptLibrary.gs_main_instance.ByReference(); + // create instance + int result = GhostscriptLibrary.instance.gsapi_new_instance( + nativeInstanceByRef.getPointer(), null); + + // test result + if (result != 0) { + // failure + nativeInstanceByRef = null; + throw new GhostscriptException( + "Cannot get Ghostscript interpreter instance. Error code is " + + result); + } } - return len; - } - }; - - // stderr callback, if no stdout explicitly defined, use a - // GhostscriptLoggerOutputStream to log messages - GhostscriptLibrary.stderr_fn stderrCallback = null; - if (getStdErr() == null) { - setStdErr(new GhostscriptLoggerOutputStream(Level.ERROR)); + return nativeInstanceByRef; } - stderrCallback = new GhostscriptLibrary.stderr_fn() { + private synchronized DisplayData getDisplayData() { - public int callback(Pointer caller_handle, String str, int len) { + if (displayData == null) { + displayData = new DisplayData(); + } + return displayData; + } + + /** + * Gets Ghostscript revision data. + * + * @return Revision data. + */ + public static GhostscriptRevision getRevision() { + + // prepare revision structure and call revision function + GhostscriptLibrary.gsapi_revision_s revision = new GhostscriptLibrary.gsapi_revision_s(); + GhostscriptLibrary.instance.gsapi_revision(revision, revision.size()); + + GhostscriptRevision result = new GhostscriptRevision(); + result.setProduct(revision.product); + result.setCopyright(revision.copyright); + + /* This does not do the right thing. Ghostscript 9.55.0 returns a + * revision number of 9550 but this results in 95.5. Perhaps it would + * be better to simply return 9550? + */ + //result.setNumber(Float.valueOf(revision.revision.floatValue() / 100)); + result.setNumber(revision.revision.longValue()); + // parse revision date try { - getStdErr().write(str.getBytes(), 0, len); - } catch (IOException ex) { - // do nothing + DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyyMMdd"); + result.setRevisionDate(LocalDate.parse( + revision.revisiondate.toString(), dtf)); + } catch (DateTimeParseException e) { + result.setRevisionDate(null); } - return len; - } - }; + return result; - // io setting - result = GhostscriptLibrary.instance.gsapi_set_stdio( - getNativeInstanceByRef().getValue(), stdinCallback, - stdoutCallback, stderrCallback); - - // test result - if (result != 0) { - throw new GhostscriptException( - "Cannot set IO on Ghostscript interpreter. Error code is " - + result); } - // display callback setting - if (getDisplayCallback() != null) { - result = GhostscriptLibrary.instance.gsapi_set_display_callback( - getNativeInstanceByRef().getValue(), - buildNativeDisplayCallback(getDisplayCallback())); - - // test result - if (result != 0) { - throw new GhostscriptException( - "Cannot set display callback on Ghostscript interpreter. Error code is " - + result); - } - } + /** + * Initializes Ghostscript interpreter. + * + * @param args + * Interpreter parameters. Use the same as Ghostscript command + * line arguments. + * @throws org.ghost4j.GhostscriptException + */ + public void initialize(String[] args) throws GhostscriptException { + + int result = 0; + + // stdin callback + GhostscriptLibrary.stdin_fn stdinCallback = null; + if (getStdIn() != null) { + stdinCallback = new GhostscriptLibrary.stdin_fn() { + + public int callback(Pointer caller_handle, Pointer buf, int len) { + + // retrieve encoding, if no ghost4j encoding defined = use + // JVM default + String encoding = System.getProperty( + PROPERTY_NAME_ENCODING, + System.getProperty("file.encoding")); + + try { + byte[] buffer = new byte[1000]; + int read = getStdIn().read(buffer); + if (read != -1) { + buf.setString(0, new String(buffer, 0, read, + encoding)); + buffer = null; + return read; + } + } catch (Exception e) { + // an error occurs: do nothing + } + + return 0; + } + }; + } - // init - result = GhostscriptLibrary.instance.gsapi_set_arg_encoding(getNativeInstanceByRef().getValue(), GhostscriptLibrary.GS_ARG_ENCODING_UTF8); - if (args != null) { - result = GhostscriptLibrary.instance.gsapi_init_with_args( - getNativeInstanceByRef().getValue(), args.length, args); - } else { - result = GhostscriptLibrary.instance.gsapi_init_with_args( - getNativeInstanceByRef().getValue(), 0, null); - } + // stdout callback, if no stdout explicitly defined, use a + // GhostscriptLoggerOutputStream to log messages + GhostscriptLibrary.stdout_fn stdoutCallback = null; + if (getStdOut() == null) { + setStdOut(new GhostscriptLoggerOutputStream(Level.INFO)); + } - // interpreter exited: this is not an error - if (result == -101) { - exit(); - result = 0; - } + stdoutCallback = new GhostscriptLibrary.stdout_fn() { - // test result - if (result != 0) { - throw new GhostscriptException( - "Cannot initialize Ghostscript interpreter. Error code is " - + result); - } - } - - /** - * Builds a native display callback from a DisplayCallback object. - * - * @param displayCallback - * DisplayCallback to use. - * @return The created native display callback. - */ - private synchronized GhostscriptLibrary.display_callback_s buildNativeDisplayCallback( - DisplayCallback displayCallback) throws GhostscriptException { - - nativeDisplayCallback = new GhostscriptLibrary.display_callback_s(); - - // determine display callback version from Ghostscript version - float version = Float.parseFloat(getRevision().getNumber()); - // some versions report version 8.15 as 815.05 - if (version < 8.50 || version > 100) { - nativeDisplayCallback.version_major = 1; - } else { - nativeDisplayCallback.version_major = 2; - } - nativeDisplayCallback.version_minor = 0; + public int callback(Pointer caller_handle, String str, int len) { - nativeDisplayCallback.display_open = new GhostscriptLibrary.display_callback_s.display_open() { + try { + getStdOut().write(str.getBytes(), 0, len); + } catch (IOException ex) { + // do nothing + } - public int callback(Pointer handle, Pointer device) { + return len; + } + }; - // call to java callback - try { - getDisplayCallback().displayOpen(); - } catch (GhostscriptException e) { - return 1; + // stderr callback, if no stdout explicitly defined, use a + // GhostscriptLoggerOutputStream to log messages + GhostscriptLibrary.stderr_fn stderrCallback = null; + if (getStdErr() == null) { + setStdErr(new GhostscriptLoggerOutputStream(Level.ERROR)); } - return 0; - } - }; - nativeDisplayCallback.display_preclose = new GhostscriptLibrary.display_callback_s.display_preclose() { + stderrCallback = new GhostscriptLibrary.stderr_fn() { - public int callback(Pointer handle, Pointer device) { + public int callback(Pointer caller_handle, String str, int len) { - // call to java callback - try { - getDisplayCallback().displayPreClose(); - } catch (GhostscriptException e) { - return 1; + try { + getStdErr().write(str.getBytes(), 0, len); + } catch (IOException ex) { + // do nothing + } + + return len; + } + }; + + // io setting + result = GhostscriptLibrary.instance.gsapi_set_stdio( + getNativeInstanceByRef().getValue(), stdinCallback, + stdoutCallback, stderrCallback); + + // test result + if (result != 0) { + throw new GhostscriptException( + "Cannot set IO on Ghostscript interpreter. Error code is " + + result); } - return 0; - } - }; - nativeDisplayCallback.display_close = new GhostscriptLibrary.display_callback_s.display_close() { + // display callback setting + if (getDisplayCallback() != null) { + result = GhostscriptLibrary.instance.gsapi_set_display_callback( + getNativeInstanceByRef().getValue(), + buildNativeDisplayCallback(getDisplayCallback())); + + // test result + if (result != 0) { + throw new GhostscriptException( + "Cannot set display callback on Ghostscript interpreter. Error code is " + + result); + } + } - public int callback(Pointer handle, Pointer device) { + // init + Logger logger = LoggerFactory.getLogger(LOGGER_NAME); + GhostscriptRevision rev = Ghostscript.getRevision(); + logger.info(rev.getProduct() + " " + rev.getNumber() + " (" + + rev.getRevisionDateFormatted() + ")"); + logger.info(rev.getCopyright()); + + /* Ghostscript versions older than 9.08 (at least) do not have the + * gsapi_set_arg_encoding() function call. On those older versions + * there is no requirement to call it. So if we detect a version + * older than 9.08 skip the call. + */ + if (rev.getNumber() > Long.valueOf("9070")) { + result = GhostscriptLibrary.instance.gsapi_set_arg_encoding(getNativeInstanceByRef().getValue(), GhostscriptLibrary.GS_ARG_ENCODING_UTF8); + } - // call to java callback - try { - getDisplayCallback().displayClose(); - } catch (GhostscriptException e) { - return 1; + if (args != null) { + result = GhostscriptLibrary.instance.gsapi_init_with_args( + getNativeInstanceByRef().getValue(), args.length, args); + } else { + result = GhostscriptLibrary.instance.gsapi_init_with_args( + getNativeInstanceByRef().getValue(), 0, null); } - return 0; - } - }; - nativeDisplayCallback.display_presize = new GhostscriptLibrary.display_callback_s.display_presize() { + // interpreter exited: this is not an error + if (result == -101) { + exit(); + result = 0; + } - public int callback(Pointer handle, Pointer device, int width, - int height, int raster, int format) { + // test result + if (result != 0) { + throw new GhostscriptException( + "Cannot initialize Ghostscript interpreter. Error code is " + + result); + } + } - // call to java callback - try { - getDisplayCallback().displayPreSize(width, height, raster, - format); - } catch (GhostscriptException e) { - return 1; + /** + * Builds a native display callback from a DisplayCallback object. + * + * @param displayCallback + * DisplayCallback to use. + * @return The created native display callback. + */ + private synchronized GhostscriptLibrary.display_callback_s buildNativeDisplayCallback( + DisplayCallback displayCallback) throws GhostscriptException { + + nativeDisplayCallback = new GhostscriptLibrary.display_callback_s(); + + // determine display callback version from Ghostscript version + //float version = getRevision().getNumber(); + float version = Float.valueOf(getRevision().getNumber() / 100); + // some versions report version 8.15 as 815.05 + if (version < 8.50 || version > 100) { + nativeDisplayCallback.version_major = 1; + } else { + nativeDisplayCallback.version_major = 2; } + nativeDisplayCallback.version_minor = 0; - return 0; - } - }; - nativeDisplayCallback.display_size = new GhostscriptLibrary.display_callback_s.display_size() { + nativeDisplayCallback.display_open = new GhostscriptLibrary.display_callback_s.display_open() { - public int callback(Pointer handle, Pointer device, int width, - int height, int raster, int format, Pointer pimage) { + public int callback(Pointer handle, Pointer device) { - // prepare current page data - getDisplayData().setWidth(width); - getDisplayData().setHeight(height); - getDisplayData().setRaster(raster); - getDisplayData().setFormat(format); - getDisplayData().setPimage(pimage); + // call to java callback + try { + getDisplayCallback().displayOpen(); + } catch (GhostscriptException e) { + return 1; + } - // call to java callback - try { - getDisplayCallback().displaySize(width, height, raster, - format); - } catch (GhostscriptException e) { - return 1; - } + return 0; + } + }; + nativeDisplayCallback.display_preclose = new GhostscriptLibrary.display_callback_s.display_preclose() { - return 0; - } - }; - nativeDisplayCallback.display_sync = new GhostscriptLibrary.display_callback_s.display_sync() { + public int callback(Pointer handle, Pointer device) { - public int callback(Pointer handle, Pointer device) { + // call to java callback + try { + getDisplayCallback().displayPreClose(); + } catch (GhostscriptException e) { + return 1; + } - // call to java callback - try { - getDisplayCallback().displaySync(); - } catch (GhostscriptException e) { - return 1; - } + return 0; + } + }; + nativeDisplayCallback.display_close = new GhostscriptLibrary.display_callback_s.display_close() { - return 0; - } - }; - nativeDisplayCallback.display_page = new GhostscriptLibrary.display_callback_s.display_page() { + public int callback(Pointer handle, Pointer device) { - public int callback(Pointer handle, Pointer device, int copies, - int flush) { + // call to java callback + try { + getDisplayCallback().displayClose(); + } catch (GhostscriptException e) { + return 1; + } - byte[] data = getDisplayData().getPimage().getByteArray( - 0, - getDisplayData().getRaster() - * getDisplayData().getHeight()); + return 0; + } + }; + nativeDisplayCallback.display_presize = new GhostscriptLibrary.display_callback_s.display_presize() { - // call to java callback - try { - getDisplayCallback().displayPage( - getDisplayData().getWidth(), - getDisplayData().getHeight(), - getDisplayData().getRaster(), - getDisplayData().getFormat(), copies, flush, data); - } catch (GhostscriptException e) { - return 1; - } + public int callback(Pointer handle, Pointer device, int width, + int height, int raster, int format) { - return 0; - } - }; - nativeDisplayCallback.display_update = new GhostscriptLibrary.display_callback_s.display_update() { + // call to java callback + try { + getDisplayCallback().displayPreSize(width, height, raster, + format); + } catch (GhostscriptException e) { + return 1; + } + + return 0; + } + }; + nativeDisplayCallback.display_size = new GhostscriptLibrary.display_callback_s.display_size() { + + public int callback(Pointer handle, Pointer device, int width, + int height, int raster, int format, Pointer pimage) { + + // prepare current page data + getDisplayData().setWidth(width); + getDisplayData().setHeight(height); + getDisplayData().setRaster(raster); + getDisplayData().setFormat(format); + getDisplayData().setPimage(pimage); + + // call to java callback + try { + getDisplayCallback().displaySize(width, height, raster, + format); + } catch (GhostscriptException e) { + return 1; + } + + return 0; + } + }; + nativeDisplayCallback.display_sync = new GhostscriptLibrary.display_callback_s.display_sync() { - public int callback(Pointer handle, Pointer device, int x, int y, - int w, int h) { + public int callback(Pointer handle, Pointer device) { - // call to java callback - try { - getDisplayCallback().displayUpdate(x, y, w, h); - } catch (GhostscriptException e) { - return 1; - } + // call to java callback + try { + getDisplayCallback().displaySync(); + } catch (GhostscriptException e) { + return 1; + } - return 0; - } - }; - - nativeDisplayCallback.display_memalloc = null; - nativeDisplayCallback.display_memfree = null; - - switch (nativeDisplayCallback.version_major) { - case 1: - nativeDisplayCallback.size = nativeDisplayCallback.size() - - Pointer.SIZE; - break; - default: - nativeDisplayCallback.size = nativeDisplayCallback.size(); - break; - } + return 0; + } + }; + nativeDisplayCallback.display_page = new GhostscriptLibrary.display_callback_s.display_page() { + + public int callback(Pointer handle, Pointer device, int copies, + int flush) { + + byte[] data = getDisplayData().getPimage().getByteArray( + 0, + getDisplayData().getRaster() + * getDisplayData().getHeight()); + + // call to java callback + try { + getDisplayCallback().displayPage( + getDisplayData().getWidth(), + getDisplayData().getHeight(), + getDisplayData().getRaster(), + getDisplayData().getFormat(), copies, flush, data); + } catch (GhostscriptException e) { + return 1; + } + + return 0; + } + }; + nativeDisplayCallback.display_update = new GhostscriptLibrary.display_callback_s.display_update() { - nativeDisplayCallback.display_separation = null; + public int callback(Pointer handle, Pointer device, int x, int y, + int w, int h) { - return nativeDisplayCallback; - } + // call to java callback + try { + getDisplayCallback().displayUpdate(x, y, w, h); + } catch (GhostscriptException e) { + return 1; + } - /** - * Exits Ghostscript interpreter. Must be called after initialize. - * - * @throws org.ghost4j.GhostscriptException - */ - public void exit() throws GhostscriptException { + return 0; + } + }; + + nativeDisplayCallback.display_memalloc = null; + nativeDisplayCallback.display_memfree = null; + + switch (nativeDisplayCallback.version_major) { + case 1: + nativeDisplayCallback.size = nativeDisplayCallback.size() + - Native.POINTER_SIZE; + break; + default: + nativeDisplayCallback.size = nativeDisplayCallback.size(); + break; + } - if (nativeInstanceByRef != null) { - int result = GhostscriptLibrary.instance - .gsapi_exit(getNativeInstanceByRef().getValue()); + nativeDisplayCallback.display_separation = null; - if (result != 0) { - throw new GhostscriptException( - "Cannot exit Ghostscript interpreter. Error code is " - + result); - } - } - } - - /** - * Sends command string to Ghostscript interpreter. Must be called after - * initialize method. - * - * @param string - * Command string - * @throws org.ghost4j.GhostscriptException - */ - public void runString(String string) throws GhostscriptException { - - IntByReference exitCode = new IntByReference(); - - GhostscriptLibrary.instance.gsapi_run_string_begin( - getNativeInstanceByRef().getValue(), 0, exitCode); - - // test exit code - if (exitCode.getValue() != 0) { - throw new GhostscriptException( - "Cannot run command on Ghostscript interpreter. gsapi_run_string_begin failed with error code " - + exitCode.getValue()); + return nativeDisplayCallback; } - // split string on carriage return - String[] slices = string.split("\n"); - - for (int i = 0; i < slices.length; i++) { - String slice = slices[i] + "\n"; - GhostscriptLibrary.instance.gsapi_run_string_continue( - getNativeInstanceByRef().getValue(), slice, slice.length(), - 0, exitCode); - - // test exit code - if (exitCode.getValue() != 0) { - throw new GhostscriptException( - "Cannot run command on Ghostscript interpreter. gsapi_run_string_continue failed with error code " - + exitCode.getValue()); - } + /** + * Exits Ghostscript interpreter. Must be called after initialize. + * + * @throws org.ghost4j.GhostscriptException + */ + public void exit() throws GhostscriptException { + + if (nativeInstanceByRef != null) { + int result = GhostscriptLibrary.instance + .gsapi_exit(getNativeInstanceByRef().getValue()); + + if (result != 0) { + throw new GhostscriptException( + "Cannot exit Ghostscript interpreter. Error code is " + + result); + } + } } - GhostscriptLibrary.instance.gsapi_run_string_end( - getNativeInstanceByRef().getValue(), 0, exitCode); + /** + * Sends command string to Ghostscript interpreter. Must be called after + * initialize method. + * + * @param string + * Command string + * @throws org.ghost4j.GhostscriptException + */ + public void runString(String string) throws GhostscriptException { + + IntByReference exitCode = new IntByReference(); + + GhostscriptLibrary.instance.gsapi_run_string_begin( + getNativeInstanceByRef().getValue(), 0, exitCode); + + // test exit code + if (exitCode.getValue() != 0) { + throw new GhostscriptException( + "Cannot run command on Ghostscript interpreter. gsapi_run_string_begin failed with error code " + + exitCode.getValue()); + } - // test exit code - if (exitCode.getValue() != 0) { - throw new GhostscriptException( - "Cannot run command on Ghostscript interpreter. gsapi_run_string_end failed with error code " - + exitCode.getValue()); - } + // split string on carriage return + String[] slices = string.split("\n"); - } + for (int i = 0; i < slices.length; i++) { + String slice = slices[i] + "\n"; + GhostscriptLibrary.instance.gsapi_run_string_continue( + getNativeInstanceByRef().getValue(), slice, slice.length(), + 0, exitCode); - /** - * Sends file Ghostscript interpreter. Must be called after initialize - * method. - * - * @param fileName - * File name - * @throws org.ghost4j.GhostscriptException - */ - public void runFile(String fileName) throws GhostscriptException { + // test exit code + if (exitCode.getValue() != 0) { + throw new GhostscriptException( + "Cannot run command on Ghostscript interpreter. gsapi_run_string_continue failed with error code " + + exitCode.getValue()); + } + } - IntByReference exitCode = new IntByReference(); + GhostscriptLibrary.instance.gsapi_run_string_end( + getNativeInstanceByRef().getValue(), 0, exitCode); - GhostscriptLibrary.instance.gsapi_run_file(getNativeInstanceByRef() - .getValue(), fileName, 0, exitCode); + // test exit code + if (exitCode.getValue() != 0) { + throw new GhostscriptException( + "Cannot run command on Ghostscript interpreter. gsapi_run_string_end failed with error code " + + exitCode.getValue()); + } - // test exit code - if (exitCode.getValue() != 0) { - throw new GhostscriptException( - "Cannot run file on Ghostscript interpreter. Error code " - + exitCode.getValue()); } - } - - /** - * Deletes the singleton instance of the Ghostscript object. This ensures - * that the native Ghostscrit interpreter instance is deleted. This method - * must be called if Ghostscript is not used anymore or maybe reinitialized. - * - * @throws org.ghost4j.GhostscriptException - */ - public static synchronized void deleteInstance() - throws GhostscriptException { - - // clear instance - if (instance != null) { - // unreference singleton instance - instance = null; + /** + * Sends file Ghostscript interpreter. Must be called after initialize + * method. + * + * @param fileName + * File name + * @throws org.ghost4j.GhostscriptException + */ + public void runFile(String fileName) throws GhostscriptException { + + IntByReference exitCode = new IntByReference(); + + GhostscriptLibrary.instance.gsapi_run_file(getNativeInstanceByRef() + .getValue(), fileName, 0, exitCode); + + // test exit code + if (exitCode.getValue() != 0) { + throw new GhostscriptException( + "Cannot run file on Ghostscript interpreter. Error code " + + exitCode.getValue()); + } + } - // delete native interpeter instance - if (nativeInstanceByRef != null) { - GhostscriptLibrary.instance - .gsapi_delete_instance(nativeInstanceByRef.getValue()); - nativeInstanceByRef = null; + /** + * Deletes the singleton instance of the Ghostscript object. This ensures + * that the native Ghostscrit interpreter instance is deleted. This method + * must be called if Ghostscript is not used anymore or maybe reinitialized. + * + * @throws org.ghost4j.GhostscriptException + */ + public static synchronized void deleteInstance() + throws GhostscriptException { + + // clear instance + if (instance != null) { + // unreference singleton instance + instance = null; + } + + // delete native interpeter instance + if (nativeInstanceByRef != null) { + GhostscriptLibrary.instance + .gsapi_delete_instance(nativeInstanceByRef.getValue()); + nativeInstanceByRef = null; + } } - } } diff --git a/src/main/java/org/ghost4j/GhostscriptException.java b/src/main/java/org/ghost4j/GhostscriptException.java index c4895ac..17fbdbf 100644 --- a/src/main/java/org/ghost4j/GhostscriptException.java +++ b/src/main/java/org/ghost4j/GhostscriptException.java @@ -13,25 +13,24 @@ */ public class GhostscriptException extends Exception { - /** - * Serial version UID. - */ - private static final long serialVersionUID = -3901110749568935981L; + /** + * Serial version UID. + */ + private static final long serialVersionUID = -3901110749568935981L; - public GhostscriptException() { - super(); - } + public GhostscriptException() { + super(); + } - public GhostscriptException(String message) { - super(message); - } + public GhostscriptException(String message) { + super(message); + } - public GhostscriptException(Throwable cause) { - super(cause); - } + public GhostscriptException(Throwable cause) { + super(cause); + } - public GhostscriptException(String message, Throwable cause) { - super(message, cause); - } - -} \ No newline at end of file + public GhostscriptException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/src/main/java/org/ghost4j/GhostscriptLibrary.java b/src/main/java/org/ghost4j/GhostscriptLibrary.java index 8b1d461..4c50f59 100644 --- a/src/main/java/org/ghost4j/GhostscriptLibrary.java +++ b/src/main/java/org/ghost4j/GhostscriptLibrary.java @@ -15,7 +15,6 @@ import com.sun.jna.Structure; import com.sun.jna.ptr.IntByReference; import com.sun.jna.ptr.PointerByReference; -import com.sun.jna.win32.StdCallLibrary.StdCallCallback; import java.util.Arrays; import java.util.List; @@ -29,455 +28,455 @@ * @author Gilles Grousset (gi.grousset@gmail.com) */ public interface GhostscriptLibrary extends Library { - - final int GS_ARG_ENCODING_UTF8 = 1; - - /** - * Static instance of the library itself. - */ - public static GhostscriptLibrary instance = GhostscriptLibraryLoader - .loadLibrary(); - - /** - * Structure in charge of holding Ghostscript revision data. - */ - public class gsapi_revision_s extends Structure { - - /** - * Product name. - */ - public String product; - /** - * Copyright. - */ - public String copyright; - /** - * Revision number. - */ - public NativeLong revision; - /** - * Revision date. - */ - public NativeLong revisiondate; - - protected List getFieldOrder() { - return Arrays.asList("product", "copyright", "revision", "revisiondate"); - } - } - - /** - * Structure defining display callback functions. - */ - public class display_callback_s extends Structure { - - /** - * Callback called when new device has been opened. This is the first - * event from this device. - */ - public static interface display_open extends Callback { - - public int callback(Pointer handle, Pointer device); - } - - /** - * Callback called when device is about to be closed. Device will not be - * closed until this function returns. - */ - public static interface display_preclose extends Callback { - - public int callback(Pointer handle, Pointer device); - } - - /** - * Callback called when device has been closed. This is the last event - * from this device. - */ - public static interface display_close extends Callback { - - public int callback(Pointer handle, Pointer device); - } - - /** - * Callback called when device is about to be resized. Resize will only - * occur if this function returns 0. raster is byte count of a row. - */ - public static interface display_presize extends Callback { - - public int callback(Pointer handle, Pointer device, int width, - int height, int raster, int format); - } - - /** - * Callback called when device has been resized. New pointer to raster - * is returned in pimage. - */ - public static interface display_size extends Callback { - - public int callback(Pointer handle, Pointer device, int width, - int height, int raster, int format, Pointer pimage); - } - - /** - * Callback called on page flush. - */ - public static interface display_sync extends Callback { - - public int callback(Pointer handle, Pointer device); - } - - /** - * Callback called on show page. If you want to pause on showpage, then - * don't return immediately. - */ - public static interface display_page extends Callback { - - public int callback(Pointer handle, Pointer device, int copies, - int flush); - } - - /** - * Callback called to notify the caller whenever a portion of the raster - * is updated. This can be used for cooperative multitasking or for - * progressive update of the display. - */ - public static interface display_update extends Callback { - - public int callback(Pointer handle, Pointer device, int x, int y, - int w, int h); - } - - /** - * Callback called to allocate memory for bitmap This is provided in - * case you need to create memory in a special way, e.g. shared. This - * will only be called to allocate the image buffer. The first row will - * be placed at the address returned by display_memalloc. - */ - public static interface display_memalloc extends Callback { - - public void callback(Pointer handle, Pointer device, NativeLong size); - } - - /** - * Callback called to free memory for bitmap. - */ - public static interface display_memfree extends Callback { - - public int callback(Pointer handle, Pointer device, Pointer mem); - } - - public static interface display_separation extends Callback { - - public int callback(Pointer handle, Pointer device, int component, - String component_name, short c, short m, short y, short k); - } - - /** - * Size of this structure. Used for checking if we have been handed a - * valid structure. - */ - public int size; - /** - * Major version of this structure. The major version number will change - * if this structure changes. - */ - public int version_major; - /** - * Minor version of this structure. The minor version number will change - * if new features are added without changes to this structure. For - * example, a new color format. - */ - public int version_minor; - /** - * Holds a display_open callback. - */ - public display_open display_open; - /** - * Holds a display_preclose callback. - */ - public display_preclose display_preclose; - /** - * Holds a display_close callback. - */ - public display_close display_close; - /** - * Holds a display_presize callback. - */ - public display_presize display_presize; - /** - * Holds a display_size callback. - */ - public display_size display_size; - /** - * Holds a display_sync callback. - */ - public display_sync display_sync; - /** - * Holds a display_page callback. - */ - public display_page display_page; - /** - * Holds a display_update callback. Set this to null if not required. - */ - public display_update display_update; - /** - * Holds a display_memalloc callback. Set this to null if not required. - */ - public display_memalloc display_memalloc; - /** - * Holds a display_memfree callback. Set this to null if not required. - */ - public display_memfree display_memfree; - /** - * Holds a display_separation callback. Set this to null if not - * required. Ghostscript must only use this callback if version_major >= - * 2. - */ - public display_separation display_separation; - - protected List getFieldOrder() { - return Arrays.asList("size", "version_major", "version_minor", "display_open", "display_preclose", "display_close", "display_presize", "display_size", "display_sync", "display_page", "display_update", "display_memalloc", "display_memfree", "display_separation"); - } - } - - /** - * Pointer holding a native Ghostscript instance. - */ - public class gs_main_instance extends PointerType { - - @Override - public Object fromNative(Object arg0, FromNativeContext arg1) { - return super.fromNative(arg0, arg1); - } - - public static class ByReference extends PointerByReference { - - @Override - public Object fromNative(Object arg0, FromNativeContext arg1) { - return super.fromNative(arg0, arg1); - } - } - } - - /** - * Callback called to provide a custom input to Ghostscript. buf is a - * pointer to a char array. len is the length of the char array. - */ - public interface stdin_fn extends StdCallCallback { - - public int callback(Pointer caller_handle, Pointer buf, int len); - } - - /** - * Callback called to provide a custom output to Ghostscript. Important: The - * output is not the resulting file, but the output of the Postscript - * interpreter. str holds output characters. len is the length for str. - */ - public interface stdout_fn extends StdCallCallback { - - public int callback(Pointer caller_handle, String str, int len); - } - - /** - * Callback called to provide a custom error output to Ghostscript. str - * holds output characters. len is the length for str. - */ - public interface stderr_fn extends StdCallCallback { - - public int callback(Pointer caller_handle, String str, int len); - } - - /** - * This function returns the revision numbers and strings of the Ghostscript - * interpreter library. You should call it before any other interpreter - * library functions to make sure that the correct version of the - * Ghostscript interpreter has been loaded. - * - * @param pr Pointer to the gsapi_revision_s that will hold return values. - * @param len pr Length - * @see gsapi_revision_s - * @return 0 if everything is OK, < 0 otherwise - */ - public int gsapi_revision(Structure pr, int len); - - /** - * Create a new instance of Ghostscript. This instance is passed to most - * other gsapi functions. The caller_handle will be provided to callback - * functions. At this stage, Ghostscript supports only one instance. - * - * @param pinstance Pointer to gs_main_instance that will hold the - * Ghostscript instance. - * @param caller_handle Caller handler pointer (may be null). - * @return 0 if everything is OK, < 0 otherwise - */ - public int gsapi_new_instance(Pointer pinstance, Pointer caller_handle); - - /** - * Destroy an instance of Ghostscript. Before you call this, Ghostscript - * must have finished. If Ghostscript has been initialised, you must call - * gsapi_exit before gsapi_delete_instance. - * - * @param instance Pointer to the Ghostscript instance. - */ - public void gsapi_delete_instance(Pointer instance); - - /** - * Exit the interpreter. This must be called on shutdown if - * gsapi_init_with_args() has been called, and just before - * gsapi_delete_instance(). - * - * @param instance Pointer to the Ghostscript instance. - * @return 0 if everything is OK, < 0 otherwise - */ - public int gsapi_exit(Pointer instance); - - /** - * Set the encoding used for the interpretation of all subsequent args - * supplied via the gsapi interface on this instance. By default we expect - * args to be in encoding 0 (the 'local' encoding for this OS). On Windows - * this means "the currently selected codepage". On Linux this typically - * means utf8. This means that omitting to call this function will leave - * Ghostscript running exactly as it always has. Please note that use of the - * 'local' encoding is now deprecated and should be avoided in new code. - * This must be called after gsapi_new_instance() and before - * gsapi_init_with_args(). - * - * @param instance - * @param encoding - * @return - */ - public int gsapi_set_arg_encoding(Pointer instance, int encoding); - - /** - * Initialise the interpreter. This calls gs_main_init_with_args() in - * imainarg.c. The arguments are the same as the "C" main function: argv[0] - * is ignored and the user supplied arguments are argv[1] to argv[argc-1]. - * - * @param instance Pointer to the Ghostscript instance. - * @param argc Argument count - * @param argv Argument array - * @return 0 if everything is OK, < 0 otherwise - */ - public int gsapi_init_with_args(Pointer instance, int argc, String[] argv); - - /** - * Send instruction to the Ghostscript interpreter. The address passed in - * pexit_code will be used to return the exit code for the interpreter in - * case of a quit or fatal error. - * - * @param instance Pointer to the Ghostscript instance. - * @param str Instructions. Max length for the string is 65535. - * @param user_errors If set to 0 errors are returned the normal way (to the - * interpreter output), if a negative value is used errors are returns - * directly by the function. - * @param pexit_code Pointer to the exit return code - * @return 0 if everything is OK, < 0 otherwise - */ - public int gsapi_run_string(Pointer instance, String str, int user_errors, - IntByReference pexit_code); - - /** - * Send instruction to the Ghostscript interpreter. The address passed in - * pexit_code will be used to return the exit code for the interpreter in - * case of a quit or fatal error. - * - * @param instance Pointer to the Ghostscript instance. - * @param str Instructions. Max length for the string is 65535. - * @param length str length. - * @param user_errors If set to 0 errors are returned the normal way (to the - * interpreter output), if a negative value is used errors are returns - * directly by the function. - * @param pexit_code Pointer to the exit return code - * @return 0 if everything is OK, < 0 otherwise - */ - public int gsapi_run_string_with_length(Pointer instance, String str, - int length, int user_errors, IntByReference pexit_code); - - /** - * Open an instruction block to the Ghostscript interpreter. The address - * passed in pexit_code will be used to return the exit code for the - * interpreter in case of a quit or fatal error. - * - * @param instance Pointer to the Ghostscript instance. - * @param user_errors If set to 0 errors are returned the normal way (to the - * interpreter output), if a negative value is used errors are returns - * directly by the function. - * @param pexit_code Pointer to the exit return code - * @return 0 if everything is OK, < 0 otherwise - */ - public int gsapi_run_string_begin(Pointer instance, int user_errors, - IntByReference pexit_code); - - /** - * Send instruction to the Ghostscript interpreter. Must be used after - * gsapi_run_string_begin is called. The address passed in pexit_code will - * be used to return the exit code for the interpreter in case of a quit or - * fatal error. - * - * @param instance Pointer to the Ghostscript instance. - * @param str Instructions. Max length for the string is 65535. - * @param length str length. - * @param user_errors If set to 0 errors are returned the normal way (to the - * interpreter output), if a negative value is used errors are returns - * directly by the function. - * @param pexit_code Pointer to the exit return code - * @return 0 if everything is OK, < 0 otherwise - */ - public int gsapi_run_string_continue(Pointer instance, String str, - int length, int user_errors, IntByReference pexit_code); - - /** - * Close an instruction block to the Ghostscript interpreter. The address - * passed in pexit_code will be used to return the exit code for the - * interpreter in case of a quit or fatal error. - * - * @param instance Pointer to the Ghostscript instance. - * @param user_errors If set to 0 errors are returned the normal way (to the - * interpreter output), if a negative value is used errors are returns - * directly by the function. - * @param pexit_code Pointer to the exit return code - * @return 0 if everything is OK, < 0 otherwise - */ - public int gsapi_run_string_end(Pointer instance, int user_errors, - IntByReference pexit_code); - - /** - * Send instructions from a file to the Ghostscript interpreter. - * - * @param instance Pointer to the Ghostscript instance. - * @param file_name File name. - * @param user_errors If set to 0 errors are returned the normal way (to the - * interpreter output), if a negative value is used errors are returns - * directly by the function. - * @param pexit_code - * @return 0 if everything is OK, < 0 otherwise - */ - public int gsapi_run_file(Pointer instance, String file_name, - int user_errors, IntByReference pexit_code); - - /** - * Set the callback functions for stdio. The stdin callback function should - * return the number of characters read, 0 for EOF, or -1 for error. The - * stdout and stderr callback functions should return the number of - * characters written. - * - * @param instance Pointer to the Ghostscript instance. - * @param stdin_fn Stdin callback function. - * @param stdout_fn Stdout callback function. - * @param stderr_fn Stderr callback function. - * @return 0 if everything is OK, < 0 otherwise - */ - public int gsapi_set_stdio(Pointer instance, stdin_fn stdin_fn, - stdout_fn stdout_fn, stderr_fn stderr_fn); - - /** - * Set the callback structure for the display device. If the display device - * is used, this must be called after gsapi_new_instance() and before - * gsapi_init_with_args(). See gdevdsp.h for more details. - * - * @param instance Pointer to the Ghostscript instance. - * @param callback display_callback_s Structure holding display callback - * functions. - * @return 0 if everything is OK, < 0 otherwise - */ - public int gsapi_set_display_callback(Pointer instance, Structure callback); + + final int GS_ARG_ENCODING_UTF8 = 1; + + /** + * Static instance of the library itself. + */ + public static GhostscriptLibrary instance = GhostscriptLibraryLoader + .loadLibrary(); + + /** + * Structure in charge of holding Ghostscript revision data. + */ + public class gsapi_revision_s extends Structure { + + /** + * Product name. + */ + public String product; + /** + * Copyright. + */ + public String copyright; + /** + * Revision number. + */ + public NativeLong revision; + /** + * Revision date. + */ + public NativeLong revisiondate; + + protected List getFieldOrder() { + return Arrays.asList("product", "copyright", "revision", "revisiondate"); + } + } + + /** + * Structure defining display callback functions. + */ + public class display_callback_s extends Structure { + + /** + * Callback called when new device has been opened. This is the first + * event from this device. + */ + public static interface display_open extends Callback { + + public int callback(Pointer handle, Pointer device); + } + + /** + * Callback called when device is about to be closed. Device will not be + * closed until this function returns. + */ + public static interface display_preclose extends Callback { + + public int callback(Pointer handle, Pointer device); + } + + /** + * Callback called when device has been closed. This is the last event + * from this device. + */ + public static interface display_close extends Callback { + + public int callback(Pointer handle, Pointer device); + } + + /** + * Callback called when device is about to be resized. Resize will only + * occur if this function returns 0. raster is byte count of a row. + */ + public static interface display_presize extends Callback { + + public int callback(Pointer handle, Pointer device, int width, + int height, int raster, int format); + } + + /** + * Callback called when device has been resized. New pointer to raster + * is returned in pimage. + */ + public static interface display_size extends Callback { + + public int callback(Pointer handle, Pointer device, int width, + int height, int raster, int format, Pointer pimage); + } + + /** + * Callback called on page flush. + */ + public static interface display_sync extends Callback { + + public int callback(Pointer handle, Pointer device); + } + + /** + * Callback called on show page. If you want to pause on showpage, then + * don't return immediately. + */ + public static interface display_page extends Callback { + + public int callback(Pointer handle, Pointer device, int copies, + int flush); + } + + /** + * Callback called to notify the caller whenever a portion of the raster + * is updated. This can be used for cooperative multitasking or for + * progressive update of the display. + */ + public static interface display_update extends Callback { + + public int callback(Pointer handle, Pointer device, int x, int y, + int w, int h); + } + + /** + * Callback called to allocate memory for bitmap This is provided in + * case you need to create memory in a special way, e.g. shared. This + * will only be called to allocate the image buffer. The first row will + * be placed at the address returned by display_memalloc. + */ + public static interface display_memalloc extends Callback { + + public void callback(Pointer handle, Pointer device, NativeLong size); + } + + /** + * Callback called to free memory for bitmap. + */ + public static interface display_memfree extends Callback { + + public int callback(Pointer handle, Pointer device, Pointer mem); + } + + public static interface display_separation extends Callback { + + public int callback(Pointer handle, Pointer device, int component, + String component_name, short c, short m, short y, short k); + } + + /** + * Size of this structure. Used for checking if we have been handed a + * valid structure. + */ + public int size; + /** + * Major version of this structure. The major version number will change + * if this structure changes. + */ + public int version_major; + /** + * Minor version of this structure. The minor version number will change + * if new features are added without changes to this structure. For + * example, a new color format. + */ + public int version_minor; + /** + * Holds a display_open callback. + */ + public display_open display_open; + /** + * Holds a display_preclose callback. + */ + public display_preclose display_preclose; + /** + * Holds a display_close callback. + */ + public display_close display_close; + /** + * Holds a display_presize callback. + */ + public display_presize display_presize; + /** + * Holds a display_size callback. + */ + public display_size display_size; + /** + * Holds a display_sync callback. + */ + public display_sync display_sync; + /** + * Holds a display_page callback. + */ + public display_page display_page; + /** + * Holds a display_update callback. Set this to null if not required. + */ + public display_update display_update; + /** + * Holds a display_memalloc callback. Set this to null if not required. + */ + public display_memalloc display_memalloc; + /** + * Holds a display_memfree callback. Set this to null if not required. + */ + public display_memfree display_memfree; + /** + * Holds a display_separation callback. Set this to null if not + * required. Ghostscript must only use this callback if version_major >= + * 2. + */ + public display_separation display_separation; + + protected List getFieldOrder() { + return Arrays.asList("size", "version_major", "version_minor", "display_open", "display_preclose", "display_close", "display_presize", "display_size", "display_sync", "display_page", "display_update", "display_memalloc", "display_memfree", "display_separation"); + } + } + + /** + * Pointer holding a native Ghostscript instance. + */ + public class gs_main_instance extends PointerType { + + @Override + public Object fromNative(Object arg0, FromNativeContext arg1) { + return super.fromNative(arg0, arg1); + } + + public static class ByReference extends PointerByReference { + + @Override + public Object fromNative(Object arg0, FromNativeContext arg1) { + return super.fromNative(arg0, arg1); + } + } + } + + /** + * Callback called to provide a custom input to Ghostscript. buf is a + * pointer to a char array. len is the length of the char array. + */ + public interface stdin_fn extends Callback { + + public int callback(Pointer caller_handle, Pointer buf, int len); + } + + /** + * Callback called to provide a custom output to Ghostscript. Important: The + * output is not the resulting file, but the output of the Postscript + * interpreter. str holds output characters. len is the length for str. + */ + public interface stdout_fn extends Callback { + + public int callback(Pointer caller_handle, String str, int len); + } + + /** + * Callback called to provide a custom error output to Ghostscript. str + * holds output characters. len is the length for str. + */ + public interface stderr_fn extends Callback { + + public int callback(Pointer caller_handle, String str, int len); + } + + /** + * This function returns the revision numbers and strings of the Ghostscript + * interpreter library. You should call it before any other interpreter + * library functions to make sure that the correct version of the + * Ghostscript interpreter has been loaded. + * + * @param pr Pointer to the gsapi_revision_s that will hold return values. + * @param len pr Length + * @see gsapi_revision_s + * @return 0 if everything is OK, < 0 otherwise + */ + public int gsapi_revision(Structure pr, int len); + + /** + * Create a new instance of Ghostscript. This instance is passed to most + * other gsapi functions. The caller_handle will be provided to callback + * functions. At this stage, Ghostscript supports only one instance. + * + * @param pinstance Pointer to gs_main_instance that will hold the + * Ghostscript instance. + * @param caller_handle Caller handler pointer (may be null). + * @return 0 if everything is OK, < 0 otherwise + */ + public int gsapi_new_instance(Pointer pinstance, Pointer caller_handle); + + /** + * Destroy an instance of Ghostscript. Before you call this, Ghostscript + * must have finished. If Ghostscript has been initialised, you must call + * gsapi_exit before gsapi_delete_instance. + * + * @param instance Pointer to the Ghostscript instance. + */ + public void gsapi_delete_instance(Pointer instance); + + /** + * Exit the interpreter. This must be called on shutdown if + * gsapi_init_with_args() has been called, and just before + * gsapi_delete_instance(). + * + * @param instance Pointer to the Ghostscript instance. + * @return 0 if everything is OK, < 0 otherwise + */ + public int gsapi_exit(Pointer instance); + + /** + * Set the encoding used for the interpretation of all subsequent args + * supplied via the gsapi interface on this instance. By default we expect + * args to be in encoding 0 (the 'local' encoding for this OS). On Windows + * this means "the currently selected codepage". On Linux this typically + * means utf8. This means that omitting to call this function will leave + * Ghostscript running exactly as it always has. Please note that use of the + * 'local' encoding is now deprecated and should be avoided in new code. + * This must be called after gsapi_new_instance() and before + * gsapi_init_with_args(). + * + * @param instance + * @param encoding + * @return + */ + public int gsapi_set_arg_encoding(Pointer instance, int encoding); + + /** + * Initialise the interpreter. This calls gs_main_init_with_args() in + * imainarg.c. The arguments are the same as the "C" main function: argv[0] + * is ignored and the user supplied arguments are argv[1] to argv[argc-1]. + * + * @param instance Pointer to the Ghostscript instance. + * @param argc Argument count + * @param argv Argument array + * @return 0 if everything is OK, < 0 otherwise + */ + public int gsapi_init_with_args(Pointer instance, int argc, String[] argv); + + /** + * Send instruction to the Ghostscript interpreter. The address passed in + * pexit_code will be used to return the exit code for the interpreter in + * case of a quit or fatal error. + * + * @param instance Pointer to the Ghostscript instance. + * @param str Instructions. Max length for the string is 65535. + * @param user_errors If set to 0 errors are returned the normal way (to the + * interpreter output), if a negative value is used errors are returns + * directly by the function. + * @param pexit_code Pointer to the exit return code + * @return 0 if everything is OK, < 0 otherwise + */ + public int gsapi_run_string(Pointer instance, String str, int user_errors, + IntByReference pexit_code); + + /** + * Send instruction to the Ghostscript interpreter. The address passed in + * pexit_code will be used to return the exit code for the interpreter in + * case of a quit or fatal error. + * + * @param instance Pointer to the Ghostscript instance. + * @param str Instructions. Max length for the string is 65535. + * @param length str length. + * @param user_errors If set to 0 errors are returned the normal way (to the + * interpreter output), if a negative value is used errors are returns + * directly by the function. + * @param pexit_code Pointer to the exit return code + * @return 0 if everything is OK, < 0 otherwise + */ + public int gsapi_run_string_with_length(Pointer instance, String str, + int length, int user_errors, IntByReference pexit_code); + + /** + * Open an instruction block to the Ghostscript interpreter. The address + * passed in pexit_code will be used to return the exit code for the + * interpreter in case of a quit or fatal error. + * + * @param instance Pointer to the Ghostscript instance. + * @param user_errors If set to 0 errors are returned the normal way (to the + * interpreter output), if a negative value is used errors are returns + * directly by the function. + * @param pexit_code Pointer to the exit return code + * @return 0 if everything is OK, < 0 otherwise + */ + public int gsapi_run_string_begin(Pointer instance, int user_errors, + IntByReference pexit_code); + + /** + * Send instruction to the Ghostscript interpreter. Must be used after + * gsapi_run_string_begin is called. The address passed in pexit_code will + * be used to return the exit code for the interpreter in case of a quit or + * fatal error. + * + * @param instance Pointer to the Ghostscript instance. + * @param str Instructions. Max length for the string is 65535. + * @param length str length. + * @param user_errors If set to 0 errors are returned the normal way (to the + * interpreter output), if a negative value is used errors are returns + * directly by the function. + * @param pexit_code Pointer to the exit return code + * @return 0 if everything is OK, < 0 otherwise + */ + public int gsapi_run_string_continue(Pointer instance, String str, + int length, int user_errors, IntByReference pexit_code); + + /** + * Close an instruction block to the Ghostscript interpreter. The address + * passed in pexit_code will be used to return the exit code for the + * interpreter in case of a quit or fatal error. + * + * @param instance Pointer to the Ghostscript instance. + * @param user_errors If set to 0 errors are returned the normal way (to the + * interpreter output), if a negative value is used errors are returns + * directly by the function. + * @param pexit_code Pointer to the exit return code + * @return 0 if everything is OK, < 0 otherwise + */ + public int gsapi_run_string_end(Pointer instance, int user_errors, + IntByReference pexit_code); + + /** + * Send instructions from a file to the Ghostscript interpreter. + * + * @param instance Pointer to the Ghostscript instance. + * @param file_name File name. + * @param user_errors If set to 0 errors are returned the normal way (to the + * interpreter output), if a negative value is used errors are returns + * directly by the function. + * @param pexit_code + * @return 0 if everything is OK, < 0 otherwise + */ + public int gsapi_run_file(Pointer instance, String file_name, + int user_errors, IntByReference pexit_code); + + /** + * Set the callback functions for stdio. The stdin callback function should + * return the number of characters read, 0 for EOF, or -1 for error. The + * stdout and stderr callback functions should return the number of + * characters written. + * + * @param instance Pointer to the Ghostscript instance. + * @param stdin_fn Stdin callback function. + * @param stdout_fn Stdout callback function. + * @param stderr_fn Stderr callback function. + * @return 0 if everything is OK, < 0 otherwise + */ + public int gsapi_set_stdio(Pointer instance, stdin_fn stdin_fn, + stdout_fn stdout_fn, stderr_fn stderr_fn); + + /** + * Set the callback structure for the display device. If the display device + * is used, this must be called after gsapi_new_instance() and before + * gsapi_init_with_args(). See gdevdsp.h for more details. + * + * @param instance Pointer to the Ghostscript instance. + * @param callback display_callback_s Structure holding display callback + * functions. + * @return 0 if everything is OK, < 0 otherwise + */ + public int gsapi_set_display_callback(Pointer instance, Structure callback); } diff --git a/src/main/java/org/ghost4j/GhostscriptLibraryLoader.java b/src/main/java/org/ghost4j/GhostscriptLibraryLoader.java index 19d3673..428beb9 100644 --- a/src/main/java/org/ghost4j/GhostscriptLibraryLoader.java +++ b/src/main/java/org/ghost4j/GhostscriptLibraryLoader.java @@ -16,27 +16,27 @@ */ public class GhostscriptLibraryLoader { - /** - * Load native library according to host OS. - * - * @return The loaded library. - */ - protected static GhostscriptLibrary loadLibrary() { + /** + * Load native library according to host OS. + * + * @return The loaded library. + */ + protected static GhostscriptLibrary loadLibrary() { - // library name - String libName = "gs"; + // library name + String libName = "gs"; - // on Windows: library has a different name according to the - // architecture - if (Platform.isWindows()) { - // architecture - String arch = System.getProperty("sun.arch.data.model"); + // on Windows: library has a different name according to the + // architecture + if (Platform.isWindows()) { + // architecture + String arch = System.getProperty("sun.arch.data.model"); - libName = "gsdll" + arch; + libName = "gsdll" + arch; - } + } - return (GhostscriptLibrary) Native.loadLibrary(libName, - GhostscriptLibrary.class); - } + return (GhostscriptLibrary) Native.load(libName, + GhostscriptLibrary.class); + } } diff --git a/src/main/java/org/ghost4j/GhostscriptLoggerOutputStream.java b/src/main/java/org/ghost4j/GhostscriptLoggerOutputStream.java index ca84b9d..a1abc0c 100644 --- a/src/main/java/org/ghost4j/GhostscriptLoggerOutputStream.java +++ b/src/main/java/org/ghost4j/GhostscriptLoggerOutputStream.java @@ -21,62 +21,62 @@ */ public class GhostscriptLoggerOutputStream extends OutputStream { - /** - * Logger name. - */ - private static final String LOGGER_NAME = Ghostscript.class.getName(); + /** + * Logger name. + */ + private static final String LOGGER_NAME = Ghostscript.class.getName(); - /** - * Line termination for a log message. - */ - private static final int LINE_END = (int) '\n'; + /** + * Line termination for a log message. + */ + private static final int LINE_END = (int) '\n'; - /** - * ByteArrayOutputStream used to store outputed messages being written. - */ - private ByteArrayOutputStream baos = new ByteArrayOutputStream(); + /** + * ByteArrayOutputStream used to store outputed messages being written. + */ + private ByteArrayOutputStream baos = new ByteArrayOutputStream(); - /** - * Logger used to log messages. - */ - private Logger logger; + /** + * Logger used to log messages. + */ + private final Logger logger; - /** - * Log level used when outputing messages to the logger. - */ - private Level level; + /** + * Log level used when outputing messages to the logger. + */ + private final Level level; - /** - * Constructor. - * - * @param level - * Defines the log level of outputed messages. - */ - public GhostscriptLoggerOutputStream(Level level) { - logger = LoggerFactory.getLogger(LOGGER_NAME); - baos = new ByteArrayOutputStream(); - this.level = level; - } + /** + * Constructor. + * + * @param level + * Defines the log level of outputed messages. + */ + public GhostscriptLoggerOutputStream(final Level level) { + logger = LoggerFactory.getLogger(LOGGER_NAME); + baos = new ByteArrayOutputStream(); + this.level = level; + } - /** - * Write method that stores data to write in the ByteArrayOutputStream and - * sends messages to the logger when a line ends. - * - * @param b - * Byte to write - * @throws IOException - */ - public void write(int b) throws IOException { + /** + * Write method that stores data to write in the ByteArrayOutputStream and + * sends messages to the logger when a line ends. + * + * @param b + * Byte to write + * @throws IOException + */ + public void write(int b) throws IOException { - if (b == LINE_END) { - if (level == Level.INFO) { - logger.info(baos.toString()); - } else if (level == Level.ERROR) { - logger.error(baos.toString()); - } - baos.reset(); - } else { - baos.write(b); + if (b == LINE_END) { + if (level == Level.INFO) { + logger.info(baos.toString()); + } else if (level == Level.ERROR) { + logger.error(baos.toString()); + } + baos.reset(); + } else { + baos.write(b); + } } - } } diff --git a/src/main/java/org/ghost4j/GhostscriptRevision.java b/src/main/java/org/ghost4j/GhostscriptRevision.java index 84d10b2..dd6f0de 100644 --- a/src/main/java/org/ghost4j/GhostscriptRevision.java +++ b/src/main/java/org/ghost4j/GhostscriptRevision.java @@ -6,7 +6,8 @@ */ package org.ghost4j; -import java.util.Date; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; /** * Class used to carry Ghostscript revision data. @@ -15,52 +16,60 @@ */ public class GhostscriptRevision { - /** - * Product name. - */ - private String product; - /** - * Copyright. - */ - private String copyright; - /** - * Revision number. - */ - private String number; - /** - * Revision date. - */ - private Date revisionDate; + /** + * Product name. + */ + private String product; + /** + * Copyright. + */ + private String copyright; + /** + * Revision number. + */ + private long number; + /** + * Revision date. + */ + private LocalDate revisionDate; - public String getProduct() { - return product; - } + public String getProduct() { + return product; + } - public void setProduct(String product) { - this.product = product; - } + public void setProduct(final String product) { + this.product = product; + } - public String getCopyright() { - return copyright; - } + public String getCopyright() { + return copyright; + } - public void setCopyright(String copyright) { - this.copyright = copyright; - } + public void setCopyright(final String copyright) { + this.copyright = copyright; + } - public Date getRevisionDate() { - return revisionDate; - } + public LocalDate getRevisionDate() { + return revisionDate; + } - public void setRevisionDate(Date revisionDate) { - this.revisionDate = revisionDate; - } + public String getRevisionDateFormatted() { + if (revisionDate != null) { + return revisionDate.format(DateTimeFormatter.ISO_DATE); + } else { + return "unavailable"; + } + } - public String getNumber() { - return number; - } + public void setRevisionDate(final LocalDate revisionDate) { + this.revisionDate = revisionDate; + } - public void setNumber(String number) { - this.number = number; - } + public long getNumber() { + return number; + } + + public void setNumber(final long number) { + this.number = number; + } } diff --git a/src/main/java/org/ghost4j/converter/AbstractConverter.java b/src/main/java/org/ghost4j/converter/AbstractConverter.java index 57fa51e..fc06e14 100644 --- a/src/main/java/org/ghost4j/converter/AbstractConverter.java +++ b/src/main/java/org/ghost4j/converter/AbstractConverter.java @@ -20,16 +20,16 @@ * @author Gilles Grousset (gi.grousset@gmail.com) */ public abstract class AbstractConverter extends AbstractComponent implements - Converter { +Converter { - public void convert(Document document, OutputStream outputStream) - throws IOException, ConverterException, DocumentException { + public void convert(Document document, OutputStream outputStream) + throws IOException, ConverterException, DocumentException { - // perform actual processing - run(document, outputStream); + // perform actual processing + run(document, outputStream); - } + } - protected abstract void run(Document document, OutputStream outputStream) - throws IOException, ConverterException, DocumentException; + protected abstract void run(Document document, OutputStream outputStream) + throws IOException, ConverterException, DocumentException; } diff --git a/src/main/java/org/ghost4j/converter/AbstractRemoteConverter.java b/src/main/java/org/ghost4j/converter/AbstractRemoteConverter.java index 68a6871..41ee119 100644 --- a/src/main/java/org/ghost4j/converter/AbstractRemoteConverter.java +++ b/src/main/java/org/ghost4j/converter/AbstractRemoteConverter.java @@ -25,117 +25,117 @@ * @author Gilles Grousset (gi.grousset@gmail.com) */ public abstract class AbstractRemoteConverter extends AbstractRemoteComponent - implements RemoteConverter { - - protected abstract void run(Document document, OutputStream outputStream) - throws IOException, ConverterException, DocumentException; - - /** - * Starts a remote converter server. - * - * @param remoteConverter - * @throws ConverterException - */ - protected static void startRemoteConverter(RemoteConverter remoteConverter) - throws ConverterException { - - try { - - // get port - if (System.getenv("cajo.port") == null) { - throw new ConverterException( - "No Cajo port defined for remote converter"); - } - int cajoPort = Integer.parseInt(System.getenv("cajo.port")); - - // export converter - RemoteConverter converterCopy = remoteConverter.getClass() - .newInstance(); - converterCopy.setMaxProcessCount(0); - - Remote.config(null, cajoPort, null, 0); - ItemServer.bind(converterCopy, - RemoteConverter.class.getCanonicalName()); - - } catch (Exception e) { - throw new ConverterException(e); +implements RemoteConverter { + + protected abstract void run(Document document, OutputStream outputStream) + throws IOException, ConverterException, DocumentException; + + /** + * Starts a remote converter server. + * + * @param remoteConverter + * @throws ConverterException + */ + protected static void startRemoteConverter(RemoteConverter remoteConverter) + throws ConverterException { + + try { + + // get port + if (System.getenv("cajo.port") == null) { + throw new ConverterException( + "No Cajo port defined for remote converter"); + } + int cajoPort = Integer.parseInt(System.getenv("cajo.port")); + + // export converter + RemoteConverter converterCopy = remoteConverter.getClass() + .getDeclaredConstructor().newInstance(); + converterCopy.setMaxProcessCount(0); + + Remote.config(null, cajoPort, null, 0); + ItemServer.bind(converterCopy, + RemoteConverter.class.getCanonicalName()); + + } catch (Exception e) { + throw new ConverterException(e); + } + } - } + public byte[] remoteConvert(Document document) throws IOException, + ConverterException, DocumentException { - public byte[] remoteConvert(Document document) throws IOException, - ConverterException, DocumentException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + run(document, baos); - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - run(document, baos); + byte[] result = baos.toByteArray(); + baos.close(); - byte[] result = baos.toByteArray(); - baos.close(); + return result; - return result; + } - } + public void convert(Document document, OutputStream outputStream) + throws IOException, ConverterException, DocumentException { - public void convert(Document document, OutputStream outputStream) - throws IOException, ConverterException, DocumentException { + if (maxProcessCount == 0) { - if (maxProcessCount == 0) { + // perform actual processing + run(document, outputStream); - // perform actual processing - run(document, outputStream); + } else { - } else { + // handle parallel processes - // handle parallel processes + // wait for a process to get free + this.waitForFreeProcess(); + processCount++; - // wait for a process to get free - this.waitForFreeProcess(); - processCount++; + // check if current class supports stand alone mode + if (!this.isStandAloneModeSupported()) { + throw new ConverterException( + "Standalone mode is not supported by this converter: no 'main' method found"); + } - // check if current class supports stand alone mode - if (!this.isStandAloneModeSupported()) { - throw new ConverterException( - "Standalone mode is not supported by this converter: no 'main' method found"); - } + // prepare new JVM + JavaFork fork = this.buildJavaFork(); - // prepare new JVM - JavaFork fork = this.buildJavaFork(); + // set JVM Xmx parameter according to the document size + int documentMbSize = (document.getSize() / 1024 / 1024) + 1; + int xmxValue = 64 + documentMbSize; + fork.setXmx(xmxValue + "m"); - // set JVM Xmx parameter according to the document size - int documentMbSize = (document.getSize() / 1024 / 1024) + 1; - int xmxValue = 64 + documentMbSize; - fork.setXmx(xmxValue + "m"); + int cajoPort = 0; - int cajoPort = 0; + try { - try { + // start remove server + cajoPort = this.startRemoteServer(fork); - // start remove server - cajoPort = this.startRemoteServer(fork); + // get remote component + Object remote = this.getRemoteComponent(cajoPort, + RemoteConverter.class); - // get remote component - Object remote = this.getRemoteComponent(cajoPort, - RemoteConverter.class); + // copy converter settings to remote converter + Remote.invoke(remote, "copySettings", this.extractSettings()); - // copy converter settings to remote converter - Remote.invoke(remote, "copySettings", this.extractSettings()); + // perform remote conversion + byte[] result = (byte[]) Remote.invoke(remote, "remoteConvert", + document); - // perform remote conversion - byte[] result = (byte[]) Remote.invoke(remote, "remoteConvert", - document); + // write result to output stream + outputStream.write(result); - // write result to output stream - outputStream.write(result); + } catch (IOException e) { + throw e; + } catch (Exception e) { + throw new ConverterException(e); + } finally { + processCount--; + fork.stop(); + } + } - } catch (IOException e) { - throw e; - } catch (Exception e) { - throw new ConverterException(e); - } finally { - processCount--; - fork.stop(); - } } - - } } diff --git a/src/main/java/org/ghost4j/converter/Converter.java b/src/main/java/org/ghost4j/converter/Converter.java index f8378ec..37dd5d4 100644 --- a/src/main/java/org/ghost4j/converter/Converter.java +++ b/src/main/java/org/ghost4j/converter/Converter.java @@ -21,19 +21,19 @@ */ public interface Converter extends Component { - /** - * Converts a given document and output results in provided output stream. - * - * @param document - * Document to convert. Document type may or may no be supported - * (support left to the convert final implementation). - * @param outputStream - * Output stream where converted document is written. - * @throws IOException - * @throws ConverterException - * @throws DocumentException - */ - public void convert(Document document, OutputStream outputStream) - throws IOException, ConverterException, DocumentException; + /** + * Converts a given document and output results in provided output stream. + * + * @param document + * Document to convert. Document type may or may no be supported + * (support left to the convert final implementation). + * @param outputStream + * Output stream where converted document is written. + * @throws IOException + * @throws ConverterException + * @throws DocumentException + */ + public void convert(Document document, OutputStream outputStream) + throws IOException, ConverterException, DocumentException; } diff --git a/src/main/java/org/ghost4j/converter/ConverterException.java b/src/main/java/org/ghost4j/converter/ConverterException.java index e50a535..98c4a63 100644 --- a/src/main/java/org/ghost4j/converter/ConverterException.java +++ b/src/main/java/org/ghost4j/converter/ConverterException.java @@ -15,24 +15,24 @@ */ public class ConverterException extends Exception { - /** - * Serial version UID. - */ - private static final long serialVersionUID = -4246261539550729104L; + /** + * Serial version UID. + */ + private static final long serialVersionUID = -4246261539550729104L; - public ConverterException() { - super(); - } + public ConverterException() { + super(); + } - public ConverterException(String message) { - super(message); - } + public ConverterException(String message) { + super(message); + } - public ConverterException(Throwable cause) { - super(cause); - } + public ConverterException(Throwable cause) { + super(cause); + } - public ConverterException(String message, Throwable cause) { - super(message, cause); - } + public ConverterException(String message, Throwable cause) { + super(message, cause); + } } diff --git a/src/main/java/org/ghost4j/converter/PDFConverter.java b/src/main/java/org/ghost4j/converter/PDFConverter.java index 29ed9cb..94cdb7a 100644 --- a/src/main/java/org/ghost4j/converter/PDFConverter.java +++ b/src/main/java/org/ghost4j/converter/PDFConverter.java @@ -13,7 +13,6 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; - import org.apache.commons.io.IOUtils; import org.ghost4j.Ghostscript; import org.ghost4j.GhostscriptException; @@ -22,6 +21,8 @@ import org.ghost4j.document.PSDocument; import org.ghost4j.document.PaperSize; import org.ghost4j.util.DiskStore; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * PDF converter. @@ -30,297 +31,308 @@ */ public class PDFConverter extends AbstractRemoteConverter { - public static final int OPTION_AUTOROTATEPAGES_NONE = 0; - public static final int OPTION_AUTOROTATEPAGES_ALL = 1; - public static final int OPTION_AUTOROTATEPAGES_PAGEBYPAGE = 2; - public static final int OPTION_AUTOROTATEPAGES_OFF = 3; - - public static final int OPTION_PROCESSCOLORMODEL_RGB = 0; - public static final int OPTION_PROCESSCOLORMODEL_GRAY = 1; - public static final int OPTION_PROCESSCOLORMODEL_CMYK = 2; - - public static final int OPTION_PDFSETTINGS_DEFAULT = 0; - public static final int OPTION_PDFSETTINGS_SCREEN = 1; - public static final int OPTION_PDFSETTINGS_EBOOK = 2; - public static final int OPTION_PDFSETTINGS_PRINTER = 3; - public static final int OPTION_PDFSETTINGS_PREPRESS = 4; - - /** - * Define auto rotate pages behaviour. Can be OPTION_AUTOROTATEPAGES_NONE, - * OPTION_AUTOROTATEPAGES_ALL, OPTION_AUTOROTATEPAGES_PAGEBYPAGE or - * OPTION_AUTOROTATEPAGES_OFF (default). - */ - private int autoRotatePages = OPTION_AUTOROTATEPAGES_OFF; - - /** - * Define process color model. Can be OPTION_PROCESSCOLORMODEL_RGB, - * OPTION_PROCESSCOLORMODEL_GRAY or OPTION_PROCESSCOLORMODEL_CMYK. - */ - private int processColorModel; - - /** - * Define PDF settings to use. Can be OPTION_PDFSETTINGS_DEFAULT, - * OPTION_PDFSETTINGS_SCREEN, OPTION_PDFSETTINGS_EBOOK, - * OPTION_PDFSETTINGS_PRINTER or OPTION_PDFSETTINGS_PREPRESS. - */ - private int PDFSettings; - - /** - * Define PDF version compatibility level (default is "1.4"). - */ - private String compatibilityLevel = "1.4"; - - /** - * Enable PDFX generation (default is false). - */ - private boolean PDFX = false; - - /** - * Define standard paper size for the generated PDF file. This parameter is - * ignored if a paper size is provided in the input file. Default value is - * "letter". - */ - private PaperSize paperSize = PaperSize.LETTER; - - public PDFConverter() { - - // set supported classes - supportedDocumentClasses = new Class[1]; - supportedDocumentClasses[0] = PSDocument.class; - } - - /** - * Main method used to start the converter in standalone 'slave mode'. - * - * @param args - * @throws ConverterException - */ - public static void main(String args[]) throws ConverterException { - - startRemoteConverter(new PDFConverter()); - } - - /** - * Run method called to perform the actual process of the converter. - * - * @param document - * @param outputStream - * @throws IOException - * @throws ConverterException - * @throws DocumentException - */ - @Override - public void run(Document document, OutputStream outputStream) - throws IOException, ConverterException, DocumentException { - - // if no output = nothing to do - if (outputStream == null) { - return; + public static final int OPTION_AUTOROTATEPAGES_NONE = 0; + public static final int OPTION_AUTOROTATEPAGES_ALL = 1; + public static final int OPTION_AUTOROTATEPAGES_PAGEBYPAGE = 2; + public static final int OPTION_AUTOROTATEPAGES_OFF = 3; + + public static final int OPTION_PROCESSCOLORMODEL_RGB = 0; + public static final int OPTION_PROCESSCOLORMODEL_GRAY = 1; + public static final int OPTION_PROCESSCOLORMODEL_CMYK = 2; + + public static final int OPTION_PDFSETTINGS_DEFAULT = 0; + public static final int OPTION_PDFSETTINGS_SCREEN = 1; + public static final int OPTION_PDFSETTINGS_EBOOK = 2; + public static final int OPTION_PDFSETTINGS_PRINTER = 3; + public static final int OPTION_PDFSETTINGS_PREPRESS = 4; + + /** + * Logger name. + */ + private static final String LOGGER_NAME = Ghostscript.class.getName(); + + /** + * Define auto rotate pages behaviour. Can be OPTION_AUTOROTATEPAGES_NONE, + * OPTION_AUTOROTATEPAGES_ALL, OPTION_AUTOROTATEPAGES_PAGEBYPAGE or + * OPTION_AUTOROTATEPAGES_OFF (default). + */ + private int autoRotatePages = OPTION_AUTOROTATEPAGES_OFF; + + /** + * Define process color model. Can be OPTION_PROCESSCOLORMODEL_RGB, + * OPTION_PROCESSCOLORMODEL_GRAY or OPTION_PROCESSCOLORMODEL_CMYK. + */ + private int processColorModel; + + /** + * Define PDF settings to use. Can be OPTION_PDFSETTINGS_DEFAULT, + * OPTION_PDFSETTINGS_SCREEN, OPTION_PDFSETTINGS_EBOOK, + * OPTION_PDFSETTINGS_PRINTER or OPTION_PDFSETTINGS_PREPRESS. + */ + private int PDFSettings; + + /** + * Define PDF version compatibility level (default is "1.4"). + */ + private String compatibilityLevel = "1.4"; + + /** + * Enable PDFX generation (default is false). + */ + private boolean PDFX = false; + + /** + * Define standard paper size for the generated PDF file. This parameter is + * ignored if a paper size is provided in the input file. Default value is + * "letter". + */ + private PaperSize paperSize = PaperSize.LETTER; + + public PDFConverter() { + + // set supported classes + supportedDocumentClasses = new Class[1]; + supportedDocumentClasses[0] = PSDocument.class; } - // assert document is supported - this.assertDocumentSupported(document); + /** + * Main method used to start the converter in standalone 'slave mode'. + * + * @param args + * @throws ConverterException + */ + public static void main(String args[]) throws ConverterException { - // get Ghostscript instance - Ghostscript gs = Ghostscript.getInstance(); + startRemoteConverter(new PDFConverter()); + } - // generate a unique diskstore key - DiskStore diskStore = DiskStore.getInstance(); - String diskStoreKey = diskStore.generateUniqueKey(); + /** + * Run method called to perform the actual process of the converter. + * + * @param document + * @param outputStream + * @throws IOException + * @throws ConverterException + * @throws DocumentException + */ + @Override + public void run(Document document, OutputStream outputStream) + throws IOException, ConverterException, DocumentException { + + // if no output = nothing to do + if (outputStream == null) { + return; + } + + // assert document is supported + this.assertDocumentSupported(document); + + // get Ghostscript instance + Ghostscript gs = Ghostscript.getInstance(); + + // generate a unique diskstore key + DiskStore diskStore = DiskStore.getInstance(); + String diskStoreKey = diskStore.generateUniqueKey(); + + // prepare Ghostscript interpreter parameters + int argCount = 15; + if (autoRotatePages != OPTION_AUTOROTATEPAGES_OFF) { + argCount++; + } + String[] gsArgs = new String[argCount]; + + gsArgs[0] = "-ps2pdf"; // the first argument in the arg array is ignored + gsArgs[1] = "-dNOPAUSE"; + gsArgs[2] = "-dBATCH"; + gsArgs[3] = "-dSAFER"; + + int paramPosition = 3; + + // autorotatepages + switch (autoRotatePages) { + case OPTION_AUTOROTATEPAGES_NONE: + paramPosition++; + gsArgs[paramPosition] = "-dAutoRotatePages=/None"; + break; + case OPTION_AUTOROTATEPAGES_ALL: + paramPosition++; + gsArgs[paramPosition] = "-dAutoRotatePages=/All"; + break; + case OPTION_AUTOROTATEPAGES_PAGEBYPAGE: + paramPosition++; + gsArgs[paramPosition] = "-dAutoRotatePages=/PageByPage"; + break; + default: + // nothing + break; + + } + + // processcolormodel + paramPosition++; + switch (processColorModel) { + case OPTION_PROCESSCOLORMODEL_CMYK: + gsArgs[paramPosition] = "-dProcessColorModel=/DeviceCMYK"; + break; + case OPTION_PROCESSCOLORMODEL_GRAY: + gsArgs[paramPosition] = "-dProcessColorModel=/DeviceGray"; + break; + default: + gsArgs[paramPosition] = "-dProcessColorModel=/DeviceRGB"; + } + + // pdf settings + paramPosition++; + switch (PDFSettings) { + case OPTION_PDFSETTINGS_EBOOK: + gsArgs[paramPosition] = "-dPDFSETTINGS=/ebook"; + break; + case OPTION_PDFSETTINGS_SCREEN: + gsArgs[paramPosition] = "-dPDFSETTINGS=/screen"; + break; + case OPTION_PDFSETTINGS_PRINTER: + gsArgs[paramPosition] = "-dPDFSETTINGS=/printer"; + break; + case OPTION_PDFSETTINGS_PREPRESS: + gsArgs[paramPosition] = "-dPDFSETTINGS=/prepress"; + break; + default: + gsArgs[paramPosition] = "-dPDFSETTINGS=/default"; + } + + // compatibilitylevel + paramPosition++; + gsArgs[paramPosition] = "-dCompatibilityLevel=" + compatibilityLevel; + + // PDFX + paramPosition++; + gsArgs[paramPosition] = "-dPDFX=" + PDFX; + + // papersize + paramPosition++; + gsArgs[paramPosition] = "-dDEVICEWIDTHPOINTS=" + paperSize.getWidth(); + paramPosition++; + gsArgs[paramPosition] = "-dDEVICEHEIGHTPOINTS=" + paperSize.getHeight(); + + paramPosition++; + gsArgs[paramPosition] = "-sDEVICE=pdfwrite"; + // output to file, as stdout redirect does not work properly + paramPosition++; + gsArgs[paramPosition] = "-sOutputFile=" + + diskStore.addFile(diskStoreKey).getAbsolutePath(); + paramPosition++; + gsArgs[paramPosition] = "-q"; + paramPosition++; + gsArgs[paramPosition] = "-f"; + paramPosition++; + gsArgs[paramPosition] = "-"; + + InputStream is = new ByteArrayInputStream(document.getContent()); + + try { + + // execute and exit interpreter + synchronized (gs) { + Logger logger = LoggerFactory.getLogger(LOGGER_NAME); + logger.debug("Arguments to gs:"); + + for (String arg : gsArgs) { + logger.debug(arg); + } + + gs.setStdIn(is); + gs.initialize(gsArgs); + } + + // write obtained file to output stream + File outputFile = diskStore.getFile(diskStoreKey); + if (outputFile == null) { + throw new ConverterException("Cannot retrieve file with key " + + diskStoreKey + " from disk store"); + } + + FileInputStream fis = new FileInputStream(outputFile); + byte[] content = new byte[(int) outputFile.length()]; + fis.read(content); + fis.close(); + + outputStream.write(content); + + } catch (GhostscriptException e) { + + throw new ConverterException(e); + + } finally { + + IOUtils.closeQuietly(is); + + // delete Ghostscript instance + try { + Ghostscript.deleteInstance(); + } catch (GhostscriptException e) { + throw new ConverterException(e); + } + + // remove temporary file + diskStore.removeFile(diskStoreKey); + } - // prepare Ghostscript interpreter parameters - int argCount = 15; - if (autoRotatePages != OPTION_AUTOROTATEPAGES_OFF) { - argCount++; } - String[] gsArgs = new String[argCount]; - - gsArgs[0] = "-ps2pdf"; - gsArgs[1] = "-dNOPAUSE"; - gsArgs[2] = "-dBATCH"; - gsArgs[3] = "-dSAFER"; - - int paramPosition = 3; - - // autorotatepages - switch (autoRotatePages) { - case OPTION_AUTOROTATEPAGES_NONE: - paramPosition++; - gsArgs[paramPosition] = "-dAutoRotatePages=/None"; - break; - case OPTION_AUTOROTATEPAGES_ALL: - paramPosition++; - gsArgs[paramPosition] = "-dAutoRotatePages=/All"; - break; - case OPTION_AUTOROTATEPAGES_PAGEBYPAGE: - paramPosition++; - gsArgs[paramPosition] = "-dAutoRotatePages=/PageByPage"; - break; - default: - // nothing - break; + public int getAutoRotatePages() { + return autoRotatePages; } - // processcolormodel - paramPosition++; - switch (processColorModel) { - case OPTION_PROCESSCOLORMODEL_CMYK: - gsArgs[paramPosition] = "-dProcessColorModel=/DeviceCMYK"; - break; - case OPTION_PROCESSCOLORMODEL_GRAY: - gsArgs[paramPosition] = "-dProcessColorModel=/DeviceGray"; - break; - default: - gsArgs[paramPosition] = "-dProcessColorModel=/DeviceRGB"; + public void setAutoRotatePages(int autoRotatePages) { + this.autoRotatePages = autoRotatePages; } - // pdf settings - paramPosition++; - switch (PDFSettings) { - case OPTION_PDFSETTINGS_EBOOK: - gsArgs[paramPosition] = "-dPDFSETTINGS=/ebook"; - break; - case OPTION_PDFSETTINGS_SCREEN: - gsArgs[paramPosition] = "-dPDFSETTINGS=/screen"; - break; - case OPTION_PDFSETTINGS_PRINTER: - gsArgs[paramPosition] = "-dPDFSETTINGS=/printer"; - break; - case OPTION_PDFSETTINGS_PREPRESS: - gsArgs[paramPosition] = "-dPDFSETTINGS=/prepress"; - break; - default: - gsArgs[paramPosition] = "-dPDFSETTINGS=/default"; + public int getProcessColorModel() { + return processColorModel; } - // compatibilitylevel - paramPosition++; - gsArgs[paramPosition] = "-dCompatibilityLevel=" + compatibilityLevel; - - // PDFX - paramPosition++; - gsArgs[paramPosition] = "-dPDFX=" + PDFX; - - // papersize - paramPosition++; - gsArgs[paramPosition] = "-dDEVICEWIDTHPOINTS=" + paperSize.getWidth(); - paramPosition++; - gsArgs[paramPosition] = "-dDEVICEHEIGHTPOINTS=" + paperSize.getHeight(); - - paramPosition++; - gsArgs[paramPosition] = "-sDEVICE=pdfwrite"; - // output to file, as stdout redirect does not work properly - paramPosition++; - gsArgs[paramPosition] = "-sOutputFile=" - + diskStore.addFile(diskStoreKey).getAbsolutePath(); - paramPosition++; - gsArgs[paramPosition] = "-q"; - paramPosition++; - gsArgs[paramPosition] = "-f"; - paramPosition++; - gsArgs[paramPosition] = "-"; - - InputStream is = new ByteArrayInputStream(document.getContent()); - - try { - - // execute and exit interpreter - synchronized (gs) { - gs.setStdIn(is); - gs.initialize(gsArgs); - - } - - // write obtained file to output stream - File outputFile = diskStore.getFile(diskStoreKey); - if (outputFile == null) { - throw new ConverterException("Cannot retrieve file with key " - + diskStoreKey + " from disk store"); - } - - FileInputStream fis = new FileInputStream(outputFile); - byte[] content = new byte[(int) outputFile.length()]; - fis.read(content); - fis.close(); - - outputStream.write(content); - - } catch (GhostscriptException e) { - - throw new ConverterException(e); - - } finally { - - IOUtils.closeQuietly(is); - - // delete Ghostscript instance - try { - Ghostscript.deleteInstance(); - } catch (GhostscriptException e) { - throw new ConverterException(e); - } - - // remove temporary file - diskStore.removeFile(diskStoreKey); + public void setProcessColorModel(int processColorModel) { + this.processColorModel = processColorModel; } - } - - public int getAutoRotatePages() { - return autoRotatePages; - } - - public void setAutoRotatePages(int autoRotatePages) { - this.autoRotatePages = autoRotatePages; - } - - public int getProcessColorModel() { - return processColorModel; - } - - public void setProcessColorModel(int processColorModel) { - this.processColorModel = processColorModel; - } - - public String getCompatibilityLevel() { - return compatibilityLevel; - } + public String getCompatibilityLevel() { + return compatibilityLevel; + } - public void setCompatibilityLevel(String compatibilityLevel) { - this.compatibilityLevel = compatibilityLevel; - } + public void setCompatibilityLevel(String compatibilityLevel) { + this.compatibilityLevel = compatibilityLevel; + } - public int getPDFSettings() { - return PDFSettings; - } + public int getPDFSettings() { + return PDFSettings; + } - public void setPDFSettings(int PDFSettings) { - this.PDFSettings = PDFSettings; - } + public void setPDFSettings(int PDFSettings) { + this.PDFSettings = PDFSettings; + } - public boolean isPDFX() { - return PDFX; - } + public boolean isPDFX() { + return PDFX; + } - public void setPDFX(boolean PDFX) { - this.PDFX = PDFX; - } + public void setPDFX(boolean PDFX) { + this.PDFX = PDFX; + } - public PaperSize getPaperSize() { - return paperSize; - } + public PaperSize getPaperSize() { + return paperSize; + } - public void setPaperSize(PaperSize paperSize) { - this.paperSize = paperSize; - } + public void setPaperSize(PaperSize paperSize) { + this.paperSize = paperSize; + } - public void setPaperSize(String paperSizeName) { + public void setPaperSize(String paperSizeName) { - PaperSize found = PaperSize.getStandardPaperSize(paperSizeName); - if (found != null) { - this.setPaperSize(found); + PaperSize found = PaperSize.getStandardPaperSize(paperSizeName); + if (found != null) { + this.setPaperSize(found); + } } - } } diff --git a/src/main/java/org/ghost4j/converter/PSConverter.java b/src/main/java/org/ghost4j/converter/PSConverter.java index 4c7e930..511ae85 100644 --- a/src/main/java/org/ghost4j/converter/PSConverter.java +++ b/src/main/java/org/ghost4j/converter/PSConverter.java @@ -27,177 +27,177 @@ */ public class PSConverter extends AbstractRemoteConverter { - public static final int OPTION_DEVICE_AUTO = 0; - public static final int OPTION_DEVICE_PSWRITE = 1; - public static final int OPTION_DEVICE_PS2WRITE = 2; - - /** - * Ghostscript device to use to perform conversion. - */ - private int device = 0; - - /** - * PostScript language level: 1, 2 or 3. - */ - private int languageLevel = 3; - - /** - * Define standard paper size for the generated PDF file. This parameter is - * ignored if a paper size is provided in the input file. Default value is - * "letter". - */ - private PaperSize paperSize = PaperSize.LETTER; - - public PSConverter() { - - // set supported classes - supportedDocumentClasses = new Class[2]; - supportedDocumentClasses[0] = PSDocument.class; - supportedDocumentClasses[1] = PDFDocument.class; - } - - /** - * Main method used to start the converter in standalone 'slave mode'. - * - * @param args - * @throws ConverterException - */ - public static void main(String args[]) throws ConverterException { - - startRemoteConverter(new PSConverter()); - } - - @Override - public void run(Document document, OutputStream outputStream) - throws IOException, ConverterException, DocumentException { - - // if no output = nothing to do - if (outputStream == null) { - return; + public static final int OPTION_DEVICE_AUTO = 0; + public static final int OPTION_DEVICE_PSWRITE = 1; + public static final int OPTION_DEVICE_PS2WRITE = 2; + + /** + * Ghostscript device to use to perform conversion. + */ + private int device = 0; + + /** + * PostScript language level: 1, 2 or 3. + */ + private int languageLevel = 3; + + /** + * Define standard paper size for the generated PDF file. This parameter is + * ignored if a paper size is provided in the input file. Default value is + * "letter". + */ + private PaperSize paperSize = PaperSize.LETTER; + + public PSConverter() { + + // set supported classes + supportedDocumentClasses = new Class[2]; + supportedDocumentClasses[0] = PSDocument.class; + supportedDocumentClasses[1] = PDFDocument.class; } - // assert document is supported - this.assertDocumentSupported(document); - - // determine device to use - String deviceName = ""; - try { - switch (device) { - case OPTION_DEVICE_PSWRITE: - deviceName = "pswrite"; - break; - case OPTION_DEVICE_PS2WRITE: - deviceName = "ps2write"; - break; - case OPTION_DEVICE_AUTO: - // automatic : use ps2write is available otherwise pswrite - if (this.isDeviceSupported("ps2write")) { - deviceName = "ps2write"; - } else { - deviceName = "pswrite"; - } - break; - default: - deviceName = "pswrite"; - break; - } - } catch (GhostscriptException e) { - throw new ConverterException(e); + /** + * Main method used to start the converter in standalone 'slave mode'. + * + * @param args + * @throws ConverterException + */ + public static void main(String args[]) throws ConverterException { + + startRemoteConverter(new PSConverter()); } - // get Ghostscript instance - Ghostscript gs = Ghostscript.getInstance(); - - // generate a unique diskstore key for output file - DiskStore diskStore = DiskStore.getInstance(); - String outputDiskStoreKey = diskStore.generateUniqueKey(); - // generate a unique diskstore key for input file - String inputDiskStoreKey = diskStore.generateUniqueKey(); - // write document to input file - document.write(diskStore.addFile(inputDiskStoreKey)); - - // prepare Ghostscript interpreter parameters - String[] gsArgs = { - // dummy value to prevent interpreter from blocking - "-psconv", - "-dNOPAUSE", - "-dBATCH", - "-dSAFER", - "-dLanguageLevel=" + languageLevel, - "-dDEVICEWIDTHPOINTS=" + paperSize.getWidth(), - "-dDEVICEHEIGHTPOINTS=" + paperSize.getHeight(), - "-sDEVICE=" + deviceName, - // output to file, as stdout redirect does not work properly - "-sOutputFile=" - + diskStore.addFile(outputDiskStoreKey) + @Override + public void run(Document document, OutputStream outputStream) + throws IOException, ConverterException, DocumentException { + + // if no output = nothing to do + if (outputStream == null) { + return; + } + + // assert document is supported + this.assertDocumentSupported(document); + + // determine device to use + String deviceName = ""; + try { + switch (device) { + case OPTION_DEVICE_PSWRITE: + deviceName = "pswrite"; + break; + case OPTION_DEVICE_PS2WRITE: + deviceName = "ps2write"; + break; + case OPTION_DEVICE_AUTO: + // automatic : use ps2write is available otherwise pswrite + if (this.isDeviceSupported("ps2write")) { + deviceName = "ps2write"; + } else { + deviceName = "pswrite"; + } + break; + default: + deviceName = "pswrite"; + break; + } + } catch (GhostscriptException e) { + throw new ConverterException(e); + } + + // get Ghostscript instance + Ghostscript gs = Ghostscript.getInstance(); + + // generate a unique diskstore key for output file + DiskStore diskStore = DiskStore.getInstance(); + String outputDiskStoreKey = diskStore.generateUniqueKey(); + // generate a unique diskstore key for input file + String inputDiskStoreKey = diskStore.generateUniqueKey(); + // write document to input file + document.write(diskStore.addFile(inputDiskStoreKey)); + + // prepare Ghostscript interpreter parameters + String[] gsArgs = { + // dummy value to prevent interpreter from blocking + "-psconv", + "-dNOPAUSE", + "-dBATCH", + "-dSAFER", + "-dLanguageLevel=" + languageLevel, + "-dDEVICEWIDTHPOINTS=" + paperSize.getWidth(), + "-dDEVICEHEIGHTPOINTS=" + paperSize.getHeight(), + "-sDEVICE=" + deviceName, + // output to file, as stdout redirect does not work properly + "-sOutputFile=" + + diskStore.addFile(outputDiskStoreKey) .getAbsolutePath(), "-q", "-f", - // read from a file as stdin redirect does not work properly - // with PDF file as input - diskStore.getFile(inputDiskStoreKey).getAbsolutePath() }; + // read from a file as stdin redirect does not work properly + // with PDF file as input + diskStore.getFile(inputDiskStoreKey).getAbsolutePath() }; - try { + try { - // execute and exit interpreter - synchronized (gs) { - gs.initialize(gsArgs); - gs.exit(); - } + // execute and exit interpreter + synchronized (gs) { + gs.initialize(gsArgs); + gs.exit(); + } - // write obtained file to output stream - File outputFile = diskStore.getFile(outputDiskStoreKey); - if (outputFile == null) { - throw new ConverterException("Cannot retrieve file with key " - + outputDiskStoreKey + " from disk store"); - } + // write obtained file to output stream + File outputFile = diskStore.getFile(outputDiskStoreKey); + if (outputFile == null) { + throw new ConverterException("Cannot retrieve file with key " + + outputDiskStoreKey + " from disk store"); + } - FileInputStream fis = new FileInputStream(outputFile); - byte[] content = new byte[(int) outputFile.length()]; - fis.read(content); - fis.close(); + FileInputStream fis = new FileInputStream(outputFile); + byte[] content = new byte[(int) outputFile.length()]; + fis.read(content); + fis.close(); - outputStream.write(content); + outputStream.write(content); - } catch (GhostscriptException e) { + } catch (GhostscriptException e) { - throw new ConverterException(e); + throw new ConverterException(e); - } finally { + } finally { - // delete Ghostscript instance - try { - Ghostscript.deleteInstance(); - } catch (GhostscriptException e) { - throw new ConverterException(e); - } + // delete Ghostscript instance + try { + Ghostscript.deleteInstance(); + } catch (GhostscriptException e) { + throw new ConverterException(e); + } - // remove temporary files - diskStore.removeFile(outputDiskStoreKey); - diskStore.removeFile(inputDiskStoreKey); - } + // remove temporary files + diskStore.removeFile(outputDiskStoreKey); + diskStore.removeFile(inputDiskStoreKey); + } - } + } - public int getLanguageLevel() { - return languageLevel; - } + public int getLanguageLevel() { + return languageLevel; + } - public void setLanguageLevel(int languageLevel) { - this.languageLevel = languageLevel; - } + public void setLanguageLevel(int languageLevel) { + this.languageLevel = languageLevel; + } - public PaperSize getPaperSize() { - return paperSize; - } + public PaperSize getPaperSize() { + return paperSize; + } - public void setPaperSize(PaperSize paperSize) { - this.paperSize = paperSize; - } + public void setPaperSize(PaperSize paperSize) { + this.paperSize = paperSize; + } - public int getDevice() { - return device; - } + public int getDevice() { + return device; + } - public void setDevice(int device) { - this.device = device; - } + public void setDevice(int device) { + this.device = device; + } } diff --git a/src/main/java/org/ghost4j/converter/RemoteConverter.java b/src/main/java/org/ghost4j/converter/RemoteConverter.java index a5fd206..7656f3f 100644 --- a/src/main/java/org/ghost4j/converter/RemoteConverter.java +++ b/src/main/java/org/ghost4j/converter/RemoteConverter.java @@ -14,11 +14,11 @@ */ public interface RemoteConverter extends Converter { - /** - * Sets max parallel conversion processes allowed for the converter - * - * @param maxProcessCount - */ - public void setMaxProcessCount(int maxProcessCount); + /** + * Sets max parallel conversion processes allowed for the converter + * + * @param maxProcessCount + */ + public void setMaxProcessCount(int maxProcessCount); } diff --git a/src/main/java/org/ghost4j/display/DisplayCallback.java b/src/main/java/org/ghost4j/display/DisplayCallback.java index eb0f5bb..4cf59e2 100644 --- a/src/main/java/org/ghost4j/display/DisplayCallback.java +++ b/src/main/java/org/ghost4j/display/DisplayCallback.java @@ -24,106 +24,106 @@ */ public interface DisplayCallback { - /** - * Method called when new device has been opened. This is the first event - * from this device. - * - * @throws org.ghost4j.GhostscriptException - */ - public void displayOpen() throws GhostscriptException; + /** + * Method called when new device has been opened. This is the first event + * from this device. + * + * @throws org.ghost4j.GhostscriptException + */ + public void displayOpen() throws GhostscriptException; - /** - * Method called when device is about to be closed. Device will not be - * closed until this function returns. - * - * @throws org.ghost4j.GhostscriptException - */ - public void displayPreClose() throws GhostscriptException; + /** + * Method called when device is about to be closed. Device will not be + * closed until this function returns. + * + * @throws org.ghost4j.GhostscriptException + */ + public void displayPreClose() throws GhostscriptException; - /** - * Method called when device has been closed. This is the last event from - * this device. - * - * @throws org.ghost4j.GhostscriptException - */ - public void displayClose() throws GhostscriptException; + /** + * Method called when device has been closed. This is the last event from + * this device. + * + * @throws org.ghost4j.GhostscriptException + */ + public void displayClose() throws GhostscriptException; - /** - * Method called when device is about to be resized. - * - * @param width - * Width - * @param height - * Height - * @param raster - * Raster - * @param format - * Format - * @throws org.ghost4j.GhostscriptException - */ - public void displayPreSize(int width, int height, int raster, int format) - throws GhostscriptException; + /** + * Method called when device is about to be resized. + * + * @param width + * Width + * @param height + * Height + * @param raster + * Raster + * @param format + * Format + * @throws org.ghost4j.GhostscriptException + */ + public void displayPreSize(int width, int height, int raster, int format) + throws GhostscriptException; - /** - * Method called when device has been resized. - * - * @param width - * Width - * @param height - * Height - * @param raster - * Raster - * @param format - * Format - * @throws org.ghost4j.GhostscriptException - */ - public void displaySize(int width, int height, int raster, int format) - throws GhostscriptException; + /** + * Method called when device has been resized. + * + * @param width + * Width + * @param height + * Height + * @param raster + * Raster + * @param format + * Format + * @throws org.ghost4j.GhostscriptException + */ + public void displaySize(int width, int height, int raster, int format) + throws GhostscriptException; - /** - * Method called on page flush. - * - * @throws org.ghost4j.GhostscriptException - */ - public void displaySync() throws GhostscriptException; + /** + * Method called on page flush. + * + * @throws org.ghost4j.GhostscriptException + */ + public void displaySync() throws GhostscriptException; - /** - * Method called on show page. - * - * @param width - * Width - * @param height - * Height - * @param raster - * Raster - * @param format - * Format - * @param copies - * Copies - * @param flush - * Flush - * @param imageData - * Byte array representing image data. Data layout and order is - * controlled by the -dDisplayFormat argument. - * @throws org.ghost4j.GhostscriptException - */ - public void displayPage(int width, int height, int raster, int format, - int copies, int flush, byte[] imageData) - throws GhostscriptException; + /** + * Method called on show page. + * + * @param width + * Width + * @param height + * Height + * @param raster + * Raster + * @param format + * Format + * @param copies + * Copies + * @param flush + * Flush + * @param imageData + * Byte array representing image data. Data layout and order is + * controlled by the -dDisplayFormat argument. + * @throws org.ghost4j.GhostscriptException + */ + public void displayPage(int width, int height, int raster, int format, + int copies, int flush, byte[] imageData) + throws GhostscriptException; - /** - * Method called to notify whenever a portion of the raster is updated. - * - * @param x - * X coordinate - * @param y - * Y coordinate - * @param width - * Width - * @param height - * Height - * @throws org.ghost4j.GhostscriptException - */ - public void displayUpdate(int x, int y, int width, int height) - throws GhostscriptException; + /** + * Method called to notify whenever a portion of the raster is updated. + * + * @param x + * X coordinate + * @param y + * Y coordinate + * @param width + * Width + * @param height + * Height + * @throws org.ghost4j.GhostscriptException + */ + public void displayUpdate(int x, int y, int width, int height) + throws GhostscriptException; } diff --git a/src/main/java/org/ghost4j/display/DisplayData.java b/src/main/java/org/ghost4j/display/DisplayData.java index 69a6922..0e4d056 100644 --- a/src/main/java/org/ghost4j/display/DisplayData.java +++ b/src/main/java/org/ghost4j/display/DisplayData.java @@ -15,49 +15,49 @@ */ public class DisplayData { - private int width; - private int height; - private int raster; - private int format; - private Pointer pimage; - - public int getWidth() { - return width; - } - - public void setWidth(int width) { - this.width = width; - } - - public int getHeight() { - return height; - } - - public void setHeight(int height) { - this.height = height; - } - - public int getRaster() { - return raster; - } - - public void setRaster(int raster) { - this.raster = raster; - } - - public int getFormat() { - return format; - } - - public void setFormat(int format) { - this.format = format; - } - - public Pointer getPimage() { - return pimage; - } - - public void setPimage(Pointer pimage) { - this.pimage = pimage; - } + private int width; + private int height; + private int raster; + private int format; + private Pointer pimage; + + public int getWidth() { + return width; + } + + public void setWidth(int width) { + this.width = width; + } + + public int getHeight() { + return height; + } + + public void setHeight(int height) { + this.height = height; + } + + public int getRaster() { + return raster; + } + + public void setRaster(int raster) { + this.raster = raster; + } + + public int getFormat() { + return format; + } + + public void setFormat(int format) { + this.format = format; + } + + public Pointer getPimage() { + return pimage; + } + + public void setPimage(Pointer pimage) { + this.pimage = pimage; + } } diff --git a/src/main/java/org/ghost4j/display/ImageWriterDisplayCallback.java b/src/main/java/org/ghost4j/display/ImageWriterDisplayCallback.java index 9b3a6f4..30840b3 100644 --- a/src/main/java/org/ghost4j/display/ImageWriterDisplayCallback.java +++ b/src/main/java/org/ghost4j/display/ImageWriterDisplayCallback.java @@ -21,67 +21,67 @@ */ public class ImageWriterDisplayCallback implements DisplayCallback { - /** - * Holds document images. - */ - private List images; + /** + * Holds document images. + */ + private List images; - /** - * Constructor. - */ - public ImageWriterDisplayCallback() { - images = new ArrayList(); - } + /** + * Constructor. + */ + public ImageWriterDisplayCallback() { + images = new ArrayList(); + } - public void displayOpen() throws GhostscriptException { + public void displayOpen() throws GhostscriptException { - } + } - public void displayPreClose() throws GhostscriptException { + public void displayPreClose() throws GhostscriptException { - } + } - public void displayClose() throws GhostscriptException { + public void displayClose() throws GhostscriptException { - } + } - public void displayPreSize(int width, int height, int raster, int format) - throws GhostscriptException { + public void displayPreSize(int width, int height, int raster, int format) + throws GhostscriptException { - } + } - public void displaySize(int width, int height, int raster, int format) - throws GhostscriptException { + public void displaySize(int width, int height, int raster, int format) + throws GhostscriptException { - } + } - public void displaySync() throws GhostscriptException { + public void displaySync() throws GhostscriptException { - } + } - public void displayPage(int width, int height, int raster, int format, - int copies, int flush, byte[] imageData) - throws GhostscriptException { + public void displayPage(int width, int height, int raster, int format, + int copies, int flush, byte[] imageData) + throws GhostscriptException { - // create new raster - PageRaster pageRaster = new PageRaster(); - pageRaster.setWidth(width); - pageRaster.setHeight(height); - pageRaster.setRaster(raster); - pageRaster.setFormat(format); - pageRaster.setData(imageData); + // create new raster + PageRaster pageRaster = new PageRaster(); + pageRaster.setWidth(width); + pageRaster.setHeight(height); + pageRaster.setRaster(raster); + pageRaster.setFormat(format); + pageRaster.setData(imageData); - // convert to image and add to list - images.add(ImageUtil.converterPageRasterToImage(pageRaster)); + // convert to image and add to list + images.add(ImageUtil.converterPageRasterToImage(pageRaster)); - } + } - public void displayUpdate(int x, int y, int width, int height) - throws GhostscriptException { + public void displayUpdate(int x, int y, int width, int height) + throws GhostscriptException { - } + } - public List getImages() { - return images; - } + public List getImages() { + return images; + } } diff --git a/src/main/java/org/ghost4j/display/PageRaster.java b/src/main/java/org/ghost4j/display/PageRaster.java index a685007..2d35980 100644 --- a/src/main/java/org/ghost4j/display/PageRaster.java +++ b/src/main/java/org/ghost4j/display/PageRaster.java @@ -16,53 +16,53 @@ */ public class PageRaster implements Serializable { - /** - * Serial version UID. - */ - private static final long serialVersionUID = -977080307761838114L; - private int width; - private int height; - private int raster; - private int format; - private byte[] data; + /** + * Serial version UID. + */ + private static final long serialVersionUID = -977080307761838114L; + private int width; + private int height; + private int raster; + private int format; + private byte[] data; - public int getWidth() { - return width; - } + public int getWidth() { + return width; + } - public void setWidth(int width) { - this.width = width; - } + public void setWidth(int width) { + this.width = width; + } - public int getHeight() { - return height; - } + public int getHeight() { + return height; + } - public void setHeight(int height) { - this.height = height; - } + public void setHeight(int height) { + this.height = height; + } - public int getRaster() { - return raster; - } + public int getRaster() { + return raster; + } - public void setRaster(int raster) { - this.raster = raster; - } + public void setRaster(int raster) { + this.raster = raster; + } - public int getFormat() { - return format; - } + public int getFormat() { + return format; + } - public void setFormat(int format) { - this.format = format; - } + public void setFormat(int format) { + this.format = format; + } - public byte[] getData() { - return data; - } + public byte[] getData() { + return data; + } - public void setData(byte[] data) { - this.data = data; - } + public void setData(byte[] data) { + this.data = data; + } } diff --git a/src/main/java/org/ghost4j/display/PageRasterDisplayCallback.java b/src/main/java/org/ghost4j/display/PageRasterDisplayCallback.java index 82ae44b..c12ed8f 100644 --- a/src/main/java/org/ghost4j/display/PageRasterDisplayCallback.java +++ b/src/main/java/org/ghost4j/display/PageRasterDisplayCallback.java @@ -19,65 +19,65 @@ */ public class PageRasterDisplayCallback implements DisplayCallback { - private List rasters; + private List rasters; - /** - * Constructor - */ - public PageRasterDisplayCallback() { + /** + * Constructor + */ + public PageRasterDisplayCallback() { - rasters = new ArrayList(); - } + rasters = new ArrayList(); + } - public void displayClose() throws GhostscriptException { + public void displayClose() throws GhostscriptException { - } + } - public void displayOpen() throws GhostscriptException { + public void displayOpen() throws GhostscriptException { - } + } - public void displayPage(int width, int height, int raster, int format, - int copies, int flush, byte[] imageData) - throws GhostscriptException { + public void displayPage(int width, int height, int raster, int format, + int copies, int flush, byte[] imageData) + throws GhostscriptException { - // prepare new raster - PageRaster pageRaster = new PageRaster(); - pageRaster.setWidth(width); - pageRaster.setHeight(height); - pageRaster.setRaster(raster); - pageRaster.setFormat(format); - pageRaster.setData(imageData); + // prepare new raster + PageRaster pageRaster = new PageRaster(); + pageRaster.setWidth(width); + pageRaster.setHeight(height); + pageRaster.setRaster(raster); + pageRaster.setFormat(format); + pageRaster.setData(imageData); - // add it to the result list - rasters.add(pageRaster); + // add it to the result list + rasters.add(pageRaster); - } + } - public void displayPreClose() throws GhostscriptException { + public void displayPreClose() throws GhostscriptException { - } + } - public void displayPreSize(int width, int height, int raster, int format) - throws GhostscriptException { + public void displayPreSize(int width, int height, int raster, int format) + throws GhostscriptException { - } + } - public void displaySize(int width, int height, int raster, int format) - throws GhostscriptException { + public void displaySize(int width, int height, int raster, int format) + throws GhostscriptException { - } + } - public void displaySync() throws GhostscriptException { + public void displaySync() throws GhostscriptException { - } + } - public void displayUpdate(int x, int y, int width, int height) - throws GhostscriptException { - } + public void displayUpdate(int x, int y, int width, int height) + throws GhostscriptException { + } - public List getRasters() { - return rasters; - } + public List getRasters() { + return rasters; + } } diff --git a/src/main/java/org/ghost4j/document/AbstractDocument.java b/src/main/java/org/ghost4j/document/AbstractDocument.java index d82a1e9..b300922 100644 --- a/src/main/java/org/ghost4j/document/AbstractDocument.java +++ b/src/main/java/org/ghost4j/document/AbstractDocument.java @@ -29,124 +29,128 @@ */ public abstract class AbstractDocument implements Document, Serializable { - /** - * Serial version UID. - */ - private static final long serialVersionUID = -7160779330993730486L; - - /** - * Buffer size used while reading (loading) document content. - */ - public static final int READ_BUFFER_SIZE = 1024; - - /** - * Content of the document. - */ - protected byte[] content; - - public void load(File file) throws FileNotFoundException, IOException { + /** + * Serial version UID. + */ + private static final long serialVersionUID = -7160779330993730486L; + + /** + * Buffer size used while reading (loading) document content. + */ + public static final int READ_BUFFER_SIZE = 1024; + + /** + * Content of the document. + */ + protected byte[] content; + + public int load(File file) throws FileNotFoundException, IOException { + + FileInputStream fis = new FileInputStream(file); + int totalread = load(fis); + IOUtils.closeQuietly(fis); + return totalread; + } - FileInputStream fis = new FileInputStream(file); - load(fis); - IOUtils.closeQuietly(fis); - } + public int load(InputStream inputStream) throws IOException { - public void load(InputStream inputStream) throws IOException { + int totalread = 0; + byte[] buffer = new byte[READ_BUFFER_SIZE]; + ByteArrayOutputStream baos = new ByteArrayOutputStream(); - byte[] buffer = new byte[READ_BUFFER_SIZE]; - ByteArrayOutputStream baos = new ByteArrayOutputStream(); + int readCount = 0; + while ((readCount = inputStream.read(buffer)) > 0) { + baos.write(buffer, 0, readCount); + totalread+=readCount; + } + content = baos.toByteArray(); - int readCount = 0; - while ((readCount = inputStream.read(buffer)) > 0) { - baos.write(buffer, 0, readCount); + IOUtils.closeQuietly(baos); + return totalread; } - content = baos.toByteArray(); - IOUtils.closeQuietly(baos); - } + public void write(File file) throws IOException { - public void write(File file) throws IOException { + FileOutputStream fos = new FileOutputStream(file); + write(fos); + IOUtils.closeQuietly(fos); - FileOutputStream fos = new FileOutputStream(file); - write(fos); - IOUtils.closeQuietly(fos); + } - } + public void write(OutputStream outputStream) throws IOException { - public void write(OutputStream outputStream) throws IOException { + outputStream.write(content); - outputStream.write(content); + } - } + public int getSize() { - public int getSize() { + if (content == null) { + return 0; + } else { + return content.length; + } + } - if (content == null) { - return 0; - } else { - return content.length; + public byte[] getContent() { + return content; } - } - - public byte[] getContent() { - return content; - } - - /** - * Assert the given page index is valid for the current document. - * - * @param index - * Index to check - * @throws DocumentException - * Thrown if index is not valid - */ - protected void assertValidPageIndex(int index) throws DocumentException { - - if (content == null || index > this.getPageCount()) { - throw new DocumentException("Invalid page index: " + index); + + /** + * Assert the given page index is valid for the current document. + * + * @param index + * Index to check + * @throws DocumentException + * Thrown if index is not valid + */ + protected void assertValidPageIndex(int index) throws DocumentException { + + if (content == null || index > this.getPageCount()) { + throw new DocumentException("Invalid page index: " + index); + } } - } - - /** - * Assert the given page range is valid for the current document. - * - * @param begin - * Range begin index - * @param end - * Range end index - * @throws DocumentException - */ - protected void assertValidPageRange(int begin, int end) - throws DocumentException { - - this.assertValidPageIndex(begin); - this.assertValidPageIndex(end); - - if (begin > end) { - throw new DocumentException("Invalid page range: " + begin + " - " - + end); + + /** + * Assert the given page range is valid for the current document. + * + * @param begin + * Range begin index + * @param end + * Range end index + * @throws DocumentException + */ + protected void assertValidPageRange(int begin, int end) + throws DocumentException { + + this.assertValidPageIndex(begin); + this.assertValidPageIndex(end); + + if (begin > end) { + throw new DocumentException("Invalid page range: " + begin + " - " + + end); + } } - } - public void append(Document document) throws DocumentException { + public void append(Document document) throws DocumentException { - if (document == null || !this.getType().equals(document.getType())) { - throw new DocumentException( - "Cannot append document of different types"); + if (document == null || !this.getType().equals(document.getType())) { + throw new DocumentException( + "Cannot append document of different types"); + } } - } - public List explode() throws DocumentException { + public List explode() throws DocumentException { - List result = new ArrayList(); + List result = new ArrayList(); - int pageCount = this.getPageCount(); + int pageCount = this.getPageCount(); - for (int i = 0; i < pageCount; i++) { - result.add(this.extract(i + 1, i + 1)); - } + for (int i = 0; i < pageCount; i++) { + result.add(this.extract(i + 1, i + 1)); + } - return result; - } + return result; + } } diff --git a/src/main/java/org/ghost4j/document/Document.java b/src/main/java/org/ghost4j/document/Document.java index 9bd3a83..8f24293 100644 --- a/src/main/java/org/ghost4j/document/Document.java +++ b/src/main/java/org/ghost4j/document/Document.java @@ -21,98 +21,98 @@ */ public interface Document { - public static final String TYPE_POSTSCRIPT = "PostScript"; - public static final String TYPE_PDF = "PDF"; + public static final String TYPE_POSTSCRIPT = "PostScript"; + public static final String TYPE_PDF = "PDF"; - /** - * Load document from a File. - * - * @param file - * File. - * @throws FileNotFoundException - * @throws IOException - */ - public void load(File file) throws FileNotFoundException, IOException; + /** + * Load document from a File. + * + * @param file + * File. + * @throws FileNotFoundException + * @throws IOException + */ + public int load(File file) throws FileNotFoundException, IOException; - /** - * Load document from an InputStream. - * - * @param inputStream - * @throws IOException - */ - public void load(InputStream inputStream) throws IOException; + /** + * Load document from an InputStream. + * + * @param inputStream + * @throws IOException + */ + public int load(InputStream inputStream) throws IOException; - /** - * Write document to a File. - * - * @param file - * File. - * @throws IOException - */ - public void write(File file) throws IOException; + /** + * Write document to a File. + * + * @param file + * File. + * @throws IOException + */ + public void write(File file) throws IOException; - /** - * Write document to an OutputStream - * - * @param outputStream - * @throws IOException - */ - public void write(OutputStream outputStream) throws IOException; + /** + * Write document to an OutputStream + * + * @param outputStream + * @throws IOException + */ + public void write(OutputStream outputStream) throws IOException; - /** - * Return document page count - * - * @return Number of pages. - */ - public int getPageCount() throws DocumentException; + /** + * Return document page count + * + * @return Number of pages. + */ + public int getPageCount() throws DocumentException; - /** - * Return the type of the document. - * - * @return A String representing the document type name. - */ - public String getType(); + /** + * Return the type of the document. + * + * @return A String representing the document type name. + */ + public String getType(); - /** - * Return document size - * - * @return Document size in bytes. - */ - public int getSize(); + /** + * Return document size + * + * @return Document size in bytes. + */ + public int getSize(); - /** - * Return document content as a byte array - * - * @return Byte array - */ - public byte[] getContent(); + /** + * Return document content as a byte array + * + * @return Byte array + */ + public byte[] getContent(); - /** - * Return a new document containing pages of a given range. Note : begin and - * end indicies start at 1 - * - * @param begin - * Index of the first page to extract - * @param end - * Index of the last page to extract - * @return A new document. - */ - public Document extract(int begin, int end) throws DocumentException; + /** + * Return a new document containing pages of a given range. Note : begin and + * end indicies start at 1 + * + * @param begin + * Index of the first page to extract + * @param end + * Index of the last page to extract + * @return A new document. + */ + public Document extract(int begin, int end) throws DocumentException; - /** - * Append pages of another document to the current document. - * - * @param document - * Document ot append - * @throws DocumentException - */ - public void append(Document document) throws DocumentException; + /** + * Append pages of another document to the current document. + * + * @param document + * Document ot append + * @throws DocumentException + */ + public void append(Document document) throws DocumentException; - /** - * Separate each pages to a new document. - * - * @return A list of Document. - * @throws DocumentException - */ - public List explode() throws DocumentException; + /** + * Separate each pages to a new document. + * + * @return A list of Document. + * @throws DocumentException + */ + public List explode() throws DocumentException; } diff --git a/src/main/java/org/ghost4j/document/DocumentException.java b/src/main/java/org/ghost4j/document/DocumentException.java index f09d7fe..7134cdb 100644 --- a/src/main/java/org/ghost4j/document/DocumentException.java +++ b/src/main/java/org/ghost4j/document/DocumentException.java @@ -15,25 +15,25 @@ */ public class DocumentException extends Exception { - /** - * Serial version UID. - */ - private static final long serialVersionUID = 3773482793220746656L; - - public DocumentException() { - super(); - } - - public DocumentException(String message) { - super(message); - } - - public DocumentException(Throwable cause) { - super(cause); - } - - public DocumentException(String message, Throwable cause) { - super(message, cause); - } + /** + * Serial version UID. + */ + private static final long serialVersionUID = 3773482793220746656L; + + public DocumentException() { + super(); + } + + public DocumentException(String message) { + super(message); + } + + public DocumentException(Throwable cause) { + super(cause); + } + + public DocumentException(String message, Throwable cause) { + super(message, cause); + } } diff --git a/src/main/java/org/ghost4j/document/PDFDocument.java b/src/main/java/org/ghost4j/document/PDFDocument.java index 113bb20..bc53aa2 100644 --- a/src/main/java/org/ghost4j/document/PDFDocument.java +++ b/src/main/java/org/ghost4j/document/PDFDocument.java @@ -26,160 +26,162 @@ */ public class PDFDocument extends AbstractDocument { - /** - * Serial version UID. - */ - private static final long serialVersionUID = 6331191005700202153L; - - @Override - public void load(InputStream inputStream) throws IOException { - super.load(inputStream); - - // check that the file is a PDF - ByteArrayInputStream bais = null; - PdfReader reader = null; - - try { - - bais = new ByteArrayInputStream(content); - reader = new PdfReader(bais); - - } catch (Exception e) { - throw new IOException("PDF document is not valid"); - } finally { - if (reader != null) - reader.close(); - IOUtils.closeQuietly(bais); + /** + * Serial version UID. + */ + private static final long serialVersionUID = 6331191005700202153L; + + @Override + public int load(InputStream inputStream) throws IOException { + int totalread = super.load(inputStream); + + // check that the file is a PDF + ByteArrayInputStream bais = null; + PdfReader reader = null; + + try { + + bais = new ByteArrayInputStream(content); + reader = new PdfReader(bais); + + } catch (Exception e) { + throw new IOException("PDF document is not valid"); + } finally { + if (reader != null) + reader.close(); + IOUtils.closeQuietly(bais); + } + + return totalread; } - } - public int getPageCount() throws DocumentException { + public int getPageCount() throws DocumentException { - int pageCount = 0; + int pageCount = 0; - if (content == null) { - return pageCount; - } + if (content == null) { + return pageCount; + } - ByteArrayInputStream bais = null; - PdfReader reader = null; + ByteArrayInputStream bais = null; + PdfReader reader = null; - try { + try { - bais = new ByteArrayInputStream(content); - reader = new PdfReader(bais); - pageCount = reader.getNumberOfPages(); + bais = new ByteArrayInputStream(content); + reader = new PdfReader(bais); + pageCount = reader.getNumberOfPages(); - } catch (Exception e) { - throw new DocumentException(e); - } finally { - if (reader != null) - reader.close(); - IOUtils.closeQuietly(bais); - } + } catch (Exception e) { + throw new DocumentException(e); + } finally { + if (reader != null) + reader.close(); + IOUtils.closeQuietly(bais); + } - return pageCount; + return pageCount; - } + } - public Document extract(int begin, int end) throws DocumentException { + public Document extract(int begin, int end) throws DocumentException { - this.assertValidPageRange(begin, end); + this.assertValidPageRange(begin, end); - PDFDocument result = new PDFDocument(); + PDFDocument result = new PDFDocument(); - ByteArrayInputStream bais = null; - ByteArrayOutputStream baos = null; + ByteArrayInputStream bais = null; + ByteArrayOutputStream baos = null; - if (content != null) { + if (content != null) { - com.lowagie.text.Document document = new com.lowagie.text.Document(); + com.lowagie.text.Document document = new com.lowagie.text.Document(); - try { + try { - bais = new ByteArrayInputStream(content); - baos = new ByteArrayOutputStream(); + bais = new ByteArrayInputStream(content); + baos = new ByteArrayOutputStream(); - PdfReader inputPDF = new PdfReader(bais); + PdfReader inputPDF = new PdfReader(bais); - // create a writer for the outputstream - PdfWriter writer = PdfWriter.getInstance(document, baos); + // create a writer for the outputstream + PdfWriter writer = PdfWriter.getInstance(document, baos); - document.open(); - PdfContentByte cb = writer.getDirectContent(); + document.open(); + PdfContentByte cb = writer.getDirectContent(); - PdfImportedPage page; + PdfImportedPage page; - while (begin <= end) { - document.newPage(); - page = writer.getImportedPage(inputPDF, begin); - cb.addTemplate(page, 0, 0); - begin++; - } + while (begin <= end) { + document.newPage(); + page = writer.getImportedPage(inputPDF, begin); + cb.addTemplate(page, 0, 0); + begin++; + } + + document.close(); - document.close(); + result.load(new ByteArrayInputStream(baos.toByteArray())); - result.load(new ByteArrayInputStream(baos.toByteArray())); + } catch (Exception e) { + throw new DocumentException(e); + } finally { + if (document.isOpen()) + document.close(); + IOUtils.closeQuietly(bais); + IOUtils.closeQuietly(baos); + } - } catch (Exception e) { - throw new DocumentException(e); - } finally { - if (document.isOpen()) - document.close(); - IOUtils.closeQuietly(bais); - IOUtils.closeQuietly(baos); - } + } + return result; } - return result; - } + @Override + public void append(Document document) throws DocumentException { - @Override - public void append(Document document) throws DocumentException { + super.append(document); - super.append(document); + ByteArrayOutputStream baos = null; + com.lowagie.text.Document mergedDocument = new com.lowagie.text.Document(); - ByteArrayOutputStream baos = null; - com.lowagie.text.Document mergedDocument = new com.lowagie.text.Document(); + try { - try { + baos = new ByteArrayOutputStream(); + PdfCopy copy = new PdfCopy(mergedDocument, baos); - baos = new ByteArrayOutputStream(); - PdfCopy copy = new PdfCopy(mergedDocument, baos); + mergedDocument.open(); - mergedDocument.open(); + // copy current document + PdfReader reader = new PdfReader(content); + int pageCount = reader.getNumberOfPages(); + for (int i = 0; i < pageCount;) { + copy.addPage(copy.getImportedPage(reader, ++i)); + } - // copy current document - PdfReader reader = new PdfReader(content); - int pageCount = reader.getNumberOfPages(); - for (int i = 0; i < pageCount;) { - copy.addPage(copy.getImportedPage(reader, ++i)); - } + // copy new document + reader = new PdfReader(document.getContent()); + pageCount = reader.getNumberOfPages(); + for (int i = 0; i < pageCount;) { + copy.addPage(copy.getImportedPage(reader, ++i)); + } - // copy new document - reader = new PdfReader(document.getContent()); - pageCount = reader.getNumberOfPages(); - for (int i = 0; i < pageCount;) { - copy.addPage(copy.getImportedPage(reader, ++i)); - } + mergedDocument.close(); - mergedDocument.close(); + // replace content with new content + content = baos.toByteArray(); - // replace content with new content - content = baos.toByteArray(); + } catch (Exception e) { + throw new DocumentException(e); + } finally { + if (mergedDocument.isOpen()) + mergedDocument.close(); + IOUtils.closeQuietly(baos); + } - } catch (Exception e) { - throw new DocumentException(e); - } finally { - if (mergedDocument.isOpen()) - mergedDocument.close(); - IOUtils.closeQuietly(baos); } - } - - public String getType() { - return TYPE_PDF; - } + public String getType() { + return TYPE_PDF; + } } diff --git a/src/main/java/org/ghost4j/document/PSDocument.java b/src/main/java/org/ghost4j/document/PSDocument.java index bc30b7c..a13b57d 100644 --- a/src/main/java/org/ghost4j/document/PSDocument.java +++ b/src/main/java/org/ghost4j/document/PSDocument.java @@ -36,229 +36,231 @@ */ public class PSDocument extends AbstractDocument { - /** - * Serial version UID. - */ - private static final long serialVersionUID = 7225098893496658222L; + /** + * Serial version UID. + */ + private static final long serialVersionUID = 7225098893496658222L; - @Override - public void load(InputStream inputStream) throws IOException { + @Override + public int load(InputStream inputStream) throws IOException { - super.load(inputStream); + int totalread = super.load(inputStream); - // check that the file is a PostScript - ByteArrayInputStream bais = null; - try { + // check that the file is a PostScript + ByteArrayInputStream bais = null; + try { - bais = new ByteArrayInputStream(content); + bais = new ByteArrayInputStream(content); - DSCParser parser = new DSCParser(bais); - if (parser.nextDSCComment(DSCConstants.END_COMMENTS) == null) { - throw new IOException("PostScript document is not valid"); - } + DSCParser parser = new DSCParser(bais); + if (parser.nextDSCComment(DSCConstants.END_COMMENTS) == null) { + throw new IOException("PostScript document is not valid"); + } - } catch (DSCException e) { - throw new IOException(e.getMessage()); - } finally { - IOUtils.closeQuietly(bais); + } catch (DSCException e) { + throw new IOException(e.getMessage()); + } finally { + IOUtils.closeQuietly(bais); + } + + return totalread; } - } - public int getPageCount() throws DocumentException { + public int getPageCount() throws DocumentException { - int pageCount = 0; + int pageCount = 0; - if (content == null) { - return pageCount; - } + if (content == null) { + return pageCount; + } + + ByteArrayInputStream bais = null; - ByteArrayInputStream bais = null; + try { - try { + // read pages from the %%Pages DSC comment - // read pages from the %%Pages DSC comment + bais = new ByteArrayInputStream(content); - bais = new ByteArrayInputStream(content); + DSCParser parser = new DSCParser(bais); + Object tP = parser.nextDSCComment(DSCConstants.PAGES); + while (tP instanceof DSCAtend) + tP = parser.nextDSCComment(DSCConstants.PAGES); + DSCCommentPages pages = (DSCCommentPages) tP; + pageCount = pages.getPageCount(); - DSCParser parser = new DSCParser(bais); - Object tP = parser.nextDSCComment(DSCConstants.PAGES); - while (tP instanceof DSCAtend) - tP = parser.nextDSCComment(DSCConstants.PAGES); - DSCCommentPages pages = (DSCCommentPages) tP; - pageCount = pages.getPageCount(); + } catch (Exception e) { + throw new DocumentException(e); + } finally { + IOUtils.closeQuietly(bais); + } - } catch (Exception e) { - throw new DocumentException(e); - } finally { - IOUtils.closeQuietly(bais); + return pageCount; } - return pageCount; - } + public Document extract(int begin, int end) throws DocumentException { - public Document extract(int begin, int end) throws DocumentException { + this.assertValidPageRange(begin, end); - this.assertValidPageRange(begin, end); + PSDocument result = new PSDocument(); - PSDocument result = new PSDocument(); + ByteArrayInputStream bais = null; + ByteArrayOutputStream baos = null; - ByteArrayInputStream bais = null; - ByteArrayOutputStream baos = null; + if (content != null) { - if (content != null) { + try { - try { + bais = new ByteArrayInputStream(content); + baos = new ByteArrayOutputStream(); - bais = new ByteArrayInputStream(content); - baos = new ByteArrayOutputStream(); + PageExtractor.extractPages(bais, baos, begin, end); - PageExtractor.extractPages(bais, baos, begin, end); + result.load(new ByteArrayInputStream(baos.toByteArray())); - result.load(new ByteArrayInputStream(baos.toByteArray())); + } catch (Exception e) { + throw new DocumentException(e); + } finally { + IOUtils.closeQuietly(bais); + IOUtils.closeQuietly(baos); + } - } catch (Exception e) { - throw new DocumentException(e); - } finally { - IOUtils.closeQuietly(bais); - IOUtils.closeQuietly(baos); - } + } + return result; } - return result; - } - - /** - * This methods appends pages the DSC way (only by relying on the - * structure). It does not work with documents generated by different - * softwares / spools. It is intended to be used only for rearranging pages - * of the same document. If you need to append a different document, - * consider using the SafeAppenderModifier instead. - * - * @see SafeAppenderModifier - */ - @Override - public void append(Document document) throws DocumentException { - - super.append(document); - - ByteArrayInputStream baisCurrent = null; - ByteArrayInputStream baisNew = null; - ByteArrayOutputStream baos = null; - - int currentPageCount = this.getPageCount(); - int totalPageCount = currentPageCount + document.getPageCount(); - - try { - baisCurrent = new ByteArrayInputStream(content); - baos = new ByteArrayOutputStream(); - - DSCParser currentParser = new DSCParser(baisCurrent); - PSGenerator gen = new PSGenerator(baos); - currentParser.addListener(new DefaultNestedDocumentHandler(gen)); - - // skip DSC header - DSCHeaderComment header = DSCTools - .checkAndSkipDSC30Header(currentParser); - header.generate(gen); - // set number of pages - DSCCommentPages pages = new DSCCommentPages(totalPageCount); - pages.generate(gen); - - currentParser.setFilter(new DSCFilter() { - public boolean accept(DSCEvent event) { - if (event.isDSCComment()) { - - // filter %%Pages which we add manually above - return !event.asDSCComment().getName() - .equals(DSCConstants.PAGES); - } else { - return true; - } - } - }); - - // skip the prolog and to the first page - DSCComment pageOrTrailer = currentParser.nextDSCComment( - DSCConstants.PAGE, gen); - if (pageOrTrailer == null) { - throw new DSCException("Page expected, but none found"); - } - - // remove filter - currentParser.setFilter(null); - - // process individual pages - while (true) { - DSCCommentPage page = (DSCCommentPage) pageOrTrailer; - page.setPagePosition(page.getPagePosition()); - page.generate(gen); - pageOrTrailer = DSCTools.nextPageOrTrailer(currentParser, gen); - if (pageOrTrailer == null) { - throw new DSCException( - "File is not DSC-compliant: Unexpected end of file"); - } else if (!DSCConstants.PAGE.equals(pageOrTrailer.getName())) { - break; - } - } - - // append pages of the new document now - baisNew = new ByteArrayInputStream(document.getContent()); - DSCParser newParser = new DSCParser(baisNew); - header = DSCTools.checkAndSkipDSC30Header(newParser); - pageOrTrailer = newParser.nextDSCComment(DSCConstants.PAGE); - if (pageOrTrailer == null) { - throw new DSCException("Page expected, but none found"); - } - int i = 1; - while (true) { - DSCCommentPage page = (DSCCommentPage) pageOrTrailer; - page.setPageName(String.valueOf(currentPageCount + i)); - page.setPagePosition(currentPageCount + i); - page.generate(gen); - pageOrTrailer = DSCTools.nextPageOrTrailer(newParser, gen); - if (pageOrTrailer == null) { - throw new DSCException( - "File is not DSC-compliant: Unexpected end of file"); - } else if (!DSCConstants.PAGE.equals(pageOrTrailer.getName())) { - pageOrTrailer.generate(gen); - break; + /** + * This methods appends pages the DSC way (only by relying on the + * structure). It does not work with documents generated by different + * softwares / spools. It is intended to be used only for rearranging pages + * of the same document. If you need to append a different document, + * consider using the SafeAppenderModifier instead. + * + * @see SafeAppenderModifier + */ + @Override + public void append(Document document) throws DocumentException { + + super.append(document); + + ByteArrayInputStream baisCurrent = null; + ByteArrayInputStream baisNew = null; + ByteArrayOutputStream baos = null; + + int currentPageCount = this.getPageCount(); + int totalPageCount = currentPageCount + document.getPageCount(); + + try { + baisCurrent = new ByteArrayInputStream(content); + baos = new ByteArrayOutputStream(); + + DSCParser currentParser = new DSCParser(baisCurrent); + PSGenerator gen = new PSGenerator(baos); + currentParser.addListener(new DefaultNestedDocumentHandler(gen)); + + // skip DSC header + DSCHeaderComment header = DSCTools + .checkAndSkipDSC30Header(currentParser); + header.generate(gen); + // set number of pages + DSCCommentPages pages = new DSCCommentPages(totalPageCount); + pages.generate(gen); + + currentParser.setFilter(new DSCFilter() { + public boolean accept(DSCEvent event) { + if (event.isDSCComment()) { + + // filter %%Pages which we add manually above + return !event.asDSCComment().getName() + .equals(DSCConstants.PAGES); + } else { + return true; + } + } + }); + + // skip the prolog and to the first page + DSCComment pageOrTrailer = currentParser.nextDSCComment( + DSCConstants.PAGE, gen); + if (pageOrTrailer == null) { + throw new DSCException("Page expected, but none found"); + } + + // remove filter + currentParser.setFilter(null); + + // process individual pages + while (true) { + DSCCommentPage page = (DSCCommentPage) pageOrTrailer; + page.setPagePosition(page.getPagePosition()); + page.generate(gen); + pageOrTrailer = DSCTools.nextPageOrTrailer(currentParser, gen); + if (pageOrTrailer == null) { + throw new DSCException( + "File is not DSC-compliant: Unexpected end of file"); + } else if (!DSCConstants.PAGE.equals(pageOrTrailer.getName())) { + break; + } + } + + // append pages of the new document now + baisNew = new ByteArrayInputStream(document.getContent()); + DSCParser newParser = new DSCParser(baisNew); + header = DSCTools.checkAndSkipDSC30Header(newParser); + pageOrTrailer = newParser.nextDSCComment(DSCConstants.PAGE); + if (pageOrTrailer == null) { + throw new DSCException("Page expected, but none found"); + } + int i = 1; + while (true) { + DSCCommentPage page = (DSCCommentPage) pageOrTrailer; + page.setPageName(String.valueOf(currentPageCount + i)); + page.setPagePosition(currentPageCount + i); + page.generate(gen); + pageOrTrailer = DSCTools.nextPageOrTrailer(newParser, gen); + if (pageOrTrailer == null) { + throw new DSCException( + "File is not DSC-compliant: Unexpected end of file"); + } else if (!DSCConstants.PAGE.equals(pageOrTrailer.getName())) { + pageOrTrailer.generate(gen); + break; + } + i++; + } + + // write the rest (end) + currentParser.setFilter(new DSCFilter() { + public boolean accept(DSCEvent event) { + if (event.isDSCComment()) { + + // filter %%Pages (in case of attend) + return !event.asDSCComment().getName() + .equals(DSCConstants.PAGES); + } else { + return true; + } + } + }); + while (currentParser.hasNext()) { + DSCEvent event = currentParser.nextEvent(); + event.generate(gen); + } + + // update current document content + content = baos.toByteArray(); + + } catch (Exception e) { + throw new DocumentException(e); + } finally { + IOUtils.closeQuietly(baisCurrent); + IOUtils.closeQuietly(baisNew); + IOUtils.closeQuietly(baos); } - i++; - } - - // write the rest (end) - currentParser.setFilter(new DSCFilter() { - public boolean accept(DSCEvent event) { - if (event.isDSCComment()) { - - // filter %%Pages (in case of attend) - return !event.asDSCComment().getName() - .equals(DSCConstants.PAGES); - } else { - return true; - } - } - }); - while (currentParser.hasNext()) { - DSCEvent event = currentParser.nextEvent(); - event.generate(gen); - } - - // update current document content - content = baos.toByteArray(); - - } catch (Exception e) { - throw new DocumentException(e); - } finally { - IOUtils.closeQuietly(baisCurrent); - IOUtils.closeQuietly(baisNew); - IOUtils.closeQuietly(baos); } - } - public String getType() { - return TYPE_POSTSCRIPT; - } + public String getType() { + return TYPE_POSTSCRIPT; + } } diff --git a/src/main/java/org/ghost4j/document/PaperSize.java b/src/main/java/org/ghost4j/document/PaperSize.java index 0c2cebf..b64a43b 100644 --- a/src/main/java/org/ghost4j/document/PaperSize.java +++ b/src/main/java/org/ghost4j/document/PaperSize.java @@ -19,149 +19,149 @@ */ public class PaperSize implements Serializable { - /** - * Serial UID. - */ - private static final long serialVersionUID = -1614204334526018509L; - - /** - * Standard paper sizes index map. Allows faster paer size lookup by name. - */ - private static final Map index = new HashMap(); - - public static final PaperSize LEDGER = new PaperSize("ledger", 1224, 792); - public static final PaperSize LEGAL = new PaperSize("legal", 612, 1008); - public static final PaperSize LETTER = new PaperSize("letter", 612, 792); - public static final PaperSize ARCHE = new PaperSize("archE", 2592, 3456); - public static final PaperSize ARCHD = new PaperSize("archD", 1728, 2592); - public static final PaperSize ARCHC = new PaperSize("archC", 1296, 1728); - public static final PaperSize ARCHB = new PaperSize("archB", 864, 1296); - public static final PaperSize ARCHA = new PaperSize("archA", 648, 864); - public static final PaperSize A0 = new PaperSize("a0", 2384, 3370); - public static final PaperSize A1 = new PaperSize("a1", 1684, 2384); - public static final PaperSize A2 = new PaperSize("a2", 1191, 1684); - public static final PaperSize A3 = new PaperSize("a3", 842, 1191); - public static final PaperSize A4 = new PaperSize("a4", 595, 842); - public static final PaperSize A5 = new PaperSize("a5", 420, 595); - public static final PaperSize A6 = new PaperSize("a6", 297, 420); - public static final PaperSize A7 = new PaperSize("a7", 210, 297); - public static final PaperSize A8 = new PaperSize("a8", 148, 210); - public static final PaperSize A9 = new PaperSize("a9", 105, 148); - public static final PaperSize A10 = new PaperSize("a10", 73, 105); - - /** - * Paper width in points. - */ - private final int width; - - /** - * Paper height in points. - */ - private final int height; - - /** - * Paper name (if standard paper size) - */ - private String name; - - /** - * Constructor accepting dimensions. - * - * @param width - * Width - * @param height - * Height - */ - public PaperSize(int width, int height) { - this.width = width; - this.height = height; - } - - /** - * Constructor accepting dimensions and name. - * - * @param name - * Name. If provided, considered as a standard size (will be - * accessible with the getStandardPaperSize later on). - * @param width - * Width - * @param height - * Height - */ - public PaperSize(String name, int width, int height) { - this.width = width; - this.height = height; - this.name = name; - - // if name: add to the index of standard sizes - if (this.name != null) { - synchronized (index) { - index.put(this.name.toLowerCase(), this); - } + /** + * Serial UID. + */ + private static final long serialVersionUID = -1614204334526018509L; + + /** + * Standard paper sizes index map. Allows faster paer size lookup by name. + */ + private static final Map index = new HashMap(); + + public static final PaperSize LEDGER = new PaperSize("ledger", 1224, 792); + public static final PaperSize LEGAL = new PaperSize("legal", 612, 1008); + public static final PaperSize LETTER = new PaperSize("letter", 612, 792); + public static final PaperSize ARCHE = new PaperSize("archE", 2592, 3456); + public static final PaperSize ARCHD = new PaperSize("archD", 1728, 2592); + public static final PaperSize ARCHC = new PaperSize("archC", 1296, 1728); + public static final PaperSize ARCHB = new PaperSize("archB", 864, 1296); + public static final PaperSize ARCHA = new PaperSize("archA", 648, 864); + public static final PaperSize A0 = new PaperSize("a0", 2384, 3370); + public static final PaperSize A1 = new PaperSize("a1", 1684, 2384); + public static final PaperSize A2 = new PaperSize("a2", 1191, 1684); + public static final PaperSize A3 = new PaperSize("a3", 842, 1191); + public static final PaperSize A4 = new PaperSize("a4", 595, 842); + public static final PaperSize A5 = new PaperSize("a5", 420, 595); + public static final PaperSize A6 = new PaperSize("a6", 297, 420); + public static final PaperSize A7 = new PaperSize("a7", 210, 297); + public static final PaperSize A8 = new PaperSize("a8", 148, 210); + public static final PaperSize A9 = new PaperSize("a9", 105, 148); + public static final PaperSize A10 = new PaperSize("a10", 73, 105); + + /** + * Paper width in points. + */ + private final int width; + + /** + * Paper height in points. + */ + private final int height; + + /** + * Paper name (if standard paper size) + */ + private String name; + + /** + * Constructor accepting dimensions. + * + * @param width + * Width + * @param height + * Height + */ + public PaperSize(int width, int height) { + this.width = width; + this.height = height; } - } - - /** - * Returns a scaled PaperSize according to a scale factor. - * - * @param factor - * Scale factor - * @return Scaled PaperSize - */ - public PaperSize scale(float factor) { - - return new PaperSize((int) (width * factor), (int) (height * factor)); - - } - - /** - * Returns a portrait orientation of the PaperSize. - * - * @return A PaperSize. - */ - public PaperSize portrait() { - if (width > height) { - return new PaperSize(height, width); - } else { - return new PaperSize(width, height); + + /** + * Constructor accepting dimensions and name. + * + * @param name + * Name. If provided, considered as a standard size (will be + * accessible with the getStandardPaperSize later on). + * @param width + * Width + * @param height + * Height + */ + public PaperSize(String name, int width, int height) { + this.width = width; + this.height = height; + this.name = name; + + // if name: add to the index of standard sizes + if (this.name != null) { + synchronized (index) { + index.put(this.name.toLowerCase(), this); + } + } + } + + /** + * Returns a scaled PaperSize according to a scale factor. + * + * @param factor + * Scale factor + * @return Scaled PaperSize + */ + public PaperSize scale(float factor) { + + return new PaperSize((int) (width * factor), (int) (height * factor)); + + } + + /** + * Returns a portrait orientation of the PaperSize. + * + * @return A PaperSize. + */ + public PaperSize portrait() { + if (width > height) { + return new PaperSize(height, width); + } else { + return new PaperSize(width, height); + } + } + + /** + * Returns a landscape orientation of the PaperSize. + * + * @return A PaperSize. + */ + public PaperSize landscape() { + if (width < height) { + return new PaperSize(height, width); + } else { + return new PaperSize(width, height); + } } - } - - /** - * Returns a landscape orientation of the PaperSize. - * - * @return A PaperSize. - */ - public PaperSize landscape() { - if (width < height) { - return new PaperSize(height, width); - } else { - return new PaperSize(width, height); + + /** + * Looks for a standard paper size with a given name. + * + * @param name + * Paper size name (not case sensitive). + * @return PaperSize found or null + */ + public static synchronized PaperSize getStandardPaperSize(String name) { + + return index.get(name.toLowerCase()); + } + + public int getWidth() { + return width; + } + + public int getHeight() { + return height; + } + + public String getName() { + return name; } - } - - /** - * Looks for a standard paper size with a given name. - * - * @param name - * Paper size name (not case sensitive). - * @return PaperSize found or null - */ - public static synchronized PaperSize getStandardPaperSize(String name) { - - return index.get(name.toLowerCase()); - } - - public int getWidth() { - return width; - } - - public int getHeight() { - return height; - } - - public String getName() { - return name; - } } diff --git a/src/main/java/org/ghost4j/modifier/AbstractModifier.java b/src/main/java/org/ghost4j/modifier/AbstractModifier.java index f12aed5..4888e6a 100644 --- a/src/main/java/org/ghost4j/modifier/AbstractModifier.java +++ b/src/main/java/org/ghost4j/modifier/AbstractModifier.java @@ -22,16 +22,16 @@ * @author Gilles Grousset (gi.grousset@gmail.com) */ public abstract class AbstractModifier extends AbstractComponent implements - Modifier { +Modifier { - public Document modify(Document source, Map parameters) - throws ModifierException, DocumentException, IOException { + public Document modify(Document source, Map parameters) + throws ModifierException, DocumentException, IOException { - // perform actual processing - return run(source, parameters); - } + // perform actual processing + return run(source, parameters); + } - protected abstract Document run(Document source, - Map parameters) throws ModifierException, - DocumentException; + protected abstract Document run(Document source, + Map parameters) throws ModifierException, + DocumentException; } diff --git a/src/main/java/org/ghost4j/modifier/AbstractRemoteModifier.java b/src/main/java/org/ghost4j/modifier/AbstractRemoteModifier.java index 488275a..974fd06 100644 --- a/src/main/java/org/ghost4j/modifier/AbstractRemoteModifier.java +++ b/src/main/java/org/ghost4j/modifier/AbstractRemoteModifier.java @@ -26,112 +26,112 @@ * @author Gilles Grousset (gi.grousset@gmail.com) */ public abstract class AbstractRemoteModifier extends AbstractRemoteComponent - implements RemoteModifier { - - protected abstract Document run(Document source, - Map parameters) throws ModifierException, - DocumentException, IOException; - - /** - * Starts a remote modifier server. - * - * @param remoteModifier - * @throws ModifierException - */ - protected static void startRemoteModifier(RemoteModifier remoteModifier) - throws ModifierException { - - try { - - // get port - if (System.getenv("cajo.port") == null) { - throw new ModifierException( - "No Cajo port defined for remote converter"); - } - int cajoPort = Integer.parseInt(System.getenv("cajo.port")); - - // export modifier - RemoteModifier modifierCopy = remoteModifier.getClass() - .newInstance(); - remoteModifier.setMaxProcessCount(0); - - Remote.config(null, cajoPort, null, 0); - ItemServer.bind(modifierCopy, - RemoteModifier.class.getCanonicalName()); - - } catch (Exception e) { - throw new ModifierException(e); +implements RemoteModifier { + + protected abstract Document run(Document source, + Map parameters) throws ModifierException, + DocumentException, IOException; + + /** + * Starts a remote modifier server. + * + * @param remoteModifier + * @throws ModifierException + */ + protected static void startRemoteModifier(RemoteModifier remoteModifier) + throws ModifierException { + + try { + + // get port + if (System.getenv("cajo.port") == null) { + throw new ModifierException( + "No Cajo port defined for remote converter"); + } + int cajoPort = Integer.parseInt(System.getenv("cajo.port")); + + // export modifier + RemoteModifier modifierCopy = remoteModifier.getClass() + .getDeclaredConstructor().newInstance(); + remoteModifier.setMaxProcessCount(0); + + Remote.config(null, cajoPort, null, 0); + ItemServer.bind(modifierCopy, + RemoteModifier.class.getCanonicalName()); + + } catch (Exception e) { + throw new ModifierException(e); + } + } - } + public Document remoteModify(Document source, + Map parameters) throws ModifierException, + DocumentException, IOException { - public Document remoteModify(Document source, - Map parameters) throws ModifierException, - DocumentException, IOException { + return run(source, parameters); - return run(source, parameters); + } - } + public Document modify(Document source, Map parameters) + throws ModifierException, DocumentException, IOException { - public Document modify(Document source, Map parameters) - throws ModifierException, DocumentException, IOException { + if (maxProcessCount == 0) { - if (maxProcessCount == 0) { + // perform actual processing + return run(source, parameters); - // perform actual processing - return run(source, parameters); + } else { - } else { + // handle parallel processes - // handle parallel processes + // wait for a process to get free + this.waitForFreeProcess(); + processCount++; - // wait for a process to get free - this.waitForFreeProcess(); - processCount++; + // check if current class supports stand alone mode + if (!this.isStandAloneModeSupported()) { + throw new ModifierException( + "Standalone mode is not supported by this modifier: no 'main' method found"); + } - // check if current class supports stand alone mode - if (!this.isStandAloneModeSupported()) { - throw new ModifierException( - "Standalone mode is not supported by this modifier: no 'main' method found"); - } + // prepare new JVM + JavaFork fork = this.buildJavaFork(); - // prepare new JVM - JavaFork fork = this.buildJavaFork(); + // set JVM Xmx parameter according to the document size + int documentMbSize = ((source.getSize() / 1024 / 1024) + 1) * 2; + int xmxValue = 64 + documentMbSize; + fork.setXmx(xmxValue + "m"); - // set JVM Xmx parameter according to the document size - int documentMbSize = ((source.getSize() / 1024 / 1024) + 1) * 2; - int xmxValue = 64 + documentMbSize; - fork.setXmx(xmxValue + "m"); + int cajoPort = 0; - int cajoPort = 0; + try { - try { + // start remove server + cajoPort = this.startRemoteServer(fork); - // start remove server - cajoPort = this.startRemoteServer(fork); + // get remote component + Object remote = this.getRemoteComponent(cajoPort, + RemoteModifier.class); - // get remote component - Object remote = this.getRemoteComponent(cajoPort, - RemoteModifier.class); + // copy modifier settings to remote converter + Remote.invoke(remote, "copySettings", this.extractSettings()); - // copy modifier settings to remote converter - Remote.invoke(remote, "copySettings", this.extractSettings()); + // perform remote conversion + Object[] args = { source, parameters }; + Document result = (Document) Remote.invoke(remote, + "remoteModify", args); - // perform remote conversion - Object[] args = { source, parameters }; - Document result = (Document) Remote.invoke(remote, - "remoteModify", args); + // return result + return result; - // return result - return result; + } catch (Exception e) { + throw new ModifierException(e); + } finally { + processCount--; + fork.stop(); + } + } - } catch (Exception e) { - throw new ModifierException(e); - } finally { - processCount--; - fork.stop(); - } } - - } } diff --git a/src/main/java/org/ghost4j/modifier/Modifier.java b/src/main/java/org/ghost4j/modifier/Modifier.java index 2c47554..848d9f9 100644 --- a/src/main/java/org/ghost4j/modifier/Modifier.java +++ b/src/main/java/org/ghost4j/modifier/Modifier.java @@ -21,16 +21,16 @@ */ public interface Modifier { - /** - * Modify a document with optional parameters - * - * @param source - * Document to modify - * @param parameters - * Modifier parameters - * @return Modifier version of the document - * @throws ModifierException - */ - public Document modify(Document source, Map parameters) - throws ModifierException, DocumentException, IOException; + /** + * Modify a document with optional parameters + * + * @param source + * Document to modify + * @param parameters + * Modifier parameters + * @return Modifier version of the document + * @throws ModifierException + */ + public Document modify(Document source, Map parameters) + throws ModifierException, DocumentException, IOException; } diff --git a/src/main/java/org/ghost4j/modifier/ModifierException.java b/src/main/java/org/ghost4j/modifier/ModifierException.java index cedee95..55fcf55 100644 --- a/src/main/java/org/ghost4j/modifier/ModifierException.java +++ b/src/main/java/org/ghost4j/modifier/ModifierException.java @@ -15,24 +15,24 @@ */ public class ModifierException extends Exception { - /** - * Serial version UID. - */ - private static final long serialVersionUID = 2810773454523525125L; + /** + * Serial version UID. + */ + private static final long serialVersionUID = 2810773454523525125L; - public ModifierException() { - super(); - } + public ModifierException() { + super(); + } - public ModifierException(String message) { - super(message); - } + public ModifierException(String message) { + super(message); + } - public ModifierException(Throwable cause) { - super(cause); - } + public ModifierException(Throwable cause) { + super(cause); + } - public ModifierException(String message, Throwable cause) { - super(message, cause); - } + public ModifierException(String message, Throwable cause) { + super(message, cause); + } } diff --git a/src/main/java/org/ghost4j/modifier/RemoteModifier.java b/src/main/java/org/ghost4j/modifier/RemoteModifier.java index b9ff913..6f2414a 100644 --- a/src/main/java/org/ghost4j/modifier/RemoteModifier.java +++ b/src/main/java/org/ghost4j/modifier/RemoteModifier.java @@ -13,11 +13,11 @@ */ public interface RemoteModifier extends Modifier { - /** - * Sets max parallel rendering processes allowed for the modifier - * - * @param maxProcessCount - */ - public void setMaxProcessCount(int maxProcessCount); + /** + * Sets max parallel rendering processes allowed for the modifier + * + * @param maxProcessCount + */ + public void setMaxProcessCount(int maxProcessCount); } diff --git a/src/main/java/org/ghost4j/modifier/SafeAppenderModifier.java b/src/main/java/org/ghost4j/modifier/SafeAppenderModifier.java index 46e07ff..1f9cce0 100644 --- a/src/main/java/org/ghost4j/modifier/SafeAppenderModifier.java +++ b/src/main/java/org/ghost4j/modifier/SafeAppenderModifier.java @@ -31,117 +31,117 @@ */ public class SafeAppenderModifier extends AbstractRemoteModifier { - public static final String PARAMETER_APPEND_DOCUMENT = "APPEND_DOCUMENT"; - - public SafeAppenderModifier() { - - // set supported classes - supportedDocumentClasses = new Class[] { PSDocument.class, - PDFDocument.class }; - } - - /** - * Main method used to start the modifier in standalone 'slave mode'. - * - * @param args - * @throws ModifierException - */ - public static void main(String args[]) throws ModifierException { - startRemoteModifier(new SafeAppenderModifier()); - } - - @Override - protected Document run(Document source, Map parameters) - throws ModifierException, DocumentException, IOException { - - // check that document to append is provided as parameter - Document append = (Document) parameters.get(PARAMETER_APPEND_DOCUMENT); - if (append == null) { - throw new ModifierException( - "No document to append found in parameters map"); - } + public static final String PARAMETER_APPEND_DOCUMENT = "APPEND_DOCUMENT"; - // get Ghostscript instance - Ghostscript gs = Ghostscript.getInstance(); - - // generate a unique diskstore key for source and append documents, and - // for output file - DiskStore diskStore = DiskStore.getInstance(); - String sourceDiskStoreKey = diskStore.generateUniqueKey(); - String appendDiskStoreKey = diskStore.generateUniqueKey(); - String outputDiskStoreKey = diskStore.generateUniqueKey(); - - // write source and append to files - source.write(diskStore.addFile(sourceDiskStoreKey)); - append.write(diskStore.addFile(appendDiskStoreKey)); - - // guess output device from source document type - String deviceName = "pswrite"; - try { - if (source.getType().equals(Document.TYPE_PDF)) { - deviceName = "pdfwrite"; - } else { - // for Postscript : use ps2write if available - if (this.isDeviceSupported("ps2write")) { - deviceName = "ps2write"; - } else { - deviceName = "pswrite"; - } - } - } catch (GhostscriptException e) { - throw new ModifierException(e); + public SafeAppenderModifier() { + + // set supported classes + supportedDocumentClasses = new Class[] { PSDocument.class, + PDFDocument.class }; } - // prepare args - String[] gsArgs = { - "-psconv", - "-dNOPAUSE", - "-dSAFER", - "-dBATCH", - "-sDEVICE=" + deviceName, - "-sOutputFile=" - + diskStore.addFile(outputDiskStoreKey) - .getAbsolutePath(), "-q", "-f", - diskStore.getFile(sourceDiskStoreKey).getAbsolutePath(), - diskStore.getFile(appendDiskStoreKey).getAbsolutePath() }; - - Document result = null; - - try { - - // execute and exit interpreter - synchronized (gs) { - gs.initialize(gsArgs); - gs.exit(); - } - - // load obtained document (same type as source document) - if (source.getType().equals(Document.TYPE_PDF)) { - result = new PDFDocument(); - } else if (source.getType().equals(Document.TYPE_POSTSCRIPT)) { - result = new PSDocument(); - } - result.load(diskStore.getFile(outputDiskStoreKey)); - - } catch (GhostscriptException e) { - - throw new ModifierException(e); - - } finally { - - // delete Ghostscript instance - try { - Ghostscript.deleteInstance(); - } catch (GhostscriptException e) { - throw new ModifierException(e); - } - - // remove temporary files - diskStore.removeFile(outputDiskStoreKey); - diskStore.removeFile(sourceDiskStoreKey); - diskStore.removeFile(appendDiskStoreKey); + /** + * Main method used to start the modifier in standalone 'slave mode'. + * + * @param args + * @throws ModifierException + */ + public static void main(String args[]) throws ModifierException { + startRemoteModifier(new SafeAppenderModifier()); } - return result; - } + @Override + protected Document run(Document source, Map parameters) + throws ModifierException, DocumentException, IOException { + + // check that document to append is provided as parameter + Document append = (Document) parameters.get(PARAMETER_APPEND_DOCUMENT); + if (append == null) { + throw new ModifierException( + "No document to append found in parameters map"); + } + + // get Ghostscript instance + Ghostscript gs = Ghostscript.getInstance(); + + // generate a unique diskstore key for source and append documents, and + // for output file + DiskStore diskStore = DiskStore.getInstance(); + String sourceDiskStoreKey = diskStore.generateUniqueKey(); + String appendDiskStoreKey = diskStore.generateUniqueKey(); + String outputDiskStoreKey = diskStore.generateUniqueKey(); + + // write source and append to files + source.write(diskStore.addFile(sourceDiskStoreKey)); + append.write(diskStore.addFile(appendDiskStoreKey)); + + // guess output device from source document type + String deviceName = "pswrite"; + try { + if (source.getType().equals(Document.TYPE_PDF)) { + deviceName = "pdfwrite"; + } else { + // for Postscript : use ps2write if available + if (this.isDeviceSupported("ps2write")) { + deviceName = "ps2write"; + } else { + deviceName = "pswrite"; + } + } + } catch (GhostscriptException e) { + throw new ModifierException(e); + } + + // prepare args + String[] gsArgs = { + "-psconv", + "-dNOPAUSE", + "-dSAFER", + "-dBATCH", + "-sDEVICE=" + deviceName, + "-sOutputFile=" + + diskStore.addFile(outputDiskStoreKey) + .getAbsolutePath(), "-q", "-f", + diskStore.getFile(sourceDiskStoreKey).getAbsolutePath(), + diskStore.getFile(appendDiskStoreKey).getAbsolutePath() }; + + Document result = null; + + try { + + // execute and exit interpreter + synchronized (gs) { + gs.initialize(gsArgs); + gs.exit(); + } + + // load obtained document (same type as source document) + if (source.getType().equals(Document.TYPE_PDF)) { + result = new PDFDocument(); + } else if (source.getType().equals(Document.TYPE_POSTSCRIPT)) { + result = new PSDocument(); + } + result.load(diskStore.getFile(outputDiskStoreKey)); + + } catch (GhostscriptException e) { + + throw new ModifierException(e); + + } finally { + + // delete Ghostscript instance + try { + Ghostscript.deleteInstance(); + } catch (GhostscriptException e) { + throw new ModifierException(e); + } + + // remove temporary files + diskStore.removeFile(outputDiskStoreKey); + diskStore.removeFile(sourceDiskStoreKey); + diskStore.removeFile(appendDiskStoreKey); + } + + return result; + } } diff --git a/src/main/java/org/ghost4j/util/DiskStore.java b/src/main/java/org/ghost4j/util/DiskStore.java index 4a0f9af..e75e4bd 100644 --- a/src/main/java/org/ghost4j/util/DiskStore.java +++ b/src/main/java/org/ghost4j/util/DiskStore.java @@ -22,142 +22,142 @@ */ public class DiskStore { - public static final String ROOT_PATH = System.getProperty("java.io.tmpdir") - + File.separator + "ghost4j"; - - /** - * Shared instance. - */ - private static DiskStore instance; - - /** - * Map used to store references to temprorary files. - */ - private final Map map; - - /** - * Access to the shared instance. - * - * @return The shared DiskStore instance. - */ - public static synchronized DiskStore getInstance() { - - if (instance == null) { - instance = new DiskStore(); + public static final String ROOT_PATH = System.getProperty("java.io.tmpdir") + + File.separator + "ghost4j"; + + /** + * Shared instance. + */ + private static DiskStore instance; + + /** + * Map used to store references to temprorary files. + */ + private final Map map; + + /** + * Access to the shared instance. + * + * @return The shared DiskStore instance. + */ + public static synchronized DiskStore getInstance() { + + if (instance == null) { + instance = new DiskStore(); + } + + return instance; + } - return instance; + /** + * Private constructor. + */ + private DiskStore() { - } + map = new HashMap(); - /** - * Private constructor. - */ - private DiskStore() { + // register a shutdown hook to ensure temporary files are deleted at + // shutdown + Runtime.getRuntime().addShutdownHook(new Thread() { - map = new HashMap(); + @Override + public void run() { + super.run(); - // register a shutdown hook to ensure temporary files are deleted at - // shutdown - Runtime.getRuntime().addShutdownHook(new Thread() { + DiskStore diskStore = DiskStore.getInstance(); - @Override - public void run() { - super.run(); + // remove all files when store is destroyed + try { + for (String key : map.keySet()) { + diskStore.removeFile(key); + } - DiskStore diskStore = DiskStore.getInstance(); + // remove root dir + new File(ROOT_PATH).delete(); - // remove all files when store is destroyed - try { - for (String key : map.keySet()) { - diskStore.removeFile(key); - } + } catch (Exception e) { + // fail silently... + } + } + }); - // remove root dir - new File(ROOT_PATH).delete(); + } - } catch (Exception e) { - // fail silently... + /** + * Generates a unique diskstore key. Use the JVM PID and UUID. + * + * @return A unique key as string + */ + public synchronized String generateUniqueKey() { + + UUID id = UUID.randomUUID(); + String pid = ManagementFactory.getRuntimeMXBean().getName(); + if (pid.contains("@")) { + pid = pid.split("@")[0]; } - } - }); - - } - - /** - * Generates a unique diskstore key. Use the JVM PID and UUID. - * - * @return A unique key as string - */ - public synchronized String generateUniqueKey() { - - UUID id = UUID.randomUUID(); - String pid = ManagementFactory.getRuntimeMXBean().getName(); - if (pid.contains("@")) { - pid = pid.split("@")[0]; + return id + "@" + pid; } - return id + "@" + pid; - } - - /** - * Retrieve a File from a store key. If key is unknown, null is returned. - * - * @param key - * Unique file resource identifier. - * @return File or null (if not found). - */ - public synchronized File getFile(String key) { - - return map.get(key); - - } - - /** - * Remove a file from the store. This also deleted the temporary file from - * the file system. - * - * @param key - * Unique file resource identifier. - * @throws IOException - * In case the file cannot be deleted. - */ - public synchronized void removeFile(String key) throws IOException { - - File file = this.getFile(key); - - if (file != null && file.exists()) { - - // delete file - if (!file.delete()) { - throw new IOException("Temporary file " - + file.getAbsolutePath() + " cannot be deleted"); - } - - // remove from map - map.remove(key); + + /** + * Retrieve a File from a store key. If key is unknown, null is returned. + * + * @param key + * Unique file resource identifier. + * @return File or null (if not found). + */ + public synchronized File getFile(String key) { + + return map.get(key); + } - } - /** - * Add a file to the store. - * - * @param key - * File unique identifier. - * @return The generated (empty) file. - */ - public synchronized File addFile(String key) { + /** + * Remove a file from the store. This also deleted the temporary file from + * the file system. + * + * @param key + * Unique file resource identifier. + * @throws IOException + * In case the file cannot be deleted. + */ + public synchronized void removeFile(String key) throws IOException { + + File file = this.getFile(key); + + if (file != null && file.exists()) { + + // delete file + if (!file.delete()) { + throw new IOException("Temporary file " + + file.getAbsolutePath() + " cannot be deleted"); + } + + // remove from map + map.remove(key); + } + } - // prepare file - File file = new File(ROOT_PATH, key); + /** + * Add a file to the store. + * + * @param key + * File unique identifier. + * @return The generated (empty) file. + */ + public synchronized File addFile(String key) { - // ensure store root is created - file.getParentFile().mkdirs(); + // prepare file + File file = new File(ROOT_PATH, key); - // add to map - map.put(key, file); + // ensure store root is created + file.getParentFile().mkdirs(); - return file; + // add to map + map.put(key, file); - } + return file; + + } } diff --git a/src/main/java/org/ghost4j/util/ImageUtil.java b/src/main/java/org/ghost4j/util/ImageUtil.java index 80d8dd4..9edfaae 100644 --- a/src/main/java/org/ghost4j/util/ImageUtil.java +++ b/src/main/java/org/ghost4j/util/ImageUtil.java @@ -28,48 +28,48 @@ */ public class ImageUtil { - /** - * Converts a list of PageRaster objects to a list of Image objects - * - * @param rasters - * Page rasters to convert - * @return A list of images - */ - public static List convertPageRastersToImages( - List rasters) { + /** + * Converts a list of PageRaster objects to a list of Image objects + * + * @param rasters + * Page rasters to convert + * @return A list of images + */ + public static List convertPageRastersToImages( + List rasters) { - List result = new ArrayList(); + List result = new ArrayList(); - for (PageRaster raster : rasters) { - result.add(converterPageRasterToImage(raster)); - } + for (PageRaster raster : rasters) { + result.add(converterPageRasterToImage(raster)); + } - return result; - } + return result; + } - /** - * Converts a PageRaster object to an Image object. Raster data is supposed - * to hold RGB image data - * - * @param raster - * Page raster to convert - * @return An image - */ - public static Image converterPageRasterToImage(PageRaster raster) { + /** + * Converts a PageRaster object to an Image object. Raster data is supposed + * to hold RGB image data + * + * @param raster + * Page raster to convert + * @return An image + */ + public static Image converterPageRasterToImage(PageRaster raster) { - // create raster - DataBufferByte dbb = new DataBufferByte(raster.getData(), - raster.getData().length); - WritableRaster wr = Raster.createInterleavedRaster(dbb, - raster.getWidth(), raster.getHeight(), raster.getRaster(), 3, - new int[] { 0, 1, 2 }, null); + // create raster + DataBufferByte dbb = new DataBufferByte(raster.getData(), + raster.getData().length); + WritableRaster wr = Raster.createInterleavedRaster(dbb, + raster.getWidth(), raster.getHeight(), raster.getRaster(), 3, + new int[] { 0, 1, 2 }, null); - // create color space - ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_sRGB); - ColorModel cm = new ComponentColorModel(cs, false, false, - Transparency.OPAQUE, DataBuffer.TYPE_BYTE); + // create color space + ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_sRGB); + ColorModel cm = new ComponentColorModel(cs, false, false, + Transparency.OPAQUE, DataBuffer.TYPE_BYTE); - // create image and return it - return new BufferedImage(cm, wr, false, null); - } + // create image and return it + return new BufferedImage(cm, wr, false, null); + } } diff --git a/src/main/java/org/ghost4j/util/JavaFork.java b/src/main/java/org/ghost4j/util/JavaFork.java index 5b940e2..6a700c8 100644 --- a/src/main/java/org/ghost4j/util/JavaFork.java +++ b/src/main/java/org/ghost4j/util/JavaFork.java @@ -25,289 +25,289 @@ */ public class JavaFork implements Runnable { - private static final String JAVA_COMMAND; - private static final String PATH_SEPARATOR = System - .getProperty("path.separator"); + private static final String JAVA_COMMAND; + private static final String PATH_SEPARATOR = System + .getProperty("path.separator"); private static final String FILE_SEPARATOR = File.separator; - static { - if (System.getProperty("os.name").toLowerCase().contains("windows")) { - JAVA_COMMAND = "javaw"; - } else { - JAVA_COMMAND = "java"; - } - } - - /** - * Start class of the JVM. - */ - private Class startClass; - /** - * Process object of the JVM. Is null if the JVM is not running. - */ - private Process process; - /** - * If set to TRUE, output and error streams are redirected to the main JVM - * output stream - */ - private boolean redirectStreams; - /** - * If set to TRUE, main JVM will wait for this JVM to stop before exiting. - */ - private boolean waitBeforeExiting = false; - - /** - * Additional environment variables. - */ - private Map environment; - - /** - * Xmx parameter. Default value is set to 128M. - */ - private String xmx = "128m"; - - /** - * Xms parameter. Default value is set to 64M. - */ - private String xms = "64m"; - - public void start(Class startClass) { - - this.setStartClass(startClass); - this.start(); - - } - - public void start() { - - // start thread - final Thread thread = new Thread(this); - thread.setDaemon(false); - thread.start(); - - // register shutdown hook to wait for thread when JVM exists - if (waitBeforeExiting) { - Runtime.getRuntime().addShutdownHook(new Thread() { - - @Override - public void run() { - try { - thread.join(); - } catch (InterruptedException e) { - // nothing - } + static { + if (System.getProperty("os.name").toLowerCase().contains("windows")) { + JAVA_COMMAND = "javaw"; + } else { + JAVA_COMMAND = "java"; } - - }); } - } + /** + * Start class of the JVM. + */ + private Class startClass; + /** + * Process object of the JVM. Is null if the JVM is not running. + */ + private Process process; + /** + * If set to TRUE, output and error streams are redirected to the main JVM + * output stream + */ + private boolean redirectStreams; + /** + * If set to TRUE, main JVM will wait for this JVM to stop before exiting. + */ + private boolean waitBeforeExiting = false; + + /** + * Additional environment variables. + */ + private Map environment; + + /** + * Xmx parameter. Default value is set to 128M. + */ + private String xmx = "128m"; + + /** + * Xms parameter. Default value is set to 64M. + */ + private String xms = "64m"; + + public void start(Class startClass) { + + this.setStartClass(startClass); + this.start(); - public void stop() { - - if (process != null) { - process.destroy(); } - } - public void run() { + public void start() { - // check if process is not already running - if (process != null) { - throw new RuntimeException("Fork is already running"); - } + // start thread + final Thread thread = new Thread(this); + thread.setDaemon(false); + thread.start(); - // check if start class is set - if (startClass == null) { - throw new RuntimeException("No start class defined"); - } + // register shutdown hook to wait for thread when JVM exists + if (waitBeforeExiting) { + Runtime.getRuntime().addShutdownHook(new Thread() { - // retrieve classpath - String classPath = this.getCurrentClasspath(); + @Override + public void run() { + try { + thread.join(); + } catch (InterruptedException e) { + // nothing + } + } + + }); + } - // build child process - String ghost4JEncoding = System.getProperty("ghost4j.encoding"); - String fileEncoding = "-Dfile.encoding="; - if (ghost4JEncoding != null) { - fileEncoding += ghost4JEncoding; - } else { - fileEncoding += System.getProperty("file.encoding"); - } - ProcessBuilder processBuilder = new ProcessBuilder(JAVA_COMMAND, - fileEncoding, "-Xms" + xms, "-Xmx" + xmx, "-cp", classPath, - startClass.getName()); - if (System.getProperty("jna.library.path") != null) { - String jnaLibraryPath = "-Djna.library.path=" - + System.getProperty("jna.library.path"); - processBuilder = new ProcessBuilder(JAVA_COMMAND, fileEncoding, - jnaLibraryPath, "-Xms" + xms, "-Xmx" + xmx, "-cp", - classPath, startClass.getName()); - } - processBuilder.directory(new File(System.getProperty("user.dir"))); - processBuilder.environment().putAll(System.getenv()); - if (getEnvironment() != null) { - processBuilder.environment().putAll(getEnvironment()); } - // start - try { - process = processBuilder.start(); + public void stop() { - // redirect output stream to main process output stream - if (redirectStreams) { - // error stream - processBuilder.redirectErrorStream(true); - // standard stream - StreamGobbler outputStreamGobbler = new StreamGobbler( - process.getInputStream(), System.out); - outputStreamGobbler.start(); - } + if (process != null) { + process.destroy(); + } + } - process.waitFor(); + public void run() { - } catch (Exception e) { - throw new RuntimeException(e); - } + // check if process is not already running + if (process != null) { + throw new RuntimeException("Fork is already running"); + } - } + // check if start class is set + if (startClass == null) { + throw new RuntimeException("No start class defined"); + } - private String getCurrentClasspath() { - StringBuilder cpBuilder = new StringBuilder(); - ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); - - if(classLoader instanceof URLClassLoader) { - URL[] urls = ((URLClassLoader) classLoader).getURLs(); - - for (int i = 0; i < urls.length; i++) { - // need to do some conversion to get the paths right - // otherwise paths get broken on windows - String s = urls[i].toExternalForm(); + // retrieve classpath + String classPath = this.getCurrentClasspath(); - try { - s = URLDecoder.decode(s, "UTF-8"); - urls[i] = new URL(s); - s = new File(urls[i].getFile()).getAbsolutePath(); - cpBuilder.append(s); - if (i < urls.length - 1) - cpBuilder.append(PATH_SEPARATOR); - } catch (UnsupportedEncodingException e) { - // should never happen as we pass supported encoding UTF-8 - } catch (MalformedURLException e) { - // should also never happen at this point, but who knows ;-) - } + // build child process + String ghost4JEncoding = System.getProperty("ghost4j.encoding"); + String fileEncoding = "-Dfile.encoding="; + if (ghost4JEncoding != null) { + fileEncoding += ghost4JEncoding; + } else { + fileEncoding += System.getProperty("file.encoding"); + } + ProcessBuilder processBuilder = new ProcessBuilder(JAVA_COMMAND, + fileEncoding, "-Xms" + xms, "-Xmx" + xmx, "-cp", classPath, + startClass.getName()); + if (System.getProperty("jna.library.path") != null) { + String jnaLibraryPath = "-Djna.library.path=" + + System.getProperty("jna.library.path"); + processBuilder = new ProcessBuilder(JAVA_COMMAND, fileEncoding, + jnaLibraryPath, "-Xms" + xms, "-Xmx" + xmx, "-cp", + classPath, startClass.getName()); + } + processBuilder.directory(new File(System.getProperty("user.dir"))); + processBuilder.environment().putAll(System.getenv()); + if (getEnvironment() != null) { + processBuilder.environment().putAll(getEnvironment()); } - } else if (classLoader.getClass().getName().equals("org.jboss.modules.ModuleClassLoader")) { - // This branch is for JBoss 7 & JBoss EAP 6 support. Not sure about Wildfly + + // start try { - Enumeration urls2 = classLoader.getResources("/"); - - while (urls2.hasMoreElements()) { - URL path = urls2.nextElement(); - String s = path.toExternalForm(); - if(s.startsWith("vfs:")) { - if(s.contains(".jar")) { - URLConnection conn = new URL(URLDecoder.decode(s, "UTF-8")).openConnection(); - Object vf = conn.getContent(); - // Use reflection to call getPhysicalFile() method org.jboss.vfs.VirtualFile - // This eliminates the dependency on JBoss specific jar files - Method getPhysicalFile = vf.getClass().getDeclaredMethod("getPhysicalFile"); - getPhysicalFile.setAccessible(true); - File physicalFile = (File)getPhysicalFile.invoke(vf); - String jarVFSPath = physicalFile.getAbsolutePath(); - - int idxJarExt = jarVFSPath.lastIndexOf(".jar"); - int idxSlashAfterJar = jarVFSPath.indexOf(FILE_SEPARATOR, idxJarExt); - int idxSlashBeforeJar = jarVFSPath.substring(0,idxJarExt).lastIndexOf(FILE_SEPARATOR); - - String jarFileName2 = jarVFSPath.substring(idxSlashBeforeJar + 1, idxJarExt + 4); - String jarFolder = jarVFSPath.substring(0, idxSlashAfterJar); - String jarFullPath = jarFolder + FILE_SEPARATOR + jarFileName2; - - cpBuilder.append(jarFullPath); - if (urls2.hasMoreElements()) { - cpBuilder.append(PATH_SEPARATOR); - } - } else if(s.contains("classes")) { - URLConnection conn = new URL(URLDecoder.decode(s, "UTF-8")).openConnection(); - Object vf = conn.getContent(); - // Use reflection to call getPhysicalFile() method org.jboss.vfs.VirtualFile - // This eliminates the dependency on JBoss specific jar files - Method getPhysicalFile = vf.getClass().getDeclaredMethod("getPhysicalFile"); - getPhysicalFile.setAccessible(true); - File physicalFile = (File)getPhysicalFile.invoke(vf); - String jarVFSPath = physicalFile.getAbsolutePath(); - cpBuilder.append(jarVFSPath); - if (urls2.hasMoreElements()) { - cpBuilder.append(PATH_SEPARATOR); - } - } - } + process = processBuilder.start(); + + // redirect output stream to main process output stream + if (redirectStreams) { + // error stream + processBuilder.redirectErrorStream(true); + // standard stream + StreamGobbler outputStreamGobbler = new StreamGobbler( + process.getInputStream(), System.out); + outputStreamGobbler.start(); } + + process.waitFor(); + } catch (Exception e) { throw new RuntimeException(e); } - } else { - throw new RuntimeException("Found unknown ClassLoader type, cannot scan classes: " + classLoader.getClass().getName()); - } - - String cp = cpBuilder.toString(); - - if (cp.isEmpty() || cp.contains("surefirebooter")) { - // if called from Maven: use the java.class.path property as - // classpath - return System.getProperty("java.class.path"); - } else { - return cp; + } - } + private String getCurrentClasspath() { + StringBuilder cpBuilder = new StringBuilder(); + ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); + + if(classLoader instanceof URLClassLoader) { + URL[] urls = ((URLClassLoader) classLoader).getURLs(); + + for (int i = 0; i < urls.length; i++) { + // need to do some conversion to get the paths right + // otherwise paths get broken on windows + String s = urls[i].toExternalForm(); + + try { + s = URLDecoder.decode(s, "UTF-8"); + urls[i] = new URL(s); + s = new File(urls[i].getFile()).getAbsolutePath(); + cpBuilder.append(s); + if (i < urls.length - 1) + cpBuilder.append(PATH_SEPARATOR); + } catch (UnsupportedEncodingException e) { + // should never happen as we pass supported encoding UTF-8 + } catch (MalformedURLException e) { + // should also never happen at this point, but who knows ;-) + } + } + } else if (classLoader.getClass().getName().equals("org.jboss.modules.ModuleClassLoader")) { + // This branch is for JBoss 7 & JBoss EAP 6 support. Not sure about Wildfly + try { + Enumeration urls2 = classLoader.getResources("/"); + + while (urls2.hasMoreElements()) { + URL path = urls2.nextElement(); + String s = path.toExternalForm(); + if(s.startsWith("vfs:")) { + if(s.contains(".jar")) { + URLConnection conn = new URL(URLDecoder.decode(s, "UTF-8")).openConnection(); + Object vf = conn.getContent(); + // Use reflection to call getPhysicalFile() method org.jboss.vfs.VirtualFile + // This eliminates the dependency on JBoss specific jar files + Method getPhysicalFile = vf.getClass().getDeclaredMethod("getPhysicalFile"); + getPhysicalFile.setAccessible(true); + File physicalFile = (File)getPhysicalFile.invoke(vf); + String jarVFSPath = physicalFile.getAbsolutePath(); + + int idxJarExt = jarVFSPath.lastIndexOf(".jar"); + int idxSlashAfterJar = jarVFSPath.indexOf(FILE_SEPARATOR, idxJarExt); + int idxSlashBeforeJar = jarVFSPath.substring(0,idxJarExt).lastIndexOf(FILE_SEPARATOR); + + String jarFileName2 = jarVFSPath.substring(idxSlashBeforeJar + 1, idxJarExt + 4); + String jarFolder = jarVFSPath.substring(0, idxSlashAfterJar); + String jarFullPath = jarFolder + FILE_SEPARATOR + jarFileName2; + + cpBuilder.append(jarFullPath); + if (urls2.hasMoreElements()) { + cpBuilder.append(PATH_SEPARATOR); + } + } else if(s.contains("classes")) { + URLConnection conn = new URL(URLDecoder.decode(s, "UTF-8")).openConnection(); + Object vf = conn.getContent(); + // Use reflection to call getPhysicalFile() method org.jboss.vfs.VirtualFile + // This eliminates the dependency on JBoss specific jar files + Method getPhysicalFile = vf.getClass().getDeclaredMethod("getPhysicalFile"); + getPhysicalFile.setAccessible(true); + File physicalFile = (File)getPhysicalFile.invoke(vf); + String jarVFSPath = physicalFile.getAbsolutePath(); + cpBuilder.append(jarVFSPath); + if (urls2.hasMoreElements()) { + cpBuilder.append(PATH_SEPARATOR); + } + } + } + } + } catch (Exception e) { + throw new RuntimeException(e); + } + } else { + throw new RuntimeException("Found unknown ClassLoader type, cannot scan classes: " + classLoader.getClass().getName()); + } - public Class getStartClass() { - return startClass; - } + String cp = cpBuilder.toString(); - public void setStartClass(Class startClass) { - this.startClass = startClass; - } + if (cp.isEmpty() || cp.contains("surefirebooter")) { + // if called from Maven: use the java.class.path property as + // classpath + return System.getProperty("java.class.path"); + } else { + return cp; + } - public boolean getRedirectStreams() { - return redirectStreams; - } + } - public void setRedirectStreams(boolean redirectStreams) { - this.redirectStreams = redirectStreams; - } + public Class getStartClass() { + return startClass; + } - public boolean getWaitBeforeExiting() { - return waitBeforeExiting; - } + public void setStartClass(Class startClass) { + this.startClass = startClass; + } - public void setWaitBeforeExiting(boolean waitBeforeExiting) { - this.waitBeforeExiting = waitBeforeExiting; - } + public boolean getRedirectStreams() { + return redirectStreams; + } - public Map getEnvironment() { - return environment; - } + public void setRedirectStreams(boolean redirectStreams) { + this.redirectStreams = redirectStreams; + } + + public boolean getWaitBeforeExiting() { + return waitBeforeExiting; + } + + public void setWaitBeforeExiting(boolean waitBeforeExiting) { + this.waitBeforeExiting = waitBeforeExiting; + } + + public Map getEnvironment() { + return environment; + } - public void setEnvironment(Map environment) { - this.environment = environment; - } + public void setEnvironment(Map environment) { + this.environment = environment; + } - public String getXmx() { - return xmx; - } + public String getXmx() { + return xmx; + } - public void setXmx(String xmx) { - this.xmx = xmx; - } + public void setXmx(String xmx) { + this.xmx = xmx; + } - public String getXms() { - return xms; - } + public String getXms() { + return xms; + } - public void setXms(String xms) { - this.xms = xms; - } + public void setXms(String xms) { + this.xms = xms; + } } diff --git a/src/main/java/org/ghost4j/util/NetworkUtil.java b/src/main/java/org/ghost4j/util/NetworkUtil.java index 5518684..1df7263 100644 --- a/src/main/java/org/ghost4j/util/NetworkUtil.java +++ b/src/main/java/org/ghost4j/util/NetworkUtil.java @@ -17,76 +17,76 @@ */ public class NetworkUtil { - /** - * Finds an available port within a port range on a host - * - * @param hostname - * Host name - * @param startPort - * Port number starting the range - * @param endPort - * Port number ending the range - * @return An available port number, or 0 if none is available. - */ - public static synchronized int findAvailablePort(String hostname, - int startPort, int endPort) { + /** + * Finds an available port within a port range on a host + * + * @param hostname + * Host name + * @param startPort + * Port number starting the range + * @param endPort + * Port number ending the range + * @return An available port number, or 0 if none is available. + */ + public static synchronized int findAvailablePort(String hostname, + int startPort, int endPort) { - for (int port = startPort; port < (endPort + 1); port++) { + for (int port = startPort; port < (endPort + 1); port++) { - try { - Socket socket = new Socket(InetAddress.getByName(hostname), - port); - // port not available - socket.close(); - } catch (IOException e) { - // port available - return port; - } + try { + Socket socket = new Socket(InetAddress.getByName(hostname), + port); + // port not available + socket.close(); + } catch (IOException e) { + // port available + return port; + } + } + + return 0; } - return 0; - } + /** + * Waits until a port is listening on a given host. An exception is thrown + * if the timeout is excedeed. + * + * @param hostname + * Host name + * @param port + * Port number + * @param timeout + * Timeout in seconds + * @throws IOException + * If a connection error occurs or if the timeout is exceeded + */ + public static void waitUntilPortListening(String hostname, int port, + int timeout) throws IOException { - /** - * Waits until a port is listening on a given host. An exception is thrown - * if the timeout is excedeed. - * - * @param hostname - * Host name - * @param port - * Port number - * @param timeout - * Timeout in seconds - * @throws IOException - * If a connection error occurs or if the timeout is exceeded - */ - public static void waitUntilPortListening(String hostname, int port, - int timeout) throws IOException { + int i = 0; + while (i < timeout) { - int i = 0; - while (i < timeout) { + // try to get connection + try { + Socket socket = new Socket(InetAddress.getByName(hostname), + port); + // connection OK: exit + socket.close(); + return; + } catch (IOException e) { + // nothing + } - // try to get connection - try { - Socket socket = new Socket(InetAddress.getByName(hostname), - port); - // connection OK: exit - socket.close(); - return; - } catch (IOException e) { - // nothing - } + i++; - i++; + // wait for 1 second + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + // nothing + } + } - // wait for 1 second - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - // nothing - } + throw new IOException("Timeout waiting for port " + port + " to listen"); } - - throw new IOException("Timeout waiting for port " + port + " to listen"); - } } diff --git a/src/main/java/org/ghost4j/util/StreamGobbler.java b/src/main/java/org/ghost4j/util/StreamGobbler.java index 0173b59..435fc9b 100644 --- a/src/main/java/org/ghost4j/util/StreamGobbler.java +++ b/src/main/java/org/ghost4j/util/StreamGobbler.java @@ -21,51 +21,51 @@ */ public class StreamGobbler extends Thread { - /** - * Input stream to read. - */ - InputStream inputStream; - /** - * Output stream to write. - */ - OutputStream outputStream; + /** + * Input stream to read. + */ + InputStream inputStream; + /** + * Output stream to write. + */ + OutputStream outputStream; - public StreamGobbler(InputStream inputStream, OutputStream outputStream) { + public StreamGobbler(InputStream inputStream, OutputStream outputStream) { - this.inputStream = inputStream; - this.outputStream = outputStream; - } + this.inputStream = inputStream; + this.outputStream = outputStream; + } - @Override - public void run() { + @Override + public void run() { - try { + try { - PrintWriter printWriter = null; - if (outputStream != null) { - printWriter = new PrintWriter(outputStream); - } + PrintWriter printWriter = null; + if (outputStream != null) { + printWriter = new PrintWriter(outputStream); + } - InputStreamReader inputStreamReader = new InputStreamReader( - inputStream); - BufferedReader bufferedReader = new BufferedReader( - inputStreamReader); - String line = null; - while ((line = bufferedReader.readLine()) != null) { - if (printWriter != null) { - printWriter.println(line); - } - } + InputStreamReader inputStreamReader = new InputStreamReader( + inputStream); + BufferedReader bufferedReader = new BufferedReader( + inputStreamReader); + String line = null; + while ((line = bufferedReader.readLine()) != null) { + if (printWriter != null) { + printWriter.println(line); + } + } - if (printWriter != null) { - printWriter.flush(); - } + if (printWriter != null) { + printWriter.flush(); + } - } catch (IOException e) { + } catch (IOException e) { - // nothing + // nothing - } + } - } + } }