HierarchicalTable.cs
// 
// このコードは、DioDocs for Imaging のサンプルの一部として提供されています。
// © MESCIUS inc. All rights reserved.
// 
using System;
using System.IO;
using System.Drawing;
using System.Numerics;
using GrapeCity.Documents.Drawing;
using GrapeCity.Documents.Text;
using GrapeCity.Documents.Imaging;
using GrapeCity.Documents.Layout;
using GCTEXT = GrapeCity.Documents.Text;
using GCDRAW = GrapeCity.Documents.Drawing;

namespace DsImagingWeb.Demos
{
    // このサンプルでは、GrapeCity.Documents.Drawing.TableRenderer および
    // 関連クラスを使用して、階層データを含むテーブルを描画する方法を示します。
    public class HierarchicalTable
    {
        public GcBitmap GenerateImage(Size pixelSize, float dpi, bool opaque, string[] sampleParams = null)
        {
            var bmp = new GcBitmap(pixelSize.Width, pixelSize.Height, opaque, dpi, dpi);
            using var g = bmp.CreateGraphics(Color.White);
            g.Transform = Matrix3x2.CreateScale(1.6f);
            DrawTable(g, pixelSize.Width, pixelSize.Height);
            g.Transform = Matrix3x2.Identity;
            return bmp;
        }

        class Range
        {
            public Range(double valueLow, double deltaLow, double valueHigh, double deltaHigh)
            {
                ValueLow = valueLow;
                DeltaLow = deltaLow;
                ValueHigh = valueHigh;
                DeltaHigh = deltaHigh;
            }
            public double ValueLow { get; }
            public double DeltaLow { get; } 
            public double ValueHigh { get; } 
            public double DeltaHigh { get; }
        }

        class Scope
        {
            public Scope(int ntrials, Range ad, Range @as, Range nr, Range ra, Range av)
            {
                Ntrials = ntrials;
                AD = ad;
                AS = @as;
                NR = nr;
                RA = ra;
                AV = av;
            }
            public int Ntrials { get; }
            public Range AD { get; }
            public Range AS { get; }
            public Range NR { get; }
            public Range RA { get; }
            public Range AV { get; }
        }

        static readonly Scope[] Data = new Scope[]
        {
            new Scope(20, new Range(50.5, 3.2, 74.8, 4.9), new Range(50.4, 3.6, 74.9, 6.8),
                new Range(50.3, 2.7, 75.2, 6.5), new Range(49.9, 2.6, 74.8, 6.6), new Range(50.3, 3.0, 74.9, 6.2)),
            new Scope(60, new Range(50.3, 4.5, 75.1, 4.5), new Range(50.8, 4.1, 75.6, 6.5),
                new Range(50.7, 3.4, 75.7, 5.7), new Range(49.5, 2.9, 74.7, 5.9), new Range(50.3, 3.7, 75.3, 5.6)),
            new Scope(100, new Range(52.6, 4.7, 75.9, 4.8), new Range(50.3, 4.6, 75.6, 5.9),
                new Range(51.1, 4.1, 75.3, 5.4), new Range(49.8, 3.3, 74.9, 5.3), new Range(51.9, 4.2, 75.4, 5.4)),
            new Scope(140, new Range(51.7, 5.3, 75.8, 7.0), new Range(49.8, 5.7, 75.4, 6.8),
                new Range(51.4, 4.4, 75.6, 5.6), new Range(49.5, 3.5, 74.6, 6.2), new Range(50.6, 4.7, 75.4, 6.4)),
            new Scope(180, new Range(51.5, 8.4, 76.2, 9.5), new Range(52.2, 6.6, 74.9, 7.0),
                new Range(52.0, 4.9, 75.7, 6.4), new Range(50.7, 4.2, 74.8, 6.3), new Range(51.6, 6.0, 75.4, 7.3))
        };

        static void DrawTable(GcGraphics g, float pageWidth, float pageHeight)
        {
            var host = new LayoutHost();
            var view = host.CreateView(pageWidth, pageHeight);

            var rt = view.CreateRect();
            rt.AnchorTopLeft(null, 36, 36);

            int scopeCount = Data.Length;

            var ta = new TableRenderer(g,
                rt, FixedTableSides.TopLeft,
                rowCount: 12,
                columnCount: scopeCount + 2,
                gridLineColor: Color.Black,
                gridLineWidth: 1,
                rowMinHeight: 20);

            // 一部のグリッド線に対してより太い線幅を設定します。
            ta.SetVerticalGridLineWidth(2, 3f);
            for (int i = 0; i < 13; i += 2)
            {
                ta.SetHorizontalGridLineWidth(i, 3f);
            }

            var fmt = new TextFormat
            {
                Font = GCTEXT.Font.FromFile(Path.Combine("Resources", "Fonts", "NotoSerif-Regular.ttf")),
                FontSize = 16,
                FontSizeInGraphicUnits = true
            };

            ta.AddCell(0, 0, 2, 2);
            ta.AddCell(new CellStyle
            {
                TextAlignment = TextAlignment.Center,
                CreateTextLayout = (g, cs, data) =>
                {
                    var tl = g.CreateTextLayout();
                    tl.Append("|", fmt);
                    tl.Append("N", new TextFormat(fmt) { FontItalic = true });
                    tl.Append("trials", new TextFormat(fmt) { FontItalic = true, Subscript = true });
                    tl.Append("|", fmt);
                    return tl;
                }
            }, 0, 2, 1, scopeCount, null);

            var cs = new CellStyle
            {
                PaddingLeftRight = 10,
                TextAlignment = TextAlignment.Center,
                ParagraphAlignment = ParagraphAlignment.Center,
                FixedWidth = false,
                TextFormat = fmt
            };

            // 回転したセルを追加します。
            ta.AddCell(new CellStyle(cs)
            {
                PaddingTopBottom = 10,
                RotationAngle = 270
            }, 2, 0, 8, 1, "Subject");

            // AddCellメソッドの各呼び出しにCellStyleを渡すのを避けるため、
            // DefaultCellStyle にスタイルを割り当てます。
            ta.DefaultCellStyle = cs;

            ta.AddCell(2, 1, 2, 1, "AD");
            ta.AddCell(4, 1, 2, 1, "AS");
            ta.AddCell(6, 1, 2, 1, "NR");
            ta.AddCell(8, 1, 2, 1, "RA");
            ta.AddCell(10, 0, 2, 2, "Average:");

            for (int i = 0; i < scopeCount; i++)
            {
                var sc = Data[i];
                int ci = i + 2;
                ta.AddCell(1, ci, sc.Ntrials.ToString());
                ta.AddCell(2, ci, $"{sc.AD.ValueLow:n1} ± {sc.AD.DeltaLow:n1}");
                ta.AddCell(3, ci, $"{sc.AD.ValueHigh:n1} ± {sc.AD.DeltaHigh:n1}");
                ta.AddCell(4, ci, $"{sc.AS.ValueLow:n1} ± {sc.AS.DeltaLow:n1}");
                ta.AddCell(5, ci, $"{sc.AS.ValueHigh:n1} ± {sc.AS.DeltaHigh:n1}");
                ta.AddCell(6, ci, $"{sc.NR.ValueLow:n1} ± {sc.NR.DeltaLow:n1}");
                ta.AddCell(7, ci, $"{sc.NR.ValueHigh:n1} ± {sc.NR.DeltaHigh:n1}");
                ta.AddCell(8, ci, $"{sc.RA.ValueLow:n1} ± {sc.RA.DeltaLow:n1}");
                ta.AddCell(9, ci, $"{sc.RA.ValueHigh:n1} ± {sc.RA.DeltaHigh:n1}");
                ta.AddCell(10, ci, $"{sc.AV.ValueLow:n1} ± {sc.AV.DeltaLow:n1}");
                ta.AddCell(11, ci, $"{sc.AV.ValueHigh:n1} ± {sc.AV.DeltaHigh:n1}");
            }

            // 1行おきに特別な背景を追加します。
            ta.DefaultCellStyle = new CellStyle
            {
                FillColor = Color.FromArgb(233, 240, 222),
                Background = true
            };
            for (int i = 3; i < 12; i += 2)
            {
                ta.AddCell(i, 2, 1, scopeCount);
            }

            ta.Render();
        }
    }
}