| 
	
 
The information in this article applies to:
 
- Professional Edition of Microsoft Visual Basic for Windows,
   versions 1.0 (Professional Toolkit only), 2.0, and 3.0
  
 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:
 - Start a new project in Visual Basic. Form1 is created by default.
 - Add a Graph control (Graph1) and two Picture controls (Picture1
   and Picture2) to the form.
 - 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
 - Add a code module (Module1) to the project by choosing New Module from
   the File menu (ALT, F, M).
 - 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
 - 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
 - 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
 - Save the project and run the program by pressing the F5 key.
 - Select any of the three targets for the composite of text and
   metafiles to be rendered.
 - 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.
   
	
	 |