mirror of
https://github.com/typst/typst
synced 2025-05-14 17:15:28 +08:00
Castable optional and smart values
This commit is contained in:
parent
cef46e6c40
commit
ed50661378
@ -252,43 +252,6 @@ pub trait Cast<V>: Sized {
|
|||||||
fn cast(value: V) -> StrResult<Self>;
|
fn cast(value: V) -> StrResult<Self>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Cast<Value> for Value {
|
|
||||||
fn is(_: &Value) -> bool {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
fn cast(value: Value) -> StrResult<Self> {
|
|
||||||
Ok(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Cast<Spanned<Value>> for T
|
|
||||||
where
|
|
||||||
T: Cast<Value>,
|
|
||||||
{
|
|
||||||
fn is(value: &Spanned<Value>) -> bool {
|
|
||||||
T::is(&value.v)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn cast(value: Spanned<Value>) -> StrResult<Self> {
|
|
||||||
T::cast(value.v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Cast<Spanned<Value>> for Spanned<T>
|
|
||||||
where
|
|
||||||
T: Cast<Value>,
|
|
||||||
{
|
|
||||||
fn is(value: &Spanned<Value>) -> bool {
|
|
||||||
T::is(&value.v)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn cast(value: Spanned<Value>) -> StrResult<Self> {
|
|
||||||
let span = value.span;
|
|
||||||
T::cast(value.v).map(|t| Spanned::new(t, span))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Implement traits for primitives.
|
/// Implement traits for primitives.
|
||||||
macro_rules! primitive {
|
macro_rules! primitive {
|
||||||
(
|
(
|
||||||
@ -400,6 +363,113 @@ primitive! { Dict: "dictionary", Dict }
|
|||||||
primitive! { Template: "template", Template }
|
primitive! { Template: "template", Template }
|
||||||
primitive! { Function: "function", Func }
|
primitive! { Function: "function", Func }
|
||||||
|
|
||||||
|
impl Cast<Value> for Value {
|
||||||
|
fn is(_: &Value) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cast(value: Value) -> StrResult<Self> {
|
||||||
|
Ok(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Cast<Spanned<Value>> for T
|
||||||
|
where
|
||||||
|
T: Cast<Value>,
|
||||||
|
{
|
||||||
|
fn is(value: &Spanned<Value>) -> bool {
|
||||||
|
T::is(&value.v)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cast(value: Spanned<Value>) -> StrResult<Self> {
|
||||||
|
T::cast(value.v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Cast<Spanned<Value>> for Spanned<T>
|
||||||
|
where
|
||||||
|
T: Cast<Value>,
|
||||||
|
{
|
||||||
|
fn is(value: &Spanned<Value>) -> bool {
|
||||||
|
T::is(&value.v)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cast(value: Spanned<Value>) -> StrResult<Self> {
|
||||||
|
let span = value.span;
|
||||||
|
T::cast(value.v).map(|t| Spanned::new(t, span))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A value that can be automatically determined.
|
||||||
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||||
|
pub enum Smart<T> {
|
||||||
|
/// The value should be determined smartly based on the
|
||||||
|
/// circumstances.
|
||||||
|
Auto,
|
||||||
|
/// A forced, specific value.
|
||||||
|
Custom(T),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Smart<T> {
|
||||||
|
/// Returns the contained custom value or a provided default value.
|
||||||
|
pub fn unwrap_or(self, default: T) -> T {
|
||||||
|
match self {
|
||||||
|
Self::Auto => default,
|
||||||
|
Self::Custom(x) => x,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Default for Smart<T> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::Auto
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Cast<Value> for Option<T>
|
||||||
|
where
|
||||||
|
T: Cast<Value>,
|
||||||
|
{
|
||||||
|
fn is(value: &Value) -> bool {
|
||||||
|
matches!(value, Value::None) || T::is(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cast(value: Value) -> StrResult<Self> {
|
||||||
|
match value {
|
||||||
|
Value::None => Ok(None),
|
||||||
|
v => T::cast(v).map(Some).map_err(|msg| with_alternative(msg, "none")),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Cast<Value> for Smart<T>
|
||||||
|
where
|
||||||
|
T: Cast<Value>,
|
||||||
|
{
|
||||||
|
fn is(value: &Value) -> bool {
|
||||||
|
matches!(value, Value::Auto) || T::is(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cast(value: Value) -> StrResult<Self> {
|
||||||
|
match value {
|
||||||
|
Value::Auto => Ok(Self::Auto),
|
||||||
|
v => T::cast(v)
|
||||||
|
.map(Self::Custom)
|
||||||
|
.map_err(|msg| with_alternative(msg, "auto")),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Transform `expected X, found Y` into `expected X or A, found Y`.
|
||||||
|
fn with_alternative(msg: String, alt: &str) -> String {
|
||||||
|
let mut parts = msg.split(", found ");
|
||||||
|
if let (Some(a), Some(b)) = (parts.next(), parts.next()) {
|
||||||
|
format!("{} or {}, found {}", a, alt, b)
|
||||||
|
} else {
|
||||||
|
msg
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
@ -17,7 +17,7 @@ pub fn overline(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn line_impl(args: &mut Args, kind: LineKind) -> TypResult<Value> {
|
fn line_impl(args: &mut Args, kind: LineKind) -> TypResult<Value> {
|
||||||
let stroke = args.named("stroke")?.or_else(|| args.find()).map(Paint::Solid);
|
let stroke = args.named("stroke")?.or_else(|| args.find());
|
||||||
let thickness = args.named::<Linear>("thickness")?.or_else(|| args.find());
|
let thickness = args.named::<Linear>("thickness")?.or_else(|| args.find());
|
||||||
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();
|
||||||
|
@ -26,7 +26,7 @@ mod prelude {
|
|||||||
pub use std::rc::Rc;
|
pub use std::rc::Rc;
|
||||||
|
|
||||||
pub use crate::diag::{At, TypResult};
|
pub use crate::diag::{At, TypResult};
|
||||||
pub use crate::eval::{Args, EvalContext, Template, Value};
|
pub use crate::eval::{Args, EvalContext, Smart, Template, Value};
|
||||||
pub use crate::frame::*;
|
pub use crate::frame::*;
|
||||||
pub use crate::geom::*;
|
pub use crate::geom::*;
|
||||||
pub use crate::layout::*;
|
pub use crate::layout::*;
|
||||||
@ -144,3 +144,9 @@ dynamic! {
|
|||||||
FontFamily: "font family",
|
FontFamily: "font family",
|
||||||
Value::Str(string) => Self::Named(string.to_lowercase()),
|
Value::Str(string) => Self::Named(string.to_lowercase()),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
castable! {
|
||||||
|
Paint,
|
||||||
|
Expected: "color",
|
||||||
|
Value::Color(color) => Paint::Solid(color),
|
||||||
|
}
|
||||||
|
@ -12,13 +12,13 @@ pub fn page(ctx: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
|
|||||||
let paper = args.named::<Paper>("paper")?.or_else(|| args.find());
|
let paper = args.named::<Paper>("paper")?.or_else(|| args.find());
|
||||||
let width = args.named("width")?;
|
let width = args.named("width")?;
|
||||||
let height = args.named("height")?;
|
let height = args.named("height")?;
|
||||||
|
let flip = args.named("flip")?;
|
||||||
let margins = args.named("margins")?;
|
let margins = args.named("margins")?;
|
||||||
let left = args.named("left")?;
|
let left = args.named("left")?;
|
||||||
let top = args.named("top")?;
|
let top = args.named("top")?;
|
||||||
let right = args.named("right")?;
|
let right = args.named("right")?;
|
||||||
let bottom = args.named("bottom")?;
|
let bottom = args.named("bottom")?;
|
||||||
let flip = args.named("flip")?;
|
let fill = args.named("fill")?;
|
||||||
let fill = args.named("fill")?.map(Paint::Solid);
|
|
||||||
|
|
||||||
ctx.template.modify(move |style| {
|
ctx.template.modify(move |style| {
|
||||||
let page = style.page_mut();
|
let page = style.page_mut();
|
||||||
@ -33,37 +33,37 @@ pub fn page(ctx: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
|
|||||||
page.size.w = width;
|
page.size.w = width;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if flip.unwrap_or(false) {
|
||||||
|
std::mem::swap(&mut page.size.w, &mut page.size.h);
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(height) = height {
|
if let Some(height) = height {
|
||||||
page.class = PaperClass::Custom;
|
page.class = PaperClass::Custom;
|
||||||
page.size.h = height;
|
page.size.h = height;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(margins) = margins {
|
if let Some(margins) = margins {
|
||||||
page.margins = Sides::splat(Some(margins));
|
page.margins = Sides::splat(margins);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(left) = left {
|
if let Some(left) = left {
|
||||||
page.margins.left = Some(left);
|
page.margins.left = left;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(top) = top {
|
if let Some(top) = top {
|
||||||
page.margins.top = Some(top);
|
page.margins.top = top;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(right) = right {
|
if let Some(right) = right {
|
||||||
page.margins.right = Some(right);
|
page.margins.right = right;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(bottom) = bottom {
|
if let Some(bottom) = bottom {
|
||||||
page.margins.bottom = Some(bottom);
|
page.margins.bottom = bottom;
|
||||||
}
|
|
||||||
|
|
||||||
if flip.unwrap_or(false) {
|
|
||||||
std::mem::swap(&mut page.size.w, &mut page.size.h);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(fill) = fill {
|
if let Some(fill) = fill {
|
||||||
page.fill = Some(fill);
|
page.fill = fill;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -58,11 +58,11 @@ fn shape_impl(
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Parse fill & stroke.
|
// Parse fill & stroke.
|
||||||
let fill = args.named("fill")?.map(Paint::Solid);
|
let fill = args.named("fill")?.unwrap_or(None);
|
||||||
let stroke = match (args.named("stroke")?, args.named("thickness")?) {
|
let stroke = match (args.named("stroke")?, args.named("thickness")?) {
|
||||||
(None, None) => fill.is_none().then(|| default),
|
(None, None) => fill.is_none().then(|| default),
|
||||||
(color, thickness) => Some(Stroke {
|
(color, thickness) => color.unwrap_or(Some(default.paint)).map(|paint| Stroke {
|
||||||
paint: color.map(Paint::Solid).unwrap_or(default.paint),
|
paint,
|
||||||
thickness: thickness.unwrap_or(default.thickness),
|
thickness: thickness.unwrap_or(default.thickness),
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
@ -93,18 +93,16 @@ pub fn font(ctx: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
|
|||||||
|
|
||||||
castable! {
|
castable! {
|
||||||
StylisticSet,
|
StylisticSet,
|
||||||
Expected: "none or integer",
|
Expected: "integer",
|
||||||
Value::None => Self(None),
|
|
||||||
Value::Int(v) => match v {
|
Value::Int(v) => match v {
|
||||||
1 ..= 20 => Self(Some(v as u8)),
|
1 ..= 20 => Self::new(v as u8),
|
||||||
_ => Err("must be between 1 and 20")?,
|
_ => Err("must be between 1 and 20")?,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
castable! {
|
castable! {
|
||||||
NumberType,
|
NumberType,
|
||||||
Expected: "auto or string",
|
Expected: "string",
|
||||||
Value::Auto => Self::Auto,
|
|
||||||
Value::Str(string) => match string.as_str() {
|
Value::Str(string) => match string.as_str() {
|
||||||
"lining" => Self::Lining,
|
"lining" => Self::Lining,
|
||||||
"old-style" => Self::OldStyle,
|
"old-style" => Self::OldStyle,
|
||||||
@ -114,8 +112,7 @@ pub fn font(ctx: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
|
|||||||
|
|
||||||
castable! {
|
castable! {
|
||||||
NumberWidth,
|
NumberWidth,
|
||||||
Expected: "auto or string",
|
Expected: "string",
|
||||||
Value::Auto => Self::Auto,
|
|
||||||
Value::Str(string) => match string.as_str() {
|
Value::Str(string) => match string.as_str() {
|
||||||
"proportional" => Self::Proportional,
|
"proportional" => Self::Proportional,
|
||||||
"tabular" => Self::Tabular,
|
"tabular" => Self::Tabular,
|
||||||
@ -629,8 +626,8 @@ fn tags(features: &FontFeatures) -> Vec<Feature> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let storage;
|
let storage;
|
||||||
if let StylisticSet(Some(set @ 1 ..= 20)) = features.stylistic_set {
|
if let Some(set) = features.stylistic_set {
|
||||||
storage = [b's', b's', b'0' + set / 10, b'0' + set % 10];
|
storage = [b's', b's', b'0' + set.get() / 10, b'0' + set.get() % 10];
|
||||||
feat(&storage, 1);
|
feat(&storage, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -648,15 +645,15 @@ fn tags(features: &FontFeatures) -> Vec<Feature> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
match features.numbers.type_ {
|
match features.numbers.type_ {
|
||||||
NumberType::Auto => {}
|
Smart::Auto => {}
|
||||||
NumberType::Lining => feat(b"lnum", 1),
|
Smart::Custom(NumberType::Lining) => feat(b"lnum", 1),
|
||||||
NumberType::OldStyle => feat(b"onum", 1),
|
Smart::Custom(NumberType::OldStyle) => feat(b"onum", 1),
|
||||||
}
|
}
|
||||||
|
|
||||||
match features.numbers.width {
|
match features.numbers.width {
|
||||||
NumberWidth::Auto => {}
|
Smart::Auto => {}
|
||||||
NumberWidth::Proportional => feat(b"pnum", 1),
|
Smart::Custom(NumberWidth::Proportional) => feat(b"pnum", 1),
|
||||||
NumberWidth::Tabular => feat(b"tnum", 1),
|
Smart::Custom(NumberWidth::Tabular) => feat(b"tnum", 1),
|
||||||
}
|
}
|
||||||
|
|
||||||
match features.numbers.position {
|
match features.numbers.position {
|
||||||
|
@ -9,6 +9,7 @@ use std::rc::Rc;
|
|||||||
|
|
||||||
use ttf_parser::Tag;
|
use ttf_parser::Tag;
|
||||||
|
|
||||||
|
use crate::eval::Smart;
|
||||||
use crate::font::*;
|
use crate::font::*;
|
||||||
use crate::geom::*;
|
use crate::geom::*;
|
||||||
use crate::util::EcoString;
|
use crate::util::EcoString;
|
||||||
@ -70,7 +71,7 @@ pub struct PageStyle {
|
|||||||
pub size: Size,
|
pub size: Size,
|
||||||
/// The amount of white space on each side of the page. If a side is set to
|
/// The amount of white space on each side of the page. If a side is set to
|
||||||
/// `None`, the default for the paper class is used.
|
/// `None`, the default for the paper class is used.
|
||||||
pub margins: Sides<Option<Linear>>,
|
pub margins: Sides<Smart<Linear>>,
|
||||||
/// The background fill of the page.
|
/// The background fill of the page.
|
||||||
pub fill: Option<Paint>,
|
pub fill: Option<Paint>,
|
||||||
}
|
}
|
||||||
@ -94,7 +95,7 @@ impl Default for PageStyle {
|
|||||||
Self {
|
Self {
|
||||||
class: paper.class(),
|
class: paper.class(),
|
||||||
size: paper.size(),
|
size: paper.size(),
|
||||||
margins: Sides::splat(None),
|
margins: Sides::splat(Smart::Auto),
|
||||||
fill: None,
|
fill: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -301,7 +302,7 @@ pub struct FontFeatures {
|
|||||||
/// Whether to apply stylistic alternates. ("salt")
|
/// Whether to apply stylistic alternates. ("salt")
|
||||||
pub alternates: bool,
|
pub alternates: bool,
|
||||||
/// Which stylistic set to apply. ("ss01" - "ss20")
|
/// Which stylistic set to apply. ("ss01" - "ss20")
|
||||||
pub stylistic_set: StylisticSet,
|
pub stylistic_set: Option<StylisticSet>,
|
||||||
/// Configuration of ligature features.
|
/// Configuration of ligature features.
|
||||||
pub ligatures: LigatureFeatures,
|
pub ligatures: LigatureFeatures,
|
||||||
/// Configuration of numbers features.
|
/// Configuration of numbers features.
|
||||||
@ -316,7 +317,7 @@ impl Default for FontFeatures {
|
|||||||
kerning: true,
|
kerning: true,
|
||||||
smallcaps: false,
|
smallcaps: false,
|
||||||
alternates: false,
|
alternates: false,
|
||||||
stylistic_set: StylisticSet::default(),
|
stylistic_set: None,
|
||||||
ligatures: LigatureFeatures::default(),
|
ligatures: LigatureFeatures::default(),
|
||||||
numbers: NumberFeatures::default(),
|
numbers: NumberFeatures::default(),
|
||||||
raw: vec![],
|
raw: vec![],
|
||||||
@ -326,11 +327,17 @@ impl Default for FontFeatures {
|
|||||||
|
|
||||||
/// A stylistic set in a font face.
|
/// A stylistic set in a font face.
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||||
pub struct StylisticSet(pub Option<u8>);
|
pub struct StylisticSet(u8);
|
||||||
|
|
||||||
impl Default for StylisticSet {
|
impl StylisticSet {
|
||||||
fn default() -> Self {
|
/// Creates a new set, clamping to 1-20.
|
||||||
Self(None)
|
pub fn new(index: u8) -> Self {
|
||||||
|
Self(index.clamp(1, 20))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the value, guaranteed to be 1-20.
|
||||||
|
pub fn get(self) -> u8 {
|
||||||
|
self.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -359,9 +366,9 @@ impl Default for LigatureFeatures {
|
|||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||||
pub struct NumberFeatures {
|
pub struct NumberFeatures {
|
||||||
/// Whether to use lining or old-style numbers.
|
/// Whether to use lining or old-style numbers.
|
||||||
pub type_: NumberType,
|
pub type_: Smart<NumberType>,
|
||||||
/// Whether to use proportional or tabular numbers.
|
/// Whether to use proportional or tabular numbers.
|
||||||
pub width: NumberWidth,
|
pub width: Smart<NumberWidth>,
|
||||||
/// How to position numbers vertically.
|
/// How to position numbers vertically.
|
||||||
pub position: NumberPosition,
|
pub position: NumberPosition,
|
||||||
/// Whether to have a slash through the zero glyph. ("zero")
|
/// Whether to have a slash through the zero glyph. ("zero")
|
||||||
@ -373,8 +380,8 @@ pub struct NumberFeatures {
|
|||||||
impl Default for NumberFeatures {
|
impl Default for NumberFeatures {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
type_: NumberType::Auto,
|
type_: Smart::Auto,
|
||||||
width: NumberWidth::Auto,
|
width: Smart::Auto,
|
||||||
position: NumberPosition::Normal,
|
position: NumberPosition::Normal,
|
||||||
slashed_zero: false,
|
slashed_zero: false,
|
||||||
fractions: false,
|
fractions: false,
|
||||||
@ -385,8 +392,6 @@ impl Default for NumberFeatures {
|
|||||||
/// Which kind of numbers / figures to select.
|
/// Which kind of numbers / figures to select.
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||||
pub enum NumberType {
|
pub enum NumberType {
|
||||||
/// Select the font's preference.
|
|
||||||
Auto,
|
|
||||||
/// Numbers that fit well with capital text. ("lnum")
|
/// Numbers that fit well with capital text. ("lnum")
|
||||||
Lining,
|
Lining,
|
||||||
/// Numbers that fit well into flow of upper- and lowercase text. ("onum")
|
/// Numbers that fit well into flow of upper- and lowercase text. ("onum")
|
||||||
@ -396,8 +401,6 @@ pub enum NumberType {
|
|||||||
/// The width of numbers / figures.
|
/// The width of numbers / figures.
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||||
pub enum NumberWidth {
|
pub enum NumberWidth {
|
||||||
/// Select the font's preference.
|
|
||||||
Auto,
|
|
||||||
/// Number widths are glyph specific. ("pnum")
|
/// Number widths are glyph specific. ("pnum")
|
||||||
Proportional,
|
Proportional,
|
||||||
/// All numbers are of equal width / monospaced. ("tnum")
|
/// All numbers are of equal width / monospaced. ("tnum")
|
||||||
|
BIN
tests/ref/elements/fill-stroke.png
Normal file
BIN
tests/ref/elements/fill-stroke.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.9 KiB |
Binary file not shown.
Before Width: | Height: | Size: 5.7 KiB After Width: | Height: | Size: 4.1 KiB |
Binary file not shown.
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 3.1 KiB |
26
tests/typ/elements/fill-stroke.typ
Normal file
26
tests/typ/elements/fill-stroke.typ
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
// Test shape fill & stroke.
|
||||||
|
|
||||||
|
---
|
||||||
|
#let rect with (width: 20pt, height: 10pt)
|
||||||
|
#let items = for i, rect in (
|
||||||
|
rect(stroke: none),
|
||||||
|
rect(),
|
||||||
|
rect(fill: none),
|
||||||
|
rect(thickness: 2pt),
|
||||||
|
rect(stroke: eastern),
|
||||||
|
rect(stroke: eastern, thickness: 2pt),
|
||||||
|
rect(fill: eastern),
|
||||||
|
rect(fill: eastern, stroke: none),
|
||||||
|
rect(fill: forest, stroke: none, thickness: 2pt),
|
||||||
|
rect(fill: forest, stroke: conifer),
|
||||||
|
rect(fill: forest, thickness: 2pt),
|
||||||
|
rect(fill: forest, stroke: conifer, thickness: 2pt),
|
||||||
|
) {
|
||||||
|
(align(vertical: center)[{i + 1}.], rect, [])
|
||||||
|
}
|
||||||
|
|
||||||
|
#grid(
|
||||||
|
columns: (auto, auto, 1fr, auto, auto, 0fr),
|
||||||
|
gutter: 5pt,
|
||||||
|
..items,
|
||||||
|
)
|
@ -26,21 +26,9 @@
|
|||||||
// Flipped predefined paper.
|
// Flipped predefined paper.
|
||||||
[#page(paper: "a11", flip: true) Flipped A11]
|
[#page(paper: "a11", flip: true) Flipped A11]
|
||||||
|
|
||||||
---
|
|
||||||
// Test a combination of pages with bodies and normal content.
|
|
||||||
|
|
||||||
#page(width: 80pt, height: 30pt)
|
|
||||||
|
|
||||||
[#page() First]
|
|
||||||
[#page() Second]
|
|
||||||
#pagebreak()
|
|
||||||
#pagebreak()
|
|
||||||
Fourth
|
|
||||||
[#page(height: 25pt)]
|
|
||||||
Sixth
|
|
||||||
[#page() Seventh]
|
|
||||||
|
|
||||||
---
|
---
|
||||||
#page(width: 80pt, height: 40pt, fill: eastern)
|
#page(width: 80pt, height: 40pt, fill: eastern)
|
||||||
#font(15pt, "Roboto", fill: white, smallcaps: true)
|
#font(15pt, "Roboto", fill: white, smallcaps: true)[Typst]
|
||||||
Typst
|
|
||||||
|
#page(width: 40pt, fill: none, margins: auto, top: 10pt)
|
||||||
|
Hi
|
||||||
|
@ -18,3 +18,17 @@ C
|
|||||||
// No consequences from the page("A4") call here.
|
// No consequences from the page("A4") call here.
|
||||||
#pagebreak()
|
#pagebreak()
|
||||||
D
|
D
|
||||||
|
|
||||||
|
---
|
||||||
|
// Test a combination of pages with bodies and normal content.
|
||||||
|
|
||||||
|
#page(width: 80pt, height: 30pt)
|
||||||
|
|
||||||
|
[#page() First]
|
||||||
|
[#page() Second]
|
||||||
|
#pagebreak()
|
||||||
|
#pagebreak()
|
||||||
|
Fourth
|
||||||
|
[#page(height: 25pt)]
|
||||||
|
Sixth
|
||||||
|
[#page() Seventh]
|
||||||
|
@ -52,10 +52,18 @@ fi vs. #font(ligatures: false)[No fi] \
|
|||||||
#font(features: ("smcp",))[Smcp] \
|
#font(features: ("smcp",))[Smcp] \
|
||||||
fi vs. #font(features: (liga: 0))[No fi]
|
fi vs. #font(features: (liga: 0))[No fi]
|
||||||
|
|
||||||
|
---
|
||||||
|
// Error: 22-27 expected integer or none, found boolean
|
||||||
|
#font(stylistic-set: false)
|
||||||
|
|
||||||
---
|
---
|
||||||
// Error: 22-24 must be between 1 and 20
|
// Error: 22-24 must be between 1 and 20
|
||||||
#font(stylistic-set: 25)
|
#font(stylistic-set: 25)
|
||||||
|
|
||||||
|
---
|
||||||
|
// Error: 20-21 expected string or auto, found integer
|
||||||
|
#font(number-type: 2)
|
||||||
|
|
||||||
---
|
---
|
||||||
// Error: 20-31 expected "lining" or "old-style"
|
// Error: 20-31 expected "lining" or "old-style"
|
||||||
#font(number-type: "different")
|
#font(number-type: "different")
|
||||||
|
@ -10,7 +10,7 @@ use ttf_parser::{GlyphId, OutlineBuilder};
|
|||||||
use walkdir::WalkDir;
|
use walkdir::WalkDir;
|
||||||
|
|
||||||
use typst::diag::Error;
|
use typst::diag::Error;
|
||||||
use typst::eval::Value;
|
use typst::eval::{Smart, Value};
|
||||||
use typst::font::Face;
|
use typst::font::Face;
|
||||||
use typst::frame::{Element, Frame, Geometry, Shape, Stroke, Text};
|
use typst::frame::{Element, Frame, Geometry, Shape, Stroke, Text};
|
||||||
use typst::geom::{self, Color, Length, Paint, PathElement, RgbaColor, Sides, Size};
|
use typst::geom::{self, Color, Length, Paint, PathElement, RgbaColor, Sides, Size};
|
||||||
@ -64,7 +64,7 @@ fn main() {
|
|||||||
// large and fit them to match their content.
|
// large and fit them to match their content.
|
||||||
let mut style = Style::default();
|
let mut style = Style::default();
|
||||||
style.page_mut().size = Size::new(Length::pt(120.0), Length::inf());
|
style.page_mut().size = Size::new(Length::pt(120.0), Length::inf());
|
||||||
style.page_mut().margins = Sides::splat(Some(Length::pt(10.0).into()));
|
style.page_mut().margins = Sides::splat(Smart::Custom(Length::pt(10.0).into()));
|
||||||
style.text_mut().size = Length::pt(10.0);
|
style.text_mut().size = Length::pt(10.0);
|
||||||
|
|
||||||
// Hook up an assert function into the global scope.
|
// Hook up an assert function into the global scope.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user