libhpdftbl 1.5.0
Table construction library for Haru PDF library
|
So far we have constructed the layout of table by issuing API calls per table to set up, for example, the column widths and what cells should merge with what other cells and so on. Previously we saw that data to be put in the table could be specified by either directly issuing API calls per cell, using a 2D array that we populate with data and then finally use callbacks to generate the data in the cells.
The final and most powerful way of constructing a table is to define the table structure as data. This structural data together with a style theme can completely define a table.
This will allow the dynamic construction of tables with only one API call instead of the multiple call required to construct a table the usual way. It can initially seem more complex but for advanced table this is indeed a much simpler and easy to maintain. In fact, this will allow a table to be (almost, we'll get back to the limitations) defined entirely in a database and makes it possible to adjust tha table as the data changes without ever updating the code (or recompile).
There are two data structure that are used when defining a table. First there is a data structure for the overall table specifics and then in that structure a structure to specify the layout of each cell. In addition, a theme needs to be defined (see section Themes). It is possible to omit the theme by specifying NULL
in which case the default theme will be used.
To stroke a table from data the following API call is used
In order to populate the table with suitable data callback functions are used (see section Using callbacks)
The overall table is first defined as an instance of
Then each cell (referenced above in the cell_spec
field) is defined as an instance of
To understand how this is done lets start to define a basic 3x3 table with header row (so 4x3 in total) as data. First we create an instance of the table data
Then the actual API call is trivial compared to the table creation code we have seen in the previous examples and consists of only one line of code
The result is as expected and shown in Figure 13 but with much less code!
Figure 13: *Defining a table with a data structure tut_ex13_1.c*
In the previous example we kept it simple didn't specify any format or content fór a table cell. Let us therefore create a slightly more complex example where we create a form which easily could be used to display data records from a DB.
The nice thing about separating layout and table structure from the data population in the callbacks is that this can almost be seen as a poor man's model-view-controller where the table structure is completely separate from the data (and how it is created).
A good way to start designing a table is to make a sketch on how it should look. Our goal is to crete the table structure as shown in the empty table in Figure 14 below
Figure 14: Sketch of table to be designed
To get this layout we use a basic table with :
To make it easier to see how to construct the table we can overlay the sketch with a grid shown in blue in Figure 15. As can be seen this is a basic 5x4 table where a number of cells span multiple columns.
Figure 15: Sketch of table to be designed with 5x4 table overlaid
To start we set up the table specification as in the previous example with necessary changes. We will also need to specify cell specifications this time, and we assume those are available in an array of cell structures called cell_specs
.
Before we specify the table structure we have one design decision to make. For the callbacks we can either use the table callback for all cells and check row and column to get the appropriate data, or we can add individual callbacks for each cell. The first case has the advantage to only need one callback function (but a lot of tests) and the second that each callback will be small and focused to get the data for that individual cell, but we will need potentially one callback for each cell unless there are commonalities between the cells so one callback can serve multiple cells. Remember that we still get the row and column as arguments in the callback so we weill always know exactly for which cell the callback was made.
To keep the size of this example we will use the table callback method for content and specify the label directly in the cell specification. With this decision made we get the following definition cell specifications
As can be seen we need to have an end of cell specification sentinel since we could decide to provide details for one or more cells and there is no way for the library to know how many fields to read otherwise. There is even a convenience constant in the library PDFTBL_END_CELLSPECS
that can be used as the last record.
The overall table specification is pretty much as before but with the added cell specifications.
When this is run (see tut_ex13_2.c) it generates the following image, Figure 16
Figure 16: Specifying a table as data with cell specifications.
What remains is to write the proper table content callback that will populate the table. In a real life scenario his data will most likely come from a database but adding that in our example would bring too far. Instead, we will just use some fake static dummy data to illustrate the principle.
Since we have one callback for all cells we need to test from which cell the call come from. Here is a very important point to make. The row and column number will be the row and cell columns in the original table before any column or row spans was applied. In this example it means that for example the "Date" field (upper right) will have row=0
and col=3
and not (0,1)
!!.
With this information we can write the following (dummy) table callback
and we get the (expected) result as shown in Figure 17 below.
Figure 17: Specifying a table as data with cell specifications and "dummy" data.
The alternative of specifying individual callback for each cell would then require that each cell have a callback provided or perhaps even a mix with both a general table callback and selected cell callbacks.
The priority is such that a cell callback will always override a table callback. In the above example the callback for the name field could as an example be