Generate Output in Memory

TFormer_PrintToStream sends generated output to a TFN_StreamWriteCallback. The callback can receive open, data, and close events, and a data event can occur more than once.

For PDF output, the callback receives one stream. This C++ example uses the native C API to append each data block to an application-owned std::vector<BYTE> and ignores the open and close events. Other output types can produce several streams and require separate stream handling, which is not part of this PDF example.

Setup: Windows build. For a supplied project and layout, see the Quick Start.

Using TFormer_PrintToStream

The following excerpt assumes that form already has its form layout and data configured, and that pdfData and WriteToMemory are defined as in the complete example below:

CHECK(TFormer_SetPrinterType(form, ePrinterType_PDFFile));
CHECK(TFormer_PrintToStream(
    form, WriteToMemory, reinterpret_cast<LPARAM>(&pdfData)));

// Use pdfData.data() and pdfData.size() here.
std::printf("PDF size: %zu bytes\n", pdfData.size());

Use pdfData.data() and pdfData.size() only after TFormer_PrintToStream returns ErrOk. The vector owns the captured bytes and releases them automatically when it leaves scope. No output filename or fixed capacity is required.

Complete Example

The following standalone C++ program includes the layout and data setup. Place both code blocks, in this order, in the same source file. Replace the layout filename and data-field name before running it. Include the public TFormer.h header and link the TFormer import library that matches the Windows application architecture and installed SDK release.

#include <cstdio>
#include <new>
#include <vector>
#define TECIT_DLLIMPORT
#include "TFormer.h"
#undef TECIT_DLLIMPORT
static ERRCODE __stdcall WriteToMemory(
    BYTE *data, INT size, DWORD event, LPARAM userData) noexcept
{
    if (event != eWriteCallBack_Data)
        return ErrOk;
    if (userData == 0 || size < 0 || (data == nullptr && size != 0))
        return E_INVALIDARG;
    if (size == 0)
        return ErrOk;
    auto *buffer = reinterpret_cast<std::vector<BYTE> *>(userData);
    try {
        buffer->insert(buffer->end(), data, data + static_cast<size_t>(size));
        return ErrOk;
    } catch (const std::bad_alloc &) {
        return E_OUTOFMEMORY;
    } catch (...) {
        return E_FAIL;
    }
}
#define CHECK(call) \
    do { \
        result = (call); \
        if (result != ErrOk) { \
            LPCSTR message = TFormer_GetLastErrorDescription(form); \
            std::fprintf( \
                stderr, "%s failed: %s\n", #call, \
                message != nullptr ? message : "no error description"); \
            goto cleanup; \
        } \
    } while (0)

int main()
{
    ERRCODE result = ErrOk;
    HTFORM form = TFormer_Init(nullptr);
    std::vector<BYTE> pdfData;

    if (form == nullptr)
        return 1;

    CHECK(TFormer_SetRepositoryName(
        form, "FILENAME_OF_YOUR_FORMLAYOUT.tff"));
    CHECK(TFormer_NewRecord(form));
    CHECK(TFormer_SetVarValue(
        form, "NAME_OF_YOUR_DATAFIELD", "Value of the data field"));
    CHECK(TFormer_SetPrinterType(form, ePrinterType_PDFFile));
    CHECK(TFormer_PrintToStream(
        form, WriteToMemory, reinterpret_cast<LPARAM>(&pdfData)));

    // Use pdfData.data() and pdfData.size() here.
    std::printf("PDF size: %zu bytes\n", pdfData.size());

cleanup:
    if (form != nullptr) {
        ERRCODE exitResult = TFormer_Exit(form);
        if (result == ErrOk)
            result = exitResult;
    }
    return result == ErrOk ? 0 : 1;
}

The callback contains C++ exceptions and converts allocation failure to E_OUTOFMEMORY. Other callback exceptions become E_FAIL; none cross the native C callback boundary. The program reports the captured PDF byte count and does not write a PDF file.

Search documentation

Search in
More scopes
Additional scopes

All contents