mirror of
https://github.com/servo/servo.git
synced 2025-08-06 06:00:15 +01:00
auto merge of #1507 : deokjinkim/servo/white_space, r=metajack
In order to support line-break by new-line character, Fist, calculate new-line character's position.(fn flush_clump_to_list) Second, split box(which includes new-line character) and do line-break.(fn scan_for_lines)
This commit is contained in:
commit
26fc108924
5 changed files with 147 additions and 26 deletions
|
@ -20,10 +20,11 @@ enum CompressionMode {
|
|||
// * Issue #114: record skipped and kept chars for mapping original to new text
|
||||
//
|
||||
// * Untracked: various edge cases for bidi, CJK, etc.
|
||||
pub fn transform_text(text: &str, mode: CompressionMode, incoming_whitespace: bool) -> (~str, bool) {
|
||||
pub fn transform_text(text: &str, mode: CompressionMode, incoming_whitespace: bool, new_line_pos: &mut ~[uint]) -> (~str, bool) {
|
||||
let mut out_str: ~str = ~"";
|
||||
let out_whitespace = match mode {
|
||||
CompressNone | DiscardNewline => {
|
||||
let mut new_line_index = 0;
|
||||
for ch in text.chars() {
|
||||
if is_discardable_char(ch, mode) {
|
||||
// TODO: record skipped char
|
||||
|
@ -31,8 +32,17 @@ pub fn transform_text(text: &str, mode: CompressionMode, incoming_whitespace: bo
|
|||
// TODO: record kept char
|
||||
if ch == '\t' {
|
||||
// TODO: set "has tab" flag
|
||||
} else if ch == '\n' {
|
||||
// Save new-line's position for line-break
|
||||
// This value is relative(not absolute)
|
||||
new_line_pos.push(new_line_index);
|
||||
new_line_index = 0;
|
||||
}
|
||||
out_str.push_char(ch);
|
||||
|
||||
if ch != '\n' {
|
||||
new_line_index += 1;
|
||||
}
|
||||
out_str.push_char(ch);
|
||||
}
|
||||
}
|
||||
text.len() > 0 && is_in_whitespace(text.char_at_reverse(0), mode)
|
||||
|
@ -139,7 +149,8 @@ fn test_transform_compress_none() {
|
|||
let mode = CompressNone;
|
||||
|
||||
for i in range(0, test_strs.len()) {
|
||||
let (trimmed_str, _out) = transform_text(test_strs[i], mode, true);
|
||||
let mut new_line_pos = ~[];
|
||||
let (trimmed_str, _out) = transform_text(test_strs[i], mode, true, &mut new_line_pos);
|
||||
assert_eq!(&trimmed_str, &test_strs[i])
|
||||
}
|
||||
}
|
||||
|
@ -167,7 +178,8 @@ fn test_transform_discard_newline() {
|
|||
let mode = DiscardNewline;
|
||||
|
||||
for i in range(0, test_strs.len()) {
|
||||
let (trimmed_str, _out) = transform_text(test_strs[i], mode, true);
|
||||
let mut new_line_pos = ~[];
|
||||
let (trimmed_str, _out) = transform_text(test_strs[i], mode, true, &mut new_line_pos);
|
||||
assert_eq!(&trimmed_str, &oracle_strs[i])
|
||||
}
|
||||
}
|
||||
|
@ -195,7 +207,8 @@ fn test_transform_compress_whitespace() {
|
|||
let mode = CompressWhitespace;
|
||||
|
||||
for i in range(0, test_strs.len()) {
|
||||
let (trimmed_str, _out) = transform_text(test_strs[i], mode, true);
|
||||
let mut new_line_pos = ~[];
|
||||
let (trimmed_str, _out) = transform_text(test_strs[i], mode, true, &mut new_line_pos);
|
||||
assert_eq!(&trimmed_str, &oracle_strs[i])
|
||||
}
|
||||
}
|
||||
|
@ -222,7 +235,8 @@ fn test_transform_compress_whitespace_newline() {
|
|||
let mode = CompressWhitespaceNewline;
|
||||
|
||||
for i in range(0, test_strs.len()) {
|
||||
let (trimmed_str, _out) = transform_text(test_strs[i], mode, true);
|
||||
let mut new_line_pos = ~[];
|
||||
let (trimmed_str, _out) = transform_text(test_strs[i], mode, true, &mut new_line_pos);
|
||||
assert_eq!(&trimmed_str, &oracle_strs[i])
|
||||
}
|
||||
}
|
||||
|
@ -252,7 +266,8 @@ fn test_transform_compress_whitespace_newline_no_incoming() {
|
|||
let mode = CompressWhitespaceNewline;
|
||||
|
||||
for i in range(0, test_strs.len()) {
|
||||
let (trimmed_str, _out) = transform_text(test_strs[i], mode, false);
|
||||
let mut new_line_pos = ~[];
|
||||
let (trimmed_str, _out) = transform_text(test_strs[i], mode, false, &mut new_line_pos);
|
||||
assert_eq!(&trimmed_str, &oracle_strs[i])
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ use std::num::Zero;
|
|||
use style::{ComputedValues, TElement, TNode, cascade};
|
||||
use style::computed_values::{LengthOrPercentage, LengthOrPercentageOrAuto, overflow, LPA_Auto};
|
||||
use style::computed_values::{border_style, clear, font_family, line_height};
|
||||
use style::computed_values::{text_align, text_decoration, vertical_align, visibility};
|
||||
use style::computed_values::{text_align, text_decoration, vertical_align, visibility, white_space};
|
||||
|
||||
use css::node_style::StyledNode;
|
||||
use layout::context::LayoutContext;
|
||||
|
@ -91,6 +91,9 @@ pub struct Box {
|
|||
|
||||
/// Inline data
|
||||
inline_info: RefCell<Option<InlineInfo>>,
|
||||
|
||||
/// New-line chracter(\n)'s positions(relative, not absolute)
|
||||
new_line_pos: ~[uint],
|
||||
}
|
||||
|
||||
/// Info specific to the kind of box. Keep this enum small.
|
||||
|
@ -330,6 +333,7 @@ impl Box {
|
|||
specific: specific,
|
||||
position_offsets: RefCell::new(Zero::zero()),
|
||||
inline_info: RefCell::new(None),
|
||||
new_line_pos: ~[],
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -419,6 +423,7 @@ impl Box {
|
|||
specific: specific,
|
||||
position_offsets: RefCell::new(Zero::zero()),
|
||||
inline_info: self.inline_info.clone(),
|
||||
new_line_pos: self.new_line_pos.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -578,6 +583,10 @@ impl Box {
|
|||
self.style().Box.vertical_align
|
||||
}
|
||||
|
||||
pub fn white_space(&self) -> white_space::T {
|
||||
self.style().Text.white_space
|
||||
}
|
||||
|
||||
/// Returns the text decoration of this box, according to the style of the nearest ancestor
|
||||
/// element.
|
||||
///
|
||||
|
@ -1023,6 +1032,45 @@ impl Box {
|
|||
}
|
||||
}
|
||||
|
||||
/// Split box which includes new-line character
|
||||
pub fn split_by_new_line(&self) -> SplitBoxResult {
|
||||
match self.specific {
|
||||
GenericBox | IframeBox(_) | ImageBox(_) => CannotSplit,
|
||||
UnscannedTextBox(_) => fail!("Unscanned text boxes should have been scanned by now!"),
|
||||
ScannedTextBox(ref text_box_info) => {
|
||||
let mut new_line_pos = self.new_line_pos.clone();
|
||||
let cur_new_line_pos = new_line_pos.shift();
|
||||
|
||||
let left_range = Range::new(text_box_info.range.begin(), cur_new_line_pos);
|
||||
let right_range = Range::new(text_box_info.range.begin() + cur_new_line_pos + 1, text_box_info.range.length() - (cur_new_line_pos + 1));
|
||||
|
||||
// Left box is for left text of first founded new-line character.
|
||||
let left_box = if left_range.length() > 0 {
|
||||
let new_text_box_info = ScannedTextBoxInfo::new(text_box_info.run.clone(), left_range);
|
||||
let new_metrics = new_text_box_info.run.get().metrics_for_range(&left_range);
|
||||
let mut new_box = self.transform(new_metrics.bounding_box.size, ScannedTextBox(new_text_box_info));
|
||||
new_box.new_line_pos = ~[];
|
||||
Some(new_box)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// Right box is for right text of first founded new-line character.
|
||||
let right_box = if right_range.length() > 0 {
|
||||
let new_text_box_info = ScannedTextBoxInfo::new(text_box_info.run.clone(), right_range);
|
||||
let new_metrics = new_text_box_info.run.get().metrics_for_range(&right_range);
|
||||
let mut new_box = self.transform(new_metrics.bounding_box.size, ScannedTextBox(new_text_box_info));
|
||||
new_box.new_line_pos = new_line_pos;
|
||||
Some(new_box)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
SplitDidFit(left_box, right_box)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempts to split this box so that its width is no more than `max_width`.
|
||||
pub fn split_to_width(&self, max_width: Au, starts_line: bool) -> SplitBoxResult {
|
||||
match self.specific {
|
||||
|
|
|
@ -22,7 +22,7 @@ use servo_util::range::Range;
|
|||
use std::cell::RefCell;
|
||||
use std::u16;
|
||||
use std::util;
|
||||
use style::computed_values::{text_align, vertical_align};
|
||||
use style::computed_values::{text_align, vertical_align, white_space};
|
||||
|
||||
/// Lineboxes are represented as offsets into the child list, rather than
|
||||
/// as an object that "owns" boxes. Choosing a different set of line
|
||||
|
@ -116,7 +116,11 @@ impl LineboxScanner {
|
|||
box_
|
||||
};
|
||||
|
||||
let box_was_appended = self.try_append_to_line(cur_box, flow);
|
||||
let box_was_appended = match cur_box.white_space() {
|
||||
white_space::normal => self.try_append_to_line(cur_box, flow),
|
||||
white_space::pre => self.try_append_to_line_by_new_line(cur_box),
|
||||
};
|
||||
|
||||
if !box_was_appended {
|
||||
debug!("LineboxScanner: Box wasn't appended, because line {:u} was full.",
|
||||
self.lines.len());
|
||||
|
@ -306,6 +310,35 @@ impl LineboxScanner {
|
|||
false
|
||||
}
|
||||
|
||||
fn try_append_to_line_by_new_line(&mut self, in_box: Box) -> bool {
|
||||
if in_box.new_line_pos.len() == 0 {
|
||||
// In case of box does not include new-line character
|
||||
self.push_box_to_line(in_box);
|
||||
true
|
||||
} else {
|
||||
// In case of box includes new-line character
|
||||
match in_box.split_by_new_line() {
|
||||
SplitDidFit(left, right) => {
|
||||
match (left, right) {
|
||||
(Some(left_box), Some(right_box)) => {
|
||||
self.push_box_to_line(left_box);
|
||||
self.work_list.push_front(right_box);
|
||||
}
|
||||
(Some(left_box), None) => {
|
||||
self.push_box_to_line(left_box);
|
||||
}
|
||||
(None, Some(right_box)) => {
|
||||
self.work_list.push_front(right_box);
|
||||
}
|
||||
(None, None) => error!("LineboxScanner: This split case makes no sense!"),
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// Tries to append the given box to the line, splitting it if necessary. Returns false only if
|
||||
/// we should break the line.
|
||||
fn try_append_to_line(&mut self, in_box: Box, flow: &mut InlineFlow) -> bool {
|
||||
|
|
|
@ -10,9 +10,10 @@ use layout::flow::Flow;
|
|||
|
||||
use extra::arc::Arc;
|
||||
use gfx::text::text_run::TextRun;
|
||||
use gfx::text::util::{CompressWhitespaceNewline, transform_text};
|
||||
use gfx::text::util::{CompressWhitespaceNewline, transform_text, CompressNone};
|
||||
use servo_util::range::Range;
|
||||
use std::vec;
|
||||
use style::computed_values::white_space;
|
||||
|
||||
/// A stack-allocated object for scanning an inline flow into `TextRun`-containing `TextBox`es.
|
||||
pub struct TextRunScanner {
|
||||
|
@ -111,11 +112,18 @@ impl TextRunScanner {
|
|||
let decoration = old_box.text_decoration();
|
||||
|
||||
// TODO(#115): Use the actual CSS `white-space` property of the relevant style.
|
||||
let compression = CompressWhitespaceNewline;
|
||||
let compression = match old_box.white_space() {
|
||||
white_space::normal => CompressWhitespaceNewline,
|
||||
white_space::pre => CompressNone,
|
||||
};
|
||||
|
||||
let mut new_line_pos = ~[];
|
||||
|
||||
let (transformed_text, whitespace) = transform_text(*text,
|
||||
compression,
|
||||
last_whitespace);
|
||||
last_whitespace,
|
||||
&mut new_line_pos);
|
||||
|
||||
new_whitespace = whitespace;
|
||||
|
||||
if transformed_text.len() > 0 {
|
||||
|
@ -131,14 +139,32 @@ impl TextRunScanner {
|
|||
let range = Range::new(0, run.char_len());
|
||||
let new_metrics = run.metrics_for_range(&range);
|
||||
let new_text_box_info = ScannedTextBoxInfo::new(Arc::new(run), range);
|
||||
let new_box = old_box.transform(new_metrics.bounding_box.size,
|
||||
let mut new_box = old_box.transform(new_metrics.bounding_box.size,
|
||||
ScannedTextBox(new_text_box_info));
|
||||
new_box.new_line_pos = new_line_pos;
|
||||
out_boxes.push(new_box)
|
||||
}
|
||||
},
|
||||
(false, true) => {
|
||||
// TODO(#177): Text run creation must account for the renderability of text by
|
||||
// font group fonts. This is probably achieved by creating the font group above
|
||||
// and then letting `FontGroup` decide which `Font` to stick into the text run.
|
||||
let in_box = &in_boxes[self.clump.begin()];
|
||||
let font_style = in_box.font_style();
|
||||
let fontgroup = ctx.font_ctx.get_resolved_font_for_style(&font_style);
|
||||
let decoration = in_box.text_decoration();
|
||||
|
||||
// TODO(#115): Use the actual CSS `white-space` property of the relevant style.
|
||||
let compression = CompressWhitespaceNewline;
|
||||
let compression = match in_box.white_space() {
|
||||
white_space::normal => CompressWhitespaceNewline,
|
||||
white_space::pre => CompressNone,
|
||||
};
|
||||
|
||||
struct NewLinePositions {
|
||||
new_line_pos: ~[uint],
|
||||
}
|
||||
|
||||
let mut new_line_positions: ~[NewLinePositions] = ~[];
|
||||
|
||||
// First, transform/compress text of all the nodes.
|
||||
let mut last_whitespace_in_clump = new_whitespace;
|
||||
|
@ -152,9 +178,14 @@ impl TextRunScanner {
|
|||
_ => fail!("Expected an unscanned text box!"),
|
||||
};
|
||||
|
||||
let mut new_line_pos = ~[];
|
||||
|
||||
let (new_str, new_whitespace) = transform_text(*in_box,
|
||||
compression,
|
||||
last_whitespace_in_clump);
|
||||
last_whitespace_in_clump,
|
||||
&mut new_line_pos);
|
||||
new_line_positions.push(NewLinePositions { new_line_pos: new_line_pos });
|
||||
|
||||
last_whitespace_in_clump = new_whitespace;
|
||||
new_str
|
||||
});
|
||||
|
@ -173,15 +204,6 @@ impl TextRunScanner {
|
|||
}
|
||||
|
||||
// Now create the run.
|
||||
//
|
||||
// TODO(#177): Text run creation must account for the renderability of text by
|
||||
// font group fonts. This is probably achieved by creating the font group above
|
||||
// and then letting `FontGroup` decide which `Font` to stick into the text run.
|
||||
let in_box = &in_boxes[self.clump.begin()];
|
||||
let font_style = in_box.font_style();
|
||||
let fontgroup = ctx.font_ctx.get_resolved_font_for_style(&font_style);
|
||||
let decoration = in_box.text_decoration();
|
||||
|
||||
// TextRuns contain a cycle which is usually resolved by the teardown
|
||||
// sequence. If no clump takes ownership, however, it will leak.
|
||||
let clump = self.clump;
|
||||
|
@ -208,8 +230,9 @@ impl TextRunScanner {
|
|||
|
||||
let new_text_box_info = ScannedTextBoxInfo::new(run.get_ref().clone(), range);
|
||||
let new_metrics = new_text_box_info.run.get().metrics_for_range(&range);
|
||||
let new_box = in_boxes[i].transform(new_metrics.bounding_box.size,
|
||||
let mut new_box = in_boxes[i].transform(new_metrics.bounding_box.size,
|
||||
ScannedTextBox(new_text_box_info));
|
||||
new_box.new_line_pos = new_line_positions[i].new_line_pos.clone();
|
||||
out_boxes.push(new_box)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -709,6 +709,8 @@ pub mod longhands {
|
|||
}
|
||||
</%self:longhand>
|
||||
|
||||
${single_keyword("white-space", "normal pre", inherited=True)}
|
||||
|
||||
// CSS 2.1, Section 17 - Tables
|
||||
|
||||
// CSS 2.1, Section 18 - User interface
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue