Commit:
ca7cd45Parent:
10d188bMove `ReceiptPrinterService` to its own file
src/main.rs
+10
-115
diff --git a/src/main.rs b/src/main.rs
index c1a1718..679271a 100644
@@ -1,22 +1,11 @@
mod proto;
mod receipt_printer_service;
use std::env::args;
use std::time::Duration;
use tonic::transport::Server;
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;
use crate::proto::receipt_printer_server::ReceiptPrinterServer;
use crate::receipt_printer_service::ReceiptPrinterService;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
@@ -27,19 +16,18 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
};
let Some((printer_host, printer_port)) = args[0]
.split_once(':')
.map(|(a, b)| b.parse::<u16>().ok().map(|b| (a, b)))
.flatten()
.and_then(|(a, b)| b.parse::<u16>().ok().map(|b| (a, b)))
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,
};
let receipt_printer_service = ReceiptPrinterService::new(
template_path.to_owned(),
printer_host.to_owned(),
printer_port,
);
Server::builder()
.add_service(ReceiptPrinterServer::new(receipt_printer_service))
@@ -48,96 +36,3 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
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()))
}
}
src/receipt_printer_service.rs
+117
-0
diff --git a/src/receipt_printer_service.rs b/src/receipt_printer_service.rs
new file mode 100644
index 0000000..71a176f
@@ -0,0 +1,117 @@
use std::time::Duration;
use escpos::driver::NetworkDriver;
use escpos::printer::Printer;
use escpos::utils::{BitImageOption, BitImageSize, Protocol};
use tonic::{Request, Response, Status};
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::print_request::Content;
use crate::proto::receipt_printer_server::ReceiptPrinter;
use crate::proto::{PrintRequest, PrintResponse, TypstContent};
#[derive(Debug)]
pub struct ReceiptPrinterService {
template_path: String,
printer_host: String,
printer_port: u16,
}
impl ReceiptPrinterService {
pub fn new(template_path: String, printer_host: String, printer_port: u16) -> Self {
Self {
template_path,
printer_host,
printer_port,
}
}
}
#[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()))
}
}