# Handling Untrusted Documents

> Open received files in a Windows Forms TextControl: limit Open dialog formats, catch FilterException, check links in HypertextLinkClicked and redact text.

- **Product:** TX Text Control .NET for Windows Forms
- **Section:** Documentation
- **Kind:** Topic
- **Path:** TX Text Control .NET for Windows Forms > Security > Handling Untrusted Documents
- **4565 words**
- **Web URL:** https://docs.textcontrol.com/tx-text-control-dotnet-winforms/security/untrusted-documents/
- **LLMs URL:** https://docs.textcontrol.com/tx-text-control-dotnet-winforms/security/untrusted-documents/llms.txt
- **LLMs-Full URL:** https://docs.textcontrol.com/tx-text-control-dotnet-winforms/security/untrusted-documents/llms-full.txt

---

Documents from email attachments, downloads or other people's shares can be damaged, can be something other than their file name says, and can contain links, images and file paths that point anywhere. When your Windows Forms application opens such a file in a `TextControl`, a few decisions keep you in control: which formats it accepts, what it imports, what happens when a user clicks a link and how it reacts to a file that doesn't load. Each of these is a setting or an event of TX Text Control that your code uses.

The samples extend *Form1.cs* with an editor named `textControl1`, as in [Create Your First Windows Forms Application](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/getting-started/document-editor/first-application/llms-full.txt). They need `using System.Diagnostics;` and `using TXTextControl;` at the top of the file.

### Accept Only the Formats You Need

Called with a [StreamType](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol/streamtype/enumeration/llms-full.txt) and [LoadSettings](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol/loadsettings/llms-full.txt), [TextControl.Load](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol/textcontrol/load-method/llms-full.txt) opens the **Open** dialog box with the formats you combine with `|` as its file filters. The following handler offers DOCX, RTF and PDF, imports the document without its images and hypertext links, and reports a file that can't be loaded:

```csharp
private void openButton_Click(object sender, EventArgs e)
{
    LoadSettings loadSettings = new LoadSettings()
    {
        // Don't load images and links of the document
        LoadImages = false,
        LoadHypertextLinks = false
    };

    try
    {
        // The Open dialog box offers only these formats
        if (textControl1.Load(StreamType.WordprocessingML | StreamType.RichTextFormat | StreamType.AdobePDF,
            loadSettings) == DialogResult.OK)
        {
            Text = Path.GetFileName(loadSettings.LoadedFile);
        }
    }
    catch (FilterException exception)
    {
        // For example WrongFileType, NotWellFormed or InvalidPassword
        MessageBox.Show(this, "The document can't be opened: " + exception.Reason);
    }
}
```

The code works as follows:

- The `StreamType` values are the file filters of the dialog box. Offer only the formats your application works with; HTML and XML, for example, aren't needed to fill in a contract.
- [LoadImages](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol/loadsettings/loadimages-property/llms-full.txt) set to `false` shows each image as a rectangle with the image's name, and [LoadHypertextLinks](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol/loadsettings/loadhypertextlinks-property/llms-full.txt) set to `false` doesn't load the links. Both are loaded by default. Use them where the text is what matters, such as a preview.
- `Load` returns `DialogResult.OK` when a file was loaded. [LoadedFile](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol/loadsettings/loadedfile-property/llms-full.txt) then holds its path.
- A file that can't be loaded throws a [FilterException](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol/filterexception/llms-full.txt), described in the next section.

### Handle Files That Don't Load

The [Reason](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol/filterexception/reason-property/llms-full.txt) of a `FilterException` names the cause. For documents from outside, the usual values are `WrongFileType`, `BadToken`, `NotWellFormed`, `FileTooBig`, `Unsupported`, `CannotOpen`, `CannotRead`, `Encrypted` and `InvalidPassword`; [Read the Reason of a FilterException](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/troubleshooting/conversion/#read-the-reason-of-a-filterexception) explains each of them.

Catch the exception around every `Load` call with a document from outside, and show the user a message rather than letting the application end. A PDF with a user password is loaded again with the password, as [Open a Password-Protected PDF](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/pdf/passwords/#open-a-password-protected-pdf) shows; which PDF encryption TX Text Control imports is listed in [Encrypted PDF Documents](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/pdf/import/#encrypted-pdf-documents). [Document Conversion](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/troubleshooting/conversion/llms-full.txt) in Troubleshooting helps when a file you expect to load doesn't.

### Decide What a Link Opens

A document's hypertext links can point to web addresses, files or other programs. The `TextControl` doesn't follow them by itself: a click on a link has no effect until your application handles the [HypertextLinkClicked](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol/textcontrol/hypertextlinkclicked-event/llms-full.txt) event. The handler below opens web addresses only, and only after the user agrees. Connect it in the **Properties** window of `textControl1`, on the **Events** tab:

```csharp
private void textControl1_HypertextLinkClicked(object sender, HypertextLinkEventArgs e)
{
    // Open web addresses only, and only when the user agrees
    if (Uri.TryCreate(e.HypertextLink.Target, UriKind.Absolute, out Uri uri)
        && (uri.Scheme == Uri.UriSchemeHttps || uri.Scheme == Uri.UriSchemeHttp))
    {
        if (MessageBox.Show(this, "Open " + uri.AbsoluteUri + "?", "Open Link",
            MessageBoxButtons.YesNo) == DialogResult.Yes)
        {
            Process.Start(new ProcessStartInfo(uri.AbsoluteUri) { UseShellExecute = true });
        }
    }
}
```

The code works as follows:

- [HypertextLinkEventArgs.HypertextLink](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol/hypertextlinkeventargs/hypertextlink-property/llms-full.txt) is the clicked link, and its [Target](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol/hypertextlink/target-property/llms-full.txt) is the address it points to, as the document states it.
- The address is shown to the user before anything is opened, because the visible text of a link can differ from its target.
- Targets that aren't web addresses, such as file paths, are ignored. The TX Words sample *Open Hyperlink* goes further and opens linked files in a new instance of the application; decide which targets your application trusts before you do the same.

Links to places in the same document raise [DocumentLinkClicked](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol/textcontrol/documentlinkclicked-event/llms-full.txt) instead and don't leave the document. [Fields, Hyperlinks, and Bookmarks](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/document-editor/fields/llms-full.txt) describes both kinds.

### Keep File References in One Folder

Documents can refer to images and link targets by absolute file paths, and a document from another computer brings the paths of that computer with it. [ImageSearchPath](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol/loadsettings/imagesearchpath-property/llms-full.txt) of the `LoadSettings` sets a folder that replaces all absolute file paths used in the document, so that images and links are looked for in the folder you choose:

```csharp
LoadSettings loadSettings = new LoadSettings()
{
    // Resolve the file paths of the document in this folder only
    ImageSearchPath = Path.Combine(Application.StartupPath, "Resources")
};

textControl1.Load("received.rtf", StreamType.RichTextFormat, loadSettings);
```

### Check Hidden Content Before Passing a Document On

A document from outside can carry more than its visible text: comments, tracked changes with deleted text, document properties such as the author, and, in a PDF, attached files. When your application forwards such a document or saves a copy for somebody else, remove what the recipient shouldn't get. The blog article [Sanitizing Data in Document Pipelines: A Practical Approach with TX Text Control in C# .NET](https://www.textcontrol.com/blog/2026/04/20/sanitizing-data-in-document-pipelines-a-practical-approach-with-tx-text-control-in-csharp-dotnet/) reports what a document contains, removes comments, tracked changes, metadata and form field values, and reports again, so that the result can be audited. [Comments and Tracked Changes](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/document-processing/comments-changes/llms-full.txt) shows the collections the article works with.

### Redact Text

A black rectangle drawn over a name hides it on screen, but the name is still in the document and can be copied out of it or found by a search. To redact a passage, remove its text and put a black box of the same size in its place. The following handler does this for the text the user has selected, line by line. It needs `using System.Collections.Generic;`, `using System.Drawing;`, `using System.IO;` and `using System.Text;` in addition:

```csharp
private void redactButton_Click(object sender, EventArgs e)
{
    int start = textControl1.Selection.Start;
    int end = start + textControl1.Selection.Length;

    // One black box for each line of the selection
    List<(int Start, int Length, Rectangle Bounds)> boxes = new List<(int, int, Rectangle)>();
    int boxStart = -1, boxEnd = -1, boxLine = -1;
    Rectangle boxBounds = Rectangle.Empty;

    for (int position = start; position <= end; position++)
    {
        // TextChars counts from 1, text positions from 0
        TextChar textChar = position < end ? textControl1.TextChars[position + 1] : null;
        Line line = position < end ? textControl1.Lines.GetItem(position) : null;

        // Close the box at the end of the selection, at a break and at a new line
        if (boxStart >= 0 && (textChar == null || char.IsControl(textChar.Char) || line.Number != boxLine))
        {
            boxes.Add((boxStart, boxEnd - boxStart + 1, boxBounds));
            boxStart = -1;
        }

        // Spaces and breaks don't start a box
        if (textChar == null || char.IsWhiteSpace(textChar.Char) || char.IsControl(textChar.Char))
        {
            continue;
        }

        // From the top of the character to the baseline of its line, in twips
        Rectangle bounds = textChar.Bounds;
        bounds.Height = line.Baseline - bounds.Top;

        if (boxStart < 0)
        {
            boxStart = position;
            boxLine = line.Number;
            boxBounds = bounds;
        }
        else
        {
            boxBounds = Rectangle.Union(boxBounds, bounds);
        }

        boxEnd = position;
    }

    // Replace the text from the last box to the first, so that the positions stay valid
    for (int i = boxes.Count - 1; i >= 0; i--)
    {
        textControl1.Select(boxes[i].Start, boxes[i].Length);
        textControl1.Selection.Text = "";

        byte[] svg = CreateBlackBox(boxes[i].Bounds.Size);

        using (MemoryStream stream = new MemoryStream(svg, 0, svg.Length, false, true))
        {
            // -1 inserts the image at the input position, where the text was
            textControl1.Images.Add(new TXTextControl.Image(stream), -1);
        }
    }
}

private static byte[] CreateBlackBox(Size size)
{
    // 20 twips are one point
    string svg = "<svg xmlns=\"http://www.w3.org/2000/svg\" " +
        $"width=\"{size.Width / 20f}pt\" height=\"{size.Height / 20f}pt\">" +
        "<rect width=\"100%\" height=\"100%\" fill=\"black\" /></svg>";

    return Encoding.UTF8.GetBytes(svg);
}
```

The code works as follows:

- [Selection.Start](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol/selection/start-property/llms-full.txt) and [Selection.Length](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol/selection/length-property/llms-full.txt) give the selected range. [TextChars](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol/textcontrol/textchars-property/llms-full.txt) returns each character of the main text; its indexer counts from 1, so text position 0 is `TextChars[1]`.
- [TextChar.Bounds](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol/textchar/bounds-property/llms-full.txt) is the rectangle of a character in twips, measured from the upper left corner of the document. [LineCollection.GetItem](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol/linecollection/getitem-method/llms-full.txt) returns the [Line](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol/line/llms-full.txt) of a text position, whose [Number](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol/line/number-property/llms-full.txt) tells when the selection wraps to the next line and whose [Baseline](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol/line/baseline-property/llms-full.txt) ends the box. A box never spans two lines.
- Setting [Selection.Text](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol/selection/text-property/llms-full.txt) to an empty string deletes the text of one box. [ImageCollection.Add](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol/imagecollection/add-method/llms-full.txt) with `-1` then inserts an SVG image of the box's size inline at that position, like a single character.
- `CreateBlackBox` writes the SVG in points, one twentieth of a twip value. The image stream is read-only with a publicly visible buffer, which the [Image](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol/image/constructor/llms-full.txt) constructor needs.

In the editor, black boxes now stand where the selected names and addresses were:

![A contract in the editor with control characters shown, in which black boxes replace the names and addresses of the parties.](https://docs.textcontrol.com/assets/img/tx-text-control-dotnet-winforms/redacted-text-editor.09f24101.webp)

Save the document as PDF, for example with `textControl1.Save("contract-redacted.pdf", StreamType.AdobePDF)`, before you pass it on. In the PDF, the text behind the boxes doesn't exist, so it can't be selected, copied or found:

![A nondisclosure agreement opened in Adobe Acrobat Reader, with the names and addresses of the parties replaced by black boxes.](https://docs.textcontrol.com/assets/img/tx-text-control-dotnet-winforms/redacted-pdf.e8e0911a.webp)

The blog article [True Document and PDF Text Redaction in .NET C#](https://www.textcontrol.com/blog/2024/11/05/true-document-and-pdf-text-redaction-in-net-c-sharp/) describes the technique this handler follows. There, the user first marks several passages as [SubTextPart](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol/subtextpart/llms-full.txt) objects with the name *tx\_redact*, and the code redacts all of them in one pass; the repository [TextControl/Redaction](https://github.com/TextControl/Redaction) holds its `Redaction` class, written for `ServerTextControl`. To find the passages to redact, [A Rule-Based PHI and PII Risk Scanner for Documents Using C# .NET](https://www.textcontrol.com/blog/2026/02/03/a-rule-based-phi-and-pii-risk-scanner-for-documents-using-csharp-dotnet/) searches the text of a document with regular expressions defined in JSON rules and reports each match with its location.

### Next Steps

- [PDF Passwords and Permissions](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/security/pdf-passwords/llms-full.txt): open encrypted PDF documents and respect their permissions.
- [Document Protection and User Permissions](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/security/document-protection/llms-full.txt): restrict what users change in documents you hand out.
- [Loading and Saving Documents](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/document-editor/load-save/llms-full.txt): the load and save options of the `TextControl`.
- [Security Overview](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/security/overview/llms-full.txt): where documents are processed and what your application decides.

---

## Nearby

- **Previous:** [Document Protection and User Permissions](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/security/document-protection/llms-full.txt)
- **Next:** [PDF Passwords and Permissions](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/security/pdf-passwords/llms-full.txt)
