Go to file
echo a77829aad7 fix sidebar 2024-02-17 23:44:38 +01:00
cuddly add all files 2024-02-17 16:29:06 +01:00
samples fix sidebar 2024-02-17 23:44:38 +01:00
.gitignore Initial commit 2024-02-17 16:19:46 +01:00
LICENSE Initial commit 2024-02-17 16:19:46 +01:00
README.md fix typos 2024-02-17 17:18:38 +01:00
pyproject.toml fix pyproject 2024-02-17 16:37:04 +01:00



Generate HTML and PDF from KDL structed data.


git clone https://git.bksp.space/hannaeko/cuddly
cd cuddly
python -m venv env
env/bin/pip install .



Generating a PDF:

env/bin/cuddly --template samples/cv/cv.template.html --input samples/cv.kdl --output samples/cv.pdf

Generating a HTML file

env/bin/cuddly --template samples/cv/cv.template.html --input samples/cv.kdl --output samples/cv.html

Template usage

The template language used is Jinja, source template need to be a single HTML template file.

Base URL

When generating the PDF the base URL is set to the source template directory. All relative links to other resources (for example CSS and images) must be relative to that directory.

Accessing nodes values and attributes

Nodes are available as global variables in the template. The rendered value of a node is the first value that has been assigned to the node. This value can also be accessed using the value property. The list of values can be accessed using the values property.

my_node "something" "other thing"
{{ my_node }}
{{ my_node.value }}
{{ my_node.values | join(', ')}}

renders to

something, other thing

Accessing children nodes can be done using properties of the children names.

my_node {
    other_node "test"
{{ my_node.other_node.value }}

renders to


If a node has multiple children of a same node type, then directly accessing the children properties will fail, a loop will be needed. Note that, on the other hand, looping on the children always work, even if there is only one child of the given type.

my_node {
    other_node "first"
    other_node "second"
{% for node in my_node.other_node %}
{{ node.value }}
{% endofr %}

renders to


Attributes of a node can be accessed through the attributes property:

my_node version=42 "something"
{{ my_node.attributes.version }}

renders to


Templating nodes

Node specific templates can be defined in the components element in the HTML template file. Nodes can then be rendered using those micro-templates using the render filter.

The context of the micro-template is the node being rendered. As such, children and properties of that node are in the global scope.

menu "Midnight meal" price="10 €" {
    dish name="Tomato soup" "Fancy tomato soup with a slice of bread"
    dish name="Banana bread" "Amazing banana bread with a scoop of icecream"
<!DOCTYPE html>
<html lang="{{ lang }}">
    <meta charset="UTF-8">
        <h1>{{ value }}</h1>
            <dd>{{ attributes.prices }}</dd>

        {% for dish in dish %}
            {{ dish | render }}
        {% endfor %}
            <dt>{{ attribute.name }}</dt>
            <dd>{{ value }}</dd>
    {{ menu | render }}


  • samples/cv/cv.template.html: Template to generate a CV, see samples/cv.kdl for the input data.