//----------------------------------------------------------------------------//
// //
// G h o s t s c r i p t //
// //
//----------------------------------------------------------------------------//
// <editor-fold defaultstate="collapsed" desc="hdr"> //
// Copyright © Hervé Bitteur and others 2000-2013. All rights reserved. //
// This software is released under the GNU General Public License. //
// Goto http://kenai.com/projects/audiveris to report bugs or suggestions. //
//----------------------------------------------------------------------------//
// </editor-fold>
package omr.sheet.picture;
import omr.constant.Constant;
import omr.constant.ConstantSet;
import static omr.util.RegexUtil.*;
import omr.util.WindowsRegistry;
import omr.WellKnowns;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Class {@code Ghostscript} provides the path to a suitable
* Ghostscript executable, which may depend on underlying OS and
* architecture, and on versions found.
*
* @author Hervé Bitteur
*/
public class Ghostscript
{
//~ Static fields/initializers ---------------------------------------------
/** Specific application parameters */
private static final Constants constants = new Constants();
/** Usual logger utility */
private static final Logger logger = LoggerFactory.getLogger(Ghostscript.class);
/** Path to exec. */
private static volatile String path;
//~ Methods ----------------------------------------------------------------
//
//---------//
// getPath //
//---------//
/**
* Report the path to suitable ghostscript executable.
*
* @return the best path found, or an empty string if nothing found
*/
public static String getPath ()
{
if (path == null) {
if (WellKnowns.LINUX || WellKnowns.MAC_OS_X) {
path = "gs";
} else if (WellKnowns.WINDOWS) {
path = getWindowsPath();
} else {
logger.error("OS not supported");
}
// To avoid endless initialization attempts
if (path == null) {
path = "";
}
}
return path;
}
//----------------//
// getWindowsPath //
//----------------//
/**
* Retrieve the path to suitable ghostscript executable on Windows
* environments.
*
* This is implemented on registry informations, using CLI "reg" command:
* reg query "HKLM\SOFTWARE\GPL Ghostscript" /s
*
* @return the best suitable path, or null if nothing found
*/
private static String getWindowsPath ()
{
// Group names
final String VERSION = "version";
final String PATH = "path";
final String ARCH = "arch";
/** Regex for registry key line. */
final Pattern keyPattern = Pattern.compile(
"^HKEY_LOCAL_MACHINE\\\\SOFTWARE\\\\(Wow6432Node\\\\)?GPL Ghostscript\\\\"
+ group(VERSION, "\\d+\\.\\d+") + "$");
/** Regex for registry value line. */
final Pattern valPattern = Pattern.compile(
"^\\s+GS_DLL\\s+REG_SZ\\s+" + group(PATH, ".+") + "$");
/** Regex for dll name. */
final Pattern dllPattern = Pattern.compile(
"gsdll" + group(ARCH, "\\d+") + "\\.dll$");
Double bestVersion = null; // Best version found so far
String bestPath = null; // Best path found so far
boolean relevant = false; // Is current registry info interesting?
int index = 0; // Line number in registry outputs
// Browse registry output lines in sequence
for (String line : getRegistryOutputs()) {
logger.debug("Line#{}:{}", ++index, line);
Matcher keyMatcher = keyPattern.matcher(line);
if (keyMatcher.matches()) {
relevant = false;
// Check version information
String versionStr = getGroup(keyMatcher, VERSION);
logger.debug("Version read as: {}", versionStr);
Double version = Double.valueOf(versionStr);
if ((version != null)
&& (version >= constants.minVersion.getValue())
&& (version <= constants.maxVersion.getValue())) {
// We have an acceptable version
if ((bestVersion == null) || (bestVersion < version)) {
bestVersion = version;
logger.debug("Best version is: {}", versionStr);
relevant = true;
} else {
logger.debug("Version discarded: {}", versionStr);
}
} else {
logger.debug("Version unacceptable: {}", versionStr);
}
} else if (relevant) {
Matcher valMatcher = valPattern.matcher(line);
if (valMatcher.matches()) {
// Read path information
bestPath = getGroup(valMatcher, PATH);
logger.debug("Best path is: {}", bestPath);
}
}
}
// Extract prefix and dll from best path found, regardless of arch
if (bestPath != null) {
int lastSep = bestPath.lastIndexOf("\\");
String prefix = bestPath.substring(0, lastSep);
logger.debug("Prefix is: {}", prefix);
String dll = bestPath.substring(lastSep + 1);
logger.debug("Dll is: {}", dll);
Matcher dllMatcher = dllPattern.matcher(dll);
if (dllMatcher.matches()) {
String arch = getGroup(dllMatcher, ARCH);
String result = prefix + "\\gswin" + arch + "c.exe";
logger.debug("Final path is: {}", result);
return result; // Normal exit
}
}
logger.warn("Could not find suitable Ghostscript software");
return null; // Abnormal exit
}
//--------------------//
// getRegistryOutputs //
//--------------------//
/**
* Collect the output lines from registry queries.
*
* @return the output lines
*/
private static List<String> getRegistryOutputs ()
{
/** Radices used in registry search (32, 64 or Wow). */
final String[] radices = new String[]{
"HKLM\\SOFTWARE\\GPL Ghostscript", // Pure 32/32 or 64/64
"HKLM\\SOFTWARE\\Wow6432Node\\GPL Ghostscript" // Wow (64/32)
};
List<String> outputs = new ArrayList<>();
// Access registry twice, one for win32 & win64 and one for Wow
for (String radix : radices) {
logger.debug("Radix: {}", radix);
outputs.addAll(WindowsRegistry.query(radix, "/s"));
}
return outputs;
}
//~ Inner Classes ----------------------------------------------------------
//-----------//
// Constants //
//-----------//
private static final class Constants
extends ConstantSet
{
//~ Instance fields ----------------------------------------------------
Constant.Double minVersion = new Constant.Double(
"version",
9.06,
"Minimum Ghostscript acceptable version");
Constant.Double maxVersion = new Constant.Double(
"version",
9999,
"Maximum Ghostscript acceptable version");
}
}