Printing and PDF Output in Windows Business Apps — Choosing Between System.Drawing.Printing, WPF, and Report Libraries
· Go Komura · CSharp, .NET, WinForms, WPF, Printing, PDF, Reports, Windows Development, Technical Consulting
Printing from a Windows business app is never as simple as “just draw something with PrintDocument and call it done.” Page breaks that never kick in, margins that drift, a preview that doesn’t match the actual output, a print dialog popping up when all you wanted was a PDF — these problems almost always trace back to starting implementation without understanding how printing actually works under the hood.
This article organizes four options — WinForms’ System.Drawing.Printing, WPF’s FlowDocument/FixedDocument, PDF output libraries, and reports generated via Excel/Word — around how to choose between them based on requirements.
1. The Bottom Line First
- For a simple list or an extension of existing WinForms code,
PrintDocumentis enough. Page breaks work by repeatedly firing thePrintPageevent untilHasMorePagesbecomesfalse; forget this and nothing past the first page gets printed. 12 - Print DPI is a completely different thing from screen DPI.
PageSettings.PrinterResolutionrepresents the printer’s resolution, and margins come in two layers:PageSettings.HardMarginX/HardMarginY(the printer’s physical non-printable area) andMargins(the logical margin the app specifies). Reuse screen coordinates directly for printing, and this mismatch in DPI and coordinate systems will throw the layout off. 345 - In WPF, Windows’ standard printing paths split into two: the GDI print path and the XPS print path. WPF applications natively use the XPS print path, and jobs sent to a printer that doesn’t support XPSDrv are automatically converted to GDI format. 67
- WPF documents follow two different design philosophies: the “flowing”
FlowDocumentand the “fixed-layout”FixedDocument.FlowDocumentprioritizes readability and re-lays out its content based on window size and resolution, whileFixedDocumentprioritizes fidelity to the display or print device. 89 - Specifying paper size or tray goes through WPF’s
PrintDialogtogether withPrintTicket/PrintQueue. Standard practice is to validate and merge your requested settings against the printer’s actual capabilities withPrintQueue.MergeAndValidatePrintTicketbefore printing. 1011 - Microsoft Print to PDF is a print driver built around interactive operation. It’s positioned as a print queue and driver package built into the OS, and by default it prompts with a save-location dialog on every run — which makes it unsuitable for unattended batch processing. 12
- Microsoft officially documents Office COM automation from a server or a service as unsupported and not recommended. Office is designed around an interactive desktop and a user profile, and running it in an unattended, non-interactive environment can cause unstable behavior or deadlocks. 13
- Windows services run in Session 0, so they can’t display dialogs and can’t rely on the default printer tied to a user’s session. Keep this boundary in mind from the very start when designing any resident process that involves printing. 14
- Forget to release GDI objects (handles) during long-running operation, and you’ll hit the per-process limit and crash. That limit is a finite value adjustable via the
GDIProcessHandleQuotaregistry setting — you can’t acquire an unlimited number of them. 15
Decision Table — Requirement × Approach
| Requirement | Suitable approach | Reason |
|---|---|---|
| Printing a few pages of a simple list or slip (WinForms) | PrintDocument |
Self-contained with standard classes alone; reuses existing GDI+ drawing code as-is |
| Reports with heavy ruled lines, multiple paper sizes, or complex layouts | A report library, or a fixed layout built with FixedDocument |
Writing your own coordinate math, page-break control, and Japanese typesetting is expensive |
| Large-volume batch printing (unattended overnight runs) | Programmatic PrintDocument.Print() / direct submission to XpsDocumentWriter |
Must complete with zero dialogs, and dependence on the default printer should also be avoided |
| Only PDF output is needed (no actual printing) | Generate the PDF directly with a PDF library | Doesn’t depend on the print dialog, the default printer, or spooler state |
| Preview is mandatory, an in-house review flow matters | PrintPreviewDialog (WinForms), DocumentViewer (WPF) |
A standard pre-print review UI is available out of the box |
| Want to reuse an existing Excel template | Direct generation via the Open XML SDK or similar, or limited COM automation | Reuses existing assets, but avoid COM automation inside a resident service |
The rest of this article looks at each of these in more detail.
2. The Bare Minimum on How Windows Printing Works
Windows’ printing architecture is built from a print spooler and a driver for each printer. An application only ever calls device-independent functions, and a print job can be sent to a wide range of output destinations — laser printers, vector plotters, raster printers, fax, and more. 16
There are broadly two printing paths.
- The GDI print path. When a Win32 GDI application prints, the GDI graphics engine either spools the drawing commands as an EMF (Enhanced Metafile), or works together with the printer driver to render a printable image directly and send it to the spooler. Traditional WinForms apps go through this path. 166
- The XPS print path. This path targets XPS (XML Paper Specification)-based printer drivers, which WPF applications natively use, sending jobs to the spooler in XPS document format. If the target printer isn’t an XPSDrv driver, the job is automatically converted to GDI format before being sent. 67
Why the on-screen look and the printed result don’t match. The most common cause is mixing up DPI. A screen Graphics object normally builds its coordinate system around roughly 96 DPI, while a printer’s Graphics runs at a far higher resolution — often around 600 DPI — determined by PageSettings.PrinterResolution. 3 Hand a pixel coordinate you calculated for the screen straight to the printing Graphics, and it gets drawn much smaller than you intended (or larger, depending on the resolution setting). The safe approach is to always build coordinates around hundredths of an inch or a real-world unit (millimeters or inches), so the layout doesn’t depend on the Graphics object’s resolution.
Another thing that’s easy to overlook is the printer’s physical non-printable area. Most printers can’t print within a few millimeters of the paper’s edge. This physical constraint can be retrieved via PageSettings.HardMarginX/HardMarginY, and the official documentation describes it as representing “the physical margin set by the printer.” 4 The logical margin the app specifies is PageSettings.Margins (1 inch on all sides by default), but even setting this to 0 won’t let output extend further inward than the printer’s hard margin. 5 Most bugs where “I set the margin to 0, but the edge still gets cut off / it’s still off from what I expected” trace back to not accounting for this hard margin.
3. WinForms: System.Drawing.Printing in Practice
WinForms printing is built around the PrintDocument component. The basic flow: create a PrintDocument, configure PrinterSettings and DefaultPageSettings, do the actual drawing in the PrintPage event, and start printing with the Print() method. 1
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Printing;
using System.Windows.Forms;
public sealed class ReportPrinter
{
// Data to print. Assumes one line per order-slip item
private readonly IReadOnlyList<string> _lines;
private int _lineIndex;
public ReportPrinter(IReadOnlyList<string> lines) => _lines = lines;
public void Print(string printerName)
{
using var document = new PrintDocument();
document.DocumentName = "Order Details";
if (!string.IsNullOrEmpty(printerName))
{
document.PrinterSettings.PrinterName = printerName;
}
// Becomes false here if an unknown printer name is given (not usable for offline detection)
if (!document.PrinterSettings.IsValid)
{
throw new InvalidOperationException(
$"The specified printer was not found: {printerName}");
}
_lineIndex = 0;
document.PrintPage += Document_PrintPage;
document.Print();
}
private void Document_PrintPage(object? sender, PrintPageEventArgs e)
{
// MarginBounds is the drawable area reflecting the logical margin (Margins).
// PageBounds is the whole sheet; drawing inside the hard margin still gets clipped
using var font = new Font("Meiryo", 10);
float lineHeight = font.GetHeight(e.Graphics);
float y = e.MarginBounds.Top;
while (_lineIndex < _lines.Count)
{
if (y + lineHeight > e.MarginBounds.Bottom)
{
// This page ends here. There's more to draw, so move to the next page
e.HasMorePages = true;
return;
}
e.Graphics.DrawString(_lines[_lineIndex], font, Brushes.Black,
e.MarginBounds.Left, y);
y += lineHeight;
_lineIndex++;
}
// All lines are drawn, so this is the last page
e.HasMorePages = false;
}
}
The classic pattern behind “the second page never comes out” is either forgetting to explicitly set HasMorePages to false (the default is already false), or the opposite — a branching mistake that leaves it stuck at false on every exit. Given that the PrintPage event fires repeatedly until this property becomes false, make sure you explicitly check “are there still lines left to draw” before setting it. 12 If you need to reflect per-page settings (paper size, orientation, and so on), you can also make use of the QueryPageSettings event alongside PrintPage.
The classic pattern behind “the margin is off” is building coordinates around e.Graphics.VisibleClipBounds or e.PageBounds while ignoring MarginBounds (the area reflecting the logical margin) and HardMarginX/HardMarginY (the printer’s physical constraint). 4 This is especially prone to trouble when the development machine’s printer happens to have a small hard margin, so nothing goes wrong there, but the printer at the delivery site has a larger one and ruled lines or fields end up clipped. Verifying printing across multiple printer models is a step that’s easy to overlook during development.
If you need to provide a preview, use PrintPreviewDialog; just assign the same PrintDocument to its Document property and you can reuse your PrintPage logic as-is. 1 If you want to switch output based on printer resolution, retrieve the available options from PrinterSettings.PrinterResolutions and set PageSettings.PrinterResolution. 3
4. WPF: FlowDocument / FixedDocument and PrintDialog
In WPF, whether you build around FlowDocument or FixedDocument depends on the nature of the document.
FlowDocument. A readability-first document that dynamically re-lays out its content based on runtime variables like window size and font settings. It comes with built-in support for search, paging, and switching view modes. 8FixedDocument. A fidelity-first, WYSIWYG document where the application fully controls the layout. It suits cases that need high-precision reproducibility on a display or print device. 9
The baseline rule of thumb: use FlowDocument for an internal report where flowing readability matters, and FixedDocument for a slip or label where you need ruled-line and field positions pinned down to the pixel.
Basic printing goes through System.Windows.Controls.PrintDialog. This class is distinct from System.Windows.Forms.PrintDialog — it’s WPF-only. 10
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
public static class FlowDocumentPrinter
{
public static void Print(FlowDocument document, string jobName)
{
var printDialog = new PrintDialog();
// Only print if the user clicked OK
if (printDialog.ShowDialog() != true)
{
return;
}
// FlowDocument assumes different page sizes for display vs. printing,
// so match the print target's page size/margins to the printer's printable area
var paginator = ((IDocumentPaginatorSource)document).DocumentPaginator;
paginator.PageSize = new Size(
printDialog.PrintableAreaWidth, printDialog.PrintableAreaHeight);
printDialog.PrintDocument(paginator, jobName);
}
}
If you need fine-grained control over paper size, input tray, duplex printing, and the like, use PrintQueue together with PrintTicket/PrintCapabilities. PrintTicket represents instructions for the print job, and PrintCapabilities represents what the printer actually supports; the standard practice is to merge and validate your requested settings into printer-specific valid values via PrintQueue.MergeAndValidatePrintTicket before use. 711
using System.Linq;
using System.Printing;
public static class DuplexPrintTicketBuilder
{
public static PrintTicket BuildDuplexTicket(PrintQueue printQueue)
{
PrintCapabilities capabilities = printQueue.GetPrintCapabilities();
// Check up front whether the printer supports duplex printing (long-edge binding)
bool supportsDuplex = capabilities.DuplexingCapability
.Contains(Duplexing.TwoSidedLongEdge);
var requestedTicket = new PrintTicket();
if (supportsDuplex)
{
requestedTicket.Duplexing = Duplexing.TwoSidedLongEdge;
}
// Merge only the duplex-printing request into the user's default PrintTicket.
// Unsupported settings are automatically replaced with valid values
ValidationResult result = printQueue.MergeAndValidatePrintTicket(
printQueue.UserPrintTicket, requestedTicket);
return result.ValidatedPrintTicket;
}
}
If you build a fixed-layout report with FixedDocument and send it straight down the XPS print path, add the job to the PrintQueue using XpsDocumentWriter’s Write/WriteAsync methods. If the target is a printer that doesn’t support XPSDrv, it’s automatically converted to GDI format internally before being sent, so the calling code doesn’t need to care which path is used. 7
5. Options for PDF Output
The requirement “we want PDF output” actually changes your design depending on which of the following three it really means.
- As an extension of printing, it’s enough if the user can manually choose to save as a PDF
- Generating a PDF from within the app, going through the print dialog
- No printing at all — generating large volumes of PDF files in an unattended batch
Microsoft Print to PDF is a feature aimed at cases 1 and 2. It’s a feature built into Windows 10 and later, positioned as a print queue and driver package, and any printing app sees it as just another ordinary printer. 12 In practice, though, it’s a mechanism built around interactive behavior — “ask for a save-location filename every time you print” — and the only officially documented control mechanism sits at the granularity of an administrator removing the print queue and its driver package from an image. 12 By design, it’s a poor fit for use case 3 — quietly emitting PDFs to a fixed file path in a large-volume, unattended batch job. If you need case 3, the straightforward approach is a library that generates PDFs directly, bypassing the printing pipeline altogether.
Points of comparison when choosing a PDF generation library — before favoring any specific product, laying out your own requirements along the following axes keeps the selection from wobbling.
| Axis | What to check |
|---|---|
| Licensing | Is it OSS (MIT/Apache-family, or GPL/AGPL-family) or commercially licensed? If you distribute or sell your own app externally, always check the primary source (the license text itself, the vendor’s official site) to confirm the library’s license terms — source-disclosure obligations, redistribution conditions, whether commercial use is allowed — don’t conflict with how you distribute your own app |
| Handling of Japanese fonts | Does it support subset-embedding Japanese fonts? Can it embed the font’s actual data into the PDF (so text doesn’t garble in a viewing environment that lacks the font)? If the business needs vertical writing or variant characters (IVS), what’s the support status? |
| Report-oriented features | Does it support template-based report definitions? Barcode/2D-code generation, digital signatures, PDF/A (long-term archival standard) support, and how easily an existing paper-report layout can be ported over as-is |
| Dependencies / runtime environment | Does it run in a server environment (a GUI-less Windows Server, a container)? Does it depend on native libraries? What’s its track record running on .NET Core/.NET (non-Framework)? |
| Performance | Memory usage when batch-generating large page counts or large job volumes; behavior under parallel generation |
License terms vary by product and version, so always check the vendor’s official documentation and the license text itself before deciding to adopt one. This article doesn’t recommend any specific library or product by name — it only lays out the “axes” for comparison.
6. Reports via Excel / Word as an Option
If you already have reports operating on an Excel template, a design that leverages that existing asset can sometimes be more realistic than rebuilding from scratch. There are broadly two approaches.
- Generate/edit xlsx/docx directly with the Open XML SDK or similar. This reads and writes the file format (ZIP + XML) directly without launching the Excel/Word executable, so it runs stably even in a server environment. It fits well with a design that keeps the template’s ruled lines and formatting intact and just plugs in values — see “How to Build Excel Report Output — COM / Open XML / Templates” for details.
- Drive
Excel.Applicationdirectly through Excel COM Interop. This gets chosen when you want to reuse an existing Excel workbook as-is, macros and complex recalculation included, but it’s prone to a problem where leftover EXCEL.EXE processes linger due to unreleased COM references — countermeasures and decision criteria are covered in “The EXCEL.EXE-Stays-Running Problem in C# Excel Automation”.
What matters here is that Microsoft explicitly and officially documents Office COM automation from the server side or a Windows service as “not recommended and unsupported.” Office applications are designed around an interactive desktop and a logged-on user’s profile, and running one in an unattended, non-interactive environment is documented as potentially causing unstable behavior or deadlocks. 13 From a licensing standpoint too, using server-side automation to expose Office functionality to clients isn’t something the standard EULA anticipates. 13
So if you want to build report output into a resident service or a batch job, even if you’re reusing an existing Excel template, it’s safer to avoid a configuration that actually launches Excel.exe at runtime (COM Interop) and lean toward direct file generation via the Open XML SDK or similar. If COM Interop is genuinely unavoidable, the realistic split is to configure it as a resident application (not a service) running inside an interactively logged-on user session.
7. Practical Pitfalls
Printing from a Windows Service — Session 0
It’s a fairly common consulting request to want to build printing into a resident service, but Windows services run in Session 0. Since Windows Vista, Session 0 has been reserved exclusively for services and processes not associated with an interactive user, and it’s isolated from the user’s interactive session. 14 Because of this isolation, a service can display a dialog box, but the user never sees it — and any code that waits on user input (such as waiting for a print dialog to be dismissed) will look as if it’s hung. 14
Another pitfall that trips people up in practice is how network printers and per-user-mapped printers are visible. A network printer a user connected to after logging on is usually visible tied to that user’s interactive session, and it doesn’t appear the same way from a different session (such as the process context of a service running in Session 0). Any resident process that involves printing needs to confirm, right at the design stage, “which printers are visible from which account and which session.” The overall picture of session isolation is covered in “Understanding Windows Session Isolation,” and the basics of implementing and operating a Windows service are covered in “How to Build and Operate a Windows Service”.
Behavior When the Printer Is Missing or Offline
Specify a printer name in PrinterSettings.PrinterName that doesn’t exist, or run in an environment where no default printer is set, and an exception or failure occurs the moment printing is attempted. At a minimum, validate up front with PrinterSettings.IsValid, and design for retry and error notification assuming print failures will happen. When a network printer is temporarily offline, a job can still land in the spooler while the actual output stays stuck, so don’t treat “the job was submitted” as the end of the story — consider also building in a way to check job status when needed.
The Danger of Relying on the Default Printer
An implementation that never explicitly specifies PrinterSettings.PrinterName and always relies on “the default printer” becomes riskier the longer it stays in operation. That’s because environmental changes — a user switching their default to a different printer, or carrying a laptop somewhere else where the default printer changes — can send output to an unintended destination. Business-critical reports should explicitly hold the target printer name in a config file or an in-app settings screen, designed so they’re never dragged along by a change in the default printer.
GDI Handle Leaks Over Long-Running Operation
Write printing code that doesn’t reliably release GDI/GDI+ resources like Graphics, Font, Brush, or Pen via using or Dispose(), and after long-running operation GDI object handles run out, at which point drawing-related API calls start failing. GDI object handles have a default per-process limit, and they’re a finite resource adjustable between 256 and 65,536 via the GDIProcessHandleQuota registry setting. 15 The more frequently a resident application prints, the more likely this kind of leak is to become a bug that only surfaces in production. How to investigate and isolate handle leaks is covered concretely in “Investigating a Long-Running Crash in an Industrial Camera — The Handle-Leak Edition,” which walks through an industrial-camera long-running-crash case.
Related Articles
- How to Build Excel Report Output — COM / Open XML / Templates
- The EXCEL.EXE-Stays-Running Problem in C# Excel Automation — COM Reference Release Patterns and the Replacement Decision
- How to Build and Operate a Windows Service — From Choosing Between It and Task Scheduler to Turning a BackgroundService into a Service
- Understanding Windows Session Isolation — Session 0, RDP, and Concurrent Multi-User Sessions
- Investigating a Long-Running Crash in an Industrial Camera — The Handle-Leak Edition
Related Consulting Areas
KomuraSoft LLC handles technical consulting on designing printing and report-output features for Windows business apps, rebuilding reports that leverage existing Excel assets, and designing print/output for resident services.
References
-
Microsoft Learn, PrintDocument Class. On the role of
PrintDocument(configuringDocumentName/PrinterSettingsand starting printing withPrint()), and the guidance that printing from WPF should use theSystem.Printingnamespace instead. ↩ ↩2 ↩3 ↩4 -
Microsoft Learn, PrintPageEventArgs.HasMorePages Property. On the default value being
false, and on thePrintPageevent firing repeatedly until this property becomesfalse. ↩ ↩2 -
Microsoft Learn, PageSettings.PrinterResolution Property. On the default print resolution for a page being the printer’s default resolution, and on being able to retrieve the list of selectable resolutions from
PrinterSettings.PrinterResolutions. ↩ ↩2 ↩3 -
Microsoft Learn, PageSettings.HardMarginX Property. On the hard margin representing the physical margin set by the printer. ↩ ↩2 ↩3
-
Microsoft Learn, PageSettings.Margins Property. On the default page margin being 1 inch on all sides, and on being able to compute the printable area by combining it with the
Boundsproperty in thePrintPageevent. ↩ ↩2 -
Microsoft Learn, Windows Print Path Overview. On Windows having two main print paths — the GDI print path (originating from Win32 applications) and the XPS print path (originating from WPF applications or the XPS Print API). ↩ ↩2 ↩3
-
Microsoft Learn, Printing documents overview. On WPF applications using the XPS print path, basic printing via
PrintDialog, advanced printing viaPrintTicket/PrintCapabilities/PrintQueue/XpsDocumentWriter, and automatic conversion to GDI for printers that don’t support XPSDrv. ↩ ↩2 ↩3 ↩4 -
Microsoft Learn, Flow Document Overview. On
FlowDocumentbeing a readability-first document that re-lays out content based on runtime variables such as window size and resolution. ↩ ↩2 -
Microsoft Learn, FixedDocument Class. On
FixedDocumentbeing a WYSIWYG design that prioritizes faithful reproduction on a display or print device, with a design philosophy distinct fromFlowDocument’s. ↩ ↩2 -
Microsoft Learn, PrintDialog Class. On
System.Windows.Controls.PrintDialogbeing a dialog that configures aPrintTicketandPrintQueuebased on user input, and on it being a class distinct fromSystem.Windows.Forms.PrintDialog. ↩ ↩2 -
Microsoft Learn, How to: Validate and Merge PrintTickets. On the procedure of checking a printer’s supported capabilities with
PrintQueue.GetPrintCapabilitiesand merging/validating your requested settings into a printer-specific validPrintTicketwithMergeAndValidatePrintTicket. ↩ ↩2 -
Microsoft Learn, RemoveMPDW. On Microsoft Print to PDF being an optional feature installed by default, and being configured/removed at the granularity of a print queue and driver package. ↩ ↩2 ↩3
-
Microsoft Support, Considerations for server-side Automation of Office. On Microsoft not recommending or supporting Office automation from unattended, non-interactive client applications — including ASP, ASP.NET, DCOM, and NT services — and on Office being designed around an interactive desktop and a user profile. ↩ ↩2 ↩3
-
Microsoft Learn, Service Changes for Windows Vista - Session 0 Isolation. On Session 0 having been reserved, since Windows Vista, exclusively for applications not associated with services or an interactive user, and on services no longer being able to directly display dialog boxes as a result. ↩ ↩2 ↩3
-
Microsoft Learn, GDI Objects. On GDI object handles having a default per-process limit, and on that limit being adjustable between 256 and 65,536 via the
GDIProcessHandleQuotaregistry value. ↩ ↩2 -
Microsoft Learn, Introduction to printing. On Windows’ printing architecture being built from a spooler and printer drivers, and on the mechanism by which a Win32 GDI application’s drawing commands are spooled as EMF. ↩ ↩2
Related Articles
Recent articles sharing the same tags. Deepen your understanding with closely related topics.
Pinpointing "Slow" with PerfView and dotnet-trace — A Practical Introduction to .NET Performance Investigation
When a business app is "slow," "pegs the CPU," or "occasionally freezes," which tool should you reach for, and what should you look at? T...
Integrating Entra ID Authentication into WinForms/WPF Apps — A Practical Architecture with MSAL.NET and the WAM Broker
A practical, hands-on look at integrating Entra ID (formerly Azure AD) authentication into WinForms/WPF desktop apps: the public client m...
Windows App Outsourcing and Contract Development: What to Sort Out Before You Ask
Before commissioning Windows app outsourcing or contract development, here is how to sort out existing software modification, device inte...
Why Use the .NET Generic Host and BackgroundService in Desktop Apps
How to use the Generic Host and BackgroundService to organize startup, periodic processing, shutdown, logging, configuration, and DI in W...
An Introduction to Windows Event Log and ETW — Putting Your Business App's Logs on the OS's Standard Mechanisms
Are you relying on file logging alone for your Windows business app? Event Log and ETW are records visible on a different layer — to oper...
Related Topics
These topic pages place the article in a broader service and decision context.
Windows Technical Topics
Topic hub for KomuraSoft LLC's Windows development, investigation, and legacy-asset articles.
UI Threading & Timers
Topic page for WPF / WinForms UI threading, async flow, Dispatcher usage, and timer decisions.
Where This Topic Connects
This article connects naturally to the following service pages.
Windows App Development
Designing and implementing printing and PDF output features for business apps falls within the scope of Windows app development consulting.
Frequently Asked Questions
Common questions about the topic of this article.
- Should I build printing with PrintDocument or with WPF's printing stack?
- The straightforward choice is PrintDocument if the UI is WinForms, and FlowDocument/FixedDocument if it's WPF. There's no need to mix technologies — matching the existing app's UI framework is the practical decision criterion. If you're choosing WPF for something new, decide up front whether the report is flow-oriented or fixed-layout, so you know whether to center the design on FlowDocument or FixedDocument.
- Should we buy a report library?
- If the cost of building complex, ruled-line-heavy reports, multi-paper-size support, embedded Japanese fonts, or vertical writing yourself in GDI+/WPF drawing is high, a commercial report library's adoption cost often works out cheaper. Conversely, a simple report such as an A4 list is often perfectly served by a homegrown implementation with PrintDocument or FixedDocument — the safe approach is to estimate the complexity of the requirements first, then decide.
- Is it fine to build a setup that only produces PDFs and never prints?
- Absolutely. A PDF is just one output format, and generating it directly with a PDF library that doesn't depend on the print dialog or the default printer often fits batch processing and automation much better. Microsoft Print to PDF is a driver built around interactive operation and isn't suited to unattended PDF generation.
- Can this also support label printers or receipt printers?
- It's possible, but extending the general-purpose PrintDocument/FixedDocument approach can be difficult in some cases. Most label printers have their own command language or SDK, and are often controlled through a path separate from Windows' standard printing paths. Start by checking whether the target model behaves as an ordinary Windows printer driver, or requires control through a dedicated SDK.
Author Profile
Profile page for the article author.
Go Komura
Representative of KomuraSoft LLC
Focused on Windows software development, technical consulting, and investigations into failures that are difficult to reproduce.
Public links