Compartilhar via


Guia de design da API do Aplicativo de Suporte de Impressão v3

Este artigo fornece diretrizes e exemplos para OEMs de impressora e IHVs que estão implementando um PSA (Aplicativo de Suporte à Impressão) v3 para seu dispositivo.

Terminologia

Prazo Definição
PSA Aplicativo de Suporte a Impressão. Aplicativo UWP que usa a API neste documento.
IPP Protocolo de Impressão da Internet. Usado em um dispositivo cliente para interagir com a impressora para recuperar e definir preferências de impressão e enviar o documento para ser impresso.
Impressora associada ao suporte de impressão Impressora IPP física vinculada ao PSA.
Impressora IPP Impressora que dá suporte ao protocolo IPP.
PDL Idioma de descrição da página. O formato no qual um documento é enviado para a impressora.
Impressora PSA associada Impressora IPP física associada a um aplicativo PSA.
CapacidadesDoDispositivoDeImpressão Formato de documento XML para definir recursos de impressora.
PrintSupportExtension Tarefa em segundo plano do PSA responsável por fornecer recursos de extensão de restrição de impressora.

Este artigo contém as extensões v3 para a API pública do Aplicativo de Suporte à Impressão existente descrita no guia de design do aplicativo de suporte de impressão e Windows.Graphics.Printing.PrintSupport Namespace. A API de PSA permite que os fabricantes de impressoras desenvolvam aplicativos de Suporte a Hardware que podem aprimorar a experiência de impressão de um usuário do Windows ao usar o Driver de Classe IPP da Microsoft do Sistema de Impressão integrada sem precisar desenvolver um driver personalizado. Os componentes de impressão se comunicam com o aplicativo PSA através de um processo intermediário do PSA.

Para obter mais informações, consulte os seguintes artigos:

Tópico Descrição
Guia de design do aplicativo de Suporte à Impressão Fornece diretrizes e exemplos para OEMs de impressora e IHVs que estão implementando um PSA (aplicativo de suporte à impressão) para seu dispositivo.
Guia de design da API do Aplicativo de Suporte à Impressão v4 Fornece diretrizes e exemplos para OEMs de impressora e IHVs que estão implementando um PSA (Aplicativo de Suporte à Impressão) v4 para seu dispositivo.
Especificação de manifesto MSIX para impressora virtual com suporte de impressão Fornece orientação e exemplos de manifesto MSIX para OEMs e IHVs de impressora que estão implementando um aplicativo de suporte à impressão.
Associação de aplicativos de suporte à impressão Fornece diretrizes e exemplos para associar um PSA (aplicativo de suporte à impressão) a uma impressora.

As extensões significativas para a API são as seguintes:

  • Compressão IPP - A API de PSA v3 adiciona um recurso para melhorar a impressão IPP, oferecendo compactação em trabalhos de impressão para impressoras IPP que suportam essa função. Alguns PSAs podem ter compactação personalizada, o que faz com que o trabalho IPP sofra uma compactação dupla, afetando o performance. Para atenuar isso, a API PSA v3 apresenta uma propriedade IsIppCompressionEnabled e uma função DisableIppCompressionForJob (para desabilitar a compactação para o trabalho atual, se necessário) na classe de tempo de execução PrintWorkflowJobStartingEventArgs (API PSA v1).

  • Tratamento de Erro de Trabalho do IPP e Error Toast - A PSA v3 API introduz um evento JobIssueDetected na classe de runtime PrintWorkflowJobBackgroundSession (PSA v1 API). O evento é gerado sempre que o PSA detecta um erro/aviso no trabalho de impressão. O PSA torna-se responsável por mostrar a notificação de erro ao usuário. Quando o PSA registra-se para este evento e define a propriedade SkipSystemErrorToast como verdadeira em PrintWorkflowJobIssueDetectedEventArgs, ele instrui o sistema de impressão a não mostrar o alerta do sistema de impressão do Windows. A API de PSA v3 também fornece um mecanismo para o PSA iniciar a interface do usuário quando o usuário interage com o sistema.

  • Tempos de espera de comunicação IPP personalizados – A API de PSA v3 fornece uma interface pela qual um PSA pode substituir os tempos de espera do IPP. Além disso, a classe de runtime PrintSupportIppCommunicationConfiguration é adicionada a PrintSupportPrintDeviceCapabilitiesChangedEventArgs para manipular os tempos limite de comunicação do IPP. Além disso, a API psa v3 apresenta um evento, que é gerado quando há um erro com a comunicação do IPP. O evento foi introduzido para que o IHV pudesse investigar a falha e ajustar os valores de tempo limite adequadamente.

  • Suporte a IPPFaxOut - A API de PSA v3 adiciona um recurso ao sistema de impressão para dar suporte a impressoras IPPFaxOut. Para dar suporte a Fax, o PSA dá suporte a um filtro de renderização para converter XPS em Tiff. Como o PSA pode manipular o conteúdo XPS antes de converter em TIFF, ele fornece o valor de enumeração XpsToTiff em PrintWorkflowPdlConversionType para que o PSA possa ter acesso ao conversor XPS para TIFF. Além disso, ele fornece a propriedade IsIPPFaxOutPrinter à classe de runtime IppPrintDevice para que o PSA possa diferenciar entre as impressoras IPPFaxOut e impressoras IPPFaxOut padrão.

Desabilitando a compactação do IPP

A compactação IPP é mostrada no exemplo de código a seguir.

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;
    } 
} 

Tratamento de erro de trabalho IPP

Este exemplo demonstra como o aplicativo de PSA pode registrar erros de trabalho, mostrar notificações para esses erros e iniciar a interface do usuário quando o usuário ativar as notificações.

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;
    }
}

Configurando o tempo limite de comunicação do IPP

Este exemplo demonstra como definir o tempo limite de comunicação do 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;
        }
    }
}

Lidando com erros de comunicação do IPP

Este exemplo demonstra como lidar com erros de comunicação do 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
            }
        }
    }  
}

Impressão em impressora de saída de fax IPP no PSA

Este exemplo demonstra como imprimir em uma impressora IPPFaxOut em um PSA.

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;
    }
}

Desabilitar a interface de fax para o número de telefone do PSA

Com o suporte da impressora de fax IPP, este exemplo mostra uma interface do usuário para solicitar que o usuário insira o número de Fax ao imprimir na impressora Fax. Mas o PSA pode querer mostrar sua própria interface do usuário com mais informações e opções. Como é confuso para o usuário se houver duas interfaces de usuário para fax, este exemplo ilustra uma opção para um PSA desabilitar a interface de usuário do sistema quando quiser mostrar sua interface de usuário de fax.

O exemplo a seguir demonstra o uso da 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;
    }  
}

Observações

Os exemplos neste artigo são baseados nos exemplos das APIs de PSA v1 e v2 no Guia de design do aplicativo de Suporte de Impressão com a suposição de que o desenvolvedor está familiarizado com o fluxo de trabalho da API de PSA.

Este artigo contém as extensões da API de Aplicativo de Suporte à Impressão pública existente descrita no guia de design do Aplicativo de Suporte à Impressão e Windows.Graphics.Printing.PrintSupport Namespace. A API de PSA permite que os fabricantes de impressoras desenvolvam aplicativos de UWP que podem aprimorar a experiência de impressão dos usuários do Windows ao usar o Driver de Classe IPP da Microsoft do Sistema de Impressão integrada sem a necessidade de desenvolver um driver personalizado.

Os componentes de impressão se comunicam com o app PSA por meio de um processo intermediário do PSA.

DisableIppCompressionForJob

Fim do plano de manutenção para drivers de impressora de terceiros no Windows

IsIppCompressionEnabled

IsIPPFaxOutPrinter

IppPrintDevice

ProblemaDeTrabalhoDetectado

PrintSupportIppCommunicationConfiguration

PrintSupportPrintDeviceCapabilitiesChangedEventArgs

PrintWorkflowJobBackgroundSession

PrintWorkflowJobIssueDetectedEventArgs

PrintWorkflowJobStartingEventArgs

PrintWorkflowPdlConversionType

SkipSystemErrorToast

Windows.Graphics.Printing.PrintSupport