mirror of
https://github.com/typst/typst
synced 2025-05-19 03:25:27 +08:00
Text colors 🦩 (#18)
This commit is contained in:
parent
898dc38ec1
commit
6cb9fe9064
@ -178,6 +178,7 @@ impl<'a> ExecContext<'a> {
|
|||||||
font_size: self.state.font.font_size(),
|
font_size: self.state.font.font_size(),
|
||||||
top_edge: self.state.font.top_edge,
|
top_edge: self.state.font.top_edge,
|
||||||
bottom_edge: self.state.font.bottom_edge,
|
bottom_edge: self.state.font.bottom_edge,
|
||||||
|
color: self.state.font.color,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,8 +2,9 @@ use std::rc::Rc;
|
|||||||
|
|
||||||
use fontdock::{fallback, FallbackTree, FontStretch, FontStyle, FontVariant, FontWeight};
|
use fontdock::{fallback, FallbackTree, FontStretch, FontStyle, FontVariant, FontWeight};
|
||||||
|
|
||||||
|
use crate::color::{Color, RgbaColor};
|
||||||
use crate::geom::*;
|
use crate::geom::*;
|
||||||
use crate::layout::VerticalFontMetric;
|
use crate::layout::{Fill, VerticalFontMetric};
|
||||||
use crate::paper::{Paper, PaperClass, PAPER_A4};
|
use crate::paper::{Paper, PaperClass, PAPER_A4};
|
||||||
|
|
||||||
/// The evaluation state.
|
/// The evaluation state.
|
||||||
@ -115,6 +116,8 @@ pub struct FontState {
|
|||||||
/// Whether the emphasis toggle is active or inactive. This determines
|
/// Whether the emphasis toggle is active or inactive. This determines
|
||||||
/// whether the next `_` makes italic or non-italic.
|
/// whether the next `_` makes italic or non-italic.
|
||||||
pub emph: bool,
|
pub emph: bool,
|
||||||
|
/// The glyph fill color / texture.
|
||||||
|
pub color: Fill,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FontState {
|
impl FontState {
|
||||||
@ -149,6 +152,7 @@ impl Default for FontState {
|
|||||||
scale: Linear::ONE,
|
scale: Linear::ONE,
|
||||||
strong: false,
|
strong: false,
|
||||||
emph: false,
|
emph: false,
|
||||||
|
color: Fill::Color(Color::Rgba(RgbaColor::BLACK)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -133,6 +133,22 @@ impl<'a> PdfExporter<'a> {
|
|||||||
// do that, we need to remember the active face.
|
// do that, we need to remember the active face.
|
||||||
let mut face = FaceId::MAX;
|
let mut face = FaceId::MAX;
|
||||||
let mut size = Length::ZERO;
|
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 {
|
for (pos, element) in &page.elements {
|
||||||
let x = pos.x.to_pt() as f32;
|
let x = pos.x.to_pt() as f32;
|
||||||
@ -152,17 +168,7 @@ impl<'a> PdfExporter<'a> {
|
|||||||
|
|
||||||
Element::Geometry(geometry) => {
|
Element::Geometry(geometry) => {
|
||||||
content.save_state();
|
content.save_state();
|
||||||
|
change_color(&mut content, geometry.fill);
|
||||||
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!(),
|
|
||||||
}
|
|
||||||
|
|
||||||
match &geometry.shape {
|
match &geometry.shape {
|
||||||
Shape::Rect(r) => {
|
Shape::Rect(r) => {
|
||||||
@ -179,9 +185,12 @@ impl<'a> PdfExporter<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Element::Text(shaped) => {
|
Element::Text(shaped) => {
|
||||||
|
change_color(&mut content, shaped.color);
|
||||||
|
|
||||||
let mut text = content.text();
|
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 {
|
if shaped.face != face || shaped.font_size != size {
|
||||||
face = shaped.face;
|
face = shaped.face;
|
||||||
size = shaped.font_size;
|
size = shaped.font_size;
|
||||||
|
@ -11,7 +11,7 @@ use ttf_parser::{Face, GlyphId};
|
|||||||
|
|
||||||
use crate::env::FontLoader;
|
use crate::env::FontLoader;
|
||||||
use crate::geom::{Dir, Length, Point, Size};
|
use crate::geom::{Dir, Length, Point, Size};
|
||||||
use crate::layout::{Element, Frame};
|
use crate::layout::{Element, Fill, Frame};
|
||||||
|
|
||||||
/// A shaped run of text.
|
/// A shaped run of text.
|
||||||
#[derive(Clone, PartialEq)]
|
#[derive(Clone, PartialEq)]
|
||||||
@ -27,17 +27,20 @@ pub struct Shaped {
|
|||||||
pub offsets: Vec<Length>,
|
pub offsets: Vec<Length>,
|
||||||
/// The font size.
|
/// The font size.
|
||||||
pub font_size: Length,
|
pub font_size: Length,
|
||||||
|
/// The glyph fill color / texture.
|
||||||
|
pub color: Fill,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Shaped {
|
impl Shaped {
|
||||||
/// Create a new shape run with empty `text`, `glyphs` and `offsets`.
|
/// 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 {
|
Self {
|
||||||
text: String::new(),
|
text: String::new(),
|
||||||
face,
|
face,
|
||||||
glyphs: vec![],
|
glyphs: vec![],
|
||||||
offsets: vec![],
|
offsets: vec![],
|
||||||
font_size,
|
font_size,
|
||||||
|
color: color,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -100,10 +103,11 @@ pub fn shape(
|
|||||||
font_size: Length,
|
font_size: Length,
|
||||||
top_edge: VerticalFontMetric,
|
top_edge: VerticalFontMetric,
|
||||||
bottom_edge: VerticalFontMetric,
|
bottom_edge: VerticalFontMetric,
|
||||||
|
color: Fill,
|
||||||
loader: &mut FontLoader,
|
loader: &mut FontLoader,
|
||||||
) -> Frame {
|
) -> Frame {
|
||||||
let mut frame = Frame::new(Size::new(Length::ZERO, Length::ZERO));
|
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 width = Length::ZERO;
|
||||||
let mut top = Length::ZERO;
|
let mut top = Length::ZERO;
|
||||||
let mut bottom = Length::ZERO;
|
let mut bottom = Length::ZERO;
|
||||||
@ -133,7 +137,7 @@ pub fn shape(
|
|||||||
if shaped.face != id {
|
if shaped.face != id {
|
||||||
place(&mut frame, shaped, width, top, bottom);
|
place(&mut frame, shaped, width, top, bottom);
|
||||||
|
|
||||||
shaped = Shaped::new(id, font_size);
|
shaped = Shaped::new(id, font_size, color);
|
||||||
width = Length::ZERO;
|
width = Length::ZERO;
|
||||||
top = convert(f64::from(lookup_metric(face, top_edge)));
|
top = convert(f64::from(lookup_metric(face, top_edge)));
|
||||||
bottom = convert(f64::from(lookup_metric(face, bottom_edge)));
|
bottom = convert(f64::from(lookup_metric(face, bottom_edge)));
|
||||||
|
@ -24,6 +24,8 @@ pub struct TextNode {
|
|||||||
pub top_edge: VerticalFontMetric,
|
pub top_edge: VerticalFontMetric,
|
||||||
/// The bottom end of the text bounding box.
|
/// The bottom end of the text bounding box.
|
||||||
pub bottom_edge: VerticalFontMetric,
|
pub bottom_edge: VerticalFontMetric,
|
||||||
|
/// The glyph fill.
|
||||||
|
pub color: Fill,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Layout for TextNode {
|
impl Layout for TextNode {
|
||||||
@ -37,6 +39,7 @@ impl Layout for TextNode {
|
|||||||
self.font_size,
|
self.font_size,
|
||||||
self.top_edge,
|
self.top_edge,
|
||||||
self.bottom_edge,
|
self.bottom_edge,
|
||||||
|
self.color,
|
||||||
&mut ctx.env.fonts,
|
&mut ctx.env.fonts,
|
||||||
),
|
),
|
||||||
self.aligns,
|
self.aligns,
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
use crate::layout::Fill;
|
||||||
use fontdock::{FontStretch, FontStyle, FontWeight};
|
use fontdock::{FontStretch, FontStyle, FontWeight};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
@ -15,6 +16,7 @@ use super::*;
|
|||||||
/// - Font Stretch: `stretch`, of type `relative`, between 0.5 and 2.0.
|
/// - Font Stretch: `stretch`, of type `relative`, between 0.5 and 2.0.
|
||||||
/// - Top edge of the font: `top-edge`, of type `vertical-font-metric`.
|
/// - Top edge of the font: `top-edge`, of type `vertical-font-metric`.
|
||||||
/// - Bottom edge of the font: `bottom-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`.
|
/// - Serif family definition: `serif`, of type `font-familiy-list`.
|
||||||
/// - Sans-serif family definition: `sans-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`.
|
/// - 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 stretch = args.get(ctx, "stretch");
|
||||||
let top_edge = args.get(ctx, "top-edge");
|
let top_edge = args.get(ctx, "top-edge");
|
||||||
let bottom_edge = args.get(ctx, "bottom-edge");
|
let bottom_edge = args.get(ctx, "bottom-edge");
|
||||||
|
let color = args.get(ctx, "color");
|
||||||
let serif = args.get(ctx, "serif");
|
let serif = args.get(ctx, "serif");
|
||||||
let sans_serif = args.get(ctx, "sans-serif");
|
let sans_serif = args.get(ctx, "sans-serif");
|
||||||
let monospace = args.get(ctx, "monospace");
|
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;
|
ctx.state.font.bottom_edge = bottom_edge;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(color) = color {
|
||||||
|
ctx.state.font.color = Fill::Color(color);
|
||||||
|
}
|
||||||
|
|
||||||
for (variant, arg) in &[
|
for (variant, arg) in &[
|
||||||
(FontFamily::Serif, &serif),
|
(FontFamily::Serif, &serif),
|
||||||
(FontFamily::SansSerif, &sans_serif),
|
(FontFamily::SansSerif, &sans_serif),
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 8.7 KiB After Width: | Height: | Size: 10 KiB |
@ -31,6 +31,9 @@ Emoji: 🐪, 🌋, 🏞
|
|||||||
∫ 𝛼 + 3𝛽 d𝑡
|
∫ 𝛼 + 3𝛽 d𝑡
|
||||||
]
|
]
|
||||||
|
|
||||||
|
// Colors.
|
||||||
|
#font(color: #239DAD)[This is #font(color: #FA644B)[way more] colorful.]
|
||||||
|
|
||||||
---
|
---
|
||||||
// Test top and bottom edge.
|
// Test top and bottom edge.
|
||||||
|
|
||||||
|
@ -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())
|
.transform(&Transform::from_row(scale, 0.0, 0.0, -scale, x, y).unwrap())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let mut paint = Paint::default();
|
let mut paint = paint_from_fill(shaped.color);
|
||||||
paint.anti_alias = true;
|
paint.anti_alias = true;
|
||||||
|
|
||||||
canvas.fill_path(&placed, &paint, FillRule::default());
|
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 x = pos.x.to_pt() as f32;
|
||||||
let y = pos.y.to_pt() as f32;
|
let y = pos.y.to_pt() as f32;
|
||||||
|
|
||||||
let mut paint = Paint::default();
|
let paint = paint_from_fill(element.fill);
|
||||||
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!(),
|
|
||||||
};
|
|
||||||
|
|
||||||
match &element.shape {
|
match &element.shape {
|
||||||
Shape::Rect(s) => {
|
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) {
|
fn draw_image(env: &Env, canvas: &mut Canvas, pos: Point, element: &Image) {
|
||||||
let img = &env.resources.loaded::<ImageResource>(element.res);
|
let img = &env.resources.loaded::<ImageResource>(element.res);
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user