You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

ColumnSetup.java 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  1. /*
  2. * Licensed to the Apache Software Foundation (ASF) under one or more
  3. * contributor license agreements. See the NOTICE file distributed with
  4. * this work for additional information regarding copyright ownership.
  5. * The ASF licenses this file to You under the Apache License, Version 2.0
  6. * (the "License"); you may not use this file except in compliance with
  7. * the License. You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. /* $Id$ */
  18. package org.apache.fop.layoutmgr.table;
  19. import java.util.Iterator;
  20. import java.util.List;
  21. import java.util.ListIterator;
  22. import org.apache.commons.logging.Log;
  23. import org.apache.commons.logging.LogFactory;
  24. import org.apache.fop.datatypes.Length;
  25. import org.apache.fop.datatypes.PercentBaseContext;
  26. import org.apache.fop.fo.FONode;
  27. import org.apache.fop.fo.expr.RelativeNumericProperty;
  28. import org.apache.fop.fo.flow.table.Table;
  29. import org.apache.fop.fo.flow.table.TableColumn;
  30. import org.apache.fop.fo.properties.TableColLength;
  31. import org.apache.fop.traits.Direction;
  32. import org.apache.fop.traits.WritingModeTraits;
  33. import org.apache.fop.traits.WritingModeTraitsGetter;
  34. /**
  35. * Class holding a number of columns making up the column setup of a row.
  36. */
  37. public class ColumnSetup {
  38. /** Logger **/
  39. private static Log log = LogFactory.getLog(ColumnSetup.class);
  40. private Table table;
  41. private WritingModeTraitsGetter wmTraits;
  42. private List columns = new java.util.ArrayList();
  43. private List colWidths = new java.util.ArrayList();
  44. private int maxColIndexReferenced = 0;
  45. /**
  46. * Main Constructor.
  47. * @param table the table to construct this column setup for
  48. */
  49. public ColumnSetup(Table table) {
  50. assert table != null;
  51. this.table = table;
  52. this.wmTraits = WritingModeTraits.getWritingModeTraitsGetter ( table );
  53. prepareColumns();
  54. initializeColumnWidths();
  55. }
  56. private void prepareColumns() {
  57. List rawCols = table.getColumns();
  58. if (rawCols != null) {
  59. int colnum = 1;
  60. ListIterator iter = rawCols.listIterator();
  61. while (iter.hasNext()) {
  62. TableColumn col = (TableColumn)iter.next();
  63. if (col == null) {
  64. continue;
  65. }
  66. colnum = col.getColumnNumber();
  67. for (int i = 0; i < col.getNumberColumnsRepeated(); i++) {
  68. while (colnum > columns.size()) {
  69. columns.add(null);
  70. }
  71. columns.set(colnum - 1, col);
  72. colnum++;
  73. }
  74. }
  75. //Post-processing the list (looking for gaps)
  76. //TODO The following block could possibly be removed
  77. int pos = 1;
  78. ListIterator ppIter = columns.listIterator();
  79. while (ppIter.hasNext()) {
  80. TableColumn col = (TableColumn)ppIter.next();
  81. if (col == null) {
  82. assert false; //Gaps are filled earlier by fo.flow.table.Table.finalizeColumns()
  83. //log.error("Found a gap in the table-columns at position " + pos);
  84. }
  85. pos++;
  86. }
  87. }
  88. }
  89. /**
  90. * Returns a column. If the index of the column is bigger than the number of explicitly
  91. * defined columns the last column is returned.
  92. * @param index index of the column (1 is the first column)
  93. * @return the requested column
  94. */
  95. public TableColumn getColumn(int index) {
  96. int size = columns.size();
  97. if (index > size) {
  98. if (index > maxColIndexReferenced) {
  99. maxColIndexReferenced = index;
  100. TableColumn col = getColumn(1);
  101. if (!(size == 1 && col.isImplicitColumn())) {
  102. assert false; //TODO Seems to be removable as this is now done in the FO tree
  103. log.warn(FONode.decorateWithContextInfo(
  104. "There are fewer table-columns than are needed. "
  105. + "Column " + index + " was accessed, but only "
  106. + size + " columns have been defined. "
  107. + "The last defined column will be reused."
  108. , table));
  109. if (!table.isAutoLayout()) {
  110. log.warn("Please note that according XSL-FO 1.0 (7.26.9) says that "
  111. + "the 'column-width' property must be specified for every "
  112. + "column, unless the automatic table layout is used.");
  113. }
  114. }
  115. }
  116. return (TableColumn) columns.get(size - 1);
  117. } else {
  118. return (TableColumn) columns.get(index - 1);
  119. }
  120. }
  121. /** {@inheritDoc} */
  122. public String toString() {
  123. return columns.toString();
  124. }
  125. /** @return the number of columns in the setup. */
  126. public int getColumnCount() {
  127. if (maxColIndexReferenced > columns.size()) {
  128. return maxColIndexReferenced;
  129. } else {
  130. return columns.size();
  131. }
  132. }
  133. /** @return an Iterator over all columns */
  134. public Iterator iterator() {
  135. return this.columns.iterator();
  136. }
  137. /*
  138. private void createColumnsFromFirstRow() {
  139. //TODO Create oldColumns from first row here
  140. //--> rule 2 in "fixed table layout", see CSS2, 17.5.2
  141. //Alternative: extend oldColumns on-the-fly, but in this case we need the
  142. //new property evaluation context so proportional-column-width() works
  143. //correctly.
  144. if (columns.size() == 0) {
  145. this.columns.add(table.getDefaultColumn());
  146. }
  147. }
  148. */
  149. /**
  150. * Initializes the column's widths
  151. *
  152. */
  153. private void initializeColumnWidths() {
  154. TableColumn col;
  155. Length colWidth;
  156. for (int i = columns.size(); --i >= 0;) {
  157. if (columns.get(i) != null) {
  158. col = (TableColumn) columns.get(i);
  159. colWidth = col.getColumnWidth();
  160. colWidths.add(0, colWidth);
  161. }
  162. }
  163. colWidths.add(0, null);
  164. }
  165. /**
  166. * Works out the base unit for resolving proportional-column-width()
  167. * [p-c-w(x) = x * base_unit_ipd]
  168. *
  169. * @param tlm the TableLayoutManager
  170. * @return the computed base unit (in millipoint)
  171. */
  172. protected double computeTableUnit(TableLayoutManager tlm) {
  173. return computeTableUnit(tlm, tlm.getContentAreaIPD());
  174. }
  175. /**
  176. * Works out the base unit for resolving proportional-column-width()
  177. * [p-c-w(x) = x * base_unit_ipd]
  178. *
  179. * @param percentBaseContext the percent base context for relative values
  180. * @param contentAreaIPD the IPD of the available content area
  181. * @return the computed base unit (in millipoints)
  182. */
  183. public float computeTableUnit(PercentBaseContext percentBaseContext, int contentAreaIPD) {
  184. int sumCols = 0;
  185. float factors = 0;
  186. float unit = 0;
  187. /* calculate the total width (specified absolute/percentages),
  188. * and work out the total number of factors to use to distribute
  189. * the remaining space (if any)
  190. */
  191. for (Iterator i = colWidths.iterator(); i.hasNext();) {
  192. Length colWidth = (Length) i.next();
  193. if (colWidth != null) {
  194. sumCols += colWidth.getValue(percentBaseContext);
  195. if (colWidth instanceof RelativeNumericProperty) {
  196. factors += ((RelativeNumericProperty) colWidth).getTableUnits();
  197. } else if (colWidth instanceof TableColLength) {
  198. factors += ((TableColLength) colWidth).getTableUnits();
  199. }
  200. }
  201. }
  202. /* distribute the remaining space over the accumulated
  203. * factors (if any)
  204. */
  205. if (factors > 0) {
  206. if (sumCols < contentAreaIPD) {
  207. unit = (contentAreaIPD - sumCols) / factors;
  208. } else {
  209. log.warn("No space remaining to distribute over columns.");
  210. }
  211. }
  212. return unit;
  213. }
  214. /**
  215. * Determine the X offset of the indicated column, where this
  216. * offset denotes the left edge of the column irrespective of writing
  217. * mode. If writing mode's column progression direction is right-to-left,
  218. * then the first column is the right-most column and the last column is
  219. * the left-most column; otherwise, the first column is the left-most
  220. * column.
  221. * @param col column index (1 is first column)
  222. * @param nrColSpan number columns spanned (for calculating offset in rtl mode)
  223. * @param context the context for percentage based calculations
  224. * @return the X offset of the requested column
  225. */
  226. public int getXOffset(int col, int nrColSpan, PercentBaseContext context) {
  227. // TODO handle vertical WMs [GA]
  228. if ( (wmTraits != null) && (wmTraits.getColumnProgressionDirection() == Direction.RL) ) {
  229. return getXOffsetRTL(col, nrColSpan, context);
  230. } else {
  231. return getXOffsetLTR(col, context);
  232. }
  233. }
  234. /*
  235. * Determine x offset by summing widths of columns to left of specified
  236. * column; i.e., those columns whose column numbers are greater than the
  237. * specified column number.
  238. */
  239. private int getXOffsetRTL(int col, int nrColSpan, PercentBaseContext context) {
  240. int xoffset = 0;
  241. for (int i = (col + nrColSpan - 1), nc = colWidths.size(); ++i < nc;) {
  242. int effCol = i;
  243. if (colWidths.get(effCol) != null) {
  244. xoffset += ((Length) colWidths.get(effCol)).getValue(context);
  245. }
  246. }
  247. return xoffset;
  248. }
  249. /*
  250. * Determine x offset by summing widths of columns to left of specified
  251. * column; i.e., those columns whose column numbers are less than the
  252. * specified column number.
  253. */
  254. private int getXOffsetLTR(int col, PercentBaseContext context) {
  255. int xoffset = 0;
  256. for (int i = col; --i >= 0;) {
  257. int effCol;
  258. if (i < colWidths.size()) {
  259. effCol = i;
  260. } else {
  261. effCol = colWidths.size() - 1;
  262. }
  263. if (colWidths.get(effCol) != null) {
  264. xoffset += ((Length) colWidths.get(effCol)).getValue(context);
  265. }
  266. }
  267. return xoffset;
  268. }
  269. /**
  270. * Calculates the sum of all column widths.
  271. * @param context the context for percentage based calculations
  272. * @return the requested sum in millipoints
  273. */
  274. public int getSumOfColumnWidths(PercentBaseContext context) {
  275. int sum = 0;
  276. for (int i = 1, c = getColumnCount(); i <= c; i++) {
  277. int effIndex = i;
  278. if (i >= colWidths.size()) {
  279. effIndex = colWidths.size() - 1;
  280. }
  281. if (colWidths.get(effIndex) != null) {
  282. sum += ((Length) colWidths.get(effIndex)).getValue(context);
  283. }
  284. }
  285. return sum;
  286. }
  287. }