table-organize — automatically organize table cells into rows or columns


Attribute Pos. Req. Default Description
cols | columns Yes 2 Number of columns.
rows Optional number of rows. Implies "table" parameter.
columnize Display cells in "newspaper" column order. (Rotate the table — instead of filling rows, fill columns).
min_rows On small result sets, it can be ugly to build more than the necessary number of columns. This option will guarantee a minimum number of rows — columns will change as numbers change. Formula: $num_cells % $opt->{min_rows}.
embed Allows embedding other table elements within tables you want to organize. See more in the section called “DESCRIPTION” and examples.
limit Maximum number of cells to use. Truncates extra cells silently.
table If specified, causes a surrounding HTML <table> </table> to be generated with the specified attributes.
caption Table <caption> container text, if any. (Can be an array).
tr Attributes for table rows. (Can be an array).
td Attributes for table cells. (Can be an array).
pretty Adds newline and TAB characters to provide some reasonable indenting in the HTML source.
filler &nbsp; (non-breaking space) Content to automatically place in empty, "filler" cells. It could be important to provide at least minimal content in there since some browsers do not display empty cells.
font Attributes for HTML <font> inside table cells, if any.
joiner \n\t\t if pretty is specified, none otherwise. Element to use in joining cells. This is mostly used for visual layout in HTML source.
interpolate     1 interpolate input?
reparse     1 interpolate output?
hide     0 Hide the tag return value?


table-organize takes an bunch of table cells and organizes them into rows based on the specified number of columns.

If the number of cells is not on an even modulus of the number of columns, then "filler" cells will be included to keep table structure correct.

Attributes tr, td and caption can be specified as an array (with indexes); if they are, they will alternate according to the modulus. The td array size should always equal the number of columns; if it is bigger, then trailing elements are ignored. If it is smaller, the attribute is ignored altogether.

If you will want to embed other tables inside the table you want to organize, you'll run into an interesting problem; [table-organize] won't know whether <td>s belong to the table you want to arrange or to the "subtable" that should be left intact. To solve this problem, we resort to differentiating them by lowercase <td> and uppercase <TD>. See more in the section called “EXAMPLES”.


This tag does not appear to be affected by, or affect, the rest of Interchange.


Example: Advanced table-organize example

To produce a table that alternates between two row background colors and specifies custom alignment for the three columns, use:

  [loop list="1 2 3 1a 2a 3a 1b"] <td> [loop-code] </td> [/loop]

(In the above example, [loop] tag is used to produce example data for the table cells.) The final result produced will look like this:

  <tr bgcolor="#EEEEEE">
    <td align=right>1</td>
    <td align=center>2</td>
    <td align=left>3</td>
  <tr bgcolor="#FFFFFF">
    <td align=right>1a</td>
    <td align=center>2a</td>
    <td align=left>3a</td>
  <tr bgcolor="#EEEEEE">
    <td align=right>1b</td>
    <td align=center>&nbsp;</td>
    <td align=left>&nbsp;</td>

If you also provide the columnize=1 attribute, the result will be a "rotated" table:

  <tr bgcolor="#EEEEEE">
    <td align=right>1</td>
    <td align=center>1a</td>
    <td align=left>1b</td>
  <tr bgcolor="#FFFFFF">
    <td align=right>2</td>
    <td align=center>2a</td>
    <td align=left>&nbsp;</td>
  <tr bgcolor="#EEEEEE">
    <td align=right>3</td>
    <td align=center>3a</td>
    <td align=left>&nbsp;</td>

Example: Embedding tables

To embed tables, make sure the table you want to organize uses lowercase <td> and set attribute embed=lc. To invert the meaning and make uppercase <TD>s arranged (ignoring lower- or mixed-case cells), set the embed attribute to any other true value except lc (embed=uc will work well).

  [table-organize embed=lc]
    <TD>something embedded</TD>


  [table-organize embed=uc]



table-organize is available in Interchange versions:

4.6.0-5.9.0 (git-head)


Interchange 5.9.0:

Source: code/UserTag/table_organize.tag
Lines: 185

# Copyright 2002-2007 Interchange Development Group and others
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.  See the LICENSE file for details.
# $Id: table_organize.tag,v 1.11 2007-11-05 20:15:27 docelic Exp $

UserTag table-organize Order         cols
UserTag table-organize attrAlias     columns cols
UserTag table-organize Interpolate
UserTag table-organize addAttr
UserTag table-organize hasEndTag
UserTag table-organize Version       $Revision: 1.11 $
UserTag table-organize Routine <<EOR
sub {
my ($cols, $opt, $body) = @_;
$cols = int($cols) || 2;
$body =~ s/(.*?)(<td)\b/$2/is
  or return;
my $out = $1;
$body =~ s:(</td>)(?!.*</td>)(.*):$1:is;
my $postamble = $2;

my @cells;
if($opt->{cells} and ref($opt->{cells}) eq 'ARRAY') {
  @cells = @{$opt->{cells}};
elsif($opt->{embed}) {
  if($opt->{embed} eq 'lc') {
    push @cells, $1 while $body =~ s:(<td\b.*?</td>)::s;
  else {
    push @cells, $1 while $body =~ s:(<TD\b.*?</TD>)::s;
else {
  push @cells, $1 while $body =~ s:(<td\b.*?</td>)::is;

while ($opt->{min_rows} and ($opt->{min_rows} * ($cols - 1)) > scalar(@cells) ) {
  last if $cols == 1;

if(int($opt->{limit}) and $opt->{limit} < scalar(@cells) ) {
  splice(@cells, $opt->{limit});

for(qw/ table/) {
  $opt->{$_} = defined $opt->{$_} ? " $opt->{$_}" : '';

my @td;

if(! $opt->{td}) {
  @td = '' x $cols;
elsif (ref $opt->{td} ) {
  @td = @{$opt->{td}};
  push @td, '' while scalar(@td) < $cols;
else {
  @td = (" $opt->{td}") x $cols;

my %attr;
for(qw/caption tr pre post/) {
  if( ! $opt->{$_} ) {
    #do nothing
  elsif (ref $opt->{$_}) {
    $attr{$_} = $opt->{$_};
  else {
    $attr{$_} = [$opt->{$_}];

my $pretty = $opt->{pretty};

#$opt->{td} =~ s/^(\S)/ $1/;
#$opt->{tr} =~ s/^(\S)/ $1/;

my @rest;
my $rows;

my $rmod;
my $tmod = 0;
my $total_mod;

$opt->{filler} = '&nbsp;' if ! defined $opt->{filler};

my $td_beg;
my $td_end;
if($opt->{font}) {
  $td_beg = qq{<FONT $opt->{font}>};
  $td_end = qq{</FONT>};

if($rows = int($opt->{rows}) ) {
  $total_mod = $rows * $cols;
  @rest = splice(@cells, $total_mod)
    if $total_mod < @cells;
  $opt->{table} = ' ' if ! $opt->{table};

my $joiner = $opt->{joiner} || ($pretty ? "\n\t\t" : "");
while(@cells) {
  if ($opt->{columnize}) {
    my $cell_count = scalar @cells;
    my $row_count_ceil = POSIX::ceil($cell_count / $cols);
    my $row_count_floor = int($cell_count / $cols);
    my $remainder = $cell_count % $cols;
    my @tmp = splice(@cells, 0);
    my $index;
    for (my $r = 0; $r < $row_count_ceil; $r++) {
      for (my $c = 0; $c < $cols; $c++) {
        if ($c >= $remainder + 1) {
          $index = $r + $row_count_floor * $c + $remainder;
        else {
          $index = $r + $row_count_ceil * $c;
        push @cells, $tmp[$index];
        last if $r + 1 == $row_count_ceil and $c + 1 == $remainder;

  while (scalar(@cells) % $cols) {
    push @cells, "<td>$opt->{filler}</td>";

  #$out .= "<!-- starting table tmod=$tmod -->";
  if($opt->{table}) {
    $out .= "<table$opt->{table}>";
    $out .= "\n" if $pretty;
    if($opt->{caption}) {
      my $idx = $tmod % scalar(@{$attr{caption}});
      #$out .= "<!-- caption index $idx -->";
      $out .= "\n" if $pretty;
      $out .= "<caption>" . $attr{caption}[$idx] . "</caption>";
      $out .= "\n" if $pretty;
  $rmod = 0;
  while(@cells) {
    $out .= "\t" if $pretty;
    $out .= "<tr";
    if($opt->{tr}) {
      my $idx = $rmod % scalar(@{$attr{tr}});
      $out .= " " . $attr{tr}[$idx];
    $out .= ">";
    $out .= "\n\t\t" if $pretty;
    my @op =  splice (@cells, 0, $cols);
    if($opt->{td}) {
      for ( my $i = 0; $i < $cols; $i++) {
        $op[$i] =~ s/(<td)/$1 $td[$i]/i;
    @op = map { s/>/>$td_beg/; $_ }       @op  if $td_beg;
    @op = map { s/(<[^<]+)$/$td_end$1/; $_ } @op  if $td_end;

    $out .= join($joiner, @op);
    $out .= "\n\t" if $pretty;
    $out .= "</tr>";
    $out .= "\n" if $pretty;
  if($opt->{table}) {
    $out .= "</table>";
    $out .= "\n" if $pretty;
  if(@rest) {
    my $num = $total_mod < scalar(@rest) ? $total_mod : scalar(@rest);
    @cells = splice(@rest, 0, $num);
return $out . $postamble;


Interchange Development Group


DocBook! Interchange!