Saving and Converting Documents
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: saving the editor's document, converting a single file, byte arrays and PDF/A. PDF Processing covers PDF passwords, attachments and metadata. MainWindow.xaml.cs needs these using directives for the examples:
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:
// 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:
OpenFolderDialogof theMicrosoft.Win32namespace is available in WPF on .NET 8 and higher, the versions the TX Text Control package targets.ShowDialogreturnstruewhen the user chose a folder.- A ServerTextControl converts the files, so the document in the window stays as it is. Its Load replaces the previous file, and one instance handles the whole folder.
- Save writes the complete document and overwrites a file of the same name.
- A FilterException with its Reason 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 and Using ServerTextControl in Desktop Applications 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 object, Save opens the Save As dialog box; 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:
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, BytesWritten and SavedStreamType 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 saves only the selected text to a file, a byte array or a string, and Paragraph.Save saves a single paragraph to a byte array or a string:
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 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 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, saved beside it; an existing file is kept |
OverwriteFile | Like CreateFile, but an existing CSS file is overwritten |
With SaveSettings.ImageSaveMode 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:
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:
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 sets the quality of lossy compression from 1 to 100 for images compressed with a lossy algorithm; 0 selects a default.
- ImageMaxResolution 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#.
Leave Out Content and Add Properties
SaveSettings.OmittedContent 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:
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 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:
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.ApplicationFieldFormatis set before loading; see Loading Documents. - Images keep their original format unless
SaveSettings.ImageExportFilterIndexselects 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 lists per document format. - Fonts missing on the computer that converts are replaced, which can move line and page breaks. Font Embedding and Substitution shows how to detect this.
- PDF/A embeds its fonts, so they must be embeddable, as PDF/A describes.
When a converted document doesn't look right, see Document Conversion.
Next Steps
- PDF Creation and Conversion: PDF options, metadata and PDF/UA.
- Loading Documents: load documents with all their options.
- Background Operations: convert documents without blocking the window.
- Supported File Formats: the formats you can load and save.