Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement white-space property(pre) #1507

Merged
merged 1 commit into from Jan 23, 2014
Merged
Changes from all commits
Commits
File filter...
Filter file types
Jump to…
Jump to file
Failed to load files.

Always

Just for now

@@ -20,19 +20,29 @@ 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
} else {
// 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
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.