# Using Object Data Tables

An `ObjectDataTable` is a table that – instead of simply containing text information – embeds objects such as JavaScript functions that can be accessed via callback functions.

The following sample relies on the DemoDataModel example, which can be found in the distribution package in the publishadvanced sample solution. For simplicity, all of the code below should be added to DemoDataModel.cpp in the DemoDataModel project.

## Defining an ObjectDataTable

First, let’s write a simple function that will create our `ObjectDataTable` into which we’ll later insert our JavaScript actions. We’ll define our `A3DPDFDataTableData` just as we would any other table in Advanced Publishing, but to create the table we’ll call `A3DPDFObjectDataTableCreate()`, which will create an `ObjectDataTable`:

    A3DStatus stCreateObjectTable(A3DPDFDocument* pDoc, const std::vector<std::vector<std::string> > &aRows, A3DPDFDataTable*& pDataTable)
    {
        A3DStatus iRet = A3D_SUCCESS;
        A3DUns32 iNbRows = (int)aRows.size();
        A3DUns32 iNbCols = (iNbRows > 0) ? (int)aRows[0].size() : 0;
        A3DPDFDataTableData sTableData = A3D_MAKE_DATA(A3DPDFDataTableData);
        sTableData.m_iNbRows = iNbRows;
        sTableData.m_iNbCols = iNbCols;
        sTableData.m_ppcTexts = (A3DUTF8Char**)malloc(iNbRows * iNbCols * A3DUns32(sizeof(A3DUTF8Char*)));
        for (A3DUns32 ir = 0; ir < iNbRows; ir++)
        {
            for (A3DUns32 ic = 0; ic < iNbCols; ic++)
                mallocAndSetString(
                (A3DUTF8Char*)(char*)aRows[ir][ic].c_str(),       // src
                    sTableData.m_ppcTexts[ir*iNbCols + ic]);        // dest
        }
        CHECK_RET(A3DPDFObjectDataTableCreate(pDoc, &sTableData, &pDataTable));
        stFreeTable(sTableData);
        return iRet;
    }

## Injecting JavaScript

Now that we’ve created an `ObjectDataTable`, we can add custom actions by embedding JavaScript code. Continuing with the DemoDataModel example from the Advanced Publishing package, we’ll replace the existing stBuildDocument() function with some new code specifically for our `ObjectDataTable`. The full code for our stBuildDocument() function is available at the bottom of this page.

Let’s define a simple JavaScript function to serve as a callback in our PDF document. This callback function can be triggered in JavaScript actions we’ll later assign to individual fields:

    std::basic_ostringstream<char> osJsOnDoc;
    osJsOnDoc << "\n\
    \n\
    function scrolltable_callback_withuserdata(i, j, userdata)\n\
    {\n\
      app.alert( { cMsg:\" JavaScript action on cell [\"+i+\" - \"+j+\"]\\n model name \"+userdata.modelname+\" ; owner \"+userdata.modelowner+\"  \" } );\n\
      console.println(\"in scrolltable_callback ; i=\"+i+\" - j=\"+j);\n\
      console.println(\"in scrolltable_callback userdata=\"+userdata);\n\
      console.println(\"in scrolltable_callback userdata.modelname=\"+userdata.modelname);\n\
      \n\
    }\n\
    ";
    CHECK_RET(A3DPDFDocumentAddJavascriptFromString(pDoc, "STCallbacks", osJsOnDoc.str().c_str()));

In this snippet, we’re simply reading our JavaScript function into a C++ stream and calling A3DPDFDocumentAddJavascriptFromString() to embed it in our PDF document.

Next we’ll create a `A3DPDFDataTable` and add rows to it one at a time. Each row is a standard vector object, and each element in the vector represents a column in the `A3DPDFDataTable`. Within each column, we’ll define an `actionjscallback` function and an `actionjscallbackdata` object, which we’ll use to write some information to an alert window whenever a user clicks on that particular cell.

In addition, we’ll define an `actionurl`, which will launch a browser window and retrieve a URL whenever the user clicks on the associated icon.

    A3DPDFDataTable* pDataTable_Bom = NULL;
    // structure for Tables definitions
    std::vector<std::vector<std::string> > aRows;
    std::vector<std::string> aOneRow;
    aRows.clear();
    aOneRow.clear();
    aOneRow.push_back("{ caption:\"1\" }"); // default action: action datamodel
    aOneRow.push_back("{ caption:\"crank damper\", actionjscallback:\"scrolltable_callback_withuserdata\", actionjscallbackdata: { modelname:\"crank_damper\" , modelowner:\"Joe\" } }");
    aOneRow.push_back("{ caption:\"2\"}"); // default action: action datamodel
    aOneRow.push_back("{ caption:\"iron\"}"); // default action: action datamodel
    sIconId = stStoreIcon(pDoc, std::string(ICONS_PATH) + "crank_damper.jpg");
    aOneRow.push_back(std::string("{ icon:\"") + sIconId + std::string("\", actionurl:\"https://www.techsoft3d.com/get?model=crank_damper\" }"));
    aRows.push_back(aOneRow);
    //...add 5 more rows here.
    stCreateObjectTable(pDoc, aRows, pDataTable_Bom);
    // --- Widgets binding for Table BOM
    const A3DInt32 piMapColumns_Bom[] = { 0, 1, 2, 3, 4, 5 };
    CHECK_RET(A3DPDFScrollTableBindToTable(pScrollTableBom, pDataTable_Bom, sizeof(piMapColumns_Bom) / sizeof(A3DInt32), piMapColumns_Bom));

In the final section of the snippet, we’ve called `A3DPDFScrollTableBindToTable()` to embed our `ObjectDataTable` inside of a `A3DPDFScrollTable`.

## Running the Sample

Compiling and then running the DemoDataModel sample will create a new PDF in the samples directory called sample_DemoDataModel.pdf:

The PDF will contain a part list – clicking on a part name will trigger the `actionjscallback` custom event, popping up an alert window containing the data retrieved with our JavaScript functions. Clicking on an image icon will launch the `actionurl` in a browser.

## Full Sample Code

    A3DStatus stCreateObjectTable(A3DPDFDocument* pDoc, const std::vector<std::vector<std::string> > &aRows, A3DPDFDataTable*& pDataTable)
    {
        A3DStatus iRet = A3D_SUCCESS;
        A3DUns32 iNbRows = (int)aRows.size();
        A3DUns32 iNbCols = (iNbRows > 0) ? (int)aRows[0].size() : 0;
        A3DPDFDataTableData sTableData = A3D_MAKE_DATA(A3DPDFDataTableData);
        sTableData.m_iNbRows = iNbRows;
        sTableData.m_iNbCols = iNbCols;
        sTableData.m_ppcTexts = (A3DUTF8Char**)malloc(iNbRows * iNbCols * A3DUns32(sizeof(A3DUTF8Char*)));
        for (A3DUns32 ir = 0; ir < iNbRows; ir++)
        {
            for (A3DUns32 ic = 0; ic < iNbCols; ic++)
                mallocAndSetString(
                (A3DUTF8Char*)(char*)aRows[ir][ic].c_str(),       // src
                    sTableData.m_ppcTexts[ir*iNbCols + ic]);        // dest
        }
        CHECK_RET(A3DPDFObjectDataTableCreate(pDoc, &sTableData, &pDataTable));
        stFreeTable(sTableData);
        return iRet;
    }
    // In DemoDataModel example, replace existing stBuildDocument() function with this code.
    A3DStatus stBuildDocument(A3DPDFDocument* pDoc)
    {
        A3DStatus iRet = A3D_SUCCESS;
        A3DPDFPage* pPage = NULL;
        int indexpage = 0;
        std::string sSuffixFields;
        // populating the document metadata
        A3DPDFDocumentInformationData sInformationData = A3D_MAKE_DATA(A3DPDFDocumentInformationData);
        sInformationData.m_pcTitle = (char*) "Advanced Publishing Functionalities";
        sInformationData.m_pcCreator = (char*) "Advanced Publishing Sample Application";
        sInformationData.m_pcSubject = (char*) "This sample demoes the data model functionality of advanced publishing";
        sInformationData.m_pcAuthor = (char*) "Tech Soft 3D";
        CHECK_RET(A3DPDFDocumentSetInformation(pDoc, &sInformationData));
        // creating a document page from scratch
        A3DPDFPageData2 sPageData = A3D_MAKE_DATA(A3DPDFPageData2);
        sPageData.m_ePageOrientation = kA3DPDFPageLandscape;
        sPageData.m_ePageSize = kA3DPDFPageLetter;     // standard letter format: 612 x 792
        const int iPageWidth = 792, iPageHeight = 612;
        CHECK_RET(A3DPDFDocumentAppendNewPage2(pDoc, &sPageData, &pPage));
        if (pPage != NULL)
        {
            std::string sIconId;
            // create Scroll table on the page
            A3DPDFScrollTable* pScrollTableBom = NULL;
            std::vector<int> aiWidthCols; // in points
            aiWidthCols.push_back(30);
            aiWidthCols.push_back(170);
            aiWidthCols.push_back(30);
            aiWidthCols.push_back(60);
            aiWidthCols.push_back(50);
            std::vector<std::string> aRowHeader;
            aRowHeader.push_back("No");
            aRowHeader.push_back("NAME PART");
            aRowHeader.push_back("QTY");
            aRowHeader.push_back("MATERIAL");
            aRowHeader.push_back("SNAPSHOT");
            std::vector<A3DPDFEColumnType> aeTypes;
            aeTypes.push_back(kA3DPDFTextContent);
            aeTypes.push_back(kA3DPDFTextContent);
            aeTypes.push_back(kA3DPDFTextContent);
            aeTypes.push_back(kA3DPDFTextContent);
            aeTypes.push_back(kA3DPDFImageContent);
            CHECK_RET(stAddScrollTable(pDoc, pPage,
                20, 500, // iPosLeft, iPosTop
                15, //iSliderWidth
                3,  // iNbRowsInFrame
                20, // iHeightRowHeader - in points
                40, // iHeightRows - in points
                aiWidthCols, // in points
                aRowHeader,
                aeTypes,
                &pScrollTableBom));
            // store JavaScript functions on the document. these functions can be used by JavaScript stored on the fields.
            std::basic_ostringstream<char> osJsOnDoc;
            osJsOnDoc << "\n\
    \n\
    function scrolltable_callback_withuserdata(i, j, userdata)\n\
    {\n\
      app.alert( { cMsg:\" JavaScript action on cell [\"+i+\" - \"+j+\"]\\n model name \"+userdata.modelname+\" ; owner \"+userdata.modelowner+\"  \" } );\n\
      console.println(\"in scrolltable_callback ; i=\"+i+\" - j=\"+j);\n\
      console.println(\"in scrolltable_callback userdata=\"+userdata);\n\
      console.println(\"in scrolltable_callback userdata.modelname=\"+userdata.modelname);\n\
      \n\
    }\n\
    ";
            CHECK_RET(A3DPDFDocumentAddJavascriptFromString(pDoc, "STCallbacks", osJsOnDoc.str().c_str()));
            A3DPDFDataTable* pDataTable_Bom = NULL;
            //CHECK_RET(stCreateTable_Sple1_Manufacturers(pDoc, pDataTable_Bom));
            // structure for Tables definitions
            std::vector<std::vector<std::string> > aRows;
            std::vector<std::string> aOneRow;
            aRows.clear();
            aOneRow.clear();
            aOneRow.push_back("{ caption:\"1\" }"); // default action: action datamodel
            aOneRow.push_back("{ caption:\"crank damper\", actionjscallback:\"scrolltable_callback_withuserdata\", actionjscallbackdata: { modelname:\"crank_damper\" , modelowner:\"Joe\" } }");
            aOneRow.push_back("{ caption:\"2\"}"); // default action: action datamodel
            aOneRow.push_back("{ caption:\"iron\"}"); // default action: action datamodel
            sIconId = stStoreIcon(pDoc, std::string(ICONS_PATH) + "crank_damper.jpg");
            aOneRow.push_back(std::string("{ icon:\"") + sIconId + std::string("\", actionurl:\"https://www.techsoft3d.com/get?model=crank_damper\" }"));
            aRows.push_back(aOneRow);
            aOneRow.clear();
            aOneRow.push_back("{ caption:\"2\" }");
            aOneRow.push_back("{ caption:\"crank crankshaft\", actionjscallback:\"scrolltable_callback_withuserdata\", actionjscallbackdata: { modelname:\"crank_crankshaft\" , modelowner:\"James\" } }");
            aOneRow.push_back("{ caption:\"1\"}");// default: action datamodel
            aOneRow.push_back("{ caption:\"steel\" }"); // default: action datamodel
            sIconId = stStoreIcon(pDoc, std::string(ICONS_PATH) + "crank_crankshaft.jpg");
            aOneRow.push_back(std::string("{ icon:\"") + sIconId + std::string("\", actionurl:\"https://www.techsoft3d.com/get?model=crank_crankshaft\" }"));
            aRows.push_back(aOneRow);
            aOneRow.clear();
            aOneRow.push_back("{ caption:\"3\" }");
            aOneRow.push_back("{ caption:\"crank valve\", actionjscallback:\"scrolltable_callback_withuserdata\", actionjscallbackdata: { modelname:\"crank_valve\" , modelowner:\"Bill\" } }");
            aOneRow.push_back("{ caption:\"1\"}");
            aOneRow.push_back("{ caption:\"steel\", action:\"_rowdatamodel_\"}");
            sIconId = stStoreIcon(pDoc, std::string(ICONS_PATH) + "crank_valve.jpg");
            aOneRow.push_back(std::string("{ icon:\"") + sIconId + std::string("\", actionurl:\"https://www.techsoft3d.com/get?model=crank_valve\" }"));
            aRows.push_back(aOneRow);
            aOneRow.clear();
            aOneRow.push_back("{ caption:\"4\" }");
            aOneRow.push_back("{ caption:\"crank connectingrod boltnut\", actionjscallback:\"scrolltable_callback_withuserdata\", actionjscallbackdata: { modelname:\"crank_connectingrod_boltnut\" , modelowner:\"Neal\" } }");
            aOneRow.push_back("{ caption:\"1\"}");// default: action datamodel
            aOneRow.push_back("{ caption:\"steel\" }"); // default: action datamodel
            sIconId = stStoreIcon(pDoc, std::string(ICONS_PATH) + "crank_connectingrod_boltnut.jpg");
            aOneRow.push_back(std::string("{ icon:\"") + sIconId + std::string("\", actionurl:\"https://www.techsoft3d.com/get?model=crank_connectingrod_boltnut\" }"));
            aRows.push_back(aOneRow);
            aOneRow.clear();
            aOneRow.push_back("{ caption:\"5\" }");
            aOneRow.push_back("{ caption:\"crank pistonpin\", actionjscallback:\"scrolltable_callback_withuserdata\", actionjscallbackdata: { modelname:\"crank_pistonpin\" , modelowner:\"Henry\" } }");
            aOneRow.push_back("{ caption:\"2\"}");// default: action datamodel
            aOneRow.push_back("{ caption:\"steel2\" }"); // default: action datamodel
            sIconId = stStoreIcon(pDoc, std::string(ICONS_PATH) + "crank_pistonpin.jpg");
            aOneRow.push_back(std::string("{ icon:\"") + sIconId + std::string("\", actionurl:\"https://www.techsoft3d.com/get?model=crank_pistonpin\" }"));
            aRows.push_back(aOneRow);
            aOneRow.clear();
            aOneRow.push_back("{ caption:\"6\" }");
            aOneRow.push_back("{ caption:\"crank connectingrod bushing\", actionjscallback:\"scrolltable_callback_withuserdata\", actionjscallbackdata: { modelname:\"crank_connectingrod_bushing\" , modelowner:\"Jack\" } }");
            aOneRow.push_back("{ caption:\"3\"}");// default: action datamodel
            aOneRow.push_back("{ caption:\"steel3\" }"); // default: action datamodel
            sIconId = stStoreIcon(pDoc, std::string(ICONS_PATH) + "crank_connectingrod_bushing.jpg");
            aOneRow.push_back(std::string("{ icon:\"") + sIconId + std::string("\", actionurl:\"https://www.techsoft3d.com/get?model=crank_connectingrod_bushing\" }"));
            aRows.push_back(aOneRow);
            stCreateObjectTable(pDoc, aRows, pDataTable_Bom);
            // --- Widgets binding for Table BOM
            const A3DInt32 piMapColumns_Bom[] = { 0, 1, 2, 3, 4, 5 };
            CHECK_RET(A3DPDFScrollTableBindToTable(pScrollTableBom, pDataTable_Bom, sizeof(piMapColumns_Bom) / sizeof(A3DInt32), piMapColumns_Bom));
            indexpage++;
        }
        // end document
        CHECK_RET(A3DPDFDocumentSaveEx(pDoc, kA3DPDFSaveFull|kA3DPDFSaveOptimized, OUT_FILE));
        return iRet;
    }

Conversation saved
HOOPSY BETA

### Hello! I'm HOOPSY

Your multilingual generative AI assistant for documentation. Ask me anything about HOOPS Exchange in your own language, or choose a sample question below to start a conversation:

What is HOOPS Exchange?
What are the latest release notes for HOOPS Exchange?
How do I start evaluating HOOPS Exchange?
How do I write my first sample application?
How can I get assistance?

HOOPSY may store conversations according to OpenAI's policy. Responses may not always be accurate.

HOOPSY is thinking...