Make it possible to set attributes per glyph
This commit is contained in:
parent
4798c7ee1a
commit
ea64291abb
4 changed files with 254 additions and 168 deletions
|
|
@ -1,6 +1,6 @@
|
||||||
// SPDX-License-Identifier: MIT OR Apache-2.0
|
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||||
|
|
||||||
use cosmic_text::{Attrs, AttrsSpan, Color, Family, FontSystem, Style, SwashCache,
|
use cosmic_text::{Attrs, Color, Family, FontSystem, Style, SwashCache,
|
||||||
TextAction, TextBuffer, TextBufferLine, TextMetrics, Weight};
|
TextAction, TextBuffer, TextBufferLine, TextMetrics, Weight};
|
||||||
use orbclient::{EventOption, Renderer, Window, WindowFlag};
|
use orbclient::{EventOption, Renderer, Window, WindowFlag};
|
||||||
use std::{env, fs, process, thread, time::{Duration, Instant}};
|
use std::{env, fs, process, thread, time::{Duration, Instant}};
|
||||||
|
|
@ -39,6 +39,11 @@ fn main() {
|
||||||
|
|
||||||
let mut line_i = 0;
|
let mut line_i = 0;
|
||||||
for &(text, attrs) in &[
|
for &(text, attrs) in &[
|
||||||
|
("B", attrs.weight(Weight::BOLD)),
|
||||||
|
("old ", attrs),
|
||||||
|
("I", attrs.style(Style::Italic)),
|
||||||
|
("talic", attrs),
|
||||||
|
("\n", attrs),
|
||||||
("Sans-Serif Normal ", attrs),
|
("Sans-Serif Normal ", attrs),
|
||||||
("Sans-Serif Bold ", attrs.weight(Weight::BOLD)),
|
("Sans-Serif Bold ", attrs.weight(Weight::BOLD)),
|
||||||
("Sans-Serif Italic ", attrs.style(Style::Italic)),
|
("Sans-Serif Italic ", attrs.style(Style::Italic)),
|
||||||
|
|
@ -73,6 +78,13 @@ fn main() {
|
||||||
("Blue ", attrs.color(Color::rgb(0x00, 0x00, 0xFF))),
|
("Blue ", attrs.color(Color::rgb(0x00, 0x00, 0xFF))),
|
||||||
("Indigo ", attrs.color(Color::rgb(0x4B, 0x00, 0x82))),
|
("Indigo ", attrs.color(Color::rgb(0x4B, 0x00, 0x82))),
|
||||||
("Violet ", attrs.color(Color::rgb(0x94, 0x00, 0xD3))),
|
("Violet ", attrs.color(Color::rgb(0x94, 0x00, 0xD3))),
|
||||||
|
("U", attrs.color(Color::rgb(0x94, 0x00, 0xD3))),
|
||||||
|
("N", attrs.color(Color::rgb(0x4B, 0x00, 0x82))),
|
||||||
|
("I", attrs.color(Color::rgb(0x00, 0x00, 0xFF))),
|
||||||
|
("C", attrs.color(Color::rgb(0x00, 0xFF, 0x00))),
|
||||||
|
("O", attrs.color(Color::rgb(0xFF, 0xFF, 0x00))),
|
||||||
|
("R", attrs.color(Color::rgb(0xFF, 0x7F, 0x00))),
|
||||||
|
("N", attrs.color(Color::rgb(0xFF, 0x00, 0x00))),
|
||||||
] {
|
] {
|
||||||
if text == "\n" {
|
if text == "\n" {
|
||||||
line_i += 1;
|
line_i += 1;
|
||||||
|
|
@ -86,11 +98,7 @@ fn main() {
|
||||||
let start = line.text.len();
|
let start = line.text.len();
|
||||||
line.text.push_str(text);
|
line.text.push_str(text);
|
||||||
let end = line.text.len();
|
let end = line.text.len();
|
||||||
line.attrs_spans.push(AttrsSpan {
|
line.attrs_list.add_span(start, end, attrs);
|
||||||
start,
|
|
||||||
end,
|
|
||||||
attrs,
|
|
||||||
});
|
|
||||||
line.reset();
|
line.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
50
src/attrs.rs
50
src/attrs.rs
|
|
@ -80,10 +80,52 @@ impl<'a> Attrs<'a> {
|
||||||
//TODO: smarter way of including emoji
|
//TODO: smarter way of including emoji
|
||||||
(face.monospaced == self.monospaced || face.post_script_name.contains("Emoji"))
|
(face.monospaced == self.monospaced || face.post_script_name.contains("Emoji"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn compatible(&self, other: &Self) -> bool {
|
||||||
|
self.family == other.family
|
||||||
|
&& self.monospaced == other.monospaced
|
||||||
|
&& self.stretch == other.stretch
|
||||||
|
&& self.style == other.style
|
||||||
|
&& self.weight == other.weight
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct AttrsSpan<'a> {
|
pub struct AttrsList<'a> {
|
||||||
pub start: usize,
|
defaults: Attrs<'a>,
|
||||||
pub end: usize,
|
spans: Vec<(usize, usize, Attrs<'a>)>,
|
||||||
pub attrs: Attrs<'a>
|
}
|
||||||
|
|
||||||
|
impl<'a> AttrsList<'a> {
|
||||||
|
pub fn new(defaults: Attrs<'a>) -> Self {
|
||||||
|
Self {
|
||||||
|
defaults,
|
||||||
|
spans: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn defaults(&self) -> Attrs<'a> {
|
||||||
|
self.defaults
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn spans(&self) -> &Vec<(usize, usize, Attrs<'a>)> {
|
||||||
|
&self.spans
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clear_spans(&mut self) {
|
||||||
|
self.spans.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_span(&mut self, start: usize, end: usize, attrs: Attrs<'a>) {
|
||||||
|
self.spans.push((start, end, attrs));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_span(&self, start: usize, end: usize) -> Attrs<'a> {
|
||||||
|
let mut attrs = self.defaults;
|
||||||
|
for span in self.spans.iter() {
|
||||||
|
if start >= span.0 && end <= span.1 {
|
||||||
|
attrs = span.2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
attrs
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ use std::{
|
||||||
};
|
};
|
||||||
use unicode_segmentation::UnicodeSegmentation;
|
use unicode_segmentation::UnicodeSegmentation;
|
||||||
|
|
||||||
use crate::{Attrs, AttrsSpan, FontSystem, LayoutGlyph, LayoutLine, ShapeLine};
|
use crate::{Attrs, AttrsList, FontSystem, LayoutGlyph, LayoutLine, ShapeLine};
|
||||||
|
|
||||||
/// An action to perform on a [TextBuffer]
|
/// An action to perform on a [TextBuffer]
|
||||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||||
|
|
@ -175,21 +175,16 @@ impl fmt::Display for TextMetrics {
|
||||||
|
|
||||||
pub struct TextBufferLine<'a> {
|
pub struct TextBufferLine<'a> {
|
||||||
pub text: String,
|
pub text: String,
|
||||||
pub attrs_spans: Vec<AttrsSpan<'a>>,
|
pub attrs_list: AttrsList<'a>,
|
||||||
shape_opt: Option<ShapeLine>,
|
shape_opt: Option<ShapeLine>,
|
||||||
layout_opt: Option<Vec<LayoutLine>>,
|
layout_opt: Option<Vec<LayoutLine>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> TextBufferLine<'a> {
|
impl<'a> TextBufferLine<'a> {
|
||||||
pub fn new(text: String, attrs: Attrs<'a>) -> Self {
|
pub fn new(text: String, attrs: Attrs<'a>) -> Self {
|
||||||
let attrs_spans = vec![AttrsSpan {
|
|
||||||
start: 0,
|
|
||||||
end: text.len(),
|
|
||||||
attrs
|
|
||||||
}];
|
|
||||||
Self {
|
Self {
|
||||||
text,
|
text,
|
||||||
attrs_spans,
|
attrs_list: AttrsList::new(attrs),
|
||||||
shape_opt: None,
|
shape_opt: None,
|
||||||
layout_opt: None,
|
layout_opt: None,
|
||||||
}
|
}
|
||||||
|
|
@ -206,7 +201,7 @@ impl<'a> TextBufferLine<'a> {
|
||||||
|
|
||||||
pub fn shape(&mut self, font_system: &'a FontSystem<'a>) -> &ShapeLine {
|
pub fn shape(&mut self, font_system: &'a FontSystem<'a>) -> &ShapeLine {
|
||||||
if self.shape_opt.is_none() {
|
if self.shape_opt.is_none() {
|
||||||
self.shape_opt = Some(ShapeLine::new(font_system, &self.text, &self.attrs_spans));
|
self.shape_opt = Some(ShapeLine::new(font_system, &self.text, &self.attrs_list));
|
||||||
self.layout_opt = None;
|
self.layout_opt = None;
|
||||||
}
|
}
|
||||||
self.shape_opt.as_ref().unwrap()
|
self.shape_opt.as_ref().unwrap()
|
||||||
|
|
@ -509,12 +504,8 @@ impl<'a> TextBuffer<'a> {
|
||||||
self.attrs = attrs;
|
self.attrs = attrs;
|
||||||
|
|
||||||
for line in self.lines.iter_mut() {
|
for line in self.lines.iter_mut() {
|
||||||
|
line.attrs_list = AttrsList::new(attrs);
|
||||||
line.reset();
|
line.reset();
|
||||||
line.attrs_spans = vec![AttrsSpan {
|
|
||||||
start: 0,
|
|
||||||
end: line.text.len(),
|
|
||||||
attrs
|
|
||||||
}];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.shape_until_scroll();
|
self.shape_until_scroll();
|
||||||
|
|
|
||||||
333
src/shape.rs
333
src/shape.rs
|
|
@ -1,19 +1,20 @@
|
||||||
// SPDX-License-Identifier: MIT OR Apache-2.0
|
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||||
|
|
||||||
use unicode_script::{Script, UnicodeScript};
|
use unicode_script::{Script, UnicodeScript};
|
||||||
|
use unicode_segmentation::UnicodeSegmentation;
|
||||||
|
|
||||||
use crate::{Attrs, AttrsSpan, CacheKey, Color, Font, FontSystem, LayoutGlyph, LayoutLine};
|
use crate::{AttrsList, CacheKey, Color, Font, FontSystem, LayoutGlyph, LayoutLine};
|
||||||
use crate::fallback::FontFallbackIter;
|
use crate::fallback::FontFallbackIter;
|
||||||
|
|
||||||
fn shape_fallback(
|
fn shape_fallback(
|
||||||
font: &Font,
|
font: &Font,
|
||||||
line: &str,
|
line: &str,
|
||||||
attrs_spans: &[AttrsSpan],
|
attrs_list: &AttrsList,
|
||||||
start_word: usize,
|
start_run: usize,
|
||||||
end_word: usize,
|
end_run: usize,
|
||||||
span_rtl: bool,
|
span_rtl: bool,
|
||||||
) -> (Vec<ShapeGlyph>, Vec<usize>) {
|
) -> (Vec<ShapeGlyph>, Vec<usize>) {
|
||||||
let word = &line[start_word..end_word];
|
let run = &line[start_run..end_run];
|
||||||
|
|
||||||
let font_scale = font.rustybuzz.units_per_em() as f32;
|
let font_scale = font.rustybuzz.units_per_em() as f32;
|
||||||
|
|
||||||
|
|
@ -23,7 +24,7 @@ fn shape_fallback(
|
||||||
} else {
|
} else {
|
||||||
rustybuzz::Direction::LeftToRight
|
rustybuzz::Direction::LeftToRight
|
||||||
});
|
});
|
||||||
buffer.push_str(word);
|
buffer.push_str(run);
|
||||||
buffer.guess_segment_properties();
|
buffer.guess_segment_properties();
|
||||||
|
|
||||||
let rtl = match buffer.direction() {
|
let rtl = match buffer.direction() {
|
||||||
|
|
@ -45,32 +46,23 @@ fn shape_fallback(
|
||||||
let x_offset = pos.x_offset as f32 / font_scale;
|
let x_offset = pos.x_offset as f32 / font_scale;
|
||||||
let y_offset = pos.y_offset as f32 / font_scale;
|
let y_offset = pos.y_offset as f32 / font_scale;
|
||||||
|
|
||||||
let start_glyph = start_word + info.cluster as usize;
|
let start_glyph = start_run + info.cluster as usize;
|
||||||
|
|
||||||
//println!(" {:?} {:?}", info, pos);
|
//println!(" {:?} {:?}", info, pos);
|
||||||
if info.glyph_id == 0 {
|
if info.glyph_id == 0 {
|
||||||
missing.push(start_glyph);
|
missing.push(start_glyph);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut attrs = Attrs::new();
|
|
||||||
for attrs_span in attrs_spans.iter() {
|
|
||||||
//TODO: BETTER SUPPORT ATTRIBUTES PER GLYPH
|
|
||||||
if attrs_span.start <= start_glyph && attrs_span.end > start_glyph {
|
|
||||||
attrs = attrs_span.attrs;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
glyphs.push(ShapeGlyph {
|
glyphs.push(ShapeGlyph {
|
||||||
start: start_glyph,
|
start: start_glyph,
|
||||||
end: end_word, // Set later
|
end: end_run, // Set later
|
||||||
x_advance,
|
x_advance,
|
||||||
y_advance,
|
y_advance,
|
||||||
x_offset,
|
x_offset,
|
||||||
y_offset,
|
y_offset,
|
||||||
font_id: font.info.id,
|
font_id: font.info.id,
|
||||||
glyph_id: info.glyph_id.try_into().unwrap(),
|
glyph_id: info.glyph_id.try_into().unwrap(),
|
||||||
color_opt: attrs.color_opt,
|
color_opt: None,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -99,9 +91,149 @@ fn shape_fallback(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set color
|
||||||
|
//TODO: these attributes should not be related to shaping
|
||||||
|
for glyph in glyphs.iter_mut() {
|
||||||
|
let attrs = attrs_list.get_span(glyph.start, glyph.end);
|
||||||
|
glyph.color_opt = attrs.color_opt;
|
||||||
|
}
|
||||||
|
|
||||||
(glyphs, missing)
|
(glyphs, missing)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn shape_run<'a>(
|
||||||
|
font_system: &'a FontSystem<'a>,
|
||||||
|
line: &str,
|
||||||
|
attrs_list: &AttrsList<'a>,
|
||||||
|
start_run: usize,
|
||||||
|
end_run: usize,
|
||||||
|
span_rtl: bool,
|
||||||
|
) -> Vec<ShapeGlyph> {
|
||||||
|
//TODO: use smallvec?
|
||||||
|
let mut scripts = Vec::new();
|
||||||
|
for c in line[start_run..end_run].chars() {
|
||||||
|
match c.script() {
|
||||||
|
Script::Common |
|
||||||
|
Script::Inherited |
|
||||||
|
Script::Latin |
|
||||||
|
Script::Unknown => (),
|
||||||
|
script => if ! scripts.contains(&script) {
|
||||||
|
scripts.push(script);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log::trace!(
|
||||||
|
" Run {:?}: '{}'",
|
||||||
|
scripts,
|
||||||
|
&line[start_run..end_run],
|
||||||
|
);
|
||||||
|
|
||||||
|
let attrs = attrs_list.get_span(start_run, end_run);
|
||||||
|
|
||||||
|
let font_matches = font_system.get_font_matches(attrs);
|
||||||
|
|
||||||
|
let default_families = [font_matches.default_family.as_str()];
|
||||||
|
let mut font_iter = FontFallbackIter::new(
|
||||||
|
&font_matches.fonts,
|
||||||
|
&default_families,
|
||||||
|
scripts,
|
||||||
|
font_matches.locale
|
||||||
|
);
|
||||||
|
|
||||||
|
let (mut glyphs, mut missing) = shape_fallback(
|
||||||
|
font_iter.next().unwrap(),
|
||||||
|
line,
|
||||||
|
attrs_list,
|
||||||
|
start_run,
|
||||||
|
end_run,
|
||||||
|
span_rtl,
|
||||||
|
);
|
||||||
|
|
||||||
|
//TODO: improve performance!
|
||||||
|
while !missing.is_empty() {
|
||||||
|
let font = match font_iter.next() {
|
||||||
|
Some(some) => some,
|
||||||
|
None => break,
|
||||||
|
};
|
||||||
|
|
||||||
|
log::trace!("Evaluating fallback with font '{}'", font.info.family);
|
||||||
|
let (mut fb_glyphs, fb_missing) = shape_fallback(
|
||||||
|
font,
|
||||||
|
line,
|
||||||
|
attrs_list,
|
||||||
|
start_run,
|
||||||
|
end_run,
|
||||||
|
span_rtl,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Insert all matching glyphs
|
||||||
|
let mut fb_i = 0;
|
||||||
|
while fb_i < fb_glyphs.len() {
|
||||||
|
let start = fb_glyphs[fb_i].start;
|
||||||
|
let end = fb_glyphs[fb_i].end;
|
||||||
|
|
||||||
|
// Skip clusters that are not missing, or where the fallback font is missing
|
||||||
|
if !missing.contains(&start) || fb_missing.contains(&start) {
|
||||||
|
fb_i += 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut missing_i = 0;
|
||||||
|
while missing_i < missing.len() {
|
||||||
|
if missing[missing_i] >= start && missing[missing_i] < end {
|
||||||
|
// println!("No longer missing {}", missing[missing_i]);
|
||||||
|
missing.remove(missing_i);
|
||||||
|
} else {
|
||||||
|
missing_i += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find prior glyphs
|
||||||
|
let mut i = 0;
|
||||||
|
while i < glyphs.len() {
|
||||||
|
if glyphs[i].start >= start && glyphs[i].end <= end {
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove prior glyphs
|
||||||
|
while i < glyphs.len() {
|
||||||
|
if glyphs[i].start >= start && glyphs[i].end <= end {
|
||||||
|
let _glyph = glyphs.remove(i);
|
||||||
|
// log::trace!("Removed {},{} from {}", _glyph.start, _glyph.end, i);
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while fb_i < fb_glyphs.len() {
|
||||||
|
if fb_glyphs[fb_i].start >= start && fb_glyphs[fb_i].end <= end {
|
||||||
|
let fb_glyph = fb_glyphs.remove(fb_i);
|
||||||
|
// log::trace!("Insert {},{} from font {} at {}", fb_glyph.start, fb_glyph.end, font_i, i);
|
||||||
|
glyphs.insert(i, fb_glyph);
|
||||||
|
i += 1;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Debug missing font fallbacks
|
||||||
|
font_iter.check_missing(&line[start_run..end_run]);
|
||||||
|
|
||||||
|
/*
|
||||||
|
for glyph in glyphs.iter() {
|
||||||
|
log::trace!("'{}': {}, {}, {}, {}", &line[glyph.start..glyph.end], glyph.x_advance, glyph.y_advance, glyph.x_offset, glyph.y_offset);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
glyphs
|
||||||
|
}
|
||||||
|
|
||||||
pub struct ShapeGlyph {
|
pub struct ShapeGlyph {
|
||||||
pub start: usize,
|
pub start: usize,
|
||||||
pub end: usize,
|
pub end: usize,
|
||||||
|
|
@ -149,142 +281,55 @@ impl ShapeWord {
|
||||||
pub fn new<'a>(
|
pub fn new<'a>(
|
||||||
font_system: &'a FontSystem<'a>,
|
font_system: &'a FontSystem<'a>,
|
||||||
line: &str,
|
line: &str,
|
||||||
attrs_spans: &[AttrsSpan<'a>],
|
attrs_list: &AttrsList<'a>,
|
||||||
start_word: usize,
|
start_word: usize,
|
||||||
end_word: usize,
|
end_word: usize,
|
||||||
span_rtl: bool,
|
span_rtl: bool,
|
||||||
blank: bool,
|
blank: bool,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
//TODO: use smallvec?
|
let word = &line[start_word..end_word];
|
||||||
let mut scripts = Vec::new();
|
|
||||||
for c in line[start_word..end_word].chars() {
|
|
||||||
match c.script() {
|
|
||||||
Script::Common |
|
|
||||||
Script::Inherited |
|
|
||||||
Script::Latin |
|
|
||||||
Script::Unknown => (),
|
|
||||||
script => if ! scripts.contains(&script) {
|
|
||||||
scripts.push(script);
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
log::trace!(
|
log::trace!(
|
||||||
" Word {:?}{}: '{}'",
|
" Word{}: '{}'",
|
||||||
scripts,
|
|
||||||
if blank { " BLANK" } else { "" },
|
if blank { " BLANK" } else { "" },
|
||||||
&line[start_word..end_word],
|
word
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut attrs = Attrs::new();
|
let mut glyphs = Vec::new();
|
||||||
for attrs_span in attrs_spans.iter() {
|
|
||||||
//TODO: BETTER SUPPORT ATTRIBUTES PER GLYPH
|
let mut start_run = start_word;
|
||||||
if attrs_span.start <= start_word && attrs_span.end >= end_word {
|
let mut attrs = attrs_list.defaults();
|
||||||
attrs = attrs_span.attrs;
|
for (egc_i, egc) in word.grapheme_indices(true) {
|
||||||
break;
|
let start_egc = start_word + egc_i;
|
||||||
|
let end_egc = start_egc + egc.len();
|
||||||
|
let attrs_egc = attrs_list.get_span(start_egc, end_egc);
|
||||||
|
if ! attrs.compatible(&attrs_egc) {
|
||||||
|
//TODO: more efficient
|
||||||
|
glyphs.append(&mut shape_run(
|
||||||
|
font_system,
|
||||||
|
line,
|
||||||
|
attrs_list,
|
||||||
|
start_run,
|
||||||
|
start_egc,
|
||||||
|
span_rtl
|
||||||
|
));
|
||||||
|
|
||||||
|
start_run = start_egc;
|
||||||
|
attrs = attrs_egc;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if start_run < end_word {
|
||||||
let font_matches = font_system.get_font_matches(attrs);
|
//TODO: more efficient
|
||||||
|
glyphs.append(&mut shape_run(
|
||||||
let default_families = [font_matches.default_family.as_str()];
|
font_system,
|
||||||
let mut font_iter = FontFallbackIter::new(
|
|
||||||
&font_matches.fonts,
|
|
||||||
&default_families,
|
|
||||||
scripts,
|
|
||||||
font_matches.locale
|
|
||||||
);
|
|
||||||
|
|
||||||
let (mut glyphs, mut missing) = shape_fallback(
|
|
||||||
font_iter.next().unwrap(),
|
|
||||||
line,
|
|
||||||
attrs_spans,
|
|
||||||
start_word,
|
|
||||||
end_word,
|
|
||||||
span_rtl,
|
|
||||||
);
|
|
||||||
|
|
||||||
//TODO: improve performance!
|
|
||||||
while !missing.is_empty() {
|
|
||||||
let font = match font_iter.next() {
|
|
||||||
Some(some) => some,
|
|
||||||
None => break,
|
|
||||||
};
|
|
||||||
|
|
||||||
log::trace!("Evaluating fallback with font '{}'", font.info.family);
|
|
||||||
let (mut fb_glyphs, fb_missing) = shape_fallback(
|
|
||||||
font,
|
|
||||||
line,
|
line,
|
||||||
attrs_spans,
|
attrs_list,
|
||||||
start_word,
|
start_run,
|
||||||
end_word,
|
end_word,
|
||||||
span_rtl,
|
span_rtl
|
||||||
);
|
));
|
||||||
|
|
||||||
// Insert all matching glyphs
|
|
||||||
let mut fb_i = 0;
|
|
||||||
while fb_i < fb_glyphs.len() {
|
|
||||||
let start = fb_glyphs[fb_i].start;
|
|
||||||
let end = fb_glyphs[fb_i].end;
|
|
||||||
|
|
||||||
// Skip clusters that are not missing, or where the fallback font is missing
|
|
||||||
if !missing.contains(&start) || fb_missing.contains(&start) {
|
|
||||||
fb_i += 1;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut missing_i = 0;
|
|
||||||
while missing_i < missing.len() {
|
|
||||||
if missing[missing_i] >= start && missing[missing_i] < end {
|
|
||||||
// println!("No longer missing {}", missing[missing_i]);
|
|
||||||
missing.remove(missing_i);
|
|
||||||
} else {
|
|
||||||
missing_i += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find prior glyphs
|
|
||||||
let mut i = 0;
|
|
||||||
while i < glyphs.len() {
|
|
||||||
if glyphs[i].start >= start && glyphs[i].end <= end {
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
i += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove prior glyphs
|
|
||||||
while i < glyphs.len() {
|
|
||||||
if glyphs[i].start >= start && glyphs[i].end <= end {
|
|
||||||
let _glyph = glyphs.remove(i);
|
|
||||||
// log::trace!("Removed {},{} from {}", _glyph.start, _glyph.end, i);
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
while fb_i < fb_glyphs.len() {
|
|
||||||
if fb_glyphs[fb_i].start >= start && fb_glyphs[fb_i].end <= end {
|
|
||||||
let fb_glyph = fb_glyphs.remove(fb_i);
|
|
||||||
// log::trace!("Insert {},{} from font {} at {}", fb_glyph.start, fb_glyph.end, font_i, i);
|
|
||||||
glyphs.insert(i, fb_glyph);
|
|
||||||
i += 1;
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Debug missing font fallbacks
|
|
||||||
font_iter.check_missing(&line[start_word..end_word]);
|
|
||||||
|
|
||||||
/*
|
|
||||||
for glyph in glyphs.iter() {
|
|
||||||
log::trace!("'{}': {}, {}, {}, {}", &line[glyph.start..glyph.end], glyph.x_advance, glyph.y_advance, glyph.x_offset, glyph.y_offset);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
Self { blank, glyphs }
|
Self { blank, glyphs }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -298,7 +343,7 @@ impl ShapeSpan {
|
||||||
pub fn new<'a>(
|
pub fn new<'a>(
|
||||||
font_system: &'a FontSystem<'a>,
|
font_system: &'a FontSystem<'a>,
|
||||||
line: &str,
|
line: &str,
|
||||||
attrs_spans: &[AttrsSpan<'a>],
|
attrs_list: &AttrsList<'a>,
|
||||||
start_span: usize,
|
start_span: usize,
|
||||||
end_span: usize,
|
end_span: usize,
|
||||||
line_rtl: bool,
|
line_rtl: bool,
|
||||||
|
|
@ -328,7 +373,7 @@ impl ShapeSpan {
|
||||||
words.push(ShapeWord::new(
|
words.push(ShapeWord::new(
|
||||||
font_system,
|
font_system,
|
||||||
line,
|
line,
|
||||||
attrs_spans,
|
attrs_list,
|
||||||
start_span + start_word,
|
start_span + start_word,
|
||||||
start_span + start_lb,
|
start_span + start_lb,
|
||||||
span_rtl,
|
span_rtl,
|
||||||
|
|
@ -339,7 +384,7 @@ impl ShapeSpan {
|
||||||
words.push(ShapeWord::new(
|
words.push(ShapeWord::new(
|
||||||
font_system,
|
font_system,
|
||||||
line,
|
line,
|
||||||
attrs_spans,
|
attrs_list,
|
||||||
start_span + start_lb,
|
start_span + start_lb,
|
||||||
start_span + end_lb,
|
start_span + end_lb,
|
||||||
span_rtl,
|
span_rtl,
|
||||||
|
|
@ -377,7 +422,7 @@ impl ShapeLine {
|
||||||
pub fn new<'a>(
|
pub fn new<'a>(
|
||||||
font_system: &'a FontSystem<'a>,
|
font_system: &'a FontSystem<'a>,
|
||||||
line: &str,
|
line: &str,
|
||||||
attrs_spans: &[AttrsSpan<'a>]
|
attrs_list: &AttrsList<'a>
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let mut spans = Vec::new();
|
let mut spans = Vec::new();
|
||||||
|
|
||||||
|
|
@ -401,7 +446,7 @@ impl ShapeLine {
|
||||||
spans.push(ShapeSpan::new(
|
spans.push(ShapeSpan::new(
|
||||||
font_system,
|
font_system,
|
||||||
line,
|
line,
|
||||||
attrs_spans,
|
attrs_list,
|
||||||
start,
|
start,
|
||||||
i,
|
i,
|
||||||
line_rtl,
|
line_rtl,
|
||||||
|
|
@ -414,7 +459,7 @@ impl ShapeLine {
|
||||||
spans.push(ShapeSpan::new(
|
spans.push(ShapeSpan::new(
|
||||||
font_system,
|
font_system,
|
||||||
line,
|
line,
|
||||||
attrs_spans,
|
attrs_list,
|
||||||
start,
|
start,
|
||||||
line.len(),
|
line.len(),
|
||||||
line_rtl,
|
line_rtl,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue