From 6cb9fe9064a037224b6560b69b441b72e787fa94 Mon Sep 17 00:00:00 2001 From: Martin Date: Fri, 19 Mar 2021 22:36:13 +0100 Subject: [PATCH] =?UTF-8?q?Text=20colors=20=F0=9F=A6=A9=20(#18)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/exec/context.rs | 1 + src/exec/state.rs | 6 +++++- src/export/pdf.rs | 33 +++++++++++++++++++++------------ src/layout/shaping.rs | 12 ++++++++---- src/layout/text.rs | 3 +++ src/library/font.rs | 7 +++++++ tests/ref/library/font.png | Bin 8878 -> 10697 bytes tests/typ/library/font.typ | 3 +++ tests/typeset.rs | 22 ++++++++++++++-------- 9 files changed, 62 insertions(+), 25 deletions(-) diff --git a/src/exec/context.rs b/src/exec/context.rs index 311619cc6..761977fcc 100644 --- a/src/exec/context.rs +++ b/src/exec/context.rs @@ -178,6 +178,7 @@ impl<'a> ExecContext<'a> { font_size: self.state.font.font_size(), top_edge: self.state.font.top_edge, bottom_edge: self.state.font.bottom_edge, + color: self.state.font.color, } } diff --git a/src/exec/state.rs b/src/exec/state.rs index 6775f394b..f66694fd7 100644 --- a/src/exec/state.rs +++ b/src/exec/state.rs @@ -2,8 +2,9 @@ use std::rc::Rc; use fontdock::{fallback, FallbackTree, FontStretch, FontStyle, FontVariant, FontWeight}; +use crate::color::{Color, RgbaColor}; use crate::geom::*; -use crate::layout::VerticalFontMetric; +use crate::layout::{Fill, VerticalFontMetric}; use crate::paper::{Paper, PaperClass, PAPER_A4}; /// The evaluation state. @@ -115,6 +116,8 @@ pub struct FontState { /// Whether the emphasis toggle is active or inactive. This determines /// whether the next `_` makes italic or non-italic. pub emph: bool, + /// The glyph fill color / texture. + pub color: Fill, } impl FontState { @@ -149,6 +152,7 @@ impl Default for FontState { scale: Linear::ONE, strong: false, emph: false, + color: Fill::Color(Color::Rgba(RgbaColor::BLACK)), } } } diff --git a/src/export/pdf.rs b/src/export/pdf.rs index 43c58403e..d8391e2dd 100644 --- a/src/export/pdf.rs +++ b/src/export/pdf.rs @@ -133,6 +133,22 @@ impl<'a> PdfExporter<'a> { // do that, we need to remember the active face. let mut face = FaceId::MAX; let mut size = Length::ZERO; + let mut fill: Option = None; + let mut change_color = |content: &mut Content, new_fill: Fill| { + if fill != Some(new_fill) { + match new_fill { + Fill::Color(Color::Rgba(c)) => { + content.fill_rgb( + c.r as f32 / 255.0, + c.g as f32 / 255.0, + c.b as f32 / 255.0, + ); + } + Fill::Image(_) => todo!(), + } + fill = Some(new_fill); + } + }; for (pos, element) in &page.elements { let x = pos.x.to_pt() as f32; @@ -152,17 +168,7 @@ impl<'a> PdfExporter<'a> { Element::Geometry(geometry) => { content.save_state(); - - match geometry.fill { - Fill::Color(Color::Rgba(c)) => { - content.fill_rgb( - c.r as f32 / 255.0, - c.g as f32 / 255.0, - c.b as f32 / 255.0, - ); - } - Fill::Image(_) => todo!(), - } + change_color(&mut content, geometry.fill); match &geometry.shape { Shape::Rect(r) => { @@ -179,9 +185,12 @@ impl<'a> PdfExporter<'a> { } Element::Text(shaped) => { + change_color(&mut content, shaped.color); + let mut text = content.text(); - // Check if we need to issue a font switching action. + // Then, also check if we need to + // issue a font switching action. if shaped.face != face || shaped.font_size != size { face = shaped.face; size = shaped.font_size; diff --git a/src/layout/shaping.rs b/src/layout/shaping.rs index 1b769db7d..df27e2871 100644 --- a/src/layout/shaping.rs +++ b/src/layout/shaping.rs @@ -11,7 +11,7 @@ use ttf_parser::{Face, GlyphId}; use crate::env::FontLoader; use crate::geom::{Dir, Length, Point, Size}; -use crate::layout::{Element, Frame}; +use crate::layout::{Element, Fill, Frame}; /// A shaped run of text. #[derive(Clone, PartialEq)] @@ -27,17 +27,20 @@ pub struct Shaped { pub offsets: Vec, /// The font size. pub font_size: Length, + /// The glyph fill color / texture. + pub color: Fill, } impl Shaped { /// Create a new shape run with empty `text`, `glyphs` and `offsets`. - pub fn new(face: FaceId, font_size: Length) -> Self { + pub fn new(face: FaceId, font_size: Length, color: Fill) -> Self { Self { text: String::new(), face, glyphs: vec![], offsets: vec![], font_size, + color: color, } } @@ -100,10 +103,11 @@ pub fn shape( font_size: Length, top_edge: VerticalFontMetric, bottom_edge: VerticalFontMetric, + color: Fill, loader: &mut FontLoader, ) -> Frame { let mut frame = Frame::new(Size::new(Length::ZERO, Length::ZERO)); - let mut shaped = Shaped::new(FaceId::MAX, font_size); + let mut shaped = Shaped::new(FaceId::MAX, font_size, color); let mut width = Length::ZERO; let mut top = Length::ZERO; let mut bottom = Length::ZERO; @@ -133,7 +137,7 @@ pub fn shape( if shaped.face != id { place(&mut frame, shaped, width, top, bottom); - shaped = Shaped::new(id, font_size); + shaped = Shaped::new(id, font_size, color); width = Length::ZERO; top = convert(f64::from(lookup_metric(face, top_edge))); bottom = convert(f64::from(lookup_metric(face, bottom_edge))); diff --git a/src/layout/text.rs b/src/layout/text.rs index 7faefa0d5..7f8f97cc6 100644 --- a/src/layout/text.rs +++ b/src/layout/text.rs @@ -24,6 +24,8 @@ pub struct TextNode { pub top_edge: VerticalFontMetric, /// The bottom end of the text bounding box. pub bottom_edge: VerticalFontMetric, + /// The glyph fill. + pub color: Fill, } impl Layout for TextNode { @@ -37,6 +39,7 @@ impl Layout for TextNode { self.font_size, self.top_edge, self.bottom_edge, + self.color, &mut ctx.env.fonts, ), self.aligns, diff --git a/src/library/font.rs b/src/library/font.rs index 00fd0e81c..0993f7f0f 100644 --- a/src/library/font.rs +++ b/src/library/font.rs @@ -1,3 +1,4 @@ +use crate::layout::Fill; use fontdock::{FontStretch, FontStyle, FontWeight}; use super::*; @@ -15,6 +16,7 @@ use super::*; /// - Font Stretch: `stretch`, of type `relative`, between 0.5 and 2.0. /// - Top edge of the font: `top-edge`, of type `vertical-font-metric`. /// - Bottom edge of the font: `bottom-edge`, of type `vertical-font-metric`. +/// - Fill color the glyphs: `color`, of type `color`. /// - Serif family definition: `serif`, of type `font-familiy-list`. /// - Sans-serif family definition: `sans-serif`, of type `font-familiy-list`. /// - Monospace family definition: `monospace`, of type `font-familiy-list`. @@ -62,6 +64,7 @@ pub fn font(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value { let stretch = args.get(ctx, "stretch"); let top_edge = args.get(ctx, "top-edge"); let bottom_edge = args.get(ctx, "bottom-edge"); + let color = args.get(ctx, "color"); let serif = args.get(ctx, "serif"); let sans_serif = args.get(ctx, "sans-serif"); let monospace = args.get(ctx, "monospace"); @@ -105,6 +108,10 @@ pub fn font(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value { ctx.state.font.bottom_edge = bottom_edge; } + if let Some(color) = color { + ctx.state.font.color = Fill::Color(color); + } + for (variant, arg) in &[ (FontFamily::Serif, &serif), (FontFamily::SansSerif, &sans_serif), diff --git a/tests/ref/library/font.png b/tests/ref/library/font.png index 199a15f2c0308a575782b496b1109e503a5d1085..1cfbc49970abf51d9fbf483be7be1ba85ce94069 100644 GIT binary patch literal 10697 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 ArvLx| delta 8504 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 diff --git a/tests/typ/library/font.typ b/tests/typ/library/font.typ index ecea303f7..3e818f197 100644 --- a/tests/typ/library/font.typ +++ b/tests/typ/library/font.typ @@ -31,6 +31,9 @@ Emoji: 🐪, 🌋, 🏞 ∫ 𝛼 + 3𝛽 d𝑡 ] +// Colors. +#font(color: #239DAD)[This is #font(color: #FA644B)[way more] colorful.] + --- // Test top and bottom edge. diff --git a/tests/typeset.rs b/tests/typeset.rs index 7e7efc677..653470339 100644 --- a/tests/typeset.rs +++ b/tests/typeset.rs @@ -426,7 +426,7 @@ fn draw_text(env: &Env, canvas: &mut Canvas, pos: Point, shaped: &Shaped) { .transform(&Transform::from_row(scale, 0.0, 0.0, -scale, x, y).unwrap()) .unwrap(); - let mut paint = Paint::default(); + let mut paint = paint_from_fill(shaped.color); paint.anti_alias = true; canvas.fill_path(&placed, &paint, FillRule::default()); @@ -438,13 +438,7 @@ fn draw_geometry(_: &Env, canvas: &mut Canvas, pos: Point, element: &Geometry) { let x = pos.x.to_pt() as f32; let y = pos.y.to_pt() as f32; - let mut paint = Paint::default(); - match &element.fill { - Fill::Color(c) => match c { - typst::color::Color::Rgba(c) => paint.set_color_rgba8(c.r, c.g, c.b, c.a), - }, - Fill::Image(_) => todo!(), - }; + let paint = paint_from_fill(element.fill); match &element.shape { Shape::Rect(s) => { @@ -454,6 +448,18 @@ fn draw_geometry(_: &Env, canvas: &mut Canvas, pos: Point, element: &Geometry) { }; } +fn paint_from_fill(fill: Fill) -> Paint<'static> { + let mut paint = Paint::default(); + match fill { + Fill::Color(c) => match c { + typst::color::Color::Rgba(c) => paint.set_color_rgba8(c.r, c.g, c.b, c.a), + }, + Fill::Image(_) => todo!(), + } + + paint +} + fn draw_image(env: &Env, canvas: &mut Canvas, pos: Point, element: &Image) { let img = &env.resources.loaded::(element.res);