수정 이력(7건, 최종 수정 2026년 09월 03일)
이 글에 적용한 변경 사항의 기록입니다. 보관해 둔 수정 전 버전은 DOI가 부여된 고정 URL에서 읽을 수 있습니다.
- permalink·저자 표기·지식 맵 래퍼·깨진 내부 링크 등 CI가 지적한 표시용 수정을 반영했습니다. 본문의 기술적인 주장은 바꾸지 않았습니다.
- 기사 서두에 「이 기사의 지식 맵」 절을 추가했습니다. 본문에서 다루는 개념과 그 관계를 요약·그림·상세 페이지 링크로 정리한 것입니다. 본문의 주장은 바꾸지 않았습니다.
- 외부 리뷰(1283건) 대응으로 본문을 업데이트했습니다. 개별 변경 내용은 아래 이력을 참조하세요.
- `PrintableArea`와의 교집합을 취하는 예에서 좌표계 변환이 빠져 있었습니다. `MarginBounds`와 `PrintableArea` 모두 용지 왼쪽 위 기준 좌표이지만, `OriginAtMargins`가 기본값 `false`일 때 `Graphics`의 원점은 인쇄 가능 영역의 왼쪽 위에 있습니다. 그대로 그리면 하드 마진만큼 오른쪽 아래로 밀려 오른쪽과 아래가 잘립니다 ── 이 예가 막으려던 증상 그 자체였습니다. 그리기 전에 `Graphics` 좌표로 옮기도록 하고, `OriginAtMargins`를 바꾸면 보정값도 바뀐다는 점을 명시했습니다.
- 여백 그림에서 `MarginBounds`를 하드 마진 안쪽에 넣어 그리던 것을 고쳤습니다. `MarginBounds`는 `Margins`만으로 계산되며, 인쇄 가능 영역 안으로 맞춰지지 않습니다. `Margins`를 하드 마진보다 작게 하면 인쇄 가능 영역을 벗어나므로, 안쪽에 들어 있는 관계로 읽으면 잘리는 원인을 잘못 짚게 됩니다. 둘이 따로 정해지는 그림으로 다시 그리고, `PrintableArea`와의 교집합을 취하는 코드 예와, `OriginAtMargins` 기본값이 `false`라 원점 결정에 `Margins`가 쓰이지 않는다는 점을 추가했습니다.
- 선택지를 좁히는 결정 트리와 구현 전 점검을 정리 절로 새로 넣었습니다. 함께 GDI와 XPS 인쇄 경로 그림, 여백을 안쪽에 넣어 그린 그림, 라이브러리 비교 절차, 용어 설명을 추가했습니다.
- 인쇄 좌표 단위 설명의 오기를 「100분의 1인치 단위(hundredths of an inch)」로 고쳤습니다.
- 최초 공개
이 글을 인용하기(DOI(등록된 아카이브): 10.5281/zenodo.21635386)
아래 DOI는 이전에 등록된 아카이브를 가리키며 현재 본문과 다를 수 있습니다. 현재 본문을 참조할 때는 이 페이지의 URL을 사용하세요.
Go Komura (2026). 「Windows 업무 앱의 인쇄와 PDF 출력 ── System.Drawing.Printing / WPF / 장표 라이브러리 구분」. 합동회사 코무라소프트. https://comcomponent.com/ko/blog/windows-app-printing-pdf-guide/
- DOI(등록된 아카이브)
- 10.5281/zenodo.21635386
- DOI(마지막 등록 버전)
- 10.5281/zenodo.21635387
Windows 업무 앱의 인쇄는 「PrintDocument로 대충 그리면 끝」이 아닙니다. 페이지가 나뉘지 않고, 여백이 어긋나고, 미리보기와 실제 출력이 다르고, PDF만 필요한데 인쇄 대화 상자가 끼어듭니다 ── 이런 문제는 대개 인쇄 메커니즘 자체를 오해한 채로 구현을 시작한 것이 원인입니다.
이 글에서는 다음 네 가지 선택지를 요건별로 어떻게 가려 쓸지라는 관점으로 정리합니다. 이후에는 이 번호로 참조합니다.
| # | 선택지 | 주로 다루는 장 |
|---|---|---|
| ① | WinForms의 System.Drawing.Printing(PrintDocument) |
3장 |
| ② | WPF의 FlowDocument / FixedDocument |
4장 |
| ③ | PDF 출력 라이브러리로 직접 생성 | 5장 |
| ④ | Excel / Word를 통한 장표(Open XML SDK, COM 자동화) | 6장 |
1. 먼저 결론
- 간단한 목록이나 기존 WinForms 자산의 연장이면
PrintDocument로 충분합니다. 페이지 나눔은PrintPage이벤트를HasMorePages가false가 될 때까지 반복 호출하는 구조이며, 이를 빼먹으면 2페이지부터 나오지 않습니다. 12 - 인쇄 DPI는 화면 DPI와 별개입니다.
PageSettings.PrinterResolution은 프린터 해상도를 나타내며, 여백은PageSettings.HardMarginX/HardMarginY(프린터가 물리적으로 가진 인쇄 불가 영역)와Margins(앱이 지정하는 논리 여백)의 이중 구조입니다. 화면 좌표를 그대로 인쇄에 쓰면 이 두 DPI·좌표계 차이로 레이아웃이 어긋납니다. 345 - WPF에서는 Windows 표준 인쇄 경로가 GDI 인쇄 경로와 XPS 인쇄 경로 두 계통으로 나뉩니다. WPF 애플리케이션은 본래 XPS 인쇄 경로를 쓰고, XPSDrv 미지원 프린터로 보낼 때는 자동으로 GDI 형식으로 변환됩니다. 67
- WPF 문서는 「흐름형」인
FlowDocument와 「고정 레이아웃」인FixedDocument로 설계 사상이 다릅니다.FlowDocument는 가독성 우선으로 창 크기나 해상도에 따라 다시 배치되고,FixedDocument는 표시·인쇄 장치의 충실도를 우선하는 구성입니다. 89 - 용지나 트레이 지정은 WPF의
PrintDialog와PrintTicket/PrintQueue로 합니다. 인쇄 전에PrintQueue.MergeAndValidatePrintTicket으로 프린터의 실제 능력에 대해 검증·병합하는 것이 정석입니다. 1011 - Microsoft Print to PDF는 대화형 조작을 전제로 한 인쇄 드라이버입니다. OS에 기본 탑재된 인쇄 큐와 드라이버 패키지라는 위치이며, 실행할 때마다 저장 위치를 고르는 대화 상자가 끼는 구성이 기본이라 무인 배치 처리에는 맞지 않습니다. 12
- 서버나 서비스에서의 Office COM 자동화는 Microsoft가 공식으로 비지원·비권장합니다. Office는 대화형 데스크톱과 사용자 프로필을 전제로 설계되어 있으며, 무인·비대화 환경에서는 불안정한 동작이나 데드락이 날 수 있습니다. 13
- Windows 서비스는 세션 0에서 동작하므로 대화 상자를 띄울 수 없고, 사용자 세션에 묶인 기본 프린터에도 의존할 수 없습니다. 인쇄가 따르는 상주 처리를 설계할 때는 이 경계를 먼저 의식하세요. 14
- 장시간 가동에서 GDI 객체(핸들)를 해제하지 않으면 프로세스당 상한에 닿아 크래시합니다. 상한은 레지스트리
GDIProcessHandleQuota로 조정 가능한 유한 값이며, 무한히 확보할 수 있는 것이 아닙니다. 15
판단표 ── 요건 × 수단
| 요건 | 적합한 수단 | 선택지 | 이유 |
|---|---|---|---|
| 간단한 목록·전표를 몇 장 인쇄하고 싶다(WinForms) | PrintDocument |
① | 표준 클래스만으로 끝나고, 기존 GDI+ 그리기 자산을 그대로 재사용할 수 있다 |
| 괘선이 많은 장표, 여러 용지·복잡한 레이아웃 | 장표 라이브러리, 또는 FixedDocument 고정 레이아웃 |
② 또는 ③ | 좌표 계산·페이지 나눔 제어·일본어 조판을 직접 쓰는 비용이 크다 |
| 대량 배치 인쇄(야간 무인 실행) | 프로그램적으로 PrintDocument.Print()/XpsDocumentWriter에 직접 전송 |
① 또는 ② | 대화 상자를 전혀 내지 않고 끝내야 하며, 기본 프린터 의존도 피하고 싶다 |
| PDF 저장만 하면 된다(인쇄는 쓰지 않음) | PDF 생성 라이브러리로 PDF를 직접 만든다 | ③ | 인쇄 대화 상자나 기본 프린터, 스풀러 상태에 의존하지 않는다 |
| 미리보기가 필수, 사내 확인 흐름이 중요 | PrintPreviewDialog(WinForms), DocumentViewer(WPF) |
① 또는 ② | 인쇄 전 확인의 표준 UI를 그대로 쓸 수 있다 |
| 기존 Excel 템플릿을 재사용하고 싶다 | Open XML SDK 등으로 직접 생성, 또는 한정적인 COM 자동화 | ④ | 자산을 재사용할 수 있으나, 상주 서비스에서의 COM 자동화는 피한다 |
이하는 각 항목의 상세입니다.
그림의 실선은 항상 성립하는 관계, 점선은 조건이 붙는 관계입니다(성립 조건은 상세 페이지의 관계별 설명에 적혀 있습니다). 관계 전체 목록(총 21건, 근거와 확신도 포함)과 주요 개념의 정의는 지식 맵 상세 페이지에 정리되어 있습니다(일본어). 데이터: JSON-LD / Turtle
2. Windows 인쇄 메커니즘, 최소한만
이 장부터 쓰는 용어를 먼저 정의합니다.
| 용어 | 의미 |
|---|---|
| DPI(dots per inch) | 1인치당 도트 수. 해상도 단위. 화면은 보통 96 DPI 전후, 프린터는 600 DPI 전후로 자릿수가 다르다 |
| 스풀러 | 앱에서 받은 인쇄 작업을 일단 쌓아 두고, 순서대로 프린터 드라이버에 넘기는 Windows 서비스. 프린터가 멈춰도 앱 쪽은 먼저 진행할 수 있는 구조 |
| EMF(확장 메타파일) | GDI 그리기 명령을 기록한 파일 형식. GDI 인쇄 경로에서는 이 형태로 스풀러에 전달된다 |
| WYSIWYG(What You See Is What You Get) | 화면에 보이는 것이 그대로 출력되는 상태. 인쇄에서는 「미리보기와 실제 종이가 일치한다」는 뜻 |
| 세션 0 | Windows Vista 이후, 서비스처럼 대화형 사용자에 묶이지 않은 프로세스 전용으로 예약된 세션. 로그온 중인 사용자 화면과 분리되어 있다14 |
| 하드 마진 | 프린터가 물리적으로 인쇄할 수 없는, 용지 가장자리 띠. 앱에서는 좁힐 수 없다 |
Windows 인쇄 아키텍처는 인쇄 스풀러와 각 프린터용 드라이버로 구성됩니다. 애플리케이션은 장치에 의존하지 않는 함수만 호출하면, 레이저 프린터, 벡터 플로터, 래스터 프린터, FAX 등 다양한 출력 대상으로 인쇄 작업을 보낼 수 있습니다. 16
인쇄 경로는 크게 두 계통입니다.
- GDI 인쇄 경로. Win32 GDI 애플리케이션이 인쇄하면, GDI 그래픽스 엔진이 그리기 명령을 EMF(확장 메타파일)로 스풀하거나, 프린터 드라이버와 연동해 인쇄 가능한 이미지를 직접 렌더링해 스풀러로 보냅니다. 기존 WinForms 앱은 이 경로를 탑니다. 166
- XPS 인쇄 경로. XPS(XML Paper Specification) 기반 프린터 드라이버용 경로이며, WPF 애플리케이션은 본래 이쪽을 쓰고 XPS 문서 형식으로 스풀러에 보냅니다. 대상 프린터가 XPSDrv 드라이버가 아니면 GDI 형식으로 자동 변환된 뒤 보내집니다. 67
그림으로 그리면 다음과 같습니다. 어느 경로를 타든, 최종적으로는 스풀러와 드라이버를 거친다는 점은 같습니다.
flowchart LR
WF["WinForms 앱<br/>선택지 ① PrintDocument"] --> GDI["GDI 인쇄 경로<br/>그리기 명령을 EMF로 스풀"]
WPF["WPF 앱<br/>선택지 ② FlowDocument·FixedDocument"] --> XPS["XPS 인쇄 경로<br/>XPS 문서로 스풀"]
XPS -->|"XPSDrv 대응 프린터"| SPL["인쇄 스풀러"]
XPS -->|"XPSDrv 미지원 프린터"| CONV["GDI 형식으로 자동 변환"]
CONV --> SPL
GDI --> SPL
SPL --> DRV["프린터 드라이버"]
DRV --> OUT["프린터·FAX<br/>Microsoft Print to PDF 등"]
그림 1: GDI 인쇄 경로와 XPS 인쇄 경로. WPF에서 XPSDrv 미지원 프린터로 보내면 도중에 GDI 형식으로 변환됩니다
왜 화면 모습과 인쇄 결과가 어긋나는가. 가장 흔한 원인은 DPI를 혼동하는 것입니다. 화면 Graphics는 보통 96 DPI 전후로 좌표계가 짜여 있지만, 프린터 Graphics는 PageSettings.PrinterResolution으로 정해지며, 많은 경우 600 DPI 전후의 훨씬 높은 해상도로 동작합니다. 3 화면용으로 계산한 픽셀 좌표를 그대로 인쇄용 Graphics에 넘기면, 의도보다 훨씬 작게(또는 고해상도 설정에 따라서는 크게) 그려집니다. 인쇄에서는 항상 100분의 1인치 단위(hundredths of an inch)나 실측(밀리·인치)을 기준으로 좌표를 짜고, Graphics 해상도에 의존하지 않는 설계가 안전합니다.
또 하나 놓치기 쉬운 것이 프린터가 가진 물리적 인쇄 불가 영역입니다. 많은 프린터는 용지 가장자리에서 수 밀리 범위를 인쇄하지 못합니다. 이 물리 제약은 PageSettings.HardMarginX/HardMarginY로 얻을 수 있고, 공식 문서에서도 「프린터가 설정하는 물리적 여백을 나타낸다」고 설명합니다. 4 앱 쪽에서 지정하는 논리 여백은 PageSettings.Margins(기본값은 상하좌우 1인치)이지만, 이를 0으로 해도 프린터 하드 마진보다 안쪽에는 출력할 수 없습니다. 5 「여백을 0으로 했는데 가장자리가 잘리거나 생각보다 어긋난다」는 장애의 상당수는 이 하드 마진을 의식하지 않은 것이 원인입니다.
여기서 중요한 것은 이 둘이 따로 정해진다는 점입니다. 하드 마진은 프린터가 정하고, MarginBounds는 앱이 지정한 Margins만으로 계산됩니다. MarginBounds가 인쇄 가능 영역 안으로 자동으로 맞춰지지는 않습니다. 관계를 그림으로 그리면 한쪽이 다른 쪽 안에 들어가는 관계가 아니라 이렇게 됩니다.
flowchart TB
subgraph PB["PageBounds ── 용지 전체 크기"]
HM["인쇄 가능 영역<br/>PrintableArea·HardMarginX·HardMarginY<br/>프린터가 정한다. 앱에서는 좁힐 수 없다"]
MB["MarginBounds<br/>Margins만으로 계산된다<br/>인쇄 가능 영역 안으로 맞춰지지 않는다"]
end
HM --> SAFE["확실히 나오는 범위 ── 둘이 겹치는 부분"]
MB --> SAFE
그림 2: MarginBounds와 하드 마진은 따로 정해집니다. Margins를 하드 마진보다 작게 하면 MarginBounds는 인쇄 가능 영역을 벗어납니다
- PageBounds: 용지 전체 크기.
- 하드 마진: 프린터가 정하는 물리적 인쇄 불가 영역.
HardMarginX/HardMarginY로 얻을 수 있고, 앱에서는 좁힐 수 없습니다. 4 인쇄 가능 영역 자체는PageSettings.PrintableArea로 가져옵니다(100분의 1인치 단위). 17 - MarginBounds: 앱이 지정한
Margins(기본값은 상하좌우 1인치)를 반영한 그리기 영역. 5
즉 Margins를 0으로 한 경우처럼 논리 여백이 하드 마진보다 작으면 MarginBounds는 인쇄 가능 영역 밖으로 나갑니다. 그 상태에서 MarginBounds를 기준으로 그리면, 나간 띠 안의 내용은 인쇄되지 않고 잘립니다. 「여백을 0으로 했는데 가장자리가 잘린다」의 정체가 이것입니다.
안전한 쪽으로 두려면 다음 중 하나를 택합니다.
- 논리 여백을 하드 마진 이상으로 둔다.
Margins의 각 변을HardMarginX/HardMarginY와 비교해, 작은 쪽을 올립니다 - 그리기에 쓸 사각형을
PrintableArea와 겹쳐서 구한다.MarginBounds를 그대로 쓰지 않고,Rectangle.Intersect로 인쇄 가능 영역과의 공통 부분을 취합니다
// PrintPage 이벤트 안. 단위는 모두 100분의 1인치
private void OnPrintPage(object? sender, PrintPageEventArgs e)
{
PageSettings page = e.PageSettings;
// 용지 왼쪽 위 기준 「인쇄 가능 영역」. 왼쪽 위 위치는 하드 마진 그 자체
var printableOnPaper = new RectangleF(
page.HardMarginX, page.HardMarginY,
page.PrintableArea.Width, page.PrintableArea.Height);
// MarginBounds는 Margins에서 온 계산값이며, 인쇄 가능 영역 안으로 맞춰지지 않는다.
// 교집합을 취하면 하드 마진이 큰 기종에서도 잘리지 않는 범위가 나온다.
// 여기까지는 「용지 왼쪽 위」를 원점으로 한 좌표
Rectangle safeOnPaper = Rectangle.Intersect(
e.MarginBounds, Rectangle.Round(printableOnPaper));
// 여기가 주의점. OriginAtMargins가 기본값 false일 때 e.Graphics의 원점은
// 「인쇄 가능 영역의 왼쪽 위」이며, 용지 왼쪽 위가 아니다. 용지 좌표 그대로
// 그리면 하드 마진만큼 오른쪽 아래로 밀려, 오른쪽과 아래가 인쇄 가능 영역에서
// 벗어나 잘린다 ── 이 예가 막으려는 증상 그 자체다.
// 그리기 전에 Graphics 좌표로 옮긴다
Rectangle safe = safeOnPaper;
safe.Offset(
-(int)Math.Round(page.HardMarginX),
-(int)Math.Round(page.HardMarginY));
e.Graphics!.DrawRectangle(Pens.Black, safe);
}
OriginAtMargins = true로 한 경우에는 원점 위치가 바뀌므로 이 보정도 바뀝니다. 어느 쪽으로 그릴지를 먼저 정한 뒤 좌표를 짜세요. 섞으면 기종만 바꿨을 때 위치가 어긋나는, 추적하기 어려운 장애가 됩니다.
좌표 원점에도 주의가 필요합니다. PrintDocument.OriginAtMargins의 기본값은 false이며, 이때 Graphics의 원점은 인쇄 가능 영역의 왼쪽 위에 놓이고, Margins 값은 원점 결정에 쓰이지 않습니다. 18 「Margins를 설정했는데 그만큼 밀리지 않는다」거나 반대로 「필요 이상으로 밀린다」고 느끼면 먼저 여기를 의심합니다. 원점을 여백 안쪽에 두고 싶다면 OriginAtMargins = true를 명시합니다.
3. WinForms: System.Drawing.Printing의 실무
WinForms 인쇄는 PrintDocument 컴포넌트를 중심으로 구성합니다. 기본 흐름은 PrintDocument를 만들고, PrinterSettings나 DefaultPageSettings를 설정하고, PrintPage 이벤트에서 실제 그리기를 한 뒤, Print() 메서드로 인쇄를 시작하는 것입니다. 1
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Printing;
using System.Windows.Forms;
public sealed class ReportPrinter
{
// 인쇄 대상 데이터. 1행 1건의 전표 명세를 가정
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 = "受注明細";
if (!string.IsNullOrEmpty(printerName))
{
document.PrinterSettings.PrinterName = printerName;
}
// 알 수 없는 프린터 이름을 지정한 경우 여기서 false가 된다(오프라인 감지에는 쓸 수 없음)
if (!document.PrinterSettings.IsValid)
{
throw new InvalidOperationException(
$"指定したプリンターが見つかりません: {printerName}");
}
_lineIndex = 0;
document.PrintPage += Document_PrintPage;
document.Print();
}
private void Document_PrintPage(object? sender, PrintPageEventArgs e)
{
// MarginBounds는 논리 여백(Margins)을 반영한 그리기 가능 영역.
// PageBounds는 용지 전체이며, 하드 마진보다 안쪽에 그려도 잘릴 수 있다
using var font = new Font("メイリオ", 10);
float lineHeight = font.GetHeight(e.Graphics);
float y = e.MarginBounds.Top;
while (_lineIndex < _lines.Count)
{
if (y + lineHeight > e.MarginBounds.Bottom)
{
// 이 페이지는 여기까지. 나머지가 있으므로 다음 페이지로
e.HasMorePages = true;
return;
}
e.Graphics.DrawString(_lines[_lineIndex], font, Brushes.Black,
e.MarginBounds.Left, y);
y += lineHeight;
_lineIndex++;
}
// 모든 행을 그렸으므로 이것이 마지막 페이지
e.HasMorePages = false;
}
}
「2페이지가 나오지 않는다」 장애의 전형은 HasMorePages를 명시적으로 false로 두지 않거나(기본값은 false), 반대로 조건 분기를 잘못 써서 항상 false인 채로 빠져 버리는 경우입니다. PrintPage 이벤트는 이 속성이 false가 될 때까지 반복 호출되므로, 「아직 그릴 행이 남았는지」를 명시적으로 판정한 뒤 설정하세요. 12 페이지마다 다른 설정(용지 크기나 방향 등)을 반영하려면 PrintPage에 더해 QueryPageSettings 이벤트도 활용할 수 있습니다.
「여백이 어긋난다」 장애의 전형은 e.Graphics.VisibleClipBounds나 e.PageBounds를 기준으로 좌표를 짜고, MarginBounds(논리 여백을 반영한 영역)와 HardMarginX/HardMarginY(프린터의 물리 제약)를 무시하는 것입니다. 4 특히 개발 머신 프린터는 하드 마진이 작아 우연히 문제가 없고, 납품처의 다른 기종에서 하드 마진이 커서 괘선이나 항목이 빠져 보이는 사고가 나기 쉽습니다. 여러 기종에서의 인쇄 검증은 개발 때 빠지기 쉬운 공정입니다.
미리보기를 제공할 때는 PrintPreviewDialog를 쓰고, Document 속성에 같은 PrintDocument를 할당하기만 하면 PrintPage 이벤트 로직을 그대로 재사용할 수 있습니다. 1 프린터 해상도에 따라 출력을 바꾸고 싶다면 PrinterSettings.PrinterResolutions에서 선택지를 가져와 PageSettings.PrinterResolution에 설정합니다. 3
4. WPF: FlowDocument / FixedDocument와 PrintDialog
WPF에서는 문서 성격에 따라 FlowDocument와 FixedDocument 중 무엇을 축으로 할지 달라집니다.
FlowDocument. 창 크기나 글꼴 설정 등 실행 시 변수에 따라 내용을 동적으로 다시 배치하는, 가독성 우선 문서입니다. 검색, 페이징, 표시 모드 변경 같은 기능을 기본으로 가집니다. 8FixedDocument. 애플리케이션 쪽이 레이아웃을 완전히 제어하는, 충실도 우선 WYSIWYG 문서입니다. 표시·인쇄 장치에 대해 고정밀도 재현이 필요할 때 맞습니다. 9
흐름형으로 읽기 쉬움을 우선하는 사내용 리포트라면 FlowDocument, 괘선 위치나 항목 위치를 픽셀 단위로 고정하고 싶은 전표·라벨이라면 FixedDocument가 기본입니다.
기본적인 인쇄는 System.Windows.Controls.PrintDialog로 합니다. 이 클래스는 System.Windows.Forms.PrintDialog와 다른, WPF 전용입니다. 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();
// 사용자가 OK를 누른 경우에만 인쇄한다
if (printDialog.ShowDialog() != true)
{
return;
}
// FlowDocument는 표시용과 인쇄용으로 페이지 크기 전제가 다르므로,
// 인쇄 대상의 페이지 크기·여백을 프린터의 인쇄 가능 영역에 맞춘다
var paginator = ((IDocumentPaginatorSource)document).DocumentPaginator;
paginator.PageSize = new Size(
printDialog.PrintableAreaWidth, printDialog.PrintableAreaHeight);
printDialog.PrintDocument(paginator, jobName);
}
}
용지 크기·급지 트레이·양면 인쇄 등을 세밀히 지정하려면 PrintQueue와 PrintTicket/PrintCapabilities를 씁니다. PrintTicket은 인쇄 작업에 대한 지시, PrintCapabilities는 프린터가 실제로 지원하는 기능을 나타내며, 지정한 설정은 PrintQueue.MergeAndValidatePrintTicket으로 프린터 고유의 타당한 값에 병합·검증한 뒤 쓰는 것이 정석입니다. 711
using System.Linq;
using System.Printing;
public static class DuplexPrintTicketBuilder
{
public static PrintTicket BuildDuplexTicket(PrintQueue printQueue)
{
PrintCapabilities capabilities = printQueue.GetPrintCapabilities();
// 양면 인쇄(장변 제본)를 지원하는 프린터인지 미리 확인한다
bool supportsDuplex = capabilities.DuplexingCapability
.Contains(Duplexing.TwoSidedLongEdge);
var requestedTicket = new PrintTicket();
if (supportsDuplex)
{
requestedTicket.Duplexing = Duplexing.TwoSidedLongEdge;
}
// 사용자 기본 PrintTicket에 대해 양면 인쇄 요구만 병합한다.
// 미지원 항목은 타당한 값으로 자동 치환된다
ValidationResult result = printQueue.MergeAndValidatePrintTicket(
printQueue.UserPrintTicket, requestedTicket);
return result.ValidatedPrintTicket;
}
}
고정 레이아웃 장표를 FixedDocument로 짜서 XPS 인쇄 경로에 직접 보낼 때는 XpsDocumentWriter의 Write/WriteAsync 메서드로 PrintQueue에 작업을 추가합니다. 대상이 XPSDrv 미지원 프린터이면 내부에서 GDI 형식으로 자동 변환된 뒤 보내지므로, 호출 쪽에서 경로를 의식할 필요는 없습니다. 7
5. PDF 출력의 선택지
「PDF로 내고 싶다」는 요건은, 실은 다음 셋 중 무엇을 가리키는지에 따라 설계가 달라집니다.
- 인쇄의 연장으로, 사용자가 수동으로 PDF 저장을 고르면 된다
- 앱 안에서 인쇄 대화 상자를 거쳐 PDF를 생성하고 싶다
- 인쇄는 전혀 하지 않고, 무인 배치로 PDF 파일만 대량 생성하고 싶다
Microsoft Print to PDF는 1과 2를 위한 기능입니다. Windows 10 이후 기본 탑재된, 인쇄 큐와 드라이버 패키지라는 위치의 기능이며, 인쇄 대상 앱에서는 보통 프린터 하나로 보입니다. 12 다만 실체는 「인쇄할 때마다 저장 파일 이름을 묻는다」는 대화형 동작을 전제로 한 구조이며, 공식으로도 인쇄 큐와 해당 드라이버 패키지 자체를 관리자가 이미지에서 제거할 수 있다는 단위로만 제어 수단이 제공됩니다. 12 무인·대량 배치 처리에서 파일 경로를 고정하고 조용히 PDF를 내는 3의 용도에는 설계상 맞지 않습니다. 3을 실현하려면 인쇄 파이프라인을 거치지 않고 PDF를 직접 생성하는 라이브러리를 쓰는 편이 자연스럽습니다.
PDF 생성 라이브러리를 고를 때의 비교 관점은, 특정 제품을 추천하기 전에 다음 축으로 자사 요건을 정리해 두면 선정이 흔들리지 않습니다.
| 관점 | 확인할 것 |
|---|---|
| 라이선스 | OSS(MIT/Apache 계열인지, GPL/AGPL 계열인지)인지 상용 라이선스인지. 자사 앱을 사외에 배포·판매할 경우, 라이브러리 라이선스 조건(소스 코드 공개 의무 유무, 재배포 조건, 상용 이용 가부)이 자사 배포 형태와 모순되지 않는지를 반드시 1차 정보(라이선스 조문·제공처 공식 사이트)로 확인한다 |
| 일본어 글꼴 취급 | 일본어 글꼴 서브셋 임베딩을 지원하는지, 임베딩 없이 PDF 쪽에 글꼴 실체를 둘 수 있는지(열람 환경에 글꼴이 없을 때 깨지지 않는지), 세로쓰기나 이체자(IVS)가 필요한 업무라면 그 대응 상황 |
| 장표용 기능 | 템플릿 기반 장표 정의를 지원하는지, 바코드·2차원 코드 생성, 전자 서명, PDF/A(장기 보존 규격) 대응, 기존 종이 장표 레이아웃을 그대로 이식하기 쉬운지 |
| 의존성·실행 환경 | 서버 환경(GUI 없는 Windows Server, 컨테이너)에서도 동작하는지, 네이티브 라이브러리 의존이 있는지, .NET Core/.NET(비 Framework) 환경에서의 동작 실적 |
| 성능 | 대량 페이지·대량 작업 배치 생성 시 메모리 사용량, 병렬 생성 시 동작 |
라이선스 조건은 제품이나 버전에 따라 바뀌므로, 반드시 제공처의 공식 문서나 라이선스 조문 자체를 확인한 뒤 채택을 판단하세요. 이 글에서는 특정 라이브러리 이름이나 제품 추천은 하지 않고, 비교의 「관점」과 그 정보를 어디서 확인할지를 제시합니다.
후보를 찾아 비교하는 절차는 다음과 같습니다. 관점만 들고 검색하면 글이 쓰인 시기에 따라 결론이 바뀌므로, 1차 정보에 닿는 순서를 정해 두는 편이 확실합니다.
| 단계 | 볼 곳 | 구체적으로 볼 것 |
|---|---|---|
| 1. 후보를 올린다 | NuGet Gallery의 pdf 태그, Categories(Tags) 검색 |
다운로드 수 자릿수. 자릿수가 둘 이상 다르면 채택 실적 차이로 의미가 있다 |
| 2. 살아 있는지 확인한다 | NuGet 패키지 페이지 「Version History」 | 최근 1년 안에 릴리스가 있는지, 릴리스 간격이 너무 벌어지지 않았는지 |
| 3. 개발 실태를 본다 | GitHub 리포지토리(NuGet 페이지의 「Source repository」 링크) | 최근 커밋 날짜, Open Issue 적체, Issue에 대한 유지보수자의 답변 유무 |
| 4. .NET 대응을 확인한다 | NuGet 페이지의 「Frameworks」란 | 자사가 쓰는 net8.0 등의 타깃이 대상에 들어 있는지. net472만 있는 것은 .NET(Core 계열)에서는 쓸 수 없다 |
| 5. 라이선스를 확인한다 | NuGet 페이지 「License」 링크 대상의 조문 자체 | 상용 배포 가부, 소스 코드 공개 의무 유무. 요약 글이 아니라 조문을 읽는다 |
| 6. 실물로 시험한다 | 작은 검증 프로젝트 | 일본어 글꼴 임베딩, 예상 페이지 수에서의 생성 시간과 메모리 사용량 |
특히 빠지기 쉬운 것이 4와 5입니다. 일본어 소개 글을 보고 정한 뒤에 「.NET Framework만 지원했다」「AGPL이라 자사 제품에 넣을 수 없었다」를 알게 되면 검증 공수를 통째로 버리게 됩니다. 후보를 하나로 좁히기 전에 4와 5만 먼저 통과하면 되돌아감이 줄어듭니다.
6. Excel / Word를 통한 장표라는 선택지
이미 Excel 템플릿으로 운영 중인 장표가 있으면, 그것을 처음부터 다시 만들기보다 기존 자산을 살리는 구성이 현실적인 경우가 있습니다. 방식은 크게 둘입니다.
- Open XML SDK 등으로 xlsx/docx를 직접 생성·편집한다. Excel/Word 실행 파일을 띄우지 않고 파일 형식(ZIP + XML)을 직접 읽고 쓰므로 서버 환경에서도 안정적으로 동작합니다. 템플릿의 괘선·서식을 그대로 살리면서 값만 끼워 넣는 구성과 잘 맞으며, 자세한 내용은 「Excel 장표 출력 만드는 법 - COM/Open XML/템플릿」에 정리되어 있습니다.
- Excel COM Interop으로 실제로 Excel.Application을 조작한다. 매크로나 복잡한 재계산이 있는 기존 Excel 통합 문서를 그대로 재사용하고 싶을 때 고르지만, COM 참조 해제 누락으로 EXCEL.EXE 프로세스가 남는 문제가 나기 쉽고, 대책과 판단 기준은 「C#의 Excel 조작에서 EXCEL.EXE가 남는 문제」에서 다룹니다.
여기서 중요한 것은 서버 사이드나 Windows 서비스에서의 Office COM 자동화는 Microsoft가 공식으로 「비권장·비지원」이라고 명시한다는 점입니다. Office 애플리케이션은 대화형 데스크톱과 로그온된 사용자 프로필을 전제로 설계되어 있으며, 무인·비대화 환경에서 실행하면 불안정한 동작이나 데드락이 날 수 있다고 합니다. 13 라이선스상으로도 서버 쪽 자동화로 Office 기능을 클라이언트에 제공하는 쓰임새는 통상의 EULA에서는 상정되어 있지 않습니다. 13
따라서 장표 출력을 상주 서비스나 배치 작업에 넣고 싶다면, 기존 Excel 템플릿을 재사용하더라도 실행 시 실제로 Excel.exe를 띄우는 구성(COM Interop)은 피하고, Open XML SDK 등에 의한 파일 직접 생성 쪽으로 가져가는 편이 안전합니다. 꼭 COM Interop이 필요하면, 대화형으로 로그온한 사용자 세션에서 도는 상주 애플리케이션(서비스가 아님)으로 구성하는 구분이 현실적입니다.
7. 실무의 함정
Windows 서비스·세션 0에서의 인쇄
인쇄 처리를 상주 서비스에 넣고 싶다는 상담은 적지 않지만, Windows 서비스는 세션 0에서 동작합니다. 세션 0은 Windows Vista 이후 서비스나 대화형 사용자와 관련 없는 프로세스 전용으로 예약된 세션이며, 사용자의 대화형 세션과 분리되어 있습니다. 14 이 분리 때문에 서비스가 대화 상자를 띄워도 사용자에게 보이지 않고, 사용자 입력을 기다리는 코드(인쇄 대화 상자 표시 대기 등)를 쓰면 멈춘 것처럼 보입니다. 14
실무에서 함정이 되기 쉬운 것이 네트워크 프린터나 사용자별로 매핑된 프린터가 보이는 방식입니다. 사용자가 로그온해서 연결한 네트워크 프린터는 그 사용자의 대화형 세션에 묶여 보이는 경우가 많고, 다른 세션(세션 0에서 도는 서비스 프로세스 컨텍스트)에서는 같게 보이지 않습니다. 인쇄가 따르는 상주 처리는 「어느 계정·어느 세션에서, 어느 프린터가 보이는가」를 설계 처음에 확인해야 합니다. 세션 분리의 전체 그림은 「Windows 세션 분리를 어떻게 이해할 것인가」에, 서비스로서의 구현·운용 기초는 「Windows서비스 만드는 법과 운영」에 정리되어 있습니다.
프린터 부재·오프라인 시 동작
PrinterSettings.PrinterName에 없는 프린터 이름을 지정하거나, 기본 프린터가 미설정인 환경에서 실행하면 인쇄를 시도한 시점에 예외나 실패가 납니다. PrinterSettings.IsValid로 미리 검증한 뒤 진행하고, 인쇄 실패를 상정한 재시도·오류 알림을 설계해 두는 두 가지는 최소한 잡아 두고 싶습니다. 네트워크 프린터가 일시적으로 오프라인이면 스풀러에 작업은 쌓여도 실제 출력은 멈춘 채로 남을 수 있으므로, 인쇄 결과를 「보냈다」로 끝내지 않고 필요하면 작업 상태를 확인하는 구조도 검토하세요.
기본 프린터 의존의 위험
PrinterSettings.PrinterName을 명시하지 않고 항상 「기본 프린터」에 기대는 구현은 운용이 길어질수록 위험이 됩니다. 사용자가 다른 프린터를 기본으로 바꾸거나, 노트북을 다른 장소로 가져가 기본 프린터가 바뀌는 환경 변화로 의도하지 않은 출력 대상으로 보내지기 때문입니다. 업무상 중요한 장표는 설정 파일이나 앱 안 설정 화면에서 인쇄 대상 프린터 이름을 명시적으로 유지하고, 기본 프린터 변화에 끌려가지 않는 설계로 두어야 합니다.
장시간 가동에서의 GDI 핸들 누수
Graphics, Font, Brush, Pen 같은 GDI/GDI+ 리소스를 using이나 Dispose()로 확실히 해제하지 않는 코드를 인쇄 처리 안에 쓰면, 장시간 가동 후 GDI 객체 핸들이 고갈되어 그리기 관련 API 호출이 실패하기 시작합니다. GDI 객체 핸들은 프로세스마다 기본 상한이 있고, 레지스트리 GDIProcessHandleQuota로 256에서 65,536 범위에서 조정할 수 있는 유한 리소스입니다. 15 인쇄를 자주 하는 상주 애플리케이션일수록 이 종류의 누수가 운영 환경에서만 드러나는 장애가 되기 쉽습니다. 핸들 누수 조사·원인 분리 방법은 산업용 카메라 장시간 가동 크래시 사례를 다룬 「산업용 카메라 장기 가동 크래시 조사 - 핸들 누수편」에서 구체적으로 설명합니다.
8. 정리
결정 트리 ── 네 선택지 중 무엇을 고를까
서두에 든 ①〜④는 다음 순서로 물으면 좁혀집니다.
- 인쇄가 정말 필요한가. 산출물이 PDF 파일만이면 되고, 종이에 내는 것은 이용자가 임의로 하는 일이라면 인쇄 파이프라인을 거치지 않는 ③(PDF 라이브러리 직접 생성)이 가장 자연스럽습니다. Microsoft Print to PDF는 저장 위치를 묻는 대화형 동작이 전제라 무인 배치에는 맞지 않습니다(5장).
- 기존 Excel/Word 템플릿을 재사용하고 싶은가. 그렇다면 ④. 다만 상주 서비스나 배치에서의 COM 자동화는 Microsoft가 비지원이라고 명시하므로, Open XML SDK 등으로의 직접 생성 쪽으로 가져갑니다(6장).
- 화면이 WinForms인가 WPF인가. 기존 앱 프레임워크에 맞춥니다. WinForms이면 ①, WPF이면 ②. 기술을 섞을 필요는 없습니다(3장·4장).
- WPF를 골랐다면, 흐름형인가 고정 레이아웃인가. 가독성 우선 사내 리포트는
FlowDocument, 괘선 위치를 고정하고 싶은 전표·라벨은FixedDocument입니다(4장).
구현 전에 확인할 것
- 단위와 좌표계를 먼저 정한다. 화면 픽셀 좌표를 인쇄에 그대로 쓰지 않는다. 100분의 1인치 단위나 실측(밀리·인치)을 기준으로 한다(2장).
- 여백은 이중 구조라고 이해한다.
MarginBounds를 기준으로 그리고,PageBounds를 기준으로 하지 않는다.Margins를 0으로 해도 하드 마진 안쪽에만 낼 수 있다(2장, 그림 2). HasMorePages를 명시적으로 설정한다. 「아직 그릴 행이 남았는지」를 판정한 뒤 설정한다. 기본값은false이며, 설정 누락이 「2페이지가 나오지 않는다」의 전형 원인이다(3장).- 인쇄 대상 프린터를 명시적으로 유지한다. 기본 프린터에 기대지 않고, 설정 파일이나 앱 설정 화면에서 프린터 이름을 가진다(7장).
- GDI 리소스는
using으로 반드시 해제한다.Font·Brush·Pen누수는 장시간 가동 운영 환경에서만 드러납니다(7장). - 서비스에서의 인쇄는 처음에 경계를 확인한다. 세션 0에서는 대화 상자를 띄울 수 없고, 사용자에 묶인 네트워크 프린터도 보이지 않습니다(7장).
- 여러 기종에서 검증한다. 하드 마진 크기는 기종마다 다르므로, 개발 머신에서 문제가 없어도 납품처에서 괘선이 빠질 수 있습니다(3장).
관련 기사
- Excel 장표 출력 만드는 법 - COM/Open XML/템플릿
- C#의 Excel 조작에서 EXCEL.EXE가 남는 문제 ── COM 참조 해제 패턴과 교체 판단
- Windows서비스 만드는 법과 운영 ── 작업 스케줄러와의 구분부터 BackgroundService의 서비스화까지
- Windows 세션 분리를 어떻게 이해할 것인가 ── Session 0·RDP·다중 사용자 동시 실행
- 산업용 카메라 장기 가동 크래시 조사 - 핸들 누수편
관련 상담 영역
합동회사 코무라소프트에서는 Windows 업무 앱의 인쇄·장표 출력 기능 설계, 기존 Excel 자산을 살린 장표 재작성, 상주 서비스에서의 인쇄·출력 설계 기술 상담을 다룹니다.
참고 링크
-
Microsoft Learn, PrintDocument Class.
PrintDocument의 역할(DocumentName·PrinterSettings를 설정하고Print()로 인쇄를 시작함)과, WPF에서 인쇄할 때는System.Printing네임스페이스를 써야 한다는 안내에 대해. ↩ ↩2 ↩3 ↩4 -
Microsoft Learn, PrintPageEventArgs.HasMorePages Property. 기본값이
false인 것,PrintPage이벤트는 이 속성이false가 될 때까지 반복 발생하는 것에 대해. ↩ ↩2 -
Microsoft Learn, PageSettings.PrinterResolution Property. 페이지 인쇄 해상도의 기본값이 프린터 기본 해상도인 것,
PrinterSettings.PrinterResolutions에서 선택 가능한 해상도 목록을 얻을 수 있는 것에 대해. ↩ ↩2 ↩3 -
Microsoft Learn, PageSettings.HardMarginX Property. 하드 마진이 프린터가 설정하는 물리적 여백을 나타낸다는 점에 대해. ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, PageSettings.Margins Property. 페이지 여백의 기본값이 상하좌우 1인치인 것,
PrintPage이벤트에서Bounds속성과 조합해 인쇄 가능 영역을 계산할 수 있는 것에 대해. ↩ ↩2 ↩3 -
Microsoft Learn, Windows Print Path Overview. Windows에 GDI 인쇄 경로(Win32 애플리케이션 유래)와 XPS 인쇄 경로(WPF 애플리케이션 또는 XPS Print API 유래)의 두 주요 인쇄 경로가 존재하는 것에 대해. ↩ ↩2 ↩3
-
Microsoft Learn, Printing documents overview. WPF 애플리케이션이 XPS 인쇄 경로를 쓰는 것,
PrintDialog에 의한 기본 인쇄,PrintTicket/PrintCapabilities/PrintQueue/XpsDocumentWriter에 의한 고급 인쇄, XPSDrv 미지원 프린터로의 GDI 자동 변환에 대해. ↩ ↩2 ↩3 ↩4 -
Microsoft Learn, Flow Document Overview.
FlowDocument가 창 크기나 해상도 등 실행 시 변수에 따라 콘텐츠를 다시 배치하는, 가독성 우선 문서인 것에 대해. ↩ ↩2 -
Microsoft Learn, FixedDocument Class.
FixedDocument가 표시·인쇄 장치에 대한 충실한 재현을 우선하는 WYSIWYG 설계이며,FlowDocument와 설계 사상이 다른 것에 대해. ↩ ↩2 -
Microsoft Learn, PrintDialog Class.
System.Windows.Controls.PrintDialog가PrintTicket과PrintQueue를 사용자 입력에 따라 구성하는 대화 상자인 것,System.Windows.Forms.PrintDialog와 다른 클래스인 것에 대해. ↩ ↩2 -
Microsoft Learn, How to: Validate and Merge PrintTickets.
PrintQueue.GetPrintCapabilities로 프린터의 지원 기능을 확인하고,MergeAndValidatePrintTicket으로 요구 내용을 프린터 고유의 타당한PrintTicket에 병합·검증하는 절차에 대해. ↩ ↩2 -
Microsoft Learn, RemoveMPDW. Microsoft Print to PDF가 기본적으로 설치되는 옵션 기능이며, 인쇄 큐와 드라이버 패키지라는 단위로 구성·삭제되는 것에 대해. ↩ ↩2 ↩3
-
Microsoft Support, Considerations for server-side Automation of Office. Microsoft가 ASP/ASP.NET/DCOM/NT 서비스를 포함한 무인·비대화 클라이언트 애플리케이션에서의 Office 자동화를 권장·지원하지 않는 것, Office가 대화형 데스크톱과 사용자 프로필을 전제로 설계된 것에 대해. ↩ ↩2 ↩3
-
Microsoft Learn, Service Changes for Windows Vista - Session 0 Isolation. Windows Vista 이후 세션 0이 서비스나 대화형 사용자와 관련 없는 애플리케이션 전용으로 예약되어, 서비스가 대화 상자를 직접 표시할 수 없게 된 것에 대해. ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, GDI Objects. GDI 객체 핸들이 프로세스마다 기본 상한을 갖는 것,
GDIProcessHandleQuota레지스트리 값으로 256에서 65,536 범위에서 상한을 조정할 수 있는 것에 대해. ↩ ↩2 -
Microsoft Learn, Introduction to printing. Windows 인쇄 아키텍처가 스풀러와 프린터 드라이버로 구성되는 것, Win32 GDI 애플리케이션의 그리기 명령이 EMF로 스풀되는 구조에 대해. ↩ ↩2
-
Microsoft Learn, PageSettings.PrintableArea Property. 프린터가 인쇄할 수 있는 범위를 100분의 1인치 단위로 나타내는 것, 이 속성을 쓰면 페이지 여백 밖·인쇄 가능 영역 안쪽에 인쇄할 수 있는 것에 대해. ↩
-
Microsoft Learn, PrintDocument.OriginAtMargins Property. 기본값이
false인 것,false일 때는 원점 결정에 인쇄 가능 영역만 쓰이고PageSettings.Margins가 무시되는 것에 대해. ↩
관련 기사
같은 태그를 공유하는 최신 기사입니다. 더 가까운 주제로 지식을 넓힐 수 있습니다.
Windows 프린터 드라이버 제공 종료 ── 업무 앱의 장표·라벨 인쇄는 어떻게 대비할 것인가
Microsoft는 v3/v4 프린터 드라이버의 제공 종료를 단계적으로 진행하고 있으며, 2026년 7월부터는 IPP 클래스 드라이버가 우선됩니다. Windows protected print mode에서 무엇이 사라지는지, 업무 앱의 장표·라벨 ...
WinForms/WPF 앱의 다국어화 ── resx·satellite assembly·culture 전환의 실무
Windows 데스크톱 앱의 다국어화를 정리합니다. CurrentCulture와 CurrentUICulture의 차이, resx와 satellite assembly의 구조, WPF에서 현실적인 방식 선택, 런타임 언어 전환, 서식·RTL까지 설명...
Windows 앱의 작업 트레이 상주와 토스트 알림 ── NotifyIcon의 함정과 AppNotification 고르는 법
업무용 Windows 앱의 작업 트레이 상주와 토스트 알림 구현을 정리합니다. NotifyIcon의 올바른 사용법, Explorer 재시작 시 재등록, 토스트 API 3종의 선정 판단표, 알림이 도착하지 않는 경우까지 설명합니다.
WinForms / WPF 앱의 CI/CD 실전 ── GitHub Actions로 빌드부터 서명·배포까지 자동화하기
WinForms / WPF 앱의 CI/CD를 GitHub Actions로 구성하는 실무 가이드입니다. windows-latest에서 빌드+테스트를 돌리는 최소 YAML, 태그 기반 버전 부여, signtool 서명 연동, MSI/MSIX/Clic...
CSV는 「그냥 텍스트」가 아닙니다 ── C# 업무 앱의 CSV 실무(문자 코드·Excel 호환·인젝션 대책)
업무 앱 CSV 입출력의 전형적인 사고──Split(',') 자체 파싱, BOM 없는 UTF-8의 문자 깨짐, 앞자리 0 소실──을 정리하고, RFC 4180 규칙, Shift_JIS 취급, TextFieldParser를 이용한 안전한 읽기까지 ...
관련 토픽
이 기사와 가까운 토픽 페이지입니다. 기사를 출발점 삼아 관련 서비스와 다른 기사로 이어집니다.
Windows 기술 토픽
Windows 개발, 장애 조사, 기존 자산 활용에 관한 KomuraSoft LLC 기사를 모은 토픽 허브입니다.
UI 스레드 & 타이머
WPF / WinForms UI 스레드, async 흐름, Dispatcher 사용, 타이머 판단을 정리한 토픽 페이지입니다.
이 주제와 연결되는 서비스
이 기사는 다음 서비스 페이지로 이어집니다. 가까운 입구부터 확인해 주세요.
Windows 앱 개발
업무 앱의 인쇄·PDF 출력 기능 설계·구현은 Windows 앱 개발 상담 범위이기 때문입니다.
자주 묻는 질문
이 기사 주제에 대해 상담 시 자주 나오는 질문을 모았습니다.
- PrintDocument와 WPF 인쇄 중 어느 쪽으로 만들어야 합니까?
- 화면이 WinForms이면 PrintDocument, WPF이면 FlowDocument/FixedDocument를 쓰는 편이 자연스럽습니다. 기술을 섞을 필요는 없고, 기존 앱의 화면 프레임워크에 맞추는 것이 실무적인 판단 기준입니다. 새로 WPF를 고른다면, 장표가 흐름형인지 고정 레이아웃인지에 따라 FlowDocument와 FixedDocument 중 무엇을 축으로 할지 먼저 정해 두면 헤매지 않습니다.
- 장표 라이브러리는 사야 합니까?
- 괘선이 많은 복잡한 장표, 여러 용지 크기 대응, 일본어 글꼴 임베딩이나 세로쓰기 등을 자체 GDI+/WPF 그리기로 만들 비용이 크면, 장표 라이브러리 도입 비용이 더 싼 경우가 많습니다. 반대로 A4 목록 정도인 단순한 장표라면 PrintDocument나 FixedDocument 자체 구현으로 충분한 경우도 많아서, 먼저 요건의 복잡도를 가늠한 뒤 판단하는 편이 안전합니다.
- PDF만 만들고 인쇄는 쓰지 않는 구성도 있습니까?
- 충분히 있습니다. PDF는 어디까지나 출력 형식 하나이며, 인쇄 대화 상자나 기본 프린터에 의존하지 않는 PDF 라이브러리 직접 생성이 배치 처리·자동화와 더 잘 맞는 경우가 많습니다. Microsoft Print to PDF는 대화형 조작을 전제로 한 드라이버라, 무인 PDF 생성에는 맞지 않습니다.
- 라벨 프린터나 영수증 프린터에도 대응할 수 있습니까?
- 가능하지만, 범용 PrintDocument/FixedDocument의 연장만으로는 대응하기 어려운 경우가 있습니다. 라벨 프린터 상당수는 독자 명령어나 SDK를 갖고, Windows 표준 인쇄 경로와 다른 경로로 제어하는 일도 많기 때문입니다. 먼저 대상 기종이 Windows의 일반 프린터 드라이버로 동작하는지, 전용 SDK를 통해야 하는지를 확인하는 것부터 시작하세요.