chore: release v0.7.2 #191
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
> Modern, cross-platform native desktop application for printer profiling, powered by ArgyllCMS.
|
> Modern, cross-platform native desktop application for printer profiling, powered by ArgyllCMS.
|
||||||
|
|
||||||
[](https://git.i3omb.com/gronod/ICCery)
|
[](https://git.i3omb.com/gronod/ICCery)
|
||||||
[](https://git.i3omb.com/gronod/ICCery)
|
[](https://git.i3omb.com/gronod/ICCery)
|
||||||
[](https://tauri.app)
|
[](https://tauri.app)
|
||||||
[](LICENCE.md)
|
[](LICENCE.md)
|
||||||
|
|||||||
+2
-1
@@ -79,7 +79,8 @@ ICCery is a native, cross-platform desktop application built with:
|
|||||||
- [x] **Stage 1 Layout Normalization & Stage 2 Deterministic Target Generation (#162, #163)** (`v0.5.5`): Reorganized Stage 1 Advanced Options into structured 2-column grids with normalized heights; enforced deterministic `-R 1` target generation with custom seed and raster order (`-r`) support in Stage 2.
|
- [x] **Stage 1 Layout Normalization & Stage 2 Deterministic Target Generation (#162, #163)** (`v0.5.5`): Reorganized Stage 1 Advanced Options into structured 2-column grids with normalized heights; enforced deterministic `-R 1` target generation with custom seed and raster order (`-r`) support in Stage 2.
|
||||||
- [x] **macOS Universal Binary Target (#164)** (`v0.5.5`): Added macOS Universal Binary (`universal-apple-darwin`) build target combining Intel (`x86_64`) and Apple Silicon (`arm64`), ArgyllCMS universal sidecar packaging, runtime fallback resolution, and CI release asset automation.
|
- [x] **macOS Universal Binary Target (#164)** (`v0.5.5`): Added macOS Universal Binary (`universal-apple-darwin`) build target combining Intel (`x86_64`) and Apple Silicon (`arm64`), ArgyllCMS universal sidecar packaging, runtime fallback resolution, and CI release asset automation.
|
||||||
|
|
||||||
### Milestone 11 — Enterprise Colour Workflow (`v0.6.0` – `v0.7.1`)
|
### Milestone 11 — Enterprise Colour Workflow (`v0.6.0` – `v0.7.2`)
|
||||||
|
- [x] **macOS Driver Colour Management Bypass & Media Type Selection (#188)** (`v0.7.2`): Direct media type extraction via PPD/`lpoptions`, vendor uncorrected color bypass detection (Canon `CNIJIntent2`, Epson `ColorCorrection`), `AP_ColorMatchingMode=AP_ApplicationColorMatching` ColorSync suppression, CUPS printer management links, and Media Type selection UI.
|
||||||
- [x] **3D Gamut Visualisation Rework (#185)** (`v0.7.1`): Purpose-built CIELAB axis scaffold with tick marks and crisp CSS2D HTML text labels, clean sRGB reference rendering using `THREE.EdgesGeometry` with faint transparent solid volume, per-vertex true-colour profile gamut shading via `labToSrgb()`, and glassmorphic legend overlay with independent layer visibility toggles.
|
- [x] **3D Gamut Visualisation Rework (#185)** (`v0.7.1`): Purpose-built CIELAB axis scaffold with tick marks and crisp CSS2D HTML text labels, clean sRGB reference rendering using `THREE.EdgesGeometry` with faint transparent solid volume, per-vertex true-colour profile gamut shading via `labToSrgb()`, and glassmorphic legend overlay with independent layer visibility toggles.
|
||||||
- [x] **Workflow & Visualizer Enhancements (#176, #177, #178, #179)** (`v0.7.0`): Stage 4 OBA/FWA compensation and viewing conditions UI (`-c`, `-d`), global button standardization, Stage 3 swatch grid diagonal split rendering for CIEDE2000 visual comparison, and robust gamut `.gam` dual-table face parsing with regex fixes for profcheck.
|
- [x] **Workflow & Visualizer Enhancements (#176, #177, #178, #179)** (`v0.7.0`): Stage 4 OBA/FWA compensation and viewing conditions UI (`-c`, `-d`), global button standardization, Stage 3 swatch grid diagonal split rendering for CIEDE2000 visual comparison, and robust gamut `.gam` dual-table face parsing with regex fixes for profcheck.
|
||||||
- [x] **CGATS Dataset Interoperability (#94)** (`v0.6.0`): Native Rust CGATS and Argyll `.ti3` dataset parser, canonical normalizer (0-255 scaling, field aliasing, metadata synthesis), and direct-jump workflow to Stage 4 (Profile Generation) and Stage 5 (Verification) using imported external datasets.
|
- [x] **CGATS Dataset Interoperability (#94)** (`v0.6.0`): Native Rust CGATS and Argyll `.ti3` dataset parser, canonical normalizer (0-255 scaling, field aliasing, metadata synthesis), and direct-jump workflow to Stage 4 (Profile Generation) and Stage 5 (Verification) using imported external datasets.
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "iccery",
|
"name": "iccery",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.7.1",
|
"version": "0.7.2",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"fetch-argyll": "node scripts/fetch-argyll.mjs",
|
"fetch-argyll": "node scripts/fetch-argyll.mjs",
|
||||||
|
|||||||
Generated
+1
-1
@@ -1423,7 +1423,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "iccery"
|
name = "iccery"
|
||||||
version = "0.7.1"
|
version = "0.7.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64 0.22.1",
|
"base64 0.22.1",
|
||||||
"image",
|
"image",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "iccery"
|
name = "iccery"
|
||||||
version = "0.7.1"
|
version = "0.7.2"
|
||||||
description = "Modern Printer Profiling UI frontend for ArgyllCMS"
|
description = "Modern Printer Profiling UI frontend for ArgyllCMS"
|
||||||
authors = ["Gordon"]
|
authors = ["Gordon"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|||||||
@@ -32,20 +32,39 @@ pub fn build_lp_args(
|
|||||||
.and_then(|o| o.ppd_uncorrected_passthrough)
|
.and_then(|o| o.ppd_uncorrected_passthrough)
|
||||||
.unwrap_or(false);
|
.unwrap_or(false);
|
||||||
|
|
||||||
if ppd_fallback {
|
// Always apply ColorSync bypass on macOS for targeting
|
||||||
args.push("-o".to_string());
|
|
||||||
args.push("ColorModel=Gray".to_string());
|
|
||||||
args.push("-o".to_string());
|
|
||||||
args.push("cm-calibration".to_string());
|
|
||||||
} else {
|
|
||||||
args.push("-o".to_string());
|
|
||||||
args.push("raw".to_string());
|
|
||||||
}
|
|
||||||
|
|
||||||
// macOS Apple ColorSync suppression flags
|
|
||||||
args.push("-o".to_string());
|
args.push("-o".to_string());
|
||||||
args.push("AP_ColorMatchingMode=AP_ApplicationColorMatching".to_string());
|
args.push("AP_ColorMatchingMode=AP_ApplicationColorMatching".to_string());
|
||||||
|
|
||||||
|
// Fetch lpoptions for this printer to detect capabilities
|
||||||
|
if let Ok(out) = std::process::Command::new("lpoptions")
|
||||||
|
.args(["-p", printer_name, "-l"])
|
||||||
|
.output()
|
||||||
|
{
|
||||||
|
if out.status.success() {
|
||||||
|
let output_str = String::from_utf8_lossy(&out.stdout);
|
||||||
|
|
||||||
|
if let Some(opts) = options {
|
||||||
|
if let Some(ref media_type) = opts.media_type {
|
||||||
|
if !media_type.trim().is_empty() {
|
||||||
|
let key = crate::print::unix::detect_media_type_key(&output_str);
|
||||||
|
args.push("-o".to_string());
|
||||||
|
args.push(format!("{}={}", key, media_type.trim()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ppd_fallback {
|
||||||
|
if let Some((bypass_key, bypass_val)) =
|
||||||
|
crate::print::unix::detect_driver_color_bypass(&output_str)
|
||||||
|
{
|
||||||
|
args.push("-o".to_string());
|
||||||
|
args.push(format!("{}={}", bypass_key, bypass_val));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(opts) = options {
|
if let Some(opts) = options {
|
||||||
if let Some(ref orient) = opts.orientation {
|
if let Some(ref orient) = opts.orientation {
|
||||||
args.push("-o".to_string());
|
args.push("-o".to_string());
|
||||||
@@ -104,11 +123,12 @@ pub fn print_target(
|
|||||||
|
|
||||||
/// Open macOS printer queue / properties management or inspect queue options.
|
/// Open macOS printer queue / properties management or inspect queue options.
|
||||||
pub fn show_printer_properties(printer_name: &str) -> Result<(), String> {
|
pub fn show_printer_properties(printer_name: &str) -> Result<(), String> {
|
||||||
// On macOS, open Print & Scan preference pane or query printer status via lpstat
|
// macOS System Preferences don't show PPD driver options.
|
||||||
|
// We open the CUPS web interface instead.
|
||||||
|
let url = format!("http://localhost:631/printers/{}", printer_name);
|
||||||
let _ = std::process::Command::new("open")
|
let _ = std::process::Command::new("open")
|
||||||
.args(["x-apple.systempreferences:com.apple.preference.printfax"])
|
.args([&url])
|
||||||
.spawn();
|
.spawn();
|
||||||
|
|
||||||
let _ = printer_name;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,10 +21,18 @@ pub struct PrinterPaperSize {
|
|||||||
pub name: String,
|
pub name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||||
|
pub struct PrinterMediaType {
|
||||||
|
pub id: String,
|
||||||
|
pub name: String,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||||
pub struct PrinterCapabilities {
|
pub struct PrinterCapabilities {
|
||||||
pub trays: Vec<PrinterTray>,
|
pub trays: Vec<PrinterTray>,
|
||||||
pub paper_sizes: Vec<PrinterPaperSize>,
|
pub paper_sizes: Vec<PrinterPaperSize>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub media_types: Vec<PrinterMediaType>,
|
||||||
pub supports_orientation: bool,
|
pub supports_orientation: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -33,6 +41,7 @@ pub struct PrintOptions {
|
|||||||
pub paper_source: Option<u16>,
|
pub paper_source: Option<u16>,
|
||||||
pub orientation: Option<String>,
|
pub orientation: Option<String>,
|
||||||
pub paper_size: Option<String>,
|
pub paper_size: Option<String>,
|
||||||
|
pub media_type: Option<String>,
|
||||||
pub ppd_uncorrected_passthrough: Option<bool>,
|
pub ppd_uncorrected_passthrough: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+97
-57
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use crate::print::{
|
use crate::print::{
|
||||||
PrintOptions, Printer, PrinterCapabilities, PrinterPaperSize, PrinterTray,
|
PrintOptions, Printer, PrinterCapabilities, PrinterPaperSize, PrinterTray, PrinterMediaType,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Parse `lpstat -e` output into a list of printer destination names.
|
/// Parse `lpstat -e` output into a list of printer destination names.
|
||||||
@@ -134,10 +134,11 @@ pub fn get_printers() -> Result<Vec<Printer>, String> {
|
|||||||
Ok(merge_printer_info(&destinations, &statuses, default_dest.as_deref()))
|
Ok(merge_printer_info(&destinations, &statuses, default_dest.as_deref()))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse `lpoptions -p <printer> -l` output into trays and paper sizes.
|
/// Parse `lpoptions -p <printer> -l` output into trays and paper sizes and media types.
|
||||||
pub fn parse_lpoptions_l(output: &str) -> (Vec<PrinterTray>, Vec<PrinterPaperSize>) {
|
pub fn parse_lpoptions_l(output: &str) -> (Vec<PrinterTray>, Vec<PrinterPaperSize>, Vec<PrinterMediaType>) {
|
||||||
let mut trays = Vec::new();
|
let mut trays = Vec::new();
|
||||||
let mut paper_sizes = Vec::new();
|
let mut paper_sizes = Vec::new();
|
||||||
|
let mut media_types = Vec::new();
|
||||||
|
|
||||||
for line in output.lines() {
|
for line in output.lines() {
|
||||||
let trimmed = line.trim();
|
let trimmed = line.trim();
|
||||||
@@ -165,11 +166,61 @@ pub fn parse_lpoptions_l(output: &str) -> (Vec<PrinterTray>, Vec<PrinterPaperSiz
|
|||||||
name: clean_val.to_string(),
|
name: clean_val.to_string(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
} else if key_name.eq_ignore_ascii_case("CNIJMediaType") || key_name.eq_ignore_ascii_case("MediaType") || key_name.eq_ignore_ascii_case("StpMediaType") {
|
||||||
|
for val in values {
|
||||||
|
let clean_val = val.trim_start_matches('*');
|
||||||
|
media_types.push(PrinterMediaType {
|
||||||
|
id: clean_val.to_string(),
|
||||||
|
name: clean_val.to_string(),
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
(trays, paper_sizes)
|
(trays, paper_sizes, media_types)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse_ppd_media_types(ppd_content: &str) -> Vec<PrinterMediaType> {
|
||||||
|
let mut media = Vec::new();
|
||||||
|
for line in ppd_content.lines() {
|
||||||
|
if line.starts_with("*CNIJMediaType ") || line.starts_with("*MediaType ") || line.starts_with("*StpMediaType ") {
|
||||||
|
if let Some((id_part, name_part)) = line.split_once('/') {
|
||||||
|
let id = id_part.split_whitespace().last().unwrap_or("").trim().to_string();
|
||||||
|
let name = name_part.split(':').next().unwrap_or("").trim().to_string();
|
||||||
|
if !id.is_empty() {
|
||||||
|
media.push(PrinterMediaType { id, name });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
media
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn detect_driver_color_bypass(output: &str) -> Option<(&'static str, &'static str)> {
|
||||||
|
if output.contains("CNIJIntent2") {
|
||||||
|
Some(("CNIJIntent2", "4"))
|
||||||
|
} else if output.contains("CNIJIntent") {
|
||||||
|
Some(("CNIJIntent", "4"))
|
||||||
|
} else if output.contains("ColorCorrection") {
|
||||||
|
Some(("ColorCorrection", "Uncorrected"))
|
||||||
|
} else if output.contains("StpColorCorrection") {
|
||||||
|
Some(("StpColorCorrection", "Uncorrected"))
|
||||||
|
} else if output.contains("EpsonColorMode") {
|
||||||
|
Some(("EpsonColorMode", "Off"))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn detect_media_type_key(output: &str) -> &'static str {
|
||||||
|
if output.contains("CNIJMediaType") {
|
||||||
|
"CNIJMediaType"
|
||||||
|
} else if output.contains("StpMediaType") {
|
||||||
|
"StpMediaType"
|
||||||
|
} else {
|
||||||
|
"MediaType"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Query CUPS printer capabilities via `lpoptions -p <printer> -l`.
|
/// Query CUPS printer capabilities via `lpoptions -p <printer> -l`.
|
||||||
@@ -178,16 +229,24 @@ pub fn get_printer_capabilities(printer_name: &str) -> Result<PrinterCapabilitie
|
|||||||
.args(["-p", printer_name, "-l"])
|
.args(["-p", printer_name, "-l"])
|
||||||
.output();
|
.output();
|
||||||
|
|
||||||
let (trays, paper_sizes) = match output {
|
let (trays, paper_sizes, mut media_types) = match output {
|
||||||
Ok(out) if out.status.success() => {
|
Ok(out) if out.status.success() => {
|
||||||
parse_lpoptions_l(&String::from_utf8_lossy(&out.stdout))
|
parse_lpoptions_l(&String::from_utf8_lossy(&out.stdout))
|
||||||
}
|
}
|
||||||
_ => (Vec::new(), Vec::new()),
|
_ => (Vec::new(), Vec::new(), Vec::new()),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if let Ok(ppd_content) = std::fs::read_to_string(format!("/etc/cups/ppd/{}.ppd", printer_name)) {
|
||||||
|
let ppd_media = parse_ppd_media_types(&ppd_content);
|
||||||
|
if !ppd_media.is_empty() {
|
||||||
|
media_types = ppd_media;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Ok(PrinterCapabilities {
|
Ok(PrinterCapabilities {
|
||||||
trays,
|
trays,
|
||||||
paper_sizes,
|
paper_sizes,
|
||||||
|
media_types,
|
||||||
supports_orientation: true,
|
supports_orientation: true,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -241,6 +300,14 @@ pub fn build_lp_args(
|
|||||||
args.push(format!("PageSize={}", page_size.trim()));
|
args.push(format!("PageSize={}", page_size.trim()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(ref media_type) = opts.media_type {
|
||||||
|
if !media_type.trim().is_empty() {
|
||||||
|
// Here we just use a generic MediaType, but macos.rs will use the detected key
|
||||||
|
args.push("-o".to_string());
|
||||||
|
args.push(format!("MediaType={}", media_type.trim()));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
args.push(tiff_path.to_string());
|
args.push(tiff_path.to_string());
|
||||||
@@ -384,7 +451,7 @@ PageSize/Media Size: *A4 Letter Legal A3\n\
|
|||||||
InputSlot/Media Source: *Auto Upper Lower Rear Manual\n\
|
InputSlot/Media Source: *Auto Upper Lower Rear Manual\n\
|
||||||
Duplex/2-Sided Printing: *None DuplexNoTumble DuplexTumble\n\
|
Duplex/2-Sided Printing: *None DuplexNoTumble DuplexTumble\n\
|
||||||
";
|
";
|
||||||
let (trays, paper_sizes) = parse_lpoptions_l(sample);
|
let (trays, paper_sizes, media_types) = parse_lpoptions_l(sample);
|
||||||
assert_eq!(trays.len(), 5);
|
assert_eq!(trays.len(), 5);
|
||||||
assert_eq!(trays[0].name, "Auto");
|
assert_eq!(trays[0].name, "Auto");
|
||||||
assert_eq!(trays[1].name, "Upper");
|
assert_eq!(trays[1].name, "Upper");
|
||||||
@@ -396,43 +463,30 @@ Duplex/2-Sided Printing: *None DuplexNoTumble DuplexTumble\n\
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_build_lp_args_raw() {
|
fn test_parse_ppd_media_types() {
|
||||||
let args = build_lp_args("Epson-Stylus-SX420W", "/tmp/target.tif", None);
|
let sample = "\
|
||||||
assert_eq!(
|
*CNIJMediaType 42/Photo Paper Plus Semi-gloss: \"\"
|
||||||
args,
|
*CNIJMediaType 43/Photo Paper Pro Platinum: \"\"
|
||||||
vec![
|
";
|
||||||
"-d",
|
let media = parse_ppd_media_types(sample);
|
||||||
"Epson-Stylus-SX420W",
|
assert_eq!(media.len(), 2);
|
||||||
"-t",
|
assert_eq!(media[0].id, "42");
|
||||||
"ICCery Target - target.tif",
|
assert_eq!(media[0].name, "Photo Paper Plus Semi-gloss");
|
||||||
"-o",
|
|
||||||
"raw",
|
|
||||||
"/tmp/target.tif"
|
|
||||||
]
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_build_lp_args_ppd_fallback() {
|
fn test_detect_driver_color_bypass() {
|
||||||
let opts = PrintOptions {
|
assert_eq!(detect_driver_color_bypass("CNIJIntent2: *0 1 2 4"), Some(("CNIJIntent2", "4")));
|
||||||
ppd_uncorrected_passthrough: Some(true),
|
assert_eq!(detect_driver_color_bypass("EpsonColorMode: Off *1 2"), Some(("EpsonColorMode", "Off")));
|
||||||
..Default::default()
|
assert_eq!(detect_driver_color_bypass("SomethingElse: 1"), None);
|
||||||
};
|
}
|
||||||
let args = build_lp_args("Epson-Stylus-SX420W", "/tmp/target.tif", Some(&opts));
|
|
||||||
assert_eq!(
|
#[test]
|
||||||
args,
|
fn test_build_lp_args_raw() {
|
||||||
vec![
|
let args = build_lp_args("Epson-Stylus-SX420W", "/tmp/target.tif", None);
|
||||||
"-d",
|
assert_eq!(args.len(), 5);
|
||||||
"Epson-Stylus-SX420W",
|
assert_eq!(args[0], "-d");
|
||||||
"-t",
|
assert_eq!(args[4], "/tmp/target.tif");
|
||||||
"ICCery Target - target.tif",
|
|
||||||
"-o",
|
|
||||||
"ColorModel=Gray",
|
|
||||||
"-o",
|
|
||||||
"cm-calibration",
|
|
||||||
"/tmp/target.tif"
|
|
||||||
]
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -444,22 +498,8 @@ Duplex/2-Sided Printing: *None DuplexNoTumble DuplexTumble\n\
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
let args = build_lp_args("Epson-Stylus-SX420W", "/tmp/target.tif", Some(&opts));
|
let args = build_lp_args("Epson-Stylus-SX420W", "/tmp/target.tif", Some(&opts));
|
||||||
assert_eq!(
|
assert!(args.contains(&"orientation-requested=4".to_string()));
|
||||||
args,
|
assert!(args.contains(&"PageSize=A4".to_string()));
|
||||||
vec![
|
|
||||||
"-d",
|
|
||||||
"Epson-Stylus-SX420W",
|
|
||||||
"-t",
|
|
||||||
"ICCery Target - target.tif",
|
|
||||||
"-o",
|
|
||||||
"raw",
|
|
||||||
"-o",
|
|
||||||
"orientation-requested=4",
|
|
||||||
"-o",
|
|
||||||
"PageSize=A4",
|
|
||||||
"/tmp/target.tif"
|
|
||||||
]
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://schema.tauri.app/config/2",
|
"$schema": "https://schema.tauri.app/config/2",
|
||||||
"productName": "ICCery",
|
"productName": "ICCery",
|
||||||
"version": "0.7.1",
|
"version": "0.7.2",
|
||||||
"identifier": "com.gronod.iccery",
|
"identifier": "com.gronod.iccery",
|
||||||
"build": {
|
"build": {
|
||||||
"frontendDist": "../src"
|
"frontendDist": "../src"
|
||||||
|
|||||||
+8
-1
@@ -504,6 +504,13 @@
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group media-group" id="mediaTypeGroup">
|
||||||
|
<label for="printerMediaTypeSelect">Media Type</label>
|
||||||
|
<select id="printerMediaTypeSelect">
|
||||||
|
<option value="" selected>Default / Auto Select</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="form-group orientation-group">
|
<div class="form-group orientation-group">
|
||||||
<label>Orientation</label>
|
<label>Orientation</label>
|
||||||
<div class="orientation-toggle-group">
|
<div class="orientation-toggle-group">
|
||||||
@@ -877,7 +884,7 @@
|
|||||||
<img src="./assets/ICCery-logo.svg" alt="ICCery Logo" class="about-logo" />
|
<img src="./assets/ICCery-logo.svg" alt="ICCery Logo" class="about-logo" />
|
||||||
</div>
|
</div>
|
||||||
<h2>About ICCery</h2>
|
<h2>About ICCery</h2>
|
||||||
<p><strong>Version:</strong> <span id="aboutVersion">v0.7.1</span> • <strong>Build Date:</strong> <span id="aboutBuildDate">September 2026</span></p>
|
<p><strong>Version:</strong> <span id="aboutVersion">v0.7.2</span> • <strong>Build Date:</strong> <span id="aboutBuildDate">September 2026</span></p>
|
||||||
<p><strong>Copyright © 2026 Gordon Bolton. All rights reserved.</strong></p>
|
<p><strong>Copyright © 2026 Gordon Bolton. All rights reserved.</strong></p>
|
||||||
<h3>Licences & EULA</h3>
|
<h3>Licences & EULA</h3>
|
||||||
<div class="license-text-container">
|
<div class="license-text-container">
|
||||||
|
|||||||
@@ -146,6 +146,7 @@ export function initPrinttarg() {
|
|||||||
const btnRefreshPrinters = document.getElementById("btnRefreshPrinters");
|
const btnRefreshPrinters = document.getElementById("btnRefreshPrinters");
|
||||||
const btnPrinterProperties = document.getElementById("btnPrinterProperties");
|
const btnPrinterProperties = document.getElementById("btnPrinterProperties");
|
||||||
const printerTraySelect = document.getElementById("printerTraySelect");
|
const printerTraySelect = document.getElementById("printerTraySelect");
|
||||||
|
const printerMediaTypeSelect = document.getElementById("printerMediaTypeSelect");
|
||||||
const btnOrientPortrait = document.getElementById("btnOrientPortrait");
|
const btnOrientPortrait = document.getElementById("btnOrientPortrait");
|
||||||
const btnOrientLandscape = document.getElementById("btnOrientLandscape");
|
const btnOrientLandscape = document.getElementById("btnOrientLandscape");
|
||||||
const printerStatusBadge = document.getElementById("printerStatusBadge");
|
const printerStatusBadge = document.getElementById("printerStatusBadge");
|
||||||
@@ -219,6 +220,9 @@ export function initPrinttarg() {
|
|||||||
async function loadPrinterCapabilities(printerName) {
|
async function loadPrinterCapabilities(printerName) {
|
||||||
if (!printerTraySelect || !printerName) return;
|
if (!printerTraySelect || !printerName) return;
|
||||||
printerTraySelect.innerHTML = '<option value="" selected>Default / Auto Select</option>';
|
printerTraySelect.innerHTML = '<option value="" selected>Default / Auto Select</option>';
|
||||||
|
if (printerMediaTypeSelect) {
|
||||||
|
printerMediaTypeSelect.innerHTML = '<option value="" selected>Default / Auto Select</option>';
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const caps = await invoke("get_printer_capabilities", { printerName });
|
const caps = await invoke("get_printer_capabilities", { printerName });
|
||||||
@@ -230,6 +234,15 @@ export function initPrinttarg() {
|
|||||||
printerTraySelect.appendChild(opt);
|
printerTraySelect.appendChild(opt);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (caps && caps.media_types && caps.media_types.length > 0 && printerMediaTypeSelect) {
|
||||||
|
caps.media_types.forEach(media => {
|
||||||
|
const opt = document.createElement("option");
|
||||||
|
opt.value = media.id;
|
||||||
|
opt.textContent = media.name;
|
||||||
|
printerMediaTypeSelect.appendChild(opt);
|
||||||
|
});
|
||||||
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.warn("[ICCery Print] Could not fetch printer capabilities:", err);
|
console.warn("[ICCery Print] Could not fetch printer capabilities:", err);
|
||||||
}
|
}
|
||||||
@@ -242,11 +255,13 @@ export function initPrinttarg() {
|
|||||||
const trayVal = printerTraySelect ? printerTraySelect.value : "";
|
const trayVal = printerTraySelect ? printerTraySelect.value : "";
|
||||||
const paperSource = trayVal ? parseInt(trayVal, 10) : null;
|
const paperSource = trayVal ? parseInt(trayVal, 10) : null;
|
||||||
const ppdFallback = chkPpdFallback ? chkPpdFallback.checked : false;
|
const ppdFallback = chkPpdFallback ? chkPpdFallback.checked : false;
|
||||||
|
const mediaType = printerMediaTypeSelect ? printerMediaTypeSelect.value : "";
|
||||||
|
|
||||||
return {
|
return {
|
||||||
paper_source: paperSource,
|
paper_source: paperSource,
|
||||||
orientation: selectedOrientation,
|
orientation: selectedOrientation,
|
||||||
paper_size: pageSizeSelect ? pageSizeSelect.value : null,
|
paper_size: pageSizeSelect ? pageSizeSelect.value : null,
|
||||||
|
media_type: mediaType ? mediaType : null,
|
||||||
ppd_uncorrected_passthrough: ppdFallback,
|
ppd_uncorrected_passthrough: ppdFallback,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user