diff --git a/src/Tesseract.Tests/BaseApiTests.cs b/src/Tesseract.Tests/BaseApiTests.cs index 8fa602b3..73e6d627 100644 --- a/src/Tesseract.Tests/BaseApiTests.cs +++ b/src/Tesseract.Tests/BaseApiTests.cs @@ -11,7 +11,7 @@ public class BaseApiTests [Test] public void GetVersion_Is302() { - var version = Interop.TessApi.GetVersion(); + var version = Interop.TessApi.Native.GetVersion(); Assert.That(version, Is.EqualTo("3.02")); } } diff --git a/src/Tesseract/Interop/BaseApi.cs b/src/Tesseract/Interop/BaseApi.cs index 0a6e8198..6ecd9ffb 100644 --- a/src/Tesseract/Interop/BaseApi.cs +++ b/src/Tesseract/Interop/BaseApi.cs @@ -1,12 +1,172 @@ - -using System; +using System; using System.Runtime.InteropServices; using System.Text; +using InteropDotNet; namespace Tesseract.Interop { - public static class TessApi - { + public interface ITessApiSignatures + { + // Helper functions + [RuntimeDllImport(Constants.TesseractDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "TessVersion")] + string GetVersion(); + + [RuntimeDllImport(Constants.TesseractDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "TessDeleteText")] + void DeleteText(IntPtr textPtr); + + [RuntimeDllImport(Constants.TesseractDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "TessDeleteTextArray")] + void DeleteTextArray(IntPtr arr); + + [RuntimeDllImport(Constants.TesseractDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "TessDeleteIntArray")] + void DeleteIntArray(IntPtr arr); + + [RuntimeDllImport(Constants.TesseractDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "TessDeleteBlockList")] + void DeleteBlockList(IntPtr arr); + + // Base API + + /// + /// Creates a new BaseAPI instance + /// + /// + [RuntimeDllImport(Constants.TesseractDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "TessBaseAPICreate")] + IntPtr BaseApiCreate(); + + + /// + /// Deletes a base api instance. + /// + /// + [RuntimeDllImport(Constants.TesseractDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "TessBaseAPIDelete")] + void BaseApiDelete(HandleRef ptr); + + [RuntimeDllImport(Constants.TesseractDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "TessBaseAPIInit1")] + int BaseApiInit(HandleRef handle, + string datapath, + string language, + int mode, + IntPtr configs, int configs_size, + IntPtr vars_vec, int vars_vec_size, + IntPtr vars_values, int vars_values_size); + + + [RuntimeDllImport(Constants.TesseractDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "TessBaseAPISetVariable")] + int BaseApiSetVariable(HandleRef handle, string name, string value); + + + [RuntimeDllImport(Constants.TesseractDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "TessBaseAPISetDebugVariable")] + int BaseApiSetDebugVariable(HandleRef handle, string name, string value); + + [RuntimeDllImport(Constants.TesseractDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "TessBaseAPIGetIntVariable")] + int BaseApiGetIntVariable(HandleRef handle, string name, out int value); + + [RuntimeDllImport(Constants.TesseractDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "TessBaseAPIGetBoolVariable")] + int BaseApiGetBoolVariable(HandleRef handle, string name, out int value); + + [RuntimeDllImport(Constants.TesseractDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "TessBaseAPIGetDoubleVariable")] + int BaseApiGetDoubleVariable(HandleRef handle, string name, out double value); + + [RuntimeDllImport(Constants.TesseractDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "TessBaseAPIGetStringVariable")] + IntPtr BaseApiGetStringVariableInternal(HandleRef handle, string name); + + [RuntimeDllImport(Constants.TesseractDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "TessBaseAPISetPageSegMode")] + void BaseAPISetPageSegMode(HandleRef handle, PageSegMode mode); + + [RuntimeDllImport(Constants.TesseractDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "TessBaseAPIGetPageSegMode")] + PageSegMode BaseAPIGetPageSegMode(HandleRef handle); + + // image analysis + [RuntimeDllImport(Constants.TesseractDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "TessBaseAPISetImage2")] + void BaseApiSetImage(HandleRef handle, HandleRef pixHandle); + + [RuntimeDllImport(Constants.TesseractDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "TessBaseAPISetInputName")] + void BaseApiSetInputName(HandleRef handle, string value); + + + [RuntimeDllImport(Constants.TesseractDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "TessBaseAPISetRectangle")] + void BaseApiSetRectangle(HandleRef handle, int left, int top, int width, int height); + + [RuntimeDllImport(Constants.TesseractDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "TessBaseAPIRecognize")] + int BaseApiRecognize(HandleRef handle, HandleRef monitor); + + [RuntimeDllImport(Constants.TesseractDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "TessBaseAPIAnalyseLayout")] + IntPtr BaseAPIAnalyseLayout(HandleRef handle); + + [RuntimeDllImport(Constants.TesseractDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "TessBaseAPIGetIterator")] + IntPtr BaseApiGetIterator(HandleRef handle); + + [RuntimeDllImport(Constants.TesseractDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "TessBaseAPIGetUTF8Text")] + IntPtr BaseAPIGetUTF8TextInternal(HandleRef handle); + + [RuntimeDllImport(Constants.TesseractDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "TessBaseAPIGetHOCRText")] + IntPtr BaseAPIGetHOCRTextInternal(HandleRef handle, int pageNum); + + [RuntimeDllImport(Constants.TesseractDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "TessBaseAPIMeanTextConf")] + int BaseAPIMeanTextConf(HandleRef handle); + + + [RuntimeDllImport(Constants.TesseractDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "TessBaseAPIClear")] + void BaseAPIClear(HandleRef handle); + + // result iterator + + [RuntimeDllImport(Constants.TesseractDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "TessResultIteratorDelete")] + void ResultIteratorDelete(HandleRef handle); + + [RuntimeDllImport(Constants.TesseractDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "TessResultIteratorCopy")] + IntPtr ResultIteratorCopy(HandleRef handle); + + [RuntimeDllImport(Constants.TesseractDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "TessResultIteratorGetPageIterator")] + IntPtr ResultIteratorGetPageIterator(HandleRef handle); + + [RuntimeDllImport(Constants.TesseractDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "TessResultIteratorGetUTF8Text")] + IntPtr ResultIteratorGetUTF8TextInternal(HandleRef handle, PageIteratorLevel level); + + [RuntimeDllImport(Constants.TesseractDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "TessResultIteratorConfidence")] + float ResultIteratorGetConfidence(HandleRef handle, PageIteratorLevel level); + + // page iterator + + [RuntimeDllImport(Constants.TesseractDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "TessPageIteratorDelete")] + void PageIteratorDelete(HandleRef handle); + + [RuntimeDllImport(Constants.TesseractDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "TessPageIteratorCopy")] + IntPtr PageIteratorCopy(HandleRef handle); + + [RuntimeDllImport(Constants.TesseractDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "TessPageIteratorBegin")] + void PageIteratorBegin(HandleRef handle); + + [RuntimeDllImport(Constants.TesseractDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "TessPageIteratorNext")] + int PageIteratorNext(HandleRef handle, PageIteratorLevel level); + + [RuntimeDllImport(Constants.TesseractDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "TessPageIteratorIsAtBeginningOf")] + int PageIteratorIsAtBeginningOf(HandleRef handle, PageIteratorLevel level); + + [RuntimeDllImport(Constants.TesseractDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "TessPageIteratorIsAtFinalElement")] + int PageIteratorIsAtFinalElement(HandleRef handle, PageIteratorLevel level, PageIteratorLevel element); + + [RuntimeDllImport(Constants.TesseractDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "TessPageIteratorBoundingBox")] + int PageIteratorBoundingBox(HandleRef handle, PageIteratorLevel level, out int left, out int top, out int right, out int bottom); + + [RuntimeDllImport(Constants.TesseractDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "TessPageIteratorBlockType")] + PolyBlockType PageIteratorBlockType(HandleRef handle); + + [RuntimeDllImport(Constants.TesseractDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "TessPageIteratorGetBinaryImage")] + IntPtr PageIteratorGetBinaryImage(HandleRef handle, PageIteratorLevel level); + + [RuntimeDllImport(Constants.TesseractDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "TessPageIteratorGetImage")] + IntPtr PageIteratorGetImage(HandleRef handle, PageIteratorLevel level, int padding, out int left, out int top); + + [RuntimeDllImport(Constants.TesseractDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "TessPageIteratorBaseline")] + int PageIteratorBaseline(HandleRef handle, PageIteratorLevel level, out int x1, out int y1, out int x2, out int y2); + + [RuntimeDllImport(Constants.TesseractDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "TessPageIteratorOrientation")] + void PageIteratorOrientation(HandleRef handle, out Orientation orientation, out WritingDirection writing_direction, out TextLineOrder textLineOrder, out float deskew_angle); + } + + public static class TessApi + { + public const string htmlBeginTag = "\n" @@ -17,128 +177,56 @@ public static class TessApi public const string htmlEndTag = "\n\n"; - static TessApi() + private static ITessApiSignatures native; + + public static ITessApiSignatures Native { - // first load liblept as tesseract depends on it. - WindowsLibraryLoader.Instance.LoadLibrary(Constants.LeptonicaDllName); - // now load unmanaged tesseract dll. - WindowsLibraryLoader.Instance.LoadLibrary(Constants.TesseractDllName); + get + { + if (native == null) + Initialize(); + return native; + } } - // Helper functions - [DllImport(Constants.TesseractDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint="TessVersion")] - public static extern string GetVersion(); - - [DllImport(Constants.TesseractDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint="TessDeleteText")] - public static extern void DeleteText(IntPtr textPtr); - - [DllImport(Constants.TesseractDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint="TessDeleteTextArray")] - public static extern void DeleteTextArray(IntPtr arr); - - [DllImport(Constants.TesseractDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint="TessDeleteIntArray")] - public static extern void DeleteIntArray(IntPtr arr); - - [DllImport(Constants.TesseractDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint="TessDeleteBlockList")] - public static extern void DeleteBlockList(IntPtr arr); - - // Base API - - /// - /// Creates a new BaseAPI instance - /// - /// - [DllImport(Constants.TesseractDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint="TessBaseAPICreate")] - public static extern IntPtr BaseApiCreate(); - - - /// - /// Deletes a base api instance. - /// - /// - [DllImport(Constants.TesseractDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint="TessBaseAPIDelete")] - public static extern void BaseApiDelete(HandleRef ptr); - - [DllImport(Constants.TesseractDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint="TessBaseAPIInit1")] - public static extern int BaseApiInit(HandleRef handle, - string datapath, - string language, - int mode, - IntPtr configs, int configs_size, - IntPtr vars_vec, int vars_vec_size, - IntPtr vars_values, int vars_values_size); - - - [DllImport(Constants.TesseractDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint="TessBaseAPISetVariable")] - public static extern int BaseApiSetVariable(HandleRef handle, string name, string value); - - - [DllImport(Constants.TesseractDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint="TessBaseAPISetDebugVariable")] - public static extern int BaseApiSetDebugVariable(HandleRef handle, string name, string value); - - [DllImport(Constants.TesseractDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint="TessBaseAPIGetIntVariable")] - public static extern int BaseApiGetIntVariable(HandleRef handle, string name, out int value); - - [DllImport(Constants.TesseractDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint="TessBaseAPIGetBoolVariable")] - public static extern int BaseApiGetBoolVariable(HandleRef handle, string name, out int value); - - [DllImport(Constants.TesseractDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint="TessBaseAPIGetDoubleVariable")] - public static extern int BaseApiGetDoubleVariable(HandleRef handle, string name, out double value); - - public static string BaseApiGetStringVariable(HandleRef handle, string name) - { - var resultHandle = BaseApiGetStringVariableInternal(handle, name); - - return Marshal.PtrToStringAnsi(resultHandle); - } - - [DllImport(Constants.TesseractDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint="TessBaseAPIGetStringVariable")] - public static extern IntPtr BaseApiGetStringVariableInternal(HandleRef handle, string name); - - [DllImport(Constants.TesseractDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint="TessBaseAPISetPageSegMode")] - public static extern void BaseAPISetPageSegMode(HandleRef handle, PageSegMode mode); - - [DllImport(Constants.TesseractDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "TessBaseAPIGetPageSegMode")] - public static extern PageSegMode BaseAPIGetPageSegMode(HandleRef handle); - - // image analysis - [DllImport(Constants.TesseractDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "TessBaseAPISetImage2")] - public static extern void BaseApiSetImage(HandleRef handle, HandleRef pixHandle); - - [DllImport(Constants.TesseractDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "TessBaseAPISetInputName")] - public static extern void BaseApiSetInputName(HandleRef handle, string value); + public static void Initialize() + { + if (native == null) + { + LeptonicaApi.Initialize(); + native = InteropRuntimeImplementer.CreateInstance(); + } + } - - [DllImport(Constants.TesseractDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "TessBaseAPISetRectangle")] - public static extern void BaseApiSetRectangle(HandleRef handle, int left, int top, int width, int height); - - [DllImport(Constants.TesseractDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "TessBaseAPIRecognize")] - public static extern int BaseApiRecognize(HandleRef handle, HandleRef monitor); - - [DllImport(Constants.TesseractDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "TessBaseAPIAnalyseLayout")] - public static extern IntPtr BaseAPIAnalyseLayout(HandleRef handle); + public static string BaseApiGetStringVariable(HandleRef handle, string name) + { + var resultHandle = Native.BaseApiGetStringVariableInternal(handle, name); - [DllImport(Constants.TesseractDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "TessBaseAPIGetIterator")] - public static extern IntPtr BaseApiGetIterator(HandleRef handle); + return Marshal.PtrToStringAnsi(resultHandle); + } public static string BaseAPIGetUTF8Text(HandleRef handle) { - IntPtr txtHandle = BaseAPIGetUTF8TextInternal(handle); - if (txtHandle != IntPtr.Zero) { + IntPtr txtHandle = Native.BaseAPIGetUTF8TextInternal(handle); + if (txtHandle != IntPtr.Zero) + { var result = MarshalHelper.PtrToString(txtHandle, Encoding.UTF8); - TessApi.DeleteText(txtHandle); + TessApi.Native.DeleteText(txtHandle); return result; - } else { + } + else + { return null; } } public static string BaseAPIGetHOCRText(HandleRef handle, int pageNum) { - IntPtr txtHandle = BaseAPIGetHOCRTextInternal(handle, pageNum); + IntPtr txtHandle = Native.BaseAPIGetHOCRTextInternal(handle, pageNum); if (txtHandle != IntPtr.Zero) { var result = MarshalHelper.PtrToString(txtHandle, Encoding.UTF8); - TessApi.DeleteText(txtHandle); + TessApi.Native.DeleteText(txtHandle); return htmlBeginTag + result + htmlEndTag; } else @@ -147,84 +235,19 @@ public static string BaseAPIGetHOCRText(HandleRef handle, int pageNum) } } - [DllImport(Constants.TesseractDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "TessBaseAPIGetUTF8Text")] - private static extern IntPtr BaseAPIGetUTF8TextInternal(HandleRef handle); - - [DllImport(Constants.TesseractDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "TessBaseAPIGetHOCRText")] - private static extern IntPtr BaseAPIGetHOCRTextInternal(HandleRef handle, int pageNum); - - [DllImport(Constants.TesseractDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "TessBaseAPIMeanTextConf")] - public static extern int BaseAPIMeanTextConf(HandleRef handle); - - - [DllImport(Constants.TesseractDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "TessBaseAPIClear")] - public static extern void BaseAPIClear(HandleRef handle); - - // result iterator - - [DllImport(Constants.TesseractDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "TessResultIteratorDelete")] - public static extern void ResultIteratorDelete(HandleRef handle); - - [DllImport(Constants.TesseractDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "TessResultIteratorCopy")] - public static extern IntPtr ResultIteratorCopy(HandleRef handle); - - [DllImport(Constants.TesseractDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "TessResultIteratorGetPageIterator")] - public static extern IntPtr ResultIteratorGetPageIterator(HandleRef handle); - public static string ResultIteratorGetUTF8Text(HandleRef handle, PageIteratorLevel level) { - IntPtr txtHandle = ResultIteratorGetUTF8TextInternal(handle, level); - if (txtHandle != IntPtr.Zero) { + IntPtr txtHandle = Native.ResultIteratorGetUTF8TextInternal(handle, level); + if (txtHandle != IntPtr.Zero) + { var result = MarshalHelper.PtrToString(txtHandle, Encoding.UTF8); - TessApi.DeleteText(txtHandle); + TessApi.Native.DeleteText(txtHandle); return result; - } else { + } + else + { return null; } } - - [DllImport(Constants.TesseractDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "TessResultIteratorGetUTF8Text")] - private static extern IntPtr ResultIteratorGetUTF8TextInternal(HandleRef handle, PageIteratorLevel level); - - [DllImport(Constants.TesseractDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "TessResultIteratorConfidence")] - public static extern float ResultIteratorGetConfidence(HandleRef handle, PageIteratorLevel level); - - // page iterator - - [DllImport(Constants.TesseractDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "TessPageIteratorDelete")] - public static extern void PageIteratorDelete(HandleRef handle); - - [DllImport(Constants.TesseractDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "TessPageIteratorCopy")] - public static extern IntPtr PageIteratorCopy(HandleRef handle); - - [DllImport(Constants.TesseractDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "TessPageIteratorBegin")] - public static extern void PageIteratorBegin(HandleRef handle); - - [DllImport(Constants.TesseractDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "TessPageIteratorNext")] - public static extern int PageIteratorNext(HandleRef handle, PageIteratorLevel level); - - [DllImport(Constants.TesseractDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "TessPageIteratorIsAtBeginningOf")] - public static extern int PageIteratorIsAtBeginningOf(HandleRef handle, PageIteratorLevel level); - - [DllImport(Constants.TesseractDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "TessPageIteratorIsAtFinalElement")] - public static extern int PageIteratorIsAtFinalElement(HandleRef handle, PageIteratorLevel level, PageIteratorLevel element); - - [DllImport(Constants.TesseractDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "TessPageIteratorBoundingBox")] - public static extern int PageIteratorBoundingBox(HandleRef handle, PageIteratorLevel level, out int left, out int top, out int right, out int bottom); - - [DllImport(Constants.TesseractDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "TessPageIteratorBlockType")] - public static extern PolyBlockType PageIteratorBlockType(HandleRef handle); - - [DllImport(Constants.TesseractDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "TessPageIteratorGetBinaryImage")] - public static extern IntPtr PageIteratorGetBinaryImage(HandleRef handle, PageIteratorLevel level); - - [DllImport(Constants.TesseractDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "TessPageIteratorGetImage")] - public static extern IntPtr PageIteratorGetImage(HandleRef handle, PageIteratorLevel level, int padding, out int left, out int top); - - [DllImport(Constants.TesseractDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "TessPageIteratorBaseline")] - public static extern int PageIteratorBaseline(HandleRef handle, PageIteratorLevel level, out int x1, out int y1, out int x2, out int y2); - - [DllImport(Constants.TesseractDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "TessPageIteratorOrientation")] - public static extern void PageIteratorOrientation(HandleRef handle, out Orientation orientation, out WritingDirection writing_direction, out TextLineOrder textLineOrder, out float deskew_angle); - } + } } diff --git a/src/Tesseract/Interop/LeptonicaApi.cs b/src/Tesseract/Interop/LeptonicaApi.cs index 36f86cba..82c64596 100644 --- a/src/Tesseract/Interop/LeptonicaApi.cs +++ b/src/Tesseract/Interop/LeptonicaApi.cs @@ -2,118 +2,112 @@ using System.Collections.Generic; using System.Runtime.InteropServices; using System.Text; +using InteropDotNet; namespace Tesseract.Interop { - public unsafe static class LeptonicaApi + public interface ILeptonicaApiSignatures { - static LeptonicaApi() - { - // This may have already been loaded by tesseract but that's fine (EmbeddedDllLoader won't try and load the dll again). - WindowsLibraryLoader.Instance.LoadLibrary("liblept168.dll"); - } + #region Pix + [RuntimeDllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixCreate")] + unsafe IntPtr pixCreate(int width, int height, int depth); - #region Pix + [RuntimeDllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixClone")] + unsafe IntPtr pixClone(HandleRef pix); + + + [RuntimeDllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixDestroy")] + void pixDestroy(ref IntPtr pix); + + [RuntimeDllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixGetWidth")] + int pixGetWidth(HandleRef pix); - [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixCreate")] - public static unsafe extern IntPtr pixCreate(int width, int height, int depth); - - [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixClone")] - public static unsafe extern IntPtr pixClone(HandleRef pix); - - - [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixDestroy")] - public static extern void pixDestroy(ref IntPtr pix); + [RuntimeDllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixGetHeight")] + int pixGetHeight(HandleRef pix); - [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixGetWidth")] - public static extern int pixGetWidth(HandleRef pix); - [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixGetHeight")] - public static extern int pixGetHeight(HandleRef pix); + [RuntimeDllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixGetDepth")] + int pixGetDepth(HandleRef pix); + [RuntimeDllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixGetXRes")] + int pixGetXRes(HandleRef pix); - [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixGetDepth")] - public static extern int pixGetDepth(HandleRef pix); - - [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixGetXRes")] - public static extern int pixGetXRes(HandleRef pix); + [RuntimeDllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixGetYRes")] + int pixGetYRes(HandleRef pix); - [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixGetYRes")] - public static extern int pixGetYRes(HandleRef pix); + [RuntimeDllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixGetResolution")] + int pixGetResolution(HandleRef pix, out int xres, out int yres); - [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixGetResolution")] - public static extern int pixGetResolution(HandleRef pix, out int xres, out int yres); + [RuntimeDllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixGetWpl")] + int pixGetWpl(HandleRef pix); - [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixGetWpl")] - public static extern int pixGetWpl(HandleRef pix); + [RuntimeDllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixSetXRes")] + int pixSetXRes(HandleRef pix, int xres); - [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixSetXRes")] - public static extern int pixSetXRes(HandleRef pix, int xres); + [RuntimeDllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixSetYRes")] + int pixSetYRes(HandleRef pix, int yres); - [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixSetYRes")] - public static extern int pixSetYRes(HandleRef pix, int yres); + [RuntimeDllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixSetResolution")] + int pixSetResolution(HandleRef pix, int xres, int yres); - [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixSetResolution")] - public static extern int pixSetResolution(HandleRef pix, int xres, int yres); + [RuntimeDllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixScaleResolution")] + int pixScaleResolution(HandleRef pix, float xscale, float yscale); - [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixScaleResolution")] - public static extern int pixScaleResolution(HandleRef pix, float xscale, float yscale); + [RuntimeDllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixGetData")] + IntPtr pixGetData(HandleRef pix); - [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixGetData")] - public static extern IntPtr pixGetData(HandleRef pix); + [RuntimeDllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixGetInputFormat")] + ImageFormat pixGetInputFormat(HandleRef pix); - [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixGetInputFormat")] - public static extern ImageFormat pixGetInputFormat(HandleRef pix); + [RuntimeDllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixSetInputFormat")] + int pixSetInputFormat(HandleRef pix, ImageFormat inputFormat); - [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixSetInputFormat")] - public static extern int pixSetInputFormat(HandleRef pix, ImageFormat inputFormat); - - [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixEndianByteSwap")] - public static extern int pixEndianByteSwap(HandleRef pix); + [RuntimeDllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixEndianByteSwap")] + int pixEndianByteSwap(HandleRef pix); - [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixRead")] - public static extern IntPtr pixRead(string filename); + [RuntimeDllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixRead")] + IntPtr pixRead(string filename); - [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixWrite")] - public static extern int pixWrite(string filename, HandleRef handle, ImageFormat format); - - [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixGetColormap")] - public static extern IntPtr pixGetColormap(HandleRef pix); + [RuntimeDllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixWrite")] + int pixWrite(string filename, HandleRef handle, ImageFormat format); - [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixSetColormap")] - public static extern int pixSetColormap(HandleRef pix, HandleRef pixCmap); - - [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixDestroyColormap")] - public static extern int pixDestroyColormap(HandleRef pix); + [RuntimeDllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixGetColormap")] + IntPtr pixGetColormap(HandleRef pix); + + [RuntimeDllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixSetColormap")] + int pixSetColormap(HandleRef pix, HandleRef pixCmap); + + [RuntimeDllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixDestroyColormap")] + int pixDestroyColormap(HandleRef pix); // pixconv.h functions - [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixConvertRGBToGray")] - public static extern IntPtr pixConvertRGBToGray(HandleRef pix, float rwt, float gwt, float bwt); + [RuntimeDllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixConvertRGBToGray")] + IntPtr pixConvertRGBToGray(HandleRef pix, float rwt, float gwt, float bwt); // image analysis and manipulation functions // skew - [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixDeskewGeneral")] - public static extern IntPtr pixDeskewGeneral(HandleRef pix, int redSweep, float sweepRange, float sweepDelta, int redSearch, int thresh, out float pAngle, out float pConf); - + [RuntimeDllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixDeskewGeneral")] + IntPtr pixDeskewGeneral(HandleRef pix, int redSweep, float sweepRange, float sweepDelta, int redSearch, int thresh, out float pAngle, out float pConf); + // rotation - - [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixRotate")] - public static extern IntPtr pixRotate(HandleRef pixs, float angle, RotationMethod type, RotationFill fillColor, int width, int heigh); - - [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixRotateOrth")] - public static extern IntPtr pixRotateOrth(HandleRef pixs, int quads); - + + [RuntimeDllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixRotate")] + IntPtr pixRotate(HandleRef pixs, float angle, RotationMethod type, RotationFill fillColor, int width, int heigh); + + [RuntimeDllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixRotateOrth")] + IntPtr pixRotateOrth(HandleRef pixs, int quads); + // Binarization - src/binarize.c - [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixOtsuAdaptiveThreshold")] - public static extern int pixOtsuAdaptiveThreshold(HandleRef pix, int sx, int sy, int smoothx, int smoothy, float scorefract, out IntPtr ppixth, out IntPtr ppixd); + [RuntimeDllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixOtsuAdaptiveThreshold")] + int pixOtsuAdaptiveThreshold(HandleRef pix, int sx, int sy, int smoothx, int smoothy, float scorefract, out IntPtr ppixth, out IntPtr ppixd); #endregion @@ -126,8 +120,8 @@ static LeptonicaApi() /// /// The depth of the pix in bpp, can be 2, 4, or 8 /// The pointer to the color map, or null on error. - [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixcmapCreate")] - public static extern IntPtr pixcmapCreate(int depth); + [RuntimeDllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixcmapCreate")] + IntPtr pixcmapCreate(int depth); /// /// Creates a new colormap of the specified with random colors where the first color can optionally be set to black, and the last optionally set to white. @@ -136,33 +130,33 @@ static LeptonicaApi() /// If set to 1 the first color will be black. /// If set to 1 the last color will be white. /// The pointer to the color map, or null on error. - [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixcmapCreateRandom")] - public static extern IntPtr pixcmapCreateRandom(int depth, int hasBlack, int hasWhite); - + [RuntimeDllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixcmapCreateRandom")] + IntPtr pixcmapCreateRandom(int depth, int hasBlack, int hasWhite); + /// /// Creates a new colormap of the specified with equally spaced gray color values. /// /// The depth of the pix in bpp, can be 2, 4, or 8 /// The number of levels (must be between 2 and 2^ /// The pointer to the colormap, or null on error. - [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixcmapCreateLinear")] - public static extern IntPtr pixcmapCreateLinear(int depth, int levels); + [RuntimeDllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixcmapCreateLinear")] + IntPtr pixcmapCreateLinear(int depth, int levels); /// /// Performs a deep copy of the color map. /// /// The pointer to the colormap instance. /// The pointer to the colormap, or null on error. - [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixcmapCopy")] - public static extern IntPtr pixcmapCopy(HandleRef cmaps); + [RuntimeDllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixcmapCopy")] + IntPtr pixcmapCopy(HandleRef cmaps); /// /// Destorys and cleans up any memory used by the color map. /// /// The pointer to the colormap instance, set to null on success. - [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixcmapDestroy")] - public static extern void pixcmapDestroy(ref IntPtr cmap); - + [RuntimeDllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixcmapDestroy")] + void pixcmapDestroy(ref IntPtr cmap); + // colormap metadata (depth, count, etc) /// @@ -170,21 +164,21 @@ static LeptonicaApi() /// /// The pointer to the colormap instance. /// Returns the number of color entries in the color map, or 0 on error. - [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixcmapGetCount")] - public static extern int pixcmapGetCount(HandleRef cmap); + [RuntimeDllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixcmapGetCount")] + int pixcmapGetCount(HandleRef cmap); /// /// Gets the number of free color entries in the color map. /// /// The pointer to the colormap instance. /// Returns the number of free color entries in the color map, or 0 on error. - [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixcmapGetFreeCount")] - public static extern int pixcmapGetFreeCount(HandleRef cmap); - + [RuntimeDllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixcmapGetFreeCount")] + int pixcmapGetFreeCount(HandleRef cmap); + /// Returns color maps depth, or 0 on error. - [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixcmapGetDepth")] - public static extern int pixcmapGetDepth(HandleRef cmap); + [RuntimeDllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixcmapGetDepth")] + int pixcmapGetDepth(HandleRef cmap); /// /// Gets the minimum pix depth required to support the color map. @@ -192,9 +186,9 @@ static LeptonicaApi() /// The pointer to the colormap instance. /// Returns the minimum depth to support the colormap /// Returns 0 if OK, 1 on error. - [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixcmapGetMinDepth")] - public static extern int pixcmapGetMinDepth(HandleRef cmap, out int minDepth); - + [RuntimeDllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixcmapGetMinDepth")] + int pixcmapGetMinDepth(HandleRef cmap, out int minDepth); + // colormap - color addition\clearing /// @@ -202,15 +196,15 @@ static LeptonicaApi() /// /// The pointer to the colormap instance. /// Returns 0 if OK, 1 on error. - [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixcmapClear")] - public static extern int pixcmapClear(HandleRef cmap); + [RuntimeDllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixcmapClear")] + int pixcmapClear(HandleRef cmap); /// /// Adds the color to the pix color map if their is room. /// /// Returns 0 if OK, 1 on error. - [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixcmapAddColor")] - public static extern int pixcmapAddColor(HandleRef cmap, int redValue, int greenValue, int blueValue); + [RuntimeDllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixcmapAddColor")] + int pixcmapAddColor(HandleRef cmap, int redValue, int greenValue, int blueValue); /// /// Adds the specified color if it doesn't already exist, returning the colors index in the data array. @@ -221,8 +215,8 @@ static LeptonicaApi() /// The blue value /// The index of the new color if it was added, or the existing color if it already existed. /// Returns 0 for success, 1 for error, 2 for not enough space. - [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixcmapAddNewColor")] - public static extern int pixcmapAddNewColor(HandleRef cmap, int redValue, int greenValue, int blueValue, out int colorIndex); + [RuntimeDllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixcmapAddNewColor")] + int pixcmapAddNewColor(HandleRef cmap, int redValue, int greenValue, int blueValue, out int colorIndex); /// /// Adds the specified color if it doesn't already exist, returning the color's index in the data array. @@ -236,8 +230,8 @@ static LeptonicaApi() /// The blue value /// The index of the new color if it was added, or the existing color if it already existed. /// Returns 0 for success, 1 for error, 2 for not enough space. - [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixcmapAddNearestColor")] - public static extern int pixcmapAddNearestColor(HandleRef cmap, int redValue, int greenValue, int blueValue, out int colorIndex); + [RuntimeDllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixcmapAddNearestColor")] + int pixcmapAddNearestColor(HandleRef cmap, int redValue, int greenValue, int blueValue, out int colorIndex); /// /// Checks if the color already exists or if their is enough room to add it. @@ -248,8 +242,8 @@ static LeptonicaApi() /// The blue value /// Returns 1 if usable; 0 if not. /// Returns 0 if OK, 1 on error. - [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixcmapUsableColor")] - public static extern int pixcmapUsableColor(HandleRef cmap, int redValue, int greenValue, int blueValue, out int usable); + [RuntimeDllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixcmapUsableColor")] + int pixcmapUsableColor(HandleRef cmap, int redValue, int greenValue, int blueValue, out int usable); /// /// Adds a color (black\white) if not already there returning it's index through . @@ -258,8 +252,8 @@ static LeptonicaApi() /// The color to add (0 for black; 1 for white) /// The index of the color. /// Returns 0 if OK; 1 on error. - [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixcmapAddBlackOrWhite")] - public static extern int pixcmapAddBlackOrWhite(HandleRef cmap, int color, out int index); + [RuntimeDllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixcmapAddBlackOrWhite")] + int pixcmapAddBlackOrWhite(HandleRef cmap, int color, out int index); /// /// Sets the darkest color in the colormap to black, if is 1. @@ -269,8 +263,8 @@ static LeptonicaApi() /// 0 for no operation; 1 to set darket color to black /// 0 for no operation; 1 to set lightest color to white /// Returns 0 if OK; 1 on error. - [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixcmapSetBlackAndWhite")] - public static extern int pixcmapSetBlackAndWhite(HandleRef cmap, int setBlack, int setWhite); + [RuntimeDllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixcmapSetBlackAndWhite")] + int pixcmapSetBlackAndWhite(HandleRef cmap, int setBlack, int setWhite); // color access - color entry access @@ -283,8 +277,8 @@ static LeptonicaApi() /// The color entry's blue value. /// The color entry's green value. /// Returns 0 if OK; 1 if not accessable (caller should check). - [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixcmapGetColor")] - public static extern int pixcmapGetColor(HandleRef cmap, int index, out int redValue, out int blueValue, out int greenValue); + [RuntimeDllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixcmapGetColor")] + int pixcmapGetColor(HandleRef cmap, int index, out int redValue, out int blueValue, out int greenValue); /// /// Gets the color at the specified index. @@ -296,8 +290,8 @@ static LeptonicaApi() /// The index of the color entry. /// The color entry as 32 bit value /// Returns 0 if OK; 1 if not accessable (caller should check). - [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixcmapGetColor32")] - public static extern int pixcmapGetColor32(HandleRef cmap, int index, out int color); + [RuntimeDllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixcmapGetColor32")] + int pixcmapGetColor32(HandleRef cmap, int index, out int color); /// /// Sets a previously allocated color entry. @@ -308,45 +302,45 @@ static LeptonicaApi() /// /// /// Returns 0 if OK; 1 if not accessable (caller should check). - [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixcmapResetColor")] - public static extern int pixcmapResetColor(HandleRef cmap, int index, int redValue, int blueValue, int greenValue); + [RuntimeDllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixcmapResetColor")] + int pixcmapResetColor(HandleRef cmap, int index, int redValue, int blueValue, int greenValue); /// /// Gets the index of the color entry with the specified color, return 0 if found; 1 if not. /// - [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixcmapGetIndex")] - public static extern int pixcmapGetIndex(HandleRef cmap, int redValue, int blueValue, int greenValue, out int index); + [RuntimeDllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixcmapGetIndex")] + int pixcmapGetIndex(HandleRef cmap, int redValue, int blueValue, int greenValue, out int index); /// /// Returns 0 if the color exists in the color map; otherwise 1. /// /// Returns 0 if OK; 1 on error. - [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixcmapHasColor")] - public static extern int pixcmapHasColor(HandleRef cmap, int color); + [RuntimeDllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixcmapHasColor")] + int pixcmapHasColor(HandleRef cmap, int color); /// /// Returns the number of unique grey colors including black and white. /// /// Returns 0 if OK; 1 on error. - [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixcmapCountGrayColors")] - public static extern int pixcmapCountGrayColors(HandleRef cmap, out int ngray); - + [RuntimeDllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixcmapCountGrayColors")] + int pixcmapCountGrayColors(HandleRef cmap, out int ngray); + /// /// Finds the index of the color entry with the rank intensity. /// /// Returns 0 if OK; 1 on error. - [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixcmapCountGrayColors")] - public static extern int pixcmapGetRankIntensity(HandleRef cmap, float rankVal, out int index); + [RuntimeDllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixcmapCountGrayColors")] + int pixcmapGetRankIntensity(HandleRef cmap, float rankVal, out int index); /// /// Finds the index of the color entry closest to the specified color. /// /// Returns 0 if OK; 1 on error. - [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixcmapGetNearestIndex")] - public static extern int pixcmapGetNearestIndex(HandleRef cmap, int rVal, int bVal, int gVal, out int index); + [RuntimeDllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixcmapGetNearestIndex")] + int pixcmapGetNearestIndex(HandleRef cmap, int rVal, int bVal, int gVal, out int index); /// /// Finds the index of the color entry closest to the specified color. @@ -355,54 +349,77 @@ static LeptonicaApi() /// Should only be used on gray colormaps. /// /// Returns 0 if OK; 1 on error. - [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixcmapGetNearestGrayIndex")] - public static extern int pixcmapGetNearestGrayIndex(HandleRef cmap, int val, out int index); + [RuntimeDllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixcmapGetNearestGrayIndex")] + int pixcmapGetNearestGrayIndex(HandleRef cmap, int val, out int index); - [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixcmapGetComponentRange")] - public static extern int pixcmapGetComponentRange(HandleRef cmap, int component, out int minVal, out int maxVal); + [RuntimeDllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixcmapGetComponentRange")] + int pixcmapGetComponentRange(HandleRef cmap, int component, out int minVal, out int maxVal); - [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixcmapGetExtremeValue")] - public static extern int pixcmapGetExtremeValue(HandleRef cmap, int type, out int rVal, out int gVal, out int bVal); + [RuntimeDllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixcmapGetExtremeValue")] + int pixcmapGetExtremeValue(HandleRef cmap, int type, out int rVal, out int gVal, out int bVal); // color map conversion - [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixcmapGrayToColor")] - public static extern IntPtr pixcmapGrayToColor(int color); + [RuntimeDllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixcmapGrayToColor")] + IntPtr pixcmapGrayToColor(int color); - [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixcmapColorToGray")] - public static extern IntPtr pixcmapColorToGray(HandleRef cmaps, float redWeight, float greenWeight, float blueWeight); + [RuntimeDllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixcmapColorToGray")] + IntPtr pixcmapColorToGray(HandleRef cmaps, float redWeight, float greenWeight, float blueWeight); // colormap serialization - [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixcmapColorToGray")] - public static extern int pixcmapToArrays(HandleRef cmap, out IntPtr redMap, out IntPtr blueMap, out IntPtr greenMap); + [RuntimeDllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixcmapColorToGray")] + int pixcmapToArrays(HandleRef cmap, out IntPtr redMap, out IntPtr blueMap, out IntPtr greenMap); + + [RuntimeDllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixcmapToRGBTable")] + int pixcmapToRGBTable(HandleRef cmap, out IntPtr colorTable, out int colorCount); - [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixcmapToRGBTable")] - public static extern int pixcmapToRGBTable(HandleRef cmap, out IntPtr colorTable, out int colorCount); + [RuntimeDllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixcmapSerializeToMemory")] + int pixcmapSerializeToMemory(HandleRef cmap, out int components, out int colorCount, out IntPtr colorData, out int colorDataLength); - [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixcmapSerializeToMemory")] - public static extern int pixcmapSerializeToMemory(HandleRef cmap, out int components, out int colorCount, out IntPtr colorData, out int colorDataLength); + [RuntimeDllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixcmapDeserializeFromMemory")] + IntPtr pixcmapDeserializeFromMemory(HandleRef colorData, int colorCount, int colorDataLength); - [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixcmapSerializeToMemory")] - public static extern IntPtr pixcmapSerializeToMemory(HandleRef colorData, int colorCount, int colorDataLength); - // colormap transformations - [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixcmapGammaTRC")] - public static extern int pixcmapGammaTRC(HandleRef cmap, float gamma, int minVal, int maxVal); + [RuntimeDllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixcmapGammaTRC")] + int pixcmapGammaTRC(HandleRef cmap, float gamma, int minVal, int maxVal); - [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixcmapContrastTRC")] - public static extern int pixcmapContrastTRC(HandleRef cmap, float factor); - - [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixcmapShiftIntensity")] - public static extern int pixcmapShiftIntensity(HandleRef cmap, float fraction); + [RuntimeDllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixcmapContrastTRC")] + int pixcmapContrastTRC(HandleRef cmap, float factor); + + [RuntimeDllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixcmapShiftIntensity")] + int pixcmapShiftIntensity(HandleRef cmap, float fraction); #endregion } + + public unsafe static class LeptonicaApi + { + private static ILeptonicaApiSignatures native; + + public static ILeptonicaApiSignatures Native + { + get + { + if (native == null) + Initialize(); + return native; + } + } + + public static void Initialize() + { + if (native == null) + { + native = InteropRuntimeImplementer.CreateInstance(); + } + } + } } diff --git a/src/Tesseract/Interop/WindowsLibraryLoader.cs b/src/Tesseract/Interop/WindowsLibraryLoader.cs deleted file mode 100644 index d66e763a..00000000 --- a/src/Tesseract/Interop/WindowsLibraryLoader.cs +++ /dev/null @@ -1,264 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Globalization; -using System.IO; -using System.Reflection; -using System.Runtime.InteropServices; -using System.Text; -using System.Web; - -namespace Tesseract.Interop -{ - /// - /// Handles loading embedded dlls into memory, based on http://stackoverflow.com/questions/666799/embedding-unmanaged-dll-into-a-managed-c-sharp-dll. - /// - public class WindowsLibraryLoader - { - private class ProcessArchitectureInfo - { - public ProcessArchitectureInfo() - { - Warnings = new List(); - } - - public string Architecture { get; set; } - public List Warnings { get; set; } - - public bool HasWarnings - { - get { return Warnings.Count > 0; } - } - - public string WarningText() - { -#if Net20 - return String.Join("\r\n", Warnings.ToArray()); -#else - return String.Join("\r\n", Warnings); -#endif - } - } - - #region Singleton pattern - - private static readonly WindowsLibraryLoader instance = new WindowsLibraryLoader(); - - public static WindowsLibraryLoader Instance { get { return instance; } } - - - #endregion - - private readonly object syncLock = new object(); - - /// - /// The default base directory name to copy the assemblies too. - /// - private const string DefaultTempDirectory = "TesseractOcr"; - private const string PROCESSOR_ARCHITECTURE = "PROCESSOR_ARCHITECTURE"; - private const string DllFileExtension = ".dll"; - -#if Net40 || Net45 - private HashSet loadedAssemblies = new HashSet(); -#else - private List loadedAssemblies = new List(); -#endif - // Map processor - private readonly Dictionary processorArchitecturePlatforms; - - // Used as a sanity check for the returned processor architecture to double check the returned value. - private readonly Dictionary processorArchitectureAddressWidthPlatforms; - private WindowsLibraryLoader() - { - processorArchitecturePlatforms = new Dictionary(StringComparer.OrdinalIgnoreCase); - processorArchitecturePlatforms.Add("x86", "x86"); - processorArchitecturePlatforms.Add("AMD64", "x64"); - processorArchitecturePlatforms.Add("IA64", "Itanium"); - processorArchitecturePlatforms.Add("ARM", "WinCE"); - - processorArchitectureAddressWidthPlatforms = new Dictionary(StringComparer.OrdinalIgnoreCase); - processorArchitectureAddressWidthPlatforms.Add("x86", 4); - processorArchitectureAddressWidthPlatforms.Add("AMD64", 8); - processorArchitectureAddressWidthPlatforms.Add("IA64", 8); - processorArchitectureAddressWidthPlatforms.Add("ARM", 4); - } - - public bool IsLibraryLoaded(string dllName) - { - lock (syncLock) { - return loadedAssemblies.Contains(dllName); - } - } - - public bool IsCurrentPlatformSupported() - { - return Environment.OSVersion.Platform == PlatformID.Win32NT || - Environment.OSVersion.Platform == PlatformID.Win32Windows; - } - - public void LoadLibrary(string dllName) - { - if (IsCurrentPlatformSupported()) { - try { - lock (syncLock) { - if (!loadedAssemblies.Contains(dllName)) { - var processArch = GetProcessArchitecture(); - IntPtr dllHandle; - string baseDirectory; - - // Try loading from executing assembly domain - var executingAssembly = Assembly.GetExecutingAssembly(); - baseDirectory = Path.GetDirectoryName(executingAssembly.Location); - dllHandle = LoadLibraryInternal(dllName, baseDirectory, processArch); - if (dllHandle != IntPtr.Zero) return; - - // Fallback to current app domain - baseDirectory = Path.GetFullPath(AppDomain.CurrentDomain.BaseDirectory); - dllHandle = LoadLibraryInternal(dllName, baseDirectory, processArch); - if (dllHandle != IntPtr.Zero) return; - - // Finally try the working directory - baseDirectory = Path.GetFullPath(Environment.CurrentDirectory); - dllHandle = LoadLibraryInternal(dllName, baseDirectory, processArch); - if (dllHandle != IntPtr.Zero) return; - - // ASP.NET hack, requires an active context - #if !ClientProfile - if(HttpContext.Current != null) { - var server = HttpContext.Current.Server; - baseDirectory = Path.GetFullPath(server.MapPath("~/bin")); - dllHandle = LoadLibraryInternal(dllName, baseDirectory, processArch); - if (dllHandle != IntPtr.Zero) return; - } - #endif - StringBuilder errorMessage = new StringBuilder(); - errorMessage.AppendFormat("Failed to find dll \"{0}\", for processor architecture {1}.", dllName, processArch.Architecture); - if (processArch.HasWarnings) { - // include process detection warnings - errorMessage.AppendFormat("\r\nWarnings: \r\n{0}", processArch.WarningText()); - } - throw new LoadLibraryException(errorMessage.ToString()); - } - } - } catch (LoadLibraryException e) { - Trace.TraceError(e.Message); - } - } - } - - /// - /// Get's the current process architecture while keeping track of any assumptions or possible errors. - /// - /// - private ProcessArchitectureInfo GetProcessArchitecture() - { - // BUGBUG: Will this always be reliable? - string processArchitecture = Environment.GetEnvironmentVariable(PROCESSOR_ARCHITECTURE); - var processInfo = new ProcessArchitectureInfo(); - if (!String.IsNullOrEmpty(processArchitecture)) { - // Sanity check - processInfo.Architecture = processArchitecture; - } else { - processInfo.Architecture = "x86"; - processInfo.Warnings.Add("Failed to detect processor architecture, falling back to x86."); - } - - - var addressWidth = processorArchitectureAddressWidthPlatforms[processInfo.Architecture]; - if (addressWidth != IntPtr.Size) { - if(String.Equals(processInfo.Architecture, "AMD64", StringComparison.OrdinalIgnoreCase) && IntPtr.Size == 4) { - // fall back to x86 if detected x64 but has an address width of 32 bits. - processInfo.Architecture = "x86"; - processInfo.Warnings.Add(String.Format("Expected the detected processing architecture of {0} to have an address width of {1} Bytes but was {2} Bytes, falling back to x86.", processInfo.Architecture, addressWidth, IntPtr.Size)); - } else { - // no fallback possible - processInfo.Warnings.Add(String.Format("Expected the detected processing architecture of {0} to have an address width of {1} Bytes but was {2} Bytes.", processInfo.Architecture, addressWidth, IntPtr.Size)); - - } - } - - return processInfo; - } - - private IntPtr LoadLibraryInternal(string dllName, string baseDirectory, ProcessArchitectureInfo processArchInfo) - { - IntPtr libraryHandle = IntPtr.Zero; - var platformName = GetPlatformName(processArchInfo.Architecture); - var expectedDllDirectory = Path.Combine(baseDirectory, platformName); - var fileName = FixUpDllFileName(Path.Combine(expectedDllDirectory, dllName)); - - if (File.Exists(fileName)) { - // Attempt to load dll - try { - // Show where we're trying to load the file from - Trace.TraceInformation(String.Format(CultureInfo.CurrentCulture, - "Trying to load native library \"{0}\"...", - fileName)); - - libraryHandle = Win32LoadLibrary(fileName); - if (libraryHandle != IntPtr.Zero) { - // library has been loaded - Trace.TraceInformation(String.Format(CultureInfo.CurrentCulture, - "Successfully loaded native library \"{0}\".", - fileName)); - loadedAssemblies.Add(dllName); - } else { - Trace.TraceError(String.Format("Failed to load native library \"{0}\".\r\nCheck windows event log.", fileName)); - } - } catch (Exception e) { - var lastError = Marshal.GetLastWin32Error(); - Trace.TraceError(String.Format("Failed to load native library \"{0}\".\r\nLast Error:{1}\r\nCheck inner exception and\\or windows event log.\r\nInner Exception: {2}", fileName, lastError, e.ToString())); - } - } else { - Trace.TraceWarning(String.Format(CultureInfo.CurrentCulture, - "The native library \"{0}\" does not exist.", - fileName)); - } - - return libraryHandle; - } - - /// - /// Determines if the dynamic link library file name requires a suffix - /// and adds it if necessary. - /// - private string FixUpDllFileName(string fileName) - { - if (!String.IsNullOrEmpty(fileName)) { - PlatformID platformId = Environment.OSVersion.Platform; - - if ((platformId == PlatformID.Win32S) || - (platformId == PlatformID.Win32Windows) || - (platformId == PlatformID.Win32NT) || - (platformId == PlatformID.WinCE)) { - if (!fileName.EndsWith(DllFileExtension, - StringComparison.OrdinalIgnoreCase)) { - return fileName + DllFileExtension; - } - } - } - - return fileName; - } - - /// - /// Given the processor architecture, returns the name of the platform. - /// - private string GetPlatformName(string processorArchitecture) - { - if (String.IsNullOrEmpty(processorArchitecture)) - return null; - - string platformName; - if (processorArchitecturePlatforms.TryGetValue(processorArchitecture, out platformName)) { - return platformName; - } - - return null; - } - - [DllImport("kernel32", EntryPoint = "LoadLibrary", CallingConvention = CallingConvention.Winapi, - SetLastError = true, CharSet = CharSet.Auto, BestFitMapping = false, ThrowOnUnmappableChar = true)] - private static extern IntPtr Win32LoadLibrary(string dllPath); - } -} diff --git a/src/Tesseract/InteropDotNet/ILibraryLoaderLogic.cs b/src/Tesseract/InteropDotNet/ILibraryLoaderLogic.cs new file mode 100644 index 00000000..05dff1ca --- /dev/null +++ b/src/Tesseract/InteropDotNet/ILibraryLoaderLogic.cs @@ -0,0 +1,15 @@ +// Copyright (c) 2014 Andrey Akinshin +// Project URL: https://github.com/AndreyAkinshin/InteropDotNet +// Distributed under the MIT License: http://opensource.org/licenses/MIT +using System; + +namespace InteropDotNet +{ + internal interface ILibraryLoaderLogic + { + IntPtr LoadLibrary(string fileName); + bool FreeLibrary(IntPtr libraryHandle); + IntPtr GetProcAddress(IntPtr libraryHandle, string functionName); + string FixUpLibraryName(string fileName); + } +} \ No newline at end of file diff --git a/src/Tesseract/InteropDotNet/InteropRuntimeImplementer.cs b/src/Tesseract/InteropDotNet/InteropRuntimeImplementer.cs new file mode 100644 index 00000000..959c3768 --- /dev/null +++ b/src/Tesseract/InteropDotNet/InteropRuntimeImplementer.cs @@ -0,0 +1,390 @@ +// Copyright (c) 2014 Andrey Akinshin +// Project URL: https://github.com/AndreyAkinshin/InteropDotNet +// Distributed under the MIT License: http://opensource.org/licenses/MIT +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Reflection.Emit; +using System.Runtime.InteropServices; +using System.Threading; + +namespace InteropDotNet +{ + public static class InteropRuntimeImplementer + { + public static T CreateInstance() where T : class + { + var interfaceType = typeof(T); + if (!typeof(T).IsInterface) + throw new Exception(string.Format("The type {0} should be an interface", interfaceType.Name)); + if (!interfaceType.IsPublic) + throw new Exception(string.Format("The interface {0} should be public", interfaceType.Name)); + + var assemblyName = GetAssemblyName(interfaceType); + var assemblyBuilder = Thread.GetDomain().DefineDynamicAssembly(new AssemblyName(assemblyName), AssemblyBuilderAccess.Run); + var moduleBuilder = assemblyBuilder.DefineDynamicModule(assemblyName); + + var typeName = GetImplementationTypeName(assemblyName, interfaceType); + var typeBuilder = moduleBuilder.DefineType(typeName, TypeAttributes.Public, + typeof(object), new[] { interfaceType }); + var methods = BuildMethods(interfaceType); + + ImplementDelegates(assemblyName, moduleBuilder, methods); + ImplementFields(typeBuilder, methods); + ImplementMethods(typeBuilder, methods); + ImplementConstructor(typeBuilder, methods); + + var implementationType = typeBuilder.CreateType(); + return (T)Activator.CreateInstance(implementationType, LibraryLoader.Instance); + } + + #region Main steps + + private static MethodItem[] BuildMethods(Type interfaceType) + { + var methodInfoArray = interfaceType.GetMethods(); + var methods = new MethodItem[methodInfoArray.Length]; + for (int i = 0; i < methodInfoArray.Length; i++) + { + methods[i] = new MethodItem { Info = methodInfoArray[i] }; + var attribute = GetRuntimeDllImportAttribute(methodInfoArray[i]); + if (attribute == null) + throw new Exception(string.Format("Method '{0}' of interface '{1}' should be marked with the RuntimeDllImport attribute", + methodInfoArray[i].Name, interfaceType.Name)); + methods[i].DllImportAttribute = attribute; + } + return methods; + } + + private static void ImplementDelegates(string assemblyName, ModuleBuilder moduleBuilder, IEnumerable methods) + { + foreach (var method in methods) + method.DelegateType = ImplementMethodDelegate(assemblyName, moduleBuilder, method); + } + + private static Type ImplementMethodDelegate(string assemblyName, ModuleBuilder moduleBuilder, MethodItem method) + { + // Consts + const MethodAttributes methodAttributes = MethodAttributes.Public | MethodAttributes.HideBySig | + MethodAttributes.NewSlot | MethodAttributes.Virtual; + + // Initial + var delegateName = GetDelegateName(assemblyName, method.Info); + var delegateBuilder = moduleBuilder.DefineType(delegateName, + TypeAttributes.Public | TypeAttributes.AutoClass | TypeAttributes.Sealed, typeof(MulticastDelegate)); + + // UnmanagedFunctionPointer + var importAttribute = method.DllImportAttribute; + var attributeCtor = typeof(UnmanagedFunctionPointerAttribute).GetConstructor(new[] { typeof(CallingConvention) }); + if (attributeCtor == null) + throw new Exception("There is no the target constructor of the UnmanagedFunctionPointerAttribute"); + var attributeBuilder = new CustomAttributeBuilder(attributeCtor, new object[] { importAttribute.CallingConvention }, + new[] + { + typeof(UnmanagedFunctionPointerAttribute).GetField("CharSet"), + typeof(UnmanagedFunctionPointerAttribute).GetField("BestFitMapping"), + typeof(UnmanagedFunctionPointerAttribute).GetField("ThrowOnUnmappableChar"), + typeof(UnmanagedFunctionPointerAttribute).GetField("SetLastError") + }, + new object[] + { + importAttribute.CharSet, + importAttribute.BestFitMapping, + importAttribute.ThrowOnUnmappableChar, + importAttribute.SetLastError + }); + delegateBuilder.SetCustomAttribute(attributeBuilder); + + + // ctor + var ctorBuilder = delegateBuilder.DefineConstructor(MethodAttributes.Public | MethodAttributes.HideBySig | + MethodAttributes.SpecialName | MethodAttributes.RTSpecialName, CallingConventions.Standard, + new[] { typeof(object), typeof(IntPtr) }); + ctorBuilder.SetImplementationFlags(MethodImplAttributes.CodeTypeMask); + ctorBuilder.DefineParameter(1, ParameterAttributes.HasDefault, "object"); + ctorBuilder.DefineParameter(2, ParameterAttributes.HasDefault, "method"); + + // Invoke + var parameters = GetParameterInfoArray(method.Info); + var methodBuilder = DefineMethod(delegateBuilder, "Invoke", methodAttributes, method.ReturnType, parameters); + methodBuilder.SetImplementationFlags(MethodImplAttributes.CodeTypeMask); + + // BeginInvoke + parameters = GetParameterInfoArray(method.Info, InfoArrayMode.BeginInvoke); + methodBuilder = DefineMethod(delegateBuilder, "BeginInvoke", methodAttributes, typeof(IAsyncResult), parameters); + methodBuilder.SetImplementationFlags(MethodImplAttributes.CodeTypeMask); + + // EndInvoke + parameters = GetParameterInfoArray(method.Info, InfoArrayMode.EndInvoke); + methodBuilder = DefineMethod(delegateBuilder, "EndInvoke", methodAttributes, method.ReturnType, parameters); + methodBuilder.SetImplementationFlags(MethodImplAttributes.CodeTypeMask); + + // Create type + return delegateBuilder.CreateType(); + } + + private static void ImplementFields(TypeBuilder typeBuilder, IEnumerable methods) + { + foreach (var method in methods) + { + var fieldName = method.Info.Name + "Field"; + var fieldBuilder = typeBuilder.DefineField(fieldName, method.DelegateType, FieldAttributes.Private); + method.FieldInfo = fieldBuilder; + } + } + + private static void ImplementMethods(TypeBuilder typeBuilder, IEnumerable methods) + { + foreach (var method in methods) + { + var infoArray = GetParameterInfoArray(method.Info); + var methodBuilder = DefineMethod(typeBuilder, method.Name, + MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.NewSlot | + MethodAttributes.Final | MethodAttributes.Virtual, + method.ReturnType, infoArray); + + var ilGen = methodBuilder.GetILGenerator(); + + // Load this + ilGen.Emit(OpCodes.Ldarg_0); + // Load field + ilGen.Emit(OpCodes.Ldfld, method.FieldInfo); + // Load arguments + for (int i = 0; i < infoArray.Length; i++) + LdArg(ilGen, i + 1); + // Invoke delegate + ilGen.Emit(OpCodes.Callvirt, method.DelegateType.GetMethod("Invoke")); + // Return value + ilGen.Emit(OpCodes.Ret); + + // Associate the method body with the interface method + typeBuilder.DefineMethodOverride(methodBuilder, method.Info); + } + } + + private static void ImplementConstructor(TypeBuilder typeBuilder, MethodItem[] methods) + { + // Preparing + var ctorBuilder = typeBuilder.DefineConstructor(MethodAttributes.Public, + CallingConventions.Standard, new[] { typeof(LibraryLoader) }); + ctorBuilder.DefineParameter(1, ParameterAttributes.HasDefault, "loader"); + if (typeBuilder.BaseType == null) + throw new Exception("There is no a BaseType of typeBuilder"); + var baseCtor = typeBuilder.BaseType.GetConstructor(new Type[0]); + if (baseCtor == null) + throw new Exception("There is no a default constructor of BaseType of typeBuilder"); + + // Build list of library names + var libraries = new List(); + foreach (var method in methods) + { + var libraryName = method.DllImportAttribute.LibraryFileName; + if (!libraries.Contains(libraryName)) + libraries.Add(libraryName); + } + + // Create ILGenerator + var ilGen = ctorBuilder.GetILGenerator(); + // Declare locals for library handles + for (int i = 0; i < libraries.Count; i++) + ilGen.DeclareLocal(typeof(IntPtr)); + // Declare locals for a method handle + ilGen.DeclareLocal(typeof(IntPtr)); + // Load this + ilGen.Emit(OpCodes.Ldarg_0); + // Run objector..ctor() + ilGen.Emit(OpCodes.Call, baseCtor); + for (int i = 0; i < libraries.Count; i++) + { + // Preparing + var library = libraries[i]; + // Load LibraryLoader + ilGen.Emit(OpCodes.Ldarg_1); + // Load libraryName + ilGen.Emit(OpCodes.Ldstr, library); + // Load null + ilGen.Emit(OpCodes.Ldnull); + // Call LibraryLoader.LoadLibrary(libraryName, null) + ilGen.Emit(OpCodes.Callvirt, typeof(LibraryLoader).GetMethod("LoadLibrary")); + // Store libraryHandle in locals[i] + ilGen.Emit(OpCodes.Stloc, i); + } + foreach (var method in methods) + { + // Preparing + var libraryIndex = libraries.IndexOf(method.DllImportAttribute.LibraryFileName); + var methodName = method.DllImportAttribute.EntryPoint ?? method.Info.Name; + // Load Library Loader + ilGen.Emit(OpCodes.Ldarg_1); + // Load libraryHandle (locals[libraryIndex]) + ilGen.Emit(OpCodes.Ldloc, libraryIndex); + // Load methodName + ilGen.Emit(OpCodes.Ldstr, methodName); + // Call LibraryLoader.GetProcAddress(libraryHandle, methodName) + ilGen.Emit(OpCodes.Callvirt, typeof(LibraryLoader).GetMethod("GetProcAddress")); + // Store methodHandle in locals + ilGen.Emit(OpCodes.Stloc, libraries.Count); + // Load this + ilGen.Emit(OpCodes.Ldarg_0); + // Load methodHandle from locals + ilGen.Emit(OpCodes.Ldloc_1); + // Load methodDelegate token + ilGen.Emit(OpCodes.Ldtoken, method.DelegateType); + // Call typeof(methodDelegate) + ilGen.Emit(OpCodes.Call, typeof(Type).GetMethod("GetTypeFromHandle")); + // Call Marshal.GetDelegateForFunctionPointer(methodHandle, typeof(methodDelegate)) + ilGen.Emit(OpCodes.Call, typeof(Marshal).GetMethod("GetDelegateForFunctionPointer", + new[] { typeof(IntPtr), typeof(Type) })); + // Cast result to typeof(methodDelegate) + ilGen.Emit(OpCodes.Castclass, method.DelegateType); + // Store result in methodField + ilGen.Emit(OpCodes.Stfld, method.FieldInfo); + } + // Return + ilGen.Emit(OpCodes.Ret); + } + + #endregion + + #region Reflection and emit helpers + + private static RuntimeDllImportAttribute GetRuntimeDllImportAttribute(MethodInfo methodInfo) + { + var attributes = methodInfo.GetCustomAttributes(typeof(RuntimeDllImportAttribute), true); + if (attributes.Length == 0) + throw new Exception(string.Format("RuntimeDllImportAttribute for method '{0}' not found", methodInfo.Name)); + return (RuntimeDllImportAttribute)attributes[0]; + } + + private static void LdArg(ILGenerator ilGen, int index) + { + switch (index) + { + case 0: + ilGen.Emit(OpCodes.Ldarg_0); + break; + case 1: + ilGen.Emit(OpCodes.Ldarg_1); + break; + case 2: + ilGen.Emit(OpCodes.Ldarg_2); + break; + case 3: + ilGen.Emit(OpCodes.Ldarg_3); + break; + default: + ilGen.Emit(OpCodes.Ldarg, index); + break; + } + } + + private static MethodBuilder DefineMethod(TypeBuilder typeBuilder, string name, + MethodAttributes attributes, Type returnType, LightParameterInfo[] infoArray) + { + var methodBuilder = typeBuilder.DefineMethod(name, attributes, returnType, GetParameterTypeArray(infoArray)); + for (int parameterIndex = 0; parameterIndex < infoArray.Length; parameterIndex++) + methodBuilder.DefineParameter(parameterIndex + 1, + infoArray[parameterIndex].Attributes, infoArray[parameterIndex].Name); + return methodBuilder; + } + + #endregion + + #region Method helpers + + private class MethodItem + { + public MethodInfo Info { get; set; } + public RuntimeDllImportAttribute DllImportAttribute { get; set; } + + public Type DelegateType { get; set; } + public FieldInfo FieldInfo { get; set; } + + public string Name { get { return Info.Name; } } + public Type ReturnType { get { return Info.ReturnType; } } + } + + private class LightParameterInfo + { + public LightParameterInfo(ParameterInfo info) + { + Type = info.ParameterType; + Name = info.Name; + Attributes = info.Attributes; + } + + public LightParameterInfo(Type type, string name) + { + Type = type; + Name = name; + Attributes = ParameterAttributes.HasDefault; + } + + public Type Type { get; private set; } + public string Name { get; private set; } + public ParameterAttributes Attributes { get; private set; } + } + + private enum InfoArrayMode + { + Invoke, BeginInvoke, EndInvoke + } + + private static LightParameterInfo[] GetParameterInfoArray(MethodInfo methodInfo, InfoArrayMode mode = InfoArrayMode.Invoke) + { + var parameters = methodInfo.GetParameters(); + var infoList = new List(); + for (int i = 0; i < parameters.Length; i++) + if (mode != InfoArrayMode.EndInvoke || parameters[i].ParameterType.IsByRef) + infoList.Add(new LightParameterInfo(parameters[i])); + if (mode == InfoArrayMode.BeginInvoke) + { + infoList.Add(new LightParameterInfo(typeof(AsyncCallback), "callback")); + infoList.Add(new LightParameterInfo(typeof(object), "object")); + } + if (mode == InfoArrayMode.EndInvoke) + infoList.Add(new LightParameterInfo(typeof(IAsyncResult), "result")); + var infoArray = new LightParameterInfo[infoList.Count]; + for (int i = 0; i < infoList.Count; i++) + infoArray[i] = infoList[i]; + return infoArray; + } + + private static Type[] GetParameterTypeArray(LightParameterInfo[] infoArray) + { + var typeArray = new Type[infoArray.Length]; + for (int i = 0; i < infoArray.Length; i++) + typeArray[i] = infoArray[i].Type; + return typeArray; + } + + #endregion + + #region Name helpers + + private static string GetAssemblyName(Type interfaceType) + { + return string.Format("InteropRuntimeImplementer.{0}Instance", GetSubstantialName(interfaceType)); + } + + private static string GetImplementationTypeName(string assemblyName, Type interfaceType) + { + return string.Format("{0}.{1}Implementation", assemblyName, GetSubstantialName(interfaceType)); + } + + private static string GetSubstantialName(Type interfaceType) + { + var name = interfaceType.Name; + if (name.StartsWith("I")) + name = name.Substring(1); + return name; + } + + private static string GetDelegateName(string assemblyName, MethodInfo methodInfo) + { + return string.Format("{0}.{1}Delegate", assemblyName, methodInfo.Name); + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Tesseract/InteropDotNet/LibraryLoader.cs b/src/Tesseract/InteropDotNet/LibraryLoader.cs new file mode 100644 index 00000000..9aa34160 --- /dev/null +++ b/src/Tesseract/InteropDotNet/LibraryLoader.cs @@ -0,0 +1,142 @@ +// Copyright (c) 2014 Andrey Akinshin +// Project URL: https://github.com/AndreyAkinshin/InteropDotNet +// Distributed under the MIT License: http://opensource.org/licenses/MIT +using System; +using System.Collections.Generic; +using System.IO; +using System.Reflection; + +namespace InteropDotNet +{ + public class LibraryLoader + { + private readonly ILibraryLoaderLogic logic; + + private LibraryLoader(ILibraryLoaderLogic logic) + { + this.logic = logic; + } + + private readonly object syncLock = new object(); + private readonly Dictionary loadedAssemblies = new Dictionary(); + + public IntPtr LoadLibrary(string fileName, string platformName = null) + { + fileName = FixUpLibraryName(fileName); + lock (syncLock) + { + if (!loadedAssemblies.ContainsKey(fileName)) + { + if (platformName == null) + platformName = SystemManager.GetPlatformName(); + LibraryLoaderTrace.TraceInformation("Current platform: " + platformName); + IntPtr dllHandle = CheckExecutingAssemblyDomain(fileName, platformName); + if (dllHandle == IntPtr.Zero) + dllHandle = CheckCurrentAppDomain(fileName, platformName); + if (dllHandle == IntPtr.Zero) + dllHandle = CheckWorkingDirecotry(fileName, platformName); + + if (dllHandle != IntPtr.Zero) + loadedAssemblies[fileName] = dllHandle; + else + LibraryLoaderTrace.TraceError("Failed to find library \"{0}\" for platform {1}.", fileName, platformName); + } + } + return loadedAssemblies[fileName]; + } + + private IntPtr CheckExecutingAssemblyDomain(string fileName, string platformName) + { + var baseDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); + return InternalLoadLibrary(baseDirectory, platformName, fileName); + } + + private IntPtr CheckCurrentAppDomain(string fileName, string platformName) + { + var baseDirectory = Path.GetFullPath(AppDomain.CurrentDomain.BaseDirectory); + return InternalLoadLibrary(baseDirectory, platformName, fileName); + } + + private IntPtr CheckWorkingDirecotry(string fileName, string platformName) + { + var baseDirectory = Path.GetFullPath(Environment.CurrentDirectory); + return InternalLoadLibrary(baseDirectory, platformName, fileName); + } + + private IntPtr InternalLoadLibrary(string baseDirectory, string platformName, string fileName) + { + var fullPath = Path.Combine(baseDirectory, Path.Combine(platformName, fileName)); + return File.Exists(fullPath) ? logic.LoadLibrary(fullPath) : IntPtr.Zero; + } + + public bool FreeLibrary(string fileName) + { + fileName = FixUpLibraryName(fileName); + lock (syncLock) + { + if (!IsLibraryLoaded(fileName)) + { + LibraryLoaderTrace.TraceWarning("Failed to free library \"{0}\" because it is not loaded", fileName); + return false; + } + if (logic.FreeLibrary(loadedAssemblies[fileName])) + { + loadedAssemblies.Remove(fileName); + return true; + } + return false; + } + } + + public IntPtr GetProcAddress(IntPtr dllHandle, string name) + { + return logic.GetProcAddress(dllHandle, name); + } + + public bool IsLibraryLoaded(string fileName) + { + fileName = FixUpLibraryName(fileName); + lock (syncLock) + return loadedAssemblies.ContainsKey(fileName); + } + + private string FixUpLibraryName(string fileName) + { + return logic.FixUpLibraryName(fileName); + + } + + #region Singleton + + private static LibraryLoader instance; + + public static LibraryLoader Instance + { + get + { + if (instance == null) + { + var operatingSystem = SystemManager.GetOperatingSystem(); + switch (operatingSystem) + { + case OperatingSystem.Windows: + LibraryLoaderTrace.TraceInformation("Current OS: Windows"); + instance = new LibraryLoader(new WindowsLibraryLoaderLogic()); + break; + case OperatingSystem.Unix: + LibraryLoaderTrace.TraceInformation("Current OS: Unix"); + instance = new LibraryLoader(new UnixLibraryLoaderLogic()); + break; + case OperatingSystem.MacOSX: + throw new Exception("Unsupported operation system"); + default: + throw new Exception("Unsupported operation system"); + } + } + return instance; + } + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Tesseract/InteropDotNet/LibraryLoaderTrace.cs b/src/Tesseract/InteropDotNet/LibraryLoaderTrace.cs new file mode 100644 index 00000000..4c92d568 --- /dev/null +++ b/src/Tesseract/InteropDotNet/LibraryLoaderTrace.cs @@ -0,0 +1,45 @@ +// Copyright (c) 2014 Andrey Akinshin +// Project URL: https://github.com/AndreyAkinshin/InteropDotNet +// Distributed under the MIT License: http://opensource.org/licenses/MIT +using System; +using System.Diagnostics; +using System.Globalization; + +namespace InteropDotNet +{ + internal static class LibraryLoaderTrace + { + private static bool printToConsole = false; + + private static void Print(string message) + { + Console.ForegroundColor = ConsoleColor.Red; + Console.WriteLine(message); + Console.ResetColor(); + } + + public static void TraceInformation(string format, params object[] args) + { + if (printToConsole) + Print(string.Format(CultureInfo.CurrentCulture, format, args)); + else + Trace.TraceInformation(string.Format(CultureInfo.CurrentCulture, format, args)); + } + + public static void TraceError(string format, params object[] args) + { + if (printToConsole) + Print(string.Format(CultureInfo.CurrentCulture, format, args)); + else + Trace.TraceError(string.Format(CultureInfo.CurrentCulture, format, args)); + } + + public static void TraceWarning(string format, params object[] args) + { + if (printToConsole) + Print(string.Format(CultureInfo.CurrentCulture, format, args)); + else + Trace.TraceWarning(string.Format(CultureInfo.CurrentCulture, format, args)); + } + } +} \ No newline at end of file diff --git a/src/Tesseract/InteropDotNet/RuntimeDllImportAttribute.cs b/src/Tesseract/InteropDotNet/RuntimeDllImportAttribute.cs new file mode 100644 index 00000000..90dff71c --- /dev/null +++ b/src/Tesseract/InteropDotNet/RuntimeDllImportAttribute.cs @@ -0,0 +1,32 @@ +// Copyright (c) 2014 Andrey Akinshin +// Project URL: https://github.com/AndreyAkinshin/InteropDotNet +// Distributed under the MIT License: http://opensource.org/licenses/MIT +using System; +using System.Runtime.InteropServices; + +namespace InteropDotNet +{ + [ComVisible(true)] + [AttributeUsage(AttributeTargets.Method, Inherited = false, AllowMultiple = false)] + public sealed class RuntimeDllImportAttribute : Attribute + { + public string EntryPoint; + + public CallingConvention CallingConvention; + + public CharSet CharSet; + + public bool SetLastError; + + public bool BestFitMapping; + + public bool ThrowOnUnmappableChar; + + public string LibraryFileName { get; private set; } + + public RuntimeDllImportAttribute(string libraryFileName) + { + LibraryFileName = libraryFileName; + } + } +} \ No newline at end of file diff --git a/src/Tesseract/InteropDotNet/SystemManager.cs b/src/Tesseract/InteropDotNet/SystemManager.cs new file mode 100644 index 00000000..24b1f042 --- /dev/null +++ b/src/Tesseract/InteropDotNet/SystemManager.cs @@ -0,0 +1,43 @@ +// Copyright (c) 2014 Andrey Akinshin +// Project URL: https://github.com/AndreyAkinshin/InteropDotNet +// Distributed under the MIT License: http://opensource.org/licenses/MIT +using System; + +namespace InteropDotNet +{ + internal static class SystemManager + { + public static string GetPlatformName() + { + return IntPtr.Size == sizeof(int) ? "x86" : "x64"; + } + + public static OperatingSystem GetOperatingSystem() + { + var pid = (int)Environment.OSVersion.Platform; + switch (pid) + { + case (int)PlatformID.Win32NT: + case (int)PlatformID.Win32S: + case (int)PlatformID.Win32Windows: + case (int)PlatformID.WinCE: + return OperatingSystem.Windows; + case (int)PlatformID.Unix: + case 128: + return OperatingSystem.Unix; + case (int)PlatformID.MacOSX: + return OperatingSystem.MacOSX; + default: + return OperatingSystem.Unknown; + } + } + } + + internal enum OperatingSystem + { + Windows, + Unix, + MacOSX, + Unknown + } +} \ No newline at end of file diff --git a/src/Tesseract/InteropDotNet/UnixLibraryLoaderLogic.cs b/src/Tesseract/InteropDotNet/UnixLibraryLoaderLogic.cs new file mode 100644 index 00000000..e1cc7974 --- /dev/null +++ b/src/Tesseract/InteropDotNet/UnixLibraryLoaderLogic.cs @@ -0,0 +1,82 @@ +// Copyright (c) 2014 Andrey Akinshin +// Project URL: https://github.com/AndreyAkinshin/InteropDotNet +// Distributed under the MIT License: http://opensource.org/licenses/MIT +using System; +using System.Runtime.InteropServices; + +namespace InteropDotNet +{ + internal class UnixLibraryLoaderLogic : ILibraryLoaderLogic + { + public IntPtr LoadLibrary(string fileName) + { + var libraryHandle = IntPtr.Zero; + + try + { + LibraryLoaderTrace.TraceInformation("Trying to load native library \"{0}\"...", fileName); + libraryHandle = UnixLoadLibrary(fileName, RTLD_NOW); + if (libraryHandle != IntPtr.Zero) + LibraryLoaderTrace.TraceInformation("Successfully loaded native library \"{0}\", handle = {1}.", fileName, libraryHandle); + else + LibraryLoaderTrace.TraceError("Failed to load native library \"{0}\".\r\nCheck windows event log.", fileName); + } + catch (Exception e) + { + var lastError = UnixGetLastError(); + LibraryLoaderTrace.TraceError("Failed to load native library \"{0}\".\r\nLast Error:{1}\r\nCheck inner exception and\\or windows event log.\r\nInner Exception: {2}", fileName, lastError, e.ToString()); + } + + return libraryHandle; + } + + public bool FreeLibrary(IntPtr libraryHandle) + { + return UnixFreeLibrary(libraryHandle) != 0; + } + + public IntPtr GetProcAddress(IntPtr libraryHandle, string functionName) + { + UnixGetLastError(); // Clearing previous errors + LibraryLoaderTrace.TraceInformation("Trying to load native function \"{0}\" from the library with handle {1}...", + functionName, libraryHandle); + var functionHandle = UnixGetProcAddress(libraryHandle, functionName); + var errorPointer = UnixGetLastError(); + if (errorPointer != IntPtr.Zero) + throw new Exception("dlsym: " + Marshal.PtrToStringAnsi(errorPointer)); + if (functionHandle != IntPtr.Zero && errorPointer == IntPtr.Zero) + LibraryLoaderTrace.TraceInformation("Successfully loaded native function \"{0}\", function handle = {1}.", + functionName, functionHandle); + else + LibraryLoaderTrace.TraceError("Failed to load native function \"{0}\", function handle = {1}, error pointer = {2}", + functionName, functionHandle, errorPointer); + return functionHandle; + } + + public string FixUpLibraryName(string fileName) + { + if (!string.IsNullOrEmpty(fileName)) + { + if (!fileName.EndsWith(".so", StringComparison.OrdinalIgnoreCase)) + fileName += ".so"; + if (!fileName.StartsWith("lib", StringComparison.OrdinalIgnoreCase)) + fileName = "lib" + fileName; + } + return fileName; + } + + const int RTLD_NOW = 2; + + [DllImport("libdl.so", EntryPoint = "dlopen")] + private static extern IntPtr UnixLoadLibrary(String fileName, int flags); + + [DllImport("libdl.so", EntryPoint = "dlclose", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] + private static extern int UnixFreeLibrary(IntPtr handle); + + [DllImport("libdl.so", EntryPoint = "dlsym", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] + private static extern IntPtr UnixGetProcAddress(IntPtr handle, String symbol); + + [DllImport("libdl.so", EntryPoint = "dlerror", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] + private static extern IntPtr UnixGetLastError(); + } +} \ No newline at end of file diff --git a/src/Tesseract/InteropDotNet/WindowsLibraryLoaderLogic.cs b/src/Tesseract/InteropDotNet/WindowsLibraryLoaderLogic.cs new file mode 100644 index 00000000..8312dddb --- /dev/null +++ b/src/Tesseract/InteropDotNet/WindowsLibraryLoaderLogic.cs @@ -0,0 +1,100 @@ +// Copyright (c) 2014 Andrey Akinshin +// Project URL: https://github.com/AndreyAkinshin/InteropDotNet +// Distributed under the MIT License: http://opensource.org/licenses/MIT +using System; +using System.Runtime.InteropServices; + +namespace InteropDotNet +{ + internal class WindowsLibraryLoaderLogic : ILibraryLoaderLogic + { + public IntPtr LoadLibrary(string fileName) + { + var libraryHandle = IntPtr.Zero; + + try + { + LibraryLoaderTrace.TraceInformation("Trying to load native library \"{0}\"...", fileName); + libraryHandle = WindowsLoadLibrary(fileName); + if (libraryHandle != IntPtr.Zero) + LibraryLoaderTrace.TraceInformation("Successfully loaded native library \"{0}\", handle = {1}.", fileName, libraryHandle); + else + LibraryLoaderTrace.TraceError("Failed to load native library \"{0}\".\r\nCheck windows event log.", fileName); + } + catch (Exception e) + { + var lastError = WindowsGetLastError(); + LibraryLoaderTrace.TraceError("Failed to load native library \"{0}\".\r\nLast Error:{1}\r\nCheck inner exception and\\or windows event log.\r\nInner Exception: {2}", fileName, lastError, e.ToString()); + } + + return libraryHandle; + } + + public bool FreeLibrary(IntPtr libraryHandle) + { + try + { + LibraryLoaderTrace.TraceInformation("Trying to free native library with handle {0} ...", libraryHandle); + var isSuccess = WindowsFreeLibrary(libraryHandle); + if (isSuccess) + LibraryLoaderTrace.TraceInformation("Successfully freed native library with handle {0}.", libraryHandle); + else + LibraryLoaderTrace.TraceError("Failed to free native library with handle {0}.\r\nCheck windows event log.", libraryHandle); + return isSuccess; + } + catch (Exception e) + { + var lastError = WindowsGetLastError(); + LibraryLoaderTrace.TraceError("Failed to free native library with handle {0}.\r\nLast Error:{1}\r\nCheck inner exception and\\or windows event log.\r\nInner Exception: {2}", libraryHandle, lastError, e.ToString()); + return false; + } + } + + public IntPtr GetProcAddress(IntPtr libraryHandle, string functionName) + { + try + { + LibraryLoaderTrace.TraceInformation("Trying to load native function \"{0}\" from the library with handle {1}...", + functionName, libraryHandle); + var functionHandle = WindowsGetProcAddress(libraryHandle, functionName); + if (functionHandle != IntPtr.Zero) + LibraryLoaderTrace.TraceInformation("Successfully loaded native function \"{0}\", function handle = {1}.", + functionName, functionHandle); + else + LibraryLoaderTrace.TraceError("Failed to load native function \"{0}\", function handle = {1}", + functionName, functionHandle); + return functionHandle; + } + catch (Exception e) + { + var lastError = WindowsGetLastError(); + LibraryLoaderTrace.TraceError("Failed to free native library with handle {0}.\r\nLast Error:{1}\r\nCheck inner exception and\\or windows event log.\r\nInner Exception: {2}", libraryHandle, lastError, e.ToString()); + return IntPtr.Zero; + } + } + + public string FixUpLibraryName(string fileName) + { + if (!String.IsNullOrEmpty(fileName) && !fileName.EndsWith(".dll", StringComparison.OrdinalIgnoreCase)) + return fileName + ".dll"; + return fileName; + } + + [DllImport("kernel32", EntryPoint = "LoadLibrary", CallingConvention = CallingConvention.Winapi, + SetLastError = true, CharSet = CharSet.Auto, BestFitMapping = false, ThrowOnUnmappableChar = true)] + private static extern IntPtr WindowsLoadLibrary(string dllPath); + + [DllImport("kernel32", EntryPoint = "FreeLibrary", CallingConvention = CallingConvention.Winapi, + SetLastError = true, CharSet = CharSet.Auto, BestFitMapping = false, ThrowOnUnmappableChar = true)] + private static extern bool WindowsFreeLibrary(IntPtr handle); + + [DllImport("kernel32", EntryPoint = "GetProcAddress", CallingConvention = CallingConvention.Winapi, + SetLastError = true)] + private static extern IntPtr WindowsGetProcAddress(IntPtr handle, string procedureName); + + private static int WindowsGetLastError() + { + return Marshal.GetLastWin32Error(); + } + } +} \ No newline at end of file diff --git a/src/Tesseract/Page.cs b/src/Tesseract/Page.cs index 56cd25de..1cff5bfb 100644 --- a/src/Tesseract/Page.cs +++ b/src/Tesseract/Page.cs @@ -36,7 +36,7 @@ internal Page(TesseractEngine engine, Pix image, Rect regionOfInterest) regionOfInterest = value; // update region of interest in image - Interop.TessApi.BaseApiSetRectangle(Engine.Handle, regionOfInterest.X1, regionOfInterest.Y1, regionOfInterest.Width, regionOfInterest.Height); + Interop.TessApi.Native.BaseApiSetRectangle(Engine.Handle, regionOfInterest.X1, regionOfInterest.Y1, regionOfInterest.Width, regionOfInterest.Height); // request rerun of recognition on the next call that requires recognition runRecognitionPhase = false; @@ -46,14 +46,14 @@ internal Page(TesseractEngine engine, Pix image, Rect regionOfInterest) public PageIterator AnalyseLayout() { - var resultIteratorHandle = Interop.TessApi.BaseAPIAnalyseLayout(Engine.Handle); + var resultIteratorHandle = Interop.TessApi.Native.BaseAPIAnalyseLayout(Engine.Handle); return new PageIterator(resultIteratorHandle); } public ResultIterator GetIterator() { Recognize(); - var resultIteratorHandle = Interop.TessApi.BaseApiGetIterator(Engine.Handle); + var resultIteratorHandle = Interop.TessApi.Native.BaseApiGetIterator(Engine.Handle); return new ResultIterator(resultIteratorHandle); } @@ -72,7 +72,7 @@ public string GetHOCRText(int pageNum) public float GetMeanConfidence() { Recognize(); - return Interop.TessApi.BaseAPIMeanTextConf(Engine.Handle) / 100.0f; + return Interop.TessApi.Native.BaseAPIMeanTextConf(Engine.Handle) / 100.0f; } @@ -82,7 +82,8 @@ public float GetMeanConfidence() private void Recognize() { if (!runRecognitionPhase) { - if (Interop.TessApi.BaseApiRecognize(Engine.Handle, new HandleRef(this, IntPtr.Zero)) != 0) { + if (Interop.TessApi.Native.BaseApiRecognize(Engine.Handle, new HandleRef(this, IntPtr.Zero)) != 0) + { throw new InvalidOperationException("Recognition of image failed."); } runRecognitionPhase = true; @@ -93,7 +94,7 @@ private void Recognize() protected override void Dispose(bool disposing) { if (disposing) { - Interop.TessApi.BaseAPIClear(Engine.Handle); + Interop.TessApi.Native.BaseAPIClear(Engine.Handle); } } } diff --git a/src/Tesseract/PageIterator.cs b/src/Tesseract/PageIterator.cs index 8dbb6e9b..6baaf61a 100644 --- a/src/Tesseract/PageIterator.cs +++ b/src/Tesseract/PageIterator.cs @@ -26,7 +26,7 @@ internal PageIterator(IntPtr handle) /// public void Begin() { - Interop.TessApi.PageIteratorBegin(handle); + Interop.TessApi.Native.PageIteratorBegin(handle); } /// @@ -39,7 +39,7 @@ public void Begin() /// public bool Next(PageIteratorLevel level) { - return Interop.TessApi.PageIteratorNext(handle, level) != 0; + return Interop.TessApi.Native.PageIteratorNext(handle, level) != 0; } /// @@ -68,7 +68,7 @@ public bool Next(PageIteratorLevel level, PageIteratorLevel element) /// public bool IsAtBeginningOf(PageIteratorLevel level) { - return Interop.TessApi.PageIteratorIsAtBeginningOf(handle, level) != 0; + return Interop.TessApi.Native.PageIteratorIsAtBeginningOf(handle, level) != 0; } /// @@ -79,22 +79,22 @@ public bool IsAtBeginningOf(PageIteratorLevel level) /// public bool IsAtFinalOf(PageIteratorLevel level, PageIteratorLevel element) { - return Interop.TessApi.PageIteratorIsAtFinalElement(handle, level, element) != 0; + return Interop.TessApi.Native.PageIteratorIsAtFinalElement(handle, level, element) != 0; } public PolyBlockType BlockType { - get { return Interop.TessApi.PageIteratorBlockType(handle); } + get { return Interop.TessApi.Native.PageIteratorBlockType(handle); } } public Pix GetBinaryImage(PageIteratorLevel level) { - return Pix.Create(Interop.TessApi.PageIteratorGetBinaryImage(handle, level)); + return Pix.Create(Interop.TessApi.Native.PageIteratorGetBinaryImage(handle, level)); } public Pix GetImage(PageIteratorLevel level, int padding, out int x, out int y) { - return Pix.Create(Interop.TessApi.PageIteratorGetImage(handle, level, padding, out x, out y)); + return Pix.Create(Interop.TessApi.Native.PageIteratorGetImage(handle, level, padding, out x, out y)); } /// @@ -106,7 +106,8 @@ public Pix GetImage(PageIteratorLevel level, int padding, out int x, out int y) public bool TryGetBoundingBox(PageIteratorLevel level, out Rect bounds) { int x1, y1, x2, y2; - if (Interop.TessApi.PageIteratorBoundingBox(handle, level, out x1, out y1, out x2, out y2) != 0) { + if (Interop.TessApi.Native.PageIteratorBoundingBox(handle, level, out x1, out y1, out x2, out y2) != 0) + { bounds = Rect.FromCoords(x1, y1, x2, y2); return true; } else { @@ -127,7 +128,8 @@ public bool TryGetBoundingBox(PageIteratorLevel level, out Rect bounds) public bool TryGetBaseline(PageIteratorLevel level, out Rect bounds) { int x1, y1, x2, y2; - if (Interop.TessApi.PageIteratorBaseline(handle, level, out x1, out y1, out x2, out y2) != 0) { + if (Interop.TessApi.Native.PageIteratorBaseline(handle, level, out x1, out y1, out x2, out y2) != 0) + { bounds = Rect.FromCoords(x1, y1, x2, y2); return true; } else { @@ -145,7 +147,7 @@ public ElementProperties GetProperties() WritingDirection writing_direction; TextLineOrder textLineOrder; float deskew_angle; - Interop.TessApi.PageIteratorOrientation(handle, out orientation, out writing_direction, out textLineOrder, out deskew_angle); + Interop.TessApi.Native.PageIteratorOrientation(handle, out orientation, out writing_direction, out textLineOrder, out deskew_angle); return new ElementProperties(orientation, textLineOrder, writing_direction, deskew_angle); } @@ -153,7 +155,7 @@ public ElementProperties GetProperties() protected override void Dispose(bool disposing) { - Interop.TessApi.PageIteratorDelete(handle); + Interop.TessApi.Native.PageIteratorDelete(handle); } } } diff --git a/src/Tesseract/Pix.cs b/src/Tesseract/Pix.cs index 87d99c0a..56cd2ba7 100644 --- a/src/Tesseract/Pix.cs +++ b/src/Tesseract/Pix.cs @@ -55,8 +55,8 @@ public static Pix Create(int width, int height, int depth) if (width <= 0) throw new ArgumentException("Width must be greater than zero", "width"); if (height <= 0) throw new ArgumentException("Height must be greater than zero", "height"); - - var handle = Interop.LeptonicaApi.pixCreate(width, height, depth); + + var handle = Interop.LeptonicaApi.Native.pixCreate(width, height, depth); if (handle == IntPtr.Zero) throw new InvalidOperationException("Failed to create pix, this normally occurs because the requested image size is too large, please check Standard Error Output."); return Create(handle); @@ -71,7 +71,7 @@ public static Pix Create(IntPtr handle) public static Pix LoadFromFile(string filename) { - var pixHandle = Interop.LeptonicaApi.pixRead(filename); + var pixHandle = Interop.LeptonicaApi.Native.pixRead(filename); if (pixHandle == IntPtr.Zero) { throw new IOException(String.Format("Failed to load image '{0}'.", filename)); } @@ -90,11 +90,11 @@ private Pix(IntPtr handle) if (handle == IntPtr.Zero) throw new ArgumentNullException("handle"); this.handle = new HandleRef(this, handle); - this.width = Interop.LeptonicaApi.pixGetWidth(this.handle); - this.height = Interop.LeptonicaApi.pixGetHeight(this.handle); - this.depth = Interop.LeptonicaApi.pixGetDepth(this.handle); + this.width = Interop.LeptonicaApi.Native.pixGetWidth(this.handle); + this.height = Interop.LeptonicaApi.Native.pixGetHeight(this.handle); + this.depth = Interop.LeptonicaApi.Native.pixGetDepth(this.handle); - var colorMapHandle = Interop.LeptonicaApi.pixGetColormap(this.handle); + var colorMapHandle = Interop.LeptonicaApi.Native.pixGetColormap(this.handle); if (colorMapHandle != IntPtr.Zero) { this.colormap = new PixColormap(colorMapHandle); } @@ -110,11 +110,13 @@ public PixColormap Colormap set { if (value != null) { - if (Interop.LeptonicaApi.pixSetColormap(handle, value.Handle) == 0) { + if (Interop.LeptonicaApi.Native.pixSetColormap(handle, value.Handle) == 0) + { colormap = value; } } else { - if (Interop.LeptonicaApi.pixDestroyColormap(handle) == 0) { + if (Interop.LeptonicaApi.Native.pixDestroyColormap(handle) == 0) + { colormap = null; } } @@ -168,9 +170,10 @@ public void Save(string filename, ImageFormat? format = null) } else { actualFormat = format.Value; } - - - if (Interop.LeptonicaApi.pixWrite(filename, handle, actualFormat) != 0) { + + + if (Interop.LeptonicaApi.Native.pixWrite(filename, handle, actualFormat) != 0) + { throw new IOException(String.Format("Failed to save image '{0}'.", filename)); } } @@ -199,7 +202,7 @@ public void Save(string filename, ImageFormat? format = null) /// The pix with it's reference count incremented. public Pix Clone() { - var clonedHandle = Interop.LeptonicaApi.pixClone(handle); + var clonedHandle = Interop.LeptonicaApi.Native.pixClone(handle); return new Pix(clonedHandle); } @@ -269,7 +272,7 @@ public Pix Deskew(int redSearch, out Scew scew) public Pix Deskew(ScewSweep sweep, int redSearch, int thresh, out Scew scew) { float pAngle, pConf; - var resultPixHandle = Interop.LeptonicaApi.pixDeskewGeneral(handle, sweep.Reduction, sweep.Range, sweep.Delta, redSearch, thresh, out pAngle, out pConf); + var resultPixHandle = Interop.LeptonicaApi.Native.pixDeskewGeneral(handle, sweep.Reduction, sweep.Range, sweep.Delta, redSearch, thresh, out pAngle, out pConf); if (resultPixHandle == IntPtr.Zero) throw new TesseractException("Failed to deskew image."); scew = new Scew(pAngle, pConf); return new Pix(resultPixHandle); @@ -287,7 +290,7 @@ public Pix Deskew(ScewSweep sweep, int redSearch, int thresh, out Scew scew) public Pix BinarizeOtsuAdaptiveThreshold(int sx, int sy, int smoothx, int smoothy, float scorefract) { IntPtr ppixth, ppixd; - int result = Interop.LeptonicaApi.pixOtsuAdaptiveThreshold(handle, sx, sy, smoothx, smoothy, scorefract, out ppixth, out ppixd); + int result = Interop.LeptonicaApi.Native.pixOtsuAdaptiveThreshold(handle, sx, sy, smoothx, smoothy, scorefract, out ppixth, out ppixd); if (result == 1) throw new TesseractException("Failed to binarize image."); return new Pix(ppixd); } @@ -301,7 +304,7 @@ public Pix BinarizeOtsuAdaptiveThreshold(int sx, int sy, int smoothx, int smooth /// public Pix ConvertRGBToGray(float rwt, float gwt, float bwt) { - var resultPixHandle = Interop.LeptonicaApi.pixConvertRGBToGray(handle, rwt, gwt, bwt); + var resultPixHandle = Interop.LeptonicaApi.Native.pixConvertRGBToGray(handle, rwt, gwt, bwt); if (resultPixHandle == IntPtr.Zero) throw new TesseractException("Failed to convert to grayscale."); return new Pix(resultPixHandle); } @@ -346,10 +349,10 @@ public Pix Rotate(float angle, RotationMethod method = RotationMethod.AreaMap, R var rotations = 2 * angle / Math.PI; if(Math.Abs(rotations - Math.Floor(rotations)) < VerySmallAngle) { // handle special case of orthoganal rotations (90, 180, 270) - resultHandle = Interop.LeptonicaApi.pixRotateOrth(handle, (int)rotations); + resultHandle = Interop.LeptonicaApi.Native.pixRotateOrth(handle, (int)rotations); } else { // handle general case - resultHandle = Interop.LeptonicaApi.pixRotate(handle, angle, method, fillColor, width.Value, height.Value); + resultHandle = Interop.LeptonicaApi.Native.pixRotate(handle, angle, method, fillColor, width.Value, height.Value); } if(resultHandle == IntPtr.Zero) throw new LeptonicaException("Failed to rotate image around it's centre."); @@ -365,7 +368,7 @@ public Pix Rotate(float angle, RotationMethod method = RotationMethod.AreaMap, R protected override void Dispose(bool disposing) { var tmpHandle = handle.Handle; - Interop.LeptonicaApi.pixDestroy(ref tmpHandle); + Interop.LeptonicaApi.Native.pixDestroy(ref tmpHandle); this.handle = new HandleRef(this, IntPtr.Zero); } diff --git a/src/Tesseract/PixColormap.cs b/src/Tesseract/PixColormap.cs index b2e763fa..9b3e778b 100644 --- a/src/Tesseract/PixColormap.cs +++ b/src/Tesseract/PixColormap.cs @@ -27,7 +27,7 @@ public static PixColormap Create(int depth) throw new ArgumentOutOfRangeException("depth", "Depth must be 1, 2, 4, or 8 bpp."); } - var handle = Interop.LeptonicaApi.pixcmapCreate(depth); + var handle = Interop.LeptonicaApi.Native.pixcmapCreate(depth); if (handle == IntPtr.Zero) { throw new InvalidOperationException("Failed to create colormap."); } @@ -42,7 +42,7 @@ public static PixColormap CreateLinear(int depth, int levels) if (levels < 2 || levels > (2 << depth)) throw new ArgumentOutOfRangeException("levels", "Depth must be 2 and 2^depth (inclusive)."); - var handle = Interop.LeptonicaApi.pixcmapCreateLinear(depth, levels); + var handle = Interop.LeptonicaApi.Native.pixcmapCreateLinear(depth, levels); if (handle == IntPtr.Zero) { throw new InvalidOperationException("Failed to create colormap."); } @@ -55,7 +55,7 @@ public static PixColormap CreateLinear(int depth, bool firstIsBlack, bool lastIs throw new ArgumentOutOfRangeException("depth", "Depth must be 1, 2, 4, or 8 bpp."); } - var handle = Interop.LeptonicaApi.pixcmapCreateRandom(depth, firstIsBlack ? 1 : 0, lastIsWhite ? 1 : 0); + var handle = Interop.LeptonicaApi.Native.pixcmapCreateRandom(depth, firstIsBlack ? 1 : 0, lastIsWhite ? 1 : 0); if (handle == IntPtr.Zero) { throw new InvalidOperationException("Failed to create colormap."); } @@ -69,48 +69,49 @@ public HandleRef Handle public int Depth { - get { return Interop.LeptonicaApi.pixcmapGetDepth(handle); } + get { return Interop.LeptonicaApi.Native.pixcmapGetDepth(handle); } } public int Count { - get { return Interop.LeptonicaApi.pixcmapGetCount(handle); } + get { return Interop.LeptonicaApi.Native.pixcmapGetCount(handle); } } public int FreeCount { - get { return Interop.LeptonicaApi.pixcmapGetFreeCount(handle); } + get { return Interop.LeptonicaApi.Native.pixcmapGetFreeCount(handle); } } public bool AddColor(PixColor color) { - return Interop.LeptonicaApi.pixcmapAddColor(handle, color.Red, color.Green, color.Blue) == 0; + return Interop.LeptonicaApi.Native.pixcmapAddColor(handle, color.Red, color.Green, color.Blue) == 0; } public bool AddNewColor(PixColor color, out int index) { - return Interop.LeptonicaApi.pixcmapAddNewColor(handle, color.Red, color.Green, color.Blue, out index) == 0; + return Interop.LeptonicaApi.Native.pixcmapAddNewColor(handle, color.Red, color.Green, color.Blue, out index) == 0; } public bool AddNearestColor(PixColor color, out int index) { - return Interop.LeptonicaApi.pixcmapAddNearestColor(handle, color.Red, color.Green, color.Blue, out index) == 0; + return Interop.LeptonicaApi.Native.pixcmapAddNearestColor(handle, color.Red, color.Green, color.Blue, out index) == 0; } public bool AddBlackOrWhite(int color, out int index) { - return Interop.LeptonicaApi.pixcmapAddBlackOrWhite(handle, color, out index) == 0; + return Interop.LeptonicaApi.Native.pixcmapAddBlackOrWhite(handle, color, out index) == 0; } public bool SetBlackOrWhite(bool setBlack, bool setWhite) { - return Interop.LeptonicaApi.pixcmapSetBlackAndWhite(handle, setBlack ? 1 : 0, setWhite ? 1 : 0) == 0; + return Interop.LeptonicaApi.Native.pixcmapSetBlackAndWhite(handle, setBlack ? 1 : 0, setWhite ? 1 : 0) == 0; } public bool IsUsableColor(PixColor color) { int usable; - if (Interop.LeptonicaApi.pixcmapUsableColor(handle, color.Red, color.Green, color.Blue, out usable) == 0) { + if (Interop.LeptonicaApi.Native.pixcmapUsableColor(handle, color.Red, color.Green, color.Blue, out usable) == 0) + { return usable == 1; } else { throw new InvalidOperationException("Failed to detect if color was usable or not."); @@ -119,7 +120,8 @@ public bool IsUsableColor(PixColor color) public void Clear() { - if (Interop.LeptonicaApi.pixcmapClear(handle) != 0) { + if (Interop.LeptonicaApi.Native.pixcmapClear(handle) != 0) + { throw new InvalidOperationException("Failed to clear color map."); } } @@ -129,7 +131,8 @@ public void Clear() get { int color; - if (Interop.LeptonicaApi.pixcmapGetColor32(handle, index, out color) == 0) { + if (Interop.LeptonicaApi.Native.pixcmapGetColor32(handle, index, out color) == 0) + { return PixColor.FromRgb((uint)color); } else { throw new InvalidOperationException("Failed to retrieve color."); @@ -137,7 +140,8 @@ public void Clear() } set { - if (Interop.LeptonicaApi.pixcmapResetColor(handle, index, value.Red, value.Green, value.Blue) != 0) { + if (Interop.LeptonicaApi.Native.pixcmapResetColor(handle, index, value.Red, value.Green, value.Blue) != 0) + { throw new InvalidOperationException("Failed to reset color."); } } @@ -146,7 +150,7 @@ public void Clear() public void Dispose() { IntPtr tmpHandle = Handle.Handle; - Interop.LeptonicaApi.pixcmapDestroy(ref tmpHandle); + Interop.LeptonicaApi.Native.pixcmapDestroy(ref tmpHandle); this.handle = new HandleRef(this, IntPtr.Zero); } } diff --git a/src/Tesseract/PixData.cs b/src/Tesseract/PixData.cs index b66e173f..f6a1d9ba 100644 --- a/src/Tesseract/PixData.cs +++ b/src/Tesseract/PixData.cs @@ -13,8 +13,8 @@ public unsafe class PixData internal PixData(Pix pix) { Pix = pix; - Data = Interop.LeptonicaApi.pixGetData(Pix.Handle); - WordsPerLine = Interop.LeptonicaApi.pixGetWpl(Pix.Handle); + Data = Interop.LeptonicaApi.Native.pixGetData(Pix.Handle); + WordsPerLine = Interop.LeptonicaApi.Native.pixGetWpl(Pix.Handle); } /// @@ -36,7 +36,7 @@ internal PixData(Pix pix) /// public void EndianByteSwap() { - Interop.LeptonicaApi.pixEndianByteSwap(Pix.Handle); + Interop.LeptonicaApi.Native.pixEndianByteSwap(Pix.Handle); } #if Net45 diff --git a/src/Tesseract/ResultIterator.cs b/src/Tesseract/ResultIterator.cs index 753f96aa..293c064c 100644 --- a/src/Tesseract/ResultIterator.cs +++ b/src/Tesseract/ResultIterator.cs @@ -11,7 +11,7 @@ internal ResultIterator(IntPtr handle) public float GetConfidence(PageIteratorLevel level) { - return Interop.TessApi.ResultIteratorGetConfidence(handle, level); + return Interop.TessApi.Native.ResultIteratorGetConfidence(handle, level); } public string GetText(PageIteratorLevel level) diff --git a/src/Tesseract/Tesseract.csproj b/src/Tesseract/Tesseract.csproj index d4eac3a8..a8602b07 100644 --- a/src/Tesseract/Tesseract.csproj +++ b/src/Tesseract/Tesseract.csproj @@ -59,6 +59,14 @@ + + + + + + + + @@ -73,7 +81,6 @@ - @@ -118,6 +125,22 @@ x86\libtesseract302.dll PreserveNewest + + x64\liblept168.so + PreserveNewest + + + x64\libtesseract302.so + PreserveNewest + + + x86\liblept168.so + PreserveNewest + + + x86\libtesseract302.so + PreserveNewest + diff --git a/src/Tesseract/TesseractEngine.cs b/src/Tesseract/TesseractEngine.cs index e846df24..a7949ca4 100644 --- a/src/Tesseract/TesseractEngine.cs +++ b/src/Tesseract/TesseractEngine.cs @@ -57,7 +57,7 @@ void OnPageDisposed(object sender, System.EventArgs e) public TesseractEngine(string datapath, string language, EngineMode engineMode = EngineMode.Default) { DefaultPageSegMode = PageSegMode.Auto; - handle = new HandleRef(this, Interop.TessApi.BaseApiCreate()); + handle = new HandleRef(this, Interop.TessApi.Native.BaseApiCreate()); Initialise(datapath, language, engineMode); } @@ -69,7 +69,7 @@ public HandleRef Handle public string Version { - get { return Interop.TessApi.GetVersion(); } + get { return Interop.TessApi.Native.GetVersion(); } } #region Config @@ -81,8 +81,8 @@ public string Version /// The new value of the variable. /// Returns True if successful; otherwise False. public bool SetVariable(string name, string value) - { - return Interop.TessApi.BaseApiSetVariable(handle, name, value) != 0; + { + return Interop.TessApi.Native.BaseApiSetVariable(handle, name, value) != 0; } /// @@ -94,7 +94,7 @@ public bool SetVariable(string name, string value) public bool SetVariable(string name, bool value) { var strEncodedValue = value ? "TRUE" : "FALSE"; - return Interop.TessApi.BaseApiSetVariable(handle, name, strEncodedValue) != 0; + return Interop.TessApi.Native.BaseApiSetVariable(handle, name, strEncodedValue) != 0; } /// @@ -106,7 +106,7 @@ public bool SetVariable(string name, bool value) public bool SetVariable(string name, int value) { var strEncodedValue = value.ToString("D", CultureInfo.InvariantCulture.NumberFormat); - return Interop.TessApi.BaseApiSetVariable(handle, name, strEncodedValue) != 0; + return Interop.TessApi.Native.BaseApiSetVariable(handle, name, strEncodedValue) != 0; } /// @@ -118,12 +118,12 @@ public bool SetVariable(string name, int value) public bool SetVariable(string name, double value) { var strEncodedValue = value.ToString("R", CultureInfo.InvariantCulture.NumberFormat); - return Interop.TessApi.BaseApiSetVariable(handle, name, strEncodedValue) != 0; + return Interop.TessApi.Native.BaseApiSetVariable(handle, name, strEncodedValue) != 0; } public bool SetDebugVariable(string name, string value) { - return Interop.TessApi.BaseApiSetDebugVariable(handle, name, value) != 0; + return Interop.TessApi.Native.BaseApiSetDebugVariable(handle, name, value) != 0; } /// @@ -135,7 +135,8 @@ public bool SetDebugVariable(string name, string value) public bool TryGetBoolVariable(string name, out bool value) { int val; - if (Interop.TessApi.BaseApiGetBoolVariable(handle, name, out val) != 0) { + if (Interop.TessApi.Native.BaseApiGetBoolVariable(handle, name, out val) != 0) + { value = (val != 0); return true; } else { @@ -152,7 +153,7 @@ public bool TryGetBoolVariable(string name, out bool value) /// Returns True if successful; otherwise False. public bool TryGetIntVariable(string name, out int value) { - return Interop.TessApi.BaseApiGetIntVariable(handle, name, out value) != 0; + return Interop.TessApi.Native.BaseApiGetIntVariable(handle, name, out value) != 0; } /// @@ -163,7 +164,7 @@ public bool TryGetIntVariable(string name, out int value) /// Returns True if successful; otherwise False. public bool TryGetDoubleVariable(string name, out double value) { - return Interop.TessApi.BaseApiGetDoubleVariable(handle, name, out value) != 0; + return Interop.TessApi.Native.BaseApiGetDoubleVariable(handle, name, out value) != 0; } /// @@ -212,8 +213,8 @@ private void Initialise(string datapath, string language, EngineMode engineMode) if(tessDataPrefix != null) { Trace.TraceWarning("Detected that the environment variable 'TESSDATA_PREFIX' is set to '{0}', this will be used as the data directory by tesseract.", tessDataPrefix); } - - if (Interop.TessApi.BaseApiInit(handle, datapath, language, (int)engineMode, IntPtr.Zero, 0, IntPtr.Zero, 0, IntPtr.Zero, 0) != 0) + + if (Interop.TessApi.Native.BaseApiInit(handle, datapath, language, (int)engineMode, IntPtr.Zero, 0, IntPtr.Zero, 0, IntPtr.Zero, 0) != 0) { // Special case logic to handle cleaning up as init has already released the handle if it fails. handle = new HandleRef(this, IntPtr.Zero); @@ -286,10 +287,10 @@ public Page Process(Pix image, string inputName, Rect region, PageSegMode? pageS processCount++; - Interop.TessApi.BaseAPISetPageSegMode(handle, pageSegMode.HasValue ? pageSegMode.Value : DefaultPageSegMode); - Interop.TessApi.BaseApiSetImage(handle, image.Handle); + Interop.TessApi.Native.BaseAPISetPageSegMode(handle, pageSegMode.HasValue ? pageSegMode.Value : DefaultPageSegMode); + Interop.TessApi.Native.BaseApiSetImage(handle, image.Handle); if(!String.IsNullOrEmpty(inputName)) { - Interop.TessApi.BaseApiSetInputName(handle, inputName); + Interop.TessApi.Native.BaseApiSetInputName(handle, inputName); } var page = new Page(this, image, region); page.Disposed += OnIteratorDisposed; @@ -370,7 +371,7 @@ public Page Process(Bitmap image, string inputName, Rect region, PageSegMode? pa protected override void Dispose(bool disposing) { if (handle.Handle != IntPtr.Zero) { - Interop.TessApi.BaseApiDelete(handle); + Interop.TessApi.Native.BaseApiDelete(handle); handle = new HandleRef(this, IntPtr.Zero); } } diff --git a/src/lib/TesseractOcr/x64/liblept168.so b/src/lib/TesseractOcr/x64/liblept168.so new file mode 100644 index 00000000..8b0d51f7 Binary files /dev/null and b/src/lib/TesseractOcr/x64/liblept168.so differ diff --git a/src/lib/TesseractOcr/x64/libtesseract302.so b/src/lib/TesseractOcr/x64/libtesseract302.so new file mode 100644 index 00000000..466f234f Binary files /dev/null and b/src/lib/TesseractOcr/x64/libtesseract302.so differ diff --git a/src/lib/TesseractOcr/x86/liblept168.so b/src/lib/TesseractOcr/x86/liblept168.so new file mode 100644 index 00000000..ce6489b0 Binary files /dev/null and b/src/lib/TesseractOcr/x86/liblept168.so differ diff --git a/src/lib/TesseractOcr/x86/libtesseract302.so b/src/lib/TesseractOcr/x86/libtesseract302.so new file mode 100644 index 00000000..9d2195e4 Binary files /dev/null and b/src/lib/TesseractOcr/x86/libtesseract302.so differ