• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * This file is part of the HTML rendering engine for KDE.
3  *
4  * Copyright (C) 2002 Lars Knoll (knoll@kde.org)
5  *           (C) 2002 Dirk Mueller (mueller@kde.org)
6  * Copyright (C) 2003, 2006 Apple Computer, Inc.
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Library General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Library General Public License
19  * along with this library; see the file COPYING.LIB.  If not, write to
20  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21  * Boston, MA 02110-1301, USA.
22  */
23 
24 #include "config.h"
25 #include "FixedTableLayout.h"
26 
27 #include "RenderTable.h"
28 #include "RenderTableCell.h"
29 #include "RenderTableCol.h"
30 #include "RenderTableSection.h"
31 
32 /*
33   The text below is from the CSS 2.1 specs.
34 
35   Fixed table layout
36 
37   With this (fast) algorithm, the horizontal layout of the table does
38   not depend on the contents of the cells; it only depends on the
39   table's width, the width of the columns, and borders or cell
40   spacing.
41 
42   The table's width may be specified explicitly with the 'width'
43   property. A value of 'auto' (for both 'display: table' and 'display:
44   inline-table') means use the automatic table layout algorithm.
45 
46   In the fixed table layout algorithm, the width of each column is
47   determined as follows:
48 
49     1. A column element with a value other than 'auto' for the 'width'
50     property sets the width for that column.
51 
52     2. Otherwise, a cell in the first row with a value other than
53     'auto' for the 'width' property sets the width for that column. If
54     the cell spans more than one column, the width is divided over the
55     columns.
56 
57     3. Any remaining columns equally divide the remaining horizontal
58     table space (minus borders or cell spacing).
59 
60   The width of the table is then the greater of the value of the
61   'width' property for the table element and the sum of the column
62   widths (plus cell spacing or borders). If the table is wider than
63   the columns, the extra space should be distributed over the columns.
64 
65 
66   In this manner, the user agent can begin to lay out the table once
67   the entire first row has been received. Cells in subsequent rows do
68   not affect column widths. Any cell that has content that overflows
69   uses the 'overflow' property to determine whether to clip the
70   overflow content.
71 */
72 
73 using namespace std;
74 
75 namespace WebCore {
76 
FixedTableLayout(RenderTable * table)77 FixedTableLayout::FixedTableLayout(RenderTable* table)
78     : TableLayout(table)
79 {
80 }
81 
calcWidthArray(int)82 int FixedTableLayout::calcWidthArray(int)
83 {
84     int usedWidth = 0;
85 
86     // iterate over all <col> elements
87     RenderObject* child = m_table->firstChild();
88     int nEffCols = m_table->numEffCols();
89     m_width.resize(nEffCols);
90     m_width.fill(Length(Auto));
91 
92     int currentEffectiveColumn = 0;
93     Length grpWidth;
94     while (child) {
95         if (child->isTableCol()) {
96             RenderTableCol* col = toRenderTableCol(child);
97             if (col->firstChild())
98                 grpWidth = col->style()->width();
99             else {
100                 Length w = col->style()->width();
101                 if (w.isAuto())
102                     w = grpWidth;
103                 int effWidth = 0;
104                 if (w.isFixed() && w.value() > 0)
105                     effWidth = w.value();
106 
107                 int span = col->span();
108                 while (span) {
109                     int spanInCurrentEffectiveColumn;
110                     if (currentEffectiveColumn >= nEffCols) {
111                         m_table->appendColumn(span);
112                         nEffCols++;
113                         m_width.append(Length());
114                         spanInCurrentEffectiveColumn = span;
115                     } else {
116                         if (span < m_table->spanOfEffCol(currentEffectiveColumn)) {
117                             m_table->splitColumn(currentEffectiveColumn, span);
118                             nEffCols++;
119                             m_width.append(Length());
120                         }
121                         spanInCurrentEffectiveColumn = m_table->spanOfEffCol(currentEffectiveColumn);
122                     }
123                     if ((w.isFixed() || w.isPercent()) && w.isPositive()) {
124                         m_width[currentEffectiveColumn].setRawValue(w.type(), w.rawValue() * spanInCurrentEffectiveColumn);
125                         usedWidth += effWidth * spanInCurrentEffectiveColumn;
126                     }
127                     span -= spanInCurrentEffectiveColumn;
128                     currentEffectiveColumn++;
129                 }
130             }
131             toRenderTableCol(child)->calcPrefWidths();
132         } else
133             break;
134 
135         RenderObject* next = child->firstChild();
136         if (!next)
137             next = child->nextSibling();
138         if (!next && child->parent()->isTableCol()) {
139             next = child->parent()->nextSibling();
140             grpWidth = Length();
141         }
142         child = next;
143     }
144 
145     // Iterate over the first row in case some are unspecified.
146     RenderTableSection* section = m_table->header();
147     if (!section)
148         section = m_table->firstBody();
149     if (!section)
150         section = m_table->footer();
151     if (section && !section->numRows())
152         section = m_table->sectionBelow(section, true);
153     if (section) {
154         int cCol = 0;
155         RenderObject* firstRow = section->firstChild();
156         child = firstRow->firstChild();
157         while (child) {
158             if (child->isTableCell()) {
159                 RenderTableCell* cell = toRenderTableCell(child);
160                 if (cell->prefWidthsDirty())
161                     cell->calcPrefWidths();
162 
163                 Length w = cell->styleOrColWidth();
164                 int span = cell->colSpan();
165                 int effWidth = 0;
166                 if (w.isFixed() && w.isPositive())
167                     effWidth = w.value();
168 
169                 int usedSpan = 0;
170                 int i = 0;
171                 while (usedSpan < span) {
172                     ASSERT(cCol + i < nEffCols);
173                     int eSpan = m_table->spanOfEffCol(cCol + i);
174                     // Only set if no col element has already set it.
175                     if (m_width[cCol + i].isAuto() && w.type() != Auto) {
176                         m_width[cCol + i].setRawValue(w.type(), w.rawValue() * eSpan / span);
177                         usedWidth += effWidth * eSpan / span;
178                     }
179                     usedSpan += eSpan;
180                     i++;
181                 }
182                 cCol += i;
183             }
184             child = child->nextSibling();
185         }
186     }
187 
188     return usedWidth;
189 }
190 
calcPrefWidths(int & minWidth,int & maxWidth)191 void FixedTableLayout::calcPrefWidths(int& minWidth, int& maxWidth)
192 {
193     // FIXME: This entire calculation is incorrect for both minwidth and maxwidth.
194 
195     // we might want to wait until we have all of the first row before
196     // layouting for the first time.
197 
198     // only need to calculate the minimum width as the sum of the
199     // cols/cells with a fixed width.
200     //
201     // The maximum width is max(minWidth, tableWidth).
202     int bs = m_table->bordersPaddingAndSpacing();
203 
204     int tableWidth = m_table->style()->width().isFixed() ? m_table->style()->width().value() - bs : 0;
205     int mw = calcWidthArray(tableWidth) + bs;
206 
207     minWidth = max(mw, tableWidth);
208     maxWidth = minWidth;
209 }
210 
layout()211 void FixedTableLayout::layout()
212 {
213     int tableWidth = m_table->width() - m_table->bordersPaddingAndSpacing();
214     int nEffCols = m_table->numEffCols();
215     Vector<int> calcWidth(nEffCols, 0);
216 
217     int numAuto = 0;
218     int autoSpan = 0;
219     int totalFixedWidth = 0;
220     int totalPercentWidth = 0;
221     int totalRawPercent = 0;
222 
223     // Compute requirements and try to satisfy fixed and percent widths.
224     // Percentages are of the table's width, so for example
225     // for a table width of 100px with columns (40px, 10%), the 10% compute
226     // to 10px here, and will scale up to 20px in the final (80px, 20px).
227     for (int i = 0; i < nEffCols; i++) {
228         if (m_width[i].isFixed()) {
229             calcWidth[i] = m_width[i].value();
230             totalFixedWidth += calcWidth[i];
231         } else if (m_width[i].isPercent()) {
232             calcWidth[i] = m_width[i].calcValue(tableWidth);
233             totalPercentWidth += calcWidth[i];
234             totalRawPercent += m_width[i].rawValue();
235         } else if (m_width[i].isAuto()) {
236             numAuto++;
237             autoSpan += m_table->spanOfEffCol(i);
238         }
239     }
240 
241     int hspacing = m_table->hBorderSpacing();
242     int totalWidth = totalFixedWidth + totalPercentWidth;
243     if (!numAuto || totalWidth > tableWidth) {
244         // If there are no auto columns, or if the total is too wide, take
245         // what we have and scale it to fit as necessary.
246         if (totalWidth != tableWidth) {
247             // Fixed widths only scale up
248             if (totalFixedWidth && totalWidth < tableWidth) {
249                 totalFixedWidth = 0;
250                 for (int i = 0; i < nEffCols; i++) {
251                     if (m_width[i].isFixed()) {
252                         calcWidth[i] = calcWidth[i] * tableWidth / totalWidth;
253                         totalFixedWidth += calcWidth[i];
254                     }
255                 }
256             }
257             if (totalRawPercent) {
258                 totalPercentWidth = 0;
259                 for (int i = 0; i < nEffCols; i++) {
260                     if (m_width[i].isPercent()) {
261                         calcWidth[i] = m_width[i].rawValue() * (tableWidth - totalFixedWidth) / totalRawPercent;
262                         totalPercentWidth += calcWidth[i];
263                     }
264                 }
265             }
266             totalWidth = totalFixedWidth + totalPercentWidth;
267         }
268     } else {
269         // Divide the remaining width among the auto columns.
270         int remainingWidth = tableWidth - totalFixedWidth - totalPercentWidth - hspacing * (autoSpan - numAuto);
271         int lastAuto = 0;
272         for (int i = 0; i < nEffCols; i++) {
273             if (m_width[i].isAuto()) {
274                 int span = m_table->spanOfEffCol(i);
275                 int w = remainingWidth * span / autoSpan;
276                 calcWidth[i] = w + hspacing * (span - 1);
277                 remainingWidth -= w;
278                 if (!remainingWidth)
279                     break;
280                 lastAuto = i;
281                 numAuto--;
282                 autoSpan -= span;
283             }
284         }
285         // Last one gets the remainder.
286         if (remainingWidth)
287             calcWidth[lastAuto] += remainingWidth;
288         totalWidth = tableWidth;
289     }
290 
291     if (totalWidth < tableWidth) {
292         // Spread extra space over columns.
293         int remainingWidth = tableWidth - totalWidth;
294         int total = nEffCols;
295         while (total) {
296             int w = remainingWidth / total;
297             remainingWidth -= w;
298             calcWidth[--total] += w;
299         }
300         if (nEffCols > 0)
301             calcWidth[nEffCols - 1] += remainingWidth;
302     }
303 
304     int pos = 0;
305     for (int i = 0; i < nEffCols; i++) {
306         m_table->columnPositions()[i] = pos;
307         pos += calcWidth[i] + hspacing;
308     }
309     int colPositionsSize = m_table->columnPositions().size();
310     if (colPositionsSize > 0)
311         m_table->columnPositions()[colPositionsSize - 1] = pos;
312 }
313 
314 } // namespace WebCore
315