Compartilhar via


Guia de design do Aplicativo de Suporte de Impressão v1 e v2

Este artigo fornece diretrizes e exemplos para IHVs e OEMs de impressora desenvolverem um PSA (aplicativo de suporte à impressão) que pode aprimorar a experiência de impressão de um usuário do Windows de várias maneiras.

Importante

A partir do lançamento do SDK do Windows 11 (22000.1), os Aplicativos de Suporte à Impressão (PSA) são o método recomendado para desenvolver aplicativos UWP para impressoras. Para desenvolver um Aplicativo de Suporte à Impressão para seu dispositivo de impressão, baixe e instale o SDK do Windows 11 para a versão do Windows que você está direcionando.

Importante

Este artigo contém seções que descrevem a funcionalidade PSA que está disponível a partir do Windows 11, versão 22H2. Essas seções contêm uma nota indicando que ela se aplica a essa versão.

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

Tópico Descrição
Guia de design dos aplicativos API v3 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) v3 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.

Alguns recursos de impressora não são apresentados em caixas de diálogo de impressão mostradas pelo Windows, pois são recursos especiais que precisam da ajuda de um aplicativo do fabricante para serem configurados corretamente. Eles também podem ser recursos que não são fornecidos nos recursos padrão da impressora.

Os recursos específicos da impressora podem ser agrupados de uma maneira que facilite para o usuário escolher uma opção e confiar que todos os recursos envolvidos nesse cenário sejam automaticamente definidos como os valores corretos. Um exemplo disso pode ser uma opção entre o salvamento de tinta, a economia de papel e os modos de mais alta qualidade que podem manipular vários recursos de impressão automaticamente com base em uma seleção do usuário. O Windows não consegue agrupá-los automaticamente, pois isso requer a compreensão de todos os recursos personalizados de cada modelo de impressora.

Essa necessidade de mostrar preferências de impressão personalizadas é tratada por essa API com um contrato de extensão UWP opcional que pode ser ativado pelo usuário de todas as caixas de diálogo de impressão do Windows e caixas de diálogo de impressão personalizada que usam a API fornecida pelo Windows. Os fabricantes são capazes de adaptar sua interface do usuário para fornecer a melhor experiência de impressão para a impressora específica que o usuário possui.

Outra área em que os fabricantes de impressoras podem melhorar e diferenciar é a qualidade da impressão. Os fabricantes podem melhorar a qualidade da impressão após a renderização otimizando o conteúdo da impressora específica. Eles também podem apresentar uma visualização de alta fidelidade que representa melhor a saída final, pois pode levar em consideração recursos específicos da impressora.

aplicativo de suporte à impressão linha do tempo de impressão

Terminologia

Prazo Definição
PSA Aplicativo de Suporte a Impressão. Um aplicativo UWP que usa a API descrita neste artigo.
MPD Caixa de Diálogo de Impressão Moderna. Isso é mostrado ao usuário quando um aplicativo está imprimindo usando a API Windows.Graphics.Printing.
CPD Caixa de Diálogo de Impressão Comum. Isso é mostrado ao usuário quando o aplicativo está imprimindo usando a API Win32. Os aplicativos que precisam mostrar a visualização de impressão não acionam essa caixa de diálogo e implementam uma versão da caixa de diálogo. Os aplicativos do Office são um exemplo primordial disso.
Índice de Preços ao Produtor (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 vinculada ao PSA.
Impressora IPP Impressora que dá suporte ao protocolo IPP.
Mais configurações Link que abre a interface do usuário do aplicativo fornecido pelo parceiro no MPD. O padrão é abrir a interface do usuário das preferências de impressão internas quando não há PSA instalado.
Interface do usuário de preferências da impressora Caixa de diálogo usada para definir as opções de impressora padrão que são aplicadas no momento da impressão. Por exemplo: orientação, tamanho do papel, cor, impressão em ambos os lados e assim por diante.
PDL Linguagem 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. Para obter mais informações, consulte Tecnologias de tíquete de impressão e recursos de impressão.
PrintTicket Coleção de vários recursos relacionados à impressão e seus valores usados para capturar a intenção do usuário para um determinado trabalho de impressão.
PrintSupportExtension Tarefa em segundo plano do PSA responsável por fornecer recursos de extensão de restrição de impressora.

Esses exemplos fazem referência a um namespace printsupport, que é definido como:

    xmlns:printsupport="http://schemas.microsoft.com/appx/manifest/printsupport/windows10"

Quando um usuário está prestes a imprimir um documento, ele geralmente gostaria de definir algumas preferências com as quais imprimi-lo. Por exemplo, eles podem optar por imprimir um documento em modo paisagem. Eles também podem aproveitar um recurso personalizado compatível com a impressora. O Windows fornece a interface do usuário padrão para mostrar preferências personalizadas, mas o usuário pode não entendê-las, pois não há ícones ou descrições apropriados. O Windows também pode estar usando o controle de interface do usuário errado para apresentá-lo. Esse recurso personalizado é melhor apresentado por um aplicativo que entende completamente o recurso. Essa é a motivação por trás da oferta de uma API que permite que os fabricantes de impressora criem aplicativos personalizados para os vários modelos de impressora que eles criam.

Um novo contrato de extensão UAP é criado com uma nova categoria chamada windows.printSupportSettingsUI. Os aplicativos ativados com esse contrato recebem um novo ActivationKind chamado PrintSupportSettingsUI. Esse contrato não requer nenhuma nova funcionalidade.

<Extensions>
    <printsupport:Extension Category="windows.printSupportSettingsUI" 
        EntryPoint="PsaSample.PsaSettingsUISample"/>
</Extensions>

Esse contrato é invocado quando o usuário seleciona Mais Configurações no MPD ou Preferências no CPD. Esse contrato também pode ser invocado nas Preferências de Impressão () do aplicativo de Configurações. Quando o contrato é ativado, o aplicativo recebe um objeto PrintSupportSettingsUISession que pode ser usado para obter os objetos PrintTicket e PrintDevice atuais. O objeto PrintDevice pode ser usado para se comunicar com a impressora para receber atributos de impressora e trabalho. O aplicativo pode mostrar a interface do usuário com as opções adequadas da impressora para o usuário. Quando o usuário faz as escolhas e seleciona OK, o aplicativo pode então modificar o tíquete de impressão, validá-lo e enviá-lo novamente usando o objeto PrintSupportPrintTicketTarget. Se o usuário optar por cancelar a janela de preferências, as alterações deverão ser descartadas e o aplicativo deverá sair concluindo o adiamento obtido do objeto PrintSupportSettingsUISession.

Espera-se que o Aplicativo de Suporte à Impressão manipule várias ativações simultâneas para diferentes trabalhos de impressão, portanto, esse aplicativo deve dar suporte a várias instâncias usando o elemento supportsMultipleInstances no arquivo package.appxmanifest. Não fazer isso pode resultar em situações em que confirmar preferências de um trabalho de impressão pode fechar outras janelas de preferências que podem estar abertas. É necessário que o usuário abra essas janelas de preferências novamente.

O diagrama de sequências a seguir representa o conceito de manipulação de tíquete de impressão da interface do usuário de configurações:

diagrama de sequência de configurações U I manipulação de tíquetes de impressão

Alterando PrintTicket na interface do usuário de configurações

Código de exemplo em C# para ativação da interface do usuário de Configurações quando iniciado a partir de qualquer caixa de diálogo de impressão (MPD/CPD ou caixa de diálogo de impressão personalizada) ou das configurações do sistema:

namespace PsaSampleApp
{
    sealed partial class App : Application
    {
        Deferral settingsDeferral;
        protected override void OnActivated(IActivatedEventArgs args)
        {
            if (args.Kind == ActivationKind.PrintSupportSettingsUI)
           {
                // Get the activation arguments
                var settingsEventArgs = args as PrintSupportSettingsActivatedEventArgs;
                PrintSupportSettingsUISession settingsSession = settingsEventArgs.Session;
                // Take deferral
                this.settingsDeferral = settingsEventArgs.GetDeferral();

                // Create root frame
                var rootFrame = new Frame();
                
        // Choose the page to be shown based upon where the application is being launched from
                switch (settingsSession.LaunchKind)
                {
                    case SettingsLaunchKind.UserDefaultPrintTicket:
                    {
                        // Show settings page when launched for default printer settings
                        rootFrame.Navigate(typeof(DefaultSettingsView), settingsSession);
                    }
                    break;
                    case SettingsLaunchKind.JobPrintTicket:
                    {
               // Show settings page when launched from printing app
                       rootFrame.Navigate(typeof(JobSettingsView), settingsSession);
                    }
                    break;
                }
                
   
                Window.Current.Content = rootFrame; 
            }
        }

        internal void ExitSettings()
        {
            settingsDeferral.Complete();
        } 
    }
}

XAML para classe DefaultSettingsView:

<Page
    x:Class="PsaSampleApp.DefaultSettingsView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:PsaSampleApp"
    Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">

    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
        <StackPanel Grid.Row="0"  Orientation="Vertical" Margin="30,50,0,0">
           <ComboBox x:Name="OrientationOptions" ItemsSource="{x:Bind OrientationFeatureOptions}" SelectedItem="{x:Bind SelectedOrientationOption, Mode=TwoWay}" DisplayMemberPath="DisplayName" HorizontalAlignment="Left" Height="Auto" Width="Auto" VerticalAlignment="Top"/>
       </StackPanel>

        <StackPanel Grid.Row="1" Orientation="Horizontal">
            <Button x:Name="Ok" Content="Ok" HorizontalAlignment="Left" Margin="50,0,0,0" VerticalAlignment="Top" Click="OkClicked"/>
            <Button x:Name="Cancel" Content="Cancel" HorizontalAlignment="Left" Margin="20,0,0,0" VerticalAlignment="Top" Click="CancelClicked"/>
        </StackPanel>
    </Grid>
</Page>

Código de exemplo do C# para mostrar a interface do usuário e alterar PrintTicket:

namespace PsaSampleApp
{
    /// <summary>
    /// Class for showing print settings to the user and allow user to modify it
    /// </summary>
    public sealed partial class DefaultSettingsView: Page
    {
        private IppPrintDevice printer;
        private PrintSupportSettingsUISession uiSession;
        private WorkflowPrintTicket printTicket;
        private App application;
        // Bound to XAML combo box
        public ObservableCollection<PrintTicketOption> OrientationFeatureOptions { get; } = new ObservableCollection<PrintTicketOption>();
        public PrintTicketOption SelectedOrientationOption { get; set; }  

        public SettingsView()
        {
            this.InitializeComponent();
            this.application = Application.Current as App;
            this.orientationFeatureOptions = new ObservableCollection<PrintTicketOption>();
        }

        internal void OnNavigatedTo(NavigationEventArgs e)
        {
            this.uiSession = = e.Parameter as PrintSupportSettingsUISession;
            this.printer = session.SessionInfo.Printer;
            this.printTicket = session.SessionPrintTicket;
            
            PrintTicketCapabilities printTicketCapabilities = this.printTicket.GetCapabilities();

            // Read orientation feature from PrintTicket capabilities
            PrintTicketFeature feature = printTicketCapabilities.PageOrientationFeature;
            // Populate XAML combo box with orientation feature options
            this.PopulateOrientationOptionComboBox(feature.Options); 

            PrintTicketOption printTicketOrientationOption = printTicket.PageOrientationFeature.GetSelectedOption();
            // Update orientation option in XAML combo box
            this.SelectedOrientationOption = this.orientationFeatureOptions.Single((option)=> (option.Name == printTicketOrientationOption.Name && option.XmlNamespace == printTicketOrientationOption.XmlNamespace));
        }

        private async void OkClicked(object sender, RoutedEventArgs e)
        {
            // Disable Ok button while the print ticket is being submitted
            this.Ok.IsEnabled = false;

            // Set selected orientation option in the PrintTicket and submit it
            PrintTicketFeature orientationFeature = this.printTicket.PageOrientationFeature;
            orientationFeature.SetSelectedOption(this.SelectedOrientationOption);
            // Validate and submit PrintTicket
            WorkflowPrintTicketValidationResult result = await printTicket.ValidateAsync();
            if (result.Validated)
            {
                // PrintTicket validated successfully – submit and exit
                this.uiSession.UpdatePrintTicket(printTicket);
                this.application.ExitSettings();
            }
            else
            {
                this.Ok.IsEnabled = true;
                // PrintTicket is not valid – show error
                this.ShowInvalidPrintTicketError(result.ExtendedError);
            }
        }

        private void CancelClicked(object sender, RoutedEventArgs e)
        {
            this.application.ExitSettings();
        }
    }
}

Obter atributos de impressora do dispositivo de impressora

Resposta do Wireshark de uma impressora IPP a uma consulta get-printer-attributes:

resposta do wireshark de uma impressora IP a uma consulta para obter atributos da impressora de consulta de atributos de impressora

Código de exemplo C# para obter nomes de tinta e níveis de tinta da impressora:

namespace PsaSampleApp
{
    /// <summary>
    /// Class for showing print settings to the user
    /// </summary>
    public sealed partial class SettingsView : Page
    { 
       IList<string> inkNames;
       IList<int> inkLevels;
        
        private async void GetPrinterAttributes()
        {
            // Read ink names and levels, along with loaded media-sizes
            var attributes = new List<string>();
            attributes.Add("marker-names");
            attributes.Add("marker-levels");
            attributes.Add("media-col-ready");
            IDictionary<string, IppAttributeValue> printerAttributes = this.printer.GetPrinterAttributes(attributes);

            IppAttributeValue inkNamesValue = printerAttributes["marker-names"];
            CheckValueType(inkNamesValue, IppAttributeValueKind.Keyword);
            this.inkNames = inkNamesValue.GetKeywordArray();
            
            IppAttributeValue inkLevelsValue = printerAttributes["marker-levels"];
            CheckValueType(inkLevelsValue, IppAttributeValueKind.Integer);
            this.inkLevels = inkLevelsValue.GetIntegerArray();
    
            // Read loaded print media sizes
        IppAttributeValue mediaReadyCollectionsValue = printerAttributes["media-col-ready"];
            foreach (var mediaReadyCollection in mediaReadyCollectionsValue.GetCollectionArray())
            {
                IppAttributeValue mediaSizeCollection;
                if (mediaReadyCollection.TryGetValue("media-size", out mediaSizeCollection))
                {
                    var xDimensionValue = mediaSizeCollection.GetCollectionArray().First()["x-dimension"];
                    var yDimensionValue = mediaSizeCollection.GetCollectionArray().First()["y-dimension"];
                    CheckValueType(xDimensionValue, IppAttributeValueKind.Integer);
                    CheckValueType(yDimensionValue, IppAttributeValueKind.Integer);
                    int xDimension = xDimensionValue.GetIntegerArray().First();
                    int yDimension = yDimensionValue.GetIntegerArray().First();
                    this.AddMediaSize(xDimension, yDimension);
                }
            }
        }

        private void CheckValueType(IppAttributeValue value, IppAttributeValueKind expectedKind)
        {
            if (value.Kind != expectedKind)
            {
                throw new Exception(string.Format("Non conformant type found: {0}, expected: {1}", value.Kind, expectedKind));
            }
        }
    }
}

Definindo atributos de impressora na impressora

Código de exemplo C# para definir atributos de impressora:

int defaultResolutionX = 1200;
int defaultResolutionY = 1200;
string pdlFormat = "image/pwg-raster";
private async void SetPrinterAttributes()
{
    var attributes = new Dictionary<string, IppAttributeValue>();
    attributes.Add("document-format-default", IppAttributeValue.CreateKeyword(this.pdlFormat));
    var resolution = new IppResolution(this.defaultResolutionX, this.defaultResolutionY, IppResolutionUnit.DotsPerInch);
    attributes.Add("printer-resolution-default", IppAttributeValue.CreateResolution(resolution));
            
    var result = this.printer.SetPrinterAttributes(attributes);
    if (!result.Succeeded)
    {
        foreach (var attributeError in result.AttributeErrors)
        {
            var attributeName = attributeError.Key;
            switch (attributeError.Value.Reason)
            {
            case IppAttributeErrorReason.AttributeValuesNotSupported:
                var values = attributeError.Value.GetUnsupportedValues().First();
                this.LogUnSupportedValues(attributeName, values);
                break;
            case IppAttributeErrorReason.AttributeNotSettable:
                this.LogAttributeNotSettable(attributeName);
                break;
            case IppAttributeErrorReason.AttributeNotSupported:
                this.LogAttributeNotSupported(attributeName);
                break;
            case IppAttributeErrorReason.RequestEntityTooLarge:
                this.LogAttributeNotEntityTooLarge(attributeName);
                break;
            case IppAttributeErrorReason. ConflictingAttributes:
                this.LogConflictingAttributes(attributeName);
                break;
            }
        }
    }
}

Estendendo restrições de impressora

O Aplicativo de Suporte à Impressão dá suporte à validação personalizada do PrintTicket e à definição do PrintTicket padrão. Esta seção descreve como damos suporte a esses recursos.

Para dar suporte a restrições de extensão de impressora, um novo tipo de tarefa em segundo plano, PrintSupportExtension, foi implementado. O Package.appxmanifest tem uma entrada de extensibilidade para a Extensão de Suporte à Impressão, conforme mostrado aqui:

<Extensions>
    <printsupport:Extension Category="windows.printSupportExtension" 
        EntryPoint="PsaBackgroundTasks.PrintSupportExtension"/>
</Extensions>

Esse serviço pode ser executado a qualquer momento em um trabalho de impressão para a impressora IPP associada. Como a Extensão de Suporte à Impressão é ativada por meio da função IBackgroundTaskInstance, uma instância de IBackgroundTaskInstance é dada a PrintSupportExtension para fornecer acesso à classe de runtime PrintSupportExtensionTriggerDetails, que fornece internamente PrintSupportExtensionSession como uma propriedade. A classe de plano de fundo PrintSupportExtension pode usar o objeto de sessão para registrar eventos que deseja fornecer funcionalidade personalizada.

  1. event Windows.Foundation.TypedEventHandler<PrintSupportExtensionSession, PrintSupportPrintTicketValidationRequestedEventArgs>; PrintTicketValidationRequested;

    Se a Extensão de Suporte à Impressão fornecer seu próprio mecanismo de validação PrintTicket, ela poderá se registrar para esse evento. Sempre que um PrintTicket precisar ser validado, o sistema de impressão gera esse evento. PrintSupportExtension obterá o PrintTicket atual que precisa ser validado em EventArgs. A classe de plano de fundo PrintSupportExtension pode verificar a validade do PrintTicket e modificá-lo para resolver quaisquer conflitos. A classe de segundo plano PrintSupportExtension deve definir o resultado para validação usando a função SetPrintTicketResult para indicar se o PrintTicket foi resolvido, tem conflitos ou é inválido. Esse evento pode ser gerado a qualquer momento durante o ciclo de vida de um trabalho de impressão. Se a classe PrintSupportExtension não se registrar para esse evento, o sistema de impressão executará sua própria validação do PrintTicket.

  2. event Windows.Foundation.TypedEventHandler<PrintSupportExtensionSession, PrintSupportPrintDeviceCapabilitiesChangedEventArgs>; PrintDeviceCapabilitiesChanged;

    O evento é gerado depois que o sistema de impressão atualiza o PrintDeviceCapabilities of armazenado em cache da impressora IPP associada. Quando esse evento é gerado, a classe de plano de fundo PrintSupportExtension pode inspecionar o PrintDeviceCapabilities alterado e modificá-lo.

Validação personalizada do ticket de impressão

Código de exemplo de C# para fornecer o serviço de validação PrintTicket:

public void Run(IBackgroundTaskInstance taskInstance)
{
    // Take task deferral
    this.taskDeferral = taskInstance.GetDeferral();
    // Associate a cancellation handler with the background task
    taskInstance.Canceled += OnTaskCanceled;

    var psaTriggerDetails = taskInstance.TriggerDetails as PrintSupportExtensionTriggerDetails;

    var serviceSession = psaTriggerDetails.Session as PrintSupportExtensionSession;

    this.ippPrintDevice = serviceSession.Printer;
    serviceSession.PrintTicketValidationRequested += this.OnPrintTicketValidationRequested;
    serviceSession.PrinterDeviceCapabilitiesChanged += this.OnPdcChanged;
    serviceSession.Start();
}

private void OnTaskCanceled(IBackgroundTaskInstance sender, BackgroundTaskCancellationReason reason)
{
    // Complete the deferral
    this.taskDeferral.Complete();
}

private void OnPrintTicketValidationRequested(PrintSupportExtensionSession session, PrintSupportPrintTicketValidationRequestedEventArgs args)
{
    using (args.GetDeferral())
    {
        // Get PrintTicket that needs needs to be validated and resolved   
        var printTicket = args.PrintTicket;
                
        // Validate and resolve PrintTicket
        WorkflowPrintTicketValidationStatus validationStatus = this.ValidateAndResolvePrintTicket(printTicket);
        args.SetPrintTicketValidationStatus(validationStatus);
    }
}

Atualizando CapacidadesDoDispositivoDeImpressão

private void OnPdcChanged(PrintSupportExtensionSession session, PrintSupportPrintDeviceCapabilitiesChangedEventArgs args)
{
    using (args.GetDeferral())
    {
        var pdc = args.GetCurrentPrintDeviceCapabilities();

        // Check current PDC and make changes according to printer device capabilities
        XmlDocument newPdc = this.CheckAndUpdatePrintDeviceCapabilities(pdc);
        args.UpdatePrintDeviceCapabilities(newPdc);
    }
}

Aprimoramento da qualidade da impressão

Depois que o usuário tiver se comprometido a imprimir pressionando o botão imprimir na caixa de diálogo de impressão, o documento a ser impresso será enviado para a pilha de impressão do aplicativo que está imprimindo. Em seguida, este documento passa por transformação (renderização para PDL) para torná-lo adequado para a impressora de destino. O Windows determinará qual transformação escolher com base nos atributos consultados na impressora. Em seguida, o documento transformado é enviado para a impressora. Embora isso funcione bem para a maioria das impressoras, há casos em que a qualidade da impressão pode ser melhorada permitindo que um aplicativo parceiro participe da transformação. Para facilitar isso, a API de fluxo de trabalho de impressão atual é estendida para incluir chamadas para o aplicativo em pontos adicionais da pilha de impressão. Essa API dá suporte a dois novos eventos para os quais o aplicativo PSA pode se registrar. Estes são os únicos pontos de entrada na superfície da API do PSA:

  1. JobStarting

    • Esse evento é gerado quando um trabalho de impressão é iniciado por qualquer aplicativo. Quando o evento é gerado, um Aplicativo de Suporte à Impressão pode optar por ignorar a renderização do sistema chamando SetSkipSystemRendering em PrintWorkflowJobStartingEventArgs. Se a opção de ignorar a renderização do sistema for escolhida, o sistema de impressão não converterá o documento XPS no formato PDL requerido pela impressora. Em vez disso, o XPS gerado pelo aplicativo de impressão será diretamente dado ao PSA que, em seguida, é responsável pela conversão de XPS no formato PDL.
  2. PdlModificationRequested

    • Esse evento é gerado quando o Windows inicia a conversão do fluxo XPS para o formato PDL indicado pela impressora. A classe de runtime PrintWorkflowPdlModificationRequestedEventArgs é fornecida como um argumento para esse evento. Esta classe de eventos fornece objetos de origem e de destino PDL para leitura e gravação do conteúdo do trabalho de impressão. Se o Aplicativo determinar que precisa de entrada do usuário, poderá iniciar a interface do usuário usando PrintWorkflowUILauncher do EventArgs. Essa API usa o padrão Tester-Doer. PrintWorkflowUILauncher não conseguirá invocar a UI se a função IsUILaunchEnabled retornar false. Essa função retornará false se a sessão PSA estiver sendo executada no modo silencioso (modo sem periféricos ou de quiosque). O Aplicativo de Suporte à Impressão não deve tentar iniciar a interface do usuário se a função retornar false.

    Um OutputStream está disponível como parte do PrintWorkflowPdlTargetStream que é retornado pela função GetStreamTargetAsync. O conteúdo gravado no OutputStream de destino é passado para a impressora como conteúdo do documento.

Diagrama de sequência para o evento de modificação PDL:

diagrama de sequência para o evento de modificação P D L do fluxo de origem

O aplicativo em primeiro plano PSA é iniciado quando a tarefa em segundo plano PSA solicita a inicialização da interface do usuário. O PSA pode usar o contrato de primeiro plano para obter a entrada do usuário e/ou mostrar uma visualização de impressão para o usuário.

Foi definido um novo tipo de tarefa em segundo plano: printSupportWorkflow. O Package.appxmanifest tem a seguinte entrada de extensibilidade para o contrato PrintSupportWorkflow:

<Extensions>
    <printsupport:Extension Category="windows.printSupportWorkflow" 
        EntryPoint="PsaBackgroundTasks.PrintSupportWorkflowSample"/>
</Extensions>

Na ativação do contrato, PrintWorkflowJobTriggerDetails é fornecido como IBackgroundTaskInstance->TriggerDetails. PrintWorkflowJobTriggerDetails fornece internamente PrintWorkflowJobBackgroundSession como parte de suas propriedades. O aplicativo pode usar PrintWorkflowJobBackgroundSession para registrar eventos relacionados a vários pontos de injeção no fluxo do trabalho de impressão. Depois que o registro do evento for concluído, o aplicativo deverá chamar PrintWorkflowJobBackgroundSession::Start para que o sistema de impressão comece a disparar eventos relacionados a vários pontos de injeção.

Um novo ActivationKind chamado PrintSupportJobUI foi definido. Isso não requer uma nova funcionalidade.

<Extensions>
    <printsupport:Extension Category="windows.printSupportJobUI" 
        EntryPoint="PsaSample.PrintSupportJobUISample"/>
</Extensions>

Este é um contrato de interface do usuário que pode ser iniciado a partir do contrato em segundo plano do Fluxo de Trabalho de Suporte à Impressão ou quando o usuário seleciona uma notificação do sistema de erro de trabalho de impressão. Na ativação, PrintWorkflowJobActivatedEventArgs é fornecido, o qual tem um objeto PrintWorkflowJobUISession. Usando PrintWorkflowJobUISession, o aplicativo de primeiro plano deve se registrar para o evento PdlDataAvailable se quiser acessar os dados de PDL. Se o aplicativo em primeiro plano quiser mostrar mensagens de erro personalizadas para quaisquer erros que possam ocorrer durante o trabalho, ele deverá se registrar para o evento JobNotification. Depois que os eventos são registrados, o aplicativo deve chamar a função PrintWorkflowJobUISession::Start para que o sistema de impressão comece a disparar eventos.

Ignorando a renderização do sistema

namespace PsaBackground
{
    class PrintSupportWorkflowBackgroundTask : IBackgroundTask
    {
        BackgroundTaskDeferral taskDeferral;
        public void Run(IBackgroundTaskInstance taskInstance)
        {
            // Take Task Deferral            
            taskDeferral = taskInstance.GetDeferral();

            var jobTriggerDetails = taskInstance.TriggerDetails as PrintWorkflowJobTriggerDetails;

            var workflowBackgroundSession = jobTriggerDetails.PrintWorkflowJobSession as PrintWorkflowJobBackgroundSession;
            // Register for events
            workflowBackgroundSession.JobStarting += this.OnJobStarting;
            workflowBackgroundSession.PdlModificationRequested += this.OnPdlModificationRequested;
            // Start Firing events
            workflowBackgroundSession.Start();
        }
    
        private void OnJobStarting(PrintWorkflowJobBackgroundSession session, PrintWorkflowJobStartingEventArgs args)
        {
            using (args.GetDeferral())
            {
                // Call SetSkipSystemRendering to skip conversion for XPS to PDL, so that PSA can directly manipulate the XPS file.
                args.SetSkipSystemRendering();
            }
        }
     }
}

Evento de modificação de PDL

Diagrama de sequência para o evento de modificação PDL:

diagrama de sequência para o evento de modificação P D L do fluxo de entrada

Código de exemplo em C# para o Monitor de Trabalho de Suporte de Impressão, que lê e escreve o conteúdo do trabalho de impressão:

private void OnPdlModificationRequested(PrintWorkflowJobBackgroundSession session, PrintWorkflowPdlModificationRequestedEventArgs args)
{
    using (args.GetDeferral())
    {
        IInputStream pdlContent = args.SourceContent.GetInputStream();
        // Specify the Content type of stream that will be written to target that is passed to printer accordingly.
        PrintWorkflowPdlTargetStream streamTarget = args.CreateJobOnPrinter(args.SourceStream.ContentType);
        IOutputStream outputStream = streamTarget.GetOutputStream();

        using (var inputReader = new Windows.Storage.Streams.DataReader(pdlContent))
        {
            inputReader.InputStreamOptions = InputStreamOptions.Partial;
            using (var outputWriter = new Windows.Storage.Streams.DataWriter(outputStream))
            {
                // Write the updated Print stream from input stream to the output stream
                uint chunkSizeInBytes = 256 * 1024; // 256K chunks
                
                uint lastAllocSize = 0;
                byte[] contentData = new byte[chunkSize];
                while(this.ReadChunk(inputReader, ref contentData))
                {
                    
                    // Make any changes required to the input data
                    // ...                        
                    // Write out the modified content
                    outputWriter.WriteBytes(contentData);
                    await outputWriter.StoreAsync();
                }
            }
        }
        streamTarget.CompleteStreamSubmission(PrintWorkflowSubmittedStatus.Succeeded);
        this.taskDeferral.Complete();
        }
    }
}

Iniciando a interface do usuário a partir do plano de fundo do fluxo de trabalho

Código de exemplo de C# para iniciar a interface do usuário do Trabalho de Suporte à Impressão do contrato de evento solicitado de modificação do PSA PDL:

private async void OnPdlModificationRequested(PrintWorkflowJobBackgroundSession session, PrintWorkflowPdlModificationRequestedEventArgs args)
{
    IInputStream pdlContent = args.SourceContent.GetInputStream();
    WorkflowPrintTicket printTicket = args.PrinterJob.GetJobPrintTicket();

    bool uiRequired = this.IsUIRequired(pdlContent, printTicket);
    if (!uiRequired)
    {
        // Specify the Content type of content that will be written to target that is passed to printer accordingly.
        PrintWorkflowPdlTargetStream streamTarget = args.CreateJobOnPrinter (args.SourceStream.ContentType);
        // Process content directly if UI is not required
        this.ProcessContent(pdlContent, streamTarget);
    }
    else if (args.UILauncher.IsUILaunchEnabled())
    {
        // LaunchAndCompleteUIAsync will launch the UI and wait for it to complete before returning 
        PrintWorkflowUICompletionStatus status = await args.UILauncher.LaunchAndCompleteUIAsync();
        if (status == PrintWorkflowUICompletionStatus.Completed)
        {
            PrintWorkflowPdlTargetStream streamTarget = args.CreateJobOnPrinter(args.SourceStream.ContentType);
            this.ProcessContent(pdlContent, streamTarget);
        }
        else
        {
            if (status == PrintWorkflowUICompletionStatus.UserCanceled)
            {
                // Log user cancellation and cleanup here.
                this.taskDeferral.Complete();
            }
            else
            {
                // UI launch failed, abort print job.
                args.Configuration.AbortPrintFlow(PrintWorkflowAbortReason.JobFailed);
                this.taskDeferral.Complete();
            }
        }
    }
    else
    {
        // PSA requires to show UI, but launching UI is not supported at this point because of user selection.
        args.Configuration.AbortPrintFlow(PrintWorkflowAbortReason.JobFailed);
        this.taskDeferral.Complete();
    }
}

Ativação da interface do usuário do trabalho de fluxo de trabalho para o evento PDLDataAvailable

Diagrama de sequência para ativação da interface do usuário do trabalho de impressão para o evento PdlDataAvailable:

diagrama de sequência para ativação da interface do usuário do trabalho de impressão para o evento P D L data available

Código de exemplo de C# para o contrato de ativação da interface do usuário do trabalho do PSA:

namespace PsaSampleApp
{
    sealed partial class App : Application
    {
        protected override void OnActivated(IActivatedEventArgs args)
        {
            if (args.Kind == ActivationKind.PrintSupportJobUI)
            {
                var rootFrame = new Frame();
        
                rootFrame.Navigate(typeof(JobUIPage));
                Window.Current.Content = rootFrame;
        
                var jobUI = rootFrame.Content as JobUIPage;

                // Get the activation arguments
                var workflowJobUIEventArgs = args as PrintWorkflowJobActivatedEventArgs;

                PrintWorkflowJobUISession session = workflowJobUIEventArgs.Session;
                session.PdlDataAvailable += jobUI.OnPdlDataAvailable;
                session.JobNotification += jobUI.OnJobNotification;
                // Start firing events
                session.Start(); 
            }
        }
    }
}

namespace PsaSampleApp
{
    public sealed partial class JobUIPage : Page    
    {
        public JobUIPage()
        {
            this.InitializeComponent();
        }

        public string WorkflowHeadingLabel;

        public void OnPdlDataAvailable(PrintWorkflowJobUISession session, PrintWorkflowPdlDataAvailableEventArgs args)
        {
            using (args.GetDeferral())
            {
                string jobTitle = args.Configuration.JobTitle;
                string sourceApplicationName = args.Configuration.SourceAppDisplayName;            
                string printerName = args.Printer.PrinterName;
                this.WorkflowHeadingLabel = string.Format(this.formatHeading, jobTitle, sourceApplicationName, printerName);

                // Get pdl stream and content type
                IInputStream pdlContent = args.SourceContent.GetInputStream();
                string contentType = args.SourceContent.ContentType;
                this.ShowPrintPreview(pdlContent, contentType);
            }
        }
    }
}

Obter atributos de trabalho de impressora

Código de exemplo C# para obter atributos de trabalho para um trabalho de impressão:

namespace PsaBackground
{
    class PrintSupportWorkflowBackgroundTask : IBackgroundTask
    {
        private async void OnPdlModificationRequested(PrintWorkflowJobBackgroundSession session, 
                             PrintWorkflowPdlModificationRequestedEventArgs args)
        {
            using (args.GetDeferral())
            {
                string colorMode = this.GetJobColorMode(args.PrinterJob);
                if (colorMode != "monochrome")
                {
                    this.SetJobColorModeToMonochrome(args.PrinterJob);
                } 
            }
        }

        private string GetJobColorMode(PrintWorkflowPrinterJob printerJob)
        {
            var attributes = new List<string>();
            attributes.Add("print-color-mode");
             // Gets the IPP attributes from the current print job
            IDictionary<string, IppAttributeValue> printerAttributes = printerJob.GetJobAttributes(attributes);

            var colorModeValue =  printerAttributes["print-color-mode"];
            this.CheckValueType(colorModeValue, IppAttributeValueKind.Keyword);

            return colorModeValue.GetKeywordArray().First();
        }
    }
} 

Definir atributos de trabalho de impressora

Código de exemplo C#, continuando na seção Obter atributos de trabalho de impressora acima, demonstrando a configuração de atributos de trabalho:

private async void SetJobColorModeToMonochrome(PrintWorkflowPrinterJob printerJob)
{
    var attributes = new Dictionary<string, IppAttributeValue>();
    attributes.Add("print-color-mode", IppAttributeValue.CreateKeyword("monochrome"));

    var result = PrinterJob.SetJobAttributes(attributes);
    if (!result.Succeeded)
    {
        this.LogSetAttributeError(result.AttributeErrors);
    }
}

Algumas impressoras IPP não dão suporte à obtenção/configuração de atributos de trabalho após a criação do trabalho. Para essas impressoras, PrintJob tem a propriedade JobId definida como "0" e GetJobAttributes/SetJobAttributes falhará imediatamente com uma exceção.

Proporcionando acesso a arquivos de armazenamento para o conteúdo PDL

Alguns formatos PDL, como PDF, precisam de um fluxo completo para estarem disponíveis para iniciar o processamento. Por esse motivo, um novo método chamado GetContentFileAsync é fornecido na classe PrintWorkflowPdlSourceContent que retorna um StorageFile do conteúdo de origem.

public sealed partial class JobUIPage : Page
{
    public async void OnPdlDataAvailable(PrintWorkflowJobUISession session, PrintWorkflowPdlDataAvailableEventArgs args)
    {
        using (args.GetDeferral())
        {
            if (String.Equals(args.SourceContent.ContentType, "application/pdf", StringComparison.OrdinalIgnoreCase))
            {
                // Wait for all PDL data to be available
                StorageFile sourceFile == await args.SourceContent.GetContentFileAsync();
                IRandomAccessStream sourceStream = await sourceFile.OpenReadAsync();

                PdfDocument pdfDocument = await PdfDocument.LoadFromStreamAsync(sourceStream);

                for (uint i = 0; i < pdfDocument.PageCount; i++)
                {
                    PdfPage page = pdfDocument.GetPage(i);
                    var pageImage = new InMemoryRandomAccessStream();
                    await page.RenderToStreamAsync(pageImage);
                    this.AddImageToPreviewImageList(pageImage);
                }
            }
        }
    }
}    

Conversão PDL de XPS em PDF

Código de exemplo em C# mostrando a conversão PDL de XPS em PDF:

private async void OnPdlModificationRequested(PrintWorkflowJobBackgroundSession session, PrintWorkflowPdlModificationRequestedEventArgs args)
{
    using (args.GetDeferral())
    {
        if (String.Equals(args.SourceContent.ContentType, "application/oxps", StringComparison.OrdinalIgnoreCase))
        {
            var xpsContent = args.SourceContent.GetInputStream();

            var printTicket = args.PrinterJob.GetJobPrintTicket();
            PrintWorkflowPdlTargetStream streamTarget = args.CreateJobOnPrinter("application/pdf");

            // Modify XPS stream here to make the needed changes 
            // for example adding a watermark

            PrintWorkflowPdlConverter pdlConverter = args.GetPdlConverter(PrintWorkflowPdlConversionType.XpsToPdf);
            await pdlConverter.ConvertPdlAsync(printTicket, xpsContent, streamTarget.GetOutputStream());

            streamTarget.CompleteStreamSubmission(PrintWorkflowSubmittedStatus.Succeeded);
        }
        else
        {
            // We except source content to be XPS in this case, abort the session if it is not XPS.
            args.Configuration.AbortPrintFlow(PrintWorkflowAbortReason.JobFailed);
        }
    }
    this.taskDeferral.Complete();
}

Evento de notificação de trabalho

Diagrama de sequência para evento de notificação de trabalho:

diagrama de sequência para o evento de notificação de trabalho

Código de exemplo de C# (continuação da ativação da interface do usuário do trabalho de fluxo de trabalho para a seção do evento PDLDataAvailable acima, para mostrar o erro na notificação do trabalho:

public sealed partial class JobUIPage : Page    
{
    public void OnJobNotification(PrintWorkflowJobUISession session, PrintWorkflowJobNotificationEventArgs args)
    {
        using (args.GetDeferral())
        {
            PrintWorkflowPrinterJobStatus jobStatus = args.PrintJob.GetJobStatus();

            switch (jobStatus)
            {
                case PrintWorkflowPrinterJobStatus::Error:
                    // Show print job error to the user
                    Frame->Navigate(JobErrorPage::typeid, this);
                break;
                case PrintWorkflowPrinterJobStatus::Abort:
                    // Show message that print job has been aborted.
                    Frame->Navigate(JobAbortPage::typeid, this);
                break;
                case PrintWorkflowPrinterJobStatus::Completed:
                    // Show job successfully completed message to the user.
                    Frame->Navigate(JobCompletedPage::typeid, this);
                break;
            }
        }
    }    
}

Criar trabalho com atributos de trabalho inicial

Atualmente, algumas impressoras IPP não dão suporte à operação set-attribute. A função CreateJobOnPrinterWithAttributes e a função CreateJobOnPrinterWithAttributesBuffer no PrintWorkflowPdlDataAvailableEventArgs são fornecidas para atenuar esse problema. Usando essas APIs, um desenvolvedor de PSA pode fornecer atributos de trabalho que são passados para a impressora quando o trabalho é criado na impressora.

public sealed partial class JobUIPage : Page
{
    public async void OnPdlDataAvailable(PrintWorkflowJobUISession session, PrintWorkflowPdlDataAvailableEventArgs args)
    {
       var attributes = new Dictionary<string, IppAttributeValue>();
       attributes.Add("print-color-mode", IppAttributeValue.CreateKeyword("monochrome"));
       // Create job on printer with initial job attributes
       PrintWorkflowPdlTargetStream streamTarget = args.CreateJobOnPrinterWithAttributes(attributes, "application/pdf");
        // Write data to target stream
    }
}

Processamento de XPS sequencial

Código de exemplo de C++/Winrt para processar XPS sequencialmente antes que o spool seja concluído.

namespace winrt
{
    struct WorkflowReceiver : public winrt::implements<WorkflowReceiver, IPrintWorkflowXpsReceiver2>
    {
        STDMETHODIMP SetDocumentSequencePrintTicket(_In_ IStream* documentSequencePrintTicket) noexcept override
        {
            // process document sequence print ticket
            return S_OK;
        }

        STDMETHODIMP SetDocumentSequenceUri(PCWSTR documentSequenceUri) noexcept override
        {
            // process document sequence URI
        }

        STDMETHODIMP AddDocumentData(UINT32 documentId, _In_ IStream* documentPrintTicket,
            PCWSTR documentUri) noexcept override
        {
            // process document URI and print ticket
            return S_OK;
        }

        STDMETHODIMP AddPage(UINT32 documentId, UINT32 pageId,
            _In_ IXpsOMPageReference* pageReference, PCWSTR pageUri)  noexcept override
        {
            // process XPS page
            return S_OK;
        }

        STDMETHODIMP Close() noexcept override
        {
            // XPS processing finished
            return S_OK;
        }

        STDMETHODIMP Failed(HRESULT XpsError) noexcept override
        {
            // XPS processing failed, log error and exit
            return S_OK;
        }
    };

    void PsaBackgroundTask::OnPdlModificationRequested(PrintWorkflowJobBackgroundSession session,
        PrintWorkflowPdlModificationRequestedEventArgs args)
    {
    auto contentType = args.SourceContent().ContentType();
        if (contentType == L"application/oxps")
        {
                    auto xpsContent = args.SourceContent().GetInputStream();
                    PrintWorkflowObjectModelSourceFileContent xpsContentObjectModel(xpsContent);
                    com_ptr<IPrintWorkflowObjectModelSourceFileContentNative> xpsContentObjectModelNative;
                    check_hresult(winrt::get_unknown(xpsContentObjectModel)->QueryInterface( 
                                                        IID_PPV_ARGS(xpsContentObjectModelNative.put())));
        
                    auto xpsreceiver = make_self<WorkflowReceiver>();
                    check_hresult(xpsContentObjectModelNative->StartXpsOMGeneration(xpsreceiver.get()));
        }
    }
}

Localização de nome de exibição e integração da API de Passagem do PDL

Importante

Esta seção descreve a funcionalidade PSA disponível a partir de Windows 11, versão 22H2.

Nesse cenário, o PSA personaliza as Capacidades do Dispositivo de Impressão (PDC) e fornece Recursos de Dispositivo de Impressão (PDR) para localização de cadeia de caracteres.

O PSA também define os tipos de conteúdo da API de Passagem do PDL (formatos PDL) compatíveis. Se o PSA não se inscrever no evento ou não chamar SetSupportedPdlPassthroughContentTypes explicitamente, a Passagem do PDL será desabilitada para as impressoras associadas a este aplicativo PSA.

// Event handler called every time PrintSystem updates PDC or BindPrinter is called
 private void OnPdcChanged(PrintSupportExtensionSession session, PrintSupportPrintDeviceCapabilitiesChangedEventArgs args)
{
    using (args.GetDeferral())
    {
        XmlDocument pdc = args.GetCurrentPrintDeviceCapabilities();
        XmlDocument pdr = args.GetCurrentPrintDeviceResources();
        
        // Check current PDC and make changes according to printer device capabilities 
        XmlDocument newPdc = this.CheckAndUpdatePrintDeviceCapabilities(pdc);
        // Get updated printer devices resources, corresponding to the new PDC 
        XmlDocument newPdr = this.GetPrintDeviceResourcesInfo(newPdc, pdr, args.ResourceLanguage);

        // Update supported PDL formats 
        args.SetSupportedPdlPassthroughContentTypes(GetSupportedPdlContentTypes());
        
        args.UpdatePrintDeviceCapabilities(newPdc);
        args.UpdatePrintDeviceResources(newPdr);
    }
}

Suporte a recursos de nível de página e atributos de operação

Importante

Esta seção descreve a funcionalidade PSA disponível no Windows 11, a partir da versão 22H2.

Os cenários de suporte a recursos de nível de página e atributos de operação são agrupados porque foram resolvidos fazendo alterações no mesmo local no código de exemplo.

  • Suporte a recursos de nível de página: nesse cenário, o aplicativo PSA especifica o atributo de nível de página, que não deve ser substituído por um atributo IPP analisado no PrintTicket.

  • Coleção separada para suporte a atributos de operação (impressão PIN): Neste cenário, o aplicativo PSA especifica atributos de operação IPP personalizados (por exemplo, PIN).

O código de exemplo de C# a seguir mostra as alterações necessárias para os cenários Suporte a recursos de nível de página e Coleta separada para atributos de operação.

private void OnPdlModificationRequested(PrintWorkflowJobBackgroundSession session, PrintWorkflowPdlModificationRequestedEventArgs args)
{
    using (args.GetDeferral())
    {
        IInputStream pdlContent = args.SourceContent.GetInputStream();
    
        // Custom job attributes to add to the printJob
        IDictionary<string, IppAttributeValue> jobAttributes = LocalStorageUtil.GetCustomIppJobAttributes();
        // Custom operation attributes to add to printJob
        IDictionary<string, IppAttributeValue> operationAttributes = LocalStorageUtil.GetCustomIppOperationAttributes();
        
        // PSA has an option to select preferred PDL format
        string documentFormat = GetDocumentFormat(args.PrinterJob.Printer);
    
        // Create PrintJob with specified PDL and custom attributes
        PrintWorkflowPdlTargetStream targetStream = args.CreateJobOnPrinterWithAttributes(jobAttributes, documentFormat  , operationAttributes,
           PrintWorkflowAttributesMergePolicy  .DoNotMergeWithPrintTicket /*jobAttributesMergePolicy*/, PrintWorkflowAttributesMergePolicy.MergePreferPsaOnConflict /*operationAttributesMergePolicy*/);
    
        // Adding a watermark to the output(targetStream) if source payload type is XPS
        this.ModifyPayloadIfNeeded(targetStream, args, documentFormat, deferral);
    
        // Marking the stream submission as Succeeded.
        targetStream.CompleteStreamSubmission(PrintWorkflowSubmittedStatus.Succeeded);
    
        this.taskDeferral.Complete();
    }
}

Aprimorando a caixa de diálogo de impressão com PSA

Importante

Esta seção descreve a funcionalidade PSA disponível desde a versão 22H2 do Windows 11.

Nesse cenário, o uso da caixa de diálogo de impressão com a integração do PSA habilita as seguintes ações:

  • Obter um retorno de chamada quando a seleção é alterada no MPD para a impressora associada ao PSA

  • Mostrar um AdaptiveCard com suporte à ação openUrl

  • Mostrar recursos e parâmetros personalizados na caixa de diálogo de impressão

  • Modifique o PrintTicket, alterando assim a seleção das opções de recurso mostradas na caixa de diálogo de impressão

  • Obter o Windows.ApplicationModel.AppInfo do aplicativo de impressão, abrindo a caixa de diálogo de impressão

O exemplo de C# a seguir ilustra esses aprimoramentos da caixa de diálogo de impressão:

public BackgroundTaskDeferral TaskInstanceDeferral { get; set; }

public void Run(IBackgroundTaskInstance taskInstance)
{
    // Take task deferral 
    TaskInstanceDeferral   = taskInstance.GetDeferral();
    // Associate a cancellation handler with the background task 
    taskInstance.Canceled += OnTaskCanceled;

    if (taskInstance.TriggerDetails is PrintSupportExtensionTriggerDetails extensionDetails)
    {
         PrintSupportExtensionSession session = extensionDetails.Session;
         session.PrintTicketValidationRequested += OnSessionPrintTicketValidationRequested;
         session.PrintDeviceCapabilitiesChanged += OnSessionPrintDeviceCapabilitiesChanged;
         session.PrinterSelected += this.OnPrinterSelected;
    }
}

private void OnTaskInstanceCanceled(IBackgroundTaskInstance sender, BackgroundTaskCancellationReason reason)
{
    TaskInstanceDeferral.Complete();
}

// Event handler called when the PSA Associated printer is selected in Print Dialog
private void OnPrinterSelected(PrintSupportExtensionSession session, PrintSupportPrinterSelectedEventArgs args)
{
    using (args.GetDeferral())
    {
        // Show adaptive card in the Print Dialog (generated based on Printer and Printing App) 
        args.SetAdaptiveCard  (GetCustomAdaptiveCard(session.Printer, args.SourceAppInfo));

        // Request to show Features and Parameters in the Print Dialog if not shown already
        const string xmlNamespace = "\"http://schemas.microsoft.com/windows/2003/08/printing/printschemakeywords\"";
        var additionalFeatures= new List<PrintSupportPrintTicketElement> { new PrintSupportPrintTicketElement { LocalName = "PageMediaType", NamespaceUri = xmlNamespace } };                  
        var additionalParameters = new List<PrintSupportPrintTicketElement> { new PrintSupportPrintTicketElement { LocalName = "JobCopiesAllDocuments", NamespaceUri = xmlNamespace } };

        if ((featuresToShow.Count + parametersToShow.Count) <= args.AllowedCustomFeaturesAndParametersCount)
        {
            args.SetAdditionalFeatures(additionalFeatures);
            args.SetAdditionalParameter(additionalParameters);
        }
        else
        {
            // Cannot show that many additional features and parameters, consider reducing the number
            // of additional features and parameters by selecting only the most important ones
        }
    }
}

// Create simple AdaptiveCard to show in MPD
public IAdaptiveCard GetCustomAdaptiveCard(IppPrintDevice ippPrinter, AppInfo appInfo)
{
    return AdaptiveCardBuilder.CreateAdaptiveCardFromJson($@"
        {{""body"": [
                {{ 
                    ""type"": ""TextBlock"",
                    ""text"": ""Hello {appInfo.DisplayInfo.DisplayName} from {ippPrinter.PrinterName}!""
                }}
              ],
              ""$schema"": ""http://adaptivecards.io/schemas/adaptive-card.json"",
            ""type"": ""AdaptiveCard"",
            ""version"": ""1.0""
        }}");
}

Conversão de PDL com sinalizadores de processamento baseados em host

Importante

Esta seção descreve a funcionalidade PSA disponível a partir do Windows 11, versão 22H2.

A API de conversão PDL atual, PrintWorkflowPdlConverter.ConvertPdlAsync, faz o processamento baseado em host por padrão. Isso significa que o computador que hospeda/imprime faz a rotação, a ordem das páginas e assim por diante, para que a impressora não precise executar essas operações. No entanto, os IHVs de impressora podem preferir a conversão PDL realizada diretamente na impressora, sem processamento baseado em host, pois ela pode fazer isso melhor. A função ConvertPdlAsync usa sinalizadores de processamento baseados em host para atender a esse requisito. O PSA pode ignorar todo o processamento baseado em host ou uma operação de processamento baseada em host específica usando esse sinalizador.

class HostBaseProcessingRequirements
{
    public bool CopiesNeedsHostBasedProcessing = false;
    public bool PageOrderingNeedsHostBasedProcessing = false;
    public bool PageRotationNeedsHostBasedProcessing = false;
    public bool BlankPageInsertionNeedsHostBasedProcessing = false;
}

private async void OnPdlModificationRequested(PrintWorkflowJobBackgroundSession sender, PrintWorkflowPdlModificationRequestedEventArgs args)
{
    using (args.GetDeferral())
    {
        var targetStream = args.CreateJobOnPrinter("application/pdf");
        var pdlConverter = args.GetPdlConverter(PrintWorkflowPdlConversionType.XpsToPdf);

        var hostBasedRequirements = this.ReadHostBasedProcessingRequirements(args.PrinterJob.Printer);
            
        PdlConversionHostBasedProcessingOperations hostBasedProcessing = PdlConversionHostBasedProcessingOperations.None;
        if (hostBasedRequirements.CopiesNeedsHostBasedProcessing)
        {
            hostBasedProcessing |= PdlConversionHostBasedProcessingOperations.Copies;
        }

        if (hostBasedRequirements.PageOrderingNeedsHostBasedProcessing)
        {
            hostBasedProcessing |= PdlConversionHostBasedProcessingOperations.PageOrdering;
        }

        if (hostBasedRequirements.PageRotationNeedsHostBasedProcessing)
        {
            hostBasedProcessing |= PdlConversionHostBasedProcessingOperations.PageRotation;
        }

        if (hostBasedRequirements.BlankPageInsertionNeedsHostBasedProcessing)
        {
            hostBasedProcessing |= PdlConversionHostBasedProcessingOperations.BlankPageInsertion;
        }

        await pdlConverter.ConvertPdlAsync(args.PrinterJob.GetJobPrintTicket(), args.SourceContent.GetInputStream(), targetStream.GetOutputStream(), hostBasedProcessing);
    }
}

private HostBaseProcessingRequirements ReadHostBasedProcessingRequirements(IppPrintDevice printDevice)
{
    // Read Host based processing requirements for the printer
}

Definir a política de atualização de recursos de dispositivo de impressão (PDC)

Importante

Esta seção descreve a funcionalidade PSA disponível a partir do Windows 11, versão 22H2.

Os IHVs da impressora podem ter requisitos diferentes sobre quando as funcionalidades de dispositivo de impressão (PDC) precisam ser atualizadas. Para atender a esses requisitos, PrintSupportPrintDeviceCapabilitiesUpdatePolicy pode definir uma política de atualização para o PDC. O PSA pode definir a política de atualização do PDC com base no tempo ou no número de trabalhos de impressão usando essa API.

Definir a política de atualização do PDC com base no número de trabalhos

// Event handler called every time PrintSystem updates PDC
private void OnPdcChanged(PrintSupportExtensionSession session, PrintSupportPrintDeviceCapabilitiesChangedEventArgs args)
{
    using (args.GetDeferral())
    {
        // Set update policy to update the PDC on bind printer of every print job.
        var updatePolicy = PrintSupportPrintDeviceCapabilitiesUpdatePolicy.CreatePrintJobRefresh(1);
        args.SetPrintDeviceCapabilitiesUpdatePolicy(updatePolicy);      
    }
}

Definir a política de atualização do PDC com base no TimeOut

// Event handler called every time PrintSystem updates PDC
private void OnPdcChanged(PrintSupportExtensionSession session, PrintSupportPrintDeviceCapabilitiesChangedEventArgs args)
{
    using (args.GetDeferral())
    {
        // Set update policy to update the PDC on bind printer of every print job.
        var updatePolicy = PrintSupportPrintDeviceCapabilitiesUpdatePolicy.CreatePrintJobRefresh(1);
        args.SetPrintDeviceCapabilitiesUpdatePolicy(updatePolicy);      
    }
}

Diretrizes gerais de design de aplicativo de suporte à impressão (PSA)

Ao criar um aplicativo de suporte à impressão, é importante incluir esses aspectos no design:

  • Os contratos em primeiro e em segundo plano devem ser marcados como suporte a várias instâncias; por exemplo, SupportsMultipleInstance deve estar presente no manifesto do pacote. Isso é para garantir que o tempo de vida dos contratos possa ser gerenciado de forma confiável para vários trabalhos simultâneos.

  • Trate a interface do usuário de inicialização para modificação de PDL como uma etapa opcional. Faça o melhor esforço para concluir o trabalho de impressão com êxito, mesmo que o lançamento da interface do usuário não tenha sido permitido. Os trabalhos de impressão só deverão ser anulados se não houver como concluí-los com êxito sem a entrada do usuário durante a modificação do PDL. Considere enviar o PDL não modificado nesses casos.

  • Ao iniciar a interface do usuário para modificação do PDL, chame IsUILaunchEnabled antes de chamar LaunchAndCompleteUIAsync. Isso é para garantir que os cenários que não podem mostrar a interface do usuário no momento continuem sendo impressos corretamente. Esses cenários podem estar em um dispositivo sem periféricos ou em um dispositivo que esteja atualmente no modo de quiosque ou no modo não perturbe.

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

especificação do IPP (Protocolo de Impressão da Internet)

Associação de aplicativos de suporte à impressão

Windows.Devices.Printers

Windows.Graphics.Printing.PrintSupport

Windows.Graphics.Printing.Workflow