Skip to content
DemoFAQ

PDF Generation Template Editor

Open-source visual PDF generation engine with customizable templates and developer-friendly APIs.

View on GitHub

Use Custom AI Models via Callbacks ​

Overview ​

Starting with SDK v4.1.0, ComPDF Conversion SDK exposes a callback-based extension point that lets you plug in your own AI inference engine for OCR, Layout Analysis, and Table Recognition. Instead of relying on the built-in DocumentAI model loaded by LibraryManager::SetDocumentAIModel, you can:

  • Run inference with any model or runtime you choose (e.g. your in-house engine, PaddleOCR, a cloud OCR API).
  • Return the result to the SDK as a JSON string with a well-defined schema.

When the relevant callback pair is registered on CConvertCallback, the SDK skips its built-in model invocation for that capability and consumes your JSON output instead. If a pair is left as nullptr, the SDK falls back to the built-in DocumentAI model (when available).

Callback Pairs ​

Each AI capability uses two callbacks: a trigger (invoked by the SDK with the path to a page image saved as PNG in a temp directory) and a result getter (invoked by the SDK immediately afterwards to retrieve the JSON string).

CapabilityTrigger callbackResult getter callbackTriggered when
OCRocrget_ocr_resultenable_ocr = true
Layout Analysislayoutget_layout_resultenable_ai_layout = true or enable_ocr = true
Table Recognitiontableget_table_resultenable_ai_table_recognition = true and a table region is detected by layout analysis

Rules:

  • The trigger receives a UTF-8 path to a PNG file. Return true if your inference succeeded, false to make the SDK ignore the result for that page.
  • The getter must return a UTF-8 JSON C-string. The pointer must remain valid until the SDK has finished parsing it (typically until the next call to the same getter).
  • Both callbacks for a capability must be set together. If only one is provided, the SDK falls back to the built-in path.
  • Coordinates in your JSON must be in the pixel space of the image the trigger received (top-left origin, X right, Y down).
  • Confidence filtering: OCR spans with confidence < 0.1 and layout objects with confidence < 0.45 are discarded by the SDK.

Sample ​

c++
#include "compdf_conversion.h"
#include <string>

using namespace compdf;
using namespace compdf::common;
using namespace compdf::conversion;

// Buffers owned by the integrator. Must outlive each SDK getter call.
static std::string g_ocr_json;
static std::string g_layout_json;
static std::string g_table_json;

// --- OCR ---
bool my_ocr_trigger(const char* image_path) {
    g_ocr_json = run_my_ocr_model(image_path);   // produce JSON (see schema below)
    return !g_ocr_json.empty();
}
const char* my_ocr_getter() { return g_ocr_json.c_str(); }

// --- Layout Analysis ---
bool my_layout_trigger(const char* image_path) {
    g_layout_json = run_my_layout_model(image_path);
    return !g_layout_json.empty();
}
const char* my_layout_getter() { return g_layout_json.c_str(); }

// --- Table Recognition ---
bool my_table_trigger(const char* image_path) {
    g_table_json = run_my_table_model(image_path);
    return !g_table_json.empty();
}
const char* my_table_getter() { return g_table_json.c_str(); }

int main() {
    LibraryManager::LicenseVerify("<license>", "<device_id>", "<app_id>");
    LibraryManager::Initialize("resource");
     /* SetDocumentAIModel is not required when callbacks cover OCR,
       Layout Analysis, and Table Recognition. */

    CConvertCallback callback   = {};
    callback.handle             = nullptr;
    callback.progress           = nullptr;
    callback.cancel             = nullptr;
    callback.ocr                = &my_ocr_trigger;
    callback.get_ocr_result     = &my_ocr_getter;
    callback.layout             = &my_layout_trigger;
    callback.get_layout_result  = &my_layout_getter;
    callback.table              = &my_table_trigger;
    callback.get_table_result   = &my_table_getter;

    ConvertOptions opt;
    opt.enable_ocr       = true;
    opt.enable_ai_layout = true;
    opt.languages        = { OCRLanguage::e_English };

    CPDFConversion::StartPDFToWord("input.pdf", "", "output.docx", opt, &callback);
    return 0;
}

You can register only the capabilities you want to override and leave the others as nullptr to keep the built-in behaviour.

Thread Safety and Lifetime ​

  • Callbacks are invoked from the SDK conversion thread. Implement them in a thread-safe manner if your model engine is shared across calls.
  • The PNG image at image_path lives in the SDK temp directory and may be deleted shortly after the trigger returns; copy or process it before returning.
  • The JSON pointer returned from a getter must remain valid until the same getter is called again (or until the conversion task ends).

OCR Result JSON Schema ​

Returned by get_ocr_result. The SDK populates each text_spans[].chars[] either from words[] if provided, or by uniformly splitting the span rect.

json
{
  "text_spans": [
    {
      "text": "Hello World",
      "confidence": 0.98,
      "rotation": 0.0,
      "rect":  { "left": 120, "top": 80, "right": 320, "bottom": 110 },
      "style": {
        "font_size":  18.0,
        "font_color": { "r": 0, "g": 0, "b": 0 }
      },
      "words": [
        { "text": "Hello", "rect": { "left": 120, "top": 80, "right": 200, "bottom": 110 } },
        { "text": "World", "rect": { "left": 210, "top": 80, "right": 320, "bottom": 110 } }
      ]
    }
  ]
}
FieldTypeRequiredDescription
text_spansarrayYesRecognized text spans on the page.
textstringYesUTF-8 text content of the span.
confidencenumberNo0.0 – 1.0. Spans below 0.1 are discarded.
rotationnumberNoText rotation in degrees. Default 0.
rectobjectYesBounding box in image pixels (left/top/right/bottom).
style.font_sizenumberNoEstimated font size in pixels.
style.font_colorobjectNo{ r, g, b } 0 – 255.
wordsarrayNoPer-word boxes. If omitted, the SDK splits the span rect evenly. Strongly recommended for CJK + Latin mixed lines for correct glyph spacing.

Layout Analysis Result JSON Schema ​

Returned by get_layout_result. Objects with confidence < 0.45 are discarded.

json
{
  "objects": [
    { "type": "title",     "confidence": 0.95, "rect": { "left": 60, "top": 50,  "right": 540, "bottom": 90  } },
    { "type": "paragraph", "confidence": 0.97, "rect": { "left": 60, "top": 100, "right": 540, "bottom": 220 } },
    { "type": "figure",    "confidence": 0.92, "rect": { "left": 80, "top": 240, "right": 520, "bottom": 460 } },
    { "type": "table",     "confidence": 0.93, "rect": { "left": 60, "top": 480, "right": 540, "bottom": 700 } }
  ]
}

Supported type values:

ValueMeaning
paragraphBody text paragraph
titleHeading
figureImage or figure
figure_titleFigure caption header
figure_captionFigure caption text
tableTable region. Whether the table is bordered or borderless is determined by the table recognition stage, not by the layout label.
table_titleTable caption header
table_captionTable caption text
ordered_listOrdered list
unordered_listUnordered list
catalogueTable of contents
formulaMath formula
codeCode block
algorithmAlgorithm block
headerPage header
footerPage footer
page_numberPage number
referenceReference or citation

Objects with a type value that is not listed above are ignored. Use the values in this table as the canonical layout labels in your custom output.

Table Recognition Result JSON Schema ​

Returned by get_table_result once per detected table region. Polygons use 8 integers [x0, y0, x1, y1, x2, y2, x3, y3] in the order top-left, top-right, bottom-right, bottom-left.

json
{
  "type": "table_with_line",
  "position": [60, 480, 540, 480, 540, 700, 60, 700],
  "rows": 3,
  "cols": 2,
  "angle": 0.0,
  "height_of_rows": [40, 60, 60],
  "width_of_cols":  [200, 280],
  "table_cells": [
    {
      "start_row": 0, "end_row": 0,
      "start_col": 0, "end_col": 0,
      "cell_background_color_r": 240,
      "cell_background_color_g": 240,
      "cell_background_color_b": 240,
      "position": [60, 480, 260, 480, 260, 520, 60, 520]
    }
  ]
}
FieldTypeDescription
typestringtable_with_line for bordered tables; any other value is treated as a non-standard (borderless) table.
positionint[8]Table polygon in image pixels.
rows / colsintRow / column counts.
anglenumberSkew angle in degrees.
height_of_rowsint[]Per-row pixel heights (length = rows).
width_of_colsint[]Per-column pixel widths (length = cols).
table_cells[]arrayOne entry per merged cell.
start_row / end_rowintInclusive row span of the cell.
start_col / end_colintInclusive column span of the cell.
cell_background_color_*intCell background color components (0 – 255).
positionint[8]Cell polygon in image pixels.

Tip: Validate Your JSON ​

If you need a reference output to compare against, run a conversion once with the built-in DocumentAI model — the SDK uses the same JSON shape internally, so your custom output should follow the same structure.