|
| 1 | +use std::path::PathBuf; |
| 2 | + |
| 3 | +use clap::Parser; |
| 4 | +use color_eyre::eyre::{Context, ContextCompat, OptionExt}; |
| 5 | +use image::{GrayImage, ImageFormat}; |
| 6 | +use sdo_tool::ttf::{glyph_index_vec, LigatureInfo}; |
| 7 | +use signum::chsets::{ |
| 8 | + metrics::{FontMetrics, DEFAULT_FONT_SIZE}, |
| 9 | + printer::PrinterKind, |
| 10 | +}; |
| 11 | +use ttf_parser::{Face, GlyphId}; |
| 12 | + |
| 13 | +#[derive(Parser)] |
| 14 | +/// Options for decoding an ATARI String |
| 15 | +pub struct Opts { |
| 16 | + /// The file to convert |
| 17 | + font_file: PathBuf, |
| 18 | + |
| 19 | + /// The ligature to check |
| 20 | + #[clap(default_value = "ch")] |
| 21 | + ligature: String, |
| 22 | + |
| 23 | + #[clap(short, long, default_value = "0")] |
| 24 | + index: u32, |
| 25 | +} |
| 26 | + |
| 27 | +fn main() -> color_eyre::Result<()> { |
| 28 | + color_eyre::install()?; |
| 29 | + let opt: Opts = Opts::parse(); |
| 30 | + let data = std::fs::read(&opt.font_file)?; |
| 31 | + |
| 32 | + let face = Face::parse(&data, opt.index)?; |
| 33 | + let l = LigatureInfo::new(&face); |
| 34 | + |
| 35 | + let lig = opt.ligature.chars().collect::<Vec<_>>(); |
| 36 | + |
| 37 | + let glyphs = glyph_index_vec(&face, &lig).ok_or_eyre("Not all glyphs in font")?; |
| 38 | + let lig_glyph = l.find(&glyphs); |
| 39 | + println!("{:?} => {:?}", lig, lig_glyph); |
| 40 | + |
| 41 | + let font = fontdue::Font::from_bytes( |
| 42 | + &data[..], |
| 43 | + fontdue::FontSettings { |
| 44 | + collection_index: opt.index, |
| 45 | + scale: 40.0, |
| 46 | + load_substitutions: true, |
| 47 | + }, |
| 48 | + ) |
| 49 | + .expect("already ttf parsed"); |
| 50 | + if let Some(gl) = lig_glyph { |
| 51 | + let img = _raster(&font, gl)?; |
| 52 | + _show(&img)?; |
| 53 | + } |
| 54 | + |
| 55 | + Ok(()) |
| 56 | +} |
| 57 | + |
| 58 | +fn _raster(font: &fontdue::Font, g: GlyphId) -> color_eyre::Result<GrayImage> { |
| 59 | + let font_size = DEFAULT_FONT_SIZE; |
| 60 | + let pk = PrinterKind::Needle24; |
| 61 | + let fm = FontMetrics::new(pk, font_size); |
| 62 | + let px_per_em = fm.em_square_pixels(); |
| 63 | + |
| 64 | + let (metrics, bitmap) = font.rasterize_indexed(g.0, px_per_em as f32); |
| 65 | + let inverted = bitmap.iter().copied().map(|c| 255 - c).collect(); |
| 66 | + let img = GrayImage::from_vec(metrics.width as u32, metrics.height as u32, inverted) |
| 67 | + .context("image creation")?; |
| 68 | + Ok(img) |
| 69 | +} |
| 70 | + |
| 71 | +fn _show(img: &GrayImage) -> color_eyre::Result<()> { |
| 72 | + let mut tmpfile = |
| 73 | + tempfile::NamedTempFile::with_suffix(".png").context("failed to create tempfile")?; |
| 74 | + img.write_to(&mut tmpfile, ImageFormat::Png) |
| 75 | + .expect("image to PDF"); |
| 76 | + |
| 77 | + let mut child = std::process::Command::new("eog") |
| 78 | + .arg(tmpfile.path()) |
| 79 | + .spawn()?; |
| 80 | + |
| 81 | + let _o = child.wait()?; |
| 82 | + Ok(()) |
| 83 | +} |
0 commit comments