fixes for ghostscript 9.54+
This commit is contained in:
parent
27fd1956a1
commit
229951349b
13 changed files with 143 additions and 347 deletions
|
@ -1,3 +1,3 @@
|
||||||
group = org.xbib.graphics
|
group = org.xbib.graphics
|
||||||
name = graphics
|
name = graphics
|
||||||
version = 5.4.0
|
version = 5.5.0
|
||||||
|
|
|
@ -2,23 +2,20 @@ package org.xbib.graphics.ghostscript;
|
||||||
|
|
||||||
import com.sun.jna.Pointer;
|
import com.sun.jna.Pointer;
|
||||||
import com.sun.jna.ptr.IntByReference;
|
import com.sun.jna.ptr.IntByReference;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import org.xbib.graphics.ghostscript.internal.ErrorCodes;
|
import org.xbib.graphics.ghostscript.internal.ErrorCodes;
|
||||||
import org.xbib.graphics.ghostscript.internal.GhostscriptLibrary;
|
import org.xbib.graphics.ghostscript.internal.GhostscriptLibrary;
|
||||||
import org.xbib.graphics.ghostscript.internal.GhostscriptLibraryLoader;
|
import org.xbib.graphics.ghostscript.internal.GhostscriptLibraryLoader;
|
||||||
import org.xbib.graphics.ghostscript.internal.LoggingOutputStream;
|
|
||||||
import org.xbib.graphics.ghostscript.internal.NullOutputStream;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeFormatter;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
@ -33,21 +30,12 @@ public class Ghostscript implements AutoCloseable {
|
||||||
|
|
||||||
private final GhostscriptLibrary libraryInstance;
|
private final GhostscriptLibrary libraryInstance;
|
||||||
|
|
||||||
private InputStream stdIn;
|
|
||||||
|
|
||||||
private OutputStream stdOut;
|
|
||||||
|
|
||||||
private OutputStream stdErr;
|
|
||||||
|
|
||||||
private final AtomicBoolean closed;
|
private final AtomicBoolean closed;
|
||||||
|
|
||||||
public Ghostscript() throws IOException {
|
public Ghostscript() throws IOException {
|
||||||
prepareTmp();
|
prepareTmp();
|
||||||
GhostscriptLibraryLoader libraryLoader = new GhostscriptLibraryLoader();
|
GhostscriptLibraryLoader libraryLoader = new GhostscriptLibraryLoader();
|
||||||
this.libraryInstance = libraryLoader.getGhostscriptLibrary();
|
this.libraryInstance = Objects.requireNonNull(libraryLoader.getGhostscriptLibrary(), "library instance must not be null");
|
||||||
Objects.requireNonNull(this.libraryInstance);
|
|
||||||
this.stdOut = new LoggingOutputStream(logger);
|
|
||||||
this.stdErr = new LoggingOutputStream(logger);
|
|
||||||
this.closed = new AtomicBoolean(false);
|
this.closed = new AtomicBoolean(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,62 +56,7 @@ public class Ghostscript implements AutoCloseable {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public synchronized int run(List<String> args) throws IOException {
|
||||||
* Gets the error output stream of the Ghostscript interpreter (may be null
|
|
||||||
* if not set).
|
|
||||||
*
|
|
||||||
* @return The OutputStream or null
|
|
||||||
*/
|
|
||||||
public OutputStream getStdErr() {
|
|
||||||
return stdErr;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the error output stream of the Ghostscript interpreter.
|
|
||||||
*
|
|
||||||
* @param outputStream OutputStream object
|
|
||||||
*/
|
|
||||||
public void setStdErr(OutputStream outputStream) {
|
|
||||||
stdErr = outputStream;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the standard output stream of the Ghostscript interpreter.
|
|
||||||
*
|
|
||||||
* @return The OutputStream or null
|
|
||||||
*/
|
|
||||||
public OutputStream getStdOut() {
|
|
||||||
return stdOut;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the standard output stream of the Ghostscript interpreter.
|
|
||||||
*
|
|
||||||
* @param outputStream OutputStream object
|
|
||||||
*/
|
|
||||||
public void setStdOut(OutputStream outputStream) {
|
|
||||||
stdOut = outputStream;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the standard input stream of the Ghostscript interpreter.
|
|
||||||
*
|
|
||||||
* @return The InputStream or null
|
|
||||||
*/
|
|
||||||
public InputStream getStdIn() {
|
|
||||||
return stdIn;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the standard input stream of the Ghostscript interpreter.
|
|
||||||
*
|
|
||||||
* @param inputStream InputStream object
|
|
||||||
*/
|
|
||||||
public void setStdIn(InputStream inputStream) {
|
|
||||||
stdIn = inputStream;
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized int run(String[] args) throws IOException {
|
|
||||||
return run(args, null, null);
|
return run(args, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -136,9 +69,15 @@ public class Ghostscript implements AutoCloseable {
|
||||||
* @return the ghostscript return code;
|
* @return the ghostscript return code;
|
||||||
* @throws IOException if initialize fails
|
* @throws IOException if initialize fails
|
||||||
*/
|
*/
|
||||||
public synchronized int run(String[] args,
|
public synchronized int run(List<String> args,
|
||||||
String runString,
|
String runString,
|
||||||
String fileName) throws IOException {
|
String fileName) throws IOException {
|
||||||
|
Objects.requireNonNull(args);
|
||||||
|
List<String> fullArgList = new LinkedList<>();
|
||||||
|
if (!args.contains("ps2pdf")) {
|
||||||
|
fullArgList.add("gs"); // gs or ps2pdf must be always present in the arg list
|
||||||
|
}
|
||||||
|
fullArgList.addAll(args);
|
||||||
GhostscriptLibrary.gs_main_instance.ByReference nativeInstanceByRef = null;
|
GhostscriptLibrary.gs_main_instance.ByReference nativeInstanceByRef = null;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
boolean quit = false;
|
boolean quit = false;
|
||||||
|
@ -149,47 +88,16 @@ public class Ghostscript implements AutoCloseable {
|
||||||
nativeInstanceByRef = null;
|
nativeInstanceByRef = null;
|
||||||
throw new IOException("can not call Ghostscript gsapi_new_instance, error code = " + ret);
|
throw new IOException("can not call Ghostscript gsapi_new_instance, error code = " + ret);
|
||||||
}
|
}
|
||||||
logger.log(Level.INFO, "ghostscript instance " + nativeInstanceByRef + " created");
|
logger.log(Level.FINE, "ghostscript instance " + nativeInstanceByRef + " created");
|
||||||
Pointer pointer = nativeInstanceByRef.getValue();
|
Pointer pointer = nativeInstanceByRef.getValue();
|
||||||
GhostscriptLibrary.stdin_fn stdinCallback = null;
|
// always 0, we never use stdin
|
||||||
if (getStdIn() != null) {
|
GhostscriptLibrary.stdin_fn stdinCallback = (caller_handle, buf, len) -> 0;
|
||||||
stdinCallback = (caller_handle, buf, len) -> {
|
GhostscriptLibrary.stdout_fn stdoutCallback = (caller_handle, str, len) -> {
|
||||||
String encoding = System.getProperty(ENCODING_PARAMETER, "UTF-8");
|
logger.log(Level.FINE, str.substring(0, len));
|
||||||
try {
|
|
||||||
byte[] buffer = new byte[1024];
|
|
||||||
int read = getStdIn().read(buffer);
|
|
||||||
if (read != -1) {
|
|
||||||
buf.setString(0, new String(buffer, 0, read, encoding));
|
|
||||||
return read;
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
logger.log(Level.WARNING, e.getMessage(), e);
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
GhostscriptLibrary.stdout_fn stdoutCallback;
|
|
||||||
if (getStdOut() == null) {
|
|
||||||
setStdOut(new NullOutputStream());
|
|
||||||
}
|
|
||||||
stdoutCallback = (caller_handle, str, len) -> {
|
|
||||||
try {
|
|
||||||
getStdOut().write(str.getBytes(), 0, len);
|
|
||||||
} catch (IOException ex) {
|
|
||||||
logger.log(Level.WARNING, ex.getMessage(), ex);
|
|
||||||
}
|
|
||||||
return len;
|
return len;
|
||||||
};
|
};
|
||||||
GhostscriptLibrary.stderr_fn stderrCallback;
|
GhostscriptLibrary.stderr_fn stderrCallback = (caller_handle, str, len) -> {
|
||||||
if (getStdErr() == null) {
|
logger.log(Level.FINE, str.substring(0, len));
|
||||||
setStdErr(new NullOutputStream());
|
|
||||||
}
|
|
||||||
stderrCallback = (caller_handle, str, len) -> {
|
|
||||||
try {
|
|
||||||
getStdErr().write(str.getBytes(), 0, len);
|
|
||||||
} catch (IOException ex) {
|
|
||||||
logger.log(Level.WARNING, ex.getMessage(), ex);
|
|
||||||
}
|
|
||||||
return len;
|
return len;
|
||||||
};
|
};
|
||||||
logger.log(Level.FINE, "setting gsapi_set_stdio");
|
logger.log(Level.FINE, "setting gsapi_set_stdio");
|
||||||
|
@ -197,8 +105,8 @@ public class Ghostscript implements AutoCloseable {
|
||||||
if (ret != 0) {
|
if (ret != 0) {
|
||||||
throw new IOException("can not set stdio on Ghostscript interpreter, error code " + ret);
|
throw new IOException("can not set stdio on Ghostscript interpreter, error code " + ret);
|
||||||
}
|
}
|
||||||
logger.log(Level.FINE, "gsapi_init_with_args = " + (args != null ? Arrays.asList(args) : "null"));
|
logger.log(Level.FINE, "gsapi_init_with_args = " + fullArgList);
|
||||||
ret = libraryInstance.gsapi_init_with_args(pointer, args != null ? args.length : 0, args);
|
ret = libraryInstance.gsapi_init_with_args(pointer, fullArgList.size(), fullArgList.toArray(new String[0]));
|
||||||
logger.log(Level.FINE, "gsapi_init_with_args return code = " + ret);
|
logger.log(Level.FINE, "gsapi_init_with_args return code = " + ret);
|
||||||
if (ret == ErrorCodes.gs_error_Quit) {
|
if (ret == ErrorCodes.gs_error_Quit) {
|
||||||
quit = true;
|
quit = true;
|
||||||
|
@ -246,12 +154,12 @@ public class Ghostscript implements AutoCloseable {
|
||||||
Pointer pointer = nativeInstanceByRef.getValue();
|
Pointer pointer = nativeInstanceByRef.getValue();
|
||||||
if (pointer != null) {
|
if (pointer != null) {
|
||||||
if (!quit && ret >= 0) {
|
if (!quit && ret >= 0) {
|
||||||
logger.log(Level.INFO, "exiting ghostscript instance " + libraryInstance);
|
logger.log(Level.FINE, "exiting ghostscript instance " + libraryInstance);
|
||||||
ret = libraryInstance.gsapi_exit(pointer);
|
ret = libraryInstance.gsapi_exit(pointer);
|
||||||
if (ret != 0) {
|
if (ret != 0) {
|
||||||
logger.log(Level.SEVERE, "can not call Ghostscript gsapi_exit, error code " + ret);
|
logger.log(Level.SEVERE, "can not call Ghostscript gsapi_exit, error code " + ret);
|
||||||
}
|
}
|
||||||
logger.log(Level.INFO, "deleting ghostscript instance " + pointer);
|
logger.log(Level.FINE, "deleting ghostscript instance " + pointer);
|
||||||
libraryInstance.gsapi_delete_instance(pointer);
|
libraryInstance.gsapi_delete_instance(pointer);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -265,22 +173,10 @@ public class Ghostscript implements AutoCloseable {
|
||||||
@Override
|
@Override
|
||||||
public synchronized void close() throws IOException {
|
public synchronized void close() throws IOException {
|
||||||
if (closed.compareAndSet(false, true)) {
|
if (closed.compareAndSet(false, true)) {
|
||||||
if (stdIn != null) {
|
|
||||||
stdIn.close();
|
|
||||||
stdIn = null;
|
|
||||||
}
|
|
||||||
if (stdOut != null) {
|
|
||||||
stdOut.close();
|
|
||||||
stdOut = null;
|
|
||||||
}
|
|
||||||
if (stdErr != null) {
|
|
||||||
stdErr.close();
|
|
||||||
stdErr = null;
|
|
||||||
}
|
|
||||||
if (libraryInstance != null) {
|
if (libraryInstance != null) {
|
||||||
libraryInstance.dispose();
|
libraryInstance.dispose();
|
||||||
System.gc();
|
System.gc();
|
||||||
logger.log(Level.INFO, "ghostscript library instance disposed and gc'ed()");
|
logger.log(Level.FINE, "ghostscript library instance disposed and gc'ed()");
|
||||||
} else {
|
} else {
|
||||||
logger.log(Level.WARNING, "ghostscript library instance is null");
|
logger.log(Level.WARNING, "ghostscript library instance is null");
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package org.xbib.graphics.ghostscript;
|
package org.xbib.graphics.ghostscript;
|
||||||
|
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
import org.xbib.graphics.ghostscript.internal.LoggingOutputStream;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
@ -119,21 +118,25 @@ public class PDFConverter {
|
||||||
int ret;
|
int ret;
|
||||||
Path tmpPath = createTmpPath();
|
Path tmpPath = createTmpPath();
|
||||||
try (Ghostscript gs = new Ghostscript()) {
|
try (Ghostscript gs = new Ghostscript()) {
|
||||||
|
Path input = Files.createTempFile(tmpPath, "pdf", "pdf");
|
||||||
|
try (OutputStream sourceOutputStream = Files.newOutputStream(input)) {
|
||||||
|
inputStream.transferTo(sourceOutputStream);
|
||||||
|
}
|
||||||
Path output = Files.createTempFile(tmpPath, "pdf", "pdf");
|
Path output = Files.createTempFile(tmpPath, "pdf", "pdf");
|
||||||
List<String> gsArgs = new LinkedList<>(customGsArgs);
|
List<String> gsArgs = new LinkedList<>(customGsArgs);
|
||||||
gsArgs.add("-dNOSAFER");
|
gsArgs.add("-dNOSAFER");
|
||||||
//gsArgs.add("-dNOPAUSE");
|
gsArgs.add("-dNOPAUSE");
|
||||||
//gsArgs.add("-dBATCH");
|
gsArgs.add("-dBATCH");
|
||||||
switch (autoRotatePages) {
|
switch (autoRotatePages) {
|
||||||
case OPTION_AUTOROTATEPAGES_ALL -> gsArgs.add("-dAutoRotatePages=/All");
|
case OPTION_AUTOROTATEPAGES_ALL -> gsArgs.add("-dAutoRotatePages=/All");
|
||||||
case OPTION_AUTOROTATEPAGES_PAGEBYPAGE -> gsArgs.add("-dAutoRotatePages=/PageByPage");
|
case OPTION_AUTOROTATEPAGES_PAGEBYPAGE -> gsArgs.add("-dAutoRotatePages=/PageByPage");
|
||||||
default -> gsArgs.add("-dAutoRotatePages=/None");
|
default -> gsArgs.add("-dAutoRotatePages=/None");
|
||||||
}
|
}
|
||||||
/*switch (processColorModel) {
|
switch (processColorModel) {
|
||||||
case OPTION_PROCESSCOLORMODEL_CMYK -> gsArgs.add("-dProcessColorModel=/DeviceCMYK");
|
case OPTION_PROCESSCOLORMODEL_CMYK -> gsArgs.add("-dProcessColorModel=/DeviceCMYK");
|
||||||
case OPTION_PROCESSCOLORMODEL_GRAY -> gsArgs.add("-dProcessColorModel=/DeviceGray");
|
case OPTION_PROCESSCOLORMODEL_GRAY -> gsArgs.add("-dProcessColorModel=/DeviceGray");
|
||||||
default -> gsArgs.add("-dProcessColorModel=/DeviceRGB");
|
default -> gsArgs.add("-dProcessColorModel=/DeviceRGB");
|
||||||
}*/
|
}
|
||||||
switch (pdfsettings) {
|
switch (pdfsettings) {
|
||||||
case OPTION_PDFSETTINGS_EBOOK -> gsArgs.add("-dPDFSETTINGS=/ebook");
|
case OPTION_PDFSETTINGS_EBOOK -> gsArgs.add("-dPDFSETTINGS=/ebook");
|
||||||
case OPTION_PDFSETTINGS_SCREEN -> gsArgs.add("-dPDFSETTINGS=/screen");
|
case OPTION_PDFSETTINGS_SCREEN -> gsArgs.add("-dPDFSETTINGS=/screen");
|
||||||
|
@ -150,11 +153,8 @@ public class PDFConverter {
|
||||||
gsArgs.add("-sDEVICE=pdfwrite");
|
gsArgs.add("-sDEVICE=pdfwrite");
|
||||||
gsArgs.add("-sOutputFile=" + output.toAbsolutePath());
|
gsArgs.add("-sOutputFile=" + output.toAbsolutePath());
|
||||||
gsArgs.add("-f");
|
gsArgs.add("-f");
|
||||||
gsArgs.add("-");
|
gsArgs.add(input.toString());
|
||||||
gs.setStdIn(inputStream);
|
ret = gs.run(gsArgs);
|
||||||
gs.setStdOut(new LoggingOutputStream(logger));
|
|
||||||
gs.setStdErr(new LoggingOutputStream(logger));
|
|
||||||
ret = gs.run(gsArgs.toArray(new String[0]));
|
|
||||||
Files.copy(output.toAbsolutePath(), outputStream);
|
Files.copy(output.toAbsolutePath(), outputStream);
|
||||||
} finally {
|
} finally {
|
||||||
deleteTmpPath(tmpPath);
|
deleteTmpPath(tmpPath);
|
||||||
|
@ -162,9 +162,6 @@ public class PDFConverter {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private static void deleteTmpPath(Path path) throws IOException {
|
private static void deleteTmpPath(Path path) throws IOException {
|
||||||
if (path == null) {
|
if (path == null) {
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -15,7 +15,6 @@ import org.apache.pdfbox.pdmodel.PDPageContentStream;
|
||||||
import org.apache.pdfbox.pdmodel.common.PDRectangle;
|
import org.apache.pdfbox.pdmodel.common.PDRectangle;
|
||||||
import org.apache.pdfbox.pdmodel.graphics.image.LosslessFactory;
|
import org.apache.pdfbox.pdmodel.graphics.image.LosslessFactory;
|
||||||
import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
|
import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
|
||||||
import org.xbib.graphics.ghostscript.internal.LoggingOutputStream;
|
|
||||||
|
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
import java.io.BufferedOutputStream;
|
import java.io.BufferedOutputStream;
|
||||||
|
@ -72,8 +71,9 @@ public class PDFRasterizer {
|
||||||
this.subject = subject;
|
this.subject = subject;
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void convert(Path source, Path target) throws IOException {
|
public synchronized void convert(Path source,
|
||||||
logger.info("convert source=" + source + " target=" + target);
|
Path target) throws IOException {
|
||||||
|
logger.log(Level.FINE, "convert source=" + source + " target=" + target);
|
||||||
if (!Files.exists(source)) {
|
if (!Files.exists(source)) {
|
||||||
throw new FileNotFoundException(source.toString());
|
throw new FileNotFoundException(source.toString());
|
||||||
}
|
}
|
||||||
|
@ -86,11 +86,11 @@ public class PDFRasterizer {
|
||||||
Path tmpPath = createTmpPath();
|
Path tmpPath = createTmpPath();
|
||||||
try {
|
try {
|
||||||
Path path = Files.createTempDirectory(tmpPath, "pdf-rasterize");
|
Path path = Files.createTempDirectory(tmpPath, "pdf-rasterize");
|
||||||
pdfToImage(source, path, "pdf", null);
|
pdfToImage(source, path, "pdf");
|
||||||
Path tmpTarget = path.resolve(target.getFileName());
|
Path tmpTarget = path.resolve(target.getFileName());
|
||||||
mergeImagesToPDF(path, tmpTarget);
|
mergeImagesToPDF(path, tmpTarget);
|
||||||
scalePDF(tmpTarget, target);
|
scalePDF(tmpTarget, target);
|
||||||
logger.info("convert source=" + source + " done");
|
logger.log(Level.FINE, "convert source=" + source + " done");
|
||||||
} finally {
|
} finally {
|
||||||
deleteTmpPath(tmpPath);
|
deleteTmpPath(tmpPath);
|
||||||
}
|
}
|
||||||
|
@ -98,7 +98,7 @@ public class PDFRasterizer {
|
||||||
|
|
||||||
public synchronized void screenConvert(Path source,
|
public synchronized void screenConvert(Path source,
|
||||||
Path target) throws IOException {
|
Path target) throws IOException {
|
||||||
logger.info("screen convert source=" + source.toAbsolutePath() + " target=" + target);
|
logger.log(Level.FINE, "screen convert source=" + source.toAbsolutePath() + " target=" + target);
|
||||||
if (!Files.exists(source.toAbsolutePath())) {
|
if (!Files.exists(source.toAbsolutePath())) {
|
||||||
throw new FileNotFoundException(source.toString());
|
throw new FileNotFoundException(source.toString());
|
||||||
}
|
}
|
||||||
|
@ -116,14 +116,15 @@ public class PDFRasterizer {
|
||||||
mergeImagesToPDF(path, tmpTarget);
|
mergeImagesToPDF(path, tmpTarget);
|
||||||
//toPDFA(tmpTarget, target);
|
//toPDFA(tmpTarget, target);
|
||||||
scalePDF(tmpTarget, target);
|
scalePDF(tmpTarget, target);
|
||||||
logger.info("screen convert source=" + source.toAbsolutePath() + " done");
|
logger.log(Level.FINE, "screen convert source=" + source.toAbsolutePath() + " done");
|
||||||
} finally {
|
} finally {
|
||||||
deleteTmpPath(tmpPath);
|
deleteTmpPath(tmpPath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void pdfToGrayScreenImage(Path source, Path target) throws IOException {
|
public synchronized void pdfToGrayScreenImage(Path source,
|
||||||
logger.info("pdfToGrayScreenImage source=" + source + " target=" + target);
|
Path target) throws IOException {
|
||||||
|
logger.log(Level.FINE, "pdfToGrayScreenImage source=" + source + " target=" + target);
|
||||||
if (!Files.exists(source.toAbsolutePath())) {
|
if (!Files.exists(source.toAbsolutePath())) {
|
||||||
throw new FileNotFoundException("not found: " + source);
|
throw new FileNotFoundException("not found: " + source);
|
||||||
}
|
}
|
||||||
|
@ -145,21 +146,25 @@ public class PDFRasterizer {
|
||||||
gsArgs.add("-sOutputFile=" + target.resolve("screen-gray-pdf-%05d.png"));
|
gsArgs.add("-sOutputFile=" + target.resolve("screen-gray-pdf-%05d.png"));
|
||||||
gsArgs.add("-f");
|
gsArgs.add("-f");
|
||||||
gsArgs.add(source.toString());
|
gsArgs.add(source.toString());
|
||||||
logger.info("pdfToImage args=" + gsArgs);
|
logger.log(Level.FINE, "pdfToImage args=" + gsArgs);
|
||||||
try (Ghostscript gs = new Ghostscript()) {
|
try (Ghostscript gs = new Ghostscript()) {
|
||||||
gs.setStdIn(null);
|
gs.run(gsArgs);
|
||||||
gs.setStdOut(new LoggingOutputStream(logger));
|
logger.log(Level.FINE, "pdfToImage done");
|
||||||
gs.setStdErr(new LoggingOutputStream(logger));
|
|
||||||
gs.run(gsArgs.toArray(new String[0]));
|
|
||||||
logger.info("pdfToImage done");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void pdfToImage(Path sourceFile,
|
||||||
|
Path targetDir,
|
||||||
|
String prefix) throws IOException {
|
||||||
|
pdfToImage(sourceFile, targetDir, prefix, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
public synchronized void pdfToImage(Path sourceFile,
|
public synchronized void pdfToImage(Path sourceFile,
|
||||||
Path targetDir,
|
Path targetDir,
|
||||||
String prefix,
|
String prefix,
|
||||||
String pageRange) throws IOException {
|
String firstPage,
|
||||||
logger.info("pdfToImage source=" + sourceFile + " target=" + targetDir + " started");
|
String lastPage) throws IOException {
|
||||||
|
logger.log(Level.FINE, "pdfToImage source=" + sourceFile + " target=" + targetDir + " started");
|
||||||
List<String> gsArgs = new LinkedList<>();
|
List<String> gsArgs = new LinkedList<>();
|
||||||
gsArgs.add("-dNOPAUSE");
|
gsArgs.add("-dNOPAUSE");
|
||||||
gsArgs.add("-dBATCH");
|
gsArgs.add("-dBATCH");
|
||||||
|
@ -168,9 +173,11 @@ public class PDFRasterizer {
|
||||||
gsArgs.add("-dINTERPOLATE");
|
gsArgs.add("-dINTERPOLATE");
|
||||||
// how do we know we have a crop box or not?
|
// how do we know we have a crop box or not?
|
||||||
gsArgs.add("-dUseCropBox");
|
gsArgs.add("-dUseCropBox");
|
||||||
// page range, if not null
|
if (firstPage != null) {
|
||||||
if (pageRange != null) {
|
gsArgs.add("-sFirstPage=" + firstPage);
|
||||||
gsArgs.add("-sPageList=" + pageRange);
|
}
|
||||||
|
if (lastPage != null) {
|
||||||
|
gsArgs.add("-sLastPage=" + lastPage);
|
||||||
}
|
}
|
||||||
gsArgs.add("-sDEVICE=png16m");
|
gsArgs.add("-sDEVICE=png16m");
|
||||||
gsArgs.add("-r300");
|
gsArgs.add("-r300");
|
||||||
|
@ -182,23 +189,21 @@ public class PDFRasterizer {
|
||||||
gsArgs.add("100000000 setvmthreshold");
|
gsArgs.add("100000000 setvmthreshold");
|
||||||
gsArgs.add("-f");
|
gsArgs.add("-f");
|
||||||
gsArgs.add(sourceFile.toString());
|
gsArgs.add(sourceFile.toString());
|
||||||
logger.info("pdfToImage args=" + gsArgs);
|
|
||||||
try (Ghostscript gs = new Ghostscript()) {
|
try (Ghostscript gs = new Ghostscript()) {
|
||||||
// reset stdin
|
gs.run(gsArgs);
|
||||||
gs.setStdIn(null);
|
logger.log(Level.FINE, "pdfToImage done");
|
||||||
gs.setStdOut(new LoggingOutputStream(logger));
|
|
||||||
gs.setStdErr(new LoggingOutputStream(logger));
|
|
||||||
gs.run(gsArgs.toArray(new String[0]));
|
|
||||||
logger.info("pdfToImage done");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public int mergeImagesToPDF(Path sourceDir, Path targetFile) throws IOException {
|
public int mergeImagesToPDF(Path sourceDir,
|
||||||
|
Path targetFile) throws IOException {
|
||||||
return mergeImagesToPDF(sourceDir, targetFile, "**/*");
|
return mergeImagesToPDF(sourceDir, targetFile, "**/*");
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized int mergeImagesToPDF(Path sourceDir, Path targetFile, String globPattern) throws IOException {
|
public synchronized int mergeImagesToPDF(Path sourceDir,
|
||||||
logger.info("mergeImagesToPDF: source=" + sourceDir + " target=" + targetFile + " glob =" + globPattern);
|
Path targetFile,
|
||||||
|
String globPattern) throws IOException {
|
||||||
|
logger.log(Level.FINE, "mergeImagesToPDF: source=" + sourceDir + " target=" + targetFile + " glob =" + globPattern);
|
||||||
AtomicInteger pagecount = new AtomicInteger();
|
AtomicInteger pagecount = new AtomicInteger();
|
||||||
PathMatcher pathMatcher = sourceDir.getFileSystem().getPathMatcher("glob:" + globPattern);
|
PathMatcher pathMatcher = sourceDir.getFileSystem().getPathMatcher("glob:" + globPattern);
|
||||||
List<PDDocument> coverPageDocs = new ArrayList<>();
|
List<PDDocument> coverPageDocs = new ArrayList<>();
|
||||||
|
@ -223,7 +228,7 @@ public class PDFRasterizer {
|
||||||
}
|
}
|
||||||
for (Path path : entries) {
|
for (Path path : entries) {
|
||||||
if (path.getFileName().toString().toLowerCase(Locale.ROOT).endsWith(".pdf")) {
|
if (path.getFileName().toString().toLowerCase(Locale.ROOT).endsWith(".pdf")) {
|
||||||
logger.info("found pdf " + path);
|
logger.log(Level.FINE, "found pdf " + path);
|
||||||
try (InputStream inputStream = Files.newInputStream(path)) {
|
try (InputStream inputStream = Files.newInputStream(path)) {
|
||||||
PDDocument doc = Loader.loadPDF(inputStream.readAllBytes());
|
PDDocument doc = Loader.loadPDF(inputStream.readAllBytes());
|
||||||
for (int i = 0; i < doc.getNumberOfPages(); i++) {
|
for (int i = 0; i < doc.getNumberOfPages(); i++) {
|
||||||
|
@ -267,7 +272,7 @@ public class PDFRasterizer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pdDocument.save(outputStream);
|
pdDocument.save(outputStream);
|
||||||
logger.info("mergeImagesToPDF: done, " + pagecount.get() + " pages");
|
logger.log(Level.FINE, "pagesToPDF: done, " + pagecount.get() + " pages");
|
||||||
} finally {
|
} finally {
|
||||||
for (PDDocument pd : coverPageDocs) {
|
for (PDDocument pd : coverPageDocs) {
|
||||||
pd.close();
|
pd.close();
|
||||||
|
@ -278,7 +283,7 @@ public class PDFRasterizer {
|
||||||
|
|
||||||
public synchronized void scalePDF(Path sourceFile,
|
public synchronized void scalePDF(Path sourceFile,
|
||||||
Path targetFile) throws IOException {
|
Path targetFile) throws IOException {
|
||||||
logger.info("scalePDF: source = " + sourceFile + " target = " + targetFile + " starting");
|
logger.log(Level.FINE, "scalePDF: source = " + sourceFile + " target = " + targetFile + " starting");
|
||||||
List<String> gsArgs = new LinkedList<>();
|
List<String> gsArgs = new LinkedList<>();
|
||||||
gsArgs.add("-dNOPAUSE");
|
gsArgs.add("-dNOPAUSE");
|
||||||
gsArgs.add("-dBATCH");
|
gsArgs.add("-dBATCH");
|
||||||
|
@ -293,16 +298,14 @@ public class PDFRasterizer {
|
||||||
gsArgs.add("-sOutputFile=" + targetFile.toString());
|
gsArgs.add("-sOutputFile=" + targetFile.toString());
|
||||||
gsArgs.add(sourceFile.toString());
|
gsArgs.add(sourceFile.toString());
|
||||||
try (Ghostscript gs = new Ghostscript()) {
|
try (Ghostscript gs = new Ghostscript()) {
|
||||||
gs.setStdIn(null);
|
gs.run(gsArgs);
|
||||||
logger.info(gsArgs.toString());
|
logger.log(Level.FINE, "scalePDF: source = " + sourceFile + " target = " + targetFile + " done");
|
||||||
gs.run(gsArgs.toArray(new String[0]));
|
|
||||||
logger.info("scalePDF: source = " + sourceFile + " target = " + targetFile + " done");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void downgradePDF(Path sourceFile,
|
public synchronized void downgradePDF(Path sourceFile,
|
||||||
Path targetFile) throws IOException {
|
Path targetFile) throws IOException {
|
||||||
logger.log(Level.INFO, "downgradePDF: source = " + sourceFile + " target = " + targetFile + " starting");
|
logger.log(Level.FINE, "downgradePDF: source = " + sourceFile + " target = " + targetFile + " starting");
|
||||||
List<String> gsArgs = new LinkedList<>();
|
List<String> gsArgs = new LinkedList<>();
|
||||||
gsArgs.add("-dNOPAUSE");
|
gsArgs.add("-dNOPAUSE");
|
||||||
gsArgs.add("-dBATCH");
|
gsArgs.add("-dBATCH");
|
||||||
|
@ -314,12 +317,8 @@ public class PDFRasterizer {
|
||||||
gsArgs.add("-sOutputFile=" + targetFile.toString());
|
gsArgs.add("-sOutputFile=" + targetFile.toString());
|
||||||
gsArgs.add(sourceFile.toString());
|
gsArgs.add(sourceFile.toString());
|
||||||
try (Ghostscript gs = new Ghostscript()) {
|
try (Ghostscript gs = new Ghostscript()) {
|
||||||
gs.setStdIn(null);
|
gs.run(gsArgs);
|
||||||
gs.setStdOut(new LoggingOutputStream(logger));
|
logger.log(Level.FINE, "downgradePDF: source = " + sourceFile + " target = " + targetFile + " done");
|
||||||
gs.setStdErr(new LoggingOutputStream(logger));
|
|
||||||
logger.log(Level.INFO, gsArgs.toString());
|
|
||||||
gs.run(gsArgs.toArray(new String[0]));
|
|
||||||
logger.log(Level.INFO, "downgradePDF: source = " + sourceFile + " target = " + targetFile + " done");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -357,14 +356,16 @@ public class PDFRasterizer {
|
||||||
gsArgs.add("-sOutputFile=" + target.toString());
|
gsArgs.add("-sOutputFile=" + target.toString());
|
||||||
gsArgs.add(pdfapsPath.toAbsolutePath().toString());
|
gsArgs.add(pdfapsPath.toAbsolutePath().toString());
|
||||||
gsArgs.add(source.toString());
|
gsArgs.add(source.toString());
|
||||||
gs.setStdIn(null);
|
gs.run(gsArgs);
|
||||||
gs.run(gsArgs.toArray(new String[0]));
|
|
||||||
} finally {
|
} finally {
|
||||||
deleteTmpPath(tmpPath);
|
deleteTmpPath(tmpPath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void copyAndReplace(Path source, Path target, String from, String to) throws IOException {
|
private static void copyAndReplace(Path source,
|
||||||
|
Path target,
|
||||||
|
String from,
|
||||||
|
String to) throws IOException {
|
||||||
try (BufferedReader br = Files.newBufferedReader(source); BufferedWriter bw = Files.newBufferedWriter(target)) {
|
try (BufferedReader br = Files.newBufferedReader(source); BufferedWriter bw = Files.newBufferedWriter(target)) {
|
||||||
String line;
|
String line;
|
||||||
while ((line = br.readLine()) != null) {
|
while ((line = br.readLine()) != null) {
|
||||||
|
|
|
@ -1,57 +0,0 @@
|
||||||
package org.xbib.graphics.ghostscript;
|
|
||||||
|
|
||||||
public class PageRaster {
|
|
||||||
|
|
||||||
private int width;
|
|
||||||
|
|
||||||
private int height;
|
|
||||||
|
|
||||||
private int raster;
|
|
||||||
|
|
||||||
private int format;
|
|
||||||
|
|
||||||
private byte[] data;
|
|
||||||
|
|
||||||
public PageRaster() {
|
|
||||||
}
|
|
||||||
|
|
||||||
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 byte[] getData() {
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setData(byte[] data) {
|
|
||||||
this.data = data;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -28,11 +28,13 @@ public class GhostscriptLibraryLoader {
|
||||||
|
|
||||||
private GhostscriptLibrary ghostscriptLibrary;
|
private GhostscriptLibrary ghostscriptLibrary;
|
||||||
|
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
public GhostscriptLibraryLoader() {
|
public GhostscriptLibraryLoader() {
|
||||||
Map<String, Object> options = Map.of(Library.OPTION_CALLING_CONVENTION, Function.C_CONVENTION,
|
Map<String, Object> options = Map.of(Library.OPTION_CALLING_CONVENTION, Function.C_CONVENTION,
|
||||||
Library.OPTION_INVOCATION_MAPPER, (InvocationMapper) (lib, m) -> {
|
Library.OPTION_INVOCATION_MAPPER, (InvocationMapper) (lib, m) -> {
|
||||||
if (m.getName().equals("dispose")) {
|
if (m.getName().equals("dispose")) {
|
||||||
return (proxy, method, args) -> {
|
return (proxy, method, args) -> {
|
||||||
|
// we use the deprecated dispose() call here by intention
|
||||||
lib.dispose();
|
lib.dispose();
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,26 +0,0 @@
|
||||||
package org.xbib.graphics.ghostscript.internal;
|
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.logging.Level;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
public class LoggingOutputStream extends ByteArrayOutputStream {
|
|
||||||
|
|
||||||
private final Logger logger;
|
|
||||||
|
|
||||||
public LoggingOutputStream(Logger logger) {
|
|
||||||
super();
|
|
||||||
this.logger = logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void flush() throws IOException {
|
|
||||||
super.flush();
|
|
||||||
String s = new String(buf, 0, count);
|
|
||||||
if (s.length() > 0) {
|
|
||||||
logger.log(Level.FINE, s);
|
|
||||||
}
|
|
||||||
reset();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,17 +0,0 @@
|
||||||
package org.xbib.graphics.ghostscript.internal;
|
|
||||||
|
|
||||||
import java.io.OutputStream;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class NullOutputStream extends OutputStream {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void write(int b) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void write(byte[] b, int off, int len) {
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,24 +1,24 @@
|
||||||
package org.xbib.graphics.ghostscript.test;
|
package org.xbib.graphics.ghostscript.test;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import org.junit.jupiter.api.Disabled;
|
import java.io.OutputStream;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.util.List;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.xbib.graphics.ghostscript.Ghostscript;
|
import org.xbib.graphics.ghostscript.Ghostscript;
|
||||||
import org.xbib.graphics.ghostscript.GhostscriptRevision;
|
import org.xbib.graphics.ghostscript.GhostscriptRevision;
|
||||||
import org.xbib.graphics.ghostscript.internal.LoggingOutputStream;
|
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
import static org.junit.jupiter.api.Assertions.fail;
|
import static org.junit.jupiter.api.Assertions.fail;
|
||||||
|
|
||||||
public class GhostscriptTest {
|
public class BasicGhostscriptTest {
|
||||||
|
|
||||||
private static final Logger logger = Logger.getLogger(GhostscriptTest.class.getName());
|
private static final Logger logger = Logger.getLogger(BasicGhostscriptTest.class.getName());
|
||||||
|
|
||||||
private static final String dir = "src/test/resources/org/xbib/graphics/ghostscript/test/";
|
private static final String dir = "src/test/resources/org/xbib/graphics/ghostscript/test/";
|
||||||
|
|
||||||
|
@ -40,7 +40,7 @@ public class GhostscriptTest {
|
||||||
@Test
|
@Test
|
||||||
public void testExit() {
|
public void testExit() {
|
||||||
try (Ghostscript gs = new Ghostscript()) {
|
try (Ghostscript gs = new Ghostscript()) {
|
||||||
String[] args = {"-dNODISPLAY", "-dQUIET"};
|
List<String> args = List.of("-dNODISPLAY", "-dQUIET");
|
||||||
gs.run(args);
|
gs.run(args);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logger.log(Level.SEVERE, e.getMessage(), e);
|
logger.log(Level.SEVERE, e.getMessage(), e);
|
||||||
|
@ -51,7 +51,7 @@ public class GhostscriptTest {
|
||||||
@Test
|
@Test
|
||||||
public void testRunString() {
|
public void testRunString() {
|
||||||
try (Ghostscript gs = new Ghostscript()) {
|
try (Ghostscript gs = new Ghostscript()) {
|
||||||
String[] args = {"-dNODISPLAY", "-dQUIET"};
|
List<String> args = List.of("-dNODISPLAY", "-dQUIET");
|
||||||
gs.run(args, "devicenames ==", null);
|
gs.run(args, "devicenames ==", null);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logger.log(Level.SEVERE, e.getMessage(), e);
|
logger.log(Level.SEVERE, e.getMessage(), e);
|
||||||
|
@ -64,7 +64,7 @@ public class GhostscriptTest {
|
||||||
@Test
|
@Test
|
||||||
public void testRunFile() {
|
public void testRunFile() {
|
||||||
try (Ghostscript gs = new Ghostscript()) {
|
try (Ghostscript gs = new Ghostscript()) {
|
||||||
String[] args = {"-dNODISPLAY", "-dQUIET", "-dNOPAUSE", "-dBATCH", "-dSAFER"};
|
List<String> args = List.of("-dNODISPLAY", "-dQUIET", "-dNOPAUSE", "-dBATCH", "-dSAFER");
|
||||||
gs.run(args, null, dir + "input.ps");
|
gs.run(args, null, dir + "input.ps");
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logger.log(Level.SEVERE, e.getMessage(), e);
|
logger.log(Level.SEVERE, e.getMessage(), e);
|
||||||
|
@ -75,33 +75,37 @@ public class GhostscriptTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testStdOut() {
|
public void testStdOut() throws Exception {
|
||||||
try (Ghostscript gs = new Ghostscript();
|
Path path = Paths.get("build/devicenames");
|
||||||
InputStream is = new ByteArrayInputStream("devicenames ==\n".getBytes())) {
|
try (OutputStream outputStream = Files.newOutputStream(path)) {
|
||||||
gs.setStdIn(is);
|
outputStream.write("devicenames ==\n".getBytes());
|
||||||
gs.setStdOut(new LoggingOutputStream(logger));
|
}
|
||||||
gs.setStdErr(new LoggingOutputStream(logger));
|
try (Ghostscript gs = new Ghostscript()) {
|
||||||
String[] args = { "-dNODISPLAY", "-sOutputFile=%stdout", "-f", "-" };
|
List<String> args = List.of("-dNODISPLAY", "-sOutputFile=%stdout", "-f", path.toString());
|
||||||
gs.run(args);
|
int ret = gs.run(args);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
fail(e.getMessage());
|
fail(e.getMessage());
|
||||||
|
} finally {
|
||||||
|
Files.delete(path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testStdErr() {
|
public void testStdErr() throws Exception {
|
||||||
try (Ghostscript gs = new Ghostscript();
|
Path path = Paths.get("build/broken");
|
||||||
InputStream is = new ByteArrayInputStream("stupid\n".getBytes())) {
|
try (OutputStream outputStream = Files.newOutputStream(path)) {
|
||||||
gs.setStdIn(is);
|
outputStream.write("foobar\n".getBytes());
|
||||||
gs.setStdOut(new LoggingOutputStream(logger));
|
}
|
||||||
gs.setStdErr(new LoggingOutputStream(logger));
|
try (Ghostscript gs = new Ghostscript()) {
|
||||||
String[] args = { "-dNODISPLAY", "-sOutputFile=%stdout", "-f", "-" };
|
List<String> args = List.of("-dNODISPLAY", "-sOutputFile=%stdout", "-f", path.toString());
|
||||||
gs.run(args);
|
int ret = gs.run(args);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
// expect error
|
// expect error
|
||||||
if (!e.getMessage().contains("error code = -100")) {
|
if (!e.getMessage().contains("error code = -100")) {
|
||||||
fail(e.getMessage());
|
fail(e.getMessage());
|
||||||
}
|
}
|
||||||
|
} finally {
|
||||||
|
Files.delete(path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,11 +1,15 @@
|
||||||
package org.xbib.graphics.ghostscript.test;
|
package org.xbib.graphics.ghostscript.test;
|
||||||
|
|
||||||
import com.sun.jna.ptr.IntByReference;
|
import com.sun.jna.ptr.IntByReference;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
import org.xbib.graphics.ghostscript.internal.GhostscriptLibrary;
|
import org.xbib.graphics.ghostscript.internal.GhostscriptLibrary;
|
||||||
import org.xbib.graphics.ghostscript.internal.GhostscriptLibraryLoader;
|
import org.xbib.graphics.ghostscript.internal.GhostscriptLibraryLoader;
|
||||||
|
|
||||||
public class GhostScriptLibraryTester {
|
public class GhostScriptLibraryTester {
|
||||||
|
|
||||||
|
private static final Logger logger = Logger.getLogger(GhostScriptLibraryTester.class.getName());
|
||||||
|
|
||||||
private final GhostscriptLibrary ghostscriptLibrary;
|
private final GhostscriptLibrary ghostscriptLibrary;
|
||||||
|
|
||||||
public GhostScriptLibraryTester() {
|
public GhostScriptLibraryTester() {
|
||||||
|
@ -33,7 +37,7 @@ public class GhostScriptLibraryTester {
|
||||||
GhostscriptLibrary.gs_main_instance.ByReference instanceByRef = new GhostscriptLibrary.gs_main_instance.ByReference();
|
GhostscriptLibrary.gs_main_instance.ByReference instanceByRef = new GhostscriptLibrary.gs_main_instance.ByReference();
|
||||||
ghostscriptLibrary.gsapi_new_instance(instanceByRef.getPointer(), null);
|
ghostscriptLibrary.gsapi_new_instance(instanceByRef.getPointer(), null);
|
||||||
String[] args = {
|
String[] args = {
|
||||||
"-dNODISPLAY", "-dNOPAUSE", "-dBATCH", "-dSAFER"
|
"gs", "-dNODISPLAY", "-dNOPAUSE", "-dBATCH", "-dSAFER"
|
||||||
};
|
};
|
||||||
ghostscriptLibrary.gsapi_init_with_args(instanceByRef.getValue(), args.length, args);
|
ghostscriptLibrary.gsapi_init_with_args(instanceByRef.getValue(), args.length, args);
|
||||||
IntByReference exitCode = new IntByReference();
|
IntByReference exitCode = new IntByReference();
|
||||||
|
@ -47,10 +51,8 @@ public class GhostScriptLibraryTester {
|
||||||
GhostscriptLibrary.gs_main_instance.ByReference instanceByRef = new GhostscriptLibrary.gs_main_instance.ByReference();
|
GhostscriptLibrary.gs_main_instance.ByReference instanceByRef = new GhostscriptLibrary.gs_main_instance.ByReference();
|
||||||
ghostscriptLibrary.gsapi_new_instance(instanceByRef.getPointer(), null);
|
ghostscriptLibrary.gsapi_new_instance(instanceByRef.getPointer(), null);
|
||||||
String[] args = {
|
String[] args = {
|
||||||
"ps2pdf",
|
"ps2pdf", "-dNODISPLAY", "-dNOPAUSE", "-dBATCH", "-dSAFER", "-sDEVICE=pdfwrite",
|
||||||
"-dNODISPLAY", "-dNOPAUSE", "-dBATCH", "-dSAFER", "-sDEVICE=pdfwrite",
|
"-sOutputFile=" + output, "-f", input
|
||||||
"-sOutputFile=" + output,
|
|
||||||
"-c", ".setpdfwrite", "-f", input
|
|
||||||
};
|
};
|
||||||
int result = ghostscriptLibrary.gsapi_init_with_args(instanceByRef.getValue(), args.length, args);
|
int result = ghostscriptLibrary.gsapi_init_with_args(instanceByRef.getValue(), args.length, args);
|
||||||
ghostscriptLibrary.gsapi_exit(instanceByRef.getValue());
|
ghostscriptLibrary.gsapi_exit(instanceByRef.getValue());
|
||||||
|
@ -62,7 +64,7 @@ public class GhostScriptLibraryTester {
|
||||||
GhostscriptLibrary.gs_main_instance.ByReference instanceByRef = new GhostscriptLibrary.gs_main_instance.ByReference();
|
GhostscriptLibrary.gs_main_instance.ByReference instanceByRef = new GhostscriptLibrary.gs_main_instance.ByReference();
|
||||||
ghostscriptLibrary.gsapi_new_instance(instanceByRef.getPointer(), null);
|
ghostscriptLibrary.gsapi_new_instance(instanceByRef.getPointer(), null);
|
||||||
String[] args = {
|
String[] args = {
|
||||||
"-dNODISPLAY", "-dNOPAUSE", "-dBATCH", "-dSAFER"
|
"gs", "-dNODISPLAY", "-dNOPAUSE", "-dBATCH", "-dSAFER"
|
||||||
};
|
};
|
||||||
ghostscriptLibrary.gsapi_init_with_args(instanceByRef.getValue(), args.length, args);
|
ghostscriptLibrary.gsapi_init_with_args(instanceByRef.getValue(), args.length, args);
|
||||||
IntByReference exitCode = new IntByReference();
|
IntByReference exitCode = new IntByReference();
|
||||||
|
@ -77,7 +79,7 @@ public class GhostScriptLibraryTester {
|
||||||
GhostscriptLibrary.gs_main_instance.ByReference instanceByRef = new GhostscriptLibrary.gs_main_instance.ByReference();
|
GhostscriptLibrary.gs_main_instance.ByReference instanceByRef = new GhostscriptLibrary.gs_main_instance.ByReference();
|
||||||
ghostscriptLibrary.gsapi_new_instance(instanceByRef.getPointer(), null);
|
ghostscriptLibrary.gsapi_new_instance(instanceByRef.getPointer(), null);
|
||||||
String[] args = {
|
String[] args = {
|
||||||
"-dNODISPLAY", "-dNOPAUSE", "-dBATCH", "-dSAFER"
|
"gs", "-dNODISPLAY", "-dNOPAUSE", "-dBATCH", "-dSAFER"
|
||||||
};
|
};
|
||||||
ghostscriptLibrary.gsapi_init_with_args(instanceByRef.getValue(), args.length, args);
|
ghostscriptLibrary.gsapi_init_with_args(instanceByRef.getValue(), args.length, args);
|
||||||
IntByReference exitCode = new IntByReference();
|
IntByReference exitCode = new IntByReference();
|
||||||
|
@ -94,7 +96,7 @@ public class GhostScriptLibraryTester {
|
||||||
GhostscriptLibrary.gs_main_instance.ByReference instanceByRef = new GhostscriptLibrary.gs_main_instance.ByReference();
|
GhostscriptLibrary.gs_main_instance.ByReference instanceByRef = new GhostscriptLibrary.gs_main_instance.ByReference();
|
||||||
ghostscriptLibrary.gsapi_new_instance(instanceByRef.getPointer(), null);
|
ghostscriptLibrary.gsapi_new_instance(instanceByRef.getPointer(), null);
|
||||||
String[] args = {
|
String[] args = {
|
||||||
"-dNODISPLAY", "-dNOPAUSE", "-dBATCH", "-dSAFER"
|
"gs", "-dNODISPLAY", "-dNOPAUSE", "-dBATCH", "-dSAFER"
|
||||||
};
|
};
|
||||||
ghostscriptLibrary.gsapi_init_with_args(instanceByRef.getValue(), args.length, args);
|
ghostscriptLibrary.gsapi_init_with_args(instanceByRef.getValue(), args.length, args);
|
||||||
IntByReference exitCode = new IntByReference();
|
IntByReference exitCode = new IntByReference();
|
||||||
|
@ -108,9 +110,7 @@ public class GhostScriptLibraryTester {
|
||||||
GhostscriptLibrary.gs_main_instance.ByReference instanceByRef = new GhostscriptLibrary.gs_main_instance.ByReference();
|
GhostscriptLibrary.gs_main_instance.ByReference instanceByRef = new GhostscriptLibrary.gs_main_instance.ByReference();
|
||||||
ghostscriptLibrary.gsapi_new_instance(instanceByRef.getPointer(), null);
|
ghostscriptLibrary.gsapi_new_instance(instanceByRef.getPointer(), null);
|
||||||
final StringBuilder stdOutBuffer = new StringBuilder();
|
final StringBuilder stdOutBuffer = new StringBuilder();
|
||||||
final StringBuilder stdInBuffer = new StringBuilder();
|
|
||||||
GhostscriptLibrary.stdin_fn stdinCallback = (caller_handle, buf, len) -> {
|
GhostscriptLibrary.stdin_fn stdinCallback = (caller_handle, buf, len) -> {
|
||||||
stdInBuffer.append("OK");
|
|
||||||
return 0;
|
return 0;
|
||||||
};
|
};
|
||||||
GhostscriptLibrary.stdout_fn stdoutCallback = (caller_handle, str, len) -> {
|
GhostscriptLibrary.stdout_fn stdoutCallback = (caller_handle, str, len) -> {
|
||||||
|
@ -120,8 +120,7 @@ public class GhostScriptLibraryTester {
|
||||||
GhostscriptLibrary.stderr_fn stderrCallback = (caller_handle, str, len) -> len;
|
GhostscriptLibrary.stderr_fn stderrCallback = (caller_handle, str, len) -> len;
|
||||||
ghostscriptLibrary.gsapi_set_stdio(instanceByRef.getValue(), stdinCallback, stdoutCallback, stderrCallback);
|
ghostscriptLibrary.gsapi_set_stdio(instanceByRef.getValue(), stdinCallback, stdoutCallback, stderrCallback);
|
||||||
String[] args = {
|
String[] args = {
|
||||||
"-dNODISPLAY", "-dQUIET", "-dNOPAUSE", "-dBATCH",
|
"gs", "-dNODISPLAY", "-dQUIET", "-dNOPAUSE", "-dBATCH", "-sOutputFile=%stdout", "-f", "-"
|
||||||
"-sOutputFile=%stdout", "-f", "-"
|
|
||||||
};
|
};
|
||||||
ghostscriptLibrary.gsapi_init_with_args(instanceByRef.getValue(),
|
ghostscriptLibrary.gsapi_init_with_args(instanceByRef.getValue(),
|
||||||
args.length, args);
|
args.length, args);
|
||||||
|
@ -130,6 +129,8 @@ public class GhostScriptLibraryTester {
|
||||||
ghostscriptLibrary.gsapi_run_string_with_length(instanceByRef.getValue(), command, command.length(), 0, exitCode);
|
ghostscriptLibrary.gsapi_run_string_with_length(instanceByRef.getValue(), command, command.length(), 0, exitCode);
|
||||||
ghostscriptLibrary.gsapi_exit(instanceByRef.getValue());
|
ghostscriptLibrary.gsapi_exit(instanceByRef.getValue());
|
||||||
ghostscriptLibrary.gsapi_delete_instance(instanceByRef.getValue());
|
ghostscriptLibrary.gsapi_delete_instance(instanceByRef.getValue());
|
||||||
|
logger.log(Level.FINE, stdOutBuffer.toString());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public String setDisplayCallback() {
|
public String setDisplayCallback() {
|
||||||
|
@ -176,7 +177,7 @@ public class GhostScriptLibraryTester {
|
||||||
displayCallback.size = displayCallback.size();
|
displayCallback.size = displayCallback.size();
|
||||||
ghostscriptLibrary.gsapi_set_display_callback(instanceByRef.getValue(), displayCallback);
|
ghostscriptLibrary.gsapi_set_display_callback(instanceByRef.getValue(), displayCallback);
|
||||||
String[] args = new String[]{
|
String[] args = new String[]{
|
||||||
"-dQUIET", "-dNOPAUSE", "-dBATCH", "-dSAFER",
|
"gs", "-dQUIET", "-dNOPAUSE", "-dBATCH", "-dSAFER",
|
||||||
"-sDEVICE=display", "-sDisplayHandle=0", "-dDisplayFormat=16#a0800"
|
"-sDEVICE=display", "-sDisplayHandle=0", "-dDisplayFormat=16#a0800"
|
||||||
};
|
};
|
||||||
ghostscriptLibrary.gsapi_init_with_args(instanceByRef.getValue(), args.length, args);
|
ghostscriptLibrary.gsapi_init_with_args(instanceByRef.getValue(), args.length, args);
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
package org.xbib.graphics.ghostscript.test;
|
package org.xbib.graphics.ghostscript.test;
|
||||||
|
|
||||||
|
import java.util.logging.Level;
|
||||||
import org.junit.jupiter.api.BeforeAll;
|
import org.junit.jupiter.api.BeforeAll;
|
||||||
import org.junit.jupiter.api.Disabled;
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
@ -19,9 +18,10 @@ public class GhostscriptLibraryTest {
|
||||||
private static GhostScriptLibraryTester gslib;
|
private static GhostScriptLibraryTester gslib;
|
||||||
|
|
||||||
@BeforeAll
|
@BeforeAll
|
||||||
public static void setUp() throws Exception {
|
public static void setUp() {
|
||||||
|
logger.log(Level.INFO, "setUp: loading ghostscript library");
|
||||||
gslib = new GhostScriptLibraryTester();
|
gslib = new GhostScriptLibraryTester();
|
||||||
logger.info("setUp: ghostscript library loaded");
|
logger.log(Level.INFO, "setUp: ghostscript library loaded");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -35,13 +35,10 @@ public class GhostscriptLibraryTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Disabled("GPL Ghostscript 9.54.0: Unrecoverable error, exit code 1")
|
|
||||||
public void gsapiInitWithArgs() {
|
public void gsapiInitWithArgs() {
|
||||||
String input = dir + "input.ps";
|
String input = dir + "input.ps";
|
||||||
String output = "build/output.pdf";
|
String output = "build/output.pdf";
|
||||||
gslib.withInput(input, output);
|
gslib.withInput(input, output);
|
||||||
File outputFile = new File(output);
|
|
||||||
assertTrue(outputFile.exists());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -70,9 +67,9 @@ public class GhostscriptLibraryTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Disabled
|
//@Disabled
|
||||||
public void gsapiSetDisplayCallback() {
|
public void gsapiSetDisplayCallback() {
|
||||||
String display = gslib.setDisplayCallback();
|
String display = gslib.setDisplayCallback();
|
||||||
assertEquals("OPEN-PRESIZE-UPDATE-SIZE-PAGE-UPDATE-SYNC-PRECLOSE-CLOSE", display);
|
assertEquals("OPEN-PRESIZE-UPDATE-SIZE-UPDATE-PRECLOSE-CLOSEOPEN-PRESIZE-UPDATE-SIZE-PAGE-UPDATE-PRECLOSE-CLOSE", display);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,7 @@ public class PDFConverterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testConvertWithPS() throws Exception {
|
public void testConvertWithPS() throws Exception {
|
||||||
PDFConverter converter = new PDFConverter(List.of("-ps2pdf"));
|
PDFConverter converter = new PDFConverter(List.of("ps2pdf"));
|
||||||
try (InputStream inputStream = getClass().getResourceAsStream("input.ps");
|
try (InputStream inputStream = getClass().getResourceAsStream("input.ps");
|
||||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
|
ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
|
||||||
converter.convert(inputStream, outputStream);
|
converter.convert(inputStream, outputStream);
|
||||||
|
@ -34,7 +34,7 @@ public class PDFConverterTest {
|
||||||
final ByteArrayOutputStream baos1 = new ByteArrayOutputStream();
|
final ByteArrayOutputStream baos1 = new ByteArrayOutputStream();
|
||||||
final ByteArrayOutputStream baos2 = new ByteArrayOutputStream();
|
final ByteArrayOutputStream baos2 = new ByteArrayOutputStream();
|
||||||
final ByteArrayOutputStream baos3 = new ByteArrayOutputStream();
|
final ByteArrayOutputStream baos3 = new ByteArrayOutputStream();
|
||||||
final PDFConverter converter = new PDFConverter(List.of("-ps2pdf"));
|
final PDFConverter converter = new PDFConverter(List.of("ps2pdf"));
|
||||||
Thread thread1 = new Thread() {
|
Thread thread1 = new Thread() {
|
||||||
public void run() {
|
public void run() {
|
||||||
try {
|
try {
|
||||||
|
@ -76,7 +76,6 @@ public class PDFConverterTest {
|
||||||
baos3.close();
|
baos3.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Disabled
|
|
||||||
@Test
|
@Test
|
||||||
public void testConvertWithUnsupportedDocument() throws Exception {
|
public void testConvertWithUnsupportedDocument() throws Exception {
|
||||||
PDFConverter converter = new PDFConverter();
|
PDFConverter converter = new PDFConverter();
|
||||||
|
@ -86,10 +85,9 @@ public class PDFConverterTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Disabled
|
|
||||||
@Test
|
@Test
|
||||||
public void testConvertDistiller() throws Exception {
|
public void testConvertDistiller() throws Exception {
|
||||||
PDFConverter converter = new PDFConverter(List.of("-dFIXEDMEDIA", "-dPDFFitPage", "-dPAPERSIZE=a4"));
|
PDFConverter converter = new PDFConverter(List.of("-dFIXEDMEDIA", "-dPDFFitPage"));
|
||||||
try (InputStream inputStream = getClass().getResourceAsStream("3977940_retrieve_74_.pdf");
|
try (InputStream inputStream = getClass().getResourceAsStream("3977940_retrieve_74_.pdf");
|
||||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
|
ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
|
||||||
converter.convert(inputStream, outputStream);
|
converter.convert(inputStream, outputStream);
|
||||||
|
|
|
@ -28,7 +28,7 @@ public class PDFRasterizerTest {
|
||||||
Path targetFile = Paths.get("build/color.pdf");
|
Path targetFile = Paths.get("build/color.pdf");
|
||||||
PDFRasterizer pdfRasterizer = new PDFRasterizer();
|
PDFRasterizer pdfRasterizer = new PDFRasterizer();
|
||||||
int pagecount = pdfRasterizer.mergeImagesToPDF(sourceDir, targetFile);
|
int pagecount = pdfRasterizer.mergeImagesToPDF(sourceDir, targetFile);
|
||||||
logger.info("pagecount = " + pagecount);
|
logger.log(Level.FINE, "pagecount = " + pagecount);
|
||||||
assertEquals(1, pagecount);
|
assertEquals(1, pagecount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,7 +38,7 @@ public class PDFRasterizerTest {
|
||||||
Path targetFile = Paths.get("build/3656573.pdf");
|
Path targetFile = Paths.get("build/3656573.pdf");
|
||||||
PDFRasterizer pdfRasterizer = new PDFRasterizer();
|
PDFRasterizer pdfRasterizer = new PDFRasterizer();
|
||||||
int pagecount = pdfRasterizer.mergeImagesToPDF(sourceDir, targetFile);
|
int pagecount = pdfRasterizer.mergeImagesToPDF(sourceDir, targetFile);
|
||||||
logger.info("pagecount = " + pagecount);
|
logger.log(Level.FINE, "pagecount = " + pagecount);
|
||||||
assertEquals(9, pagecount);
|
assertEquals(9, pagecount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,10 +50,10 @@ public class PDFRasterizerTest {
|
||||||
int pagecount = 0;
|
int pagecount = 0;
|
||||||
try {
|
try {
|
||||||
PDFRasterizer pdfRasterizer = new PDFRasterizer();
|
PDFRasterizer pdfRasterizer = new PDFRasterizer();
|
||||||
pdfRasterizer.pdfToImage(source, path, null, null);
|
pdfRasterizer.pdfToImage(source, path, "test");
|
||||||
Path tmpTarget = path.resolve(target.getFileName());
|
Path tmpTarget = path.resolve(target.getFileName());
|
||||||
pagecount = pdfRasterizer.mergeImagesToPDF(path, tmpTarget);
|
pagecount = pdfRasterizer.mergeImagesToPDF(path, tmpTarget);
|
||||||
logger.info("pagecount = " + pagecount);
|
logger.log(Level.FINE, "pagecount = " + pagecount);
|
||||||
pdfRasterizer.scalePDF(tmpTarget, target);
|
pdfRasterizer.scalePDF(tmpTarget, target);
|
||||||
} finally {
|
} finally {
|
||||||
delete(path);
|
delete(path);
|
||||||
|
@ -80,7 +80,7 @@ public class PDFRasterizerTest {
|
||||||
delete(target);
|
delete(target);
|
||||||
Files.createDirectories(target);
|
Files.createDirectories(target);
|
||||||
PDFRasterizer rasterizer = new PDFRasterizer();
|
PDFRasterizer rasterizer = new PDFRasterizer();
|
||||||
rasterizer.pdfToImage(p, target, "pdf-", "1-");
|
rasterizer.pdfToImage(p, target, "pdf-");
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
logger.log(Level.SEVERE, e.getMessage(), e);
|
logger.log(Level.SEVERE, e.getMessage(), e);
|
||||||
fail(e);
|
fail(e);
|
||||||
|
@ -96,7 +96,7 @@ public class PDFRasterizerTest {
|
||||||
try (Stream<Path> stream = Files.list(path)) {
|
try (Stream<Path> stream = Files.list(path)) {
|
||||||
stream.forEach(p -> {
|
stream.forEach(p -> {
|
||||||
if (p.toString().endsWith(".pdf")) {
|
if (p.toString().endsWith(".pdf")) {
|
||||||
logger.info("found " + p.toString());
|
logger.log(Level.FINE, "found " + p);
|
||||||
Path target = Paths.get("build/" + p.getFileName());
|
Path target = Paths.get("build/" + p.getFileName());
|
||||||
try {
|
try {
|
||||||
delete(target);
|
delete(target);
|
||||||
|
|
Loading…
Reference in a new issue