Wednesday, October 22, 2008

Batch Printing ArcMap MXD Documents

It seems like right clicking on an MXD file in Windows Explorer and trying to print never works right. Here's a simple utility that lets a user select a whole bunch of MXD files and sends them all to the printer without having to open ArcMap. Fast and easy.

This was written in Visual Basic using Visual Studio 2008 and requires that ArcMap 9.3 or later is already installed on the machine.

Here's the full Visual Studio Project:

And here's an installer if you just want the executable:

Here's some sample code that shows how the actual printing function works:

    Private Function PrintMXD(ByVal FullFileName As String) As Integer

        'Function PrintMXD
        'This opens an MXD file specified by FullFileName and sends it to the printer
        'The map will be sent to whatever printer is specified in the MXD document's settings
        'Function returns value 1 for success, 0 for failure


        'Verify that the file exists
        Dim aFileInfo As FileInfo
        Dim DocName As String
        aFileInfo = New FileInfo(FullFileName)
        If aFileInfo.Exists Then
            'Create a short string to send to the printer later
            DocName = aFileInfo.Name
            MessageBox.Show(FullFileName & " does not exist", "File not found", MessageBoxButtons.OK, MessageBoxIcon.Error)
            PrintMXD = 0
            Exit Function
        End If

        'Get this Application's hWnd

        Dim aHWnd As Long
        Dim m As System.Reflection.Module
        m = Me.GetType.Module
        aHWnd = System.Runtime.InteropServices.Marshal.GetHINSTANCE(m)

        'Make a MapDocument and open the specified file
        'If this fails, exit the function with error code

        Dim aDoc As IMapDocument = Nothing
            While aDoc Is Nothing
                aDoc = New MapDocument
            End While
        Catch ex As Exception
            MessageBox.Show("Unable to start ArcMap..." & vbNewLine & ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
            PrintMXD = 0
            Exit Function
        End Try
        'When using IMapDocument, it it necessary to Activate the Page Layout and each Map to update the displays
        Dim i As Integer
        Dim pActiveView As IActiveView
        'Activate each map
        For i = 0 To aDoc.MapCount - 1
            pActiveView = aDoc.Map(i)
        'Activate the Page Layout and keep a reference to it
        pActiveView = aDoc.PageLayout
        ' Get the printer's hDC, so we can use the Win32 GetDeviceCaps fuction to
        ' get Printer's Physical Printable Area x and y margins
        ' If this fails for some reason, batch printing won't work
        Dim pPrinter As IPrinter
        Dim hInfoDC As Integer
        Dim dpi As Double
            pPrinter = aDoc.Printer
            hInfoDC = CreateDC(pPrinter.DriverName, pPrinter.Paper.PrinterName, "", IntPtr.Zero)
            dpi = pPrinter.Resolution
        Catch ex As Exception
            MessageBox.Show("There was a problem with the printer settings for " & FullFileName & vbNewLine & _
                            "Please manually open and print this document.", "Print Settings Error", MessageBoxButtons.OK, MessageBoxIcon.Hand)
            PrintMXD = 0
            Exit Function
        End Try
        Dim docPrinterBounds As ESRI.ArcGIS.Geometry.IEnvelope
        Dim VisibleBounds As ESRI.ArcGIS.Geometry.IEnvelope
        docPrinterBounds = New EnvelopeClass()
        VisibleBounds = New EnvelopeClass()
        'Put the printer's physical bounds into docPrinterBounds
        aDoc.PageLayout.Page.GetDeviceBounds(pPrinter, 0, 0, dpi, docPrinterBounds)
        'userRect is used to specify the area on the page layout that is to be printed
        Dim userRECT As tagRECT
        userRECT.left = CType((docPrinterBounds.XMin - GetDeviceCaps(hInfoDC, 112)), Integer)
        userRECT.right = CType((docPrinterBounds.XMax - GetDeviceCaps(hInfoDC, 112)), Integer)
        userRECT.bottom = CType((docPrinterBounds.YMax - GetDeviceCaps(hInfoDC, 113)), Integer) = CType((docPrinterBounds.YMin - GetDeviceCaps(hInfoDC, 113)), Integer)
        ' Transfer offsetted PrinterBounds envelope back to the userRECT
        docPrinterBounds.PutCoords(0, 0, userRECT.right - userRECT.left, userRECT.bottom -
        aDoc.PageLayout.Page.GetPageBounds(pPrinter, 0, 0, VisibleBounds)
        'Send the Page Layout to the printer
        Dim hDc As Long
            hDc = pPrinter.StartPrinting(docPrinterBounds, 0)
            pPrinter.SpoolFileName = DocName
            pActiveView.Output(hDc, Nothing, userRECT, VisibleBounds, Nothing)
        Catch ex As Exception
            MessageBox.Show(FullFileName & vbNewLine & ex.Message, "Print Error", MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
            PrintMXD = 0
            Exit Function
        End Try
        PrintMXD = 1
    End Function


Anonymous said...

I'm running ArcGIS 9.3 and this is complaining that it can't find an ArcView license and does not work. What gives do I need ArcView?

Anonymous said...

When I installed the self installing program (MSI) and I click on the
program to run it from the start menu.
I get an immediate error message that the Batch MXD Printer has
working. Do you have any info on this program?

Right out of the error box.

Stopped working

Problem signature:
Problem Event Name: CLR20r3
Problem Signature 01: mxd_print.exe
Problem Signature 02:
Problem Signature 03: 48ff60e8
Problem Signature 04: mscorlib
Problem Signature 05:
Problem Signature 06: 48ead9ba
Problem Signature 07: 3f0
Problem Signature 08: 14
Problem Signature 09: N3CTRYE2KN3C34SGL4ZQYRBFTE4M13NB
OS Version: 6.0.6001.
Locale ID: 1033

Read our privacy statement:
I am running Vista Ultimate 64bit (Also had same problem with xp 64bit) and ArcGIS 9.3 with floating licenses of ArcInfo or ArcView.

Shari said...

This is perfect! Thanks for posting this. Very easy installation. This will be a great time save!

Anonymous said...

Very cool app.
Any chance that this could be utilized to batch save jpegs or pdfs?