Automatically fill Word templates using YAML configs, expressions, and SQL.
Generate invoices, reports, statements, and any other documents with a single command.
Features β’ Installation β’ Quick Start β’ Configuration β’ Functions β’ GUI
DocumentPlaceholder turns .docx templates into ready-to-use documents based on YAML configs with a powerful expression language.
- π Word templates β placeholders like
{KEY}in text, tables, and headers/footers. - β‘ Expression language β arithmetic, comparisons, nested function calls, and template strings.
- π’ SQLite out of the box β run database queries directly inside configs for counters, lookups, and client data.
- π 59 built-in functions β date/time, strings, math, logic, and conditions.
- π€ PDF export β automatic conversion through LibreOffice.
- π₯ GUI with syntax highlighting β config editor, live preview, SQL manager.
- π Extensible β add your own functions with a single decorator.
pip install document-placeholderOptional extras:
| Extra | Includes |
|---|---|
document-placeholder[gui] |
GUI interface (CustomTkinter) |
document-placeholder[dev] |
Development tools (pytest) |
document-placeholder[all] |
Everything |
Insert placeholders into the document:
Invoice #{INVOICE_NUM}
Date: {DAY_NUM}.{MONTH_STR}.{YEAR_NUM}
Amount: ${PRICE}
{DESCRIPTION}
ON_START:
- SQL('CREATE TABLE IF NOT EXISTS doc (num INTEGER DEFAULT 0)')
- SQL('INSERT OR IGNORE INTO doc (rowid, num) VALUES (1, 0)')
INVOICE_NUM:
SQL('SELECT num FROM doc WHERE rowid = 1') + 1
MONTH_STR:
CURRENT_DATE_STR(month)
DAY_NUM:
CURRENT_DATE_NUM(day)
YEAR_NUM:
CURRENT_DATE_NUM(year)
PRICE:
500
DESCRIPTION:
"Software Development Services
(Period: {CURRENT_DATE_NUM(day, month, year) - DAYS(7)} β {CURRENT_DATE_NUM(day, month, year)})"
OUTPUT_NAME:
"Invoice-{INVOICE_NUM}"
OUTPUT_FORMAT:
- docx
- pdf
ON_END:
SQL('UPDATE doc SET num = num + 1 WHERE rowid = 1')docplaceholder -c template.yaml -t template.docx INVOICE_NUM = 2026-2-5
MONTH_STR = February
DAY_NUM = 16
YEAR_NUM = 2026
PRICE = 500
DESCRIPTION = Software Development Services (Period: 09.02.2026 β 16.02.2026)
Output: Invoice-2026-2-5 [docx, pdf]
-> Invoice-2026-2-5.docx
-> Invoice-2026-2-5.pdf
A config is not just key-value mapping. Every value is an expression that gets evaluated.
TAX: ROUND(PRICE * 0.2, 2)
TOTAL: PRICE + TAX
IS_PREMIUM: TOTAL > 1000Inside "...", expressions like {expr} are interpolated into the final string:
PERIOD: "{CURRENT_DATE_NUM(day, month, year) - DAYS(30)} β {CURRENT_DATE_NUM(day, month, year)}"INVOICE_NUM:
"{CURRENT_DATE_NUM(year)}-{SQL('SELECT num FROM doc WHERE rowid = 1') + 1}"STATUS: IF(TOTAL > 1000, 'Premium', 'Standard')
DISCOUNT: IF(TOTAL >= 500, TOTAL * 0.1, 0)
LABEL: SWITCH(STATUS, 'Premium', 'β Premium', 'Standard', 'π Standard')Supported operators: + - * / % > < >= <= == != ()
docplaceholder [-c CONFIG] [-t TEMPLATE] [-o OUTPUT] [--db DATABASE]
| Argument | Default | Description |
|---|---|---|
-c, --config |
template.yaml |
Path to YAML config |
-t, --template |
template.docx |
Path to Word template |
-o, --output |
output.docx |
Path to output file |
--db |
data.db |
Path to SQLite database |
-V, --version |
Print program version |
| Key | Description |
|---|---|
ON_START |
Expressions executed before processing (table creation, initialization) |
ON_END |
Expressions executed after processing (increment counters, cleanup) |
OUTPUT_NAME |
Output filename template: "Invoice-{INVOICE_NUM}" |
OUTPUT_FORMAT |
List of output formats: [docx, pdf] |
All other keys are treated as placeholders and replaced in the document.
59 functions in 5 categories. Full reference: FUNCTIONS.md
TODAY: TODAY() # 16.02.2026
YEAR: CURRENT_DATE_NUM(year) # 2026
MONTH: CURRENT_DATE_STR(month) # February
CUSTOM: DATE_FORMAT(DATE(2026, 3, 8), '%d %B %Y') # 08 March 2026
WEEK_AGO: "{TODAY() - DAYS(7)}" # 09.02.2026
DIFF: DAYS_BETWEEN(DATE(2026, 1, 1), TODAY()) # 46UPPER('hello') # HELLO
TITLE('john doe') # John Doe
PAD_LEFT('42', 6, '0') # 000042
JOIN(', ', 'a', 'b', 'c') # a, b, c
REPLACE('foo bar', 'bar', 'baz') # foo baz
SPLIT('user@mail.com', '@', 1) # mail.comROUND(19.956, 2) # 19.96
FORMAT_NUM(1234567, 2) # 1,234,567.00
MIN(3, 1, 4, 1, 5) # 1
AVG(10, 20, 30) # 20.0
SQRT(144) # 12.0IF(PRICE > 1000, 'expensive', 'cheap')
COALESCE(SQL('SELECT name FROM clients'), 'Unknown')
DEFAULT(value, 'N/A')
SWITCH(status, 'draft', 'Draft', 'sent', 'Sent', 'Unknown')SQL('SELECT count(*) FROM orders WHERE user_id = 1')
SQL('INSERT INTO log (event) VALUES ("generated")')pip install document-placeholder[gui]
docplaceholder-guiThe GUI includes:
- Config editor with YAML and custom syntax highlighting (
SQL(...),{expressions}) - Live preview of evaluated values
- SQL manager for running queries and viewing tables/schema
- Keyboard shortcuts β
Ctrl+Ssave,Ctrl+Fsearch,F5refresh
Add a custom function with a single decorator:
from document_placeholder.functions import FunctionRegistry
@FunctionRegistry.register("MY_FUNC")
def my_func(arg1, arg2):
"""Your custom logic."""
return f"{arg1}-{arg2}"After importing the module, the function becomes available in config expressions:
VALUE: MY_FUNC('hello', 'world') # hello-worldfrom document_placeholder.config import Config
from document_placeholder.evaluator import Evaluator
from document_placeholder.processor import DocumentProcessor
config = Config.from_string("""
NAME: UPPER('john doe')
DATE: TODAY()
""")
evaluator = Evaluator()
values = {k: evaluator.evaluate_value(v) for k, v in config.placeholders.items()}
# {'NAME': 'JOHN DOE', 'DATE': DateValue(2026-02-16)}
processor = DocumentProcessor("template.docx")
processor.replace_placeholders(values)
processor.save("output.docx")pip install document-placeholder[dev]
pytest295 passed in 0.36s
- Fork the repository
- Create a feature branch
- Commit your changes
- Open a Pull Request
Bugs and feature requests β Issues
This project is released under the MIT license. See LICENSE for details.
Developed with β€οΈ by FlacSy