SkillAgentSearch skills...

Lotemplate

LOTemplate is document generator used to create documents programatically (ODT, DOCX, PDF) from a template (DOCX or ODT) and a json file using LibreOffice in headless mode

Install / Use

/learn @Probesys/Lotemplate

README

LOTemplate (for Libre Office Template)

Warning : This readme is for the version 2.x of LoTemplate. There are breaking changes between versions 1.x and 2.x. See UPGRADE.md documentation. You can also see the CHANGELOG.md for the versions.

Unittest

<a name="principles"></a>Principles

LOTemplate is document generator used to create documents programatically (ODT, DOCX,ODS, XLSX, PDF) from an office template and a json file.

---
title: Word / Writer document
---

flowchart LR
    template["Word Template<br/>(DOCX or ODT)"]
    json["Data<br/>(JSON)"]
    lotemplate["LO Template<br/>(accessible by API or CLI)"]
    generatedFile["Generated File<br/>(PDF, DOCX, ODT, RTF,...)"]
    
    template --> lotemplate
    json --> lotemplate
    lotemplate --> generatedFile
---
title: Excel / Calc document
---

 flowchart LR
    calc_template["Excel Template<br/>(ODS or XLSX)"]
    calc_json["Data<br/>(JSON)"]
    calc_lotemplate["LO Template<br/>(accessible by API or CLI)"]
    calc_generatedFile["Generated File<br/>(ODS, XLSX, PDF, csv,...)"]
    
    calc_template --> calc_lotemplate
    calc_json --> calc_lotemplate
    calc_lotemplate --> calc_generatedFile

What makes this tool different from others are the following features :

  • The templates are in office format (ods,odt, docx, xlsx, ... ) format
  • Word Template can have complex structures (variables, loop, conditions, counters, html,...)
  • The tool can scan the template to extract the variables sheet
  • The tool can be called by an API, a CLI or a python module.
  • The tool uses a real LibreOffice headless to fill the templates. Then the output formats are all the LibreOffice supported formats (docx, xlsx, pdf, odt, ods, text, rtf, html, ...)

The tool is written in Python and use a real LibreOffice headless to fill the templates.

Table of content

<a name="quick_start"></a>Quick start

Run the project with docker compose

Use the docker-compose.yml at the root of the project. Configure the .env file

run the service

docker-compose up -d

Use the API

# creation of a directory
curl -X PUT -H 'secretkey: lopassword' -H 'directory: test_dir1' http://localhost:8000/
# {"directory":"test_dir1","message":"Successfully created"}

Let's imagine we have an file basic_test.odt (created by libreoffice) like this :

Test document

let’s see if the tag $my_tag is replaced and this $other_tag is detected.
[if $my_tag == foo]My tag is foo[endif]
[if $my_tag != foo]My tag is not foo[endif]

Upload this file to lotemplate

# upload a template
curl -X PUT -H 'secretkey: lopassword' -F file=@/tmp/basic_test.odt http://localhost:8000/test_dir1
# {"file":"basic_test.odt","message":"Successfully uploaded","variables":{"my_tag":{"type":"text","value":""},"other_tag":{"type":"text","value":""}}}

# generate a file titi.odt from a template and a json content
curl -X POST \
    -H 'secretkey: lopassword' \
    -H 'Content-Type: application/json' \
    -d '{"name":"my_file.odt","variables":{"my_tag":{"type":"text","value":"foo"},"other_tag":{"type":"text","value":"bar"}}}' \
    --output titi.odt http://localhost:8000/test_dir1/basic_test.odt 

After the operation, you get the file titi.odt with this content :

Test document

let’s see if the tag foo is replaced and this bar is detected.

My tag is foo

<a name="api-and-cli-usage"></a>API and CLI Usage

With the API

Examples of curl requests

# creation of a directory
curl -X PUT -H 'secretkey: my_secret_key' -H 'directory: test_dir1' http://lotemplate:8000/
# {"directory":"test_dir1","message":"Successfully created"}
curl -X PUT -H 'secretkey: my_secret_key' -H 'directory: test_dir2' http://lotemplate:8000/
# {"directory":"test_dir2","message":"Successfully created"}

# look at the created directories
curl -X GET -H 'secretkey: my_secret_key' http://lotemplate:8000/
# ["test_dir2","test_dir1"]

# delete a directory (and it's content
curl -X DELETE -H 'secretkey: my_secret_key' http://lotemplate:8000/test_dir2
# {"directory":"test_dir2","message":"The directory and all his content has been deleted"}

# look at the directories
curl -X GET -H 'secretkey: my_secret_key' http://lotemplate:8000/
# ["test_dir1"]

Let's imagine we have an odt file (created by libreoffice) like this :

Test document

let’s see if the tag $my_tag is replaced and this $other_tag is detected.

Upload this file to lotemplate

# upload a template
curl -X PUT -H 'secretkey: my_secret_key' -F file=@/tmp/basic_test.odt http://lotemplate:8000/test_dir1
{"file":"basic_test.odt","message":"Successfully uploaded","variables":{"my_tag":{"type":"text","value":""},"other_tag":{"type":"text","value":""}}}

# analyse an existing file and get variables
curl -X GET -H 'secretkey: my_secret_key'  http://lotemplate:8000/test_dir1/basic_test.odt
# {"file":"basic_test.odt","message":"Successfully scanned","variables":{"my_tag":{"type":"text","value":""},"other_tag":{"type":"text","value":""}}}

# generate a file titi.odt from a template and a json content
 curl -X POST -H 'secretkey: my_secret_key' -H 'Content-Type: application/json' -d '{"name":"my_file.odt","variables":{"my_tag":{"type":"text","value":"foo"},"other_tag":{"type":"text","value":"bar"}}}' --output titi.odt http://lotemplate:8000/test_dir1/basic_test.odt 

After the operation, you get the file titi.odt with this content :

Test document

let’s see if the tag foo is replaced and this bar is detected.

API reference

Then use the following routes :

all routes take a secret key in the header, key secretkey, that correspond to the secret key configured in the .env file. If no secret key is configured, the secret key isn't required at request.

  • /

    • PUT : take a directory name in the headers, key 'directory'. Creates a directory with the specified name
    • GET : returns the list of existing directories
  • /<directory> : directory correspond to an existing directory

    • GET : returns a list of existing templates within the directory, with their scanned variables
    • PUT : take a file in the body, key 'file'. Uploads the given file in the directory, and returns the saved file name and its scanned variables
    • DELETE : deletes the specified directory, and all its contents
    • PATCH : take a name in the headers, key 'name'. Rename the directory with the specified name.
  • /<directory>/<file> : directory correspond to an existing directory, and file to an existing file within the directory

    • GET : returns the file and the scanned variables of the file
    • DELETE : deletes the specified file
    • PATCH : take a file in the body, key 'file'. replace the existing file with the given file. returns the file and the scanned variables of the file
    • POST : take a json in the raw body. fills the template with the values given in the json. returns the filled document(s).
  • /<directory>/<file>/download : directory correspond to an existing directory, and file to an existing file within the directory

    • GET : returns the original template file, as it was sent

you may wish to deploy the API on your server. Here's how to do it - but don't forget that you should have soffice installed on the server

You can also change the flask options - like port and ip - in the .flaskenv file. If you're deploying the app with Docker, port and ip are editable in the Dockerfile. You can also specify the host and port used to run and connect to soffice as command line arguments, or in a config file (config.yml/config.ini/config or specified via --config).

Execute and use the CLI

Run the script with the following arguments :

usage: lotemplate_cli.py [-h] [--json_file JSON_FILE [JSON_FILE ...]]
                         [--json JSON [JSON ...]] [--output OUTPUT]
                         [--config CONFIG] [--host HOST] [--port PORT]
                         [--scan] [--force_replacement] template_file

positional arguments:
  template_file         Template file to scan or fill

optional arguments:
  -h, --help            show this help message and exit
  --json_file JSON_FILE , -jf JSON_FILE
                        Json files that must fill the template, if any
  --json JSON , -j JSON
                        Json strings that must fill the template, if any
  --json_watermark_file JSON_FILE, -jwf JSON_FILE
                        Json files to configure pdf watermark, if any
  --json_watermark JSON , -jw JSON
                        Json strings to configure pdf watermark, if any
  --output OUTPUT, -o OUTPUT
                        Names of the filled files, if the template should
                        be filled. supported formats: pdf, html, docx, png, odt
  --config CONFIG, -c CONFIG
                        Configuration file path
  --host HOST           Host address to use for the libreoffice connection
  --port PORT           Port to use for the libreoffice connexion
  --scan, -s

Related Skills

View on GitHub
GitHub Stars47
CategoryDevelopment
Updated28d ago
Forks3

Languages

Rich Text Format

Security Score

95/100

Audited on Mar 4, 2026

No findings