Print multiple pages
PrintMultiPagesVB_NET_Addin.vb
' Copyright 2012 ESRI
' 
' All rights reserved under the copyright laws of the United States
' and applicable international laws, treaties, and conventions.
' 
' You may freely redistribute and use this sample code, with or
' without modification, provided you include the original copyright
' notice and use restrictions.
' 
' See the use restrictions.
' 

Imports System.Runtime.InteropServices
Imports System.Drawing
Imports ESRI.ArcGIS.Carto
Imports ESRI.ArcGIS.Output
Imports ESRI.ArcGIS.Geometry
Imports ESRI.ArcGIS.Display
Imports ESRI.ArcGIS.esriSystem

Public Class PrintMultiPagesVB_NET_Addin
    Inherits ESRI.ArcGIS.Desktop.AddIns.Button

    ' gdi and win32 functions - the parameters are INTEGER because in .NET integer is 32 bits.
    Private Declare Function GetDeviceCaps Lib "gdi32" (ByVal hDC As Integer, ByVal nIndex As Integer) As Integer
    Private Declare Function GetDC Lib "user32" (ByVal hwnd As Integer) As Integer
    Declare Function CreateDC Lib "gdi32" Alias "CreateDCA" (ByVal lpDriverName As String, ByVal lpDeviceName As String, ByVal lpOutput As String, ByVal lpInitData As IntPtr) As Integer
    Private Declare Function ReleaseDC Lib "user32" _
      (ByVal hwnd As Integer, ByVal hdc As Integer) As Integer

    Public Sub New()

    End Sub

    Protected Overrides Sub OnClick()
        PrintMultiPageParameterizedVB(3)
    End Sub

    Protected Overrides Sub OnUpdate()
    End Sub

    Private Sub PrintMultiPageParameterizedVB(ByVal iResampleRatio As Long)

        '  Prints tiled map using IPrinterMPage 
        Dim docActiveView As IActiveView = My.ArcMap.Document.ActiveView
        Dim docPrinter As IPrinter
        Dim iPrevOutputImageQuality As Long
        Dim docOutputRasterSettings As IOutputRasterSettings

        Dim deviceRECT As ESRI.ArcGIS.esriSystem.tagRECT
        Dim docPaper As IPaper
        Dim sNameRoot As String

        Dim iNumPages As Short
        Dim docPrinterBounds As IEnvelope
        Dim VisibleBounds As IEnvelope
        Dim sysPrintDocument As System.Drawing.Printing.PrintDocument

        docPrinterBounds = New EnvelopeClass()
        VisibleBounds = New EnvelopeClass()

        ' save the previous output image quality, so that when the export is complete it will be set back.
        docOutputRasterSettings = docActiveView.ScreenDisplay.DisplayTransformation
        iPrevOutputImageQuality = docOutputRasterSettings.ResampleRatio

        ' then set the output quality to the desired ratio
        SetOutputQuality(docActiveView, iResampleRatio)

        ' assign the output filename.  
        sNameRoot = "PrintActiveViewVBSample"

        ' testing to see if printer instantiated in sysPrintDocument is the 
        ' default printer.  It SHOULD be, but this is just a reality check.
        docPrinter = New EmfPrinter()
        sysPrintDocument = New System.Drawing.Printing.PrintDocument()
        docPaper = New Paper()

        If sysPrintDocument.PrinterSettings.IsDefaultPrinter Then

            'Set docPaper's printername to the printername of the default printer
            docPaper.PrinterName = sysPrintDocument.PrinterSettings.PrinterName
        Else

            'if we get an unexpected result, return.
            MsgBox("Error getting default printer info, exiting...")
            Return
        End If

        'make sure the paper orientation is set to the orientation matching the current view.
        docPaper.Orientation = My.ArcMap.Document.PageLayout.Page.Orientation

        'assign docPrinter's paper.  We have to do this in two steps because you cannot change a 
        'printers' paper after it's assigned.  That's why we set docPaper.PrinterName first.
        docPrinter.Paper = docPaper

        'set the spoolfilename (this is the job name that shows up in the print queue)
        docPrinter.SpoolFileName = sNameRoot

        ' Get the printer's hDC, so we can use the Win32 GetDeviceCaps function to
        '  get Printer's Physical Printable Area x and y margins
        Dim hInfoDC As Integer
        hInfoDC = CreateDC(docPrinter.DriverName, docPrinter.Paper.PrinterName, "", IntPtr.Zero)

        ' Find out how many printer pages the output will cover.  iNumPages will always be 1 
        ' unless the user explicitly sets the tiling options in the file->Print dialog.  
        If TypeOf My.ArcMap.Document.ActiveView Is IPageLayout Then
            My.ArcMap.Document.PageLayout.Page.PrinterPageCount(docPrinter, 0, iNumPages)
        Else
            'it's data view, and so we always just need one page.
            iNumPages = 1
        End If

        If iNumPages < 2 Then
            MsgBox("Sample requires map in Layout View and map to be tiled to printer paper")
        End If


        Dim PrintMPage As IPrinterMPage
        Dim intPageHandle As Integer
        PrintMPage = docPrinter

        'Code inside StartMapDocument <-----> EndMapDocument is hard coded to print the first two pages of the tiled print
        'Printer and Page Bounds need to be calculated for each corresponding tile that gets printed

        PrintMPage.StartMapDocument()

        'calculate printer bounds for the first page
        My.ArcMap.Document.PageLayout.Page.GetDeviceBounds(docPrinter, 1, 0, docPrinter.Resolution, docPrinterBounds)

        'Transfer PrinterBounds envelope, offsetting by PHYSICALOFFSETX
        ' the Win32 constant for PHYSICALOFFSETX is 112
        ' the Win32 constant for PHYSICALOFFSETY is 113
        deviceRECT.bottom = CType((docPrinterBounds.YMax - GetDeviceCaps(hInfoDC, 113)), Integer)
        deviceRECT.left = CType((docPrinterBounds.XMin - GetDeviceCaps(hInfoDC, 112)), Integer)
        deviceRECT.right = CType((docPrinterBounds.XMax - GetDeviceCaps(hInfoDC, 112)), Integer)
        deviceRECT.top = CType((docPrinterBounds.YMin - GetDeviceCaps(hInfoDC, 113)), Integer)

        ' Transfer offsetted PrinterBounds envelope back to the deviceRECT
        docPrinterBounds.PutCoords(0, 0, deviceRECT.right - deviceRECT.left, deviceRECT.bottom - deviceRECT.top)

        If TypeOf My.ArcMap.Document.ActiveView Is IPageLayout Then

            'get the visible bounds for this layout, based on the current page number.
            My.ArcMap.Document.PageLayout.Page.GetPageBounds(docPrinter, 1, 0, VisibleBounds)

        Else
            MsgBox("Please Use Map Layout View for this Sample")
            Exit Sub
        End If


        'start printing the first page bracket, returns handle of the current page.  
        'handle is then passed to the ActiveView.Output()
        intPageHandle = PrintMPage.StartPage(docPrinterBounds, hInfoDC)

        My.ArcMap.Document.ActiveView.Output(intPageHandle, docPrinter.Resolution, deviceRECT, VisibleBounds, Nothing)

        PrintMPage.EndPage() 'end printing the first page bracket

        'calculate printer bounds for the second page
        My.ArcMap.Document.PageLayout.Page.GetDeviceBounds(docPrinter, 2, 0, docPrinter.Resolution, docPrinterBounds)

        'Transfer PrinterBounds envelope, offsetting by PHYSICALOFFSETX
        ' the Win32 constant for PHYSICALOFFSETX is 112
        ' the Win32 constant for PHYSICALOFFSETY is 113
        deviceRECT.bottom = CType((docPrinterBounds.YMax - GetDeviceCaps(hInfoDC, 113)), Integer)
        deviceRECT.left = CType((docPrinterBounds.XMin - GetDeviceCaps(hInfoDC, 112)), Integer)
        deviceRECT.right = CType((docPrinterBounds.XMax - GetDeviceCaps(hInfoDC, 112)), Integer)
        deviceRECT.top = CType((docPrinterBounds.YMin - GetDeviceCaps(hInfoDC, 113)), Integer)

        ' Transfer offsetted PrinterBounds envelope back to the deviceRECT
        docPrinterBounds.PutCoords(0, 0, deviceRECT.right - deviceRECT.left, deviceRECT.bottom - deviceRECT.top)

        If TypeOf My.ArcMap.Document.ActiveView Is IPageLayout Then

            'get the visible bounds for this layout, based on the current page number.
            My.ArcMap.Document.PageLayout.Page.GetPageBounds(docPrinter, 2, 0, VisibleBounds)

        Else
            MsgBox("Please Use Map Layout View for this Sample")
            Exit Sub
        End If


        'start printing the second page bracket, returns handle of the current page.  
        'handle is then passed to the ActiveView.Output()
        intPageHandle = PrintMPage.StartPage(docPrinterBounds, hInfoDC)

        My.ArcMap.Document.ActiveView.Output(intPageHandle, docPrinter.Resolution, deviceRECT, VisibleBounds, Nothing)

        PrintMPage.EndPage() 'end printing the second page bracket

        PrintMPage.EndMapDocument()

        'now set the output quality back to the previous output quality.
        SetOutputQuality(docActiveView, iPrevOutputImageQuality)


        'release the DC...
        ReleaseDC(0, hInfoDC)

    End Sub


    Private Sub SetOutputQuality(ByVal docActiveView As IActiveView, ByVal iResampleRatio As Long)
        '  This function sets OutputImageQuality for the active view.  If the active view is a pagelayout, then
        '  it must also set the output image quality for EACH of the Maps in the pagelayout.

        Dim docGraphicsContainer As IGraphicsContainer
        Dim docElement As IElement
        Dim docOutputRasterSettings As IOutputRasterSettings
        Dim docMapFrame As IMapFrame
        Dim tmpActiveView As IActiveView

        If TypeOf docActiveView Is IMap Then
            docOutputRasterSettings = docActiveView.ScreenDisplay.DisplayTransformation
            docOutputRasterSettings.ResampleRatio = CType(iResampleRatio, Integer)
        ElseIf TypeOf docActiveView Is IPageLayout Then
            'assign ResampleRatio for PageLayout
            docOutputRasterSettings = docActiveView.ScreenDisplay.DisplayTransformation
            docOutputRasterSettings.ResampleRatio = CType(iResampleRatio, Integer)
            'and assign ResampleRatio to the Maps in the PageLayout
            docGraphicsContainer = docActiveView
            docGraphicsContainer.Reset()

            docElement = docGraphicsContainer.Next()
            While Not docElement Is Nothing
                If TypeOf docElement Is IMapFrame Then
                    docMapFrame = docElement
                    tmpActiveView = docMapFrame.Map
                    docOutputRasterSettings = tmpActiveView.ScreenDisplay.DisplayTransformation
                    docOutputRasterSettings.ResampleRatio = CType(iResampleRatio, Integer)
                End If
                docElement = docGraphicsContainer.Next()
            End While

            docMapFrame = Nothing
            docGraphicsContainer = Nothing
            tmpActiveView = Nothing
        End If
        docOutputRasterSettings = Nothing


    End Sub

End Class