xisf-header
Rust crate that reads and writes XISF
(Extensible Image Serialization Format) image-file headers: it extracts the
embedded FITS keywords and XISF <Property> elements, supports
create/read/update/delete on both, and serializes a header back into an XISF
container.
The crate is header-only: it parses and emits the 16-byte XISF preamble plus the UTF-8 XML header, and never reads image/pixel data.
Features
- Parse an XISF header from bytes or a file with
Header::parse/Header::read_from_file. TheXISF0100signature, the little-endian XML-length field (capped at 8 MiB), and UTF-8 encoding are validated. - Strict keyword access. A bare name must be unique or the accessor
returns
Error::Ambiguous; repeated keywords (e.g.HISTORY) are addressed with an(name, n)key or theget_all/counthelpers. - Typed reads and writes. One generic
get::<T>over the openFromFieldtrait (String,f64,i64,u32,bool, and a date/time), withget_str/get_f64/… wrappers; writes takeimpl IntoValue, so the Rust type chooses string vs. bare-literal formatting. <Property>round-trip. XISF properties keep theirtype,comment, andformatattributes verbatim. AStringproperty written as child text (<Property id=…>text</Property>) is read the same as the attribute form, and writes normalize it to avalue=attribute. Values are stored raw (XISF properties are not FITS-quoted).- Two write paths. Edit an existing file in place with
update_file— the common case — which splices only the changed keywords/properties into the file's raw bytes (byte-exact and data-preserving). To create a new file from pixel data you already have, usewrite_to_file(errors rather than overwriting an existing path); or emit just the header block withto_header_bytes(&hints)for full control over assembly. - Enumerate and bulk-edit. Read keywords in document order with
keywords/iter; apply atomic batches withset_many/remove_many, and clear every occurrence of a repeated name withremove_all. - No
unsafe. Dependencies are pure Rust (no C/sys crates):quick-xml,thiserror,time, and optionalserde. MSRV 1.82.
Install
[]
= "0.2"
Optional features
-
serde— deriveSerialize/DeserializeonHeader,FitsKeyword,Property, and the value types:= { = "0.2", = ["serde"] }
Usage
Parse a header and read keywords
Header::parse
reads a byte buffer into a
Header.
use Header;
let bytes = read?;
let header = parse?;
// Typed reads are strict: `?` surfaces a duplicate-keyword ambiguity; the inner
// `Option` is `None` when the keyword is absent or unreadable as that type.
let exposure = header.get_f64?;
let image_type = header.get_str?;
// XISF <Property> access.
let focal_length_m = header.;
# Ok::
Create, read, update, delete
Header::new
starts empty;
set,
set_comment,
set_with_comment,
and
remove
edit it in place.
use Header;
let mut header = new;
// `set` upserts: update a unique keyword in place, or append when absent. The
// Rust type of the value chooses its on-disk form — strings are quoted,
// numbers and logicals are bare literals.
header.set?;
header.set_comment?;
header.set?;
header.set?;
assert_eq!;
assert_eq!;
header.set?; // update
header.remove?; // delete
# Ok::
Repeated keywords
append
adds an occurrence unconditionally; read every occurrence with
get_all,
and select, update, or remove one with an
(name, n)
key.
use Header;
let mut header = new;
header.append?;
header.append?;
// A bare name is ambiguous once it repeats — select an occurrence instead.
assert!;
assert_eq!;
assert_eq!;
// Read every occurrence in order.
assert_eq!;
// Update one occurrence in place.
header.set?;
assert_eq!;
// Remove one occurrence (or clear them all with `remove_all`).
header.remove?;
assert_eq!;
# Ok::
FITS keywords vs XISF properties
Two different metadata namespaces:
- FITS keywords (
set/get/append) — embedded<FITSKeyword name=… value=… comment=…/>elements carried over for FITS compatibility. Names are ≤ 8 uppercase ASCII characters and can repeat (e.g.HISTORY). Use these for metadata other FITS-aware tools need to read. - XISF properties (
set_property/property/set_property_with_type) — native XISF<Property>elements. Ids are colon-delimited and hierarchical (e.g.Observation:Object:Name), carry an explicit XISF type, and are unique per id. Use these for XISF-native structured metadata.
XISF properties
set_property
and
set_property_with_type
write a
Property
entry;
remove_property
deletes one by id.
use Header;
let mut header = new;
// Plain `set_property` creates a `String` property; an explicit XISF type is
// kept on the property and survives round-trips.
header.set_property?;
header.set_property_with_type?;
assert_eq!;
assert_eq!;
assert_eq!;
# Ok::
Controlled numeric formatting
Fixed
and
Sci
wrap an f64 for fixed-point or scientific-notation output; both implement
IntoValue.
use ;
let mut header = new;
header.set?; // fixed-point, 2 decimals
assert_eq!;
# Ok::
Edit a file's header in place
The common path: change a keyword or property on an existing XISF file without touching its pixel data or unmodeled XML.
Header::update_file
reads a file's header, applies an edit closure, and splices the result back
into the file. It is byte-exact and data-preserving: everything outside the
edited <FITSKeyword>/<Property> elements — unmodeled XML (Metadata,
Resolution, thumbnails, …), whitespace, and the attached pixel data —
survives untouched, and a no-op edit reproduces the file byte-for-byte. If an
edit changes the header's length, the <Image location> offset is
recomputed and the original data is moved (unchanged) to the new offset.
use Header;
update_file?;
# Ok::
update_file targets the common single-image layout: exactly one <Image location="attachment:…"> element. A file with zero or multiple attachments
(e.g. a Thumbnail alongside the Image) is rejected with
Error::Unsupported
rather than risking data loss.
Create a new file
Use this only when you're assembling a fresh XISF file from pixel data you
already have — not for editing one that exists (that's update_file above).
Header::write_to_file
writes the preamble, the XML header (<Image> filled in from
StructuralHints),
and your data bytes to a new file; it errors if path already exists
rather than overwriting it, and never fabricates pixel data — data is
always the caller's own.
use ;
let mut header = new;
header.set?;
let hints = default; // 1x1x1 8-bit grayscale = 1 byte
let data = ; // the caller's own pixel data
header.write_to_file?;
let reloaded = read_from_file?;
assert_eq!;
# remove_file.ok;
# Ok::
Documentation
- Quickstart guide — a task-oriented walkthrough backed
by
examples/quickstart.rs. - docs.rs/xisf-header — full API
documentation generated from the source doc comments, published for every
release (all features enabled). Build it locally with
cargo doc --no-deps --all-features --open. Every public item is documented; CI fails the build on missing or broken documentation.
License
This project is licensed under the Mozilla Public License 2.0 — see LICENSE for details.
You can use this library in closed-source projects. If you modify any of the source files in this library, the modified files must be made available under the MPL-2.0 when distributed.