Option Explicit On
Option Strict On

Imports System.Runtime.InteropServices
Imports System.Text

Module GhostScript

    <StructLayout(LayoutKind.Sequential)>
    Public Structure gsapiRevisionStructure
        Public product As IntPtr
        Public copyright As IntPtr
        Public revision As Integer
        Public revisiondate As Integer
    End Structure

    Public Enum gs_set_param_type
        gs_spt_invalid = -1
        gs_spt_null = 0
        gs_spt_bool = 1
        gs_spt_int = 2
        gs_spt_float = 3
        gs_spt_name = 4
        gs_spt_string = 5
        gs_spt_long = 6
        gs_spt_i64 = 7
        gs_spt_size_t = 8
        gs_spt_parsed = 9
        gs_spt_more_to_come = 1 << 31
    End Enum

    Private Enum gsEncoding
        GS_ARG_ENCODING_LOCAL = 0
        GS_ARG_ENCODING_UTF8 = 1
        GS_ARG_ENCODING_UTF16LE = 2
    End Enum

    Public Class gsConstants
        Public Const E_QUIT As Integer = -101
        Public Const GS_READ_BUFFER As Integer = 32768
        Public Const DISPLAY_UNUSED_LAST As Integer = (1 << 7)
        Public Const DISPLAY_COLORS_RGB As Integer = (1 << 2)
        Public Const DISPLAY_DEPTH_8 As Integer = (1 << 11)
        Public Const DISPLAY_LITTLEENDIAN As Integer = (1 << 16)
        Public Const DISPLAY_BIGENDIAN As Integer = (0 << 16)
    End Class

    Private Const gsdll = "gsdll64.dll"

    Public Delegate Function gsStdioHandler(caller_handle As IntPtr, buffer As IntPtr, len As Integer) As Integer

    <DllImport(gsdll, EntryPoint:="gsapi_revision", CharSet:=CharSet.Ansi, CallingConvention:=CallingConvention.StdCall)>
    Public Function gsapi_revision(ByRef vers As gsapiRevisionStructure, size As Integer) As Integer
    End Function

    <DllImport(gsdll, EntryPoint:="gsapi_new_instance", CharSet:=CharSet.Ansi, CallingConvention:=CallingConvention.StdCall)>
    Public Function gsapi_new_instance(ByRef pinstance As IntPtr, caller_handle As IntPtr) As Integer
    End Function

    <DllImport(gsdll, EntryPoint:="gsapi_delete_instance", CharSet:=CharSet.Ansi, CallingConvention:=CallingConvention.StdCall)>
    Public Sub gsapi_delete_instance(instance As IntPtr)
    End Sub

    <DllImport(gsdll, EntryPoint:="gsapi_set_stdio", CharSet:=CharSet.Ansi, CallingConvention:=CallingConvention.StdCall)>
    Public Function gsapi_set_stdio(instance As IntPtr, stdin As gsStdioHandler, stdout As gsStdioHandler, stderr As gsStdioHandler) As Integer
    End Function

    <DllImport(gsdll, EntryPoint:="gsapi_set_display_callback", CharSet:=CharSet.Ansi, CallingConvention:=CallingConvention.StdCall)>
    Public Function gsapi_set_display_callback(pinstance As IntPtr, caller_handle As IntPtr) As Integer
    End Function

    <DllImport(gsdll, EntryPoint:="gsapi_set_arg_encoding", CharSet:=CharSet.Ansi, CallingConvention:=CallingConvention.StdCall)>
    Public Function gsapi_set_arg_encoding(instance As IntPtr, encoding As Integer) As Integer
    End Function

    <DllImport(gsdll, EntryPoint:="gsapi_init_with_args", CharSet:=CharSet.Ansi, CallingConvention:=CallingConvention.StdCall)>
    Public Function gsapi_init_with_args(instance As IntPtr, argc As Integer, argv As IntPtr) As Integer
    End Function

    <DllImport(gsdll, EntryPoint:="gsapi_exit", CharSet:=CharSet.Ansi, CallingConvention:=CallingConvention.StdCall)>
    Public Function gsapi_exit(instance As IntPtr) As Integer
    End Function

    <UnmanagedFunctionPointer(CallingConvention.Cdecl)>
    Public Delegate Function display_open_delegate(handle As IntPtr, device As IntPtr) As Integer

    <UnmanagedFunctionPointer(CallingConvention.Cdecl)>
    Public Delegate Function display_preclose_delegate(handle As IntPtr, device As IntPtr) As Integer

    <UnmanagedFunctionPointer(CallingConvention.Cdecl)>
    Public Delegate Function display_close_delegate(handle As IntPtr, device As IntPtr) As Integer

    <UnmanagedFunctionPointer(CallingConvention.Cdecl)>
    Public Delegate Function display_presize_delegate(handle As IntPtr, device As IntPtr, width As Integer, height As Integer, raster As Integer, format As UInteger) As Integer

    <UnmanagedFunctionPointer(CallingConvention.Cdecl)>
    Public Delegate Function display_size_delegate(handle As IntPtr, device As IntPtr, width As Integer, height As Integer, raster As Integer, format As UInteger, pimage As IntPtr) As Integer

    <UnmanagedFunctionPointer(CallingConvention.Cdecl)>
    Public Delegate Function display_sync_delegate(handle As IntPtr, device As IntPtr) As Integer

    <UnmanagedFunctionPointer(CallingConvention.Cdecl)>
    Public Delegate Function display_page_delegate(handle As IntPtr, device As IntPtr, copies As Integer, flush As Integer) As Integer

    <UnmanagedFunctionPointer(CallingConvention.Cdecl)>
    Public Delegate Function display_update_delegate(handle As IntPtr, device As IntPtr, x As Integer, y As Integer, w As Integer, h As Integer) As Integer

    <UnmanagedFunctionPointer(CallingConvention.Cdecl)>
    Public Delegate Function display_memalloc_delegate(handle As IntPtr, device As IntPtr, size As ULong) As Integer

    <UnmanagedFunctionPointer(CallingConvention.Cdecl)>
    Public Delegate Function display_memfree_delegate(handle As IntPtr, device As IntPtr, mem As IntPtr) As Integer

    Private Structure displayCallbackStructure
        Public sizeof_display_callback As Integer
        Public major_vers As Integer
        Public minor_vers As Integer
        Public display_open As display_open_delegate
        Public display_preclose As display_preclose_delegate
        Public display_close As display_close_delegate
        Public display_presize As display_presize_delegate
        Public display_size As display_size_delegate
        Public display_sync As display_sync_delegate
        Public display_page As display_page_delegate
        Public display_update As display_update_delegate
        Public display_memalloc As display_memalloc_delegate
        Public display_memfree As display_memfree_delegate
    End Structure

    Private display_callback As New displayCallbackStructure With {
        .sizeof_display_callback = Marshal.SizeOf(display_callback),
        .major_vers = 1,
        .minor_vers = 0,
        .display_open = New display_open_delegate(AddressOf display_open),
        .display_preclose = New display_preclose_delegate(AddressOf display_preclose),
        .display_close = New display_close_delegate(AddressOf display_close),
        .display_presize = New display_presize_delegate(AddressOf display_presize),
        .display_size = New display_size_delegate(AddressOf display_size),
        .display_sync = New display_sync_delegate(AddressOf display_sync),
        .display_page = New display_page_delegate(AddressOf display_page),
        .display_update = New display_update_delegate(AddressOf display_update),
        .display_memalloc = Nothing,
        .display_memfree = Nothing}

    Private displayStructure As IntPtr = Marshal.AllocHGlobal(display_callback.sizeof_display_callback)

    Private pageWidth As Integer
    Private pageHeight As Integer
    Private pageRaster As Integer
    Private pagePtr As IntPtr
    Public pageImages As New List(Of Bitmap)

    Private Function display_open(handle As IntPtr, device As IntPtr) As Integer
        Return 0
    End Function

    Private Function display_preclose(handle As IntPtr, device As IntPtr) As Integer
        Return 0
    End Function

    Private Function display_close(handle As IntPtr, device As IntPtr) As Integer
        Return 0
    End Function

    Private Function display_presize(handle As IntPtr, device As IntPtr, width As Integer, height As Integer, raster As Integer, format As UInteger) As Integer
        Return 0
    End Function

    Private Function display_size(handle As IntPtr, device As IntPtr, width As Integer, height As Integer, raster As Integer, format As UInteger, pimage As IntPtr) As Integer
        pageWidth = width
        pageHeight = height
        pageRaster = raster
        pagePtr = pimage
        Return 0
    End Function

    Private Function display_sync(handle As IntPtr, device As IntPtr) As Integer
        Return 0
    End Function

    Private Function display_page(handle As IntPtr, device As IntPtr, copies As Integer, flush As Integer) As Integer
        Using bitmap As New Bitmap(pageWidth, pageHeight, pageRaster, Imaging.PixelFormat.Format24bppRgb, pagePtr)
            pageImages.Add(New Bitmap(bitmap))
        End Using
        Return 0
    End Function

    Private Function display_update(handle As IntPtr, device As IntPtr, x As Integer, y As Integer, w As Integer, h As Integer) As Integer
        Return 0
    End Function

    Public Function stdout_callback(handle As IntPtr, buffer As IntPtr, len As Integer) As Integer
        Dim str As String = Marshal.PtrToStringAnsi(buffer, len)
        Debug.Write(str)
        Return len
    End Function

    Public Sub CallGhostScript(ByVal args As String())
        '引数をCの形式に変換する
        Dim argc As Integer = args.Length
        Dim argsAnsi(argc - 1) As Object
        Dim argsHandle(argc - 1) As GCHandle
        Dim argsPtr(argc - 1) As IntPtr
        Dim argHandle As GCHandle
        Dim argv As IntPtr
        For i As Integer = 0 To argc - 1
            argsAnsi(i) = Encoding.UTF8.GetBytes(args(i))
            argsHandle(i) = GCHandle.Alloc(argsAnsi(i), GCHandleType.Pinned)
            argsPtr(i) = argsHandle(i).AddrOfPinnedObject
        Next
        argHandle = GCHandle.Alloc(argsPtr, GCHandleType.Pinned)
        argv = argHandle.AddrOfPinnedObject
        'GhostScriptを呼び出す
        Dim instance As IntPtr
        Dim handle As IntPtr
        Dim result As Integer
        Try
            result = gsapi_new_instance(instance, handle)
            If result < 0 Then
                Throw New Exception("gsapi_new_instance()の呼び出しでエラーが発生")
            End If
            Dim stdout As New gsStdioHandler(AddressOf stdout_callback)
            result = gsapi_set_stdio(instance, Nothing, stdout, Nothing)
            If result < 0 Then
                Throw New Exception("gsapi_set_stdio()の呼び出しでエラーが発生")
            End If
            result = gsapi_set_arg_encoding(instance, gsEncoding.GS_ARG_ENCODING_UTF8)
            If result < 0 Then
                Throw New Exception("gsapi_set_arg_encoding()の呼び出しでエラーが発生")
            End If
            Marshal.StructureToPtr(display_callback, displayStructure, False)
            result = gsapi_set_display_callback(instance, displayStructure)
            If result < 0 Then
                Throw New Exception("gsapi_set_display_callback()の呼び出しでエラーが発生")
            End If
            result = gsapi_init_with_args(instance, argc, argv)
            If result < 0 Then
                Throw New Exception("gsapi_init_with_args()の呼び出しでエラーが発生")
            End If
        Catch ex As Exception
            MessageBox.Show(ex.Message, "エラー", MessageBoxButtons.OK, MessageBoxIcon.Error)
        Finally
            gsapi_exit(instance)
            gsapi_delete_instance(instance)
        End Try
        '開放処理
        For i As Integer = 0 To argc - 1
            argsHandle(i).Free()
        Next
        argHandle.Free()
    End Sub

End Module