# Pages

> Work with the formatted pages of a document in ServerTextControl: count pages, find the page of text and fields, read page text and render pages as SVG or TIFF.

- **Product:** TX Text Control .NET Server
- **Section:** Documentation
- **Kind:** Topic
- **Path:** TX Text Control .NET Server > Document Processing > Pages
- **5972 words**
- **Web URL:** https://docs.textcontrol.com/tx-text-control-dotnet-server/document-processing/pages/
- **LLMs URL:** https://docs.textcontrol.com/tx-text-control-dotnet-server/document-processing/pages/llms.txt
- **LLMs-Full URL:** https://docs.textcontrol.com/tx-text-control-dotnet-server/document-processing/pages/llms-full.txt

---

How many pages does a merged report have, and on which page does a table start? A document is flowing text, but once TX Text Control has formatted it, it consists of pages with numbers, sizes and positions. With `ServerTextControl`, you count the pages, find the page that contains a word, a field or a table, read the text of a single page, render pages as SVG images for thumbnails, combine page bitmaps in a multipage TIFF file and remove empty pages.

Page size, margins and orientation are set per section, as [Sections](https://docs.textcontrol.com/tx-text-control-dotnet-server/document-processing/sections/llms-full.txt) describes, and page numbers in headers and footers are added in [Headers and Footers](https://docs.textcontrol.com/tx-text-control-dotnet-server/document-processing/headers-footers/#add-a-footer-with-page-numbers). The examples below are console programs that load *Documents/sample.docx*.

### How Pages Work

[ServerTextControl.Pages](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol/servertextcontrol/pages-property/llms-full.txt) is the number of pages of the document, and [GetPages](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol/servertextcontrol/getpages-property/llms-full.txt) returns a [PageCollection](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol/pagecollection/llms-full.txt) with a [Page](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol/page/llms-full.txt) object for each page. A document has at least one page. A section can span several pages, and several sections can share one page, because a section doesn't have to begin at the top of a page.

A page has these properties, all read only:

| Property | Contains |
|---|---|
| [Number](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol/page/number-property/llms-full.txt) | The number of the page, starting with 1 |
| [Section](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol/page/section-property/llms-full.txt), [NumberInSection](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol/page/numberinsection-property/llms-full.txt) | The number of the section and the number of the page within it, both starting with 1. With several sections on a page, the first one, which determines the page size and orientation |
| [Start](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol/page/start-property/llms-full.txt), [Length](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol/page/length-property/llms-full.txt) | The one-based position of the first character and the number of characters, including the page break character at the end |
| [Bounds](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol/page/bounds-property/llms-full.txt), [TextBounds](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol/page/textbounds-property/llms-full.txt) | The rectangle of the page and the rectangle in which its text can be placed, in twips, relative to the top of the document |
| [Header](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol/page/header-property/llms-full.txt), [Footer](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol/page/footer-property/llms-full.txt) | The header and the footer shown on the page, or `null` |

What ends up on a page depends on the formatting of the document: the page settings of its sections, paragraph settings such as `PageBreakBefore` and `KeepWithNext`, which [Paragraphs](https://docs.textcontrol.com/tx-text-control-dotnet-server/document-processing/paragraphs/#format-paragraphs) describes, and the fonts available on the server, which [Creating Documents](https://docs.textcontrol.com/tx-text-control-dotnet-server/document-processing/creating/#make-fonts-available-on-the-server) covers.

### Count and Describe the Pages

The following program prints the number of pages and sections and describes each page:

```csharp
using TXTextControl;

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

    Console.WriteLine($"{tx.Pages} pages in {tx.Sections.Count} sections");

    foreach (Page page in tx.GetPages())
    {
        string header = page.Header == null ? "none" : page.Header.Type.ToString();
        string footer = page.Footer == null ? "none" : page.Footer.Type.ToString();

        Console.WriteLine($"Page {page.Number}: section {page.Section}, page {page.NumberInSection} of the section");
        Console.WriteLine($"  Characters {page.Start} to {page.Start + page.Length - 1}");
        Console.WriteLine($"  Page {page.Bounds}, text area {page.TextBounds}");
        Console.WriteLine($"  Header: {header}, footer: {footer}");
    }
}
```

[HeaderFooter.Type](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol/headerfooter/type-property/llms-full.txt) tells which header or footer a page shows: `FirstPageHeader` and `FirstPageFooter` on the first page, `EvenHeader` and `EvenFooter` on even pages and `Header` and `Footer` on the other pages.

### Find the Page of Text, Fields and Tables

[PageCollection.GetItem](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol/pagecollection/getitem-method/llms-full.txt) with a zero-based text position returns the page that contains the position, or `null`. The following program finds the page of a word, of every application field and of every table:

```csharp
using TXTextControl;

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

    PageCollection pages = tx.GetPages();

    // Find returns a zero-based position
    int position = tx.Find("Total");

    if (position != -1)
    {
        Console.WriteLine($"\"Total\" is on page {pages.GetItem(position).Number}");
    }

    // Start is one-based
    foreach (ApplicationField field in tx.ApplicationFields)
    {
        Page page = pages.GetItem(field.Start - 1);
        Console.WriteLine($"{field.TypeName} \"{field.Text}\": page {page?.Number}");
    }

    // A table can continue on the following pages
    foreach (Table table in tx.Tables)
    {
        int firstPage = int.MaxValue;
        int lastPage = 0;

        foreach (TableCell cell in table.Cells)
        {
            int number = pages.GetItem(cell.Start - 1).Number;
            firstPage = Math.Min(firstPage, number);
            lastPage = Math.Max(lastPage, number);
        }

        Console.WriteLine($"Table {table.ID}: pages {firstPage} to {lastPage}");
    }
}
```

The code works as follows:

- [Find](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol/servertextcontrol/find-method/llms-full.txt) returns a zero-based position, which `GetItem` takes as it is.
- [TextField.Start](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol/textfield/start-property/llms-full.txt) and [TableCell.Start](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol/tablecell/start-property/llms-full.txt) are one-based, so 1 is subtracted. Application fields are listed only if the document was loaded with `LoadSettings.ApplicationFieldFormat`, as [Fields](https://docs.textcontrol.com/tx-text-control-dotnet-server/document-processing/fields/#find-the-fields-of-all-text-parts) shows.
- The pages of the cells give the first and the last page of a table.

For a single position, [Line.Page](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol/line/page-property/llms-full.txt) returns the page number too: `tx.Lines.GetItem(position).Page`.

### Read the Text of Each Page

[Page.Select](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol/page/select-method/llms-full.txt) selects the text of a page, without the page breaks around it and without its header and footer. The following program prints the text page by page:

```csharp
using TXTextControl;

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

    foreach (Page page in tx.GetPages())
    {
        page.Select();

        Console.WriteLine($"--- Page {page.Number} ---");
        Console.WriteLine(tx.Selection.Text);
    }
}
```

After `Select`, the selection can also be saved in a format with `Selection.Save`, as [Saving and Converting Documents](https://docs.textcontrol.com/tx-text-control-dotnet-server/document-processing/saving-converting/#save-part-of-a-document) describes.

### Render Pages as SVG Images

[Page.GetImage](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol/page/getimage-method/llms-full.txt) returns a page as a string in SVG format, with the same rendering as the document in a PDF. The following program saves each page as an SVG file:

```csharp
using TXTextControl;

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

    Directory.CreateDirectory("pages");
    PageCollection pages = tx.GetPages();

    for (int i = 1; i <= pages.Count; i++)
    {
        // Text as glyph outlines, embedded images with at most 96 dpi
        string svg = pages[i].GetImage(Page.PageContent.All | Page.PageContent.GlyphOutlines, 96);
        File.WriteAllText($"pages/page-{i}.svg", svg);
    }

    Console.WriteLine($"{pages.Count} pages saved");
}
```

The code works as follows:

- The first parameter combines values of [PageContent](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol/pagecontent/enumeration/llms-full.txt), written as `Page.PageContent` in code: `Background`, `HeadersAndFooters` and `MainText` select the parts of the page, and `All` combines these three. `ScreenElements` adds the selection, control characters and helper lines.
- `GlyphOutlines` writes the characters as outlines, so the image looks the same where the font isn't installed. Without it, the SVG contains text with font information, which remains selectable.
- The second parameter is the maximum resolution of embedded images in dots per inch. Images are embedded as PNG.
- Coordinates in the SVG are in twips: a US letter page is 12240 wide and 15840 high.

Two further overloads of `GetImage` return a `System.Drawing.Imaging.Bitmap` or a `Metafile`. They need `System.Drawing.Common`, which the Classic package `TXTextControl.TextControl.ASP.SDK` depends on and the Core package doesn't; [Render Pages as Bitmaps and Multipage TIFF Files](#render-pages-as-bitmaps-and-multipage-tiff-files) shows them, and [Document Comparison](https://docs.textcontrol.com/tx-text-control-dotnet-server/document-processing/comparison/#compare-pages-as-images) uses the bitmaps.

#### Return a Page Image From ASP.NET Core

A controller action can send a page as an image that a browser shows in an `img` element, for example as a thumbnail:

```csharp
using System.Text;
using Microsoft.AspNetCore.Mvc;
using TXTextControl;

public class ThumbnailController : Controller
{
    [HttpGet]
    public IActionResult GetPageImage(int number = 1)
    {
        using (ServerTextControl tx = new ServerTextControl())
        {
            tx.Create();
            tx.Load("App_Data/sample.docx", StreamType.WordprocessingML);

            if (number < 1 || number > tx.Pages)
            {
                return NotFound();
            }

            string svg = tx.GetPages()[number].GetImage(Page.PageContent.All | Page.PageContent.GlyphOutlines, 96);
            return File(Encoding.UTF8.GetBytes(svg), "image/svg+xml");
        }
    }
}
```

The blog article [How to Generate SVG Images from Document Pages in ASP.NET Core C#](https://www.textcontrol.com/blog/2023/10/10/how-to-generate-svg-images-from-document-pages-in-aspnet-core-csharp/) embeds the images in a page as data URLs.

### Render Pages as Bitmaps and Multipage TIFF Files

A TIFF file can hold several images as frames, one for each page of a document. `Page.GetImage(int zoomFactor, Page.PageContent contents)` returns a page as a `System.Drawing.Imaging.Bitmap` in screen resolution, and the TIFF encoder of `System.Drawing` combines the bitmaps into one file. The bitmap overload needs `System.Drawing.Common`, so the program uses the Classic package `TXTextControl.TextControl.ASP.SDK`, which depends on it and runs on Windows only. On Linux, render the pages as SVG images as shown above.

```csharp
using System.Drawing;
using System.Drawing.Imaging;
using TXTextControl;

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

    // One bitmap per page, at a zoom factor of 100 percent
    List<Bitmap> bitmaps = new List<Bitmap>();

    foreach (Page page in tx.GetPages())
    {
        bitmaps.Add(page.GetImage(100, Page.PageContent.All));
    }

    SaveMultipageTiff(bitmaps, "pages.tif");
    Console.WriteLine($"{bitmaps.Count} pages saved in pages.tif");

    foreach (Bitmap bitmap in bitmaps)
    {
        bitmap.Dispose();
    }
}

static void SaveMultipageTiff(List<Bitmap> bitmaps, string fileName)
{
    ImageCodecInfo tiffCodec = ImageCodecInfo.GetImageEncoders().First(codec => codec.MimeType == "image/tiff");

    using (EncoderParameters parameters = new EncoderParameters(2))
    {
        parameters.Param[0] = new EncoderParameter(Encoder.Compression, (long)EncoderValue.CompressionLZW);

        // The first page creates the file
        parameters.Param[1] = new EncoderParameter(Encoder.SaveFlag, (long)EncoderValue.MultiFrame);
        bitmaps[0].Save(fileName, tiffCodec, parameters);

        // Every further page is added as a frame
        parameters.Param[1] = new EncoderParameter(Encoder.SaveFlag, (long)EncoderValue.FrameDimensionPage);

        for (int i = 1; i < bitmaps.Count; i++)
        {
            bitmaps[0].SaveAdd(bitmaps[i], parameters);
        }

        // Close the file
        parameters.Param[1] = new EncoderParameter(Encoder.SaveFlag, (long)EncoderValue.Flush);
        bitmaps[0].SaveAdd(parameters);
    }
}
```

The code works as follows:

- `GetImage` with a zoom factor and a `PageContent` value returns a bitmap of the page. A larger zoom factor gives a larger image.
- The first bitmap is saved with the encoder parameter `MultiFrame`, which creates a TIFF file that can hold more frames. `SaveAdd` with `FrameDimensionPage` appends each further page to it, and `SaveAdd` with `Flush` closes the file.
- `CompressionLZW` sets the compression of the frames.

### Remove Empty Pages

A page that contains only its page break or section break character has a length of 1. Such empty pages remain, for example, when a forced page break follows content that already filled a page. The following program removes them, from the last page to the first:

```csharp
using TXTextControl;

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

    int pagesBefore = tx.Pages;

    // From the last page to the first, so the pages still to check keep their numbers
    for (int number = tx.Pages; number >= 1; number--)
    {
        Page page = tx.GetPages()[number];

        if (page.Length == 1)
        {
            // Delete the break character of the empty page
            tx.Select(page.Start - 1, 1);
            tx.Selection.Text = "";
        }
    }

    Console.WriteLine($"Pages before: {pagesBefore}, after: {tx.Pages}");
    tx.Save("without-empty-pages.docx", StreamType.WordprocessingML);
}
```

To avoid empty pages in templates, start headings on a new page with the paragraph setting `PageBreakBefore` instead of a page break character, as the blog article [Document Templates Tip: Say No to Forced Page Breaks](https://www.textcontrol.com/blog/2024/05/28/document-templates-tip-say-no-to-forced-page-breaks/) explains.

### Next Steps

- [Sections](https://docs.textcontrol.com/tx-text-control-dotnet-server/document-processing/sections/llms-full.txt): set the page size, margins and orientation.
- [Headers and Footers](https://docs.textcontrol.com/tx-text-control-dotnet-server/document-processing/headers-footers/llms-full.txt): add page numbers to every page.
- [Printing](https://docs.textcontrol.com/tx-text-control-dotnet-server/document-processing/printing/llms-full.txt): print selected pages.
- [Navigation](https://docs.textcontrol.com/tx-text-control-dotnet-server/document-viewer/navigation/llms-full.txt) in the Document Viewer: show page thumbnails in the browser.

---

## Nearby

- **Previous:** [Spell Checking and Hyphenation](https://docs.textcontrol.com/tx-text-control-dotnet-server/document-processing/spell-checking/llms-full.txt)
- **Next:** [Document Comparison](https://docs.textcontrol.com/tx-text-control-dotnet-server/document-processing/comparison/llms-full.txt)
