mirror of
https://github.com/typst/typst
synced 2025-05-14 04:56:26 +08:00
Switch from state to decorations for underline/strikethrough/overline
This commit is contained in:
parent
d546453880
commit
148a06c070
@ -140,12 +140,6 @@ pub struct FontState {
|
|||||||
/// A list of font families with generic class definitions (the final
|
/// A list of font families with generic class definitions (the final
|
||||||
/// family list also depends on `monospace`).
|
/// family list also depends on `monospace`).
|
||||||
pub families: Rc<FamilyState>,
|
pub families: Rc<FamilyState>,
|
||||||
/// The specifications for a strikethrough line, if any.
|
|
||||||
pub strikethrough: Option<Rc<LineState>>,
|
|
||||||
/// The specifications for a underline, if any.
|
|
||||||
pub underline: Option<Rc<LineState>>,
|
|
||||||
/// The specifications for a overline line, if any.
|
|
||||||
pub overline: Option<Rc<LineState>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FontState {
|
impl FontState {
|
||||||
@ -212,9 +206,6 @@ impl Default for FontState {
|
|||||||
top_edge: VerticalFontMetric::CapHeight,
|
top_edge: VerticalFontMetric::CapHeight,
|
||||||
bottom_edge: VerticalFontMetric::Baseline,
|
bottom_edge: VerticalFontMetric::Baseline,
|
||||||
fill: Paint::Color(Color::Rgba(RgbaColor::BLACK)),
|
fill: Paint::Color(Color::Rgba(RgbaColor::BLACK)),
|
||||||
strikethrough: None,
|
|
||||||
underline: None,
|
|
||||||
overline: None,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -248,19 +239,3 @@ impl Default for FamilyState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Defines a line that is positioned over, under or on top of text.
|
|
||||||
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
|
||||||
pub struct LineState {
|
|
||||||
/// Stroke color of the line, defaults to the text color if `None`.
|
|
||||||
pub stroke: Option<Paint>,
|
|
||||||
/// Thickness of the line's strokes (dependent on scaled font size), read
|
|
||||||
/// from the font tables if `None`.
|
|
||||||
pub thickness: Option<Linear>,
|
|
||||||
/// Position of the line relative to the baseline (dependent on scaled font
|
|
||||||
/// size), read from the font tables if `None`.
|
|
||||||
pub offset: Option<Linear>,
|
|
||||||
/// Amount that the line will be longer or shorter than its associated text
|
|
||||||
/// (dependent on scaled font size).
|
|
||||||
pub extent: Linear,
|
|
||||||
}
|
|
||||||
|
@ -8,7 +8,8 @@ use super::{State, Str};
|
|||||||
use crate::diag::StrResult;
|
use crate::diag::StrResult;
|
||||||
use crate::geom::{Align, Dir, Gen, GenAxis, Length, Linear, Sides, Size};
|
use crate::geom::{Align, Dir, Gen, GenAxis, Length, Linear, Sides, Size};
|
||||||
use crate::layout::{
|
use crate::layout::{
|
||||||
LayoutNode, LayoutTree, PadNode, PageRun, ParChild, ParNode, StackChild, StackNode,
|
Decoration, LayoutNode, LayoutTree, PadNode, PageRun, ParChild, ParNode, StackChild,
|
||||||
|
StackNode,
|
||||||
};
|
};
|
||||||
use crate::util::EcoString;
|
use crate::util::EcoString;
|
||||||
|
|
||||||
@ -43,13 +44,6 @@ enum TemplateNode {
|
|||||||
Modify(Rc<dyn Fn(&mut State)>),
|
Modify(Rc<dyn Fn(&mut State)>),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A template node decoration.
|
|
||||||
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
|
||||||
pub enum Decoration {
|
|
||||||
/// A link.
|
|
||||||
Link(EcoString),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Template {
|
impl Template {
|
||||||
/// Create a new, empty template.
|
/// Create a new, empty template.
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
@ -114,7 +108,7 @@ impl Template {
|
|||||||
self.make_mut().push(TemplateNode::Spacing(axis, spacing));
|
self.make_mut().push(TemplateNode::Spacing(axis, spacing));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add a decoration to the last template node.
|
/// Add a decoration to all contained nodes.
|
||||||
pub fn decorate(&mut self, deco: Decoration) {
|
pub fn decorate(&mut self, deco: Decoration) {
|
||||||
for node in self.make_mut() {
|
for node in self.make_mut() {
|
||||||
let decos = match node {
|
let decos = match node {
|
||||||
|
@ -50,7 +50,7 @@ impl<'a> PdfExporter<'a> {
|
|||||||
for frame in frames {
|
for frame in frames {
|
||||||
for (_, element) in frame.elements() {
|
for (_, element) in frame.elements() {
|
||||||
match *element {
|
match *element {
|
||||||
Element::Text(ref shaped) => font_map.insert(shaped.face_id),
|
Element::Text(ref text) => font_map.insert(text.face_id),
|
||||||
Element::Geometry(_, _) => {}
|
Element::Geometry(_, _) => {}
|
||||||
Element::Image(id, _) => {
|
Element::Image(id, _) => {
|
||||||
let img = ctx.images.get(id);
|
let img = ctx.images.get(id);
|
||||||
@ -168,35 +168,35 @@ impl<'a> PdfExporter<'a> {
|
|||||||
let y = (page.size.h - pos.y).to_pt() as f32;
|
let y = (page.size.h - pos.y).to_pt() as f32;
|
||||||
|
|
||||||
match *element {
|
match *element {
|
||||||
Element::Text(ref shaped) => {
|
Element::Text(ref text) => {
|
||||||
if fill != Some(shaped.fill) {
|
if fill != Some(text.fill) {
|
||||||
write_fill(&mut content, shaped.fill);
|
write_fill(&mut content, text.fill);
|
||||||
fill = Some(shaped.fill);
|
fill = Some(text.fill);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut text = content.text();
|
let mut text_writer = content.text();
|
||||||
|
|
||||||
// Then, also check if we need to issue a font switching
|
// Then, also check if we need to issue a font switching
|
||||||
// action.
|
// action.
|
||||||
if face_id != Some(shaped.face_id) || shaped.size != size {
|
if face_id != Some(text.face_id) || text.size != size {
|
||||||
face_id = Some(shaped.face_id);
|
face_id = Some(text.face_id);
|
||||||
size = shaped.size;
|
size = text.size;
|
||||||
|
|
||||||
let name = format!("F{}", self.font_map.map(shaped.face_id));
|
let name = format!("F{}", self.font_map.map(text.face_id));
|
||||||
text.font(Name(name.as_bytes()), size.to_pt() as f32);
|
text_writer.font(Name(name.as_bytes()), size.to_pt() as f32);
|
||||||
}
|
}
|
||||||
|
|
||||||
let face = self.fonts.get(shaped.face_id);
|
let face = self.fonts.get(text.face_id);
|
||||||
|
|
||||||
// Position the text.
|
// Position the text.
|
||||||
text.matrix(1.0, 0.0, 0.0, 1.0, x, y);
|
text_writer.matrix(1.0, 0.0, 0.0, 1.0, x, y);
|
||||||
|
|
||||||
let mut positioned = text.show_positioned();
|
let mut positioned = text_writer.show_positioned();
|
||||||
let mut adjustment = Em::zero();
|
let mut adjustment = Em::zero();
|
||||||
let mut encoded = vec![];
|
let mut encoded = vec![];
|
||||||
|
|
||||||
// Write the glyphs with kerning adjustments.
|
// Write the glyphs with kerning adjustments.
|
||||||
for glyph in &shaped.glyphs {
|
for glyph in &text.glyphs {
|
||||||
adjustment += glyph.x_offset;
|
adjustment += glyph.x_offset;
|
||||||
|
|
||||||
if !adjustment.is_zero() {
|
if !adjustment.is_zero() {
|
||||||
|
@ -163,6 +163,7 @@ pub struct Face {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Metrics for a decorative line.
|
/// Metrics for a decorative line.
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub struct LineMetrics {
|
pub struct LineMetrics {
|
||||||
pub strength: Em,
|
pub strength: Em,
|
||||||
pub position: Em,
|
pub position: Em,
|
||||||
|
@ -131,6 +131,8 @@ pub struct Text {
|
|||||||
pub face_id: FaceId,
|
pub face_id: FaceId,
|
||||||
/// The font size.
|
/// The font size.
|
||||||
pub size: Length,
|
pub size: Length,
|
||||||
|
/// The width of the text run.
|
||||||
|
pub width: Length,
|
||||||
/// Glyph color.
|
/// Glyph color.
|
||||||
pub fill: Paint,
|
pub fill: Paint,
|
||||||
/// The glyphs.
|
/// The glyphs.
|
||||||
|
@ -4,7 +4,7 @@ use unicode_bidi::{BidiInfo, Level};
|
|||||||
use xi_unicode::LineBreakIterator;
|
use xi_unicode::LineBreakIterator;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::eval::{Decoration, FontState};
|
use crate::eval::FontState;
|
||||||
use crate::util::{EcoString, RangeExt, SliceExt};
|
use crate::util::{EcoString, RangeExt, SliceExt};
|
||||||
|
|
||||||
type Range = std::ops::Range<usize>;
|
type Range = std::ops::Range<usize>;
|
||||||
@ -368,7 +368,7 @@ impl<'a> LineStack<'a> {
|
|||||||
let mut first = true;
|
let mut first = true;
|
||||||
|
|
||||||
for line in self.lines.drain(..) {
|
for line in self.lines.drain(..) {
|
||||||
let frame = line.build(ctx, self.size.w);
|
let frame = line.build(self.size.w);
|
||||||
|
|
||||||
let pos = Point::new(Length::zero(), offset);
|
let pos = Point::new(Length::zero(), offset);
|
||||||
if first {
|
if first {
|
||||||
@ -380,21 +380,14 @@ impl<'a> LineStack<'a> {
|
|||||||
output.merge_frame(pos, frame);
|
output.merge_frame(pos, frame);
|
||||||
}
|
}
|
||||||
|
|
||||||
// For each frame, we look if any decorations apply.
|
for (_, child) in &mut output.children {
|
||||||
for i in 0 .. output.children.len() {
|
if let FrameChild::Frame(Some(frame_idx), frame) = child {
|
||||||
let &(point, ref child) = &output.children[i];
|
for deco in match &self.children[*frame_idx] {
|
||||||
if let &FrameChild::Frame(Some(frame_idx), ref frame) = child {
|
|
||||||
let size = frame.size;
|
|
||||||
for deco in match &self.children[frame_idx] {
|
|
||||||
ParChild::Spacing(_) => continue,
|
ParChild::Spacing(_) => continue,
|
||||||
ParChild::Text(.., decos) => decos,
|
ParChild::Text(.., decos) => decos,
|
||||||
ParChild::Any(.., decos) => decos,
|
ParChild::Any(.., decos) => decos,
|
||||||
} {
|
} {
|
||||||
match deco {
|
deco.apply(ctx, Rc::make_mut(frame));
|
||||||
Decoration::Link(href) => {
|
|
||||||
output.push(point, Element::Link(href.to_string(), size));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -528,7 +521,7 @@ impl<'a> LineLayout<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Build the line's frame.
|
/// Build the line's frame.
|
||||||
fn build(&self, ctx: &LayoutContext, width: Length) -> Frame {
|
fn build(&self, width: Length) -> Frame {
|
||||||
let size = Size::new(self.size.w.max(width), self.size.h);
|
let size = Size::new(self.size.w.max(width), self.size.h);
|
||||||
let free = size.w - self.size.w;
|
let free = size.w - self.size.w;
|
||||||
|
|
||||||
@ -544,7 +537,7 @@ impl<'a> LineLayout<'a> {
|
|||||||
}
|
}
|
||||||
ParItem::Text(ref shaped, align, _) => {
|
ParItem::Text(ref shaped, align, _) => {
|
||||||
ruler = ruler.max(align);
|
ruler = ruler.max(align);
|
||||||
Rc::new(shaped.build(ctx))
|
Rc::new(shaped.build())
|
||||||
}
|
}
|
||||||
ParItem::Frame(ref frame, align, _) => {
|
ParItem::Frame(ref frame, align, _) => {
|
||||||
ruler = ruler.max(align);
|
ruler = ruler.max(align);
|
||||||
@ -618,6 +611,96 @@ impl<'a> LineLayout<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A decoration for a paragraph child.
|
||||||
|
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
||||||
|
pub enum Decoration {
|
||||||
|
/// A link.
|
||||||
|
Link(EcoString),
|
||||||
|
/// An underline/strikethrough/overline decoration.
|
||||||
|
Line(LineDecoration),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Defines a line that is positioned over, under or on top of text.
|
||||||
|
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
||||||
|
pub struct LineDecoration {
|
||||||
|
/// The kind of line.
|
||||||
|
pub kind: LineKind,
|
||||||
|
/// Stroke color of the line, defaults to the text color if `None`.
|
||||||
|
pub stroke: Option<Paint>,
|
||||||
|
/// Thickness of the line's strokes (dependent on scaled font size), read
|
||||||
|
/// from the font tables if `None`.
|
||||||
|
pub thickness: Option<Linear>,
|
||||||
|
/// Position of the line relative to the baseline (dependent on scaled font
|
||||||
|
/// size), read from the font tables if `None`.
|
||||||
|
pub offset: Option<Linear>,
|
||||||
|
/// Amount that the line will be longer or shorter than its associated text
|
||||||
|
/// (dependent on scaled font size).
|
||||||
|
pub extent: Linear,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The kind of line decoration.
|
||||||
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||||
|
pub enum LineKind {
|
||||||
|
/// A line under text.
|
||||||
|
Underline,
|
||||||
|
/// A line through text.
|
||||||
|
Strikethrough,
|
||||||
|
/// A line over text.
|
||||||
|
Overline,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Decoration {
|
||||||
|
/// Apply a decoration to a child's frame.
|
||||||
|
pub fn apply(&self, ctx: &LayoutContext, frame: &mut Frame) {
|
||||||
|
match self {
|
||||||
|
Decoration::Link(href) => {
|
||||||
|
let link = Element::Link(href.to_string(), frame.size);
|
||||||
|
frame.push(Point::zero(), link);
|
||||||
|
}
|
||||||
|
Decoration::Line(line) => {
|
||||||
|
line.apply(ctx, frame);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LineDecoration {
|
||||||
|
/// Apply a line decoration to a all text elements in a frame.
|
||||||
|
pub fn apply(&self, ctx: &LayoutContext, frame: &mut Frame) {
|
||||||
|
for i in 0 .. frame.children.len() {
|
||||||
|
let (pos, child) = &frame.children[i];
|
||||||
|
if let FrameChild::Element(Element::Text(text)) = child {
|
||||||
|
let face = ctx.fonts.get(text.face_id);
|
||||||
|
let metrics = match self.kind {
|
||||||
|
LineKind::Underline => face.underline,
|
||||||
|
LineKind::Strikethrough => face.strikethrough,
|
||||||
|
LineKind::Overline => face.overline,
|
||||||
|
};
|
||||||
|
|
||||||
|
let stroke = self.stroke.unwrap_or(text.fill);
|
||||||
|
|
||||||
|
let thickness = self
|
||||||
|
.thickness
|
||||||
|
.map(|s| s.resolve(text.size))
|
||||||
|
.unwrap_or(metrics.strength.to_length(text.size));
|
||||||
|
|
||||||
|
let offset = self
|
||||||
|
.offset
|
||||||
|
.map(|s| s.resolve(text.size))
|
||||||
|
.unwrap_or(-metrics.position.to_length(text.size));
|
||||||
|
|
||||||
|
let extent = self.extent.resolve(text.size);
|
||||||
|
|
||||||
|
let subpos = Point::new(pos.x - extent, pos.y + offset);
|
||||||
|
let vector = Point::new(text.width + 2.0 * extent, Length::zero());
|
||||||
|
let line = Geometry::Line(vector, thickness);
|
||||||
|
|
||||||
|
frame.push(subpos, Element::Geometry(line, stroke));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Additional methods for BiDi levels.
|
/// Additional methods for BiDi levels.
|
||||||
trait LevelExt: Sized {
|
trait LevelExt: Sized {
|
||||||
fn from_dir(dir: Dir) -> Option<Self>;
|
fn from_dir(dir: Dir) -> Option<Self>;
|
||||||
|
@ -4,10 +4,9 @@ use std::ops::Range;
|
|||||||
use rustybuzz::UnicodeBuffer;
|
use rustybuzz::UnicodeBuffer;
|
||||||
|
|
||||||
use super::{Element, Frame, Glyph, LayoutContext, Text};
|
use super::{Element, Frame, Glyph, LayoutContext, Text};
|
||||||
use crate::eval::{FontState, LineState};
|
use crate::eval::FontState;
|
||||||
use crate::font::{Face, FaceId, FontVariant, LineMetrics};
|
use crate::font::{Face, FaceId, FontVariant};
|
||||||
use crate::geom::{Dir, Em, Length, Point, Size};
|
use crate::geom::{Dir, Em, Length, Point, Size};
|
||||||
use crate::layout::Geometry;
|
|
||||||
use crate::util::SliceExt;
|
use crate::util::SliceExt;
|
||||||
|
|
||||||
/// Shape text into [`ShapedText`].
|
/// Shape text into [`ShapedText`].
|
||||||
@ -85,7 +84,7 @@ pub struct ShapedGlyph {
|
|||||||
|
|
||||||
impl<'a> ShapedText<'a> {
|
impl<'a> ShapedText<'a> {
|
||||||
/// Build the shaped text's frame.
|
/// Build the shaped text's frame.
|
||||||
pub fn build(&self, ctx: &LayoutContext) -> Frame {
|
pub fn build(&self) -> Frame {
|
||||||
let mut frame = Frame::new(self.size, self.baseline);
|
let mut frame = Frame::new(self.size, self.baseline);
|
||||||
let mut offset = Length::zero();
|
let mut offset = Length::zero();
|
||||||
|
|
||||||
@ -95,24 +94,22 @@ impl<'a> ShapedText<'a> {
|
|||||||
let mut text = Text {
|
let mut text = Text {
|
||||||
face_id,
|
face_id,
|
||||||
size: self.state.size,
|
size: self.state.size,
|
||||||
|
width: Length::zero(),
|
||||||
fill: self.state.fill,
|
fill: self.state.fill,
|
||||||
glyphs: vec![],
|
glyphs: vec![],
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut width = Length::zero();
|
|
||||||
for glyph in group {
|
for glyph in group {
|
||||||
text.glyphs.push(Glyph {
|
text.glyphs.push(Glyph {
|
||||||
id: glyph.glyph_id,
|
id: glyph.glyph_id,
|
||||||
x_advance: glyph.x_advance,
|
x_advance: glyph.x_advance,
|
||||||
x_offset: glyph.x_offset,
|
x_offset: glyph.x_offset,
|
||||||
});
|
});
|
||||||
width += glyph.x_advance.to_length(text.size);
|
text.width += glyph.x_advance.to_length(text.size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
offset += text.width;
|
||||||
frame.push(pos, Element::Text(text));
|
frame.push(pos, Element::Text(text));
|
||||||
decorate(ctx, &mut frame, pos, width, face_id, &self.state);
|
|
||||||
|
|
||||||
offset += width;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
frame
|
frame
|
||||||
@ -371,48 +368,3 @@ fn measure(
|
|||||||
|
|
||||||
(Size::new(width, top + bottom), top)
|
(Size::new(width, top + bottom), top)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add underline, strikthrough and overline decorations.
|
|
||||||
fn decorate(
|
|
||||||
ctx: &LayoutContext,
|
|
||||||
frame: &mut Frame,
|
|
||||||
pos: Point,
|
|
||||||
width: Length,
|
|
||||||
face_id: FaceId,
|
|
||||||
state: &FontState,
|
|
||||||
) {
|
|
||||||
let mut apply = |substate: &LineState, metrics: fn(&Face) -> &LineMetrics| {
|
|
||||||
let metrics = metrics(ctx.fonts.get(face_id));
|
|
||||||
|
|
||||||
let stroke = substate.stroke.unwrap_or(state.fill);
|
|
||||||
|
|
||||||
let thickness = substate
|
|
||||||
.thickness
|
|
||||||
.map(|s| s.resolve(state.size))
|
|
||||||
.unwrap_or(metrics.strength.to_length(state.size));
|
|
||||||
|
|
||||||
let offset = substate
|
|
||||||
.offset
|
|
||||||
.map(|s| s.resolve(state.size))
|
|
||||||
.unwrap_or(-metrics.position.to_length(state.size));
|
|
||||||
|
|
||||||
let extent = substate.extent.resolve(state.size);
|
|
||||||
|
|
||||||
let pos = Point::new(pos.x - extent, pos.y + offset);
|
|
||||||
let target = Point::new(width + 2.0 * extent, Length::zero());
|
|
||||||
let element = Element::Geometry(Geometry::Line(target, thickness), stroke);
|
|
||||||
frame.push(pos, element);
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(strikethrough) = &state.strikethrough {
|
|
||||||
apply(strikethrough, |face| &face.strikethrough);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(underline) = &state.underline {
|
|
||||||
apply(underline, |face| &face.underline);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(overline) = &state.overline {
|
|
||||||
apply(overline, |face| &face.overline);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
use crate::eval::{Decoration, FontState, LineState};
|
use crate::layout::{Decoration, LineDecoration, LineKind, Paint};
|
||||||
use crate::layout::Paint;
|
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
@ -155,47 +154,39 @@ fn lang_dir(iso: &str) -> Dir {
|
|||||||
|
|
||||||
/// `strike`: Set striken-through text.
|
/// `strike`: Set striken-through text.
|
||||||
pub fn strike(ctx: &mut EvalContext, args: &mut Arguments) -> TypResult<Value> {
|
pub fn strike(ctx: &mut EvalContext, args: &mut Arguments) -> TypResult<Value> {
|
||||||
line_impl(ctx, args, |font| &mut font.strikethrough)
|
line_impl(ctx, args, LineKind::Strikethrough)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `underline`: Set underlined text.
|
/// `underline`: Set underlined text.
|
||||||
pub fn underline(ctx: &mut EvalContext, args: &mut Arguments) -> TypResult<Value> {
|
pub fn underline(ctx: &mut EvalContext, args: &mut Arguments) -> TypResult<Value> {
|
||||||
line_impl(ctx, args, |font| &mut font.underline)
|
line_impl(ctx, args, LineKind::Underline)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `overline`: Set text with an overline.
|
/// `overline`: Set text with an overline.
|
||||||
pub fn overline(ctx: &mut EvalContext, args: &mut Arguments) -> TypResult<Value> {
|
pub fn overline(ctx: &mut EvalContext, args: &mut Arguments) -> TypResult<Value> {
|
||||||
line_impl(ctx, args, |font| &mut font.overline)
|
line_impl(ctx, args, LineKind::Overline)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn line_impl(
|
fn line_impl(
|
||||||
_: &mut EvalContext,
|
_: &mut EvalContext,
|
||||||
args: &mut Arguments,
|
args: &mut Arguments,
|
||||||
substate: fn(&mut FontState) -> &mut Option<Rc<LineState>>,
|
kind: LineKind,
|
||||||
) -> TypResult<Value> {
|
) -> TypResult<Value> {
|
||||||
let stroke = args.named("stroke")?.or_else(|| args.eat());
|
let stroke = args.named("stroke")?.or_else(|| args.eat());
|
||||||
let thickness = args.named::<Linear>("thickness")?.or_else(|| args.eat());
|
let thickness = args.named::<Linear>("thickness")?.or_else(|| args.eat());
|
||||||
let offset = args.named("offset")?;
|
let offset = args.named("offset")?;
|
||||||
let extent = args.named("extent")?.unwrap_or_default();
|
let extent = args.named("extent")?.unwrap_or_default();
|
||||||
let body = args.expect("body")?;
|
|
||||||
|
|
||||||
// Suppress any existing strikethrough if strength is explicitly zero.
|
let mut body: Template = args.expect("body")?;
|
||||||
let line = thickness.map_or(true, |s| !s.is_zero()).then(|| {
|
body.decorate(Decoration::Line(LineDecoration {
|
||||||
Rc::new(LineState {
|
kind,
|
||||||
stroke: stroke.map(Paint::Color),
|
stroke: stroke.map(Paint::Color),
|
||||||
thickness,
|
thickness,
|
||||||
offset,
|
offset,
|
||||||
extent,
|
extent,
|
||||||
})
|
}));
|
||||||
});
|
|
||||||
|
|
||||||
let mut template = Template::new();
|
Ok(Value::Template(body))
|
||||||
template.save();
|
|
||||||
template.modify(move |state| *substate(state.font_mut()) = line.clone());
|
|
||||||
template += body;
|
|
||||||
template.restore();
|
|
||||||
|
|
||||||
Ok(Value::Template(template))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `link`: Set a link.
|
/// `link`: Set a link.
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 9.1 KiB |
@ -18,9 +18,6 @@
|
|||||||
// Both over- and underline.
|
// Both over- and underline.
|
||||||
#overline(underline[Running amongst the wolves.])
|
#overline(underline[Running amongst the wolves.])
|
||||||
|
|
||||||
// Disable underline by setting it back to 0pt.
|
|
||||||
#underline[Still important, but not #underline(0pt)[mission ]critical.]
|
|
||||||
|
|
||||||
---
|
---
|
||||||
#let redact = strike with (10pt, extent: 5%)
|
#let redact = strike with (10pt, extent: 5%)
|
||||||
#let highlight = strike with (
|
#let highlight = strike with (
|
||||||
|
Loading…
x
Reference in New Issue
Block a user