Pure-Rust PDF writer for the oxideav framework. Round 1 emits a
single-page PDF 1.4 document from a vector-graphics
VectorFrame — paths stay paths, fills
stay fills, no rasterisation along the way. Zero C dependencies.
Part of the oxideav
framework — a 100% pure Rust media stack. No C libraries, no FFI
wrappers, no *-sys crates.
- Paths:
MoveTo(m),LineTo(l),CubicCurveTo(c),QuadCurveTo(lifted to cubic via the2/3 * (control - endpoint)trick),ArcTo(flattened to cubic per SVG 1.1 Appendix F.6.5),Close(h). - Fills:
Paint::Solid(DeviceRGBsc),Paint::LinearGradient(axial pattern shading,Pattern Type 2+Function Type 2),Paint::RadialGradient(radial shading,Function Type 3). - Strokes: width (
w), cap (J), join (j), miter limit (M), dash pattern (d). - Transforms: every
Group::transformemits onecmoperator. - Groups:
q ... Qsave/restore brackets around children. Group opacity becomes anExtGStateresource referenced via/GSx gs. - Clip paths: emitted before the children's content stream as
W n(orW* nfor even-odd fill rule). - Fill rules:
NonZero(f/B) vs.EvenOdd(f*/B*). - Embedded raster:
ImageRefwhose underlyingVideoFrameis RGBA8 lands as a FlateDecodeImageXObject and is painted withDo.
- Text (waiting on
Node::Text; will use Type 0 fonts with a CIDFont built viaoxideav-ttf/oxideav-otf). - JPEG passthrough on
ImageRef(DCTDecode XObject). - Multi-page documents.
- Transparency groups beyond a per-
Group/ca+/CAopacity. - PDF reading (this crate is write-only by design — a parser would be
a separate
oxideav-pdf-parsecrate).
[dependencies]
oxideav-core = "0.1"
oxideav-pdf = "0.0"use oxideav_core::{
FillRule, Group, Node, Paint, Path, PathNode, Point, Rgba, VectorFrame,
};
use oxideav_core::TimeBase;
let mut p = Path::new();
p.move_to(Point::new(10.0, 10.0))
.line_to(Point::new(110.0, 10.0))
.line_to(Point::new(110.0, 60.0))
.line_to(Point::new(10.0, 60.0))
.close();
let frame = VectorFrame {
width: 200.0,
height: 100.0,
view_box: None,
root: Group {
children: vec![Node::Path(PathNode {
path: p,
fill: Some(Paint::Solid(Rgba::opaque(0xFF, 0x80, 0x00))),
stroke: None,
fill_rule: FillRule::NonZero,
})],
..Group::default()
},
pts: None,
time_base: TimeBase::new(1, 1),
};
let pdf = oxideav_pdf::write_pdf(&frame).expect("vector → PDF");
std::fs::write("out.pdf", pdf).unwrap();
# Ok::<(), Box<dyn std::error::Error>>(())MIT — see LICENSE.