Expert guide
Export Microsoft Access Reports to PDF with VBA
One record, one customer, or a whole batch, with sensible file names every time
Call us at +1 (385) 386-3860
Short answer: DoCmd.OutputTo acOutputReport, "rptName", acFormatPDF, filePath exports a report to PDF. To export just one record, open the report hidden with a WhereCondition first, then OutputTo, then close it. For one PDF per customer, loop a recordset doing the same, building safe file names and folders. Our Access reports and forms service builds these exports into invoice, statement and certificate workflows.
Monthly statements, invoices, certificates of analysis, job sheets: the most common Access automation request is "make a PDF of this report for each customer and save it". Access has had built-in PDF export since 2010, but the details of filtering are where most attempts go wrong.
The Basic Export
DoCmd.OutputTo acOutputReport, "rptMonthlySales", acFormatPDF, _
"C:\Reports\Monthly Sales.pdf", FalseThe final False stops the PDF opening automatically. Use True when a user clicks "Preview PDF" and wants to see it.
Export One Record
Public Sub ExportInvoice(ByVal invoiceID As Long, ByVal outPath As String)
DoCmd.OpenReport "rptInvoice", acViewPreview, , "InvoiceID=" & invoiceID, acHidden
DoCmd.OutputTo acOutputReport, "rptInvoice", acFormatPDF, outPath, False
DoCmd.Close acReport, "rptInvoice", acSaveNo
End SubBecause the report is already open with the filter, OutputTo exports that filtered instance. If you call OutputTo without opening it first, Access opens a new, unfiltered copy, which is why people end up with every invoice in one PDF.
One PDF per Customer
Public Sub ExportStatements(ByVal periodEnd As Date)
Dim rs As DAO.Recordset, folder As String, fileName As String, n As Long
folder = "\\fileserver\Statements\" & Format(periodEnd, "yyyy-mm") & "\"
EnsureFolder folder
Set rs = CurrentDb.OpenRecordset( _
"SELECT DISTINCT c.CustomerID, c.AccountNo, c.CompanyName " & _
"FROM tblCustomers AS c INNER JOIN tblInvoices AS i ON c.CustomerID = i.CustomerID " & _
"WHERE i.Balance > 0", dbOpenSnapshot)
Do While Not rs.EOF
fileName = folder & SafeFileName(rs!AccountNo & " - " & rs!CompanyName) & ".pdf"
DoCmd.OpenReport "rptStatement", acViewPreview, , "CustomerID=" & rs!CustomerID, acHidden
DoCmd.OutputTo acOutputReport, "rptStatement", acFormatPDF, fileName, False
DoCmd.Close acReport, "rptStatement", acSaveNo
n = n + 1
SysCmd acSysCmdSetStatus, "Exported " & n
rs.MoveNext
Loop
rs.Close
SysCmd acSysCmdClearStatus
MsgBox n & " statements saved to " & folder, vbInformation
End Sub
Public Function SafeFileName(ByVal s As String) As String
Dim bad As Variant
For Each bad In Array("\", "/", ":", "*", "?", """", "<", ">", "|")
s = Replace(s, bad, "-")
Next bad
SafeFileName = Left$(Trim$(s), 120)
End Function
Public Sub EnsureFolder(ByVal path As String)
Dim parts() As String, i As Long, cur As String, first As Long
If Left$(path, 2) = "\\" Then ' UNC: \\server\share\...
parts = Split(Mid$(path, 3), "\")
cur = "\\" & parts(0) & "\" & parts(1) & "\" ' server and share must exist
first = 2
Else ' drive: C:\...
parts = Split(path, "\")
cur = parts(0) & "\"
first = 1
End If
For i = first To UBound(parts)
If Len(parts(i)) > 0 Then
cur = cur & parts(i) & "\"
If Dir(cur, vbDirectory) = "" Then MkDir cur
End If
Next i
End SubCompany names often contain characters Windows does not allow in file names, such as slash, colon and question mark; SafeFileName replaces them. EnsureFolder creates the month folder (and any missing parents) on a UNC path.
Design the Report for PDF
Set the page size and margins explicitly in Page Setup; PDF output follows them.
Use Keep Together on group headers so a customer's lines do not split awkwardly.
Put the record identifier and a page number in the footer (Page [Page] of [Pages]).
Avoid controls that only make sense on screen, such as buttons or hidden fields.
Use a font available on every PC that will export, to avoid substitution.
Common Problems
| Problem | Cause | Fix |
|---|---|---|
| PDF contains all records | Report not open with the filter at export time | Open hidden with WhereCondition first |
| Blank PDF | Filter matched nothing | Check the criteria; skip records with no data |
| Error 2501 (action cancelled) | Report's NoData event cancelled it | Handle 2501 and skip, or remove Cancel from NoData |
| Error 2302 can't save output data | Folder missing, no permission, or file open in a PDF viewer | Create folder, check permissions, close the file |
| Slow batch exports | Report record source with domain functions | Base it on a joined query; see the DLookup guide |
Slow reports often use domain functions per row; see faster alternatives to DLookup.
Next Steps: Email and Summaries
Exported PDFs are usually the attachment of an email; the complete pattern is in sending email from Access with Outlook VBA. For management packs, an approved AI-written summary at the top of the report is covered in AI summaries in Access reports. Excel users needing the same for invoices should read bulk-creating PDF invoices from Excel.
FAQ
Access report to PDF FAQ
Filtering, batches and errors