Skip to content

v1.9.0

Latest

Choose a tag to compare

@github-actions github-actions released this 27 Sep 19:07
ca54ffc

1.9.0 (2026-09-27)

✨ Highlight: OpenDocument / LibreOffice templates

Templify now fills OpenDocument Text templates (.odt, and .ott templates, which produce .odt) created with LibreOffice Writer, Collabora or OpenOffice. It uses the same syntax and features as for Word documents:

  • placeholders and format specifiers;
  • conditionals (block, inline, elseif, table rows, list items);
  • loops (paragraphs, table rows, list items, nested, @index/@number);
  • headers and footers (including header/footer regions), footnotes and endnotes, text boxes and sections;
  • markdown formatting (EnableMarkdown, :raw), ValidateTemplate and document properties.

This matters for public-sector and other organisations standardising on ODF.

using TriasDev.Templify.Core;

// Explicit OpenDocument processor ...
var odt = new OdtTemplateProcessor();
ProcessingResult result = odt.ProcessTemplateFile("letter.odt", "letter-out.odt", data);

// ... or the new format-detecting facade for both .docx and .odt/.ott
var processor = new TemplateProcessor();
result = processor.ProcessTemplateFile(templatePath, outputPath, data);
TemplateFormat format = TemplateProcessor.DetectFormat(stream); // Docx, Odt or Unknown
  • No new dependencies: ODF packages are read and written with System.IO.Compression and System.Xml.
  • Memory: only the XML parts are inflated; images and other entries are streamed. Each XML part is capped at 256 MB.
  • Safety: DTDs are prohibited, and duplicate or unsafe ZIP entry names are rejected.
  • Verified with LibreOffice: outputs open and render correctly, including numbered lists that continue across loop iterations.
  • Word path unchanged: DocumentTemplateProcessor behaves as in 1.8.0, and results for the same template are identical in both formats.
  • Known differences from Word (whitespace collapsing, bookmarks, merged cells across rows, UpdateFieldsOnOpen, comments, .fodt) are documented at LibreOffice / OpenDocument.
  • Developer guide: OpenDocument (.odt).
  • The GUI accepts .odt/.ott, and the Demo and examples include a LibreOffice letter.

New public API (additive)

  • OdtTemplateProcessor: same methods as DocumentTemplateProcessor.
  • TemplateProcessor: a facade that detects the format and passes the call to the matching processor.
  • TemplateFormat: enum with Unknown, Docx and Odt.
  • ValidationErrorType.InvalidDocument: for unsupported, corrupted or rejected input. Used by the new ODT and facade paths.
  • ValidationWarningType.MissingConditionVariable: a warning when {{#if Missing}} uses a variable missing from the data as a bare operand. IsValid is not affected.

⚠️ Behavior changes (bug fixes)

The public C# API remains source- and binary-compatible with 1.8.0 (verified by package validation). These fixes can change results for existing Word templates.

ValidateTemplate for Word templates:

  • Inline expressions {{(…)}} are validated properly (#233).
    • An expression was reported as one missing variable (Variable '(A and B)' …) even when all operands were present, so correct templates showed IsValid = false. Now each operand is checked with the same rules processing uses.
    • AllPlaceholders lists the expression's variables instead of the expression text.
    • Expressions that cannot be parsed are reported as InvalidConditionalExpression.
  • Collections of nested loops appear in AllPlaceholders even when validating without data (#226).
  • An unmatched {{#if}} marker in a table row is now reported as invalid (#227). Processing already failed on it.

Output streams (#221, #232):

  • An output stream with existing content is now truncated before writing.
  • Before, a stream opened without truncation over a longer file could leave trailing bytes (ODT) or fail after partly overwriting it (Word).
  • If the stream cannot be truncated, processing returns a Failure and nothing is written.
  • Prefer File.Create for output files.

Removed dead marker handling: the unsupported {{#empty}} / {{/empty}} text is no longer treated specially, so a paragraph containing it gets normal placeholder replacement.

Features

  • format-detecting TemplateProcessor facade for .docx and .odt/.ott (#138) (#215) (1157864)
  • gui: support OpenDocument templates (#138) (#219) (5dcb0bf)
  • odt: conditionals for OpenDocument Text templates (#138) (#211) (eaf4673)
  • odt: loops, table-row and list-item loops for OpenDocument Text templates (#138) (#212) (08d2588)
  • odt: markdown via automatic styles, ValidateTemplate and document properties for ODT (#138) (#214) (1b62908)
  • odt: nested containers and unique object names after cloning (#138) (#213) (caa48c7)
  • odt: OdtTemplateProcessor with placeholder replacement for .odt/.ott (#138) (#210) (dda2bd0)
  • validation: add ValidationErrorType.InvalidDocument for unreadable or unsupported templates (#225) (271b038)
  • validation: warn about missing variables used as bare condition operands (#231) (9cd92bb)

Bug Fixes

  • never leave trailing bytes when the output stream cannot be truncated (#232) (dcd7c6e)
  • odt: continue list numbering across loop copies, process header/footer regions, drop dead {{#empty}} markers (#138) (#223) (92a9b19)
  • odt: markdown styles no longer shadow common styles of styles.xml (#229) (f38af46)
  • odt: reject duplicate and unsafe entry names, compare media types case-insensitively, drop the stale thumbnail (#138) (#222) (857bfb5)
  • odt: report an unmatched row or list-item marker once in ValidateTemplate (#228) (8932a55)
  • odt: ValidateTemplate no longer reports the markers of table-row and list-item blocks as unmatched (#138) (#216) (53c13dc)
  • validation: list collections of nested loops in AllPlaceholders without data (#226) (524ab09)
  • validation: report unmatched table row conditionals in Word templates (#227) (9838714)
  • validation: validate inline expressions via the condition AST instead of reporting them as one missing variable (#233) (d3aca9e)

Performance Improvements

  • odt: stream unprocessed package entries instead of inflating them, bound XML part sizes, truncate seekable outputs (#138) (#221) (0d678cb)