Text Frames and Shapes
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,AboveTheTextorBelowTheText. - 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 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.SingleorFrameStyle.Double, set its color and style. - InternalMargins are the distances between the border and the text, in the order left, top, right and bottom.
Addwith an alignment anchors the frame to the paragraph that contains the text position;-1is the input position. The alignment isLeft,RightorCenter.- TextFrame.Selection inserts text into the frame and formats it, as
ServerTextControl.Selectiondoes 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.DisplaceCompleteLineskeeps 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
nullif 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:
- TextFrameCreated occurs for each text frame of a loaded document, but not for frames added with
Add. TextFrameDeleted occurs when a frame is removed. - FrameBase.Size returns the size of the frame and, unlike for images, also changes it. TextPosition is the one-based position of the frame in the text.
- TextFrame.Paragraphs contains the paragraphs inside the frame.
- TextFrameCollection.Remove removes a frame and returns
trueif it was removed.
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:
- DrawingCreated occurs for each drawing of a loaded document, but not for drawings added with
Add, and DrawingDeleted when a drawing is removed. - DrawingCollection.GetItem returns a drawing by its name or ID, or
null, and Remove removes it. - DrawingCollection.Add has the same placements as
TextFrameCollection.Add, with a FrameInsertionMode as the last parameter.
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:

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
.Fillas 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.Imageis written with its namespace, becauseSystem.Drawinghas anImageclass too.
Other members of TXDrawingCore:
| Member | Purpose |
|---|---|
| BackColor, BorderColor, BorderWidth | The background and the border of the canvas; transparent, black and 0 by default |
| Save, Load | Save the shape data to a Stream and load it again |
| SizeToContent | Adapts the canvas to the visible bounds of its shapes and returns the change of each side in twips |
| Selection, SelectAll | The selected shapes, as a Drawing.Selection that brings them forward or sends them back |
| Undo, Redo, ClearUndo | The undo buffer of the canvas |
| Copy, Cut, Paste | Exchange selected shapes through the clipboard |
| Changed, ShapeCreated, AdaptBounds | Occur when the shapes or their formatting change, when a shape is added and when the shapes need a larger or smaller canvas |
Next Steps
- Charts and Barcodes: add barcodes and QR codes to documents.
- Images: insert and position images.
- Headers and Footers: place frames that appear on every page.
- Shapes in the Document Editor: let users insert text frames and shapes.