TX Text Control .NET Server

Text Frames and Shapes

Topic

An address block at a fixed place on a letter, a highlighted note beside a paragraph or a sidebar with a small table are text frames: rectangles with text of their own, placed anywhere on a page. Drawings add shapes such as lines, arrows and callouts. With ServerTextControl, you insert and format text frames, fill them with text, tables and images, find them in templates by name and read, move and remove the text frames and drawings of documents you load.

Frames are placed like images, so the placements and insertion modes described in Images apply here as well. Charts and barcodes, the other kinds of frames, are covered in Charts and Barcodes, and drawing shapes in the browser in Shapes in the Document Editor section.

How Text Frames Work

A TextFrame is a rectangle filled with text that is formatted like the main text. It's a text part of its own and implements IFormattedText, so it has paragraphs, a selection, tables, images, fields and even further text frames.

  • Collections. ServerTextControl.TextFrames contains the text frames of the main text, but not the text frames nested in other text frames. Headers and footers have HeaderFooter.TextFrames, and TextFrame.Frames contains the images, text frames, charts, barcodes and drawings inside a text frame.
  • Base class. Text frames, images, charts, barcodes and drawings derive from FrameBase, which provides the name, ID, size, location, text distances and z-order.
  • Placement. A text frame is inline, anchored to a paragraph or fixed on a page or in the document, depending on the Add overload. Every overload but the inline one takes a TextFrameInsertionMode as its last parameter, which says how the text flows around the frame: DisplaceText, DisplaceCompleteLines, AboveTheText or BelowTheText.
  • Units. Sizes, locations, borders and margins are in twips, 1/1440 inch.

Insert a Text Frame

The following program writes two paragraphs and anchors a note with a background color and a double border to the right edge of the second paragraph:

using System.Drawing;
using TXTextControl;

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

    tx.Text = "Invoice INV-2025-001\r\n" +
        "Thank you for your order. This paragraph flows around the note on the right.";

    // 3 x 1.5 inches in twips
    TextFrame note = new TextFrame(new Size(4320, 2160))
    {
        Name = "payment-note",
        BackColor = Color.LightYellow,
        BorderWidth = 20,
        BorderColor = Color.DarkBlue,
        BorderStyle = FrameStyle.Double,
        InternalMargins = new int[] { 144, 144, 144, 144 }
    };

    // Anchored to the paragraph at the input position, aligned right
    tx.TextFrames.Add(note, HorizontalAlignment.Right, -1, TextFrameInsertionMode.DisplaceText);

    // The text frame has a selection of its own
    note.Selection.Text = "Payment due within 14 days.";

    tx.Save("text-frame.pdf", StreamType.AdobePDF);
}

In text-frame.pdf, the note sits at the right edge of the second paragraph, and the text of the paragraph wraps to the left of it:

The result of the program: a light yellow text frame with a dark blue double border and the text "Payment due within 14 days" to the right of the paragraph that flows around it.

The code works as follows:

  • The TextFrame constructor takes the size of the frame.
  • BackColor is the background color. BorderWidth is the width of the border line, where 0 means no border. BorderColor and BorderStyle, FrameStyle.Single or FrameStyle.Double, set its color and style.
  • InternalMargins are the distances between the border and the text, in the order left, top, right and bottom.
  • Add with an alignment anchors the frame to the paragraph that contains the text position; -1 is the input position. The alignment is Left, Right or Center.
  • TextFrame.Selection inserts text into the frame and formats it, as ServerTextControl.Selection does in the main text.

Transparency makes the frame see-through, from 0 for opaque to 255 for completely transparent. The other overloads of Add insert a frame inline with Add(frame, textPosition), anchor it at a location relative to the paragraph or place it at a fixed position.

Fill a Text Frame in a Template

Letters with window envelopes need the address at the same place on the first page, whatever the text above it. A text frame fixed on the page holds the address, and its name lets your code find it again. The following program creates such a template and then fills the frame of the saved template:

using System.Drawing;
using TXTextControl;

using (ServerTextControl tx = new ServerTextControl())
{
    tx.Create();
    tx.Text = "Dear customer,\r\nplease find your invoice attached.";

    TextFrame address = new TextFrame(new Size(5040, 1440))
    {
        Name = "recipient",
        BorderWidth = 0
    };

    // Page 1, 1 inch from the left and 2 inches from the top edge of the page
    tx.TextFrames.Add(address, 1, new Point(1440, 2880), TextFrameInsertionMode.DisplaceCompleteLines);

    tx.Save("letter-template.docx", StreamType.WordprocessingML);
}

using (ServerTextControl tx = new ServerTextControl())
{
    tx.Create();
    tx.Load("letter-template.docx", StreamType.WordprocessingML);

    // null if no text frame has this name
    TextFrame recipient = tx.TextFrames.GetItem("recipient");

    if (recipient != null)
    {
        recipient.Selection.Text = "Acme Corporation\r\n123 Business Lane, Suite 456\r\nMetropolis, USA";
    }

    tx.Save("letter.pdf", StreamType.AdobePDF);
}

The code works as follows:

  • Add(frame, page, location, insertionMode) places the frame on a page, numbered from 1, at a location relative to the top-left corner of the page. Add(frame, location, insertionMode) places it relative to the top-left corner of the whole document.
  • DisplaceCompleteLines keeps the text above and below the frame, so the text never runs beside the address.
  • FrameBase.Name is kept when the document is saved as DOCX, DOC or RTF. TextFrameCollection.GetItem finds a frame by its name or its ID and returns null if there is none.

A text frame has the same collections as the main text: TextFrame.Tables adds a table to the frame, and TextFrame.Images an image, for example recipient.Tables.Add(2, 2, 10). To replace placeholder text in all frames at once, loop through TextParts, as Replace Text in Headers, Footers and Text Frames shows.

Read and Remove the Text Frames of a Document

The following program lists the text frames of a loaded document with their text and removes the frame named "draft-note":

using TXTextControl;

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

    // Occurs for each text frame of the loaded document
    tx.TextFrameCreated += (sender, e) => Console.WriteLine($"Loaded text frame '{e.TextFrame.Name}'");

    tx.Load("Documents/sample.docx", StreamType.WordprocessingML);

    foreach (TextFrame frame in tx.TextFrames)
    {
        Console.WriteLine($"'{frame.Name}' (ID {frame.ID}): {frame.Size.Width} x {frame.Size.Height} twips, " +
                          $"text position {frame.TextPosition}, {frame.Frames.Count} objects inside");

        foreach (Paragraph paragraph in frame.Paragraphs)
        {
            Console.WriteLine("  " + paragraph.Text);
        }
    }

    TextFrame draft = tx.TextFrames.GetItem("draft-note");

    if (draft != null)
    {
        tx.TextFrames.Remove(draft);
    }

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

The code works as follows:

FrameBase.Location moves a frame that isn't inline: for an anchored frame, relative to its paragraph, otherwise relative to the document. FrameBase.Bounds returns its rectangle relative to the top-left corner of the document. When a PDF is loaded with the default import settings, its layout is rebuilt with text frames, tables and images, as Loading Documents describes.

Drawings and Shapes

A drawing is a single shape, such as a line, a rectangle, an arrow, a star or a callout, or a canvas with several shapes. The shapes follow the preset shapes of Office Open XML, and ShapeType lists them. A DrawingFrame places a drawing in a document like a text frame, and ServerTextControl.Drawings returns the DrawingCollection of the main text. Drawings are saved and loaded with DOCX and the TX Text Control format; other formats save an image of the drawing.

The following program lists the drawings of a loaded document and removes the drawing named "fold-marks":

using TXTextControl;
using TXTextControl.DataVisualization;

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

    // Occurs for each drawing of the loaded document
    tx.DrawingCreated += (sender, e) => Console.WriteLine($"Loaded drawing '{e.DrawingFrame.Name}'");

    tx.Load("Documents/sample.docx", StreamType.WordprocessingML);

    foreach (DrawingFrame frame in tx.Drawings)
    {
        Console.WriteLine($"'{frame.Name}': {frame.Size.Width} x {frame.Size.Height} twips at {frame.Bounds.Location}");
    }

    // null if no drawing has this name
    DrawingFrame marks = tx.Drawings.GetItem("fold-marks");

    if (marks != null)
    {
        tx.Drawings.Remove(marks);
    }

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

The code works as follows:

Headers, footers and text frames have their own Drawings collections, such as HeaderFooter.Drawings. The blog article Adding Fold Marks and Punch Marks to Documents using Drawings in C# inserts line shapes behind the text of a header, with code for Windows Forms. The page it creates has two black fold marks, 87 mm and 192 mm from the top of the page, and a longer red punch mark, the center for the punch holes, at 148.5 mm, all in the left margin:

A page of text with three short horizontal lines in its left margin: two black fold marks and, between them, a longer red punch mark.

The drawing frame in that article wraps the TXDrawingControl of Windows Forms: the DrawingFrame constructor accepts the drawing controls of Windows Forms and WPF only. To draw shapes without them, render them with TXDrawingCore as shown next.

Render Shapes With TXDrawingCore

TXDrawingCore, introduced in version 33.0, is a canvas of shapes without a user interface. It holds Shape objects in a ShapeCollection and renders them as SVG. The following program draws a rounded rectangle with a blue fill and a dark outline, saves the SVG and inserts it into a document as an image:

using System.Drawing;
using TXTextControl;
using TXTextControl.Drawing;

// A canvas of 2 x 1 inches, in twips
TXDrawingCore drawing = new TXDrawingCore(2880, 1440);

Shape box = new Shape(ShapeType.RoundRectangle)
{
    Location = new Point(144, 144),
    Size = new Size(2592, 1152)
};

box.ShapeFill.Color = Color.LightSteelBlue;
box.ShapeOutline.Color = Color.DarkBlue;
box.ShapeOutline.Width = 40; // twips

drawing.Shapes.Add(box);

// The shapes as SVG, without selection handles
string svg = drawing.GetSVG(drawing.Size, false);
File.WriteAllText("shape.svg", svg);

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

    tx.Selection.Text = "Status: ";

    // 8 is the import filter for SVG
    tx.Images.Add(new TXTextControl.Image("shape.svg", 8), -1);

    tx.Save("shape.pdf", StreamType.AdobePDF);
}

The code works as follows:

  • The TXDrawingCore constructor takes the width and height of the canvas in twips. Without parameters, the canvas is 1134 x 1134 twips.
  • The Shape constructor takes a ShapeType. Location and Size are in twips from the top-left corner of the canvas.
  • ShapeFill returns the Fill of the shape, whose Color is the fill color. ShapeOutline returns the Outline with the line Color, black by default, and Width in twips, 20 by default.
  • ShapeCollection.Add adds the shape at its location and size. With ShapeCollection.AddStyle.Fill as second parameter, the shape fills the whole canvas instead.
  • GetSVG returns the shapes as an SVG string. Its second parameter shows the handles of selected shapes. The size you pass becomes the new Size of the canvas.
  • TXTextControl.Image is written with its namespace, because System.Drawing has an Image class too.

Other members of TXDrawingCore:

MemberPurpose
BackColor, BorderColor, BorderWidthThe background and the border of the canvas; transparent, black and 0 by default
Save, LoadSave the shape data to a Stream and load it again
SizeToContentAdapts the canvas to the visible bounds of its shapes and returns the change of each side in twips
Selection, SelectAllThe selected shapes, as a Drawing.Selection that brings them forward or sends them back
Undo, Redo, ClearUndoThe undo buffer of the canvas
Copy, Cut, PasteExchange selected shapes through the clipboard
Changed, ShapeCreated, AdaptBoundsOccur when the shapes or their formatting change, when a shape is added and when the shapes need a larger or smaller canvas

Next Steps