[API Proposal]: Print without `PrintQueue` abstraction using the modern print dialog
- Dominant language
- C#
- Stars
- 7.7k
- Forks
- 1.3k
- Avg merge
- 1d 11h
- Merged PRs (30d)
- 61
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`](https://learn.microsoft.com/en-us/uwp/api/windows.graphics.printing.printmanager) (The modern print dialog) for a WPF app.
See [PrintCompat.cs](https://github.com/Diahon/Diahon.WinUI.Printing/blob/921e1f9a5f3e52b723dafe87bc3b9df3530ddcaa/Diahon.WinUI.Printing.Wpf/PrintCompat.cs) and [WpfPrintDocumentSource.cs](https://github.com/Diahon/Diahon.WinUI.Printing/blob/921e1f9a5f3e52b723dafe87bc3b9df3530ddcaa/Diahon.WinUI.Printing.Wpf/WpfPrintDocumentSource.cs) for context.
### API Proposal
See [XpsSerializationHelper.il](https://github.com/Diahon/Diahon.WinUI.Printing/blob/main/ReachFrameworkHelper/XpsSerializationHelper.il) for a minimal code flow.
#### New class `XpsSerializationManagerInterop`
```cs
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`
https://github.com/dotnet/wpf/blob/7b755d2bbcc9d77760f68b5e738b2587cabe1a37/src/Microsoft.DotNet.Wpf/src/ReachFramework/Serialization/manager/XpsOMPackagingPolicy.cs#L68-L72
```diff
namespace System.Windows.Xps.Packaging
{
internal class XpsOMPackagingPolicy : BasePackagingPolicy
{
...
+ public IXpsOMPackageWriter XpsOMPackageWriter => _currentFixedDocumentSequenceWriter;
...
private IXpsOMPackageWriter _currentFixedDocumentSequenceWriter;
}
}
```
### API Usage
```cs
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:
```csharp
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.
Contributor guide
Assessment
This issue has not been assessed yet.