mirror of
https://github.com/wez/wezterm.git
synced 2024-11-22 04:56:12 +03:00
fonts: improve cap-height and use_cap_height_to_scale_fallback_fonts
We now compute the cap-height from the rasterized glyph data. Moved the scaling action of use_cap_height_to_scale_fallback_fonts from glyphcache into the font resolver: when enabled, and we have data about the baseline font and the font being resolved, then the resolving font will be scaled such that the cap-height of both fonts has the same pixel size. The effect of this is that `I` glyphs from both fonts should appear to have the same height. Added a row of `I`'s in differing styles at the bottom of styles.txt to make this easier to visualize. refs: #1189
This commit is contained in:
parent
c51c282ad8
commit
401719fb01
1
deps/freetype/bindings.h
vendored
1
deps/freetype/bindings.h
vendored
@ -6,3 +6,4 @@
|
||||
#include <freetype/ftoutln.h>
|
||||
#include <freetype/ftmm.h>
|
||||
#include <freetype/ftsynth.h>
|
||||
#include <freetype/ftglyph.h>
|
||||
|
124
deps/freetype/src/lib.rs
vendored
124
deps/freetype/src/lib.rs
vendored
@ -40,6 +40,7 @@ pub const FT_RASTER_FLAG_DEFAULT: u32 = 0;
|
||||
pub const FT_RASTER_FLAG_AA: u32 = 1;
|
||||
pub const FT_RASTER_FLAG_DIRECT: u32 = 2;
|
||||
pub const FT_RASTER_FLAG_CLIP: u32 = 4;
|
||||
pub const FT_RASTER_FLAG_SDF: u32 = 8;
|
||||
pub const FT_ERR_BASE: u32 = 0;
|
||||
pub const FT_FACE_FLAG_SCALABLE: u32 = 1;
|
||||
pub const FT_FACE_FLAG_FIXED_SIZES: u32 = 2;
|
||||
@ -275,12 +276,6 @@ pub enum FT_Glyph_Format_ {
|
||||
pub use self::FT_Glyph_Format_ as FT_Glyph_Format;
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct FT_RasterRec_ {
|
||||
_unused: [u8; 0],
|
||||
}
|
||||
pub type FT_Raster = *mut FT_RasterRec_;
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct FT_Span_ {
|
||||
pub x: ::std::os::raw::c_short,
|
||||
pub len: ::std::os::raw::c_ushort,
|
||||
@ -323,6 +318,12 @@ pub struct FT_Raster_Params_ {
|
||||
pub clip_box: FT_BBox,
|
||||
}
|
||||
pub type FT_Raster_Params = FT_Raster_Params_;
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct FT_RasterRec_ {
|
||||
_unused: [u8; 0],
|
||||
}
|
||||
pub type FT_Raster = *mut FT_RasterRec_;
|
||||
pub type FT_Raster_NewFunc = ::std::option::Option<
|
||||
unsafe extern "C" fn(
|
||||
memory: *mut ::std::os::raw::c_void,
|
||||
@ -454,6 +455,7 @@ pub const FT_Mod_Err_Type1: _bindgen_ty_1 = _bindgen_ty_1::FT_Mod_Err_Base;
|
||||
pub const FT_Mod_Err_Type42: _bindgen_ty_1 = _bindgen_ty_1::FT_Mod_Err_Base;
|
||||
pub const FT_Mod_Err_Winfonts: _bindgen_ty_1 = _bindgen_ty_1::FT_Mod_Err_Base;
|
||||
pub const FT_Mod_Err_GXvalid: _bindgen_ty_1 = _bindgen_ty_1::FT_Mod_Err_Base;
|
||||
pub const FT_Mod_Err_Sdf: _bindgen_ty_1 = _bindgen_ty_1::FT_Mod_Err_Base;
|
||||
pub const FT_Mod_Err_Max: _bindgen_ty_1 = _bindgen_ty_1::FT_Mod_Err_Max;
|
||||
#[repr(u32)]
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
@ -993,6 +995,9 @@ extern "C" {
|
||||
extern "C" {
|
||||
pub fn FT_Set_Transform(face: FT_Face, matrix: *mut FT_Matrix, delta: *mut FT_Vector);
|
||||
}
|
||||
extern "C" {
|
||||
pub fn FT_Get_Transform(face: FT_Face, matrix: *mut FT_Matrix, delta: *mut FT_Vector);
|
||||
}
|
||||
#[repr(u32)]
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum FT_Render_Mode_ {
|
||||
@ -1001,7 +1006,8 @@ pub enum FT_Render_Mode_ {
|
||||
FT_RENDER_MODE_MONO = 2,
|
||||
FT_RENDER_MODE_LCD = 3,
|
||||
FT_RENDER_MODE_LCD_V = 4,
|
||||
FT_RENDER_MODE_MAX = 5,
|
||||
FT_RENDER_MODE_SDF = 5,
|
||||
FT_RENDER_MODE_MAX = 6,
|
||||
}
|
||||
pub use self::FT_Render_Mode_ as FT_Render_Mode;
|
||||
extern "C" {
|
||||
@ -1082,23 +1088,6 @@ extern "C" {
|
||||
p_transform: *mut FT_Matrix,
|
||||
) -> FT_Error;
|
||||
}
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct FT_LayerIterator_ {
|
||||
pub num_layers: FT_UInt,
|
||||
pub layer: FT_UInt,
|
||||
pub p: *mut FT_Byte,
|
||||
}
|
||||
pub type FT_LayerIterator = FT_LayerIterator_;
|
||||
extern "C" {
|
||||
pub fn FT_Get_Color_Glyph_Layer(
|
||||
face: FT_Face,
|
||||
base_glyph: FT_UInt,
|
||||
aglyph_index: *mut FT_UInt,
|
||||
acolor_index: *mut FT_UInt,
|
||||
iterator: *mut FT_LayerIterator,
|
||||
) -> FT_Bool;
|
||||
}
|
||||
extern "C" {
|
||||
pub fn FT_Get_FSType_Flags(face: FT_Face) -> FT_UShort;
|
||||
}
|
||||
@ -1799,3 +1788,90 @@ extern "C" {
|
||||
extern "C" {
|
||||
pub fn FT_GlyphSlot_Oblique(slot: FT_GlyphSlot);
|
||||
}
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct FT_Glyph_Class_ {
|
||||
_unused: [u8; 0],
|
||||
}
|
||||
pub type FT_Glyph_Class = FT_Glyph_Class_;
|
||||
pub type FT_Glyph = *mut FT_GlyphRec_;
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct FT_GlyphRec_ {
|
||||
pub library: FT_Library,
|
||||
pub clazz: *const FT_Glyph_Class,
|
||||
pub format: FT_Glyph_Format,
|
||||
pub advance: FT_Vector,
|
||||
}
|
||||
pub type FT_GlyphRec = FT_GlyphRec_;
|
||||
pub type FT_BitmapGlyph = *mut FT_BitmapGlyphRec_;
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct FT_BitmapGlyphRec_ {
|
||||
pub root: FT_GlyphRec,
|
||||
pub left: FT_Int,
|
||||
pub top: FT_Int,
|
||||
pub bitmap: FT_Bitmap,
|
||||
}
|
||||
pub type FT_BitmapGlyphRec = FT_BitmapGlyphRec_;
|
||||
pub type FT_OutlineGlyph = *mut FT_OutlineGlyphRec_;
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct FT_OutlineGlyphRec_ {
|
||||
pub root: FT_GlyphRec,
|
||||
pub outline: FT_Outline,
|
||||
}
|
||||
pub type FT_OutlineGlyphRec = FT_OutlineGlyphRec_;
|
||||
extern "C" {
|
||||
pub fn FT_New_Glyph(
|
||||
library: FT_Library,
|
||||
format: FT_Glyph_Format,
|
||||
aglyph: *mut FT_Glyph,
|
||||
) -> FT_Error;
|
||||
}
|
||||
extern "C" {
|
||||
pub fn FT_Get_Glyph(slot: FT_GlyphSlot, aglyph: *mut FT_Glyph) -> FT_Error;
|
||||
}
|
||||
extern "C" {
|
||||
pub fn FT_Glyph_Copy(source: FT_Glyph, target: *mut FT_Glyph) -> FT_Error;
|
||||
}
|
||||
extern "C" {
|
||||
pub fn FT_Glyph_Transform(
|
||||
glyph: FT_Glyph,
|
||||
matrix: *mut FT_Matrix,
|
||||
delta: *mut FT_Vector,
|
||||
) -> FT_Error;
|
||||
}
|
||||
impl FT_Glyph_BBox_Mode_ {
|
||||
pub const FT_GLYPH_BBOX_SUBPIXELS: FT_Glyph_BBox_Mode_ =
|
||||
FT_Glyph_BBox_Mode_::FT_GLYPH_BBOX_UNSCALED;
|
||||
}
|
||||
#[repr(u32)]
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum FT_Glyph_BBox_Mode_ {
|
||||
FT_GLYPH_BBOX_UNSCALED = 0,
|
||||
FT_GLYPH_BBOX_GRIDFIT = 1,
|
||||
FT_GLYPH_BBOX_TRUNCATE = 2,
|
||||
FT_GLYPH_BBOX_PIXELS = 3,
|
||||
}
|
||||
pub use self::FT_Glyph_BBox_Mode_ as FT_Glyph_BBox_Mode;
|
||||
extern "C" {
|
||||
pub fn FT_Glyph_Get_CBox(glyph: FT_Glyph, bbox_mode: FT_UInt, acbox: *mut FT_BBox);
|
||||
}
|
||||
extern "C" {
|
||||
pub fn FT_Glyph_To_Bitmap(
|
||||
the_glyph: *mut FT_Glyph,
|
||||
render_mode: FT_Render_Mode,
|
||||
origin: *mut FT_Vector,
|
||||
destroy: FT_Bool,
|
||||
) -> FT_Error;
|
||||
}
|
||||
extern "C" {
|
||||
pub fn FT_Done_Glyph(glyph: FT_Glyph);
|
||||
}
|
||||
extern "C" {
|
||||
pub fn FT_Matrix_Multiply(a: *const FT_Matrix, b: *mut FT_Matrix);
|
||||
}
|
||||
extern "C" {
|
||||
pub fn FT_Matrix_Invert(matrix: *mut FT_Matrix) -> FT_Error;
|
||||
}
|
||||
|
@ -53,6 +53,7 @@ As features stabilize some brief notes about them will accumulate here.
|
||||
* New: [wezterm.action_callback](config/lua/wezterm/action_callback.md) function to make it easier to use custom events. Thanks to [@bew](https://github.com/bew)! [#1151](https://github.com/wez/wezterm/pull/1151)
|
||||
* New: `wezterm connect` now also supports the `--class` parameter to override the window class
|
||||
* Fixed: wezterm can now match bitmap fonts that are spread across multiple font files [#1189](https://github.com/wez/wezterm/issues/1189)
|
||||
* Improved: [use_cap_height_to_scale_fallback_fonts](config/lua/config/use_cap_height_to_scale_fallback_fonts.md) now computes *cap-height* based on the rasterized glyph bitmap which means that the data is accurate in more cases, including for bitmap fonts. Scaling is now also applied to across varying text styles; previously it only applied to a font within an `wezterm.font_with_fallback` font list.
|
||||
|
||||
### 20210814-124438-54e29167
|
||||
|
||||
|
@ -8,3 +8,5 @@ Regular
|
||||
|
||||
[5mBlink[0m
|
||||
[6mRapid Blink[0m
|
||||
|
||||
I [1mI[0m [3mI[0m [1m[3mI[0m [2mI[0m [2m[3mI[0m
|
||||
|
@ -98,13 +98,23 @@ struct FaceSize {
|
||||
dpi: u32,
|
||||
cell_width: f64,
|
||||
cell_height: f64,
|
||||
cap_height: Option<f64>,
|
||||
cap_height_to_height_ratio: Option<f64>,
|
||||
is_scaled: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct ComputedCellMetrics {
|
||||
pub width: f64,
|
||||
pub height: f64,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct SelectedFontSize {
|
||||
pub width: f64,
|
||||
pub height: f64,
|
||||
pub cap_height: Option<f64>,
|
||||
pub cap_height_to_height_ratio: Option<f64>,
|
||||
pub is_scaled: bool,
|
||||
}
|
||||
|
||||
@ -331,6 +341,8 @@ impl Face {
|
||||
width: face_size.cell_width,
|
||||
height: face_size.cell_height,
|
||||
is_scaled: face_size.is_scaled,
|
||||
cap_height: face_size.cap_height,
|
||||
cap_height_to_height_ratio: face_size.cap_height_to_height_ratio,
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -350,10 +362,12 @@ impl Face {
|
||||
let selected_size = match self.set_char_size(size, size, dpi, dpi) {
|
||||
Ok(_) => {
|
||||
// Compute metrics for the nominal monospace cell
|
||||
let (width, height) = self.cell_metrics();
|
||||
let ComputedCellMetrics { width, height } = self.cell_metrics();
|
||||
SelectedFontSize {
|
||||
width,
|
||||
height,
|
||||
cap_height: None,
|
||||
cap_height_to_height_ratio: None,
|
||||
is_scaled: true,
|
||||
}
|
||||
}
|
||||
@ -408,11 +422,14 @@ impl Face {
|
||||
// 4 pixels is too thin for this font, so we take the max of the
|
||||
// known dimensions to produce the size.
|
||||
// <https://github.com/wez/wezterm/issues/1165>
|
||||
let (m_width, m_height) = self.cell_metrics();
|
||||
let m = self.cell_metrics();
|
||||
let height = f64::from(best.height).max(m.height);
|
||||
SelectedFontSize {
|
||||
width: f64::from(best.width).max(m_width),
|
||||
height: f64::from(best.height).max(m_height),
|
||||
width: f64::from(best.width).max(m.width),
|
||||
height,
|
||||
is_scaled: false,
|
||||
cap_height: None,
|
||||
cap_height_to_height_ratio: None,
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -421,11 +438,34 @@ impl Face {
|
||||
size: point_size,
|
||||
dpi,
|
||||
cell_width: selected_size.width,
|
||||
cap_height: None,
|
||||
cap_height_to_height_ratio: None,
|
||||
cell_height: selected_size.height,
|
||||
is_scaled: selected_size.is_scaled,
|
||||
});
|
||||
|
||||
Ok(selected_size)
|
||||
// Can't compute cap height until after we've assigned self.size
|
||||
if let Ok(cap_height) = self.compute_cap_height() {
|
||||
let cap_height_to_height_ratio = cap_height / selected_size.height;
|
||||
|
||||
self.size.replace(FaceSize {
|
||||
size: point_size,
|
||||
dpi,
|
||||
cell_width: selected_size.width,
|
||||
cap_height: Some(cap_height),
|
||||
cap_height_to_height_ratio: Some(cap_height_to_height_ratio),
|
||||
cell_height: selected_size.height,
|
||||
is_scaled: selected_size.is_scaled,
|
||||
});
|
||||
|
||||
Ok(SelectedFontSize {
|
||||
cap_height: Some(cap_height),
|
||||
cap_height_to_height_ratio: Some(cap_height_to_height_ratio),
|
||||
..selected_size
|
||||
})
|
||||
} else {
|
||||
Ok(selected_size)
|
||||
}
|
||||
}
|
||||
|
||||
fn set_char_size(
|
||||
@ -516,7 +556,129 @@ impl Face {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cell_metrics(&mut self) -> (f64, f64) {
|
||||
/// Compute the cap-height metric in pixels.
|
||||
/// This is pixel-perfect based on the rendered glyph data for `I`,
|
||||
/// which is a technique that works for any font regardless
|
||||
/// of the integrity of its internal cap-height metric or
|
||||
/// whether the font is a bitmap font.
|
||||
/// `I` is chosen rather than `O` as `O` glyphs are often optically
|
||||
/// compensated and overshoot a little.
|
||||
fn compute_cap_height(&mut self) -> anyhow::Result<f64> {
|
||||
let glyph_pos = unsafe { FT_Get_Char_Index(self.face, b'I' as u64) };
|
||||
if glyph_pos == 0 {
|
||||
anyhow::bail!("no I from which to compute cap height");
|
||||
}
|
||||
let (load_flags, render_mode) = compute_load_flags_from_config();
|
||||
let ft_glyph = self.load_and_render_glyph(glyph_pos, load_flags, render_mode, false)?;
|
||||
|
||||
let mode: FT_Pixel_Mode =
|
||||
unsafe { std::mem::transmute(u32::from(ft_glyph.bitmap.pixel_mode)) };
|
||||
|
||||
// pitch is the number of bytes per source row
|
||||
let pitch = ft_glyph.bitmap.pitch.abs() as usize;
|
||||
let data = unsafe {
|
||||
std::slice::from_raw_parts_mut(
|
||||
ft_glyph.bitmap.buffer,
|
||||
ft_glyph.bitmap.rows as usize * pitch,
|
||||
)
|
||||
};
|
||||
|
||||
let mut first_row = None;
|
||||
let mut last_row = None;
|
||||
|
||||
match mode {
|
||||
FT_Pixel_Mode::FT_PIXEL_MODE_LCD => {
|
||||
let width = ft_glyph.bitmap.width as usize / 3;
|
||||
let height = ft_glyph.bitmap.rows as usize;
|
||||
|
||||
'next_line_lcd: for y in 0..height {
|
||||
let src_offset = y * pitch as usize;
|
||||
for x in 0..width {
|
||||
if data[src_offset + (x * 3)] != 0
|
||||
|| data[src_offset + (x * 3) + 1] != 0
|
||||
|| data[src_offset + (x * 3) + 2] != 0
|
||||
{
|
||||
if first_row.is_none() {
|
||||
first_row.replace(y);
|
||||
}
|
||||
last_row.replace(y);
|
||||
continue 'next_line_lcd;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FT_Pixel_Mode::FT_PIXEL_MODE_BGRA => {
|
||||
let width = ft_glyph.bitmap.width as usize;
|
||||
let height = ft_glyph.bitmap.rows as usize;
|
||||
'next_line_bgra: for y in 0..height {
|
||||
let src_offset = y * pitch as usize;
|
||||
for x in 0..width {
|
||||
let alpha = data[src_offset + (x * 4) + 3];
|
||||
if alpha != 0 {
|
||||
if first_row.is_none() {
|
||||
first_row.replace(y);
|
||||
}
|
||||
last_row.replace(y);
|
||||
continue 'next_line_bgra;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
FT_Pixel_Mode::FT_PIXEL_MODE_GRAY => {
|
||||
let width = ft_glyph.bitmap.width as usize;
|
||||
let height = ft_glyph.bitmap.rows as usize;
|
||||
'next_line_gray: for y in 0..height {
|
||||
let src_offset = y * pitch;
|
||||
for x in 0..width {
|
||||
if data[src_offset + x] != 0 {
|
||||
if first_row.is_none() {
|
||||
first_row.replace(y);
|
||||
}
|
||||
last_row.replace(y);
|
||||
continue 'next_line_gray;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
FT_Pixel_Mode::FT_PIXEL_MODE_MONO => {
|
||||
let width = ft_glyph.bitmap.width as usize;
|
||||
let height = ft_glyph.bitmap.rows as usize;
|
||||
'next_line_mono: for y in 0..height {
|
||||
let src_offset = y * pitch;
|
||||
let mut x = 0;
|
||||
for i in 0..pitch {
|
||||
if x >= width {
|
||||
break;
|
||||
}
|
||||
let mut b = data[src_offset + i];
|
||||
for _ in 0..8 {
|
||||
if x >= width {
|
||||
break;
|
||||
}
|
||||
if b & 0x80 == 0x80 {
|
||||
if first_row.is_none() {
|
||||
first_row.replace(y);
|
||||
}
|
||||
last_row.replace(y);
|
||||
continue 'next_line_mono;
|
||||
}
|
||||
b <<= 1;
|
||||
x += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => anyhow::bail!("unhandled pixel mode {:?}", mode),
|
||||
}
|
||||
|
||||
match (first_row, last_row) {
|
||||
(Some(first), Some(last)) => Ok((last - first) as f64),
|
||||
_ => anyhow::bail!("didn't find any rasterized rows?"),
|
||||
}
|
||||
}
|
||||
|
||||
fn cell_metrics(&mut self) -> ComputedCellMetrics {
|
||||
unsafe {
|
||||
let metrics = &(*(*self.face).size).metrics;
|
||||
let height = (metrics.y_scale as f64 * f64::from((*self.face).height))
|
||||
@ -555,7 +717,11 @@ impl Face {
|
||||
width = height * 64.;
|
||||
}
|
||||
}
|
||||
(width / 64.0, height)
|
||||
|
||||
ComputedCellMetrics {
|
||||
width: width / 64.0,
|
||||
height,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -474,21 +474,12 @@ impl FontConfigInner {
|
||||
Ok(loaded)
|
||||
}
|
||||
|
||||
/// Given a text style, load (with caching) the font that best
|
||||
/// matches according to the fontconfig pattern.
|
||||
fn resolve_font(&self, myself: &Rc<Self>, style: &TextStyle) -> anyhow::Result<Rc<LoadedFont>> {
|
||||
let config = self.config.borrow();
|
||||
|
||||
let mut fonts = self.fonts.borrow_mut();
|
||||
|
||||
if let Some(entry) = fonts.get(style) {
|
||||
return Ok(Rc::clone(entry));
|
||||
}
|
||||
|
||||
let font_size = config.font_size * *self.font_scale.borrow();
|
||||
let dpi = *self.dpi.borrow() as u32;
|
||||
let pixel_size = (font_size * dpi as f64 / 72.0) as u16;
|
||||
|
||||
fn resolve_font_helper(
|
||||
&self,
|
||||
style: &TextStyle,
|
||||
config: &ConfigHandle,
|
||||
pixel_size: u16,
|
||||
) -> anyhow::Result<(Box<dyn FontShaper>, Vec<ParsedFont>)> {
|
||||
let attributes = style.font_with_fallback();
|
||||
let preferred_attributes = attributes
|
||||
.iter()
|
||||
@ -561,15 +552,77 @@ impl FontConfigInner {
|
||||
}
|
||||
}
|
||||
|
||||
let shaper = new_shaper(&*config, &handles)?;
|
||||
Ok((new_shaper(&*config, &handles)?, handles))
|
||||
}
|
||||
|
||||
let metrics = shaper.metrics(font_size, dpi).with_context(|| {
|
||||
/// Given a text style, load (with caching) the font that best
|
||||
/// matches according to the fontconfig pattern.
|
||||
fn resolve_font(&self, myself: &Rc<Self>, style: &TextStyle) -> anyhow::Result<Rc<LoadedFont>> {
|
||||
let config = self.config.borrow();
|
||||
let is_default = *style == config.font;
|
||||
let def_font = if !is_default && config.use_cap_height_to_scale_fallback_fonts {
|
||||
Some(self.default_font(myself)?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let mut fonts = self.fonts.borrow_mut();
|
||||
|
||||
if let Some(entry) = fonts.get(style) {
|
||||
return Ok(Rc::clone(entry));
|
||||
}
|
||||
|
||||
let mut font_size = config.font_size * *self.font_scale.borrow();
|
||||
let dpi = *self.dpi.borrow() as u32;
|
||||
let pixel_size = (font_size * dpi as f64 / 72.0) as u16;
|
||||
|
||||
let (mut shaper, mut handles) = self.resolve_font_helper(style, &config, pixel_size)?;
|
||||
|
||||
let mut metrics = shaper.metrics(font_size, dpi).with_context(|| {
|
||||
format!(
|
||||
"obtaining metrics for font_size={} @ dpi {}",
|
||||
font_size, dpi
|
||||
)
|
||||
})?;
|
||||
|
||||
if let Some(def_font) = def_font {
|
||||
let def_metrics = def_font.metrics();
|
||||
match (def_metrics.cap_height, metrics.cap_height) {
|
||||
(Some(d), Some(m)) => {
|
||||
// Scale by the ratio of the pixel heights of the default
|
||||
// and this font; this causes the `I` glyphs to appear to
|
||||
// have the same height.
|
||||
let scale = d.get() / m.get();
|
||||
if scale != 1.0 {
|
||||
let scaled_pixel_size = (pixel_size as f64 * scale) as u16;
|
||||
let scaled_font_size = font_size * scale;
|
||||
log::trace!(
|
||||
"using cap height adjusted: pixel_size {} -> {}, font_size {} -> {}, {:?}",
|
||||
pixel_size,
|
||||
scaled_pixel_size,
|
||||
font_size,
|
||||
scaled_font_size,
|
||||
metrics,
|
||||
);
|
||||
let (alt_shaper, alt_handles) =
|
||||
self.resolve_font_helper(style, &config, scaled_pixel_size)?;
|
||||
shaper = alt_shaper;
|
||||
handles = alt_handles;
|
||||
|
||||
metrics = shaper.metrics(scaled_font_size, dpi).with_context(|| {
|
||||
format!(
|
||||
"obtaining cap-height adjusted metrics for font_size={} @ dpi {}",
|
||||
scaled_font_size, dpi
|
||||
)
|
||||
})?;
|
||||
|
||||
font_size = scaled_font_size;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
let loaded = Rc::new(LoadedFont {
|
||||
rasterizers: RefCell::new(HashMap::new()),
|
||||
handles: RefCell::new(handles),
|
||||
|
@ -227,6 +227,7 @@ impl AllsortsParsedFont {
|
||||
underline_thickness,
|
||||
underline_position,
|
||||
cap_height_ratio: None,
|
||||
cap_height: None,
|
||||
is_scaled: true, // FIXME
|
||||
};
|
||||
|
||||
|
@ -470,7 +470,8 @@ impl FontShaper for HarfbuzzShaper {
|
||||
underline_position: PixelLength::new(
|
||||
unsafe { (*pair.face.face).underline_position as f64 } * y_scale / 64.,
|
||||
),
|
||||
cap_height_ratio: pair.face.cap_height(),
|
||||
cap_height_ratio: selected_size.cap_height_to_height_ratio,
|
||||
cap_height: selected_size.cap_height.map(PixelLength::new),
|
||||
is_scaled: selected_size.is_scaled,
|
||||
};
|
||||
|
||||
|
@ -54,6 +54,7 @@ pub struct FontMetrics {
|
||||
|
||||
/// Fraction of the EM square occupied by the cap height
|
||||
pub cap_height_ratio: Option<f64>,
|
||||
pub cap_height: Option<PixelLength>,
|
||||
|
||||
/// True if the font is scalable and this is a scaled metric.
|
||||
/// False if the font only has bitmap strikes and what we
|
||||
|
@ -437,43 +437,24 @@ impl<T: Texture2d> GlyphCache<T> {
|
||||
}
|
||||
} else {
|
||||
// a scalable fallback font
|
||||
let y_scale = match (
|
||||
self.fonts.config().use_cap_height_to_scale_fallback_fonts,
|
||||
base_metrics.cap_height_ratio,
|
||||
idx_metrics.cap_height_ratio,
|
||||
) {
|
||||
(true, Some(base_cap), Some(cap)) => {
|
||||
// both fonts have cap-height metrics and we're in
|
||||
// use_cap_height_to_scale_fallback_fonts mode, so
|
||||
// scale based on their respective cap heights
|
||||
base_cap / cap
|
||||
}
|
||||
_ => {
|
||||
// Assume that the size we requested doesn't need
|
||||
// any additional scaling
|
||||
1.0
|
||||
}
|
||||
};
|
||||
|
||||
// How wide the glyph would be using the y_scale we produced
|
||||
let y_scaled_width = y_scale * glyph.width as f64;
|
||||
let f_width = glyph.width as f64;
|
||||
|
||||
if allow_width_overflow || y_scaled_width <= max_pixel_width {
|
||||
scale = y_scale;
|
||||
if allow_width_overflow || f_width <= max_pixel_width {
|
||||
scale = 1.0;
|
||||
} else {
|
||||
scale = max_pixel_width / glyph.width as f64;
|
||||
scale = max_pixel_width / f_width;
|
||||
}
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
log::debug!(
|
||||
"{} allow_width_overflow={} is_square_or_wide={} aspect={} \
|
||||
y_scaled_width={} max_pixel_width={} glyph.width={} -> scale={}",
|
||||
max_pixel_width={} glyph.width={} -> scale={}",
|
||||
info.text,
|
||||
allow_width_overflow,
|
||||
is_square_or_wide,
|
||||
aspect,
|
||||
y_scaled_width,
|
||||
max_pixel_width,
|
||||
glyph.width,
|
||||
scale
|
||||
|
Loading…
Reference in New Issue
Block a user