Compare commits
No commits in common. "ffa562d252085ebf83fad56022bea998932ffe75" and "a9f193ecc772b32fd3642240bf87ec8e88c9c3ba" have entirely different histories.
ffa562d252
...
a9f193ecc7
5 changed files with 63 additions and 1569 deletions
1035
Cargo.lock
generated
1035
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
|
@ -4,8 +4,4 @@ version = "0.1.0"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
image = "0.25.9"
|
nom = "8.0.0"
|
||||||
quick-xml = { version = "0.39.0", features = ["serialize"] }
|
|
||||||
serde = { version = "1.0.228", features = ["derive"] }
|
|
||||||
# nom = "8.0.0"
|
|
||||||
|
|
||||||
|
|
|
||||||
304
src/escpos.rs
304
src/escpos.rs
|
|
@ -1,304 +0,0 @@
|
||||||
use std::{error::Error, fmt::Display};
|
|
||||||
|
|
||||||
use image::{
|
|
||||||
DynamicImage, GrayImage,
|
|
||||||
imageops::{self, FilterType, colorops},
|
|
||||||
};
|
|
||||||
use serde::Deserialize;
|
|
||||||
|
|
||||||
// TYPES
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum EscPosError {
|
|
||||||
InvalidBarcodeLength(String),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(u8)]
|
|
||||||
#[derive(Debug, Deserialize, Default)]
|
|
||||||
#[serde(rename_all = "lowercase")]
|
|
||||||
pub enum QREcc {
|
|
||||||
Low = 48,
|
|
||||||
#[default]
|
|
||||||
Medium = 49,
|
|
||||||
Quartile = 50,
|
|
||||||
High = 51,
|
|
||||||
}
|
|
||||||
#[repr(u8)]
|
|
||||||
#[derive(Debug, Deserialize, Default)]
|
|
||||||
#[serde(rename_all = "lowercase")]
|
|
||||||
pub enum BARTextPosition {
|
|
||||||
Hidden = 0,
|
|
||||||
Above = 1,
|
|
||||||
#[default]
|
|
||||||
Below = 2,
|
|
||||||
Both = 3,
|
|
||||||
}
|
|
||||||
#[repr(u8)]
|
|
||||||
#[derive(Debug, Deserialize, Default)]
|
|
||||||
#[serde(rename_all = "lowercase")]
|
|
||||||
pub enum BARType {
|
|
||||||
#[default]
|
|
||||||
Code128 = 0x49,
|
|
||||||
Ean13 = 0x43,
|
|
||||||
Upca = 0x41,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Default)]
|
|
||||||
#[serde(rename_all = "lowercase")]
|
|
||||||
pub enum ImageOrientation {
|
|
||||||
#[default]
|
|
||||||
Preserve,
|
|
||||||
Largest,
|
|
||||||
LargestOrientation,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(u8)]
|
|
||||||
#[derive(Debug, Deserialize, Default)]
|
|
||||||
#[serde(rename_all = "lowercase")]
|
|
||||||
pub enum JustifyOrientation {
|
|
||||||
#[default]
|
|
||||||
Left = 0,
|
|
||||||
Center = 1,
|
|
||||||
Right = 2,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
|
||||||
#[serde(rename_all = "lowercase")]
|
|
||||||
pub enum TextEffect {
|
|
||||||
Bold,
|
|
||||||
DoubleHeight,
|
|
||||||
DoubleWidth,
|
|
||||||
Justify(JustifyOrientation),
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct EscPosBuilder {
|
|
||||||
pub content: Vec<u8>,
|
|
||||||
max_width: u16,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_numeric(s: &[u8]) -> bool {
|
|
||||||
s.iter().all(|&b| b.is_ascii_digit())
|
|
||||||
}
|
|
||||||
fn bitmap_auto_brighten(gray: &mut GrayImage) {
|
|
||||||
let avg = gray.pixels().map(|p| p.0[0] as u32).sum::<u32>() / gray.len() as u32;
|
|
||||||
|
|
||||||
if avg < 100 {
|
|
||||||
colorops::brighten_in_place(gray, 40);
|
|
||||||
colorops::contrast_in_place(gray, 25.0);
|
|
||||||
} else if avg > 180 {
|
|
||||||
colorops::brighten_in_place(gray, -15);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn bitmap_pack_bits(w: usize, h: usize, gray: &GrayImage) -> Vec<u8> {
|
|
||||||
let pitch = (w + 7) >> 3; // pixels per row
|
|
||||||
let mut bits = vec![0u8; pitch * h];
|
|
||||||
|
|
||||||
for y in 0..h {
|
|
||||||
for x in 0..w {
|
|
||||||
let byte = y * pitch + (x >> 3); // get index of byte that contains bit for the current pixel
|
|
||||||
let bit = 7 - (x & 7); // get bit position in byte
|
|
||||||
if gray[(x as u32, y as u32)].0[0] == 0 {
|
|
||||||
// if the pixel is black, set it to one
|
|
||||||
bits[byte] |= 1 << bit;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bits
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Copy for JustifyOrientation {}
|
|
||||||
impl Clone for JustifyOrientation {
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
*self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Error for EscPosError {}
|
|
||||||
impl Display for EscPosError {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl EscPosBuilder {
|
|
||||||
fn extend(&mut self, slice: &[u8]) {
|
|
||||||
self.content.extend([0x1B, b'@']);
|
|
||||||
self.content.extend(slice);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new(max_width: u16) -> Self {
|
|
||||||
EscPosBuilder {
|
|
||||||
content: vec![0x1B, b'@'],
|
|
||||||
max_width,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn write_feed(&mut self, amount: Option<u8>) {
|
|
||||||
let amount = amount.unwrap_or(1);
|
|
||||||
self.extend(&[0x1B, b'd', amount])
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn write_text(&mut self, text: &str, text_effect: Option<&[TextEffect]>) -> () {
|
|
||||||
let mut buf: Vec<u8> = vec![];
|
|
||||||
buf.reserve(12 + text.len());
|
|
||||||
|
|
||||||
for i in text_effect.unwrap_or(&[]) {
|
|
||||||
match i {
|
|
||||||
TextEffect::Bold => buf.extend(&[0x1B, b'!', 8]),
|
|
||||||
TextEffect::DoubleWidth => buf.extend(&[0x1B, b'!', 0x10]),
|
|
||||||
TextEffect::DoubleHeight => buf.extend(&[0x1B, b'!', 0x20]),
|
|
||||||
TextEffect::Justify(orientation) => buf.extend(&[0x1B, b'a', *orientation as u8]),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
buf.extend(text.as_bytes());
|
|
||||||
self.extend(&buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn write_bitmap(
|
|
||||||
&mut self,
|
|
||||||
img: &DynamicImage,
|
|
||||||
orientation: Option<ImageOrientation>,
|
|
||||||
) -> () {
|
|
||||||
let orientation = orientation.unwrap_or_default();
|
|
||||||
let mw = self.max_width as u32;
|
|
||||||
|
|
||||||
let mut image = img.clone();
|
|
||||||
|
|
||||||
if matches!(orientation, ImageOrientation::LargestOrientation)
|
|
||||||
&& image.width() > image.height()
|
|
||||||
{
|
|
||||||
image = image.rotate90();
|
|
||||||
}
|
|
||||||
|
|
||||||
let iw = image.width();
|
|
||||||
let ih = image.height();
|
|
||||||
|
|
||||||
let w = if iw > mw || !matches!(orientation, ImageOrientation::Preserve) {
|
|
||||||
mw
|
|
||||||
} else {
|
|
||||||
iw
|
|
||||||
};
|
|
||||||
|
|
||||||
let h = (ih * w) / iw;
|
|
||||||
let img = image.resize(w, h, FilterType::Triangle);
|
|
||||||
|
|
||||||
let mut gray = img.to_luma8();
|
|
||||||
imageops::dither(&mut gray, &imageops::colorops::BiLevel);
|
|
||||||
bitmap_auto_brighten(&mut gray);
|
|
||||||
|
|
||||||
let w = img.width();
|
|
||||||
let h = img.height();
|
|
||||||
|
|
||||||
let bits = bitmap_pack_bits(w as usize, h as usize, &gray);
|
|
||||||
let mut buf: Vec<u8> = vec![];
|
|
||||||
|
|
||||||
let width_bytes = ((w + 7) >> 3) as u16;
|
|
||||||
let height_u16 = h as u16;
|
|
||||||
|
|
||||||
buf.extend(&[
|
|
||||||
0x1D,
|
|
||||||
b'v',
|
|
||||||
b'0',
|
|
||||||
0,
|
|
||||||
(width_bytes & 0xFF) as u8,
|
|
||||||
((width_bytes >> 8) & 0xFF) as u8,
|
|
||||||
(height_u16 & 0xFF) as u8,
|
|
||||||
((height_u16 >> 8) & 0xFF) as u8,
|
|
||||||
]);
|
|
||||||
buf.extend(bits);
|
|
||||||
self.extend(&buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn write_qr(&mut self, text: &str, size: Option<u8>, ecc: Option<QREcc>) -> () {
|
|
||||||
let mut buf: Vec<u8> = vec![];
|
|
||||||
let ecc = ecc.unwrap_or_default();
|
|
||||||
let size = match size {
|
|
||||||
Some(v) => v.clamp(6, 15),
|
|
||||||
None => 10,
|
|
||||||
};
|
|
||||||
|
|
||||||
let text = text.as_bytes();
|
|
||||||
let len = text.len() + 3;
|
|
||||||
buf.extend(&[
|
|
||||||
// Size
|
|
||||||
0x1D,
|
|
||||||
0x28,
|
|
||||||
0x6B,
|
|
||||||
0x03,
|
|
||||||
0x00,
|
|
||||||
0x31,
|
|
||||||
0x43,
|
|
||||||
size,
|
|
||||||
// Error correction level
|
|
||||||
0x1D,
|
|
||||||
0x28,
|
|
||||||
0x6B,
|
|
||||||
0x03,
|
|
||||||
0x00,
|
|
||||||
0x31,
|
|
||||||
0x45,
|
|
||||||
ecc as u8,
|
|
||||||
// Storing data...
|
|
||||||
0x1D,
|
|
||||||
0x28,
|
|
||||||
0x6B,
|
|
||||||
(len & 0xFF) as u8,
|
|
||||||
((len >> 8) & 0xFF) as u8,
|
|
||||||
0x31,
|
|
||||||
0x50,
|
|
||||||
0x30,
|
|
||||||
]);
|
|
||||||
buf.extend(text);
|
|
||||||
buf.extend(&[0x1D, 0x28, 0x6B, 0x03, 0x00, 0x31, 0x51, 0x30, 0x0A]); // Print!
|
|
||||||
|
|
||||||
self.extend(&buf);
|
|
||||||
}
|
|
||||||
pub fn write_barcode(
|
|
||||||
&mut self,
|
|
||||||
text: &str,
|
|
||||||
height: Option<u8>,
|
|
||||||
mod_width: Option<u8>,
|
|
||||||
text_position: Option<BARTextPosition>,
|
|
||||||
bar_type: Option<BARType>,
|
|
||||||
) -> Result<(), EscPosError> {
|
|
||||||
let height: u8 = height.unwrap_or(80);
|
|
||||||
let mod_width: u8 = mod_width.unwrap_or(2);
|
|
||||||
let text_position = text_position.unwrap_or_default() as u8;
|
|
||||||
let bar_type = bar_type.unwrap_or_default();
|
|
||||||
|
|
||||||
let text = text.as_bytes();
|
|
||||||
let len = text.len() as u8;
|
|
||||||
let is_num = is_numeric(text);
|
|
||||||
match bar_type {
|
|
||||||
BARType::Upca if len != 11 || !is_num => Err(EscPosError::InvalidBarcodeLength(
|
|
||||||
"UCPA Requires 11 numbers.".to_string(),
|
|
||||||
)),
|
|
||||||
|
|
||||||
BARType::Ean13 if len != 12 || !is_num => Err(EscPosError::InvalidBarcodeLength(
|
|
||||||
"EAN13 Requires 12 numbers.".to_string(),
|
|
||||||
)),
|
|
||||||
_ => Ok(()),
|
|
||||||
}?;
|
|
||||||
|
|
||||||
let mut buf: Vec<u8> = vec![];
|
|
||||||
buf.extend([
|
|
||||||
0x1D,
|
|
||||||
0x68,
|
|
||||||
height,
|
|
||||||
0x1D,
|
|
||||||
0x77,
|
|
||||||
mod_width,
|
|
||||||
0x1d,
|
|
||||||
0x48,
|
|
||||||
text_position,
|
|
||||||
0x1D,
|
|
||||||
0x6B,
|
|
||||||
bar_type as u8,
|
|
||||||
len,
|
|
||||||
]);
|
|
||||||
buf.extend(text);
|
|
||||||
self.extend(&buf);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
252
src/main.rs
252
src/main.rs
|
|
@ -1,200 +1,70 @@
|
||||||
use quick_xml::de::from_str;
|
use nom::{
|
||||||
use serde::de::IntoDeserializer;
|
IResult, Parser,
|
||||||
use serde::{Deserialize, de};
|
branch::alt,
|
||||||
use std::io::{Read, Write, stdout};
|
bytes::complete::{tag, take_until},
|
||||||
use std::{error::Error, io::stdin};
|
character::complete::{line_ending, not_line_ending},
|
||||||
|
combinator::{map, success},
|
||||||
|
error::Error,
|
||||||
|
multi::fold,
|
||||||
|
sequence::{delimited, preceded},
|
||||||
|
};
|
||||||
|
use std::vec::Vec;
|
||||||
|
|
||||||
mod escpos;
|
enum Fragment<'a> {
|
||||||
use escpos::{BARTextPosition, BARType, EscPosBuilder, ImageOrientation, QREcc, TextEffect};
|
Literal(&'a str),
|
||||||
|
Header(&'a str),
|
||||||
#[derive(Debug, Deserialize)]
|
|
||||||
struct Document {
|
|
||||||
#[serde(rename = "$value")]
|
|
||||||
pub children: Vec<Node>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
fn main() {
|
||||||
#[serde(rename_all = "lowercase")]
|
let input = concat!(
|
||||||
enum Node {
|
// "Hallo daar!\n",
|
||||||
#[serde(rename = "$text")]
|
// "Hoe gaat het?\n",
|
||||||
PlainText(String),
|
"# Met mij gaat het goed\n",
|
||||||
|
"Met jou\n",
|
||||||
#[serde(rename = "h")]
|
"## Ja ook goed\n",
|
||||||
Heading(Heading),
|
// "Echt?\n",
|
||||||
|
// "### Wow (not matched because we only accept 1 or 2 '#')\n",
|
||||||
#[serde(rename = "p")]
|
// "#StartOfFileHeaderNoSpace <-- not matched because we require a space after '#'\n",
|
||||||
Text(Text),
|
// " ## Indented header\n"
|
||||||
|
|
||||||
#[serde(rename = "img")]
|
|
||||||
Img(Img),
|
|
||||||
|
|
||||||
#[serde(rename = "qr")]
|
|
||||||
Qr(Qr),
|
|
||||||
|
|
||||||
#[serde(rename = "bar")]
|
|
||||||
Barcode(Barcode),
|
|
||||||
|
|
||||||
#[serde(rename = "br")]
|
|
||||||
Feed(Feed),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
|
||||||
struct Heading {
|
|
||||||
#[serde(rename = "$value")]
|
|
||||||
pub value: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
|
||||||
struct Text {
|
|
||||||
#[serde(rename = "@effects")]
|
|
||||||
#[serde(deserialize_with = "deserialize_effects")]
|
|
||||||
#[serde(default)]
|
|
||||||
pub style: Option<Vec<TextEffect>>,
|
|
||||||
|
|
||||||
#[serde(rename = "$value")]
|
|
||||||
pub value: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
|
||||||
struct Img {
|
|
||||||
#[serde(rename = "@src")]
|
|
||||||
pub src: String,
|
|
||||||
|
|
||||||
#[serde(rename = "@orientation")]
|
|
||||||
#[serde(default)]
|
|
||||||
pub orientation: Option<ImageOrientation>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
|
||||||
struct Qr {
|
|
||||||
#[serde(rename = "@size")]
|
|
||||||
#[serde(default)]
|
|
||||||
pub size: Option<u8>,
|
|
||||||
|
|
||||||
#[serde(rename = "@ecc")]
|
|
||||||
#[serde(default)]
|
|
||||||
pub ecc: Option<QREcc>,
|
|
||||||
|
|
||||||
#[serde(rename = "$value")]
|
|
||||||
pub value: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
|
||||||
struct Barcode {
|
|
||||||
#[serde(rename = "@height")]
|
|
||||||
#[serde(default)]
|
|
||||||
pub height: Option<u8>,
|
|
||||||
|
|
||||||
#[serde(rename = "@mod_width")]
|
|
||||||
#[serde(default)]
|
|
||||||
pub mod_width: Option<u8>,
|
|
||||||
|
|
||||||
#[serde(rename = "@text_position")]
|
|
||||||
#[serde(default)]
|
|
||||||
pub text_position: Option<BARTextPosition>,
|
|
||||||
|
|
||||||
#[serde(rename = "@bar_type")]
|
|
||||||
#[serde(default)]
|
|
||||||
pub bar_type: Option<BARType>,
|
|
||||||
|
|
||||||
#[serde(rename = "$value")]
|
|
||||||
pub value: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
|
||||||
struct Feed {
|
|
||||||
#[serde(rename = "@amount")]
|
|
||||||
#[serde(default)]
|
|
||||||
pub amount: Option<u8>,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn deserialize_effects<'de, D>(deserializer: D) -> Result<Option<Vec<TextEffect>>, D::Error>
|
|
||||||
where
|
|
||||||
D: de::Deserializer<'de>,
|
|
||||||
{
|
|
||||||
let opt: Option<String> = Option::deserialize(deserializer)?;
|
|
||||||
let mut out: Vec<TextEffect> = Vec::new();
|
|
||||||
if let Some(s) = opt {
|
|
||||||
for raw in s.split(';') {
|
|
||||||
let tok = raw.trim();
|
|
||||||
if tok.is_empty() {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
let deserialized = TextEffect::deserialize(tok.into_deserializer())?;
|
|
||||||
out.push(deserialized);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(Some(out))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn handle_document(doc: Document, builder: &mut EscPosBuilder) -> Result<(), Box<dyn Error>> {
|
|
||||||
for node in doc.children {
|
|
||||||
match node {
|
|
||||||
Node::PlainText(ref t) => {
|
|
||||||
builder.write_text(t.trim(), None);
|
|
||||||
builder.write_feed(None);
|
|
||||||
}
|
|
||||||
Node::Heading(t) => {
|
|
||||||
builder.write_text(
|
|
||||||
&t.value,
|
|
||||||
Some(&[TextEffect::DoubleHeight, TextEffect::DoubleWidth]),
|
|
||||||
);
|
);
|
||||||
builder.write_feed(None);
|
|
||||||
}
|
|
||||||
Node::Text(t) => {
|
|
||||||
if let Some(ref list) = t.style {
|
|
||||||
builder.write_text(&t.value, Some(list));
|
|
||||||
} else {
|
|
||||||
builder.write_text(&t.value, None);
|
|
||||||
}
|
|
||||||
|
|
||||||
builder.write_feed(None);
|
let vec = Vec::<u8>::new();
|
||||||
}
|
use nom::bytes::complete::tag;
|
||||||
|
use nom::sequence::preceded;
|
||||||
|
|
||||||
Node::Img(img) => {
|
println!("{:?}", parse_fragment(input))
|
||||||
let dynimg = image::open(&img.src)?;
|
|
||||||
builder.write_bitmap(&dynimg, img.orientation);
|
|
||||||
}
|
|
||||||
Node::Qr(q) => {
|
|
||||||
let value = q.value;
|
|
||||||
let value = value.trim();
|
|
||||||
|
|
||||||
if value.is_empty() {
|
|
||||||
return Ok(());
|
|
||||||
};
|
|
||||||
builder.write_qr(&value, q.size, q.ecc);
|
|
||||||
}
|
|
||||||
Node::Barcode(b) => {
|
|
||||||
let value = b.value;
|
|
||||||
let value = value.trim();
|
|
||||||
|
|
||||||
if value.is_empty() {
|
|
||||||
return Ok(());
|
|
||||||
};
|
|
||||||
builder.write_barcode(
|
|
||||||
&value,
|
|
||||||
b.height,
|
|
||||||
b.mod_width,
|
|
||||||
b.text_position,
|
|
||||||
b.bar_type,
|
|
||||||
)?;
|
|
||||||
}
|
|
||||||
Node::Feed(f) => {
|
|
||||||
builder.write_feed(f.amount);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() -> Result<(), Box<dyn Error>> {
|
fn parse_header<'a>(input: &'a str) -> IResult<&'a str, &str, ()> {
|
||||||
let mut xml: String = String::new();
|
delimited(tag::<&str, &str, ()>("# "), not_line_ending, line_ending).parse(input)
|
||||||
stdin().read_to_string(&mut xml)?;
|
|
||||||
let doc: Document = from_str::<Document>(&xml)?;
|
|
||||||
|
|
||||||
let mut builder = EscPosBuilder::new(384);
|
|
||||||
handle_document(doc, &mut builder)?;
|
|
||||||
|
|
||||||
stdout().write(&builder.content)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
fn parse_fragment<'a>(input: &'a str) -> IResult<&str, Fragment, ()> {
|
||||||
|
alt((
|
||||||
|
map(parse_header, Fragment::Header),
|
||||||
|
map(not_line_ending, Fragment::Literal),
|
||||||
|
))
|
||||||
|
.parse(input)
|
||||||
|
}
|
||||||
|
fn parse_string<'a>(input: &'a str) -> IResult<&'a str, Vec<Fragment<'a>>, ()> {
|
||||||
|
fold(
|
||||||
|
0..,
|
||||||
|
parse_fragment,
|
||||||
|
Vec::<u8>::new(),
|
||||||
|
|mut vector, fragment| vector,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// fn parse_fragment<'a, E>(input: &'a str) -> IResult<&'a str, StringFragment<'a>, E>
|
||||||
|
// where
|
||||||
|
// E: ParseError<&'a str> + FromExternalError<&'a str, std::num::ParseIntError>,
|
||||||
|
// {
|
||||||
|
// alt((map(parse_header, Fragment::Header)))
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// fn parse_header<'a, E: ParseError<&'a str>>(input: &'a str) -> IResult<&'a str, &'a str, E> {
|
||||||
|
// preceded(
|
||||||
|
// permutation((char('\n'), many_m_n(1, 2, char('#')))),
|
||||||
|
// take_until("\n"),
|
||||||
|
// )
|
||||||
|
// .parse(input)
|
||||||
|
// }
|
||||||
|
|
|
||||||
35
structure.md
35
structure.md
|
|
@ -1,35 +0,0 @@
|
||||||
## Structs
|
|
||||||
### EscPosBuilder
|
|
||||||
Fields:
|
|
||||||
- content: Vec
|
|
||||||
- max_width: u16
|
|
||||||
### Document
|
|
||||||
Fields:
|
|
||||||
- children: Vec
|
|
||||||
### Heading
|
|
||||||
Fields:
|
|
||||||
- value: String
|
|
||||||
### Text
|
|
||||||
Fields:
|
|
||||||
- style: Option
|
|
||||||
- value: String
|
|
||||||
### Img
|
|
||||||
Fields:
|
|
||||||
- src: String
|
|
||||||
- orientation: Option
|
|
||||||
### Qr
|
|
||||||
Fields:
|
|
||||||
- size: Option
|
|
||||||
- ecc: Option
|
|
||||||
- value: String
|
|
||||||
### Barcode
|
|
||||||
Fields:
|
|
||||||
- height: Option
|
|
||||||
- mod_width: Option
|
|
||||||
- text_position: Option
|
|
||||||
- bar_type: Option
|
|
||||||
- value: String
|
|
||||||
### Feed
|
|
||||||
Fields:
|
|
||||||
- amount: Option
|
|
||||||
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue