Next / Previous / Contents / TCC Help System / NM Tech homepage

29.4. Row.fold(): Fold a logical row into physical rows

This method is used by both the XHTML and XSL-FO output sides to build the physical rows of the final table so as to use no more than a given number of detail columns. In the XHTML rendering this reduces or eliminates horizontal scrolling; in the PDF it is necessary to fit on the physical paper.


The actual number of table columns is one greater than the number of detail columns, but the row label column is not involved in the computation of the physical column count. Note that the phrase “detail columns” always refers to one less than the actual table column count.

The general procedure here is:

  1. create a list of empty PhysRow instances, each with no row label, and all detail cells set to empty (SpacerCell);

  2. copy the logical row label into the row label of the first PhysRow and mark it as spanning all of the physical rows;

  3. distribute the logical detail cells into the physical rows from left to right and top to bottom; and finally

  4. distribute the suffix cells into the end of the last physical row, from right to left.
# - - -   R o w . f o l d

    def fold(self, maxPhysCols):
        '''Convert one logical row to one or more physical rows.

          [ maxPhysCols > 0 ->
              return a list of PhysRow instances representing self
              folded into one or more physical rows each with no
              more than (maxPhysCols) detail columns ]

First calculate the number of physical rows required; see Section 29.2, “Row.nPhysRows(): How many physical rows will this logical row occupy?” and Section 29.3, “Row.nPhysCols(): How many physical columns?”. Create rowList, containing empty PhysRow instances.
        #-- 1
        # [ nPhysRows  :=  number of physical rows required to hold
        #       this logical row
        #   nPhysCols  :=  number of physical columns required to
        #       hold this logical row ]
        nPhysRows = self.nPhysRows(maxPhysCols)
        nPhysCols = self.nPhysCols(maxPhysCols)

        #-- 2
        # [ rowList  :=  a list of (nPhysRows) PhysRow instances with
        #       (nPhysCols) columns each, no row label, and all
        #       cells set to SpacerCell instances ]
        rowList = [ PhysRow(nPhysCols)
                    for k in range(nPhysRows) ]

Next, we copy the logical row's label to the first physical row. When rendered, the label cell will span all the following physical rows. The remaining physical rows, if any, will have None as their row label, which tells the output generation logic that the first cell of those rows has been spanned from a previous row; in that case it will generate no corresponding output cell. See Section 45.2, “PhysRow.setLabel().
        #-- 3
        rowList[0].setLabel(self._rowLabel, nPhysRows)

Copying logical detail cells into physical detail cells is straightforward. Index logx steps through the logical cells. For a given cell, its physical location is in row (logx/nPhysCols) and physical column (logx % nPhysCols).
        #-- 4
        # [ rowList  :=  rowList with the detail cells of self copied
        #       from left to right, then top to bottom ]
        for logx in range(self.cbcHist.nCols):
            rowx, colx = divmod(logx, nPhysCols)
            rowList[rowx].setCell(colx, self._cellList[logx])

The suffix cells are also copied into the physical rows, but they occupy the last positions. See Section 29.5, “Row._setSuffixCells(): Copy the suffix cells to physical rows”.
        #-- 5
        # [ rowList  :=  rowList with the last Row.N_SUFFIX_COLS
        #       cells set to the concrete class's suffix
        #       cells ]
        self._setSuffixCells(nPhysRows, nPhysCols, rowList)

        #-- 6
        return rowList