From 1ae74be6bad41cf91034005993f3eb8054e4e989 Mon Sep 17 00:00:00 2001 From: Vaceslav Ustinov Date: Sat, 26 Sep 2026 16:51:36 +0200 Subject: [PATCH] docs: open ODT output files with File.Create, explain why File.OpenWrite can corrupt (#138) Found by the independent review of the OpenDocument support. --- .../Documentation/OpenDocumentSamplesTests.cs | 6 +++--- docs/for-developers/opendocument.md | 15 ++++++++++----- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/TriasDev.Templify.Tests/Documentation/OpenDocumentSamplesTests.cs b/TriasDev.Templify.Tests/Documentation/OpenDocumentSamplesTests.cs index 0fa88af..c2d59d1 100644 --- a/TriasDev.Templify.Tests/Documentation/OpenDocumentSamplesTests.cs +++ b/TriasDev.Templify.Tests/Documentation/OpenDocumentSamplesTests.cs @@ -76,9 +76,9 @@ public void OdtTemplateProcessor_OttProducesOdt() Assert.Equal("application/vnd.oasis.opendocument.text", verifier.ManifestRootMediaType); } - // opendocument.md "Streams and Byte Arrays": bytes, and a write-only output stream (File.OpenWrite) + // opendocument.md "Streams and Byte Arrays": bytes, and an output stream opened with File.Create [Fact] - public void OdtTemplateProcessor_BytesAndWriteOnlyOutputStream() + public void OdtTemplateProcessor_BytesAndOutputStream() { OdtTemplateProcessor processor = new OdtTemplateProcessor(Options()); string templatePath = Write("template.ott", SampleTemplate().AsTemplate().ToBytes()); @@ -88,7 +88,7 @@ public void OdtTemplateProcessor_BytesAndWriteOnlyOutputStream() ProcessingResult streamResult; using (FileStream templateStream = File.OpenRead(templatePath)) - using (FileStream outputStream = File.OpenWrite(PathOf("output.odt"))) + using (FileStream outputStream = File.Create(PathOf("output.odt"))) { streamResult = processor.ProcessTemplate(templateStream, outputStream, SampleData()); } diff --git a/docs/for-developers/opendocument.md b/docs/for-developers/opendocument.md index 7323f55..b9b9be4 100644 --- a/docs/for-developers/opendocument.md +++ b/docs/for-developers/opendocument.md @@ -55,15 +55,19 @@ ProcessingResult bytesResult = processor.ProcessTemplate(template, data, out byt // Streams: the output stream only has to be writable. using var templateStream = File.OpenRead("template.ott"); -using var outputStream = File.OpenWrite("output.odt"); +using var outputStream = File.Create("output.odt"); // creates or truncates the file ProcessingResult streamResult = processor.ProcessTemplate(templateStream, outputStream, data); ``` The requirements are looser than for Word documents: - The template stream must be readable. It does not have to be seekable. -- The output stream only has to be **writable** (`File.OpenWrite` works). The document is built in memory and written - in one go, and **only when processing succeeds**. On failure nothing is written. +- The output stream only has to be **writable**; it does not have to be readable or seekable. The document is built + in memory and written in one go, and **only when processing succeeds**. On failure nothing is written. +- To write a file, open it with **`File.Create`**, which creates the file or truncates an existing one. + `File.OpenWrite` does not truncate: it writes over an existing file from the start and keeps any bytes after the + new content, which leaves a corrupt ZIP file when the old file was longer. Templify cuts off seekable outputs after + the document (see [Memory Use](#memory-use)), but `File.Create` does not depend on that. A template that is not an OpenDocument Text package is reported as a failed result, with an `ErrorMessage` that names what was found. This covers a Word file, a spreadsheet, a flat `.fodt` file or a password-protected document. @@ -81,8 +85,9 @@ these limits fails with an `ErrorMessage` such as `content.xml exceeds the maxim are far below these limits. Treat templates from untrusted sources with care anyway: an entry that unpacks to a very large size is not held in memory, but it still costs time to copy. -When the output stream is seekable (a file or a `MemoryStream`), it is cut off after the written document, so an -existing, longer file opened with `File.OpenWrite` does not keep bytes of its earlier content. +When the output stream is seekable (a file or a `MemoryStream`), it is cut off after the written document, so even +an existing, longer file opened with `File.OpenWrite` does not keep bytes of its earlier content. Prefer +`File.Create` anyway. ### Options