lib3mf 0.1.2

Pure Rust implementation for 3MF (3D Manufacturing Format) parsing and writing
Documentation

lib3mf_rust

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

License

Overview

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

Features

  • 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)

Installation

Add this to your Cargo.toml:

[dependencies]
lib3mf = "0.1"

Usage

Reading 3MF Files

Basic Example

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(())
}

Writing 3MF Files

Creating and Writing a Simple Model

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(())
}

Round-Trip Example

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(())
}

Streaming Parser for Large Files

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(())
}

Accessing Production Extension Data

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(())
}

Accessing Displacement Data

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(())
}

Accessing Beam Lattice Data

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(())
}

Advanced Materials

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(())
}

Extension Support

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 & Properties
  • Extension::Production - Production information (UUIDs, paths)
  • Extension::Slice - Slice data for layer-by-layer manufacturing
  • Extension::BeamLattice - Beam and lattice structures
  • Extension::SecureContent - Digital signatures and encryption
  • Extension::BooleanOperations - Volumetric design
  • Extension::Displacement - Surface displacement maps
  • Extension::Volumetric - Volumetric data (voxel grids and implicit volumes)

Polygon Clipping for Slice Self-Intersection Resolution

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).

Custom Extension Support

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.

Mesh Operations and Geometry Analysis

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.

Mesh Subdivision

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.

Running Examples

The repository includes several comprehensive examples demonstrating different features:

Basic Parsing

cargo run --example parse_3mf

Extension Support

cargo run --example extension_support test_files/material/kinect_scan.3mf all
cargo run --example extension_support test_files/material/kinect_scan.3mf core-only

Export to Other Formats

Convert 3MF files to STL (for 3D printing):

cargo run --example export_to_stl test_files/core/box.3mf output.stl

Convert 3MF files to OBJ (for 3D modeling):

cargo run --example export_to_obj test_files/core/box.3mf output.obj

Validation and Error Handling

cargo run --example validation_errors test_files/core/box.3mf permissive
cargo run --example validation_errors test_files/core/box.3mf strict

Working with Build Items and Transformations

cargo run --example build_transformations test_files/core/box.3mf

Mesh Operations and Geometry Analysis

cargo run --example mesh_analysis test_files/core/box.3mf

Extracting Color Information

cargo run --example extract_colors test_files/material/kinect_scan.3mf

3MF Viewer Tool

The 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-all

The 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.

Architecture

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 containers
  • parser - XML parsing for 3D model files
  • error - Comprehensive error types with detailed messages

3MF Format Support

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

Extension Support and Validation

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.

Future Enhancements

Potential future additions could include:

  • Extended conformance test coverage
  • Additional export formats
  • Performance optimizations for very large files
  • Streaming support for additional extensions

Testing

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 -- --nocapture

Continuous Integration

The 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.

Conformance Testing

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_tests

Key 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:

  1. Test-only decryption using Suite 8 test keys for conformance validation
  2. Complete keystore metadata extraction for production applications

Files encrypted with Suite 8 test keys (consumerid="test3mf01") are automatically decrypted during parsing. For production applications, access all encryption metadata (consumers, encryption parameters, access rights) and implement decryption using external cryptographic libraries. Never use embedded test keys in production.

See CONFORMANCE_REPORT.md for detailed test results and analysis.

Fuzzing

The library includes comprehensive fuzzing infrastructure using cargo-fuzz and libFuzzer to discover bugs, crashes, and security vulnerabilities.

Fuzzing Targets

  • 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)

Running Fuzzers Locally

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
done

Continuous Fuzzing

Fuzzing 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.

Performance

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_large

Typical Performance:

  • Small files (1,000 vertices): ~1 ms
  • Medium files (10,000 vertices): ~7 ms
  • Large files (100,000 vertices): ~70 ms

Safety

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

Dependencies

The library uses minimal, well-maintained dependencies:

  • zip - For reading ZIP archives (3MF container format)
  • quick-xml - For parsing XML model files
  • thiserror - For error handling
  • parry3d - 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-audit against the RustSec Advisory Database
  • Manual review: Dependencies are updated and reviewed regularly

To run a security audit locally:

cargo install cargo-audit
cargo audit

Contributing

Contributions are welcome! Please feel free to submit issues or pull requests.

License

This project is licensed under the MIT License. See LICENSE for details.

References

Acknowledgments

This implementation is inspired by the official lib3mf library but is a complete rewrite in Rust with a focus on safety and simplicity.