use crate::alphabet::{Alphabet, Char};
use crate::output::OutputOpts;
use bitflags::bitflags;
use std::cmp::min;
use std::collections::HashMap;
use std::default::Default;
#[derive(Debug)]
pub struct Font {
opts: FontOpts,
comment: String,
alphabet: Alphabet,
}
enum SmushStatus {
Valid,
End,
Invalid,
}
impl Font {
pub fn parse(data: &str) -> Self {
let mut lines = data.lines();
let header = lines.next().unwrap();
let opts = FontOpts::parse(header);
let mut comment = String::new();
for _ in 0..opts.num_comment_lines {
comment.push_str(lines.next().unwrap());
comment.push('\n');
}
let mut alphabet = Alphabet::new();
for c in [
32u8, 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, 196, 214, 220, 228, 246,
252, 223,
]
.iter()
{
let mut letter = Char::with_height(opts.height);
for l in letter.0.iter_mut() {
let mut line_i = lines.next().unwrap().as_bytes().to_vec();
let end_char = line_i.pop().unwrap();
while line_i.len() > 0 && line_i[line_i.len() - 1] == end_char {
line_i.pop().unwrap();
}
unsafe { *l = String::from_utf8_unchecked(line_i) }
}
alphabet.0.insert(*c, letter);
}
Self {
opts,
comment,
alphabet,
}
}
fn can_vertical_smush(&self, txt1: &str, txt2: &str) -> SmushStatus {
unimplemented!();
}
}
#[derive(Default, Debug)]
pub struct FontOpts {
pub hard_blank: char,
pub height: usize,
pub baseline: usize,
pub max_len: usize,
pub num_comment_lines: usize,
pub print_direction: u8,
pub code_tag_count: Option<usize>,
pub fitting_rules: FittingRules,
}
impl FontOpts {
fn parse(header: &str) -> Self {
let mut header = header.split(' ');
let hard_blank = header.next().unwrap().chars().nth(5).unwrap();
let height = header.next().map(|n| n.parse::<usize>().unwrap()).unwrap();
let baseline = header.next().map(|n| n.parse::<usize>().unwrap()).unwrap();
let max_len = header.next().map(|n| n.parse::<usize>().unwrap()).unwrap();
let old_layout = header.next().map(|n| n.parse::<i8>().unwrap()).unwrap();
let num_comment_lines = header.next().map(|n| n.parse::<usize>().unwrap()).unwrap();
let print_direction = header.next().map(|n| n.parse::<u8>().unwrap()).unwrap_or(0);
let full_layout = header
.next()
.map(|n| RuleFlags::from_bits(n.parse::<u16>().unwrap()).unwrap());
let code_tag_count = header.next().map(|n| n.parse::<usize>().unwrap());
let fitting_rules = FittingRules::from_flags(old_layout, full_layout);
Self {
hard_blank,
height,
baseline,
max_len,
num_comment_lines,
print_direction,
code_tag_count,
fitting_rules,
}
}
}
#[derive(Copy, Clone, Debug)]
enum Layout {
FullWidth,
Fitting,
Smushing,
}
impl Default for Layout {
fn default() -> Self {
Self::FullWidth
}
}
#[derive(Default, Debug)]
pub struct FittingRules {
hlayout: Layout,
vlayout: Layout,
hrules: [bool; 6],
vrules: [bool; 5],
}
#[rustfmt::skip]
bitflags! {
struct RuleFlags: u16 {
const NULL = 0b0000000000000000;
const HRULE1 = 0b0000000000000001;
const HRULE2 = 0b0000000000000010;
const HRULE3 = 0b0000000000000100;
const HRULE4 = 0b0000000000001000;
const HRULE5 = 0b0000000000010000;
const HRULE6 = 0b0000000000100000;
const HLAYOUT_FIT = 0b0000000001000000;
const HLAYOUT_SMUSH = 0b0000000010000000;
const VRULE1 = 0b0000000100000000;
const VRULE2 = 0b0000001000000000;
const VRULE3 = 0b0000010000000000;
const VRULE4 = 0b0000100000000000;
const VRULE5 = 0b0001000000000000;
const VLAYOUT_FIT = 0b0010000000000000;
const VLAYOUT_SMUSH = 0b0100000000000000;
}
}
impl FittingRules {
fn from_flags(oldflags: i8, newflags: Option<RuleFlags>) -> Self {
let mut hlayout: Layout;
let mut vlayout: Layout;
let mut hrules = [false; 6];
let mut vrules = [false; 5];
let mut flags: RuleFlags;
let parse_rules = |flags: RuleFlags| {
(
[
flags.contains(RuleFlags::HRULE1),
flags.contains(RuleFlags::HRULE2),
flags.contains(RuleFlags::HRULE3),
flags.contains(RuleFlags::HRULE4),
flags.contains(RuleFlags::HRULE5),
flags.contains(RuleFlags::HRULE6),
],
[
flags.contains(RuleFlags::VRULE1),
flags.contains(RuleFlags::VRULE2),
flags.contains(RuleFlags::VRULE3),
flags.contains(RuleFlags::VRULE4),
flags.contains(RuleFlags::VRULE5),
],
)
};
flags = if let Some(flags) = newflags {
hlayout = if flags.contains(RuleFlags::HLAYOUT_SMUSH) {
Layout::Smushing
} else if flags.contains(RuleFlags::HLAYOUT_FIT) {
Layout::Fitting
} else {
Layout::FullWidth
};
vlayout = if flags.contains(RuleFlags::VLAYOUT_SMUSH) {
Layout::Smushing
} else if flags.contains(RuleFlags::VLAYOUT_FIT) {
Layout::Fitting
} else {
Layout::FullWidth
};
flags
} else {
match oldflags {
-1 => {
hlayout = Layout::FullWidth;
vlayout = Layout::FullWidth;
RuleFlags::empty()
}
0 => {
hlayout = Layout::Fitting;
vlayout = Layout::FullWidth;
RuleFlags::empty()
}
_ => {
hlayout = Layout::Smushing;
vlayout = Layout::FullWidth;
RuleFlags::from_bits(oldflags as u16).unwrap()
}
}
};
let (hrules, vrules) = parse_rules(flags);
Self {
hlayout,
vlayout,
hrules,
vrules,
}
}
}