docs: initial documents

This commit is contained in:
ArcticLampyrid committed 2024-03-14 16:07:47 +08:00
1 parent d39ad272c1
commit 6651a8a9af
18 files changed
+269 -1

No files matched your search

+96
View File
@@ -1,8 +1,104 @@
#![cfg(windows)]
#![warn(missing_docs)]
//! A crate for printing to a Windows printer device using Windows API.
//!
//! # Examples
//! ## Print a file
//! For a simple example presenting how to print a file:
//! - Filter for the device you want to use.
//! - Wrap the printer device with a printer.
//! - Send a file to the printer.
//!
//! First, get all printer devices via `PrinterDevice::all()` and filter for the device you want to use.
//!
//! ```rust
//! use winprint::printer::PrinterDevice;
//!
//! fn get_my_device() -> PrinterDevice {
//! let printers = PrinterDevice::all().expect("Failed to get printers");
//! printers
//! .into_iter()
//! .find(|x| x.name() == "My Printer")
//! .expect("My Printer not found")
//! }
//! ```
//!
//! Then, create a printer and send a file to it. Currently, there are two kinds of printers available:
//! - [`printer::XpsPrinter`]: For printing XPS files.
//! - [`printer::PdfiumPrinter`]: For printing PDF files via PDFium library. (Feature `pdfium` must be enabled)
//!
//! **Note**: The concept *`Printer`* here is a warpper of device for printing specific types of data,
//! not meaning the printer device.
//!
//! ```rust
//! use std::path::Path;
//! use winprint::printer::FilePrinter;
//! use winprint::printer::PrinterDevice;
//! use winprint::printer::XpsPrinter;
//! # use winprint::test_utils::null_device::thread_local as get_my_device;
//!
//! let my_device = get_my_device();
//! let xps = XpsPrinter::new(my_device);
//! let path = Path::new("path/to/test/document.xps");
//! # let path_buf = Path::new(env!("CARGO_MANIFEST_DIR")).join("test_data/test_document.xps");
//! # let path = path_buf.as_path();
//! xps.print(path, Default::default()).unwrap();
//! ```
//!
//! ## Specify the printing preferences
//! Print ticket is a set of options that can be to specify the printing preferences,
//! It can be used to set options such as the media size, orientation, and so on.
//! If you want to specify the printing preferences, you may use print tickets.
//!
//! See [Print Schema Specification] for technical details.
//!
//! Here is an example presenting how to use print tickets with this crate:
//! - Fetch print capabilities from the printer device.
//! - Filter the capabilities you want to use.
//! - Create a print ticket builder for your printer device.
//! - Merge the capabilities into the print ticket you are to build.
//! - Build the print ticket.
//! - Print the file with the print ticket.
//!
//! ```rust
//! use std::path::Path;
//! use winprint::printer::FilePrinter;
//! use winprint::printer::PrinterDevice;
//! use winprint::printer::XpsPrinter;
//! use winprint::ticket::PredefinedMediaName;
//! use winprint::ticket::PrintCapabilities;
//! use winprint::ticket::PrintTicket;
//! use winprint::ticket::PrintTicketBuilder;
//! # use winprint::test_utils::null_device::thread_local as get_my_device;
//!
//! let my_device = get_my_device();
//! let capabilities = PrintCapabilities::fetch(&my_device).unwrap();
//! let a4_media = capabilities
//! .page_media_size()
//! .find(|x| x.as_predefined_name() == Some(PredefinedMediaName::ISOA4))
//! .unwrap();
//! let mut builder = PrintTicketBuilder::new(&my_device).unwrap();
//! builder.merge(a4_media).unwrap();
//! let ticket = builder.build().unwrap();
//! let xps = XpsPrinter::new(my_device);
//! let path = Path::new("path/to/test/document.xps");
//! # let path_buf = Path::new(env!("CARGO_MANIFEST_DIR")).join("test_data/test_document.xps");
//! # let path = path_buf.as_path();
//! xps.print(path, ticket).unwrap();
//! ```
//!
//! [Print Schema Specification]: https://learn.microsoft.com/en-us/windows/win32/printdocs/printschema
//!
//! # Features
//! - `pdfium`: Enable PDFium support for printing PDF files.
mod bindings;
/// Provides a way to print various types of data to a printer device.
pub mod printer;
/// Utilities for testing
pub mod test_utils;
/// Provides a way to specify the printing preferences.
pub mod ticket;
mod utils;
#[cfg(test)]
+6
View File
@@ -7,6 +7,7 @@ use thiserror::Error;
use windows::core::PCWSTR;
use windows::Win32::Graphics::Printing::*;
#[derive(Clone, Debug)]
/// Represents a printer device.
pub struct PrinterDevice {
name: String,
os_server: OsString,
@@ -15,12 +16,15 @@ pub struct PrinterDevice {
}
#[derive(Error, Debug)]
/// Represents an error occurred while enumerating printer devices.
pub enum EnumDeviceError {
/// Failed to enum printer device.
#[error("Failed to enum printer device: {0}")]
FailedToEnumPrinterDevice(windows::core::Error),
}
impl PrinterDevice {
/// Fetch all printer devices.
pub fn all() -> Result<Vec<Self>, EnumDeviceError> {
let mut bytes_needed = 0;
let mut count_returned = 0;
@@ -91,10 +95,12 @@ impl PrinterDevice {
self.os_name.as_ref()
}
/// Check if the printer device is local.
pub fn is_local(&self) -> bool {
self.os_attributes & PRINTER_ATTRIBUTE_LOCAL != 0
}
/// Check if the printer device is remote.
pub fn is_remote(&self) -> bool {
self.os_attributes & PRINTER_ATTRIBUTE_NETWORK != 0
}
+4
View File
@@ -1,7 +1,11 @@
use std::path::Path;
/// A trait representing a printer that can print files.
pub trait FilePrinter {
/// The options type for the printer.
type Options;
/// The error type for the printer.
type Error;
/// Print the file with the given options.
fn print(&self, path: &Path, options: Self::Options) -> std::result::Result<(), Self::Error>;
}
+6
View File
@@ -21,20 +21,26 @@ use windows::{
};
#[derive(Error, Debug)]
/// Represents an error from [`PdfiumPrinter`].
pub enum PdfiumPrinterError {
/// Failed to open printer.
#[error("Failed to open printer")]
FailedToOpenPrinter,
/// File I/O error.
#[error("File I/O error: {0}")]
FileIOError(std::io::Error),
/// Print ticket error.
#[error("Print Ticker Error: {0}")]
PrintTicketError(ToDevModeError),
}
/// A printer that uses Pdfium to print PDF documents.
pub struct PdfiumPrinter {
printer: PrinterDevice,
}
impl PdfiumPrinter {
/// Create a new [`PdfiumPrinter`] for the given printer device.
pub fn new(printer: PrinterDevice) -> Self {
Self { printer }
}
+9
View File
@@ -17,26 +17,35 @@ use windows::{
};
#[derive(Error, Debug)]
/// Represents an error from [`XpsPrinter`].
pub enum XpsPrinterError {
/// Failed to create event.
#[error("Failed to create event: {0}")]
FailedToCreateEvent(windows::core::Error),
/// Failed to create object factory.
#[error("Failed to create object factory: {0}")]
FailedToCreateObjectFactory(windows::core::Error),
/// Failed to start job.
#[error("Failed to start job: {0}")]
FailedToStartJob(windows::core::Error),
/// Failed to apply print ticket.
#[error("Failed to apply print ticket: {0}")]
FailedToApplyPrintTicket(windows::core::Error),
/// Failed to write document.
#[error("Failed to write document: {0}")]
FailedToWriteDocument(windows::core::Error),
/// Stream is not available.
#[error("Stream is not available")]
StreamNotAvailable,
}
/// A printer to print XPS documents.
pub struct XpsPrinter {
printer: PrinterDevice,
}
impl XpsPrinter {
/// Create a new [`XpsPrinter`] for the given printer device.
pub fn new(printer: PrinterDevice) -> Self {
Self { printer }
}
+1
View File
@@ -1,2 +1,3 @@
#![cfg(feature = "test-utils")]
/// Provides a auto-managed null printer device for testing.
pub mod null_device;
+2
View File
@@ -74,6 +74,8 @@ impl Drop for NullPrinterDevice {
}
}
/// Get a thread-local null printer device.
/// This device is automatically managed and to be deleted when the thread ends.
pub fn thread_local() -> PrinterDevice {
NULL_DEVICE.with(|f| f.get_or_init(NullPrinterDevice::new).printer.clone())
}
+9
View File
@@ -15,24 +15,31 @@ use windows::{
},
};
/// Represents a print ticket builder, which can be used to build a print ticket.
pub struct PrintTicketBuilder {
xml: Vec<u8>,
provider: HPTPROVIDER,
}
#[derive(Error, Debug)]
/// Represents an error occurred while building a print ticket.
pub enum PrintTicketBuilderError {
/// Failed to open print ticket provider.
#[error("Failed to open print ticket provider: {0}")]
OpenProviderFailed(windows::core::Error),
/// Stream not allocated.
#[error("Stream not allocated")]
StreamNotAllocated,
/// Failed to merge print tickets.
#[error("Failed to merge print tickets: {0}")]
MergePrintTicketsFailed(String, windows::core::Error),
/// Failed to decode print ticket.
#[error("Failed to decode print ticket: {0}")]
DecodePrintTicketFailed(windows::core::Error),
}
impl PrintTicketBuilder {
/// Create a new print ticket builder for the given printer device.
pub fn new(device: &PrinterDevice) -> Result<Self, PrintTicketBuilderError> {
let provider = unsafe {
PTOpenProvider(PCWSTR(wchar::to_wide_chars(device.os_name()).as_ptr()), 1)
@@ -44,6 +51,7 @@ impl PrintTicketBuilder {
})
}
/// Merge the given print ticket into the current print ticket.
pub fn merge(&mut self, delta: impl Into<PrintTicket>) -> Result<(), PrintTicketBuilderError> {
unsafe {
let base = SHCreateMemStream(Some(self.xml.as_ref()))
@@ -73,6 +81,7 @@ impl PrintTicketBuilder {
Ok(())
}
/// Build the print ticket.
pub fn build(mut self) -> Result<PrintTicket, PrintTicketBuilderError> {
let xml = std::mem::take(&mut self.xml);
Ok(PrintTicket { xml })
+6
View File
@@ -1,12 +1,18 @@
mod print_schema;
/// Reader for parse XML bytes as Print Schema.
pub mod reader;
/// Writer for serialize Print Schema as XML bytes.
pub mod writer;
pub use print_schema::*;
/// The namespace URI for the Print Schema Framework.
pub const NS_PSF: &str =
"http://schemas.microsoft.com/windows/2003/08/printing/printschemaframework";
/// The namespace URI for the Print Schema Keywords.
pub const NS_PSK: &str =
"http://schemas.microsoft.com/windows/2003/08/printing/printschemakeywords";
/// The namespace URI for the XML Schema.
pub const NS_XSD: &str = "http://www.w3.org/2001/XMLSchema";
/// The namespace URI for the XML Schema Instance.
pub const NS_XSI: &str = "http://www.w3.org/2001/XMLSchema-instance";
+60
View File
@@ -2,83 +2,126 @@ use std::fmt;
use xml::name::OwnedName;
#[derive(Clone, PartialEq, Eq, Hash, fmt_derive::Debug)]
/// Represents a Print Schema document.
pub enum PrintSchemaDocument {
/// Documents that typed as [`PrintCapabilitiesDocument`].
PrintCapabilities(PrintCapabilitiesDocument),
/// Documents that typed as [`PrintTicketDocument`].
PrintTicket(PrintTicketDocument),
}
#[derive(Clone, PartialEq, Eq, Hash, fmt_derive::Debug)]
/// Represents a PrintCapabilities document.
pub struct PrintCapabilitiesDocument {
/// Properties of the document
pub properties: Vec<Property>,
/// Parameter definitions
pub parameter_defs: Vec<ParameterDef>,
/// Features
pub features: Vec<PrintFeature>,
}
#[derive(Clone, PartialEq, Eq, Hash, fmt_derive::Debug)]
/// Represents a PrintTicket document.
pub struct PrintTicketDocument {
/// Properties of the document
pub properties: Vec<Property>,
/// Parameter initializations
pub parameter_inits: Vec<ParameterInit>,
/// Features
pub features: Vec<PrintFeature>,
}
#[derive(Clone, PartialEq, Eq, Hash, fmt_derive::Debug)]
/// Represents a Print Feature.
pub struct PrintFeature {
/// The name of the feature.
#[fmt("{}", self.name)]
pub name: OwnedName,
/// Properties of the feature
pub properties: Vec<Property>,
/// Available options
pub options: Vec<PrintFeatureOption>,
/// Sub-features of the feature
pub features: Vec<PrintFeature>,
}
#[derive(Clone, PartialEq, Eq, Hash, fmt_derive::Debug)]
/// Represents a parameter initialization used in a [`PrintTicketDocument`].
pub struct ParameterInit {
/// The name of the parameter.
#[fmt("{}", self.name)]
pub name: OwnedName,
/// The value of the parameter.
#[fmt("{:?}", self.value)]
pub value: PropertyValue,
}
#[derive(Clone, PartialEq, Eq, Hash, fmt_derive::Debug)]
/// Represents a parameter definition used in a [`PrintCapabilitiesDocument`].
pub struct ParameterDef {
/// The name of the parameter.
#[fmt("{}", self.name)]
pub name: OwnedName,
/// Properties of the parameter
pub properties: Vec<Property>,
}
#[derive(Clone, PartialEq, Eq, Hash, fmt_derive::Debug)]
/// Represents a possible option for a [`PrintFeature`].
pub struct PrintFeatureOption {
/// The name of the option.
#[fmt("{}", self.name.as_ref().map(|x| x.to_string()).unwrap_or("<unnamed>".to_string()))]
pub name: Option<OwnedName>,
/// Scored-properties of the option
pub scored_properties: Vec<ScoredProperty>,
/// Properties of the option
pub properties: Vec<Property>,
}
#[derive(Clone, PartialEq, Eq, Hash, fmt_derive::Debug)]
/// Represents a scored-property.
/// A [`ScoredProperty`] declares a property that is intrinsic to an [Option](PrintFeatureOption).
/// Such properties should be compared when evaluating how closely a requested Option matches a device-supported Option.
pub struct ScoredProperty {
/// The name of the scored-property.
#[fmt("{}", self.name.as_ref().map(|x| x.to_string()).unwrap_or("<unnamed>".to_string()))]
pub name: Option<OwnedName>,
/// The parameter that this scored-property depends on.
#[fmt("{}", self.parameter_ref.as_ref().map(|x| x.to_string()).unwrap_or("<unnamed>".to_string()))]
pub parameter_ref: Option<OwnedName>,
/// The value of the scored-property.
#[fmt("{}", self.value.as_ref().map(|x| format!("{:?}", x)).unwrap_or("<none>".to_string()))]
pub value: Option<PropertyValue>,
/// Sub-scored-properties of the scored-property
pub scored_properties: Vec<ScoredProperty>,
/// Properties of the scored-property
pub properties: Vec<Property>,
}
#[derive(Clone, PartialEq, Eq, Hash, fmt_derive::Debug)]
/// Represents a property.
pub struct Property {
/// The name of the property.
#[fmt("{}", self.name)]
pub name: OwnedName,
/// The value of the property.
#[fmt("{}", self.value.as_ref().map(|x| format!("{:?}", x)).unwrap_or("<none>".to_string()))]
pub value: Option<PropertyValue>,
/// Sub-properties of the property
pub properties: Vec<Property>,
}
#[derive(Clone, PartialEq, Eq, Hash)]
/// Represents a property value or a scored-property value.
pub enum PropertyValue {
/// A string value.
String(String),
/// An integer value.
Integer(i32),
/// A qualified name value.
QName(OwnedName),
/// An unknown-typed value.
Unknown(OwnedName, String),
}
@@ -94,6 +137,7 @@ impl fmt::Debug for PropertyValue {
}
impl ParameterDef {
/// Get the default value of this parameter.
pub fn default_value(&self) -> Option<&PropertyValue> {
self.properties
.iter()
@@ -105,6 +149,7 @@ impl ParameterDef {
}
impl PropertyValue {
/// Get the `xsi:type` of this value.
pub fn xsi_type(&self) -> OwnedName {
match self {
PropertyValue::String(_) => OwnedName::qualified("string", super::NS_XSD, Some("xsd")),
@@ -115,18 +160,21 @@ impl PropertyValue {
PropertyValue::Unknown(n, _) => n.clone(),
}
}
/// Try as [`PropertyValue::String`] value.
pub fn string(&self) -> Option<&str> {
match self {
PropertyValue::String(s) => Some(s),
_ => None,
}
}
/// Try as [`PropertyValue::Integer`] value.
pub fn integer(&self) -> Option<i32> {
match self {
PropertyValue::Integer(i) => Some(*i),
_ => None,
}
}
/// Try as [`PropertyValue::QName`] value.
pub fn qualified_name(&self) -> Option<&OwnedName> {
match self {
PropertyValue::QName(q) => Some(q),
@@ -136,6 +184,7 @@ impl PropertyValue {
}
impl PrintFeatureOption {
/// Collect all parameters that this option depends on.
pub fn parameters_dependent(&self) -> Vec<OwnedName> {
let mut result = vec![];
for scored_property in &self.scored_properties {
@@ -146,6 +195,7 @@ impl PrintFeatureOption {
}
impl ScoredProperty {
/// Collect all parameters that this scored-property depends on.
pub fn parameters_dependent(&self) -> Vec<OwnedName> {
let mut result = vec![];
if let Some(ref parameter_ref) = self.parameter_ref {
@@ -156,6 +206,8 @@ impl ScoredProperty {
}
result
}
/// Get the value of this scored-property, or the value of the parameter it references.
pub fn value_with<'a>(&'a self, parameters: &'a [ParameterInit]) -> Option<&'a PropertyValue> {
if let Some(ref parameter_ref) = self.parameter_ref {
parameters
@@ -180,8 +232,12 @@ impl From<PrintCapabilitiesDocument> for PrintSchemaDocument {
}
}
/// A trait for types that have scored-properties.
pub trait WithScoredProperties {
/// Get the scored properties.
fn scored_properties(&self) -> &[ScoredProperty];
/// Get the scored-property with the given name and namespace.
fn get_scored_property(&self, name: &str, namespace: Option<&str>) -> Option<&ScoredProperty> {
self.scored_properties().iter().find(|x| {
x.name.as_ref().map_or(false, |x| {
@@ -203,8 +259,12 @@ impl WithScoredProperties for ScoredProperty {
}
}
/// A trait for types that have properties.
pub trait WithProperties {
/// Get the properties.
fn properties(&self) -> &[Property];
/// Get the property with the given name and namespace.
fn get_property(&self, name: &str, namespace: Option<&str>) -> Option<&Property> {
self.properties()
.iter()
+16 -1
View File
@@ -14,25 +14,40 @@ use xml::{
};
#[derive(Error, Debug)]
/// Represents an error occurred while parsing print schema.
pub enum ParsePrintSchemaError {
/// Invalid XML.
#[error("Invalid xml: {0}")]
InvalidXml(xml::reader::Error),
/// Invalid print schema.
#[error("Invalid print schema: (at {pos}) {reason}")]
InvalidPrintSchema { pos: TextPosition, reason: String },
InvalidPrintSchema {
/// Position in the document.
pos: TextPosition,
/// Reason of the error.
reason: String,
},
/// Wrong document type.
#[error("Wrong document type: expected {expected} but found {found}")]
WrongDocumentType {
/// Expected document type.
expected: &'static str,
/// Found document type.
found: &'static str,
},
}
/// Represents a root element which can be parsed from XML.
pub trait ParsableXmlDocument: Sized {
/// The error type that can be returned when parsing fails.
type Error;
/// Parse the XML document from the given XML reader.
fn parse<R>(reader: &mut EventReader<R>) -> Result<Self, Self::Error>
where
R: std::io::Read;
/// Parse the XML document from the given bytes.
fn parse_from_bytes(xml: impl AsRef<[u8]>) -> Result<Self, Self::Error> {
let mut reader = EventReader::new(Cursor::new(xml));
Self::parse(&mut reader)
+5
View File
@@ -9,12 +9,17 @@ use xml::{
impl XmlDocumentRoot for super::PrintCapabilitiesDocument {}
impl XmlDocumentRoot for super::PrintTicketDocument {}
/// A trait for serializing as XML element.
pub trait XmlSerializer {
/// Collect the namespace URIs and prefixes used in the XML element.
fn collect_namespace(&self, ns: &mut Namespace);
/// Write the XML element to the writer.
fn write_to<W: Write>(&self, writer: &mut EventWriter<W>) -> Result<(), xml::writer::Error>;
}
/// A trait for serializing as XML document.
pub trait XmlDocumentRoot: XmlSerializer {
/// Convert the struct to XML.
fn to_xml(&self) -> Vec<u8> {
let mut writer = EmitterConfig::new()
.perform_indent(false)
+8
View File
@@ -1,26 +1,34 @@
use std::fmt;
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
/// Represents a media size tuple.
pub struct MediaSizeTuple(u32, u32);
impl MediaSizeTuple {
/// Create a new media size tuple using width and height in micron.
pub const fn micron(width: u32, height: u32) -> Self {
MediaSizeTuple(width, height)
}
/// Create a new media size tuple using width and height in millimeter.
pub const fn mm(width: u32, height: u32) -> Self {
MediaSizeTuple(width * 1000, height * 1000)
}
/// Get the width in micron.
pub const fn width_in_micron(&self) -> u32 {
self.0
}
/// Get the height in micron.
pub const fn height_in_micron(&self) -> u32 {
self.1
}
/// Set the width in micron.
pub fn set_width_in_micron(&mut self, width: u32) {
self.0 = width;
}
/// Set the height in micron.
pub fn set_height_in_micron(&mut self, height: u32) {
self.1 = height;
}
/// Determine if the media is a roll media.
pub fn is_roll(&self) -> bool {
// for roll media, either width or height is 0
self.0 == 0 || self.1 == 0
+2
View File
@@ -1,4 +1,5 @@
mod builder;
/// Document object model representation of print schema.
pub mod document;
mod media_size_tuple;
mod page_media_size;
@@ -13,4 +14,5 @@ pub use predefined_media_name::*;
pub use print_capabilities::*;
pub use print_ticket::*;
/// The default print ticket XML.
pub const DEFAULT_PRINT_TICKET_XML: &str = r#"<psf:PrintTicket xmlns:psf="http://schemas.microsoft.com/windows/2003/08/printing/printschemaframework" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" version="1" xmlns:psk="http://schemas.microsoft.com/windows/2003/08/printing/printschemakeywords"></psf:PrintTicket>"#;
+7
View File
@@ -8,16 +8,21 @@ use super::{
use xml::name::OwnedName;
#[derive(Clone, Debug)]
/// Represents a page media size.
pub struct PageMediaSize {
/// The DOM of the option for media size.
pub option: PrintFeatureOption,
/// The parameters that is referenced by the option.
pub parameters: Vec<ParameterInit>,
}
impl PageMediaSize {
/// Create a new [`PageMediaSize`] from the given DOM.
pub fn new(option: PrintFeatureOption, parameters: Vec<ParameterInit>) -> Self {
Self { option, parameters }
}
/// Get the size of the media.
pub fn size(&self) -> MediaSizeTuple {
let width = self
.option
@@ -34,6 +39,8 @@ impl PageMediaSize {
MediaSizeTuple::micron(width as u32, height as u32)
}
/// Get the predefined name of the media.
/// If the media is not predefined, `None` is returned.
pub fn as_predefined_name(&self) -> Option<PredefinedMediaName> {
self.option
.name
+3
View File
@@ -4,6 +4,8 @@ use strum::EnumString;
use xml::name::OwnedName;
#[derive(EnumString, Debug, PartialEq, Eq, Hash, Clone, Copy)]
#[allow(missing_docs)]
/// Represents a predefined media name.
pub enum PredefinedMediaName {
BusinessCard,
CreditCard,
@@ -178,6 +180,7 @@ pub enum PredefinedMediaName {
}
impl PredefinedMediaName {
/// Get predefined media name from the given name.
pub fn from_name(name: &OwnedName) -> Option<Self> {
if name.namespace_ref() == Some(NS_PSK) {
Self::from_str(name.local_name.as_str()).ok()
+14
View File
@@ -24,25 +24,34 @@ use windows::{
use xml::name::OwnedName;
#[derive(Error, Debug)]
/// Represents an error occurred while fetching print capabilities.
pub enum FetchPrintCapabilitiesError {
/// Failed to open print ticket provider.
#[error("Failed to open print ticket provider: {0}")]
OpenProviderFailed(windows::core::Error),
/// Stream not allocated.
#[error("Stream not allocated")]
StreamNotAllocated,
/// Cannot get print capabilities.
#[error("Cannot get print capabilities: {0}")]
CannotGetPrintCapabilities(String, windows::core::Error),
/// Failed to read stream.
#[error("Failed to read stream: {0}")]
ReadStreamFailed(windows::core::Error),
/// Failed to parse print capabilities.
#[error("Failed to parse print capabilities: {0}")]
ParseError(ParsePrintSchemaError),
}
#[derive(Clone, Debug)]
/// Represents print capabilities.
pub struct PrintCapabilities {
/// DOM of print capabilities document.
pub document: PrintCapabilitiesDocument,
}
impl PrintCapabilities {
/// Fetch print capabilities XML (without parsing it) for the given printer device.
pub fn fetch_xml(device: &PrinterDevice) -> Result<Vec<u8>, FetchPrintCapabilitiesError> {
unsafe {
let provider =
@@ -71,6 +80,7 @@ impl PrintCapabilities {
}
}
/// Fetch and parse print capabilities for the given printer device.
pub fn fetch(device: &PrinterDevice) -> Result<PrintCapabilities, FetchPrintCapabilitiesError> {
let xml = Self::fetch_xml(device)?;
let document = PrintCapabilitiesDocument::parse_from_bytes(xml)
@@ -78,6 +88,7 @@ impl PrintCapabilities {
Ok(PrintCapabilities { document })
}
/// Defines all parameters with default values.
pub fn default_parameters(&self) -> impl Iterator<Item = ParameterInit> + '_ {
self.document.parameter_defs.iter().filter_map(|param_def| {
param_def
@@ -89,6 +100,7 @@ impl PrintCapabilities {
})
}
/// Defines the given parameters with default values.
pub fn default_parameters_for<'a>(
&'a self,
filters: &'a [OwnedName],
@@ -116,6 +128,7 @@ impl PrintCapabilities {
})
}
/// Get all options for the given feature.
pub fn options_for_feature<'a>(
&'a self,
feature_name: &'a str,
@@ -138,6 +151,7 @@ impl PrintCapabilities {
.collect()
}
/// Get all page media sizes.
pub fn page_media_size(&self) -> impl Iterator<Item = PageMediaSize> + '_ {
self.options_for_feature("PageMediaSize", Some(NS_PSK))
.map(|option| {
+15
View File
@@ -25,6 +25,7 @@ use windows::{
};
#[derive(Clone, Debug)]
/// Represents a print ticket.
pub struct PrintTicket {
pub(crate) xml: Vec<u8>,
}
@@ -38,32 +39,46 @@ impl Default for PrintTicket {
}
#[derive(Error, Debug)]
/// Represents an error occurred while converting print ticket to dev mode.
pub enum ToDevModeError {
/// Failed to open print ticket provider.
#[error("Failed to open print ticket provider: {0}")]
OpenProviderFailed(windows::core::Error),
/// Stream not allocated.
#[error("Stream not allocated")]
StreamNotAllocated,
/// Failed to convert print ticket to dev mode.
#[error("Failed to convert print ticket to dev mode: {0}")]
ConvertPrintTicketToDevModeFailed(String, windows::core::Error),
/// Failed to open printer.
#[error("Failed to open printer: {0}")]
FailedToOpenPrinter(windows::core::Error),
/// Failed to correct dev mode via [`DocumentProperties`].
///
/// [`DocumentProperties`]: https://learn.microsoft.com/en-us/windows/win32/printdocs/documentproperties
#[error("Failed to correct dev mode via DocumentProperties")]
FailedToCorrectDevMode,
}
impl PrintTicket {
/// Create a new print ticket from the given XML.
pub fn from_xml(xml: impl Into<Vec<u8>>) -> Self {
Self { xml: xml.into() }
}
/// Into the XML of the print ticket.
pub fn into_xml(self) -> Vec<u8> {
self.xml
}
/// Get the XML of the print ticket.
pub fn get_xml(&self) -> &[u8] {
&self.xml
}
/// Convert the print ticket to [`DEVMODE`] data.
///
/// [`DEVMODE`]: https://learn.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-devmodew
pub fn to_dev_mode(&self, device: &PrinterDevice) -> Result<Vec<u8>, ToDevModeError> {
unsafe {
let provider =