#![doc(html_root_url = "https://docs.rs/font-glyph/0.3.1")]
use std::{error::{Error}, path};
use bezier_interpolation::ncr::{recurse, Itpl, Path2d, OfsSclPch, GlyphContour};
use freetype as ft;
use image;
use image::imageops;
use plotters::prelude::*;
use plotters::prelude::full_palette::{GREY, BLUEGREY, PINK, BROWN};
use plotters::prelude::full_palette::{DEEPORANGE, DEEPPURPLE, ORANGE, PURPLE};
use plotters::prelude::full_palette::{TEAL, AMBER, INDIGO, LIME};
use plotters::prelude::full_palette::{LIGHTBLUE, LIGHTGREEN};
use plotters::backend::RGBPixel;
use plotters::coord::Shift;
use unicode_width::UnicodeWidthStr;
pub struct FreeTypeManager<'a> {
pub fname: String,
pub libft: ft::Library,
pub face: ft::Face<'a>,
pub last: u32
}
impl FreeTypeManager<'_> {
pub fn new(fname: &str) -> Result<FreeTypeManager, Box<dyn Error>> {
let libft = ft::Library::init()?;
let face = libft.new_face(fname, 0)?;
face.set_char_size(40 * 64, 0, 50, 0)?; face.set_pixel_sizes(24u32, 24u32)?; Ok(FreeTypeManager{fname: fname.to_string(), libft: libft,
face: face, last: 0u32})
}
pub fn glyph_metrics_inf(&self, glyph: &ft::GlyphSlot) -> ft::GlyphMetrics {
let gm = glyph.metrics(); println!(" wh({}, {})", gm.width, gm.height);
gm
}
pub fn face_size_metrics_inf(&self) -> ft::ffi::FT_Size_Metrics { let sm = self.face.size_metrics().unwrap(); println!(" ppem: ({}, {})", sm.x_ppem, sm.y_ppem);
println!(" scale: ({}, {})", sm.x_scale, sm.y_scale);
println!(" ascender: {} descender: {}", sm.ascender, sm.descender);
println!(" height: {} max_advance: {}", sm.height, sm.max_advance);
sm
}
pub fn glyph2poly(&mut self,
cp: u32) -> Result<(Vec<GlyphContour>, ft::GlyphMetrics), Box<dyn Error>> {
let ch = char::from_u32(cp).ok_or("not unicode 32")?;
let sf = format!("{}", ch);
print!("cp: 0x{:08x} {}{}", cp, sf, if sf.width() == 1 {" "} else {""});
self.face.load_char(cp as usize, ft::face::NO_SCALE)?; let glyph: &ft::GlyphSlot = self.face.glyph();
let outline: &ft::Outline = &glyph.outline().ok_or("no glyph")?;
let kern_ofs: Itpl<i32> = match self.last {
0u32 => (0, 0),
_ => {
let cpi_l = self.face.get_char_index(self.last as usize);
let cpi_r = self.face.get_char_index(cp as usize);
let kn: ft::Vector = self.face.get_kerning(cpi_l, cpi_r,
ft::face::KerningMode::KerningDefault)?;
(kn.x, kn.y) }
};
self.last = cp;
print!(" kerning: {:?}", kern_ofs);
let gm = self.glyph_metrics_inf(glyph);
Ok((kern_ofs.outline2contours(outline), gm))
}
pub fn draw_str_glyph(&mut self, bm: &DrawingArea<BitMapBackend<'_>, Shift>,
fgbg: &Vec<&RGBColor>, pals: &Vec<Vec<&RGBColor>>, ctrl: bool,
msg: &str, o: OfsSclPch) -> Result<OfsSclPch, Box<dyn Error>> {
let mut osp: OfsSclPch = o;
self.last = 0u32;
for ch in msg.chars() {
let (poly_glyph, gm) = self.glyph2poly(ch as u32)?; recurse(&poly_glyph, &|pg: &Vec<GlyphContour>, draw_glyph| {
for g in pg {
let _ = match bm.draw(
&Polygon::new(g.osp_pts(osp, false), fgbg[if g.lr {1} else {0}])) {
Err(e) => { println!("{:?}", e); Err(Box::new(std::fmt::Error{})) },
Ok(_) => Ok(())
};
if g.children.len() > 0 { draw_glyph(&g.children); }
}
});
let ctrl_osp = osp; osp.0.0 += (gm.width as f64 * osp.1) as i32 + osp.2;
if !ctrl { continue; };
let ss: Vec<Vec<ShapeStyle>> = pals.iter().map(|v| v.iter().map(|&c|
ShapeStyle::from(c)).collect()).collect();
for g in poly_glyph.iter() {
let pal = &ss[if g.lr {1} else {0}];
let mut sz = g.control.len();
if sz > 0 && g.control[0] == g.control[sz - 1] { sz -= 1; }
for (i, &c) in g.osp_pts(ctrl_osp, true).iter().enumerate() {
if g.spec[i] & 1u8 != 0 { continue; }
let mut s = pal[g.spec[i] as usize / 2]; if sz > 1 && i == sz - 1 { s = pal[7]; } if i == 0 {
bm.draw(&Cross::new(c, 8, s.stroke_width(2)))?; }else{
let f = if g.spec[i] == 2 || g.spec[i] == 4 || g.spec[i] == 8 {
s.filled() }else{ s.stroke_width(1) };
bm.draw(&Circle::new(c, 4, f))?;
}
}
}
}
Ok(osp)
}
}
fn main() -> Result<(), Box<dyn Error>> {
let _cname: Vec<&str> = vec![
"GREY", "BLUEGREY", "PINK", "BROWN",
"DEEPORANGE", "DEEPPURPLE", "ORANGE", "PURPLE",
"TEAL", "AMBER", "INDIGO", "LIME",
"LIGHTBLUE", "LIGHTGREEN"];
let _cols: Vec<&RGBColor> = vec![
&GREY, &BLUEGREY, &PINK, &BROWN,
&&DEEPORANGE, &DEEPPURPLE, &ORANGE, &PURPLE,
&TEAL, &AMBER, &INDIGO, &LIME,
&LIGHTBLUE, &LIGHTGREEN];
let wsz: (u32, u32) = (1024, 768);
let isz: (u32, u32) = (256, 192);
let img = image::open("./img/_4c.png")?
.resize_exact(isz.0, isz.1, imageops::FilterType::Nearest)
.to_rgb8().to_vec();
let bases = vec!["./fonts", "/windows/fonts", "/prj/font-glyph/fonts"];
let ff = (0, "mikaP.ttf"); let pf = path::Path::new(bases[ff.0]).join(ff.1); let fname = pf.to_str().unwrap();
println!("loading: {} {}", fname, pf.is_file());
let mut ftm = FreeTypeManager::new(fname)?;
let sm = ftm.face_size_metrics_inf();
let buf: &mut Vec<u8> = &mut vec![0u8; (3 * wsz.0 * wsz.1) as usize];
let bb = match BitMapBackend::<RGBPixel>::with_buffer_and_format(buf, wsz) {
Err(be) => { println!("error: {:?}", be); Err(Box::new(std::fmt::Error{})) },
Ok(bm) => Ok(bm)
}?;
let bm = bb.into_drawing_area();
bm.fill(&BLUEGREY)?;
bm.draw(&BitMapElement::with_ref((256, 192), isz, &img).unwrap())?;
let fgbg = vec![&PURPLE, &PINK];
let fb = vec![&AMBER, &TEAL]; let pals = vec![ vec![&RED, &RED, &RED, &PURPLE, &RED, &PURPLE, &PURPLE, &BLACK],
vec![&BLUE, &BLUE, &BLUE, &INDIGO, &BLUE, &INDIGO, &INDIGO, &BLACK]];
let sc = sm.x_scale as f64 / 300000.0; let p = 16; ftm.draw_str_glyph(&bm, &fgbg, &pals, false, "ゐむみ", ((32, 512), sc, p))?;
ftm.draw_str_glyph(&bm, &fb, &pals, true, "WAあ個", ((0, 384 - 60), sc, p))?;
ftm.draw_str_glyph(&bm, &fb, &pals, true, "gぬQ鬱", ((0, 768 - 60), sc, p))?;
bm.present()?;
drop(bm);
let im = BitMapBackend::new("./img/_4c_fonts.png",
wsz).into_drawing_area();
im.draw(&BitMapElement::with_ref((0, 0), wsz, buf).unwrap())?;
im.present()?;
drop(im);
Ok(())
}