Metadata-Version: 2.4
Name: django-typst
Version: 0.1.0
Summary: A Django template engine powered by Typst
Author: Jonathan Moss
Author-email: Jonathan Moss <xirisr@gmail.com>
License-Expression: BSD-3-Clause
License-File: LICENSE
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Web Environment
Classifier: Framework :: Django
Classifier: Framework :: Django :: 5.2
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: BSD License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Internet :: WWW/HTTP
Classifier: Topic :: Printing
Classifier: Topic :: Software Development :: Libraries
Requires-Dist: django>=5.2.0
Requires-Dist: tomlkit>=0.13.3
Requires-Dist: typst==0.13.4
Requires-Python: >=3.11
Project-URL: Documentation, https://github.com/a-musing-moose/django-typst-engine/blob/master/README.md
Project-URL: Homepage, https://github.com/a-musing-moose/django-typst-engine/
Project-URL: Readme, https://github.com/a-musing-moose/django-typst-engine/blob/master/README.md
Project-URL: Repository, https://github.com/a-musing-moose/django-typst-engine/
Description-Content-Type: text/markdown

# Django Typst

A [Django] template engine that uses [Typst] to render Portable Document Format (PDF)
files.

## Configuration

To make the typst engine available, you need to add it to the `TEMPLATES` configuration
in your `settings.py`. e.g.:

```python
TEMPLATES = [
    ...
    {
        "BACKEND": "django_typst.TypstEngine",
        "NAME": "typst",
        "DIRS": [BASE_DIR / "templates"],
        "APP_DIRS": False,
        "OPTIONS": {
            "ROOT": None,
            "FONT_PATHS": [],
            "IGNORE_SYSTEM_FONTS": False,
            "PDF_STANDARD": "1.7",
            "PPI": None,
        }
    },
]
```

Note that this should be in _addition_ to the standard Django template engine that was
already there.

All the `OPTIONS` are... optional and the values above represent their defaults should
no alternative be provided.

| Option              | Description                                    | Default  |
| ------------------- | ---------------------------------------------- | -------- |
| ROOT                | The root path to use for relative paths        | `None`\* |
| FONT_PATHS          | Paths to look in for fonts                     | `[]`     |
| IGNORE_SYSTEM_FONTS | Only consider fonts in the defined font paths  | `False`  |
| PDF_STANDARD        | PDF revision to target (`1.7`, `a2-b`, `a3-b`) | `"1.7"`  |
| PPI                 | Pixel Per Inch for included PNG                | `None`   |

\* _The engine with use the folder the template is in as the root if one is not
specified._

## Usage

To use this engine with one of the standard Django class based views you only need to
set the `template_engine` class property to the value `"typst"`. You should also set the
`content_type` property to the value `"application/pdf"` to ensure the PDF is returned
with the correct content type. For example, in a simple `TemplateView` it would look
like this:

```python
from django.views import generic

class MyTemplateView(generic.TemplateView):
  template_engine = "typst"
  content_type = "application/pdf"

  ...
```

## Handling Context

Context data passed into the template it is first encoded in [TOML] format. Note this
means that only data types that can be serialized as TOML can be passed as context
variables. We use [tomlkit] to serialize the context. In practice this means the
following types can be included in the context:

- `str`
- `int`
- `float`
- `bool`
- `datetime.datetime`, `.time`, and `.date`
- `list`
- `dict`

In addition to these types, you can also include `decimal.Decimal`, `uuid.UUID` objects,
which are rendered to strings before serializing.

There is also special handling for the Django `HTTPRequest` object, which is converted
to a dict before serializing. The contents of that dict are:

```python
{
    "path": request.path,
    "path_info": request.path_info,
    "method": request.method,
    "content_type": request.content_type,
    "content_params": request.content_params,
    "headers": {},  # header-name: header-value
}
```

To then make use of context within a Typst template you must parse the incoming toml
data by adding the following to the top of the typst file:

```typst
#let ctx = toml(bytes(sys.inputs.context))
```

This will deserialize the context and assign it to the typst variable `ctx` which can
then be used in the template like any other variable. For example if you context was
something like:

```python
{
  "name": "J Moss",
  "flight": "QF1",
}
```

Then in your template you will be able to reference it with:

```typst
#ctx.name your flight number is #ctx.flight
```

Or even better is to parse the context as follows:

```typst
// Parse context or use defaults
#let ctx = if ("context" in sys.inputs) {
  toml(bytes(sys.inputs.context))
} else {
  (
    "name": "A Citizen",
    "flight": "DL31"
  )
}
```

This version sets a default if `context` is not passed in. This allows you to test the
template in isolation - say with the [Tinymist] Extension to VSCode or even just running
`typst` directly on it.

<!-- Links -->

[django]: https://www.djangoproject.com/
[tinymist]: https://github.com/Myriad-Dreamin/tinymist
[toml]: https://toml.io/en/
[tomlkit]: https://tomlkit.readthedocs.io/en/latest/
[typst]: https://typst.app/
