Guida alla progettazione dell'app di supporto per la stampa

Questo articolo fornisce indicazioni ed esempi per gli OEM e gli IHD della stampante per sviluppare un'app di supporto per la stampa (PSA) che può migliorare l'esperienza di stampa di un utente Windows in diversi modi.

Importante

A partire dalla versione di Windows 11 SDK (22000.1), Print Support Apps (PSA) sono il metodo consigliato per lo sviluppo di app UWP per stampanti. Per sviluppare un'app di supporto di stampa per il dispositivo di stampa, scaricare e installare Windows 11 SDK per la versione di Windows di destinazione.

Importante

Questo argomento contiene sezioni che descrivono le funzionalità di PSA disponibili a partire da Windows 11 versione 22H2. Tali sezioni contengono una nota che indica che si applica a tale versione.

Alcune funzionalità della stampante non vengono presentate nelle finestre di dialogo di stampa visualizzate da Windows perché sono funzionalità speciali che richiedono assistenza da un'app produttore per essere configurate correttamente. Possono anche essere funzionalità che non sono disponibili nelle funzionalità predefinite della stampante.

Le funzionalità specifiche della stampante possono essere raggruppate in modo da consentire all'utente di scegliere facilmente un'opzione e considerare attendibile che tutte le funzionalità coinvolte in tale scenario vengano impostate automaticamente sui valori corretti. Un esempio di questo potrebbe essere una scelta tra input penna-saver, risparmio carta e modalità di alta qualità ciò che potrebbe modificare automaticamente varie funzionalità di stampa in base a una selezione dall'utente. Windows non è in grado di raggrupparli automaticamente perché richiede la comprensione di tutte le funzionalità personalizzate di ogni modello di stampante.

Questa necessità di visualizzare le preferenze di stampa personalizzate è indirizzata da questa API con un contratto di estensione UWP facoltativo che può essere attivato dall'utente da tutte le finestre di dialogo di stampa di Windows e finestre di dialogo di stampa personalizzate che usano l'API fornita da Windows. I produttori sono in grado di personalizzare l'interfaccia utente per offrire la migliore esperienza di stampa per la stampante specifica di proprietà dell'utente.

Un'altra area in cui i produttori di stampanti possono migliorare e distinguere è la qualità della stampa. I produttori possono migliorare la qualità di stampa dopo il rendering ottimizzando il contenuto per la stampante specifica. Possono anche presentare un'anteprima ad alta fedeltà che rappresenta meglio l'output finale in quanto potrebbe prendere in considerazione funzionalità specifiche della stampante.

print support app print timeline

Terminologia

Termine Definizione
PSA Stampa applicazione di supporto. Un'app UWP che usa l'API descritta in questo articolo.
MPD Finestra di dialogo Stampa moderna. Viene visualizzato all'utente quando un'app sta stampando tramite l'API Windows.Graphics.Printing.
CPD Finestra di dialogo stampa comune. Questo viene visualizzato all'utente quando l'app esegue la stampa usando l'API Win32. Le app che devono mostrare l'anteprima di stampa non attivano questa finestra di dialogo e implementano autonomamente una versione del dialogo. app Office sono un esempio principale di questo.
IPP Protocollo di stampa Internet. Utilizzato da un dispositivo client per interagire con la stampante per recuperare e impostare le preferenze di stampa e inviare il documento da stampare.
Stampa supporto stampante associato Stampante collegata a PSA.
Stampante IPP Stampante che supporta il protocollo IPP.
Altre Impostazioni Collegamento che apre l'interfaccia utente dell'app fornita dal partner in MPD. Per impostazione predefinita, l'apertura dell'interfaccia utente delle preferenze di stampa predefinite quando non è installato PSA.
Interfaccia utente preferenze stampante Finestra di dialogo utilizzata per impostare le opzioni predefinite della stampante applicate in fase di stampa. Ad esempio: orientamento, formato carta, colore, stampa su entrambi i lati e così via.
PDL Lingua descrizione pagina. Formato in cui un documento viene inviato alla stampante.
Stampante PSA associata Stampante IPP fisica associata a un'applicazione PSA.
PrintDeviceCapabilities Formato documento XML per la definizione delle funzionalità della stampante. Per altre informazioni, vedere Print Ticket and Print Capabilities Technologies.For more information, see Print Ticket and Print Capabilities Technologies.
PrintTicket Raccolta di varie funzionalità correlate alla stampa e i relativi valori usati per acquisire la finalità dell'utente per un determinato processo di stampa.
PrintSupportExtension Attività in background PSA responsabile della fornitura di funzionalità di estensione dei vincoli della stampante.

Questi esempi fanno riferimento a uno spazio dei nomi printsupport , definito come:

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

Quando un utente sta per stampare un documento, spesso desidera impostare alcune preferenze con cui stamparlo. Ad esempio, possono scegliere di stampare un documento con orientamento orizzontale. Possono anche sfruttare una funzionalità personalizzata supportata dalla stampante. Windows fornisce l'interfaccia utente predefinita per visualizzare le preferenze personalizzate, ma l'utente potrebbe non comprenderle perché non ci sono icone o descrizioni appropriate. Windows potrebbe anche usare il controllo dell'interfaccia utente errato per presentarlo. Una funzionalità personalizzata di questo tipo è la migliore presentata da un'app che comprende completamente la funzionalità. Questa è la motivazione alla base dell'offerta di un'API che consente ai produttori di stampanti di creare app personalizzate per i vari modelli di stampante che creano.

Viene creato un nuovo contratto di estensione UAP con una nuova categoria denominata windows.printSupport Impostazioni UI. Le app attivate con questo contratto ricevono un nuovo ActivationKind denominato PrintSupport Impostazioni UI. Questo contratto non richiede alcuna nuova funzionalità.

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

Questo contratto viene richiamato quando l'utente seleziona Altri Impostazioni in MPD o Preferenze in CPD. Questo contratto può essere richiamato anche da Preferenze di stampa nell'app Impostazioni. Quando il contratto viene attivato, l'app riceve un oggetto PrintSupport Impostazioni UISession che può essere usato per ottenere gli oggetti PrintTicket e PrintDevice correnti. L'oggetto PrintDevice può essere utilizzato per comunicare con la stampante per ricevere gli attributi della stampante e del processo. L'app può quindi visualizzare l'interfaccia utente con le opzioni appropriate della stampante all'utente. Quando l'utente effettua le scelte e seleziona OK, l'applicazione può quindi modificare il ticket di stampa, convalidarlo e quindi inviarlo usando l'oggetto PrintSupportPrintTicketTarget . Se l'utente sceglie di annullare la finestra delle preferenze, le modifiche devono essere rimosse e l'applicazione deve uscire completando il differimento ottenuto dall'oggetto PrintSupport Impostazioni UISession.

L'app di supporto di stampa deve gestire più attivazioni simultanee per processi di stampa diversi, pertanto tale app deve supportare più istanze usando l'elemento SupportsMultipleInstances nel file package.appxmanifest. In caso contrario, potrebbero verificarsi situazioni in cui la conferma delle preferenze di un processo di stampa potrebbe chiudere altre finestre delle preferenze che potrebbero essere aperte. L'utente deve aprire nuovamente le finestre delle preferenze.

Il diagramma di sequenza seguente rappresenta il concetto di manipolazione dei ticket di stampa dell'interfaccia utente Impostazioni:

sequence diagram of settings U I print ticket manipulation

Modifica di PrintTicket nell'interfaccia utente delle impostazioni

Codice di esempio C# per l'attivazione dell'interfaccia utente Impostazioni all'avvio da qualsiasi finestra di dialogo di stampa (MPD/CPD o finestra di dialogo di stampa personalizzata) o da impostazioni di 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 per la classe Default Impostazioni View:

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

Codice di esempio C# per visualizzare l'interfaccia utente e modificare 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();
        }
    }
}

Ottenere gli attributi della stampante dal dispositivo stampante

Risposta WireShark da una stampante IPP a una query get-printer-attributes:

wireshark response from an I P P printer to a get printer attributes query

Codice di esempio C# per ottenere nomi di input penna e livelli di input penna dalla stampante:

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

Impostazione degli attributi della stampante sulla stampante

Codice di esempio C# per l'impostazione degli attributi della stampante:

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

Estensione dei vincoli della stampante

L'app supporto stampa supporta la convalida printticket personalizzata e definisce l'elemento PrintTicket predefinito. Questa sezione descrive come vengono supportate queste funzionalità.

Per supportare i vincoli di estensione della stampante, è stato implementato un nuovo tipo di attività in background PrintSupportExtension. Package.appxmanifest include una voce di estendibilità per l'estensione supporto di stampa, come illustrato di seguito:

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

Questo servizio può essere eseguito in qualsiasi momento in un processo di stampa per la stampante IPP associata. Quando l'estensione del supporto di stampa viene attivata tramite la funzione Run(IBackgroundTaskInstance taskInstance), un'istanza di IBackgroundTaskInstance viene assegnata a PrintSupportExtension per fornire l'accesso alla classe di runtime PrintSupportExtensionTriggerDetails, che fornisce internamente PrintSupportExtensionSession come proprietà. La classe di sfondo PrintSupportExtension può quindi usare l'oggetto sessione per registrare gli eventi che desidera fornire funzionalità personalizzate.

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

    Se l'estensione supporto di stampa fornisce il proprio meccanismo di convalida PrintTicket, può registrarsi per questo evento. Ogni volta che è necessario convalidare un printTicket, il sistema di stampa genera questo evento. PrintSupportExtension otterrà quindi l'oggetto PrintTicket corrente che deve essere convalidato all'interno di EventArgs. La classe di sfondo PrintSupportExtension può quindi verificare la validità di PrintTicket e modificarla per risolvere eventuali conflitti. La classe di sfondo PrintSupportExtension deve quindi impostare il risultato per la convalida usando la funzione SetPrintTicketResult per indicare se PrintTicket è stato risolto, presenta conflitti o non è valido. Questo evento può essere generato in qualsiasi momento durante la durata di un processo di stampa. Se la classe PrintSupportExtension non viene registrata per questo evento, il sistema di stampa esegue la propria convalida di PrintTicket.

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

    L'evento viene generato dopo che il sistema di stampa aggiorna la cache PrintDeviceCapabilities della stampante IPP associata. Quando viene generato questo evento, la classe di sfondo PrintSupportExtension può esaminare la proprietà PrintDeviceCapabilities modificata e modificarla.

Convalida personalizzata del ticket di stampa

Codice di esempio C# per fornire il servizio di convalida 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);
    }
}

Aggiornamento di 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 capabilites
        XmlDocument newPdc = this.CheckAndUpdatePrintDeviceCapabilities(pdc);
        args.UpdatePrintDeviceCapabilities(newPdc);
    }
}

Miglioramento della qualità di stampa

Dopo che l'utente si è impegnato a stampare premendo il pulsante di stampa nella finestra di dialogo di stampa, il documento da stampare viene inviato allo stack di stampa dall'app che sta stampando. Questo documento viene quindi sottoposto a trasformazione (rendering in PDL) per renderlo adatto alla stampante di destinazione. Windows determinerà la trasformazione da scegliere in base agli attributi sottoposti a query dalla stampante. Il documento trasformato viene quindi inviato alla stampante. Sebbene questo funzioni bene per la maggior parte delle stampanti, esistono casi in cui la qualità della stampa potrebbe essere migliorata consentendo a un'app partner di partecipare alla trasformazione. Per semplificare questa operazione, l'API flusso di lavoro di stampa corrente viene estesa per includere le chiamate all'app in punti aggiuntivi dallo stack di stampa. Questa API supporta due nuovi eventi per cui l'app PSA può registrarsi. Questi sono gli unici punti di ingresso nella superficie dell'API PSA:

  1. JobStarting

    • Questo evento viene generato quando un processo di stampa viene avviato da qualsiasi applicazione. Quando viene generato l'evento, un'app di supporto di stampa può scegliere di ignorare il rendering del sistema chiamando SetSkipSystemRendering in PrintWorkflowJobStartingEventArgs. Se si seleziona il rendering del sistema ignora, il sistema di stampa non convertirà il documento XPS nel formato PDL richiesto dalla stampante. Al contrario, l'XPS generato dall'applicazione di stampa verrà assegnato direttamente al PSA che è quindi responsabile della conversione di XPS in formato PDL.
  2. PdlModificationRequested

    • Questo evento viene generato quando Windows avvia la conversione del flusso XPS nel formato PDL indicato dalla stampante. La classe di runtime PrintWorkflowPdlModificationRequestedEventArgs viene fornita come argomento per questo evento. Questa classe di evento fornisce oggetti di origine e destinazione PDL per la lettura e la scrittura del contenuto del processo di stampa. Se l'app determina che richiede l'input dell'utente, può avviare l'interfaccia utente usando PrintWorkflowUILauncher da EventArgs. Questa API usa il modello Tester-Doer. PrintWorkflowUILauncher non sarà in grado di richiamare l'interfaccia utente se la funzione IsUILaunchEnabled restituisce false. Questa funzione restituisce false se la sessione PSA è in esecuzione in modalità invisibile all'utente (modalità headless o tutto schermo). L'app di supporto per la stampa non deve provare ad avviare l'interfaccia utente se la funzione restituisce false.

    OutputStream è disponibile come parte di PrintWorkflowPdlTargetStream restituito dalla funzione GetStreamTargetAsync. Il contenuto scritto nella destinazione OutputStream viene passato alla stampante come contenuto del documento.

Diagramma sequenza per l'evento di modifica PDL:

sequence diagram for the source stream P D L modification event

L'applicazione psa in primo piano viene avviata quando l'attività in background PSA richiede l'avvio dell'interfaccia utente. Il PSA può usare il contratto in primo piano per ottenere l'input dell'utente e/o per visualizzare un'anteprima di stampa all'utente.

È stato definito un nuovo tipo di attività in background printSupportWorkflow . Package.appxmanifest include la voce di estendibilità seguente per il contratto PrintSupportWorkflow :

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

All'attivazione del contratto, PrintWorkflowJobTriggerDetails viene fornito come IBackgroundTaskInstance-TriggerDetails>. PrintWorkflowJobTriggerDetails fornisce internamente PrintWorkflowJobBackgroundSession come parte delle relative proprietà. L'app può usare PrintWorkflowJobBackgroundSession per registrarsi per gli eventi correlati a vari punti di inserimento nel flusso di lavoro del processo di stampa. Al termine della registrazione dell'evento, l'app deve chiamare PrintWorkflowJobBackgroundSession::Start affinché il sistema di stampa inizi a generare eventi correlati a vari punti di inserimento.

Viene definito un nuovo ActivationKind denominato PrintSupportJobUI . Questa funzionalità non richiede una nuova funzionalità.

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

Si tratta di un contratto dell'interfaccia utente che può essere avviato dal contratto in background Print Support Workflow o quando l'utente seleziona un avviso popup di errore del processo di stampa. All'attivazione viene fornito PrintWorkflowJobActivatedEventArgs , che ha un oggetto PrintWorkflowJobUISession . Usando PrintWorkflowJobUISession, l'applicazione in primo piano deve registrarsi per l'evento PdlDataAvailable se vuole accedere ai dati PDL. Se l'applicazione in primo piano desidera visualizzare messaggi di errore personalizzati per eventuali errori che possono verificarsi durante il processo, deve registrarsi per l'evento JobNotification . Dopo la registrazione degli eventi, l'applicazione deve chiamare la funzione PrintWorkflowJobUISession::Start affinché il sistema di stampa inizi a generare eventi.

Ignorare il rendering del 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 di modifica PDL

Diagramma sequenza per l'evento di modifica PDL:

sequence diagram for the input stream P D L modification event

Codice di esempio C# per Print Support Job Monitor lettura e scrittura del contenuto del processo di stampa:

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

Avvio dell'interfaccia utente dallo sfondo del flusso di lavoro

Codice di esempio C# per l'avvio dell'interfaccia utente del processo di supporto di stampa da PSA PDL richiesta contratto di evento:

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

Attivazione dell'interfaccia utente del processo del flusso di lavoro per l'evento PDLDataAvailable

Diagramma sequenza per l'attivazione dell'interfaccia utente del processo di stampa per l'evento PdlDataAvailable :

sequence diagram for print job U I activation for the P D L data available event

Codice di esempio C# per il contratto di attivazione dell'interfaccia utente del processo 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);
            }
        }
    }
}

Ottenere gli attributi del processo della stampante

Codice di esempio C# per ottenere gli attributi del processo per un processo di stampa:

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

Impostare gli attributi del processo della stampante

Codice di esempio C#, continuando dalla sezione Ottenere gli attributi del processo della stampante precedente, illustrando l'impostazione degli attributi del processo:

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

Alcune stampanti IPP non supportano l'acquisizione/impostazione degli attributi del processo dopo la creazione del processo. Per tali stampanti, PrintJob ha la proprietà JobId impostata su "0" e GetJobAttributes SetJobAttributes/ avrà esito negativo immediatamente con un'eccezione.

Fornire l'accesso ai file di archiviazione al contenuto PDL

Alcuni formati PDL, ad esempio PDF, necessitano di un flusso completo per iniziare l'elaborazione. Per questo motivo, viene fornito un nuovo metodo denominato GetContentFileAsync nella classe PrintWorkflowPdlSourceContent che restituisce un Archiviazione File del contenuto di origine.

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

Conversione PDL di XPS in PDF

Codice di esempio C# che mostra la conversione PDL di XPS in 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 di notifica del processo

Diagramma sequenza per l'evento di notifica del processo:

sequence diagram for the job notification event

Codice di esempio C#, continuando dalla sezione precedente dell'attivazione dell'interfaccia utente del processo del flusso di lavoro per l'evento PDLDataAvailable , per visualizzare l'errore nella notifica del processo:

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

Creare un processo con attributi di processo iniziali

Attualmente, alcune stampanti IPP non supportano l'operazione set-attribute. La funzione CreateJobOnPrinterWithAttributes e la funzione CreateJobOnPrinterWithAttributesBuffer in PrintWorkflowPdlDataAvailableEventArgs vengono fornite per attenuare questo problema. Usando queste API, uno sviluppatore PSA può fornire attributi di processo passati alla stampante quando viene creato il processo sulla stampante.

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

Elaborazione sequenziale XPS

Codice di esempio C++/Winrt per l'elaborazione sequenziale di XPS prima del completamento dello 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()));
        }
    }
}

Localizzazione dei nomi visualizzati e integrazione dell'API pass-through PDL

Importante

Questa sezione descrive le funzionalità di PSA disponibili a partire da Windows 11 versione 22H2.

In questo scenario, PSA personalizza le funzionalità del dispositivo di stampa (PDC) e fornisce le risorse del dispositivo di stampa (PDR) per la localizzazione delle stringhe.

Il PSA imposta anche i tipi di contenuto dell'API pass-through PDL supportati (formati PDL). Se il PSA non sottoscrive l'evento o non chiama SetSupportedPdlPassthroughContentTypes in modo esplicito, il pass-through PDL è disabilitato per le stampanti associate a questa app 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);
    }
}

Supporto delle funzionalità a livello di pagina e attributi dell'operazione

Importante

Questa sezione descrive le funzionalità di PSA disponibili a partire da Windows 11 versione 22H2.

Gli scenari di supporto delle funzionalità a livello di pagina e degli attributi dell'operazione vengono raggruppati perché vengono risolti apportando modifiche nella stessa posizione nel codice di esempio.

  • Supporto delle funzionalità a livello di pagina: in questo scenario, l'applicazione PSA specifica l'attributo a livello di pagina, che non deve essere sottoposto a override da un attributo IPP analizzato da PrintTicket.

  • Raccolta separata per il supporto degli attributi dell'operazione (stampa PIN): in questo scenario, l'applicazione PSA specifica attributi di operazione IPP personalizzati, ad esempio PIN.

Il codice di esempio C# seguente mostra le modifiche necessarie per il supporto delle funzionalità a livello di pagina e Raccolta separata per gli scenari degli attributi dell'operazione.

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

Miglioramento del dialogo di stampa con PSA

Importante

Questa sezione descrive le funzionalità di PSA disponibili a partire da Windows 11 versione 22H2.

In questo scenario, l'uso della finestra di dialogo di stampa con l'integrazione PSA abilita le azioni seguenti:

  • Ottenere un callback quando la selezione viene modificata nel MPD alla stampante associata a PSA

  • Mostra una scheda adattiva con il supporto dell'azione openUrl

  • Mostra caratteristiche e parametri personalizzati nella finestra di dialogo di stampa

  • Modificare PrintTicket, modificando così la selezione per le opzioni di funzionalità visualizzate nella finestra di dialogo di stampa

  • Ottenere Windows.ApplicationModel.AppInfo dell'app di stampa, aprendo la finestra di dialogo di stampa

L'esempio C# seguente illustra i miglioramenti apportati alla finestra di dialogo di stampa:

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

Conversione PDL con flag di elaborazione basati su host

Importante

Questa sezione descrive le funzionalità di PSA disponibili a partire da Windows 11 versione 22H2.

L'API di conversione PDL corrente, PrintWorkflowPdlConverter.ConvertPdlAsync, esegue l'elaborazione basata su host per impostazione predefinita. Ciò significa che il computer host/stampa esegue la rotazione, l'ordine di pagina e così via, in modo che la stampante non debba eseguire queste operazioni. Tuttavia, gli IHV della stampante possono richiedere la conversione PDL senza l'elaborazione basata su host perché la stampante può fare meglio. La funzione ConvertPdlAsync accetta flag di elaborazione basati su host per soddisfare questo requisito. Il PSA può ignorare tutte le operazioni di elaborazione basate su host o una particolare operazione di elaborazione basata su host usando questo flag.

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
}

Impostare i criteri di aggiornamento pdc (Print Device Capabilities)

Importante

Questa sezione descrive le funzionalità di PSA disponibili a partire da Windows 11 versione 22H2.

Gli IHV della stampante possono avere requisiti diversi quando è necessario aggiornare le funzionalità del dispositivo di stampa.Printer IHVs may have different requirements on when Print Device Capabilities (PDC) deve essere aggiornato. Per soddisfare questi requisiti, PrintSupportPrintDeviceCapabilitiesUpdatePolicy può impostare un criterio di aggiornamento per il PDC. PSA può impostare i criteri di aggiornamento PDC in base all'ora o al numero di processi di stampa usando questa API.

Impostare i criteri di aggiornamento PDC in base al numero di processi

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

Impostare i criteri di aggiornamento PDC in base al 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);      
    }
}

Linee guida generali per la progettazione dell'app di supporto per la stampa (PSA)

Quando si progetta un'app di supporto per la stampa, è importante includere questi aspetti nella progettazione:

  • Sia i contratti in primo piano che in background devono essere contrassegnati come supporto di più istanze, ad esempio SupportsMultipleInstance devono essere presenti nel manifesto del pacchetto. Ciò consente di garantire che la durata dei contratti possa essere gestita in modo affidabile per più processi simultanei.

  • Considerare l'avvio dell'interfaccia utente per la modifica PDL come passaggio facoltativo. Eseguire un'operazione ottimale per completare correttamente il processo di stampa anche se l'avvio dell'interfaccia utente non è consentito. I processi di stampa devono essere interrotti solo se non è possibile completarli correttamente senza l'input dell'utente durante la modifica PDL. Prendere in considerazione l'invio del PDL non modificato in questi casi.

  • Quando si avvia l'interfaccia utente per la modifica PDL, chiamare IsUILaunchEnabled prima di chiamare LaunchAndCompleteUIAsync. Ciò consente di assicurarsi che gli scenari che non possono mostrare l'interfaccia utente al momento corrente continuano a essere stampati correttamente. Questi scenari possono trovarsi in un dispositivo headless o in un dispositivo attualmente in modalità tutto schermo o non disturbare.

Stampare l'associazione dell'app di supporto

Windows.Devices.Printers

Windows.Graphics.Printing.PrintSupport

Windows.Graphics.Printing.Workflow

Specifica IPP (Internet Printing Protocol)