TX Text Control .NET Server

Large Documents

Topic

A contract of a few hundred pages, a merged report of several thousand or a DOCX file full of photos behaves differently from a one-page letter. It takes longer to load and render, it may exceed the request size limits of your server when a browser sends it, and a merge that creates it no longer fits into a single HTTP request. None of this needs a different API. It needs the right settings on the server and the right place for long-running work, and both are described below for the Document Viewer, the Document Editor, MailMerge and ServerTextControl.

Show Large Documents in the Document Viewer

The Document Viewer doesn't wait for the whole document. Since version 32.0 SP3, it loads documents with long polling: the server keeps the request open and sends each page as soon as it's rendered, so users read and annotate the first pages while the rest is still being processed. Your code doesn't need to do anything for this, as Large Documents in Document Viewer notes.

  • Rendering. DocumentViewerSettings.RenderMode chooses how pages are sent: as vector graphics with text, as glyph outlines or as bitmaps. The default, Auto, switches between them to balance performance and appearance. Render Modes in Document Viewer Architecture compares the modes.
  • PDF documents. The viewer can display PDF documents with PDF.js in the browser instead of rendering them on the server, as Document Viewer Architecture describes.
  • Documents sent from the browser. A document that the view passes as DocumentData or that JavaScript loads with loadDocument reaches the server in a POST request, as base64-encoded data. A large document can exceed the request size limit of the server, which the next section raises.

Raise the Request Size Limits

ASP.NET Core limits the size of request bodies. The limits apply to every request that carries a document: uploads, documents that the Document Viewer posts to its controller and documents that a page sends to your controller after saving them in the Document Editor.

ServerLimitDefaultWhen it's exceeded
KestrelKestrelServerLimits.MaxRequestBodySize30,000,000 bytes, about 28.6 MBThe connection is reset
IIS, in-process hostingmaxAllowedContentLength in web.config and IISServerOptions.MaxRequestBodySize30,000,000 bytes eachIIS answers with the status code 404.13
Form uploads with IFormFileFormOptions.MultipartBodyLengthLimit134,217,728 bytes, 128 MBAn InvalidDataException

Raise the limits in Program.cs before builder.Build(). The following code allows request bodies of up to 100,000,000 bytes, both in Kestrel and in IIS:

// Kestrel: applications without IIS, containers, Linux
builder.WebHost.ConfigureKestrel(serverOptions =>
{
    serverOptions.Limits.MaxRequestBodySize = 100_000_000;
});

// IIS with the in-process hosting model
builder.Services.Configure<IISServerOptions>(options =>
{
    options.MaxRequestBodySize = 100_000_000;
});

IIS checks the size of a request before it reaches your application. Add the same value to the web.config file of the published application:

<configuration>
  <system.webServer>
    <security>
      <requestFiltering>
        <requestLimits maxAllowedContentLength="100000000" />
      </requestFiltering>
    </security>
  </system.webServer>
</configuration>

The code works as follows:

  • ConfigureKestrel sets the limit of Kestrel for all requests. With out-of-process hosting behind IIS, IIS sets the limit and Kestrel's limit doesn't apply.
  • IISServerOptions.MaxRequestBodySize and maxAllowedContentLength both apply with in-process hosting, which is the default for IIS. Raise both, or the lower one rejects the request.
  • To raise the limit for a single endpoint only, such as the action that saves documents from the editor, put the attribute [RequestSizeLimit(100_000_000)] on it instead. It can't exceed maxAllowedContentLength in IIS.

Microsoft describes the limits in Upload files in ASP.NET Core. Choose a limit that fits your largest documents: base64 encoding makes the data that a page sends larger than the document itself.

Save Large Documents From the Document Editor

saveDocument in the Document Editor returns binary formats such as DOCX as base64-encoded data, and the page usually posts it to a controller of your application, as Store Documents on the Server shows. For large documents, raise the limit of this action with [RequestSizeLimit].

Server code can also save the document of an open editor without a request from the browser: WebSocketHandler.GetInstance returns the handler of an editor by its connection ID, and its SaveText method writes the document into a byte array on the server, as Reach a Session on the Server shows.

Merge Documents With Thousands of Pages

A typical invoice is merged in a few hundred milliseconds. A document with many nested merge blocks and hundreds or thousands of pages takes seconds or minutes, which is too long for an HTTP request:

  • Merge in the background. Accept the request, merge the document in a background service and notify the client when it's ready, as Keep the Service Responsive in REST Services describes.
  • Create one document per record. When the data contains many records, such as one invoice per customer, a merge with append set to false creates a separate document for each record, and the DataRowMerged event delivers each of them. Create One Document or Separate Documents shows the code.
  • Design long tables with care. In Text Control's benchmark of 2023, run with version 31.0 and the Classic engine, the time for one merge block grew faster than its number of rows: one row took 0.23 seconds, and the benchmark went up to 1,280 rows on 59 pages. The same rows split into two merge blocks merged faster, and formulas in merge blocks add a calculation for each row. The blog article MailMerge Class Performance Benchmark shows the measurements.

Replace Text in Many Large Files

ServerTextControl works on large DOCX files with the same members as on small ones. In a benchmark of Text Control on an eight-core Ryzen 7 processor, loading an 80-page DOCX file with about 30,000 words, replacing five placeholders and saving it took 0.46 seconds. Processing the file 400 times, 32,000 pages in total, took 73.32 seconds, 0.18 seconds per document, with one ServerTextControl that loaded and saved it again and again.

The following program replaces a placeholder in all DOCX files of the folder Documents and saves the results in Output:

using TXTextControl;

string searchText = "%%company_name%%";
string replacementText = "Text Control, LLC";

Directory.CreateDirectory("Output");

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

    foreach (string file in Directory.GetFiles("Documents", "*.docx"))
    {
        tx.Load(file, StreamType.WordprocessingML);

        int start = 0;
        int position;

        while ((position = tx.Find(searchText, start, FindOptions.MatchCase)) != -1)
        {
            tx.Select(position, searchText.Length);
            tx.Selection.Text = replacementText;

            // Continue behind the inserted text
            start = position + replacementText.Length;
        }

        tx.Save(Path.Combine("Output", Path.GetFileName(file)), StreamType.WordprocessingML);
    }
}

The code works as follows:

  • One ServerTextControl is created once and loads one file after another; Load replaces the previous document.
  • Find returns the position of the next occurrence, or -1 when there is none. Select selects it, and setting Selection.Text replaces it.
  • Save writes the changed document as DOCX.

Find and Replace Text explains the search options, and the blog article High-Performance Text Replacement in Large DOCX Files using C# .NET contains the benchmark. For batches of hundreds or thousands of documents, Text Control recommends several processes instead of several threads in one process, which can wait for each other at TX Text Control's critical sections, as Multiple Processes describes.

Large PDF Documents

  • Display. The Document Viewer can render PDF documents in the browser with PDF.js, as described above.
  • Text. To read the text of a PDF, such as for a search index, PDF.Contents.Lines returns its lines with their page and position, without loading the document into a ServerTextControl in your code, as Text Extraction shows.

Keep Documents Small

Images can take up most of the size of a PDF document. When a document is saved as PDF, SaveSettings.ImageCompressionQuality compresses its images, and ImageMaxResolution reduces images with a higher resolution. In a test of Text Control with a 10-page document and two large images, the PDF took 14.68 MB with lossless compression and 1.48 MB with a quality of 70. Reduce the Size of PDF Files shows the code, and the blog article Best Practices for Image Compression when Exporting to PDF in .NET C# the measurements.

Next Steps