How to print the active view


This sample demonstrates how to print a map programmatically. It will work from either Data View or Page Layout View. The PrintActiveView subroutine demonstrates the minimum steps necessary to output a correctly scaled page to the application's current printer. The PrintActiveView2() subroutine demonstrates how to reproduce the full functionality of ArcMap's print command, including using a Win32 API device context functions to detect and compensate for the physical offset of the print device.

How to use

  1. Paste this code into the Visual Basic editor and select Run Sub from the Run menu.
[VBA]
Private Declare Function GetDeviceCaps Lib "gdi32" (ByVal hDC As Long, ByVal nIndex As Long) As Long
Private Declare Function CreateIC Lib "gdi32" Alias "CreateICA" (ByVal lpDriverName As String, ByVal lpDeviceName As String, _
    ByVal lpOutput As String, ByVal lpInitData As Long) As Long

Public Sub PrintActiveView()
    Dim pMxDoc As IMxDocument
    Set pMxDoc = ThisDocument
    Dim pMxApp As IMxApplication
    Set pMxApp = ThisDocument.Parent
    
    ' Create a read-only reference to the application's current printer
    Dim pPrinter As IPrinter
    Set pPrinter = pMxApp.Printer
    
    ' Initialize the three bounds variables used for printing. Leave VisibleBounds
    '  set to Nothing for now.
    Dim pPrinterBounds As IEnvelope
    Set pPrinterBounds = New Envelope
    Dim deviceRECT As tagRECT
    Dim pVisibleBounds As IEnvelope
    
    ' Populate the PrinterBounds envelope with the device bounds of the application's Page object
    '  and then assign the same values to the deviceRECT structure.
    pMxDoc.PageLayout.Page.GetDeviceBounds pPrinter, 1, 0, pPrinter.Resolution, pPrinterBounds
    With deviceRECT
        .bottom = Round(pPrinterBounds.YMax)
        .Left = Round(pPrinterBounds.xmin)
        .Right = Round(pPrinterBounds.XMax)
        .Top = Round(pPrinterBounds.ymin)
    End With
    
    ' When printing a PageLayout view you must populate the VisibleBounds envelope.
    '  When printing a Map view, leave VisibleBounds set to Nothing.
    If TypeOf pMxDoc.ActiveView Is IPageLayout Then
        Set pVisibleBounds = New Envelope
        pMxDoc.PageLayout.Page.GetPageBounds pPrinter, 1, 0, pVisibleBounds
    End If
    
    ' Call the StartPrinting method to create a windows device context
    Dim hDC As Long
    hDC = pPrinter.StartPrinting(pPrinterBounds, 0)
    
    ' Call the output method to redraw the map, directing the output to the printer's hDC.
    pMxDoc.ActiveView.Output hDC, pPrinter.Resolution, deviceRECT, pVisibleBounds, Nothing
    pPrinter.FinishPrinting
    
End Sub


Public Sub PrintActiveView2()
    
    Dim pMxDoc As IMxDocument
    Set pMxDoc = ThisDocument
    Dim pMxApp As IMxApplication
    Set pMxApp = ThisDocument.Parent
    Dim iNumPages As Integer, lCurrentPageNum As Long
    Dim sMxdPath As String, sMxdName As String
    Dim pCancel As ITrackCancel
    Set pCancel = New CancelTracker
    Dim pPrinterBounds As IEnvelope
    Set pPrinterBounds = New Envelope
    Dim deviceRECT As tagRECT
    
    
    ' Create a new printer object. This example demonstrates an EmfPrinter object,
    '  but you could instead create a PsPrinter, providing access to the
    '  PostScript Printer engine.
    ' The properties of the Application's Printer object cannot be changed
    '  in place, so you must create a new printer if you want to change
    '  any of the values.  You can use the new printer through code immediately
    '  after setting it up, or you can set the Application's Printer property
    '  to the new printer if you want the application to match your changes.
    Dim pPrinter As IPrinter
    Set pPrinter = New EmfPrinter
    
    ' Set up the printer properties using the Paper object.  When we set pPaper
    '  to point to the Application's Paper object, the property values will
    '  reflect the Application's current settings.  We can force alternative
    '  values here.
    Set pPrinter.Paper = pMxApp.Paper
    pPrinter.Paper.PrinterName = "\\typo\zzyzx"
    
    ' Orientation 1=Portrait  2=Landscape
    pPrinter.Paper.Orientation = 2
    
    ' Get the MXD name and assign it to the SpoolFileName property. This name
    '  will show up in the print status window, and is the name that will be
    '  recorded by any print spool logging software.
    sMxdPath = Application.Templates.Item(Application.Templates.Count - 1)
    sMxdName = Split(sMxdPath, "\")(UBound(Split(sMxdPath, "\")))
    pPrinter.SpoolFileName = sMxdName
    
    ' Set up an informational hDC, so we can use the Win32 GetDeviceCaps fuction to
    '  get Printer's Physical Printable Area x and y margins
    Dim hInfoDC As Long
    hInfoDC = CreateIC(vbNullString, pPrinter.Paper.PrinterName, vbNullString, 0)
    
    ' Find out how many printer pages the output will cover.  If iNumPages greater
    '  than 1 then we're going to tile the pagelayout onto multiple pages.
    '  This will only be the case if pPage.PageToPrinterMapping = esriPageMappingTile
    If TypeOf pMxDoc.ActiveView Is IPageLayout Then
        pMxDoc.PageLayout.Page.PrinterPageCount pPrinter, 0, iNumPages
    Else
        iNumPages = 1
    End If
    
    For lCurrentPageNum = 1 To iNumPages
        pMxDoc.PageLayout.Page.GetDeviceBounds pPrinter, lCurrentPageNum, 0, pPrinter.Resolution, pPrinterBounds
        
        'Transfer PrinterBounds envelope, offsetting by PHYSICALOFFSETX
        ' the Win32 constant for PHYSICALOFFSETX is 112
        ' the Win32 constant for PHYSICALOFFSETY is 113
        With deviceRECT
            .bottom = Round(pPrinterBounds.YMax - GetDeviceCaps(hInfoDC, 113))
            .Left = Round(pPrinterBounds.xmin - GetDeviceCaps(hInfoDC, 112))
            .Right = Round(pPrinterBounds.XMax - GetDeviceCaps(hInfoDC, 112))
            .Top = Round(pPrinterBounds.ymin - GetDeviceCaps(hInfoDC, 113))
        End With
        
        'Transfer offsetted PrinterBounds envelope back to the deviceRECT
        pPrinterBounds.PutCoords 0, 0, deviceRECT.Right - deviceRECT.Left, deviceRECT.bottom - deviceRECT.Top
        
        Dim pVisibleBounds As IEnvelope
        If TypeOf pMxDoc.ActiveView Is IPageLayout Then
            Set pVisibleBounds = New Envelope
            pMxDoc.PageLayout.Page.GetPageBounds pPrinter, lCurrentPageNum, 0, pVisibleBounds
        End If
        
        pMxDoc.ActiveView.Output pPrinter.StartPrinting(pPrinterBounds, 0), pPrinter.Resolution, deviceRECT, pVisibleBounds, pCancel
        pPrinter.FinishPrinting
    Next
    
    Set pPrinter = Nothing
    Set pPrinterBounds = Nothing
    Set pVisibleBounds = Nothing
    Set pCancel = Nothing
    
End Sub