convert to pdf

main
reng 4 months ago
parent 7159fdb110
commit a97a98c74f
  1. 10
      package-lock.json
  2. 1
      package.json
  3. 2
      src-tauri/2
  4. 978
      src-tauri/Cargo.lock
  5. 2
      src-tauri/Cargo.toml
  6. 3
      src-tauri/capabilities/default.json
  7. 107
      src-tauri/src/lib.rs
  8. 57
      src-tauri/src/util.rs
  9. 2
      src/App.jsx

10
package-lock.json generated

@ -11,6 +11,7 @@
"@tailwindcss/vite": "^4.1.11",
"@tauri-apps/api": "^2",
"@tauri-apps/plugin-dialog": "^2.3.2",
"@tauri-apps/plugin-fs": "^2.4.1",
"@tauri-apps/plugin-opener": "^2",
"react": "^18.3.1",
"react-dom": "^18.3.1"
@ -1547,6 +1548,15 @@
"@tauri-apps/api": "^2.6.0"
}
},
"node_modules/@tauri-apps/plugin-fs": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/@tauri-apps/plugin-fs/-/plugin-fs-2.4.1.tgz",
"integrity": "sha512-vJlKZVGF3UAFGoIEVT6Oq5L4HGDCD78WmA4uhzitToqYiBKWAvZR61M6zAyQzHqLs0ADemkE4RSy/5sCmZm6ZQ==",
"license": "MIT OR Apache-2.0",
"dependencies": {
"@tauri-apps/api": "^2.6.0"
}
},
"node_modules/@tauri-apps/plugin-opener": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/@tauri-apps/plugin-opener/-/plugin-opener-2.4.0.tgz",

@ -13,6 +13,7 @@
"@tailwindcss/vite": "^4.1.11",
"@tauri-apps/api": "^2",
"@tauri-apps/plugin-dialog": "^2.3.2",
"@tauri-apps/plugin-fs": "^2.4.1",
"@tauri-apps/plugin-opener": "^2",
"react": "^18.3.1",
"react-dom": "^18.3.1"

@ -1,5 +1,5 @@
added 1 package, and audited 169 packages in 3s
added 1 package, and audited 170 packages in 4s
25 packages are looking for funding
run `npm fund` for details

978
src-tauri/Cargo.lock generated

File diff suppressed because it is too large Load Diff

@ -25,3 +25,5 @@ serde_json = "1"
printers = "2.2.0"
winprint = "0.2.0"
tauri-plugin-dialog = "2"
printpdf = { version="0.8.2", features= ["png"] }
tauri-plugin-fs = "2"

@ -8,6 +8,7 @@
"permissions": [
"core:default",
"opener:default",
"dialog:default"
"dialog:default",
"fs:default"
]
}

@ -3,10 +3,13 @@ use std::path::Path;
use winprint::printer::{FilePrinter, ImagePrinter, PdfiumPrinter, PrinterDevice};
use winprint::ticket::{
FeatureOptionPackWithPredefined, PredefinedMediaName, PrintCapabilities, PrintTicket,
PrintTicketBuilder,PredefinedPageOutputColor
FeatureOptionPackWithPredefined, PredefinedMediaName, PredefinedPageOutputColor,
PrintCapabilities, PrintTicket, PrintTicketBuilder,
};
// use util::imageToPdf;
mod util;
#[tauri::command]
fn get_all_printers() -> Vec<String> {
let printers = PrinterDevice::all().expect("Failed to get printers");
@ -31,6 +34,18 @@ fn get_printer_capabilities(device_name: String) -> HashMap<String, Vec<String>>
}
};
println!("My Printer: {:?}", my_device.name());
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);
}
Err(e) => {
println!("Failed to fetch capabilities: {:?}", e);
}
}
match PrintCapabilities::fetch(&my_device) {
Ok(capabilities) => {
let dpi_opts = capabilities.page_resolutions();
@ -66,26 +81,42 @@ fn get_printer_capabilities(device_name: String) -> HashMap<String, Vec<String>>
fn default_ticket() -> PrintTicket {
// let mut ticket = PrintTicket::new();
let xml=r#"<?xml version="1.0" encoding="UTF-8"?>
let xml = r#"<?xml version="1.0" encoding="UTF-8"?>
<psf:PrintTicket version="1"
xmlns:psf="http://schemas.microsoft.com/windows/2003/08/printing/printschemaframework"
xmlns:psk="http://schemas.microsoft.com/windows/2003/08/printing/printschemakeywords">
<psf:Feature name="psk:PageScaling">
<psf:Option name="psk:FitToPage" />
<psf:Option name="psk:PageScalingFit"/>
</psf:Feature>
<psf:Feature name="psk:PageOutputQuality">
<psf:Option name="psk:High" />
</psf:Feature>
<psf:Feature name="psk:PageMediaSize">
<psf:Option name="psk:PageMediaSizeA6"/>
</psf:Feature>
<psf:Feature name="psk:PageOutputColor">
<psf:Option name="psk:Monochrome"/>
</psf:Feature>
</psf:PrintTicket>"#;
let ticket = PrintTicket::from_xml(xml);
ticket
}
#[tauri::command]
fn print_document(device_name: String, document_path: String, dpi: String, color: String, size: String) {
println!("Printing document: {}, {}, {}, {}", document_path, size, dpi, color);
fn print_document(
device_name: String,
document_path: String,
dpi: String,
color: String,
size: String,
) {
println!(
"Printing document: {}, {}, {}, {}",
document_path, size, dpi, color
);
let my_device = match get_my_device(device_name.clone()) {
Some(device) => device,
None => {
@ -98,12 +129,13 @@ fn print_document(device_name: String, document_path: String, dpi: String, color
let capabilities = PrintCapabilities::fetch(&my_device).unwrap();
// size
let parsed_size = size.parse::<PredefinedMediaName>()
.expect("Invalid media size name");
let parsed_size = size
.parse::<PredefinedMediaName>()
.expect("Invalid media size name");
let a6_media = capabilities
.page_media_sizes()
.find(|x| x.as_predefined_name() == Some(parsed_size))
.expect("Media size not supported by printer");
.page_media_sizes()
.find(|x| x.as_predefined_name() == Some(parsed_size))
.expect("Media size not supported by printer");
// dpi
let parsed_dpi = dpi
@ -117,11 +149,14 @@ fn print_document(device_name: String, document_path: String, dpi: String, color
.expect("DPI not supported by printer");
// color
let parsed_color =capabilities.page_output_colors()
.find(|x| x.as_predefined_name() == Some(color.parse::<PredefinedPageOutputColor>().unwrap()))
let parsed_color = capabilities
.page_output_colors()
.find(|x| {
x.as_predefined_name() == Some(color.parse::<PredefinedPageOutputColor>().unwrap())
})
.map(|x| x.as_predefined_name().unwrap())
.expect("Invalid color name");
let supported_color = capabilities
.page_output_colors()
.find(|x| x.as_predefined_name() == Some(parsed_color))
@ -132,32 +167,50 @@ fn print_document(device_name: String, document_path: String, dpi: String, color
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 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 path = Path::new(&document_path);
// let xml=ticket.get_xml();
// let xml_string = String::from_utf8_lossy(&xml).to_string();
// println!("PrintTicket XML as String: {}", xml_string);
// return;
if path.extension().map(|ext| ext.eq_ignore_ascii_case("pdf")).unwrap_or(false) {
let printer=PdfiumPrinter::new(my_device);
let path = Path::new(&document_path);
let printer = PdfiumPrinter::new(my_device);
if path
.extension()
.map(|ext| ext.eq_ignore_ascii_case("pdf"))
.unwrap_or(false)
{
printer.print(path, ticket).unwrap();
} else {
let printer=ImagePrinter::new(my_device);
printer.print(path, ticket).unwrap();
match util::imageToPdf(document_path.clone()) {
Ok(pdf_path) => {
// Print the converted PDF using the printer
printer.print(Path::new(&pdf_path), ticket).unwrap();
}
Err(e) => {
println!("Error converting image to PDF: {}", e);
}
}
};
// let theprinter = PdfiumPrinter::new(my_device);
// let theprinter = ImagePrinter::new(my_device);
// let theprinter = ImagePrinter::new(my_device);
// theprinter.print(path, ticket).unwrap();
}
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
tauri::Builder::default()
.plugin(tauri_plugin_fs::init())
.plugin(tauri_plugin_dialog::init())
.plugin(tauri_plugin_opener::init())
.invoke_handler(tauri::generate_handler![

@ -0,0 +1,57 @@
use printpdf::*;
use std::path::Path;
pub fn imageToPdf(path: String) -> Result<String, String> {
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 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);
// 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 = 1.0 / 25.4; // Conversion factor
// let width_pt = output_width * mm_to_pt * dpi / image.width as f64;
// let height_pt = output_height * mm_to_pt * dpi / image.height as f64;
// println!("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)),
scale_x: Some(1.0),
scale_y: Some(1.0),
dpi: Some(300.0),
..Default::default()
},
});
let page1 = PdfPage::new(Mm(105.0), Mm(148.0), ops);
let pdf_bytes: Vec<u8> = doc
.with_pages(vec![page1])
.save(&PdfSaveOptions::default(), &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)),
}
}

@ -69,8 +69,8 @@ function App() {
open({
multiple: false,
filters: [
{ name: 'PDF Files', extensions: ['pdf'] },
{ name: 'Image Files', extensions: ['png', 'jpg', 'jpeg'] },
{ name: 'PDF Files', extensions: ['pdf'] },
]
}).then((filePath) => {
if (filePath) {

Loading…
Cancel
Save