Vai al contenuto principale
Questa sezione fornisce istruzioni su come distribuire un’applicazione FRE 12 per Windows in Azure App Service. Come esempio, viene presentata una coppia di progetti WebJob che utilizzano dati di un account Azure Storage. L’elaborazione dei file viene eseguita tramite un contenitore BLOB.
Con questo scenario, otterrai i migliori risultati di riconoscimento per documenti piccoli di una sola pagina, come fatture, ricevute e così via.
La distribuzione dell’applicazione in App Service comprende diversi passaggi:
  1. Preparare il computer locale e l’istanza dell’applicazione usando i prerequisiti
  2. Eseguire i passaggi preparatori prima di distribuire l’applicazione
  3. Distribuire ed eseguire l’applicazione in App Service
Usa gli esempi di codice riportati nelle istruzioni seguenti.

Prerequisiti

Macchina locale

Prima di creare il tuo App Service, usa i seguenti requisiti per configurare la tua macchina locale:
  • Visual Studio 2019 e i relativi moduli per lo sviluppo di applicazioni per Azure (consulta Funzionalità di Azure in Visual Studio oppure usa Visual Studio Installer per scaricare tali moduli)
  • Azure SDK (scaricalo qui)
  • .NET Framework 4.7.2
  • Pacchetti NuGet per lavorare con Azure Storage e i contenitori Blob:
    • Azure.Storage.Blobs (scaricalo qui)
    • Azure.Storage.Queues (scaricalo qui)
    • Newtonsoft.Json (scaricalo qui)
    • System.IO.Compression.ZipFile (scaricalo qui)
  • Wrapper di ABBYY FineReader Engine per .NET Framework 4.7 (nella cartella C:\ProgramData\ABBYY\SDK\12\FineReader Engine\Inc.NET interops dopo l’installazione Developer)
  • Interfaccia IFileWriter ridefinita per lavorare con i contenitori Blob (vedi l’esempio riportato di seguito)
  • Azure Storage Explorer (facoltativo - scaricalo qui)
  • Macchina virtuale nel tuo account Azure dedicata alle procedure di gestione delle licenze. Per completare la configurazione, ti occorrono:
    • Indirizzo IP
    • Porta di connessione aperta (predefinita o definita dall’utente). Per aprirla, usa un firewall
    • Dettagli della connessione tramite protocollo di rete Sockets

Passaggi preliminari

I passaggi preliminari devono essere eseguiti sul computer locale. Completandoli, preparerai tutte le impostazioni e i file necessari per iniziare la distribuzione dell’applicazione:
  1. Crea un archivio con la ABBYY FineReader Engine Library (ad esempio, LibraryPackage.zip). L’elenco dei file è riportato nel file FREngineDistribution.csv.
    Importante! Se disponi di spazio di archiviazione limitato (ad esempio, se usi un App Service Plan con 1 GB di spazio), ti consigliamo di utilizzare l’opzione /extract per creare un pacchetto ABBYY FineReader Engine personalizzato di dimensioni minime. Lo spazio di archiviazione restante verrà utilizzato per l’elaborazione dei file.
    Quando crei l’archivio, tieni presente che le impostazioni di gestione delle licenze di ABBYY FineReader Engine devono essere configurate in base alle impostazioni della macchina virtuale:
    • Il file LicensingSettings.xml deve essere configurato per la modalità Network (vedi Utilizzo del file LicensingSettings.xml).
    • Deve essere utilizzato il protocollo di rete Sockets.
    • Il file del token della licenza online deve trovarsi nella cartella Bin64.
  2. Crea un account Azure Storage (frestorage in questo articolo). Tutte le istruzioni necessarie sono disponibili sul sito web di Azure.
  3. Crea l’App Service desiderato (vedi le istruzioni qui).
  4. Crea due contenitori Blob all’interno di frestorage:
    • fre-lib - per i file di ABBYY FineReader Engine
    • processing-container - per i risultati dell’elaborazione
  5. Carica LibraryPackage.zip nel contenitore fre-lib nel modo che preferisci (usando .NET, Powershell, uno script Python o le applicazioni Azure Storage Explorer/Azure Portal).
  6. Distribuisci e configura la macchina virtuale con le impostazioni di gestione delle licenze nel tuo account Azure:
    • Installa la utility License Manager tramite installLM.exe da LibraryPackage.zip.
    • Configura il protocollo di rete Sockets nel file LicensingSettings.xml, quindi riavvia il Licensing Service.
    • Assicurati che Azure App Service possa accedere alla porta di connessione del Licensing Service (modifica le regole di Windows Firewall sulla macchina virtuale).
    • Attiva la licenza (solo per Software protection; Online protection non richiede attivazione).
  7. Crea due code all’interno di frestorage:
    • processing-queue - per impostare le attività di elaborazione dei file
    • status-queue - per notificare il completamento delle attività
  8. Crea due progetti Azure WebJob (.NET Framework) in Visual Studio 2019 per lavorare con frestorage:
    • FreDeployerJob - per distribuire LibraryPackage.zip nell’App Service (vedi l’elenco dei relativi file: Config.cs, Functions.cs, Program.cs di seguito)
    • FreProcessorJob - per l’elaborazione dei documenti (vedi l’elenco dei relativi file: Config.cs, Functions.cs, Program.cs, EngineLoader.cs, IFileWriter.cs, Processor.cs di seguito)

Distribuzione e avvio di ABBYY FineReader Engine in App Service

Per distribuire ABBYY FineReader Engine:
  1. Pubblicare FreDeployerJob in Azure App Service usando Visual Studio (impostare Triggered come WebJob Type).
  2. Aprire l’App Service nel portale di Azure.
  3. Aprire la sezione WebJobs dell’App Service.
  4. Trovare FreDeployerJob nell’elenco dei WebJobs.
  5. Avviare FreDeployerJob con il comando del menu contestuale Run nella scheda WebJobs.
È possibile accedere alla scheda Logs per verificare il risultato della distribuzione. Se l’operazione ha esito positivo, LibraryPackage.zip viene caricato dal container fre-lib e distribuito nella cartella %HOME_EXPANDED%, disponibile per tutte le entità in App Service. Per distribuire FreProcessorJob, pubblicare FreProcessorJob in Azure App Service usando Visual Studio (impostare Continuous come WebJob Type). Di conseguenza, FreProcessorJob verrà visualizzato nell’elenco dei WebJobs dell’App Service. Per elaborare un file:
  1. Caricare il file da elaborare nel processing-container.
  2. Aggiungere alla processing-queue un messaggio JSON per una nuova attività di elaborazione nel formato {“blob-item-name” : “file_name”}. Se si carica Demo.tif nel processing-container, il messaggio dovrà essere:
{"blob-item-name" : "Demo.tif"}
  1. Attendere il completamento dell’attività. Non appena viene impostata la nuova attività, FreProcessorJob inizia a elaborare in memoria il file specificato. La status-queue conterrà voci relative all’esecuzione di questa attività.
  2. Trovare il file di output nel processing-container.
  1. FreProcessorJob funziona come un processo a thread singolo. Se si intende elaborare i file in parallelo, è necessario creare più FreProcessorJob in ascolto della stessa coda. 2. Ogni FreProcessorJob aggiuntivo consuma memoria aggiuntiva. Tenere conto di questo aspetto quando si acquista il Service Plan. Ad esempio, con Azure Free Service Plan è preferibile avere un solo FreProcessorJob, che consuma poca memoria e garantisce quindi la stabilità dell’elaborazione dei file. 3. L’uso di un solo FreProcessorJob non è adatto all’elaborazione di documenti di grandi dimensioni composti da più pagine. In questo caso, considerare invece il riconoscimento del documento in Azure Cloud Service o Azure Virtual Machine anziché in App Service.

Esempi di codice

Questa sezione include esempi di codice utilizzati per distribuire e implementare l’API ABBYY FineReader Engine in App Service.

FreDeployerJob:

using System.IO;
class Config
{
    // Stringa di connessione al blob container
    public static readonly string ConnectionString = "your_connection_string";
    // La directory HOME_EXPANDED è comune a tutte le cartelle di WebJobs
    public static readonly string LibraryFolder = Path.Combine(System.Environment.GetEnvironmentVariable("HOME_EXPANDED"), "FRE");
    // Nome dei container di input e output nello storage
    public static readonly string LibraryContainerName = "fre-lib";
namespace FreDeployerJob
{
    public class Functions
    {
        // Questa funzione non viene attivata automaticamente: devi eseguirla manualmente
        [NoAutomaticTrigger]
        [Timeout("01:00:00")]
        public static void DeployFRE()
        {
            Console.WriteLine("Deploying FRE");
            // Connessione al container di input esistente <InputContainerName> tramite la stringa di connessione dell'account di archiviazione
            BlobContainerClient inputContainerClient = new BlobContainerClient(Config.ConnectionString, Config.LibraryContainerName);
            // Creazione della directory della libreria e delle cartelle AppData e Temp per l'inizializzazione di ABBYY FineReader Engine
            if (Directory.Exists(Config.LibraryFolder) == true)
            {
                Directory.Delete(Config.LibraryFolder, true);
            }
            Directory.CreateDirectory(Config.LibraryFolder);
            Directory.CreateDirectory(Path.Combine(Config.LibraryFolder, "Temp"));
            Directory.CreateDirectory(Path.Combine(Config.LibraryFolder, "AppData"));
            // Iterazione dei blob nel container. Il blob in <InputContainerName> corrisponde a un file immagine
            foreach (BlobItem blobItem in inputContainerClient.GetBlobs())
            {
                Console.WriteLine("\t" + blobItem.Name);
                // Ricerca della versione light della libreria ABBYY FineReader Engine
                if (blobItem.Name == "LibraryPackage.zip")
                {
                    Console.WriteLine("LibraryPackage.zip was found.");
                    // Connessione al blob per accedere al relativo contenuto
                    BlobClient blobClient = new BlobClient(Config.ConnectionString, Config.LibraryContainerName, blobItem.Name);
                    Console.WriteLine("Downloading to memory...");
                    // Download del file zip in memoria
                    using (MemoryStream memoryStream = new MemoryStream())
                    {
                        blobClient.DownloadTo(memoryStream);
                        Console.WriteLine("LibraryPackage.zip was downloaded.");
                        Console.WriteLine("Unzipping...");
                        // Decompressione senza usare la cartella temporanea (solo elaborazione in memoria)
                        using (ZipArchive archive = new ZipArchive(memoryStream))
                        {
                            foreach (ZipArchiveEntry entry in archive.Entries)
                            {
                                string subDirectory = Path.GetDirectoryName(Path.Combine(Config.LibraryFolder, entry.FullName));
                                if (Directory.Exists(subDirectory) == false)
                                {
                                    Directory.CreateDirectory(subDirectory);
                                }
                                if (entry.Name.Length != 0)
                                {
                                    entry.ExtractToFile(Path.Combine(subDirectory, entry.Name));
                                }
                            }
                        }
                        Console.WriteLine("LibraryPackage.zip was unzipped to HOME_EXPANDED.");
                    }
                }
            }
        }
    }
}
namespace FreDeployerJob
{
    // Per ulteriori informazioni su Microsoft Azure WebJobs SDK, vedi https://go.microsoft.com/fwlink/?LinkID=320976
    class Program
    {
        // Imposta le seguenti stringhe di connessione in app.config affinché questi WebJobs vengano eseguiti:
        // AzureWebJobsDashboard and AzureWebJobsStorage
        static void Main()
        {
            // Quando viene attivato, questo WebJob chiamerà solo questo metodo
            Functions.DeployFRE();
        }
    }
}

FreProcessorJob:

using System;
using System.IO;
namespace FreProcessorJob
{
    class Config
    {
        // Stringa di connessione al blob container
        public static readonly string ConnectionString = "your_connection_string";
        // La directory HOME_EXPANDED è comune a tutte le cartelle WebJobs
        // È la stessa utilizzata nel progetto FreDeployerJob
        public static readonly string LibraryFolder = Path.Combine(System.Environment.GetEnvironmentVariable("HOME_EXPANDED"), "FRE");
        // Nome del container di elaborazione nell'archiviazione
        public static readonly string ProcessingContainerName = "processing-container";
        // Nome della coda di elaborazione
        public static readonly string ProcessingQueueName = "processing-queue";
        public static readonly string StatusQueueName = "status-queue";
        // Restituisce il Customer Project ID per ABBYY FineReader Engine
        public static String GetCustomerProjectId()
        {
            return "your_cpid";
        }
 
        // Restituisce il nome del token della licenza online
        // Se non utilizzi una licenza online, lascia una stringa vuota
        // Il token deve trovarsi nella cartella Bin64 del pacchetto della libreria
        public static String GetLicenseTokenName()
        {
            return "your_online_license_token_if_you_have_it";
        }
 
        // Restituisce la password della licenza online
        // Se non utilizzi una licenza online, lascia una stringa vuota
        public static String GetLicensePassword()
        {
            return "online_license_password_if_you have_it";
        }
 
        // Restituisce il nome del token di licenza
        // La gestione delle licenze si trova nella cartella Bin64 del pacchetto della libreria
        public static String GetLicenseTokenName()
        {
            return "your_licence_for_ABBYY FineReader Engine";
        }
 
        // Restituisce la password della licenza
        public static String GetLicensePassword()
        {
            return "license_password";
        }
 
        // Restituisce il percorso del motore
        public static String GetEngineFolder()
        {
            string engineSubfolder = "Bin64";
            string engineDllFolder = Path.Combine(LibraryFolder, engineSubfolder);
            return engineDllFolder;
        }
    }
}
using Microsoft.Azure.WebJobs;
using System;
using System.IO;
using Azure.Storage.Blobs;
using Azure.Storage.Queues;
using Newtonsoft.Json.Linq;
namespace FreProcessorJob
{
    public class Functions
    {
        // Questa funzione viene attivata/eseguita quando un nuovo messaggio viene scritto
        // in una Azure Queue denominata processing-queue
        // Il messaggio previsto è un messaggio JSON con la chiave 'blob-item-name'
        // i risultati dell'elaborazione verranno salvati nel container processing
        // lo stato dell'elaborazione verrà inviato a status-queue in formato JSON
        public static void ProcessQueueMessage([QueueTrigger("processing-queue")] string message)
        {
            // Per prima cosa, connessione a status-queue
            QueueClient queueClient = new QueueClient(Config.ConnectionString, Config.StatusQueueName);
            try
            {
                // Questo verrà registrato nei log del WebJob nel portale Azure
                Console.WriteLine("Accepted task: " + message);
                JObject task = JObject.Parse(message);
                task["processor_id"] = Environment.GetEnvironmentVariable("WEBJOBS_NAME");
 
                // Questo verrà inviato a status-queue
                task["status"] = "accepted";
                queueClient.SendMessage(task.ToString());
 
                // Recupero di blob-item-name, ovvero il nome del file nel container Processing
                string blobFileName = task["blob-item-name"].ToString();
                BlobClient blobClient = new BlobClient(Config.ConnectionString, Config.ProcessingContainerName, blobFileName);
                // Caricamento del blob in memoria
                Console.WriteLine("\t Downloading blob to memory: " + blobFileName);
                MemoryStream memoryStream = new MemoryStream();
                blobClient.DownloadTo(memoryStream);
                Console.WriteLine("\t Downloaded.");
                // Aggiornamento dello stato a processing
                Console.WriteLine("\t Processing in FRE: " + blobFileName);
                task["status"] = "processing";
                queueClient.SendMessage(task.ToString());
                // Elaborazione del blob scaricato in ABBYY FineReader Engine utilizzando metodi di elaborazione in memoria
                // L'output è il nome del risultato dell'elaborazione salvato come blob in <ProcessingContainerName>
                string resultBlobName = "";
                using (FreProcessor.Processor freProcessor = new FreProcessor.Processor())
                {
                    resultBlobName = freProcessor.ProcessBlobFromMemory(memoryStream, blobFileName);
                    Console.WriteLine("\t Result blob name in output container: " + resultBlobName);
                }
                // Eliminazione dell'immagine di input
                Console.WriteLine("\t Deleting from input container: " + blobFileName);
                blobClient.Delete();
                // Aggiornamento dello stato a succeeded
                // nei log del portale Azure
                Console.WriteLine("Succeeded");
 
                // in status-queue
                task["status"] = "succeeded";
                task["result-blob-name"] = resultBlobName;
                queueClient.SendMessage(task.ToString());
            }
            catch (Exception error)
            {
                // In caso di errori, segnalazione
                // nei log del portale Azure
                Console.WriteLine("Failed: " + error.Message);
                // a status-queue
                JObject task = new JObject(); 
                task["processor_id"] = Environment.GetEnvironmentVariable("WEBJOBS_NAME");
                task["status"] = "failed";
                task["error"] = error.Message;
                task["task"] = message;
                queueClient.SendMessage(task.ToString());
            }
        }
    }
}
using Microsoft.Azure.WebJobs;
namespace FreProcessorJob
{
    // Per ulteriori informazioni su Microsoft Azure WebJobs SDK, vedere https://go.microsoft.com/fwlink/?LinkID=320976
    class Program
    {
        // Impostare le seguenti connection string in app.config affinché questi WebJobs possano essere eseguiti:
        // AzureWebJobsDashboard and AzureWebJobsStorage
        static void Main()
        {
            var config = new JobHostConfiguration();
            if (config.IsDevelopment)
            {
                config.UseDevelopmentSettings();
            }
            // ABBYY FineReader Engine non è thread-safe, quindi non è possibile elaborare più di un messaggio contemporaneamente
            config.Queues.BatchSize = 1;
            var host = new JobHost(config);
            // Il codice seguente garantisce che il WebJob resti in esecuzione continua
            // poiché una delle funzioni è collegata a una Azure Queue e rimane in ascolto di nuove attività
            host.RunAndBlock();
        }
    }
}
using System;
using System.IO;
using System.Runtime.InteropServices;
using FREngine;
namespace FreProcessorJob.FreProcessor
{
    // Classe per il caricamento/scaricamento di FREngine.dll e l'inizializzazione/deinizializzazione del motore
    // Il caricamento viene eseguito nel costruttore, lo scaricamento in Dispose()
    // Genera eccezioni in caso di errore durante il caricamento
    public class EngineLoader : IDisposable
    {
        // Carica ABBYY FineReader Engine con le impostazioni memorizzate in SamplesConfig.cs
        public EngineLoader()
        {
            string enginePath = Path.Combine(Config.GetEngineFolder(), "FREngine.dll");
            string customerProjectId = Config.GetCustomerProjectId();
            string licensePath = Path.Combine(Config.GetEngineFolder(), Config.GetLicenseTokenName());
            string licensePassword = Config.GetLicensePassword();
            try
            {
                // Carica la libreria FREngine.dll
                dllHandle = LoadLibraryEx(enginePath, IntPtr.Zero, LOAD_WITH_ALTERED_SEARCH_PATH);
                if (dllHandle == IntPtr.Zero)
                {
                    int error = Marshal.GetLastWin32Error();
                    Console.WriteLine("L'ultimo errore Win32 è stato: " + error);
                    throw new Exception("Impossibile caricare " + enginePath);
                }
 
                IntPtr initializeEnginePtr = GetProcAddress(dllHandle, "InitializeEngine");
                if (initializeEnginePtr == IntPtr.Zero)
                {
                    throw new Exception("Impossibile trovare la funzione InitializeEngine");
                }
                IntPtr deinitializeEnginePtr = GetProcAddress(dllHandle, "DeinitializeEngine");
                if (deinitializeEnginePtr == IntPtr.Zero)
                {
                    throw new Exception("Impossibile trovare la funzione DeinitializeEngine");
                }
                IntPtr dllCanUnloadNowPtr = GetProcAddress(dllHandle, "DllCanUnloadNow");
                if (dllCanUnloadNowPtr == IntPtr.Zero)
                {
                    throw new Exception("Impossibile trovare la funzione DllCanUnloadNow");
                }
                // Converte i puntatori in delegati
                initializeEngine = (InitializeEngine)Marshal.GetDelegateForFunctionPointer(
                    initializeEnginePtr, typeof(InitializeEngine));
                deinitializeEngine = (DeinitializeEngine)Marshal.GetDelegateForFunctionPointer(
                    deinitializeEnginePtr, typeof(DeinitializeEngine));
                dllCanUnloadNow = (DllCanUnloadNow)Marshal.GetDelegateForFunctionPointer(
                    dllCanUnloadNowPtr, typeof(DllCanUnloadNow));
                // Chiama la funzione InitializeEngine
                string dataFolder = Path.Combine(Config.LibraryFolder, "AppData");
                string tempFolder = Path.Combine(Config.LibraryFolder, "Temp");
                int hresult = initializeEngine(customerProjectId, licensePath, licensePassword,
                    dataFolder, tempFolder, false, ref engine);
                Marshal.ThrowExceptionForHR(hresult);
            }
            catch (Exception)
            {
                // Libera la libreria FREngine.dll
                engine = null;
                // Eliminazione di tutti gli oggetti prima della chiamata a FreeLibrary
                GC.Collect();
                GC.WaitForPendingFinalizers();
                GC.Collect();
                FreeLibrary(dllHandle);
                dllHandle = IntPtr.Zero;
                initializeEngine = null;
                deinitializeEngine = null;
                dllCanUnloadNow = null;
                throw;
            }
        }
        // Scarica ABBYY FineReader Engine
        public void Dispose()
        {
            if (engine == null)
            {
                // Il motore non è stato caricato
                return;
            }
            engine = null;
            int hresult = deinitializeEngine();
            // Eliminazione di tutti gli oggetti prima della chiamata a FreeLibrary
            GC.Collect();
            GC.WaitForPendingFinalizers();
            GC.Collect();
            hresult = dllCanUnloadNow();
            if (hresult == 0)
            {
                FreeLibrary(dllHandle);
            }
            dllHandle = IntPtr.Zero;
            initializeEngine = null;
            deinitializeEngine = null;
            dllCanUnloadNow = null;
            // Generazione dell'eccezione dopo la pulizia
            Marshal.ThrowExceptionForHR(hresult);
        }
        // Restituisce il puntatore all'oggetto principale di ABBYY FineReader Engine
        public IEngine Engine
        {
            get
            {
                return engine;
            }
        }
        // Funzioni di Kernel32.dll
        [DllImport("kernel32.dll")]
        private static extern IntPtr LoadLibraryEx(string dllToLoad, IntPtr reserved, uint flags);
        private const uint LOAD_WITH_ALTERED_SEARCH_PATH = 0x00000008;
        [DllImport("kernel32.dll")]
        private static extern IntPtr GetProcAddress(IntPtr hModule, string procedureName);
        [DllImport("kernel32.dll")]
        private static extern bool FreeLibrary(IntPtr hModule);
        // Funzioni di FREngine.dll
        [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode)]
        private delegate int InitializeEngine(string customerProjectId, string licensePath, string licensePassword,
            string dataFolder, string tempFolder, bool isSharedCPUCoresMode, ref FREngine.IEngine engine);
        [UnmanagedFunctionPointer(CallingConvention.StdCall)]
        private delegate int DeinitializeEngine();
        [UnmanagedFunctionPointer(CallingConvention.StdCall)]
        private delegate int DllCanUnloadNow();
        // Variabili private
        private FREngine.IEngine engine = null;
        // Handle per FREngine.dll
        private IntPtr dllHandle = IntPtr.Zero;
        private InitializeEngine initializeEngine = null;
        private DeinitializeEngine deinitializeEngine = null;
        private DllCanUnloadNow dllCanUnloadNow = null;
    }
}
using System;
using System.IO;
using Azure.Storage.Blobs;
namespace FreProcessorJob.FreProcessor
{
    public class FileWriter : FREngine.IFileWriter, IDisposable
    {
        public FileWriter(string _resultBlobName, string _fileExtension)
        {
            resultBlobName = _resultBlobName;
            fileExtension = _fileExtension;
        }
        public void Open(string fileName, ref int bufferSize)
        {
            stream = new MemoryStream();
        }
        public void Write(byte[] data)
        {
            stream.Write(data, 0, data.Length);
        }
        public void Close()
        {
            // Crea una connessione a un nuovo blob in <ProcessingContainerName>. Il risultato dell'elaborazione verrà archiviato qui
            BlobClient resultBlobClient = new BlobClient(Config.ConnectionString, 
                Config.ProcessingContainerName,
                resultBlobName + fileExtension);
            // Sovrascrive il file esistente
            resultBlobClient.DeleteIfExists();
            // Imposta la posizione su 0 per scrivere il file dall'inizio
            stream.Position = 0;
            resultBlobClient.Upload(stream);
            stream.Close();
        }
        public void Dispose()
        {
            // Chiude lo stream di memoria durante il rilascio per potervi accedere dopo la scrittura dei dati
            stream.Close();
        }
        private string resultBlobName;
        private string fileExtension;
        private MemoryStream stream;
    }
}
using System;
using System.Runtime.InteropServices;
using System.IO;
using FREngine;
namespace FreProcessorJob.FreProcessor
{
    class Processor : IDisposable
    {
        private EngineLoader engineLoader = null;
        private void displayMessage(string text)
        {
            Console.WriteLine("\t" + text);
        }
        private void setupFREngine()
        {
            displayMessage("Caricamento del profilo predefinito...");
            // operazione facoltativa
            engineLoader.Engine.LoadPredefinedProfile("DocumentConversion_Accuracy");
            // operazione obbligatoria nei piani App Service Plan con prestazioni ridotte, altrimenti si verificheranno errori durante l'elaborazione parallela
            engineLoader.Engine.MultiProcessingParams.MultiProcessingMode = MultiProcessingModeEnum.MPM_Sequential;
        }
        private void LoadEngine()
        {
            try
            {
                if (engineLoader == null)
                {
                    engineLoader = new EngineLoader();
                }
                setupFREngine();
            }
            catch (Exception error)
            {
                displayMessage("errore: " + error.Message);
            }
        }
        private void UnloadEngine()
        {
            try
            {
                if (engineLoader != null)
                {
                    engineLoader.Dispose();
                    engineLoader = null;
                }
            }
            catch (Exception error)
            {
                displayMessage("errore: " + error.Message);
            }
        }
        public Processor()
        {
            LoadEngine();
        }
        public string ProcessBlobFromMemory(MemoryStream inputMemoryStream, string inputBlobName)
        {
            FRDocument document = engineLoader.Engine.CreateFRDocument();
            string resultBlobName = "";
            try
            {
                document.PageFlushingPolicy = FREngine.PageFlushingPolicyEnum.PFP_KeepInMemory;
 
                // Aggiunge il file immagine al documento
                displayMessage("Caricamento dell'immagine...");
                IntPtr handle = Marshal.AllocHGlobal(inputMemoryStream.GetBuffer().Length);
                Marshal.Copy(inputMemoryStream.GetBuffer(), 0, handle, inputMemoryStream.GetBuffer().Length);
                document.AddImageFileFromMemory(handle.ToInt64(), null, null);
                // Riconosce il documento
                displayMessage("Riconoscimento in corso...");
                document.Process(null);
                // Salva i risultati
                displayMessage("Salvataggio dei risultati...");
                FileWriter fileWriter = new FileWriter(inputBlobName, ".pdf");
                resultBlobName = inputBlobName + ".pdf";
                document.ExportToMemory(fileWriter, FREngine.FileExportFormatEnum.FEF_PDF, null);
            }
            catch (Exception error)
            {
                displayMessage("errore: " + error.Message);
                throw error;
            }
            finally
            {
                // Chiude il documento
                document.Close();
            }
            return resultBlobName;
        }
        public void Dispose()
        {
            UnloadEngine();
        }
    }
}