Metadata-Version: 2.4
Name: reprofig
Version: 0.4.0
Summary: Verifiable figures with their data, provenance, and optional statistics built in
Author: Jamie Malcolm
License-Expression: BSD-3-Clause
Project-URL: Homepage, https://github.com/Jay2owe/ReproFig
Project-URL: Repository, https://github.com/Jay2owe/ReproFig
Project-URL: Issues, https://github.com/Jay2owe/ReproFig/issues
Project-URL: Changelog, https://github.com/Jay2owe/ReproFig/releases
Keywords: figures,provenance,reproducibility,scientific-figures,metadata
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Science/Research
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Provides-Extra: excel
Requires-Dist: openpyxl>=3.1; extra == "excel"
Provides-Extra: proof
Requires-Dist: openpyxl>=3.1; extra == "proof"
Requires-Dist: scipy>=1.10; extra == "proof"
Requires-Dist: Pillow>=10; extra == "proof"
Requires-Dist: cryptography>=42; extra == "proof"
Requires-Dist: argon2-cffi>=23.1; extra == "proof"
Requires-Dist: pypdfium2>=4.30; extra == "proof"
Provides-Extra: matplotlib
Requires-Dist: matplotlib>=3.6; extra == "matplotlib"
Provides-Extra: pandas
Requires-Dist: pandas>=1.5; extra == "pandas"
Provides-Extra: rocrate
Requires-Dist: rocrate>=0.13; extra == "rocrate"
Provides-Extra: pdf
Requires-Dist: pikepdf>=10; extra == "pdf"
Provides-Extra: pdf-render
Requires-Dist: pypdfium2>=4.30; extra == "pdf-render"
Requires-Dist: Pillow>=10; extra == "pdf-render"
Provides-Extra: raster
Requires-Dist: Pillow>=10; extra == "raster"
Requires-Dist: tifftools>=1.6; extra == "raster"
Provides-Extra: heif
Requires-Dist: Pillow>=11.3; extra == "heif"
Requires-Dist: pillow-heif>=1.0; extra == "heif"
Provides-Extra: hdf5
Requires-Dist: h5py>=3.10; extra == "hdf5"
Provides-Extra: netcdf
Requires-Dist: netCDF4>=1.7; extra == "netcdf"
Requires-Dist: h5py>=3.10; extra == "netcdf"
Provides-Extra: fits
Requires-Dist: astropy>=6; extra == "fits"
Provides-Extra: all-formats
Requires-Dist: pikepdf>=10; extra == "all-formats"
Requires-Dist: Pillow>=11.3; extra == "all-formats"
Requires-Dist: tifftools>=1.6; extra == "all-formats"
Requires-Dist: pillow-heif>=1.0; extra == "all-formats"
Requires-Dist: h5py>=3.10; extra == "all-formats"
Requires-Dist: netCDF4>=1.7; extra == "all-formats"
Requires-Dist: astropy>=6; extra == "all-formats"
Provides-Extra: dev
Requires-Dist: pytest>=8; extra == "dev"
Requires-Dist: build>=1; extra == "dev"
Requires-Dist: twine>=5; extra == "dev"
Requires-Dist: openpyxl>=3.1; extra == "dev"
Requires-Dist: scipy>=1.10; extra == "dev"
Requires-Dist: cryptography>=42; extra == "dev"
Requires-Dist: argon2-cffi>=23.1; extra == "dev"
Requires-Dist: pypdfium2>=4.30; extra == "dev"
Requires-Dist: pandas>=1.5; extra == "dev"
Requires-Dist: matplotlib>=3.6; extra == "dev"
Requires-Dist: pikepdf>=10; extra == "dev"
Requires-Dist: Pillow>=11.3; extra == "dev"
Requires-Dist: tifftools>=1.6; extra == "dev"
Requires-Dist: pillow-heif>=1.0; extra == "dev"
Requires-Dist: h5py>=3.10; extra == "dev"
Requires-Dist: netCDF4>=1.7; extra == "dev"
Requires-Dist: astropy>=6; extra == "dev"
Dynamic: license-file

# ReproFig

ReproFig makes a figure carry the exact comma-separated values (CSV), optional
statistics, software version, source fingerprints, and reproduction
instructions needed to audit it later. It works for general data figures;
scientific figures and publication workflows are the main use case because
their evidence and reporting requirements benefit most. The same `reprofig/1` record works in
SVG, PDF, PNG, JPEG, TIFF, WebP, AVIF/HEIF, PowerPoint, Word, Excel, HTML,
HDF5, netCDF-4, FITS, and deterministic ZIP/RO-Crate bundles.

```console
python -m pip install reprofig
```

ReproFig requires Python 3.10 or newer. The core has no required scientific
stack. The main installation choices are:

```console
python -m pip install reprofig           # ordinary save, embed, inspect, extract
python -m pip install "reprofig[excel]"  # publication workbooks
python -m pip install "reprofig[proof]"  # workbooks, statistics, visuals, signatures, encryption
```

Carrier-specific extras remain available for PDF, HEIF, HDF5, netCDF and FITS.

## Create a master figure

```python
from reprofig import save_figure

save_figure(
    figure,
    "Figure 1.pdf",
    plotted_data=dataframe,
    statistics=records,
    producer={"package": "my-analysis", "version": "1.4.0"},
    figure_profile="master",
)
```

This ordinary workflow is unchanged and does not activate proof checks,
cryptography, interception, or extra dependencies.

A `master` embeds the exact CSV bytes used for the plot. This is the auditable
source of truth and may contain private data or local paths, so do not upload it
without checking it first. Sidecar files are optional and can be regenerated:

```python
from reprofig import extract_artifact, publish_artifacts

extract_artifact("Figure 1.pdf", "Figure 1 extracted")
publish_artifacts(
    ["Figure 1.pdf", "Figure 2.jpg", "Slides.pptx"],
    output_dir="Publication",
    figure_profile="public",
    safe_columns=["condition", "value"],
)
```

## Integrate another plotting package

Attach the plot meaning before its normal save step:

```python
from reprofig import attach, save_figure

attach(
    figure,
    plotted_data=analysis_rows,
    statistics=statistical_records,
    analysis={"independent_unit": "participant"},
    column_classification={
        "condition": "safe",
        "value": "safe",
        "participant_id": "private",
    },
)
save_figure(
    figure,
    "Figure.png",
    producer={"package": "my-analysis", "package_version": "2.1.0"},
    render_preset="line_art",
)
```

New raster figures default to 300 dots per inch (DPI). Use `screen` for 150
DPI, `continuous_tone` for 300 DPI, `line_art` for 600 DPI, or pass an exact
`dpi`, `width`, and `height`. Existing raster files are never resampled by
`embed_file`; AVIF/HEIF metadata changes require `allow_reencode=True` because
the available backend must rebuild those images.

## Make publication-safe copies

The `public` profile retains only explicitly approved columns and source links.
The `minimal_public` profile retains summary statistics and provenance without
row-level data. Both are one-way derivatives of the master:

```text
reprofig publish Figure.svg Figure.jpg --output-dir Submission \
  --profile minimal-public --safe-columns condition,value \
  --public-source dataset=https://repository.example/data.csv
```

The command-line interface also provides `formats`, `inspect`, `validate`,
`embed`, `extract`, `caption`, `scan`, `bundle`, and `fsb-export`. Run
`reprofig formats` to see optional dependencies and carrier capabilities.

Embedded metadata can be stripped by editors, social platforms, publisher
pipelines, or format conversion. Keep the master. When the delivery route is
unknown, send a `.reprofig.zip` bundle alongside the visible figure; its fixed
layout and SHA-256 checksums make missing or changed files detectable.

## Build a publication source-data workbook

Combine every unique embedded CSV with a normalized table of all plotted and
unplotted tests:

```python
from reprofig import build_publication_workbook

result = build_publication_workbook(
    "figures/",
    "Publication-source-data.xlsx",
    experiment_statistics="analysis/all-tests.json",
)
```

Set the ledger's coverage to `analysis_complete` only when it intentionally
lists every analysis, including unplotted tests. That is a declaration, not
proof that undisclosed analyses never occurred. See
[`docs/publication_workbook.md`](https://github.com/Jay2owe/ReproFig/blob/main/docs/publication_workbook.md).

## Opt into proof-carrying output

```python
from reprofig import bind_artist, save_figure, verify_proof

bind_artist(line, semantic_id="treated-series", columns=["time", "signal"])
save_figure(
    figure,
    "Figure-1.svg",
    plotted_data=rows,
    statistics=statistics,
    proof=True,
)
report = verify_proof(
    "Figure-1.svg",
    required=["internally_consistent", "display_verified"],
)
```

Complete figure reproduction is deliberately separate because it executes the
embedded producer. It saves a second carrier and a report; later verification
only reads those files and never reruns code:

```python
from reprofig import reproduce_figure

run = reproduce_figure(
    "Figure-1.svg",
    bundle_root="figure-bundle",
    output_dir="figure-bundle/verification/reproduced",
    execute_trusted_producer=True,
)
```

Use `statistics_reproduced` when declared statistics match the same
implementation, `statistics_independently_verified` when a separate reference
implementation matches, and `figure_reproduced` only when a separately saved
figure also matches. See
[`docs/figure-reproduction.md`](https://github.com/Jay2owe/ReproFig/blob/main/docs/figure-reproduction.md).

Typed statistical specifications can additionally reconstruct declared source
transformations and recalculate supported tests. A passing report proves that
the stated evidence agrees with the output; it does not prove that source data
are true or that the chosen method is scientifically appropriate.

Signatures answer “has this evidence changed since this key signed it?” Trust
stores separately answer “do I accept that key for this purpose?” Individual
tables, statistics, provenance, or specifications can be encrypted for a
password or named X25519 recipient before signing. See
[`docs/proof-carrying-verification.md`](https://github.com/Jay2owe/ReproFig/blob/main/docs/proof-carrying-verification.md)
and [`docs/security.md`](https://github.com/Jay2owe/ReproFig/blob/main/docs/security.md).

## Develop and verify

```console
python -m pip install --upgrade build twine
python -m build
python -m twine check dist/*
```

Report problems through the [GitHub issue tracker](https://github.com/Jay2owe/ReproFig/issues).
Please cite ReproFig using the metadata in `CITATION.cff`.

ReproFig is licensed under the BSD 3-Clause licence.
