Take the 2-minute tour ×
Geographic Information Systems Stack Exchange is a question and answer site for cartographers, geographers and GIS professionals. It's 100% free, no registration required.

We are building an standalone ArcGIS Engine application and we are going to add support for printing.

There are several APIs for printing a PageLayout in ArcObjects, in ArcGIS 10 there are at least three different ways to do this:

Can someone give recommendations on which API we should choose? Is there any known pitfalls in these APIs?

Update: Found one pitfall during my research:

  • When working with ArcGIS Server layers, only PrintAndExport draw the patches/swatches on the legend when printing the layout.
share|improve this question
    
Is there a requirement that you need to be able to run this in batch (as a console application)? –  Kirk Kuykendall Aug 30 '10 at 14:19
    
No, the user will have a GUI that makes it possible to choose a layout from different MXD documents (A3, A4 etc.) that can be printed, but only one layout at a time can be printed. –  MathiasWestin Aug 30 '10 at 14:42
    
I've sent a bug report to ESRI on the problem with IOutput and Pagelayout APIs. –  MathiasWestin Sep 9 '10 at 20:06
add comment

4 Answers

PrintAndExport is the recommended approach at ArcGIS 10. This class was not just added for data driven pages support. It was added as a wrapper method to simplify print and export for developers. In addition to wrapping many COM calls, it also handles many of the GDI/Win32 calls that are needed for correct printing and exporting.

share|improve this answer
add comment

This is the result of my own research of the PrintPageLayout method, it's a good way to print a pagelayout if you don't need full control on how the printing is made, It seems to be a wrapper around the IOutput to make it easier to print the PageLayout:

''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
        'Customization of this sample
        'http://resources.esri.com/help/9.3/ArcGISEngine/dotnet/ViewCodePages/12940E59-1342-4d53-B619-2B609D298221PrintPageLayout.cs.htm

        Dim control As AxPageLayoutControl = AxPageLayoutControl1
        Dim PrintDialog1 As PrintDialog = New PrintDialog()

        Dim res As System.Windows.Forms.DialogResult = PrintDialog1.ShowDialog()
        If res = DialogResult.Cancel Then
            Return
        End If
        Dim paper As ESRI.ArcGIS.Output.IPaper = New ESRI.ArcGIS.Output.Paper

        paper.Attach(PrintDialog1.PrinterSettings.GetHdevmode().ToInt32(), PrintDialog1.PrinterSettings.GetHdevnames().ToInt32())

        'make sure the paper orientation is set to the orientation matching the current view.
        paper.Orientation = CType(control.Object, IPageLayoutControl3).PageLayout.Page.Orientation

        control.Printer.Paper = paper
        control.PrintPageLayout(1, 1, 0)
        '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''  
share|improve this answer
add comment

This is the result of my own research of the IOutput method, The IOutput give you a more posibilitys to control how the printing is made. For example if you want to print multiple pages.

''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
    'Customization of this sample
    'http://resources.esri.com/help/9.3/ArcGISEngine/dotnet/025b5da7-9b10-4623-a51e-c038c348ce29.htm

    <DllImport("GDI32.dll")> _
       Public Shared Function GetDeviceCaps(ByVal hdc As Integer, ByVal nIndex As Integer) As Integer
    End Function

    <DllImport("GDI32.dll")> _
    Public Shared Function CreateDC(ByVal strDriver As String, ByVal strDevice As String, ByVal strOutput As String, ByVal pData As IntPtr) As Integer
    End Function

    <DllImport("User32.dll")> _
    Public Shared Function ReleaseDC(ByVal hWnd As Integer, ByVal hDC As Integer) As Integer
    End Function

    Public Sub PrintActiveViewParameterized(ByVal iResampleRatio As Long)

        ' Prints the Active View of the document to selected output format. 

        ' 
        Dim docActiveView As IActiveView = m_hookHelper.ActiveView
        Dim docPrinter As IPrinter
        Dim iPrevOutputImageQuality As Long
        Dim docOutputRasterSettings As IOutputRasterSettings
        Dim deviceRECT As ESRI.ArcGIS.esriSystem.tagRECT
        Dim docPaper As IPaper
        ' printdocument is from the .NET assembly system.drawing.printing 

        Dim sysPrintDocumentDocument As System.Drawing.Printing.PrintDocument


        Dim iNumPages As Short
        Dim docPrinterBounds As IEnvelope
        Dim VisibleBounds As IEnvelope


        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 = TryCast(docActiveView.ScreenDisplay.DisplayTransformation, IOutputRasterSettings)
        iPrevOutputImageQuality = docOutputRasterSettings.ResampleRatio

        SetOutputQuality(docActiveView, iResampleRatio)


        ' Now we need to get the default printer name. Since this is a generic command,
        ' * we can't use the printername property of the document. So instead, we use the 
        ' * System.Drawing.Printing objects to find the default printer.
        ' 

        docPrinter = New EmfPrinterClass()

        'Fyller i defaultvärden i printerdialog
        Dim PrintDialog1 As PrintDialog = New PrintDialog()


        'Visar utskriftsdialog
        Dim res As System.Windows.Forms.DialogResult = PrintDialog1.ShowDialog()
        If res = DialogResult.Cancel Then
            Return
        End If

        sysPrintDocumentDocument = New System.Drawing.Printing.PrintDocument()
        docPaper = New PaperClass()
        docPaper.Attach(PrintDialog1.PrinterSettings.GetHdevmode().ToInt32(), PrintDialog1.PrinterSettings.GetHdevnames().ToInt32())



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

        ' Now assign docPrinter the paper and with it the printername. This process is two steps
        ' * because you cannot change an IPrinter's printer except by passing it as a part of 
        ' * the IPaper. That's why we setup docPrinter.Paper.PrinterName first.
        ' 

        docPrinter.Paper = docPaper

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

        ' Get the printer's hDC, so we can use the Win32 GetDeviceCaps fuction 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. 


        If TypeOf m_hookHelper.ActiveView Is IPageLayout Then

            m_hookHelper.PageLayout.Page.PrinterPageCount(docPrinter, 0, iNumPages)
        Else
            iNumPages = 1
        End If

        For lCurrentPageNum As Short = 0 To iNumPages - CType(1, Short)

            m_hookHelper.PageLayout.Page.GetDeviceBounds(docPrinter, lCurrentPageNum, 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 = CInt((docPrinterBounds.YMax - GetDeviceCaps(hInfoDC, 113)))
            deviceRECT.left = CInt((docPrinterBounds.XMin - GetDeviceCaps(hInfoDC, 112)))
            deviceRECT.right = CInt((docPrinterBounds.XMax - GetDeviceCaps(hInfoDC, 112)))
            deviceRECT.top = CInt((docPrinterBounds.YMin - GetDeviceCaps(hInfoDC, 113)))

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



            If TypeOf m_hookHelper.ActiveView Is IPageLayout Then
                'get the visible bounds for this layout, based on the current page number.
                m_hookHelper.PageLayout.Page.GetPageBounds(docPrinter, lCurrentPageNum, 0, VisibleBounds)
            Else
                'if it's not a page layout, export whatever's on screen at the printer's dpi.

                'get the page
                Dim pPage As IPage
                pPage = m_hookHelper.PageLayout.Page

                'get paper object
                docPaper = docPrinter.Paper
                If docPaper Is Nothing Then
                    MessageBox.Show("no paper defined")
                    Exit Sub
                End If

                'create an envelope for the bounds of the printer's page size in device units.
                Dim pDeviceFrame As IEnvelope
                pDeviceFrame = New EnvelopeClass()
                pPage.GetDeviceBounds(docPrinter, 1, 0, docPrinter.Resolution, pDeviceFrame)

                'create and populate an envelope with the bounds of the printer page.
                Dim wksdevice As WKSEnvelope
                pDeviceFrame.QueryWKSCoords(wksdevice)

                'transfer the envelope to the deviceRECT. 
                deviceRECT.left = CInt(Math.Round(wksdevice.XMin))
                deviceRECT.top = CInt(Math.Round(wksdevice.YMin))
                deviceRECT.right = CInt(Math.Round(wksdevice.XMax))
                deviceRECT.bottom = CInt(Math.Round(wksdevice.YMax))

                'since this is a data view, not a pagelayout, we don't need VisibleBounds, so set it to NULL.

                VisibleBounds = Nothing
            End If


            'Here's where the printing actually begins:
            '==========================================
            'The docPrinter.StartPrinting method returns the hDC of the printer, which we then
            'use as the hDC parameter of activeview.output. ActiveView.Output is the function that
            'draws the content on the active view, at the specified resolution, to the deviceRECT specified.
            'VisibleBounds can be null, which means "print the active view", or can be set to a zoom extent.
            If iNumPages = 1 Then
                VisibleBounds = Nothing
            End If
            m_hookHelper.ActiveView.Output(docPrinter.StartPrinting(docPrinterBounds, 0), docPrinter.Resolution, deviceRECT, VisibleBounds, Nothing)

            'here we call FinishPrinting, which actually flushes the output to the printer.
            docPrinter.FinishPrinting()

            'now set the output quality back to the previous output quality.

            SetOutputQuality(docActiveView, iPrevOutputImageQuality)
        Next
        '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 = TryCast(docActiveView.ScreenDisplay.DisplayTransformation, IOutputRasterSettings)
            docOutputRasterSettings.ResampleRatio = CInt(iResampleRatio)
        ElseIf TypeOf docActiveView Is IPageLayout Then
            'assign ResampleRatio for PageLayout
            docOutputRasterSettings = TryCast(docActiveView.ScreenDisplay.DisplayTransformation, IOutputRasterSettings)
            docOutputRasterSettings.ResampleRatio = CInt(iResampleRatio)
            'and assign ResampleRatio to the Maps in the PageLayout
            docGraphicsContainer = TryCast(docActiveView, IGraphicsContainer)
            docGraphicsContainer.Reset()

            docElement = docGraphicsContainer.[Next]()
            While docElement IsNot Nothing
                If TypeOf docElement Is IMapFrame Then
                    docMapFrame = TryCast(docElement, IMapFrame)
                    tmpActiveView = TryCast(docMapFrame.Map, IActiveView)
                    docOutputRasterSettings = TryCast(tmpActiveView.ScreenDisplay.DisplayTransformation, IOutputRasterSettings)
                    docOutputRasterSettings.ResampleRatio = CInt(iResampleRatio)
                End If
                docElement = docGraphicsContainer.[Next]()
            End While

            docMapFrame = Nothing
            docGraphicsContainer = Nothing
            tmpActiveView = Nothing
        End If

        docOutputRasterSettings = Nothing
    End Sub

    ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
share|improve this answer
add comment

This is the result of my own research of the PrintAndExport method, This seems to be an other wrapper of IOutput made by ESRI to support Data Driven pages.

''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
    'Customization of this sample
    'http://help.arcgis.com/en/sdk/10.0/arcobjects_net/conceptualhelp/index.html#/Sample_Print_active_view/0001000002ms000000/
    Private Sub PrintActiveViewVB(ByVal iResampleRatio As Integer)

        '  Prints the Active View of the document to selected output format. 

        Dim docActiveView As IActiveView = m_hookHelper.ActiveView
        Dim docPrinter As IPrinter
        Dim PrintAndExport As IPrintAndExport = New PrintAndExport
        Dim docPaper As IPaper
        Dim sNameRoot As String

        Dim iNumPages As Short
        Dim sysPrintDocument As System.Drawing.Printing.PrintDocument

        ' assign the output filename.  
        sNameRoot = "PrintActiveViewVBSample"

        docPrinter = New EmfPrinter()

        'sets default values in print dialog
        Dim PrintDialog1 As PrintDialog = New PrintDialog()


        'Show print dialog
        Dim res As System.Windows.Forms.DialogResult = PrintDialog1.ShowDialog()
        If res = DialogResult.Cancel Then
            Return
        End If

        sysPrintDocument = New System.Drawing.Printing.PrintDocument()
        docPaper = New Paper()
        docPaper.Attach(PrintDialog1.PrinterSettings.GetHdevmode().ToInt32(), PrintDialog1.PrinterSettings.GetHdevnames().ToInt32())



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

        'assing 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

        ' 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 m_hookHelper.ActiveView Is IPageLayout Then
            m_hookHelper.PageLayout.Page.PrinterPageCount(docPrinter, 0, iNumPages)
        Else
            'it's data view, and so we always just need one page.
            iNumPages = 1
        End If

        Dim lCurrentPageNum As Integer
        For lCurrentPageNum = 1 To iNumPages Step lCurrentPageNum + 1
            Try
                PrintAndExport.Print(docActiveView, docPrinter, m_hookHelper.PageLayout.Page, lCurrentPageNum, iResampleRatio, Nothing)
            Catch ex As Exception
                MessageBox.Show("An error has occurred: " + ex.Message)
            End Try

        Next

    End Sub
    ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.