1. ACF Plugin for Adobe InDesign

ACF Plugin for Adobe InDesign brings the same ACF language and compiled package format used by the FileMaker and 4D plugins into a page-layout environment. An ACF package can retrieve editorial or product data, present workflow controls, and return structured instructions that InDesign applies to the active document.

  1. ACF Plugin for Adobe InDesign
    1. How the InDesign plugin is different
    2. The package lifecycle
    3. The package startup function
    4. The package menu function
      1. Menu-definition fields
      2. Menu item fields
    5. Menu action functions
    6. Return values control InDesign
      1. No further action
      2. Show a message
      3. Reload compiled packages
      4. Perform document operations
    7. Floating palettes
    8. A minimal complete package
    9. Designing portable packages
    10. Development workflow
    11. SVG image compatibility
    12. Related reference material

The ACF language is the same across all ACF Plugin host versions:

The syntax, compiler, data types, package system, JSON and XML support, filesystem functions, HTTP functions, SQLite support, cryptography, and other common runtime features are shared. Refer to the rest of this reference manual for the language itself and for common library functions.

What changes between the plugins is how the host starts an ACF function and what host-specific services are available while it runs.

One ACF language and three host execution models

1.1. How the InDesign plugin is different

FileMaker and 4D are programming environments. Their native scripts or methods explicitly invoke an ACF function:

FileMaker calculation or script → ACF_Run(...)
4D method                      → ACF_Run* function

InDesign is primarily a creative layout application. It does not provide the same general-purpose application scripting environment for this integration. Instead, the ACF InDesign plugin owns the connection between InDesign events and compiled ACF packages:

InDesign menu or palette event
    → ACF package function
    → typed ACF return value
    → native InDesign action
Host What starts the ACF function? Typical result
FileMaker A FileMaker script or calculation calls ACF_Run. Data returned to the FileMaker calculation.
4D A 4D method calls one of the ACF_Run* functions. Data returned to the 4D method.
InDesign The user selects a package menu command or interacts with a floating palette. JSON interpreted as document, palette, message, or package actions.

An InDesign ACF function can still do ordinary computational work. It can call an API, parse XML, query SQLite, read files, open an ACF dialog, or use shared package helpers. The important difference is that changing an InDesign document is requested through the return contract rather than by calling FileMaker or 4D host functions.

1.2. The package lifecycle

InDesign loads compiled .dsComp packages. Source .acf files are compiled by the host-independent ACFcompiler, so one source package can contain shared functions for several ACF hosts.

During startup or Reload Packages, the plugin:

  1. Scans its configured package locations for compiled packages.
  2. Installs new or changed .dsComp packages.
  3. Skips packages whose compiled hash is unchanged.
  4. Runs each optional package startup function.
  5. Runs each optional package menu function.
  6. Validates the returned menu JSON.
  7. Creates native InDesign menu actions and maps them to ACF functions.

The development command is located at:

Plug-in-moduler
└── ACF
    └── Reload Packages

Reloading makes it possible to compile and test changed ACF packages without restarting InDesign. Changes to the native plugin itself still require rebuilding, reinstalling, and restarting InDesign.

ACF package lifecycle and InDesign action flow

1.3. The package startup function

A package can define an optional, public, zero-parameter startup function:

function <package_name>_startup ()
    // Initialize package state.
    return "OK";
end

For a package named NewsDesk, the function is:

function NewsDesk_startup ()
    return "OK";
end

The startup function runs after the package has been installed and before its menu is discovered. It is suitable for initialization that must happen before menu labels or actions are built, for example:

The startup return value is not interpreted as an InDesign action. Runtime exceptions are caught at the plugin boundary and written to the InDesign ACF log.

Because startup functions run again during Reload Packages, they should be safe to call more than once.

1.4. The package menu function

A package publishes menu commands by defining a public, zero-parameter function named:

<package_name>_menu()

It returns a native ACF JSON object. Helper functions in the package do not become menu commands unless they are referenced by this JSON.

package NewsDesk "Editorial tools for InDesign";

function NewsDesk_menu ()
    return JSON(
        "title", _("News Desk"),
        "MenuLocation", "TOP",
        "items", JSONarray(
            JSON(
                "title", _("Open Article Palette"),
                "function", "NewsDesk_open_palette"
            ),
            JSON(
                "title", _("Place Selected Article"),
                "function", "NewsDesk_place_article",
                "enabledWhen", "documentOpen"
            )
        )
    );
end

1.4.1. Menu-definition fields

Field Required Type Meaning
title Yes string Displayed package menu title.
MenuLocation No string STD or TOP. Defaults to STD.
items Yes array Ordered package menu commands.
version No integer Menu schema version. Defaults to version 1.

MenuLocation controls where the package menu appears:

"MenuLocation": "STD"

STD places the package below InDesign's Plug-in-moduler menu:

Plug-in-moduler
└── News Desk
    ├── Open Article Palette
    └── Place Selected Article
"MenuLocation": "TOP"

TOP creates a separate item in the main InDesign menu bar:

News Desk
├── Open Article Palette
└── Place Selected Article

Use TOP for a central workflow package that users access frequently. Use the default STD location for utilities and packages that should remain grouped with other plugin modules.

1.4.2. Menu item fields

Field Required Type Meaning
title Yes string Displayed command label.
function Yes string ACF function to invoke in the same package namespace.
enabledWhen No string Native enablement rule. Defaults to always.

Contract values for enabledWhen are always, documentOpen, layoutView, selection, textSelection, and pageItemSelection.

Menu labels can use _() so the language package can collect them in messages.pot and apply the selected translation before menus are built.

1.5. Menu action functions

A function referenced by the menu JSON can have either signature:

function NewsDesk_action ()

or:

function NewsDesk_action (JSON info)

The zero-parameter form is useful when the action does not need document context. The JSON form receives a fresh snapshot when the user invokes the action.

The information object can contain:

function NewsDesk_place_article (JSON info)

    float availableWidth = info["page.placementArea.width"];
    float availableHeight = info["page.placementArea.height"];
    string documentPath = string(info["document.path"]);

    // Retrieve and prepare the article here.

    return JSON(
        "type", "indesign.message",
        "version", 1,
        "message", format(
            "Available page area: %.1f × %.1f pt",
            availableWidth,
            availableHeight
        )
    );
end

All measurements in the action information object are points, independent of the user's current ruler settings.

1.6. Return values control InDesign

The ACF function's return type determines what the plugin does next:

ACF return value InDesign behavior
Empty return or empty STRING Finish without another action.
Non-empty STRING Show the string in an InDesign information dialog.
JSON Validate its type and dispatch the corresponding InDesign action.
Unsupported ACF type Show and log an unsupported return-type error.
Runtime exception Catch it, show an error dialog, and append it to the plugin log.

ACF functions should keep a consistent return type. A JSON-returning function should use indesign.noop for cancellation rather than returning an empty string from one branch.

1.6.1. No further action

return JSON(
    "type", "indesign.noop",
    "version", 1
);

1.6.2. Show a message

return JSON(
    "type", "indesign.message",
    "version", 1,
    "severity", "information",
    "message", _("The article was retrieved successfully.")
);

severity can be information, warning, or error.

1.6.3. Reload compiled packages

return JSON(
    "type", "indesign.reload_packages",
    "version", 1,
    "message", _("Reload the ACF packages now?")
);

1.6.4. Perform document operations

indesign.operations contains an ordered commands array. The plugin validates the complete command document before applying it.

return JSON(
    "type", "indesign.operations",
    "version", 1,
    "units", "pt",
    "coordinateSpace", "page",
    "transaction", JSON(
        "name", "Place article",
        "cancelPolicy", "keep"
    ),
    "commands", JSONarray(
        JSON(
            "op", "create_text_frame",
            "id", "headline",
            "persistentId", "article:18425:headline",
            "size", JSONarray(420, 60),
            "place", JSONarray(0, 0),
            "text", "New harbour project"
        ),
        JSON(
            "op", "create_text_frame",
            "id", "body",
            "persistentId", "article:18425:body",
            "size", JSONarray(420, 400),
            "place", JSON(
                "mode", "relative",
                "relativeTo", "headline",
                "edge", "bottom",
                "offset", JSONarray(0, 6)
            ),
            "text", "Article body..."
        )
    )
);

Current operation families include:

id identifies an object inside one command batch. persistentId is stored with the InDesign object so a later action can find and update it after the document has been saved and reopened.

1.7. Floating palettes

open_palette() opens a modeless floating palette. InDesign uses the shared native palette contract: an AppKit panel on macOS and the shared Win32 renderer on Windows. It uses the same JSON layout as open_dialog(), so a definition made with the Dialog Configurator can normally be reused.

function NewsDesk_open_palette ()

    JSON definition = JSON(
        "id", "newsdesk_articles",
        "title", _("News Desk"),
        "size", JSONarray(720, 540),
        "rows", JSONarray(
            JSON(
                "fields", JSONarray(
                    JSON(
                        "name", "placeArticle",
                        "type", "button",
                        "buttonLabel", _("Place selected article"),
                        "action", JSON(
                            "function", "NewsDesk_place_article",
                            "returnAction", "dispatch"
                        )
                    )
                )
            )
        ),
        "buttons", JSONarray(
            JSON("id", "close", "label", _("Close"), "cancel", true)
        )
    );

    JSON paletteResult = open_palette(definition);
    if (paletteResult["status"] == "error") then
        throw string(paletteResult["error"]);
    end if

    return JSON("type", "indesign.noop", "version", 1);
end

Unlike open_dialog(), open_palette() returns immediately. The palette stays open while the user works in InDesign.

Palette actions can update individual fields, options, hidden data, and portal rows without rebuilding the full palette. A palette button using returnAction: "dispatch" invokes its ACF function through the InDesign action bridge. The function receives current palette values under info["palette"], and its return value is interpreted exactly like a package menu result.

Palette position is stored in the InDesign ACF configuration so the workspace can be restored later. The palette title bar also provides collapse and expand controls while it is open.

1.8. A minimal complete package

package HelloInDesign "Minimal ACF InDesign package";

function HelloInDesign_startup ()
    return "OK";
end

function HelloInDesign_menu ()
    return JSON(
        "title", "Hello ACF",
        "MenuLocation", "STD",
        "items", JSONarray(
            JSON(
                "title", "Create greeting",
                "function", "HelloInDesign_create_greeting"
            )
        )
    );
end

function HelloInDesign_create_greeting (JSON info)
    return JSON(
        "type", "indesign.operations",
        "version", 1,
        "units", "pt",
        "coordinateSpace", "page",
        "commands", JSONarray(
            JSON(
                "op", "create_text_frame",
                "persistentId", "hello:greeting",
                "size", JSONarray(260, 55),
                "place", "user",
                "text", "Hello from an ACF package"
            )
        )
    );
end

Compile the source with ACFcompiler, place the resulting .dsComp file in the development package directory, and choose Plug-in-moduler → ACF → Reload Packages. The new package menu is then available without restarting InDesign.

1.9. Designing portable packages

The compiler does not create a separate package format for each host. A source file can therefore share its data model, API client, SQLite helpers, parsing, translations, and business rules across FileMaker, 4D, and InDesign.

Runtime constants can separate genuinely host-specific entry points:

if (isFileMaker) then
    // FileMaker-specific behavior.
elseif (is4D) then
    // 4D-specific behavior.
elseif (isIndesign) then
    // Return an InDesign action contract.
end if

Keep most functions host-independent. Use these constants at the outer integration boundary rather than duplicating the shared implementation.

Not every library is necessarily available in every host build. A host-specific unavailable feature throws a clear runtime error. Check the documentation for the relevant function when designing a package intended for several hosts.

1.10. Development workflow

  1. Edit the .acf source file.
  2. Compile it with the host-independent ACFcompiler.
  3. Confirm that compilation has no warnings, especially mixed function return types.
  4. Put the .dsComp file in the InDesign development package directory.
  5. Choose Plug-in-moduler → ACF → Reload Packages.
  6. Test the menu or palette action.
  7. Inspect the InDesign ACF log if an exception or contract validation error occurs.

The plugin compares package hashes, so unchanged packages are not needlessly reinstalled. Menu and palette callbacks resolve the currently installed function when invoked, which allows many ACF changes to be tested without restarting InDesign.

1.11. SVG image compatibility

Adobe InDesign can place SVG files, but its SVG renderer supports a more limited feature set than modern web browsers and dedicated SVG editors. Complex SVG artwork may therefore appear incomplete in an InDesign document. The same result occurs with manual placement and placement through the ACF plugin, so this is an InDesign limitation rather than a plugin issue.

If an SVG does not render correctly, convert it to PDF to retain vector quality, or convert it to an appropriate raster format such as PNG or TIFF before placement.

1.12. Related reference material