Ghostscript32.cs
Classe C# pour encapsuler l’API Ghostscript 32-bit (via P/Invoke).
using System;
using System.IO;
using System.Runtime.InteropServices;
namespace HeraklesFacturX.Util
{
public class Ghostscript32
{
private static readonly object _lockObject = new object();
// Déclarations DllImport inchangées
[DllImport("gsdll32.dll", EntryPoint = "gsapi_new_instance")]
internal static extern int CreateAPIInstance(out IntPtr pinstance, IntPtr caller_handle);
[DllImport("gsdll32.dll", EntryPoint = "gsapi_init_with_args")]
internal static extern int InitAPI(IntPtr instance, int argc, string[] argv);
[DllImport("gsdll32.dll", EntryPoint = "gsapi_exit")]
internal static extern int ExitAPI(IntPtr instance);
[DllImport("gsdll32.dll", EntryPoint = "gsapi_delete_instance")]
internal static extern void DeleteAPIInstance(IntPtr instance);
// --- Nouvelle méthode générique avec Verrouillage ---
/// <summary>
/// Exécute une commande Ghostscript en utilisant les arguments spécifiés.
/// Un verrouillage est utilisé pour garantir l'atomicité, car l'API Ghostscript est généralement non thread-safe.
/// </summary>
/// <param name="args">Tableau de chaînes de caractères représentant les arguments de la ligne de commande Ghostscript.</param>
/// <returns>Le code de retour de l'exécution de l'API Ghostscript (0 pour succès).</returns>
/// <exception cref="ArgumentException">Lance une exception si le tableau d'arguments est null ou vide.</exception>
/// <exception cref="InvalidOperationException">Lance une exception si l'instance Ghostscript ne peut pas être créée ou si l'exécution échoue.</exception>
public static int ExecuteGhostscript(string[] args)
{
if (args == null || args.Length == 0)
{
throw new ArgumentException("Le tableau d'arguments ne peut pas être null ou vide.", nameof(args));
}
// 1. Verrouillage de l'accès à l'API Ghostscript
lock (_lockObject)
{
IntPtr gsInstance = IntPtr.Zero;
int finalCode = -1;
try
{
// 2. Création de l'instance API
int code = CreateAPIInstance(out gsInstance, IntPtr.Zero);
if (code != 0)
{
throw new InvalidOperationException($"Échec de création de l'instance Ghostscript (code: {code})");
}
// 3. Initialisation et exécution de l'API
finalCode = InitAPI(gsInstance, args.Length, args);
// Remarque : Même si l'exécution réussit, il est important d'exécuter le nettoyage.
if (finalCode != 0)
{
// L'API a retourné un code d'erreur lors de l'exécution
throw new InvalidOperationException($"Ghostscript a échoué (code: {finalCode})");
}
return finalCode; // Retourne 0 en cas de succès
}
finally
{
// 4. Nettoyage, même en cas d'erreur
if (gsInstance != IntPtr.Zero)
{
ExitAPI(gsInstance);
DeleteAPIInstance(gsInstance);
}
}
GC.Collect();
GC.WaitForPendingFinalizers();
} // Le verrou est libéré ici
}
// --- Exemple de fonction pour l'incrustation de polices utilisant la nouvelle méthode ---
/// <summary>
/// Incorpore toutes les polices d'un fichier PDF source dans un fichier de sortie.
/// </summary>
/// <param name="inputFile">Chemin du fichier PDF source.</param>
/// <param name="outputFile">Chemin du fichier PDF de sortie.</param>
/// <returns>Vrai si l'exécution de Ghostscript a réussi et que le fichier de sortie existe.</returns>
/// <exception cref="FileNotFoundException">Lance une exception si le fichier PDF source est introuvable.</exception>
public static bool EmbedingFonts(string inputFile, string outputFile)
{
if (!File.Exists(inputFile))
throw new FileNotFoundException($"Le fichier PDF source est introuvable : {inputFile}");
// Arguments spécifiques pour l'incrustation de polices
string[] gsArgs =
{
"-dBATCH", "-dNOPAUSE", "-sDEVICE=pdfwrite",
"-dEmbedAllFonts=true", "-dSubsetFonts=true",
"-dCompatibilityLevel=1.4",
$"-sOutputFile={outputFile}",
inputFile
};
int code = ExecuteGhostscript(gsArgs);
// Vérifie le code de retour et l'existence du fichier
return code == 0 && File.Exists(outputFile);
}
}
}