mirror of
https://github.com/servo/servo.git
synced 2025-08-05 21:50:18 +01:00
Implement sideways text.
This commit is contained in:
parent
db80b76d0a
commit
80704aeabd
6 changed files with 126 additions and 37 deletions
|
@ -21,7 +21,7 @@ use text::TextRun;
|
||||||
|
|
||||||
use collections::dlist::DList;
|
use collections::dlist::DList;
|
||||||
use collections::dlist;
|
use collections::dlist;
|
||||||
use geom::{Point2D, Rect, SideOffsets2D, Size2D};
|
use geom::{Point2D, Rect, SideOffsets2D, Size2D, Matrix2D};
|
||||||
use libc::uintptr_t;
|
use libc::uintptr_t;
|
||||||
use servo_net::image::base::Image;
|
use servo_net::image::base::Image;
|
||||||
use servo_util::geometry::Au;
|
use servo_util::geometry::Au;
|
||||||
|
@ -337,10 +337,11 @@ impl DisplayList {
|
||||||
|
|
||||||
/// Draws the display list into the given render context. The display list must be flattened
|
/// Draws the display list into the given render context. The display list must be flattened
|
||||||
/// first for correct painting.
|
/// first for correct painting.
|
||||||
pub fn draw_into_context(&self, render_context: &mut RenderContext) {
|
pub fn draw_into_context(&self, render_context: &mut RenderContext,
|
||||||
|
current_transform: &Matrix2D<AzFloat>) {
|
||||||
debug!("Beginning display list.");
|
debug!("Beginning display list.");
|
||||||
for item in self.list.iter() {
|
for item in self.list.iter() {
|
||||||
item.draw_into_context(render_context)
|
item.draw_into_context(render_context, current_transform)
|
||||||
}
|
}
|
||||||
debug!("Ending display list.");
|
debug!("Ending display list.");
|
||||||
}
|
}
|
||||||
|
@ -483,6 +484,16 @@ pub struct TextDisplayItem {
|
||||||
|
|
||||||
/// The color of the text.
|
/// The color of the text.
|
||||||
pub text_color: Color,
|
pub text_color: Color,
|
||||||
|
|
||||||
|
pub baseline_origin: Point2D<Au>,
|
||||||
|
pub orientation: TextOrientation,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[deriving(Clone, Eq, PartialEq)]
|
||||||
|
pub enum TextOrientation {
|
||||||
|
Upright,
|
||||||
|
SidewaysLeft,
|
||||||
|
SidewaysRight,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Renders an image.
|
/// Renders an image.
|
||||||
|
@ -560,7 +571,8 @@ impl<'a> Iterator<&'a DisplayItem> for DisplayItemIterator<'a> {
|
||||||
|
|
||||||
impl DisplayItem {
|
impl DisplayItem {
|
||||||
/// Renders this display item into the given render context.
|
/// Renders this display item into the given render context.
|
||||||
fn draw_into_context(&self, render_context: &mut RenderContext) {
|
fn draw_into_context(&self, render_context: &mut RenderContext,
|
||||||
|
current_transform: &Matrix2D<AzFloat>) {
|
||||||
// This should have been flattened to the content stacking level first.
|
// This should have been flattened to the content stacking level first.
|
||||||
assert!(self.base().level == ContentStackingLevel);
|
assert!(self.base().level == ContentStackingLevel);
|
||||||
|
|
||||||
|
@ -572,31 +584,61 @@ impl DisplayItem {
|
||||||
ClipDisplayItemClass(ref clip) => {
|
ClipDisplayItemClass(ref clip) => {
|
||||||
render_context.draw_push_clip(&clip.base.bounds);
|
render_context.draw_push_clip(&clip.base.bounds);
|
||||||
for item in clip.children.iter() {
|
for item in clip.children.iter() {
|
||||||
(*item).draw_into_context(render_context);
|
(*item).draw_into_context(render_context, current_transform);
|
||||||
}
|
}
|
||||||
render_context.draw_pop_clip();
|
render_context.draw_pop_clip();
|
||||||
}
|
}
|
||||||
|
|
||||||
TextDisplayItemClass(ref text) => {
|
TextDisplayItemClass(ref text) => {
|
||||||
debug!("Drawing text at {:?}.", text.base.bounds);
|
debug!("Drawing text at {}.", text.base.bounds);
|
||||||
|
|
||||||
// FIXME(pcwalton): Allocating? Why?
|
// Optimization: Don’t set a transform matrix for upright text,
|
||||||
let text_run = text.text_run.clone();
|
// and pass a strart point to `draw_text_into_context`.
|
||||||
|
// For sideways text, it’s easier to do the rotation such that its center
|
||||||
|
// (the baseline’s start point) is at (0, 0) coordinates.
|
||||||
|
let baseline_origin = match text.orientation {
|
||||||
|
Upright => text.baseline_origin,
|
||||||
|
SidewaysLeft => {
|
||||||
|
let x = text.baseline_origin.x.to_nearest_px() as AzFloat;
|
||||||
|
let y = text.baseline_origin.y.to_nearest_px() as AzFloat;
|
||||||
|
render_context.draw_target.set_transform(¤t_transform.mul(
|
||||||
|
&Matrix2D::new(
|
||||||
|
0., -1.,
|
||||||
|
1., 0.,
|
||||||
|
x, y
|
||||||
|
)
|
||||||
|
));
|
||||||
|
Zero::zero()
|
||||||
|
},
|
||||||
|
SidewaysRight => {
|
||||||
|
let x = text.baseline_origin.x.to_nearest_px() as AzFloat;
|
||||||
|
let y = text.baseline_origin.y.to_nearest_px() as AzFloat;
|
||||||
|
render_context.draw_target.set_transform(¤t_transform.mul(
|
||||||
|
&Matrix2D::new(
|
||||||
|
0., 1.,
|
||||||
|
-1., 0.,
|
||||||
|
x, y
|
||||||
|
)
|
||||||
|
));
|
||||||
|
Zero::zero()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let font = render_context.font_ctx.get_render_font_from_template(
|
render_context.font_ctx.get_render_font_from_template(
|
||||||
&text_run.font_template,
|
&text.text_run.font_template,
|
||||||
text_run.pt_size,
|
text.text_run.pt_size,
|
||||||
render_context.opts.render_backend);
|
render_context.opts.render_backend
|
||||||
let font = font.borrow();
|
).borrow().draw_text_into_context(
|
||||||
|
render_context,
|
||||||
|
&*text.text_run,
|
||||||
|
&text.range,
|
||||||
|
baseline_origin,
|
||||||
|
text.text_color
|
||||||
|
);
|
||||||
|
|
||||||
let origin = text.base.bounds.origin;
|
// Undo the transform, only when we did one.
|
||||||
let baseline_origin = Point2D(origin.x, origin.y + text_run.font_metrics.ascent);
|
if text.orientation != Upright {
|
||||||
{
|
render_context.draw_target.set_transform(current_transform)
|
||||||
font.draw_text_into_context(render_context,
|
|
||||||
&*text.text_run,
|
|
||||||
&text.range,
|
|
||||||
baseline_origin,
|
|
||||||
text.text_color);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -352,7 +352,7 @@ impl<C:RenderListener + Send> RenderTask<C> {
|
||||||
|
|
||||||
// Draw the display list.
|
// Draw the display list.
|
||||||
profile(time::RenderingDrawingCategory, self.time_profiler_chan.clone(), || {
|
profile(time::RenderingDrawingCategory, self.time_profiler_chan.clone(), || {
|
||||||
display_list.draw_into_context(&mut ctx);
|
display_list.draw_into_context(&mut ctx, &matrix);
|
||||||
ctx.draw_target.flush();
|
ctx.draw_target.flush();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,7 @@ use gfx::display_list::{ImageDisplayItemClass, LineDisplayItem};
|
||||||
use gfx::display_list::{LineDisplayItemClass, OpaqueNode, PseudoDisplayItemClass};
|
use gfx::display_list::{LineDisplayItemClass, OpaqueNode, PseudoDisplayItemClass};
|
||||||
use gfx::display_list::{SolidColorDisplayItem, SolidColorDisplayItemClass, StackingLevel};
|
use gfx::display_list::{SolidColorDisplayItem, SolidColorDisplayItemClass, StackingLevel};
|
||||||
use gfx::display_list::{TextDisplayItem, TextDisplayItemClass};
|
use gfx::display_list::{TextDisplayItem, TextDisplayItemClass};
|
||||||
|
use gfx::display_list::{Upright, SidewaysLeft, SidewaysRight};
|
||||||
use gfx::font::FontStyle;
|
use gfx::font::FontStyle;
|
||||||
use gfx::text::glyph::CharIndex;
|
use gfx::text::glyph::CharIndex;
|
||||||
use gfx::text::text_run::TextRun;
|
use gfx::text::text_run::TextRun;
|
||||||
|
@ -935,12 +936,31 @@ impl Fragment {
|
||||||
TableColumnFragment(_) => fail!("Shouldn't see table column fragments here."),
|
TableColumnFragment(_) => fail!("Shouldn't see table column fragments here."),
|
||||||
ScannedTextFragment(ref text_fragment) => {
|
ScannedTextFragment(ref text_fragment) => {
|
||||||
// Create the text display item.
|
// Create the text display item.
|
||||||
|
let orientation = if self.style.writing_mode.is_vertical() {
|
||||||
|
if self.style.writing_mode.is_sideways_left() {
|
||||||
|
SidewaysLeft
|
||||||
|
} else {
|
||||||
|
SidewaysRight
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Upright
|
||||||
|
};
|
||||||
|
|
||||||
|
let metrics = &text_fragment.run.font_metrics;
|
||||||
|
let baseline_origin ={
|
||||||
|
let mut tmp = content_box.start;
|
||||||
|
tmp.b = tmp.b + metrics.ascent;
|
||||||
|
tmp.to_physical(self.style.writing_mode, container_size) + flow_origin
|
||||||
|
};
|
||||||
|
|
||||||
let text_display_item = box TextDisplayItem {
|
let text_display_item = box TextDisplayItem {
|
||||||
base: BaseDisplayItem::new(
|
base: BaseDisplayItem::new(
|
||||||
absolute_content_box, self.node, ContentStackingLevel),
|
absolute_content_box, self.node, ContentStackingLevel),
|
||||||
text_run: text_fragment.run.clone(),
|
text_run: text_fragment.run.clone(),
|
||||||
range: text_fragment.range,
|
range: text_fragment.range,
|
||||||
text_color: self.style().get_color().color.to_gfx_color(),
|
text_color: self.style().get_color().color.to_gfx_color(),
|
||||||
|
orientation: orientation,
|
||||||
|
baseline_origin: baseline_origin,
|
||||||
};
|
};
|
||||||
accumulator.push(display_list, TextDisplayItemClass(text_display_item));
|
accumulator.push(display_list, TextDisplayItemClass(text_display_item));
|
||||||
|
|
||||||
|
@ -965,7 +985,6 @@ impl Fragment {
|
||||||
|
|
||||||
let text_decorations =
|
let text_decorations =
|
||||||
self.style().get_inheritedtext()._servo_text_decorations_in_effect;
|
self.style().get_inheritedtext()._servo_text_decorations_in_effect;
|
||||||
let metrics = &text_fragment.run.font_metrics;
|
|
||||||
line(text_decorations.underline, || {
|
line(text_decorations.underline, || {
|
||||||
let mut rect = content_box.clone();
|
let mut rect = content_box.clone();
|
||||||
rect.start.b = rect.start.b + metrics.ascent - metrics.underline_offset;
|
rect.start.b = rect.start.b + metrics.ascent - metrics.underline_offset;
|
||||||
|
|
|
@ -1100,13 +1100,10 @@ impl Flow for InlineFlow {
|
||||||
line_distance_from_flow_block_start = line_distance_from_flow_block_start + line.bounds.size.block;
|
line_distance_from_flow_block_start = line_distance_from_flow_block_start + line.bounds.size.block;
|
||||||
} // End of `lines.each` loop.
|
} // End of `lines.each` loop.
|
||||||
|
|
||||||
self.base.position.size.block =
|
self.base.position.size.block = match self.lines.as_slice().last() {
|
||||||
if self.lines.len() > 0 {
|
Some(ref last_line) => last_line.bounds.start.b + last_line.bounds.size.block,
|
||||||
self.lines.as_slice().last().get_ref().bounds.start.b +
|
None => Au::new(0)
|
||||||
self.lines.as_slice().last().get_ref().bounds.size.block
|
};
|
||||||
} else {
|
|
||||||
Au::new(0)
|
|
||||||
};
|
|
||||||
|
|
||||||
self.base.floats = scanner.floats();
|
self.base.floats = scanner.floats();
|
||||||
self.base.floats.translate(LogicalSize::new(
|
self.base.floats.translate(LogicalSize::new(
|
||||||
|
|
|
@ -9,16 +9,16 @@
|
||||||
use flow::Flow;
|
use flow::Flow;
|
||||||
use fragment::{Fragment, ScannedTextFragment, ScannedTextFragmentInfo, UnscannedTextFragment};
|
use fragment::{Fragment, ScannedTextFragment, ScannedTextFragmentInfo, UnscannedTextFragment};
|
||||||
|
|
||||||
use gfx::font::{FontMetrics, FontStyle};
|
use gfx::font::{FontMetrics, FontStyle, RunMetrics};
|
||||||
use gfx::font_context::FontContext;
|
use gfx::font_context::FontContext;
|
||||||
use gfx::text::glyph::CharIndex;
|
use gfx::text::glyph::CharIndex;
|
||||||
use gfx::text::text_run::TextRun;
|
use gfx::text::text_run::TextRun;
|
||||||
use gfx::text::util::{CompressWhitespaceNewline, transform_text, CompressNone};
|
use gfx::text::util::{CompressWhitespaceNewline, transform_text, CompressNone};
|
||||||
use servo_util::geometry::Au;
|
use servo_util::geometry::Au;
|
||||||
use servo_util::logical_geometry::LogicalSize;
|
use servo_util::logical_geometry::{LogicalSize, WritingMode};
|
||||||
use servo_util::range::Range;
|
use servo_util::range::Range;
|
||||||
use style::ComputedValues;
|
use style::ComputedValues;
|
||||||
use style::computed_values::{font_family, line_height, white_space};
|
use style::computed_values::{font_family, line_height, text_orientation, white_space};
|
||||||
use sync::Arc;
|
use sync::Arc;
|
||||||
|
|
||||||
struct NewLinePositions {
|
struct NewLinePositions {
|
||||||
|
@ -150,8 +150,8 @@ impl TextRunScanner {
|
||||||
*text);
|
*text);
|
||||||
let range = Range::new(CharIndex(0), run.char_len());
|
let range = Range::new(CharIndex(0), run.char_len());
|
||||||
let new_metrics = run.metrics_for_range(&range);
|
let new_metrics = run.metrics_for_range(&range);
|
||||||
let bounding_box_size = LogicalSize::from_physical(
|
let bounding_box_size = bounding_box_for_run_metrics(
|
||||||
old_fragment.style.writing_mode, new_metrics.bounding_box.size);
|
&new_metrics, old_fragment.style.writing_mode);
|
||||||
let new_text_fragment_info = ScannedTextFragmentInfo::new(Arc::new(run), range);
|
let new_text_fragment_info = ScannedTextFragmentInfo::new(Arc::new(run), range);
|
||||||
let mut new_fragment = old_fragment.transform(
|
let mut new_fragment = old_fragment.transform(
|
||||||
bounding_box_size, ScannedTextFragment(new_text_fragment_info));
|
bounding_box_size, ScannedTextFragment(new_text_fragment_info));
|
||||||
|
@ -234,8 +234,8 @@ impl TextRunScanner {
|
||||||
let new_text_fragment_info = ScannedTextFragmentInfo::new(run.get_ref().clone(), range);
|
let new_text_fragment_info = ScannedTextFragmentInfo::new(run.get_ref().clone(), range);
|
||||||
let old_fragment = &in_fragments[i.to_uint()];
|
let old_fragment = &in_fragments[i.to_uint()];
|
||||||
let new_metrics = new_text_fragment_info.run.metrics_for_range(&range);
|
let new_metrics = new_text_fragment_info.run.metrics_for_range(&range);
|
||||||
let bounding_box_size = LogicalSize::from_physical(
|
let bounding_box_size = bounding_box_for_run_metrics(
|
||||||
old_fragment.style.writing_mode, new_metrics.bounding_box.size);
|
&new_metrics, old_fragment.style.writing_mode);
|
||||||
let mut new_fragment = old_fragment.transform(
|
let mut new_fragment = old_fragment.transform(
|
||||||
bounding_box_size, ScannedTextFragment(new_text_fragment_info));
|
bounding_box_size, ScannedTextFragment(new_text_fragment_info));
|
||||||
new_fragment.new_line_pos = new_line_positions[logical_offset.to_uint()].new_line_pos.clone();
|
new_fragment.new_line_pos = new_line_positions[logical_offset.to_uint()].new_line_pos.clone();
|
||||||
|
@ -251,6 +251,32 @@ impl TextRunScanner {
|
||||||
} // End of `flush_clump_to_list`.
|
} // End of `flush_clump_to_list`.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn bounding_box_for_run_metrics(metrics: &RunMetrics, writing_mode: WritingMode)
|
||||||
|
-> LogicalSize<Au> {
|
||||||
|
|
||||||
|
// This does nothing, but it will fail to build
|
||||||
|
// when more values are added to the `text-orientation` CSS property.
|
||||||
|
// This will be a reminder to update the code below.
|
||||||
|
let dummy: Option<text_orientation::T> = None;
|
||||||
|
match dummy {
|
||||||
|
Some(text_orientation::sideways_right) |
|
||||||
|
Some(text_orientation::sideways_left) |
|
||||||
|
Some(text_orientation::sideways) |
|
||||||
|
None => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// In vertical sideways or horizontal upgright text,
|
||||||
|
// the "width" of text metrics is always inline
|
||||||
|
// This will need to be updated when other text orientations are supported.
|
||||||
|
LogicalSize::new(
|
||||||
|
writing_mode,
|
||||||
|
metrics.bounding_box.size.width,
|
||||||
|
metrics.bounding_box.size.height)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the metrics of the font represented by the given `FontStyle`, respectively.
|
/// Returns the metrics of the font represented by the given `FontStyle`, respectively.
|
||||||
///
|
///
|
||||||
/// `#[inline]` because often the caller only needs a few fields from the font metrics.
|
/// `#[inline]` because often the caller only needs a few fields from the font metrics.
|
||||||
|
|
|
@ -41,6 +41,11 @@ impl WritingMode {
|
||||||
pub fn is_bidi_ltr(&self) -> bool {
|
pub fn is_bidi_ltr(&self) -> bool {
|
||||||
!self.intersects(FlagRTL)
|
!self.intersects(FlagRTL)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn is_sideways_left(&self) -> bool {
|
||||||
|
self.intersects(FlagSidewaysLeft)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Show for WritingMode {
|
impl Show for WritingMode {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue