Text colors 🦩 (#18)

This commit is contained in:
Martin 2021-03-19 22:36:13 +01:00 committed by GitHub
parent 898dc38ec1
commit 6cb9fe9064
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 62 additions and 25 deletions

View File

@ -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,
}
}

View File

@ -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)),
}
}
}

View File

@ -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<Fill> = 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;

View File

@ -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<Length>,
/// 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)));

View File

@ -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,

View File

@ -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),

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.7 KiB

After

Width:  |  Height:  |  Size: 10 KiB

View File

@ -31,6 +31,9 @@ Emoji: 🐪, 🌋, 🏞
𝛼 + 3𝛽 d𝑡
]
// Colors.
#font(color: #239DAD)[This is #font(color: #FA644B)[way more] colorful.]
---
// Test top and bottom edge.

View File

@ -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::<ImageResource>(element.res);