Dear Wiki user, You have subscribed to a wiki page or wiki category on "Xmlgraphics-fop Wiki" for change notification.
The "AutoTableLayout" page has been changed by GregorBerg: https://wiki.apache.org/xmlgraphics-fop/AutoTableLayout?action=diff&rev1=7&rev2=8 Comment: more details all over the place, only nested tables are left * w("abc") = width of the string "abc" * LM as abbreviation for !LayoutManager + * PGU as abbreviation for !PrimaryGridUnit == Simple Tables == === Without Breaks === @@ -33, +34 @@ ||a ||ab ||a b c || - The first row is checked and the width of each column is set to the width of its first table cell, thus: min/opt/max = w(character). Afterwards, the second row is inspected. + The first row is checked and the width of each column is set to the width of its first table cell, thus: min/opt/max = w(character). During the determination of a cell's width, its PGU is inspected in the TableContentLM's setLength(PGU) to obtain the minimal (''minIPD'') and maximal (''ipd'') width. Afterwards, the second row is inspected. * column 1: min/opt/max are identical for the second table-cell and, therefore, the column width remains the same * column 2: since this table cell contains two '''consecutive''' characters, its minimum increases to their width. Again, the maximum width is identical and this column is set to min/opt/max = w(two characters) * column 3: for this table cell, the ''minimum ''value is the width of the widest letter since FOP can insert a linebreak after each individual letter. The ''maximum ''and ''optimal'' value, however, are w("a b c") - After the table was inspected, each column has a !MinOptMax instance representing its viable widths. Since the sum of all max values is less than the available space, the opt value (initialized as ''max'') does not need to be changed. All columns are rendered using their respective opt/max value + After the table was inspected, each column has a !MinOptMax instance representing its viable widths. Since the sum of all max values is less than the available space, the opt value (initialized as ''max'') does not need to be changed. All columns are rendered using their respective opt value (in this case, opt==max). === With Breaks === ||<tablewidth="200px">a ||b ||c || @@ -55, +56 @@ * max = w("a b c d") * opt (for now) = w("a b c d") - Again, each column has a !MinOptMax instance representing its viable widths. In this case, however, the sum of max values is greater than the available space. The available space has to redistributed among the columns (cf. !ColumnSetup's redistribute()). + Again, each column has a !MinOptMax instance representing its viable widths. In this case, however, the sum of max values is greater than the available space (checked in computeOptimalColumnWidthsForAutoLayout()). The available space has to redistributed among the columns (cf. !ColumnSetup's redistribute()). Out of a list of all columns, FOP removes those which cannot be shrinked. At first, FOP excludes any columns containing only consecutive letters (i.e. min == max, in the example the first two columns) and columns which have a static width. Then, the remaining columns are set to math.max(column's min value, column's max value * shrink factor) - thus, they are either shrinked as far as possible or set to their min value. Opt values are assigned to these columns (iteratively) until the sum of their opt values is <= the available space. @@ -64, +65 @@ '''Note: '''Columns with a static width (e.g. "2cm") are never resized. === Not Enough Space === - In case the sum of all min values is greater than the available space, a warning ('columnsInAutoTableTooWide') is produced. FOP sets all columns to their respective min value and will most probably produce an overflow. + In case the sum of all min values is greater than the available space, a warning is displayed ('columnsInAutoTableTooWide', produced in computeOptimalColumnWidthsForAutoLayout()). FOP sets all columns to their respective min value and will most probably produce an overflow. == Cells Spanning Rows and Columns == While tables containing table cells employing row-span values are not a problem (''please have a look at'' [[https://issues.apache.org/jira/browse/FOP-2451|FOP-2451]] ''for a related patch''), col-span table cells have to be taken into account. FOP produces a !PrimaryGridUnit (PGU) for each table cell. If this table cell happens to span y rows and x columns, the corresponding x*y-1 !GridUnits are created to fill the model of the table with placeholders. Since FOP inspects a table from left to right (using a !TableRowIterator) and from top to bottom (using a lot of them), the PGU is always encountered first during the FOP's column width determination. Spanned rows do not have an impact on the column's width since (after the content of the PGU was already processed) they do not carry additional content. For spanned columns, on the other hand, the following three cases exist. - === Balanced Expansion/Reduction === + === Balanced Expansion === - ||<tablewidth="auto">a ||b ||c || + ||<tablewidth="auto"width="15px">a ||b ||c || ||||<style="text-align:center">abc abc abc ||a b c || - In case a table cell spans several columns, its !MinOptMax determination is different. In the above example, the first row leads to columns with !MinOptMax values of w(a), w(b), and w(c), respectively. In the second row, FOP encounters a cell which has a min of w("abc") and a max of w("abc abc abc"), all of which are greater than the max widths of the spanned columns (=w(a) + w(b) + indentations between these spanned columns). Since the spanned columns are supposed to display the content of the spanning table cell as well, their potential widths need to be adapted. This is done when the corresponding PGU is processed in !TableContentLM's setBaseLength(). + In case a table cell spans several columns, its !MinOptMax determination is different. In the above example, the first row leads to columns with !MinOptMax values of 15 points (static column), w(b), and w(c), respectively. - * calculate sum of the min and max - * TODO: algorithm for expanding columns until their combined width suffices for cells spanning these columns (+ reduction) + In the second row, FOP encounters a cell which has a min of w("abc") and a max of w("abc abc abc"), all of which are greater than the max widths of the spanned columns (=w(a) + w(b) + indentations between these spanned columns). Since the spanned columns are supposed to display the content of the spanning table cell as well, their potential widths need to be adapted. This is done when the corresponding PGU is processed in TableContentLM's setBaseLength(PGU). + + First of all, the minimal spanned width (minSpanWidth := ''sum of min widths of all spanned columns'') and the optimal spanned width (availableSpanWidth := ''sum of opt widths of all spanned columns'') are calculated. Based on the dimension of the PGU's content, FOP already knows the spanning table cell's minimal width (''minIPD := w("abc")'') and its optimal width (''ipd := w("abc abc abc")''). + + FOP determines that the spanned columns need to be widened since their minimal width (''isNotSufficientForMinIPD := minSpanWidth < minIPD'') and optimal width (''isNotSufficientForIPD := availableSpanWidth < ipd'') are insufficient. From the spanned columns (a and b in the example), FOP selects those which are not static (only b) and, thus, can be widened. If no column can be found, a warning is displayed. + + If the minimum width is unavailable, all selected table columns' minimal width values (and their opt/max values) are increased (each gets a proportional cut of the missing space, cf. increaseMinimumWidthOfSpannedColumns()) to avoid an overflow. + + The space gained through this increase is counted towards the ''sum of opt widths of all spanned columns''. If'' ''this optimal width is still insufficient, the opt/max widths of the selected columns is increased even more (increaseOptimalWidthOfSpannedColumns()). + + In the example, column b would be the only column to widen - it would end up with an optimal width of w("abc abc abc") - (w("a") + Indentations). Of course, these are only the optimal values. If the table's optimal width would be too big, the columns would be reduced as far as possible as described before. In this case, the spanned columns (only column b) would provide their new minimal width (w("abc") - (w("a") + Indentations)). Thus, the described injection of new min, opt and max widths ensures that the width of table cells spanning multiple columns is always distributed as equally as possible among the columns which are spanned. Also, this distribution approach works quite robust - a column may be spanned by an arbitrary number of cells in almost any configuration imaginable. One exception is described in the sections "Worst Case". + + An example illustrating a complex case can be found in issue [[https://issues.apache.org/jira/browse/FOP-2450|FOP-2450]] (resize-all-but-static-spanned-columns.xml/pdf). === Special Case: requiresSecondRun === + There is a special case for the distribution approach described above (at least in the current implementation of the patch - it may and should be fixed for increased performance). The approach described above extends columns which already have a !MinOptMax instance assigned to them. This is not possible if the very first row of a table contains a table cell spanning multiple rows (see table in this section) - at this point, the necessary data structures are not initialized. Therefore, the current implementation explicitly checks for this case (TableContentLM's iterateOverTableRows(): ''is there PGU which spans multiple columns in the first row?''), skips this PGU, and sets a flag (''requiresSecondDeterminationRun = true;'') informing the table's TableLM to start the determination again (twice the effort for this case!). '''Potential improvement:''' ''instead of revisiting the whole table, simply go back to the first row (if it fulfills the special case) and recalculate this row only before finishing the determination. '' - '''TODO:''' special case if the very first row contains a table cell spanning multiple columns. - ||||<tablewidth="auto"style="text-align:center">abc abc abc ||a b c || ||a ||b ||c || - '''Potential improvement:''' instead of starting over, simply go back to the first row and recalculate *only this row* before finishing the determination. + === Worst Case === + In a complex table, table cells spanning over multiple columns may overlap not only one, but constantly over one or more columns - HTML tables ignore this case, so here is an ASCII example: - In this case, FOP encounters a column for which no !PrimaryGridUnit can be found, except for the one in the first row. This case is ignored in HTML tables - column b is simply ignored... - ||||<tablewidth="auto"style="text-align:center">ab (rowspan=2) ||c || - ||a ||||<style="text-align:center">bc (rowspan=2) || - - - - text + {{{ + ============= + | abcde | c | + ============= + | a | abcde | + ============= + ^a ^b ^c + }}} + In this case, FOP has to cope with a column (''b'') for which no !PrimaryGridUnit can be found. Since the width determination of table cells is based on PGUs, no exact determination is possible. In its current implementation, the patch seems to favor such columns over any other column if spanned columns have to be widened, which can lead to imbalanced expansions. == Nested Tables == So far, all scenarios only described the handling of isolated tables. However, nested tables are also quite common and, depending on their kind, require a specific treatment. === Auto Table in Fixed Table === - This case is quite trivial: FOP knows the exact dimensions of the surrounding table cell (which either has a static width of is exactly one table-column unit) and, thus, can determine the optimal column widths for the available space. + This case is quite trivial: FOP knows the exact dimensions of the surrounding table cell (which either has a static width or requires exactly one table-column unit) and, thus, can determine the optimal widths for the nested table's columns based on the available space. === Fixed Table in Auto Table === - If an auto table's table cell contains a fixed table, things get a little bit complicated. + If an auto table's table cell contains a fixed table, things get a little bit more complicated. The surrounding auto table needs to know the min and max widths of its columns, i.e. of each individual table cell. Only then will it be able to determine its optimal column width which, in turn, is required to inform the nested table how much space is has available. Luckily, the determination of the minimal the nested table's TableLM can determine the minimal width independently. So, when the nested TableLM's getMinimumIPD() is invoked, it returns the following result as its minimal required width for a complete table (cf. getMinimumIPDforFixedLayout()): * sum of all static columns + number of dynamic columns * (highest minimal value of all dynamic columns) - '''TODO''''': max width via ''getNextKnuthElements ''in !TableCellLM -- table cell demands more space when its childLMs request more space than it allocated for them (#226) '' + '''TODO''''': max width via ''getNextKnuthElements ''in TableCellLM -- table cell demands more space when its childLMs request more space than it allocated for them (#226) '' === Auto Table in Auto Table === Again, the surrounding table needs to know the required space for the contained one while the contained table needs to know how much is available. --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
