Skip to content

[API Proposal]: Print without PrintQueue abstraction using the modern print dialog #8940

Open

Description

Background and motivation

AFAIK there's currently no solution to print without a PrintQueue in case an application already has an instance of IXpsDocumentPackageTarget.
This scenario occurs when implementing the WinRT PrintManager (The modern print dialog) for a WPF app.
See PrintCompat.cs and WpfPrintDocumentSource.cs for context.

API Proposal

See XpsSerializationHelper.il for a minimal code flow.

New class XpsSerializationManagerInterop

using System.Windows.Xps.Serialization.RCW;
using System.Windows.Xps.Packaging;

namespace System.Windows.Xps.Serialization;
public sealed class XpsSerializationManagerInterop
{
    readonly XpsOMSerializationManager _serializer;
    readonly IXpsOMPackageWriter _writer;
    private XpsSerializationManagerInterop(...);

    public static XpsSerializationManagerInterop CreateFromXpsDocumentPackageTarget(object target)
    {
        // https://github.com/dotnet/wpf/blob/7b755d2bbcc9d77760f68b5e738b2587cabe1a37/src/Microsoft.DotNet.Wpf/src/System.Printing/CPP/src/PrintQueue.cpp#L4723-L4734
        XpsOMPackagingPolicy policy = new((IXpsDocumentPackageTarget)target);
        policy.EnsureXpsOMPackageWriter();

        // Internally expose XpsOMPackagingPolicy._currentFixedDocumentSequenceWriter
        var writer = policy.XpsOMPackageWriter;

        XpsOMSerializationManager serializer = new(policy);
        return new(serializer, )
    }

    // Proxy events from XpsOMSerializationManager
    public event XpsSerializationPrintTicketRequiredEventHandler? PrintTicketRequired;
    public event XpsSerializationProgressChangedEventHandler? ProgressChanged;

    public void Print(object serializedObject)
    {
        _serializer.SaveAsXaml(serializedObject);
        _serializer.Commit();

        // Close writer to flush to printer
        // Only close on success (no exception)
        // https://github.com/dotnet/wpf/blob/906eb2878bbe4d5f86e77e9a0d4f85815e9f5aaf/src/Microsoft.DotNet.Wpf/src/System.Printing/CPP/src/XpsCompatiblePrinter.cpp#L125
        _writer.Close();
    }
}

Internally expose IXpsOMPackageWriter from XpsOMPackagingPolicy

_currentFixedDocumentSequenceWriter = _packageTarget.GetXpsOMPackageWriter(partUri, null);
if (_printQueue != null)
{
((PrintQueue)_printQueue).XpsOMPackageWriter = _currentFixedDocumentSequenceWriter;
}

namespace System.Windows.Xps.Packaging
{
    internal class XpsOMPackagingPolicy : BasePackagingPolicy
    {
        ...
+        public IXpsOMPackageWriter XpsOMPackageWriter => _currentFixedDocumentSequenceWriter;
        ...
        private IXpsOMPackageWriter _currentFixedDocumentSequenceWriter;
    }
}

API Usage

object target = ...; // IXpsDocumentPackageTarget com object
var serializer = XpsSerializationManagerInterop.CreateFromXpsDocumentPackageTarget(target);
serializer.PrintTicketRequired += ...;
serializer.Print(paginator); // Might throw

Alternative Designs

Create an alternative interop PrintQueue implementation:

object target = ...; // IXpsDocumentPackageTarget com object
var queue = PrintQueueInterop.CreateFromXpsOMPackageWriter(target);
var writer = PrintQueue.CreateXpsDocumentWriter(queue);
writer.Write(paginator, printTicket);

This has less new public api surface (only one new method CreateFromXpsOMPackageWriter) but might require a lot more internal api changes / abstractions.

Risks

Both solutions can likely only be used for a single print, as the IXpsOMPackageWriter has to be closed to flush the buffered data to the printer.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Assignees

No one assigned

    Labels

    API suggestionEarly API idea and discussion, it is NOT ready for implementation

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions