Implement underlines.

This commit is contained in:
David Wendt 2020-06-18 21:34:18 -04:00
parent d63b3f23e9
commit f097a6584e
2 changed files with 103 additions and 0 deletions

View File

@ -50,6 +50,11 @@ impl EvalParameters {
kerning: span.kerning, kerning: span.kerning,
} }
} }
/// Get the height the font would be evaluated at.
pub fn height(&self) -> Twips {
self.height
}
} }
#[derive(Debug, Clone, Collect, Copy)] #[derive(Debug, Clone, Collect, Copy)]
@ -188,6 +193,13 @@ impl<'gc> Font<'gc> {
Twips::new((self.0.leading as f32 * scale) as i32) Twips::new((self.0.leading as f32 * scale) as i32)
} }
/// Get the baseline from the top of the glyph at a given height.
pub fn get_baseline_for_height(self, height: Twips) -> Twips {
let scale = height.get() as f32 / self.scale();
Twips::new((self.0.ascent as f32 * scale) as i32)
}
/// Returns whether this font contains kerning information. /// Returns whether this font contains kerning information.
pub fn has_kerning_info(self) -> bool { pub fn has_kerning_info(self) -> bool {
!self.0.kerning_pairs.is_empty() !self.0.kerning_pairs.is_empty()

View File

@ -5,12 +5,33 @@ use crate::drawing::Drawing;
use crate::font::{EvalParameters, Font}; use crate::font::{EvalParameters, Font};
use crate::html::dimensions::{BoxBounds, Position, Size}; use crate::html::dimensions::{BoxBounds, Position, Size};
use crate::html::text_format::{FormatSpans, TextFormat, TextSpan}; use crate::html::text_format::{FormatSpans, TextFormat, TextSpan};
use crate::shape_utils::DrawCommand;
use crate::tag_utils::SwfMovie; use crate::tag_utils::SwfMovie;
use gc_arena::{Collect, GcCell, MutationContext}; use gc_arena::{Collect, GcCell, MutationContext};
use std::cmp::{max, min}; use std::cmp::{max, min};
use std::sync::Arc; use std::sync::Arc;
use swf::Twips; use swf::Twips;
/// Draw an underline on a particular drawing.
///
/// This will not draw underlines shorter than a pixel in width.
fn draw_underline(drawing: &mut Drawing, starting_pos: Position<Twips>, width: Twips) {
if width < Twips::from_pixels(1.0) {
return;
}
let ending_pos = starting_pos + Position::from((width, Twips::zero()));
drawing.draw_command(DrawCommand::MoveTo {
x: starting_pos.x(),
y: starting_pos.y(),
});
drawing.draw_command(DrawCommand::LineTo {
x: ending_pos.x(),
y: ending_pos.y(),
});
}
/// Contains information relating to the current layout operation. /// Contains information relating to the current layout operation.
pub struct LayoutContext<'a, 'gc> { pub struct LayoutContext<'a, 'gc> {
/// The movie this layout context is pulling fonts from. /// The movie this layout context is pulling fonts from.
@ -116,6 +137,74 @@ impl<'a, 'gc> LayoutContext<'a, 'gc> {
} }
} }
/// Construct an underline drawing for the current line of text and add it
/// to the line.
fn append_underlines(&mut self, context: &mut UpdateContext<'_, 'gc, '_>) {
let mut starting_pos: Option<Position<Twips>> = None;
let mut current_width: Option<Twips> = None;
let mut line_drawing = Drawing::new();
let mut line = self.current_line;
line_drawing.set_line_style(Some(swf::LineStyle::new_v1(
Twips::new(1),
swf::Color::from_rgb(0, 255),
)));
while let Some(linebox) = line {
let read = linebox.read();
if read.is_text_box() {
if let Some((_t, tf, font, params, _color)) = read.as_renderable_text(self.text) {
let underline_baseline =
font.get_baseline_for_height(params.height()) + Twips::from_pixels(2.0);
let mut line_extended = false;
if let Some(starting_pos) = starting_pos {
if tf.underline.unwrap_or(false)
&& underline_baseline + read.bounds().origin().y() == starting_pos.y()
{
//Underline is at the same baseline, extend it
current_width = Some(read.bounds().extent_x() - starting_pos.x());
line_extended = true;
}
}
if !line_extended {
//For whatever reason, we cannot extend the current underline.
//This can happen if we don't have an underline to extend, the
//underlines don't match, or this span doesn't call for one.
if let (Some(pos), Some(width)) = (starting_pos, current_width) {
draw_underline(&mut line_drawing, pos, width);
starting_pos = None;
current_width = None;
}
if tf.underline.unwrap_or(false) {
starting_pos = Some(
read.bounds().origin()
+ Position::from((Twips::zero(), underline_baseline)),
);
current_width = Some(read.bounds().width());
}
}
}
}
line = read.next_sibling();
}
if let (Some(starting_pos), Some(current_width)) = (starting_pos, current_width) {
draw_underline(&mut line_drawing, starting_pos, current_width);
}
self.append_box(
context.gc_context,
LayoutBox::from_drawing(context.gc_context, line_drawing),
);
}
/// Apply all indents and alignment to the current line, if necessary. /// Apply all indents and alignment to the current line, if necessary.
/// ///
/// The `only_line` parameter should be flagged if this is the only line in /// The `only_line` parameter should be flagged if this is the only line in
@ -223,6 +312,8 @@ impl<'a, 'gc> LayoutContext<'a, 'gc> {
box_count += 1; box_count += 1;
} }
self.append_underlines(context);
line_bounds += line_bounds +=
Position::from((left_adjustment + align_adjustment, Twips::from_pixels(0.0))); Position::from((left_adjustment + align_adjustment, Twips::from_pixels(0.0)));
line_bounds += Size::from((Twips::from_pixels(0.0), font_leading_adjustment)); line_bounds += Size::from((Twips::from_pixels(0.0), font_leading_adjustment));