Scripting Table Columns
A few days ago, Sergio, one of my regular commenters, posted a question about programmatically adding new columns to a table. My reply to his comment quickly turned into something that I thought should be promoted to a blog post so here it is.
This question required some investigation because it led me to the discovery of a bug related to adding/removing instances of a table column in a form viewed in a version of Acrobat prior to 8.0. More on that later in this post.
The short answer to Sergio’s question is that yes, in fact, you can modify the set of columns in a table programmatically at runtime. You can do this by either using the presence attribute — although this isn’t recommended because it can lead to data merging problems — or you can use Instance Managers to do it, which is the recommended method to use.
Here’s a sample form that contains a table with a “repeatable column”. Using the add and remove buttons that are provided, you can add and remove instances of the 3rd column.
Minimum Requirements: Designer 7.1, Acrobat 7.0.5.
Table Structure
To better understand what’s going on here, I’ll start by explaining how tables are represented in XFA. In reality, there are no <table>, <tr> or <td> elements in XFA. What happens is that <subform layout=”table|row”> elements get used. When a subform represents a table, it’s layout attribute is set to table and when it represents a row, it’s set to row.
Table Subforms
If you read about using a subform’s Instance Manager in order to add/remove instances, you should know that a subform only becomes dynamic or repeatable once it’s placed in a flowed container and its set as repeatable using the Object palette’s Binding tab. By default, a <subform layout=”table”> element is flowed from top to bottom. Since the table subform contains row subforms which, in turn contain the cells, that means that every row in a table could easily be set as repeatable and then get an Instance Manager that you could use to add/remove instances of a particular row.
Row Subforms
Just like the table subform, a <subform layout=”row”> is flowed. Its flow direction, however, isn’t top to bottom, it’s right to left. This means that its contents — the cells — are flowed.
Cells and Columns
Table cells are little different. Essentially, since they reside in a row subform which is flowed from right to left, there’s no specific definition for what is a table cell. It’s simply any type of object (text, numeric field, subform, button, etc.) that’s placed within a row subform. The number of objects in the row determines the number of columns there are. Therefore, to have a 3×3 table, you would need 3 row subforms each containing 3 objects.
There’s no actual definition for a table column either. Table columns are inferred by the cells (put simply, without going into details about column spanning). This means that a column consists of objects in separate row subforms which are above and below each other (e.g. the first object on every row make-up the first column in a table).
Adding/Removing Columns
Given the explanations in my little “table primer” above, we now know that table cells are objects which are flowed from right to left within row subforms. Since row subforms are flowed by nature, it means that if a cell were a subform itself, it could be made repeatable and it would then get its own Instance Manager for free! And once we have Instance Managers, we can start adding and removing instances of those cells.
So the trick here lies in converting every cell which constitutes a column into a subform which then contains the type of object you would normally use to display information in that cell (e.g. a text field, a check box, etc.).
Now if you’ve never noticed the Type property on the Field tab in the Object palette before, you’ll want to check it out because it’s about to come in real handy for setting-up a dynamic table column which consists of cells which are all subforms. This property is used to change the type of the selected object(s). For instance, if you put a text field on a form and decide that it should have been a numeric field, you can change it’s type from text field to numeric field simply by using this property. Typically, you cannot change anything into a subform but when you select a table cell which is a text object (the default cell object type when you insert a new table into a form using the Insert Table menu command or the Table object in the Library’s Standard tab), you can, in fact, change it into a subform. So just select each cell in the column and change its type to a subform using this property. The result will be a cell which is a subform that contains a text object. Then you can change the text object into some other field type which better represents the data which will go into the cell.
Making Cell Subforms Repeatable
Unfortunately, this is one case where you’ll have to use the XML Source tab because the repeatable property isn’t available for cell subforms on the Object palette’s Binding tab. Since it’s a valid XFA setting, you can set this yourself using the XML Source.
Switch to the XML Source window by selecting “XML Source” from the top-level View menu. Then, insert the following XML inside each subform which defines a cell:
<occur max="-1"/>
This means that the cell subform goes from looking like this:
<subform layout="tb" name="Col3SF">
<field name="Cell3" w="29.9999mm" h="10mm">
to looking like this:
<subform layout="tb" name="Col3SF">
<occur max="-1"/>
<field name="Cell3" w="29.9999mm" h="10mm">
and signifies that the cell will have one initial and minimum instance and can have as many additional instances as you need (no maximum).
Last but not least, the script!
After all that, the script is actually quite straight-forward: To add an instance of a column just add an instance of every repeatable subform cell that constitute a column. In my sample, I have a 3×3 table and each subform cell is named “Col3SF”. That means that the script looks like this (in JavaScript):
Table1.HeaderRow._Col3SF.addInstance(0);
Table1.Row1._Col3SF.addInstance(0);
Table1.Row2._Col3SF.addInstance(0);
Table1.Row3._Col3SF.addInstance(0);
Then, to remove an instance of the column, just do the reverse: Use the Instance Manager’s removeInstance method to remove an instance of each cell subform.
Bug when adding/removing table cell instances
Of course, everything was going great until now… Unfortunately, I discovered a little snag in playing with table column instances while making this sample. Fortunately, the bug has already been fixed in Acrobat 8.
Description
The first manifests itself when the second new instance of a column is added (by adding an instance of a column, I mean adding an instance of each cell subform in each row which collectively constitute a table column — as in my sample script above earlier). As of the second new instance, the cell in the header row will stop appearing in all subsequent instances.
There’s also an issue with removing column instances (by removing an instance of each cell subform). In this case, all new instances are removed until what was originally the first instance to be added is removed, leaving only the original, initial instance. What happens is that part of the last instance to be removed remains rendered on the form which doesn’t really look nice because it looks like the instance is still there (even though you can’t click on the cells anymore).
The problem is that the form’s layout isn’t properly updated after columns are added or removed.
Workaround
Luckily, there’s the xfa.layout object which gives us a solution to this problem when version of Acrobat prior to 8.0 are used to render the form. More specifically, versions 7.0.5+ since table support didn’t appear until then, when Designer 7.1 was released.
Using the xfa.layout object, you can tell Acrobat to re-render the form at your command. This effectively repaints the entire form and gets rid of any artifacts (those being the incorrectly rendered column instances). So, after adding or removing column instances, just place the following command:
xfa.layout.relayout();
Please use this with caution, however, since it may adversely affect the performance of your form (since this statement will essentially cause the entire form to be re-rendered every time a column is added/removed). That’s why I check the version of Acrobat that’s rendering the form in my sample so that I know whether I need to apply the workaround or not.
The other workaround is simply to use Acrobat 8.0 to render the form (save is as an “Acrobat 7.0.5 Dynamic PDF Form” but open it in Acrobat 8.0). Acrobat 8.0 now properly renders all instances as they get added or removed.
Fix
Please refer to the Bug List for updated information on the version(s) affected by this bug as well as if and when it was/will be fixed.
Posted by Stefan Cameron on October 28th, 2006
Filed under Acrobat, Bugs, Instance Manager, Scripting, Tables, Tutorials
You can skip to the end and leave a response. Pinging is currently not allowed.


Hi Stefan,
Thank you for your help.
I was wondering, however, if it is possible to extend the goals: to have dynamicall rows at the same time. Is there any way to get the number or rows at a given time. I ask this because we need to know the number of rows to make your current code dynamical and not fixed to 3 rows.
Thank you again.
Sergio,
Tables are flowed containers by definition which means that as long as you make a row “repeatable” (by checking the “Repeat row for each data item” property on the Binding tab in the Object palette), it’ll get an . From there, you can use its count property which will tell you how many instances of that row currently exist:
Table1._TableRow.count
This is very helpful and think the expandable rows and columns will solve 90% of my issue. Just one more left, what happens when the user adds a column
that will move the data off the right margin of the page? Is there a way to tell the form to start a new page when this occurs? And does the instance count continue or
start over?
I’m pretty new to LiveCycle and Jscript is my first programming in 15 years (back then it was Visual Basic for Applications) So thanks for any light you can shed on this.
Brian,
I’m glad you found this article to be helpful.
Unfortunately, vertical breaks aren’t supported and therefore adding a column that will move the data off the right margin of a page will simply result in the column being added but only partially visible (cut-off at the content area on the master page). You may continue to add columns at that point but they just won’t be visible and there isn’t a way that you can cause the column to wrap to the next “line” (underneath the existing ones).
As an alternative, you could use basic subforms organized in columns (to mimic a table but without the full table structure) and placed in a flowed subform with its “Flow Direction” property (on the Subform tab of the Object palette) set to “Western Text” (which means left-to-right). You could then give a specific width to the flowed “container” subform such that a new instance of the “column” subform within it (which would go beyond the container subform’s area) would end-up getting wrapped below and to the left of the existing columns.
Since this is a little difficult to explain in words, I’ve posted a simple form that demonstrates this alternative (requires Designer/Acrobat 7.x or later).
Does that look like what you’re trying to achieve?
Stefan,
I might be able to use this flow, but the user wants the form to be more complicated than simply left to right flowing will do. Let me try to explain the form this way:
Down the page there will be conditions and this number of conditions will be variable (addrow method to get more of them with instance manager)
Across the page will be alternatives and this number is variable (your add column article get these added)
This basically re-creates a spreadsheet grid where each intersection / cell will have a text field and score field.
I have many headaches with this design, but the two major ones are:
1. After I’ve added a row or column; how do I reference that row/column to add instances to other column/rows?
For example. I start the form out and add 3 rows with the instance manager so there are 4 rows on the screen.
Then I create a second column. This only creates the 1st row instance by default; how can I get the other 3 dynamically added instances to show up in the new column.
2. If I add a column that will go off the edge of the page, how can I wrap it with all the appropriate rows included. For this one I am thinking I need to trigger a
new page or page instance somehow.
For example. I have 4 rows and when I add a column; it would go outside the margin. The form should move the new column below with 4 rows under the
heading/first row.
You would think that for my first form, I’d get something easier….
Thanks for your insight.
Brian
Brian,
Hmmm… Sounds complicated. Maybe we should take a step back for a minute to make sure you’re designing this the right way: Is this form essentially supposed to have a spreadsheet layout where each cell of the spreadsheet contains two fields (text and score)? And are you needing to add and/or remove cells individually or could the form ask the user for a grid size and then create the spreadsheet (e.g. the user would specify a “3 x 4″ grid and the form would then create a spreadsheet with 3 rows and 4 columns)?
Stefan,
That is basically the approach I took — redesign the form. I was able to simplify things and get it to be a (relatively) simple row instance form.
For the columns I let the user add pages so the form became instances in instances (IE: each page allows 3 columns so the user can add columns in groups of 3)
This made for a bit more coding but it isn’t bad.
Also, I just got the upgrade to Acrobat 8.1 and it seems to really speed up my forms. Have you experienced the same? And will there be a corrisponding
Acrobat Reader upgrade?
Thanks again for you insight.
Brian
Brian,
I’m glad you got your form to work. Sometimes it helps to start with a clean slate. :)
I gather a lot of customers will notice an increase in responsiveness and rendering speeds of their forms in Acrobat/Reader 8.1. The reason for this is Acrobat/Reader 8.1’s new “Direct Rendering” technology which essentially renders the XFA form directly to the screen rather than relying on the Acrobat Object Model. I’ll be writing an article soon on details about the new renderer. Note that the speed increase will only be on dynamic forms as static forms will require PDF structure — again, more details on that soon.
And yes, there is an 8.1 update for Reader as well.
Stephen is it possible to ask the user how big of a table they want to insert.
I have a table with 4 columns and n number of rows. In the last column there is a button that has insert table. This will merge the 4 columns in the exisiting table and then add to that row a table with user specified dimensions. How do I get the dimensions and insert a table?
Thanks,
Tyler
Tyler,
You can query a user for desired table dimensions using the “xfa.host.response” function:
For example, to get the number of rows and columns, you could do this (show here in JavaScript) in your “insert table” button’s Click event:
That would get you the dimensions for the new table. As far as merging the existing table, I’m not quite certain I understand exactly what you’re trying to do with the old and new tables and what the end-result is supposed to be.
In the end, as per this blog post, you would need a table with a single row and a single column where the column is a repeatable subform and the table row is repeatable as well. In the form’s Initialize event, you would remove the only existing instance of the table using its Instance Manager. When the user clicks on the “insert table” button, you would then add the instance back-in (in order to display it) and you would then add the specified number of rows by adding X number of instances of the repeatable row and you would do the same for the columns, by adding Y number of instances of the repeatable column subform to each row instance that you add.
Stefan,
Your post here on creating table columns was incredibly helpful. However, I seem to have run into a version issue. I am using your method to add new columns to a table - the table has two rows - the first a label (static text box), the second a text field where a user puts in input.
I have tied the creation of the table columns into a drop down box change event - depending on which item is selected in the drop down list, the table creates X number of columns and puts a value in the first row of each - the label.
This works perfectly in 8 but for some reason in 7 I can’t get it to display the new label values. Even if I put actually write in a value in the label before opening the pdf it doesnt’ display - which is odd because everything else I do displays fine - e.g. borders, and cell coloring- it is just the text that won’t show.
It seems to have something to do with adding the subform. If I remove the subform from the table ‘cell’ it displays fine - but of course I can’t then create new ‘columns’.
If I ask the program what the ‘value.text.value’ of the textbox is, it will tell me the right value - but for some reason it just doesn’t display.
The oddity is that if I change the type of field that my label is - e.g. if I make it an actual text field (but make it read only) then I add my values to the rawValue - it works fine. The problem is that it won’t wrap and so my labels don’t show properly.
I hope you are still monitoring this blog, despite its age. Any help would be appreciated.
Thank you.
-Andrew Albrecht
Andrew A.,
I’m glad you found this post helpful.
Unfortunately, I can’t think of why the text in the labels isn’t showing-up in 7 (I assume you mean Acrobat). I do know that some things related to table rendering were fixed in Acrobat 8 so that could be one of the reasons.
As an alternative, I suggest you do use a text field instead of a static text object for the label. You said text doesn’t wrap but if you make that text field support multiple lines (Object palette > Field tab) and set its height to be expandable (Layout palette > Height > Expand to fit), text entered on a single line will wrap! The last thing to do would be to make the text field read-only (Object palette > Value tab > Type property), remove its caption (Layout palette > Caption > Position > None) and remove its content area border (Object palette > Field tab > Appearance > None). That should give you a text field that looks and behaves like a static text object but that renders correctly in Acrobat 7.
Hi experts,
I have an adobe interactive form that shows a table. The table header is repeated for each page.
I have a subform that has a checkbox. When the checkbox is checked, the first table column is to be hidden.
I have written the following script on the click event of the checkbox: The script is in formCalc.
var cnt = xfa.form.form1.subform_main.Table1.Row1.instanceManager.count
cnt = cnt - 1 //zero-based index
var hdr_cnt = xfa.form.form1.subform_main.Table1.HeaderRow.instanceManager.count
if ($.rawValue == 1) then
for i=0 upto cnt do
xfa.form.form1.subform_main.Table1.Row1[i].field1.presence = “hidden”
endfor
for j=0 upto cnt_hdr do
xfa.form.form1.subform_main.Table1.HeaderRow[j].cell1.presence = “hidden”
endfor
else
for i=0 upto cnt do
xfa.form.form1.subform_main.Table1.Row1[i].field1.presence = “visible”
endfor
for j=0 upto cnt_hdr do
xfa.form.form1.subform_main.Table1.HeaderRow[j].cell1.presence = “visible”
endfor
endif
At runtime, When I click on the checkbox, all the rows of column1 are hidden but cell1 of header row is hidden only on the first page. for all other pages, the entire header is displayed.
Stefan can you help me find the solution.
I am using Adobe 8.0.0 and Designer 7.1
Regards,
Reema.
Reema,
Have you tried forcing an update to the form’s layout after you’ve executed your script? It’s possible that subsequent pages, where the header rows are repeated, aren’t getting updated after the first column field has been hidden or made visible. At the end of your script, add the following line:
Please let me know if this resolves the problem.