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 HOOPS Publish 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 HOOPS Publish, 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)
{
A3DUns32 iNbRows = (int)aRows.size();
A3DUns32 iNbCols = (iNbRows > 0) ? (int)aRows[0].size() : 0;
A3DPDFDataTableData sTableData;
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 Publish 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:\"http://localhost:11180/hoops_web_viewer.html?scs=crank_damper.scs\" }"));
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)
{
A3DUns32 iNbRows = (int)aRows.size();
A3DUns32 iNbCols = (iNbRows > 0) ? (int)aRows[0].size() : 0;
A3DPDFDataTableData sTableData;
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)
{
A3DPDFPage* pPage = NULL;
int indexpage = 0;
std::string sSuffixFields;
// populating the document metadata
sInformationData.m_pcTitle = (char*) "HOOPS Publish Functionalities";
sInformationData.m_pcCreator = (char*) "HOOPS Publish Sample Application";
sInformationData.m_pcSubject = (char*) "This sample demoes the data model functionality of HOOPS Publish";
sInformationData.m_pcAuthor = (char*) "Tech Soft 3D";
CHECK_RET(A3DPDFDocumentSetInformation(pDoc, &sInformationData));
// creating a document page from scratch
A3DPDFPageData2 sPageData;
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:\"http://localhost:11180/hoops_web_viewer.html?scs=crank_damper.scs\" }"));
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:\"http://localhost:11180/hoops_web_viewer.html?scs=crank_crankshaft.scs\" }"));
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:\"http://localhost:11180/hoops_web_viewer.html?scs=crank_valve.scs\" }"));
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:\"http://localhost:11180/hoops_web_viewer.html?scs=crank_connectingrod_boltnut.scs\" }"));
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:\"http://localhost:11180/hoops_web_viewer.html?scs=crank_pistonpin.scs\" }"));
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:\"http://localhost:11180/hoops_web_viewer.html?scs=crank_connectingrod_bushing.scs\" }"));
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;
}
top_level:1 prog_guide:2