A PDF from a Rest API (1 Viewer)

gemma-the-husky

Super Moderator
Staff member
Local time
Today, 08:44
Joined
Sep 12, 2006
Messages
15,614
I am stuck on this point.

Supposedly a GETPDF retrieves a PDF. What it actually retrieves is a string, and I am struggling to find the right way to process the string to then be able to save it as a viewable PDF. I tried changing it to an array of bytes, and tried to encode/decode/whatever to base64 strings. I think I am close. but not quite getting it. Can anybody give me any pointers, please?
 

Ranman256

Well-known member
Local time
Today, 04:44
Joined
Apr 9, 2015
Messages
4,339
if you just want to open it, add this code to a module, then
OpenNativeApp txtBox
where txtBox has the file path.

Note this code opens ANYTHING. pdf opens in acrobat, .xlsx will open in excel, etc


Code:
#If Win64 Then      'Public Dclare PtrSafe Function
Private Declare PtrSafe Function ShellExecute Lib "shell32.dll" Alias "ShellExecuteA" (ByVal hwnd As Long, ByVal lpszOp As String, ByVal lpszFile As String, ByVal lpszParams As String, ByVal lpszDir As String, ByVal FsShowCmd As Long) As Long
Private Declare PtrSafe Function GetDesktopWindow Lib "user32" () As Long
#else
Private Declare Function ShellExecute Lib "shell32.dll" Alias "ShellExecuteA" (ByVal hwnd As Long, ByVal lpszOp As String, ByVal lpszFile As String, ByVal lpszParams As String, ByVal lpszDir As String, ByVal FsShowCmd As Long) As Long
Private Declare Function GetDesktopWindow Lib "user32" () As Long
#End If
Const SW_SHOWNORMAL = 1
Const SE_ERR_FNF = 2&
Const SE_ERR_PNF = 3&
Const SE_ERR_ACCESSDENIED = 5&
Const SE_ERR_OOM = 8&
Const SE_ERR_DLLNOTFOUND = 32&
Const SE_ERR_SHARE = 26&
Const SE_ERR_ASSOCINCOMPLETE = 27&
Const SE_ERR_DDETIMEOUT = 28&
Const SE_ERR_DDEFAIL = 29&
Const SE_ERR_DDEBUSY = 30&
Const SE_ERR_NOASSOC = 31&
Const ERROR_BAD_FORMAT = 11&

Public Sub OpenNativeApp(ByVal psDocName As String)
Dim r As Long, msg As String
r = StartDoc(psDocName)
If r <= 32 Then
'There was an error
Select Case r
Case SE_ERR_FNF
msg = "File not found"
Case SE_ERR_PNF
msg = "Path not found"
Case SE_ERR_ACCESSDENIED
msg = "Access denied"
Case SE_ERR_OOM
msg = "Out of memory"
Case SE_ERR_DLLNOTFOUND
msg = "DLL not found"
Case SE_ERR_SHARE
msg = "A sharing violation occurred"
Case SE_ERR_ASSOCINCOMPLETE
msg = "Incomplete or invalid file association"
Case SE_ERR_DDETIMEOUT
msg = "DDE Time out"
Case SE_ERR_DDEFAIL
msg = "DDE transaction failed"
Case SE_ERR_DDEBUSY
msg = "DDE busy"
Case SE_ERR_NOASSOC
msg = "No association for file extension"
Case ERROR_BAD_FORMAT
msg = "Invalid EXE file or error in EXE image"
Case Else
msg = "Unknown error"
End Select
' MsgBox msg
End If
End Sub
 

theDBguy

I’m here to help
Staff member
Local time
Today, 01:44
Joined
Oct 29, 2018
Messages
21,358
See if this article helps give you any idea.

 

gemma-the-husky

Super Moderator
Staff member
Local time
Today, 08:44
Joined
Sep 12, 2006
Messages
15,614
See if this article helps give you any idea.



Thanks for this, but no joy

I tried
FileByte = .responseBody AND
FileByte = .responsetext

Both give me a pdffile of 366Kb, but it won't open.

I use a Rest API that says I get this response.

200OKA PDF filestring

If I try to parse it with a JSON converter to see if there are other elements, that fails, and I think I just have the pdf. The string I recover is unicode, and I think I need to pre-process it somehow.

eg The correct pdf should start with this (there is a chr(10) after the W).

0.57 w
%PDF-1.7


The pdf I get from my string has a null char after every char at the start so it's sort of looks like this in a hex editor
0 . 5 7 2 w
% P D F etc.


However the file size is 244Kb, so it's not a matter of getting rid of (half) just the zero bytes (although it might be).
That's why I was trying to pass it through some CodeBase64 process, but I am not really sure what I need to do.

Any more thoughts?
 

theDBguy

I’m here to help
Staff member
Local time
Today, 01:44
Joined
Oct 29, 2018
Messages
21,358
Thanks for this, but no joy

I tried
FileByte = .responseBody AND
FileByte = .responsetext

Both give me a pdffile of 366Kb, but it won't open.

I use a Rest API that says I get this response.

200OKA PDF filestring

If I try to parse it with a JSON converter to see if there are other elements, that fails, and I think I just have the pdf. The string I recover is unicode, and I think I need to pre-process it somehow.

eg The correct pdf should start with this (there is a chr(10) after the W).

0.57 w
%PDF-1.7


The pdf I get from my string has a null char after every char at the start so it's sort of looks like this in a hex editor
0 . 5 7 2 w
% P D F etc.


However the file size is 244Kb, so it's not a matter of getting rid of (half) just the zero bytes (although it might be).
That's why I was trying to pass it through some CodeBase64 process, but I am not really sure what I need to do.

Any more thoughts?
If FileByte = .responseBody didn't work, then my guess is you're not getting binary data back from the API. Check the API documentation to see what sort of data it's supposed to return.
 

gemma-the-husky

Super Moderator
Staff member
Local time
Today, 08:44
Joined
Sep 12, 2006
Messages
15,614
it says it's a string being returned from the Rest API.

I have spent a long time on this, partly for my own amusement. I saw many other people with similar issues, with lots of different suggestions, but none working for them or me.

I used Postman to make the same request and that instantly pulled up the pdf, but the data looks different so there must be some way of processing it. I get a string, but the the header shows technical stuff and then there's a data section indicated by stream and endstream

edit. I now belief that the string that vba collects is a unicode string, and that the unicode string is different to the real string, and the differences prevent it working correctly as a pdf. Is that possible?

Is there any way to specify a different string type when you dim the string in vba?
 
Last edited:

gemma-the-husky

Super Moderator
Staff member
Local time
Today, 08:44
Joined
Sep 12, 2006
Messages
15,614
Further explanation.
What I mean is, I am not downloading a file.

The REST API sends me a string that should represent the file contents.
I think I am meant to assign the string to an array of byte, and then write (PUT) the array to the file.

dim a() as byte
a = MyString
...
...
put #fno,,a


but this doesn't produce a loadable pdf file.

My conjecture is that Access/VBA is treating the string as a unicode string, and thereby changes the contents, so the PDF is not readable.

The actual pdf for this document is 288Kb, but the string I download is about 175K characters, and when loaded into a byte array, the array is therefore 350K in size and saves to a 350Kb file size.

Hence, is there anything I can do to get this working?

Can I process the .responsetext without assigning it to a string, so that it stays as the original size, or is it actually already changed by the time Access vba can see it.?
 
Last edited:

sonic8

AWF VIP
Local time
Today, 09:44
Joined
Oct 27, 2015
Messages
998
The REST API sends me a string that should represent the file contents.
I think I am meant to assign the string to an array of byte, and then write (PUT) the array to the file.

dim a() as byte
a = MyString
Where is MyString coming from? The REST API does not send you a VBA String. Use the byte array in the code location where you initially get the data from the REST API.
Th proper way to convert a Unicode string to a Non-Unicode byte array is:
a = StrConv(MyString, vbFromUnicode)

The data you get is probably Base64 encoded. You need to decode that before storing it to a file.
 

gemma-the-husky

Super Moderator
Staff member
Local time
Today, 08:44
Joined
Sep 12, 2006
Messages
15,614
Thanks for looking.

Effectively I do this, with a responsecode of 200, for this process.

Set objRequest = CreateObject("MSXML2.XMLHTTP")
Open "GET", strUrl, blnAsync

'then

with objrequest
msgbox .responsetext


for most requests .responsetext is a JSON string which is fine.
for this request it's not JSON, it's a string that is supposed to represent the pdf.
In post 4 I showed the online spec, which says it is a string. resposnebody is just the same.


the string actually contains sections like these.
text
data - between <stream> <endstream> tags
text
data - between <stream> <endstream> tags
text
etc


The sample pdf looks different to the one I produce, as in this download all of the text characters have an extra chr(0), which I stripped out with strconv()

I've tried manipulating the whole thing with strconv() and with base64 conversions. I haven't tried manipulating just the data streams in the above, but I can't believe that POSTMAN does all this preprocessing on a file.

So I am constrained by what the REST API is sending, but I wondered whether I can change the objrequest to get a different string format, or process the string differently in Access vba, or even whether objrequest has anything else I can use.

Maybe when you use c for instance, you just don't get the string automatically treated as a unicode string, and there's no issue.
 

NigelShaw

Registered User.
Local time
Today, 08:44
Joined
Jan 11, 2008
Messages
1,573
Hey

been out of touch for a looooong time.. This popped up on my email this morning and hopefully i can help you answer, VBA ability permitting.

If youre getting the PDF as string from a Restful service then its most likely going to be stored as binary in a Byte() array. If thats the case, you will need to stream read it to a memory object that use something to view that object.

I have 2 examples below, one is my where i am streaming a document out of a zip file into memory and the other is a straight forward PDF read from a database (database code removed). It may not give you a direct answer but it could offer suggestions and you a little :)

Example 1 - Read into Memory from XML file
C#:
Dim StepElement As XElement = doc.XPathSelectElement("//STEPFile")
Dim s As String = StepElement.Value

Dim bytes As Byte() = Convert.FromBase64String(s)
Using ms As New MemoryStream(bytes)
    Using sr As New StreamReader(ms)
        Dim result = sr.ReadToEnd()
        'do something here to add the result to a viewer'
    End Using
StepElement = Nothing

'If you want to save to file
Dim Path As String = "C\MyPath"
IO.File.WriteAllText(Path, result)
End Using

Example 2 - Read a PDF from a dataset
C#:
 'Open and read file using SqlFileStream Class
Dim sqlFileStream As New SqlTypes.SqlFileStream(filePath, txContext, FileAccess.Read)
Dim buffer As Byte() = New Byte(sqlFileStream.Length) {}
sqlFileStream.Read(buffer, 0, buffer.Length)

'Bind the image data to an image control
Dim ms As MemoryStream = New MemoryStream(buffer)
Dim bmp As Bitmap = New Bitmap(ms)
ItemImage.Image = bmp

'Cleanup
sqlFileStream.Close()
 
Last edited:

Users who are viewing this thread

Top Bottom