1. ACF-Plugin Functions for PDF Documents

  1. ACF-Plugin Functions for PDF Documents
    1. Introduction
  2. Commands and Functions
    1. Functions for Creating and Managing PDF Files
      1. pdf_create
      2. pdf_close
      3. pdf_newPage
    2. Page Setup and Metadata
      1. pdf_setPageOptions
      2. pdf_setPageHeader
      3. pdf_setPageFooter
    3. Font Handling
      1. pdf_setFont
    4. Text Commands
      1. pdf_drawText
      2. pdf_drawTextBox
      3. Wrapping Text Around Floating Objects
      4. Markdown Text in Tables
    5. Drawing Commands
      1. pdf_drawRect
      2. pdf_drawOval
      3. pdf_drawCircle
      4. pdf_drawLine
      5. pdf_drawPolyline
    6. Colors
    7. Images
      1. pdf_addImage
      2. pdf_placeImage
    8. Tables
      1. pdf_drawTable
      2. Cell Borders
    9. Streamed Tables
      1. pdf_drawTableStart
      2. pdf_drawTableRows
      3. pdf_drawTableEnd
    10. Information Functions
      1. pdf_getCurrentPage
      2. pdf_getCursorPos
      3. pdf_getUnplacedRest
    11. Complete Example
    12. Notes

1.1. Introduction

The ACF-Plugin introduces PDF generation functions in plugin version 1.8.0.3. These functions create PDF documents directly from ACF code, with support for text, Markdown in table columns, tables, streamed tables, headers, footers, images, drawing primitives, page breaks, metadata, and embedded fonts.

Plugin version 1.8.0.4 extends the PDF text layout engine with rotated text, stretch alignment, paragraph spacing, multi-column text boxes, text wrapping around floating objects, locale-aware line breaking, and retrieval of unplaced text from fixed-height boxes.

Native FileMaker layouts can already print and save PDFs, but this functionality gives an alternate route for generating PDF documents directly. It is useful for reports that contain mixed content, conditional sections, data from several sources, streamed rows, dynamic tables, or layouts that are difficult to maintain with traditional layout printing and sliding objects.

The PDF commands are designed for report generation. A document is created with pdf_create, drawing commands are added to the document, and the document is written with pdf_close or close_pdf.

PDF coordinates are measured in points. The origin is the upper-left corner of the page. The default page size is A4 portrait, 595 x 842 points.

2. Commands and Functions

2.1. Functions for Creating and Managing PDF Files

2.1.1. pdf_create

Example:

int pdf = pdf_create("~/Desktop/MyReport.pdf");

2.1.2. pdf_close

Example:

pdf_close(pdf);

2.1.3. pdf_newPage

Example:

pdf_drawText(pdf, "First page", JSON("x", 50, "width", 495));
pdf_newPage(pdf);
pdf_drawText(pdf, "Second page", JSON("x", 50, "width", 495));

2.2. Page Setup and Metadata

2.2.1. pdf_setPageOptions

Common options:

Example:

pdf_setPageOptions(pdf, JSON(
    "title", "Order confirmation",
    "author", "ACF report",
    "pageSize", "A4",
    "orientation", "landscape",
    "topMargin", 50,
    "bottomMargin", 50
));

Supported named page sizes:

Name Size PDF points
A0 841 x 1189 mm 2383.94 x 3370.39
A1 594 x 841 mm 1683.78 x 2383.94
A2 420 x 594 mm 1190.55 x 1683.78
A3 297 x 420 mm 841.89 x 1190.55
A4 210 x 297 mm 595.28 x 841.89
A5 148 x 210 mm 419.53 x 595.28
A6 105 x 148 mm 297.64 x 419.53
A7 74 x 105 mm 209.76 x 297.64
A8 52 x 74 mm 147.40 x 209.76
A9 37 x 52 mm 104.88 x 147.40
Letter, US Letter 8.5 x 11 in 612 x 792
Legal, US Legal 8.5 x 14 in 612 x 1008
Tabloid 11 x 17 in 792 x 1224
Ledger 17 x 11 in 1224 x 792
Executive 7.25 x 10.5 in 522 x 756
Statement, Half Letter 5.5 x 8.5 in 396 x 612

2.2.2. pdf_setPageHeader

Header object fields:

Example:

pdf_setPageHeader(pdf, JSON(
    "height", 76,
    "bodyGap", 20,
    "commands", JSONarray(
        JSON("op", "rect", "x", 0, "y", 0, "width", 595, "height", 76, "mode", "fill"),
        JSON("op", "text", "x", 50, "y", 32, "size", 18, "value", "Sales report")
    )
), "all");

2.2.3. pdf_setPageFooter

Example:

pdf_setPageFooter(pdf, JSON(
    "height", 42,
    "commands", JSONarray(
        JSON("op", "line", "x1", 50, "y1", 0, "x2", 545, "y2", 0,
             "lineStyle", "thin", "color", "#B8B8B8"),
        JSON("op", "text", "x", 50, "y", 24, "size", 9,
             "value", "Generated by ACF"),
        JSON("op", "text", "x", 50, "y", 24, "width", 495, "size", 9,
             "align", "right", "value", "Page {page} of {pages}")
    )
), "all");

2.3. Font Handling

2.3.1. pdf_setFont

Common font options:

Example:

pdf_setFont(pdf, JSON("name", "Arial", "embed", true));
pdf_drawText(pdf, "ÆØÅ éüñ €", JSON("x", 50, "width", 495, "size", 12));

2.4. Text Commands

2.4.1. pdf_drawText

Common options:

Example:

pdf_drawText(pdf,
    "This text wraps naturally inside the available width.",
    JSON("x", 50, "width", 495, "size", 11, "lineHeight", 15)
);

2.4.2. pdf_drawTextBox

Common options:

Example:

pdf_drawTextBox(pdf,
    "Customer address line 1\nCustomer address line 2",
    50, 160, 220, 70,
    JSON("size", 10, "lineHeight", 13)
);

Stretch alignment distributes extra spacing between words on each non-final line in a paragraph. The final line of each paragraph keeps natural spacing.

Two-column article style example:

pdf_drawTextBox(pdf, bodyText, 50, 260, 495, 300, JSON(
    "size", 10.5,
    "lineHeight", 14,
    "align", "stretch",
    "paragraphSpacing", 7,
    "columns", 2,
    "gutter", 14,
    "balanceColumns", true
));

If a fixed-height box cannot place all text, the unplaced part can be retrieved with pdf_getUnplacedRest(...).

2.4.3. Wrapping Text Around Floating Objects

Plain flowed pdf_drawText(...) and pdf_drawTextBox(...) can wrap around floating objects by using the wrap option. The wrap value may be one object or an array of objects.

Supported floating object types:

Common wrap object fields:

Image wrap example:

pdf_drawText(pdf, productText, JSON(
    "x", 56,
    "width", 483,
    "size", 11,
    "lineHeight", 14,
    "wrap", JSON(
        "type", "image",
        "imageId", productImage,
        "align", "right",
        "width", 150,
        "height", 82,
        "margin", JSON("t", 0, "r", 0, "b", 6, "l", 10)
    )
));

Multiple wrap objects can be supplied with JSONarray(...), for example an image with a small table below it:

pdf_drawText(pdf, productText, JSON(
    "x", 56,
    "width", 483,
    "size", 11,
    "lineHeight", 14,
    "wrap", JSONarray(
        JSON("type", "image", "imageId", productImage, "align", "right",
             "width", 150, "height", 82, "margin", JSON("t", 0, "l", 10, "b", 6)),
        JSON("type", "table", "align", "right", "width", 150,
             "margin", JSON("t", 4, "l", 10, "b", 10),
             "columns", JSONarray(
                 JSON("key", "label", "width", 66),
                 JSON("key", "value", "width", 84, "align", "right")
             ),
             "values", JSONarray(
                 JSON("label", "SKU", "value", "PNG-140"),
                 JSON("label", "Stock", "value", "42")
             ),
             "tableProperties", JSON("showHeader", false, "fontSize", 8,
                 "rowHeight", 14, "padding", 2))
    )
));

When the floating object has no explicit y, margin.t controls how it lines up with the current text flow. Use margin.t set to 0 when the image should start at the top of the text.

2.4.4. Markdown Text in Tables

Markdown text is supported in table columns by setting "markdown", true on a column.

Example column:

JSON("key", "comment", "title", "Comment", "width", 300, "markdown", true)

2.5. Drawing Commands

2.5.1. pdf_drawRect

Example:

pdf_drawRect(pdf, 50, 120, 200, 80, JSON("mode", "stroke"));

2.5.2. pdf_drawOval

Example:

pdf_drawOval(pdf, 50, 220, 120, 60, JSON("mode", "stroke"));

2.5.3. pdf_drawCircle

Example:

pdf_drawCircle(pdf, 120, 340, 30, JSON("mode", "stroke"));

2.5.4. pdf_drawLine

Example:

pdf_drawLine(pdf, 50, 400, 545, 400,
    JSON("lineStyle", "thin", "color", "#606060")
);

2.5.5. pdf_drawPolyline

Example:

pdf_drawPolyline(pdf,
    JSON("points", JSONarray(50, 460, 120, 480, 180, 450)),
    JSON("lineStyle", "medium")
);

2.6. Colors

Colors can be specified as:

Example:

pdf_drawLine(pdf, 50, 80, 545, 80, JSON("color", "#A8B4C0"));

2.7. Images

2.7.1. pdf_addImage

Example:

int logoId = pdf_addImage(pdf, logoContainer);

2.7.2. pdf_placeImage

Example:

int logoId = pdf_addImage(pdf, logoContainer);
pdf_placeImage(pdf, logoId, 400, 40, 140, 60, JSON());

2.8. Tables

2.8.1. pdf_drawTable

Table object fields:

Column fields:

Table property fields:

Example:

json tableDef = JSON(
    "x", 50,
    "width", 495,
    "columns", JSONarray(
        JSON("key", "item", "title", "Item", "width", 320, "resizeMode", "expand"),
        JSON("key", "qty", "title", "Qty", "width", 55, "align", "right"),
        JSON("key", "amount", "title", "Amount", "width", 120, "align", "right")
    ),
    "values", JSONarray(
        JSON("item", "Curtains", "qty", "2", "amount", "25 000,00"),
        JSON("item", "Mounting", "qty", "1", "amount", "6 250,00")
    ),
    "tableProperties", JSON(
        "fontSize", 10,
        "rowHeight", 24,
        "alternateRows", true,
        "headerFill", "#DCEAF7",
        "alternateFill", "#EEF6FF",
        "borderColor", "#A8B4C0"
    ),
    "footer", JSON(
        "cells", JSONarray(
            JSON("text", "Total", "colSpan", 2, "align", "right"),
            JSON("text", "31 250,00", "align", "right")
        )
    )
);

pdf_drawTable(pdf, tableDef);

2.8.2. Cell Borders

Cells can override table borders by using a cell object with text or value and a border property.

Border can be a side string such as "tb" or an object:

JSON("text", "**Sum total:**", "border", JSON(
    "sides", "tb",
    "lineStyle", "thin",
    "color", "#404040",
    "width", 0.5
))

Supported side letters are:

2.9. Streamed Tables

For long reports, a table can be started, rows can be added in chunks, and the table can be ended later. This is useful when the PDF is created by several ACF calls or while looping records in FileMaker.

The odd/even row state is preserved between pdf_drawTableRows calls, so alternating row colors continue correctly across chunks.

2.9.1. pdf_drawTableStart

2.9.2. pdf_drawTableRows

2.9.3. pdf_drawTableEnd

Example:

json tableDef = JSON(
    "x", 50,
    "width", 495,
    "columns", JSONarray(
        JSON("key", "nr", "title", "#", "width", 42, "align", "right"),
        JSON("key", "text", "title", "Text", "width", 378, "resizeMode", "expand"),
        JSON("key", "amount", "title", "Amount", "width", 75, "align", "right")
    ),
    "tableProperties", JSON(
        "fontSize", 10,
        "rowHeight", 24,
        "headerHeight", 26,
        "alternateRows", true,
        "alternateFill", "#EEF6FF"
    ),
    "continuationNote", "Continues on the next page"
);

pdf_drawTableStart(pdf, tableDef);
pdf_drawTableRows(pdf, JSONarray(
    JSON("nr", "1", "text", "First streamed row", "amount", "100,00"),
    JSON("nr", "2", "text", "Second streamed row", "amount", "200,00")
));
pdf_drawTableRows(pdf, JSONarray(
    JSON("nr", "3", "text", "Third streamed row", "amount", "300,00")
));
pdf_drawTableEnd(pdf, JSON(
    "footer", JSON(
        "cells", JSONarray(
            JSON("text", "Total rows", "colSpan", 2, "align", "right"),
            JSON("text", "3", "align", "right")
        )
    )
));

2.10. Information Functions

2.10.1. pdf_getCurrentPage

Example:

int pageNo = pdf_getCurrentPage(pdf);

2.10.2. pdf_getCursorPos

Example:

float y = pdf_getCursorPos(pdf);

2.10.3. pdf_getUnplacedRest

Example:

pdf_drawTextBox(pdf, articleText, 50, 180, 495, 220, JSON(
    "size", 10.5,
    "lineHeight", 14,
    "columns", 2,
    "gutter", 14,
    "align", "stretch"
));

string rest = pdf_getUnplacedRest(pdf);
if (rest != "") then
    pdf_newPage(pdf);
    pdf_drawTextBox(pdf, rest, 50, 80, 495, 680, JSON(
        "size", 10.5,
        "lineHeight", 14,
        "columns", 2,
        "gutter", 14,
        "align", "stretch"
    ));
end if

2.11. Complete Example

package pdfExample "PDF example";

function PdfExample(container logo)
    string outPath = "~/Desktop/acf-pdf-example.pdf";
    int pdf = pdf_create(outPath);

    pdf_setPageOptions(pdf, JSON(
        "title", "ACF PDF example",
        "author", "ACF Plugin",
        "topMargin", 50,
        "bottomMargin", 50
    ));

    pdf_setPageHeader(pdf, JSON(
        "height", 76,
        "bodyGap", 20,
        "commands", JSONarray(
            JSON("op", "setFillColor", "color", "#E8EEF5"),
            JSON("op", "rect", "x", 0, "y", 0, "width", 595, "height", 76, "mode", "fill"),
            JSON("op", "setFillColor", "color", "#1A2433"),
            JSON("op", "text", "x", 50, "y", 32, "size", 18, "value", "PDF report")
        )
    ), "all");

    pdf_setPageFooter(pdf, JSON(
        "height", 42,
        "commands", JSONarray(
            JSON("op", "line", "x1", 50, "y1", 0, "x2", 545, "y2", 0, "color", "#B8B8B8"),
            JSON("op", "text", "x", 50, "y", 24, "size", 9, "value", "Generated by ACF"),
            JSON("op", "text", "x", 50, "y", 24, "width", 495, "size", 9,
                 "align", "right", "value", "Page {page} of {pages}")
        )
    ), "all");

    pdf_setFont(pdf, JSON("name", "Arial", "embed", true));

    int logoId = pdf_addImage(pdf, logo);
    pdf_placeImage(pdf, logoId, 405, 96, 140, 60, JSON());

    pdf_drawText(pdf,
        "This text is drawn in the body flow area. If the y value is omitted, the current cursor is used.",
        JSON("x", 50, "width", 330, "size", 11, "lineHeight", 15)
    );

    pdf_drawTable(pdf, JSON(
        "x", 50,
        "width", 495,
        "paddingTop", 16,
        "columns", JSONarray(
            JSON("key", "description", "title", "Description", "width", 320, "resizeMode", "expand"),
            JSON("key", "qty", "title", "Qty", "width", 55, "align", "right"),
            JSON("key", "amount", "title", "Amount", "width", 120, "align", "right")
        ),
        "values", JSONarray(
            JSON("description", "Product A", "qty", "2", "amount", "10 000,00"),
            JSON("description", "Product B", "qty", "1", "amount", "5 000,00")
        ),
        "tableProperties", JSON(
            "fontSize", 10,
            "rowHeight", 24,
            "alternateRows", true,
            "headerFill", "#DCEAF7",
            "alternateFill", "#EEF6FF",
            "borderColor", "#A8B4C0"
        ),
        "footer", JSON(
            "cells", JSONarray(
                JSON("text", "Total", "colSpan", 2, "align", "right"),
                JSON("text", "15 000,00", "align", "right")
            )
        )
    ));

    pdf_newPage(pdf);
    pdf_drawText(pdf, "Manual page break created this page.", JSON("x", 50, "width", 495));

    pdf_close(pdf);
    return outPath;
end

2.12. Notes