TX Text Control .NET for WPF

PDF Import and Its Limitations

Topic

Users of a WPF application sometimes want to edit a PDF they received, or reuse its text in a new document. TX Text Control loads a PDF into the TextControl of a window, or into a ServerTextControl, by reconstructing an editable document from its pages. A PDF records where characters are drawn, not the document they came from, so the result depends on the file. The sections below explain what the import does, which LoadSettings control it and where its limits are.

Open a Password-Protected PDF shows how to open an encrypted PDF, and Loading Documents the other options of LoadSettings.

What Happens When a PDF Is Loaded

Like PostScript, PDF is a page description format. It defines what each character looks like and where it is placed, but not always which Unicode character it is, and it rarely says in which order the text flows or whether a piece of text is a heading or a table cell. Most PDF files include a character mapping, which lets a program turn the characters into text.

TX Text Control's PDF import filter extracts all text it can find:

  • It sorts the text blocks into their logical order and adds missing spaces and paragraph breaks.
  • It keeps font names, sizes and styles.
  • It supports Unicode and reads PDF and PDF/A files. Adobe Acrobat isn't needed.
  • How it rebuilds the document is set with LoadSettings.PDFImportSettings.

The imported document looks like the PDF. Its paragraphs, tables and text frames are rebuilt from the positions of the text on the pages.

Choose How the Document Is Rebuilt

The values of PDFImportSettings:

ValueResult
GenerateLinesText only; images and paragraph formatting are discarded. Suited for searching PDF files.
GenerateParagraphsParagraphs generated from the text flow; images are discarded.
GenerateTextFramesDefault. Tables, images and text frames create a document that comes as close as possible to the appearance of the original.
GenerateTextFramesFirstPageOnlyLike GenerateTextFrames, but only the first page is loaded, for example for a preview.
LoadEmbeddedDataMetadata, form fields and text coordinates are provided through LoadSettings.EmbeddedData.
LoadEmbeddedFilesDefault. Files embedded in the PDF are provided through EmbeddedFiles.

The first four values decide the layout, the last two add data, and the values are combined with |. The obsolete value GenerateXML is replaced by LoadEmbeddedData.

Which layout fits depends on the task:

TaskValue
Edit the text and reuse it in another documentGenerateParagraphs
Show forms and complex layouts as they lookGenerateTextFrames
Search or index the contentGenerateLines
Show a quick preview of the first pageGenerateTextFramesFirstPageOnly

Open a PDF in the Window

Add a button with the handler OpenPdfButton_Click to MainWindow.xaml. The handler asks for a PDF with the OpenFileDialog of WPF, loads it with paragraphs from the text flow and warns when the file has no text:

private void OpenPdfButton_Click(object sender, RoutedEventArgs e)
{
    OpenFileDialog dialog = new OpenFileDialog() { Filter = "PDF files|*.pdf" };

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

    LoadSettings loadSettings = new LoadSettings()
    {
        // Paragraphs from the text flow, without images
        PDFImportSettings = PDFImportSettings.GenerateParagraphs
    };

    try
    {
        textControl1.Load(dialog.FileName, StreamType.AdobePDF, loadSettings);
    }
    catch (FilterException exception)
    {
        MessageBox.Show(this, "The PDF could not be loaded: " + exception.Reason, "Open PDF");
        return;
    }

    if (textControl1.Text.Trim().Length == 0)
    {
        MessageBox.Show(this, "The PDF contains no text that can be extracted.", "Open PDF");
    }
}

MainWindow.xaml.cs needs these using directives:

using System.Windows;
using Microsoft.Win32;
using TXTextControl;

The code works as follows:

  • OpenFileDialog comes from Microsoft.Win32, and ShowDialog returns true when the user chose a file.
  • Load with StreamType.AdobePDF replaces the document of textControl1. PDF/A files are loaded the same way, because StreamType.AdobePDFA is for saving only.
  • A FilterException reports a PDF that can't be imported, and its Reason says why, as Encrypted PDF Documents describes.
  • Text is empty when TX Text Control found no text in the PDF.

A preview of a long PDF loads its first page only and keeps the text read-only:

private void PreviewPdfButton_Click(object sender, RoutedEventArgs e)
{
    LoadSettings loadSettings = new LoadSettings()
    {
        PDFImportSettings = PDFImportSettings.GenerateTextFramesFirstPageOnly
    };

    textControl1.Load("report.pdf", StreamType.AdobePDF, loadSettings);
    textControl1.EditMode = EditMode.ReadAndSelect;
}

With EditMode ReadAndSelect, the user can read and select the text but not change it.

Convert PDF Documents Without the Window

For conversions, a ServerTextControl does the work without showing anything. The following handler lets the user pick a folder with the OpenFolderDialog of WPF, which requires .NET 8 or later, converts each PDF in it to DOCX and lists the files it skipped:

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

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

    List<string> skipped = new List<string>();

    foreach (string file in Directory.GetFiles(dialog.FolderName, "*.pdf"))
    {
        using (ServerTextControl tx = new ServerTextControl())
        {
            // Initialize the new instance before using it
            tx.Create();

            LoadSettings loadSettings = new LoadSettings()
            {
                PDFImportSettings = PDFImportSettings.GenerateParagraphs
            };

            try
            {
                tx.Load(file, StreamType.AdobePDF, loadSettings);
            }
            catch (FilterException exception)
            {
                // For example InvalidPassword or Encrypted
                skipped.Add(System.IO.Path.GetFileName(file) + ": " + exception.Reason);
                continue;
            }

            if (tx.Text.Trim().Length == 0)
            {
                skipped.Add(System.IO.Path.GetFileName(file) + ": no text");
                continue;
            }

            tx.Save(System.IO.Path.ChangeExtension(file, ".docx"), StreamType.WordprocessingML);
        }
    }

    MessageBox.Show(this, skipped.Count == 0
        ? "All PDF documents were converted."
        : "Not converted:\n" + string.Join("\n", skipped), "Convert PDF");
}

The handler also needs using System.Collections.Generic; and using System.IO;. Every file gets its own ServerTextControl, and Create initializes it before Load. ServerTextControl.Text returns the imported text, and StreamType.WordprocessingML saves DOCX. The loop runs on the UI thread, so the window doesn't respond while it converts.

Where the Import Reaches Its Limits

  • Scanned documents are images of pages and contain no text. Use OCR software before importing them.
  • PDF files without a character mapping contain text that can't be converted into characters, so the import finds no text or only some of it.
  • The structure is an approximation. Most PDF files don't record headings, table cells or the order of the text, so the import rebuilds them from positions.
  • Images are part of the document with GenerateTextFrames and GenerateTextFramesFirstPageOnly only.
  • Form field values are read with Forms.GetAcroFormFields, as Reading Form Fields and Values describes.

Encrypted PDF Documents

A PDF can be encrypted and have a user password, needed to open it, and an owner password, which protects its permissions. TX Text Control imports these encryptions:

EncryptionCreated by
40-bit RC4Acrobat 3.0 and later
128-bit RC4Acrobat 5.0 and later
128-bit AESAcrobat 7.0 and later

PDF documents encrypted with 256-bit AES (Acrobat 9.0 and later) or with certificates throw a FilterException with the reason Encrypted. Without the correct user password, the reason is InvalidPassword; ask the user and load again with UserPassword, as Passwords and Access Permissions describes.

Other Ways to Get Data Out of a PDF

Next Steps