Skip to main content

winprint_ext/ticket/
page_imageable_size.rs

1use super::{
2    document::{
3        reader::{ParsableXmlDocument, ParsePrintSchemaError},
4        PrintCapabilitiesDocument, WithProperties, NS_PSK,
5    },
6    FetchPrintCapabilitiesError, MediaSizeTuple, PageMediaSize, PrintCapabilities, PrintTicket,
7};
8use crate::printer::PrinterDevice;
9use thiserror::Error;
10
11#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
12/// Represents a media size tuple.
13pub struct PageImageableSize {
14    /// The size of the page.
15    pub size: MediaSizeTuple,
16    /// The origin of the imageable area.
17    pub origin: MediaSizeTuple,
18    /// The extent of the imageable area.
19    pub extent: MediaSizeTuple,
20}
21
22#[derive(Error, Debug)]
23/// Represents an error occurred while fetching imageable size.
24pub enum PageImageableSizeError {
25    /// Failed to fetch print capabilities.
26    #[error("Failed to fetch print capabilities")]
27    FetchCapabilitiesError(#[from] FetchPrintCapabilitiesError),
28    /// Failed to parse print capabilities.
29    #[error("Failed to parse print capabilities")]
30    ParseCapabilitiesError(#[from] ParsePrintSchemaError),
31    /// Field error.
32    #[error("Field error: {field}")]
33    FieldError {
34        /// Indicates the field name.
35        field: &'static str,
36    },
37}
38
39fn get_u32_property(
40    properties: &impl WithProperties,
41    name: &'static str,
42    ns: Option<&str>,
43) -> Result<u32, PageImageableSizeError> {
44    properties
45        .get_property(name, ns)
46        .and_then(|x| x.value.as_ref())
47        .and_then(|x| x.integer())
48        .and_then(|x| u32::try_from(x).ok())
49        .ok_or(PageImageableSizeError::FieldError { field: name })
50}
51
52impl PageImageableSize {
53    /// Try to fetch the imageable size for the given printer device and media size.
54    pub fn try_fetch(
55        device: &PrinterDevice,
56        media: PageMediaSize,
57    ) -> Result<Self, PageImageableSizeError> {
58        let ticket = PrintTicket::from(media);
59        let caps_xml = PrintCapabilities::fetch_xml_for_ticket(device, Some(&ticket))?;
60        let caps = PrintCapabilitiesDocument::parse_from_bytes(&caps_xml)?;
61        let imageable_size = caps.get_property("PageImageableSize", Some(NS_PSK)).ok_or(
62            PageImageableSizeError::FieldError {
63                field: "PageImageableSize",
64            },
65        )?;
66        let size_w = get_u32_property(imageable_size, "ImageableSizeWidth", Some(NS_PSK))?;
67        let size_h = get_u32_property(imageable_size, "ImageableSizeHeight", Some(NS_PSK))?;
68        let imageable_area = imageable_size
69            .get_property("ImageableArea", Some(NS_PSK))
70            .ok_or(PageImageableSizeError::FieldError {
71                field: "ImageableArea",
72            })?;
73        let origin_w = get_u32_property(imageable_area, "OriginWidth", Some(NS_PSK))?;
74        let origin_h = get_u32_property(imageable_area, "OriginHeight", Some(NS_PSK))?;
75        let extent_w = get_u32_property(imageable_area, "ExtentWidth", Some(NS_PSK))?;
76        let extent_h = get_u32_property(imageable_area, "ExtentHeight", Some(NS_PSK))?;
77        Ok(Self {
78            size: MediaSizeTuple::micron(size_w, size_h),
79            origin: MediaSizeTuple::micron(origin_w, origin_h),
80            extent: MediaSizeTuple::micron(extent_w, extent_h),
81        })
82    }
83}
84
85#[cfg(test)]
86mod tests {
87    use super::PageImageableSize;
88    use crate::{test_utils::null_device, ticket::PrintCapabilities};
89
90    #[test]
91    fn get_imageable_size() {
92        let device = null_device::thread_local();
93        let capabilities = PrintCapabilities::fetch(&device).unwrap();
94        for media in capabilities.page_media_sizes() {
95            let imageable_size = PageImageableSize::try_fetch(&device, media);
96            assert!(imageable_size.is_ok());
97            println!("{:#?}", imageable_size);
98        }
99    }
100}