How to view PDF with Windows API? (PDF -> bitmap)

Get help with using AutoHotkey and its commands and hotkeys
User avatar
mikeyww
Posts: 5104
Joined: 09 Sep 2014, 18:38

Re: How to view PDF with Windows API? (PDF -> bitmap)

07 Sep 2020, 11:02

Not sure, but it's easy with ImageMagick.

Code: Select all

source = ....pdf ; Adjust this line
SplitPath, source,, dir,, fnBare
target = %dir%\%fnBare%.bmp
FileRecycle, %target%
SplashTextOn, 300, 100, Converting, Please wait....
RunWait, convert.exe "%source%" "%target%",, Hide
SplashTextOff
If ErrorLevel {
 MsgBox, 16, Error, An error occurred during the file conversion.
 ExitApp
}
If !FileExist(target) {
 done := False, n := -1
 While !done {
  n++, target := Format("{}\{}-{}.bmp", dir, fnbare, n), done := !FileExist(target)
  If !done {
   Run, %target%
   Sleep, 100
  }
 }
} Else Run, %target%
ExitApp
iseahound
Posts: 633
Joined: 13 Aug 2016, 21:04
GitHub: iseahound

Re: How to view PDF with Windows API? (PDF -> bitmap)

07 Sep 2020, 11:58

Next time, try reading the question. Because I am clearly trying to get a pointer to to an IDXGIDevice or ID2D1DeviceContext which I need to figure out how to convert back into a GDI device context using interop functions, or a GDI+ bitmap.

For example, the GDI+ function GdipCreateBitmapFromDirectDrawSurface combined with IPdfRendererNative::RenderPageToSurface seems promising.
https://docs.microsoft.com/en-us/windows/win32/api/windows.data.pdf.interop/nf-windows-data-pdf-interop-ipdfrenderernative-renderpagetosurface
Creates a Bitmap::Bitmap object based on a DirectDraw surface. The Bitmap::Bitmap object maintains a reference to the DirectDraw surface until the Bitmap::Bitmap object is deleted or goes out of scope.
which implies that after GdipCreateBitmapFromDirectDrawSurface is called, GdipCloneImage should be called.

The problem is that I am not familiar with the DirectDraw factories
User avatar
mikeyww
Posts: 5104
Joined: 09 Sep 2014, 18:38

Re: How to view PDF with Windows API? (PDF -> bitmap)

07 Sep 2020, 12:58

No problem at all. I understood that the question was that you wanted to convert a PDF into a bitmap image, but I was wrong.
malcev
Posts: 855
Joined: 12 Aug 2014, 12:37

Re: How to view PDF with Windows API? (PDF -> bitmap)

08 Sep 2020, 03:17

No. You have to
1) ShCore\CreateRandomAccessStreamOnFile
2) PdfDocument.LoadFromStreamAsync
3) PdfDocument.GetPage(UInt32)
4) PdfPage.RenderToStreamAsync
5) ShCore\CreateStreamOverRandomAccessStream
6) GdipCreateBitmapFromStream
Look at example of OCR UWP
https://www.autohotkey.com/boards/viewtopic.php?f=6&t=72674
teadrinker
Posts: 2167
Joined: 29 Mar 2015, 09:41
Contact:

Re: How to view PDF with Windows API? (PDF -> bitmap)

08 Sep 2020, 07:02

malcev wrote: 4) PdfPage.RenderToStreamAsync
5) ShCore\CreateStreamOverRandomAccessStream
I think, before PdfPage.RenderToStreamAsync you need to create new IStream, then call CreateRandomAccessStreamOverStream.
malcev
Posts: 855
Joined: 12 Aug 2014, 12:37

Re: How to view PDF with Windows API? (PDF -> bitmap)

08 Sep 2020, 12:35

Yes, I think You are right.
1) ShCore\CreateRandomAccessStreamOnFile
2) PdfDocument.LoadFromStreamAsync
3) PdfDocument.GetPage(UInt32)
4) Ole32\CreateStreamOnHGlobal
5) ShCore\CreateRandomAccessStreamOverStream
6) PdfPage.RenderToStreamAsync
7) GdipCreateBitmapFromStream
User avatar
kczx3
Posts: 1250
Joined: 06 Oct 2015, 21:39

Re: How to view PDF with Windows API? (PDF -> bitmap)

05 Dec 2020, 19:07

Curious if anyone ever got this functioning based on the comments by malcev and teadrinker?
malcev
Posts: 855
Joined: 12 Aug 2014, 12:37

Re: How to view PDF with Windows API? (PDF -> bitmap)

05 Dec 2020, 20:04

Something like this:

Code: Select all

pdfPath := "D:\1064754760.pdf"

VarSetCapacity(GUID, 16)
DllCall("ole32\CLSIDFromString", "wstr", IID_RandomAccessStream := "{905A0FE1-BC53-11DF-8C49-001E4FC686DA}", "ptr", &GUID)
DllCall("ShCore\CreateRandomAccessStreamOnFile", "wstr", pdfPath, "uint", Read := 0, "ptr", &GUID, "ptr*", IRandomAccessStream)
CreateClass("Windows.Data.Pdf.PdfDocument", IPdfDocumentStatics := "{433A0B5F-C007-4788-90F2-08143D922599}", PdfDocumentStatics)
DllCall(NumGet(NumGet(PdfDocumentStatics+0)+8*A_PtrSize), "ptr", PdfDocumentStatics, "ptr", IRandomAccessStream, "ptr*", PdfDocument)   ; LoadFromStreamAsync
WaitForAsync(PdfDocument)
DllCall(NumGet(NumGet(PdfDocument+0)+6*A_PtrSize), "ptr", PdfDocument, "uint", 0, "ptr*", PdfPage)   ; GetPage
DllCall("Ole32\CreateStreamOnHGlobal", "ptr", 0, "uint", true, "ptr*", IStream)
DllCall("ShCore\CreateRandomAccessStreamOverStream", "ptr", IStream, "uint", BSOS_DEFAULT := 0, "ptr", &GUID, "ptr*", IRandomAccessStreamOut)
DllCall(NumGet(NumGet(PdfPage+0)+6*A_PtrSize), "ptr", PdfPage, "ptr", IRandomAccessStreamOut, "ptr*", asyncInfo)   ; RenderToStreamAsync
WaitForAsync(asyncInfo)

DllCall("LoadLibrary", "str", "gdiplus")
VarSetCapacity(si, A_PtrSize = 8 ? 24 : 16, 0), si := Chr(1)
DllCall("gdiplus\GdiplusStartup", "ptr*", pToken, "ptr", &si, "ptr", 0)
DllCall("gdiplus\GdipCreateBitmapFromStream", "ptr", IStream, "ptr*", pBitmap)
DllCall("gdiplus\GdipCreateHBITMAPFromBitmap", "ptr", pBitmap, "ptr*", hBitmap, "uint", 0xffffffff)
Gui, Add, Picture, , HBITMAP:%hBitmap%
Gui, Show

DllCall("Gdiplus.dll\GdipDisposeImage", "ptr", pBitmap)
Close := ComObjQuery(IRandomAccessStream, IClosable := "{30D5A829-7FA4-4026-83BB-D75BAE4EA99E}")
DllCall(NumGet(NumGet(Close+0)+6*A_PtrSize), "ptr", Close)   ; Close
ObjRelease(Close)
ObjRelease(IRandomAccessStream)
ObjRelease(PdfDocumentStatics)
ObjRelease(PdfDocument)
ObjRelease(PdfPage)
ObjRelease(IStream)
Close := ComObjQuery(IRandomAccessStreamOut, IClosable := "{30D5A829-7FA4-4026-83BB-D75BAE4EA99E}")
DllCall(NumGet(NumGet(Close+0)+6*A_PtrSize), "ptr", Close)   ; Close
ObjRelease(Close)
ObjRelease(IRandomAccessStreamOut)




CreateClass(string, interface, ByRef Class)
{
   CreateHString(string, hString)
   VarSetCapacity(GUID, 16)
   DllCall("ole32\CLSIDFromString", "wstr", interface, "ptr", &GUID)
   result := DllCall("Combase.dll\RoGetActivationFactory", "ptr", hString, "ptr", &GUID, "ptr*", Class, "uint")
   if (result != 0)
   {
      if (result = 0x80004002)
         msgbox No such interface supported
      else if (result = 0x80040154)
         msgbox Class not registered
      else
         msgbox error: %result%
      ExitApp
   }
   DeleteHString(hString)
}

CreateHString(string, ByRef hString)
{
    DllCall("Combase.dll\WindowsCreateString", "wstr", string, "uint", StrLen(string), "ptr*", hString)
}

DeleteHString(hString)
{
   DllCall("Combase.dll\WindowsDeleteString", "ptr", hString)
}

WaitForAsync(ByRef Object)
{
   AsyncInfo := ComObjQuery(Object, IAsyncInfo := "{00000036-0000-0000-C000-000000000046}")
   loop
   {
      DllCall(NumGet(NumGet(AsyncInfo+0)+7*A_PtrSize), "ptr", AsyncInfo, "uint*", status)   ; IAsyncInfo.Status
      if (status != 0)
      {
         if (status != 1)
         {
            DllCall(NumGet(NumGet(AsyncInfo+0)+8*A_PtrSize), "ptr", AsyncInfo, "uint*", ErrorCode)   ; IAsyncInfo.ErrorCode
            msgbox AsyncInfo status error: %ErrorCode%
            ExitApp
         }
         ObjRelease(AsyncInfo)
         break
      }
      sleep 10
   }
   DllCall(NumGet(NumGet(Object+0)+8*A_PtrSize), "ptr", Object, "ptr*", ObjectResult)   ; GetResults
   ObjRelease(Object)
   Object := ObjectResult
}
User avatar
kczx3
Posts: 1250
Joined: 06 Oct 2015, 21:39

Re: How to view PDF with Windows API? (PDF -> bitmap)

05 Dec 2020, 20:10

Interesting. I was on the right track however I was having trouble knowing the proper offsets for each method. It seems like the methods start at 6*A_PtrSize? I’m also using AHKv2 and was try to use ComCall instead of DllCall with NumGets. I’ll take a stab at trying to convert what you’ve posted. Thanks!
User avatar
kczx3
Posts: 1250
Joined: 06 Oct 2015, 21:39

Re: How to view PDF with Windows API? (PDF -> bitmap)

05 Dec 2020, 20:33

Sweet thanks! I was looking here for things - https://github.com/wmliang/wdk-10/blob/master/Include/10.0.10586.0/winrt/windows.data.pdf.idl

It seems like I am getting back the pdfDocument but ComObjQuery isn't returning the IAsyncInfo it appears.

Code: Select all

file := A_MyDocuments . "\basement-stuff\mmi-door-interior-price-pages.pdf"
GUID := BufferAlloc(16)
DllCall("ole32\CLSIDFromString", "wstr", IID_IRandomAccessStream := "{905A0FE1-BC53-11DF-8C49-001E4FC686DA}", "ptr", GUID)

DllCall("ShCore\CreateRandomAccessStreamOnFile", "wstr", file, "uint", Read := 0, "ptr", GUID, "ptr*", IRandomAccessStream := 0)
pdfDoc := CreateClass("Windows.Data.Pdf.PdfDocument", iPdfDocumentStatics := "{433A0B5F-C007-4788-90F2-08143D922599}")
ComCall(8, pdfDoc, "Ptr", IRandomAccessStream, "Ptr*", pdfDocument := 0)
WaitForAsync(pdfDocument)
pdfPage := ComCall(6, pdfDocument, "UInt", 0, "UInt")

DllCall("Ole32\CreateStreamOnHGlobal", "Ptr", 0, "UInt", true, "PtrP", pIStream := 0, "UInt")
riid := CLSIDFromString(IID_IRandomAccessStream)
DllCall("ShCore\CreateRandomAccessStreamOverStream", "Ptr", pIStream, "UInt", BSOS_DEFAULT := 0, "Ptr", riid, "Ptr*", pIRandomAccessStream := 0, "UInt")

iAsyncPageRender := ComCall(0, pdfPage, "Ptr", pIRandomAccessStream)

WaitForAsync(iAsyncPageRender)

DllCall( "LoadLibrary", "Str", "gdiplus" )
si := BufferAlloc(16, 0), Numput("Int", 1, si)
DllCall( "gdiplus\GdiplusStartup", "UPtr*", pToken := 0, "Ptr", si, "UInt",0 )

DllCall( "gdiplus\GdipCreateBitmapFromStream", "Ptr", pIRandomAccessStream, "Ptr*", pBitmap := 0 )

DllCall("gdiplus\GdipCreateHBITMAPFromBitmap", "ptr", pBitmap, "ptr*", hBitmap := 0, "uint", 0xffffffff)

main := gui.new()
main.add("Pic", "HBITMAP:" hBitmap)

main.show()

CLSIDFromString(IID) {
   CLSID := BufferAlloc(16, 0)
   if res := DllCall("ole32\CLSIDFromString", "WStr", IID, "Ptr", CLSID, "UInt")
      throw Exception("CLSIDFromString failed. Error: " . Format("{:#x}", res))
   Return CLSID
}

CreateClass(string, interface) {
   hString := CreateHString(string)
   GUID := BufferAlloc(16)
   DllCall("ole32\CLSIDFromString", "wstr", interface, "ptr", GUID)
   result := DllCall("Combase.dll\RoGetActivationFactory", "ptr", hString, "ptr", GUID, "ptr*", pClass := 0)
   if (result != 0) {
      if (result = 0x80004002)
         msgbox("No such interface supported")
      else if (result = 0x80040154)
         msgbox("Class not registered")
      else
         msgbox("error: " . result)
      ExitApp()
   }
   DeleteHString(hString)
   return pClass
}

CreateHString(string) {
    DllCall("Combase.dll\WindowsCreateString", "wstr", string, "uint", StrLen(string), "ptr*", hString := 0)
    return hString
}

DeleteHString(hString) {
   DllCall("Combase.dll\WindowsDeleteString", "ptr", hString)
}

WaitForAsync(ByRef Object) {
    AsyncInfo := ComObjQuery(Object, IAsyncInfo := "{00000036-0000-0000-C000-000000000046}")
    MsgBox(AsyncInfo)
    loop {
        ComCall(7, AsyncInfo, "UInt*", status := 0)
        ; DllCall(NumGet(NumGet(AsyncInfo+0)+7*A_PtrSize), "ptr", AsyncInfo, "uint*", status)   ; IAsyncInfo.Status
        if (status != 0) {
            if (status != 1) {
                ComCall(8, AsyncInfo, "UInt*", ErrorCode := 0)
                ; DllCall(NumGet(NumGet(AsyncInfo+0)+8*A_PtrSize), "ptr", AsyncInfo, "uint*", ErrorCode)   ; IAsyncInfo.ErrorCode
                msgbox("AsyncInfo status error: " . ErrorCode)
                ExitApp()
            }
            ObjRelease(AsyncInfo)
            break
        }
        sleep(10)
    }
    ComCall(8, Object, "Ptr*", ObjectResult := 0)
    ; DllCall(NumGet(NumGet(Object+0)+8*A_PtrSize), "ptr", Object, "ptr*", ObjectResult)   ; GetResults
    ObjRelease(Object)
    Object := ObjectResult
}
User avatar
kczx3
Posts: 1250
Joined: 06 Oct 2015, 21:39

Re: How to view PDF with Windows API? (PDF -> bitmap)

05 Dec 2020, 21:16

Finally got it... This stuff is not my specialty :)

Code: Select all

file := A_MyDocuments . "\basement-stuff\mmi-door-interior-price-pages.pdf"
GUID := BufferAlloc(16)
DllCall("ole32\CLSIDFromString", "wstr", IID_IRandomAccessStream := "{905A0FE1-BC53-11DF-8C49-001E4FC686DA}", "ptr", GUID)

DllCall("ShCore\CreateRandomAccessStreamOnFile", "wstr", file, "uint", Read := 0, "ptr", GUID, "ptr*", IRandomAccessStream := 0)
pdfDoc := CreateClass("Windows.Data.Pdf.PdfDocument", iPdfDocumentStatics := "{433A0B5F-C007-4788-90F2-08143D922599}")
ComCall(8, pdfDoc, "Ptr", IRandomAccessStream, "Ptr*", pdfDocument := 0)
pdfDocument := WaitForAsync(pdfDocument)
ComCall(6, pdfDocument, "UInt", 0, "Ptr*", pdfPage := 0)

DllCall("Ole32\CreateStreamOnHGlobal", "Ptr", 0, "UInt", 1, "Ptr*", pIStream := 0)
; MsgBox(pIStream)
DllCall("ShCore\CreateRandomAccessStreamOverStream", "Ptr", pIStream, "UInt", BSOS_DEFAULT := 0, "Ptr", GUID, "Ptr*", pIRandomAccessStream := 0)

ComCall(6, pdfPage, "Ptr", pIRandomAccessStream, "Ptr*", iAsyncPageRender := 0)

WaitForAsync(iAsyncPageRender)
; MsgBox(pIRandomAccessStream)

DllCall( "LoadLibrary", "Str", "gdiplus" )
si := BufferAlloc(A_PtrSize == 8 ? 24 : 16, 0), Numput("Int", 1, si)
DllCall( "gdiplus\GdiplusStartup", "Ptr*", pToken := 0, "Ptr", si, "Ptr", 0 )
; MsgBox(pToken)

DllCall( "gdiplus\GdipCreateBitmapFromStream", "Ptr", pIStream, "Ptr*", pBitmap := 0 )
; MsgBox(pBitmap)
DllCall("gdiplus\GdipCreateHBITMAPFromBitmap", "ptr", pBitmap, "ptr*", hBitmap := 0, "uint", 0xffffffff)
; MsgBox(hBitmap)
main := gui.new()
main.add("Pic", "h600 w-1", "HBITMAP:" hBitmap)

main.show()

CLSIDFromString(IID) {
   CLSID := BufferAlloc(16, 0)
   if res := DllCall("ole32\CLSIDFromString", "WStr", IID, "Ptr", CLSID, "UInt")
      throw Exception("CLSIDFromString failed. Error: " . Format("{:#x}", res))
   Return CLSID
}

CreateClass(string, interface) {
   hString := CreateHString(string)
   GUID := BufferAlloc(16)
   DllCall("ole32\CLSIDFromString", "wstr", interface, "ptr", GUID)
   result := DllCall("Combase.dll\RoGetActivationFactory", "ptr", hString, "ptr", GUID, "ptr*", pClass := 0)
   if (result != 0) {
      if (result = 0x80004002)
         msgbox("No such interface supported")
      else if (result = 0x80040154)
         msgbox("Class not registered")
      else
         msgbox("error: " . result)
      ExitApp()
   }
   DeleteHString(hString)
   return pClass
}

CreateHString(string) {
    DllCall("Combase.dll\WindowsCreateString", "wstr", string, "uint", StrLen(string), "ptr*", hString := 0)
    return hString
}

DeleteHString(hString) {
   DllCall("Combase.dll\WindowsDeleteString", "ptr", hString)
}

WaitForAsync(Object) {
    AsyncInfo := ComObjQuery(Object, IAsyncInfo := "{00000036-0000-0000-C000-000000000046}")
    ; MsgBox(AsyncInfo)
    loop {
        ComCall(7, AsyncInfo, "UInt*", status := 0)
        ; DllCall(NumGet(NumGet(AsyncInfo+0)+7*A_PtrSize), "ptr", AsyncInfo, "uint*", status)   ; IAsyncInfo.Status
        if (status != 0) {
            if (status != 1) {
                ComCall(8, AsyncInfo, "UInt*", ErrorCode := 0)
                ; DllCall(NumGet(NumGet(AsyncInfo+0)+8*A_PtrSize), "ptr", AsyncInfo, "uint*", ErrorCode)   ; IAsyncInfo.ErrorCode
                msgbox("AsyncInfo status error: " . ErrorCode)
                ExitApp()
            }
            ; ObjRelease(AsyncInfo.ptr)
            break
        }
        sleep(10)
    }
    ComCall(8, Object, "Ptr*", ObjectResult := 0)
    ; DllCall(NumGet(NumGet(Object+0)+8*A_PtrSize), "ptr", Object, "ptr*", ObjectResult)   ; GetResults
    Object := AsyncInfo := ""
    return ObjectResult
}
User avatar
kczx3
Posts: 1250
Joined: 06 Oct 2015, 21:39

Re: How to view PDF with Windows API? (PDF -> bitmap)

06 Dec 2020, 14:29

Doesn't seem like the memory is being released. Is there a Close/Dispose call we're missing? I see that pdfPage has a Close/Dispose but querying for IClosable and calling Close doesn't seem to help.
malcev
Posts: 855
Joined: 12 Aug 2014, 12:37

Re: How to view PDF with Windows API? (PDF -> bitmap)

06 Dec 2020, 15:14

I didnot find any variant, only like this

Code: Select all

DllCall("psapi.dll\EmptyWorkingSet", "ptr", -1)
User avatar
kczx3
Posts: 1250
Joined: 06 Oct 2015, 21:39

Re: How to view PDF with Windows API? (PDF -> bitmap)

06 Dec 2020, 17:31

Interesting. That seems a bit extreme.

Return to “Ask For Help”

Who is online

Users browsing this forum: AHKStudent, asundrus, Bing [Bot], Google [Bot], Ioniq, superpeter, tomjtoth and 41 guests