Guia de design da aplicação de suporte de impressão v1 e v2
Este artigo fornece orientações e exemplos para OEMs e IHVs de impressoras desenvolverem um aplicativo de suporte de impressão (PSA) 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 de impressão (PSA) são o método recomendado para desenvolver aplicativos UWP para impressoras. Para desenvolver uma Aplicação de Suporte de Impressão para o seu dispositivo de impressão, transfira e instale o SDK do Windows 11 para a versão do Windows a que se destina.
Importante
Este artigo contém seções que descrevem a funcionalidade PSA que está disponível a partir do Windows 11, versão 22H2. Estas secções contêm uma nota que indica que se aplica a essa versão.
Para obter mais informações, consulte os seguintes artigos:
Tópico | Descrição |
---|---|
Guia de design da API Print Support App v3 | Fornece orientações e exemplos para OEMs e IHVs de impressoras que estão implementando um aplicativo de suporte de impressão (PSA) v3 para seus dispositivos. |
Guia de conceção da API Print Support App v4 | Fornece orientação e exemplos para OEMs de impressoras e IHVs que estão implementando um aplicativo de suporte de impressão v4 (PSA) para seus dispositivos. |
Especificação de Manifesto MSIX para Impressora Virtual de Suporte de Impressão | Fornece orientação e exemplos de manifesto MSIX para OEMs e IHVs de impressoras que estão a implementar uma impressora virtual de suporte para impressão. |
Associação de aplicativo de suporte de impressão | Fornece orientações e exemplos para associar uma aplicação de suporte de impressão (PSA) a uma impressora. |
Alguns recursos da impressora não são apresentados nas 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 forma a tornar mais fácil para o usuário escolher uma opção e confiar que todos os recursos envolvidos nesse cenário são automaticamente definidos para os valores corretos. Um exemplo disso poderia ser uma escolha entre os modos de economia de tinta, economia de papel e alta qualidade, o que poderia 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 é abordada por esta API com um contrato de extensão UWP opcional que pode ser ativado pelo usuário a partir de todas as caixas de diálogo de impressão do Windows e caixas de diálogo de impressão personalizadas 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 de impressão. Os fabricantes podem melhorar a qualidade de impressão após a renderização, otimizando o conteúdo para a 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 os recursos específicos da impressora.
Terminologia
Vigência | Definição |
---|---|
PSA | Aplicação de Suporte de 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. |
DPC | Caixa de diálogo de impressão comum. Isso é mostrado ao usuário quando o aplicativo está imprimindo usando a API do Win32. As aplicações que precisam mostrar a pré-visualização de impressão não acionam essa caixa de diálogo e implementam uma versão da caixa de diálogo eles próprios. Os aplicativos do Office são um excelente exemplo disso. |
IPP | Protocolo de impressão pela Internet. Usado a partir de um dispositivo cliente para interagir com a impressora para recuperar e definir preferências de impressão e para enviar o documento a ser impresso. |
Impressora vinculada ao suporte de impressão | Impressora ligada à PSA. |
Impressora IPP | Impressora que suporta o protocolo IPP. |
Mais configurações | Link que abre a interface de utilizador do aplicativo fornecido pelo parceiro no MPD. O padrão é abrir a IU de preferências de impressão embutidas quando não há PSA instalado. |
Interface de Preferências da Impressora | Caixa de diálogo usada para definir as opções padrão da impressora 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 em que um documento é enviado para a impressora. |
Impressora PSA associada | Impressora IPP física associada a uma aplicação PSA. |
CapacidadesDoDispositivoDeImpressão | Formato de documento XML para definir os recursos da impressora. Para obter mais informações, consulte "Print Ticket and Print Capabilities Technologies". |
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 PSA responsável por fornecer capacidades de extensão de restrições da impressora. |
Namespace de suporte de impressão
Esses exemplos fazem referência a um namespace printsupport, que é definido como:
xmlns:printsupport="http://schemas.microsoft.com/appx/manifest/printsupport/windows10"
Interface de configurações de suporte de impressão
Quando um usuário está prestes a imprimir um documento, ele geralmente gostaria de definir algumas preferências para imprimi-lo. Por exemplo, eles podem optar por imprimir um documento em modo paisagem. Eles também podem tirar proveito de um recurso personalizado que sua impressora suporta. 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 apropriadas. 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 o recurso completamente. Esta é a motivação por trás da oferta de uma API que permite aos fabricantes de impressoras criar aplicativos adaptados aos vários modelos de impressora que eles fazem.
Um novo contrato de extensão UAP é criado com uma nova categoria chamada windows.printSupportSettingsUI. Os aplicativos ativados com este contrato recebem um novo ActivationKind chamado PrintSupportSettingsUI. Este contrato não requer nenhuma nova capacidade.
<Extensions>
<printsupport:Extension Category="windows.printSupportSettingsUI"
EntryPoint="PsaSample.PsaSettingsUISample"/>
</Extensions>
Este contrato é invocado quando o utilizador seleciona Mais Configurações no MPD ou Preferências no CPD. Este contrato também pode ser invocado a partir das Preferências de Impressão na aplicação de Definições. Quando o contrato é ativado, a aplicação recebe um objeto PrintSupportSettingsUISession que pode ser usado para obter o PrintTicket atual e os objetos PrintDevice. O objeto PrintDevice pode ser usado para se comunicar com a impressora para receber atributos de impressora e trabalho. A aplicação pode então mostrar a interface com as opções apropriadas da impressora. Quando o utilizador faz as escolhas e seleciona OK, a aplicação pode então modificar o bilhete de impressão, validá-lo e enviar de volta em seguida usando o objeto PrintSupportPrintTicketTarget. Se o utilizador optar por cancelar a janela de preferências, as alterações devem ser descartadas e a aplicação deve sair, concluindo o adiamento retirado do objeto PrintSupportSettingsUISession.
Espera-se que o aplicativo de suporte de impressão manipule várias ativações simultâneas para diferentes trabalhos de impressão, portanto, esse aplicativo deve oferecer suporte a várias instâncias usando o elemento
O diagrama de sequência seguinte representa o conceito de manipulação do bilhete de impressão na interface de utilizador das definições.
Alterando o PrintTicket na interface de configurações do utilizador
Código de exemplo C# para a ativação da interface de utilizador das Definições ao ser lançada a partir de qualquer caixa de diálogo de impressão (MPD/CPD ou caixa de diálogo de impressão personalizada) ou das definiçõ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 C# para mostrar a interface do usuário e alterar o 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 da impressora a partir do dispositivo da impressora
Resposta do WireShark de uma impressora IPP a uma consulta para obter os atributos da impressora:
Código de exemplo em 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 da 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 as restrições da impressora
O aplicativo de suporte de impressão suporta a validação personalizada do PrintTicket e a definição do PrintTicket padrão. Esta seção descreve como oferecemos suporte a esses recursos.
Para suportar 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 de impressão, conforme mostrado aqui:
<Extensions>
<printsupport:Extension Category="windows.printSupportExtension"
EntryPoint="PsaBackgroundTasks.PrintSupportExtension"/>
</Extensions>
Este serviço pode ser executado em qualquer ponto de um trabalho de impressão para a impressora IPP associada. Como a extensão de suporte de impressão é ativada através da função IBackgroundTaskInstance, uma instância de IBackgroundTaskInstance é fornecida à PrintSupportExtension para permitir o acesso à classe de execução PrintSupportExtensionTriggerDetails, que, por sua vez, fornece PrintSupportExtensionSession como uma propriedade. A classe de plano de fundo PrintSupportExtension pode então usar o objeto de sessão para se inscrever em eventos nos quais deseja implementar funcionalidades personalizadas.
event Windows.Foundation.TypedEventHandler<PrintSupportExtensionSession, PrintSupportPrintTicketValidationRequestedEventArgs>; PrintTicketValidationRequested;
Se a Extensão de Suporte de Impressão fornecer o seu próprio mecanismo de validação de PrintTicket, ela poderá inscrever-se para este evento. Sempre que um PrintTicket precisa ser validado, o sistema de impressão gera esse evento.
PrintSupportExtension obterá o atual PrintTicket que precisa ser validado dentro do EventArgs. A classe de fundo PrintSupportExtension pode então verificar o PrintTicket para verificação de validade e modificá-lo para resolver quaisquer conflitos. A classe de plano de fundo PrintSupportExtension deve definir o resultado da validação usando a função SetPrintTicketResult para indicar se PrintTicket foi resolvido, tem conflitos ou é inválido. Este evento pode ser ativado a qualquer momento durante a duração de um trabalho de impressão. Se a classe PrintSupportExtension não se registrar nesse evento, o sistema de impressão executará sua própria validação do PrintTicket. event Windows.Foundation.TypedEventHandler<PrintSupportExtensionSession, PrintSupportPrintDeviceCapabilitiesChangedEventArgs>; PrintDeviceCapabilitiesChanged;
O evento é gerado depois que o sistema de impressão atualiza o cache PrintDeviceCapabilities de a impressora IPP associada. Quando este evento é gerado, a classe de fundo PrintSupportExtension pode inspecionar as capacidades do dispositivo de impressão alteradas e modificá-las.
Validação personalizada de bilhete impresso
Código de exemplo C# para fornecer serviço de validação de 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);
}
}
Atualizar Capacidades do Dispositivo de Impressã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);
}
}
Melhoria da qualidade de impressão
Uma vez que o usuário se comprometeu a imprimir pressionando o botão de impressão na caixa de diálogo de impressão, o documento a ser impresso é enviado para a pilha de impressão a partir do aplicativo que está imprimindo. Este documento passa então 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 da impressora. O documento transformado é então enviado para a impressora. Embora isso funcione bem para a maioria das impressoras, há casos em que a qualidade de 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 é ampliada para incluir invocações à aplicação em pontos adicionais do stack de impressão. Esta API suporta 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 PSA:
TrabalhoAIniciar
- Esse evento é gerado quando um trabalho de impressão é iniciado por qualquer aplicativo. Quando o evento é gerado, um aplicativo de suporte de impressão pode escolher ignorar a renderização do sistema ao chamar SetSkipSystemRendering em PrintWorkflowJobStartingEventArgs. Se for escolhida a opção de ignorar a renderização do sistema, o sistema de impressão não converterá o documento XPS para o formato PDL exigido pela impressora. Em vez disso, o XPS gerado pelo aplicativo de impressão será entregue diretamente ao PSA, que é responsável pela conversão do XPS para o formato PDL.
ModificaçãoPdlSolicitada
- Esse evento é gerado quando o Windows inicia a conversão do fluxo XPS para o formato PDL indicado pela impressora. A classe de tempo de execução PrintWorkflowPdlModificationRequestedEventArgs é fornecida como um argumento para esse evento. Esta classe de evento fornece objetos de origem e destino de PDL para a leitura e escrita dos conteúdos dos trabalhos de impressão. Se a aplicação determinar que precisa de input do utilizador, poderá iniciar a interface usando PrintWorkflowUILauncher do EventArgs. Esta API usa o padrão Tester-Doer. PrintWorkflowUILauncher não poderá invocar a interface de utilizador se a função IsUILaunchEnabled retornar false. Esta função retorna false se a sessão PSA estiver sendo executada no modo silencioso (modo headless ou kiosk). O aplicativo de suporte de 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:
O aplicativo de 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 execução em primeiro plano para obter a entrada do usuário e/ou para mostrar uma visualização de impressão para o usuário.
Tarefa em segundo plano no fluxo de trabalho de suporte à impressão
Um novo tipo de tarefa de segundo plano printSupportWorkflow foi definido. O Package.appxmanifest tem a seguinte entrada de extensão para o contrato PrintSupportWorkflow:
<Extensions>
<printsupport:Extension Category="windows.printSupportWorkflow"
EntryPoint="PsaBackgroundTasks.PrintSupportWorkflowSample"/>
</Extensions>
Na ativação do contrato, PrintWorkflowJobTriggerDetails é dado como IBackgroundTaskInstance->TriggerDetails. PrintWorkflowJobTriggerDetails fornece internamente PrintWorkflowJobBackgroundSession como parte de suas propriedades. O aplicativo pode usar PrintWorkflowJobBackgroundSession para se registrar em eventos relacionados a vários pontos de injeção no fluxo de trabalho de impressão. Depois que o registro do evento for feito, o aplicativo deve chamar PrintWorkflowJobBackgroundSession::Start para que o sistema de impressão comece a disparar eventos relacionados a vários pontos de injeção.
Interface do utilizador para trabalhos de impressão em fluxo de trabalho
Um novo ActivationKind chamado PrintSupportJobUI é definido. Isso não requer um novo recurso.
<Extensions>
<printsupport:Extension Category="windows.printSupportJobUI"
EntryPoint="PsaSample.PrintSupportJobUISample"/>
</Extensions>
Este é um contrato de interface do utilizador que pode ser iniciado a partir do contrato de plano de fundo do Fluxo de Trabalho de Suporte de Impressão ou quando o utilizador seleciona um aviso de erro de trabalho de impressão. Na ativação,
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 do PDL
Diagrama de sequência para o evento de modificação PDL:
Código de exemplo em C# para o Monitor de Suporte de Trabalho de Impressão na leitura e gravação do 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 em C# para iniciar a interface de utilizador do trabalho de suporte de impressão a partir do contrato de evento relativo à modificação solicitada 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 utilizador da tarefa do fluxo de trabalho para o evento PDLDataAvailable
Diagrama de sequência para ativação da interface do utilizador do trabalho de impressão para o evento PdlDataAvailable:
Código de exemplo C# para o contrato de ativação da interface do usuário do trabalho 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 da impressora
Código de exemplo em 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 do trabalho da impressora
Código de exemplo em C#, continuando a partir da seção Obter atributos da tarefa de impressão acima, demonstrando como definir os atributos de uma tarefa.
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 suportam a obtenção/definiçã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.
Fornecendo acesso aos ficheiros de armazenamento do conteúdo PDL
Alguns formatos PDL, como o PDF, precisam de um fluxo completo para estarem disponíveis para iniciar o processamento. Por esse motivo, um novo método chamado
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 para PDF
Código de exemplo C# mostrando a conversão PDL de XPS para 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:
Código de exemplo C#, continuando da ativação da UI do trabalho no fluxo de trabalho para a seção de evento PDLDataAvailable acima, para mostrar erro na notificação de erro 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 tarefa com atributos de tarefa iniciais
Atualmente, algumas impressoras IPP não suportam a operação de definição de atributos. A função
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 XPS sequencial
Código de exemplo C++/Winrt para processar XPS sequencialmente antes de o spool ser 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 do nome de exibição e integração da API de PDL Passthrough
Importante
Esta seção descreve a funcionalidade PSA disponível a partir do Windows 11, versão 22H2.
Nesse cenário, o PSA personaliza as Capacidades do Dispositivo de Impressão (PDC) e fornece Recursos do Dispositivo de Impressão (PDR) para a localização de strings.
O PSA também define os tipos de conteúdo suportados da API de Passagem PDL (formatos PDL). Se o PSA não se inscrever no evento ou não chamar SetSupportedPdlPassthroughContentTypes explicitamente, a Passagem PDL será desabilitada para as impressoras associadas a esse 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 funcionalidades no nível da página e atributos de operação
Importante
Esta seção descreve a funcionalidade PSA disponível a partir do Windows 11, 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 são abordados 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 a partir do PrintTicket.
Recolha separada para suporte a atributos de operação (impressão PIN): Nesse cenário, o aplicativo PSA especifica atributos de operação IPP personalizados (por exemplo, PIN).
O código de exemplo C# a seguir mostra as alterações necessárias para suporte ao recurso de nível de página nos cenários e para a coleta separada de atributos de operação nos cenários .
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();
}
}
Melhorar a caixa de diálogo de impressão com PSA
Importante
Esta seção descreve a funcionalidade PSA disponível a partir do Windows 11, versão 22H2.
Nesse cenário, usar a caixa de diálogo de impressão com integração PSA permite as seguintes ações:
Receber uma notificação de chamada quando a seleção for alterada no MPD para a impressora associada ao PSA.
Mostrar um AdaptiveCard com o suporte de para a 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 recursos mostradas na caixa de diálogo de impressão
Obtenha 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 essas melhorias do 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 host/impressão 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 impressoras podem querer a conversão PDL sem processamento baseado em host, pois a impressora deles pode fazer isso melhor. A função ConvertPdlAsync usa sinalizadores de processamento baseados em host para satisfazer 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 política de atualização de recursos do 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 os recursos do dispositivo de impressão (PDC) precisam ser atualizados. Para atender a esses requisitos, PrintSupportPrintDeviceCapabilitiesUpdatePolicy pode definir uma política de atualização para o PDC. A 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);
}
}
Orientações gerais de design da aplicação de suporte à impressão (PSA)
Ao projetar um aplicativo de suporte de impressão, é importante incluir estes aspetos no design:
Os contratos de primeiro plano e em segundo plano devem ser marcados como suportando várias instâncias, por exemplo, SupportsMultipleInstance devem estar presentes no manifesto do pacote. Isso é para garantir que a vida útil dos contratos possa ser gerenciada de forma confiável para vários trabalhos simultâneos.
Trate a inicialização da interface do usuário para modificação PDL como uma etapa opcional. Faça o melhor esforço para concluir o trabalho de impressão com êxito, mesmo que a inicialização da interface do usuário não tenha sido permitida. Os trabalhos de impressão só devem 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 sem modificações nesses casos.
Ao iniciar a UI para modificação 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 atual continuem a ser impressos corretamente. Esses cenários podem estar em um dispositivo sem cabeça ou em um dispositivo que esteja atualmente no modo de quiosque ou no modo não perturbe.
Artigos relacionados
Fim do plano de manutenção para drivers de impressora de terceiros no Windows
especificação IPP (Internet Printing Protocol)
Associação de aplicativo de suporte de impressão