# Saving and Converting Documents

> Convert a folder picked with OpenFolderDialog to PDF, save with the Save As dialog, export HTML with CSS, compress PDF images and leave out comments in WPF.

- **Product:** TX Text Control .NET for WPF
- **Section:** Documentation
- **Kind:** Topic
- **Path:** TX Text Control .NET for WPF > Document Processing > Saving and Converting Documents
- **4900 words**
- **Web URL:** https://docs.textcontrol.com/tx-text-control-dotnet-wpf/document-processing/saving-converting/
- **LLMs URL:** https://docs.textcontrol.com/tx-text-control-dotnet-wpf/document-processing/saving-converting/llms.txt
- **LLMs-Full URL:** https://docs.textcontrol.com/tx-text-control-dotnet-wpf/document-processing/saving-converting/llms-full.txt

---

Saving is also how a WPF application converts: a document loaded as DOCX, RTF or HTML becomes a PDF file with one call of `Save`. The choices around that call make the difference, such as saving the whole document or only a part, keeping HTML styles in a separate file, shrinking the images of a PDF or leaving comments out. The `Click` handlers below convert a folder, save parts of a document and control the output with `SaveSettings`.

The basics are in [Save as DOCX and PDF](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/getting-started/document-processing/save-docx-pdf/llms-full.txt): saving the editor's document, converting a single file, byte arrays and PDF/A. [PDF Processing](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/pdf/llms-full.txt) covers PDF passwords, attachments and metadata. *MainWindow.xaml.cs* needs these `using` directives for the examples:

```csharp
using System;
using System.Collections.Generic;
using System.IO;
using System.Windows;
using Microsoft.Win32;
using TXTextControl;
```

### Convert a Folder of Documents

The following handler lets the user pick a folder with the `OpenFolderDialog` of WPF, converts each DOCX, DOC, RTF and HTML file in it to PDF in the subfolder *PDF* and lists the files that failed:

```csharp
// The load format for each file extension
private static readonly Dictionary<string, StreamType> SourceFormats =
    new Dictionary<string, StreamType>(StringComparer.OrdinalIgnoreCase)
    {
        { ".docx", StreamType.WordprocessingML },
        { ".doc", StreamType.MSWord },
        { ".rtf", StreamType.RichTextFormat },
        { ".html", StreamType.HTMLFormat }
    };

private void ConvertFolderButton_Click(object sender, RoutedEventArgs e)
{
    OpenFolderDialog dialog = new OpenFolderDialog();

    if (dialog.ShowDialog(this) != true)
    {
        return;
    }

    string outputFolder = System.IO.Path.Combine(dialog.FolderName, "PDF");
    Directory.CreateDirectory(outputFolder);

    int converted = 0;
    List<string> failed = new List<string>();

    using (ServerTextControl tx = new ServerTextControl())
    {
        tx.Create();

        foreach (string file in Directory.GetFiles(dialog.FolderName))
        {
            if (!SourceFormats.TryGetValue(System.IO.Path.GetExtension(file), out StreamType format))
            {
                continue;
            }

            try
            {
                // Load replaces the previous document
                tx.Load(file, format);
                tx.Save(System.IO.Path.Combine(outputFolder,
                    System.IO.Path.GetFileNameWithoutExtension(file) + ".pdf"), StreamType.AdobePDF);
                converted++;
            }
            catch (FilterException exception)
            {
                failed.Add($"{System.IO.Path.GetFileName(file)}: {exception.Reason}");
            }
        }
    }

    MessageBox.Show($"{converted} documents converted.\r\n" + string.Join("\r\n", failed));
}
```

The code works as follows:

- `OpenFolderDialog` of the `Microsoft.Win32` namespace is available in WPF on .NET 8 and higher, the versions the TX Text Control package targets. `ShowDialog` returns `true` when the user chose a folder.
- A [ServerTextControl](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol/servertextcontrol/llms-full.txt) converts the files, so the document in the window stays as it is. Its [Load](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol/servertextcontrol/load-method/llms-full.txt) replaces the previous file, and one instance handles the whole folder.
- [Save](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol/servertextcontrol/save-method/llms-full.txt) writes the complete document and overwrites a file of the same name.
- A [FilterException](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol/filterexception/llms-full.txt) with its [Reason](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol/filterexception/reason-property/llms-full.txt) reports a file that couldn't be converted, and the loop continues.

The handler runs on the UI thread of the window, which doesn't respond until the last file is saved. [Background Operations](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/performance/background-operations/llms-full.txt) and [Using ServerTextControl in Desktop Applications](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/document-processing/servertextcontrol/llms-full.txt) show how to run conversions in the background. For very large batches, Text Control recommends separate processes, because threads that use TX Text Control can wait for each other.

### Save With the Save As Dialog Box

With a `StreamType` and a [SaveSettings](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol/savesettings/llms-full.txt) object, `Save` opens the **Save As** dialog box; [Open and Save With the Built-In Dialog Boxes](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/document-editor/load-save/#open-and-save-with-the-built-in-dialog-boxes) describes its file filters, its return value and the options it can offer. When the user has saved, the object tells you what was written:

```csharp
private void SaveAsButton_Click(object sender, RoutedEventArgs e)
{
    SaveSettings saveSettings = new SaveSettings();

    // Opens the Save As dialog box with the filters of these formats
    TXTextControl.WPF.DialogResult result = textControl1.Save(
        StreamType.WordprocessingML | StreamType.AdobePDF, saveSettings);

    if (result == TXTextControl.WPF.DialogResult.OK)
    {
        MessageBox.Show($"{saveSettings.SavedFile}: {saveSettings.BytesWritten} bytes, " +
                        $"format {saveSettings.SavedStreamType}");
    }
}
```

[SavedFile](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol/savesettings/savedfile-property/llms-full.txt), [BytesWritten](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol/savesettings/byteswritten-property/llms-full.txt) and [SavedStreamType](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol/savesettings/savedstreamtype-property/llms-full.txt) are the file with its path, its size and its format; `SavedFile` is empty when the document was saved to a byte array or a string.

### Save Part of a Document

[Selection.Save](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol/selection/save-method/llms-full.txt) saves only the selected text to a file, a byte array or a string, and [Paragraph.Save](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol/paragraph/save-method/llms-full.txt) saves a single paragraph to a byte array or a string:

```csharp
private void SavePartButton_Click(object sender, RoutedEventArgs e)
{
    // The paragraph at the input position as an HTML string
    TXTextControl.Paragraph paragraph = textControl1.Paragraphs.GetItem(textControl1.Selection.Start);

    string paragraphHtml;
    paragraph.Save(out paragraphHtml, StringStreamType.HTMLFormat);

    // The selected text as RTF and as a DOCX file
    if (textControl1.Selection.Length > 0)
    {
        string rtf;
        textControl1.Selection.Save(out rtf, StringStreamType.RichTextFormat);

        textControl1.Selection.Save("excerpt.docx", StreamType.WordprocessingML);
    }

    MessageBox.Show(paragraphHtml);
}
```

[ParagraphCollection.GetItem](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol/paragraphcollection/getitem-method/llms-full.txt) returns the paragraph at a zero-based text position. Only complete documents can be saved as PDF and PDF/A.

### Save HTML With Stylesheets and Images

HTML output depends on two settings. [SaveSettings.CssSaveMode](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol/savesettings/csssavemode-property/llms-full.txt) decides where the formatting is written:

| Value | The formatting is written |
|---|---|
| `None` | Into the HTML tags of the body |
| `Inline` | As stylesheets in the head of the HTML document |
| `CreateFile` | Into a CSS file with the name of the HTML file, or the name in [CssFileName](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol/savesettings/cssfilename-property/llms-full.txt), saved beside it; an existing file is kept |
| `OverwriteFile` | Like `CreateFile`, but an existing CSS file is overwritten |

With [SaveSettings.ImageSaveMode](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol/savesettings/imagesavemode-property/llms-full.txt) set to `SaveAsData`, the images are embedded in the HTML. The following handler writes an HTML file with a separate CSS file and an HTML string with the styles in its head:

```csharp
private void SaveHtmlButton_Click(object sender, RoutedEventArgs e)
{
    // An HTML file with a stylesheet file of the same name, newsletter.css
    SaveSettings fileSettings = new SaveSettings()
    {
        CssSaveMode = CssSaveMode.OverwriteFile,
        ImageSaveMode = ImageSaveMode.SaveAsData
    };

    textControl1.Save("newsletter.html", StreamType.HTMLFormat, fileSettings);

    // An HTML string can't have a stylesheet file, so the styles go into the head
    SaveSettings stringSettings = new SaveSettings()
    {
        CssSaveMode = CssSaveMode.Inline,
        ImageSaveMode = ImageSaveMode.SaveAsData
    };

    string html;
    textControl1.Save(out html, StringStreamType.HTMLFormat, stringSettings);
}
```

When HTML is saved to a string or a byte array, `CreateFile` and `OverwriteFile` can't be used.

### Reduce the Size of PDF Files

Most of the size of a PDF file with photos is image data. Two settings reduce it:

```csharp
private void SmallPdfButton_Click(object sender, RoutedEventArgs e)
{
    SaveSettings saveSettings = new SaveSettings()
    {
        ImageCompressionQuality = 70, // 1 to 100
        ImageMaxResolution = 150      // dpi; larger images are reduced
    };

    textControl1.Save("invoice.pdf", StreamType.AdobePDF, saveSettings);
}
```

- [ImageCompressionQuality](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol/savesettings/imagecompressionquality-property/llms-full.txt) sets the quality of lossy compression from 1 to 100 for images compressed with a lossy algorithm; 0 selects a default.
- [ImageMaxResolution](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol/savesettings/imagemaxresolution-property/llms-full.txt) reduces images whose resolution is higher. The default, 0, keeps each image's resolution.

Suitable values for documents read on screen and printed are compared in the blog article [Best Practices for Image Compression when Exporting to PDF in .NET C#](https://www.textcontrol.com/blog/2025/08/22/best-practices-for-image-compression-when-exporting-to-pdf-in-dotnet-csharp/).

### Leave Out Content and Add Properties

[SaveSettings.OmittedContent](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol/savesettings/omittedcontent-property/llms-full.txt) keeps parts of the document out of the saved file, such as the comments of an invoice that goes to a customer. The document in the window keeps them:

```csharp
private void SaveForCustomerButton_Click(object sender, RoutedEventArgs e)
{
    UserDefinedPropertyDictionary properties = new UserDefinedPropertyDictionary();
    properties.Add("InvoiceNumber", "INV-2025-001");
    properties.Add("AmountDue", 299.97);

    SaveSettings saveSettings = new SaveSettings()
    {
        DocumentTitle = "Invoice INV-2025-001",
        UserDefinedDocumentProperties = properties,
        OmittedContent = OmittedContent.Comments
    };

    textControl1.Save("invoice-customer.docx", StreamType.WordprocessingML, saveSettings);
}
```

`OmittedContent` takes these values, and none of them can be used with PDF and PDF/A:

| Value | Leaves out |
|---|---|
| `Comments`, `Footnotes` | Comments and footnotes |
| `TextFields` | All kinds of text fields |
| `TrackedChanges` | The markup of tracked changes; text and formatting are saved |
| `SubTextParts`, `EditableRegions`, `TablesOfContents` | The markers or data of these elements; text and formatting are saved |
| `Sections` | The section data |

[UserDefinedDocumentProperties](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol/savesettings/userdefineddocumentproperties-property/llms-full.txt) takes strings, Boolean values, integers and double values, and `DocumentTitle` replaces the title of `DocumentSettings` for this save.

### Save to Streams

The stream overload of `Save` takes a `FileStream`. For any other stream, save to a byte array first:

```csharp
private void SaveToStreamButton_Click(object sender, RoutedEventArgs e)
{
    // A FileStream is written at its current position
    using (FileStream fileStream = new FileStream("invoice.rtf", FileMode.Create))
    {
        textControl1.Save(fileStream, StreamType.RichTextFormat);
    }

    // Other streams: save to a byte array and wrap it
    byte[] pdf;
    textControl1.Save(out pdf, BinaryStreamType.AdobePDF);

    using (MemoryStream memoryStream = new MemoryStream(pdf))
    {
        MessageBox.Show($"Stream length: {memoryStream.Length}");
    }
}
```

### What Changes When Converting

Each format stores different things, so a conversion can change a document:

- **Plain text** keeps the text only, with a carriage return and a line feed after each paragraph.
- **Word fields** are imported as fields only when `LoadSettings.ApplicationFieldFormat` is set before loading; see [Loading Documents](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/document-processing/loading/#control-the-import-with-loadsettings).
- **Images** keep their original format unless `SaveSettings.ImageExportFilterIndex` selects another one. When the target format can't store it, the image is saved in the default image format of the target, as [Control How Images Are Saved](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/document-editor/images/#control-how-images-are-saved) lists per document format.
- **Fonts** missing on the computer that converts are replaced, which can move line and page breaks. [Font Embedding and Substitution](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/pdf/fonts/#choose-the-replacement-fonts) shows how to detect this.
- **PDF/A** embeds its fonts, so they must be embeddable, as [PDF/A](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/pdf/pdf-a/llms-full.txt) describes.

When a converted document doesn't look right, see [Document Conversion](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/troubleshooting/conversion/llms-full.txt).

### Next Steps

- [PDF Creation and Conversion](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/pdf/creation-conversion/llms-full.txt): PDF options, metadata and PDF/UA.
- [Loading Documents](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/document-processing/loading/llms-full.txt): load documents with all their options.
- [Background Operations](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/performance/background-operations/llms-full.txt): convert documents without blocking the window.
- [Supported File Formats](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/overview/file-formats/llms-full.txt): the formats you can load and save.

---

## Nearby

- **Previous:** [Loading Documents](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/document-processing/loading/llms-full.txt)
- **Next:** [Text, Paragraphs, and Stylesheets](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/document-processing/text-paragraphs/llms-full.txt)
