Implement the unicode-bidi property

This commit is contained in:
Matt Brubeck 2015-07-23 10:16:45 -07:00
parent 028707f5cd
commit f8e92b2b01
20 changed files with 128 additions and 71 deletions

View file

@ -146,7 +146,8 @@ impl Font {
}
let mut glyphs = GlyphStore::new(text.chars().count(),
options.flags.contains(IS_WHITESPACE_SHAPING_FLAG));
options.flags.contains(IS_WHITESPACE_SHAPING_FLAG),
options.flags.contains(RTL_FLAG));
shaper.as_ref().unwrap().shape_text(text, options, &mut glyphs);
let glyphs = Arc::new(glyphs);

View file

@ -512,6 +512,7 @@ pub struct GlyphStore {
detail_store: DetailedGlyphStore,
is_whitespace: bool,
is_rtl: bool,
}
int_range_index! {
@ -526,13 +527,14 @@ impl<'a> GlyphStore {
/// Initializes the glyph store, but doesn't actually shape anything.
///
/// Use the `add_*` methods to store glyph data.
pub fn new(length: usize, is_whitespace: bool) -> GlyphStore {
pub fn new(length: usize, is_whitespace: bool, is_rtl: bool) -> GlyphStore {
assert!(length > 0);
GlyphStore {
entry_buffer: vec![GlyphEntry::initial(); length],
detail_store: DetailedGlyphStore::new(),
is_whitespace: is_whitespace,
is_rtl: is_rtl,
}
}
@ -633,8 +635,8 @@ impl<'a> GlyphStore {
GlyphIterator {
store: self,
char_index: rang.begin(),
char_range: rang.each_index(),
char_index: if self.is_rtl { rang.end() } else { rang.begin() - CharIndex(1) },
char_range: *rang,
glyph_range: None,
}
}
@ -736,7 +738,7 @@ impl<'a> GlyphStore {
pub struct GlyphIterator<'a> {
store: &'a GlyphStore,
char_index: CharIndex,
char_range: EachIndex<isize, CharIndex>,
char_range: Range<CharIndex>,
glyph_range: Option<EachIndex<isize, CharIndex>>,
}
@ -776,23 +778,28 @@ impl<'a> Iterator for GlyphIterator<'a> {
// `next_complex_glyph()`.
#[inline(always)]
fn next(&mut self) -> Option<(CharIndex, GlyphInfo<'a>)> {
// Would use 'match' here but it borrows contents in a way that
// interferes with mutation.
// Would use 'match' here but it borrows contents in a way that interferes with mutation.
if self.glyph_range.is_some() {
self.next_glyph_range()
return self.next_glyph_range()
}
// No glyph range. Look at next character.
self.char_index = self.char_index + if self.store.is_rtl {
CharIndex(-1)
} else {
// No glyph range. Look at next character.
self.char_range.next().and_then(|i| {
self.char_index = i;
assert!(i < self.store.char_len());
let entry = self.store.entry_buffer[i.to_usize()];
if entry.is_simple() {
Some((self.char_index, GlyphInfo::Simple(self.store, i)))
} else {
// Fall back to the slow path.
self.next_complex_glyph(&entry, i)
}
})
CharIndex(1)
};
let i = self.char_index;
if !self.char_range.contains(i) {
return None
}
debug_assert!(i < self.store.char_len());
let entry = self.store.entry_buffer[i.to_usize()];
if entry.is_simple() {
Some((i, GlyphInfo::Simple(self.store, i)))
} else {
// Fall back to the slow path.
self.next_complex_glyph(&entry, i)
}
}
}

View file

@ -9,7 +9,7 @@ use font::{IGNORE_LIGATURES_SHAPING_FLAG, RTL_FLAG, ShapingOptions};
use platform::font::FontTable;
use text::glyph::{CharIndex, GlyphStore, GlyphId, GlyphData};
use text::shaping::ShaperMethods;
use text::util::{float_to_fixed, fixed_to_float};
use text::util::{float_to_fixed, fixed_to_float, is_bidi_control};
use euclid::Point2D;
use harfbuzz::{HB_MEMORY_MODE_READONLY, HB_DIRECTION_LTR, HB_DIRECTION_RTL};
@ -279,7 +279,11 @@ impl Shaper {
// GlyphStore records are indexed by character, not byte offset.
// so, we must be careful to increment this when saving glyph entries.
let mut char_idx = CharIndex(0);
let (mut char_idx, char_step) = if options.flags.contains(RTL_FLAG) {
(CharIndex(char_max as isize - 1), CharIndex(-1))
} else {
(CharIndex(0), CharIndex(1))
};
debug!("Shaped text[char count={}], got back {} glyph info records.",
char_max,
@ -318,10 +322,10 @@ impl Shaper {
debug!("{} -> {}", i, loc);
}
debug!("text: {}", text);
debug!("text: {:?}", text);
debug!("(char idx): char->(glyph index):");
for (i, ch) in text.char_indices() {
debug!("{}: {} --> {}", i, ch, byte_to_glyph[i]);
debug!("{}: {:?} --> {}", i, ch, byte_to_glyph[i]);
}
// some helpers
@ -453,16 +457,20 @@ impl Shaper {
//
// NB: When we acquire the ability to handle ligatures that cross word boundaries,
// we'll need to do something special to handle `word-spacing` properly.
let shape = glyph_data.get_entry_for_glyph(glyph_span.begin(), &mut y_pos);
let character = text.char_at(char_byte_span.begin());
let advance = self.advance_for_shaped_glyph(shape.advance, character, options);
let data = GlyphData::new(shape.codepoint,
advance,
shape.offset,
false,
true,
true);
glyphs.add_glyph_for_char_index(char_idx, Some(character), &data);
if is_bidi_control(character) {
glyphs.add_nonglyph_for_char_index(char_idx, false, false);
} else {
let shape = glyph_data.get_entry_for_glyph(glyph_span.begin(), &mut y_pos);
let advance = self.advance_for_shaped_glyph(shape.advance, character, options);
let data = GlyphData::new(shape.codepoint,
advance,
shape.offset,
false,
true,
true);
glyphs.add_glyph_for_char_index(char_idx, Some(character), &data);
}
} else {
// collect all glyphs to be assigned to the first character.
let mut datas = vec!();
@ -488,7 +496,7 @@ impl Shaper {
drop(range.ch);
i = range.next;
if i >= covered_byte_span.end() { break; }
char_idx = char_idx + CharIndex(1);
char_idx = char_idx + char_step;
glyphs.add_nonglyph_for_char_index(char_idx, false, false);
}
}
@ -498,7 +506,7 @@ impl Shaper {
glyph_span.reset(end, 0);
let end = char_byte_span.end();; // FIXME: borrow checker workaround
char_byte_span.reset(end, 0);
char_idx = char_idx + CharIndex(1);
char_idx = char_idx + char_step;
}
// this must be called after adding all glyph data; it sorts the

View file

@ -92,9 +92,9 @@ pub fn transform_text(text: &str,
}
}
fn is_always_discardable_char(_ch: char) -> bool {
// TODO: check for bidi control chars, soft hyphens.
false
fn is_always_discardable_char(ch: char) -> bool {
// TODO: check for soft hyphens.
is_bidi_control(ch)
}
}
@ -105,3 +105,12 @@ pub fn float_to_fixed(before: usize, f: f64) -> i32 {
pub fn fixed_to_float(before: usize, f: i32) -> f64 {
f as f64 * 1.0f64 / ((1i32 << before) as f64)
}
pub fn is_bidi_control(c: char) -> bool {
match c {
'\u{202A}'...'\u{202E}' => true,
'\u{2066}'...'\u{2069}' => true,
'\u{200E}' | '\u{200F}' | '\u{061C}' => true,
_ => false
}
}