| Name | Message | Date |
|---|---|---|
| 📄 main.rs | 1 month ago | |
| 📄 proto.rs | 1 month ago |
📄
src/main.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
mod proto; use std::env::args; use std::time::Duration; use escpos::driver::NetworkDriver; use escpos::printer::Printer; use escpos::utils::{BitImageOption, BitImageSize, Protocol}; use tonic::{Request, Response, Status, transport::Server}; use proto::receipt_printer_server::{ReceiptPrinter, ReceiptPrinterServer}; use proto::{PrintRequest, PrintResponse}; use typst::foundations::{Dict, IntoValue}; use typst::layout::PagedDocument; use typst_as_lib::TypstEngine; use typst_as_lib::typst_kit_options::TypstKitFontOptions; use crate::proto::TypstContent; use crate::proto::print_request::Content; #[tokio::main] async fn main() -> Result<(), Box<dyn std::error::Error>> { let args: Vec<_> = args().skip(1).collect(); if args.len() != 2 { println!("Please provide the printer connection string and .typ template as arguments"); return Ok(()); }; let Some((printer_host, printer_port)) = args[0] .split_once(':') .map(|(a, b)| b.parse::<u16>().ok().map(|b| (a, b))) .flatten() else { println!("Printer connection string must be on format `host:port`"); return Ok(()); }; let template_path = &args[1]; let receipt_printer_service = ReceiptPrinterService { template_path: template_path.to_owned(), printer_host: printer_host.to_owned(), printer_port: printer_port, }; Server::builder() .add_service(ReceiptPrinterServer::new(receipt_printer_service)) .serve("[::1]:50051".parse()?) .await?; Ok(()) } #[derive(Debug)] pub struct ReceiptPrinterService { template_path: String, printer_host: String, printer_port: u16, } #[tonic::async_trait] impl ReceiptPrinter for ReceiptPrinterService { async fn print( &self, request: Request<PrintRequest>, ) -> Result<Response<PrintResponse>, Status> { let Some(Content::Typst(TypstContent { content: Some(content), })) = &request.get_ref().content else { println!("No Typst content received"); return Ok(Response::new(PrintResponse::default())); }; let engine = TypstEngine::builder() .with_file_system_resolver(".") .search_fonts_with(TypstKitFontOptions::new().include_system_fonts(true)) .build(); println!("Compiling document..."); let mut inputs = Dict::new(); inputs.insert("content".into(), content.clone().into_value()); let document: PagedDocument = match engine .compile_with_input(self.template_path.as_ref(), inputs) .output { Ok(document) => document, Err(error) => { println!("Failed to compile with message: {error}"); return Ok(Response::new(PrintResponse::default())); } }; let width_pixel = 72 * 8; let width_cm = 7.2; let width_pt = width_cm / 0.03527; let pixel_per_pt = width_pixel as f32 / width_pt; println!("Rendering document..."); let pixmap = typst_render::render(&document.pages[0], pixel_per_pt); println!("Pixel size: {}x{}", pixmap.width(), pixmap.height()); let png = match pixmap.encode_png() { Ok(png) => png, Err(error) => { println!("Could not encode image to PNG: {error}"); return Ok(Response::new(PrintResponse::default())); } }; println!("Connecting to printer..."); let driver = match NetworkDriver::open( self.printer_host.as_ref(), self.printer_port, Some(Duration::from_secs(5)), ) { Ok(driver) => driver, Err(error) => { println!("Error connecting to printer: {error}"); return Ok(Response::new(PrintResponse::default())); } }; println!("Initializing printer..."); let mut printer = Printer::new(driver, Protocol::default(), None); if let Err(error) = printer.init() { println!("Error initializing printer: {error}"); return Ok(Response::new(PrintResponse::default())); } println!("Sending image to printer..."); if let Err(error) = printer.bit_image_from_bytes_option( &png, BitImageOption::new(Some(width_pixel), None, BitImageSize::Normal).unwrap(), ) { println!("Error when printing document: {error}"); return Ok(Response::new(PrintResponse::default())); } println!("Printing..."); if let Err(error) = printer.print_cut() { println!("Error when printing: {error}"); return Ok(Response::new(PrintResponse::default())); } println!("Done!"); Ok(Response::new(PrintResponse::default())) } }