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