-
Notifications
You must be signed in to change notification settings - Fork 4.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Feature Request]: Modern library for printer communication and print control #75628
Comments
I couldn't figure out the best area label to add to this issue. If you have write-permissions please help me learn by adding exactly one area label. |
Tagging subscribers to this area: @dotnet/area-system-runtime Issue DetailsI have noticed that there is still a big flaw in printing in the .NET environment. Even in an age of digitalization, printers are used extensively. In companies to print labels, delivery bills or labels, in the private environment to print documents, tax returns and many other applications. Unfortunately, there is still no really good print library in the .NET environment. There is The more companies move into the cloud, the more important it is to make the whole thing platform-independent. My suggestion is to add an API that
As you can easily see my suggestion is to recreate It would make sense to distribute this API as a Nuget package for this reason instead of including it in the BCL. The whole thing here is just a rough wish. I have heard especially in the company where I work as well as from other companies that they really want something like this and therefore a real-world case is there. Feel free to leave your feedback. Since this would be a larger project, if there is an interest on the part of Microsoft and the community, I would deal with it in detail and create API Proposals.
|
Tagging subscribers to this area: @dotnet/area-system-drawing Issue DetailsI have noticed that there is still a big flaw in printing in the .NET environment. Even in an age of digitalization, printers are used extensively. In companies to print labels, delivery bills or labels, in the private environment to print documents, tax returns and many other applications. Unfortunately, there is still no really good print library in the .NET environment. There is The more companies move into the cloud, the more important it is to make the whole thing platform-independent. My suggestion is to add an API that
As you can easily see my suggestion is to recreate It would make sense to distribute this API as a Nuget package for this reason instead of including it in the BCL. The whole thing here is just a rough wish. I have heard especially in the company where I work as well as from other companies that they really want something like this and therefore a real-world case is there. Feel free to leave your feedback. Since this would be a larger project, if there is an interest on the part of Microsoft and the community, I would deal with it in detail and create API Proposals.
|
@danmoseley this might fit better in dotnet/iot |
@krwq What does printing have to do with IoT? |
@deeprobin dotnet/iot is about hardware and printer is a physical device like any other. Printer doesn't really fit runtime repo in my eyes since most apps won't need it - APIs sounds super useful but it's a bit of its own beast. It seems like it would need either a separate package or be added to other repo. IMO from dotnet org dotnet/iot seems might fit best. |
@krwq I assumed that I would also like to refer to the description in the README.md here:
Since, depending on the implementation, the Windows API2 (using GDI or XPS) can be used instead of, say, IPP, I thought the @richlander I saw you are active in However, we could also consider starting a separate repository for this. A repository like I am also willing to support the printing project, but this will be a somewhat larger project (especially in the design of the API shape - the implementation is rather secondary) and I would definitely need support. Footnotes
|
@deeprobin it's true majority of the devices are I2C/SPI based but there are some exceptions, i.e. still picture or RFID/NFC devices - while device itself would maybe sometimes match criteria, reading data is way out of scope and is super complex but it would make device pointless without it. Then we have some other border-line devices i.e. NMEA 0183 - the device is serial port but protocol is essentially rather advanced parser or FT4222 which is a USB device which requires external driver but exposes GPIO/I2C/SPI so we added it for easier interop between that and other devices. As for naming it predates me but System.Device.Gpio is only used for implementing generic purpose protocols and I think singular refers to specific protocol in use at a moment. Iot.Device.Bindings has plural form though and that's where devices live. Regardless dotnet/iot seems like better match than dotnet/runtime even if it's not perfect and can be used as a stop (Iot.Device.Bindings specifically where making justified breaking changes is ok). If it grows too large we could potentially make this a separate nuget package. |
Tagging subscribers to this area: @dotnet/area-meta Issue DetailsI have noticed that there is still a big flaw in printing in the .NET environment. Even in an age of digitalization, printers are used extensively. In companies to print labels, delivery bills or labels, in the private environment to print documents, tax returns and many other applications. Unfortunately, there is still no really good print library in the .NET environment. There is The more companies move into the cloud, the more important it is to make the whole thing platform-independent. My suggestion is to add an API that
As you can easily see my suggestion is to recreate It would make sense to distribute this API as a Nuget package for this reason instead of including it in the BCL. The whole thing here is just a rough wish. I have heard especially in the company where I work as well as from other companies that they really want something like this and therefore a real-world case is there. Feel free to leave your feedback. Since this would be a larger project, if there is an interest on the part of Microsoft and the community, I would deal with it in detail and create API Proposals.
|
@deeprobin do you know of any community projects that aim to fill this gap? While we are .NET library experts we aren't aiming to write every possible library that applications might need. |
Specifically this gap as I have described not. There is a project called SharpIpp which implements an IPP client although this implementation has probably fallen asleep a bit and only supports limited features of this protocol. tagging the author to possibly give his mustard here: @Zelenov
That is, of course, clear. But this is an essential API in my opinion. Many companies (at least in Germany) that do not use SAP and have their own ERP system mostly use C#. Printing is an essential function. And the .NET libraries are there for essential functions in my opinion. Of course, a small e-commerce company probably only needs something like this in exceptional cases. But as soon as it goes in the direction of logistics, there are, for example, customs documents that are printed, etc.. I'm not talking about adding this to the runtime repository anymore. But as an external NuGet package this is certainly very used.
In addition, there are currently already libraries in the .NET runtime that not every use case needs. Without downplaying the value of these libraries, one could for example use Tar, Brotli, Cbor, ... could also be classified as "every possible library". The implementation is important, even if the actual demand is probably not too big. |
This'd be quite useful, and there seems to be a gap for it. .NET's printing ecosystem needs a little attention over the next few versions. Some of it is tightly coupled to the GUI, (albeit for good reason at the time) while other parts haven't kept pace with the cross-platform transition. If we want to print a document right now, we've got a few different options:
There are also other tasks which we'd reasonably view as printing, but which don't necessarily fit into the current object model. This can include things like barcode/label printing, and 3D printing. If .NET had a clear printing-related object model, we'd be able to provide a common interface for all of the above, and eventually expand it with cross-platform implementations; the current options could then be deprecated. I think there might be a case for .NET to have two implementations (for Windows and Linux/MacOS support) at some point, but just having that common interface would be a useful starting point. I'd eventually hope to see libraries for third-part printer types (whether that's a cloud printing service, a Klipper/Moonraker 3D print API, or something else.) This model would only cover submitting, managing and streaming data to print jobs. The hardware-specific tasks (paper tray status, printer bed temperature, ink levels) could easily go in dotnet/iot, as suggested - it's too tied to the physical hardware, when the Windows spooler and CUPS both work with print queues. With this type of approach, a user would be able to print a document in one or two ways:
I think this'd mean that less work is taken on than we'd expect: a common design for the object model, one (perhaps two) translation layers (the first being a simple "pass through from Stream", the second possibly allowing the rendering of a Graphics object to an EMF HDC) and either one or two print queue providers for the Windows spooler or CUPS. Could we consider starting this work for .NET 9/10? |
I've thought about an API design, hopefully to start a discussion of the amount of work required. I'd be happy to help with that work, but it's foundational enough that I think it should definitely be part of a .NET package rather than a community library. Windows Forms, WPF and WinRT have the functionality built in, so there's some precedent there (and being able to unlock printing on Linux is nice too.) I'd suggest three layers:
Base object modelThis lays down the fundamental structure of the printing model:
I'd only expect these to have basic routing in them, providing an interface for the creation of jobs, and the streaming of job segments (probably from an IAsyncEnumerable) Initial API shapepublic abstract class PrintQueueProvider
{
// Gets all queues capable of handling a specific type of print job.
public abstract IAsyncEnumerable<IPrintQueue<TPrintJob>> EnumerateQueuesAsync<TPrintJob>()
where TPrintJob : PrintJob<TPrintJob>;
// Gets a queue by its name and print job type. A print queue might support multiple
// job types (e.g. sending raw printer command jobs, and printing a raster image.)
// In such a case, the queue class would implement IPrintQueue of each job type.
public abstract ValueTask<IPrintQueue<TPrintJob>> GetQueueAsync<TPrintJob>(string name)
where TPrintJob : PrintJob<TPrintJob>;
}
public interface IPrintQueue<TPrintJob>
where TPrintJob : PrintJob<TPrintJob>
{
// Creates a single job of type TPrintJob, using segmentFactory to convert from a managed
// object into a list of job segments. This might cover a single segment of raw printer commands, or
// it might accept a paginated document's list of pages.
ValueTask<TPrintJob> CreateJobAsync<T, TJobSegment>(T sourceObject,
Func<T, IAsyncEnumerable<TJobSegment>> segmentFactory)
where TJobSegment : JobSegment<TJobSegment>;
// Enumerates all jobs in the queue.
IAsyncEnumerable<TPrintJob> EnumerateJobs();
}
public abstract class PrintJob<TSelf>
where TSelf : PrintJob<TSelf>
{
// <common properties - DateQueued, DateStarted, TotalSegments, SegmentsPrinted, SegmentsGenerated>
public JobStatus Status { get; protected set; }
public string Title { get; private set; }
public int Identifier { get; protected set; }
// Gets the first segment in the linked list - page one, or the initial setup commands for a 3D printer.
public abstract ValueTask<TJobSegment> GetFirstSegmentAsync()
where TJobSegment : JobSegment<TJobSegment>;
// Indicates whether this print job will ever allow a particular job segment type.
public abstract bool SupportsSegmentType<TJobSegment>()
where TJobSegment : JobSegment<TJobSegment>;
// Command and wait methods
public abstract ValueTask PauseAsync();
public abstract ValueTask ResumeAsync();
public abstract ValueTask CancelAsync();
public abstract ValueTask WaitForStatusAsync(JobStatus status);
public abstract ValueTask WaitForSegmentAsync(int segmentNumber);
}
public abstract class JobSegment<TSelf>
where TSelf : JobSegment<TSelf>
{
public int SequenceNumber { get; private set; }
// Gets the data to be sent to the printer in a printer-recognisable format (e.g. EMF, PostScript, GCode)
public abstract ValueTask<ReadOnlyMemory<byte>> GetSegmentDataAsync();
// Gets the next segment. Returns null if this is the last segment.
public async ValueTask<TSelf?> GetNextSegmentAsync();
} "Friendly" object modelThis abstracts the raw model which treats print job page data as a binary blob, and exposes it as an object model which a developer could reasonably work with - and more importantly, it means that all downstream clients have a consistent view of obvious abstractions, such as a paginated print job. Initial API shapepublic abstract class PaginatedRasterPrintJob : PaginatedPrintJob
{
}
public abstract class PaginatedPrintJob : PrintJob<PaginatedPrintJob>
{
public override bool SupportsSegmentType<TJobSegment>()
=> typeof(Page).IsAssignableFrom(typeof(TJobSegment));
}
public abstract class Page : JobSegment<Page>
{
} ImplementationMost of the above work is design-based, with some code to plumb data through to individual job segments. The core implementation work here would be to fold Windows' print spooler into this design, then to add an IPP Anywhere implementation to enable CUPS on Linux and Mac. I'm happy to help with that. I'd also add the two higher-level utility classes I mentioned a while ago: one which reads from a Stream and outputs a single JobSegment, and another which accepts a Graphics object and outputs it to a printer as a raster image. These enable three scenarios:
|
Thank you for your initiative and the first draft proposal for the API shape. I'd still like a few refinements such as cancellation tokens for the asynchronous methods, but otherwise I think it's a good place to start. However, I'm still a little undecided myself. The Windows Print Queue & (probably) CUPS also support this in any case. However, if we manually communicate "RAW" with our printer via TCP, I have no idea how we can pause or even cancel the print - these are perhaps special functions in e.g. ZPL or PCL, but nothing related to the print transport. Maybe it would make sense to introduce a property like "IsPauseSupported" and throw a NotSupportedException in the method if it is not supported. Another idea would be to build individual interfaces for the individual functions, similar to BCL with Generic Math. @krwq What do you think of this proposal? |
Thanks. I've split the responsibilities apart into three main areas:
I'd personally expect the cancellation of the print job to signal the print spooler, and the spooler would handle the underlying printer comms. Azure Universal Print is a useful example here: the client will probably never be able to contact the underlying printer, but that doesn't matter because it's sending the command to the AUP service. In some restricted enterprise networks, the "local queue" presented by the Windows print spooler might actually sit on a remote print server, and the client is firewalled away from the printer itself. This should hopefully simplify things a little, because it means that we don't need to worry about developing for an unbounded set of printer protocols - we could just call SetJob or invoke a Cancel-Job operation and run. CancellationTokens make a lot of sense. It could be unexpected if we're making REST calls and we invoke the CancellationToken after the IPP server has performed the action but before it's returned a response to the client. I think that's just a matter of documentation though - practically anything could fall into that category, it's just more obvious when there's something coming out the printer! We might need quite a few properties like "IsPauseSupported". I'm not sure whether we'd want to have that, a Stream-style |
Perhaps we could include the product owners of Azure Universal Print in this topic. They already have this Universal Print product, which could benefit from this API.
I think a cancellation token must be present on the asynchronous methods in any case. In this case, a cancellation would only affect the current operation and not the entire print job. If a REST call is cancelled (e.g. socket close), I would expect the remote not to execute it either. But as you say, this would have to be documented. What I have now noticed afterwards: In your proposal, you also define a "Status" property at PrintJob level. Status is a frequently changing property. Especially where the print queues are "externally managed" (e.g. on the printer or print server), you would have to execute a request every time to keep them up to date. Wouldn't an asynchronous method with a ValueTask return make sense here? Something like: public abstract partial class PrintJob<TSelf>
where TSelf : PrintJob<TSelf>
{
public ValueTask<JobStatus> GetJobStatusAsync(CancellationToken cancellationToken = default);
} I think we should generally try to stick to the Print Working Group (PWG) in this area, as these printing topics are already dealt with very thoroughly there. There is generally a semantic model there, which could perhaps help us in future proposals. |
Making the Status property an async function sounds sensible to me. The Windows spooler is synchronous, but on Linux (and for anything remote) that probably won't hold true. The PWG have some useful publications. Pages 35-37 of the IPP Shared Infrastructure Extensions list common operations, which would help inform the appropriate interface on PrintJob and PrintQueue. A lot of their data model also makes sense, and at a glance, there's a lot of overlap with the Windows print spooler - there are only so many ways to reinvent that wheel! The mandated operations for an IPP client look pretty sensible:
The recommended ones also seem reasonable:
I'd also add a few of the optional operations too:
I think there's value in looking for overlap between the widest variety of printers: the PWG standards, Winspool's prior art, one of the more unusual printers (I've referred to Zebra printers, but the goal's just to find a printer which isn't a standard inkjet / laser printer) and a 3D printer. Hopefully if there's anything key which overlaps between them, it can go in the root PrintJob class. Speaking more widely to anyone looking at this from the .NET teams: which way around should this go? Do we need a firm API proposal to request a |
Yes, CUPS seems to be very "async" - many functions have callbacks. Is perhaps the person responsible for the WinNT Print Spooler also here somewhere on GitHub? (cc MSFT)
In principle, such APIs should always be proposed at some point in order to comply with the typical API review process. @ericstj @bartonjs |
Hey @michaelrsweet, I hope you don't mind me pinging you here. We are currently discussing what a universal printing API in the .NET environment could look like in general. |
2024 and still no printing in MAUI. What a joke. |
@deeprobin Sorry I just saw this bug (not sure why I didn't get the notification)... I'll add my comments later today... |
OK, so initial comments:
WRT printing not being commonly used by applications, that is IMHO a fairly myopic view of things. Printing is a key part of shipping (200,000,000,000 packages per year with shipping labels, packing lists, and hazard labels), medicine (wrist bands, drug prescriptions, test reports, "replacement parts", etc.), and commerce (invoices, receipts, etc.) In some cases the content you are printing might be provided electronically (particularly packing lists, test reports, invoices, and receipts) but you still need to produce the content even if you don't print it. So I think a key part of any printing API is an API for producing (paginated) documents that can be printed, emailed, saved, etc. |
XPS Print isn't depricated @deeprobin ... |
@wstaelens The Win32 docs don't recommend them very good.
|
Still seeing it a lot in the wild.... |
Thanks @michaelrsweet, modelling a client API round IPP Everywhere makes perfect sense. Your point around temporary print queues is also helpful: it seems to me that if .NET gains printing support in the future, it'd be best to think in terms of "print destinations" or similar, so as to specifically avoid a situation where .NET refers to something as a "print queue", while Windows might send the data to a Print Document Package-style printing app, CUPS might send it to a Printer Application's IPP endpoint, etc. Any future object model should probably separate concerns similarly to CUPS and IPP. I think the Windows printing API (whether that's Winspool or a Print Document Package API) can fit fairly tidily within this:
Some implementations of the discovery and the transport layers would be a better fit for the dotnet/iot repo, or for user libraries. I'd expect the generation of the PDL / paginated documents to also be handled completely by user libraries. However, for printing support to be useful in things like Windows services, WPF, ASP.NET Core and Windows Forms, a core object model needs to be in the basic runtime, as does an implementation of Winspool / CUPS which is exposed via that model. This would go a long way to undoing the fragmentation which is currently in place across WinForms, UWP and WPF, and also provides a starting point for Maui (and perhaps ASP.NET Core.) A community library can't cover these platforms: even if it takes a long time, the end goal would hopefully be for the OOTB common controls for each UI framework to wrap around the same common object model (ideally, with community libraries building pluggable platform-specific discovery, data formatting or transport components.) Coming back to the wider point about printing in general: it's probably not a headline feature of a major .NET release, but it remains important. Physical paper documents are sometimes required for legal/contractual reasons; paginated reports are generated by software like SQL Server Reporting Services; practically any warehouse will print finished goods labels. At the moment, .NET doesn't present a clear, cross-platform way for developers to engage with printing (whether that's a traditional printer in an office, something in an industrial setting which requires lower-level control over the same operations like a label printer, or something more unusual such as a 3D printer.) If .NET 10 has an object model and 2-3 basic implementations which expose Winspool, CUPS and potentially the Print Document Package API, it starts to unblock consistent cross-platform functionality in most of the downstream implementations. |
Printing in WinUI3 is still broken and it doesn't seem like anyone is trying to fix it. Many of us still need the ability to print to PDF, print to file, etc. similar issue |
I have noticed that there is still a big flaw in printing in the .NET environment.
Even in an age of digitalization, printers are used extensively. In companies to print labels, delivery bills or labels, in the private environment to print documents, tax returns and many other applications.
Unfortunately, there is still no really good print library in the .NET environment. There is
System.Drawing.Printing
and the C++/CLI library in the WPF repositorySystem.Printing
.Both are strictly limited to Windows, already very legacy and very limited.
The more companies move into the cloud, the more important it is to make the whole thing platform-independent.
My suggestion is to add an API that
System.Printing.Ipp
(the protocol which is used by CUPS)System.Printing.Smb
(if there are no licensing problems, because SMB is proprietary)System.Printing.Builtin
System.Printing
As you can easily see my suggestion is to recreate
System.Printing
even if this API already exists in WPF (I could well imagine that WPF would then use this API in the long run).It would make sense to distribute this API as a Nuget package for this reason instead of including it in the BCL.
The whole thing here is just a rough wish. I have heard especially in the company where I work as well as from other companies that they really want something like this and therefore a real-world case is there.
Feel free to leave your feedback. Since this would be a larger project, if there is an interest on the part of Microsoft and the community, I would deal with it in detail and create API Proposals.
The text was updated successfully, but these errors were encountered: