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

namespace DsImagingWeb.Demos
{
    // このサンプルでは、SVG を一から作成し、視覚的に関連する要素をグループ化したうえで、
    // 各グループに一意の HTML ID を割り当てる方法を示します。
    // これにより、各グループを JavaScript コードから
    // 検索および操作(例:描画内の一部要素を
    // JavaScript で表示/非表示にするなど)できるようになります。
    // サンプルでは、SvgGraphicsArch と同様のアーチを描画しますが、
    // 以下の要素に対してそれぞれ個別の ID を割り当てています。
    // - 背景
    // - アーチの描画
    // - 寸法線
    public class SvgGraphicsHtmlId
    {
        public string DefaultMime { get => Common.Util.MimeTypes.SVG; }

        public Stream GenerateImageStream(string targetMime, Size pixelSize, float dpi, bool opaque, string[] sampleParams = null)
        {
            if (targetMime != Common.Util.MimeTypes.SVG)
                throw new Exception("This sample only supports SVG output format.");

            Color background = Color.PaleGoldenrod,
                line = Color.FromArgb(11, 83, 69),
                arc = Color.FromArgb(22, 160, 133),
                fill = Color.FromArgb(255, 171, 145),
                marks = Color.DarkGray,
                text = Color.Black,
                textBack = Color.Cornsilk;

            var Inch = dpi;

            // ラベル用のテキスト書式です。
            var tf = new TextFormat()
            {
                Font = GCTEXT.Font.FromFile(Path.Combine("Resources", "Fonts", "FreeSerif.ttf")),
                FontSize = Inch / 6,
                ForeColor = text,
            };
            // アーチの境界長方形です。
            var rc = new RectangleF(0, 0, Inch * 4, Inch * 4.8f);
            // 描画を中央に配置するための変形操作です。
            var transform = Matrix3x2.CreateTranslation(pixelSize.Width / 2f - rc.Width / 2, pixelSize.Height / 2f - rc.Height / 2);
            // 各レイヤー用のドキュメントリストです。
            var svgs = new List<GcSvgDocument>();

            // 0から2までのループ内で、目的の現在のレイヤーに属するコンテンツをgraphics上に描画し、
            // そのgraphicsを一時的なGcSvgDocumentに変換してリストに保存します。
            for (int i = 0; i < 3; ++i)
            {
                using var g = new GcSvgGraphics(pixelSize.Width, pixelSize.Height);
                if (i == 0)
                {
                    // 背景
                    g.FillRectangle(new RectangleF(0, 0, g.Width, g.Height), background);

                }
                else if (i == 1)
                {
                    // アーチとテキストラベル
                    g.Transform = transform;
                    DrawArch(g, rc, rc.Height * 0.3f, rc.Width * 0.08f, line, arc, fill);
                    g.DrawString("ARCH", tf, rc, TextAlignment.Center, ParagraphAlignment.Center, false);
                    g.Transform = Matrix3x2.Identity;
                }
                else if (i == 2)
                {
                    // 計測線
                    g.Transform = transform;
                    var w = Inch / 8;
                    var pen = new GCDRAW.Pen(marks);
                    // 幅の線とラベル
                    var txt = $"{(rc.Width / Inch):F}\"";
                    var s = g.MeasureString(txt, tf);
                    var d = s.Height * 1.5f;
                    g.DrawLine(rc.Left, rc.Top - d, rc.Right, rc.Top - d, pen);
                    g.DrawLine(rc.Left, rc.Top - d, rc.Left + w, rc.Top - d - w, pen);
                    g.DrawLine(rc.Left, rc.Top - d, rc.Left + w, rc.Top - d + w, pen);
                    g.DrawLine(rc.Right, rc.Top - d, rc.Right - w, rc.Top - d - w, pen);
                    g.DrawLine(rc.Right, rc.Top - d, rc.Right - w, rc.Top - d + w, pen);
                    var rcTxt = new RectangleF(rc.Left + rc.Width / 2 - s.Width / 2, rc.Top - d - s.Height / 2, s.Width, s.Height);
                    rcTxt.Inflate(2, 2);
                    g.FillRectangle(rcTxt, textBack);
                    g.DrawString(txt, tf, rcTxt, TextAlignment.Center, ParagraphAlignment.Center, false);
                    // 高さの線とラベル
                    txt = $"{(rc.Height / Inch):F}\"";
                    s = g.MeasureString(txt, tf);
                    d = s.Width;
                    g.DrawLine(rc.Left - d, rc.Top, rc.Left - d, rc.Bottom, pen);
                    g.DrawLine(rc.Left - d, rc.Top, rc.Left - d - w, rc.Top + w, pen);
                    g.DrawLine(rc.Left - d, rc.Top, rc.Left - d + w, rc.Top + w, pen);
                    g.DrawLine(rc.Left - d, rc.Bottom, rc.Left - d - w, rc.Bottom - w, pen);
                    g.DrawLine(rc.Left - d, rc.Bottom, rc.Left - d + w, rc.Bottom - w, pen);
                    rcTxt = new RectangleF(rc.Left - d - s.Width / 2, rc.Top + rc.Height / 2 - s.Height / 2, s.Width, s.Height);
                    rcTxt.Inflate(2, 2);
                    g.FillRectangle(rcTxt, textBack);
                    g.DrawString(txt, tf, rcTxt, TextAlignment.Center, ParagraphAlignment.Center, false);
                    g.Transform = Matrix3x2.Identity;
                }
                // レイヤーをGcSvgDocumentに変換し、リストに追加します。
                svgs.Add(g.ToSvgDocument());
            }

            // 結果となるSVGを作成し、一時ドキュメントの各要素を、検索や操作に使用できる
            // ユニークなIDを持つ個別のグループに追加します。
            var svg = new GcSvgDocument();
            for (int i = 0; i < svgs.Count; ++i)
            {
                var tsvg = svgs[i];
                var group = new SvgGroupElement()
                {
                    ID = $"myId{i}",
                };
                while (tsvg.RootSvg.Children.Count > 0)
                {
                    var e = tsvg.RootSvg.Children[0];
                    tsvg.RootSvg.Children.RemoveAt(0);
                    group.Children.Add(e);
                }
                svg.RootSvg.Children.Add(group);
            }
            svg.RootSvg.ViewBox = new SvgViewBox(0, 0, pixelSize.Width, pixelSize.Height);

            // 生成されたSVGをブラウザで表示すると、グループのユニークなIDを使用して、
            // JavaScriptから3つのレイヤーのいずれかを見つけて操作することができます。
            // 例えば、生成されたSVGをダウンロードしてブラウザで開き、ブラウザのコンソールで
            // 次のコマンドを実行することで、背景と計測線を非表示にできます。
            // document.getElementById("myId0").style.display= 'none' 
            // document.getElementById("myId2").style.display= 'none' 

            // 画像ファイルを保存します。
            var ms = new MemoryStream();
            svg.Save(ms);
            ms.Seek(0, SeekOrigin.Begin);
            return ms;
        }

        private void DrawArch(GcGraphics g, RectangleF rc, float harc, float wd, Color line, Color arch, Color fill)
        {
            var path = g.CreatePath();
            // 内部の塗りつぶしです(左下から始めて時計回りに描画します)。
            path.BeginFigure(rc.Left + wd, rc.Bottom - wd);
            path.AddLine(rc.Left + wd, rc.Top + harc);
            path.AddArc(new ArcSegment()
            {
                ArcSize = ArcSize.Small,
                Point = new PointF(rc.Right - wd, rc.Top + harc),
                RotationAngle = 0,
                Size = new SizeF(rc.Width / 2f - wd, harc - wd),
                SweepDirection = SweepDirection.Clockwise
            });
            path.AddLine(rc.Right - wd, rc.Bottom - wd);
            path.EndFigure(FigureEnd.Closed);
            g.FillPath(path, fill);
            path.Dispose();

            // アーチの外形線です(左下から始めて時計回りに描画します)。
            path = g.CreatePath();
            path.BeginFigure(rc.Left, rc.Bottom);
            path.AddLine(rc.Left, rc.Top + harc);
            path.AddLine(rc.Left + wd, rc.Top + harc);
            path.AddLine(rc.Left + wd, rc.Bottom - wd);
            path.EndFigure(FigureEnd.Closed);
            path.BeginFigure(rc.Left, rc.Top + harc);
            path.AddArc(new ArcSegment()
            {
                ArcSize = ArcSize.Small,
                Point = new PointF(rc.Right, rc.Top + harc),
                RotationAngle = 0,
                Size = new SizeF(rc.Width / 2f, harc),
                SweepDirection = SweepDirection.Clockwise
            });
            path.AddLine(rc.Right - wd, rc.Top + harc);
            path.AddArc(new ArcSegment()
            {
                ArcSize = ArcSize.Small,
                Point = new PointF(rc.Left + wd, rc.Top + harc),
                RotationAngle = 0,
                Size = new SizeF(rc.Width / 2f - wd, harc - wd),
                SweepDirection = SweepDirection.CounterClockwise
            });
            path.EndFigure(FigureEnd.Closed);
            path.BeginFigure(rc.Right, rc.Top + harc);
            path.AddLine(rc.Right, rc.Bottom);
            path.AddLine(rc.Right - wd, rc.Bottom - wd);
            path.AddLine(rc.Right - wd, rc.Top + harc);
            path.EndFigure(FigureEnd.Closed);
            path.BeginFigure(rc.Right, rc.Bottom);
            path.AddLine(rc.Left, rc.Bottom);
            path.AddLine(rc.Left + wd, rc.Bottom - wd);
            path.AddLine(rc.Right - wd, rc.Bottom - wd);
            path.EndFigure(FigureEnd.Closed);

            g.FillPath(path, arch);
            g.DrawPath(path, new GCDRAW.Pen(line, 2));

            path.Dispose();
        }
    }
}