ocr 0.1.0

A minimalist OCR library for Rust — from scratch, no external engine
Documentation
use image::{DynamicImage, GrayImage, Luma};

use crate::font::{self, FONT_HEIGHT, FONT_WIDTH};

pub fn render_text(text: &str, scale: u32) -> DynamicImage {
    let lines: Vec<&str> = text.split('\n').collect();
    let char_w = FONT_WIDTH as u32 * scale;
    let char_h = FONT_HEIGHT as u32 * scale;
    let h_gap = scale;
    let v_gap = scale * 3;

    let max_line_len = lines.iter().map(|l| l.len()).max().unwrap_or(0) as u32;
    if max_line_len == 0 {
        return DynamicImage::ImageLuma8(GrayImage::from_pixel(1, 1, Luma([255])));
    }

    let img_w = max_line_len * (char_w + h_gap);
    let img_h = lines.len() as u32 * (char_h + v_gap) + v_gap;

    let mut img = GrayImage::from_pixel(img_w, img_h, Luma([255u8]));

    for (li, line) in lines.iter().enumerate() {
        let base_y = li as u32 * (char_h + v_gap) + v_gap / 2;
        for (ci, ch) in line.chars().enumerate() {
            let base_x = ci as u32 * (char_w + h_gap);
            if let Some(data) = font::glyph(ch) {
                let pixels = font::glyph_to_pixels(&data);
                for (y, row) in pixels.iter().enumerate() {
                    for (x, &on) in row.iter().enumerate() {
                        if on {
                            for dy in 0..scale {
                                for dx in 0..scale {
                                    let px = base_x + x as u32 * scale + dx;
                                    let py = base_y + y as u32 * scale + dy;
                                    if px < img_w && py < img_h {
                                        img.put_pixel(px, py, Luma([0u8]));
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    DynamicImage::ImageLuma8(img)
}