mirror of
https://github.com/wez/wezterm.git
synced 2024-12-23 05:12:40 +03:00
Add fontconfig support to locate fonts
Also restructure things a little bit to facilitate more robust fallback implementation.
This commit is contained in:
parent
d82860ef5a
commit
02bab4fcc7
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,3 +1,4 @@
|
|||||||
/Cargo.lock
|
/Cargo.lock
|
||||||
/target/
|
/target/
|
||||||
**/*.rs.bk
|
**/*.rs.bk
|
||||||
|
.*.sw*
|
||||||
|
@ -6,7 +6,7 @@ version = "0.1.0"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
failure = "0.1.1"
|
failure = "0.1.1"
|
||||||
hexdump = "0.1.0"
|
hexdump = "0.1.0"
|
||||||
sdl2 = {version="0.31.0", features=["bundled"]}
|
sdl2 = {version="0.31.0", features=["bundled", "static-link"]}
|
||||||
vte = "0.3.2"
|
vte = "0.3.2"
|
||||||
|
|
||||||
[dependencies.font]
|
[dependencies.font]
|
||||||
|
266
font/src/ft/fcwrap.rs
Normal file
266
font/src/ft/fcwrap.rs
Normal file
@ -0,0 +1,266 @@
|
|||||||
|
//! Slightly higher level helper for fontconfig
|
||||||
|
use failure::{self, Error};
|
||||||
|
pub use fontconfig::fontconfig::*;
|
||||||
|
use std::ffi::{CStr, CString};
|
||||||
|
use std::mem;
|
||||||
|
use std::ptr;
|
||||||
|
|
||||||
|
static FC_MONO: i32 = 100;
|
||||||
|
|
||||||
|
pub struct FontSet {
|
||||||
|
fonts: *mut FcFontSet,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for FontSet {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
FcFontSetDestroy(self.fonts);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct FontSetIter<'a> {
|
||||||
|
set: &'a FontSet,
|
||||||
|
position: isize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Iterator for FontSetIter<'a> {
|
||||||
|
type Item = Pattern;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
unsafe {
|
||||||
|
if self.position == (*self.set.fonts).nfont as isize {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
let pat = *(*self.set.fonts)
|
||||||
|
.fonts
|
||||||
|
.offset(self.position)
|
||||||
|
.as_mut()
|
||||||
|
.unwrap();
|
||||||
|
FcPatternReference(pat);
|
||||||
|
self.position += 1;
|
||||||
|
Some(Pattern { pat })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FontSet {
|
||||||
|
pub fn iter(&self) -> FontSetIter {
|
||||||
|
FontSetIter {
|
||||||
|
set: &self,
|
||||||
|
position: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add(&mut self, pat: &Pattern) {
|
||||||
|
unsafe {
|
||||||
|
FcFontSetAdd(self.fonts, pat.pat);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub enum MatchKind {
|
||||||
|
Pattern = FcMatchPattern as isize,
|
||||||
|
Font = FcMatchFont as isize,
|
||||||
|
Scan = FcMatchScan as isize,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct FcResultWrap(FcResult);
|
||||||
|
|
||||||
|
impl FcResultWrap {
|
||||||
|
pub fn succeeded(&self) -> bool {
|
||||||
|
self.0 == FcResultMatch
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_err(&self) -> Error {
|
||||||
|
// the compiler thinks we defined these globals, when all
|
||||||
|
// we did was import them from elsewhere
|
||||||
|
#[allow(non_upper_case_globals)]
|
||||||
|
match self.0 {
|
||||||
|
FcResultMatch => failure::err_msg("FcResultMatch"),
|
||||||
|
FcResultNoMatch => failure::err_msg("FcResultNoMatch"),
|
||||||
|
FcResultTypeMismatch => failure::err_msg("FcResultTypeMismatch"),
|
||||||
|
FcResultNoId => failure::err_msg("FcResultNoId"),
|
||||||
|
FcResultOutOfMemory => failure::err_msg("FcResultOutOfMemory"),
|
||||||
|
_ => format_err!("FcResult holds invalid value {}", self.0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn result<T>(&self, t: T) -> Result<T, Error> {
|
||||||
|
match self.0 {
|
||||||
|
FcResultMatch => Ok(t),
|
||||||
|
_ => Err(self.as_err()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Pattern {
|
||||||
|
pat: *mut FcPattern,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Pattern {
|
||||||
|
pub fn new() -> Result<Pattern, Error> {
|
||||||
|
unsafe {
|
||||||
|
let p = FcPatternCreate();
|
||||||
|
ensure!(!p.is_null(), "FcPatternCreate failed");
|
||||||
|
Ok(Pattern { pat: p })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_string(&mut self, key: &str, value: &str) -> Result<(), Error> {
|
||||||
|
let key = CString::new(key)?;
|
||||||
|
let value = CString::new(value)?;
|
||||||
|
unsafe {
|
||||||
|
ensure!(
|
||||||
|
FcPatternAddString(
|
||||||
|
self.pat,
|
||||||
|
key.as_ptr(),
|
||||||
|
value.as_ptr() as *const u8,
|
||||||
|
) != 0,
|
||||||
|
"failed to add string property {:?} -> {:?}",
|
||||||
|
key,
|
||||||
|
value
|
||||||
|
);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_integer(&mut self, key: &str, value: i32) -> Result<(), Error> {
|
||||||
|
let key = CString::new(key)?;
|
||||||
|
unsafe {
|
||||||
|
ensure!(
|
||||||
|
FcPatternAddInteger(self.pat, key.as_ptr(), value) != 0,
|
||||||
|
"failed to set integer property {:?} -> {}",
|
||||||
|
key,
|
||||||
|
value
|
||||||
|
);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub fn family(&mut self, family: &str) -> Result<(), Error> {
|
||||||
|
self.add_string("family", family)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn monospace(&mut self) -> Result<(), Error> {
|
||||||
|
self.add_integer("spacing", FC_MONO)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn print(&self) {
|
||||||
|
unsafe {
|
||||||
|
FcPatternPrint(self.pat);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn format(&self, fmt: &str) -> Result<String, Error> {
|
||||||
|
let fmt = CString::new(fmt)?;
|
||||||
|
unsafe {
|
||||||
|
let s = FcPatternFormat(self.pat, fmt.as_ptr() as *const u8);
|
||||||
|
ensure!(!s.is_null(), "failed to format pattern");
|
||||||
|
|
||||||
|
let res = CStr::from_ptr(s as *const i8).to_string_lossy().into_owned();
|
||||||
|
FcStrFree(s);
|
||||||
|
Ok(res)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn render_prepare(&self, pat: &Pattern) -> Result<Pattern, Error> {
|
||||||
|
unsafe {
|
||||||
|
let pat = FcFontRenderPrepare(ptr::null_mut(), self.pat, pat.pat);
|
||||||
|
ensure!(!pat.is_null(), "failed to prepare pattern");
|
||||||
|
Ok(Pattern { pat })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse(pattern: &str) -> Result<Pattern, Error> {
|
||||||
|
let pattern = CString::new(pattern)?;
|
||||||
|
unsafe {
|
||||||
|
let p = FcNameParse(pattern.as_ptr() as *const u8);
|
||||||
|
ensure!(!p.is_null(), "failed to parse {:?}", pattern);
|
||||||
|
Ok(Pattern { pat: p })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn config_substitute(
|
||||||
|
&mut self,
|
||||||
|
match_kind: MatchKind,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
unsafe {
|
||||||
|
ensure!(
|
||||||
|
FcConfigSubstitute(
|
||||||
|
ptr::null_mut(),
|
||||||
|
self.pat,
|
||||||
|
mem::transmute(match_kind),
|
||||||
|
) != 0,
|
||||||
|
"FcConfigSubstitute failed"
|
||||||
|
);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn default_substitute(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
FcDefaultSubstitute(self.pat);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn find_match(&self) -> Result<Pattern, Error> {
|
||||||
|
unsafe {
|
||||||
|
let mut res = FcResultWrap(0);
|
||||||
|
let pat = FcFontMatch(
|
||||||
|
ptr::null_mut(),
|
||||||
|
self.pat,
|
||||||
|
&mut res.0 as *mut _,
|
||||||
|
);
|
||||||
|
res.result(Pattern{pat})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sort(&self, trim: bool) -> Result<FontSet, Error> {
|
||||||
|
unsafe {
|
||||||
|
let mut res = FcResultWrap(0);
|
||||||
|
let fonts = FcFontSort(
|
||||||
|
ptr::null_mut(),
|
||||||
|
self.pat,
|
||||||
|
if trim { 1 } else { 0 },
|
||||||
|
ptr::null_mut(),
|
||||||
|
&mut res.0 as *mut _,
|
||||||
|
);
|
||||||
|
|
||||||
|
res.result(FontSet{fonts})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_file(&self) -> Result<String, Error> {
|
||||||
|
self.get_string("file")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_string(&self, key: &str) -> Result<String, Error> {
|
||||||
|
unsafe {
|
||||||
|
let key = CString::new(key)?;
|
||||||
|
let mut ptr: *mut u8 = ptr::null_mut();
|
||||||
|
let res = FcResultWrap(FcPatternGetString(
|
||||||
|
self.pat,
|
||||||
|
key.as_ptr(),
|
||||||
|
0,
|
||||||
|
&mut ptr as *mut *mut u8,
|
||||||
|
));
|
||||||
|
if !res.succeeded() {
|
||||||
|
Err(res.as_err())
|
||||||
|
} else {
|
||||||
|
Ok(CStr::from_ptr(ptr as *const i8).to_string_lossy().into_owned())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for Pattern {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
FcPatternDestroy(self.pat);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -47,6 +47,12 @@ impl Face {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn has_codepoint(&self, cp: char) -> bool {
|
||||||
|
unsafe {
|
||||||
|
FT_Get_Char_Index(self.face, cp as u64) != 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn load_and_render_glyph(
|
pub fn load_and_render_glyph(
|
||||||
&mut self,
|
&mut self,
|
||||||
glyph_index: FT_UInt,
|
glyph_index: FT_UInt,
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
use failure::Error;
|
use failure::Error;
|
||||||
pub mod ftwrap;
|
pub mod ftwrap;
|
||||||
pub mod hbwrap;
|
pub mod hbwrap;
|
||||||
|
pub mod fcwrap;
|
||||||
use self::ftwrap::Library;
|
use self::ftwrap::Library;
|
||||||
|
|
||||||
pub struct FTEngine {
|
pub struct FTEngine {
|
||||||
|
257
src/main.rs
257
src/main.rs
@ -11,19 +11,44 @@ use sdl2::pixels::{Color, PixelFormatEnum};
|
|||||||
use sdl2::rect::Rect;
|
use sdl2::rect::Rect;
|
||||||
use sdl2::render::{BlendMode, Texture, TextureCreator};
|
use sdl2::render::{BlendMode, Texture, TextureCreator};
|
||||||
|
|
||||||
|
use font::ft::fcwrap;
|
||||||
use font::ft::ftwrap;
|
use font::ft::ftwrap;
|
||||||
use font::ft::hbwrap;
|
use font::ft::hbwrap;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::slice;
|
use std::slice;
|
||||||
|
|
||||||
struct Glyph<'a> {
|
#[derive(Copy, Clone, Debug)]
|
||||||
tex: Texture<'a>,
|
struct GlyphInfo {
|
||||||
width: u32,
|
glyph_pos: u32,
|
||||||
height: u32,
|
cluster: u32,
|
||||||
x_advance: i32,
|
x_advance: i32,
|
||||||
y_advance: i32,
|
y_advance: i32,
|
||||||
x_offset: i32,
|
x_offset: i32,
|
||||||
y_offset: i32,
|
y_offset: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GlyphInfo {
|
||||||
|
pub fn new(
|
||||||
|
info: &hbwrap::hb_glyph_info_t,
|
||||||
|
pos: &hbwrap::hb_glyph_position_t,
|
||||||
|
) -> GlyphInfo {
|
||||||
|
GlyphInfo {
|
||||||
|
glyph_pos: info.codepoint,
|
||||||
|
cluster: info.cluster,
|
||||||
|
x_advance: pos.x_advance / 64,
|
||||||
|
y_advance: pos.y_advance / 64,
|
||||||
|
x_offset: pos.x_offset / 64,
|
||||||
|
y_offset: pos.y_offset / 64,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct Glyph<'a> {
|
||||||
|
tex: Texture<'a>,
|
||||||
|
width: u32,
|
||||||
|
height: u32,
|
||||||
|
info: GlyphInfo,
|
||||||
bearing_x: i32,
|
bearing_x: i32,
|
||||||
bearing_y: i32,
|
bearing_y: i32,
|
||||||
}
|
}
|
||||||
@ -32,7 +57,7 @@ impl<'a> Glyph<'a> {
|
|||||||
fn new<T>(
|
fn new<T>(
|
||||||
texture_creator: &'a TextureCreator<T>,
|
texture_creator: &'a TextureCreator<T>,
|
||||||
glyph: &ftwrap::FT_GlyphSlotRec_,
|
glyph: &ftwrap::FT_GlyphSlotRec_,
|
||||||
pos: &hbwrap::hb_glyph_position_t,
|
info: &GlyphInfo,
|
||||||
) -> Result<Glyph<'a>, Error> {
|
) -> Result<Glyph<'a>, Error> {
|
||||||
let mode: ftwrap::FT_Pixel_Mode =
|
let mode: ftwrap::FT_Pixel_Mode =
|
||||||
unsafe { mem::transmute(glyph.bitmap.pixel_mode as u32) };
|
unsafe { mem::transmute(glyph.bitmap.pixel_mode as u32) };
|
||||||
@ -60,10 +85,7 @@ impl<'a> Glyph<'a> {
|
|||||||
tex,
|
tex,
|
||||||
width: width as u32,
|
width: width as u32,
|
||||||
height: height as u32,
|
height: height as u32,
|
||||||
x_advance: pos.x_advance / 64,
|
info: *info,
|
||||||
y_advance: pos.y_advance / 64,
|
|
||||||
x_offset: pos.x_offset / 64,
|
|
||||||
y_offset: pos.y_offset / 64,
|
|
||||||
bearing_x: glyph.bitmap_left,
|
bearing_x: glyph.bitmap_left,
|
||||||
bearing_y: glyph.bitmap_top,
|
bearing_y: glyph.bitmap_top,
|
||||||
})
|
})
|
||||||
@ -73,24 +95,88 @@ impl<'a> Glyph<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn glyphs_for_text<'a, T>(
|
struct FontInfo {
|
||||||
texture_creator: &'a TextureCreator<T>,
|
face: ftwrap::Face,
|
||||||
s: &str,
|
font: hbwrap::Font,
|
||||||
) -> Result<Vec<Glyph<'a>>, Error> {
|
}
|
||||||
|
|
||||||
|
struct FontHolder {
|
||||||
|
lib: ftwrap::Library,
|
||||||
|
size: i64,
|
||||||
|
pattern: fcwrap::Pattern,
|
||||||
|
font_list: fcwrap::FontSet,
|
||||||
|
fonts: Vec<FontInfo>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct ShapedCluster {
|
||||||
|
/// index into FontHolder.fonts
|
||||||
|
font_idx: usize,
|
||||||
|
/// holds shaped results
|
||||||
|
info: Vec<GlyphInfo>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for FontHolder {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
// Ensure that we drop the fonts before we drop the
|
||||||
|
// library, otherwise we will end up faulting
|
||||||
|
self.fonts.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FontHolder {
|
||||||
|
fn new(size: i64) -> Result<FontHolder, Error> {
|
||||||
let mut lib = ftwrap::Library::new()?;
|
let mut lib = ftwrap::Library::new()?;
|
||||||
lib.set_lcd_filter(
|
lib.set_lcd_filter(
|
||||||
ftwrap::FT_LcdFilter::FT_LCD_FILTER_DEFAULT,
|
ftwrap::FT_LcdFilter::FT_LCD_FILTER_DEFAULT,
|
||||||
)?;
|
)?;
|
||||||
let mut face =
|
|
||||||
lib.new_face("/home/wez/.fonts/OperatorMonoLig-Book.otf", 0)?;
|
let mut pattern = fcwrap::Pattern::new()?;
|
||||||
face.set_char_size(0, 36 * 64, 96, 96)?;
|
pattern.family("Operator Mono SSm Lig")?;
|
||||||
let mut font = hbwrap::Font::new(&face);
|
pattern.family("Emoji One")?;
|
||||||
let lang = hbwrap::language_from_string("en")?;
|
pattern.monospace()?;
|
||||||
let mut buf = hbwrap::Buffer::new()?;
|
pattern.config_substitute(fcwrap::MatchKind::Pattern)?;
|
||||||
buf.set_script(hbwrap::HB_SCRIPT_LATIN);
|
pattern.default_substitute();
|
||||||
buf.set_direction(hbwrap::HB_DIRECTION_LTR);
|
let font_list = pattern.sort(true)?;
|
||||||
buf.set_language(lang);
|
|
||||||
buf.add_str(s);
|
Ok(FontHolder {
|
||||||
|
lib,
|
||||||
|
size,
|
||||||
|
font_list,
|
||||||
|
pattern,
|
||||||
|
fonts: Vec::new(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_next_fallback(&mut self) -> Result<(), Error> {
|
||||||
|
let idx = self.fonts.len();
|
||||||
|
let pat = self.font_list.iter().nth(idx).ok_or(failure::err_msg(
|
||||||
|
"no more fallbacks",
|
||||||
|
))?;
|
||||||
|
let pat = self.pattern.render_prepare(&pat)?;
|
||||||
|
let file = pat.get_file()?;
|
||||||
|
|
||||||
|
let mut face = self.lib.new_face(file, 0)?;
|
||||||
|
face.set_char_size(0, self.size * 64, 96, 96)?;
|
||||||
|
let font = hbwrap::Font::new(&face);
|
||||||
|
|
||||||
|
self.fonts.push(FontInfo { face, font });
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_font(&mut self, idx: usize) -> Result<&mut FontInfo, Error> {
|
||||||
|
if idx >= self.fonts.len() {
|
||||||
|
self.load_next_fallback()?;
|
||||||
|
ensure!(
|
||||||
|
idx < self.fonts.len(),
|
||||||
|
"should not ask for a font later than the next prepared font"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(&mut self.fonts[idx])
|
||||||
|
}
|
||||||
|
|
||||||
|
fn shape(&mut self, s: &str) -> Result<Vec<ShapedCluster>, Error> {
|
||||||
let features = vec![
|
let features = vec![
|
||||||
// kerning
|
// kerning
|
||||||
hbwrap::feature_from_string("kern")?,
|
hbwrap::feature_from_string("kern")?,
|
||||||
@ -99,53 +185,95 @@ fn glyphs_for_text<'a, T>(
|
|||||||
// contextual ligatures
|
// contextual ligatures
|
||||||
hbwrap::feature_from_string("clig")?,
|
hbwrap::feature_from_string("clig")?,
|
||||||
];
|
];
|
||||||
font.shape(&mut buf, Some(features.as_slice()));
|
|
||||||
|
|
||||||
|
let mut buf = hbwrap::Buffer::new()?;
|
||||||
|
buf.set_script(hbwrap::HB_SCRIPT_LATIN);
|
||||||
|
buf.set_direction(hbwrap::HB_DIRECTION_LTR);
|
||||||
|
buf.set_language(hbwrap::language_from_string("en")?);
|
||||||
|
buf.add_str(s);
|
||||||
|
|
||||||
|
let font_idx = 0;
|
||||||
|
|
||||||
|
self.shape_with_font(font_idx, &mut buf, &features)?;
|
||||||
let infos = buf.glyph_infos();
|
let infos = buf.glyph_infos();
|
||||||
let positions = buf.glyph_positions();
|
let positions = buf.glyph_positions();
|
||||||
let mut result = Vec::new();
|
|
||||||
|
let mut cluster = Vec::new();
|
||||||
|
|
||||||
for (i, info) in infos.iter().enumerate() {
|
for (i, info) in infos.iter().enumerate() {
|
||||||
let pos = &positions[i];
|
let pos = &positions[i];
|
||||||
println!(
|
// TODO: if info.codepoint == 0 here then we should
|
||||||
"info {} glyph_pos={}, cluster={} x_adv={} y_adv={} x_off={} y_off={}",
|
// rebuild that portion of the string and reshape it
|
||||||
i,
|
// with the next fallback font
|
||||||
info.codepoint,
|
cluster.push(GlyphInfo::new(info, pos));
|
||||||
info.cluster,
|
|
||||||
pos.x_advance,
|
|
||||||
pos.y_advance,
|
|
||||||
pos.x_offset,
|
|
||||||
pos.y_offset
|
|
||||||
);
|
|
||||||
|
|
||||||
let glyph = face.load_and_render_glyph(
|
|
||||||
info.codepoint,
|
|
||||||
(ftwrap::FT_LOAD_COLOR) as i32,
|
|
||||||
ftwrap::FT_Render_Mode::FT_RENDER_MODE_LCD,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
|
|
||||||
let g = Glyph::new(texture_creator, glyph, pos)?;
|
|
||||||
|
|
||||||
/*
|
|
||||||
println!(
|
|
||||||
"width={} height={} advx={} advy={} bearing={},{}",
|
|
||||||
g.width,
|
|
||||||
g.height,
|
|
||||||
g.x_advance,
|
|
||||||
g.y_advance,
|
|
||||||
g.bearing_x,
|
|
||||||
g.bearing_y
|
|
||||||
); */
|
|
||||||
|
|
||||||
result.push(g);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
println!("shaped: {:?}", cluster);
|
||||||
|
|
||||||
|
Ok(vec![
|
||||||
|
ShapedCluster {
|
||||||
|
font_idx,
|
||||||
|
info: cluster,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
fn shape_with_font(
|
||||||
|
&mut self,
|
||||||
|
idx: usize,
|
||||||
|
buf: &mut hbwrap::Buffer,
|
||||||
|
features: &Vec<hbwrap::hb_feature_t>,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
let info = self.get_font(idx)?;
|
||||||
|
info.font.shape(buf, Some(features.as_slice()));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_glyph(
|
||||||
|
&mut self,
|
||||||
|
font_idx: usize,
|
||||||
|
glyph_pos: u32,
|
||||||
|
) -> Result<&ftwrap::FT_GlyphSlotRec_, Error> {
|
||||||
|
let info = &mut self.fonts[font_idx];
|
||||||
|
info.face.load_and_render_glyph(
|
||||||
|
glyph_pos,
|
||||||
|
(ftwrap::FT_LOAD_COLOR) as i32,
|
||||||
|
ftwrap::FT_Render_Mode::FT_RENDER_MODE_LCD,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn glyphs_for_text<'a, T>(
|
||||||
|
texture_creator: &'a TextureCreator<T>,
|
||||||
|
s: &str,
|
||||||
|
) -> Result<Vec<Glyph<'a>>, Error> {
|
||||||
|
|
||||||
|
let mut font_holder = FontHolder::new(36)?;
|
||||||
|
|
||||||
|
let mut result = Vec::new();
|
||||||
|
for cluster in font_holder.shape(s)? {
|
||||||
|
for info in cluster.info.iter() {
|
||||||
|
if info.glyph_pos == 0 {
|
||||||
|
println!("skip: no codepoint for this one");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let glyph =
|
||||||
|
font_holder.load_glyph(cluster.font_idx, info.glyph_pos)?;
|
||||||
|
|
||||||
|
if glyph.bitmap.width == 0 || glyph.bitmap.rows == 0 {
|
||||||
|
println!("skip: bitmap for this has 0 dimensions {:?}", glyph);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let g = Glyph::new(texture_creator, glyph, info)?;
|
||||||
|
result.push(g);
|
||||||
|
}
|
||||||
|
}
|
||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run() -> Result<(), Error> {
|
fn run() -> Result<(), Error> {
|
||||||
|
|
||||||
let sdl_context = sdl2::init().map_err(failure::err_msg)?;
|
let sdl_context = sdl2::init().map_err(failure::err_msg)?;
|
||||||
let video_subsys = sdl_context.video().map_err(failure::err_msg)?;
|
let video_subsys = sdl_context.video().map_err(failure::err_msg)?;
|
||||||
let window = video_subsys
|
let window = video_subsys
|
||||||
@ -155,7 +283,7 @@ fn run() -> Result<(), Error> {
|
|||||||
.build()?;
|
.build()?;
|
||||||
let mut canvas = window.into_canvas().build()?;
|
let mut canvas = window.into_canvas().build()?;
|
||||||
let texture_creator = canvas.texture_creator();
|
let texture_creator = canvas.texture_creator();
|
||||||
let glyphs = glyphs_for_text(&texture_creator, "foo->bar();")?;
|
let mut glyphs = glyphs_for_text(&texture_creator, "foo->bar(); ❤")?;
|
||||||
|
|
||||||
for event in sdl_context
|
for event in sdl_context
|
||||||
.event_pump()
|
.event_pump()
|
||||||
@ -176,21 +304,22 @@ fn run() -> Result<(), Error> {
|
|||||||
|
|
||||||
let mut x = 10i32;
|
let mut x = 10i32;
|
||||||
let mut y = 100i32;
|
let mut y = 100i32;
|
||||||
for g in glyphs.iter() {
|
for g in glyphs.iter_mut() {
|
||||||
|
g.tex.set_color_mod(0xb3, 0xb3, 0xb3);
|
||||||
canvas
|
canvas
|
||||||
.copy(
|
.copy(
|
||||||
&g.tex,
|
&g.tex,
|
||||||
Some(Rect::new(0, 0, g.width, g.height)),
|
Some(Rect::new(0, 0, g.width, g.height)),
|
||||||
Some(Rect::new(
|
Some(Rect::new(
|
||||||
x + g.x_offset - g.bearing_x,
|
x + g.info.x_offset - g.bearing_x,
|
||||||
y - (g.y_offset + g.bearing_y as i32) as i32,
|
y - (g.info.y_offset + g.bearing_y as i32) as i32,
|
||||||
g.width,
|
g.width,
|
||||||
g.height,
|
g.height,
|
||||||
)),
|
)),
|
||||||
)
|
)
|
||||||
.map_err(failure::err_msg)?;
|
.map_err(failure::err_msg)?;
|
||||||
x += g.x_advance;
|
x += g.info.x_advance;
|
||||||
y += g.y_advance;
|
y += g.info.y_advance;
|
||||||
}
|
}
|
||||||
|
|
||||||
canvas.present();
|
canvas.present();
|
||||||
|
Loading…
Reference in New Issue
Block a user