mirror of
https://github.com/servo/servo.git
synced 2025-08-03 12:40:06 +01:00
Use word-break to decide how glyph runs should be created
This commit is contained in:
parent
d5669ead29
commit
e0a48fe596
4 changed files with 15 additions and 72 deletions
|
@ -148,6 +148,8 @@ bitflags! {
|
||||||
const DISABLE_KERNING_SHAPING_FLAG = 0x04,
|
const DISABLE_KERNING_SHAPING_FLAG = 0x04,
|
||||||
#[doc = "Text direction is right-to-left."]
|
#[doc = "Text direction is right-to-left."]
|
||||||
const RTL_FLAG = 0x08,
|
const RTL_FLAG = 0x08,
|
||||||
|
#[doc = "Set if word-break is set to keep-all."]
|
||||||
|
const KEEP_ALL_FLAG = 0x10,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,8 +3,8 @@
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
use app_units::Au;
|
use app_units::Au;
|
||||||
use font::{Font, FontHandleMethods, FontMetrics, IS_WHITESPACE_SHAPING_FLAG, RunMetrics};
|
use font::{Font, FontHandleMethods, FontMetrics, IS_WHITESPACE_SHAPING_FLAG, KEEP_ALL_FLAG};
|
||||||
use font::ShapingOptions;
|
use font::{RunMetrics, ShapingOptions};
|
||||||
use platform::font_template::FontTemplateData;
|
use platform::font_template::FontTemplateData;
|
||||||
use range::Range;
|
use range::Range;
|
||||||
use std::cell::Cell;
|
use std::cell::Cell;
|
||||||
|
@ -135,53 +135,6 @@ impl<'a> Iterator for NaturalWordSliceIterator<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct SoftWrapSliceIterator<'a> {
|
|
||||||
text: &'a str,
|
|
||||||
glyph_run: Option<&'a GlyphRun>,
|
|
||||||
glyph_run_iter: Iter<'a, GlyphRun>,
|
|
||||||
range: Range<ByteIndex>,
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is like NaturalWordSliceIterator, except that soft-wrap opportunities
|
|
||||||
// are allowed. That is, word boundaries are defined solely by UAX#29,
|
|
||||||
// regardless of whether the sequence being broken into different slices is
|
|
||||||
// a sequence of alphanumeric characters. This shouldn't make a difference in
|
|
||||||
// the case of Latin text, but it does in ideographic characters, as well as
|
|
||||||
// scripts such as Thai.
|
|
||||||
impl<'a> Iterator for SoftWrapSliceIterator<'a> {
|
|
||||||
type Item = TextRunSlice<'a>;
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn next(&mut self) -> Option<TextRunSlice<'a>> {
|
|
||||||
let glyph_run = match self.glyph_run {
|
|
||||||
None => return None,
|
|
||||||
Some(glyph_run) => glyph_run,
|
|
||||||
};
|
|
||||||
|
|
||||||
let text_start = self.range.begin();
|
|
||||||
let text = &self.text[text_start.to_usize()..glyph_run.range.end().to_usize()];
|
|
||||||
let slice_text = match LineBreakIterator::new(text).next() {
|
|
||||||
Some((idx, _)) => &text[0..idx],
|
|
||||||
None => unreachable!()
|
|
||||||
};
|
|
||||||
|
|
||||||
let slice_len = ByteIndex(slice_text.len() as isize);
|
|
||||||
self.range.adjust_by(slice_len, -slice_len);
|
|
||||||
if self.range.is_empty() {
|
|
||||||
self.glyph_run = None
|
|
||||||
} else if self.range.intersect(&glyph_run.range).is_empty() {
|
|
||||||
self.glyph_run = self.glyph_run_iter.next();
|
|
||||||
}
|
|
||||||
|
|
||||||
let index_within_glyph_run = text_start - glyph_run.range.begin();
|
|
||||||
Some(TextRunSlice {
|
|
||||||
glyphs: &*glyph_run.glyph_store,
|
|
||||||
offset: glyph_run.range.begin(),
|
|
||||||
range: Range::new(index_within_glyph_run, slice_len),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct CharacterSliceIterator<'a> {
|
pub struct CharacterSliceIterator<'a> {
|
||||||
text: &'a str,
|
text: &'a str,
|
||||||
glyph_run: Option<&'a GlyphRun>,
|
glyph_run: Option<&'a GlyphRun>,
|
||||||
|
@ -256,8 +209,9 @@ impl<'a> TextRun {
|
||||||
.take_while(|&(_, c)| char_is_whitespace(c)).last() {
|
.take_while(|&(_, c)| char_is_whitespace(c)).last() {
|
||||||
whitespace.start = slice.start + i;
|
whitespace.start = slice.start + i;
|
||||||
slice.end = whitespace.start;
|
slice.end = whitespace.start;
|
||||||
} else if idx != text.len() {
|
} else if idx != text.len() && options.flags.contains(KEEP_ALL_FLAG) {
|
||||||
// If there's no whitespace, try increasing the slice.
|
// If there's no whitespace and word-break is set to
|
||||||
|
// keep-all, try increasing the slice.
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if slice.len() > 0 {
|
if slice.len() > 0 {
|
||||||
|
@ -392,24 +346,6 @@ impl<'a> TextRun {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns an iterator that will iterate over all slices of glyphs that represent natural
|
|
||||||
/// words in the given range, where soft wrap opportunities are taken into account.
|
|
||||||
pub fn soft_wrap_slices_in_range(&'a self, range: &Range<ByteIndex>)
|
|
||||||
-> SoftWrapSliceIterator<'a> {
|
|
||||||
let index = match self.index_of_first_glyph_run_containing(range.begin()) {
|
|
||||||
None => self.glyphs.len(),
|
|
||||||
Some(index) => index,
|
|
||||||
};
|
|
||||||
let mut glyph_run_iter = self.glyphs[index..].iter();
|
|
||||||
let first_glyph_run = glyph_run_iter.next();
|
|
||||||
SoftWrapSliceIterator {
|
|
||||||
text: &self.text,
|
|
||||||
glyph_run: first_glyph_run,
|
|
||||||
glyph_run_iter: glyph_run_iter,
|
|
||||||
range: *range,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns an iterator that will iterate over all slices of glyphs that represent individual
|
/// Returns an iterator that will iterate over all slices of glyphs that represent individual
|
||||||
/// characters in the given range.
|
/// characters in the given range.
|
||||||
pub fn character_slices_in_range(&'a self, range: &Range<ByteIndex>)
|
pub fn character_slices_in_range(&'a self, range: &Range<ByteIndex>)
|
||||||
|
|
|
@ -1640,7 +1640,7 @@ impl Fragment {
|
||||||
word_break::T::normal => {
|
word_break::T::normal => {
|
||||||
// Break at normal word boundaries, allowing for soft wrap opportunities.
|
// Break at normal word boundaries, allowing for soft wrap opportunities.
|
||||||
let soft_wrap_breaking_strategy =
|
let soft_wrap_breaking_strategy =
|
||||||
text_fragment_info.run.soft_wrap_slices_in_range(&text_fragment_info.range);
|
text_fragment_info.run.natural_word_slices_in_range(&text_fragment_info.range);
|
||||||
self.calculate_split_position_using_breaking_strategy(
|
self.calculate_split_position_using_breaking_strategy(
|
||||||
soft_wrap_breaking_strategy,
|
soft_wrap_breaking_strategy,
|
||||||
max_inline_size,
|
max_inline_size,
|
||||||
|
|
|
@ -10,7 +10,7 @@ use app_units::Au;
|
||||||
use fragment::{Fragment, REQUIRES_LINE_BREAK_AFTERWARD_IF_WRAPPING_ON_NEWLINES, ScannedTextFlags};
|
use fragment::{Fragment, REQUIRES_LINE_BREAK_AFTERWARD_IF_WRAPPING_ON_NEWLINES, ScannedTextFlags};
|
||||||
use fragment::{SELECTED, ScannedTextFragmentInfo, SpecificFragmentInfo, UnscannedTextFragmentInfo};
|
use fragment::{SELECTED, ScannedTextFragmentInfo, SpecificFragmentInfo, UnscannedTextFragmentInfo};
|
||||||
use gfx::font::{DISABLE_KERNING_SHAPING_FLAG, FontMetrics, IGNORE_LIGATURES_SHAPING_FLAG};
|
use gfx::font::{DISABLE_KERNING_SHAPING_FLAG, FontMetrics, IGNORE_LIGATURES_SHAPING_FLAG};
|
||||||
use gfx::font::{RTL_FLAG, RunMetrics, ShapingFlags, ShapingOptions};
|
use gfx::font::{KEEP_ALL_FLAG, RTL_FLAG, RunMetrics, ShapingFlags, ShapingOptions};
|
||||||
use gfx::font_context::FontContext;
|
use gfx::font_context::FontContext;
|
||||||
use gfx::text::glyph::ByteIndex;
|
use gfx::text::glyph::ByteIndex;
|
||||||
use gfx::text::text_run::TextRun;
|
use gfx::text::text_run::TextRun;
|
||||||
|
@ -24,7 +24,7 @@ use std::collections::LinkedList;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use style::computed_values::{line_height, text_orientation, text_rendering, text_transform};
|
use style::computed_values::{line_height, text_orientation, text_rendering, text_transform};
|
||||||
use style::computed_values::white_space;
|
use style::computed_values::{word_break, white_space};
|
||||||
use style::logical_geometry::{LogicalSize, WritingMode};
|
use style::logical_geometry::{LogicalSize, WritingMode};
|
||||||
use style::properties::ServoComputedValues;
|
use style::properties::ServoComputedValues;
|
||||||
use style::properties::style_structs;
|
use style::properties::style_structs;
|
||||||
|
@ -151,6 +151,7 @@ impl TextRunScanner {
|
||||||
let letter_spacing;
|
let letter_spacing;
|
||||||
let word_spacing;
|
let word_spacing;
|
||||||
let text_rendering;
|
let text_rendering;
|
||||||
|
let word_break;
|
||||||
{
|
{
|
||||||
let in_fragment = self.clump.front().unwrap();
|
let in_fragment = self.clump.front().unwrap();
|
||||||
let font_style = in_fragment.style().get_font_arc();
|
let font_style = in_fragment.style().get_font_arc();
|
||||||
|
@ -169,6 +170,7 @@ impl TextRunScanner {
|
||||||
.map(|lop| lop.to_hash_key())
|
.map(|lop| lop.to_hash_key())
|
||||||
.unwrap_or((Au(0), NotNaN::new(0.0).unwrap()));
|
.unwrap_or((Au(0), NotNaN::new(0.0).unwrap()));
|
||||||
text_rendering = inherited_text_style.text_rendering;
|
text_rendering = inherited_text_style.text_rendering;
|
||||||
|
word_break = inherited_text_style.word_break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// First, transform/compress text of all the nodes.
|
// First, transform/compress text of all the nodes.
|
||||||
|
@ -289,6 +291,9 @@ impl TextRunScanner {
|
||||||
flags.insert(IGNORE_LIGATURES_SHAPING_FLAG);
|
flags.insert(IGNORE_LIGATURES_SHAPING_FLAG);
|
||||||
flags.insert(DISABLE_KERNING_SHAPING_FLAG)
|
flags.insert(DISABLE_KERNING_SHAPING_FLAG)
|
||||||
}
|
}
|
||||||
|
if word_break == word_break::T::keep_all {
|
||||||
|
flags.insert(KEEP_ALL_FLAG);
|
||||||
|
}
|
||||||
let options = ShapingOptions {
|
let options = ShapingOptions {
|
||||||
letter_spacing: letter_spacing,
|
letter_spacing: letter_spacing,
|
||||||
word_spacing: word_spacing,
|
word_spacing: word_spacing,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue