> For the complete documentation index, see [llms.txt](https://docs.apryse.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.apryse.com/core/create/document-creation-api.md).

# Document creation API

Use the document creation API to programmatically design documents.

## Overview

Apryse's document creation framework (the `FlowDocument` API) lets you generate PDFs without positioning anything on a page. Create a strong foundation for end-to-end PDF creation workflows.

You can:

* You build a content tree with paragraphs, tables, lists, shapes, charts and floats.
* You style the elements with familiar word-processor concepts like fonts, colors, indents, justification, borders, tab stops.
* Apryse SDK paginates the tree into a PDF, breaking content across pages, flowing text around floats, and resolving page numbers.
* Sections let you mix page sizes, orientations, headers and footers within a single document.
* It's self-contained, with no external dependencies and no office application required.

This is complementary to template based generation. Use templates when a designer owns the layout in an Office file, and use the document creation API when the document structure itself is computed by your application.

{% hint style="info" %}
**Info**

This feature is available to all customers with a [Server SDK license](https://apryse.com/capabilities#Package/Feature) and the [Template add-on](https://apryse.com/capabilities#Template).
{% endhint %}

Use cases include:

* **Invoice generation at volume:** company branding, line-item tables, page numbers, payment information, metadata, and configured page size and margins.
* **Analytics and business reports:** structured content, charts or chart-like visual elements, branded headers, landscape pages, and custom page numbering.
* **Contracts and proposals:** logos, hyperlinks to terms or support, disclaimers in footers, shapes used as signature placeholders, metadata, and multi-page layout.
* Statements, dashboards, labels, and other dynamically generated documents produced from operational systems, CRMs, ERPs, billing systems, and reporting tools.
* OEM teams embedding the SDK in their own product and needing programmatic PDF generation as part of it.

## Hello world

{% tabs %}
{% tab title="C#" %}
{% code lineNumbers="true" %}

```csharp
using (FlowDocument doc = new FlowDocument())
{
    doc.SetDefaultPageSize(360, 140);      // in points
    doc.SetDefaultMargins(36, 36, 36, 36);

    doc.AddParagraph("Hello World! This document was created with the " +
                     "FlowDocument API and paginated into a PDF.");

    using (PDFDoc pdf = doc.PaginateToPDF())
        pdf.Save(output_path + "hello.pdf", SDFDoc.SaveOptions.e_linearized);
}
```

{% endcode %}
{% endtab %}

{% tab title="C++" %}
{% code lineNumbers="true" %}

```cpp
FlowDocument doc;
doc.SetDefaultPageSize(360, 140);      // in points
doc.SetDefaultMargins(36, 36, 36, 36);

doc.AddParagraph("Hello World! This document was created with the "
                 "FlowDocument API and paginated into a PDF.");

PDFDoc pdf = doc.PaginateToPDF();
pdf.Save(output_path + "hello.pdf", SDF::SDFDoc::e_linearized, NULL);
```

{% endcode %}
{% endtab %}

{% tab title="Go" %}
{% code lineNumbers="true" %}

```go
doc := NewFlowDocument()
doc.SetDefaultPageSize(360, 140)      // in points
doc.SetDefaultMargins(36, 36, 36, 36)

doc.AddParagraph("Hello World! This document was created with the " +
    "FlowDocument API and paginated into a PDF.")

pdf := doc.PaginateToPDF()
pdf.Save(outputPath+"hello.pdf", uint(SDFDocE_linearized))
```

{% endcode %}
{% endtab %}

{% tab title="Java" %}
{% code lineNumbers="true" %}

```java
FlowDocument doc = new FlowDocument();
doc.setDefaultPageSize(360, 140);      // in points
doc.setDefaultMargins(36, 36, 36, 36);

doc.addParagraph("Hello World! This document was created with the "
        + "FlowDocument API and paginated into a PDF.");

PDFDoc pdf = doc.paginateToPDF();
pdf.save(output_path + "hello.pdf", SDFDoc.SaveMode.LINEARIZED, null);
```

{% endcode %}
{% endtab %}

{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```javascript
const doc = await PDFNet.FlowDocument.create();
doc.setDefaultPageSize(360, 140);      // in points
doc.setDefaultMargins(36, 36, 36, 36);

await doc.addParagraphWithText('Hello World! This document was created with the ' +
    'FlowDocument API and paginated into a PDF.');

const pdf = await doc.paginateToPDF();
await pdf.save(outputPath + 'hello.pdf', PDFNet.SDFDoc.SaveOptions.e_linearized);
```

{% endcode %}
{% endtab %}

{% tab title="Obj-C" %}
{% code lineNumbers="true" %}

```objectivec
PTFlowDocument* doc = [[PTFlowDocument alloc] init];
[doc SetDefaultPageSize:360 height:140];      // in points
[doc SetDefaultMargins:36 top:36 right:36 bottom:36];

[doc AddParagraphWithText:@"Hello World! This document was created with the "
                           "FlowDocument API and paginated into a PDF."];

PTPDFDoc* pdf = [doc PaginateToPDF];
[pdf SaveToFile:[output_path stringByAppendingString:@"hello.pdf"]
          flags:e_ptlinearized];
```

{% endcode %}
{% endtab %}

{% tab title="PHP" %}
{% code lineNumbers="true" %}

```php
$doc = new FlowDocument();
$doc->SetDefaultPageSize(360, 140);      // in points
$doc->SetDefaultMargins(36, 36, 36, 36);

$doc->AddParagraph("Hello World! This document was created with the " .
                   "FlowDocument API and paginated into a PDF.");

$pdf = $doc->PaginateToPDF();
$pdf->Save($output_path."hello.pdf", SDFDoc::e_linearized);
```

{% endcode %}
{% endtab %}

{% tab title="Python" %}
{% code lineNumbers="true" %}

```python
doc = FlowDocument()
doc.SetDefaultPageSize(360, 140)      # in points
doc.SetDefaultMargins(36, 36, 36, 36)

doc.AddParagraph("Hello World! This document was created with the "
                 "FlowDocument API and paginated into a PDF.")

pdf = doc.PaginateToPDF()
pdf.Save(output_path + "hello.pdf", SDFDoc.e_linearized)
```

{% endcode %}
{% endtab %}

{% tab title="Ruby" %}
{% code lineNumbers="true" %}

```ruby
doc = FlowDocument.new()
doc.SetDefaultPageSize(360, 140)      # in points
doc.SetDefaultMargins(36, 36, 36, 36)

doc.AddParagraph("Hello World! This document was created with the " +
                 "FlowDocument API and paginated into a PDF.")

pdf = doc.PaginateToPDF()
pdf.Save(output_path + "hello.pdf", SDFDoc::E_linearized)
```

{% endcode %}
{% endtab %}
{% endtabs %}

<figure><img src="https://3779731113-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fziw3GiL98Xfj63F3He8h%2Fuploads%2FFFEeHOHjpmvuNf3H3aj5%2Fhello_world.png?alt=media&amp;token=7fd73b2a-b6ef-4eba-8b84-52e595edf507" alt="Hello world examples with text saying &#x27;hello world.&#x27;"><figcaption></figcaption></figure>

That is the whole workflow: create a `FlowDocument`, append content, call `PaginateToPDF`, and save the resulting `PDFDoc`.

Because the result is an ordinary `PDFDoc`, the rest of the SDK applies to it as usual. You can encrypt it, append other documents, or set its [document metadata](/core/create/document-creation-api/pdf-metadata.md) before saving.

## Flow layout vs. fixed layout

The `ElementBuilder` / `ElementWriter` API is a fixed layout API. You decide the exact coordinates of every glyph, line and image. The document creation API is a flow layout API:

|               | **Fixed layout (`ElementBuilder`)** | **Flow layout (`FlowDocument`)**        |
| ------------- | ----------------------------------- | --------------------------------------- |
| You specify   | Absolute positions                  | Logical structure                       |
| Pagination    | Manual                              | Automatic                               |
| Text wrapping | Manual                              | Automatic                               |
| Page numbers  | Computed by you                     | Dynamic elements                        |
| Best for      | Precise stamping, overlays          | Reports, invoices, letters, newsletters |

## The content tree

Everything you add to a document is a `ContentElement`. Elements that can contain other elements are `ContentNode`s, and the subset of nodes that accept paragraphs, tables and lists are `ContentContainer`s.

```mermaid
---
config:
  class:
    hideEmptyMembersBox: true
---
classDiagram
    class ContentElement
    class ContentNode
    class ContentContainer
    class FlowDocument
    class Section
    class Float
    class Paragraph
    class Table
    class TableRow
    class TableCell
    class List
    class ListItem
    class Shape
    class Chart
    class TextRun
    class PageNumber

    ContentElement <|-- ContentNode
    ContentElement <|-- TextRun
    ContentElement <|-- PageNumber
    ContentNode <|-- ContentContainer
    ContentNode <|-- Paragraph
    ContentNode <|-- Table
    ContentNode <|-- TableRow
    ContentNode <|-- List
    ContentNode <|-- ListItem
    ContentNode <|-- Shape
    ContentNode <|-- Chart
    ContentContainer <|-- FlowDocument
    ContentContainer <|-- Section
    ContentContainer <|-- Float
    ContentContainer <|-- TableCell
```

Key relationships to remember:

* A `FlowDocument` is itself a `ContentContainer`. Content added directly to it goes into the **current** section.
* A `TableCell` is a full `ContentContainer`, so cells can hold paragraphs, nested tables, and lists.
* A `Shape` exposes a `ContentContainer` through `GetTextBox`, which turns any shape into a text box.
* A `Float` is a `ContentContainer` that is lifted out of the main flow.

{% hint style="info" %}
**Info**

**Class names per language.** In PHP, the float and list classes are named `FloatContainer` and `ListContainer` because `float` and `list` are reserved words. In Ruby, the float class is `FloatContainer`, while the list class stays `List`. In Objective-C, every class carries a `PT` prefix, for example, `PTFlowDocument` and `PTParagraph`. In JavaScript, the classes live on the `PDFNet` namespace, for example, `PDFNet.FlowDocument`.
{% endhint %}

## Styling

Two styling surfaces exist:

* **`TextStyledElement`** : Character level styling (font face, size, bold, italic, text color, background color). Retrieved with `GetTextStyledElement` from any content element. Setting it on a node applies the style to the content added to that node.
* **Element specific setters:** Paragraph indents, spacing and justification, cell borders and alignment, shape geometry and fill, among others.

{% tabs %}
{% tab title="C#" %}
{% code lineNumbers="true" %}

```csharp
Paragraph heading = doc.AddParagraph("Quarterly Revenue Report 2025");

TextStyledElement style = heading.GetTextStyledElement();
style.SetFontSize(22);
style.SetBold(true);
style.SetTextColor(0, 51, 102);

heading.SetSpaceBefore(12);
heading.SetSpaceAfter(6);
```

{% endcode %}
{% endtab %}

{% tab title="C++" %}
{% code lineNumbers="true" %}

```cpp
Paragraph heading = doc.AddParagraph("Quarterly Revenue Report 2025");

TextStyledElement style = heading.GetTextStyledElement();
style.SetFontSize(22);
style.SetBold(true);
style.SetTextColor(0, 51, 102);

heading.SetSpaceBefore(12);
heading.SetSpaceAfter(6);
```

{% endcode %}
{% endtab %}

{% tab title="Go" %}
{% code lineNumbers="true" %}

```go
heading := doc.AddParagraph("Quarterly Revenue Report 2025")

style := heading.GetTextStyledElement()
style.SetFontSize(22)
style.SetBold(true)
style.SetTextColor(0, 51, 102)

heading.SetSpaceBefore(12)
heading.SetSpaceAfter(6)
```

{% endcode %}
{% endtab %}

{% tab title="Java" %}
{% code lineNumbers="true" %}

```java
Paragraph heading = doc.addParagraph("Quarterly Revenue Report 2025");

TextStyledElement style = heading.getTextStyledElement();
style.setFontSize(22);
style.setBold(true);
style.setTextColor(0, 51, 102);

heading.setSpaceBefore(12);
heading.setSpaceAfter(6);
```

{% endcode %}
{% endtab %}

{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```javascript
const heading = await doc.addParagraphWithText('Quarterly Revenue Report 2025');

const style = await heading.getTextStyledElement();
style.setFontSize(22);
style.setBold(true);
style.setTextColor(0, 51, 102);

heading.setSpaceBefore(12);
heading.setSpaceAfter(6);
```

{% endcode %}
{% endtab %}

{% tab title="Obj-C" %}
{% code lineNumbers="true" %}

```objectivec
PTParagraph* heading = [doc AddParagraphWithText:@"Quarterly Revenue Report 2025"];

PTTextStyledElement* style = [heading GetTextStyledElement];
[style SetFontSize:22];
[style SetBold:YES];
[style SetTextColor:0 green:51 blue:102];

[heading SetSpaceBefore:12];
[heading SetSpaceAfter:6];
```

{% endcode %}
{% endtab %}

{% tab title="PHP" %}
{% code lineNumbers="true" %}

```php
$heading = $doc->AddParagraph("Quarterly Revenue Report 2025");

$style = $heading->GetTextStyledElement();
$style->SetFontSize(22);
$style->SetBold(true);
$style->SetTextColor(0, 51, 102);

$heading->SetSpaceBefore(12);
$heading->SetSpaceAfter(6);
```

{% endcode %}
{% endtab %}

{% tab title="Python" %}
{% code lineNumbers="true" %}

```python
heading = doc.AddParagraph("Quarterly Revenue Report 2025")

style = heading.GetTextStyledElement()
style.SetFontSize(22)
style.SetBold(True)
style.SetTextColor(0, 51, 102)

heading.SetSpaceBefore(12)
heading.SetSpaceAfter(6)
```

{% endcode %}
{% endtab %}

{% tab title="Ruby" %}
{% code lineNumbers="true" %}

```ruby
heading = doc.AddParagraph("Quarterly Revenue Report 2025")

style = heading.GetTextStyledElement()
style.SetFontSize(22)
style.SetBold(true)
style.SetTextColor(0, 51, 102)

heading.SetSpaceBefore(12)
heading.SetSpaceAfter(6)
```

{% endcode %}
{% endtab %}
{% endtabs %}

## Conventions

* **All dimensions are in points** (1/72 inch). US Letter is `612 x 792`, A4 is `595 x 842`.
* **Colors are 8-bit RGB triplets** in the range `[0..255]`.
* **Fonts are resolved by name** through the platform font providers. Prefer widely available families, or ship the fonts you need, so the output is identical across machines.
* **Build, then paginate.** `PaginateToPDF` is the only expensive step; calling it more than once for the same document repeats the whole layout.
* **Generated PDFs carry structure tags,** so the output is accessible by construction.
* **Enum spelling differs per language.** C++ and Python use `Paragraph.e_text_justify_center`, C# uses `Paragraph.TextJustification.e_center`, Java and JavaScript use `Paragraph.TextJustification.e_text_justify_center`, Go uses `ParagraphE_text_justify_center`, PHP uses `Paragraph::e_text_justify_center`, Ruby uses `Paragraph::E_text_justify_center`, and Objective-C uses `e_ptpara_text_justify_center`.
* **Every JavaScript call is asynchronous.** The Node.js and WebViewer bindings return promises, so each call has to be awaited, including the property getters such as `getTextStyledElement`.
* **Objective-C renames overloads.** `AddParagraph(text)` becomes `AddParagraphWithText:`, `AddImage(w, h, path)` becomes `AddImageWithPath:height:image_path:`, and `SetNumberFormat(format, suffix, cascade)` becomes `SetNumberFormatWithSuffix:suffix:cascade:`.

## Document creation options

There are several options available to use when programmatically creating a document.

<table data-search="false"><thead><tr><th>Page</th><th>Contents</th></tr></thead><tbody><tr><td><a href="/core/create/document-creation-api/paragraphs-and-text.md">Paragraphs and text</a></td><td>Text runs, character styling, justification, indents, tab stops, hyperlinks.</td></tr><tr><td><a href="/core/create/document-creation-api/lists.md">Lists</a></td><td>Ordered lists, nesting, number formats, label styling.</td></tr><tr><td><a href="/core/create/document-creation-api/tables.md">Tables</a></td><td>Rows and cells, borders, shading, alignment, cell merging, nested content.</td></tr><tr><td><a href="/core/create/document-creation-api/images-and-shapes.md">Images and shapes</a></td><td>Inline images, shape types, text boxes, rotation and flipping.</td></tr><tr><td><a href="/core/create/document-creation-api/floats.md">Floats</a></td><td>Positioning content outside the flow and wrapping text around it.</td></tr><tr><td><a href="/core/create/document-creation-api/sections-headers-and-footers.md">Sections, headers and footers</a></td><td>Page setup per section, headers and footers, dynamic page numbers.</td></tr><tr><td><a href="/core/create/document-creation-api/charts.md">Charts</a></td><td>Adding charts and sizing them.</td></tr><tr><td><a href="/core/create/document-creation-api/chart-definition-json.md">Chart definition JSON</a></td><td>Full specification of the chart definition string.</td></tr><tr><td><a href="/core/create/document-creation-api/traverse-the-content-tree.md">Traversing the content tree</a></td><td>Inspecting and post-processing content before pagination.</td></tr><tr><td><a href="/core/create/document-creation-api/pdf-metadata.md">PDF Metadata</a></td><td>Setting the title, author, subject, and keywords of the generated PDF.</td></tr></tbody></table>

## Further Reading

[Template generation](/appian/template-generation/template-generation.md) for generating documents from Office templates and JSON data instead.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.apryse.com/core/create/document-creation-api.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
