public static void GMTable(MathNode node, XElement output) { DrawBox(node, output); //Draw cells double vshift = -node.Height + node.FrameSpacings[1]; double hshift; foreach (var rowItem in node.Rows.Select((value, r) => new { r, value })) { RowDescriptor row = rowItem.value; vshift += row.Height; hshift = node.FrameSpacings[0]; foreach (var cellItem in row.Cells.Select((value, c) => new { c, value })) { ColumnDescriptor column = node.Columns[cellItem.c]; CellDescriptor cell = row.Cells[cellItem.c]; if (cell != null && cell.Content != null) { double cellWidth; // Calculate horizontal alignment if (cell.ColSpan > 1) { cellWidth = node.Columns.Skip(cellItem.c).Take(cellItem.c + cell.ColSpan).Select(x => x.Width).Sum(); //ToDo: verify cellWidth += node.Columns.Skip(cellItem.c).Take(cellItem.c + cell.ColSpan - 1).Select(x => x.SpaceAfter).Sum(); //ToDo: verify } else { cellWidth = column.Width; } double hadjust = (cellWidth - cell.Content.Width) * (alignKeywords.ContainsKey(cell.HAlign) ? alignKeywords[cell.HAlign] : 0.5); // Calculate vertical alignment. double cellHeight; if (cell.RowSpan > 1) { cellHeight = node.Rows.Skip(rowItem.r).Take(rowItem.r + cell.RowSpan).Select(x => x.Height + x.Depth).Sum(); //ToDo: Verify cellHeight += node.Rows.Skip(rowItem.r).Take(rowItem.r + cell.RowSpan - 1).Select(x => x.SpaceAfter).Sum(); //ToDo: Verify } else { cellHeight = row.Height + row.Depth; } double vadjust; if (cell.VAlign == "top") { vadjust = cell.Content.Height - row.Height; } else if (cell.VAlign == "bottom") { vadjust = cellHeight - row.Height - cell.Content.Depth; } else if ((cell.VAlign == "axis" || cell.VAlign == "baseline") && cell.RowSpan == 1) { vadjust = -cell.VShift; // calculated in the measurer } else { vadjust = (cell.Content.Height - cell.Content.Depth + cellHeight) / 2 - row.Height; } DrawTranslatedNode(cell.Content, output, hshift + hadjust, vshift + vadjust); } hshift += column.Width + column.SpaceAfter; } vshift += row.Depth + row.SpaceAfter; } //Draw frame double x1 = node.LineWidth / 2; double y1 = node.LineWidth / 2 - node.Height; double x2 = node.Width - node.LineWidth / 2; double y2 = node.Depth - node.LineWidth / 2; DrawBorder(node, output, x1, y1, x1, y2, node.FrameLines[0]); DrawBorder(node, output, x2, y1, x2, y2, node.FrameLines[0]); DrawBorder(node, output, x1, y1, x2, y1, node.FrameLines[1]); DrawBorder(node, output, x1, y2, x2, y2, node.FrameLines[1]); // Draw intermediate lines // First, let's make a grid hshift = node.FrameSpacings[0]; List <double> hoffsets = new List <double>(); foreach (var item in node.Columns.Select((value, c) => new { c, value })) { double spacing = item.value.SpaceAfter; hshift += item.value.Width; hoffsets.Add(hshift + spacing / 2); hshift += spacing; } hoffsets[hoffsets.Count - 1] = x2; vshift = -node.Height + node.FrameSpacings[1]; List <double> voffsets = new List <double>(); foreach (var item in node.Rows.Select((value, r) => new { r, value })) { double spacing = item.value.SpaceAfter; vshift += item.value.Height + item.value.Depth; voffsets.Add(vshift + spacing / 2); vshift += spacing; } voffsets[voffsets.Count - 1] = y2; List <double> vspans = Enumerable.Repeat <double>(0, node.Columns.Count).ToList(); for (int r = 0; r < node.Rows.Count - 1; r++) { RowDescriptor row = node.Rows[r]; if (row.LineAfter == null) { continue; } foreach (var cellItem in row.Cells.Select((value, c) => new { c, value })) { CellDescriptor cell = cellItem.value; if (cell == null || cell.ColSpan == 0) { continue; } for (int i = cellItem.c; i < cellItem.c + cell.ColSpan; i++) { vspans[i] = cell.RowSpan; } } vspans = vspans.Select(x => Math.Max(0, x - 1)).ToList(); double lineY = voffsets[r]; double startX = x1; double endX = x1; foreach (var cellItem in node.Columns.Select((value, c) => new { c, value })) { if (vspans[cellItem.c] > 0) { DrawBorder(node, output, startX, lineY, endX, lineY, row.LineAfter); startX = hoffsets[cellItem.c]; } endX = hoffsets[cellItem.c]; } DrawBorder(node, output, startX, lineY, endX, lineY, row.LineAfter); } List <double> hspans = Enumerable.Repeat <double>(0, node.Columns.Count).ToList(); for (int c = 0; c < node.Columns.Count - 1; c++) { ColumnDescriptor column = node.Columns[c]; if (column.LineAfter == null) { continue; } foreach (var rowItem in node.Rows.Select((value, r) => new { r, value })) { RowDescriptor row = rowItem.value; if (row.Cells.Count <= c) { continue; } CellDescriptor cell = row.Cells[c]; if (cell == null || cell.Content == null) { continue; } for (int j = rowItem.r; j < rowItem.r + cell.RowSpan; j++) { hspans[j] = cell.ColSpan; } } hspans = hspans.Select(x => Math.Max(0, x - 1)).ToList(); double lineX = hoffsets[c]; double startY = y1; double endY = y1; foreach (var item in node.Rows.Select((value, r) => new { r, value })) { if (hspans[item.r] > 0) { DrawBorder(node, output, lineX, startY, lineX, endY, column.LineAfter); startY = voffsets[item.r]; } endY = voffsets[item.r]; } DrawBorder(node, output, lineX, startY, lineX, endY, column.LineAfter); } }
void ArrangeCells(MathNode node) { node.Rows = new List<RowDescriptor>(); node.Columns = new List<ColumnDescriptor>(); List<int> busycells = new List<int>(); // Read table-level alignment properties List<string> table_rowaligns = node.GetListProperty("rowalign"); List<string> table_columnaligns = node.GetListProperty("columnalign"); foreach (MathNode ch in node.Children) { string rowalign = GetByIndexOrLast(table_rowaligns, node.Rows.Count); List<string> row_columnaligns = table_columnaligns; List<MathNode> cells = new List<MathNode>(); if (ch.ElementName == "mtr" || ch.ElementName == "mlabeledtr") { cells = ch.Children; if (ch.Attributes.ContainsKey("rowalign")) rowalign = ch.Attributes["rowalign"]; if (ch.Attributes.ContainsKey("columnalign")) { List<string> columnaligns = node.GetListProperty("columnalign", ch.Attributes["columnalign"]); } } else { cells.Add(ch); } RowDescriptor row = new RowDescriptor(node, cells, rowalign, row_columnaligns, busycells); node.Rows.Add(row); // busycells passes information about cells spanning multiple rows busycells = busycells.Select(n => Math.Max(0, n - 1)).ToList(); while (busycells.Count < row.Cells.Count) busycells.Add(0); for(int i =0; i < row.Cells.Count; i++) { CellDescriptor cell = row.Cells[i]; if (cell == null) continue; if (cell.RowSpan > 1) { for (int j = i; j <= i + cell.ColSpan; i++) { busycells[j] = cell.RowSpan - 1; } } } } //Pad the table with empty rows until no spanning cell protrudes while (busycells.Max() > 0) { string rowalign = Measurer.GetByIndexOrLast(table_rowaligns, node.Rows.Count); node.Rows.Add(new RowDescriptor(node, new List<MathNode>(), rowalign, table_columnaligns, busycells)); busycells = busycells.Select(x => Math.Max(0, x - 1)).ToList(); } }