object MathML.MathMLVisitor.Visit(MathMLTableCellElement e, object args) { MathMLVisitor v = this; return(v.Visit((MathMLPresentationContainer)e, args)); }
object MathML.MathMLVisitor.Visit(MathMLTableCellElement e, object args) { MathMLVisitor p = this; return p.Visit((MathMLPresentationContainer)e, args); }
/** * initialize the dashed and solid lines * pre-req verticalExtent is set * * The general idea behind this line layout algorighm is that from just * a grid of cells, with some null and other not, it is impossible to know * where to place lines. If we have 2 adjacent cells, both null, we do * not know to place a line between them as the left cell could be from * a column spanning cell, and the right could be from a row spanning cell. * * So, we need to know spanning numbers. */ private void CreateLines(BoundingBox[][] extents, MathMLTableCellElement[][] cells, Row[] rows, Column[] columns, LineStyle[] columnLines, LineStyle[] rowLines, LineStyle frame) { // TODO change these to arrays ArrayList dashedLineList = new ArrayList(); ArrayList solidLineList = new ArrayList(); try { // max number of TABLE columns int maxColCount = (columns.Length - 1) / 2; int[] rowSpan = new int[maxColCount]; float top = 0; float vLen = 0; float hLen = 0; float left = 0; // outer loop rows for(int i = 0, row = 1; i < cells.Length; i++, row += 2) { if(i == 0) { top = rows[0].VerticalExtent; vLen = rows[1].VerticalExtent + rows[2].VerticalExtent / 2.0f; } else { top = top + vLen; vLen = rows[row - 1].VerticalExtent / 2.0f + rows[row].VerticalExtent + rows[row + 1].VerticalExtent / 2.0f; } // spaning column length of next row, if a cell spans rows, then it needs a line //int currColSpan = (cells.Length && 0 < cells[i].Length && cells[i][0] != null) ? // cells[i][0].ColumnSpan : 0; //i//nt nextColSpan = (i + 1 < cells.Length && 0 < cells[i + 1].Length && cells[i + 1][0] != null) ? // cells[i + 1][0].ColumnSpan : 0; // inner loop columns for(int j = 0, col = 1; j < maxColCount; j++, col += 2) { if(j == 0) { left = columns[0].Width; hLen = columns[1].Width + columns[2].Width / 2.0f; } else { left = left + hLen; hLen = columns[col - 1].Width / 2.0f + columns[col].Width + columns[col + 1].Width / 2.0f; } // do the vertical line at end of cell if((j + 1 < cells[i].Length && cells[i][j + 1] != null) || (j == cells[i].Length - 1 && cells[i].Length < maxColCount)) { if(columnLines[j] == LineStyle.Solid) { solidLineList.Add(new PointF(left + hLen, top)); solidLineList.Add(new PointF(left + hLen, top + vLen)); } else if(columnLines[j] == LineStyle.Dashed) { dashedLineList.Add(new PointF(left + hLen, top)); dashedLineList.Add(new PointF(left + hLen, top + vLen)); } } // horz line at bottom of cell, only do if we are not the last row if(i + 1 < cells.Length) { // if a span count is 0, this means that the end of a cell was encountered, so // we need to read the span count from the current cell. Otherwise, we are in the // middle of a row spanning colum and we need to decrement the count indicating that // we used up that cell if(rowSpan[j] == 0) { rowSpan[j] = j < cells[i].Length && cells[i][j] != null ? cells[i][j].RowSpan - 1 : 0; } else { rowSpan[j]--; } if(rowSpan[j] == 0) { if(rowLines[i] == LineStyle.Solid) { solidLineList.Add(new PointF(left, top + vLen)); solidLineList.Add(new PointF(left + hLen, top + vLen)); } else if(rowLines[i] == LineStyle.Dashed) { dashedLineList.Add(new PointF(left, top + vLen)); dashedLineList.Add(new PointF(left + hLen, top + vLen)); } } } } } // draw the frame if(frame == LineStyle.Solid) { solidLineList.Add(new PointF(0, 0)); solidLineList.Add(new PointF(0, box.VerticalExtent)); solidLineList.Add(new PointF(0, 0)); solidLineList.Add(new PointF(box.Width, 0)); solidLineList.Add(new PointF(box.Width, box.VerticalExtent)); solidLineList.Add(new PointF(box.Width, 0)); solidLineList.Add(new PointF(box.Width, box.VerticalExtent)); solidLineList.Add(new PointF(0, box.VerticalExtent)); } else if(frame == LineStyle.Dashed) { dashedLineList.Add(new PointF(0, 0)); dashedLineList.Add(new PointF(0, box.VerticalExtent)); dashedLineList.Add(new PointF(0, 0)); dashedLineList.Add(new PointF(box.Width, 0)); dashedLineList.Add(new PointF(box.Width, box.VerticalExtent)); dashedLineList.Add(new PointF(box.Width, 0)); dashedLineList.Add(new PointF(box.Width, box.VerticalExtent)); dashedLineList.Add(new PointF(0, box.VerticalExtent)); } } catch(Exception ex) { throw new Exception("error creating table lines, table should still display, error: " + ex.Message); } dashedLines = (PointF[])dashedLineList.ToArray(typeof(PointF)); solidLines = (PointF[])solidLineList.ToArray(typeof(PointF)); }
/** * grab all the cells from a table and return them in a 2 dimensional array * TODO optimize (cache) cell.attribute* calls */ public static MathMLTableCellElement[][] GetCells(MathMLTableElement table) { int i = 0; MathMLNodeList tableRows = table.Rows; MathMLTableCellElement[][] cells = new MathMLTableCellElement[tableRows.Count][]; int[] remainderCols = new int[0]; ArrayList rowCellsList = new ArrayList(); foreach (MathMLTableRowElement row in tableRows) { MathMLNodeList rowCells = row.Cells; rowCellsList.Clear(); foreach (MathMLTableCellElement cell in rowCells) { if (rowCellsList.Count < remainderCols.Length && remainderCols[rowCellsList.Count] > 0) { remainderCols[rowCellsList.Count] = remainderCols[rowCellsList.Count] - 1; rowCellsList.Add(null); } rowCellsList.Add(cell); for (int j = 1; j < cell.ColumnSpan; j++) { // deal with overlapping cells if (rowCellsList.Count < remainderCols.Length && remainderCols[rowCellsList.Count] > 0) { remainderCols[rowCellsList.Count] = remainderCols[rowCellsList.Count] - 1; } rowCellsList.Add(null); } } cells[i] = new MathMLTableCellElement[rowCellsList.Count]; for (int j = 0; j < rowCellsList.Count; j++) { cells[i][j] = (MathMLTableCellElement)rowCellsList[j]; } if (remainderCols.Length < cells[i].Length) { int[] tmp = new int[cells[i].Length]; for (int j = 0; j < remainderCols.Length; j++) { tmp[j] = remainderCols[j]; } for (int j = remainderCols.Length; j < tmp.Length; j++) { tmp[j] = 0; } remainderCols = tmp; } for (int j = 0; j < cells[i].Length; j++) { if (cells[i][j] != null) { Debug.Assert(remainderCols[j] == 0, "remainder columns value should be zero if we have a current cell"); remainderCols[j] = cells[i][j].RowSpan - 1; } } i++; // next row } return(cells); }
private static void AdjustSpanningCells(MathMLTableCellElement[][] cells, BoundingBox[][] minCellSizes, Row[] rows, Column[] columns) { // init to 1 to skip over first spacing row for frame // outer loop - rows for(int i = 0, rowIndex = 1; i < cells.Length; i++, rowIndex += 2) { // init to 1 to skip over first spacing column for frame // inner loop - columns for(int j = 0, colIndex = 1; j < cells[i].Length; j++, colIndex += 2) { if(cells[i][j] != null) { int rowSpan = cells[i][j].RowSpan; if(rowSpan > 1) { float rowSpanVertExt = 0; for(int k = 0, k2 = 0; k < rowSpan; k++, k2 += 2) { if(rowIndex + k2 < rows.Length) { // content row rowSpanVertExt += rows[rowIndex + k2].VerticalExtent; // only add inner space rows if(k < rowSpan - 1) { rowSpanVertExt += rows[rowIndex + k2 + 1].VerticalExtent; } } else { Debug.WriteLine("warning, row span exceeds row count"); } } float reqVertExt = minCellSizes[i][j].VerticalExtent - rowSpanVertExt; if(reqVertExt > 0) { Debug.WriteLine("need " + reqVertExt + " to accomdate spanning row"); float newSpace = reqVertExt / (float)rowSpan; for(int k = 0, k2 = 0; k < rowSpan; k++, k2 += 2) { if(rowIndex + k2 < rows.Length) { rows[rowIndex + k2].Depth += newSpace / 2.0f; rows[rowIndex + k2].Height += newSpace / 2.0f; } else { Debug.WriteLine("warning, row span exceeds row count"); } } } } } } } }
private static void CreateCellSizesAndShifts(MathMLTableCellElement[][] cells, Row[] rows, Column[] columns, ref BoundingBox[][] sizes, ref PointF[][] shifts) { sizes = new BoundingBox[cells.Length][]; shifts = new PointF[cells.Length][]; // init to 1 to skip over first spacing row for frame int rowIndex = 1; // y shift, start with upper frame space float y = rows[0].VerticalExtent; // outer loop - rows for(int i = 0; i < cells.Length; i++) { // init to 1 to skip over first spacing column for frame int colIndex = 1; // start x shift with frame space float x = columns[0].Width; sizes[i] = new BoundingBox[cells[i].Length]; shifts[i] = new PointF[cells[i].Length]; // inner loop - columns for(int j = 0; j < cells[i].Length; j++) { if(cells[i][j] != null) { BoundingBox box = BoundingBox.New(); for(int k = 0, k2 = 0; k < cells[i][j].ColumnSpan; k++, k2 += 2) { if(colIndex + k2 < columns.Length) { if(k2 == 0) { box.Width = columns[colIndex].Width; } else { // add content column box.Append(BoundingBox.New(columns[colIndex + k].Width, 0, 0)); // add space column box.Append(BoundingBox.New(columns[colIndex + k + 1].Width, 0, 0)); } } else { Debug.WriteLine("warning, insuffcient columns for spanning"); } } for(int k = 0, k2 = 0; k < cells[i][j].RowSpan; k++, k2 += 2) { if(rowIndex + k2 < rows.Length) { if(k2 == 0) { box.Height = rows[rowIndex].Height; box.Depth = rows[rowIndex].Depth; } else { // add content row box.Over(BoundingBox.New(0, rows[rowIndex + k2].Height, rows[rowIndex + k].Depth)); // add space row box.Over(BoundingBox.New(0, rows[rowIndex + k2 + 1].Height, rows[rowIndex + k + 1].Depth)); } } } Debug.Assert(box.Height == rows[rowIndex].Height); sizes[i][j] = box; shifts[i][j] = new PointF(x, y + box.Height); } // add width of current column and next column which is a spaceing column // and set index to next non space column x += columns[colIndex++].Width; x += columns[colIndex++].Width; } // add vertical extent of current row and next row which is a spaceing row // and set index to next non space row y += rows[rowIndex++].VerticalExtent; y += rows[rowIndex++].VerticalExtent; } }
/** * calculate the required space to fit all the columns */ private static Column[] CreateColumns(IFormattingContext ctx, MathMLMeasurer measurer, MathMLTableCellElement[][] cells, BoundingBox[][] minCellSizes, int columnCount, Length[] columnWidths, Length[] spaceWidths, Length frameSpacing) { Column[] columns = null; int c = 0; int colWidth = 0; int spaceWidth = 0; // new column array: max column count + spacing columns + border spacing // = (max column count) + (max column count - 1) + 2 // = (max column count) + (max column count) + 1 columns = new Column[columnCount + columnCount + 1]; // set the frame spacing columns columns[c++] = new Column(ctx, frameSpacing, 0, true); // outer loop: columns for(int i = 0; i < columnCount; i++) { BoundingBox[] columnCells = new BoundingBox[cells.Length]; // inner loop: rows for(int j = 0; j < cells.Length; j++) { columnCells[j] = i < cells[j].Length ? minCellSizes[j][i] : BoundingBox.New(); } // set the column columns[c++] = new Column(ctx, columnWidths[colWidth++], GetRequiredWidth(columnCells), false); // set the space column if(i < columnCount - 1) { columns[c++] = new Column(ctx, spaceWidths[spaceWidth++], 0, true); } } // set the right spacing column columns[c++] = new Column(ctx, frameSpacing, 0, true); // sanity check Debug.Assert(c == columns.Length, "error, maxColumnCount and processed column count do not match"); return columns; }
private static Row[] CreateRows(IFormattingContext ctx, MathMLTableCellElement[][] cells, BoundingBox[][] minCellSizes, Length[] rowSpacing, Length frameSpacing) { int i = 0; int space = 0; // new row array: row count + spacing rows + border spacing // = (row count) + (row count - 1) + 2 // = (row count) + (row count) + 1 Row[] rows = new Row[minCellSizes.Length + minCellSizes.Length + 1]; // frame spacing rows[i++] = new Row(ctx.Evaluate(frameSpacing)); for(int j = 0; j < minCellSizes.Length; j++) { // cell row rows[i++] = new Row(cells[j], minCellSizes[j]); // spacing row if(j < minCellSizes.Length - 1) { rows[i++] = new Row(ctx.Evaluate(rowSpacing[space++])); } } // final frame spacing rows[i++] = new Row(ctx.Evaluate(frameSpacing)); // sanity check Debug.Assert(i == rows.Length, "error, row count and processed row count do not match"); return rows; }
/** * grab all the cells from a table and return them in a 2 dimensional array * TODO optimize (cache) cell.attribute* calls */ public static MathMLTableCellElement[][] GetCells(MathMLTableElement table) { int i = 0; MathMLNodeList tableRows = table.Rows; MathMLTableCellElement[][] cells = new MathMLTableCellElement[tableRows.Count][]; int[] remainderCols = new int[0]; ArrayList rowCellsList = new ArrayList(); foreach(MathMLTableRowElement row in tableRows) { MathMLNodeList rowCells = row.Cells; rowCellsList.Clear(); foreach(MathMLTableCellElement cell in rowCells) { if(rowCellsList.Count < remainderCols.Length && remainderCols[rowCellsList.Count] > 0) { remainderCols[rowCellsList.Count] = remainderCols[rowCellsList.Count] - 1; rowCellsList.Add(null); } rowCellsList.Add(cell); for(int j = 1; j < cell.ColumnSpan; j++) { // deal with overlapping cells if(rowCellsList.Count < remainderCols.Length && remainderCols[rowCellsList.Count] > 0) { remainderCols[rowCellsList.Count] = remainderCols[rowCellsList.Count] - 1; } rowCellsList.Add(null); } } cells[i] = new MathMLTableCellElement[rowCellsList.Count]; for(int j = 0; j < rowCellsList.Count; j++) { cells[i][j] = (MathMLTableCellElement)rowCellsList[j]; } if(remainderCols.Length < cells[i].Length) { int[] tmp = new int[cells[i].Length]; for(int j = 0; j < remainderCols.Length; j++) { tmp[j] = remainderCols[j]; } for(int j = remainderCols.Length; j < tmp.Length; j++) { tmp[j] = 0; } remainderCols = tmp; } for(int j = 0; j < cells[i].Length; j++) { if(cells[i][j] != null) { Debug.Assert(remainderCols[j] == 0, "remainder columns value should be zero if we have a current cell"); remainderCols[j] = cells[i][j].RowSpan - 1; } } i++; // next row } return cells; }
/** * find the maximum column count from a collection of cells, * this is the row with the largest number of cells */ public int GetCellColumnCount(MathMLTableCellElement[][] cells) { int colCount = 0; // find max column count for(int i = 0; i < cells.Length; i++) { if(cells[i].Length > colCount) colCount = cells[i].Length; } return colCount; }
/** * construct a row that minimally encoloses a set of cell sizes */ public Row(MathMLTableCellElement[] cells, BoundingBox[] minCellSizes) { Spacing = false; Height = 0; Depth = 0; for(int i = 0; i < minCellSizes.Length; i++) { // do not count spanning rows, these are adjusted later if(cells[i] != null) { float h = minCellSizes[i].Height / (float)cells[i].RowSpan; float d = minCellSizes[i].Depth / (float)cells[i].RowSpan; if(h > Height) Height = h; if(d > Depth) Depth = d; } } }