Bo Durban - MoxieData Blog - Insert Images into an RTF control using GDIPlusX

Original post by Bo Durban, replicated here because the original blog http://blog.moxiedata.com is not responding.
Copied from:

Insert Images into an RTF control using GDIPlusX

The RichText control that ships with VFP is very useful for providing a formatted text edit box. While this control does support the displaying of images, there is nothing built in to the control that allows you to insert an image. I have included a function below that will allow you insert an image into the RichText control at the current cursor position.
Just specify the object reference to the RTF control and the fullpath of the image you want to insert. The image can be in any format supported by the GDIPlus library (BMP, JPEG, GIF, TIFF, EMF, PNG, etc...). You can also optionally specify the Width and Height you would like the image to be rendered.
This function requires the GDIPlusX library which can be downloaded from the VFPX project site: https://vfpx.codeplex.com/wikipage?title=GDIPlusX&referringTitle=Home.

* Function: InsertRTFImage
* Author: Bo Durban
* Inserts an image into an RTF control at the current cusor position
* Parameters:
*   toRTF - specifies an object reference to the RTF control
*   tcImage - secifies the image to insert into the RTF
*   tnWidth - Optional - Specifies the width to use for the image
*   tnHeight - Optional - Specifies the height to use for the image
FUNCTION InsertRTFImage(toRTF, tcImage, tnWidth, tnHeight)
   LOCAL lcRTF, lcPict, lqData
   LOCAL loImg AS xfcImage
   LOCAL loGfx AS xfcGraphics
   LOCAL loEMF AS xfcMetaFile
   LOCAL lhDC, lhEMF, lnWMFLen
   DECLARE Long ReleaseDC IN WIN32API LongLong
   DECLARE Long DeleteEnhMetaFile IN WIN32API Long
         Long hemf, Long cbData16, String @pData16, ;
         Integer iMapMode, Integer eFlags
   ** Make sure we have initialized the GDIPlusX library
   DO System.App
   lcPict = ""
   WITH _SCREEN.System.Drawing
      loImg = .Image.FromFile(tcImage)
      IF loImg.GetLastStatus() <> 0
         ERROR "Could not open file: "+tcImage
         ** If we didn't specify a width, use the image's width and height
         IF EMPTY(tnWidth)
            tnWidth = loImg.Width
            tnHeight = loImg.Height
         ** Get the default screen HDC as a reference
         lhDC = GetDC(0)
         ** Create a new metafile
         loEMF = .Imaging.MetaFile.New(lhDC, ;
            .Rectangle.New(0,0,tnWidth,tnHeight), ;
            .Imaging.MetafileFrameUnit.Pixel, ;
         ** We are done with the HDC reference, release it
         ReleaseDC(lhDC, 0)
         ** Get a graphics context for the metafile so we can draw to it
         loGfx = .Graphics.FromImage(loEMF)
         loGFX.SmoothingMode = .Drawing2D.SmoothingMode.HighQuality
         loGFX.InterpolationMode = .Drawing2D.InterpolationMode.HighQualityBicubic
         ** Draw the image
         loGfx.DrawImage(loImg, 0, 0, tnWidth, tnHeight)
         loGfx = NULL
         ** Convert the Metafile to a "Windows Metafile".
         ** This is the best format for the RTF control.
         lhEMF = loEMF.GetHenhmetafile()
         lnWMFLen = GdipEmfToWmfBits(lhEMF, 0, NULL, 2, 0)
         lqData = REPLICATE(0h00,lnWMFLen)
         GdipEmfToWmfBits(lhEMF, lnWMFLen, @lqData, 2, 0)
         loImg = NULL
         ** This is the RTF tags for the image
         lcPict = [{\pict\wmetafile8]+;
                  STRCONV(lqData, 15)+;
   ** Stuff the image into the RTF control
   toRTF.SelRTF = [{\rtf1\ansi\ansicpg1252\deff0\deflang1033\uc1 ]+lcPict+[}]


Bo Durban - Moxiedata Blog - Blending Two Images Using GDIPlusX

Original post by Bo Durban, replicated here because the original blog http://blog.moxiedata.com is not responding.
Copied from:

Blending Two Images Using GDIPlusX

After seeing a post on the Micrsoft forums. I set out to try and solve how to blend two images using GDI+. I could find little information on the web on how to solve this particular problem, so I came up with my own technique (with some excellent help from Craig Boyd).
Here is a sample blended image and the VFP / GDIPlusX code that I used to create it.

Paste this code into a PRG and run it. This code requires the GDIPlusX library from VFPX.
Local lcPict1, lcPict2

m.lcPict1 = Getpict()
If File(m.lcPict1)
   m.lcPict2 = Getpict()
   If File(m.lcPict2)
      ImageAlphaBlend(m.lcPict1, m.lcPict2)

Function ImageAlphaBlend(tcPictLeft, tcPictRight)
* Author: Bo Durban *

   Local loColorMatrix As xfcCOlorMatrix
   Local loImageLeft As xfcImage
   Local loImageRight As xfcImage
   Local loBitmapPart As xfcBitmap
   Local loBitmapLeft As xfcBitmap
   Local laImageAttribs[1]
   Local loGfxLeft As xfcGraphics
   Local loGfxPart As xfcGraphics
   Local loGfxScreen As xfcGraphics
   Local lnAlphaSteps, lnBlendPercentage, lnWidthOpaque, lnStep, lnHeightImage, lnLeftStart

** Sets the number of Alpha blend steps used to render the blend.
** The max is 256. lower steps equals lower quality, but higher speed
   m.lnAlphaSteps = 128
** Sets the percentage of the image that is blended
   m.lnPercentageBlend = .75

   Dimension laImageAttribs[m.lnAlphaSteps]

** Requires the GDIPlusX library (version 1.20 beta)
** http://www.codeplex.com/VFPX/Release/ProjectReleases.aspx?ReleaseId=15083
   Do (Locfile("SYSTEM.APP"))
   With _Screen.System.Drawing

      m.loImageRight = .Image.FromFile(m.tcPictLeft)
      m.loImageLeft = .Image.FromFile(m.tcPictRight)

** Force the left image to be the same size as the right image
      m.loBitmapLeft = .Bitmap.New(m.loImageRight.Width, m.loImageRight.Height)

      m.lnWidthOpaque = m.loImageRight.Width * (1 - m.lnPercentageBlend) / 2
      m.lnWidthPart = Ceiling((m.loBitmapLeft.Width-m.lnWidthOpaque*2)/m.lnAlphaSteps)
      m.loBitmapPart = .Bitmap.New(m.lnWidthPart, m.loBitmapLeft.Height)

** Pre-fill ImageAttributes with Alpha ColorMatrix blend for performance
** The biggest advantage to doing this is if you were processing multiple
** set of images at a time, which we aren't doing here.

      For m.lnCurrentStep = 1 To m.lnAlphaSteps
         laImageAttribs[m.lnCurrentStep] = .Imaging.ImageAttributes.New()
         m.loColorMatrix.Matrix33 = m.lnCurrentStep/m.lnAlphaSteps

** Adjust these for preferred image quality vs. speed
*m.loGfxLeft.InterpolationMode = .Drawing2D.InterpolationMode.Default
*m.loGfxLeft.CompositingQuality = .Drawing2D.CompositingQuality.Default
*m.loGfxLeft.SmoothingMode = .Drawing2D.SmoothingMode.Default

** Use integers instead of floats for performance
      m.loGfxLeft.UsePrecision = .F.
** Minor speed increase accessing this property once
      m.lnHeightImage = m.loImageRight.Height

      For m.lnCurrentStep = 1 To m.lnAlphaSteps
         m.lnLeftStart = m.lnCurrentStep*m.lnWidthPart + m.lnWidthOpaque

** Draw partial right image to part
         m.loGfxPart.DrawImage(m.loImageRight, ;
            0,0,m.lnWidthPart,m.lnHeightImage, ;
            m.lnLeftStart,0,m.lnWidthPart,m.lnHeightImage, ;

** Draw part to left image with Alpha blend
         m.loGfxLeft.DrawImage(m.loBitmapPart, ;
            m.lnLeftStart,0,m.lnWidthPart,m.lnHeightImage, ;
            0,0,m.lnWidthPart,m.lnHeightImage, ;
            .GraphicsUnit.Pixel, ;


** Draw last opaque part of right image
      m.loGfxLeft.DrawImage(m.loImageRight, ;
         m.loImageRight.Width-m.lnWidthOpaque, 0, m.lnWidthOpaque, m.lnHeightImage, ;
         m.loImageRight.Width-m.lnWidthOpaque, 0, m.lnWidthOpaque, m.lnHeightImage, ;

** Render to the screen
      m.loGfxScreen = .Graphics.FromHWnd(_Screen.HWnd)

** Or you can save it to a file



Bo Durban - Moxiedata Blog - Display the Print Dialog Using PrintDlgEx

Original post by Bo Durban, replicated here because the original blog http://blog.moxiedata.com is not responding.
Copied from:

Display the Print Dialog Using PrintDlgEx

As I mentioned in my post from 9/29/2008, Microsoft removed the Printer button from the "Page Setup" dialog in Vista. It was reccommneded to use the "Print" dialog to change printer preferences. However, the "Print" dialog option is not always available on the menu, and there is no simple call to activate the dialog.
In my post from 9/29, I provided code for displaying the DocumentProperties dialog. This populates a DEVMODE structure from the Advanced Properties dialog for a specific printer. In today's post, I provide sample code for displaying the Print dialog using the PrintDlgEx API function. This is the dialog Microsoft recommendeds for setting printer options in Vista. This dialog populates a DEVMODE structure also, however, it includes the ability to select a printer and specify a print range too.

Paste this code into a PRG and run it. It will display user chosen options to the screen.
While this dialog is meant to replace missing functionality from SYS(1037) in Vista, this sample does not update your user preferences. You will need to update those based on the user action (see "Action" in the code below).
Note that the PrintDlgEx API function works with Windows 2000 and later.

** Converted from: http://msdn.microsoft.com/en-us/library/ms646829.aspx



** nFlag options
#Define PD_ALLPAGES                  0x00000000
#Define PD_SELECTION                 0x00000001
#Define PD_PAGENUMS                  0x00000002
#Define PD_NOSELECTION               0x00000004
#Define PD_NOPAGENUMS                0x00000008
#Define PD_COLLATE                   0x00000010
#Define PD_PRINTTOFILE               0x00000020
#Define PD_PRINTSETUP                0x00000040
#Define PD_NOWARNING                 0x00000080
#Define PD_RETURNDC                  0x00000100
#Define PD_RETURNIC                  0x00000200
#Define PD_RETURNDEFAULT             0x00000400
#Define PD_SHOWHELP                  0x00000800
#Define PD_USEDEVMODECOPIES          0x00040000
#Define PD_DISABLEPRINTTOFILE        0x00080000
#Define PD_HIDEPRINTTOFILE           0x00100000
#Define PD_NONETWORKBUTTON           0x00200000
#Define PD_CURRENTPAGE               0x00400000
#Define PD_NOCURRENTPAGE             0x00800000

#Define START_PAGE_GENERAL    Bitlshift(0xffffffff,0)
#Define GMEM_FIXED       0x00
#Define GMEM_ZEROINIT    0x40
#Define DM_OUT_BUFFER  2
#Define DM_IN_PROMPT   4
#Define CCHFORMNAME   32
#Define PD_RESULT_CANCEL               0
#Define PD_RESULT_PRINT                1
#Define PD_RESULT_APPLY                2

Declare Long GlobalAlloc In WIN32API Long uFlags, Long uBytes
Declare Long GlobalLock In WIN32API Long Hmem
Declare Long GlobalUnlock In WIN32API Long Hmem
Declare Long GlobalFree In WIN32API Long Hmem
Declare Integer PrintDlg In comdlg32.Dll Long lppd
Declare Integer PrintDlgEx In comdlg32.Dll Long lppd
Declare Long DeleteDC In WIN32API Long hdc

Local hPRINTDLG, hPageRanges, hdc, hResult, nFlagsm, hResult
Local hGDevMode, hGDevNames, nPageRange, hPageRange, lnAction
Local lcDeviceName, lcFormName, hDevMode, hDevNames

** Allocate memory for the PRINTDLGEX structure
** Allocate memory for 10x PRINTPAGERANGE structures
hPageRanges = GlobalAlloc(GPTR,SIZEOF_PRINTPAGERANGE*10)
hdc = 0
hResult = 0

** Initialize the PRINTPAGERANGE structure

** Initialize the PRINTDLGEX structure
Sys(2600,hPRINTDLG+ 0,4,BinToC(SIZEOF_PRINTDLGEX,"4rs")) && lStructSize
Sys(2600,hPRINTDLG+ 4,4,BinToC(_vfp.HWnd,"4rs"))         && hwndOwner
Sys(2600,hPRINTDLG+20,4,BinToC(nFlags,"4rs"))            && Flags
Sys(2600,hPRINTDLG+32,4,BinToC(0,"4rs"))                 && nPageRanges
Sys(2600,hPRINTDLG+36,4,BinToC(10,"4rs"))                && nMaxPageRanges
Sys(2600,hPRINTDLG+40,4,BinToC(hPageRanges,"4rs"))       && lpPageRanges
Sys(2600,hPRINTDLG+44,4,BinToC(1,"4rs"))                 && nMinPage
Sys(2600,hPRINTDLG+48,4,BinToC(1000,"4rs"))              && nMaxPage
Sys(2600,hPRINTDLG+52,4,BinToC(1,"4rs"))                 && nCopies
Sys(2600,hPRINTDLG+76,4,BinToC(START_PAGE_GENERAL,"4rs")) && nStartPage

** Display the Print dialog
hResult = PrintDlgEx(hPRINTDLG)

** Pull updated values from PRINTDLGEX structure
hGDevMode = CToBin(Sys(2600,hPRINTDLG+8,4),"4rs")
hGDevNames = CToBin(Sys(2600,hPRINTDLG+12,4),"4rs")
hdc = CToBin(Sys(2600,hPRINTDLG+16,4),"4rs")
nFlags = CToBin(Sys(2600,hPRINTDLG+20,4),"4rs")
lnAction = CToBin(Sys(2600,hPRINTDLG+80,4),"4rs")

If hResult = 0
** Lock movable memory
   hDevMode = GlobalLock(hGDevMode)
   hDevNames = GlobalLock(hGDevNames)

** Display Action taken by user
   Do Case
      Case lnAction = PD_RESULT_CANCEL
         ?"Action: CANCEL"
      Case lnAction = PD_RESULT_PRINT
         ?"Action: PRINT"
      Case lnAction = PD_RESULT_APPLY
         ?"Action: APPLY"

** Display print range selection
   Do Case
      Case lnAction != PD_RESULT_PRINT
** No Printing
      Case Bitand(nFlags,PD_SELECTION)=PD_SELECTION
         ?" Selection"
         ?" Current Page"
      Case Bitand(nFlags,PD_PAGENUMS)=PD_PAGENUMS
         ?" Page Ranges"
         nPageRanges = CToBin(Sys(2600,hPRINTDLG+32,4),"4rs")
         For nPageRange = 0 To nPageRanges-1
            hPageRange = hPageRanges+(nPageRange*SIZEOF_PRINTPAGERANGE)
            nFrom = CToBin(Sys(2600,hPageRange,4),"4rs")
            nTo = CToBin(Sys(2600,hPageRange+4,4),"4rs")
            ?"   From: "+Transform(nFrom)+" To: "+Transform(nTo)
         ?" All Pages"

** Display the DEVMODE structure, showing printing preferences
   If hDevMode <> 0
      lcDeviceName = Sys(2600,hDevMode+0,CCHDEVICENAME)
      lcDeviceName = " "+Left(lcDeviceName, At(0h00,lcDeviceName)-1)
      ?" Device Name: ",   lcDeviceName
      ?" Orientation: ",   CToBin(Sys(2600,hDevMode+44,2),"2rs")
      ?" Paper Size: ",    CToBin(Sys(2600,hDevMode+46,2),"2rs")
      ?" Paper Length: ",  CToBin(Sys(2600,hDevMode+48,2),"2rs")
      ?" Paper Width: ",   CToBin(Sys(2600,hDevMode+50,2),"2rs")
      ?" Paper Scale: ",   CToBin(Sys(2600,hDevMode+52,2),"2rs")
      ?" Paper Copies: ",  CToBin(Sys(2600,hDevMode+54,2),"2rs")
      ?" Default Source: ",CToBin(Sys(2600,hDevMode+56,2),"2rs")
      ?" Print Qualilty: ",CToBin(Sys(2600,hDevMode+58,2),"2rs")
      ?" Color: ",         CToBin(Sys(2600,hDevMode+60,2),"2rs")
      ?" Duplex: ",        CToBin(Sys(2600,hDevMode+62,2),"2rs")
      ?" Y Resolution: ",  CToBin(Sys(2600,hDevMode+64,2),"2rs")
      ?" TT Option: ",     CToBin(Sys(2600,hDevMode+66,2),"2rs")
      ?" Collate: ",       CToBin(Sys(2600,hDevMode+68,2),"2rs")
      lcFormName = Sys(2600,hDevMode+70,CCHFORMNAME)
      lcFormName = " "+Left(lcFormName, At(0h00,lcFormName)-1)
      ?" Form Name: ",      lcFormName
      ?" LogPixels: ",      CToBin(Sys(2600,hDevMode+102,2),"2rs")
      ?" BitsPerPixel: ",   CToBin(Sys(2600,hDevMode+104,2),"2rs")

** Unlock movable memory
   ?"ERROR: "+Transform(hResult,"@0")

** Clean up allocated memory
If hGDevMode <> 0
If hGDevNames <> 0
If hdc <> 0