mirror of https://github.com/Aloxaf/silicon
Merge pull request #181 from tizee/ligature
This commit is contained in:
commit
e3311d72de
|
@ -537,6 +537,20 @@ dependencies = [
|
|||
"wasi 0.10.2+wasi-snapshot-preview1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "harfbuzz-sys"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bf8c27ca13930dc4ffe474880040fe9e0f03c2121600dc9c95423624cab3e467"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"core-graphics",
|
||||
"core-text",
|
||||
"foreign-types",
|
||||
"freetype",
|
||||
"pkg-config",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.9.1"
|
||||
|
@ -1301,6 +1315,7 @@ dependencies = [
|
|||
"dirs",
|
||||
"env_logger",
|
||||
"font-kit",
|
||||
"harfbuzz-sys",
|
||||
"image",
|
||||
"imageproc",
|
||||
"lazy_static",
|
||||
|
|
|
@ -12,7 +12,6 @@ edition = "2018"
|
|||
[dependencies]
|
||||
dirs = "3.0"
|
||||
imageproc = "0.22.0"
|
||||
font-kit = "0.10"
|
||||
clipboard = "0.5.0"
|
||||
tempfile = "3.1.0"
|
||||
conv = "0.3.3"
|
||||
|
@ -55,6 +54,13 @@ default-features = false
|
|||
features = ["termcolor", "atty", "humantime"]
|
||||
optional = true
|
||||
|
||||
[dependencies.font-kit]
|
||||
version= "0.10"
|
||||
features= ["loader-freetype-default", "source-fontconfig-default"]
|
||||
|
||||
[dependencies.harfbuzz-sys]
|
||||
version="0.5.0"
|
||||
|
||||
[patch.crates-io]
|
||||
pathfinder_simd = { version = "0.5.0", git = "https://github.com/servo/pathfinder" }
|
||||
|
||||
|
|
83
src/font.rs
83
src/font.rs
|
@ -12,6 +12,8 @@
|
|||
//! font.draw_text_mut(&mut image, Rgb([255, 0, 0]), 0, 0, FontStyle::REGULAR, "Hello, world");
|
||||
//! ```
|
||||
use crate::error::FontError;
|
||||
use crate::hb_wrapper::{feature_from_tag, HBBuffer, HBFont};
|
||||
use anyhow::Result;
|
||||
use conv::ValueInto;
|
||||
use font_kit::canvas::{Canvas, Format, RasterizationOptions};
|
||||
use font_kit::font::Font;
|
||||
|
@ -191,17 +193,6 @@ impl FontCollection {
|
|||
Ok(Self(fonts))
|
||||
}
|
||||
|
||||
fn glyph_for_char(&self, c: char, style: FontStyle) -> Option<(u32, &ImageFont, &Font)> {
|
||||
for font in &self.0 {
|
||||
let result = font.get_by_style(style);
|
||||
if let Some(id) = result.glyph_for_char(c) {
|
||||
return Some((id, font, result));
|
||||
}
|
||||
}
|
||||
eprintln!("[warning] No font found for character `{}`", c);
|
||||
None
|
||||
}
|
||||
|
||||
/// get max height of all the fonts
|
||||
pub fn get_font_height(&self) -> u32 {
|
||||
self.0
|
||||
|
@ -211,35 +202,57 @@ impl FontCollection {
|
|||
.unwrap()
|
||||
}
|
||||
|
||||
fn shape_text(&self, font: &mut HBFont, text: &str) -> Result<Vec<u32>> {
|
||||
// feature tags
|
||||
let features = vec![
|
||||
feature_from_tag("kern")?,
|
||||
feature_from_tag("clig")?,
|
||||
feature_from_tag("liga")?,
|
||||
];
|
||||
let mut buf = HBBuffer::new()?;
|
||||
buf.add_str(text);
|
||||
buf.guess_segments_properties();
|
||||
font.shape(&buf, features.as_slice());
|
||||
let hb_infos = buf.get_glyph_infos();
|
||||
let mut glyph_ids = Vec::new();
|
||||
for info in hb_infos.iter() {
|
||||
glyph_ids.push(info.codepoint);
|
||||
}
|
||||
Ok(glyph_ids)
|
||||
}
|
||||
|
||||
fn layout(&self, text: &str, style: FontStyle) -> (Vec<PositionedGlyph>, u32) {
|
||||
let mut delta_x = 0;
|
||||
let height = self.get_font_height();
|
||||
|
||||
let glyphs = text
|
||||
.chars()
|
||||
.filter_map(|c| {
|
||||
self.glyph_for_char(c, style).map(|(id, imfont, font)| {
|
||||
let raster_rect = font
|
||||
.raster_bounds(
|
||||
id,
|
||||
imfont.size,
|
||||
Transform2F::default(),
|
||||
HintingOptions::None,
|
||||
RasterizationOptions::GrayscaleAa,
|
||||
)
|
||||
.unwrap();
|
||||
let position =
|
||||
Vector2I::new(delta_x as i32, height as i32) + raster_rect.origin();
|
||||
delta_x += Self::get_glyph_width(font, id, imfont.size);
|
||||
let imfont = self.0.get(0).unwrap();
|
||||
let font = imfont.get_by_style(style);
|
||||
let mut hb_font = HBFont::new(font);
|
||||
// apply font features especially ligature with a shape engine
|
||||
let shaped_glyphs = self.shape_text(&mut hb_font, text).unwrap();
|
||||
|
||||
PositionedGlyph {
|
||||
id,
|
||||
font: font.clone(),
|
||||
size: imfont.size,
|
||||
raster_rect,
|
||||
position,
|
||||
}
|
||||
})
|
||||
let glyphs = shaped_glyphs
|
||||
.iter()
|
||||
.map(|id| {
|
||||
let raster_rect = font
|
||||
.raster_bounds(
|
||||
*id,
|
||||
imfont.size,
|
||||
Transform2F::default(),
|
||||
HintingOptions::None,
|
||||
RasterizationOptions::GrayscaleAa,
|
||||
)
|
||||
.unwrap();
|
||||
let position = Vector2I::new(delta_x as i32, height as i32) + raster_rect.origin();
|
||||
delta_x += Self::get_glyph_width(font, *id, imfont.size);
|
||||
|
||||
PositionedGlyph {
|
||||
id: *id,
|
||||
font: font.clone(),
|
||||
size: imfont.size,
|
||||
raster_rect,
|
||||
position,
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
|
|
|
@ -0,0 +1,117 @@
|
|||
use anyhow::{ensure, Result};
|
||||
use core::slice;
|
||||
// font_kit already has a wrapper around freetype called Font so use it directly
|
||||
use font_kit::font::Font;
|
||||
use font_kit::loaders::freetype::NativeFont;
|
||||
// use harfbuzz for shaping ligatures
|
||||
pub use harfbuzz::*;
|
||||
use harfbuzz_sys as harfbuzz;
|
||||
use std::mem;
|
||||
|
||||
/// font feature tag
|
||||
pub fn feature_from_tag(tag: &str) -> Result<hb_feature_t> {
|
||||
unsafe {
|
||||
let mut feature = mem::zeroed();
|
||||
ensure!(
|
||||
hb_feature_from_string(
|
||||
tag.as_ptr() as *const i8,
|
||||
tag.len() as i32,
|
||||
&mut feature as *mut _
|
||||
) != 0,
|
||||
"hb_feature_from_string failed for {}",
|
||||
tag
|
||||
);
|
||||
Ok(feature)
|
||||
}
|
||||
}
|
||||
|
||||
/// Harfbuzz font
|
||||
pub struct HBFont {
|
||||
font: *mut hb_font_t,
|
||||
}
|
||||
|
||||
// harfbuzz freetype integration
|
||||
extern "C" {
|
||||
pub fn hb_ft_font_create_referenced(face: NativeFont) -> *mut hb_font_t; // the same as hb_face_t
|
||||
}
|
||||
|
||||
impl Drop for HBFont {
|
||||
fn drop(&mut self) {
|
||||
unsafe { hb_font_destroy(self.font) }
|
||||
}
|
||||
}
|
||||
|
||||
impl HBFont {
|
||||
pub fn new(face: &Font) -> HBFont {
|
||||
HBFont {
|
||||
font: unsafe { hb_ft_font_create_referenced(face.native_font() as _) },
|
||||
}
|
||||
}
|
||||
pub fn shape(&mut self, buffer: &HBBuffer, features: &[hb_feature_t]) {
|
||||
unsafe {
|
||||
hb_shape(
|
||||
self.font,
|
||||
buffer.buffer,
|
||||
features.as_ptr(),
|
||||
features.len() as u32,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Harfbuzz buffer
|
||||
pub struct HBBuffer {
|
||||
buffer: *mut hb_buffer_t,
|
||||
}
|
||||
|
||||
impl Drop for HBBuffer {
|
||||
fn drop(&mut self) {
|
||||
unsafe { hb_buffer_destroy(self.buffer) }
|
||||
}
|
||||
}
|
||||
|
||||
impl HBBuffer {
|
||||
pub fn new() -> Result<HBBuffer> {
|
||||
let hb_buf = unsafe { hb_buffer_create() };
|
||||
ensure!(
|
||||
unsafe { hb_buffer_allocation_successful(hb_buf) } != 0,
|
||||
"hb_buffer_create failed!"
|
||||
);
|
||||
Ok(HBBuffer { buffer: hb_buf })
|
||||
}
|
||||
|
||||
pub fn guess_segments_properties(&mut self) {
|
||||
unsafe { hb_buffer_guess_segment_properties(self.buffer) };
|
||||
}
|
||||
|
||||
pub fn add_utf8(&mut self, s: &[u8]) {
|
||||
unsafe {
|
||||
hb_buffer_add_utf8(
|
||||
self.buffer,
|
||||
s.as_ptr() as *const i8,
|
||||
s.len() as i32,
|
||||
0,
|
||||
s.len() as i32,
|
||||
);
|
||||
}
|
||||
}
|
||||
pub fn add_str(&mut self, s: &str) {
|
||||
self.add_utf8(s.as_bytes());
|
||||
}
|
||||
|
||||
pub fn get_glyph_infos(&mut self) -> &[hb_glyph_info_t] {
|
||||
unsafe {
|
||||
let mut len: u32 = 0;
|
||||
let info = hb_buffer_get_glyph_infos(self.buffer, &mut len as *mut u32);
|
||||
slice::from_raw_parts(info, len as usize)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_glyph_positions(&mut self) -> &[hb_glyph_position_t] {
|
||||
unsafe {
|
||||
let mut len: u32 = 0;
|
||||
let info = hb_buffer_get_glyph_positions(self.buffer, &mut len as *mut u32);
|
||||
slice::from_raw_parts(info, len as usize)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -39,4 +39,5 @@ pub mod directories;
|
|||
pub mod error;
|
||||
pub mod font;
|
||||
pub mod formatter;
|
||||
pub mod hb_wrapper;
|
||||
pub mod utils;
|
||||
|
|
Loading…
Reference in New Issue