commit 4eb4a57a8cb99137626be94904feb01a146c1a41 Author: Michael Mikovsky <77305074+Astatin3@users.noreply.github.com> Date: Thu Sep 18 14:05:44 2025 -0600 Add rasterization to bitmap to sixel. diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..ed2ba2d --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,69 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "fontdue" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e57e16b3fe8ff4364c0661fdaac543fb38b29ea9bc9c2f45612d90adf931d2b" +dependencies = [ + "hashbrown", + "ttf-parser", +] + +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash", +] + +[[package]] +name = "icy_sixel" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccc0a9c4770bc47b0a933256a496cfb8b6531f753ea9bccb19c6dff0ff7273fc" + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "rustex" +version = "0.1.0" +dependencies = [ + "fontdue", + "icy_sixel", + "lazy_static", +] + +[[package]] +name = "ttf-parser" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c591d83f69777866b9126b24c6dd9a18351f177e49d625920d19f989fd31cf8" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..da73cc1 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "rustex" +version = "0.1.0" +edition = "2024" + +[dependencies] +fontdue = "0.9.3" +icy_sixel = "0.1.3" +lazy_static = "1.5.0" diff --git a/fonts/KaTeX_Main-Regular.ttf b/fonts/KaTeX_Main-Regular.ttf new file mode 100644 index 0000000..dd45e1e Binary files /dev/null and b/fonts/KaTeX_Main-Regular.ttf differ diff --git a/src/fonts.rs b/src/fonts.rs new file mode 100644 index 0000000..4c2a1ae --- /dev/null +++ b/src/fonts.rs @@ -0,0 +1,10 @@ +use fontdue::Font; +use lazy_static::lazy_static; + +pub static KaTeX_Main_Regular: &'static [u8] = include_bytes!("../fonts/KaTeX_Main-Regular.ttf"); + +lazy_static! { + pub static ref FONTS: Vec = vec![ + Font::from_bytes(KaTeX_Main_Regular, fontdue::FontSettings::default()).unwrap(), + ]; +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..589c912 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,105 @@ +#[allow(non_upper_case_globals)] + +mod fonts; + +use fontdue::{layout::{CoordinateSystem, Layout, LayoutSettings, TextStyle}}; +use icy_sixel::{ + DiffusionMethod, MethodForLargest, MethodForRep, PixelFormat, Quality, sixel_string, +}; + +fn main() -> Result<(), std::fmt::Error> { + let mut rustex = RusTeX::new(); + rustex.add_text(&TextStyle::new("testi12345ng! e^23", 35.0, 0)); + let (bitmap, (width, height)) = rustex.rasterize(); + print_bitmap(&bitmap, width, height); + Ok(()) +} + +struct RusTeX { + layout: Layout, +} + +impl RusTeX { + pub fn new() -> Self { + let mut layout = Layout::new(CoordinateSystem::PositiveYDown); + // By default, layout is initialized with the default layout settings. This call is redundant, but + // demonstrates setting the value with your custom settings. + layout.reset(&LayoutSettings { + ..LayoutSettings::default() + }); + + Self { + layout, + } + } + + pub fn add_text(&mut self, text_style: &TextStyle) { + self.layout.append(&fonts::FONTS, text_style); + } + + pub fn rasterize(&mut self) -> (Vec, (usize, usize)) { + let (mut maxx, mut maxy): (usize, usize) = (0,0); + for glyph in self.layout.glyphs() { + maxx = maxx.max(glyph.x as usize + glyph.width); + maxy = maxy.max(glyph.y as usize + glyph.height); + } + + let mut bitmap: Vec = vec![0; maxx*maxy]; + + for glyph in self.layout.glyphs() { + + let font = &fonts::FONTS[glyph.font_index]; + let (metrics, char_bitmap) = font.rasterize_config(glyph.key); + + + + for y in 0..metrics.height { + for x in 0..metrics.width { + let pixel = char_bitmap[y*glyph.width + x]; + + // let index = (x+glyph.x as usize)*maxy + (y+glyph.y as usize); + let index = (y+glyph.y as usize)*maxx + (x+glyph.x as usize); + + bitmap[index] = pixel; + } + } + } + + (bitmap, (maxx, maxy)) + } +} + +/// Prints a 1 byte per pixel greyscale bitmap to Sixel format in console +fn print_bitmap(bitmap: &Vec, width: usize, height: usize) { + let mut bitmap_rgb888 = vec![0; width*height*3]; + + + for y in 0..height { + for x in 0..width { + let index = y*width + x; + + let pixel = bitmap[index]; + + bitmap_rgb888[index*3] = pixel; + bitmap_rgb888[index*3 + 1] = pixel; + bitmap_rgb888[index*3 + 2] = pixel; + } + } + + + + + let sixel_data = sixel_string( + &bitmap_rgb888, + width as i32, + height as i32, + PixelFormat::RGB888, + DiffusionMethod::None, + MethodForLargest::Auto, + MethodForRep::Auto, + Quality::AUTO, + ).unwrap(); + + println!("{}", sixel_data); + +} \ No newline at end of file