A pure Rust implementation for parsing 3MF (3D Manufacturing Format) files with no unsafe code.
Note: Most part of this code has been vibe-coded using GitHub Copilot Agents
This library provides a pure Rust implementation for reading and parsing 3MF files, which are ZIP-based containers following the Open Packaging Conventions (OPC) standard and containing XML-based 3D model data.
The 3MF format is the modern standard for 3D printing, supporting rich model information including:
- 3D mesh geometry (vertices and triangles)
- Materials and colors
- Metadata
- Build specifications
- And more
- ✅ Pure Rust implementation - No C/C++ dependencies
- ✅ No unsafe code - Enforced with
#![forbid(unsafe_code)] - ✅ Extension support - Configurable support for 3MF extensions with validation
- ✅ Parse 3MF file structure (ZIP/OPC container)
- ✅ Read 3D model data including meshes, vertices, and triangles
- ✅ Write/Serialize 3MF files - Create and write 3MF files
- ✅ Support for materials and colors
- ✅ Metadata extraction and writing
- ✅ Build item specifications
- ✅ Comprehensive error handling
- ✅ Round-trip support (read-write-read)
Add this to your Cargo.toml:
[dependencies]
lib3mf = "0.1"The library provides optional features that can be enabled as needed:
The mesh-ops feature enables advanced geometric operations on meshes using computational geometry libraries (parry3d and nalgebra). This feature is enabled by default and provides:
- Volume calculation (signed volume for mesh orientation detection)
- Axis-aligned bounding box (AABB) computation
- BVH-based mesh validation
- Mesh subdivision (midpoint and Loop algorithms)
- Mesh-plane slicing with contour extraction
- Vertex normal calculation
When to disable:
- You only need to parse/write 3MF files without geometric computations
- You want to minimize compilation time and dependency tree
To disable (reduces dependencies):
[dependencies]
lib3mf = { version = "0.1", default-features = false }To explicitly enable:
[dependencies]
lib3mf = { version = "0.1", features = ["mesh-ops"] }Note: Some examples require this feature: calculate_normals, mesh_analysis, mesh_slicing_demo, mesh_subdivision.
The polygon-ops feature enables polygon clipping and triangulation operations using the clipper2 and earcutr libraries. This feature is enabled by default and provides:
- Polygon boolean operations (union, intersection, difference, XOR)
- Self-intersection resolution for slice polygons
- Ear-clipping triangulation of polygons with or without holes
When to disable:
- You parse/write 3MF files without processing slice polygon data
- You don't need polygon clipping or triangulation operations
- You want to minimize compilation time and dependency tree
To disable (reduces dependencies):
[dependencies]
lib3mf = { version = "0.1", default-features = false }To explicitly enable:
[dependencies]
lib3mf = { version = "0.1", features = ["polygon-ops"] }Note: Some examples require this feature: polygon_clipping_demo, polygon_triangulation_demo.
The crypto feature enables decryption support for the Secure Content extension. This pulls in cryptographic dependencies (rsa, aes-gcm, base64, flate2, sha1, sha2) required for decrypting encrypted 3MF files using test keys.
When to enable:
- You need to decrypt 3MF files encrypted with Suite 8 test keys
- You're running conformance tests that include encrypted files
Default: Disabled (most users don't need decryption)
[dependencies]
lib3mf = { version = "0.1", features = ["crypto"] }Note: The crypto feature only provides test key decryption for conformance validation. For production applications, use the KeyProvider trait to implement custom decryption with your own keys. The Secure Content extension metadata (consumers, keystores, encryption parameters) is always available regardless of the feature flag.
use lib3mf::Model;
use std::fs::File;
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Open and parse a 3MF file
let file = File::open("model.3mf")?;
let model = Model::from_reader(file)?;
// Access model information
println!("Unit: {}", model.unit);
println!("Objects: {}", model.resources.objects.len());
// Iterate through objects
for obj in &model.resources.objects {
if let Some(ref mesh) = obj.mesh {
println!("Object {} has {} vertices and {} triangles",
obj.id, mesh.vertices.len(), mesh.triangles.len());
}
}
Ok(())
}use lib3mf::{Model, Object, Mesh, Vertex, Triangle, BuildItem};
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Create a new model
let mut model = Model::new();
model.unit = "millimeter".to_string();
// Create a mesh with a simple triangle
let mut mesh = Mesh::new();
mesh.vertices.push(Vertex::new(0.0, 0.0, 0.0));
mesh.vertices.push(Vertex::new(10.0, 0.0, 0.0));
mesh.vertices.push(Vertex::new(5.0, 10.0, 0.0));
mesh.triangles.push(Triangle::new(0, 1, 2));
// Create an object with the mesh
let mut object = Object::new(1);
object.name = Some("Triangle".to_string());
object.mesh = Some(mesh);
// Add object to resources
model.resources.objects.push(object);
// Add to build
model.build.items.push(BuildItem::new(1));
// Write to file
model.write_to_file("output.3mf")?;
Ok(())
}use lib3mf::Model;
use std::fs::File;
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Read an existing 3MF file
let file = File::open("input.3mf")?;
let model = Model::from_reader(file)?;
// Modify the model
// ... make changes ...
// Write back to a new file
model.write_to_file("output.3mf")?;
Ok(())
}For very large files, use the streaming parser to process objects one at a time without loading everything into memory:
use lib3mf::streaming::StreamingParser;
use std::fs::File;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let file = File::open("large_model.3mf")?;
let mut parser = StreamingParser::new(file)?;
// Process objects one at a time
for object in parser.objects() {
let object = object?;
if let Some(ref mesh) = object.mesh {
println!("Object {}: {} vertices",
object.id, mesh.vertices.len());
}
// Object is dropped here, freeing memory
}
Ok(())
}The Production Extension provides UUIDs and routing paths for manufacturing workflows:
use lib3mf::Model;
use std::fs::File;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let file = File::open("production_model.3mf")?;
let model = Model::from_reader(file)?;
// Access production UUIDs from objects
for obj in &model.resources.objects {
if let Some(ref production) = obj.production {
println!("Object {} has production UUID: {}",
obj.id, production.uuid.as_deref().unwrap_or("<none>"));
if let Some(ref path) = production.path {
println!(" Production path: {}", path);
}
}
}
// Access production UUID from build element
if let Some(ref build_uuid) = model.build.production_uuid {
println!("Build has production UUID: {}", build_uuid);
}
// Access production UUIDs from build items
for item in &model.build.items {
if let Some(ref item_uuid) = item.production_uuid {
println!("Build item {} has production UUID: {}",
item.objectid, item_uuid);
}
}
Ok(())
}The library parses displacement extension data into accessible structures:
use lib3mf::Model;
use std::fs::File;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let file = File::open("displaced_model.3mf")?;
let model = Model::from_reader(file)?;
// Access displacement maps
for disp_map in &model.resources.displacement_maps {
println!("Displacement map {} at path: {}", disp_map.id, disp_map.path);
println!(" Channel: {:?}, Filter: {:?}", disp_map.channel, disp_map.filter);
println!(" Tile U: {:?}, Tile V: {:?}", disp_map.tilestyleu, disp_map.tilestylev);
}
// Access normalized vector groups
for nvgroup in &model.resources.norm_vector_groups {
println!("NormVectorGroup {} with {} vectors", nvgroup.id, nvgroup.vectors.len());
for (i, vec) in nvgroup.vectors.iter().enumerate() {
println!(" Vector {}: ({}, {}, {})", i, vec.x, vec.y, vec.z);
}
}
// Access displacement coordinate groups
for d2dgroup in &model.resources.disp2d_groups {
println!("Disp2DGroup {} using displacement map {} and vectors {}",
d2dgroup.id, d2dgroup.dispid, d2dgroup.nid);
println!(" Height: {}, Offset: {}", d2dgroup.height, d2dgroup.offset);
println!(" Coordinates: {} entries", d2dgroup.coords.len());
}
// Access advanced materials (Materials Extension)
// Texture2D resources
for texture in &model.resources.texture2d_resources {
println!("Texture2D {}: path={}, type={}",
texture.id, texture.path, texture.contenttype);
}
// Texture2D groups with UV coordinates
for tex_group in &model.resources.texture2d_groups {
println!("Texture2DGroup {} references texture {}, {} coordinates",
tex_group.id, tex_group.texid, tex_group.tex2coords.len());
}
// Composite materials
for comp in &model.resources.composite_materials {
println!("CompositeMaterials {} mixes base materials: {:?}",
comp.id, comp.matindices);
}
// Multi-properties for layered material effects
for multi in &model.resources.multi_properties {
println!("MultiProperties {} layers property groups: {:?}",
multi.id, multi.pids);
}
Ok(())
}The Beam Lattice Extension is fully supported with complete data extraction:
use lib3mf::Model;
use std::fs::File;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let file = File::open("lattice_model.3mf")?;
let model = Model::from_reader(file)?;
// Access beam lattice structures
for obj in &model.resources.objects {
if let Some(ref mesh) = obj.mesh {
if let Some(ref beamset) = mesh.beamset {
println!("BeamSet found!");
println!(" Default radius: {} mm", beamset.radius);
println!(" Minimum length: {} mm", beamset.min_length);
println!(" Cap mode: {:?}", beamset.cap_mode); // Sphere or Butt
println!(" Total beams: {}", beamset.beams.len());
// Access individual beams
for beam in &beamset.beams {
print!(" Beam: vertex {} -> vertex {}", beam.v1, beam.v2);
// Check for per-beam radii
if let Some(r1) = beam.r1 {
print!(", r1={} mm", r1);
}
if let Some(r2) = beam.r2 {
print!(", r2={} mm", r2);
}
println!();
}
}
}
}
Ok(())
}The Materials Extension now supports advanced features for full-color 3D printing:
use lib3mf::Model;
use std::fs::File;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let model = Model::from_reader(File::open("model.3mf")?)?;
// Access Texture2D resources for applying images to models
for texture in &model.resources.texture2d_resources {
println!("Texture: {} ({})", texture.path, texture.contenttype);
println!(" Tile: u={:?}, v={:?}", texture.tilestyleu, texture.tilestylev);
println!(" Filter: {:?}", texture.filter);
}
// Access texture coordinate mappings
for tex_group in &model.resources.texture2d_groups {
for (i, coord) in tex_group.tex2coords.iter().enumerate() {
println!(" Coord[{}]: u={}, v={}", i, coord.u, coord.v);
}
}
// Access composite materials (mixing base materials)
for comp in &model.resources.composite_materials {
for composite in &comp.composites {
println!(" Mix ratios: {:?}", composite.values);
}
}
// Access multi-properties (layered materials)
for multi in &model.resources.multi_properties {
println!(" Blend methods: {:?}", multi.blendmethods);
for m in &multi.multis {
println!(" Indices: {:?}", m.pindices);
}
}
Ok(())
}3MF files can require specific extensions beyond the core specification. You can control which extensions your application supports:
use lib3mf::{Model, ParserConfig, Extension};
use std::fs::File;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let file = File::open("model.3mf")?;
// Configure which extensions you support
let config = ParserConfig::new()
.with_extension(Extension::Material)
.with_extension(Extension::Production);
// Parse with validation - will reject files requiring unsupported extensions
let model = Model::from_reader_with_config(file, config)?;
// Check what extensions are required by the file
for ext in &model.required_extensions {
println!("File requires extension: {}", ext.name());
}
Ok(())
}The parser supports the following extensions:
Extension::Core- Core 3MF specification (always supported)Extension::Material- Materials & PropertiesExtension::Production- Production information (UUIDs, paths)Extension::Slice- Slice data for layer-by-layer manufacturingExtension::BeamLattice- Beam and lattice structuresExtension::SecureContent- Digital signatures and encryptionExtension::BooleanOperations- Volumetric designExtension::Displacement- Surface displacement mapsExtension::Volumetric- Volumetric data (voxel grids and implicit volumes)
The library includes polygon clipping operations for resolving self-intersections in slice data:
use lib3mf::polygon_clipping::resolve_self_intersections;
use lib3mf::model::{SlicePolygon, SliceSegment, Vertex2D};
// Create a slice polygon
let vertices = vec![
Vertex2D::new(0.0, 0.0),
Vertex2D::new(100.0, 0.0),
Vertex2D::new(100.0, 100.0),
Vertex2D::new(0.0, 100.0),
];
let mut polygon = SlicePolygon::new(0);
polygon.segments.push(SliceSegment::new(1));
polygon.segments.push(SliceSegment::new(2));
polygon.segments.push(SliceSegment::new(3));
// Resolve any self-intersections
let mut result_vertices = Vec::new();
let clean_polygons = resolve_self_intersections(
&polygon,
&vertices,
&mut result_vertices
).expect("Failed to resolve self-intersections");The polygon clipping module provides:
- Self-intersection resolution - Clean up invalid slice polygons
- Union operations - Combine overlapping slice regions
- Intersection operations - Find overlapping areas
- Difference operations - Subtract one region from another
Based on the Clipper2 library (successor to polyclipping used in C++ lib3mf).
You can register and handle custom/proprietary 3MF extensions with callback handlers:
use lib3mf::{Model, ParserConfig, CustomExtensionContext, CustomElementResult};
use std::sync::Arc;
use std::fs::File;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let file = File::open("model_with_custom_ext.3mf")?;
// Register a custom extension with element and validation handlers
let config = ParserConfig::new()
.with_custom_extension_handlers(
"http://example.com/myextension/2024/01", // Namespace URI
"MyExtension", // Human-readable name
// Element handler - called when custom elements are encountered
Arc::new(|ctx: &CustomExtensionContext| -> Result<CustomElementResult, String> {
println!("Custom element: {}", ctx.element_name);
println!("Attributes: {:?}", ctx.attributes);
// Process the custom element here
Ok(CustomElementResult::Handled)
}),
// Validation handler - called during model validation
Arc::new(|model| -> Result<(), String> {
// Add custom validation rules here
if model.resources.objects.is_empty() {
return Err("Custom validation failed".to_string());
}
Ok(())
})
);
let model = Model::from_reader_with_config(file, config)?;
// Check custom extensions required by the file
for namespace in &model.required_custom_extensions {
println!("Custom extension: {}", namespace);
}
Ok(())
}Custom extension features:
- Element handlers - Process custom XML elements from your extension
- Validation callbacks - Add custom validation rules for your extension data
- Multiple extensions - Register multiple custom extensions simultaneously
- Error handling - Custom handlers can return errors for invalid data
See examples/custom_extension.rs for a complete working example.
The library includes comprehensive mesh operation capabilities using the parry3d crate for accurate geometric computations:
use lib3mf::{mesh_ops, Model};
use std::fs::File;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let file = File::open("model.3mf")?;
let model = Model::from_reader(file)?;
for object in &model.resources.objects {
if let Some(ref mesh) = object.mesh {
// Compute signed volume (detects inverted meshes)
let signed_volume = mesh_ops::compute_mesh_signed_volume(mesh)?;
if signed_volume < 0.0 {
println!("Warning: Object {} has inverted triangles", object.id);
}
// Compute absolute volume
let volume = mesh_ops::compute_mesh_volume(mesh)?;
println!("Volume: {} cubic units", volume);
// Compute bounding box
let (min, max) = mesh_ops::compute_mesh_aabb(mesh)?;
println!("AABB: min={:?}, max={:?}", min, max);
}
}
// Analyze build items with transformations
for item in &model.build.items {
let object = model.resources.objects
.iter()
.find(|obj| obj.id == item.objectid);
if let Some(object) = object {
if let Some(ref mesh) = object.mesh {
// Compute transformed bounding box
let (min, max) = mesh_ops::compute_transformed_aabb(
mesh,
item.transform.as_ref()
)?;
println!("Transformed AABB: min={:?}, max={:?}", min, max);
}
}
}
// Compute overall build volume
if let Some((min, max)) = mesh_ops::compute_build_volume(&model) {
println!("Build Volume: {:?} to {:?}", min, max);
}
Ok(())
}Mesh Operations Features:
- Volume Computation: Signed volume for orientation detection, absolute volume for manufacturing
- Bounding Boxes: Axis-aligned bounding boxes (AABB) for spatial queries
- Transformations: Apply affine transforms and compute transformed bounding boxes
- Build Volume: Calculate overall build volume encompassing all build items
- Validation Support: Used internally for mesh orientation validation (N_XXX_0416, N_XXX_0421)
See examples/mesh_analysis.rs for a complete demonstration.
The library provides mesh subdivision utilities for increasing vertex density, which is essential for displacement mapping and other mesh processing operations:
use lib3mf::{subdivide_simple, subdivide, Mesh, Vertex, Triangle, SubdivisionMethod, SubdivisionOptions};
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Create a simple mesh
let mut mesh = Mesh::new();
mesh.vertices.push(Vertex::new(0.0, 0.0, 0.0));
mesh.vertices.push(Vertex::new(10.0, 0.0, 0.0));
mesh.vertices.push(Vertex::new(5.0, 10.0, 0.0));
mesh.triangles.push(Triangle::new(0, 1, 2));
// Simple midpoint subdivision
let subdivided = subdivide_simple(&mesh, 2);
println!("After 2 levels: {} triangles", subdivided.triangles.len()); // 16 triangles
// Using subdivision options
let options = SubdivisionOptions {
method: SubdivisionMethod::Midpoint,
levels: 1,
preserve_boundaries: true,
interpolate_uvs: true,
};
let subdivided = subdivide(&mesh, &options);
// Note: Loop subdivision is planned but not yet implemented
// Currently it produces the same result as Midpoint subdivision
Ok(())
}Subdivision Features:
- Midpoint Subdivision: Fast, simple subdivision that splits each triangle into 4
- Loop Subdivision: Planned feature for smoother surfaces (currently same as Midpoint)
- Property Preservation: Triangle properties (pid, p1, p2, p3) are preserved during subdivision
- Shared Edge Optimization: Vertices on shared edges are reused to avoid duplicates
- Iterative Subdivision: Apply subdivision multiple times for higher density
Growth Rates:
- Level 1: 4× triangles (1 → 4)
- Level 2: 16× triangles (1 → 16)
- Level 3: 64× triangles (1 → 64)
- Level 4: 256× triangles (1 → 256)
Use Cases:
- Displacement map rendering - increase vertex density for surface detail
- Mesh refinement - improve triangle quality before operations
- LOD generation - create multiple detail levels
See examples/mesh_subdivision.rs for a complete demonstration.
The repository includes several comprehensive examples demonstrating different features:
cargo run --example parse_3mfcargo run --example extension_support test_files/material/kinect_scan.3mf all
cargo run --example extension_support test_files/material/kinect_scan.3mf core-onlyConvert 3MF files to STL (for 3D printing):
cargo run --example export_to_stl test_files/core/box.3mf output.stlConvert 3MF files to OBJ (for 3D modeling):
cargo run --example export_to_obj test_files/core/box.3mf output.objcargo run --example validation_errors test_files/core/box.3mf permissive
cargo run --example validation_errors test_files/core/box.3mf strictcargo run --example build_transformations test_files/core/box.3mfcargo run --example mesh_analysis test_files/core/box.3mfcargo run --example extract_colors test_files/material/kinect_scan.3mfThe repository includes a comprehensive command-line viewer tool for analyzing and visualizing 3MF files:
# Navigate to the viewer directory
cd tools/viewer
# View a 3MF file with basic information
cargo run --release -- ../../test_files/core/box.3mf
# View with detailed mesh information
cargo run --release -- ../../test_files/material/kinect_scan.3mf --detailed
# Export a wireframe preview image
cargo run --release -- ../../test_files/core/cylinder.3mf --export-preview preview.png
# Show all vertices and triangles (very verbose)
cargo run --release -- ../../test_files/core/box.3mf --show-allThe viewer displays:
- Model information (unit, namespace, extensions)
- Metadata entries
- Object and mesh details (vertex/triangle counts, bounding boxes)
- Materials and color groups
- Build items and transformations
- Wireframe preview export capability
See tools/viewer/README.md for complete documentation.
The repository includes a command-line slicer tool for generating slice images from 3MF models:
# Navigate to the slicer directory
cd tools/slicer
# Slice a 3MF file with a configuration
cargo run --release -- ../../test_files/core/box.3mf config.json
# With custom output directory and verbose output
cargo run --release -- ../../test_files/core/box.3mf config.json --output slices --verbose
# Build with crypto support for encrypted files
cargo build --release --features cryptoThe slicer requires a JSON configuration file with:
- Slice thickness in micrometers
- Printable box dimensions (origin and end points in mm)
- Output image resolution in DPI (dots per inch)
- Optional encryption key file path
- Optional feature support flags
Example configuration:
{
"slice_thickness_um": 100,
"printable_box": {
"origin": { "x": 0.0, "y": 0.0, "z": 0.0 },
"end": { "x": 200.0, "y": 200.0, "z": 200.0 }
},
"resolution": {
"dpi": 300
}
}The slicer generates PNG images for each slice layer, supporting:
- Standard mesh geometries with affine transforms
- Materials and surface colors
- Multiple objects positioned via build items
- All major 3MF extensions
See tools/slicer/README.md for complete documentation and examples.
The library is organized into several modules:
model- Data structures representing 3MF models (vertices, triangles, meshes, objects, etc.)opc- Open Packaging Conventions (OPC) handling for ZIP-based containersparser- XML parsing for 3D model fileserror- Comprehensive error types with detailed messages
This implementation currently supports:
-
Core 3MF Specification
- Model structure (resources, build, metadata)
- Mesh geometry (vertices, triangles)
- Object definitions
- Build items with transformations
- Basic materials and colors
-
Materials Extension
- ✅ Color groups (m:colorgroup)
- ✅ Per-triangle material references (pid attributes)
- ✅ Base materials with display colors
- ✅ Texture2D resources with image paths and content types
- ✅ Texture2DGroup with UV texture coordinates
- ✅ Composite materials mixing base materials in defined ratios
- ✅ Multi-properties for layering and blending property groups
-
Displacement Extension
- Displacement2D resources (displacement maps with PNG textures)
- NormVectorGroup (normalized displacement vectors)
- Disp2DGroup (displacement coordinate groups)
- Displacement coordinates (u, v, n, f values)
- Texture filtering and tiling modes
- Surface displacement data structures
The parser supports conditional extension validation, allowing consumers to specify which 3MF extensions they support. When a 3MF file declares required extensions via the requiredextensions attribute, the parser validates that all required extensions are supported before processing the file.
Supported Extensions:
- ✅ Core Specification - Fully supported (always enabled)
- ✅ Materials Extension - Fully supported with color groups, base materials, Texture2D, composite materials, and multi-properties
- ✅ Production Extension - UUID and path extraction from objects, build, and build items
- ✅ Slice Extension - Fully supported with slice stacks and polygons
- ✅ Beam Lattice Extension - Fully supported with BeamSet, Beam structures, radii, and cap modes
- ✅ Secure Content Extension - Recognized and validated
- ✅ Boolean Operations Extension - Recognized and validated
- ✅ Displacement Extension - Fully supported with displacement maps, normal vectors, and coordinate groups
Validation Behavior:
By default, Model::from_reader() accepts files with any known extension for backward compatibility. Use Model::from_reader_with_config() to enforce specific extension requirements:
// Only accept core files (no extensions)
let config = ParserConfig::new();
// Accept core + materials
let config = ParserConfig::new().with_extension(Extension::Material);
// Accept all known extensions
let config = ParserConfig::with_all_extensions();All extensions support full data extraction and are production-ready.
Potential future additions could include:
- Extended conformance test coverage
- Additional export formats
- Performance optimizations for very large files
- Streaming support for additional extensions
The library includes comprehensive unit, integration, and conformance tests:
# Run all tests
cargo test
# Run with output
cargo test -- --nocapture
# Run linter
cargo clippy -- -D warnings
# Run official 3MF conformance tests
cargo test --test conformance_tests summary -- --ignored --nocapture
# Run a specific conformance suite
cargo test --test conformance_tests suite3_core -- --nocaptureThe repository uses GitHub Actions for continuous integration with optimized parallel execution:
- Basic Tests Job: Runs standard library and integration tests as a fast preliminary check
- Security Audit: Automated daily dependency vulnerability scanning using
cargo-audit - Conformance Test Matrix: Runs all 11 conformance test suites in parallel for faster feedback
- suite1_core_slice_prod
- suite2_core_prod_matl
- suite3_core
- suite4_core_slice
- suite5_core_prod
- suite6_core_matl
- suite7_beam
- suite8_secure
- suite9_core_ext
- suite10_boolean
- suite11_displacement
- Conformance Summary Job: Generates an overall conformance report after all suites complete
This parallel approach significantly reduces CI execution time compared to running suites sequentially.
This library has been validated against the official 3MF Consortium test suites, which include over 2,200 test cases covering all 3MF specifications and extensions.
Current Conformance Results:
- ✅ 100% Positive Test Compliance: All 1,719 valid 3MF files parse successfully
- ✅ Negative Test Compliance: Estimated ~90% (requires test_suites to measure precisely)
- 📊 Overall Conformance: Estimated ~97.6% (improved from 77.4% baseline)
Note: Precise negative test metrics require the test_suites repository. Clone with:
git clone --depth 1 https://github.com/3MFConsortium/test_suites.git
cargo run --example analyze_negative_testsKey Validation Improvements:
- ✅ Strict color format validation - Rejects invalid hexadecimal color values
- ✅ Proper resource ID namespaces - Objects and property resources have separate ID spaces
- ✅ Duplicate metadata names - Ensures metadata uniqueness
- ✅ Duplicate resource IDs - Validates property group ID uniqueness
- ✅ Invalid XML structure - Rejects malformed models
- ✅ Comprehensive material property validation
- ✅ Triangle property reference validation
The parser successfully handles files using all 3MF extensions including:
- Core Specification (1.4.0)
- Materials & Properties Extension (1.2.1)
- Production Extension (1.2.0)
- Slice Extension (1.0.2)
- Beam Lattice Extension (1.2.0)
- Secure Content Extension (1.0.2) -
⚠️ Test-only decryption + metadata extraction - Boolean Operations Extension (1.1.1)
- Displacement Extension (1.0.0)
Important Security Note: The Secure Content extension provides:
- Test-only decryption using Suite 8 test keys for conformance validation (requires
cryptofeature) - Complete keystore metadata extraction for production applications (always available)
Files encrypted with Suite 8 test keys (consumerid="test3mf01") are automatically decrypted during parsing when the crypto feature is enabled. For production applications, access all encryption metadata (consumers, keystores, encryption parameters, access rights) and implement decryption using the KeyProvider trait with your own cryptographic libraries. Never use embedded test keys in production.
To enable test key decryption for conformance testing:
[dependencies]
lib3mf = { version = "0.1", features = ["crypto"] }See CONFORMANCE_REPORT.md for detailed test results and analysis.
The library includes comprehensive fuzzing infrastructure using cargo-fuzz and libFuzzer to discover bugs, crashes, and security vulnerabilities.
- fuzz_parse_3mf: Tests complete 3MF parsing pipeline (ZIP + XML + model construction)
- fuzz_parse_with_extensions: Tests parsing with all 7 extensions enabled
- fuzz_xml_parser: Tests XML parser robustness with malformed inputs
- fuzz_mesh_validation: Tests mesh operations (volume, AABB, slicing)
Fuzzing requires Rust nightly:
# Install nightly and cargo-fuzz
rustup install nightly
rustup default nightly
cargo install cargo-fuzz
# Run a fuzzer for 5 minutes
cargo fuzz run fuzz_parse_3mf -- -max_total_time=300
# Run all fuzzers
for target in fuzz_parse_3mf fuzz_parse_with_extensions fuzz_xml_parser fuzz_mesh_validation; do
cargo fuzz run $target -- -max_total_time=60
doneFuzzing runs automatically via GitHub Actions:
- Nightly: Every day at 2 AM UTC (5 minutes per target)
- Extended: 1-hour sessions for main parsers
- PR checks: On fuzzing infrastructure changes
Automatic Bug Reporting: When crashes are discovered during nightly fuzzing, the CI automatically:
- Analyzes the crash (type, severity, stack trace)
- Creates a detailed GitHub issue with reproduction steps
- Provides initial investigation guidance
- Prevents duplicate issues for the same crash
See fuzz/README.md for detailed fuzzing documentation and docs/FUZZING_AUTOMATION.md for details on the automated bug reporting system.
The library is optimized for parsing large 3MF files efficiently:
- Linear scaling: Performance scales linearly with file size
- Memory efficient: Streaming XML parsing with pre-allocated buffers
- Benchmarked: Comprehensive benchmark suite using criterion.rs
# Run performance benchmarks
cargo bench
# Run specific benchmark group
cargo bench -- parse_largeTypical Performance:
- Small files (1,000 vertices): ~1 ms
- Medium files (10,000 vertices): ~7 ms
- Large files (100,000 vertices): ~70 ms
This library is designed with safety in mind:
- No unsafe code - The entire codebase forbids unsafe code
- Type safety - Leverages Rust's type system for correctness
- Memory safety - All memory management is handled by Rust's ownership system
- Error handling - Comprehensive error types using
thiserror - Security audits - Automated dependency vulnerability scanning via CI
The library uses minimal, well-maintained dependencies:
zip- For reading ZIP archives (3MF container format)quick-xml- For parsing XML model filesthiserror- For error handlingparry3d- For triangle mesh geometric operations (volume, bounding boxes)nalgebra- Linear algebra library (used by parry3d)
All dependencies are regularly monitored for security vulnerabilities using:
- Automated security audits: Daily CI scans using
cargo-auditagainst the RustSec Advisory Database - Manual review: Dependencies are updated and reviewed regularly
To run a security audit locally:
cargo install cargo-audit
cargo auditContributions are welcome! Please feel free to submit issues or pull requests.
This project is licensed under the MIT License. See LICENSE for details.
This implementation is inspired by the official lib3mf library but is a complete rewrite in Rust with a focus on safety and simplicity.