1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 use app_units::Au; 6 use font::{Font, FontHandleMethods, FontMetrics, ShapingFlags}; 7 use font::{RunMetrics, ShapingOptions}; 8 use platform::font_template::FontTemplateData; 9 use range::Range; 10 use std::cell::Cell; 11 use std::cmp::{Ordering, max}; 12 use std::slice::Iter; 13 use std::sync::Arc; 14 use style::str::char_is_whitespace; 15 use text::glyph::{ByteIndex, GlyphStore}; 16 use unicode_bidi as bidi; 17 use webrender_api; 18 use xi_unicode::LineBreakLeafIter; 19 20 thread_local! { 21 static INDEX_OF_FIRST_GLYPH_RUN_CACHE: Cell<Option<(*const TextRun, ByteIndex, usize)>> = 22 Cell::new(None) 23 } 24 25 /// A single "paragraph" of text in one font size and style. 26 #[derive(Clone, Deserialize, Serialize)] 27 pub struct TextRun { 28 /// The UTF-8 string represented by this text run. 29 pub text: Arc<String>, 30 pub font_template: Arc<FontTemplateData>, 31 pub actual_pt_size: Au, 32 pub font_metrics: FontMetrics, 33 pub font_key: webrender_api::FontInstanceKey, 34 /// The glyph runs that make up this text run. 35 pub glyphs: Arc<Vec<GlyphRun>>, 36 pub bidi_level: bidi::Level, 37 pub extra_word_spacing: Au, 38 } 39 40 impl Drop for TextRun { drop(&mut self)41 fn drop(&mut self) { 42 // Invalidate the glyph run cache if it was our text run that got freed. 43 INDEX_OF_FIRST_GLYPH_RUN_CACHE.with(|index_of_first_glyph_run_cache| { 44 if let Some((text_run_ptr, _, _)) = index_of_first_glyph_run_cache.get() { 45 if text_run_ptr == (self as *const TextRun) { 46 index_of_first_glyph_run_cache.set(None); 47 } 48 } 49 }) 50 } 51 } 52 53 /// A single series of glyphs within a text run. 54 #[derive(Clone, Deserialize, Serialize)] 55 pub struct GlyphRun { 56 /// The glyphs. 57 pub glyph_store: Arc<GlyphStore>, 58 /// The byte range of characters in the containing run. 59 pub range: Range<ByteIndex>, 60 } 61 62 pub struct NaturalWordSliceIterator<'a> { 63 glyphs: &'a [GlyphRun], 64 index: usize, 65 range: Range<ByteIndex>, 66 reverse: bool, 67 } 68 69 impl GlyphRun { compare(&self, key: &ByteIndex) -> Ordering70 fn compare(&self, key: &ByteIndex) -> Ordering { 71 if *key < self.range.begin() { 72 Ordering::Greater 73 } else if *key >= self.range.end() { 74 Ordering::Less 75 } else { 76 Ordering::Equal 77 } 78 } 79 } 80 81 /// A "slice" of a text run is a series of contiguous glyphs that all belong to the same glyph 82 /// store. Line breaking strategies yield these. 83 pub struct TextRunSlice<'a> { 84 /// The glyph store that the glyphs in this slice belong to. 85 pub glyphs: &'a GlyphStore, 86 /// The byte index that this slice begins at, relative to the start of the *text run*. 87 pub offset: ByteIndex, 88 /// The range that these glyphs encompass, relative to the start of the *glyph store*. 89 pub range: Range<ByteIndex>, 90 } 91 92 impl<'a> TextRunSlice<'a> { 93 /// Returns the range that these glyphs encompass, relative to the start of the *text run*. 94 #[inline] text_run_range(&self) -> Range<ByteIndex>95 pub fn text_run_range(&self) -> Range<ByteIndex> { 96 let mut range = self.range; 97 range.shift_by(self.offset); 98 range 99 } 100 } 101 102 impl<'a> Iterator for NaturalWordSliceIterator<'a> { 103 type Item = TextRunSlice<'a>; 104 105 // inline(always) due to the inefficient rt failures messing up inline heuristics, I think. 106 #[inline(always)] next(&mut self) -> Option<TextRunSlice<'a>>107 fn next(&mut self) -> Option<TextRunSlice<'a>> { 108 let slice_glyphs; 109 if self.reverse { 110 if self.index == 0 { 111 return None; 112 } 113 self.index -= 1; 114 slice_glyphs = &self.glyphs[self.index]; 115 } else { 116 if self.index >= self.glyphs.len() { 117 return None; 118 } 119 slice_glyphs = &self.glyphs[self.index]; 120 self.index += 1; 121 } 122 123 let mut byte_range = self.range.intersect(&slice_glyphs.range); 124 let slice_range_begin = slice_glyphs.range.begin(); 125 byte_range.shift_by(-slice_range_begin); 126 127 if !byte_range.is_empty() { 128 Some(TextRunSlice { 129 glyphs: &*slice_glyphs.glyph_store, 130 offset: slice_range_begin, 131 range: byte_range, 132 }) 133 } else { 134 None 135 } 136 } 137 } 138 139 pub struct CharacterSliceIterator<'a> { 140 text: &'a str, 141 glyph_run: Option<&'a GlyphRun>, 142 glyph_run_iter: Iter<'a, GlyphRun>, 143 range: Range<ByteIndex>, 144 } 145 146 impl<'a> Iterator for CharacterSliceIterator<'a> { 147 type Item = TextRunSlice<'a>; 148 149 // inline(always) due to the inefficient rt failures messing up inline heuristics, I think. 150 #[inline(always)] next(&mut self) -> Option<TextRunSlice<'a>>151 fn next(&mut self) -> Option<TextRunSlice<'a>> { 152 let glyph_run = self.glyph_run?; 153 154 debug_assert!(!self.range.is_empty()); 155 let byte_start = self.range.begin(); 156 let byte_len = match self.text[byte_start.to_usize()..].chars().next() { 157 Some(ch) => ByteIndex(ch.len_utf8() as isize), 158 None => unreachable!() // XXX refactor? 159 }; 160 161 self.range.adjust_by(byte_len, -byte_len); 162 if self.range.is_empty() { 163 // We're done. 164 self.glyph_run = None 165 } else if self.range.intersect(&glyph_run.range).is_empty() { 166 // Move on to the next glyph run. 167 self.glyph_run = self.glyph_run_iter.next(); 168 } 169 170 let index_within_glyph_run = byte_start - glyph_run.range.begin(); 171 Some(TextRunSlice { 172 glyphs: &*glyph_run.glyph_store, 173 offset: glyph_run.range.begin(), 174 range: Range::new(index_within_glyph_run, byte_len), 175 }) 176 } 177 } 178 179 impl<'a> TextRun { 180 /// Constructs a new text run. Also returns if there is a line break at the beginning new(font: &mut Font, text: String, options: &ShapingOptions, bidi_level: bidi::Level, breaker: &mut Option<LineBreakLeafIter>) -> (TextRun, bool)181 pub fn new(font: &mut Font, text: String, options: &ShapingOptions, 182 bidi_level: bidi::Level, breaker: &mut Option<LineBreakLeafIter>) -> (TextRun, bool) { 183 let (glyphs, break_at_zero) = TextRun::break_and_shape(font, &text, options, breaker); 184 (TextRun { 185 text: Arc::new(text), 186 font_metrics: font.metrics.clone(), 187 font_template: font.handle.template(), 188 font_key: font.font_key, 189 actual_pt_size: font.actual_pt_size, 190 glyphs: Arc::new(glyphs), 191 bidi_level: bidi_level, 192 extra_word_spacing: Au(0), 193 }, break_at_zero) 194 } 195 break_and_shape(font: &mut Font, text: &str, options: &ShapingOptions, breaker: &mut Option<LineBreakLeafIter>) -> (Vec<GlyphRun>, bool)196 pub fn break_and_shape(font: &mut Font, text: &str, options: &ShapingOptions, 197 breaker: &mut Option<LineBreakLeafIter>) -> (Vec<GlyphRun>, bool) { 198 let mut glyphs = vec!(); 199 let mut slice = 0..0; 200 201 let mut finished = false; 202 let mut break_at_zero = false; 203 204 if breaker.is_none() { 205 if text.len() == 0 { 206 return (glyphs, true) 207 } 208 *breaker = Some(LineBreakLeafIter::new(&text, 0)); 209 } 210 211 let breaker = breaker.as_mut().unwrap(); 212 213 while !finished { 214 let (idx, _is_hard_break) = breaker.next(text); 215 if idx == text.len() { 216 finished = true; 217 } 218 if idx == 0 { 219 break_at_zero = true; 220 } 221 222 // Extend the slice to the next UAX#14 line break opportunity. 223 slice.end = idx; 224 let word = &text[slice.clone()]; 225 226 // Split off any trailing whitespace into a separate glyph run. 227 let mut whitespace = slice.end..slice.end; 228 if let Some((i, _)) = word.char_indices().rev() 229 .take_while(|&(_, c)| char_is_whitespace(c)).last() { 230 whitespace.start = slice.start + i; 231 slice.end = whitespace.start; 232 } else if idx != text.len() && options.flags.contains(ShapingFlags::KEEP_ALL_FLAG) { 233 // If there's no whitespace and word-break is set to 234 // keep-all, try increasing the slice. 235 continue; 236 } 237 if slice.len() > 0 { 238 glyphs.push(GlyphRun { 239 glyph_store: font.shape_text(&text[slice.clone()], options), 240 range: Range::new(ByteIndex(slice.start as isize), 241 ByteIndex(slice.len() as isize)), 242 }); 243 } 244 if whitespace.len() > 0 { 245 let mut options = options.clone(); 246 options.flags.insert(ShapingFlags::IS_WHITESPACE_SHAPING_FLAG); 247 glyphs.push(GlyphRun { 248 glyph_store: font.shape_text(&text[whitespace.clone()], &options), 249 range: Range::new(ByteIndex(whitespace.start as isize), 250 ByteIndex(whitespace.len() as isize)), 251 }); 252 } 253 slice.start = whitespace.end; 254 } 255 (glyphs, break_at_zero) 256 } 257 ascent(&self) -> Au258 pub fn ascent(&self) -> Au { 259 self.font_metrics.ascent 260 } 261 descent(&self) -> Au262 pub fn descent(&self) -> Au { 263 self.font_metrics.descent 264 } 265 advance_for_range(&self, range: &Range<ByteIndex>) -> Au266 pub fn advance_for_range(&self, range: &Range<ByteIndex>) -> Au { 267 if range.is_empty() { 268 return Au(0) 269 } 270 271 // TODO(Issue #199): alter advance direction for RTL 272 // TODO(Issue #98): using inter-char and inter-word spacing settings when measuring text 273 self.natural_word_slices_in_range(range) 274 .fold(Au(0), |advance, slice| { 275 advance + slice.glyphs.advance_for_byte_range(&slice.range, self.extra_word_spacing) 276 }) 277 } 278 metrics_for_range(&self, range: &Range<ByteIndex>) -> RunMetrics279 pub fn metrics_for_range(&self, range: &Range<ByteIndex>) -> RunMetrics { 280 RunMetrics::new(self.advance_for_range(range), 281 self.font_metrics.ascent, 282 self.font_metrics.descent) 283 } 284 metrics_for_slice(&self, glyphs: &GlyphStore, slice_range: &Range<ByteIndex>) -> RunMetrics285 pub fn metrics_for_slice(&self, glyphs: &GlyphStore, slice_range: &Range<ByteIndex>) 286 -> RunMetrics { 287 RunMetrics::new(glyphs.advance_for_byte_range(slice_range, self.extra_word_spacing), 288 self.font_metrics.ascent, 289 self.font_metrics.descent) 290 } 291 min_width_for_range(&self, range: &Range<ByteIndex>) -> Au292 pub fn min_width_for_range(&self, range: &Range<ByteIndex>) -> Au { 293 debug!("iterating outer range {:?}", range); 294 self.natural_word_slices_in_range(range).fold(Au(0), |max_piece_width, slice| { 295 debug!("iterated on {:?}[{:?}]", slice.offset, slice.range); 296 max(max_piece_width, self.advance_for_range(&slice.range)) 297 }) 298 } 299 minimum_splittable_inline_size(&self, range: &Range<ByteIndex>) -> Au300 pub fn minimum_splittable_inline_size(&self, range: &Range<ByteIndex>) -> Au { 301 match self.natural_word_slices_in_range(range).next() { 302 None => Au(0), 303 Some(slice) => self.advance_for_range(&slice.range), 304 } 305 } 306 307 /// Returns the index of the first glyph run containing the given character index. index_of_first_glyph_run_containing(&self, index: ByteIndex) -> Option<usize>308 fn index_of_first_glyph_run_containing(&self, index: ByteIndex) -> Option<usize> { 309 let self_ptr = self as *const TextRun; 310 INDEX_OF_FIRST_GLYPH_RUN_CACHE.with(|index_of_first_glyph_run_cache| { 311 if let Some((last_text_run, last_index, last_result)) = 312 index_of_first_glyph_run_cache.get() { 313 if last_text_run == self_ptr && last_index == index { 314 return Some(last_result) 315 } 316 } 317 318 if let Ok(result) = (&**self.glyphs).binary_search_by(|current| current.compare(&index)) { 319 index_of_first_glyph_run_cache.set(Some((self_ptr, index, result))); 320 Some(result) 321 } else { 322 None 323 } 324 }) 325 } 326 on_glyph_run_boundary(&self, index: ByteIndex) -> bool327 pub fn on_glyph_run_boundary(&self, index: ByteIndex) -> bool { 328 if let Some(glyph_index) = self.index_of_first_glyph_run_containing(index) { 329 self.glyphs[glyph_index].range.begin() == index 330 } else { 331 true 332 } 333 } 334 335 /// Returns the index in the range of the first glyph advancing over given advance range_index_of_advance(&self, range: &Range<ByteIndex>, advance: Au) -> usize336 pub fn range_index_of_advance(&self, range: &Range<ByteIndex>, advance: Au) -> usize { 337 // TODO(Issue #199): alter advance direction for RTL 338 // TODO(Issue #98): using inter-char and inter-word spacing settings when measuring text 339 let mut remaining = advance; 340 self.natural_word_slices_in_range(range) 341 .map(|slice| { 342 let (slice_index, slice_advance) = 343 slice.glyphs.range_index_of_advance(&slice.range, remaining, self.extra_word_spacing); 344 remaining -= slice_advance; 345 slice_index 346 }) 347 .sum() 348 } 349 350 /// Returns an iterator that will iterate over all slices of glyphs that represent natural 351 /// words in the given range. natural_word_slices_in_range(&'a self, range: &Range<ByteIndex>) -> NaturalWordSliceIterator<'a>352 pub fn natural_word_slices_in_range(&'a self, range: &Range<ByteIndex>) 353 -> NaturalWordSliceIterator<'a> { 354 let index = match self.index_of_first_glyph_run_containing(range.begin()) { 355 None => self.glyphs.len(), 356 Some(index) => index, 357 }; 358 NaturalWordSliceIterator { 359 glyphs: &self.glyphs[..], 360 index: index, 361 range: *range, 362 reverse: false, 363 } 364 } 365 366 /// Returns an iterator that over natural word slices in visual order (left to right or 367 /// right to left, depending on the bidirectional embedding level). natural_word_slices_in_visual_order(&'a self, range: &Range<ByteIndex>) -> NaturalWordSliceIterator<'a>368 pub fn natural_word_slices_in_visual_order(&'a self, range: &Range<ByteIndex>) 369 -> NaturalWordSliceIterator<'a> { 370 // Iterate in reverse order if bidi level is RTL. 371 let reverse = self.bidi_level.is_rtl(); 372 373 let index = if reverse { 374 match self.index_of_first_glyph_run_containing(range.end() - ByteIndex(1)) { 375 Some(i) => i + 1, // In reverse mode, index points one past the next element. 376 None => 0 377 } 378 } else { 379 match self.index_of_first_glyph_run_containing(range.begin()) { 380 Some(i) => i, 381 None => self.glyphs.len() 382 } 383 }; 384 NaturalWordSliceIterator { 385 glyphs: &self.glyphs[..], 386 index: index, 387 range: *range, 388 reverse: reverse, 389 } 390 } 391 392 /// Returns an iterator that will iterate over all slices of glyphs that represent individual 393 /// characters in the given range. character_slices_in_range(&'a self, range: &Range<ByteIndex>) -> CharacterSliceIterator<'a>394 pub fn character_slices_in_range(&'a self, range: &Range<ByteIndex>) 395 -> CharacterSliceIterator<'a> { 396 let index = match self.index_of_first_glyph_run_containing(range.begin()) { 397 None => self.glyphs.len(), 398 Some(index) => index, 399 }; 400 let mut glyph_run_iter = self.glyphs[index..].iter(); 401 let first_glyph_run = glyph_run_iter.next(); 402 CharacterSliceIterator { 403 text: &self.text, 404 glyph_run: first_glyph_run, 405 glyph_run_iter: glyph_run_iter, 406 range: *range, 407 } 408 } 409 } 410