Metadata-Version: 2.4
Name: paperforge-diagrams
Version: 0.1.0
Summary: Vector-native PDF diagram toolkit for ReportLab Platypus
Author: Bharat Dangi
License-Expression: MIT
Classifier: Programming Language :: Python :: 3
Classifier: Operating System :: OS Independent
Requires-Python: >=3.11
Description-Content-Type: text/markdown
Requires-Dist: reportlab>=4.5.1
Requires-Dist: pydantic>=2.13.4
Provides-Extra: dev
Requires-Dist: pytest>=9.0.3; extra == "dev"
Requires-Dist: pytest-cov>=6.0; extra == "dev"
Requires-Dist: ruff>=0.9.0; extra == "dev"
Requires-Dist: mypy>=1.11.0; extra == "dev"

![PaperForge](../assets/paperforge_logo_black.svg)
# 📊 paperforge_diagrams

[![Python Version](https://img.shields.io/badge/python-3.11+-blue.svg)](https://www.python.org/)
[![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
[![Format](https://img.shields.io/badge/format-ReportLab%20Drawing-orange.svg)](#)

Vector-native PDF diagram toolkit for ReportLab Platypus. Draw complex diagrams natively inside your ReportLab documents with a clean, pythonic API. 

No raster images, no external command-line tools, and no internet connection required. All diagram types compile directly to `reportlab.graphics.shapes.Drawing` objects that wrap seamlessly as Platypus Flowables.

---

## 🚀 Features

* **Flowchart**: ANSI/ISO flowchart symbols (terminals, processes, decisions, io, connectors).
* **SequenceDiagram**: UML sequence diagrams (actors, lifelines, activation bars, message arrows, dividers).
* **ClassDiagram**: UML class diagrams (class boxes, headers, attributes, methods, relations).
* **ERDiagram**: Entity-Relationship diagrams (entities, relations, primary key and multi-value attributes).
* **StateMachine**: DFA/NFA and process state diagrams (states, transitions, initial/accepting indicators).
* **NetworkDiagram**: Network topology (hosts, servers, databases, links).
* **ArchitectureDiagram**: System architecture topologies with clients, services, databases, queues, and auto-routed connections.
* **AWSDiagram**: Cloud infrastructure architecture with AWS icons (EC2, S3, RDS, Lambda, SQS).
* **GitDiagram**: Git branch timeline diagrams with commits, merges, and distinct branch lanes.
* **SchemaDiagram**: Database table schema definition diagrams with field types, primary keys, and foreign-key links.
* **C4ContainerDiagram**: C4 Model container diagrams with System Context, Container, Component layers, and descriptive relationships.
* **TimingDiagram**: Digital waveform timing diagrams.
* **LayeredStack**: OSI model, TCP/IP stack, memory hierarchy.
* **Standalone Export**: Save drawings directly to PDF, SVG, and PNG/JPG formats.

---

## 📦 Installation

Install the package locally:

```bash
pip install ./paperforge_diagrams
```

---

## 🎨 Supported Diagram Types & Examples

### 1. Flowchart
Draw processes, decisions, and connectors with auto-layout or manual coordinates.

```python
import paperforge_diagrams as pd

# Create a horizontal flowchart with a custom scale factor
fc = pd.Flowchart(
    width=400,
    height=150,
    direction="LR",      # 'TB' (top-to-bottom, default) or 'LR' (left-to-right)
    scale_factor=1.0,    # Optional manual override for text/box sizing (default: auto-computed)
)
fc.terminal("start", "START")
fc.process("calc", "Compute Sum")
fc.decision("check", "Sum > 100?")
fc.terminal("end", "END")

fc.edge("start", "calc")
fc.edge("calc", "check")
fc.edge("check", "end", branch="yes")
fc.edge("check", "calc", branch="no", orthogonal=True)
```

### 2. Sequence Diagram
Draw actor interactions over lifelines.

```python
import paperforge_diagrams as pd

seq = pd.SequenceDiagram(width=400, height=220)
seq.actor("c", "Client")
seq.actor("s", "Server")

seq.activate("c")
seq.message("c", "s", "HTTP GET /index.html")
seq.activate("s")
seq.divider("Internal Processing")
# Arrow styles: 'solid' (default), 'dashed', 'solid_open', 'dashed_open'
seq.message("s", "c", "200 OK (HTML Document)", arrow="dashed")
seq.deactivate("s")
seq.deactivate("c")
```


### 3. Layered Stack
Draw OSI stacks, memory hierarchies, or layered architectures.

```python
import paperforge_diagrams as pd

stack = pd.LayeredStack(width=300, height=180)
stack.layer("Application", sublabel="HTTP, DNS")
stack.layer("Transport", sublabel="TCP, UDP")
stack.divider()  # Draw a thicker divider line after Transport layer
stack.layer("Network", sublabel="IP, ICMP")
stack.layer("Link", sublabel="Ethernet")
```


### 4. Network Diagram
Draw network topologies with hosts, clouds, switches, routers, and firewalls.

```python
import paperforge_diagrams as pd

net = pd.NetworkDiagram(width=500, height=220)
net.node("inet", "Internet", x=50, y=110, kind="cloud")
net.node("fw", "Firewall", x=160, y=110, kind="firewall")
net.node("sw", "Core Switch", x=270, y=110, kind="switch")
net.node("srv", "Web Server", x=380, y=150, kind="server")
net.node("db", "Database", x=380, y=70, kind="database")

net.link("inet", "fw")
net.link("fw", "sw")
net.link("sw", "srv")
net.link("sw", "db")

# Also supports building standard topologies programmatically:
# net.star_topology(center_id="sw", center_label="Switch", spoke_ids=["h1", "h2", "h3"])
# net.bus_topology(node_ids=["n1", "n2", "n3"])
# net.ring_topology(node_ids=["r1", "r2", "r3"])
# net.mesh_topology(node_ids=["m1", "m2", "m3"])
# net.tree_topology(parent_child_map={"root": ["child1", "child2"]})
```


### 5. UML Class Diagram
Draw UML class diagrams with visibility, attributes, and methods.

```python
import paperforge_diagrams as pd

cd = pd.ClassDiagram(width=400, height=250, class_w=120)
cd.uml_class("Shape", "Shape", stereotype="abstract", methods=["+ area(): double"])
cd.uml_class("Circle", "Circle", attributes=["- radius: double"], methods=["+ area(): double"])
# Relationship kinds: 'inheritance', 'realization', 'composition', 'aggregation', 'association', 'dependency'
cd.relate("Circle", "Shape", kind="inheritance")
```


### 6. Entity-Relationship Diagram (ER)
Draw entity-relationship diagrams (Chen notation) with cardinalities.

```python
import paperforge_diagrams as pd

er = pd.ERDiagram(width=450, height=200)
er.entity("Customer")
er.relationship("Buys")
er.entity("Product")
er.entity_attributes("Customer", [("ID", {"pk": True}), "Name"])
er.entity_attributes("Product", [("SKU", {"pk": True}), "Price"])
er.connect("Customer", "Buys", card_from="1", card_to="N")
er.connect("Product", "Buys", card_from="1", card_to="N")
```

### 7. State Machine (DFA / Process Transitions)
Draw finite state automata or lifecycle models.

```python
import paperforge_diagrams as pd

sm = pd.StateMachine(width=400, height=180)
sm.state("s0", "Init", x=70, y=90, initial=True)
sm.state("s1", "Active", x=200, y=90)
sm.state("s2", "Success", x=330, y=90, accepting=True)

sm.transition("s0", "s1", label="start")
sm.transition("s1", "s1", label="process")
sm.transition("s1", "s2", label="complete")
```

### 8. Timing Diagram
Draw digital timing signal waveforms.

```python
import paperforge_diagrams as pd

td = pd.TimingDiagram(width=400, height=150)
td.clock("CLK", period=20.0, cycles=6)
td.signal("RESET", transitions=[(0, 1), (15, 0)])
td.signal("DATA", transitions=[(0, 0), (35, 1), (75, 0)])
```

### 9. Database Schema Diagram
Draw database table structures with primary/foreign keys and relationships.

```python
import paperforge_diagrams as pd

schema = pd.SchemaDiagram(width=450, height=200)
schema.table("users", [
    ("id", "INTEGER", {"pk": True}),
    ("email", "VARCHAR", {}),
    ("created_at", "TIMESTAMP", {})
])
schema.table("orders", [
    ("id", "INTEGER", {"pk": True}),
    ("user_id", "INTEGER", {"fk": True}),
    ("total", "DECIMAL", {})
])
schema.relation("orders", "user_id", "users", "id")
```

### 10. Service Architecture Diagram
Draw multi-tier system topologies (clients, microservices, databases, queues) with automatic or manual layouts.

```python
import paperforge_diagrams as pd

# Supports orientation: 'horizontal' (default) or 'vertical'
arch = pd.ArchitectureDiagram(width=450, height=220, orientation="horizontal")
arch.client("web", "Web Client")
arch.service("api", "Gateway API")
arch.database("db", "Main Database")
arch.queue("q", "Message Broker")

arch.connect("web", "api", "HTTPS")
arch.connect("api", "db", "TCP/3306")
arch.connect("api", "q", "AMQP")
```


### 11. C4 Container Diagram
Draw C4 Model Container views to document software systems, containers, technologies, and relationships.

```python
import paperforge_diagrams as pd

c4 = pd.C4ContainerDiagram(width=400, height=200)
c4.system("user", "Customer")
c4.container("web", "SPA (React)", "Provides shopping UI")
c4.container("api", "API Application (Go)", "Handles business logic")
c4.container("db", "Database (Postgres)", "Stores order data")

c4.relate("user", "web", "Uses")
c4.relate("web", "api", "Makes API calls to")
c4.relate("api", "db", "Reads/Writes to")
```

### 12. Git Branch Flow Diagram
Draw commits, branch lanes, merges, and timelines horizontally.

```python
import paperforge_diagrams as pd

git = pd.GitDiagram(width=400, height=150)
git.commit("main", "C1: Initial Commit")
git.branch("main", "feature")
git.commit("feature", "C2: Implement login")
git.commit("main", "C3: Hotfix bug")
git.merge("feature", "main", "C4: Merge feature/login")
```

### 13. AWS Diagram
Draw AWS cloud infrastructure diagrams using vector-native AWS icons.

```python
import paperforge_diagrams as pd

# Supports orientation: 'horizontal' (default) or 'vertical'
aws = pd.AWSDiagram(width=450, height=220, orientation="horizontal")
# Supported node methods: ec2(), rds(), s3(), lambda_fn(), sqs()
aws.ec2("web", "Web Server")
aws.rds("db", "RDS PostgreSQL")
aws.s3("bucket", "S3 Storage")
aws.sqs("queue", "Job Queue")

aws.connect("web", "db", "SQL Connection")
aws.connect("web", "bucket", "Uploads")
aws.connect("web", "queue", "Events")
```


---

## 🎨 Diagram Theming & Integration

`paperforge_diagrams` comes with built-in dark and light themes, and can dynamically inherit themes from `paperforge_notes` for consistent visual layouts.

### 1. Using Preset Diagram Themes
Apply a theme directly to your builder instance:

```python
import paperforge_diagrams as pd

# Use preset theme (pd.DARK or pd.LIGHT)
diagram = pd.Flowchart(width=300, height=200, theme=pd.LIGHT)
```

### 2. Matching Notes Theme (Integration)
Derive matching diagram settings from the active document notes theme:

```python
import paperforge_notes as pn
import paperforge_diagrams as pd

# 1. Set the notes theme
pn.set_theme(pn.OCEAN_DARK)

# 2. Derive the matching diagram theme
diag_theme = pd.DiagramTheme.from_notes_theme(pn.get_theme())

# 3. Apply it to your diagrams
fc = pd.Flowchart(width=400, height=200, theme=diag_theme)
```

### 3. Customizing & Overriding Themes
Modify a theme on the fly using `model_copy(update={...})`:

```python
# Create a print-optimized black-and-white diagram theme
bw_theme = pd.DiagramTheme.from_notes_theme(pn.get_theme()).model_copy(
    update={
        "stack_colors": ("#ffffff", "#ffffff", "#ffffff"),
        "stack_stroke": "#000000",
        "stack_text": "#000000",
        "stack_sublabel_text": "#000000",
        "bg": "#ffffff",
        "text": "#000000",
        "node_fill": "#ffffff",
        "node_stroke": "#000000",
        "node_text": "#000000",
        "font_name": "Times-Roman",
        "font_name_bold": "Times-Bold",
        "font_name_italic": "Times-Italic",
    }
)
# Apply to stack diagram
stack = pd.LayeredStack(width=300, height=180, theme=bw_theme)
```

---

## 💾 Standalone Export

Export your diagrams directly to vector or raster formats without a ReportLab story:

```python
diagram = pd.Flowchart(width=300, height=200)
diagram.terminal("s", "START").terminal("e", "END").edge("s", "e")

# Save to PDF (vector)
diagram.save("output.pdf")

# Save to SVG (vector)
diagram.save("output.svg")

# Save to PNG (raster)
diagram.save("output.png")
```

---

## 📝 Integration with `paperforge_notes`

To embed a vector diagram into a PaperForge notes document, call `.as_flowable()` and pass it to `pn.add(...)`:

```python
import paperforge_notes as pn
import paperforge_diagrams as pd

fc = pd.Flowchart(width=pn.CW, height=200)
fc.terminal("s", "START").terminal("e", "END").edge("s", "e")

# Add the diagram flowables directly
pn.add(fc.as_flowable())
```

### Static Type-Checking (Pyright / Pylance)
If you see static type warnings in your IDE:
> `Argument of type "list[Unknown]" cannot be assigned to parameter "x" of type "Flowable" in function "add"`

This happens because `.as_flowable()` returns a `list[Flowable]` (which packages the drawing flowable along with its optional caption `Paragraph` flowable) rather than a single raw `Flowable`.

`paperforge_notes` handles this union type cleanly. Calling `pn.add(diagram.as_flowable())` is type-safe and fully compliant.

---

## 📋 Requirements & License

* **Python** >= 3.11
* **reportlab** >= 4.5.1
* **pydantic** >= 2.13.4
* Licensed under the **MIT License**.
