AmagnoVirtualPrinter/Common/VirtualPrinter.Agent.Lib/Misc/GhostScriptConverter.cs
2020-10-19 17:44:50 +02:00

218 lines
6.8 KiB
C#

using System;
using System.Diagnostics;
using System.IO;
using System.Text.RegularExpressions;
using JetBrains.Annotations;
using VirtualPrinter.Agent.Core;
using VirtualPrinter.Agent.Lib.Model;
using VirtualPrinter.Logging;
namespace VirtualPrinter.Agent.Lib.Misc
{
public class GhostScriptConverter : IPostScriptConverter
{
private const string GsWin64 = "gswin64c.exe";
private const string GsWin32 = "gswin32c.exe";
[NotNull]
private readonly IVirtualPrinterLogger<GhostScriptConverter> _logger;
[NotNull]
private readonly IRegistryRepository _registryRepository;
[NotNull]
private readonly IShell _shell;
public event EventHandler<IJob> ProgressInitialized;
public event EventHandler<IJob> ProgressFinished;
public event EventHandler<ProgressUpdateArgs> ProgressUpdate;
public GhostScriptConverter
(
[NotNull]IVirtualPrinterLogger<GhostScriptConverter> logger,
[NotNull]IRegistryRepository registryRepository,
[NotNull]IShell shell
)
{
if (logger == null)
{
throw new ArgumentNullException(nameof(logger));
}
if (registryRepository == null)
{
throw new ArgumentNullException(nameof(registryRepository));
}
if (shell == null)
{
throw new ArgumentNullException(nameof(shell));
}
_logger = logger;
_registryRepository = registryRepository;
_shell = shell;
}
public void Convert(IJob job, string target, PostScriptRenderOptions options)
{
var ghostScriptExe = GetGhostScriptPath();
if (ghostScriptExe == null)
{
throw new PostScriptConversionException("GhostScript not found. Please place local variable.");
}
ProgressInitialized?.Invoke(this, job);
var pdfTarget = target + ".pdf";
if (options.PdfOptions.Enabled)
{
Convert(job, ghostScriptExe, GetArgumentsForPdfConversion(job.RawDataPath, pdfTarget, options));
if (!_shell.FileExists(pdfTarget))
{
throw new PostScriptConversionException("Postscript conversion failed after output.");
}
}
if (options.TiffOptions.Enabled)
{
var tiffTarget = target + ".tif";
Convert(job, ghostScriptExe, GetArgumentsForTiffConversion(job.RawDataPath, tiffTarget, options));
Convert(job, ghostScriptExe, GetArgumentsForPdfConversion(job.RawDataPath, pdfTarget, options));
if (!_shell.FileExists(tiffTarget))
{
throw new PostScriptConversionException("Postscript conversion failed after output.");
}
}
ProgressFinished?.Invoke(this, job);
}
[CanBeNull]
private string GetGhostScriptPath()
{
return GetGhostScriptPath(GsWin64) ?? GetGhostScriptPath(GsWin32);
}
[CanBeNull]
private string GetGhostScriptPath(string execName)
{
if (!_registryRepository.TryGetGhostscriptPath(out var path))
{
return null;
}
var ghostScriptBinPath = Path.Combine(path, "bin");
var fullPath = Path.Combine(ghostScriptBinPath, execName);
return _shell.FileExists(fullPath) ? fullPath : null;
}
[NotNull]
private static string GetArgumentsForPdfConversion(string source, string target, PostScriptRenderOptions options)
{
const string initialArguments = "-q -P- -dSAFER -dNOPAUSE -dBATCH -dNoCancel -sDEVICE=pdfwrite";
var finalArguments = $"-sOutputFile=\"{target}\" \"{source}\"";
var optionalArguments = "";
if (options.PdfOptions.Archivable)
{
optionalArguments += "-sColorConversionStrategy=/RGB -dUseCIEColor -dPDFACompatibilityPolicy=2";
}
if (options.UserRenderDpi != null && options.UserRenderDpi > 0)
{
optionalArguments += "-r" + options.UserRenderDpi.Value;
}
return $"{initialArguments} {optionalArguments} {finalArguments}";
}
[NotNull]
private static string GetArgumentsForTiffConversion(string source, string target, PostScriptRenderOptions options)
{
const string initialArguments = "-q -P- -dSAFER -dNOPAUSE -dBATCH -sDEVICE=tiff12nc -sCompression=lzw";
var finalArguments = $"-sOutputFile=\"{target}\" \"{source}\"";
var optionalArguments = "-dTextAlphaBits=4 ";
if (options.UserRenderDpi != null && options.UserRenderDpi > 0)
{
optionalArguments += "-r" + options.UserRenderDpi.Value;
}
else
{
optionalArguments += "-r300";
}
return $"{initialArguments} {optionalArguments} {finalArguments}";
}
private void Convert(IJob job, string gsExe, string gsArguments)
{
LogDebug("Starting ps conversion ...\n"+gsExe + " " + gsArguments);
var process = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = gsExe,
Arguments = gsArguments,
UseShellExecute = false,
RedirectStandardOutput = true,
CreateNoWindow = true
}
};
process.Start();
while (!process.StandardOutput.EndOfStream)
{
var readLine = process.StandardOutput.ReadLine();
var progress = ParseProgress(readLine);
if (progress == null)
{
continue;
}
ProgressUpdate?.Invoke(this, new ProgressUpdateArgs(job, progress.Value));
}
}
private static uint? ParseProgress([CanBeNull]string line)
{
// Example output:
// %%[Page: 49]%%
if (line == null)
{
return null;
}
var groups = Regex.Match(line, @"%%\[Page:(.*)\]%%").Groups;
if (groups.Count < 2)
{
return null;
}
if (!uint.TryParse(groups[1].Value, out var result))
{
return null;
}
return result;
}
private void LogDebug(string message, params object[] args)
{
_logger.Debug(message, args);
}
}
}