The plot document.
A %Figure{} holds the canvas dimensions, axis configuration, series
list, and ornaments (legend, title block, annotations). Builder functions
return updated figures so the whole pipeline stays immutable:
Bland.figure(size: :a5_landscape)
|> Bland.line([1, 2, 3], [1, 4, 9])
|> Bland.axes(xlabel: "n", ylabel: "n²")
|> Bland.render()Paper sizes
size: accepts {width_px, height_px} tuples or one of the named paper
presets (dimensions in pixels at 96 DPI):
:a4,:a4_landscape:a5,:a5_landscape:letter,:letter_landscape:legal,:legal_landscape:square— 600×600:ieee_column— IEEE single column, 88 × 66 mm:ieee_double— IEEE page-wide, 181 × 136 mm:nature_single— Nature single column, 89 × 67 mm:nature_double— Nature double column, 183 × 137 mm
The default is :letter_landscape.
Physical size
Named presets carry a physical size in millimetres alongside their
pixel dimensions. The rendered SVG sets width/height in mm and
leaves viewBox in user units, so the figure prints at true size and
drops into a LaTeX or InDesign flow at the column width it was drawn
for. size_mm: false opts out and emits pixels; an explicit
{w, h} tuple size has no physical dimensions and always emits pixels.
Struct fields
:width,:height— canvas dimensions (px):margins—{top, right, bottom, left}inside the border:title,:subtitle— figure text:xlim,:ylim— data-space limits, or:auto(default):xscale,:yscale—:linear(default),:log, or:date:xtick_format,:ytick_format— how tick labels are printed. ACalendar.strftime/2format string for:dateaxes; one of:default | :si | :scientific | :degreesfor numeric axes; or a 1-arity function taking the tick value and returning a string.nil(default) picks the sensible thing for the scale:xticks,:yticks— explicit tick positions, overriding the nice-rounded ones:xticklabels,:yticklabels— explicit labels, positionally matched to the ticks:invert_x,:invert_y— run the axis high-to-low:xaxis,:yaxis—:full(default),:ticks(marks only, no labels or title), or:off:aspect—:auto(default) fills the margins;:equalgives one data-x unit the same page length as one data-y unit:manual_layout—trueturns off collision-aware placement, so ornaments land at their nominal positions whatever is underneath
Twin y axis
A second y scale can be drawn on the right for a series measured in
different units — temperature against pressure, gain against phase.
Series opt in with axis: :right; the right margin widens to make room.
:ylabel2,:ylim2,:yscale2,:yticks2,:yticklabels2,:ytick_format2,:invert_y2— the right axis's counterparts of the left-axis options:xlabel,:ylabel:grid—:none | :major | :both:series— list of%Bland.Series{}in draw order:legend—%{position: atom, title: string} | nil:title_block—%Bland.TitleBlock{}ornil:annotations— list of%{type: atom, ...}overlays:theme— map returned byBland.Theme.get/1:caption— explanatory text below the plot; the bottom margin grows to hold it, and long captions wrap:figure_id— a figure number ("Fig. 3"), set to the left of the caption
Summary
Functions
Appends a series to the figure. Accepts a %Bland.Series.Line{} or similar
struct, or any map tagged with :type.
Builds a new figure. opts accepts any of the struct fields, plus
:size (alias for setting :width and :height from a preset) and
:theme (atom or map, resolved via Bland.Theme.get/1).
Physical sizes of the built-in paper presets, in millimetres.
List of built-in paper size presets.
Physical {width_mm, height_mm} for a named paper preset, or nil for
a {width, height} pixel tuple, which has no physical size.
Returns the inner plotting rectangle {x, y, w, h} in pixel coordinates,
accounting for the figure's margins.
Resolves :size to {width, height}. Raises on unknown presets.
Updates the figure with a keyword list of overrides.
Types
@type size() :: atom() | {pos_integer(), pos_integer()}
@type t() :: %Bland.Figure{ annotations: [map()], aspect: :auto | :equal, axes: term(), caption: String.t() | nil, clip: :rect | :circle, colorbar: map() | nil, figure_id: String.t() | nil, grid: :none | :major | :both, height: pos_integer(), insets: [map()], invert_x: boolean(), invert_y: boolean(), invert_y2: boolean(), legend: map() | nil, manual_layout: boolean(), margins: {number(), number(), number(), number()}, projection: atom(), series: [map()], size_mm: {number(), number()} | nil | false, subtitle: String.t() | nil, theme: map(), title: String.t() | nil, title_block: map() | nil, width: pos_integer(), xaxis: :full | :ticks | :off, xlabel: String.t() | nil, xlim: {number(), number()} | :auto, xscale: :linear | :log | :date, xtick_format: term(), xticklabels: [String.t()] | nil, xticks: [number()] | nil, yaxis: :full | :ticks | :off, ylabel: String.t() | nil, ylabel2: String.t() | nil, ylim: {number(), number()} | :auto, ylim2: {number(), number()} | :auto, yscale: :linear | :log | :date, yscale2: :linear | :log, ytick_format: term(), ytick_format2: atom() | String.t() | (number() -> String.t()) | nil, yticklabels: [String.t()] | nil, yticklabels2: [String.t()] | nil, yticks: [number()] | nil, yticks2: [number()] | nil }
Functions
Appends a series to the figure. Accepts a %Bland.Series.Line{} or similar
struct, or any map tagged with :type.
Builds a new figure. opts accepts any of the struct fields, plus
:size (alias for setting :width and :height from a preset) and
:theme (atom or map, resolved via Bland.Theme.get/1).
Physical sizes of the built-in paper presets, in millimetres.
@spec paper_sizes() :: %{required(atom()) => {pos_integer(), pos_integer()}}
List of built-in paper size presets.
Physical {width_mm, height_mm} for a named paper preset, or nil for
a {width, height} pixel tuple, which has no physical size.
iex> Bland.Figure.physical_mm(:a4_landscape)
{297.0, 210.0}
iex> Bland.Figure.physical_mm({800, 600})
nil
Returns the inner plotting rectangle {x, y, w, h} in pixel coordinates,
accounting for the figure's margins.
@spec resolve_size(size()) :: {pos_integer(), pos_integer()}
Resolves :size to {width, height}. Raises on unknown presets.
Updates the figure with a keyword list of overrides.