LONG: How to Print a Metafile and Text to Form or Printer (113682)



The information in this article applies to:

  • Microsoft Visual Basic Professional Edition for Windows 3.0

This article was previously published under Q113682

SUMMARY

Visual Basic has only one native method for printing graphical objects, the PrintForm method. The PrintForm method is limited to printing one form per page and there is no way to control the placement of the image on the page or to place text on the same page with the image.

This article shows by example how to print Windows metafiles, such as those created by the graph control of the Professional edition, along with text, to the printer, a form, or a picture control. The size and placement of both the metafile and the text are under the program's control.

MORE INFORMATION

The following example shows you how to use Windows API functions to play a metafile and draw text to the hDC of the chosen object. Choose from the following objects: the printer object, a Visual Basic form, or a picture control.

The example uses the graph control of the Professional Edition to create a metafile for demonstration. Any Windows metafile, such as those provided in the \VB\METAFILES directories, can be used as well. The technique allows you to overlay text on the body of the metafile or overlay the metafile on an area containing text -- by simply changing the order in which the operations are executed. The last operation prints over the previous operation.

NOTE: if the x and y scaling factors differ when metafiles are rendered, the more restrictive of the two axes acts as the limiting factor.

Step-by-Step Example

Here are the steps necessary to construct a sample program that prints metafiles and text:
  1. Start a new project in Visual Basic. Form1 is created by default.
  2. Add a Graph control (Graph1) and two Picture controls (Picture1 and Picture2) to the form.
  3. Build the menu. The following is an excerpt from the text format version of the form. You can paste it into your form's text format file, or you can build the menus by using the menu design dialog in Visual Basic.
       Begin Menu mnuPrint
          Caption         =   "&Print "
          Begin Menu mnuToPic
             Caption         =   "To P&icture"
          End
          Begin Menu mnuToForm
             Caption         =   "To &Form"
          End
          Begin Menu mnuToPrinter
             Caption         =   "To P&rinter"
          End
       End
    
    						
  4. Add a code module (Module1) to the project by choosing New Module from the File menu (ALT, F, M).
  5. Add the following code to the general declarations section of Module1:
       Type RECT
          left As Integer
          top As Integer
          right As Integer
          bottom As Integer
       End Type
    
       Global lpDrawTextRect As RECT
    
       Type Size
          cx As Integer
          cy As Integer
       End Type
    
       Global lpold_vpextent As Size
       Global lpold_winextent As Size
       Global lpoldsize As Size
    
       Type POINTAPI
          X As Integer
          Y As Integer
       End Type
    
       Global lpoldwindoworg As POINTAPI
       Global lpoldvieworg As POINTAPI
    
       ' Set actual location of the window on the device, in device units:
       ' Enter each of the following Declare statements on one, single line:
    
       Declare Function PlayMetafile% Lib "GDI" (ByVal hDC%, ByVal hmf%)
       Declare Function SetMapMode Lib "GDI" (ByVal hDC As Integer,
          ByVal nMapMode As Integer) As Integer
       Declare Function SetViewPortExt Lib "GDI" (ByVal hDC As Integer,
          ByVal X As Integer, ByVal Y As Integer) As Long
       Declare Function SetViewPortExtEx Lib "GDI" (ByVal hDC As Integer,
          ByVal nX As Integer, ByVal nY As Integer, lpSize As Size) As Integer
       Declare Function SetViewPortOrg Lib "GDI" (ByVal hDC As Integer,
          ByVal X As Integer, ByVal Y As Integer) As Long
       Declare Function SetViewPortOrgEx Lib "GDI" (ByVal hDC As Integer,
          ByVal nX As Integer, ByVal nY As Integer, lpPoint As POINTAPI)
          As Integer
       Declare Function ScaleViewPortExtEx% Lib "GDI" (ByVal hDC%,
          ByVal nXnum%, ByVal nXdenom%, ByVal nYnum%, ByVal nYdenom%,
          lpSize As Size)
       Declare Function GetViewportExtEx Lib "GDI" (ByVal hDC As Integer,
          lpSize As Size) As Integer
       Declare Function SetWindowExt Lib "GDI" (ByVal hDC As Integer,
          ByVal X As Integer, ByVal Y As Integer) As Long
       Declare Function SetWindowExtEx Lib "GDI" (ByVal hDC As Integer,
          ByVal nX As Integer, ByVal nY As Integer, lpSize As Size) As Integer
       Declare Function SetWindowOrg Lib "GDI" (ByVal hDC As Integer,
          ByVal X As Integer, ByVal Y As Integer) As Long
       Declare Function SetWindowOrgEx Lib "GDI" (ByVal hDC As Integer,
          ByVal nX As Integer, ByVal nY As Integer, lpPoint As POINTAPI)
          As Integer
       Declare Function DrawText Lib "User" (ByVal hDC As Integer,
          ByVal lpStr As String, ByVal nCount As Integer, lpRect As RECT,
          ByVal wFormat As Integer) As Integer
    
       Global Const DT_WORDBREAK = &H10
       Global Const DT_CENTER = &H1
       Global Const DT_RIGHT = &H2
       Global Const DT_LEFT = &H0
       Global Const MM_TEXT = 1
       Global Const MM_ISOTROPIC = 7
    
    						
  6. Add the following code to Module1:
       ' Enter the following four lines as one, single line:
    
       Function PrintMetafile (printtarget As Integer, pmpicture As Control,
          pmform As Form, mfsource As Control, originx As Integer,
          originy As Integer, scalingx As Integer, scalingy As Integer)
          As Integer
    
          '*****************************************************************
          '*  PrintMetafileaccepts parameters and then prints either text or
          '*  Windows metafiles to one of three targets: printer, form or
          '*  picture control.
          '*
          '* Parameters:
          '*
          '*     printtarget accepts three values:
          '*           1 - sends output to printer object
          '*           2 - sends output to  picture control
          '*           3 - sends output to  form
          '*
          '*     pmpicture - accepts a picture control for use as output target
          '*     pmform    - accepts a form for use as the output target
          '*     mfsource  - accepts a picture control containing the metafile
          '*                 to be printed to the target
          '*
          '*     originx, originy - specifies the x and y coordinates of the
          '*                        origin of the output area
          '*
          '*     scalingx, scalingy - specifies the scaling factors in percent
          '*                          of the total output area, to create the
          '*                          output window, controlling both x, y axes
          '*
          '********************************************************************
    
          On Error GoTo handler
    
          ' Display hour glass:
          screen.MousePointer = 11
    
          ' Get the x and y extents depending on the target of printmapping:
    
          Select Case printtarget
    
          Case 1    ' For printer target:
             ' Initialize the printer object's hDC from VB's perspective:
             printer.Print " "
             printer.ScaleMode = 3  ' pixels equivalent to MM_TEXT
             pagewidth% = printer.ScaleWidth
             pageheight% = printer.ScaleHeight
    
             oldmapmode% = SetMapMode(printer.hDC, MM_ISOTROPIC)
             ' Make logical units equal to device units:
             ' The SDK recommends that this be done when using MM_ISOTROPIC:
             success% = SetWindowOrgEx(printer.hDC, 0, 0, lpoldwindoworg)
    
             ' Enter each of the following statements as one, single line:
             success% = SetWindowExtEx(printer.hDC, pagewidth%, pageheight%,
                lpold_winextent)
             success% = SetViewPortOrgEx(printer.hDC, originx, originy,
                lpoldvieworg)
             success% = SetViewPortExtEx(printer.hDC, pagewidth% / 100,
                pageheight% / 100, lpold_vpextent)
             success% = ScaleViewPortExtEx(printer.hDC, scalingx, 1,
                scalingy, 1, lpoldsize)
    
             ' Send the metafile to the target hDC:
             ApiError% = PlayMetafile(printer.hDC, mfsource.Picture)
             If ApiError% = 0 Then
                MsgBox "PlayMetaFile failed"
                PrintMetafile = False
             End If
    
             ' Reset device context to initial values:
             ' Enter each of the following statements as one, single line:
             successl& = SetWindowOrg(printer.hDC, lpoldwindoworg.X,
                lpoldwindoworg.Y)
             successl& = SetWindowExt(printer.hDC, lpold_winextent.cx,
                lpold_winextent.cy)
             successl& = SetViewPortOrg(printer.hDC, lpoldvieworg.X,
                lpoldvieworg.Y)
             successl& = SetViewPortExt(printer.hDC, lpold_vpextent.cx,
                lpold_vpextent.cy)
    
             oldmapmode% = SetMapMode(printer.hDC, oldmapmode%)
             PrintMetafile = True
    
          Case 2    ' For picture1 target:
    
             If TypeOf pmpicture Is PictureBox Then
                pmpicture.ScaleMode = 3  ' pixels equivalent to MM_TEXT
                pagewidth% = pmpicture.ScaleWidth
                pageheight% = pmpicture.ScaleHeight
                oldmapmode% = SetMapMode(pmpicture.hDC, MM_ISOTROPIC)
                ' Make logical units equal to device units:
                ' SDK recommends that this be done when using MM_ISOTROPIC:
                ' Enter each of following statements as one, single line:
                success% = SetWindowOrgEx(pmpicture.hDC, 0, 0,
                   lpoldwindoworg)
                success% = SetWindowExtEx(pmpicture.hDC, pagewidth%,
                   pageheight%, lpold_winextent)
                success% = SetViewPortOrgEx(pmpicture.hDC, originx, originy,
                   lpoldvieworg)
                success% = SetViewPortExtEx(pmpicture.hDC, pagewidth% / 100,
                   pageheight% / 100, lpold_vpextent)
                success% = ScaleViewPortExtEx(pmpicture.hDC, scalingx, 1,
                   scalingy, 1, lpoldsize)
    
                ' Send the metafile to the target hDC:
                ApiError% = PlayMetafile(pmpicture.hDC, mfsource.Picture)
                If ApiError% = 0 Then
                   MsgBox "PlayMetaFile failed"
                   PrintMetafile = False
                End If
    
                ' Reset device context to initial values:
                ' Enter each of following statements as one, single line:
                successl& = SetWindowOrg(pmpicture.hDC, lpoldwindoworg.X,
                   lpoldwindoworg.Y)
                successl& = SetWindowExt(pmpicture.hDC, lpold_winextent.cx,
                   lpold_winextent.cy)
                successl& = SetViewPortOrg(pmpicture.hDC, lpoldvieworg.X,
                   lpoldvieworg.Y)
                successl& = SetViewPortExt(pmpicture.hDC, lpold_vpextent.cx,
                   lpold_vpextent.cy)
                oldmapmode% = SetMapMode(pmpicture.hDC, oldmapmode%)
    
                PrintMetafile = True
    
             Else
                MsgBox "Target not a PictureBox control!"
                screen.MousePointer = 0
                PrintMetafile = False
                Exit Function
             End If
    
          Case 3      ' For form target:
    
             pmform.ScaleMode = 3
             pagewidth% = pmform.ScaleWidth
             pageheight% = pmform.ScaleHeight
    
             oldmapmode% = SetMapMode(pmform.hDC, MM_ISOTROPIC)
    
             ' make logical units equal to device units
             ' The SDK recommends that this be done when using MM_ISOTROPIC:
             success% = SetWindowOrgEx(pmform.hDC, 0, 0, lpoldwindoworg)
    
             ' Enter each of the following statements as one, single line:
             success% = SetWindowExtEx(pmform.hDC, pagewidth%, pageheight%,
                lpold_winextent)
             success% = SetViewPortOrgEx(pmform.hDC, originx, originy,
                 lpoldvieworg)
             success% = SetViewPortExtEx(pmform.hDC, pagewidth% / 100,
                 pageheight% / 100, lpold_vpextent)
             success% = ScaleViewPortExtEx(pmform.hDC, scalingx, 1,
                 scalingy, 1, lpoldsize)
    
             ' Send the metafile to the target hDC:
             ApiError% = PlayMetafile(pmform.hDC, mfsource.Picture)
             If ApiError% = 0 Then
                MsgBox "PlayMetaFile failed"
                PrintMetafile = False
             End If
    
             ' Reset device context to initial values:
             ' Enter each of the following statements as one, single line:
             successl& = SetWindowOrg(pmform.hDC, lpoldwindoworg.X,
                lpoldwindoworg.Y)
             successl& = SetWindowExt(pmform.hDC, lpold_winextent.cx,
                lpold_winextent.cy)
             successl& = SetViewPortOrg(pmform.hDC, lpoldvieworg.X,
                lpoldvieworg.Y)
             successl& = SetViewPortExt(pmform.hDC, lpold_vpextent.cx,
                lpold_vpextent.cy)
             oldmapmode% = SetMapMode(pmform.hDC, oldmapmode%)
             PrintMetafile = True
    
          Case Else
    
             ' Enter the following statement as one, single line:
             MsgBox "Unknown value passed to parameter printtarget
                - exiting function"
    
             screen.MousePointer = 0
             Exit Function
    
          End Select
    
          screen.MousePointer = 0
          Exit Function
    
          handler:
          ' Enter the following statement as one, single line:
          MsgBox "Function PrintMetafile has failed with error number " &
              Trim(Str(Err)) & " - recheck your parameters"
    
          screen.MousePointer = 0
          PrintMetafile = False
          Exit Function
    
       End Function
    
       ' Enter the following four lines as one, single line:
       Function PrintText (printtarget As Integer, pmpicture As Control,
          pmform As Form, ptext As String, pfontname As String, pfontsize As
          Integer, originx As Integer, originy As Integer, offsetx As Integer,
          offsety As Integer, fuFormat)
    
          '********************************************************************
          '*  PrintText accepts parameters and then prints text to the one of
          '*  three targets: printer, form or picture control.
          '*
          '* Parameters:
          '*     printtarget accepts three values:
          '*           1 - sends output to printer object
          '*           2 - sends output to picture control
          '*           3 - sends output to form
          '*
          '*     pmpicture - accepts picture control for use as output target
          '*     pmform    - accepts form for use as the output target
          '*
          '*     ptext - contains the string to be printed
          '*     pfontname - contains the fontname to be used
          '*     pfontsize - contains the fontsize to be used
          '*
          '*     originx, originy - specifies the x and y coordinates of the
          '*                        upper left origin of the output area
          '*
          '*     offsetx, offsety - specifies the coordinates of the lower
          '*                  right corner of the DrawText Rectangle
          '*                  relative to the upper left corner
          '*
          '*     fuFormat  -  Accepts four values for formatting the text
          '*                  within the rectangle specified by the previous
          '*                  four parameters
          '*                  0 - align left
          '*                  1 - align center
          '*                  2 - align right
          '*                  16 - do word wrapping in rectangle
          '*
          '*  Return value is the height of the text in current logical units
          '*
          '********************************************************************
    
          On Error GoTo handler2
    
          ' Display hour glass:
           screen.MousePointer = 11
    
          Select Case printtarget
    
          Case 1
    
             oldmapmode% = SetMapMode(printer.hDC, MM_TEXT)
             printer.FontName = pfontname
             printer.FontSize = pfontsize
    
             lpDrawTextRect.left = originx
             lpDrawTextRect.top = originy
             lpDrawTextRect.right = originx + offsetx
             lpDrawTextRect.bottom = originy + offsety
    
             ' Enter the following statement as one, single line:
             success% = DrawText(printer.hDC, ptext, Len(ptext),
                lpDrawTextRect, fuFormat)
    
             PrintText = success%
    
             ' Reset device context to initial values:
             oldmapmode% = SetMapMode(printer.hDC, oldmapmode%)
             PrintText = success%
    
          Case 2
    
             If TypeOf pmpicture Is PictureBox Then
    
                oldmapmode% = SetMapMode(pmpicture.hDC, MM_TEXT)
                pmpicture.FontName = pfontname
                pmpicture.FontSize = pfontsize
    
                lpDrawTextRect.left = originx
                lpDrawTextRect.top = originy
                lpDrawTextRect.right = originx + offsetx
                lpDrawTextRect.bottom = originy + offsety
    
                ' Enter the following statement as one, single line:
                success% = DrawText(pmpicture.hDC, ptext, Len(ptext),
                   lpDrawTextRect, fuFormat)
    
                PrintText = success%
    
                ' Reset device context to initial values:
                oldmapmode% = SetMapMode(pmpicture.hDC, oldmapmode%)
    
             Else
                MsgBox "Target not a PictureBox control!"
                screen.MousePointer = 0
                PrintText = False
                Exit Function
             End If
    
          Case 3
    
             oldmapmode% = SetMapMode(pmform.hDC, MM_TEXT)
             pmform.FontName = pfontname
             pmform.FontSize = pfontsize
    
             lpDrawTextRect.left = originx
             lpDrawTextRect.top = originy
             lpDrawTextRect.right = originx + offsetx
             lpDrawTextRect.bottom = originy + offsety
    
             ' Enter the following statement as one, single line:
             success% = DrawText(pmform.hDC, ptext, Len(ptext),
                lpDrawTextRect, fuFormat)
             PrintText = success%
    
             ' Reset device context to initial values:
             oldmapmode% = SetMapMode(pmform.hDC, oldmapmode%)
             PrintText = success%
    
          Case Else
    
             ' Enter the following statement as one, single line:
             MsgBox "Unknown value passed to parameter printtarget -
                exiting function"
             screen.MousePointer = 0
             Exit Function
          End Select
          screen.MousePointer = 0
    
          Exit Function
    
          handler2:
          ' Enter the following statement as one, single line:
          MsgBox "Function PrintText has failed with error number " &
             Trim(Str(Err)) & " - recheck your parameters"
    
          screen.MousePointer = 0
          PrintText = False
          Exit Function
    
       End Function
    
    						
  7. Add the following Sub procedures to the appropriate event procedure of Form1:
       Sub Form_Load ()
          graph1.GraphStyle = 6
          graph1.GraphType = 4          '3D Bar
          graph1.NumSets = 3
          graph1.Move 0, 0, 975, 855
          picture2.Move 0, graph1.Top + graph1.Height, 975, 855
       End Sub
    
       Sub Form_Resize ()
          picture1.Move Me.ScaleLeft, Me.ScaleHeight / 2, Me.ScaleWidth,
          Me.ScaleHeight / 2
       End Sub
    
       Sub mnuToPic_Click ()
          ' Save the graph as a metafile to disk:
          graph1.ImageFile = app.Path & "\GRAPHMF"
          graph1.DrawMode = 6
          graph1.Visible = False
          ' Allow time for graph to save the file to disk:
          DoEvents
    
          picture2.Picture = LoadPicture(app.Path & "\GRAPHMF.WMF")
          ' Or grab a metafile supplied with VB3:
          ' picture2.Picture =
          LoadPicture("C:\VB\METAFILE\BUSINESS\CENT.WMF")
    
          ' Allow the file to be loaded:
          DoEvents
    
          ret% = PrintMetafile(2, picture1, Me, picture2, 0, 0, 100, 100)
    
          NL$ = Chr$(13) & Chr$(10)
    
          ' Enter each of the following statements as one, single line:
          st1$ = "This is a string of characters that will be wrapped to fit
             the rectangle!"
          st2$ = "This is one string!" & NL$ & "This on the second line" & NL$
             & "and this is on the third!"
    
          fn$ = "Courier New"
    
          ret% = PrintMetafile(2, picture1, Me, picture2, 62, 170, 25, 25)
          ret% = PrintMetafile(2, picture1, Me, picture2, 62, 200, 16, 16)
    
          ' Word wrap in the rectangle:
          ' Enter the following statement as one, single line:
          ret% = PrintText(2, picture1, Me, st1$, fn$, 13, 190, 100,
             139, 139, 16)
    
          ' Center the text in the rectangle:
          ret% = PrintText(2, picture1, Me, st2$, fn$, 13, 69, 47, 254, 139, 1)
    
       End Sub
    
       Sub mnuToForm_Click ()
          ' Save the graph as a metafile to disk:
          graph1.ImageFile = app.Path & "\GRAPHMF"
          graph1.DrawMode = 6
          graph1.Visible = False
    
          ' Allow time for graph to save the file to disk:
          DoEvents
    
          picture2.Picture = LoadPicture(app.Path & "\GRAPHMF.WMF")
    
          ' or grab a metafile supplied with VB3:
          ' picture2.Picture =
    
          LoadPicture("C:\VB\METAFILE\BUSINESS\CENT.WMF")
    
          ' Allow the file to be loaded:
          DoEvents
    
          ret% = PrintMetafile(3, picture1, Me, picture2, 0, 0, 100, 100)
          NL$ = Chr$(13) & Chr$(10)
    
          ' Enter each of the following statements as one, single line:
          st1$ = "This is a string of characters that will be wrapped to fit
             the rectangle!"
          st2$ = "This is one string!" & NL$ & "This on the second line" &
             NL$ & "and this is on the third!"
    
          fn$ = "Times New Roman"
    
          ret% = PrintMetafile(3, picture1, Me, picture2, 62, 170, 25, 25)
          ret% = PrintMetafile(3, picture1, Me, picture2, 62, 200, 16, 16)
    
          ' Word wrap in the rectangle:
          ' Enter the following statement as one, single line:
          ret% = PrintText(3, picture1, Me, st1$, fn$, 13, 190, 100,
             139, 139, 16)
    
          ' Center the text in the rectangle:
          ret% = PrintText(3, picture1, Me, st2$, fn$, 13, 69, 47, 254, 139, 1)
    
       End Sub
    
       Sub mnuToPrinter_Click ()
          ' Save the graph as a metafile to disk:
          graph1.ImageFile = app.Path & "\GRAPHMF"
          graph1.DrawMode = 6
          graph1.Visible = False
    
          ' Allow time for graph to save the file to disk:
          DoEvents
    
          picture2.Picture = LoadPicture(app.Path & "\GRAPHMF.WMF")
          ' or grab a metafile supplied with VB3
          ' picture1.Picture =
    
          LoadPicture("C:\VB\METAFILE\BUSINESS\CENT.WMF")
    
          ' Allow the file to be loaded:
          DoEvents
    
          ret% = PrintMetafile(1, picture1, Me, picture2, 0, 0, 100, 100)
    
          NL$ = Chr$(13) & Chr$(10)
    
          ' Enter each of the following statements as one, single line:
          st1$ = "This is a string of characters that will be wrapped to fit
             the rectangle!"
          st2$ = "This is one string!" & NL$ & "This on the second line" & NL$
             & "and this is on the third!"
    
          fn$ = "Times New Roman"
    
          ret% = PrintMetafile(1, picture1, Me, picture2, 540, 1100, 29, 29)
          ret% = PrintMetafile(1, picture1, Me, picture2, 540, 1300, 19, 19)
    
          ' Word wrap in the rectangle:
          ' Enter the following statement as one, single line:
          ret% = PrintText(1, picture1, Me, st1$, fn$, 13, 1100, 300,
             400, 300, 16)
    
          ' Center the text in the rectangle:
          ' Enter the following statement as one, single line:
          ret% = PrintText(1, picture1, Me, st2$, fn$, 13, 1040, 690, 600,
             139, 1)
    
          printer.EndDoc
    
       End Sub
    
    						
  8. Save the project and run the program by pressing the F5 key.
  9. Select any of the three targets for the composite of text and metafiles to be rendered.
  10. The code in the three menu events (mnuToPrinter_Click, mnuToForm_Click, and mnuToPic_Click) are just three parallel examples demonstrating the technique. The PrintMetafile and PrintText functions are designed to be generic wrappers for the API functions used.

Modification Type:MajorLast Reviewed:10/20/2003
Keywords:kbcode kbprint KB113682