TX Text Control .NET Server

Images

Topic

Logos, product photos and diagrams reach a document as images. ServerTextControl inserts them from files and byte arrays, inline with the text, anchored to a paragraph or at a fixed position on a page. Your code then scales, finds and removes them, decides how they're saved and extracts the images of documents you load.

Templates whose image placeholders are filled from your data are the subject of Images in Mail Merge and Reporting. The console programs below read a logo.png and a logo.svg from the Documents folder.

How Images Are Placed

ServerTextControl.Images returns the ImageCollection of the main text. Headers, footers and text frames have their own collections, such as HeaderFooter.Images. An image is placed in one of three ways:

PlacementBehaviorLocation measured from
InlineTreated like a single character of the text—
Anchored to a paragraphMoves with its paragraph; the text flows around it or it lies above or below the textThe top-left corner of the paragraph
Fixed positionStays where it is when the text changesThe top-left corner of a page, or of the whole document

The Add method you call decides the placement. Locations are measured in twips, 1/1440 inch, and a text position of -1 stands for the current input position. Add without parameters opens a file dialog box and isn't meant for the server.

Supported Image Formats

The Image class loads the image formats BMP, TIF, WMF, PNG, JPG, GIF, EMF and SVG. When you create an image from a file, the constructor takes the file name and the index of the import filter:

Filter indexFormat
1BMP
2TIF
3WMF
4PNG
5JPG, JPEG
6GIF
7EMF
8SVG
  • SVG follows SVG 1.1 (Second Edition), without external images or style sheets, interactivity and animation, advanced text transformations, patterns, masking, compositing and filter effects. When a document is saved as PDF, SVG images become vector graphics.
  • EMF is available in the Core engine since version 34.0, so applications on Linux can use EMF images.

Insert an Image From a File

The following program writes a line of text and inserts a logo behind it, inline with the text:

using TXTextControl;

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

    tx.Selection.Text = "Invoice INV-2025-001 ";

    // 4 is the import filter for PNG
    Image logo = new Image("Documents/logo.png", 4);

    // -1 inserts the image inline at the current input position
    tx.Images.Add(logo, -1);

    logo.Name = "logo";
    logo.DescriptiveText = "Company logo";

    tx.Save("inline-image.pdf", StreamType.AdobePDF);
}

The code works as follows:

  • new Image("Documents/logo.png", 4) creates the image from the file with the PNG filter. For logo.svg, the filter index is 8.
  • tx.Images.Add(logo, -1) inserts the image inline and returns true if it was added.
  • Name gives the image a name your code can find it by later.
  • DescriptiveText is the alternative text of the image in accessible PDF documents, described in PDF/UA.

Insert an Image From a Byte Array

Images from a database, cloud storage or an upload arrive as bytes. Wrap the bytes in a MemoryStream and pass it to the Image constructor. The buffer of the stream must be publicly visible, which the Image class needs to read it, and the image is added while the stream is open:

using TXTextControl;

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

    byte[] data = File.ReadAllBytes("Documents/logo.png");

    // A read-only stream whose buffer is publicly visible
    using (MemoryStream stream = new MemoryStream(data, 0, data.Length, false, true))
    {
        Image image = new Image(stream);
        bool added = tx.Images.Add(image, -1);

        Console.WriteLine($"Image added: {added}");
    }

    tx.Save("image-from-bytes.docx", StreamType.WordprocessingML);
}

The fifth argument of the MemoryStream constructor, publiclyVisible, is true. The stream constructor doesn't need a filter index.

Position an Image on the Page

The other Add overloads anchor an image to a paragraph or place it at a fixed position. Their last parameter is a value of ImageInsertionMode that sets how the text flows around the image. Add accepts four values:

ValueThe text
DisplaceTextFlows around the image and fills the space left and right of it
DisplaceCompleteLinesStops above the image and continues below it
AboveTheTextIs covered by the image
BelowTheTextCovers the image

An image in a header or a footer that has to stay at the same place on every page takes FixedOnPage in addition to its text flow, as FixedOnPage | BelowTheText in Watermarks. The header then keeps its height, whatever the position of the image.

The following program anchors a logo at the right edge of a paragraph and places an SVG image on the first page, 1 inch from its top-left corner, behind the text:

using TXTextControl;

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

    tx.Text = "TX Text Control .NET Server License. Priority Support - 12 Months. " +
              "The text of this paragraph flows around the logo on the right.";

    // Anchored to the paragraph at the input position, aligned right
    Image logo = new Image("Documents/logo.png", 4);
    tx.Images.Add(logo, HorizontalAlignment.Right, -1, ImageInsertionMode.DisplaceText);

    // Fixed on page 1, 1440 twips (1 inch) from the left and top edge of the page
    Image stamp = new Image("Documents/logo.svg", 8);
    tx.Images.Add(stamp, 1, new System.Drawing.Point(1440, 1440), ImageInsertionMode.BelowTheText);

    tx.Save("positioned-images.pdf", StreamType.AdobePDF);
}

The code works as follows:

  • Add(image, alignment, textPosition, insertionMode) anchors the image to the paragraph that contains the text position. The alignment is Left, Right or Center. Another overload takes a System.Drawing.Point relative to the paragraph instead.
  • Add(image, pageNumber, location, insertionMode) places the image on a page, numbered from 1, at a location relative to the top-left corner of the page.
  • Add(image, location, insertionMode), without a page number, measures the location from the top-left corner of the whole document. The vertical value includes every page above and the gaps between the pages, starting with the gap above the first page.
  • Location of an image at a fixed position is always relative to the whole document, whichever of the two overloads inserted it. For an anchored image, it's relative to the paragraph, and it's zero for an inline image or an image with a horizontal alignment.
  • Alignment applies to anchored images only. Inline images and images at a fixed position have no horizontal alignment.
  • TextDistances sets the distance between the image and the surrounding text, and ChangeZOrder moves an image in front of or behind other objects.

Find, Change and Remove Images

The images of a loaded document are in the same collection. The following program prints the name, size and position of each image in the main text of Documents/sample.docx, scales all images to half their size and removes the image named "logo":

using TXTextControl;

using (ServerTextControl tx = new ServerTextControl())
{
    tx.Create();
    tx.Load("Documents/sample.docx", StreamType.WordprocessingML);

    Console.WriteLine($"Images in the main text: {tx.Images.Count}");

    foreach (Image image in tx.Images)
    {
        Console.WriteLine($"'{image.Name}': {image.Size.Width} x {image.Size.Height} twips, text position {image.TextPosition}");

        // Scaling factors in percent
        image.HorizontalScaling = 50;
        image.VerticalScaling = 50;
    }

    // null if no image has this name
    Image logo = tx.Images.GetItem("logo");

    if (logo != null)
    {
        tx.Images.Remove(logo);
    }

    tx.Save("sample-images-changed.docx", StreamType.WordprocessingML);
}

The code works as follows:

  • Size returns the size of an image in twips. For images, it's read only: change the size with HorizontalScaling and VerticalScaling, which are percentages.
  • TextPosition is the one-based text position of the image.
  • GetItem returns the image with a name or an ID, or null if there is none.
  • Remove removes an image from the document, and Clear removes all images of the collection.

While a document is loaded, ServerTextControl raises the ImageCreated event for each image it contains. ServerTextControl.Frames contains the images together with the text frames, charts and barcodes of a document, which Text Frames and Shapes and Charts and Barcodes describe.

Add a Logo to a Header

A logo that appears on every page belongs in the header. Add the image to the header's own collection:

using TXTextControl;

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

    tx.Text = "This document has a logo in its header.";

    tx.HeadersAndFooters.Add(HeaderFooterType.Header);
    HeaderFooter header = tx.HeadersAndFooters.GetItem(HeaderFooterType.Header);

    // Anchored at the right edge of the header's first paragraph
    Image logo = new Image("Documents/logo.png", 4);
    header.Images.Add(logo, HorizontalAlignment.Right, 0, ImageInsertionMode.DisplaceText);

    header.Selection.Text = "Invoice INV-2025-001";

    tx.Save("header-logo.pdf", StreamType.AdobePDF);
}

Headers and Footers describes headers and footers, and Watermarks images that lie behind the text of every page.

Control How Images Are Saved

An image that you add from a file is saved with a reference to that file, its name including the absolute path. Images without a file reference are saved with their data, in their original format. On a server, where the document usually leaves the machine, save the data with the document:

SaveSettings saveSettings = new SaveSettings()
{
    // Save the images with their data instead of references to image files
    ImageSaveMode = ImageSaveMode.SaveAsData
};

tx.Save("document.docx", StreamType.WordprocessingML, saveSettings);

The SaveSettings class has more options for images:

PropertySets
ImageSaveModeAuto (a file reference when one exists, otherwise the data), SaveAsData or SaveAsFileReference. Image.SaveMode sets it for a single image
ImageSavePathThe folder in which image files are created when images are saved as file references
ImageExportFilterIndexThe format images are saved in, with export filter indexes that differ from the import filters: 1 BMP, 2 WMF, 3 PNG, 4 JPG, 5 EMF, 6 TIF and 7 GIF; 0 keeps the original format
ImageCompressionQualityThe quality of lossy compression, from 1 to 100
ImageMaxResolutionThe maximum resolution in DPI; larger images are reduced

Image.ExportFilterIndex sets the format of a single image with the same export filter indexes. A value of 0 keeps the format the image was loaded in, which Image.FilterIndex returns. ImageCollection.ExportFilters lists the export filters as a string, in the order of their numbers.

Image Formats per Document Format

Not every document format can hold every image format. When the format you request isn't possible in the format of the document, the image is saved in the default image format of that document format, the first one in each row:

Document formatImage formats, default first
RTFPNG, BMP, WMF, JPG, EMF
DOCWMF, BMP, PNG, JPG, EMF, TIF
DOCXPNG, BMP, WMF, JPG, EMF, TIF, GIF, SVG
HTMLPNG, BMP, JPG, GIF
PDFBMP, JPG; the vector formats EMF, WMF and SVG are written as vector graphics
TX Text Control formatWMF, BMP, PNG, JPG, EMF, TIF, GIF, SVG

File Names of Saved Images

Images saved as file references in another format than their original one are written to new image files. Their folder and names follow these rules:

  • Folder. The folder is ImageSavePath. Without it, the image files go to a subfolder of the document's folder, named after the document with _Images appended: for C:\Documents\sample.rtf, this is C:\Documents\sample_Images.
  • File name. Image.ExportFileName sets the file name of an image. Without it, the original file name is used, with the extension of the export format.
  • Numbered names. An image without a file name gets the name of the document followed by an underscore and a number: sample_1.bmp, sample_2.bmp and so on for images saved as BMP.

With ImageSaveMode.SaveAsFileReference, an image that has no file yet is written to a new file by these rules.

When you load a document whose images are file references, LoadSettings.ImageSearchPath sets a folder that replaces their absolute paths. An image that isn't found is shown as a rectangle with its file name.

For PDF files, the blog article Best Practices for Image Compression when Exporting to PDF in .NET C# compares compression qualities and resolutions.

Extract the Images of a Document

There is no method that saves a single image to a file. To get the images of a document, save it as HTML with the image data embedded and decode the data: URLs of its img elements:

using System.Text.RegularExpressions;
using TXTextControl;

using (ServerTextControl tx = new ServerTextControl())
{
    tx.Create();
    tx.Load("Documents/sample.docx", StreamType.WordprocessingML);

    // Embed the images as Base64 data in the HTML
    SaveSettings saveSettings = new SaveSettings()
    {
        ImageSaveMode = ImageSaveMode.SaveAsData
    };

    string html;
    tx.Save(out html, StringStreamType.HTMLFormat, saveSettings);

    Regex imageData = new Regex(
        "<img[^>]+?src=[\"']data:image/(?<format>[^;]+);base64,(?<data>[^\"']+)[\"']",
        RegexOptions.IgnoreCase);

    Directory.CreateDirectory("Images");
    int number = 0;

    foreach (Match match in imageData.Matches(html))
    {
        string extension = match.Groups["format"].Value.ToLowerInvariant() switch
        {
            "jpeg" => ".jpg",
            "svg+xml" => ".svg",
            string format => "." + format
        };

        number++;
        byte[] bytes = Convert.FromBase64String(match.Groups["data"].Value);
        File.WriteAllBytes(Path.Combine("Images", $"image{number}{extension}"), bytes);
    }

    Console.WriteLine($"Images extracted: {number}");
}

The blog article Extracting Images from MS Word Documents in C# describes this approach for DOCX, DOC, RTF and PDF documents.

Next Steps