Skip to main content

figquilt

Figure quilter: A declarative CLI tool for compositing multiple figures (PDF, SVG, PNG) into publication-ready layouts.

figquilt takes a simple layout file (YAML) describing panels and their structure, composed of various inputs (plots from R/Python, diagrams, photos), and stitches them into a single output file (PDF, SVG) with automatic labeling and precise dimension control.

What's New

See CHANGELOG.md for full release notes.

v0.3.0

  • Added figquilt --check support without requiring an output path for validation-only workflows.
  • Tightened layout validation so invalid numeric values, missing assets, and output/input collisions fail earlier with clearer errors.
  • Improved PDF/SVG cover and contain rendering geometry and expanded regression tests across CLI, parser, examples, and rendering paths.

v0.2.0

  • Added page.auto_scale to auto-fit oversized/off-page explicit panels layouts into the page content area.
  • Added layout.type: auto for sequence-preserving automatic panel layout.
  • Added auto-layout controls for column bias, size similarity, and main-panel prominence hints (role, weight).

v0.1.10

  • Correct cover clipping in PDF output.
  • Better layout safety checks with clear errors for invalid margins, ratios, and gaps.
  • Auto-labels now continue after Z (AA, AB, ...).
  • SVG PDF rasterization now respects page.dpi.

Philosophy

  • Declarative over imperative: Describe what your figure should look like, not how to construct it. Layouts are data, not scripts.
  • Structural composition first: Prefer high-level layout (rows, columns, ratios) over manual coordinate placement. Let the tool handle positioning.
  • Fine control when needed: Override with explicit coordinates and dimensions when precision matters.
  • Automation-friendly: Designed to fit into reproducible workflows (Snakemake, Make, CI pipelines). No GUI, no manual steps.

Features

  • Precise Layout: Define exact physical dimensions (mm) for the page and panels.
  • Mixed Media: Combine PDF, SVG, and PNG inputs in one figure.
  • Automated Labeling: Automatically add subfigure labels (A, B, C...) with consistent styling.
  • Reproducible: Layouts are defined in version-controllable text files (YAML).
  • Language Agnostic: It is a CLI tool, so it works with outputs from any tool (R, Python, Julia, Inkscape, etc.).
  • Auto-Fit for Manual Layouts: Optional page.auto_scale preserves panel geometry while fitting explicit layouts to the page.
  • Auto Layout from Sequence: layout.type: auto builds an ordered layout from panel files, with optional size-similarity and main-panel hints.

Installation

uv tool install figquilt

Or add it as a project dependency:

uv add figquilt

Development Installation

git clone https://github.com/yy/figquilt.git
cd figquilt
uv sync

Usage

Define a layout in a YAML file (e.g., figure1.yaml):

page:
  width: 180  # mm
  height: 120 # mm
  
panels:
  - id: A
    file: "plots/scatter.pdf"
    width: 80
    x: 0
    y: 0
  - id: B
    file: "diagrams/schematic.svg"
    width: 80
    x: 90
    y: 0

Run figquilt to generate the figure:

figquilt figure1.yaml figure1.pdf

Watch Mode

Use --watch to automatically rebuild when the layout file or any panel source files change:

figquilt --watch figure1.yaml figure1.pdf

This is useful during layout iteration - edit your YAML or regenerate a panel, and the output updates automatically.

Fit Modes

When specifying both width and height for a panel, use fit to control how the source image scales:

  • contain (default): Scale to fit within the cell, preserving aspect ratio. May leave empty space (letterbox/pillarbox).
  • cover: Scale to cover the entire cell, preserving aspect ratio. May crop overflow.
panels:
  - id: A
    file: "photo.png"
    x: 0
    y: 0
    width: 80
    height: 60
    fit: cover  # Fill the cell, cropping if needed

If height is omitted, the panel automatically sizes to preserve the source aspect ratio.

Page Margins

Add consistent margins around your content with the margin property on the page:

page:
  width: 180
  height: 120
  margin: 10  # 10mm margin on all sides

panels:
  - id: A
    file: "plots/scatter.pdf"
    width: 70
    x: 0   # Positioned relative to margin, not page edge
    y: 0

Panel coordinates are relative to the margin edge. A panel at x: 0, y: 0 with a 10mm margin will appear at position (10mm, 10mm) on the page.

Auto-Scale Explicit Panels to Fit Page

When working in explicit panels mode, enable page.auto_scale to automatically fit oversized or off-page layouts:

page:
  width: 180
  height: 120
  margin: 10
  auto_scale: true

panels:
  - id: A
    file: "plots/left.pdf"
    x: -5
    y: 0
    width: 100
  - id: B
    file: "plots/right.pdf"
    x: 110
    y: 10
    width: 100

figquilt computes the panel bounding box and applies one global transform (translate + uniform scale), preserving the layout's relative geometry while fitting it into the page content area.

Grid Layout

Instead of manually specifying x/y coordinates for each panel, use the grid layout system to define structure with rows and columns:

page:
  width: 180
  height: 100
  units: mm

layout:
  type: row
  ratios: [3, 2]  # Left panel is 60%, right is 40%
  gap: 5
  children:
    - id: A
      file: "plot1.pdf"
    - id: B
      file: "plot2.pdf"

Container Types

  • row: Arranges children horizontally (left to right)
  • col: Arranges children vertically (top to bottom)

Container Properties

Property Default Description
ratios Equal Relative sizing of children (e.g., [3, 2] = 60%/40%)
gap 0 Space between children (in page units)
margin 0 Inner padding of the container

Nested Layouts

Containers can be nested for complex layouts:

layout:
  type: col
  ratios: [1, 2]  # Top row 1/3 height, bottom row 2/3
  children:
    - id: A
      file: "header.pdf"
    - type: row
      ratios: [1, 1]
      gap: 5
      children:
        - id: B
          file: "left.pdf"
        - id: C
          file: "right.pdf"

This creates:

  • Panel A spanning the full width in the top third
  • Panels B and C side-by-side in the bottom two-thirds

Auto Layout (Sequence-Preserving)

Use type: auto to let figquilt compute an ordered layout from a panel sequence:

page:
  width: 180
  height: 120
  margin: 6

layout:
  type: auto
  auto_mode: best          # best | one-column | two-column
  size_uniformity: 0.8     # 0..1, higher favors similar panel sizes
  main_scale: 2.5          # weight applied to role: main panels
  gap: 4
  children:
    - id: B
      file: "panels/main_overview.pdf"
      role: main
    - id: A
      file: "panels/detail_a.pdf"
    - id: C
      file: "panels/detail_c.pdf"
      weight: 1.2
    - id: D
      file: "panels/detail_d.pdf"

Notes:

  • Child order is preserved in reading order (left-to-right, then top-to-bottom).
  • figquilt keeps panel aspect ratios; it does not non-uniformly distort panel geometry.
  • role: main and weight provide soft prominence hints when auto-laying out panels.

Editor Autocomplete (JSON Schema)

For autocomplete and validation in your editor, reference the JSON schema in your layout file:

# yaml-language-server: $schema=https://raw.githubusercontent.com/yy/figquilt/main/schema/layout.schema.json
page:
  width: 180
  height: 120

panels:
  - id: A
    file: "plots/scatter.pdf"
    # ... your editor will now provide autocomplete for all fields

This works with YAML Language Server in VS Code (via the YAML extension) and other editors.

Metadata

Release files for figquilt 0.3.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for figquilt 0.3.0
File Size Uploaded
figquilt-0.3.0.tar.gz 24.3 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for figquilt 0.3.0
File Interpreter ABI Platform
figquilt-0.3.0-py3-none-any.whl Python 3 none any Details

Total release size: 53.3 kB

Release files / figquilt-0.3.0.tar.gz

Download URL figquilt-0.3.0.tar.gz
Size 24.3 kB
Tags Source
SHA-256 checksum
How to use checksums
944030eb1934c31322e78c82800644ed2c65858017d9540a0f4e4ca2a0cf5bb6
BLAKE2b-256 checksum
How to use checksums
05d38689e56a87191a63fbda16054bf02e2f6546cc27fe101cbfa71f711969a4
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.9.18 {"installer":{"name":"uv","version":"0.9.18","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release files / figquilt-0.3.0-py3-none-any.whl

Download URL figquilt-0.3.0-py3-none-any.whl
Size 29.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
7b76f36800b10187178b8326af6d5cdf0013ed55b634beb81c7914adb2ba4f9d
BLAKE2b-256 checksum
How to use checksums
6832030a2c980d63ce937e519f5d0fbca220324ce20b207b61f5eab050dc2e3a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.9.18 {"installer":{"name":"uv","version":"0.9.18","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release history Release notifications | RSS feed

This release

0.3.0 This release

2 release files

0.2.1

2 release files

0.1.8

2 release files

0.1.7

2 release files

0.1.6

2 release files

0.1.5

2 release files

0.1.4

2 release files

0.1.3

2 release files

0.1.2

2 release files

0.1.1

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page