Universe

To-Stuff is a collection of Typst functions for converting string values to native types. This is most useful when loading layout data from an external configuration source, such as a YAML file.

  • Avoids any use of eval().
  • All conversion functions optionally accept a default value to return on failure.
  • Where no default value is specified, a failed conversion panics with a sensible error message.

How to use

Import the package into the current scope, optionally renamed using the as keyword:

#import "@preview/to-stuff:1.0.0"
// Bindings available as to-stuff.alignment(), to-stuff.angle(), etc.

// …or…

#import "@preview/to-stuff:1.0.0" as to
// Bindings available as to.alignment(), to.angle(), etc.

The package’s let bindings have the same names as their return types, and rely on Typst’s import syntax to scope safely. It is not recommended to load the individual bindings into the current scope, as that may cause collisions with the types themselves.

#assert(type(42pt) == length) // Expected behaviour

#import "@preview/to-stuff:1.0.0": * // NOT RECOMMENDED

#assert(type(42pt) != length) // To-Stuff bindings collide with standard types
#assert(type(42pt) == std.length) // Workaround

Localisation

All conversions involving numeric values assume the number strings to be in a format understood by Typst code; specifically:

  • All numeric digits must be ASCII characters 0-9 and/or A-F (case non-sensitive).
  • Decimal separators must be a full stop/period; commas, apostrophes, etc. are not supported.
  • Thousands separators are not supported.

General Purpose Function

stuff

Attempts to convert a value to the most appropriate data type, based on brute-force process of elimination. If no valid conversion can be found, the original value is returned unaltered.

Unlike the direct conversion functions, this function does not panic in the event of a failed conversion, and does not accept a default value.

#stuff(
	value, // any
	recursive: false, // bool
	arguments-collapse: false, // bool
	numbers-as: auto, // type
	none-from: (), // array
) -> any
View arguments

value

str or array or dictionary or arguments or others (positional, required)

The value that should be converted to a native data type.

  • A string with the value "auto" is converted to auto.
  • A string representation of a native data type that is recognised by a direct conversion function is converted accordingly.
  • A dictionary that conforms to a valid alignment, relative or stroke is converted.
  • An array that conforms to a valid version is converted.
  • Any other value is returned unchanged.

recursive

bool

Whether to recurse through the items of arguments and array values, and of dictionary values that could not otherwise be converted.

Note that recursing an array means that it will not be converted to a single version, even if otherwise suitable; version-like strings will still be converted as expected.

Default: false

arguments-collapse

bool

Whether to return an arguments value as an array or dictionary, where possible.

Has no effect when recursive is not true.

  • A setting of true returns an arguments value with no named arguments as an array, or an arguments value with no positional arguments as a dictionary. An arguments value with both named and positional arguments will always be returned as an arguments.
  • A setting of false returns an arguments value as an arguments, regardless of its lack of named or positional arguments.

Default: false

numbers-as

type

The standard type to which to attempt to convert basic numbers.

  • A setting of std.int converts decimals, floats and eligible strings to int types.
  • A setting of std.float converts ints, decimals and eligible strings to float types.
  • A setting of std.decimal converts ints and eligible strings to decimal types, but ignores floats.
  • A setting of std.version converts ints and eligible strings to version types, but ignores floats and decimals.
  • A setting of auto applies the number direct conversion function, which if successful returns either an int or a float as appropriate.

Default: auto

none-from

array

Specify strings to convert to none. Case non-sensitive.

Default: ()

View examples
#import "@preview/to-stuff:1.0.0" as to

#let a = to.stuff("auto")
// -> auto

#let b = to.stuff("true")
// -> true

#let c = to.stuff("top + right")
// -> right + top

#let d = to.stuff((x: "right", y: "top"))
// -> right + top

#let e = to.stuff("45deg")
// -> 45deg

#let f = to.stuff("rgb(255, 65, 54)")
// -> rgb("#ff4136")

#let g = to.stuff("rtl")
// -> rtl

#let h = to.stuff("25e-1")
// -> 2.5

#let i = to.stuff("2.5fr")
// -> 2.5fr

#let j = to.stuff("0x2a")
// -> 42

#let k = to.stuff("45pt")
// -> 45pt

#let l = to.stuff("45%")
// -> 45%

#let m = to.stuff("45pt + 3%")
// -> 3% + 45pt

#let n = to.stuff((ratio: "3%", length: "45pt"))
// -> 3% + 45pt

#let o = to.stuff("2pt + red + densely-dashed")
// -> (paint: rgb("#ff4136"), thickness: 2pt, dash: (array: (3pt, 2pt), phase: 0pt))

#let p = to.stuff((paint: red, thickness: 2pt, dash: "densely-dashed"))
// -> (paint: rgb("#ff4136"), thickness: 2pt, dash: (array: (3pt, 2pt), phase: 0pt))

#let q = to.stuff(recursive: true, (
	mixed: ("A", "2", "33.3%", "auto", "5pt", "horizon + center"),
	origin: (x: "center", y: "horizon"),
	line: (thickness: "0.5pt", paint: "blue", dash: "dotted"),
))
/* -> (
	mixed: ("A", 2, 33.3%, auto, 5pt, center + horizon),
	origin: center + horizon,
	line: (paint: rgb("#0074d9"), thickness: 0.5pt, dash: (array: ("dot", 2pt), phase: 0pt)),
) */

#let r = to.stuff(recursive: true, arguments-collapse: false, arguments(paint: "black", thickness:
"0.9pt"))
// -> arguments(paint: luma(0%), thickness: 0.9pt)

#let s = to.stuff(recursive: true, arguments-collapse: true, arguments(paint: "black", thickness:
"0.9pt"))
// -> 0.9pt + luma(0%)

#let t = to.stuff(numbers-as: decimal, "2.5")
// -> decimal("2.5")

#let u = to.stuff(numbers-as: float, "2.5")
// -> 2.5

#let v = to.stuff(numbers-as: int, "2.5")
// -> 2

#let w = to.stuff("")
// -> ""

Direct Conversion Functions

alignment

Attempts to convert a value to an alignment.

#alignment(
	value, // str | alignment | dictionary
	default: auto, // auto | none | alignment
) -> none | alignment
View arguments

value

str or alignment or dictionary (positional, required)

The value that should be converted to an alignment.

  • An alignment is returned unchanged.
  • A string representation of any of the eight alignment values is converted to that value.
    • The string must not contain any scoping prefix, e.g. alignment.…, or the conversion will fail.
  • A string consisting of two alignment representations joined by a plus sign is converted to the corresponding 2D alignment.
    • The two alignments must be on different axes, or the conversion will fail.
  • A dictionary containing one or more of the keys x and y, and no other keys, is converted.
    • The value of x, if present, must be either a horizontal alignment or a value that would convert to one.
    • The value of y, if present, must be either a vertical alignment or a value that would convert to one.

default

auto or none or alignment

What to return if value could not be converted. If auto, failed conversions cause a panic.

Default: auto

View examples
#import "@preview/to-stuff:1.0.0" as to

#let a = to.alignment("top + right")
// -> right + top

#let b = to.alignment(top + right)
// -> right + top

#let c = to.alignment((x: "right", y: "top"))
// -> right + top

#let d = to.alignment("turnwise")
// panics with: "could not convert to alignment: \"turnwise\""

#let e = to.alignment("top + bottom")
// panics with: "cannot add two vertical alignments: \"top + bottom\""

#let f = to.alignment(default: top + right, "top + bottom")
// -> right + top

#let g = to.alignment(default: none, "top + bottom")
// -> none

angle

Attempts to convert a value to an angle.

#angle(
	value, // str | angle
	default: auto, // auto | none | angle
) -> none | angle
View arguments

value

str or angle (positional, required)

The value that should be converted to an angle.

  • An angle is returned unchanged.
  • A string representation of a number followed by the letters deg or rad is converted.
    • The number may be positive or negative, and may contain decimal places and/or an exponent.

default

auto or none or angle

What to return if value could not be converted. If auto, failed conversions cause a panic.

Default: auto

View examples
#import "@preview/to-stuff:1.0.0" as to

#let a = to.angle("45deg")
// -> 45deg

#let b = to.angle(45deg)
// -> 45deg

#let c = to.angle("42")
// panics with: "could not convert to angle: \"42\""

#let d = to.angle(default: 45deg, "42")
// -> 45deg

#let e = to.angle(default: none, "42")
// -> none

bool

Attempts to convert a value to a bool.

#bool(
	value, // str | bool
	default: auto, // auto | none | bool
) -> none | bool
View arguments

value

str or bool (positional, required)

The value that should be converted to a bool.

  • A bool is returned unchanged.
  • A string value of "true" is converted to true. Case non-sensitive.
  • A string value of "false" is converted to false. Case non-sensitive.

default

auto or none or bool

What to return if value could not be converted. If auto, failed conversions cause a panic.

Default: auto

View examples
#import "@preview/to-stuff:1.0.0" as to

#let a = to.bool("true")
// -> true

#let b = to.bool(true)
// -> true

#let c = to.bool("auto")
// panics with: "could not convert to boolean: \"auto\""

#let d = to.bool(default: true, "auto")
// -> true

#let e = to.bool(default: none, "auto")
// -> none

color

Attempts to convert a value to a color.

#color(
	value, // str | color
	default: auto, // auto | none | color
) -> none | color
View arguments

value

str or color (positional, required)

The value that should be converted to a color.

  • A color is returned unchanged.
  • A string representation of a predefined color value is converted to that built-in color.
  • A string representation of a hash symbol followed by a 6- or 8-digit hexadecimal code is converted to the corresponding RGB or RGBA value.
  • A string representation of a color space function followed by parentheses and arguments is converted.
    • The string must not contain any scoping prefix, e.g. color.…, or the conversion will fail. This includes the hsl, hsv, and linear-rgb functions.

default

auto or none or color

What to return if value could not be converted. If auto, failed conversions cause a panic.

Default: auto

View examples
#import "@preview/to-stuff:1.0.0" as to

#let a = to.color("red")
// -> rgb("#ff4136")

#let b = to.color(red)
// -> rgb("#ff4136")

#let c = to.color("#FF4136FF")
// -> rgb("#ff4136")

#let d = to.color("rgb(255, 65, 54)")
// -> rgb("#ff4136")

#let e = to.color("hsv(135deg,75%,127,100)")
// -> color.hsv(135deg, 75%, 49.8%, 39.22%)

#let f = to.color("indigo")
// panics with: "could not convert to color: \"indigo\""

#let g = to.color(default: red, "indigo")
// -> rgb("#ff4136")

#let h = to.color(default: none, "indigo")
// -> none

decimal

Attempts to convert a value to a decimal.

While the standard decimal constructor throws an error on failure, this function panics instead, which may be suppressed if desired via the default argument.

#decimal(
	value, // str | int | decimal
	default: auto, // auto | none | int | decimal
) -> none | decimal
View arguments

value

str or int or decimal (positional, required)

The value that should be converted to a decimal.

  • A decimal is returned unchanged.
  • An int is converted.
  • A string representation of a number is converted.
    • The number may be positive or negative, and may contain decimal places, but not an exponent.
    • Hexadecimal numbers, prefixed with 0x, are permitted.
    • Octal numbers, prefixed with 0o, are permitted.
    • Binary numbers, prefixed with 0b, are permitted.

default

auto or none or int or decimal

What to return if value could not be converted; int defaults are converted to decimal. If auto, failed conversions cause a panic.

Default: auto

View examples
#import "@preview/to-stuff:1.0.0" as to

#let a = to.decimal("42")
// -> decimal("42")

#let b = to.decimal(42)
// -> decimal("42")

#let c = to.decimal("0x2a")
// -> decimal("42")

#let d = to.decimal(0x2a)
// -> decimal("42")

#let e = to.decimal("2.5")
// -> decimal("2.5")

#let f = to.decimal(2.5)
// panics with: "could not convert to decimal: 2.5"

#let h = to.decimal("25e-1")
// panics with: "could not convert to decimal: \"25e-1\""

#let h = to.decimal(default: 42, "25e-1")
// -> decimal("42")

#let i = to.decimal(default: none, "25e-1")
// -> none

direction

Attempts to convert a value to a direction.

#direction(
	value, // str | direction
	default: auto, // auto | none | direction
) -> none | direction
View arguments

value

str or direction (positional, required)

The value that should be converted to a direction.

  • A direction is returned unchanged.
  • A string representation of any of the four direction values is converted to that value.
    • The string must not contain any scoping prefix, e.g. direction.…, or the conversion will fail.

default

auto or none or direction

What to return if value could not be converted. If auto, failed conversions cause a panic.

Default: auto

View examples
#import "@preview/to-stuff:1.0.0" as to

#let a = to.direction("rtl")
// -> rtl

#let b = to.direction(rtl)
// -> rtl

#let c = to.direction("btf")
// panics with: "could not convert to direction: \"btf\""

#let d = to.direction(default: rtl, "btf")
// -> rtl

#let e = to.direction(default: none, "btf")
// -> none

float

Attempts to convert a value to a float.

While the standard float constructor throws an error on failure, this function panics instead, which may be suppressed if desired via the default argument.

#float(
	value, // str | int | float | decimal
	default: auto, // auto | none | int | float | decimal
) -> none | float
View arguments

value

str or int or float or decimal (positional, required)

The value that should be converted to a float.

  • A float is returned unchanged.
  • A decimal or int is converted.
  • A string representation of a number is converted.
    • The number may be positive or negative, and may contain decimal places and/or an exponent.
    • Hexadecimal numbers, prefixed with 0x, are permitted.
    • Octal numbers, prefixed with 0o, are permitted.
    • Binary numbers, prefixed with 0b, are permitted.

default

auto or none or int or float or decimal

What to return if value could not be converted; decimal and int defaults are converted to float. If auto, failed conversions cause a panic.

Default: auto

View examples
#import "@preview/to-stuff:1.0.0" as to

#let a = to.float("42")
// -> 42.0

#let b = to.float(42)
// -> 42.0

#let c = to.float("2.5")
// -> 2.5

#let d = to.float(2.5)
// -> 2.5

#let e = to.float(25e-1)
// -> 2.5

#let f = to.float("25e-1")
// -> 2.5

#let g = to.float("0x2a")
// -> 42.0

#let h = to.float(0x2a)
// -> 42.0

#let i = to.float("42%")
// panics with: "could not convert to float: \"42%\""

#let j = to.float(default: 42, "42%")
// -> 42.0

#let k = to.float(default: none, "42%")
// -> none

fraction

Attempts to convert a value to a fraction.

#fraction(
	value, // str | fraction
	default: auto, // auto | none | fraction
) -> none | fraction
View arguments

value

str or fraction (positional, required)

The value that should be converted to a fraction.

  • A fraction is returned unchanged.
  • A string representation of a number followed by the letters fr is converted.
    • The number may be positive or negative, and may contain decimal places and/or an exponent.

default

auto or none or fraction

What to return if value could not be converted. If auto, failed conversions cause a panic.

Default: auto

View examples
#import "@preview/to-stuff:1.0.0" as to

#let a = to.fraction("2.5fr")
// -> 2.5fr

#let b = to.fraction(2.5fr)
// -> 2.5fr

#let c = to.fraction("42")
// panics with: "could not convert to fraction: \"42\""

#let d = to.fraction(default: 2.5fr, "42")
// -> 2.5fr

#let e = to.fraction(default: none, "42")
// -> none

int

Attempts to convert a value to an int.

While the standard int constructor throws an error on failure, this function panics instead, which may be suppressed if desired via the default argument.

This function can also parse correctly-prefixed strings in hexadecimal, octal and binary.

#int(
	value, // str | int | float | decimal
	default: auto, // auto | none | int
) -> none | int
View arguments

value

str or int or float or decimal (positional, required)

The value that should be converted to an int.

  • An int is returned unchanged.
  • A decimal or float is rounded towards zero and converted.
  • A string representation of a number is converted.
    • The number may be positive or negative, and may contain decimal places and/or an exponent.
    • Hexadecimal numbers, prefixed with 0x, are permitted.
    • Octal numbers, prefixed with 0o, are permitted.
    • Binary numbers, prefixed with 0b, are permitted.

default

auto or none or int

What to return if value could not be converted. If auto, failed conversions cause a panic.

Default: auto

View examples
#import "@preview/to-stuff:1.0.0" as to

#let a = to.int("42")
// -> 42

#let b = to.int(42)
// -> 42

#let c = to.int("2.5")
// -> 2

#let d = to.int(2.5)
// -> 2

#let e = to.int(25e-1)
// -> 2

#let f = to.int("25e-1")
// -> 2

#let g = to.int("0x2a")
// -> 42

#let h = to.int(0x2a)
// -> 42

#let i = to.int("42%")
// panics with: "could not convert to int: \"42%\""

#let j = to.int(default: 42, "42%")
// -> 42

#let k = to.int(default: none, "42%")
// -> none

length

Attempts to convert a value to a length.

#length(
	value, // str | length
	default: auto, // auto | none | length
) -> none | length
View arguments

value

str or length (positional, required)

The value that should be converted to a length.

  • A length is returned unchanged.
  • A string representation of a number followed by the letters pt, mm, cm, in, or em, is converted.
    • The number may be positive or negative, and may contain decimal places and/or an exponent.

default

auto or none or length

What to return if value could not be converted. If auto, failed conversions cause a panic.

Default: auto

View examples
#import "@preview/to-stuff:1.0.0" as to

#let a = to.length("45pt")
// -> 45pt

#let b = to.length(45pt)
// -> 45pt

#let c = to.length("42")
// panics with: "could not convert to length: \"42\""

#let d = to.length(default: 45pt, "42")
// -> 45pt

#let e = to.length(default: none, "42")
// -> none

number

Attempts to convert a value to an int or a float as appropriate.

#number(
	value, // str | int | float | decimal
	default: auto, // auto | none | int | float | decimal
) -> none | int | float | decimal
View arguments

value

str or int or float or decimal (positional, required)

The value that should be converted to a float or int.

  • A decimal, float or int is returned unchanged.
  • A string representation of a number is converted.
    • The number may be positive or negative.
    • A number that contains decimal places and/or an exponent, is converted to a float.
    • Hexadecimal numbers, prefixed with 0x, are converted to an int.
    • Octal numbers, prefixed with 0o, are converted to an int.
    • Binary numbers, prefixed with 0b, are converted to an int.

default

auto or none or int or float or decimal

What to return if value could not be converted. If auto, failed conversions cause a panic.

Default: auto

View examples
#import "@preview/to-stuff:1.0.0" as to

#let a = to.number("42")
// -> 42

#let b = to.number(42)
// -> 42

#let c = to.number("2.5")
// -> 2.5

#let d = to.number(2.5)
// -> 2.5

#let e = to.number(25e-1)
// -> 2.5

#let f = to.number("25e-1")
// -> 2.5

#let g = to.number("0x2a")
// -> 42

#let h = to.number(0x2a)
// -> 42

#let i = to.number("42%")
// panics with: "could not convert to int or float: \"45%\""

#let j = to.number(default: 42, "42%")
// -> 42

#let k = to.number(default: none, "42%")
// -> none

ratio

Attempts to convert a value to a ratio.

#ratio(
	value, // str | ratio
	default: auto, // auto | none | ratio
) -> none | ratio
View arguments

value

str or ratio (positional, required)

The value that should be converted to a ratio.

  • A ratio is returned unchanged.
  • A string representation of a number followed by a percent sign is converted.
    • The number may be positive or negative, and may contain decimal places and/or an exponent.

default

auto or none or ratio

What to return if value could not be converted. If auto, failed conversions cause a panic.

Default: auto

View examples
#import "@preview/to-stuff:1.0.0" as to

#let a = to.ratio("45%")
// -> 45%

#let b = to.ratio(45%)
// -> 45%

#let c = to.ratio("42")
// panics with: "could not convert to ratio: \"42\""

#let d = to.ratio(default: 45%, "42")
// -> 45%

#let e = to.ratio(default: none, "42")
// -> none

relative

Attempts to convert a value to a relative.

#relative(
	value, // str | relative | ratio | length | dictionary
	default: auto, // auto | none | relative | ratio | length
) -> none | relative
View arguments

value

str or relative or ratio or length or dictionary (positional, required)

The value that should be converted to a relative.

  • A relative is returned unchanged.
  • A ratio is returned as a relative with a length of 0pt.
  • A length is returned as a relative with a ratio of 0%.
  • A string representation of a ratio (see above) is converted.
  • A string representation of a length (see above) is converted.
  • A string consisting of multiple ratios and lengths joined by plus signs or minus signs is converted to a single relative length.
    • All length-like substrings are added.
    • All ratio-like substrings are added.
  • A dictionary containing one or more of the keys ratio and length, and no other keys, is converted.
    • The value of ratio, if present, must be either a ratio or a value that would convert to one.
    • The value of length, if present, must be either a length or a value that would convert to one.

default

auto or none or relative or ratio or length

What to return if value could not be converted. If auto, failed conversions cause a panic.

Default: auto

View examples
#import "@preview/to-stuff:1.0.0" as to

#let a = to.relative("45pt + 3%")
// -> 3% + 45pt

#let b = to.relative(45pt + 3%)
// -> 3% + 45pt

#let c = to.relative((ratio: "3%", length: "45pt"))
// -> 3% + 45pt

#let d = to.relative("42")
// panics with: "could not convert to relative: \"42\""

#let e = to.relative(default: 45pt + 3%, "42")
// -> 3% + 45pt

#let f = to.relative(default: none, "42")
// -> none

stroke

Attempts to convert a value to a stroke.

#stroke(
	value, // str | stroke | color | length | array | dictionary
	default: auto, // auto | none | stroke | color | length
) -> none | stroke
View arguments

value

str or stroke or color or length or array or dictionary (positional, required)

The value that should be converted to a stroke.

  • A stroke, color or length is returned unchanged.
  • A string representation of a color (see above) is converted.
  • A string representation of a length (see above) is converted.
  • A valid dash pattern is converted.
  • A string representation of one or more valid colors, lengths and/or predefined dash patterns joined by plus signs is converted.
    • All color-like substrings are combined via color.mix().
    • All length-like substrings added.
  • A dictionary that would otherwise be accepted as a valid stroke is converted.

default

auto or none or stroke or color or length

What to return if value could not be converted. If auto, failed conversions cause a panic.

Default: auto

View examples
#import "@preview/to-stuff:1.0.0" as to

#let a = to.stroke("red")
// -> rgb("#ff4136")

#let b = to.stroke(45pt)
// -> 45pt

#let c = to.stroke("densely-dashed")
// -> (dash: (array(3pt, 2pt), phase: 0pt))

#let d = to.stroke((3pt, 2pt))
// -> (dash: (array(3pt, 2pt), phase: 0pt))

#let e = to.stroke((paint: red, thickness: 2pt, dash: "densely-dashed"))
// -> (paint: rgb("#ff4136"), thickness: 2pt, dash: (array: (3pt, 2pt), phase: 0pt))

#let f = to.stroke("2pt + red + densely-dashed + silver + 5pt")
// -> (paint: oklab(77.85%, 0.1, 0.054), thickness: 7pt, dash: (array: (3pt, 2pt), phase: 0pt))

#let g = to.stroke("deep-dish")
// panics with: "could not convert to stroke: \"deep-dish\""

#let h = to.stroke(default: red + 45pt, "deep-dish")
// -> 45pt + rgb("#ff4136")

#let i = to.stroke(default: none, "deep-dish")
// -> none

version

Attempts to convert a value to a version.

#version(
	value, // str | version | int | array
	default: auto, // auto | none | version
) -> none | version
View arguments

value

str or version or int or array (positional, required)

The value that should be converted to a version.

  • A version is returned unchanged.
  • An int is converted.
  • A string representation of positive integers separated by periods is converted.
  • An array of any combination of the above is flattened and converted.

default

auto or none or version

What to return if value could not be converted. If auto, failed conversions cause a panic.

Default: auto

View examples
#import "@preview/to-stuff:1.0.0" as to

#let a = to.version("42")
// -> version(42)

#let b = to.version(42)
// -> version(42)

#let c = to.version("2.5")
// -> version(2, 5)

#let d = to.version(2.5)
// -> panics with: "expected integer, array or version, found float 2.5"

#let e = to.version("2.5.1")
// -> version(2, 5, 1)

#let f = to.version((2, 5, 1))
// -> version(2, 5, 1)

#let g = to.version((2, -5, 1))
// -> panics with "number must be greater than zero: -5"

#let h = to.version((2, (5, 1)))
// -> version(2, 5, 1)

#let i = to.version((2, std.version(5, 1)))
// -> version(2, 5, 1)

#let j = `to.version("42%")`
// -> panics with: "could not convert to version: \"42%\""

#let k = to.version(default: sys.version, "42%")
// -> version(0, 14, 2)

#let l = to.version(default: none, "42%")
// -> none

Changelog

1.0.0 - 2026-06-08

Changed

  • Breaking: less strict, more natural handling of integers and floats.
    • Integer and float conversions may accept integers, floats or decimals.
    • Float and generic number conversions may default to an integer, float, or decimal.
    • Integer conversions may only default to an integer (or none).

Added

  • New general-purpose function stuff to guess most appropriate conversion.
  • Expose new conversions:
    • bool
    • decimal
    • version

Removed

  • Breaking: remove deprecated quiet argument from all direct conversion functions.
  • Breaking: remove deprecated scalar function alias.

0.5.1 - 2026-01-16

Changed

  • Invalid default and quiet values throw failed assertions instead of panicking.

Fixed

  • Reject empty dictionaries when converting:
    • alignment
    • relative
    • stroke

0.5.0 - 2025-12-20

Changed

  • Rename scalar to number.
  • Deprecate scalar (retained as alias of number for backward compatibility).

Added

  • Add default argument to every conversion (#2).
  • Deprecate the quiet argument (equivalent to default: none).

Fixed

  • Additional validation restraints for color constructors (#1).

0.4.0 - 2025-12-06

Changed

  • Remove reliance on eval() from:
    • color
  • All use of eval() now entirely eliminated.

Added

  • Expose new conversions:
    • float
    • int
    • scalar (returns either float or int as appropriate)

Fixed

  • Color conversion via constructor now checks validity of all arguments.
  • Stroke miter-limit now treated as scalar.

0.3.1 - 2025-10-30

Added

  • Dictionary conversion to stroke now checks validity of cap, join and miter-limit.

0.3.0 - 2025-10-19

Changed

  • Improve conversion logic and error checking for stroke.

0.2.1 - 2025-10-10

Changed

  • Remove reliance on eval() from:
    • alignment

0.2.0 - 2025-10-08

Changed

  • Remove reliance on eval() from:
    • angle
    • fraction
    • length
    • ratio
    • relative

Added

  • Allow exponential notation in numeric conversions.

0.1.0 - 2025-09-30

Initial release.