Руководство по проектированию приложения поддержки печати версии 1 и версии 2
В этой статье приведены рекомендации и примеры для изготовителей оборудования принтеров и IHV для разработки приложения поддержки печати (PSA), которое может улучшить взаимодействие с печатью пользователя Windows несколькими способами.
Важный
Начиная с выпуска пакета SDK для Windows 11 (22000.1), приложения поддержки печати (PSA) — это рекомендуемый метод разработки приложений UWP для принтеров. Чтобы разработать приложение поддержки печати для устройства печати, скачайте и установите пакет SDK для Windows 11 для целевой версии Windows.
Важный
В этой статье содержатся разделы, описывающие функции PSA, доступные начиная с Windows 11 версии 22H2. Эти разделы содержат заметку, указывающую, что она применяется к этой версии.
Дополнительные сведения см. в следующих статьях:
Тема | Описание |
---|---|
Руководство по проектированию API Поддержки Печати версии 3 | Предоставляет рекомендации и примеры для изготовителей оборудования принтера и IHV, которые реализуют приложение поддержки печати версии 3 (PSA) для своего устройства. |
Руководство по проектированию api поддержки службы поддержки печати версии 4 | Предоставляет рекомендации и примеры для изготовителей оборудования принтера и IHV, которые реализуют приложение поддержки печати версии 4 (PSA) для своего устройства. |
Спецификация манифеста MSIX для поддержки виртуального принтера для печати | Предоставляет рекомендации по манифесту MSIX и примеры для оригинальных изготовителей оборудования принтера и независимых производителей оборудования (IHV), реализующих виртуальный принтер для поддержки функций печати. |
Ассоциация приложений поддержки печати | Предоставляет рекомендации и примеры для связывания приложения поддержки печати (PSA) с принтером. |
Некоторые функции принтера не отображаются в диалоговых окнах печати Windows, так как они являются специальными функциями, которые нуждаются в правильной настройке приложения производителя. Они также могут быть функциями, которые не предоставляются в возможностях принтера по умолчанию.
Специальные функции принтера можно сгруппировать таким образом, чтобы пользователю было проще выбрать параметр и доверять, что все функции, участвующие в этом сценарии, автоматически задаются правильными значениями. Примером этого может быть выбор между рукописным сохранением, сохранением бумаги и режимами высокого качества, которые могут автоматически управлять различными функциями печати на основе одного выбора от пользователя. Windows не может автоматически группировать их так, как это требует понимания всех пользовательских функций каждой модели принтера.
Эта необходимость в отображении пользовательских настроек печати устранена этим API с необязательным контрактом расширения UWP, который можно активировать пользователем из всех диалогов печати Windows и настраиваемых диалогов печати, использующих API, предоставляемый Windows. Производители могут адаптировать свой пользовательский интерфейс, чтобы обеспечить лучший интерфейс печати для конкретного принтера, принадлежащий пользователю.
Другая область, в которой производители принтеров могут улучшить и различать качество печати. Производители могут улучшить качество печати после отрисовки, оптимизируя содержимое для конкретного принтера. Они также могут представить высокую точность предварительного просмотра, который лучше представляет окончательные выходные данные, так как он может принимать во внимание конкретные функции принтера.
Терминология
Срок | Определение |
---|---|
PSA | Приложение поддержки печати. Приложение UWP, использующее API, описанное в этой статье. |
MPD | Диалоговое окно "Современная печать" Это отображается пользователю при печати приложения с помощью API Windows.Graphics.Printing. |
Непрерывное Профессиональное Развитие | Общее диалоговое окно печати. Это отображается пользователю при печати приложения с помощью API Win32. Приложения, которые должны отображать предварительный просмотр печати, не активируют этот диалог и реализуют версию диалога самостоятельно. Приложения Office являются основным примером этого. |
IPP | Протокол интернет-печати. Используется с клиентского устройства для взаимодействия с принтером для получения и задания параметров печати и отправки документа для печати. |
Принтер, связанный с поддержкой печати | Принтер, связанный с PSA. |
Принтер IPP | Принтер, поддерживающий протокол IPP. |
Дополнительные параметры | Ссылка, которая открывает предоставленный партнером пользовательский интерфейс приложения в MPD. По умолчанию открывается встроенный пользовательский интерфейс параметров печати при отсутствии установленной PSA. |
Пользовательский интерфейс параметров принтера | Диалоговое окно, используемое для задания параметров принтера по умолчанию, применяемых во время печати. Например, ориентация, размер бумаги, цвет, печать на обеих сторонах и т. д. |
PDL | Язык описания страницы. Формат, в котором документ отправляется принтеру. |
Связанный принтер PSA | Физический принтер IPP, связанный с приложением PSA. |
ВозможностиУстройстваПечати | Формат XML-документа для определения возможностей принтера. Дополнительные сведения см. в Print Ticket и технологии печати возможностей. |
ПечатьБилета | Коллекция различных функций печати и их значений, используемых для отслеживания намерения пользователя для заданного задания печати. |
Расширение поддержки печати | Фоновая задача PSA, отвечающая за предоставление возможностей расширения ограничений принтера. |
Пространство имен поддержки печати
Эти примеры ссылаются на пространство имен printsupport, которое определяется следующим образом:
xmlns:printsupport="http://schemas.microsoft.com/appx/manifest/printsupport/windows10"
Пользовательский интерфейс параметров поддержки печати
Когда пользователь собирается распечатать документ, он часто хочет задать некоторые параметры печати. Например, они могут выбрать печать документа в альбомной ориентации. Они также могут воспользоваться пользовательскими функциями, поддерживаемыми принтером. Windows предоставляет пользовательский интерфейс по умолчанию для отображения пользовательских настроек, но пользователь может не понимать их, так как нет соответствующих значков или описаний. Windows также может использовать неправильный элемент управления пользовательского интерфейса для презентации. Такая настраиваемая функция лучше всего представлена приложением, которое полностью понимает эту функцию. Это мотивация предложения API, который позволяет производителям принтеров создавать приложения, адаптированные к различным моделям принтера, которые они делают.
Новый контракт расширения UAP создается с новой категорией windows.printSupportSettingsUI. Приложения, активированные с помощью этого контракта, получают новый элемент ActivationKind с именем PrintSupportSettingsUI. Для этого контракта не требуется никаких новых возможностей.
<Extensions>
<printsupport:Extension Category="windows.printSupportSettingsUI"
EntryPoint="PsaSample.PsaSettingsUISample"/>
</Extensions>
Этот контракт вызывается, когда пользователь выбирает Дополнительные настройки в MPD или Предпочтения в CPD. Этот контракт также можно вызвать из Настроек печати в приложении "Настройки". При активации контракта приложение получает объект PrintSupportSettingsUISession, который можно использовать для получения текущих объектов PrintTicket и PrintDevice. Объект PrintDevice можно использовать для взаимодействия с принтером для получения атрибутов принтера и заданий. Затем приложение может отображать пользовательский интерфейс с соответствующими параметрами принтера пользователю. Когда пользователь выберет параметры и нажмёт ОК, приложение может изменить параметры печати, проверить их корректность, а затем отправить обратно с помощью объекта PrintSupportPrintTicketTarget. Если пользователь решит отменить окно настроек, изменения следует отменить, и приложение должно выйти, завершив отсрочку из объекта PrintSupportSettingsUISession.
Ожидается, что приложение для поддержки печати обрабатывает несколько одновременных активаций для разных заданий печати, поэтому такое приложение должно поддерживать несколько экземпляров с помощью элемента SupportMultipleInstances в файле package.appxmanifest. Сбой этого может привести к ситуациям, когда подтверждение настроек одного задания печати может закрыть другие окна настроек, которые могут быть открыты. Пользователю необходимо снова открыть эти окна параметров.
На следующей схеме последовательности представлена концепция манипуляции с печатающим билетом в пользовательском интерфейсе параметров.
диаграмма последовательности
Изменение PrintTicket в пользовательском интерфейсе параметров
Пример кода C# для активации пользовательского интерфейса параметров при запуске из любого диалогового окна печати (MPD/CPD или пользовательского диалогового окна печати) или из системных параметров:
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 для класса 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# для отображения пользовательского интерфейса и изменения 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();
}
}
}
Получение атрибутов принтера с устройства принтера
Ответ WireShark от принтера IPP к запросу get-printer-attributes:
Пример кода C# для получения названий чернил и уровней чернил на принтере:
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));
}
}
}
}
Настройка атрибутов принтера на принтере
Пример кода C# для задания атрибутов принтера:
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;
}
}
}
}
Расширение ограничений принтера
Приложение поддержки печати поддерживает настраиваемую проверку PrintTicket и определение стандартного PrintTicket. В этом разделе описывается, как мы поддерживаем эти функции.
Для поддержки ограничений расширения принтера реализован новый тип фоновой задачи PrintSupportExtension. Package.appxmanifest имеет запись расширяемости для расширения поддержки печати, как показано ниже:
<Extensions>
<printsupport:Extension Category="windows.printSupportExtension"
EntryPoint="PsaBackgroundTasks.PrintSupportExtension"/>
</Extensions>
Эта служба может выполняться на любом этапе задания печати для связанного принтера IPP. Так как расширение поддержки печати активируется с помощью функции IBackgroundTaskInstance, экземпляр IBackgroundTaskInstance предоставляется PrintSupportExtension для предоставления доступа к классу среды выполнения PrintSupportExtensionTriggerDetails, который внутренне предоставляет PrintSupportExtensionSession В качестве свойства. Фоновый класс PrintSupportExtension может использовать объект сеанса для регистрации на события, чтобы предоставить пользовательские функциональные возможности.
event Windows.Foundation.TypedEventHandler<PrintSupportExtensionSession, PrintSupportPrintTicketValidationRequestedEventArgs>; PrintTicketValidationRequested;
Если расширение поддержки печати предоставляет собственный механизм проверки PrintTicket, оно может зарегистрироваться на это событие. Каждый раз, когда требуется проверить PrintTicket, это событие инициируется системой печати. PrintSupportExtension затем получит текущий PrintTicket, который необходимо проверить в EventArgs. Класс PrintSupportExtension в фоновом режиме может проверить PrintTicket на предмет допустимости и изменить его, чтобы устранить любые конфликты. Затем фоновый класс PrintSupportExtension должен установить результат проверки с помощью функции SetPrintTicketResult, чтобы указать, разрешен ли PrintTicket, имеет ли он конфликты или является недействительным. Это событие можно вызвать в любое время во время выполнения задания на печать. Если класс PrintSupportExtension не регистрируется на это событие, система печати выполняет собственную проверку PrintTicket.
event Windows.Foundation.TypedEventHandler<PrintSupportExtensionSession, PrintSupportPrintDeviceCapabilitiesChangedEventArgs>; PrintDeviceCapabilitiesChanged;
Событие возникает после того, как система печати обновляет кэшированные возможности устройства для связанного принтера IPP. При возникновении этого события фоновый класс PrintSupportExtension может проверить измененные PrintDeviceCapabilities и изменить их.
Индивидуальная проверка печатного билета
Пример кода C# для предоставления службы проверки 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);
}
}
Обновление PrintDeviceCapabilities
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);
}
}
Улучшение качества печати
После того как пользователь обязался печатать, нажав кнопку печати в диалоговом окне печати, документ, который будет напечатан, отправляется в стек печати из приложения, которое печатается. Затем этот документ проходит преобразование (отрисовка в PDL), чтобы сделать его подходящим для целевого принтера. Windows определит, какое преобразование следует выбрать на основе атрибутов, запрашиваемых с принтера. Затем преобразованный документ отправляется на принтер. Хотя это работает хорошо для большинства принтеров, существуют случаи, когда качество печати может быть улучшено, позволяя партнерскому приложению участвовать в преобразовании. Чтобы упростить это, текущий API рабочего процесса печати расширен для включения вызовов приложения в дополнительные точки из стека печати. API поддерживает два новых события, для регистрации на которые может использоваться приложение PSA. Это единственные точки входа в область API PSA:
ЗапускРаботы
- Это событие возникает при запуске задания печати любым приложением. При возникновении события приложение поддержки печати может пропустить системный рендеринг, вызвав SetSkipSystemRendering на PrintWorkflowJobStartingEventArgs. Если выбран пропуск системной обработки, система печати не преобразует документ XPS в формат PDL, требуемый принтером. Вместо этого XPS, созданные приложением печати, будут непосредственно переданы PSA, которая затем отвечает за преобразование XPS в формат PDL.
PdlModificationRequested
- Это событие возникает, когда Windows запускает преобразование потока XPS в формат PDL, указанный принтером. Класс среды выполнения PrintWorkflowPdlModificationRequestedEventArgs предоставляется в качестве аргумента для этого события. Этот класс событий предоставляет исходные и целевые объекты PDL для чтения и записи содержимого задания печати. Если приложение определяет, что ему нужны пользовательские данные, он может запустить пользовательский интерфейс с помощью PrintWorkflowUILauncher из EventArgs. Этот API использует шаблон Tester-Doer. PrintWorkflowUILauncher не сможет вызывать пользовательский интерфейс, если функция IsUILaunchEnabled возвращает значение false. Эта функция возвращает значение false, если сеанс PSA работает в тихом режиме (безголовый режим или режим киоска). Приложение поддержки печати не должно пытаться запустить пользовательский интерфейс, если функция возвращает значение false.
OutputStream
доступен как часть PrintWorkflowPdlTargetStream , возвращаемой функциейGetStreamTargetAsync . Содержимое, записанное в целевой выходной поток, передается на принтер в виде содержимого документа.
Схема последовательности для события изменения PDL:
схема последовательности
PSA-приложение переднего плана запускается, когда фоновая служба PSA запрашивает запуск пользовательского интерфейса. PSA может использовать контракт на переднем плане, чтобы получить входные данные от пользователя и/или показать пользователю предварительный просмотр печати.
Фоновая задача печати в рабочем процессе поддержки
Был определен новый printSupportWorkflow тип фоновой задачи. Package.appxmanifest имеет следующую запись расширяемости для контракта PrintSupportWorkflow:
<Extensions>
<printsupport:Extension Category="windows.printSupportWorkflow"
EntryPoint="PsaBackgroundTasks.PrintSupportWorkflowSample"/>
</Extensions>
При активации контракта PrintWorkflowJobTriggerDetails предоставляется как IBackgroundTaskInstance->TriggerDetails. PrintWorkflowJobTriggerDetails внутренне включает PrintWorkflowJobBackgroundSession в качестве одной из своих характеристик. Приложение может использовать PrintWorkflowJobBackgroundSession для регистрации событий, связанных с различными точками внедрения в рабочем процессе задания печати. После завершения регистрации события приложение должно вызвать PrintWorkflowJobBackgroundSession::Start, чтобы система печати начала запускать события, связанные с различными точками внедрения.
Пользовательский интерфейс задания рабочего процесса печати
Определен новый ActivationKind с именем PrintSupportJobUI. Для этого не требуется новая возможность.
<Extensions>
<printsupport:Extension Category="windows.printSupportJobUI"
EntryPoint="PsaSample.PrintSupportJobUISample"/>
</Extensions>
Это контракт пользовательского интерфейса, который можно запустить из фонового контракта рабочего процесса поддержки печати или когда пользователь выбирает всплывающее сообщение об ошибке задания печати. При активации предоставляется PrintWorkflowJobActivatedEventArgs с объектом PrintWorkflowJobUISession. Используя
Пропуск системного рендеринга
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();
}
}
}
}
Событие изменения PDL
Схема последовательности для события изменения PDL:
схема последовательности
Пример кода C# для монитора заданий поддержки печати для чтения и записи содержимого задания печати:
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();
}
}
}
Запуск пользовательского интерфейса из фона рабочего процесса
Пример кода C# для запуска интерфейса поддержки печатных заданий из события запроса на изменение 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();
}
}
Активация пользовательского интерфейса задания рабочего процесса для события PDLDataAvailable
Схема последовательности для активации пользовательского интерфейса задания печати для события PdlDataAvailable:
схема последовательности
Пример кода C# для контракта активации UI задачи 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);
}
}
}
}
Получение атрибутов задания принтера
Пример кода C# для получения атрибутов задания печати:
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();
}
}
}
Установка атрибутов задания принтера
Пример кода C#, продолжающий предыдущий раздел Получение атрибутов задания принтера, демонстрирующий установку атрибутов задания:
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);
}
}
Некоторые принтеры IPP не поддерживают получение и настройку атрибутов задания после его создания. Для этих принтеров PrintJob имеет свойство JobId значение "0" и GetJobAttributes/SetJobAttributes завершится сбоем немедленно.
Предоставление доступа к содержимому PDL в файле хранилища
Для начала обработки полный поток должен быть доступен для некоторых форматов PDL, таких как PDF. По этой причине новый метод с именем GetContentFileAsync предоставляется в классе PrintWorkflowPdlSourceContent, который возвращает StorageFile исходного содержимого.
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);
}
}
}
}
}
Преобразование PDL XPS в PDF
Пример кода C# с преобразованием PDL XPS в 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();
}
Событие уведомления о работе
Схема последовательности для события уведомления о задании:
схема последовательности
Пример кода C#, продолжающий работу от активации интерфейса пользователя для задачи в разделе события PDLDataAvailable выше, с целью отобразить ошибку в уведомлении о задаче:
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;
}
}
}
}
Создание задания с начальными атрибутами задания
В настоящее время некоторые принтеры IPP не поддерживают операцию set-attribute. Функции CreateJobOnPrinterWithAttributes и CreateJobOnPrinterWithAttributesBuffer в PrintWorkflowPdlDataAvailableEventArgs предоставляются для устранения этой проблемы. С помощью этих API разработчик PSA может предоставлять атрибуты задания, передаваемые принтеру при создании задания на принтере.
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
}
}
Последовательная обработка XPS
Пример кода C++/Winrt для обработки XPS последовательно перед завершением spooling.
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()));
}
}
}
Локализация отображаемого имени и интеграция сквозного API PDL
Важный
В этом разделе описаны функции PSA, доступные начиная с Windows 11 версии 22H2.
В этом сценарии PSA настраивает возможности устройства печати (PDC) и предоставляет ресурсы устройства печати (PDR) для локализации строк.
PSA также задает поддерживаемые типы содержимого API PDL (форматы PDL). Если PSA не подписывается на событие или явно не вызывает SetSupportedPdlPassthroughContentTypes, то поддержка PDL Passthrough отключается для принтеров, связанных с этим приложением 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);
}
}
Поддержка функций на уровне страницы и атрибуты операций
Важный
В этом разделе описаны функции PSA, доступные начиная с Windows 11 версии 22H2.
Сценарии поддержки функций уровня страницы и атрибуты операций группируются, так как они рассматриваются путем внесения изменений в то же место в примере кода.
поддержка функций уровня страницы : В этом сценарии приложение PSA указывает атрибут уровня страницы, который не должен быть переопределен атрибутом IPP, проанализированным из PrintTicket.
отдельная коллекция для поддержки атрибутов операций (печать ПИН-кода): В этом сценарии приложение PSA задает настраиваемые атрибуты операции IPP (например, ПИН-код).
В следующем примере кода C# показаны необходимые изменения для поддержки функций уровня страниц в сценариях и и отдельной коллекции для атрибутов операций в сценариях и.
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();
}
}
Улучшение диалогового окна печати с помощью PSA
Важный
В этом разделе описаны функции PSA, доступные начиная с Windows 11 версии 22H2.
В этом сценарии использование диалогового окна печати с интеграцией PSA позволяет выполнять следующие действия:
Получите обратный вызов при изменении выделения в MPD на принтер, связанный с PSA.
Отображение одной адаптивной карты с поддержкой действия openUrl
Отображение настраиваемых функций и параметров в диалоговом окне печати
Измените PrintTicket, чтобы изменить выбор параметров функций, отображаемых в диалоговом окне печати.
Получите Windows.ApplicationModel.AppInfo приложения печати, а затем откройте диалоговое окно печати
В следующем примере C# показаны следующие улучшения диалогового окна печати:
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""
}}");
}
Преобразование PDL с флагами обработки на хосте
Важно
В этом разделе описаны функции PSA, доступные начиная с Windows 11 версии 22H2.
Текущий API преобразования PDL PrintWorkflowPdlConverter.ConvertPdlAsyncвыполняет обработку, основанную на системном хосте, по умолчанию. Это означает, что компьютер, выполняющий функции хоста/печати, осуществляет поворот, порядок страниц и прочее, чтобы принтер не выполнял эти операции. Однако независимые производители оборудования (IHV) могут предпочесть преобразование PDL без обработки на базе компьютера, так как их принтер способен справиться с этим лучше. Функция ConvertPdlAsync принимает флаги обработки на основе хоста для решения этого требования. PSA может пропустить всю обработку на основе узла или определенную операцию обработки на основе узла с помощью этого флага.
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
}
Настройка политики обновления возможностей устройства печати (PDC)
Важный
В этом разделе описаны функции PSA, доступные начиная с Windows 11 версии 22H2.
У различных производителей оборудования принтеров могут быть разные требования на то, когда необходимо обновлять возможности устройства печати (PDC). Для решения этих требований PrintSupportPrintDeviceCapabilitiesUpdatePolicy может задать политику обновления для PDC. PSA может задать политику обновления PDC в зависимости от времени или количества заданий печати с помощью этого API.
Настройка политики обновления PDC на основе количества заданий
// 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);
}
}
Настройте политику обновления PDC в зависимости от параметра 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);
}
}
Руководство по проектированию общего приложения для поддержки печати (PSA)
При разработке приложения поддержки печати важно включить в проект следующие аспекты:
Контракты фронтальные и фоновые должны быть помечены как поддерживающие несколько экземпляров, например, SupportsMultipleInstance должны присутствовать в манифесте пакета. Это позволяет обеспечить надежное управление временем существования контрактов для нескольких одновременных заданий.
Рассматривайте пользовательский интерфейс запуска для изменения PDL как необязательный шаг. Сделайте все возможное, чтобы выполнить задание печати успешно, даже если запуск пользовательского интерфейса не разрешен. Задания печати должны быть прерваны только в том случае, если их невозможно успешно завершить без ввода пользователем во время изменения PDL. Рассмотрите возможность отправки PDL, не измененного в таких случаях.
При запуске пользовательского интерфейса для изменения PDL сначала вызовите IsUILaunchEnabled, а затем вызовите LaunchAndCompleteUIAsync. Это гарантирует, что сценарии, которые не могут отображать пользовательский интерфейс в текущее время, продолжают печатать правильно. Эти сценарии могут быть на устройстве без пользовательского интерфейса или устройстве, которое в настоящее время находится в режиме киоска или режиме «Не беспокоить».
Связанные статьи
Завершение плана обслуживания сторонних драйверов принтеров в Windows
спецификация протокола Интернет-печати (IPP)
ассоциация приложений поддержки печати