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
- Paste this code into the Visual Basic editor and select Run Sub from the Run menu.
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