diff --git a/src/exec/context.rs b/src/exec/context.rs index 6a1c24167..7fd72fe3b 100644 --- a/src/exec/context.rs +++ b/src/exec/context.rs @@ -38,7 +38,7 @@ impl<'a> ExecContext<'a> { env, diags: DiagSet::new(), tree: Tree { runs: vec![] }, - page: Some(PageInfo::new(&state, Softness::Hard)), + page: Some(PageInfo::new(&state, true)), stack: NodeStack::new(&state), par: NodePar::new(&state), state, @@ -77,7 +77,8 @@ impl<'a> ExecContext<'a> { /// Push a layout node into the active paragraph. /// - /// Spacing nodes will be handled according to their [`Softness`]. + /// Spacing nodes will be handled according to their + /// [`softness`](NodeSpacing::softness). pub fn push(&mut self, node: impl Into) { push(&mut self.par.children, node.into()); } @@ -87,7 +88,7 @@ impl<'a> ExecContext<'a> { let em = self.state.font.font_size(); self.push(NodeSpacing { amount: self.state.par.word_spacing.resolve(em), - softness: Softness::Soft, + softness: 1, }); } @@ -109,15 +110,19 @@ impl<'a> ExecContext<'a> { /// Apply a forced line break. pub fn push_linebreak(&mut self) { - self.finish_par(); + let em = self.state.font.font_size(); + self.push_into_stack(NodeSpacing { + amount: self.state.par.leading.resolve(em), + softness: 2, + }); } /// Apply a forced paragraph break. pub fn push_parbreak(&mut self) { let em = self.state.font.font_size(); self.push_into_stack(NodeSpacing { - amount: self.state.par.par_spacing.resolve(em), - softness: Softness::Soft, + amount: self.state.par.spacing.resolve(em), + softness: 1, }); } @@ -163,11 +168,13 @@ impl<'a> ExecContext<'a> { NodeText { text, - aligns: self.state.aligns, dir: self.state.dirs.cross, - font_size: self.state.font.font_size(), + aligns: self.state.aligns, families: Rc::clone(&self.state.font.families), variant, + font_size: self.state.font.font_size(), + top_edge: self.state.font.top_edge, + bottom_edge: self.state.font.bottom_edge, } } @@ -192,12 +199,12 @@ impl<'a> ExecContext<'a> { } /// Finish the active page. - pub fn finish_page(&mut self, keep: bool, new_softness: Softness, source: Span) { + pub fn finish_page(&mut self, keep: bool, hard: bool, source: Span) { if let Some(info) = &mut self.page { - let info = mem::replace(info, PageInfo::new(&self.state, new_softness)); + let info = mem::replace(info, PageInfo::new(&self.state, hard)); let stack = self.finish_stack(); - if !stack.children.is_empty() || (keep && info.softness == Softness::Hard) { + if !stack.children.is_empty() || (keep && info.hard) { self.tree.runs.push(NodePages { size: info.size, child: NodePad { @@ -215,7 +222,7 @@ impl<'a> ExecContext<'a> { /// Finish execution and return the created layout tree. pub fn finish(mut self) -> Pass { assert!(self.page.is_some()); - self.finish_page(true, Softness::Soft, Span::default()); + self.finish_page(true, false, Span::default()); Pass::new(self.tree, self.diags) } } @@ -223,16 +230,18 @@ impl<'a> ExecContext<'a> { /// Push a node into a list, taking care of spacing softness. fn push(nodes: &mut Vec, node: Node) { if let Node::Spacing(spacing) = node { - if spacing.softness == Softness::Soft && nodes.is_empty() { + if nodes.is_empty() && spacing.softness > 0 { return; } if let Some(&Node::Spacing(other)) = nodes.last() { - if spacing.softness > other.softness { - nodes.pop(); - } else if spacing.softness == Softness::Soft { + if spacing.softness > 0 && spacing.softness >= other.softness { return; } + + if spacing.softness < other.softness { + nodes.pop(); + } } } @@ -242,7 +251,7 @@ fn push(nodes: &mut Vec, node: Node) { /// Remove trailing soft spacing from a node list. fn trim(nodes: &mut Vec) { if let Some(&Node::Spacing(spacing)) = nodes.last() { - if spacing.softness == Softness::Soft { + if spacing.softness > 0 { nodes.pop(); } } @@ -252,15 +261,15 @@ fn trim(nodes: &mut Vec) { struct PageInfo { size: Size, padding: Sides, - softness: Softness, + hard: bool, } impl PageInfo { - fn new(state: &State, softness: Softness) -> Self { + fn new(state: &State, hard: bool) -> Self { Self { size: state.page.size, padding: state.page.margins(), - softness, + hard, } } } @@ -281,7 +290,7 @@ impl NodePar { Self { dirs: state.dirs, aligns: state.aligns, - line_spacing: state.par.line_spacing.resolve(em), + line_spacing: state.par.leading.resolve(em), children: vec![], } } diff --git a/src/exec/mod.rs b/src/exec/mod.rs index 45abca021..35ffa2b6c 100644 --- a/src/exec/mod.rs +++ b/src/exec/mod.rs @@ -35,15 +35,6 @@ pub fn exec( ctx.finish() } -/// Defines how an item interacts with surrounding items. -#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)] -pub enum Softness { - /// A soft item can be skipped in some circumstances. - Soft, - /// A hard item is always retained. - Hard, -} - /// Execute a node. /// /// This manipulates active styling and document state and produces layout @@ -106,15 +97,15 @@ impl Exec for NodeRaw { ctx.set_monospace(); let em = ctx.state.font.font_size(); - let line_spacing = ctx.state.par.line_spacing.resolve(em); + let leading = ctx.state.par.leading.resolve(em); let mut children = vec![]; let mut newline = false; for line in &self.lines { if newline { children.push(layout::Node::Spacing(NodeSpacing { - amount: line_spacing, - softness: Softness::Soft, + amount: leading, + softness: 2, })); } diff --git a/src/exec/state.rs b/src/exec/state.rs index 65f26439d..aa2dde1c5 100644 --- a/src/exec/state.rs +++ b/src/exec/state.rs @@ -2,10 +2,9 @@ use std::rc::Rc; use fontdock::{fallback, FallbackTree, FontStretch, FontStyle, FontVariant, FontWeight}; -use crate::geom::{ - Align, Dir, LayoutAligns, LayoutDirs, Length, Linear, Relative, Sides, Size, -}; +use crate::geom::*; use crate::paper::{Paper, PaperClass, PAPER_A4}; +use crate::shaping::VerticalFontMetric; /// The evaluation state. #[derive(Debug, Clone, PartialEq)] @@ -77,20 +76,20 @@ impl Default for PageState { /// Defines paragraph properties. #[derive(Debug, Copy, Clone, PartialEq)] pub struct ParState { + /// The spacing between paragraphs (dependent on scaled font size). + pub spacing: Linear, + /// The spacing between lines (dependent on scaled font size). + pub leading: Linear, /// The spacing between words (dependent on scaled font size). pub word_spacing: Linear, - /// The spacing between lines (dependent on scaled font size). - pub line_spacing: Linear, - /// The spacing between paragraphs (dependent on scaled font size). - pub par_spacing: Linear, } impl Default for ParState { fn default() -> Self { Self { + spacing: Relative::new(1.0).into(), + leading: Relative::new(0.5).into(), word_spacing: Relative::new(0.25).into(), - line_spacing: Linear::ZERO, - par_spacing: Relative::new(0.5).into(), } } } @@ -106,6 +105,10 @@ pub struct FontState { pub size: Length, /// The linear to apply on the base font size. pub scale: Linear, + /// The top end of the text bounding box. + pub top_edge: VerticalFontMetric, + /// The bottom end of the text bounding box. + pub bottom_edge: VerticalFontMetric, /// Whether the strong toggle is active or inactive. This determines /// whether the next `*` adds or removes font weight. pub strong: bool, @@ -141,6 +144,8 @@ impl Default for FontState { stretch: FontStretch::Normal, }, size: Length::pt(11.0), + top_edge: VerticalFontMetric::CapHeight, + bottom_edge: VerticalFontMetric::Baseline, scale: Linear::ONE, strong: false, emph: false, diff --git a/src/export/pdf.rs b/src/export/pdf.rs index f86f977c5..43c58403e 100644 --- a/src/export/pdf.rs +++ b/src/export/pdf.rs @@ -180,6 +180,7 @@ impl<'a> PdfExporter<'a> { Element::Text(shaped) => { let mut text = content.text(); + // Check if we need to issue a font switching action. if shaped.face != face || shaped.font_size != size { face = shaped.face; diff --git a/src/layout/spacing.rs b/src/layout/spacing.rs index 4b564a2b5..2bcb7ac10 100644 --- a/src/layout/spacing.rs +++ b/src/layout/spacing.rs @@ -1,7 +1,6 @@ use std::fmt::{self, Debug, Formatter}; use super::*; -use crate::exec::Softness; /// A spacing node. #[derive(Copy, Clone, PartialEq)] @@ -10,13 +9,11 @@ pub struct NodeSpacing { pub amount: Length, /// Defines how spacing interacts with surrounding spacing. /// - /// Hard spacing assures that a fixed amount of spacing will always be - /// inserted. Soft spacing will be consumed by previous soft spacing or - /// neighbouring hard spacing and can be used to insert overridable spacing, - /// e.g. between words or paragraphs. - /// - /// This field is only used in evaluation, not in layouting. - pub softness: Softness, + /// Hard spacing (`softness = 0`) assures that a fixed amount of spacing + /// will always be inserted. Soft spacing (`softness >= 1`) will be consumed + /// by other spacing with lower softness and can be used to insert + /// overridable spacing, e.g. between words or paragraphs. + pub softness: u8, } impl Layout for NodeSpacing { @@ -27,10 +24,7 @@ impl Layout for NodeSpacing { impl Debug for NodeSpacing { fn fmt(&self, f: &mut Formatter) -> fmt::Result { - match self.softness { - Softness::Soft => write!(f, "Soft({})", self.amount), - Softness::Hard => write!(f, "Hard({})", self.amount), - } + write!(f, "Spacing({}, {})", self.amount, self.softness) } } diff --git a/src/layout/text.rs b/src/layout/text.rs index 256a6e6d5..7b4eb08ed 100644 --- a/src/layout/text.rs +++ b/src/layout/text.rs @@ -4,35 +4,41 @@ use std::rc::Rc; use fontdock::{FallbackTree, FontVariant}; use super::*; -use crate::shaping; +use crate::shaping::{shape, VerticalFontMetric}; /// A text node. #[derive(Clone, PartialEq)] pub struct NodeText { + /// The text. + pub text: String, /// The text direction. pub dir: Dir, /// How to align this text node in its parent. pub aligns: LayoutAligns, - /// The text. - pub text: String, - /// The font size. - pub font_size: Length, /// The families used for font fallback. pub families: Rc, /// The font variant, pub variant: FontVariant, + /// The font size. + pub font_size: Length, + /// The top end of the text bounding box. + pub top_edge: VerticalFontMetric, + /// The bottom end of the text bounding box. + pub bottom_edge: VerticalFontMetric, } impl Layout for NodeText { fn layout(&self, ctx: &mut LayoutContext, _: &Areas) -> Layouted { Layouted::Frame( - shaping::shape( + shape( &self.text, self.dir, - self.font_size, - &mut ctx.env.fonts, &self.families, self.variant, + self.font_size, + self.top_edge, + self.bottom_edge, + &mut ctx.env.fonts, ), self.aligns, ) diff --git a/src/library/font.rs b/src/library/font.rs index 460cfba16..ecc15d960 100644 --- a/src/library/font.rs +++ b/src/library/font.rs @@ -1,6 +1,7 @@ use fontdock::{FontStretch, FontStyle, FontWeight}; use super::*; +use crate::shaping::VerticalFontMetric; /// `font`: Configure the font. /// @@ -13,6 +14,8 @@ use super::*; /// - Font Style: `style`, of type `font-style`. /// - Font Weight: `weight`, of type `font-weight`. /// - 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`. /// - 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`. @@ -22,15 +25,15 @@ use super::*; /// if present. /// /// # Relevant types and constants -/// - Type `font-family-list` -/// - coerces from `string` -/// - coerces from `array` -/// - coerces from `font-family` /// - Type `font-family` /// - `serif` /// - `sans-serif` /// - `monospace` /// - coerces from `string` +/// - Type `font-family-list` +/// - coerces from `string` +/// - coerces from `array` +/// - coerces from `font-family` /// - Type `font-style` /// - `normal` /// - `italic` @@ -46,12 +49,20 @@ use super::*; /// - `extrabold` (800) /// - `black` (900) /// - coerces from `integer` +/// - Type `vertical-font-metric` +/// - `ascender` +/// - `cap-height` +/// - `x-height` +/// - `baseline` +/// - `descender` pub fn font(ctx: &mut EvalContext, args: &mut ValueArgs) -> Value { let size = args.find::(ctx); let list: Vec<_> = args.filter::(ctx).map(|f| f.to_string()).collect(); let style = args.get(ctx, "style"); let weight = args.get(ctx, "weight"); let stretch = args.get(ctx, "stretch"); + let top_edge = args.get(ctx, "top-edge"); + let bottom_edge = args.get(ctx, "bottom-edge"); let serif = args.get(ctx, "serif"); let sans_serif = args.get(ctx, "sans-serif"); let monospace = args.get(ctx, "monospace"); @@ -87,6 +98,14 @@ pub fn font(ctx: &mut EvalContext, args: &mut ValueArgs) -> Value { ctx.state.font.variant.stretch = stretch; } + if let Some(top_edge) = top_edge { + ctx.state.font.top_edge = top_edge; + } + + if let Some(bottom_edge) = bottom_edge { + ctx.state.font.bottom_edge = bottom_edge; + } + for (variant, arg) in &[ (FontFamily::Serif, &serif), (FontFamily::SansSerif, &sans_serif), @@ -185,3 +204,7 @@ typify! { }; }, } + +typify! { + VerticalFontMetric: "vertical font metric", +} diff --git a/src/library/mod.rs b/src/library/mod.rs index 09fda20f7..453eab26b 100644 --- a/src/library/mod.rs +++ b/src/library/mod.rs @@ -26,9 +26,9 @@ use std::fmt::{self, Display, Formatter}; use fontdock::{FontStyle, FontWeight}; use crate::eval::{Scope, ValueAny, ValueFunc}; -use crate::exec::Softness; use crate::layout::*; use crate::prelude::*; +use crate::shaping::VerticalFontMetric; /// Construct a scope containing all standard library definitions. pub fn new() -> Scope { @@ -81,6 +81,11 @@ pub fn new() -> Scope { set!(any: "bold", FontWeight::BOLD); set!(any: "extrabold", FontWeight::EXTRABOLD); set!(any: "black", FontWeight::BLACK); + set!(any: "ascender", VerticalFontMetric::Ascender); + set!(any: "cap-height", VerticalFontMetric::CapHeight); + set!(any: "x-height", VerticalFontMetric::XHeight); + set!(any: "baseline", VerticalFontMetric::Baseline); + set!(any: "descender", VerticalFontMetric::Descender); std } diff --git a/src/library/page.rs b/src/library/page.rs index 17021607d..f7d76eafb 100644 --- a/src/library/page.rs +++ b/src/library/page.rs @@ -95,13 +95,13 @@ pub fn page(ctx: &mut EvalContext, args: &mut ValueArgs) -> Value { } ctx.set_dirs(Gen::new(main, cross)); - ctx.finish_page(false, Softness::Hard, span); + ctx.finish_page(false, true, span); if let Some(body) = &body { // TODO: Restrict body to a single page? body.exec(ctx); ctx.state = snapshot; - ctx.finish_page(true, Softness::Soft, span); + ctx.finish_page(true, false, span); } }) } @@ -113,6 +113,6 @@ pub fn page(ctx: &mut EvalContext, args: &mut ValueArgs) -> Value { pub fn pagebreak(_: &mut EvalContext, args: &mut ValueArgs) -> Value { let span = args.span; Value::template("pagebreak", move |ctx| { - ctx.finish_page(true, Softness::Hard, span); + ctx.finish_page(true, true, span); }) } diff --git a/src/library/spacing.rs b/src/library/spacing.rs index d8f18f419..4965a2202 100644 --- a/src/library/spacing.rs +++ b/src/library/spacing.rs @@ -28,7 +28,7 @@ fn spacing(ctx: &mut EvalContext, args: &mut ValueArgs, axis: SpecAxis) -> Value Value::template("spacing", move |ctx| { if let Some(linear) = spacing { let amount = linear.resolve(ctx.state.font.font_size()); - let spacing = NodeSpacing { amount, softness: Softness::Hard }; + let spacing = NodeSpacing { amount, softness: 0 }; if axis == ctx.state.dirs.main.axis() { ctx.push_into_stack(spacing); } else { diff --git a/src/shaping.rs b/src/shaping.rs index a8d2b2bf1..41119639f 100644 --- a/src/shaping.rs +++ b/src/shaping.rs @@ -4,7 +4,7 @@ //! font for each individual character. When the direction is right-to-left, the //! word is spelled backwards. Vertical shaping is not supported. -use std::fmt::{self, Debug, Formatter}; +use std::fmt::{self, Debug, Display, Formatter}; use fontdock::{FaceId, FaceQuery, FallbackTree, FontVariant}; use ttf_parser::{Face, GlyphId}; @@ -58,20 +58,55 @@ impl Debug for Shaped { } } +/// Identifies a vertical metric of a font. +#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)] +pub enum VerticalFontMetric { + /// The distance from the baseline to the typographic ascender. + /// + /// Corresponds to the typographic ascender from the `OS/2` table if present + /// and falls back to the ascender from the `hhea` table otherwise. + Ascender, + /// The approximate height of uppercase letters. + CapHeight, + /// The approximate height of non-ascending lowercase letters. + XHeight, + /// The baseline on which the letters rest. + Baseline, + /// The distance from the baseline to the typographic descender. + /// + /// Corresponds to the typographic descender from the `OS/2` table if + /// present and falls back to the descender from the `hhea` table otherwise. + Descender, +} + +impl Display for VerticalFontMetric { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + f.pad(match self { + Self::Ascender => "ascender", + Self::CapHeight => "cap-height", + Self::XHeight => "x-height", + Self::Baseline => "baseline", + Self::Descender => "descender", + }) + } +} + /// Shape text into a frame containing [`Shaped`] runs. pub fn shape( text: &str, dir: Dir, - font_size: Length, - loader: &mut FontLoader, fallback: &FallbackTree, variant: FontVariant, + font_size: Length, + top_edge: VerticalFontMetric, + bottom_edge: VerticalFontMetric, + loader: &mut FontLoader, ) -> Frame { - let mut frame = Frame::new(Size::new(Length::ZERO, font_size)); + let mut frame = Frame::new(Size::new(Length::ZERO, Length::ZERO)); let mut shaped = Shaped::new(FaceId::MAX, font_size); - let mut offset = Length::ZERO; - let mut ascender = Length::ZERO; - let mut descender = Length::ZERO; + let mut width = Length::ZERO; + let mut top = Length::ZERO; + let mut bottom = Length::ZERO; // Create an iterator with conditional direction. let mut forwards = text.chars(); @@ -86,7 +121,7 @@ pub fn shape( let query = FaceQuery { fallback: fallback.iter(), variant, c }; if let Some(id) = loader.query(query) { let face = loader.face(id).get(); - let (glyph, width) = match lookup_glyph(face, c) { + let (glyph, glyph_width) = match lookup_glyph(face, c) { Some(v) => v, None => continue, }; @@ -96,27 +131,35 @@ pub fn shape( // Flush the buffer and reset the metrics if we use a new font face. if shaped.face != id { - place(&mut frame, shaped, offset, ascender, descender); + place(&mut frame, shaped, width, top, bottom); shaped = Shaped::new(id, font_size); - offset = Length::ZERO; - ascender = convert(f64::from(face.ascender())); - descender = convert(f64::from(face.descender())); + width = Length::ZERO; + top = convert(f64::from(lookup_metric(face, top_edge))); + bottom = convert(f64::from(lookup_metric(face, bottom_edge))); } shaped.text.push(c); shaped.glyphs.push(glyph); - shaped.offsets.push(offset); - offset += convert(f64::from(width)); + shaped.offsets.push(width); + width += convert(f64::from(glyph_width)); } } - // Flush the last buffered parts of the word. - place(&mut frame, shaped, offset, ascender, descender); + place(&mut frame, shaped, width, top, bottom); frame } +/// Place shaped text into a frame. +fn place(frame: &mut Frame, shaped: Shaped, width: Length, top: Length, bottom: Length) { + if !shaped.text.is_empty() { + frame.push(Point::new(frame.size.width, top), Element::Text(shaped)); + frame.size.width += width; + frame.size.height = frame.size.height.max(top - bottom); + } +} + /// Look up the glyph for `c` and returns its index alongside its advance width. fn lookup_glyph(face: &Face, c: char) -> Option<(GlyphId, u16)> { let glyph = face.glyph_index(c)?; @@ -124,18 +167,32 @@ fn lookup_glyph(face: &Face, c: char) -> Option<(GlyphId, u16)> { Some((glyph, width)) } -/// Place shaped text into a frame. -fn place( - frame: &mut Frame, - shaped: Shaped, - offset: Length, - ascender: Length, - descender: Length, -) { - if !shaped.text.is_empty() { - let pos = Point::new(frame.size.width, ascender); - frame.push(pos, Element::Text(shaped)); - frame.size.width += offset; - frame.size.height = frame.size.height.max(ascender - descender); +/// Look up a vertical metric. +fn lookup_metric(face: &Face, metric: VerticalFontMetric) -> i16 { + match metric { + VerticalFontMetric::Ascender => lookup_ascender(face), + VerticalFontMetric::CapHeight => face + .capital_height() + .filter(|&h| h > 0) + .unwrap_or_else(|| lookup_ascender(face)), + VerticalFontMetric::XHeight => face + .x_height() + .filter(|&h| h > 0) + .unwrap_or_else(|| lookup_ascender(face)), + VerticalFontMetric::Baseline => 0, + VerticalFontMetric::Descender => lookup_descender(face), } } + +/// The ascender of the face. +fn lookup_ascender(face: &Face) -> i16 { + // We prefer the typographic ascender over the Windows ascender because + // it can be overly large if the font has large glyphs. + face.typographic_ascender().unwrap_or_else(|| face.ascender()) +} + +/// The descender of the face. +fn lookup_descender(face: &Face) -> i16 { + // See `lookup_ascender` for reason. + face.typographic_descender().unwrap_or_else(|| face.descender()) +} diff --git a/tests/ref/comment.png b/tests/ref/comment.png index 1fad97fe8..6d5723efd 100644 Binary files a/tests/ref/comment.png and b/tests/ref/comment.png differ diff --git a/tests/ref/control/for.png b/tests/ref/control/for.png index d04ea62cf..f3830b341 100644 Binary files a/tests/ref/control/for.png and b/tests/ref/control/for.png differ diff --git a/tests/ref/control/if.png b/tests/ref/control/if.png index 99bde49e0..0527754e2 100644 Binary files a/tests/ref/control/if.png and b/tests/ref/control/if.png differ diff --git a/tests/ref/control/invalid.png b/tests/ref/control/invalid.png index ff4c69875..e9c64b8a4 100644 Binary files a/tests/ref/control/invalid.png and b/tests/ref/control/invalid.png differ diff --git a/tests/ref/control/let.png b/tests/ref/control/let.png index 5fb1c992f..20aad953a 100644 Binary files a/tests/ref/control/let.png and b/tests/ref/control/let.png differ diff --git a/tests/ref/control/while.png b/tests/ref/control/while.png index 890131f1b..2ea1bcb0a 100644 Binary files a/tests/ref/control/while.png and b/tests/ref/control/while.png differ diff --git a/tests/ref/expand.png b/tests/ref/expand.png index fc1f5de9f..f18e929b0 100644 Binary files a/tests/ref/expand.png and b/tests/ref/expand.png differ diff --git a/tests/ref/expr/array.png b/tests/ref/expr/array.png index 784553b3f..b1e203484 100644 Binary files a/tests/ref/expr/array.png and b/tests/ref/expr/array.png differ diff --git a/tests/ref/expr/block-invalid.png b/tests/ref/expr/block-invalid.png index b380defc9..d1e854025 100644 Binary files a/tests/ref/expr/block-invalid.png and b/tests/ref/expr/block-invalid.png differ diff --git a/tests/ref/expr/block.png b/tests/ref/expr/block.png index d5ad52ae9..d8ea84fb9 100644 Binary files a/tests/ref/expr/block.png and b/tests/ref/expr/block.png differ diff --git a/tests/ref/expr/call-invalid.png b/tests/ref/expr/call-invalid.png index 88f137804..eb192357d 100644 Binary files a/tests/ref/expr/call-invalid.png and b/tests/ref/expr/call-invalid.png differ diff --git a/tests/ref/expr/call.png b/tests/ref/expr/call.png index 7197bb908..26f1cb03b 100644 Binary files a/tests/ref/expr/call.png and b/tests/ref/expr/call.png differ diff --git a/tests/ref/expr/dict.png b/tests/ref/expr/dict.png index 28a7cb3bc..1caef1e0a 100644 Binary files a/tests/ref/expr/dict.png and b/tests/ref/expr/dict.png differ diff --git a/tests/ref/expr/ops.png b/tests/ref/expr/ops.png index 95a24c0ed..2f3a28ea1 100644 Binary files a/tests/ref/expr/ops.png and b/tests/ref/expr/ops.png differ diff --git a/tests/ref/full/coma.png b/tests/ref/full/coma.png index f5db682d5..5fd6d801c 100644 Binary files a/tests/ref/full/coma.png and b/tests/ref/full/coma.png differ diff --git a/tests/ref/library/font.png b/tests/ref/library/font.png index 8e611b16a..199a15f2c 100644 Binary files a/tests/ref/library/font.png and b/tests/ref/library/font.png differ diff --git a/tests/ref/library/image.png b/tests/ref/library/image.png index af497c8d6..0a45e11b6 100644 Binary files a/tests/ref/library/image.png and b/tests/ref/library/image.png differ diff --git a/tests/ref/library/pad.png b/tests/ref/library/pad.png index a655061ba..3536db5d1 100644 Binary files a/tests/ref/library/pad.png and b/tests/ref/library/pad.png differ diff --git a/tests/ref/library/page.png b/tests/ref/library/page.png index 1537fad47..7d1ff96f7 100644 Binary files a/tests/ref/library/page.png and b/tests/ref/library/page.png differ diff --git a/tests/ref/library/pagebreak.png b/tests/ref/library/pagebreak.png index b1ee0eef2..f0052f9e1 100644 Binary files a/tests/ref/library/pagebreak.png and b/tests/ref/library/pagebreak.png differ diff --git a/tests/ref/library/shapes.png b/tests/ref/library/shapes.png index 8ace6fd6d..a244feb1a 100644 Binary files a/tests/ref/library/shapes.png and b/tests/ref/library/shapes.png differ diff --git a/tests/ref/library/spacing.png b/tests/ref/library/spacing.png index 0842d4093..6a234a8a1 100644 Binary files a/tests/ref/library/spacing.png and b/tests/ref/library/spacing.png differ diff --git a/tests/ref/markup/emph.png b/tests/ref/markup/emph.png index f002610e9..75231a62c 100644 Binary files a/tests/ref/markup/emph.png and b/tests/ref/markup/emph.png differ diff --git a/tests/ref/markup/escape.png b/tests/ref/markup/escape.png index 3fa44c6dd..8e946279e 100644 Binary files a/tests/ref/markup/escape.png and b/tests/ref/markup/escape.png differ diff --git a/tests/ref/markup/heading.png b/tests/ref/markup/heading.png index 9f2ce6ece..3473f798a 100644 Binary files a/tests/ref/markup/heading.png and b/tests/ref/markup/heading.png differ diff --git a/tests/ref/markup/linebreak.png b/tests/ref/markup/linebreak.png index fec65248d..4e4702f9b 100644 Binary files a/tests/ref/markup/linebreak.png and b/tests/ref/markup/linebreak.png differ diff --git a/tests/ref/markup/nbsp.png b/tests/ref/markup/nbsp.png index 3e1f0ee74..89f75f145 100644 Binary files a/tests/ref/markup/nbsp.png and b/tests/ref/markup/nbsp.png differ diff --git a/tests/ref/markup/raw.png b/tests/ref/markup/raw.png index ce8334284..59a4e3dd6 100644 Binary files a/tests/ref/markup/raw.png and b/tests/ref/markup/raw.png differ diff --git a/tests/ref/markup/strong.png b/tests/ref/markup/strong.png index ac38c4369..7e62f0f3f 100644 Binary files a/tests/ref/markup/strong.png and b/tests/ref/markup/strong.png differ diff --git a/tests/ref/repr.png b/tests/ref/repr.png index 2f5697fd5..ad10705ca 100644 Binary files a/tests/ref/repr.png and b/tests/ref/repr.png differ diff --git a/tests/ref/spacing.png b/tests/ref/spacing.png index 8bdc98850..454b154fd 100644 Binary files a/tests/ref/spacing.png and b/tests/ref/spacing.png differ diff --git a/tests/ref/text.png b/tests/ref/text.png index 38ae364d0..1dd70c1cd 100644 Binary files a/tests/ref/text.png and b/tests/ref/text.png differ diff --git a/tests/typ/control/let.typ b/tests/typ/control/let.typ index dd971ee5e..4f84aa670 100644 --- a/tests/typ/control/let.typ +++ b/tests/typ/control/let.typ @@ -17,8 +17,8 @@ --- // Syntax sugar for function definitions. -#let background = #239dad -#let rect(body) = rect(width: 2cm, height: 1cm, fill: background, body) +#let background = #9feb52 +#let rect(body) = rect(width: 2cm, fill: background, pad(5pt, body)) #rect[Hi!] // Error: 13 expected body diff --git a/tests/typ/full/coma.typ b/tests/typ/full/coma.typ index 243c667f9..619843d76 100644 --- a/tests/typ/full/coma.typ +++ b/tests/typ/full/coma.typ @@ -30,8 +30,8 @@ // the parentheses. #align(center)[ // Markdown-like syntax for headings. - ==== 3. Übungsblatt Computerorientierte Mathematik II #v(2mm) - *Abgabe: 03.05.2019* (bis 10:10 Uhr in MA 001) #v(2mm) + ==== 3. Übungsblatt Computerorientierte Mathematik II #v(4mm) + *Abgabe: 03.05.2019* (bis 10:10 Uhr in MA 001) #v(4mm) *Alle Antworten sind zu beweisen.* ] diff --git a/tests/typ/library/font.typ b/tests/typ/library/font.typ index c00bff5af..ecea303f7 100644 --- a/tests/typ/library/font.typ +++ b/tests/typ/library/font.typ @@ -31,6 +31,20 @@ Emoji: 🐪, 🌋, 🏞 ∫ 𝛼 + 3𝛽 d𝑡 ] +--- +// Test top and bottom edge. + +#page(width: 170pt) +#let try(top, bottom) = rect(fill: #9feb52)[ + #font(top-edge: top, bottom-edge: bottom) + `From `#top` to `#bottom +] + +#try(ascender, descender) +#try(ascender, baseline) +#try(cap-height, baseline) +#try(x-height, baseline) + --- // Ref: false diff --git a/tests/typ/library/spacing.typ b/tests/typ/library/spacing.typ index a34840c67..6d50f0dcf 100644 --- a/tests/typ/library/spacing.typ +++ b/tests/typ/library/spacing.typ @@ -2,7 +2,7 @@ --- // Ends paragraphs. -Tightly #v(-5pt) packed +Tightly #v(0pt) packed // Eating up soft spacing. Inv #h(0pt) isible diff --git a/tests/typ/markup/escape.typ b/tests/typ/markup/escape.typ index eeac4997f..b0a809f96 100644 --- a/tests/typ/markup/escape.typ +++ b/tests/typ/markup/escape.typ @@ -28,3 +28,7 @@ // Unterminated. // Error: 6 expected closing brace \u{41*Bold* + +--- +// Some code stuff in text. +let f() , ; : | + - /= == 12 "string" diff --git a/tests/typ/markup/raw.typ b/tests/typ/markup/raw.typ index f5074c8ee..005d413ed 100644 --- a/tests/typ/markup/raw.typ +++ b/tests/typ/markup/raw.typ @@ -7,7 +7,7 @@ --- // Typst syntax inside. `#let x = 1` \ -`#[f 1]` +`#f(1)` --- // Multiline block splits paragraphs. diff --git a/tests/typ/text.typ b/tests/typ/text.typ index d86f48957..b424cee82 100644 --- a/tests/typ/text.typ +++ b/tests/typ/text.typ @@ -1,8 +1,19 @@ // Test simple text. ---- -Hello 🌏! +#page(width: 250pt) ---- -// Some code stuff in text. -let f() , ; : | + - /= == 12 "string" +But, soft! what light through yonder window breaks? It is the east, and Juliet +is the sun. Arise, fair sun, and kill the envious moon, Who is already sick and +pale with grief, That thou her maid art far more fair than she: Be not her maid, +since she is envious; Her vestal livery is but sick and green And none but fools +do wear it; cast it off. It is my lady, O, it is my love! O, that she knew she +were! She speaks yet she says nothing: what of that? Her eye discourses; I will +answer it. + +I am too bold, 'tis not to me she speaks: Two of the fairest stars in all the +heaven, Having some business, do entreat her eyes To twinkle in their spheres +till they return. What if her eyes were there, they in her head? The brightness +of her cheek would shame those stars, As daylight doth a lamp; her eyes in +heaven Would through the airy region stream so bright That birds would sing and +think it were not night. See, how she leans her cheek upon her hand! O, that I +were a glove upon that hand, That I might touch that cheek!