I'm trying to create a frontend app in Java to handle batch SVG conversions using Inkscape's command line feature. I'm taking and updating the code from https://sourceforge.net/projects/conversionsvg/. The way the original developer handled calling Inkscape by Runtime.getRuntime().exec(String). The issue I'm running into is some inconsistencies between using methodA and methodB. I created a simple java test project to demonstrate the different actions being performed.
CallerTest.java
package conversion;
import java.io.IOException;
public class CallerTest {
static String pathToInkscape = "\"C:\\Program Files\\Inkscape\\inkscape.exe\"";
public static void main(String[] args) {
ProcessBuilderCaller processBuilder = new ProcessBuilderCaller();
RuntimeExecCaller runtimeExec = new RuntimeExecCaller();
// methodA() uses one long command line string
try {
String oneLongString_ProcessBuilder = pathToInkscape + " -f \"C:\\test.svg\" -D -w 100 -h 100 -e \"C:\\ProcessBuilder-methodB.png\"";
String oneLongString_RuntimeExec = pathToInkscape + " -f \"C:\\test.svg\" -D -w 100 -h 100 -e \"C:\\RuntimeExec-methodA.png\"";
// processBuilder.methodA(oneLongString_ProcessBuilder);
runtimeExec.methodA(oneLongString_RuntimeExec);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// methodB() uses an array containing the command and the options to pass to the command
try {
String[] commandAndOptions_ProcessBuilder = {pathToInkscape, " -f \"C:/test.svg\" -D -w 100 -h 100 -e \"C:\\ProcessBuilder-methodB.png\""};
String[] commandAndOptions_RuntimeExec = {pathToInkscape, " -f \"C:/test.svg\" -D -w 100 -h 100 -e \"C:\\RuntimeExec-methodB.png\""};
processBuilder.methodB(commandAndOptions_ProcessBuilder);
// runtimeExec.methodB(commandAndOptions_RuntimeExec);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
RuntimeExecCaller.java
package conversion;
import java.io.IOException;
public class RuntimeExecCaller {
Process process;
// use one string
public void methodA(String oneLongString) throws IOException {
process = Runtime.getRuntime().exec(oneLongString);
}
// use the array
public void methodB(String[] commandAndOptions) throws IOException {
process = Runtime.getRuntime().exec(commandAndOptions);
}
}
ProcessBuilderCaller.java
package conversion;
import java.io.IOException;
public class ProcessBuilderCaller {
Process process;
// use one string
public void methodA(String oneLongString) throws IOException {
process = new ProcessBuilder(oneLongString).start();
}
// use the array
public void methodB(String[] commandAndOptions) throws IOException {
process = new ProcessBuilder(commandAndOptions).start();
}
}
Result
Both methodA(String) calls work, but when methodB(String[]) is called Inkscape is being started and the arguments are being passed incorrectly. After methodB(String[]) executes I get an Inkscape error dialog for each saying
Failed to load the requested file -f C:/test.svg -D -w 100 -h 100 -e C:\RuntimeExec-methodB.png
Failed to load the requested file -f C:/test.svg -D -w 100 -h 100 -e C:\ProcessBuilder-methodB.png
and when I click Close on the dialog, Inkscape pops up with a new blank document. So, I guess I have a few questions:
What is the difference between Runtime.getRuntime().exec(String) and Runtime.getRuntime().exec(String[])?
JavaDoc says that Runtime.exec(String) calls Runtime.exec(command, null) (which is Runtime.exec(String cmd, String[] envp)) which in turn calls Runtime.exec(cmdarray, envp) (which is Runtime.exec(String[] cmdarray, String[] envp)). So, if Runtime.getRuntime().exec(String) is calling Runtime.exec(String[]) anyways, why am I getting different results when using different methods?
Is something happening behind the scenes where Java sets up the environment differently depending on which method is called?
I suspect your problem stems from the way you're specifying your argument list. Essentially, you're passing "-f C:/test.svg -D -w 100 -h 100 -e C:\RuntimeExec-methodB.png
" as one single argument to Inkscape.
What you need to do is pass the arguments individually, like so:
String[] commandAndOptions_ProcessBuilder = {pathToInkscape, "-f", "C:\\est.svg", "-D", "-w", "100", "-h", "100", "-e", "C:\\ProcessBuilder-methodB.png"};
String[] commandAndOptions_RuntimeExec = {pathToInkscape, "-f", "C:\\test.svg", "-D", "-w", "100", "-h", "100", "-e","C:\\RuntimeExec-methodB.png"};
Roughly speaking, when you use Runtime.exec(String)
, the value you pass in gets evaluated by the shell, which parses out the argument list. When you use Runtime.exec(String[])
, you're providing the argument list, so it doesn't need processing. A benefit of doing this is that you don't have to escape values special to the shell, as the arguments will not be evaluated by it.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With