On this page
Historical material
This page preserves an earlier release or study. See the current guide for supported behavior.
Inklet 2.5¶
Version 2.5 adds nested grids, measured compositions and architecture modules, deferred annotation placement, publication presets, and saved-revision review. The existing Diagram, Panel, Figure and v2 document APIs remain available.
Build the complete example:
inklet build examples/v25_document.py --output out/v25 --name document
inklet watch examples/v25_document.py --output out/v25 --name document
Nested panels¶
A subfigure uses the document grid API. Add plots, components or more subfigures;
each nested grid inherits the enclosing document's theme and shares its geometry
cache. The complete page is painted and checked once. Minimum sizes remain
physical constraints, and unsatisfiable layouts raise LayoutError.
import inklet as i
left = i.plot_spec(x=(0, 2), y=(0, 5)).line([(0, 1), (1, 3), (2, 2)]).axes(key='axes')
right = i.plot_spec(x=(0, 2), y=(0, 5)).line([(0, 2), (1, 4), (2, 3)]).axes()
panels = i.subfigure(columns=2, gap=6).letters(start='a')
panels.add('control', left, row=0, column=0, min_height=45)
panels.add('treatment', right, row=0, column=1, min_height=45)
doc = i.publication('double-column').document()
doc.add('experiment', panels)
doc['experiment']['control'].replace('axes', x='Time')
compiled = doc.compile()
letters() measures the labels as part of the grid's furniture. Plots in the
same row share top and bottom margins, so different axes, legends or panel
letters do not change their data-region alignment. Lettered responsive components
also reserve space for the letter. start, style, corner, pad, inside
and size use the existing inklet.letters() options.
Measured compositions and modules¶
Use composition() for an irregular arrangement. Children have stable names;
their positions and dimensions can depend on the page size, other children's
measured extents, or registered anchors. Forward references are allowed. Cyclic
measurements or placements fail with a named layout error.
art = i.composition(150, 50)
art.add('input', i.module('Observations'), x=5, y=15)
x, y = art.point('input', 'out')
art.add('model', i.module('Shared encoder'), x=x + 12, y=15)
x, y = art.point('model', 'out')
art.add('output', i.module('Prediction'), x=x + 12, y=15)
art.link('input:out', 'model:in')
art.link('model:out', 'output:in')
art.constrain(art.page_width - art.point('output', 'e')[0], minimum=5,
message='Increase the page width for these module labels')
doc = i.document(width=160, height=60)
doc.add('architecture', art)
first = doc.compile()
art['model'].configure('A longer encoder label')
second = doc.compile() # downstream components and connections move
Modules size their boxes from measured text and padding. Define named ports with
fractions of the box, for example ports={'input': (0, .5), 'upper': (1, .25),
'lower': (1, .75)}. min_width, min_height, max_height, pad,
label_offset, text_style and box_style control physical appearance. Font
sizes and strokes stay unchanged when the page grows. Module dimensions are
intrinsic; a narrow cell reports insufficient space instead of squeezing text.
Composition coordinates default to millimetres. unit=250/2157 is useful when
reconstructing artwork in source-image coordinates: coordinates and expressions
use that unit, while component styles remain physical millimetres. Explicit
child width and height use composition units. For plots they describe the
data region, not the full extent of the axes, legend or inset.
Placement defaults to the child's existing local coordinate frame. Set
anchor='nw', 'center', a registered port, or 'area-nw' for measured alignment.
Use measure(name, 'width'), measure(name, 'height'), page_width and
page_height in arithmetic expressions. Expressions can also occur inside
component() arguments and connection waypoints. replace(name, item) retains
a child's placement constraints. bindings can hold named data and category
definitions, accessible through the same composition[name] lookup.
link() routes named branches and returns after placement. annotate() places
callouts after all components and connections exist, then runs the joint label
placer. That placer chooses sides for the group; remaining conflicts are still
reported by diagnostics. A composition's declared rectangle is its layout frame,
not a clipping mask: artwork extending beyond the page remains diagnosable.
Plot annotations¶
PlotSpec now resolves marks, axes and keys, insets, significance brackets,
callouts, then titles. You can declare a bracket before drawing its bars; its
height still clears the current data and earlier brackets. Callouts consider
the measured marks and furniture, including late insets and earlier callouts.
Use avoid_marks=False on PlotSpec.annotate() for the previous placement policy.
Explicit avoid= blockers and the native Panel annotation API remain available.
plot = i.plot_spec(x=i.band(['Control', 'Treatment']), y=(0, 10))
plot.bracket('Control', 'Treatment', '**')
plot.bars(['Control', 'Treatment'], [3, 7]).axes()
Placement cannot guarantee a collision-free result in an arbitrarily crowded panel. The review page retains all diagnostics so the author can inspect it.
Publication defaults¶
publication('single-column') uses an 89 mm page;
publication('double-column') uses 183 mm. Both use 8 pt main type, 7 pt small
type, 0.18 mm strokes, embedded fonts and 300 dpi previews. Print checks flag
type below 6 pt, strokes below 0.1 mm and raster artwork below 300 dpi at its
final transformed size. publication('slide') uses a 254 mm page with larger
type and 150 dpi previews.
These are general authoring presets, not journal submission certifications. Override the profile to match the actual destination:
profile = i.publication('single-column', width=85, min_font_pt=7, dpi=450)
doc = profile.document(height=60)
profile.document() applies the theme, physical width, diagnostic thresholds
and export settings together. Its compiled manifest records the profile.
Explicit export(dpi=..., text=...) options take precedence. The CLI uses the
profile's preview DPI unless --dpi is supplied. Profiles do not rescale artwork
or upsample source images to make a print check pass.
Review revisions¶
Every review page supports component and severity filters, text search, and clickable diagnostic locations in the editable SVG. Parent-component filters include their nested children. JSON diagnostics preserve all findings and targets.
inklet build examples/v25_document.py --output out/first --name document
# Edit the authoring file, then compare with the completed bundle:
inklet build examples/v25_document.py --output out/second --name document \
--compare-to out/first/document-manifest.json
Python: compiled.export('out/second', name='document', compare_to=previous_manifest).
A directory is also accepted when it contains <name>-manifest.json.
Matching page dimensions, DPI and pixel dimensions enable an opacity slider and a difference image amplified five times. The reported fraction counts pixels whose change exceeds 25/255 in any RGB channel; it is a visual difference measure, not a scientific accuracy score. Different page sizes or resolutions are shown side by side without a pixel score or resampling. The previous PNG and manifest are copied into the new bundle, making the review portable.
watch automatically compares each successful build with the preceding bundle.
Pass --compare-to to keep a fixed reference instead. Exports finish rendering
and validating the comparison before replacing existing files; failed builds
retain the last successful review.
Complete examples¶
The v2.5 document uses the composition APIs with nested grids, architecture modules and live plots. The twenty-panel stress test exercises larger mixed figures, edits, resizing, export diagnostics and provenance.