인쇄 지원 애플리케이션 v3 API 디자인 가이드
이 문서에서는 디바이스에 대한 v3 PSA(인쇄 지원 앱)를 구현하는 프린터 OEM 및 IHV에 대한 지침과 예제를 제공합니다.
용어
기간 | 정의 |
---|---|
PSA (미국) | 지원 애플리케이션을 인쇄합니다. 이 문서의 API를 사용하는 UWP 앱입니다. |
IPP | 인터넷 인쇄 프로토콜입니다. 클라이언트 디바이스에서 프린터와 상호 작용하여 인쇄 기본 설정을 검색하고 설정하고 인쇄할 문서를 보내는 데 사용됩니다. |
인쇄 지원과 연관된 프린터 | PSA에 연결된 물리적 IPP 프린터입니다. |
IPP 프린터 | IPP 프로토콜을 지원하는 프린터입니다. |
PDL | 페이지 설명 언어입니다. 문서를 프린터로 보내는 형식입니다. |
연결된 PSA 프린터 | PSA 애플리케이션과 연결된 물리적 IPP 프린터입니다. |
인쇄 장치 기능 | 프린터 기능을 정의하기 위한 XML 문서 형식입니다. |
인쇄 지원 확장 | 프린터 제약 조건 확장 기능을 제공하는 PSA 백그라운드 작업입니다. |
인쇄 지원 애플리케이션 v3 API
이 문서에는 인쇄 지원 앱 디자인 가이드 및 Windows.Graphics.Printing.PrintSupport 네임스페이스에
자세한 내용은 다음 문서를 참조하세요.
주제 | 설명 |
---|---|
인쇄 지원 앱 디자인 가이드 | 디바이스에 대한 PSA(인쇄 지원 앱)를 구현하는 프린터 OEM 및 IHV에 대한 지침과 예제를 제공합니다. |
인쇄 지원 앱 v4 API 디자인 가이드 | 디바이스에 대한 v4 PSA(인쇄 지원 앱)를 구현하는 프린터 OEM 및 IHV에 대한 지침과 예제를 제공합니다. |
인쇄 지원 가상 프린터용 MSIX 매니페스트 사양 | 인쇄 지원 가상 프린터를 구현하는 프린터 OEM 및 IHV에 대한 MSIX 매니페스트 지침 및 예제를 제공합니다. |
인쇄 지원 앱 연결 | PSA(인쇄 지원 앱)를 프린터와 연결하기 위한 지침과 예제를 제공합니다. |
API에 대한 중요한 확장은 다음과 같습니다.
IPP 압축 - PSA v3 API는 이 기능을 지원하는 IPP 프린터의 인쇄 작업에 IPP 압축 기능을 추가하여 IPP 인쇄를 향상시킵니다. 일부 PSA에는 사용자 지정 압축이 있을 수 있습니다. 즉, IPP 작업에 성능에 영향을 주는 이중 압축이 있습니다. 이를 완화하기 위해 PSA v3 API는
(PSA v1 API) 런타임 클래스에 IsIppCompressionEnabledPrintWorkflowJobStartingEventArgs 속성과 함수(필요한 경우 현재 작업에 대한 압축을 사용하지 않도록 설정)를 도입합니다.DisableIppCompressionForJob IPP 작업 오류 처리 및 오류 알림 - PSA v3 API는 PrintWorkflowJobBackgroundSession(PSA v1 API) 런타임 클래스에 JobIssueDetected 이벤트를 도입합니다. 이 이벤트는 PSA가 인쇄 작업에서 오류/경고를 감지할 때마다 발생합니다. 그런 다음 PSA는 사용자에게 오류 알림을 표시할 책임이 있습니다. PSA가 이 이벤트에 등록하고 SkipSystemErrorToast 속성을 PrintWorkflowJobIssueDetectedEventArgstrue로 설정하면 인쇄 시스템에 Windows 인쇄 시스템 알림이 표시되지 않도록 지시합니다. 또한 PSA v3 API는 사용자가 토스트 알림과 상호 작용할 때 PSA가 UI를 시작하는 메커니즘을 제공합니다.
사용자 지정 IPP 통신 시간 제한 - PSA v3 API는 PSA가 IPP 시간 제한을 재정의할 수 있는 API를 제공합니다. 또한 IPP 통신 시간 제한을 조작하기 위해 PrintSupportIppCommunicationConfiguration 런타임 클래스가 PrintSupportPrintDeviceCapabilitiesChangedEventArgs에 추가되었습니다. 또한 PSA v3 API는 IPP 통신에 오류가 있을 때 발생하는 이벤트를 도입합니다. IHV가 오류를 조사하고 그에 따라 시간 제한 값을 조정할 수 있도록 이벤트가 도입되었습니다.
지원 IPPFaxOut - PSA v3 API는 IPPFaxOut 프린터 지원을 위해 인쇄 시스템에 기능을 추가합니다. 팩스를 지원하기 위해 PSA는 XPS를 Tiff로 변환하는 렌더링 필터를 지원합니다. PSA는 XPS 콘텐츠를 TIFF로 변환하기 전에 조작할 수 있으므로, PSA가 XPS에서 TIFF로 변환기를 활용할 수 있게 하기 위해 PrintWorkflowPdlConversionType에 XpsToTiff 열거형 값을 제공합니다. 또한 PSA가 표준 프린터와 IPPFaxOut 프린터를 구분할 수 있도록 IsIPPFaxOutPrinter 속성을 IppPrintDevice 런타임 클래스에 제공합니다.
IPP 압축을 사용하지 않도록 설정
IPP 압축은 다음 코드 샘플에 나와 있습니다.
public sealed class PrintSupportWorkflowBackgroundTask : IBackgroundTask
{
public BackgroundTaskDeferral TaskInstanceDeferral { get; set; }
private PrintWorkflowJobBackgroundSession session;
public void Run(IBackgroundTaskInstance taskInstance)
{
TaskInstanceDeferral = taskInstance.GetDeferral();
if (taskInstance.TriggerDetails is PrintWorkflowJobTriggerDetails jobDetails)
{
session = jobDetails.PrintWorkflowJobSession;
session.JobStarting += OnJobStarting;
session.PdlModificationRequested += OnPdlModificationRequested;
session.JobIssueDetected += OnJobIssueDetected;
// Make sure to register all the event handlers before PrintWorkflowJobBackgroundSession.Start is called.
session.Start();
}
}
private void OnJobStarting(PrintWorkflowJobBackgroundSession sender, PrintWorkflowJobStartingEventArgs args)
{
using (args.GetDeferral())
{
// Skip system rendering.
args.SetSkipSystemRendering();
// If Ipp compression is enabled by the system, check to see if PSA does custom compression for the printer
// and disable system compression if required.
if (args.IsIppCompressionEnabled)
{
if (this.HasCustomCompression(args.Printer))
{
args.DisableIppCompressionForJob();
}
}
}
}
bool HasCustomCompression(IppPrintDevice device)
{
bool hasCustomCompression = false;
// Check if the PSA does custom compression for the printer
return hasCustomCompression;
}
}
IPP 작업 오류 처리
이 샘플에서는 PSA 앱이 작업 오류를 등록하고, 작업 오류에 대한 알림을 표시하고, 사용자가 알림을 활성화할 때 UI를 시작하는 방법을 보여 줍니다.
public sealed class PrintSupportWorkflowBackgroundTask : IBackgroundTask
{
public BackgroundTaskDeferral TaskInstanceDeferral { get; set; }
private PrintWorkflowJobBackgroundSession session;
public void Run(IBackgroundTaskInstance taskInstance)
{
TaskInstanceDeferral = taskInstance.GetDeferral();
if (taskInstance.TriggerDetails is PrintWorkflowJobTriggerDetails jobDetails)
{
session = jobDetails.PrintWorkflowJobSession;
session.JobStarting += OnJobStarting;
session.PdlModificationRequested += OnPdlModificationRequested;
session.JobIssueDetected += OnJobIssueDetected;
// Make sure to register all the event handlers before PrintWorkflowJobBackgroundSession.Start is called.
session.Start();
}
}
private void OnJobIssueDetected (PrintWorkflowJobBackgroundSession sender, PrintWorkflowJobIssueDetectedEventArgs args)
{
// Using a deferral to exclude the background process from being suspended while PSA displays UI.
Deferral deferral = args.GetDeferral();
var toast = new ToastNotification(GetErrorToastXml(args.ExtendedError,
args.JobIssueKind, args.PrinterJob, args.Configuration));
toast.Activated += async (toastSender, e) =>
{
// PSA UI application
PrintWorkflowUICompletionStatus uiStatus = await args.UILauncher.LaunchAndCompleteUIAsync();
// Complete deferral
deferral.Complete();
};
toast.Dismissed += async (toastSender, e) => { deferral.Complete(); };
toast.Failed += async (toastSender, e) => { deferral.Complete(); };
ToastNotificationManager.CreateToastNotifier().Show(toast);
args.SkipSystemErrorToast = true;
}
private XmlDocument GetErrorToastXml(Exception jobError, PrintWorkflowJobIssueKind issueKind,
PrintWorkflowPrinterJob printerJob, PrintWorkflowConfiguration workflowConfig )
{
var errorToastXml = new XmlDocument();
// Generate Toast Xml based on error information from Exception and PrintWorkflowPrinterJob.
return errorToastXml;
}
}
IPP 통신 시간 제한 설정
이 샘플에서는 IPP 통신 시간 제한을 설정하는 방법을 보여 줍니다.
public sealed class PrintSupportExtensionBackGroundTask : IBackgroundTask
{
public BackgroundTaskDeferral TaskInstanceDeferral { get; set; }
private PrintSupportExtensionSession session;
public void Run(IBackgroundTaskInstance taskInstance)
{
taskInstance.Canceled += OnTaskInstanceCanceled;
TaskInstanceDeferral = taskInstance.GetDeferral();
if (taskInstance.TriggerDetails is PrintSupportExtensionTriggerDetails extensionDetails)
{
session = extensionDetails.Session;
session.PrintTicketValidationRequested += OnSessionPrintTicketValidationRequested;
session.PrintDeviceCapabilitiesChanged += OnSessionPrintDeviceCapabilitiesChanged;
session.CommunicationErrorDetected += OnCommunicationErrorDetected;
// Make sure to register all the event handlers before PrintSupportExtensionSession.Start is called.
session.Start();
}
}
private void OnTaskInstanceCanceled(IBackgroundTaskInstance sender, BackgroundTaskCancellationReason reason)
{
session = null;
TaskInstanceDeferral.Complete();
}
private void OnSessionPrintDeviceCapabilitiesChanged(PrintSupportExtensionSession sender, PrintSupportPrintDeviceCapabilitiesChangedEventArgs args)
{
// Using deferral to exclude the background process from being suspended while PSA updates the printer PDC and other configurations.
using (args.GetDeferral())
{
if (args.CommunicationConfiguration.CanModifyTimeouts)
{
this.UpdateAttributeTimeouts(args.CommunicationConfiguration, sender.Printer);
this.UpdateJobTimeouts(args.CommunicationConfiguration, sender.Printer);
}
// Do other operations, such as Updating PDC, PDR, and so on here.
}
}
void UpdateAttributeTimeouts(PrintSupportIppCommunicationConfiguration config, IppPrintDevice device)
{
IppPrinterCommunicationKind communicationKind = config.CommunicationKind;
PrintSupportIppCommunicationTimeouts currentTimeouts = config.IppAttributeTimeouts;
// Adjust attribute timeouts based on the printer
switch (communicationKind)
{
case IppPrinterCommunicationKind.Network:
currentTimeouts.ConnectTimeout = TimeSpan.FromSeconds(10);
currentTimeouts.SendTimeout = TimeSpan.FromSeconds(10);
currentTimeouts.ReceiveTimeout = TimeSpan.FromSeconds(10);
break;
case IppPrinterCommunicationKind.UniversalPrint:
// adjust timeout for universal printer
break;
}
}
void UpdateJobTimeouts(
PrintSupportIppCommunicationConfiguration config, IppPrintDevice device)
{
IppPrinterCommunicationKind communicationKind = config.CommunicationKind;
PrintSupportIppCommunicationTimeouts currentTimeouts = config.IppJobTimeouts;
// Adjust job timeouts based on the printer and communication type
switch (communicationKind)
{
case IppPrinterCommunicationKind.Network:
currentTimeouts.ConnectTimeout = TimeSpan.FromSeconds(30);
currentTimeouts.SendTimeout = TimeSpan.FromSeconds(30);
currentTimeouts.ReceiveTimeout = TimeSpan.FromSeconds(30);
break;
case IppPrinterCommunicationKind.UniversalPrint:
// adjust timeout for universal printer
break;
}
}
}
IPP 통신 오류 처리
이 샘플에서는 IPP 통신 오류를 처리하는 방법을 보여 줍니다.
public sealed class PrintSupportExtensionBackGroundTask : IBackgroundTask
{
public BackgroundTaskDeferral TaskInstanceDeferral { get; set; }
private PrintSupportExtensionSession session;
public void Run(IBackgroundTaskInstance taskInstance)
{
taskInstance.Canceled += OnTaskInstanceCanceled;
TaskInstanceDeferral = taskInstance.GetDeferral();
if (taskInstance.TriggerDetails is PrintSupportExtensionTriggerDetails extensionDetails)
{
session = extensionDetails.Session;
session.PrintTicketValidationRequested += OnSessionPrintTicketValidationRequested;
session.PrintDeviceCapabilitiesChanged += OnSessionPrintDeviceCapabilitiesChanged;
session.CommunicationErrorDetected += OnCommunicationErrorDetected ;
// Make sure to register all the event handlers before PrintSupportExtensionSession.Start is called.
session.Start();
}
}
private void OnTaskInstanceCanceled(IBackgroundTaskInstance sender, BackgroundTaskCancellationReason reason)
{
session = null;
TaskInstanceDeferral.Complete();
}
private void OnCommunicationErrorDetected(PrintSupportExtensionSession sender, PrintSupportCommunicationErrorDetectedEventArgs args)
{
// Using deferral to exclude the background process from being suspended while PSA updates the timeouts.
using (args.GetDeferral())
{
if (args.ErrorKind == IppCommunicationErrorKind.Timeout)
{
PrintSupportIppCommunicationConfiguration ippConfig = args.CommunicationConfiguration;
IppPrintDevice device = sender.Printer;
// Update timeout based on the communication error
}
}
}
}
PSA에서 IPP 팩스 출력 프린터로 인쇄
이 샘플에서는 PSA에서 IPPFaxOut 프린터로 인쇄하는 방법을 보여 줍니다.
public sealed class PrintSupportWorkflowBackgroundTask : IBackgroundTask
{
public BackgroundTaskDeferral TaskInstanceDeferral { get; set; }
private PrintWorkflowJobBackgroundSession session;
public void Run(IBackgroundTaskInstance taskInstance)
{
TaskInstanceDeferral = taskInstance.GetDeferral();
if (taskInstance.TriggerDetails is PrintWorkflowJobTriggerDetails jobDetails)
{
session = jobDetails.PrintWorkflowJobSession;
session.JobStarting += OnJobStarting;
session.PdlModificationRequested += OnPdlModificationRequested;
session.JobIssueDetected += OnJobIssueDetected;
// Make sure to register all the event handlers before PrintWorkflowJobBackgroundSession.Start is called.
session.Start();
}
}
private async void OnPdlModificationRequested(PrintWorkflowJobBackgroundSession sender, PrintWorkflowPdlModificationRequestedEventArgs args)
{
using (args.GetDeferral())
{
IppPrintDevice printer = args.PrinterJob.Printer;
IInputStream xpsContent = args.SourceContent.GetInputStream();
WorkflowPrintTicket printTicket = args.PrinterJob.GetJobPrintTicket();
string documentFormat = this.GetPrinterDocumentFormat(printer);
PrintWorkflowPdlTargetStream targetStream = args.CreateJobOnPrinter(documentFormat);
IInputStream xpsSourceContent = xpsContent;
if (printer.IsIPPFaxOutPrinter)
{
// Add cover page to XPS source document
xpsSourceContent = this.AddCoverPageToXpsContent(xpsContent);
}
PrintWorkflowPdlConverter pdlConverter;
switch (documentFormat.ToLowerInvariant())
{
case "image/pwg-raster":
pdlConverter = args.GetPdlConverter(PrintWorkflowPdlConversionType.XpsToPwgr);
break;
case "application/pclm":
pdlConverter = args.GetPdlConverter(PrintWorkflowPdlConversionType.XpsToPclm);
break;
case "application/pdf":
pdlConverter = args.GetPdlConverter(PrintWorkflowPdlConversionType.XpsToPdf);
break;
case "image/tiff":
pdlConverter = args.GetPdlConverter(PrintWorkflowPdlConversionType.XpsToTiff);
break;
default:
// This should not happen, aborting workflow if PSA does not identify the supported PDLs
args.Configuration.AbortPrintFlow(PrintWorkflowJobAbortReason.JobFailed);
return;
}
// Use pdlConverter to convert the source XPS stream to printer's document format and send it to the printer using targetStream.
await pdlConverter.ConvertPdlAsync(printTicket, xpsSourceContent, targetStream.GetOutputStream());
targetStream.CompleteStreamSubmission(PrintWorkflowSubmittedStatus.Succeeded);
}
}
private IInputStream AddCoverPageToXpsContent(IInputStream xpsStream)
{
var coverPageXps = new InMemoryRandomAccessStream();
// Add cover page to XPS content and write to coverPageXps stream
return coverPageXps;
}
}
PSA의 전화 번호에 대한 팩스 UI 사용 안 함
IPP 팩스 프린터의 지원을 통해 이 샘플에서는 팩스 프린터로 인쇄할 때 사용자에게 팩스 번호를 입력하도록 요청하는 UI를 보여줍니다. 그러나 PSA는 추가 정보와 옵션을 사용하여 자체 UI를 표시하려고 할 수 있습니다. 팩스용 UI가 두 개 있는 경우 사용자에게 혼동을 주므로 이 샘플에서는 PSA가 팩스 UI를 표시하려는 경우 시스템 UI를 사용하지 않도록 설정하는 옵션을 보여 줍니다.
다음 샘플에서는 API 사용량을 보여 줍니다.
public sealed class PrintSupportWorkflowBackgroundTask : IBackgroundTask
{
private void OnJobStarting(PrintWorkflowJobBackgroundSession sender, PrintWorkflowJobStartingEventArgs args)
{
using (args.GetDeferral())
{
// If the job is printing to an Ipp fax printer,
// check whether PSA has a custom UI and disable system UI for getting the fax number.
if (args.IsIPPFaxOutPrinter)
{
if (this.HasCustomUIForFax(args.Printer))
{
args.SkipSystemFaxUI = true;
}
}
}
}
bool HasCustomUIForFax(IppPrintDevice device)
{
bool hasCustomUIForFax = false;
// Check if the PSA does custom UI for the given fax printer.
return hasCustomUIForFax;
}
}
주석
이 문서의 샘플은 개발자가 PSA API 워크플로에 익숙하다는 가정하에 Print 지원 앱 디자인 가이드 PSA v1 및 v2 API 샘플을 기반으로 작성되었습니다.
이 문서에는 인쇄 지원 앱 디자인 가이드 및 windows.Graphics.Printing.PrintSupport 네임스페이스에
인쇄 구성 요소는 PSA 브로커 프로세스를 통해 PSA 앱과 통신합니다.
관련 문서
Windows 타사 프린터 드라이버에 대한 지원 종료 계획
PrintSupportIppCommunicationConfiguration
PrintSupportPrintDeviceCapabilitiesChangedEventArgs
PrintWorkflowJobIssueDetectedEventArgs
PrintWorkflowJobStartingEventArgs