Metadata-Version: 2.4
Name: tentags
Version: 2.1.4
Summary: A declarative template language and Intermediate Representation (IR) for automated HTML, Excel, and PDF table/document generation.
Author-email: Zhandos Mambetali <zhandos.mambetali@gmail.com>
License-Expression: Apache-2.0
Project-URL: Homepage, https://tentags.org
Project-URL: Documentation, https://tentags.org
Project-URL: Repository, https://github.com/Jandos77/tentags
Project-URL: Issues, https://github.com/Jandos77/tentags/issues
Keywords: html,excel,xlsx,table,generator,spreadsheet,pycells,tags,parser
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.8
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 :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: Text Processing :: Markup :: HTML
Classifier: Topic :: Office/Business :: Financial :: Spreadsheet
Requires-Python: >=3.8
Description-Content-Type: text/markdown
License-File: LICENSE
Provides-Extra: excel
Requires-Dist: openpyxl>=3.1.0; extra == "excel"
Requires-Dist: et_xmlfile>=2.0.0; extra == "excel"
Provides-Extra: pdf
Requires-Dist: reportlab>=4.0.0; extra == "pdf"
Provides-Extra: all
Requires-Dist: openpyxl>=3.1.0; extra == "all"
Requires-Dist: et_xmlfile>=2.0.0; extra == "all"
Requires-Dist: reportlab>=4.0.0; extra == "all"
Provides-Extra: dev
Requires-Dist: openpyxl>=3.1.0; extra == "dev"
Requires-Dist: et_xmlfile>=2.0.0; extra == "dev"
Requires-Dist: reportlab>=4.0.0; extra == "dev"
Requires-Dist: pytest>=7.0.0; extra == "dev"
Requires-Dist: build>=1.0.0; extra == "dev"
Requires-Dist: twine>=4.0.0; extra == "dev"
Dynamic: license-file

# TenTags 🏷️

<p align="center">
  <a href="https://tentags.org">
    <img src="https://tentags.org/assets/img/tentags_logo.png" width="400" alt="TenTags logo">
  </a>
</p>

[![PyPI version](https://img.shields.io/pypi/v/tentags.svg)](https://pypi.org/project/tentags/)
[![Python versions](https://img.shields.io/pypi/pyversions/tentags.svg)](https://pypi.org/project/tentags/)
[![License: Apache 2.0](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)

**TenTags** is a declarative template language and **Intermediate Representation (IR)** for **HTML**, **Excel (`.xlsx`)**, and **PDF** table and document generation.

### 🚀 Current Release: 2.1.4

**TenTags 2.1.4** is a release that synchronizes package version configuration and demo environment requirements, preserving the core multiline style fixes, Serializer API, and Multitable Layout controls.

## Install

```bash
pip install tentags
```

Optional XLSX and PDF backends are described later in the installation section.

---

## Start Here: The Three Blocks

TenTags syntax is intentionally small. A table is described by only three blocks:

```text
preamble
style(...)
data(...)
```

The recommended form is:

```python
import tentags

preamble = '3,3,1,"#cbd5e1","solid-1",0,28'

style = """style(
<bg=blue><color=white><b></b></color></bg>, <bg=blue><color=white><b></b></color></bg>, <bg=blue><color=white><b></b></color></bg>;
<bg=white></bg>, <bg=white></bg>, <bg=green></bg>;
<bg=#f8fafc></bg>, <bg=#f8fafc></bg>, <bg=yellow></bg>
)"""

data = """data(
Product, Q1 Sales, Status;
Laptops, <right>12000</right>, <center>Excellent</center>;
Headphones, <right>3500</right>, <center>Good</center>
)"""

model = tentags.compile(preamble, style, data)
```

The same compiled model can be rendered to HTML, XLSX, or PDF.

---

## 1. Preamble

The preamble is the first line of a TenTags table. It defines the shape and global grid settings.

```text
rows, cols, border_width, "border_color", "border_style", stretch, cell_height
```

Example:

```text
3,3,1,"#cbd5e1","solid-1",0,28
```

| Position | Argument | Meaning |
| :---: | :--- | :--- |
| 1 | `rows` | Number of table rows. |
| 2 | `cols` | Number of table columns. |
| 3 | `border_width` | Border width in pixels. |
| 4 | `border_color` | Border color. You can use names such as `"blue"` or HEX such as `"#cbd5e1"`. |
| 5 | `border_style` | `solid`, `dashed`, `dotted`; suffix `-1` enables inner grid borders, suffix `-0` hides borders. |
| 6 | `stretch` | `0` keeps fixed row height, `1` allows cells to stretch. |
| 7 | `cell_height` | Default row height in pixels. |

The first two arguments must match your table grid. For example, `3,3,...` means that `style(...)` and `data(...)` should each describe 3 rows and 3 columns.

---

## 2. Style Block

The `style(...)` block describes presentation: background colors, text colors, font weight, alignment, merges, and other visual rules.

Rows are separated by semicolons (`;`). Columns are separated by commas (`,`):

```text
style(
row1_col1, row1_col2, row1_col3;
row2_col1, row2_col2, row2_col3;
row3_col1, row3_col2, row3_col3
)
```

Example:

```python
style = """style(
<bg=blue><color=white><b></b></color></bg>, <bg=blue><color=white><b></b></color></bg>, <bg=blue><color=white><b></b></color></bg>;
<bg=white></bg>, <bg=white></bg>, <bg=green></bg>;
<bg=#f8fafc></bg>, <bg=#f8fafc></bg>, <bg=yellow></bg>
)"""
```

Colors can be simple names such as `blue`, `green`, `white`, and `yellow`, or exact HEX values such as `#1977ff` and `#f8fafc`.

In `style(...)`, a cell may contain only tags and no text. That is normal:

```text
<left><u><bg=#eff6ff></bg></u></left>
```

This still counts as a real style cell because it carries presentation for the matching `data(...)` cell.

---

## 3. Tags

Tags can be used in `style(...)` and `data(...)`. In `style(...)`, tags usually describe presentation for a cell. In `data(...)`, tags usually wrap real content.

| Tag | Meaning | Example |
|---|---|---|
| `<b>...</b>` | Bold text. | `<b>Total</b>` |
| `<i>...</i>` | Italic text. | `<i>Pending</i>` |
| `<u>...</u>` | Underlined text. | `<u>Open</u>` |
| `<s>...</s>` | Strikethrough text. | `<s>Cancelled</s>` |
| `<fs=...>` | Font size. | `<fs=16>Title</fs>` |
| `<left>` | Left alignment. | `<left>Name</left>` |
| `<center>` | Center alignment. | `<center>Status</center>` |
| `<right>` | Right alignment. | `<right>12000</right>` |
| `<color=...>` | Text color. | `<color=green>OK</color>` |
| `<bg=...>` | Cell background. | `<bg=yellow>Review</bg>` |
| `<cm>...</cm>` | Merge cells horizontally. | `<cm>Title, ,</cm>` |
| `<rm>...</rm>` | Merge cells vertically. | `<rm>Date</rm>` |
| `<url=...>...</url>` | Link or navigation target. | `<url=https://tentags.org>Site</url>` |
| `<mark=...>` | Single tag that marks the current cell. | `<mark=Summary><b>Total</b>` |
| `<url=goto:...>...</url>` | Navigates to a marked cell or address. | `<url=goto:Summary>Go to total</url>` |
| `<img src=... w=... h=... m=...>` | Single image tag. `w`, `h`, and `m` are pixels; `m` means margin on all sides. `h=auto` keeps proportions. | `<img src=logo.png w=120 h=auto m=15>` |
| `<value=...>` | Insert value from a local cell or mark. | `<value=B2>` |

Single tags such as `<mark=...>`, `<img ...>`, and `<value=...>` are not closed.

---

## 4. Data Block

The `data(...)` block describes the actual table content. It uses the same grid rules as `style(...)`:

```text
data(
row1_col1, row1_col2, row1_col3;
row2_col1, row2_col2, row2_col3;
row3_col1, row3_col2, row3_col3
)
```

Example:

```python
data = """data(
Product, Q1 Sales, Status;
Laptops, <right>12000</right>, <center>Excellent</center>;
Headphones, <right>3500</right>, <center>Good</center>
)"""
```

The first value goes to cell `A1`, the second to `B1`, the third to `C1`. After `;`, the next row starts: `A2`, `B2`, `C2`, and so on.

For every explicit table:

```text
preamble rows == style rows == data rows
preamble cols == style cols == data cols
```

---

## 5. One-Line Formula

TenTags also supports the original compact form: preamble and `data(...)` in one formula string.

```python
import tentags

formula = '2,3,1,"#cbd5e1","solid-1",0,28,data(Product,Q1 Sales,Status;Laptops,<right>12000</right>,<center>Excellent</center>)'

model = tentags.parse(formula)
html = tentags.render_html(model)
```

The same formula can be written across several lines for readability:

```python
formula = '''2,3,1,"#cbd5e1","solid-1",0,28,data(
Product, Q1 Sales, Status;
Laptops, <right>12000</right>, <center>Excellent</center>
)'''

model = tentags.parse(formula)
```

The separated `preamble`, `style(...)`, and `data(...)` form is recommended for larger tables because styles and data are easier to maintain independently.

---

## 6. Render One Table To Files

```python
import tentags

preamble = '3,3,1,"#cbd5e1","solid-1",0,28'

style = """style(
<bg=blue><color=white><b></b></color></bg>, <bg=blue><color=white><b></b></color></bg>, <bg=blue><color=white><b></b></color></bg>;
<bg=white></bg>, <bg=white></bg>, <bg=green></bg>;
<bg=#f8fafc></bg>, <bg=#f8fafc></bg>, <bg=yellow></bg>
)"""

data = """data(
Product, Q1 Sales, Status;
Laptops, <right>12000</right>, <center>Excellent</center>;
Headphones, <right>3500</right>, <center>Good</center>
)"""

model = tentags.compile(preamble, style, data)

html = tentags.render_html(model)
with open("sales_report.html", "w", encoding="utf-8") as f:
    f.write(html)

tentags.render_xlsx(model, "sales_report.xlsx")
tentags.render_pdf(model, "sales_report.pdf")
```

---

## 7. Use Data From A Database

TenTags does not need SQL, loops, or business logic inside the DSL. Use Python for data preparation, then serialize rows into TenTags.

```python
import sqlite3
import tentags

conn = sqlite3.connect(":memory:")
conn.execute("CREATE TABLE sales (product TEXT, q1 INT, status TEXT)")
conn.executemany(
    "INSERT INTO sales VALUES (?, ?, ?)",
    [
        ("Laptops", 12000, "Excellent"),
        ("Smartphones", 24000, "Excellent"),
        ("Headphones", 3500, "Good"),
    ],
)

records = conn.execute("SELECT product, q1, status FROM sales").fetchall()

data_rows = [["Product", "Q1 Sales", "Status"]]
style_lines = [
    "<center><u><bg=blue><color=white><b></b></color></bg>, "
    "<bg=blue><color=white><b></b></color></bg>, "
    "<bg=blue><color=white><b></b></color></bg>"
]

for product, q1, status in records:
    status_bg = "green" if status == "Excellent" else "yellow"
    data_rows.append([
        product,
        f"<right>{q1}</right>",
        f"<center>{status}</center>",
    ])
    style_lines.append(f"<bg=white></bg>, <bg=white></bg>, <bg={status_bg}></bg>")

style_lines[-1] += "</u></center>"

preamble = tentags.serialize.preamble(
    len(data_rows),
    3,
    border_color="#cbd5e1",
    border_style="solid-1",
    cell_height=28,
)
style = "style(\n" + ";\n".join(style_lines) + "\n)"
data = tentags.serialize.data(data_rows, expected_rows=len(data_rows), expected_cols=3)

model = tentags.compile(preamble, style, data)
tentags.render_xlsx(model, "sales_from_db.xlsx")
```

The important separation is:

```text
Python data -> TenTags DSL -> compile() -> IR -> HTML/PDF/XLSX
```

---

## 8. What Comes Next

After the basic three-block structure, TenTags also supports:

- **MultiTable** reports: several separate tables/lists in one HTML, XLSX, or PDF output.
- **Addressing** with `Table!List!A1`, `Table!List!A1:B3`, and `Table!List!Summary`.
- **Navigation** with `<mark=Summary>` and `<url=goto:Table!List!Summary>Open</url>`.
- **Framework integrations** for Django, Flask, FastAPI, and Jinja2.
- **LLM bootstrap prompt** through `tentags.get_prompt()`.

---

## Design Principle

A new TenTags tag may be added only if all three conditions are true:

1. It can be interpreted consistently by all renderers.
2. It belongs to the logical document model, not to one renderer's physical representation.
3. It cannot be expressed cleanly with existing TenTags primitives.

This keeps TenTags compact, predictable, and renderer-independent.

---

## 📦 Installation

Install from PyPI via pip:

```bash
pip install tentags
```

---

## 🎨 Advanced Example: Beautiful Styled Table & Merges

Here is how a single, clean **TenTags** expression generates an enterprise-grade financial dashboard table featuring merged headers (`<cm>`), custom font sizing (`<fs>`), cell background colors (`<bg=>`), text alignment (`<left>`, `<right>`), and custom typography (`<b>`, `<i>`, `<color=>`) across both **HTML** and **Excel (`.xlsx`)**:

```python
import tentags

# Define an advanced 4x4 styled financial performance grid using clean empty elements (, ,) inside merges
formula = '''4,4,1,"#cbd5e1","solid",0,45, data(
    <fs=18><bg=#1e293b><color=white><b><cm>Q3 Financial Performance Dashboard, , , , </cm></b></color></bg></fs>;
    <bg=#f1f5f9><b><left>Department</left></b></bg>, <bg=#f1f5f9><b><center>Revenue</center></b></bg>, <bg=#f1f5f9><b><center>Expenses</center></b></bg>, <bg=#f1f5f9><b><center>Net Profit</center></b></bg>;
    <left>Engineering</left>, <right>"$240,000"</right>, <right>"$180,000"</right>, <bg=#dcfce7><color=#166534><b><right>"+$60,000"</right></b></color></bg>;
    <left>Sales & Marketing</left>, <right>"$310,000"</right>, <right>"$210,000"</right>, <bg=#dcfce7><color=#166534><b><right>"+$100,000"</right></b></color></bg>
)'''

# Compile IR once — render to any backend
model = tentags.parse(formula)

# 1. Export to native Excel (.xlsx) with exact fonts, fills & merge_cells
tentags.render_xlsx(model, "Q3_Financial_Dashboard.xlsx")

# 2. Export to vector PDF (.pdf) via ReportLab
tentags.render_pdf(model, "Q3_Financial_Dashboard.pdf")

# 3. Render to responsive HTML string with inline CSS
html_table = tentags.render_html(model)
print(html_table)
```

### 📋 Visual Structure & Styling Result:

<p align="center">
  <img src="https://tentags.org/assets/img/Q3_Financial_Dashboard.png" alt="Q3 Financial Performance Dashboard Output" width="750">
</p>

---

## 📊 Excel Matrix Example: Row Merges (`<rm>`), Column Merges (`<cm>`) & Colors

To see how **TenTags** shines as a native Excel spreadsheet generator, here is a 5x5 **Enterprise Allocation Matrix** utilizing combined row merges (`<rm>`), multi-column merges (`<cm>`), clean empty elements (` `, `, `), and classic Microsoft Excel color palettes (`#1F4E78`, `#DDEBF7`, `#E2EFDA`, `#FFF2CC`):

```python
import tentags

# Define an Excel matrix with vertical row merges (<rm>) and horizontal column merges (<cm>)
excel_formula = '''5,5,1,"#B0C4DE","solid",0,35, data(
    <fs=16><bg=#1F4E78><color=white><b><cm>2026 Enterprise Budget & Allocation Matrix, , , , </cm></b></color></bg></fs>;
    <bg=#DDEBF7><b><rm><center>Category</center></rm></b></bg>, <bg=#DDEBF7><b><cm><center>Q1 & Q2 Allocation, </center></cm></b></bg>, <bg=#DDEBF7><b><cm><center>Q3 & Q4 Allocation, </center></cm></b></bg>;
    <bg=#DDEBF7><b><rm> </rm></b></bg>, <bg=#F2F2F2><b><center>Hardware</center></b></bg>, <bg=#F2F2F2><b><center>Software</center></b></bg>, <bg=#F2F2F2><b><center>Hardware</center></b></bg>, <bg=#F2F2F2><b><center>Software</center></b></bg>;
    <bg=#FFF2CC><b><left>R&D Division</left></b></bg>, <right>"$150,000"</right>, <right>"$85,000"</right>, <right>"$120,000"</right>, <right>"$95,000"</right>;
    <bg=#E2EFDA><color=#375623><b><left>Total Budget</left></b></color></bg>, <bg=#E2EFDA><color=#375623><b><cm><right>"$235,000", </right></cm></b></color></bg>, <bg=#E2EFDA><color=#375623><b><cm><right>"+$215,000", </right></cm></b></color></bg>
)'''

# Compile IR once — export to all three backends
model = tentags.parse(excel_formula)

# Export to native Excel (.xlsx)
tentags.render_xlsx(model, "Enterprise_Budget_Matrix.xlsx")

# Export to vector PDF (.pdf)
tentags.render_pdf(model, "Enterprise_Budget_Matrix.pdf")
```

### 🗓️ Visual Spreadsheet Grid Structure (`A1:E5`):

<p align="center">
  <img src="https://tentags.org/assets/img/example.png" alt="TenTags Excel Matrix Output" width="750">
</p>

---

## 🛠️ API Reference

### Module Constants & Metadata
- **`tentags.__version__`**: Library version string (e.g., `'2.1.4'`).
- **`tentags.version_info`**: Version tuple for checking compatibility (e.g., `(2, 1, 4)`).
- **`tentags.__author__`**: Author name (`'Zhandos Mambetali'`).
- **`tentags.__license__`**: Project license (`'Apache-2.0'`).
- **`tentags.__homepage__`**: Link to home website (`'https://tentags.org'`).
- **`tentags.__url__`**: Canonical library URL (`'https://tentags.org'`).
- **`tentags.__copyright__`**: Copyright notice (`'Copyright (c) 2026 Zhandos Mambetali'`).

### Diagnostic & Utility Helpers

#### `tentags.info() -> None`
Prints the package version, author, license, website, Python version, and availability of the HTML, PDF, and XLSX renderers.

#### `tentags.features() -> dict`
Checks the availability of optional rendering backends. Returns a dictionary:
```python
{
    "html": True,
    "pdf": True,   # True if reportlab is installed
    "xlsx": True   # True if openpyxl is installed
}
```

#### `tentags.get_prompt(print_output: bool = False) -> str`
Returns the bundled LLM bootstrap prompt for teaching another model how to work with TenTags. Pass `print_output=True` to also print it:
```python
import tentags

prompt = tentags.get_prompt()
tentags.get_prompt(print_output=True)
```

#### `tentags.validate(formula: str) -> dict`
Syntactically checks a TenTags formula's layout configuration and markup tag balance. Returns a status dictionary:
- Success: `{"status": "ok", "message": "Syntax OK"}`
- Failure: `{"status": "error", "message": "Missing closing tag </b>..."}`

#### `tentags.demo(name: str = "dashboard") -> None`
Generates an HTML demo in the current directory and XLSX/PDF files when the corresponding optional backends are installed.
Supported templates: `'dashboard'`, `'invoice'`, `'table'`. Example:
```python
import tentags
tentags.demo("invoice") # Generates HTML, XLSX, and PDF invoices on the fly
```

### Core API Functions

### `tentags.render(preamble_or_formula, style=None, data=None, context=None) -> str`
Renders either a complete formula or decoupled preamble, style, and data blocks to a `<table>...</table>` HTML string.
- **`preamble_or_formula`**: A complete formula such as `'rows, cols, border_width, "border_color", "border_style", stretch, cell_height, data(...)'`, or a preamble used with `style` and `data`.
- **`style`**: Optional `style(...)` block for decoupled rendering.
- **`data`**: Optional `data(...)` block for decoupled rendering.
- **`context`**: Optional dictionary of variable names and their replacement values (`{'VarName': 'Value'}`).

### `tentags.parse(formula: str, context: dict = None) -> TableModel`
Parses the formula into a structured `TableModel` instance containing 2D cell grids (`CellDesc`), `BorderFlags`, and styles without generating HTML.

### `tentags.compile(preamble, style, data, context: dict = None) -> TableModel`
Builds a `TableModel` from decoupled preamble, style, and data blocks. Each block may be a TenTags string or a parsed cell grid.

### Serializer API

The Serializer API converts ordinary Python structures into canonical TenTags DSL strings. It does not create IR and it does not replace the DSL: `compile(preamble, style, data)` remains the only compiler entry point.

The canonical namespace is `tentags.serialize`. Top-level `dumps_preamble()`, `dumps_style()`, and `dumps_data()` remain available as compatible convenience aliases.

In the Python list below, quotes are only Python string syntax. They are not part of TenTags DSL. The serializer outputs plain TenTags cells without quotes.

This example deliberately keeps `style` as raw TenTags DSL, because TenTags styles can open a tag in one cell and close it later, allowing a tag to apply across a larger table region.

For LLMs and fully automatic generation, `tentags.serialize.style(...)` is still useful. It serializes a Python style matrix into `style(...)`. Use it when each generated cell already has its own complete style expression:

```python
style_rows = [
    ["<bg=blue><color=white><b></b></color></bg>"] * 3,
    ["<bg=white></bg>", "<bg=white></bg>", "<bg=green></bg>"],
    ["<bg=#f8fafc></bg>", "<bg=#f8fafc></bg>", "<bg=yellow></bg>"],
]

style = tentags.serialize.style(style_rows, expected_rows=3, expected_cols=3)
```

Use raw `style(...)` when you want TenTags tags to span across a larger region. Use `tentags.serialize.style(...)` when generated code benefits from a simple explicit matrix.

```python
import tentags

rows = [
    ["Period", "<right>Revenue</right>", "<center>Status</center>"],
    ["January", "<right>125000</right>", "<center>Closed</center>"],
    ["July", "<right>158900</right>", "<center>Review</center>"],
]

style = """style(
<center><u><bg=blue><color=white><b></b></color></bg>, <bg=blue><color=white><b></b></color></bg>, <bg=blue><color=white><b></b></color></bg>;
<bg=white></bg>, <bg=white></bg>, <bg=green></bg>;
<bg=#f8fafc></bg>, <bg=#f8fafc></bg>, <bg=yellow></bg></u></center>
)"""

preamble = tentags.serialize.preamble(len(rows), 3, border_color="#64748b", border_style="solid-1", cell_height=28)
data = tentags.serialize.data(rows, expected_rows=len(rows), expected_cols=3)

model = tentags.compile(preamble, style, data)
```

The generated `data` string is:

```text
data(
Period,<right>Revenue</right>,<center>Status</center>;
January,<right>125000</right>,<center>Closed</center>;
July,<right>158900</right>,<center>Review</center>
)
```

The same serializer pattern is useful for database-backed reports:

```python
import sqlite3
import tentags

conn = sqlite3.connect("demo_output/finance.db")
conn.row_factory = sqlite3.Row
records = [dict(row) for row in conn.execute("SELECT period, revenue, status FROM monthly_report")]
conn.close()

data_rows = [["Period", "<right>Revenue</right>", "<center>Status</center>"]]
style_lines = [
    "<center><u><bg=blue><color=white><b></b></color></bg>, "
    "<bg=blue><color=white><b></b></color></bg>, "
    "<bg=blue><color=white><b></b></color></bg>"
]

for index, record in enumerate(records):
    base_bg = "white" if index % 2 == 0 else "#f8fafc"
    data_rows.append([
        record["period"],
        f"<right>{record['revenue']}</right>",
        f"<center>{record['status']}</center>",
    ])
    style_lines.append(f"<bg={base_bg}></bg>, <bg={base_bg}></bg>, <bg={base_bg}></bg>")

style_lines[-1] += "</u></center>"

preamble = tentags.serialize.preamble(len(data_rows), 3, border_color="#64748b", border_style="solid-1", cell_height=28)
style = "style(\n" + ";\n".join(style_lines) + "\n)"
data = tentags.serialize.data(data_rows, expected_rows=len(data_rows), expected_cols=3)
model = tentags.compile(preamble, style, data)
```

#### `tentags.serialize.preamble(rows, cols, border_width=1, border_color="#cbd5e1", border_style="solid", stretch=0, cell_height=30) -> str`
Serializes Python preamble values into a TenTags preamble string such as:

```python
'9,5,1,"#64748b","solid-1",0,28'
```

#### `tentags.serialize.style(rows, expected_rows=None, expected_cols=None) -> str`
Serializes a Python matrix into `style(...)`. Cell values are raw TenTags style expressions; `None` becomes an empty cell.

#### `tentags.serialize.data(rows, expected_rows=None, expected_cols=None) -> str`
Serializes a Python matrix into `data(...)`. Cell values are raw TenTags data expressions; `None` becomes an empty cell.

### `tentags.render_html(model: TableModel) -> str`
Renders a previously parsed `TableModel` instance into an HTML string.

### `tentags.render_xlsx(model: TableModel, filepath_or_stream) -> None`
Exports a `TableModel` directly to an Excel `.xlsx` file using `openpyxl`. Applies `openpyxl.styles.Font` (bold, italic, color), `openpyxl.styles.PatternFill` (background color), and `openpyxl.styles.Border` according to the table formula. Requires `pip install tentags[excel]`.

### `tentags.render_pdf(model: TableModel, filepath_or_stream) -> None`
Exports a `TableModel` directly to a vector **PDF** file using `ReportLab`. Translates IR coordinates, merged cell regions (`SPAN`), background fills (`BACKGROUND`), fonts, alignments, and border grids into native `ReportLab` `TableStyle` commands. Automatically selects portrait or landscape page orientation based on column count. Requires `pip install tentags[pdf]`.

---

## 🗂️ Multi-Table Rendering

TenTags supports assembling several independent logical Lists/Tables into one HTML, XLSX, or PDF export. A multitable report is not one large grid: each List has its own preamble, style, data, title, and optional XLSX sheet name.

Each table item uses PyCells-compatible logical naming:

```python
table_definition = {
    "document": "Dashboard",       # logical Table / document name
    "table_name": "Menu",          # logical List name
    "sheet_name": "Menu",          # physical XLSX worksheet name
    "title": "Dashboard Menu",
    "preamble": '2,2,1,"#0f172a","solid",0,24',
    "style": "style(<bg=#dbeafe><b></b></bg>, <bg=#dbeafe><b></b></bg>; <bg=#eff6ff></bg>, <bg=#eff6ff></bg>)",
    "data": "data(<mark=Top>Section, Link; Invoice, <url=goto:Invoice!Items!A1>Open invoice</url>)",
}
```

Use named export settings for file output, table order, column validation, and renderer layout. These settings are part of the library API and are passed with `settings=...`.

The Serializer API also works inside multitable items. Each table dictionary can receive `preamble` and `data` generated by `tentags.serialize.preamble()` and `tentags.serialize.data()`. `style` can remain raw TenTags DSL when you want tags to span across a larger table region.

```python
import tentags

menu_rows = [
    ["<mark=Top>Section", "Link"],
    ["Invoice", "<url=goto:Invoice!Items!A1>Open invoice</url>"],
]

tables = [
    {
        "document": "Dashboard",
        "table_name": "Menu",
        "sheet_name": "Menu",
        "title": "Dashboard Menu",
        "preamble": tentags.serialize.preamble(len(menu_rows), 2, border_color="#0f172a", border_style="solid", cell_height=24),
        "style": "style(<center><u><bg=#dbeafe><b></b></bg>, <bg=#dbeafe><b></b></bg>; <bg=#eff6ff></bg>, <bg=#eff6ff></bg></u></center>)",
        "data": tentags.serialize.data(menu_rows, expected_rows=len(menu_rows), expected_cols=2),
    },
    {
        "document": "Invoice",
        "table_name": "Items",
        "sheet_name": "Items",
        "title": "Invoice Items",
        "preamble": '2,2,1,"#7c2d12","solid",0,24',
        "style": "style(<bg=#ffedd5><b></b></bg>, <bg=#ffedd5><b></b></bg>; <bg=#fff7ed></bg>, <bg=#fff7ed></bg>)",
        "data": "data(Item, Total; Paper, <url=goto:Dashboard!Menu!Top>$25</url>)",
    },
]

TABLE_ORDER = ["Dashboard!Menu", "Invoice!Items"]
COLUMNS = {
    "Dashboard!Menu": ["Section", "Link"],
    "Invoice!Items": ["Item", "Total"],
}

HTML_SETTINGS = {
    "output": "demo_output/combined_report.html",
    "table_order": TABLE_ORDER,
    "columns": COLUMNS,
    "tables_per_row": 2,
    "html_title": "Combined Report",
    "layout": "grid",
    "cols": 2,
    "gap": "30px",
    "full_page": True,
}

XLSX_SHEETS_SETTINGS = {
    "output": "demo_output/combined_sheets.xlsx",
    "table_order": TABLE_ORDER,
    "columns": COLUMNS,
    "tables_per_sheet": 1,
    "mode": "sheets",
}

XLSX_STACKED_SETTINGS = {
    "output": "demo_output/combined_stacked.xlsx",
    "table_order": TABLE_ORDER,
    "columns": COLUMNS,
    "tables_per_sheet": "all",
    "stacked_sheet_name": "Report",
    "mode": "stacked",
    "gap": 3,
    "show_titles": True,
}

PDF_SETTINGS = {
    "output": "demo_output/combined_report.pdf",
    "table_order": TABLE_ORDER,
    "columns": COLUMNS,
    "tables_per_row": "auto",
    "tables_per_page": "auto",
    "gap": 16,
    "page_size": "A4",
    "orientation": "landscape",
    "page_break_after_each": False,
    "margins": (24, 24, 36, 36),
}

html = tentags.multitable_html(tables, settings=HTML_SETTINGS)
tentags.multitable_xlsx(tables, settings=XLSX_SHEETS_SETTINGS)
tentags.multitable_xlsx(tables, settings=XLSX_STACKED_SETTINGS)
tentags.multitable_pdf(tables, settings=PDF_SETTINGS)
```

### Multi-Table API Reference

### `tentags.multitable_html(tables, ..., settings: dict = None) -> str`
Assembles and renders multiple tables into a single HTML container or full HTML document.
- **`settings["output"]`**: Optional HTML output path or writable stream.
- **`settings["table_order"]`**: Optional list of logical keys such as `Dashboard!Menu`.
- **`settings["columns"]`**: Optional column header validation by `Table!List`.
- **`settings["tables_per_row"]`** / **`cols`**: Number of HTML grid columns.
- **`settings["html_title"]`**: `<title>` used when `full_page=True`.
- **`layout`**, **`gap`**, **`full_page`**: HTML renderer layout options.

### `tentags.multitable_xlsx(tables, filepath_or_stream=None, ..., settings: dict = None) -> None`
Assembles and renders multiple tables into a single Excel `.xlsx` workbook.
- **`settings["output"]`**: Optional XLSX output path or stream.
- **`settings["mode"]`**: `'sheets'` for one worksheet per List or `'stacked'` for one worksheet.
- **`settings["tables_per_sheet"]`**: `1` for sheets mode or `'all'` for stacked mode.
- **`settings["stacked_sheet_name"]`**: Worksheet name used by stacked mode.
- **`settings["gap"]`** and **`settings["show_titles"]`**: Stacked worksheet layout options.

### `tentags.multitable_pdf(tables, filepath_or_stream=None, ..., settings: dict = None) -> None`
Assembles and renders multiple tables into a single PDF document.
- **`settings["output"]`**: Optional PDF output path or stream.
- **`settings["tables_per_row"]`**: Positive integer or `'auto'`. Auto computes how many tables fit across the page width.
- **`settings["tables_per_page"]`**: Positive integer or `'auto'`. Auto computes how many table blocks fit in page height.
- **`settings["gap"]`**: Spacing between table blocks in points.
- **`settings["page_size"]`**: `'letter'` or `'A4'`.
- **`settings["orientation"]`**: `'portrait'` or `'landscape'`.
- **`settings["margins"]`**: `(left, right, top, bottom)` in points.
- **`settings["page_break_after_each"]`**: Legacy/simple-flow page break flag when no multi-column layout is used.

---

## 🔌 Web Framework & Template Integrations (v2.0.0+)

TenTags comes with built-in integrations for popular Python web engines under `tentags.contrib` and Django package structures.

### 1. Django Template Tags

If `tentags` is added to `INSTALLED_APPS` in your Django `settings.py`, Django will automatically discover the template tags.

In your HTML template:
```html
{% load tentags %}

<!-- Block tag (supports short {% tt %} alias as well) -->
{% tt %}
2, 1, 1, "black", "solid-1", 0, 50,
data(
    Item, Quantity;
    {{ product.name }}, {{ product.qty }}
)
{% endtt %}

<!-- Inline tag -->
{% tentags_inline formula_string %}
```

### 2. Jinja2 / Flask / FastAPI

TenTags provides a Jinja2 Extension (`TenTagsExtension`) and global helper function (`tentags`).

#### FastAPI Integration

FastAPI integration is optional. Install FastAPI and Jinja2 in the application that uses TenTags;
use an ASGI server such as Uvicorn to run that application:

```bash
pip install fastapi jinja2 "uvicorn[standard]"
```

```python
from fastapi.templating import Jinja2Templates
from tentags.contrib.fastapi import register_templates

templates = Jinja2Templates(directory="templates")
register_templates(templates)
```

#### Flask Integration
```python
from flask import Flask
from tentags.contrib.flask import init_app

app = Flask(__name__)
init_app(app)
```

#### In your Jinja2 Templates:
```html
<!-- Block Tag -->
{% tt %}
2, 2, 3, "blue", "solid", 0, 50,
data(
    A, B;
    C, D
)
{% endtt %}

<!-- Inline Function -->
{{ tentags('2, 2, 3, "blue", "solid", 0, 50, data(A, B; C, D)') }}
```

---

## 🧪 Running Tests

To run the standalone test suite:

```bash
python -m pytest
```

Generated output files are written under `demo_output/`, including:
- `demo_output/test_output.html` — Summary HTML report of all rendered tables
- `demo_output/test_output.xlsx` — Basic Excel table
- `demo_output/test_style_output.xlsx` — Excel with styling tags
- `demo_output/Q3_Financial_Dashboard.xlsx` / `.pdf` — Financial dashboard in Excel and PDF
- `demo_output/Enterprise_Budget_Matrix.xlsx` / `.pdf` — Enterprise budget matrix in Excel and PDF

> PDF files require `pip install tentags[pdf]` (ReportLab).

---

## 📄 License

Licensed under the [Apache License 2.0](LICENSE). Copyright (c) 2026 Zhandos Mambetali.
