Metadata-Version: 2.4
Name: fusionextractor
Version: 1.2.0
Summary: Extract resources from Autodesk Fusion Electronics .f3z project files
Author-email: Jonathan Oxer <jon@oxer.com.au>
License: BSD 3-Clause License
        
        Copyright (c) 2026, Jonathan Oxer
        
        Redistribution and use in source and binary forms, with or without
        modification, are permitted provided that the following conditions are met:
        
        1. Redistributions of source code must retain the above copyright notice, this
           list of conditions and the following disclaimer.
        
        2. Redistributions in binary form must reproduce the above copyright notice,
           this list of conditions and the following disclaimer in the documentation
           and/or other materials provided with the distribution.
        
        3. Neither the name of the copyright holder nor the names of its contributors
           may be used to endorse or promote products derived from this software
           without specific prior written permission.
        
        THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
        AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
        IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
        DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
        FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
        DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
        SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
        CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
        OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
        OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
        
Project-URL: Homepage, https://github.com/SuperHouse/FusionExtractor
Project-URL: Repository, https://github.com/SuperHouse/FusionExtractor
Project-URL: Bug Tracker, https://github.com/SuperHouse/FusionExtractor/issues
Keywords: fusion,eagle,schematic,pcb,f3z,electronics
Classifier: License :: OSI Approved :: BSD License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Scientific/Engineering :: Electronic Design Automation (EDA)
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Provides-Extra: zstd
Requires-Dist: zipfile-zstd; extra == "zstd"
Provides-Extra: dev
Requires-Dist: pytest; extra == "dev"
Requires-Dist: pytest-cov; extra == "dev"
Dynamic: license-file

# FusionExtractor

A Python library for extracting resources from Autodesk Fusion Electronics `.f3z` project files.

## Overview

`.f3z` files are ZIP archives that embed multiple nested archives containing schematic (`.sch`), PCB (`.brd`), and preview image data. FusionExtractor unpacks these without any third-party dependencies.

## Installation

```bash
pip install fusionextractor                  # stdlib only
pip install "fusionextractor[zstd]"          # adds Zstandard support for 3D model previews
```

## Examples

A sample Fusion Electronics project is included in `examples/IOMOD-AD5593R_v2_0.f3z` for 
use by automatic tests and for you to use when trying out the library.

To run `examples/extract.py`, open a terminal in the library folder and type:

```
pip install -e ".[zstd]"
python examples/extract.py examples/IOMOD-AD5593R-v2_0.f3z
```

This will create a new folder called `output` in the current directory, and extract various 
assets from the sample project into it. Check the source of `extract.py` to see the 
options it uses relating to output filenames, etc.

## Usage

```python
from fusionextractor import FusionProject

with FusionProject("design.f3z") as proj:
    # Read the design name from embedded metadata
    print(proj.design_name)

    # Get raw bytes
    sch_bytes = proj.get_schematic()   # Eagle .sch file
    brd_bytes = proj.get_board()       # Eagle .brd file
    previews  = proj.get_previews()    # list[PreviewImage]
    bom       = proj.get_bom()         # list[BomEntry]

    # Extract to disk
    proj.extract_schematic("output/")           # writes original filename into output/
    proj.extract_board("output/my_board.brd")   # writes to exact path
    proj.extract_previews("output/previews/")   # writes all preview PNGs
    proj.extract_bom("output/")                 # writes {design_name}_bom.csv

    # Extract named board images
    proj.extract_board_image("schematic",    "output/")   # {design_name}_schematic.png
    proj.extract_board_image("pcb_top",      "output/")   # {design_name}_pcb_top.png
    proj.extract_board_image("pcb_3d_top",   "output/")   # {design_name}_pcb_3d_top.png
    proj.extract_board_image("pcb_3d_bottom","output/")   # {design_name}_pcb_3d_bottom.png
```

### Destination path behaviour

`extract_schematic` and `extract_board` accept an optional `dest` argument:

| `dest` value | Result |
|---|---|
| `None` (default) | Written to the current directory, original filename preserved |
| A directory path or path ending with `/` | Original filename preserved inside that directory |
| A full file path (with extension) | Written to that exact path |

### Board images

`get_board_image` and `extract_board_image` retrieve specific named views of the board:

| `view_type` | Content |
|---|---|
| `"schematic"` | Schematic diagram (one or more pages) |
| `"pcb_top"` | PCB top-layer 2D layout |
| `"pcb_3d_top"` | 3D render from the top |
| `"pcb_3d_bottom"` | 3D render from the bottom |

```python
top_png    = proj.get_board_image("pcb_3d_top")    # returns bytes
bottom_png = proj.get_board_image("pcb_3d_bottom")

proj.extract_board_image("pcb_3d_top", "output/")  # writes {design_name}_pcb_3d_top.png
proj.extract_board_image("pcb_3d_top", "render.png")  # writes to exact path
```

### Preview images

`get_previews` and `extract_previews` return all images from all nested archives, including small thumbnails and the large renders above.  Each `PreviewImage` has a `view_type` field populated automatically.

Pass `include_large_images=False` to retrieve thumbnails only.

```python
for preview in proj.get_previews(include_large_images=False):
    print(preview.source, len(preview.data), "bytes")
    # e.g. "schematic 4821 bytes"
```

`PreviewImage` fields:

| Field | Type | Description |
|---|---|---|
| `source` | `str` | Archive the image came from (`"schematic"`, `"board"`, `"project"`, `"3d_model"`) |
| `path` | `str` | Path of the image inside the nested archive |
| `data` | `bytes` | Raw PNG bytes |
| `view_type` | `str \| None` | `"schematic"`, `"pcb_top"`, `"pcb_3d_top"`, `"pcb_3d_bottom"`, or `"thumbnail"` |

Extracted preview files are named `{source}__{original_filename}` to avoid collisions when multiple archives contain a `small.png`.

## API reference

```python
class FusionProject:
    path: str | Path                                          # path to the .f3z file

    design_name: str                                          # property; from embedded metadata

    def get_schematic() -> bytes
    def get_board() -> bytes
    def get_previews(*, include_large_images: bool = True) -> list[PreviewImage]
    def get_board_image(view_type: str) -> bytes              # "schematic"|"pcb_top"|"pcb_3d_top"|"pcb_3d_bottom"
    def get_bom(*, include_power_symbols: bool = False) -> list[BomEntry]

    def extract_schematic(dest=None) -> Path
    def extract_board(dest=None) -> Path
    def extract_previews(dest=None, *, include_large_images: bool = True) -> list[Path]
    def extract_board_image(view_type: str, dest=None) -> Path
    def extract_bom(dest=None, *, include_power_symbols: bool = False) -> Path
```

### BOM

`get_bom` parses the component list from the embedded schematic.  Supply/power symbols
(GND, VCC, NC, etc.) are excluded by default; pass `include_power_symbols=True` to
include them.

```python
for entry in proj.get_bom():
    print(entry.reference, entry.device, entry.package, entry.value)
    # e.g. "C1  CAP  0603  100nF"
```

`BomEntry` fields:

| Field | Type | Description |
|---|---|---|
| `reference` | `str` | Reference designator (`"C1"`, `"U1"`) |
| `device` | `str` | Component type / part number base (`"CAP"`, `"AD5593R"`) |
| `package` | `str` | Footprint / package (`"0603"`, `"TSSOP16"`) |
| `value` | `str` | Component value (`"100nF"`, `"10K"`) — empty string when not set |
| `library` | `str` | Eagle library name (`"SuperHouse-Capacitors"`) |

`extract_bom` writes the BOM as a CSV file named `{design_name}_bom.csv`.

## Exceptions

| Exception | Raised when |
|---|---|
| `FileNotFoundError` | The `.f3z` file path does not exist |
| `FusionExtractorError` | The file is not a valid ZIP/f3z archive |
| `FileNotFoundInArchiveError` | A required entry is missing from inside the archive |

Both custom exceptions are subclasses of `FusionExtractorError` and are importable from the package root:

```python
from fusionextractor import FusionExtractorError, FileNotFoundInArchiveError
```

## Requirements

Python 3.9+. No required third-party dependencies.

The `.f3d` archive (3D model) uses Zstandard compression, which Python's stdlib `zipfile` does not support. Without the optional extra, 3D model previews are silently skipped. Install `.[zstd]` to enable them.
