plotly-print 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. plotly_print-0.1.0/.cursor/rules/artifacts.mdc +15 -0
  2. plotly_print-0.1.0/.cursor/rules/global.mdc +64 -0
  3. plotly_print-0.1.0/.cursor/rules/markdown.mdc +25 -0
  4. plotly_print-0.1.0/.github/workflows/check-tag.yml +50 -0
  5. plotly_print-0.1.0/.github/workflows/publish.yml +60 -0
  6. plotly_print-0.1.0/.github/workflows/quality-checks.yml +40 -0
  7. plotly_print-0.1.0/.github/workflows/version-check.yml +61 -0
  8. plotly_print-0.1.0/.gitignore +23 -0
  9. plotly_print-0.1.0/.mdformat.toml +3 -0
  10. plotly_print-0.1.0/.pre-commit-config.yaml +63 -0
  11. plotly_print-0.1.0/AGENTS.md +14 -0
  12. plotly_print-0.1.0/CONTRIBUTING.md +39 -0
  13. plotly_print-0.1.0/LICENSE +22 -0
  14. plotly_print-0.1.0/PKG-INFO +66 -0
  15. plotly_print-0.1.0/README.md +40 -0
  16. plotly_print-0.1.0/THIRD_PARTY_NOTICES.md +40 -0
  17. plotly_print-0.1.0/benchmark/__init__.py +1 -0
  18. plotly_print-0.1.0/benchmark/__main__.py +3 -0
  19. plotly_print-0.1.0/benchmark/cases.py +408 -0
  20. plotly_print-0.1.0/benchmark/collage.py +89 -0
  21. plotly_print-0.1.0/benchmark/compare.py +24 -0
  22. plotly_print-0.1.0/benchmark/report.py +39 -0
  23. plotly_print-0.1.0/benchmark/runner.py +129 -0
  24. plotly_print-0.1.0/pyproject.toml +105 -0
  25. plotly_print-0.1.0/src/plotly_print/__init__.py +20 -0
  26. plotly_print-0.1.0/src/plotly_print/core.py +76 -0
  27. plotly_print-0.1.0/src/plotly_print/engine.py +172 -0
  28. plotly_print-0.1.0/src/plotly_print/fonts.py +143 -0
  29. plotly_print-0.1.0/src/plotly_print/js/micro_dom.js +1195 -0
  30. plotly_print-0.1.0/src/plotly_print/js/plotly-cartesian.min.js +149 -0
  31. plotly_print-0.1.0/src/plotly_print/plotly_hook.py +95 -0
  32. plotly_print-0.1.0/src/plotly_print/raster.py +186 -0
  33. plotly_print-0.1.0/tests/test_background.py +46 -0
  34. plotly_print-0.1.0/tests/test_bar.py +24 -0
  35. plotly_print-0.1.0/tests/test_cartesian_types.py +41 -0
  36. plotly_print-0.1.0/tests/test_comprehensive_2d.py +99 -0
  37. plotly_print-0.1.0/tests/test_heatmap.py +22 -0
  38. plotly_print-0.1.0/tests/test_json_types.py +155 -0
  39. plotly_print-0.1.0/tests/test_kaleido_visual.py +37 -0
  40. plotly_print-0.1.0/tests/test_legend.py +27 -0
  41. plotly_print-0.1.0/tests/test_pie.py +18 -0
  42. plotly_print-0.1.0/tests/test_plotly_hook.py +31 -0
  43. plotly_print-0.1.0/tests/test_raster.py +129 -0
  44. plotly_print-0.1.0/tests/test_scatter.py +38 -0
  45. plotly_print-0.1.0/tests/test_svg_transforms.py +174 -0
  46. plotly_print-0.1.0/uv.lock +1054 -0
@@ -0,0 +1,15 @@
1
+ ---
2
+ description: Gitignored local output directory for agents
3
+ alwaysApply: true
4
+ ---
5
+
6
+ # `.artifacts/`
7
+
8
+ The repo root `.artifacts/` directory is **gitignored**. Use it for:
9
+
10
+ - Benchmark output (`poe benchmark` writes to `.artifacts/benchmark/`)
11
+ - LLM research notes, comparison reports, scratch exports, and other **temporary local artifacts**
12
+
13
+ Do not commit files under `.artifacts/`. Nothing there is shipped on PyPI or in the wheel.
14
+
15
+ When generating reports or large binary outputs for the user to review, prefer paths under `.artifacts/` unless they ask for another location.
@@ -0,0 +1,64 @@
1
+ ---
2
+ description: Coding standards for every project generated from cookiecutter-python-seed
3
+ alwaysApply: true
4
+ ---
5
+
6
+ # Generated project standards
7
+
8
+ This is a Python project generated from cookiecutter-python-seed. Treat `pyproject.toml` as the source of truth for toolchain, Python version, and dependencies. Minimum Python version is 3.12. Use uv to lock, sync, build, and bump versions.
9
+
10
+ You are a lazy senior developer. Lazy means efficient, not careless. The best code is the code never written.
11
+
12
+ ## Before writing code
13
+
14
+ Read the task and the code it touches, trace the real flow end to end, then stop at the first rung that holds:
15
+
16
+ 1. Does this need to be built at all? (YAGNI)
17
+ 2. Does it already exist in this codebase? Reuse it.
18
+ 3. Does the standard library already do this? Use it.
19
+ 4. Does an already-declared dependency solve it? Use it.
20
+ 5. Can this be one boring line? Make it one line.
21
+ 6. Only then: write the minimum code that works.
22
+
23
+ Bug fix = root cause, not symptom. Grep every caller of the function you touch and fix the shared function once.
24
+
25
+ ## Toolchain
26
+
27
+ - Match existing layout: `src/` for packages, `tests/` for tests, application entry points as already configured.
28
+ - Do not add a dependency unless it is declared in `pyproject.toml` (or the project's lockfile). Prefer stdlib over a new package.
29
+ - Style is enforced by Ruff. Follow `pyproject.toml`; the points below are the ones to apply consistently even when Ruff cannot.
30
+ - After editing Python files, run `poe format`.
31
+ - After editing Markdown files, run `poe format_docs`.
32
+
33
+ ## Comments and docs
34
+
35
+ - No docstrings except on base/abstract classes and methods (the contract for implementors) and on complex functions where extra explanation genuinely helps.
36
+ - No unnecessary comments. Code should be self-explanatory. If a comment is needed, explain why, not what.
37
+ - No section-divider comments inside classes or functions. Group related methods by proximity. Dividers are allowed only in test classes.
38
+
39
+ ## Language
40
+
41
+ - Type-hint every function and method. Do not nest annotations so deeply that they hurt readability.
42
+ - No unnecessary imports.
43
+ - Use f-strings for all string formatting.
44
+ - Avoid unnecessary `Path` wrappers; join with `/` on `Path` objects.
45
+ - ASCII only in identifiers, comments, and docs. No emojis. Non-ASCII letters are allowed only when the content is actually in another language (e.g. Polish, Russian).
46
+
47
+ ## Quality
48
+
49
+ - Use `poe format` / `poe format_docs` instead of hand-formatting style.
50
+ - Deletion over addition. Boring over clever. Fewest files possible. Shortest working diff wins, after you understand the problem.
51
+ - No abstractions, boilerplate, or dependencies that weren't requested.
52
+ - Question complex requests: "Do you actually need X, or does Y cover it?"
53
+ - When two stdlib approaches are the same size, pick the edge-case-correct one.
54
+ - Mark deliberate simplifications that cut a real corner with a known ceiling (global lock, O(n^2) scan, naive heuristic) with a `ponytail:` comment naming the ceiling and upgrade path.
55
+ - Non-trivial logic leaves one runnable check: the smallest test that fails if the logic breaks. Trivial one-liners need no new test. Run the project's test command before claiming work is done.
56
+ - Do not commit secrets, `.env` files, or local virtualenvs.
57
+
58
+ Not lazy about: understanding the problem, input validation at trust boundaries, error handling that prevents data loss, security, anything explicitly requested.
59
+
60
+ ## Agents
61
+
62
+ - Keep this file for conventions that should always apply.
63
+ - Put path-specific Cursor rules in additional `.mdc` files in this directory.
64
+ - Keep `AGENTS.md` as a short index that points here.
@@ -0,0 +1,25 @@
1
+ ---
2
+ description: Markdown formatting for README, docs, and other .md files
3
+ globs: "**/*.md"
4
+ alwaysApply: false
5
+ ---
6
+
7
+ # Markdown
8
+
9
+ ASCII only. No emojis, smart quotes, or non-English letters.
10
+
11
+ Use fenced code blocks with a language tag when the snippet has a language:
12
+
13
+ ```python
14
+ from pathlib import Path
15
+
16
+ Path("data") / "out.csv"
17
+ ```
18
+
19
+ Do not leave unlabeled fences for Python, TOML, shell, or JSON. Plain fences are fine for output or prose examples with no language.
20
+
21
+ Keep heading levels, lists, and tables consistent within a file: one heading style, one list marker (`-`), one table alignment. Do not mix numbered and bulleted lists for the same kind of items.
22
+
23
+ Skip extra bold, italics, and emphasis. Highlight a term only when it would otherwise be missed.
24
+
25
+ Write short, plain sentences. If a technical term is necessary, introduce it in the same sentence.
@@ -0,0 +1,50 @@
1
+ name: "Check Tag"
2
+
3
+ on:
4
+ workflow_call:
5
+ inputs:
6
+ tag:
7
+ description: Version tag like v1.2.3
8
+ required: true
9
+ type: string
10
+
11
+ permissions:
12
+ contents: read
13
+
14
+ jobs:
15
+ check-tag:
16
+ runs-on: ubuntu-latest
17
+ steps:
18
+ - name: Checkout
19
+ uses: actions/checkout@v4
20
+ with:
21
+ ref: refs/tags/${{ inputs.tag }}
22
+ fetch-tags: true
23
+ fetch-depth: 0
24
+
25
+ - name: Check that tag is on main
26
+ env:
27
+ TAG: ${{ inputs.tag }}
28
+ run: |
29
+ git fetch origin refs/heads/main:refs/remotes/origin/main --force
30
+ if ! git merge-base --is-ancestor HEAD refs/remotes/origin/main; then
31
+ echo "tag $TAG is not on main"
32
+ exit 1
33
+ fi
34
+
35
+ - name: Set up uv
36
+ uses: astral-sh/setup-uv@v7
37
+ with:
38
+ python-version: '3.12'
39
+ enable-cache: false
40
+
41
+ - name: Ensure tag matches version in pyproject.toml
42
+ env:
43
+ TAG: ${{ inputs.tag }}
44
+ run: |
45
+ version=$(uv version --short)
46
+ tag_version="${TAG#v}"
47
+ if [ "$version" != "$tag_version" ]; then
48
+ echo "version in pyproject.toml ($version) does not match tag ($TAG)"
49
+ exit 1
50
+ fi
@@ -0,0 +1,60 @@
1
+ name: "Publish"
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - 'v*.*.*'
7
+
8
+ permissions:
9
+ contents: read
10
+
11
+ jobs:
12
+ check-tag:
13
+ uses: ./.github/workflows/check-tag.yml
14
+ with:
15
+ tag: ${{ github.ref_name }}
16
+
17
+ build:
18
+ needs: check-tag
19
+ runs-on: ubuntu-latest
20
+ steps:
21
+ - name: Checkout
22
+ uses: actions/checkout@v4
23
+
24
+ - name: Set up uv
25
+ uses: astral-sh/setup-uv@v7
26
+ with:
27
+ python-version: '3.12'
28
+ enable-cache: false
29
+
30
+ - name: Build
31
+ run: uv build
32
+
33
+ - name: Upload artifact
34
+ uses: actions/upload-artifact@v4
35
+ with:
36
+ name: dist
37
+ path: dist/
38
+
39
+ publish:
40
+ needs: build
41
+ runs-on: ubuntu-latest
42
+ environment:
43
+ name: pypi
44
+ url: https://pypi.org/p/plotly-print
45
+ permissions:
46
+ id-token: write
47
+ steps:
48
+ - name: Download artifact
49
+ uses: actions/download-artifact@v4
50
+ with:
51
+ name: dist
52
+ path: dist/
53
+
54
+ - name: Set up uv
55
+ uses: astral-sh/setup-uv@v7
56
+ with:
57
+ enable-cache: false
58
+
59
+ - name: Publish
60
+ run: uv publish --trusted-publishing always
@@ -0,0 +1,40 @@
1
+ name: "Quality Checks"
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ types: [opened, synchronize, reopened]
8
+
9
+ permissions:
10
+ contents: read
11
+
12
+ jobs:
13
+ quality-checks:
14
+ runs-on: ubuntu-latest
15
+ steps:
16
+ - name: Checkout
17
+ uses: actions/checkout@v4
18
+
19
+ - name: Set up uv
20
+ uses: astral-sh/setup-uv@v7
21
+ with:
22
+ python-version: '3.12'
23
+
24
+ - name: Lock dependencies
25
+ run: uv lock --check
26
+
27
+ - name: Sync dependencies
28
+ run: uv sync --locked
29
+
30
+ - name: Check code formatting
31
+ run: uv run ruff format --check .
32
+
33
+ - name: Check code linting
34
+ run: uv run ruff check .
35
+
36
+ - name: Check Markdown formatting
37
+ run: find . -name '*.md' ! -path './.venv/*' ! -path './.git/*' -print0 | xargs -0 -r uv run mdformat --check
38
+
39
+ - name: Run tests
40
+ run: uv run pytest
@@ -0,0 +1,61 @@
1
+ name: "Version Check"
2
+
3
+ on:
4
+ pull_request:
5
+ branches: [main]
6
+ types: [opened, synchronize, reopened]
7
+
8
+ permissions:
9
+ contents: read
10
+
11
+ jobs:
12
+ version-check:
13
+ runs-on: ubuntu-latest
14
+ name: Ensure version bumped
15
+ steps:
16
+ - name: Checkout
17
+ uses: actions/checkout@v4
18
+ with:
19
+ fetch-depth: 0
20
+
21
+ - name: Set up uv
22
+ uses: astral-sh/setup-uv@v7
23
+ with:
24
+ python-version: '3.12'
25
+ enable-cache: false
26
+
27
+ - name: Determine PR version
28
+ id: get-pr-version
29
+ run: echo "version=$(uv version --short)" >> "$GITHUB_OUTPUT"
30
+
31
+ - name: Determine main version
32
+ id: get-main-version
33
+ run: |
34
+ git fetch origin refs/heads/main:refs/remotes/origin/main --depth=1
35
+ git checkout -B main origin/main
36
+ main_version=$(uv version --short)
37
+ echo "Main version: $main_version"
38
+ echo "version=$main_version" >> "$GITHUB_OUTPUT"
39
+
40
+ - name: Compare versions
41
+ run: |
42
+ pr_version="${{ steps.get-pr-version.outputs.version }}"
43
+ main_version="${{ steps.get-main-version.outputs.version }}"
44
+
45
+ if [ -z "$pr_version" ] || [ -z "$main_version" ]; then
46
+ echo "Version is not set"
47
+ exit 1
48
+ fi
49
+
50
+ if [ "$pr_version" = "$main_version" ]; then
51
+ echo "Version unchanged ($pr_version). Please bump the version (e.g. poe patch)"
52
+ exit 1
53
+ fi
54
+
55
+ highest_version=$(echo -e "$pr_version\n$main_version" | sort -V | tail -n1)
56
+ if [ "$highest_version" != "$pr_version" ]; then
57
+ echo "Version $highest_version is higher than PR version $pr_version. Please bump the version (e.g. poe patch)"
58
+ exit 1
59
+ fi
60
+
61
+ echo "Version $pr_version is valid"
@@ -0,0 +1,23 @@
1
+ .DS_Store
2
+
3
+ .venv/
4
+
5
+ __pycache__/
6
+ *.py[cod]
7
+ .ruff_cache/
8
+ .pytest_cache/
9
+ .mypy_cache/
10
+
11
+ .env
12
+ .env.*
13
+
14
+ .idea/
15
+ .vscode/
16
+
17
+ build/
18
+ dist/
19
+ sdist/
20
+ wheels/
21
+ *.egg-info/
22
+
23
+ .artifacts/
@@ -0,0 +1,3 @@
1
+ wrap = "keep"
2
+ number = true
3
+ end_of_line = "lf"
@@ -0,0 +1,63 @@
1
+ minimum_pre_commit_version: '3.2.0'
2
+
3
+ default_install_hook_types:
4
+ - pre-push
5
+ - post-checkout
6
+ - post-merge
7
+
8
+ repos:
9
+ - repo: local
10
+ hooks:
11
+ - id: uv-lock-check
12
+ name: uv lock --check
13
+ description: check that uv.lock is consistent with pyproject.toml
14
+ entry: uv lock --check
15
+ language: system
16
+ pass_filenames: false
17
+ files: '^(pyproject\.toml|uv\.lock)$'
18
+ stages: [pre-push]
19
+
20
+ - id: uv-lock-check-after-git
21
+ name: uv lock --check
22
+ description: check that uv.lock is consistent with pyproject.toml
23
+ entry: uv lock --check
24
+ language: system
25
+ pass_filenames: false
26
+ always_run: true
27
+ stages: [post-checkout, post-merge]
28
+
29
+ - id: uv-sync
30
+ name: uv sync
31
+ description: sync dependencies
32
+
33
+ entry: uv sync --no-active --locked
34
+
35
+ language: system
36
+ always_run: true
37
+ pass_filenames: false
38
+ stages: [post-checkout, post-merge]
39
+ verbose: true
40
+
41
+ - id: ruff-check
42
+ name: ruff check
43
+ description: check for style issues
44
+ entry: uv run ruff check --force-exclude
45
+ language: system
46
+ pass_filenames: false
47
+ stages: [pre-push]
48
+
49
+ - id: ruff-format
50
+ name: ruff format
51
+ description: check that files are formatted
52
+ entry: uv run ruff format --check --force-exclude
53
+ language: system
54
+ pass_filenames: false
55
+ stages: [pre-push]
56
+
57
+ - id: md-format
58
+ name: md format
59
+ description: format markdown files
60
+ entry: uv run mdformat --check
61
+ language: system
62
+ files: '\.md$'
63
+ stages: [pre-push]
@@ -0,0 +1,14 @@
1
+ # Agent instructions
2
+
3
+ This project was generated from **cookiecutter-python-seed**.
4
+
5
+ ## Tooling
6
+
7
+ - **All AI IDEs:** follow this file.
8
+ - **Cursor:** project rules live in [`.cursor/rules/`](.cursor/rules/). This file is the index; do not duplicate those rules here.
9
+
10
+ ## Standards
11
+
12
+ - Always-on: `.cursor/rules/global.mdc`
13
+ - Markdown (`.md` files): `.cursor/rules/markdown.mdc`
14
+ - Local scratch output: `.cursor/rules/artifacts.mdc`
@@ -0,0 +1,39 @@
1
+ # Contributing
2
+
3
+ ```shell
4
+ poe install
5
+ ```
6
+
7
+ That runs `uv sync --all-groups` (dev and test). To use `poe` without activating `.venv` or prefixing it with `uv run`, install Poe once as a uv tool:
8
+
9
+ ```shell
10
+ uv tool install poethepoet
11
+ ```
12
+
13
+ Commit `uv.lock`. `poe hooks` installs git hooks (pre-push, post-checkout, post-merge).
14
+
15
+ ```shell
16
+ poe check
17
+ poe check_docs
18
+ poe test
19
+ ```
20
+
21
+ `poe format` and `poe format_docs` apply the same tools in write mode.
22
+
23
+ ## Kaleido visual benchmark
24
+
25
+ Compare plotly-print output to Kaleido side by side (not part of the PyPI wheel). Kaleido is in the `test` dependency group and is installed by `poe install` / `uv sync`.
26
+
27
+ ```shell
28
+ poe benchmark_quick
29
+ ```
30
+
31
+ Full gallery (all chart fixtures): `poe benchmark`.
32
+
33
+ Outputs are written to `.artifacts/benchmark/` (`report.md`, PNGs, `benchmark_summary.json`).
34
+
35
+ ## Release
36
+
37
+ Bump the version in the PR with `poe patch`, `poe minor`, or `poe major`. CI fails if `[project].version` is not higher than on `main`.
38
+
39
+ After merge, tag `vX.Y.Z` on `main` (must match the project version) and push the tag. GitHub Actions publishes the sdist and wheel to PyPI using trusted publishing. Register this repository as a trusted publisher on PyPI before the first release.
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Benjamin Lear
4
+ Copyright (c) 2026 Roman Kaczanowski
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the "Software"), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in all
14
+ copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ SOFTWARE.
@@ -0,0 +1,66 @@
1
+ Metadata-Version: 2.5
2
+ Name: plotly-print
3
+ Version: 0.1.0
4
+ Summary: Lightweight, browser-less static image exporter for Plotly
5
+ Project-URL: Homepage, https://github.com/roman-kaczanowski/plotly-print
6
+ Project-URL: Repository, https://github.com/roman-kaczanowski/plotly-print
7
+ Project-URL: Issues, https://github.com/roman-kaczanowski/plotly-print/issues
8
+ Author-email: Roman Kaczanowski <roman.kaczanowski.pl@gmail.com>
9
+ License-Expression: MIT
10
+ License-File: LICENSE
11
+ License-File: THIRD_PARTY_NOTICES.md
12
+ Keywords: export,headless,kaleido,plotly,png,svg,visualization
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3 :: Only
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Topic :: Scientific/Engineering :: Visualization
20
+ Requires-Python: >=3.12
21
+ Requires-Dist: pillow>=9.0.0
22
+ Requires-Dist: plotly>=5.0.0
23
+ Requires-Dist: quickjs>=1.19.0
24
+ Requires-Dist: resvg-py>=0.4.0
25
+ Description-Content-Type: text/markdown
26
+
27
+ # plotly-print
28
+
29
+ [![CI](https://img.shields.io/github/actions/workflow/status/roman-kaczanowski/plotly-print/quality-checks.yml?branch=main&style=flat-square&label=CI)](https://github.com/roman-kaczanowski/plotly-print/actions/workflows/quality-checks.yml) [![PyPI](https://img.shields.io/pypi/v/plotly-print?style=flat-square)](https://pypi.org/project/plotly-print/) [![Python](https://img.shields.io/badge/python-3.12%2B-3776AB?style=flat-square&logo=python&logoColor=white)](https://docs.python.org/3/) [![License](https://img.shields.io/badge/license-MIT-green?style=flat-square)](LICENSE)
30
+
31
+ Lightweight, browser-less static image exporter for Plotly. No Chrome, Chromium, or Playwright. Figures are rendered with an embedded QuickJS runtime, Pillow font metrics, and `resvg-py` for raster output.
32
+
33
+ Works offline: an official plotly.js cartesian partial is bundled (see [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md)).
34
+
35
+ ## Install
36
+
37
+ ```bash
38
+ pip install plotly-print
39
+ ```
40
+
41
+ ## Quickstart
42
+
43
+ ```python
44
+ import plotly.express as px
45
+ import plotly_print
46
+
47
+ fig = px.scatter(x=[1, 2, 3, 4], y=[10, 11, 12, 13], title='Sales Growth')
48
+
49
+ svg = plotly_print.to_svg(fig)
50
+ png = plotly_print.to_image(fig, format='png', scale=2)
51
+ plotly_print.write_image(fig, 'chart.png')
52
+ ```
53
+
54
+ Patch Plotly's `to_image` / `write_image` to use plotly-print:
55
+
56
+ ```python
57
+ import plotly_print
58
+
59
+ plotly_print.register()
60
+ ```
61
+
62
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup and releases.
63
+
64
+ ## Attribution
65
+
66
+ plotly-print is an independent project derived from [Mirage](https://github.com/ProfLear/mirage) by Benjamin Lear (MIT). Bundled plotly.js is copyright Plotly, Inc. (MIT); see [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md).
@@ -0,0 +1,40 @@
1
+ # plotly-print
2
+
3
+ [![CI](https://img.shields.io/github/actions/workflow/status/roman-kaczanowski/plotly-print/quality-checks.yml?branch=main&style=flat-square&label=CI)](https://github.com/roman-kaczanowski/plotly-print/actions/workflows/quality-checks.yml) [![PyPI](https://img.shields.io/pypi/v/plotly-print?style=flat-square)](https://pypi.org/project/plotly-print/) [![Python](https://img.shields.io/badge/python-3.12%2B-3776AB?style=flat-square&logo=python&logoColor=white)](https://docs.python.org/3/) [![License](https://img.shields.io/badge/license-MIT-green?style=flat-square)](LICENSE)
4
+
5
+ Lightweight, browser-less static image exporter for Plotly. No Chrome, Chromium, or Playwright. Figures are rendered with an embedded QuickJS runtime, Pillow font metrics, and `resvg-py` for raster output.
6
+
7
+ Works offline: an official plotly.js cartesian partial is bundled (see [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md)).
8
+
9
+ ## Install
10
+
11
+ ```bash
12
+ pip install plotly-print
13
+ ```
14
+
15
+ ## Quickstart
16
+
17
+ ```python
18
+ import plotly.express as px
19
+ import plotly_print
20
+
21
+ fig = px.scatter(x=[1, 2, 3, 4], y=[10, 11, 12, 13], title='Sales Growth')
22
+
23
+ svg = plotly_print.to_svg(fig)
24
+ png = plotly_print.to_image(fig, format='png', scale=2)
25
+ plotly_print.write_image(fig, 'chart.png')
26
+ ```
27
+
28
+ Patch Plotly's `to_image` / `write_image` to use plotly-print:
29
+
30
+ ```python
31
+ import plotly_print
32
+
33
+ plotly_print.register()
34
+ ```
35
+
36
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup and releases.
37
+
38
+ ## Attribution
39
+
40
+ plotly-print is an independent project derived from [Mirage](https://github.com/ProfLear/mirage) by Benjamin Lear (MIT). Bundled plotly.js is copyright Plotly, Inc. (MIT); see [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md).
@@ -0,0 +1,40 @@
1
+ # Third-party notices
2
+
3
+ ## plotly.js
4
+
5
+ File: `src/plotly_print/js/plotly-cartesian.min.js`
6
+
7
+ Official plotly.js **cartesian** partial (minified) v4.1.1, redistributed for offline /
8
+ isolated environments. The full `plotly.min.js` shipped with the `plotly` Python package
9
+ does not load in the embedded QuickJS runtime, so this smaller official partial is bundled
10
+ instead.
11
+
12
+ Copyright 2012-2026, Plotly, Inc.
13
+ All rights reserved.
14
+ Licensed under the MIT license.
15
+
16
+ ```text
17
+ The MIT License (MIT)
18
+
19
+ Copyright (c) 2014-2026 Plotly, Inc
20
+
21
+ Permission is hereby granted, free of charge, to any person obtaining a copy
22
+ of this software and associated documentation files (the "Software"), to deal
23
+ in the Software without restriction, including without limitation the rights
24
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
25
+ copies of the Software, and to permit persons to whom the Software is
26
+ furnished to do so, subject to the following conditions:
27
+
28
+ The above copyright notice and this permission notice shall be included in
29
+ all copies or substantial portions of the Software.
30
+
31
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
32
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
33
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
34
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
35
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
36
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
37
+ THE SOFTWARE.
38
+ ```
39
+
40
+ Upstream: https://github.com/plotly/plotly.js
@@ -0,0 +1 @@
1
+ """Dev-only Kaleido comparison benchmark; not published on PyPI."""
@@ -0,0 +1,3 @@
1
+ from benchmark.runner import main
2
+
3
+ main()