Skip to content

Architecture

Noah Baldwin edited this page Jul 21, 2026 · 7 revisions

OXVG's artifacts are distributed in the following ways

Overview

OXVG processed documents through a series of tasks. Whether this is optimising, linting, or transforming it always runs as a set of tasks against a parsed document.

Phases

stateDiagram-v2
  [*] --> Parse

  state fork_state <<fork>>
    Parse --> fork_state
    fork_state --> markup5ever
    fork_state --> roxmltree

    state join_state <<join>>
    markup5ever --> join_state
    roxmltree --> join_state

    note left of join_state
      parsers build to common tree representation
    end note

    join_state --> Process

    note right of Process
      processors visit and may may manipulate the tree
    end note

    Process --> Output
    Output --> [*]
Loading

OXVG will process documents in three phases

  1. Parse an SVG document using one of the parsers in oxvg_ast::parse into a tree represented as oxvg_ast::node::Ref
  2. The tasks to process the document (e.g. those in oxvg_optimiser::jobs) will each run against the document by implementing oxvg_ast::visitor::Visitor to walk across the document and manipulate it as needed.
  3. Once the tasks are completed, oxvg will output the result as needed. This is usually by serializing the tree using oxvg_ast::serialize::Node

Tree Representation

There are two main parts to the representation of an SVG

  • AST, in oxvg_ast which represents the tree of nodes in an SVG document
  • Collections, in oxvg_collections, which represents the different types of elements, attributes, and data in an SVG document

Processors

There are three planned processors

  • Optimiser, in oxvg_optimiser, to shrink an SVG document
  • Linter, in oxvg_lint, to report errors to stdout or lang-server
  • Actions, in oxvg_actions, to process commands sent by an editor to manipulate the document

Parallelisation

What can be parellelised should be parallelised to achieve high performance. Threads should be used for dividing operations and async for handling non-blocking io.

Process Threadable/Asyncable? Threaded/Async?
Walking Threadable Threaded
Visitor Threadable (when immutable) No
Optimisation Jobs No (Mutates shared document) No
Linting attribute list Threadable Threaded
Linting elements Threadable No
Linting LSP Asyncable Async

Paths

SVG paths are handled in the oxvg_path crate, it's main purpose is to provide various path representations and the means to convert between them. Each path representation comes with a set of available features.

SVG paths (oxvg_path::paths::svg)

SVG paths are designed to be as compressible as possible in a string representation. A path is a set of commands which consist of a command, specified by a character followed by a fixed number of float arguments.

In OXVG, this is primarily used for parsing and serializing paths from SVG documents.

  • Path is a set of Data
  • Data is an enum of 20 variants
  • Data::* variants each have a fixed array of f64 arguments.
  • Data::Implicit is a special recursive variant that omits the command char.

Serializing

By only parsing and serializing a path, you will be given the smallest non-destructive representation of the path as possible.

  • Each Data::* variant has an associated char
  • Each f64 of Data has the following possible variations of printing
    • A signed/unsigned float (e.g. -0.1)
    • A signed/unsigned integer (e.g. 1)
    • A signed/unsigned exponential (e.g. 1e6 or 1e-6)
  • Each pair of f64 Data has the following possible variations of printing
    • A normal pair (e.g. 0.1 2 cannot have it's space omitted; 1 0.2 cannot have the leading 0 omitted)
    • A float followed by a float <0 (e.g 0.1.1 is 0.1 0.1)
    • Any number followed by a negative (e.g. -2.5e-6-2 is -2.5e-6 -2)
    • Implicit data can also make use of these rules (e.g. M0.1-2-5.1.6 is M0.1 -2 L-5.1 0.6)

Segment paths (oxvg_path::paths::svg)

Segment paths are designed to be as computationally straightforward as possible. The 20 different command types are distilled into 3 types with a richer set of methods available to them.

Segment paths has methods more useful for manipulation and calculations

  • iter_start_cursor and iter_start_cursor_mut iterates through the segment with it's Item providing useful context for each command visited.
  • oxvg_path::algorithm with various geospacial algorithms
  • simplify, used as part of oxvg_path::svg::Path::optimize to clean up and minify path data.
    • Some of options in oxvg_path::optimize::Options are directly applied by this method
  • from_svg and to_svg to convert from oxvg_path::svg::Path
    • By converting between representations, you will be given an improved representation by selecting the best options of relative/absolute commands and line/curve shorthands

  • Path is a set of Segments
  • Segment represents M/m commands as it's start property
  • Segment has a set of Data
  • Data is an enum of 3 variants
  • Data::LineTo contains Point representing the equialent L command and arguments
  • Data::CurveTo contains Curve representing the equivalent C command and arguments
  • Data::ArcTo contains Arc representing the equivalent A command with it's arguments converted to a computationally friendly representation.

Binary

The binary is defined in crates/oxvg/ and allows a user to invoke OXVG's features from the CLI.

Ideals

  • Unix Philosophy. Each command should "do one thing"
  • Ease of use. Each command can break "do one thing" within reason (e.g. for walking directories)

Overview

Individual features

These are the individual features that can be invoked by a command. The planned features are as follows

  1. Optimise
  2. Format
  3. Lint
  4. Actions
  5. TUI/Editor

Common features

These are the features that may be shared between commands

  1. Walking directories
  2. Config loading

Developing

All development should handled through cargo for crates and pnpm for packages.

Essential Dependencies

The following dependencies are essential to the OXVG binary

  1. lightningcss & cssparser for stylesheets and presentation attributes
  2. roxmltree/markup5ever for parsing
  3. selectors for querying styles
  4. typed-arena for tree allocations

The following dependencies are essential for the NAPI package

  1. napi for linking node to rust libraries

The following dependencies are essential for the WASM package

  1. wasm-bindgen for compiling rust libraries to wasm
  2. tsify for typescript codegen

Clone this wiki locally