Metadata-Version: 2.4
Name: chordsketch
Version: 0.5.0
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Programming Language :: Python :: Implementation :: PyPy
Classifier: Programming Language :: Rust
Classifier: Topic :: Multimedia :: Sound/Audio
Classifier: Topic :: Text Processing :: Markup
Summary: ChordPro file format parser and renderer
Keywords: chordpro,music,chord,parser,lyrics,rust,uniffi
Home-Page: https://github.com/koedame/chordsketch
Author: koedame
License: MIT
Requires-Python: >=3.8
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
Project-URL: Homepage, https://github.com/koedame/chordsketch
Project-URL: Issues, https://github.com/koedame/chordsketch/issues
Project-URL: Repository, https://github.com/koedame/chordsketch

<p align="center">
  <img src="https://raw.githubusercontent.com/koedame/chordsketch/main/assets/logo.svg" alt="ChordSketch" width="80" height="80">
</p>

# chordsketch

[ChordSketch](https://github.com/koedame/chordsketch) Python bindings —
parse and render [ChordPro](https://www.chordpro.org/) and
[iReal Pro](https://www.irealpro.com/) chord charts from Python via
native Rust extensions generated by [UniFFI](https://mozilla.github.io/uniffi-rs/).

**Native extension — no pure-Python fallback.**
The library compiles to a platform wheel; prebuilt wheels for common platforms
are published to PyPI so no Rust toolchain is needed to install.

## Installation

```bash
pip install chordsketch
```

Requires Python 3.8+ (CPython or PyPy).

## Quick Start

```python
import chordsketch

source = """{title: Amazing Grace}
{key: G}

[G]Amazing [G7]grace, how [C]sweet the [G]sound"""

html = chordsketch.parse_and_render_html(source)
text = chordsketch.parse_and_render_text(source)
pdf  = chordsketch.parse_and_render_pdf(source)   # bytes

print(chordsketch.version())
```

## API

The tables below cover every function in
[`crates/ffi/src/chordsketch.udl`](src/chordsketch.udl) (UniFFI's
`namespace chordsketch { ... }` block is the authoritative export
surface for all language bindings).

### Render-function parameters

`parse_and_render_*` functions all accept the same three arguments:

| Parameter | Type | Description |
|-----------|------|-------------|
| `input` | `str` | ChordPro source text |
| `config_json` | `str \| None` | Preset name (`"guitar"`, `"ukulele"`) or inline RRJSON; `None` for defaults |
| `transpose` | `int \| None` | Semitone offset; must fit in `i8` (`-128..=127`). Out-of-range values raise `ChordSketchError.InvalidConfig`. `None` defaults to 0. |

### Basic rendering

| Function | Returns | Description |
|----------|---------|-------------|
| `parse_and_render_text(input, config_json, transpose)` | `str` | Plain text output |
| `parse_and_render_html(input, config_json, transpose)` | `str` | Full HTML document |
| `parse_and_render_pdf(input, config_json, transpose)` | `bytes` | Raw PDF bytes |

### Body-only HTML and stylesheet

| Function | Returns | Description |
|----------|---------|-------------|
| `parse_and_render_html_body(input, config_json, transpose)` | `str` | Body-only `<div class="song">…</div>` HTML fragment with no `<!DOCTYPE>` / `<html>` / `<head>` / `<title>` / embedded `<style>` — pair with `render_html_css` when the host supplies its own document envelope |
| `render_html_css()` | `str` | Canonical chord-over-lyrics CSS that `parse_and_render_html` embeds inside `<style>` (byte-stable; safe to hash for cache-busting) |
| `render_html_css_with_config_json(config_json)` | `str` | Variant of `render_html_css` that honours `settings.wraplines` from the supplied config (when `wraplines` is false, `.line` emits `flex-wrap: nowrap`) |

### Captured warnings

| Function | Returns | Description |
|----------|---------|-------------|
| `parse_and_render_text_with_warnings(input, config_json, transpose)` | `TextRenderWithWarnings { output: str, warnings: list[str] }` | Plain text + captured warnings |
| `parse_and_render_html_with_warnings(input, config_json, transpose)` | `TextRenderWithWarnings { output: str, warnings: list[str] }` | HTML + captured warnings |
| `parse_and_render_pdf_with_warnings(input, config_json, transpose)` | `PdfRenderWithWarnings { output: bytes, warnings: list[str] }` | PDF + captured warnings |
| `parse_and_render_html_body_with_warnings(input, config_json, transpose)` | `TextRenderWithWarnings { output: str, warnings: list[str] }` | Body-only HTML fragment + captured warnings (body counterpart to `parse_and_render_html_with_warnings`) |

### iReal Pro conversion

| Function | Returns | Description |
|----------|---------|-------------|
| `convert_chordpro_to_irealb(input)` | `ConversionWithWarnings { output: str, warnings: list[str] }` | Convert ChordPro source to an `irealb://` URL (lossy — drops lyrics, fonts, capo) |
| `convert_irealb_to_chordpro_text(input)` | `ConversionWithWarnings { output: str, warnings: list[str] }` | Convert an `irealb://` URL to rendered ChordPro text |
| `render_ireal_svg(input)` | `str` (SVG document) | Render an `irealb://` URL as an iReal Pro-style SVG chart |
| `render_ireal_png(input)` | `bytes` (PNG byte stream) | Render an `irealb://` URL as a PNG image (300 DPI default, A4-equivalent canvas) |
| `render_ireal_pdf(input)` | `bytes` (PDF byte stream) | Render an `irealb://` URL as a single-page A4 PDF document |
| `parse_irealb(input)` | `str` (JSON) | Parse an `irealb://` URL into AST-shaped JSON (mirrors `IrealSong`) |
| `serialize_irealb(input)` | `str` (URL) | Serialize an AST-shaped JSON string back into an `irealb://` URL (round-trips with `parse_irealb`) |

`output` of `convert_irealb_to_chordpro_text` is the rendered text
representation of the converted song (`chordsketch-render-text`
output), not raw ChordPro source. Each `warnings` entry is a
`"<kind>: <message>"` string (`kind` is `lossy-drop`,
`approximated`, or `unsupported`).

```python
result = chordsketch.convert_chordpro_to_irealb("{title: Test}\n[C]Hello")
print(result.output)    # "irealb://..."
print(result.warnings)  # ["lossy-drop: lyrics are dropped", ...]

text = chordsketch.convert_irealb_to_chordpro_text(result.output)
print(text.output)
```

The `*_with_warnings` variants return the render warnings (transpose
saturation, chorus recall limits, `{columns}` clamp, etc.) as a list
alongside the output instead of forwarding them to `sys.stderr` /
`NSLog` / `System.err` / `$stderr`. Use them when embedding ChordSketch
in an app that needs to surface warnings in the UI or aggregate them.
See #1827.

### Validation

| Function | Returns | Description |
|----------|---------|-------------|
| `validate(input)` | `list[ValidationError]` (`{ line: int, column: int, message: str }`, line / column one-based) | Validate ChordPro input and return any parse errors as structured records (empty list if clean). Mirrors the WASM `validate` shape and the NAPI `ValidationError[]` interface. |

```python
errors = chordsketch.validate(source)  # list[ValidationError] — empty if clean
for e in errors:
    print(f"line {e.line}, column {e.column}: {e.message}")
```

### Chord diagrams

| Function | Returns | Description |
|----------|---------|-------------|
| `chord_diagram_svg(chord, instrument)` | `str \| None` (SVG markup) | Render a chord diagram as inline SVG. `instrument` is case-insensitive: `"guitar"`, `"ukulele"` (alias `"uke"`), or `"piano"` (aliases `"keyboard"`, `"keys"`). Returns `None` when the chord is not in the built-in voicing database; raises `ChordSketchError.InvalidConfig` on unknown instrument. |

### Utility

| Function | Returns | Description |
|----------|---------|-------------|
| `version()` | `str` | Library version string |

```python
print(chordsketch.version())  # e.g. "0.3.0"
```

## Options

```python
# Transpose up 2 semitones with the ukulele preset
html = chordsketch.parse_and_render_html(source, "ukulele", 2)

# Inline RRJSON configuration
html = chordsketch.parse_and_render_html(
    source,
    '{"settings": {"notation": "solfege"}}',
    None,
)
```

## Error handling

Functions raise `chordsketch.ChordSketchError` on invalid configuration:

```python
try:
    html = chordsketch.parse_and_render_html(source, "{ bad json !!!", None)
except chordsketch.ChordSketchError as e:
    print(e)
```

`ChordSketchError` has three variants:
- `NoSongsFound` — the input produced no parseable songs (rare with lenient parsing)
- `InvalidConfig(reason)` — the `config_json` argument is not a known preset and not valid RRJSON, or `transpose` is outside the `i8` range, or `chord_diagram_svg` was called with an unsupported instrument
- `ConversionFailed(reason)` — a ChordPro ↔ iReal Pro conversion failed (`convert_chordpro_to_irealb`, `convert_irealb_to_chordpro_text`, `render_ireal_*`, `parse_irealb`, `serialize_irealb`)

Parse errors in the ChordPro input are **not** raised — the renderer is lenient
and produces a best-effort result. Call `validate()` to surface diagnostics.

## Links

- **Project**: https://github.com/koedame/chordsketch
- **Playground**: https://chordsketch.koeda.me
- **Issues**: https://github.com/koedame/chordsketch/issues

## License

MIT

