1 // Copyright (C) 2000-2007, Luca Padovani <padovani@sti.uniurb.it>.
2 //
3 // This file is part of GtkMathView, a flexible, high-quality rendering
4 // engine for MathML documents.
5 //
6 // GtkMathView is free software; you can redistribute it and/or modify it
7 // under the terms of the GNU Lesser General Public License as published
8 // by the Free Software Foundation; either version 3 of the License, or
9 // (at your option) any later version.
10 //
11 // GtkMathView is distributed in the hope that it will be useful, but
12 // WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 // Lesser General Public License for more details.
15 //
16 // You should have received a copy of the GNU Lesser General Public License
17 // along with this program. If not, see <http://www.gnu.org/licenses/>.
18
19 #include <config.h>
20
21 #include <algorithm>
22 #include <cassert>
23
24 #include "Adapters.hh"
25 #include "MathMLTableFormatter.hh"
26 #include "MathMLValueConversion.hh"
27 #include "FormattingContext.hh"
28 #include "MathGraphicDevice.hh"
29
MathMLTableFormatter()30 MathMLTableFormatter::MathMLTableFormatter()
31 { }
32
~MathMLTableFormatter()33 MathMLTableFormatter::~MathMLTableFormatter()
34 { }
35
36 #include "BoundingBoxAux.hh"
37 #include "scaledAux.hh"
38
39 void
setWidthSpec(const FormattingContext & ctxt,const Length & spec)40 MathMLTableFormatter::Column::setWidthSpec(const FormattingContext& ctxt, const Length& spec)
41 {
42 if (spec.type == Length::PERCENTAGE_UNIT)
43 setWidthSpec(spec.value / 100.0);
44 else
45 setWidthSpec(ctxt.MGD()->evaluate(ctxt, spec, scaled::zero()));
46 }
47
48 void
setHeightSpec(const FormattingContext & ctxt,const Length & spec)49 MathMLTableFormatter::Row::setHeightSpec(const FormattingContext& ctxt, const Length& spec)
50 {
51 if (spec.type == Length::PERCENTAGE_UNIT)
52 setHeightSpec(spec.value / 100.0);
53 else
54 setHeightSpec(ctxt.MGD()->evaluate(ctxt, spec, scaled::zero()));
55 }
56
57 void
init(const FormattingContext & ctxt,unsigned nR,unsigned nC,const std::vector<SmartPtr<MathMLTableCellElement>> & cell,const std::vector<SmartPtr<MathMLTableCellElement>> & label,const SmartPtr<Value> & columnWidthV,const SmartPtr<Value> & rowSpacingV,const SmartPtr<Value> & columnSpacingV,const SmartPtr<Value> & frameV,const SmartPtr<Value> & frameSpacingV,const SmartPtr<Value> & equalRowsV,const SmartPtr<Value> & equalColumnsV,const SmartPtr<Value> & sideV,const SmartPtr<Value> & minLabelSpacingV,const SmartPtr<Value> & alignV)58 MathMLTableFormatter::init(const FormattingContext& ctxt,
59 unsigned nR,
60 unsigned nC,
61 const std::vector<SmartPtr<MathMLTableCellElement> >& cell,
62 const std::vector<SmartPtr<MathMLTableCellElement> >& label,
63 const SmartPtr<Value>& columnWidthV,
64 const SmartPtr<Value>& rowSpacingV,
65 const SmartPtr<Value>& columnSpacingV,
66 const SmartPtr<Value>& frameV,
67 const SmartPtr<Value>& frameSpacingV,
68 const SmartPtr<Value>& equalRowsV,
69 const SmartPtr<Value>& equalColumnsV,
70 const SmartPtr<Value>& sideV,
71 const SmartPtr<Value>& minLabelSpacingV,
72 const SmartPtr<Value>& alignV)
73 {
74 axis = ctxt.MGD()->axis(ctxt);
75
76 nRows = nR;
77 nColumns = nC;
78
79 tableAlign = ToTokenId(GetComponent(alignV, 0));
80 if (IsEmpty(GetComponent(alignV, 1)))
81 tableAlignRow = 0;
82 else
83 tableAlignRow = ToInteger(GetComponent(alignV, 1));
84
85 const TokenId frame = ToTokenId(frameV);
86 const TokenId side = ToTokenId(sideV);
87 const bool hasFrame = frame == T_SOLID || frame == T_DASHED;
88 const bool hasLabels = std::find_if(label.begin(), label.end(), NotNullPredicate<MathMLTableCellElement>()) != label.end();
89
90 const unsigned nGridRows = (hasFrame ? 2 : 0) + ((nRows > 0) ? (nRows * 2 - 1) : 0);
91 const unsigned nGridColumns = (hasFrame ? 2 : 0) + (hasLabels ? 4 : 0) + ((nColumns > 0) ? (nColumns * 2 - 1) : 0);
92 const unsigned contentColumnOffset = (hasFrame ? 1 : 0) + (hasLabels ? 2 : 0);
93 const unsigned contentRowOffset = (hasFrame ? 1 : 0);
94 const unsigned leftLabelOffset = (hasFrame ? 1 : 0);
95 const unsigned rightLabelOffset = (hasFrame ? 1 : 0) + nColumns * 2 + 2;
96 const unsigned labelOffset = (side == T_LEFT || side == T_LEFTOVERLAP) ? leftLabelOffset : rightLabelOffset;
97
98 equalRows = ToBoolean(equalRowsV);
99 equalColumns = ToBoolean(equalColumnsV);
100
101 assert(nGridRows >= 0);
102 assert(nGridColumns >= 0);
103
104 #if 0
105 std::cerr << "CI SIAMO: " << nRows << "x" << nColumns << std::endl
106 << "grid: " << nGridRows << "x" << nGridColumns << std::endl
107 << "frame? " << hasFrame << " labels? " << hasLabels << std::endl;
108 #endif
109
110 std::vector<Row>(nGridRows).swap(rows);
111 std::vector<Column>(nGridColumns).swap(columns);
112 std::vector<Cell>(nGridRows * nGridColumns).swap(cells);
113
114 //std::cerr << "HAS FRAME?" << hasFrame << std::endl;
115 if (hasFrame)
116 {
117 const Length hFrameSpacing = resolveLength(ctxt, frameSpacingV, 0);
118 const Length vFrameSpacing = resolveLength(ctxt, frameSpacingV, 1);
119 rows.front().setHeightSpec(ctxt, vFrameSpacing);
120 rows.back().setHeightSpec(ctxt, vFrameSpacing);
121 columns.front().setWidthSpec(ctxt, hFrameSpacing);
122 columns.back().setWidthSpec(ctxt, hFrameSpacing);
123 }
124
125 //std::cerr << "HAS LABELS?" << hasLabels << std::endl;
126 if (hasLabels)
127 {
128 const Length minLabelSpacing = resolveLength(ctxt, minLabelSpacingV);
129 columns[leftLabelOffset].setWidthSpec(Column::FIT);
130 columns[rightLabelOffset].setWidthSpec(Column::FIT);
131 if (side == T_LEFT || side == T_LEFTOVERLAP)
132 {
133 columns[leftLabelOffset + 1].setWidthSpec(ctxt, minLabelSpacing);
134 columns[rightLabelOffset - 1].setWidthSpec(scaled::zero());
135 }
136 else
137 {
138 columns[leftLabelOffset + 1].setWidthSpec(scaled::zero());
139 columns[rightLabelOffset - 1].setWidthSpec(ctxt, minLabelSpacing);
140 }
141 }
142
143 //std::cerr << "SETUP COLUMNS" << std::endl;
144 for (unsigned j = 0; j < nColumns; j++)
145 {
146 const unsigned jj = contentColumnOffset + j * 2;
147 //std::cerr << "setup column " << jj << std::endl;
148 if (equalColumns)
149 columns[jj].setWidthSpec(Column::AUTO);
150 else
151 {
152 const SmartPtr<Value> specV = GetComponent(columnWidthV, j);
153 if (isTokenId(specV, T_AUTO))
154 columns[jj].setWidthSpec(Column::AUTO);
155 else if (isTokenId(specV, T_FIT))
156 columns[jj].setWidthSpec(Column::FIT);
157 else
158 columns[jj].setWidthSpec(ctxt, resolveLength(ctxt, specV));
159 }
160 columns[jj].setContentColumn();
161
162 if (j + 1 < nColumns)
163 columns[jj + 1].setWidthSpec(ctxt, resolveLength(ctxt, columnSpacingV, j));
164 }
165
166 //std::cerr << "SETUP ROWS" << std::endl;
167 for (unsigned i = 0; i < nRows; i++)
168 {
169 const unsigned ii = contentRowOffset + i * 2;
170
171 if (hasLabels)
172 cells[ii * nGridColumns + labelOffset].setContent(label[i]);
173
174 rows[ii].setHeightSpec(Row::AUTO);
175 for (unsigned j = 0; j < nColumns; j++)
176 {
177 const unsigned jj = contentColumnOffset + j * 2;
178 cells[ii * nGridColumns + jj].setContent(cell[i * nColumns + j]);
179 }
180 rows[ii].setContentRow();
181
182 if (i + 1 < nRows)
183 rows[ii + 1].setHeightSpec(ctxt, resolveLength(ctxt, rowSpacingV, i));
184 }
185 }
186
187 void
formatCells(const FormattingContext & ctxt,const scaled & availableWidth,const SmartPtr<Value> & width) const188 MathMLTableFormatter::formatCells(const FormattingContext& ctxt,
189 const scaled& availableWidth,
190 const SmartPtr<Value>& width) const
191 {
192 }
193
194 AreaRef
formatLines(const FormattingContext & ctxt,const SmartPtr<Value> & frameV,const SmartPtr<Value> & rowLinesV,const SmartPtr<Value> & columnLinesV) const195 MathMLTableFormatter::formatLines(const FormattingContext& ctxt,
196 const SmartPtr<Value>& frameV,
197 const SmartPtr<Value>& rowLinesV,
198 const SmartPtr<Value>& columnLinesV) const
199 {
200 const TokenId frame = ToTokenId(frameV);
201 const unsigned nGridRows = rows.size();
202 const unsigned nGridColumns = columns.size();
203 const scaled defaultLineThickness = ctxt.MGD()->defaultLineThickness(ctxt);
204 const RGBColor color = ctxt.getColor();
205
206 std::vector<BoxedLayoutArea::XYArea> content;
207 for (unsigned ii = 0; ii < nGridRows; ii++)
208 if (rows[ii].isContentRow())
209 for (unsigned jj = 0; jj < nGridColumns; jj++)
210 if (const Cell& cell = getCell(ii, jj))
211 {
212 const SmartPtr<MathMLTableCellElement> el = cell.getContent();
213 assert(el);
214 const unsigned i = el->getRowIndex();
215 const unsigned j = el->getColumnIndex();
216 //std::cerr << "LINES: FOUND ELEMENT at " << i << "," << j << std::endl;
217 if (i + el->getRowSpan() < nRows
218 && ToTokenId(GetComponent(rowLinesV, i + el->getRowSpan() - 1)) != T_NONE)
219 {
220 const scaled dx0 =
221 (j == 0) ?
222 ((frame != T_NONE) ? columns[jj - 1].getLeftDisplacement()
223 : columns[jj].getLeftDisplacement())
224 : columns[jj - 1].getCenterDisplacement();
225 const scaled dx1 =
226 (j + el->getColumnSpan() == nColumns) ?
227 ((frame != T_NONE) ? columns[jj + cell.getColumnSpan()].getRightDisplacement()
228 : columns[jj + cell.getColumnSpan() - 1].getRightDisplacement())
229 : columns[jj + cell.getColumnSpan()].getCenterDisplacement();
230 const scaled dy =
231 rows[ii + cell.getRowSpan()].getCenterDisplacement();
232 BoxedLayoutArea::XYArea area(dx0, dy,
233 ctxt.MGD()->getFactory()->fixedHorizontalLine(defaultLineThickness,
234 dx1 - dx0, color));
235 //std::cerr << "draw x line " << dx0 << "," << dy << " to " << dx1 << std::endl;
236 content.push_back(area);
237 }
238
239 if (j + el->getColumnSpan() < nColumns
240 && ToTokenId(GetComponent(columnLinesV, j + el->getColumnSpan() - 1)) != T_NONE)
241 {
242 const scaled dy0 =
243 (i == 0) ?
244 ((frame != T_NONE) ? rows[ii - 1].getTopDisplacement()
245 : rows[ii].getTopDisplacement())
246 : rows[ii - 1].getCenterDisplacement();
247 const scaled dy1 =
248 (i + el->getRowSpan() == nRows) ?
249 ((frame != T_NONE) ? rows[ii + cell.getRowSpan()].getBottomDisplacement()
250 : rows[ii + cell.getRowSpan() - 1].getBottomDisplacement())
251 : rows[ii + cell.getRowSpan()].getCenterDisplacement();
252 const scaled dx =
253 columns[jj + cell.getColumnSpan()].getCenterDisplacement();
254 BoxedLayoutArea::XYArea area(dx, dy0,
255 ctxt.MGD()->getFactory()->fixedVerticalLine(defaultLineThickness,
256 scaled::zero(),
257 dy0 - dy1, color));
258 //std::cerr << "draw y line " << dx << "," << dy0 << " to " << dy1 << std::endl;
259 content.push_back(area);
260 }
261 }
262
263 if (frame != T_NONE)
264 {
265 //std::cerr << "HAS FRAME" << std::endl;
266 const scaled left = columns.front().getLeftDisplacement();
267 const scaled right = columns.back().getRightDisplacement();
268 const scaled top = rows.front().getTopDisplacement();
269 const scaled bottom = rows.back().getBottomDisplacement();
270
271 const AreaRef hline = ctxt.MGD()->getFactory()->fixedHorizontalLine(defaultLineThickness, right - left + defaultLineThickness, color);
272 const AreaRef vline = ctxt.MGD()->getFactory()->fixedVerticalLine(defaultLineThickness, scaled::zero(), top - bottom, color);
273
274 content.push_back(BoxedLayoutArea::XYArea(scaled::zero(), top, hline));
275 content.push_back(BoxedLayoutArea::XYArea(scaled::zero(), bottom, hline));
276 content.push_back(BoxedLayoutArea::XYArea(scaled::zero(), top, vline));
277 content.push_back(BoxedLayoutArea::XYArea(right, top, vline));
278 }
279
280 return content.empty() ? 0 : ctxt.MGD()->getFactory()->boxedLayout(getBoundingBox(), content);
281 }
282
283 scaled
computeTableWidth(const scaled & minimumTableWidth)284 MathMLTableFormatter::computeTableWidth(const scaled& minimumTableWidth)
285 {
286 // TODO
287 return minimumTableWidth;
288 }
289
290 BoundingBox
assignTableWidth(const scaled & minimumTableWidth)291 MathMLTableFormatter::assignTableWidth(const scaled& minimumTableWidth)
292 {
293 const scaled tableWidth = computeTableWidth(minimumTableWidth);
294 setWidth(tableWidth);
295
296 if (equalColumns)
297 assignTableWidthT(width);
298 else
299 assignTableWidthF(width);
300
301 // TODO: assignment propagation
302
303 initTempHeightDepth();
304 const scaled tableHeightDepth = equalRows ? computeTableHeightDepthT() : computeTableHeightDepthF();
305
306 if (tableAlignRow == 0)
307 alignTable(tableHeightDepth, axis, tableAlign);
308 else
309 {
310 const unsigned contentRowOffset = rows[0].isContentRow() ? 0 : 1;
311 const unsigned gridRow = contentRowOffset + 2 * ((tableAlignRow < 0) ? (nRows + tableAlignRow) : (tableAlignRow - 1));
312 alignTable(tableHeightDepth, axis, tableAlign, gridRow);
313 }
314 setDisplacements();
315 setCellPosition();
316
317 return getBoundingBox();
318 }
319
320 BoundingBox
format(std::vector<BoxedLayoutArea::XYArea> & content)321 MathMLTableFormatter::format(std::vector<BoxedLayoutArea::XYArea>& content)
322 {
323 initTempWidths();
324 const BoundingBox tableBox = assignTableWidth(computeMinimumTableWidth());
325
326 content.clear();
327 for (std::vector<Cell>::const_iterator p = cells.begin();
328 p != cells.end();
329 p++)
330 if (!p->isNull())
331 {
332 scaled dx;
333 scaled dy;
334 p->getDisplacement(dx, dy);
335 content.push_back(BoxedLayoutArea::XYArea(dx, dy, p->getArea()));
336 }
337
338 return tableBox;
339 }
340
341 scaled
getColumnContentWidth(unsigned j) const342 MathMLTableFormatter::getColumnContentWidth(unsigned j) const
343 {
344 scaled maxWidth = 0;
345 for (unsigned i = 0; i < rows.size(); i++)
346 {
347 if (rows[i].isContentRow())
348 if (const Cell& cell = getCell(i, j))
349 if (cell.getColumnSpan() == 1)
350 maxWidth = std::max(maxWidth, cell.getBoundingBox().width);
351 }
352 //std::cerr << "content width[" << j << "] = " << maxWidth << std::endl;
353 return maxWidth;
354 }
355
356 void
initTempWidths()357 MathMLTableFormatter::initTempWidths()
358 {
359 for (unsigned j = 0; j < columns.size(); j++)
360 {
361 if (columns[j].isContentColumn() && columns[j].getSpec() != Column::FIX)
362 {
363 const scaled contentWidth = getColumnContentWidth(j);
364 columns[j].setContentWidth(contentWidth);
365 columns[j].setTempWidth(contentWidth);
366 }
367 else if (columns[j].getSpec() == Column::FIX)
368 columns[j].setTempWidth(columns[j].getFixWidth());
369 else if (columns[j].getSpec() == Column::SCALE && !columns[j].isContentColumn())
370 columns[j].setTempWidth(0);
371 }
372
373 for (unsigned j = 0; j < columns.size(); j++)
374 if (columns[j].isContentColumn())
375 for (unsigned i = 0; i < rows.size(); i++)
376 if (rows[i].isContentRow())
377 if (const Cell& cell = getCell(i, j))
378 if (cell.getColumnSpan() > 1)
379 {
380 //std::cerr << "CELL " << i << "," << j << " " << cell.getColumnSpan() << " " << cell.getBoundingBox() << std::endl;
381 const scaled cellWidth = cell.getBoundingBox().width;
382 scaled spannedTempWidth = 0;
383 int n = 0;
384 for (unsigned z = j; z <= j + cell.getColumnSpan() - 1; z++)
385 {
386 spannedTempWidth += columns[z].getTempWidth();
387 if (columns[z].isContentColumn() && columns[j].getSpec() != Column::FIX)
388 n++;
389 }
390 if (cellWidth > spannedTempWidth)
391 for (unsigned z = j; z <= j + cell.getColumnSpan() - 1; z++)
392 if (columns[z].isContentColumn() && columns[j].getSpec() != Column::FIX)
393 columns[z].setTempWidth(columns[z].getTempWidth() + (cellWidth - spannedTempWidth) / n);
394 }
395 }
396
397 scaled
computeMinimumTableWidthT()398 MathMLTableFormatter::computeMinimumTableWidthT()
399 {
400 numCol = 0;
401 sumFix = 0;
402 sumCont = 0;
403 sumScale = 0;
404
405 scaled max = 0;
406 for (unsigned j = 0; j < columns.size(); j++)
407 {
408 if (columns[j].isContentColumn())
409 {
410 numCol++;
411 max = std::max(max, columns[j].getTempWidth());
412 }
413 else if (columns[j].getSpec() == Column::FIX)
414 sumFix += columns[j].getFixWidth();
415 else if (columns[j].getSpec() == Column::SCALE)
416 sumScale += columns[j].getScaleWidth();
417 }
418
419 return std::max(numCol * max + sumFix, ((numCol * max) + sumFix) / (1 - sumScale));
420 }
421
422 scaled
computeMinimumTableWidth()423 MathMLTableFormatter::computeMinimumTableWidth()
424 {
425 const scaled minimumTableWidth = equalColumns ? computeMinimumTableWidthT() : computeMinimumTableWidthF();
426 // TODO: gestire widthSpec
427 return minimumTableWidth;
428 }
429
430 void
assignTableWidthT(const scaled & tableWidth)431 MathMLTableFormatter::assignTableWidthT(const scaled& tableWidth)
432 {
433 const scaled assignedWidth = sumFix + tableWidth * sumScale;
434 const scaled availWidth = std::max(scaled::zero(), tableWidth - assignedWidth);
435
436 for (unsigned j = 0; j < columns.size(); j++)
437 if (columns[j].isContentColumn())
438 columns[j].setWidth(availWidth / numCol);
439 else if (columns[j].getSpec() == Column::FIX)
440 columns[j].setWidth(columns[j].getFixWidth());
441 else if (columns[j].getSpec() == Column::SCALE)
442 columns[j].setWidth(tableWidth * columns[j].getScaleWidth());
443 }
444
445 scaled
computeMinimumTableWidthF()446 MathMLTableFormatter::computeMinimumTableWidthF()
447 {
448 numCol = 0;
449 sumCont = 0;
450 sumFix = 0;
451 sumScale = 0;
452
453 scaled max = 0;
454 scaled tempWidth = 0;
455
456 for (unsigned j = 0; j < columns.size(); j++)
457 if (columns[j].isContentColumn() && columns[j].getSpec() == Column::SCALE)
458 max = std::max(max, columns[j].getTempWidth() / columns[j].getScaleWidth());
459
460 for (unsigned j = 0; j < columns.size(); j++)
461 if (columns[j].isContentColumn()
462 && columns[j].getSpec() != Column::FIX
463 && columns[j].getSpec() != Column::SCALE)
464 {
465 sumCont += columns[j].getTempWidth();
466 numCol++;
467 }
468 else if (columns[j].getSpec() == Column::FIX)
469 sumFix += columns[j].getFixWidth();
470 else if (columns[j].getSpec() == Column::SCALE)
471 sumScale += columns[j].getScaleWidth();
472
473 return std::max(max, std::max(sumCont + sumFix, (sumCont + sumFix) / (1 - sumScale)));
474 }
475
476 void
assignTableWidthF(const scaled & tableWidth)477 MathMLTableFormatter::assignTableWidthF(const scaled& tableWidth)
478 {
479 const scaled assignedWidth = sumFix + tableWidth * sumScale;
480 const scaled extraWidth = std::max(scaled::zero(), tableWidth - assignedWidth - sumCont);
481
482 #if 0
483 std::cerr << "initWidthsF tableWidth = " << tableWidth << std::endl
484 << "sumScale = " << sumScale << std::endl
485 << "assignedWidth = " << assignedWidth << std::endl
486 << "extraWidth = " << extraWidth << std::endl;
487 #endif
488
489 for (unsigned j = 0; j < columns.size(); j++)
490 if ((columns[j].isContentColumn()
491 && columns[j].getSpec() != Column::FIX
492 && columns[j].getSpec() != Column::SCALE))
493 columns[j].setWidth(columns[j].getTempWidth() + (extraWidth / numCol));
494 else if (columns[j].getSpec() == Column::FIX)
495 columns[j].setWidth(columns[j].getFixWidth());
496 else if (columns[j].getSpec() == Column::SCALE)
497 columns[j].setWidth(tableWidth * columns[j].getScaleWidth());
498 }
499
500 void
initTempHeightDepth()501 MathMLTableFormatter::initTempHeightDepth()
502 {
503 for (unsigned i = 0; i < rows.size(); i++)
504 {
505 if (rows[i].getSpec() == Row::FIX)
506 {
507 rows[i].setTempHeight(rows[i].getFixHeight());
508 rows[i].setTempDepth(0);
509 }
510 else if (rows[i].getSpec() == Row::SCALE)
511 {
512 rows[i].setTempHeight(0);
513 rows[i].setTempDepth(0);
514 }
515 else if (rows[i].isContentRow())
516 {
517 scaled maxH = 0;
518 scaled maxD = 0;
519 for (unsigned j = 0; j < columns.size(); j++)
520 if (const Cell& cell = getCell(i, j))
521 if (cell.getRowSpan() == 1)
522 switch (cell.getRowAlign())
523 {
524 case T_BASELINE:
525 {
526 const BoundingBox box = cell.getBoundingBox();
527 maxH = std::max(maxH, box.height);
528 maxD = std::max(maxD, box.depth);
529 }
530 break;
531 case T_AXIS:
532 {
533 const BoundingBox box = cell.getBoundingBox();
534 maxH = std::max(maxH, box.height - axis);
535 maxD = std::max(maxD, box.depth + axis);
536 }
537 break;
538 default:
539 break;
540 }
541 rows[i].setTempHeight(maxH);
542 rows[i].setTempDepth(maxD);
543 }
544 }
545
546 for (unsigned i = 0; i < rows.size(); i++)
547 if (rows[i].isContentRow())
548 for (unsigned j = 0; j < columns.size(); j++)
549 if (columns[j].isContentColumn())
550 if (const Cell& cell = getCell(i, j))
551 if (cell.getRowSpan() == 1 && cell.getRowAlign() != T_BASELINE && cell.getRowAlign() != T_AXIS)
552 {
553 const BoundingBox box = cell.getBoundingBox();
554 if ((rows[i].getTempHeight() + rows[i].getTempDepth()) < box.verticalExtent())
555 rows[i].setTempDepth(box.verticalExtent() - rows[i].getTempHeight());
556 }
557
558 for (unsigned i = 0; i < rows.size(); i++)
559 if (rows[i].isContentRow())
560 for (unsigned j = 0; j < columns.size(); j++)
561 if (columns[j].isContentColumn())
562 if (const Cell& cell = getCell(i, j))
563 if (cell.getRowSpan() > 1)
564 {
565 const scaled cellHeightDepth = cell.getBoundingBox().verticalExtent();
566 scaled spannedTempHeightDepth = 0;
567 int n = 0;
568 for (unsigned z = i; z <= i + cell.getRowSpan() - 1; z++)
569 {
570 spannedTempHeightDepth += rows[z].getTempHeight() + rows[z].getTempDepth();
571 if (rows[z].isContentRow()) n++;
572 }
573 #if 0
574 std::cerr << "CELL " << i << "," << j
575 << " cellHeightDepth = " << cellHeightDepth
576 << " spannedTempHeightDepth = " << spannedTempHeightDepth << std::endl;
577 #endif
578 if (cellHeightDepth > spannedTempHeightDepth)
579 {
580 for (unsigned z = i; z <= i + cell.getRowSpan() - 1; z++)
581 if (rows[z].isContentRow())
582 rows[z].setTempDepth(rows[z].getTempDepth() + (cellHeightDepth - spannedTempHeightDepth) / n);
583 }
584 }
585 }
586
587 scaled
computeTableHeightDepthT()588 MathMLTableFormatter::computeTableHeightDepthT()
589 {
590 int numRows = 0;
591 float sumScale = 0.0f;
592 scaled sumFixHD = 0;
593 scaled sumContHD = 0;
594 scaled max = 0;
595
596 for (unsigned i = 0; i < rows.size(); i++)
597 {
598 if (rows[i].isContentRow())
599 {
600 numRows++;
601 max = std::max(max, rows[i].getTempHeight() + rows[i].getTempDepth());
602 sumContHD += rows[i].getTempHeight() + rows[i].getTempDepth();
603 }
604 else if (rows[i].getSpec() == Row::FIX)
605 sumFixHD += rows[i].getFixHeight();
606 else if (rows[i].getSpec() == Row::SCALE)
607 sumScale += rows[i].getScaleHeight();
608 }
609
610 const scaled tableHeightDepth = std::max(numRows * max + sumFixHD, ((numRows * max) + sumFixHD) / (1 - sumScale));
611 const scaled assignedHeightDepth = sumFixHD + tableHeightDepth * sumScale;
612 const scaled availHeightDepth = std::max(scaled::zero(), tableHeightDepth - assignedHeightDepth);
613
614 for (unsigned i = 0; i < rows.size(); i++)
615 if (rows[i].isContentRow())
616 {
617 rows[i].setHeight(rows[i].getTempHeight());
618 rows[i].setDepth(availHeightDepth / numRows - rows[i].getHeight());
619 //std::cerr << "for row" << i << " HD = " << rows[i].getHeight() + rows[i].getDepth() << std::endl;
620 }
621 else if (rows[i].getSpec() == Row::FIX)
622 {
623 rows[i].setHeight(rows[i].getFixHeight());
624 rows[i].setDepth(0);
625 }
626 else if (rows[i].getSpec() == Row::SCALE)
627 {
628 rows[i].setHeight(tableHeightDepth * rows[i].getScaleHeight());
629 rows[i].setDepth(0);
630 }
631
632 return tableHeightDepth;
633 }
634
635 scaled
computeTableHeightDepthF()636 MathMLTableFormatter::computeTableHeightDepthF()
637 {
638 scaled sumContFix = 0;
639 float sumScale = 0;
640
641 for (unsigned i = 0; i < rows.size(); i++)
642 if (rows[i].isContentRow() || rows[i].getSpec() == Row::FIX)
643 {
644 sumContFix += rows[i].getTempHeight() + rows[i].getTempDepth();
645 }
646 else if (rows[i].getSpec() == Row::SCALE)
647 sumScale += rows[i].getScaleHeight();
648
649 const scaled tableHeightDepth = std::max(sumContFix, sumContFix / (1 - sumScale));
650
651 for (unsigned i = 0; i < rows.size(); i++)
652 if (rows[i].isContentRow())
653 {
654 rows[i].setHeight(rows[i].getTempHeight());
655 rows[i].setDepth(rows[i].getTempDepth());
656 }
657 else if (rows[i].getSpec() == Row::FIX)
658 {
659 rows[i].setHeight(rows[i].getFixHeight());
660 rows[i].setDepth(0);
661 }
662 else if (rows[i].getSpec() == Row::SCALE)
663 {
664 rows[i].setHeight(tableHeightDepth * rows[i].getScaleHeight());
665 rows[i].setDepth(0);
666 }
667
668 return tableHeightDepth;
669 }
670
671 void
alignTable(const scaled & tableHeightDepth,const scaled & axis,TokenId align)672 MathMLTableFormatter::alignTable(const scaled& tableHeightDepth, const scaled& axis, TokenId align)
673 {
674 switch (align)
675 {
676 case T_TOP:
677 setHeight(0);
678 break;
679 case T_BOTTOM:
680 setHeight(tableHeightDepth);
681 break;
682 case T_AXIS:
683 setHeight((tableHeightDepth / 2) + axis);
684 break;
685 case T_CENTER:
686 case T_BASELINE:
687 setHeight(tableHeightDepth / 2);
688 break;
689 default:
690 assert(false);
691 }
692
693 setDepth(tableHeightDepth - getHeight());
694 }
695
696 void
alignTable(const scaled & tableHeightDepth,const scaled & axis,TokenId align,unsigned rowNum)697 MathMLTableFormatter::alignTable(const scaled& tableHeightDepth, const scaled& axis, TokenId align, unsigned rowNum)
698 {
699 scaled temp = 0;
700
701 for (unsigned i = 0; i < rowNum; i++)
702 temp += rows[i].getVerticalExtent();
703
704 switch (align)
705 {
706 case T_TOP:
707 break;
708 case T_BOTTOM:
709 temp += rows[rowNum].getVerticalExtent();
710 break;
711 case T_AXIS:
712 temp += rows[rowNum].getHeight() - axis;
713 break;
714 case T_CENTER:
715 temp += rows[rowNum].getVerticalExtent() / 2;
716 break;
717 case T_BASELINE:
718 temp += rows[rowNum].getHeight();
719 break;
720 default:
721 assert(false);
722 }
723 setHeight(temp);
724 setDepth(tableHeightDepth - temp);
725 }
726
727 BoundingBox
getCellBoundingBox(unsigned i,unsigned j,unsigned rowSpan,unsigned columnSpan) const728 MathMLTableFormatter::getCellBoundingBox(unsigned i, unsigned j, unsigned rowSpan, unsigned columnSpan) const
729 {
730 BoundingBox box(columns[j].getWidth(), rows[i].getHeight(), rows[i].getDepth());
731
732 for (unsigned k = i + 1; k < i + rowSpan; k++)
733 box.depth += rows[k].getVerticalExtent();
734
735 for (unsigned k = j + 1; k < j + columnSpan; k++)
736 box.width += columns[k].getWidth();
737
738 return box;
739 }
740
741 void
setDisplacements()742 MathMLTableFormatter::setDisplacements()
743 {
744 scaled v = getHeight();
745 for (unsigned i = 0; i < rows.size(); i++)
746 {
747 rows[i].setDisplacement(v - rows[i].getHeight());
748 v -= rows[i].getVerticalExtent();
749 //std::cerr << "ROW[" << i << "].displacement = " << rows[i].getDisplacement() << std::endl;
750 }
751
752 scaled h = scaled::zero();
753 for (unsigned j = 0; j < columns.size(); j++)
754 {
755 columns[j].setDisplacement(h);
756 h += columns[j].getWidth();
757 //std::cerr << "COL[" << j << "].displacement = " << columns[j].getDisplacement() << std::endl;
758 }
759 }
760
761 void
setCellPosition()762 MathMLTableFormatter::setCellPosition()
763 {
764 for (unsigned i = 0; i < rows.size(); i++)
765 if (rows[i].isContentRow())
766 for (unsigned j = 0; j < columns.size(); j++)
767 if (columns[j].isContentColumn())
768 {
769 if (const Cell& cell = getCell(i, j))
770 {
771 scaled dx = scaled::zero();
772 scaled dy = scaled::zero();
773
774 const BoundingBox box = cell.getBoundingBox();
775 const BoundingBox cellBox = getCellBoundingBox(i, j, cell.getRowSpan(), cell.getColumnSpan());
776
777 //std::cerr << "CELL BOX = " << cellBox << std::endl << " CONTENT BOX = " << box << std::endl;
778
779 switch (cell.getColumnAlign())
780 {
781 case T_LEFT:
782 dx = scaled::zero();
783 break;
784 case T_RIGHT:
785 dx = cellBox.width - box.width;
786 break;
787 case T_CENTER:
788 dx = (cellBox.width - box.width) / 2;
789 break;
790 default:
791 assert(false);
792 }
793
794 switch (cell.getRowAlign())
795 {
796 case T_BASELINE:
797 dy = scaled::zero();
798 break;
799 case T_TOP:
800 dy = cellBox.height - box.height;
801 break;
802 case T_BOTTOM:
803 dy = box.depth - cellBox.depth;
804 break;
805 case T_CENTER:
806 dy = (cellBox.height - cellBox.depth - box.height + box.depth) / 2;
807 break;
808 case T_AXIS:
809 dy = -axis;
810 break;
811 default:
812 assert(false);
813 }
814
815 //std::cerr << "setting displacement for (" << i << "," << j << ") = " << dx << "," << dy << std::endl;
816 cell.setDisplacement(columns[j].getDisplacement() + dx, rows[i].getDisplacement() + dy);
817 }
818 }
819 }
820
821 const MathMLTableFormatter::Cell&
getCell(unsigned i,unsigned j) const822 MathMLTableFormatter::getCell(unsigned i, unsigned j) const
823 {
824 assert(i < rows.size());
825 assert(j < columns.size());
826 return cells[i * columns.size() + j];
827 }
828