Saving and Converting Documents
Converting a Word file to PDF in a Windows Forms application takes two calls: Load in one format and Save in another. What comes out is decided by the details: which part of a document you save, where HTML keeps its styles, how large a PDF file becomes and which content stays behind. Code for three of these tasks follows: converting a whole folder, saving parts of a document and controlling the result with SaveSettings.
Save as DOCX and PDF covers the first steps: saving the editor's document, converting one file, byte arrays and PDF/A. PDF options such as passwords and attachments are in PDF Processing. Form1.cs needs these using directives for the examples:
using System;
using System.Collections.Generic;
using System.IO;
using System.Windows.Forms;
using TXTextControl;
Convert a Folder of Documents
A conversion loads a document and saves it in another format. The following handler asks for a folder, converts every DOCX, DOC, RTF and HTML file in it to PDF in a subfolder PDF and reports the files that couldn't be converted:
// 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, EventArgs e)
{
using (FolderBrowserDialog dialog = new FolderBrowserDialog())
{
if (dialog.ShowDialog() != DialogResult.OK)
{
return;
}
string outputFolder = Path.Combine(dialog.SelectedPath, "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.SelectedPath))
{
if (!SourceFormats.TryGetValue(Path.GetExtension(file), out StreamType format))
{
continue;
}
try
{
// Load replaces the previous document
tx.Load(file, format);
tx.Save(Path.Combine(outputFolder,
Path.GetFileNameWithoutExtension(file) + ".pdf"), StreamType.AdobePDF);
converted++;
}
catch (FilterException exception)
{
failed.Add($"{Path.GetFileName(file)}: {exception.Reason}");
}
}
}
MessageBox.Show($"{converted} documents converted.\r\n" + string.Join("\r\n", failed));
}
}
The code works as follows:
- The ServerTextControl converts the files without touching the document in the editor. Load replaces its contents with each file, so one instance serves the whole folder.
- Save writes the complete document and overwrites an existing file.
- A FilterException with its Reason reports a file that couldn't be loaded or saved, and the loop goes on with the next one.
The handler runs on the thread of the form, so the window doesn't respond until the folder is done. For long conversions, run the work in the background, as Using ServerTextControl in Desktop Applications and UI Threading and Background Operations describe. For hundreds or thousands of documents, Text Control recommends separate processes over several threads, because threads that use TX Text Control can wait for each other on shared resources.
Save With the Save As Dialog Box
Save with a StreamType and a SaveSettings object opens the Save As dialog box, and afterward the same object reports the file, its size and its format. Offer Options in the Save As Dialog Box shows the call, the options it adds to the dialog box and the properties SavedFile, BytesWritten and SavedStreamType. SavedFile is empty when the document went 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. Paragraph.Save saves one paragraph to a byte array or a string:
private void savePartButton_Click(object sender, EventArgs e)
{
// The paragraph at the input position as an HTML string
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 with a zero-based text position returns the paragraph there. PDF and PDF/A are the exception: only complete documents are saved in these formats.
Save HTML With Stylesheets and Images
For HTML, two settings decide where formatting and images go. SaveSettings.CssSaveMode sets 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; an existing file is kept |
OverwriteFile | Like CreateFile, but an existing CSS file is overwritten |
SaveSettings.ImageSaveMode set to SaveAsData embeds the images, so the HTML needs no image files. The following handler writes the editor's document as an HTML file with a CSS file and as an HTML string with its styles in the head:
private void saveHtmlButton_Click(object sender, EventArgs 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);
}
CreateFile and OverwriteFile can't be used when HTML is saved to a string or a byte array.
Reduce the Size of PDF Files
Photos make up most of the size of many PDF files. Two settings reduce them when the document is saved:
private void smallPdfButton_Click(object sender, EventArgs 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 is the quality of lossy compression, from 1 to 100. It applies to images whose data is compressed with a lossy algorithm, and 0 selects a default.
- ImageMaxResolution reduces images with a higher resolution. The default, 0, keeps the resolution of each image.
The blog article Best Practices for Image Compression when Exporting to PDF in .NET C# compares values for documents read on screen and printed.
Leave Out Content and Add Properties
Before a document goes to a customer, internal comments shouldn't go with it. SaveSettings.OmittedContent leaves parts of the document out of the saved file, without changing the document in the editor:
private void saveForCustomerButton_Click(object sender, EventArgs 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);
}
The values of OmittedContent:
| 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 |
OmittedContent can't be used with PDF and PDF/A. The values of UserDefinedDocumentProperties are strings, Boolean values, integers or double values, and DocumentTitle overrides the title in DocumentSettings for this save.
Save to Streams
Save writes to a FileStream. Another stream, such as a MemoryStream that a mail library expects, is filled from a byte array:
private void saveToStreamButton_Click(object sender, EventArgs 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
Formats differ in what they can store, so a conversion can change a document:
- Plain text keeps only the text, with a carriage return and a line feed at the end of each paragraph.
- Fields of Word documents become fields only when
LoadSettings.ApplicationFieldFormatis set before loading, as Loading Documents describes. - Images are saved in their original format, or in the default image format of the target format when it doesn't support theirs, as Control How Images Are Saved lists for each document format.
- Fonts that aren't available on the computer that converts are replaced, which can change line and page breaks. Fonts shows how to find and choose the replacements.
- PDF/A needs fonts that can be embedded, which PDF/A covers.
Document Conversion in the Troubleshooting section helps when the result isn't what you expect.
Next Steps
- PDF Creation and Conversion: PDF options, metadata and PDF/UA.
- Loading Documents: load documents with all their options.
- Using ServerTextControl in Desktop Applications: convert documents in the background.
- Supported File Formats: the formats you can load and save.