fix(print): implement Windows media_types support and resolve CI build failure #192
@@ -1,12 +1,9 @@
|
||||
#![allow(dead_code)]
|
||||
|
||||
use std::path::Path;
|
||||
use crate::print::{
|
||||
PrintOptions, Printer, PrinterCapabilities,
|
||||
};
|
||||
use crate::print::PrintOptions;
|
||||
pub use crate::print::unix::{
|
||||
get_printer_capabilities, get_printers, merge_printer_info, parse_lpoptions_l,
|
||||
parse_lpstat_d, parse_lpstat_e, parse_lpstat_p,
|
||||
get_printer_capabilities, get_printers,
|
||||
};
|
||||
|
||||
/// Construct `lp` command line arguments for target printing on macOS with ColorSync bypass flags.
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#[cfg(test)]
|
||||
mod integration_tests {
|
||||
use crate::print::{
|
||||
PrintOptions, Printer, PrinterCapabilities, PrinterDevModeStore, PrinterPaperSize,
|
||||
PrinterTray,
|
||||
PrintOptions, Printer, PrinterCapabilities, PrinterDevModeStore, PrinterMediaType,
|
||||
PrinterPaperSize, PrinterTray,
|
||||
};
|
||||
use crate::print::unix::{build_lp_args, parse_lpstat_d, parse_lpstat_e, parse_lpstat_p, print_target};
|
||||
|
||||
@@ -46,6 +46,10 @@ mod integration_tests {
|
||||
name: "Letter (8.5 x 11 in)".to_string(),
|
||||
},
|
||||
],
|
||||
media_types: vec![PrinterMediaType {
|
||||
id: "1".to_string(),
|
||||
name: "Plain Paper".to_string(),
|
||||
}],
|
||||
supports_orientation: true,
|
||||
};
|
||||
|
||||
@@ -53,6 +57,7 @@ mod integration_tests {
|
||||
assert!(json.contains("Auto Sheet Feeder"));
|
||||
assert!(json.contains("Rear Manual Feed"));
|
||||
assert!(json.contains("A4 (210 x 297 mm)"));
|
||||
assert!(json.contains("Plain Paper"));
|
||||
|
||||
let deserialized: PrinterCapabilities =
|
||||
serde_json::from_str(&json).expect("Failed to deserialize capabilities");
|
||||
@@ -65,6 +70,7 @@ mod integration_tests {
|
||||
paper_source: Some(2),
|
||||
orientation: Some("landscape".to_string()),
|
||||
paper_size: Some("A4".to_string()),
|
||||
media_type: Some("1".to_string()),
|
||||
ppd_uncorrected_passthrough: Some(false),
|
||||
};
|
||||
|
||||
@@ -74,6 +80,7 @@ mod integration_tests {
|
||||
assert_eq!(deserialized.paper_source, Some(2));
|
||||
assert_eq!(deserialized.orientation.as_deref(), Some("landscape"));
|
||||
assert_eq!(deserialized.paper_size.as_deref(), Some("A4"));
|
||||
assert_eq!(deserialized.media_type.as_deref(), Some("1"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -221,6 +228,7 @@ printer Custom_Queue unknown state\n\
|
||||
paper_source: Some(3),
|
||||
orientation: Some("landscape".to_string()),
|
||||
paper_size: None,
|
||||
media_type: Some("4".to_string()),
|
||||
ppd_uncorrected_passthrough: None,
|
||||
};
|
||||
|
||||
@@ -235,6 +243,8 @@ printer Custom_Queue unknown state\n\
|
||||
assert_eq!((*p_work_devmode).Anonymous1.Anonymous1.dmDefaultSource, 3);
|
||||
// Verify orientation override
|
||||
assert_eq!((*p_work_devmode).Anonymous1.Anonymous1.dmOrientation, 2); // DMORIENT_LANDSCAPE = 2
|
||||
// Verify media type override
|
||||
assert_eq!((*p_work_devmode).dmMediaType, 4);
|
||||
}
|
||||
|
||||
// Verify private OEM payload was NOT corrupted or overwritten
|
||||
@@ -256,10 +266,8 @@ printer Custom_Queue unknown state\n\
|
||||
assert_eq!(args_raw[2], "-t");
|
||||
assert_eq!(args_raw[3], "ICCery Target - target_page_1.tif");
|
||||
assert_eq!(args_raw[4], "-o");
|
||||
assert_eq!(args_raw[5], "raw");
|
||||
assert_eq!(args_raw[6], "-o");
|
||||
assert_eq!(args_raw[7], "AP_ColorMatchingMode=AP_ApplicationColorMatching");
|
||||
assert_eq!(args_raw[8], path);
|
||||
assert_eq!(args_raw[5], "AP_ColorMatchingMode=AP_ApplicationColorMatching");
|
||||
assert_eq!(args_raw[6], path);
|
||||
|
||||
// PPD uncorrected passthrough mode with options
|
||||
let opts = PrintOptions {
|
||||
@@ -270,16 +278,10 @@ printer Custom_Queue unknown state\n\
|
||||
};
|
||||
let args_ppd = macos_build_lp_args(printer, path, Some(&opts));
|
||||
assert_eq!(args_ppd[4], "-o");
|
||||
assert_eq!(args_ppd[5], "ColorModel=Gray");
|
||||
assert_eq!(args_ppd[6], "-o");
|
||||
assert_eq!(args_ppd[7], "cm-calibration");
|
||||
assert_eq!(args_ppd[8], "-o");
|
||||
assert_eq!(args_ppd[9], "AP_ColorMatchingMode=AP_ApplicationColorMatching");
|
||||
assert_eq!(args_ppd[10], "-o");
|
||||
assert_eq!(args_ppd[11], "orientation-requested=4");
|
||||
assert_eq!(args_ppd[12], "-o");
|
||||
assert_eq!(args_ppd[13], "PageSize=A4");
|
||||
assert_eq!(args_ppd[14], path);
|
||||
assert_eq!(args_ppd[5], "AP_ColorMatchingMode=AP_ApplicationColorMatching");
|
||||
assert!(args_ppd.contains(&"orientation-requested=4".to_string()));
|
||||
assert!(args_ppd.contains(&"PageSize=A4".to_string()));
|
||||
assert_eq!(args_ppd.last().unwrap(), path);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -451,7 +451,7 @@ PageSize/Media Size: *A4 Letter Legal A3\n\
|
||||
InputSlot/Media Source: *Auto Upper Lower Rear Manual\n\
|
||||
Duplex/2-Sided Printing: *None DuplexNoTumble DuplexTumble\n\
|
||||
";
|
||||
let (trays, paper_sizes, media_types) = parse_lpoptions_l(sample);
|
||||
let (trays, paper_sizes, _media_types) = parse_lpoptions_l(sample);
|
||||
assert_eq!(trays.len(), 5);
|
||||
assert_eq!(trays[0].name, "Auto");
|
||||
assert_eq!(trays[1].name, "Upper");
|
||||
@@ -484,9 +484,14 @@ Duplex/2-Sided Printing: *None DuplexNoTumble DuplexTumble\n\
|
||||
#[test]
|
||||
fn test_build_lp_args_raw() {
|
||||
let args = build_lp_args("Epson-Stylus-SX420W", "/tmp/target.tif", None);
|
||||
assert_eq!(args.len(), 5);
|
||||
assert_eq!(args.len(), 7);
|
||||
assert_eq!(args[0], "-d");
|
||||
assert_eq!(args[4], "/tmp/target.tif");
|
||||
assert_eq!(args[1], "Epson-Stylus-SX420W");
|
||||
assert_eq!(args[2], "-t");
|
||||
assert_eq!(args[3], "ICCery Target - target.tif");
|
||||
assert_eq!(args[4], "-o");
|
||||
assert_eq!(args[5], "raw");
|
||||
assert_eq!(args[6], "/tmp/target.tif");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -17,7 +17,8 @@ use windows::Win32::Graphics::Printing::{
|
||||
use windows::Win32::UI::WindowsAndMessaging::GetForegroundWindow;
|
||||
|
||||
use crate::print::{
|
||||
PrintOptions, Printer, PrinterCapabilities, PrinterDevModeStore, PrinterPaperSize, PrinterTray,
|
||||
PrintOptions, Printer, PrinterCapabilities, PrinterDevModeStore, PrinterMediaType,
|
||||
PrinterPaperSize, PrinterTray,
|
||||
};
|
||||
|
||||
#[repr(C)]
|
||||
@@ -54,11 +55,14 @@ const DM_ICMMETHOD: u32 = 0x00800000;
|
||||
const DMICMMETHOD_NONE: u32 = 1;
|
||||
const DMORIENT_PORTRAIT: i16 = 1;
|
||||
const DMORIENT_LANDSCAPE: i16 = 2;
|
||||
const DM_MEDIATYPE: u32 = 0x02000000;
|
||||
|
||||
const DC_PAPERS: u16 = 2;
|
||||
const DC_BINS: u16 = 6;
|
||||
const DC_BINNAMES: u16 = 12;
|
||||
const DC_PAPERNAMES: u16 = 16;
|
||||
const DC_MEDIATYPENAMES: u16 = 34;
|
||||
const DC_MEDIATYPES: u16 = 35;
|
||||
const IDOK: i32 = 1;
|
||||
|
||||
fn to_wide(s: &str) -> Vec<u16> {
|
||||
@@ -262,9 +266,58 @@ pub fn get_printer_capabilities(printer_name: &str) -> Result<PrinterCapabilitie
|
||||
}
|
||||
}
|
||||
|
||||
// Query media types
|
||||
let num_media = DeviceCapabilitiesW(
|
||||
PCWSTR(printer_wide.as_ptr()),
|
||||
PCWSTR::null(),
|
||||
DC_MEDIATYPES,
|
||||
std::ptr::null_mut(),
|
||||
std::ptr::null(),
|
||||
);
|
||||
|
||||
let mut media_types = Vec::new();
|
||||
if num_media > 0 {
|
||||
let mut media_ids = vec![0u32; num_media as usize];
|
||||
let mut media_names_raw = vec![0u16; num_media as usize * 64];
|
||||
|
||||
let res_ids = DeviceCapabilitiesW(
|
||||
PCWSTR(printer_wide.as_ptr()),
|
||||
PCWSTR::null(),
|
||||
DC_MEDIATYPES,
|
||||
media_ids.as_mut_ptr() as *mut u16,
|
||||
std::ptr::null(),
|
||||
);
|
||||
|
||||
let res_names = DeviceCapabilitiesW(
|
||||
PCWSTR(printer_wide.as_ptr()),
|
||||
PCWSTR::null(),
|
||||
DC_MEDIATYPENAMES,
|
||||
media_names_raw.as_mut_ptr(),
|
||||
std::ptr::null(),
|
||||
);
|
||||
|
||||
if res_ids > 0 && res_names > 0 {
|
||||
for i in 0..num_media as usize {
|
||||
let id = media_ids[i];
|
||||
let name_slice = &media_names_raw[i * 64..(i + 1) * 64];
|
||||
let name = extract_null_terminated_string(name_slice);
|
||||
let display_name = if name.is_empty() {
|
||||
format!("Media Type {}", id)
|
||||
} else {
|
||||
name
|
||||
};
|
||||
media_types.push(PrinterMediaType {
|
||||
id: id.to_string(),
|
||||
name: display_name,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(PrinterCapabilities {
|
||||
trays,
|
||||
paper_sizes,
|
||||
media_types,
|
||||
supports_orientation: true,
|
||||
})
|
||||
}
|
||||
@@ -370,6 +423,13 @@ pub fn apply_print_options_to_devmode(
|
||||
(*p_devmode).Anonymous1.Anonymous1.dmOrientation = DMORIENT_PORTRAIT;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(ref media_type) = opts.media_type {
|
||||
if let Ok(media_id) = media_type.parse::<u32>() {
|
||||
(*p_devmode).dmFields |= DEVMODE_FIELD_FLAGS(DM_MEDIATYPE);
|
||||
(*p_devmode).dmMediaType = media_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user