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

namespace DsImagingWeb.Demos
{
    // このサンプルは、いくつかのバーコードを含む出荷ラベルのセットを印刷します。
    // タブストップを使用してデータを垂直に整列させることにも注意してください。
    public class ShippingLabels
    {
        // クライアント情報です。このサンプルは、クライアントごとに1つの出荷ラベルを印刷します。
        class Client
        {
            public string Name;
            public string Addr;
            public string City;
            public string Country;
            public Client(string name, string addr, string city, string country)
            {
                Name = name;
                Addr = addr;
                City = city;
                Country = country;
            }
        }

        // 顧客の基礎データです。
        static readonly List<Client> s_clients = new List<Client>()
        {
            new Client("Simons bistro", "Vinb詬tet 34", "Kenhavn", "Denmark"),
            new Client("Richter Supermarkt", "Starenweg 5", "Gen钁e", "Switzerland"),
            new Client("Bon app'", "12, rue des Bouchers", "Marseille", "France"),
            new Client("Rattlesnake Canyon Grocery", "2817 Milton Dr.", "Albuquerque", "USA"),
            new Client("Lehmanns Marktstand", "Magazinweg 7", "Frankfurt a.M.", "Germany"),
            new Client("LILA-Supermercado", "Carrera 52 con Ave. Bol咩ar #65-98 Llano Largo", "Barquisimeto", "Venezuela"),
            new Client("Ernst Handel", "Kirchgasse 6", "Graz", "Austria"),
            new Client("Pericles Comidas cl疽icas", "Calle Dr. Jorge Cash 321", "M騙ico D.F.", "Mexico"),
            new Client("Drachenblut Delikatessen", "Walserweg 21", "Aachen", "Germany"),
            new Client("Queen Cozinha", "Alameda dos Can灑ios, 891", "S縊 Paulo", "Brazil"),
            new Client("Tortuga Restaurante", "Avda. Azteca 123", "M騙ico D.F.", "Mexico"),
            new Client("Save-a-lot Markets", "187 Suffolk Ln.", "Boise", "USA"),
            new Client("Franchi S.p.A.", "Via Monte Bianco 34", "Torino", "Italy"),
            // new Client("", "", "", ""),
        };

        // メインのサンプルドライバーです。
        public Stream GenerateImageStream(string targetMime, Size pixelSize, float dpi, bool opaque, string[] sampleParams = null)
        {
            var bmp = new GcBitmap(pixelSize.Width, pixelSize.Height, opaque, dpi, dpi);
            var g = bmp.CreateGraphics(Color.White);
            var ms = new MemoryStream();
            GcTiffWriter tw = null;
            if (targetMime == Common.Util.MimeTypes.TIFF)
                tw = new GcTiffWriter(ms);

            Init(pixelSize);

            // 顧客ごとにループし、1ページにつき最大4枚のラベルを印刷します。
            int i = 0;
            for (int pg = 0; pg < (s_clients.Count + 3) / 4; ++pg)
            {
                PrintLabel(s_clients[i++], g, new RectangleF(hmargin, vmargin, _labelWidth, _labelHeight));
                if (i < s_clients.Count)
                    PrintLabel(s_clients[i++], g, new RectangleF(hmargin + _labelWidth, vmargin, _labelWidth, _labelHeight));
                if (i < s_clients.Count)
                    PrintLabel(s_clients[i++], g, new RectangleF(hmargin, vmargin + _labelHeight, _labelWidth, _labelHeight));
                if (i < s_clients.Count)
                    PrintLabel(s_clients[i++], g, new RectangleF(hmargin + _labelWidth, vmargin + _labelHeight, _labelWidth, _labelHeight));

                // TIFF以外の形式の場合は1ページだけ印刷します。
                if (tw == null)
                    break;
                if (i < s_clients.Count)
                {
                    tw.AppendFrame(bmp);
                    bmp.Clear(Color.White);
                }
            }

            switch (targetMime)
            {
                case Common.Util.MimeTypes.TIFF:
                    tw.AppendFrame(bmp);
                    tw.Dispose();
                    break;
                case Common.Util.MimeTypes.JPEG:
                    bmp.SaveAsJpeg(ms);
                    break;
                case Common.Util.MimeTypes.PNG:
                    bmp.SaveAsPng(ms);
                    break;
                case Common.Util.MimeTypes.BMP:
                    bmp.SaveAsBmp(ms);
                    break;
                case Common.Util.MimeTypes.GIF:
                    bmp.SaveAsGif(ms);
                    break;
                case Common.Util.MimeTypes.WEBP:
                    bmp.SaveAsWebp(ms);
                    break;
                case Common.Util.MimeTypes.ICO:
                    bmp.SaveAsIco(ms, null, IcoFrameEncoding.Png);
                    break;
                default:
                    throw new Exception($"Encoding {targetMime} is not supported.");
            }
            bmp.Dispose();
            g.Dispose();
            Term();
            return ms;
        }

        // ラベルをレンダリングするために使用される定数と変数です。
        const float hmargin = 24, vmargin = 36;
        float _labelWidth, _labelHeight;
        GCDRAW.Pen _pBold, _pNorm;
        GCTEXT.Font _fontReg, _fontBold;
        TextFormat _tfSmall, _tfSmallB, _tfLarge;
        List<TabStop> _tsHeader, _tsFrom, _tsCodes;
        GCDRAW.Image _logo;
        ImageAlign _ia;
        GcBarcode bcTop, bcBottom;

        // ラベルの描画に使用される変数を初期化します。
        void Init(Size pixelSize)
        {
            _labelWidth = (pixelSize.Width - hmargin * 2) / 2;
            _labelHeight = _labelWidth;
            _pBold = new GCDRAW.Pen(Color.Black, 2);
            _pNorm = new GCDRAW.Pen(Color.Black, 0.5f);
            _logo = GCDRAW.Image.FromFile(Path.Combine("Resources", "ImagesBis", "AcmeLogo-vertical-250px.png"));
            _fontReg = GCTEXT.Font.FromFile(Path.Combine("Resources", "Fonts", "NotoSans-Regular.ttf"));
            _fontBold = GCTEXT.Font.FromFile(Path.Combine("Resources", "Fonts", "NotoSans-Bold.ttf"));
            _tfSmall = new TextFormat() { Font = _fontReg, FontSize = 9};
            _tfSmallB = new TextFormat() { Font = _fontBold, FontSize = 10 };
            _tfLarge = new TextFormat() { Font = _fontBold, FontSize = 17 };

            _ia = new ImageAlign(ImageAlignHorz.Right, ImageAlignVert.Center, true, true, true, false, false);
            _tsHeader = [new TabStop(24, TabStopAlignment.Leading), new TabStop(108, TabStopAlignment.Leading)];
            _tsFrom = [new TabStop(12, TabStopAlignment.Leading), new TabStop(72, TabStopAlignment.Leading)];
            _tsCodes = [new TabStop(_labelWidth / 8, TabStopAlignment.Center)];

            bcTop = new GcBarcode() {
                TextFormat = _tfSmall,
                CodeType = CodeType.Code_128_B,
                HorizontalAlignment = ImageAlignHorz.Center,
                VerticalAlignment = ImageAlignVert.Center,
                ScaleFactor = 2,
            };
            bcTop.Options.CaptionPosition = BarCodeCaptionPosition.Below;
            bcBottom = new GcBarcode()
            {
                TextFormat = _tfSmall,
                CodeType = CodeType.Code_128auto,
                HorizontalAlignment = ImageAlignHorz.Center,
                VerticalAlignment = ImageAlignVert.Center,
                ScaleFactor = 2,
            };
            bcBottom.Options.CaptionPosition = BarCodeCaptionPosition.Below;
        }

        void Term()
        {
            _logo?.Dispose();
            _logo = null;
        }

        void PrintLabel(Client client, GcGraphics g, RectangleF bounds)
        {
            // いくつかのサンプルデータをランダム化するために使用します。
            var rnd = Common.Util.NewRandom();

            // サンプルの荷送人/送付先データです。
            var shipper = rnd.Next(2) == 0 ? "United Package" : "Speedy Express";
            (string name, string addr, string city, string country, string zip) sender =
                (name: "ACME Inc.", addr: "1 Main Street", city: "Metropolis", country: "USA", zip: "34567");
            var shuttle = rnd.Next(10000, 15000).ToString();
            var area = rnd.Next(1, 12).ToString();
            var tour = rnd.Next(0, 20).ToString();

            var tl = g.CreateTextLayout();
            tl.DefaultFormat.Font = _tfSmall.Font;
            tl.DefaultFormat.FontSize = _tfSmall.FontSize;
            tl.LineSpacingScaleFactor = 0.75f;
            tl.ParagraphAlignment = ParagraphAlignment.Center;

            // ヘッダー
            var hHeader = bounds.Height / 2 / 5;
            var rHeader = new RectangleF(bounds.X, bounds.Y, bounds.Width, hHeader);
            tl.TabStops = _tsHeader;
            tl.Append($"\t{shipper}");
            tl.Append("\tCMR", _tfSmallB);
            tl.Append($"\n\t\t{Common.Util.TimeNow().ToString("dd-MMM-yy", CultureInfo.InvariantCulture)}");
            tl.MaxHeight = rHeader.Height;
            tl.MaxWidth = rHeader.Width;    
            tl.PerformLayout(true);
            g.DrawTextLayout(tl, rHeader.Location);

            var rLogo = rHeader;
            rLogo.Inflate(-5, -5);
            g.DrawImage(_logo, rLogo,  null, _ia);

            // 差出人
            var hFrom = hHeader;
            var rFrom = new RectangleF(bounds.X, rHeader.Bottom, bounds.Width, hFrom);
            tl.Clear();
            tl.TabStops = _tsFrom;
            tl.Append($"\tFrom:\t{sender.name}\n\t\t{sender.addr}\n\t\t{sender.city}, {sender.country} {sender.zip}");
            tl.MaxHeight = rFrom.Height;
            tl.MaxWidth = rFrom.Width;
            tl.PerformLayout(true);
            g.DrawTextLayout(tl, rFrom.Location);

            // 宛先
            var hTo = bounds.Height / 2 / 3;
            var rTo = new RectangleF(bounds.X, rFrom.Bottom, bounds.Width, hTo);
            tl.Clear();
            tl.TabStops = _tsFrom;
            tl.Append($"\tTo:\t{client.Name}\n\t\t{client.Addr}\n\t\t{client.City}\n\t\t{client.Country}");
            tl.MaxHeight = rTo.Height;
            tl.MaxWidth = rTo.Width;
            tl.PerformLayout(true);
            g.DrawTextLayout(tl, rTo.Location);

            // コード
            var hCodes = bounds.Height / 2 / (15f / 4);
            var rCodes = new RectangleF(bounds.X, rTo.Bottom, bounds.Width, hCodes);
            tl.TabStops = _tsCodes;
            tl.Clear();
            tl.AppendLine("\tShuttle");
            tl.Append($"\t{shuttle}", _tfLarge);
            tl.MaxHeight = rCodes.Height;
            tl.MaxWidth = rCodes.Width / 4;
            tl.PerformLayout(true);
            g.DrawTextLayout(tl, rCodes.Location);

            tl.Clear();
            tl.AppendLine("\tArea");
            tl.Append($"\t{area}", _tfLarge);
            tl.PerformLayout(true);
            g.DrawTextLayout(tl, new PointF(rCodes.X + rCodes.Width / 4, rCodes.Y));

            tl.Clear();
            tl.AppendLine("\tException");
            tl.Append("\t ", _tfLarge);
            tl.PerformLayout(true);
            g.DrawTextLayout(tl, new PointF(rCodes.X + rCodes.Width / 4 * 2, rCodes.Y));

            tl.Clear();
            tl.AppendLine("\tTour");
            tl.Append($"\t{tour}", _tfLarge);
            tl.PerformLayout(true);
            g.DrawTextLayout(tl, new PointF(rCodes.X + rCodes.Width / 4 * 3, rCodes.Y));

            // バーコード
            var hBarcodes = bounds.Height / 2;
            var rBcTop = new RectangleF(bounds.X, rCodes.Bottom, bounds.Width, hBarcodes / 2);
            var rBcBottom = new RectangleF(bounds.X, rBcTop.Bottom, bounds.Width, hBarcodes / 2);

            bcTop.Text = client.Country;
            g.DrawBarcode(bcTop, rBcTop);

            // 長めの「コード」を作成します。
            var code = $"{client.Name[0]}{client.Addr[0]}{client.City[0]}{client.Country[0]}".ToUpper();
            bcBottom.Text = $"{code}{client.GetHashCode().ToString("X12")}";
            g.DrawBarcode(bcBottom, rBcBottom);

            // 線
            g.DrawLine(rHeader.Left, rHeader.Bottom, rHeader.Right, rHeader.Bottom, _pNorm);
            g.DrawLine(rFrom.Left, rFrom.Bottom, rFrom.Right, rFrom.Bottom, _pNorm);
            g.DrawLine(rTo.Left, rTo.Bottom, rTo.Right, rTo.Bottom, _pNorm);
            g.DrawLine(rCodes.Left, rCodes.Bottom, rCodes.Right, rCodes.Bottom, _pNorm);

            g.DrawLine(rCodes.Left + rCodes.Width / 4, rCodes.Top, rCodes.Left + rCodes.Width / 4, rCodes.Bottom, _pNorm);
            g.DrawLine(rCodes.Left + rCodes.Width / 4 * 2, rCodes.Top, rCodes.Left + rCodes.Width / 4 * 2, rCodes.Bottom, _pNorm);
            g.DrawLine(rCodes.Left + rCodes.Width / 4 * 3, rCodes.Top, rCodes.Left + rCodes.Width / 4 * 3, rCodes.Bottom, _pNorm);

            g.DrawRectangle(bounds, _pBold);
        }
    }
}