mirror of
https://github.com/servo/servo.git
synced 2025-08-03 04:30:10 +01:00
Basic support for bidirectional text
This commit is contained in:
parent
b386d7ae44
commit
dfac8ce4a1
20 changed files with 309 additions and 107 deletions
|
@ -106,7 +106,9 @@ bitflags! {
|
|||
#[doc="Set if we are to ignore ligatures."]
|
||||
const IGNORE_LIGATURES_SHAPING_FLAG = 0x02,
|
||||
#[doc="Set if we are to disable kerning."]
|
||||
const DISABLE_KERNING_SHAPING_FLAG = 0x04
|
||||
const DISABLE_KERNING_SHAPING_FLAG = 0x04,
|
||||
#[doc="Text direction is right-to-left."]
|
||||
const RTL_FLAG = 0x08,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1294,7 +1294,7 @@ impl ScaledFontExtensionMethods for ScaledFont {
|
|||
let mut azglyphs = vec!();
|
||||
azglyphs.reserve(range.length().to_usize());
|
||||
|
||||
for slice in run.natural_word_slices_in_range(range) {
|
||||
for slice in run.natural_word_slices_in_visual_order(range) {
|
||||
for (_i, glyph) in slice.glyphs.iter_glyphs_for_char_range(&slice.range) {
|
||||
let glyph_advance = glyph.advance();
|
||||
let glyph_offset = glyph.offset().unwrap_or(Point2D::zero());
|
||||
|
|
|
@ -5,14 +5,14 @@
|
|||
extern crate harfbuzz;
|
||||
|
||||
use font::{DISABLE_KERNING_SHAPING_FLAG, Font, FontHandleMethods, FontTableMethods, FontTableTag};
|
||||
use font::{IGNORE_LIGATURES_SHAPING_FLAG, ShapingOptions};
|
||||
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 euclid::Point2D;
|
||||
use harfbuzz::{HB_MEMORY_MODE_READONLY, HB_DIRECTION_LTR};
|
||||
use harfbuzz::{HB_MEMORY_MODE_READONLY, HB_DIRECTION_LTR, HB_DIRECTION_RTL};
|
||||
use harfbuzz::{RUST_hb_blob_create, RUST_hb_face_create_for_tables};
|
||||
use harfbuzz::{hb_blob_t};
|
||||
use harfbuzz::{hb_bool_t};
|
||||
|
@ -229,7 +229,11 @@ impl ShaperMethods for Shaper {
|
|||
fn shape_text(&self, text: &str, options: &ShapingOptions, glyphs: &mut GlyphStore) {
|
||||
unsafe {
|
||||
let hb_buffer: *mut hb_buffer_t = RUST_hb_buffer_create();
|
||||
RUST_hb_buffer_set_direction(hb_buffer, HB_DIRECTION_LTR);
|
||||
RUST_hb_buffer_set_direction(hb_buffer, if options.flags.contains(RTL_FLAG) {
|
||||
HB_DIRECTION_RTL
|
||||
} else {
|
||||
HB_DIRECTION_LTR
|
||||
});
|
||||
|
||||
RUST_hb_buffer_add_utf8(hb_buffer,
|
||||
text.as_ptr() as *const c_char,
|
||||
|
|
|
@ -5,13 +5,15 @@
|
|||
use font::{Font, FontHandleMethods, FontMetrics, IS_WHITESPACE_SHAPING_FLAG, RunMetrics};
|
||||
use font::{ShapingOptions};
|
||||
use platform::font_template::FontTemplateData;
|
||||
use text::glyph::{CharIndex, GlyphStore};
|
||||
|
||||
use util::geometry::Au;
|
||||
use util::range::Range;
|
||||
use util::vec::{Comparator, FullBinarySearchMethods};
|
||||
|
||||
use std::cmp::{Ordering, max};
|
||||
use std::slice::Iter;
|
||||
use std::sync::Arc;
|
||||
use text::glyph::{CharIndex, GlyphStore};
|
||||
|
||||
/// A single "paragraph" of text in one font size and style.
|
||||
#[derive(Clone, Deserialize, Serialize)]
|
||||
|
@ -23,6 +25,7 @@ pub struct TextRun {
|
|||
pub font_metrics: FontMetrics,
|
||||
/// The glyph runs that make up this text run.
|
||||
pub glyphs: Arc<Vec<GlyphRun>>,
|
||||
pub bidi_level: u8,
|
||||
}
|
||||
|
||||
/// A single series of glyphs within a text run.
|
||||
|
@ -35,8 +38,10 @@ pub struct GlyphRun {
|
|||
}
|
||||
|
||||
pub struct NaturalWordSliceIterator<'a> {
|
||||
glyph_iter: Iter<'a, GlyphRun>,
|
||||
glyphs: &'a [GlyphRun],
|
||||
index: usize,
|
||||
range: Range<CharIndex>,
|
||||
reverse: bool,
|
||||
}
|
||||
|
||||
struct CharIndexComparator;
|
||||
|
@ -80,11 +85,20 @@ impl<'a> Iterator for NaturalWordSliceIterator<'a> {
|
|||
// inline(always) due to the inefficient rt failures messing up inline heuristics, I think.
|
||||
#[inline(always)]
|
||||
fn next(&mut self) -> Option<TextRunSlice<'a>> {
|
||||
let slice_glyphs = self.glyph_iter.next();
|
||||
if slice_glyphs.is_none() {
|
||||
return None;
|
||||
let slice_glyphs;
|
||||
if self.reverse {
|
||||
if self.index == 0 {
|
||||
return None;
|
||||
}
|
||||
self.index -= 1;
|
||||
slice_glyphs = &self.glyphs[self.index];
|
||||
} else {
|
||||
if self.index >= self.glyphs.len() {
|
||||
return None;
|
||||
}
|
||||
slice_glyphs = &self.glyphs[self.index];
|
||||
self.index += 1;
|
||||
}
|
||||
let slice_glyphs = slice_glyphs.unwrap();
|
||||
|
||||
let mut char_range = self.range.intersect(&slice_glyphs.range);
|
||||
let slice_range_begin = slice_glyphs.range.begin();
|
||||
|
@ -140,7 +154,7 @@ impl<'a> Iterator for CharacterSliceIterator<'a> {
|
|||
}
|
||||
|
||||
impl<'a> TextRun {
|
||||
pub fn new(font: &mut Font, text: String, options: &ShapingOptions) -> TextRun {
|
||||
pub fn new(font: &mut Font, text: String, options: &ShapingOptions, bidi_level: u8) -> TextRun {
|
||||
let glyphs = TextRun::break_and_shape(font, &text, options);
|
||||
let run = TextRun {
|
||||
text: Arc::new(text),
|
||||
|
@ -148,6 +162,7 @@ impl<'a> TextRun {
|
|||
font_template: font.handle.template(),
|
||||
actual_pt_size: font.actual_pt_size,
|
||||
glyphs: Arc::new(glyphs),
|
||||
bidi_level: bidi_level,
|
||||
};
|
||||
return run;
|
||||
}
|
||||
|
@ -279,8 +294,36 @@ impl<'a> TextRun {
|
|||
Some(index) => index,
|
||||
};
|
||||
NaturalWordSliceIterator {
|
||||
glyph_iter: self.glyphs[index..].iter(),
|
||||
glyphs: &self.glyphs[..],
|
||||
index: index,
|
||||
range: *range,
|
||||
reverse: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns an iterator that over natural word slices in visual order (left to right or
|
||||
/// right to left, depending on the bidirectional embedding level).
|
||||
pub fn natural_word_slices_in_visual_order(&'a self, range: &Range<CharIndex>)
|
||||
-> NaturalWordSliceIterator<'a> {
|
||||
// Iterate in reverse order if bidi level is RTL.
|
||||
let reverse = self.bidi_level % 2 == 1;
|
||||
|
||||
let index = if reverse {
|
||||
match self.index_of_first_glyph_run_containing(range.end() - CharIndex(1)) {
|
||||
Some(i) => i + 1, // In reverse mode, index points one past the next element.
|
||||
None => 0
|
||||
}
|
||||
} else {
|
||||
match self.index_of_first_glyph_run_containing(range.begin()) {
|
||||
Some(i) => i,
|
||||
None => self.glyphs.len()
|
||||
}
|
||||
};
|
||||
NaturalWordSliceIterator {
|
||||
glyphs: &self.glyphs[..],
|
||||
index: index,
|
||||
range: *range,
|
||||
reverse: reverse,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue