• Welcome to Powerbasic Museum 2020-B.
 

Using GDI+ to add image to a PDF document

Started by Rick Kelly, May 04, 2014, 12:16:17 PM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

Rick Kelly

I have my own class that creates PDF files and want to add image support. I can use PB binary reads and read the entire file and parse through it and was interested in getting away from that as well as being able to use the GDI+ functions to load an image of various types, interrogate the image for something I can understand and then inserting the image properly into the PDF document stream.

Is this something the GDI+ support include files can handle?

Rick Kelly

Edwin Knoppert

Not sure what exact filetypes pdf handles but should at least be jpg.
I forgot but when text's are inserted binary they usually inserted as zlib compressed string buffer (stream) which is easy to do.
Maybe images are handled similarly.

Would you like to share your class to generate the pdf as it is right now, just for my own use?
I know the basics how pdf works but it is some work to prepare code.

Rick Kelly

Quote from: Edwin Knoppert on May 05, 2014, 11:37:21 AM
Not sure what exact filetypes pdf handles but should at least be jpg.
I forgot but when text's are inserted binary they usually inserted as zlib compressed string buffer (stream) which is easy to do.
Maybe images are handled similarly.

Would you like to share your class to generate the pdf as it is right now, just for my own use?
I know the basics how pdf works but it is some work to prepare code.

I'm in the cleanup/polishing up phase. My interpretation is mainly for what I need - things such as text, lines and images so doesn't have all the bells and whistles.

Here is how I am capturing image data to use later on when generating the PDF file stream. PB10 and, of course, Jose's includes ;D


TYPE ImageAttributes
    HorizontalResolution    AS SINGLE
    VerticalResolution      AS SINGLE
    ImagePixelHeight        AS LONG
    ImagePixelWidth         AS LONG
    ImageHeight             AS DOUBLE
    ImageWidth              AS DOUBLE
    ImageSize               AS LONG
    ImageDescriptorObject   AS LONG
END TYPE

%PDF_MEASUREMENT                    = 72

INSTANCE ImageDescriptor            AS IPOWERCOLLECTION
INSTANCE ImageStream                AS IPOWERCOLLECTION 

    LET ImageDescriptor = CLASS "PowerCollection"
    LET ImageStream = CLASS "PowerCollection"




------------------------------

   METHOD AddImage(BYVAL sImageFile AS STRING) AS STRING

' Add a jpeg image
' If return string is blank, an error occurred

   LOCAL sImageID           AS WSTRING
   LOCAL hStatus            AS LONG
   LOCAL nPixelFormat       AS LONG
   LOCAL hToken             AS DWORD
   LOCAL StartupInput       AS GdiplusStartupInput
   LOCAL pImage             AS DWORD
   LOCAL szImageFile        AS WSTRINGZ * %MAX_PATH
   LOCAL ImageGUID          AS GUID
   LOCAL uImageAttributes   AS ImageAttributes
   LOCAL nFile              AS INTEGER
   LOCAL nFileSize          AS LONG
   LOCAL sFileContents      AS STRING
   LOCAL nTotalImages       AS LONG
   LOCAL nWidth             AS LONG
   LOCAL nHeight            AS LONG

    sImageID = ""
    szImageFile = sImageFile

' Startup GDI+

    StartupInput.GdiplusVersion = 1
    hStatus = GdiplusStartup(hToken, StartupInput, BYVAL %NULL)

    IF hStatus <> %S_OK THEN

       MSGBOX "Unable to start graphics subsystem."
       METHOD = ""
       EXIT METHOD

    END IF

' Check if image file is valid

    IF NOT ISFILE(szImageFile) THEN

       MSGBOX szImageFile + " not found."
       METHOD = ""
       EXIT METHOD

    END IF

' Load Image file

    hStatus = GdipLoadImageFromFile(szImageFile, pImage)

    IF hStatus <> %S_OK THEN

       MSGBOX szImageFile + " is not a recognized image file."
       GOTO AddImageExit

    END IF

' Check if image format if JPEG

    hStatus = GdipGetImageRawFormat(pImage,ImageGUID)

    IF ImageGUID <> $ImageFormatJPEG THEN

       MSGBOX szImageFile + " is not a JPEG image file."
       GdipDisposeImage pImage
       GOTO AddImageExit

    END IF

' Check color depth

    hStatus = GdipGetImagePixelFormat(pImage,nPixelFormat)

    IF nPixelFormat <> %PixelFormat24bppRGB THEN

       MSGBOX "JPEG color depth is not supported."
       GdipDisposeImage pImage
       GOTO AddImageExit

    END IF

    GdipGetImageHorizontalResolution(pImage,uImageAttributes.HorizontalResolution)
    GdipGetImageVerticalResolution(pImage,uImageAttributes.VerticalResolution)
    GdipGetImageWidth(pImage,uImageAttributes.ImagePixelWidth)
    GdipGetImageHeight(pImage,uImageAttributes.ImagePixelHeight)
    uImageAttributes.ImageWidth = uImageAttributes.ImagePixelWidth * %PDF_MEASUREMENT / uImageAttributes.HorizontalResolution
    uImageAttributes.ImageHeight = uImageAttributes.ImagePixelHeight * %PDF_MEASUREMENT / uImageAttributes.VerticalResolution

' Release GDI+ Image

    GdipDisposeImage pImage

' Get file size and raw stream

    nFile = FREEFILE
    OPEN szImageFile FOR BINARY AS nFile

    uImageAttributes.ImageSize = LOF(nFile)
    GET$ nFile, uImageAttributes.ImageSize, sFileContents

    CLOSE nFile

' Save Image for reference

    nTotalImages = ImageDescriptor.Count
    INCR nTotalImages

    sImageID = "I" + FORMAT$(nTotalImages)

    ImageDescriptor.Add(sImageID,uImageAttributes AS STRING)
    ImageStream.Add(sImageID,sFileContents)

    AddImageExit:

' Shutdown GDI+

    GdiplusShutdown hToken

    METHOD = sImageID

   END METHOD


The image object is then created and looks like this:

6 0 obj
<<
/Type /XObject
/Subtype /Image
/Filter /DCTDecode
/ColorSpace /DeviceRGB
/Width 133
/Height 129
/BitsPerComponent 8
/Length 9782
>>
stream
????? Raw JPEG stream
endstream
endobj

All my images are XOBJECT items and the PDF Procedure set then looks like (includes any referenced fonts as well):

8 0 obj
<< /ProcSet [ /PDF /Text /ImageC]
/XObject <<
/I1 6 0 R
>>
/Font <<
/F1 5 0 R
>>
>>
endobj


I'll post at attachment with the class when I'm done testing and am ready to begin using it myself.

Rick Kelly

Rick Kelly

I decided if you wanted to take a look at a "work in progress", I have no real issue with that. There is still a lot of work I've got to do. There are two includes and I combined them into one file.

Rick Kelly

Edwin Knoppert

stream
?? Raw JPEG stream
endstream

If so you can try the zlib string (compress)ion method (and thus not the other, zip file creation, functions).
Imo the output of that is placed 1:1 into the pdf.
I don't know and i am not going to investigate further :)

I have messed with text streams this to remove the trail message from a pdf :)

Edwin Knoppert

Cross post :)
That's a lot of work!
As i said, i am not pursuing it at this time, sorry.

Edwin Knoppert

#6
Look for zlib114 dll and doc, this will prepare a stream buffer for you.

In my PwrDev used as:
Declare Function VD_ZLIB__compress Lib "ZLIB.DLL" Alias "compress"( ByRef dest As Any, ByRef destLen As Any, ByRef src As Any, ByVal srcLen As Long ) As Long

Function VD_ZLIB_Buffer_CompressEx( _
ByVal pDestData       As Dword _
, ByVal nDestDataLen    As Dword _
, ByVal pSrcData        As Dword _
, ByVal nSrcDataLen     As Dword _
, ByRef nErrorCode      As Long _
) As Long
If VD_ZLIB_IsLoaded() = 0 Then Exit Function

nErrorCode = VD_ZLIB__compress( _
ByVal pDestData _
, nDestDataLen _
, ByVal pSrcData _
, nSrcDataLen _
)

If nErrorCode = %Z_OK Then Function = nDestDataLen
End Function


Rick Kelly

#8
Quote from: Edwin Knoppert on May 06, 2014, 08:41:05 AM
stream
?? Raw JPEG stream
endstream

Yes, you just insert the raw JPEG into the stream.... ;)

Here is a sample pdf. I have page title centered, two numeric strings that I used right alignment on and underlined as well. I drew a rectangle and filled it and then for kicks, I added a circle. Finally, I added an image (that shiny blue sphere). Open the PDF up in notepad and you can see all the structure.

Rick

Edwin Knoppert

Are you saying that you have it working already?
Curious, in notepad i see JFIF for tag, did you insert the jpg file 1:1 or is it compressed to a string buffer first?
Maybe the compression sucks and JFIF is still shown haha.

Rick Kelly

#10
Quote from: Edwin Knoppert on May 06, 2014, 09:16:59 AM
Are you saying that you have it working already?
Curious, in notepad i see JFIF for tag, did you insert the jpg file 1:1 or is it compressed to a string buffer first?
Maybe the compression sucks and JFIF is still shown haha.

The jpg file is inserted 1:1 just as I read it from disk without any changes.

I'm adding to the class incrementally and testing as I go so I've had a basic functional PDF structure from day 1 being generated and I've been working on this on and off for a few weeks now. What got me started was just how expensive PDF libraries were when I really didn't need all the fancy bells and whistles. What I'm giving up is because, for now, I'm not going to embed fonts, I have to stick to a few widely supported ones. You can see I have Arial and Times New Roman in there and I'll add Verdana and TreBuchet. You're free to add what fonts you want (I use Adobe AFM files, version 4.1 to build my arrays) and even dive in the nasty world of embedding fonts. I have a list of all the api's involved somewhere and it's not very well documented. It's likely, I'll add kerning to both the string size calculations and the text strings written. I'm a bit haphazard and eventually I'll have to clean it all up and organize things. What I want to do is to have all my low level PDF primitives methods in there and then wrap those with a method(s) that make using the class easier.

Rick

Edwin Knoppert

At this time i will not participate (and i don't see a time for that soon unf.) but i like you shared this code.
PDF is not incredible difficult for the more basic parts but it is some work.
Fonts are an issue yes, you may want to add several free fonts.

Hope you get what you asked for at this time :)
You may still consider to prepare compressed streams of text parts, it's easy to do.

:)

Rick Kelly

Quote from: Edwin Knoppert on May 06, 2014, 11:53:51 AM
You may still consider to prepare compressed streams of text parts, it's easy to do.

:)

As one of the last things I add, it will be to compress all page objects.

I still have the following "features" to add before I embed this class into my current app:

1. Column definitions - break a page up into x number of columns each with it's own attributes
2. Pass in a bunch of rows matching up to the columns and have them put on the page with one call
3. Page templates - set up instructions for all the stuff on a page that stays the same...a lot of reports are repeaters
4. Text kerning - heck, I have the KP pairs from the AFM files and might as well see how it works out. Have to check out if Adobe kerns or not
5. Multiline boxes where the class will put as many words on each line as it can and then wrap to the next line and stay in the defined box
6. Some mechanism for keeping track of when a page break is necessary under certain circumstances and do it automatically with some options to repeat section headers that are defined as part of the column(s) definitions.
7. Maybe some of the basic shading and pattern fills.
8. And, in all my "spare" time, read up on embedding fonts and study those API's and data structures.

Looks like a few more weeks already... ::)

Rick

Rick Kelly

I have my PDF class where I need it to begin using in my latest application.

You can get all the source, including a FF demo project at:


http://www.planetsquires.com/protect/forum/index.php?topic=3547.0


Rick Kelly

Edwin Knoppert

I don't have access to those forums but anyway.., i like to have the code but understand, at this time i'll not be able to help you.
So if you find me a taker then don't publish more.

Not sure if you where doing this for printing/preview matters.., i wrote a small topic on that.
Maybe an idea for some people..?

http://www.powerbasic.com/support/pbforums/showthread.php?t=54381