diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 3aa95c0..7ca3e2a 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -3731,6 +3731,7 @@ dependencies = [ "image", "printers", "printpdf", + "quick-xml 0.38.0", "serde", "serde_json", "tauri", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 1ef7f11..24aa1de 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -29,3 +29,4 @@ printpdf = { version="0.8.2", features= ["png"] } tauri-plugin-fs = "2" image = "0.25.6" flate2 = "1.1.2" +quick-xml = "0.38.0" diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 1a18696..b2962be 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -7,8 +7,7 @@ use winprint::ticket::{ PrintCapabilities, PrintTicket, PrintTicketBuilder, }; -// use util::imageToPdf; -// mod util; +mod util; mod raw_convert; #[tauri::command] @@ -57,12 +56,19 @@ fn get_printer_capabilities(device_name: String) -> HashMap> } }; println!("My Printer: {:?}", my_device.name()); + + let mut map = HashMap::new(); match PrintCapabilities::fetch_xml(&my_device) { Ok(xml) => { println!("Printer Capabilities fetched successfully."); let string_xml = String::from_utf8_lossy(&xml).to_string(); - println!("Printer Capabilities XML: {}", string_xml); + // println!("Printer Capabilities XML: {}", string_xml); + let quality_opts = util::get_output_quality_options(&string_xml); + println!("DPI Options: {:?}", quality_opts); + + map.insert("quality_options".to_string(), quality_opts); + } Err(e) => { println!("Failed to fetch capabilities: {:?}", e); @@ -89,7 +95,7 @@ fn get_printer_capabilities(device_name: String) -> HashMap> println!("Color Options: {:?}", color_opts_value); println!("Size Options: {:?}", size_opts_value); - let mut map = HashMap::new(); + map.insert("dpi_options".to_string(), dpi_opts_value); map.insert("color_options".to_string(), color_opts_value); map.insert("size_options".to_string(), size_opts_value); @@ -101,28 +107,22 @@ fn get_printer_capabilities(device_name: String) -> HashMap> } } } -fn default_ticket() -> PrintTicket { +fn default_ticket(quality: Option) -> PrintTicket { // let mut ticket = PrintTicket::new(); + let _quality = quality.unwrap_or_else(|| "psk:Normal".to_string()); - let xml = r#" + let xml =format!(r#" - + xmlns:psf="http://schemas.microsoft.com/windows/2003/08/printing/printschemaframework" + xmlns:psk="http://schemas.microsoft.com/windows/2003/08/printing/printschemakeywords"> - + - - - - - + - - - - "#; + "#); + let ticket = PrintTicket::from_xml(xml); ticket } @@ -134,10 +134,11 @@ fn print_document( dpi: String, color: String, size: String, + quality: Option, ) { println!( - "Printing document: {}, {}, {}, {}", - document_path, size, dpi, color + "Printing document: {}, {}, {}, {}, {}", + document_path, size, dpi, color, quality.clone().unwrap_or_else(|| "none".to_string()) ); let path = Path::new(&document_path); @@ -154,6 +155,7 @@ fn print_document( dpi, color, size, + quality, ); } Err(e) => { @@ -211,22 +213,24 @@ fn print_document( .expect("Color not supported by printer"); let mut builder = PrintTicketBuilder::new(&my_device).unwrap(); + + let default_ticket = default_ticket(quality); + if let Err(e) = builder.merge(default_ticket) { + println!("Failed to merge default ticket: {:?}", e); + } + builder.merge(a6_media).unwrap(); builder.merge(supported_dpi).unwrap(); builder.merge(supported_color).unwrap(); - // let default_ticket = default_ticket(); - // if let Err(e) = builder.merge(default_ticket) { - // println!("Failed to merge default ticket: {:?}", e); - // return; - // } + let ticket = builder.build().unwrap(); - // let xml=ticket.get_xml(); - // let xml_string = String::from_utf8_lossy(&xml).to_string(); - // println!("PrintTicket XML as String: {}", xml_string); - // return; + let xml=ticket.get_xml(); + let xml_string = String::from_utf8_lossy(&xml).to_string(); + println!("PrintTicket XML as String: {}", xml_string); + return; let path = Path::new(&document_path); let printer = PdfiumPrinter::new(my_device); diff --git a/src-tauri/src/util.rs b/src-tauri/src/util.rs index c4cddcb..76d47f4 100644 --- a/src-tauri/src/util.rs +++ b/src-tauri/src/util.rs @@ -1,70 +1,114 @@ -use printpdf::*; +// use printpdf::*; use std::path::Path; +use quick_xml::Reader; +use quick_xml::events::{Event, BytesStart}; +use std::collections::HashSet; -pub fn imageToPdf(path: String) -> Result { - let mut doc = PdfDocument::new("image_to_pdf"); +// pub fn imageToPdf(path: String) -> Result { +// let mut doc = PdfDocument::new("image_to_pdf"); - let image_bytes = std::fs::read(&path).map_err(|e| e.to_string())?; - let image = RawImage::decode_from_bytes(&image_bytes, &mut Vec::new()).unwrap(); +// let image_bytes = std::fs::read(&path).map_err(|e| e.to_string())?; +// let image = RawImage::decode_from_bytes(&image_bytes, &mut Vec::new()).unwrap(); - let mut ops = Vec::new(); +// let mut ops = Vec::new(); - // In the PDF, an image is an `XObject`, identified by a unique `ImageId` - let image_xobject_id = doc.add_image(&image); +// // In the PDF, an image is an `XObject`, identified by a unique `ImageId` +// let image_xobject_id = doc.add_image(&image); - // Calculate image size in points (1 point = 1/72 inch) - let dpi = 300.0; - let output_width= 105.0; // A6 width in mm - let output_height = 148.0; // A6 height in mm - let mm_to_pt = dpi / 25.4; // Conversion factor - let width_pt = output_width * mm_to_pt; - let height_pt = output_height * mm_to_pt; +// // Calculate image size in points (1 point = 1/72 inch) +// let dpi = 300.0; +// let output_width= 105.0; // A6 width in mm +// let output_height = 148.0; // A6 height in mm +// let mm_to_pt = dpi / 25.4; // Conversion factor +// let width_pt = output_width * mm_to_pt; +// let height_pt = output_height * mm_to_pt; - println!("Image size in pixels: {}x{}", image.width, image.height); - println!("Dest Image size in points: {}x{}", width_pt, height_pt); +// println!("Image size in pixels: {}x{}", image.width, image.height); +// println!("Dest Image size in points: {}x{}", width_pt, height_pt); - // Place the image at (0,0) with the calculated size - ops.push(Op::UseXobject { - id: image_xobject_id.clone(), - transform: XObjectTransform { - translate_x: Some(Pt(0.0)), - translate_y: Some(Pt(0.0)), - dpi: Some(dpi), - ..Default::default() - }, +// // Place the image at (0,0) with the calculated size +// ops.push(Op::UseXobject { +// id: image_xobject_id.clone(), +// transform: XObjectTransform { +// translate_x: Some(Pt(0.0)), +// translate_y: Some(Pt(0.0)), +// dpi: Some(dpi), +// ..Default::default() +// }, - }); - - let save_options= PdfSaveOptions { - optimize: false, - image_optimization: Some(ImageOptimizationOptions { - quality: Some(1.0), - auto_optimize: Some(false), - format: Some(ImageCompression::None), - ..ImageOptimizationOptions::default() - }), - ..Default::default() - }; - - - let page1 = PdfPage::new(Mm(output_width), Mm(output_height), ops); - let pdf_bytes: Vec = doc - .with_pages(vec![page1]) - .save(&save_options, &mut Vec::new()); +// }); + +// let save_options= PdfSaveOptions { +// optimize: false, +// image_optimization: Some(ImageOptimizationOptions { +// quality: Some(1.0), +// auto_optimize: Some(false), +// format: Some(ImageCompression::None), +// ..ImageOptimizationOptions::default() +// }), +// ..Default::default() +// }; + + +// let page1 = PdfPage::new(Mm(output_width), Mm(output_height), ops); +// let pdf_bytes: Vec = doc +// .with_pages(vec![page1]) +// .save(&save_options, &mut Vec::new()); - let pdf_path = Path::new(&path).with_extension("pdf"); - - match std::fs::write(&pdf_path, pdf_bytes) { - Ok(_) => { - let abs_path = std::fs::canonicalize(&pdf_path) - .map(|p| p.display().to_string()) - .unwrap_or_else(|_| pdf_path.display().to_string()); - println!("PDF written successfully to {}", abs_path); - Ok(abs_path) - }, - Err(e) => Err(format!("Failed to write PDF: {}", e)), +// let pdf_path = Path::new(&path).with_extension("pdf"); + +// match std::fs::write(&pdf_path, pdf_bytes) { +// Ok(_) => { +// let abs_path = std::fs::canonicalize(&pdf_path) +// .map(|p| p.display().to_string()) +// .unwrap_or_else(|_| pdf_path.display().to_string()); +// println!("PDF written successfully to {}", abs_path); +// Ok(abs_path) +// }, +// Err(e) => Err(format!("Failed to write PDF: {}", e)), +// } +// } + + + + +pub fn get_output_quality_options(xml: &str) -> Vec { + let mut reader = Reader::from_str(xml); + // reader.trim_text(true); + + // let mut buf = Vec::new(); + let mut inside_target_feature = false; + let mut inside_option = false; + let mut current_option_name = String::new(); + let mut results = HashSet::new(); + + loop { + match reader.read_event() { + Ok(Event::Start(ref e)) => { + if e.name().as_ref() == b"psf:Feature" { + if let Some(attr) = e.attributes().filter_map(Result::ok).find(|a| a.key.as_ref() == b"name") { + if attr.unescape_value().unwrap() == "psk:PageOutputQuality" { + inside_target_feature = true; + } + } + } else if inside_target_feature && e.name().as_ref() == b"psf:Option" { + if let Some(attr) = e.attributes().filter_map(Result::ok).find(|a| a.key.as_ref() == b"name") { + results.insert(attr.unescape_value().unwrap().to_string()); + } + } + } + Ok(Event::End(ref e)) if e.name().as_ref() == b"psf:Feature" => { + inside_target_feature = false; + } + Ok(Event::Eof) => break, + _ => {} + } + // buf.clear(); } -} + let mut vec: Vec = results.into_iter().collect(); + vec.sort(); + vec +} diff --git a/src/App.jsx b/src/App.jsx index ca38992..00a2f8c 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -2,6 +2,15 @@ import { invoke } from "@tauri-apps/api/core"; import { open } from '@tauri-apps/plugin-dialog'; import "./App.css"; import { useEffect, useState } from "react"; +import { AvailableQualities } from "./params"; + + +const OptionTypes=[ + 'dpi_options', + 'color_options', + 'size_options', + 'quality_options', +] function App() { @@ -73,6 +82,7 @@ function App() { dpi: document.getElementById("dpi_options").value, color: document.getElementById("color_options").value, size: document.getElementById("size_options").value, + quality: document.getElementById("quality_options").value, }) .then(() => { console.log("Print job sent successfully."); @@ -101,38 +111,22 @@ function App() { .then((capabilities) => { console.log(`Capabilities for ${printer}:`, capabilities); - - if(capabilities.color_options){ - const colorOptionsSelect = document.getElementById("color_options"); - colorOptionsSelect.innerHTML = ""; - for(const colorOption of capabilities.color_options) { - const option = document.createElement("option"); - option.value = colorOption; - option.textContent = colorOption; - colorOptionsSelect.appendChild(option); + for(const optionType of OptionTypes) { + const selectElement = document.getElementById(optionType); + if(selectElement) { + selectElement.innerHTML = ""; // Clear existing options } - } - if(capabilities.dpi_options){ - const dpiOptionsSelect = document.getElementById("dpi_options"); - dpiOptionsSelect.innerHTML = ""; - for(const dpiOption of capabilities.dpi_options) { - const option = document.createElement("option"); - option.value = dpiOption; - option.textContent = dpiOption; - dpiOptionsSelect.appendChild(option); + if(!capabilities[optionType]) continue; + for(const option of capabilities[optionType]) { + const optionElement = document.createElement("option"); + optionElement.value = option; + optionElement.textContent = option; + if(selectElement) { + selectElement.appendChild(optionElement); + } } - } - if(capabilities.size_options){ - const sizeOptionsSelect = document.getElementById("size_options"); - sizeOptionsSelect.innerHTML = ""; - for(const sizeOption of capabilities.size_options) { - const option = document.createElement("option"); - option.value = sizeOption; - option.textContent = sizeOption; - sizeOptionsSelect.appendChild(option); - } - } + } }) @@ -155,6 +149,7 @@ function App() { +
diff --git a/src/params.js b/src/params.js new file mode 100644 index 0000000..0be8d1c --- /dev/null +++ b/src/params.js @@ -0,0 +1,14 @@ +export const AvailableQualities=[ + { "name": "ns0000:Draft", "displayname": "草稿" }, + { "name": "ns0000:DraftVivid", "displayname": "草稿鮮豔" }, + { "name": "ns0000:Standard", "displayname": "標準" }, + { "name": "ns0000:StandardVivid", "displayname": "標準鮮豔" }, + { "name": "ns0000:HighQuality", "displayname": "高" }, + { "name": "ns0000:AdvancedSetting", "displayname": "自訂" } +]; + + +export const Scales=[ + +] +