public TextileTable( string[] header, string[] styles, IList <string[]> details) { Header = header.Select(txt => { var cell = new TextileTableCell(txt); // Ignore Header Row-span cell.RowSpan = 1; return(cell); }).ToList <ITableCell>(); Details = details.Select(row => row.Select(txt => new TextileTableCell(txt)).ToList <ITableCell>() ).ToList(); // column-idx vs text-alignment Dictionary <int, TextAlignment> styleMt = styles .Select((txt, idx) => { var firstChar = txt[0]; var lastChar = txt[txt.Length - 1]; return ((firstChar == ':' && lastChar == ':') ? Tuple.Create(idx, (TextAlignment?)TextAlignment.Center) : (lastChar == ':') ? Tuple.Create(idx, (TextAlignment?)TextAlignment.Right) : (firstChar == ':') ? Tuple.Create(idx, (TextAlignment?)TextAlignment.Left) : Tuple.Create(idx, (TextAlignment?)null)); }) .Where(tpl => tpl.Item2.HasValue) .ToDictionary(tpl => tpl.Item1, tpl => tpl.Item2.Value); var styleColumnCount = styleMt.Count; // apply cell style to header var headerColumnCount = 0; { var colOffset = 0; foreach (TextileTableCell cell in Header) { cell.ColumnIndex = colOffset; // apply text align if (styleMt.TryGetValue(colOffset, out var style)) { cell.Horizontal = style; } colOffset += cell.ColSpan; } headerColumnCount = colOffset; } // apply cell style to header var colCntAtDetail = new List <int>(); var maxColCntInDetails = 1; { var multiRowsAtColIdx = new Dictionary <int, MdSpan>(); for (var rowIdx = 0; rowIdx < Details.Count; ++rowIdx) { List <ITableCell> row = Details[rowIdx]; var hasAnyCell = false; var colOffset = 0; var rowspansColOffset = multiRowsAtColIdx .Select(ent => ent.Value.ColSpan) .Sum(); /* * In this row, is space exists to insert cell? * * eg. has space * __________________________________ * | 2x1 cell | 1x1 cell | 1x1 cell | * -> | |‾‾‾‾‾‾‾‾‾‾|‾‾‾‾‾‾‾‾‾‾| * ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ * * eg. has no space: multi-rows occupy all space in this row. * __________________________________ * | 2x1 cell | 2x2 cell | * -> | | | * ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ * */ if (rowspansColOffset < maxColCntInDetails) { int colIdx; for (colIdx = 0; colIdx < row.Count;) { int colSpan; if (multiRowsAtColIdx.TryGetValue(colOffset, out var span)) { colSpan = span.ColSpan; } else { hasAnyCell = true; var cell = (TextileTableCell)row[colIdx]; cell.ColumnIndex = colOffset; // apply text align if (!cell.Horizontal.HasValue && styleMt.TryGetValue(colOffset, out var style)) { cell.Horizontal = style; } colSpan = cell.ColSpan; if (cell.RowSpan > 1) { multiRowsAtColIdx[colOffset] = new MdSpan(cell.RowSpan, cell.ColSpan); } ++colIdx; } colOffset += colSpan; } foreach (var left in multiRowsAtColIdx.Where(tpl => tpl.Key >= colOffset) .OrderBy(tpl => tpl.Key)) { while (colOffset < left.Key) { var cell = new TextileTableCell(null); cell.ColumnIndex = colOffset++; row.Add(cell); } colOffset += left.Value.ColSpan; } } colOffset += multiRowsAtColIdx .Where(ent => ent.Key >= colOffset) .Select(ent => ent.Value.ColSpan) .Sum(); foreach (var spanEntry in multiRowsAtColIdx.ToArray()) { if (--spanEntry.Value.Life == 0) { multiRowsAtColIdx.Remove(spanEntry.Key); } } colCntAtDetail.Add(colOffset); maxColCntInDetails = Math.Max(maxColCntInDetails, colOffset); if (!hasAnyCell) { Details.Insert(rowIdx, new List <ITableCell>()); } } // if any multirow is left, insert an empty row. while (multiRowsAtColIdx.Count > 0) { var row = new List <ITableCell>(); Details.Add(row); var colOffset = 0; foreach (var spanEntry in multiRowsAtColIdx.OrderBy(tpl => tpl.Key)) { while (colOffset < spanEntry.Key) { var cell = new TextileTableCell(null); cell.ColumnIndex = colOffset++; row.Add(cell); } colOffset += spanEntry.Value.ColSpan; if (--spanEntry.Value.Life == 0) { multiRowsAtColIdx.Remove(spanEntry.Key); } } colCntAtDetail.Add(colOffset); } } ColCount = Math.Max(Math.Max(headerColumnCount, styleColumnCount), maxColCntInDetails); RowCount = Details.Count; // insert cell for the shortfall for (var retry = Header.Sum(cell => cell.ColSpan); retry < ColCount; ++retry) { var cell = new TextileTableCell(null); cell.ColumnIndex = retry; Header.Add(cell); } for (var rowIdx = 0; rowIdx < Details.Count; ++rowIdx) { for (var retry = colCntAtDetail[rowIdx]; retry < ColCount; ++retry) { var cell = new TextileTableCell(null); cell.ColumnIndex = retry; Details[rowIdx].Add(cell); } } }
/// <summary> /// Initializes a new instance of <see cref="MdAdmonition"/> with the specified type and title but without any content. /// </summary> /// /// <param name="type"> /// The admonition's type. Any non-empty string is allowed. /// Recommended values are <c>attention</c>, <c>caution</c>, <c>danger</c>, <c>error</c>, /// <c>hint</c>, <c>important</c>, <c>note</c>, <c>tip</c> and <c>warning</c> /// </param> /// /// <param name="title"> /// The admonition's title. To create a admonition without title, use a different constructor overload. /// </param> /// /// <exception cref="ArgumentException">Thrown when <paramref name="type"/> is null or whitespace.</exception> /// <exception cref="ArgumentNullException">Thrown when <paramref name="title"/> is <c>null</c>.</exception> public MdAdmonition(string type, MdSpan title) : this(type, title, Array.Empty <MdBlock>()) { }
/// <summary> /// Initializes a new instance of <see cref="MdAdmonition"/>. /// </summary> /// /// <param name="type"> /// The admonition's type. Any non-empty string is allowed. /// Recommended values are <c>attention</c>, <c>caution</c>, <c>danger</c>, <c>error</c>, /// <c>hint</c>, <c>important</c>, <c>note</c>, <c>tip</c> and <c>warning</c> /// </param> /// /// <param name="title"> /// The admonition's title. To create a admonition without title, use a different constructor overload. /// </param> /// /// <param name="content"> /// The admonition's content. /// </param> /// /// <exception cref="ArgumentException">Thrown when <paramref name="type"/> is null or whitespace.</exception> /// <exception cref="ArgumentNullException">Thrown when <paramref name="title"/> is <c>null</c>.</exception> public MdAdmonition(string type, MdSpan title, MdList content) : this(type, title, (MdBlock)content) { }
/// <summary> /// Initializes a new instance of <see cref="MdAdmonition"/>. /// </summary> /// /// <param name="type"> /// The admonition's type. Any non-empty string is allowed. /// Recommended values are <c>attention</c>, <c>caution</c>, <c>danger</c>, <c>error</c>, /// <c>hint</c>, <c>important</c>, <c>note</c>, <c>tip</c> and <c>warning</c> /// </param> /// /// <param name="title"> /// The admonition's title. To create a admonition without title, use a different constructor overload. /// </param> /// /// <param name="content"> /// The admonition's content. /// </param> /// /// <exception cref="ArgumentException">Thrown when <paramref name="type"/> is null or whitespace.</exception> /// <exception cref="ArgumentNullException">Thrown when <paramref name="title"/> is <c>null</c>.</exception> public MdAdmonition(string type, MdSpan title, params MdBlock[] content) : this(type, title, (IEnumerable <MdBlock>)content) { }
/// <summary> /// Initializes a new instance of <see cref="MdAdmonition"/>. /// </summary> /// /// <param name="type"> /// The admonition's type. Any non-empty string is allowed. /// Recommended values are <c>attention</c>, <c>caution</c>, <c>danger</c>, <c>error</c>, /// <c>hint</c>, <c>important</c>, <c>note</c>, <c>tip</c> and <c>warning</c> /// </param> /// /// <param name="title"> /// The admonition's title. To create a admonition without title, use a different constructor overload. /// </param> /// /// <param name="content"> /// The admonition's content. /// </param> /// /// <exception cref="ArgumentException">Thrown when <paramref name="type"/> is null or whitespace.</exception> /// <exception cref="ArgumentNullException">Thrown when <paramref name="title"/> is <c>null</c>.</exception> public MdAdmonition(string type, MdSpan title, MdContainerBlockBase content) : this(type, title, (MdBlock)content) { }