ipp_printer_app/raster.rs
1//! [`RasterDriver`] trait + page-header DTO for raster print jobs.
2
3/// Failure of a print job, carrying IPP-visible printer reasons + a message.
4#[derive(Debug, Clone)]
5pub struct JobFailure {
6 /// Reasons OR'd into the printer's `printer-state-reasons` IPP attribute
7 /// when this job aborts.
8 pub printer_reasons: crate::flags::PrinterReason,
9 /// Human-readable message surfaced as `job-state-message`.
10 pub message: String,
11}
12
13impl JobFailure {
14 /// Build a failure with explicit `printer-state-reasons`.
15 pub fn new(
16 printer_reasons: crate::flags::PrinterReason,
17 message: impl Into<String>,
18 ) -> Self {
19 Self {
20 printer_reasons,
21 message: message.into(),
22 }
23 }
24
25 /// Shorthand for a generic failure (`PrinterReason::OTHER`).
26 pub fn other(message: impl Into<String>) -> Self {
27 Self::new(crate::flags::PrinterReason::OTHER, message)
28 }
29}
30
31impl std::fmt::Display for JobFailure {
32 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
33 write!(f, "{}", self.message)
34 }
35}
36
37impl std::error::Error for JobFailure {}
38
39/// Page geometry parsed from a CUPS/PWG raster page header.
40#[derive(Debug, Clone)]
41pub struct JobOptions {
42 /// Page width in pixels.
43 pub width: u32,
44 /// Page height in pixels.
45 pub height: u32,
46 /// Bits per pixel (typically 1 for monochrome, 8 for grayscale, 24 for RGB).
47 pub bits_per_pixel: u32,
48 /// Bytes per scanline (already pre-padded by the raster source).
49 pub bytes_per_line: u32,
50 /// Number of copies requested. Always ≥ 1.
51 pub copies: u32,
52}
53
54impl JobOptions {
55 /// Construct from a CUPS raster v1 page header. `num_copies < 1` is
56 /// clamped to 1 per IPP convention.
57 pub fn from_cups_v1(
58 width: u32,
59 height: u32,
60 bits_per_pixel: u32,
61 bytes_per_line: u32,
62 num_copies: u32,
63 ) -> Self {
64 Self {
65 width,
66 height,
67 bits_per_pixel,
68 bytes_per_line,
69 copies: num_copies.max(1),
70 }
71 }
72}
73
74/// Driver that turns a stream of raster scanlines into device bytes.
75///
76/// Implementations are *per-job stateful* — `start_job` returns a fresh
77/// value that owns the page buffer, `write_line` accumulates scanlines,
78/// `end_page` transfers the page to the device, `end_job` releases
79/// resources. The framework's IPP `Print-Job` handler drives this trait;
80/// you only need to provide a type that knows how to talk to your device.
81pub trait RasterDriver: Sized + Send + 'static {
82 /// The driver's opaque device handle (e.g. an open HID descriptor).
83 type Device: Send;
84
85 /// Allocate per-job state. Called once at the top of each job.
86 fn start_job(
87 printer: &crate::printer::PrinterHandle<'_>,
88 options: &JobOptions,
89 device: &Self::Device,
90 ) -> Result<Self, JobFailure>;
91
92 /// Called once per page before any `write_line`. Default: no-op.
93 fn start_page(
94 &mut self,
95 _options: &JobOptions,
96 _page: u32,
97 _device: &Self::Device,
98 ) -> Result<(), JobFailure> {
99 Ok(())
100 }
101
102 /// Append one scanline to the page buffer.
103 fn write_line(
104 &mut self,
105 options: &JobOptions,
106 y: u32,
107 line: &[u8],
108 ) -> Result<(), JobFailure>;
109
110 /// Transfer the completed page to the device (and repeat for copies).
111 fn end_page(
112 &mut self,
113 options: &JobOptions,
114 page: u32,
115 device: &Self::Device,
116 ) -> Result<(), JobFailure>;
117
118 /// Release per-job state. Called once at the end of the job.
119 fn end_job(self, device: &Self::Device);
120}