LangLayers.cs
// 完毕:
using System;
using System.IO;
using System.Drawing;
using System.Collections.Generic;
using GrapeCity.Documents.Pdf;
using GrapeCity.Documents.Pdf.Layers;
using GrapeCity.Documents.Pdf.Annotations;
using GrapeCity.Documents.Pdf.Graphics;
using GrapeCity.Documents.Text;
using GrapeCity.Documents.Drawing;
using GCTEXT = GrapeCity.Documents.Text;
using GCDRAW = GrapeCity.Documents.Drawing;

namespace DsPdfWeb.Demos
{
    // This sample creates a PDF with a Document Solutions data sheet.
    // PDF layers (optional content) are used to provide the data sheet
    // in several languages in the same one page PDF. A supporting viewer
    // (e.g. Adobe Reader) can be used to turn different languages on or off.
    public class LangLayers
    {
        // Layer names correspond to included languages:
        const string c_lyrEN = "English";
        const string c_lyrFR = "French";
        const string c_lyrRU = "Russian";

        public int CreatePDF(Stream stream)
        {
            var doc = new GcPdfDocument();

            // Add layers for different languages,
            // set default visibility to false for all languages except one:
            doc.OptionalContent.AddLayer(c_lyrEN);
            doc.OptionalContent.AddLayer(c_lyrFR);
            doc.OptionalContent.SetLayerDefaultState(c_lyrFR, false);
            doc.OptionalContent.AddLayer(c_lyrRU);
            doc.OptionalContent.SetLayerDefaultState(c_lyrRU, false);

            var page = doc.Pages.Add();
            page.Landscape = true;
            var g = page.Graphics;

            // Background image:
            var picBack = GCDRAW.Image.FromFile(Path.Combine("Resources", "ImagesBis", "2020-website-gcdocs-headers_tall.png"));
            var rc = page.Bounds;
            rc.Height *= 0.5f;
            g.DrawImage(picBack, rc, null, ImageAlign.StretchImage);

            // Image with some screenshots:
            var picScreens = GCDRAW.Image.FromFile(Path.Combine("Resources", "ImagesBis", "docs-main-02.png"));
            var picScreensSize = new SizeF(picScreens.Width / 2, picScreens.Height / 2);
            var rcScreen = new RectangleF(
                page.Bounds.Width - picScreensSize.Width - 8,
                8,
                picScreensSize.Width,
                picScreensSize.Height);
            g.DrawImage(picScreens, rcScreen, null, ImageAlign.StretchImage);

            // Text formats:
            var fReg = GCTEXT.Font.FromFile(Path.Combine("Resources", "Fonts", "OpenSans-Regular.ttf"));
            var fBold = GCTEXT.Font.FromFile(Path.Combine("Resources", "Fonts", "OpenSans-Bold.ttf"));
            var tfNorm = new TextFormat() { Font = fReg, FontSize = 9, ForeColor = Color.White };
            var tfCap = new TextFormat(tfNorm) { FontSize = tfNorm.FontSize * 1.6f };
            var tfBlack = new TextFormat(tfNorm) { ForeColor = Color.FromArgb(32, 32, 32) };
            var tfHdr = new TextFormat(tfBlack) { Font = fBold };

            // Draw English texts:
            var ip = new PointF(36, 24);
            var ip1 = ip;
            ip = DrawText1(c_lyrEN, doc, g, ip1, tfNorm, tfCap);
            ip.Y += 82;
            var ip2 = ip;
            DrawText2(c_lyrEN, doc, g, ip2, tfHdr, tfBlack);

            // Draw French texts:
            DrawText1(c_lyrFR, doc, g, ip1, tfNorm, tfCap);
            DrawText2(c_lyrFR, doc, g, ip2, tfHdr, tfBlack);

            // Draw Russian texts:
            DrawText1(c_lyrRU, doc, g, ip1, tfNorm, tfCap);
            DrawText2(c_lyrRU, doc, g, ip2, tfHdr, tfBlack);

            // 保存 PDF:
            doc.Save(stream);
            return doc.Pages.Count;
        }

        class Texts
        {
            public string Caption { get; set; }
            public string SubCap { get; set; }
            public string Bullet1 { get; set; }
            public string Bullet2 { get; set; }
            public string Bullet3 { get; set; }
            public string Bullet4 { get; set; }
            public string Bullet5 { get; set; }
            public string Bullet6 { get; set; }
            public string Pt1Hdr { get; set; }
            public string Pt1Txt { get; set; }
            public string Pt2Hdr { get; set; }
            public string Pt2Txt { get; set; }
            public string Pt3Hdr { get; set; }
            public string Pt3Txt { get; set; }
            public string Pt4Hdr { get; set; }
            public string Pt4Txt { get; set; }
            public string Pt5Hdr { get; set; }
            public string Pt5Txt { get; set; }
            public string Pt6Hdr { get; set; }
            public string Pt6Txt { get; set; }
        }

        static Dictionary<string, Texts> _texts = new Dictionary<string, Texts>()
        {
            { "English", new Texts() {
                Caption = "Fast, Efficient Document APIs for .NET 6 and Java Applications",
                SubCap = "Take total control of your documents with ultra-fast, low-footprint APIs for enterprise apps.",
                Bullet1 = "Generate, load, edit, save XLSX spreadsheets, PDF, Images, and DOCX files using C# .NET, VB.NET, or Java",
                Bullet2 = "View, edit, print, fill and submit documents in JavaScript PDF Viewer and PDF Editor",
                Bullet3 = "Fully compatible with Windows, macOS, and Linux",
                Bullet4 = "No dependencies on Excel, Word, or Acrobat",
                Bullet5 = "Deploy to a variety of cloud-based services, including Azure, AWS, and AWS Lambda",
                Bullet6 = "Product available individually or as a bundle",
                Pt1Hdr = "High-Speed, Small Footprint, No Dependencies",
                Pt1Txt = "The .NET 6 Document API is designed to generate large, optimized documents fast – while remaining lightweight and extensible, giving you greater flexibility, and creativity in developing your applications.",
                Pt2Hdr = "Full .NET Support for Windows, Linux, and Mac",
                Pt2Txt = "Develop for any .NET platform or major operating system with a single code base. Use in your apps for .NET, C#, VB.NET .NET Framework, Mono, Xamarin.iOS, and Xamarin.Android.",
                Pt3Hdr = "Comprehensive, Highly Programmable",
                Pt3Txt = "Do more with your Excel spreadsheets, Word documents, PDFs, and images with our feature-rich APIs. Create, load, edit, save, and convert your business documents with intuitive tools.",
                Pt4Hdr = "Zero Dependencies",
                Pt4Txt = "Generate and edit digital documents in your applications without any dependencies on Adobe Acrobat, Microsoft Word, or Excel. Build your documents even faster with zero dependencies.",
                Pt5Hdr = "Deploy Apps to the Cloud",
                Pt5Txt = "Be everywhere with cloud-based deployment using NuGet and Document Solutions. You can now deploy to Azure, AWS, and AWS Lambda in a few short steps.",
                Pt6Hdr = "Supports Hundreds of Features",
                Pt6Txt = "Generate full-featured documents and nearly lossless imports! These APIs complete your toolkit through high-performance features, including Excel pivot tables, PDF digital signatures, and Word text comments.",
            }},
            { "French", new Texts() {
                Caption = "API de document rapides et efficaces pour les applications .NET 6 et Java",
                SubCap = "Prenez le contrôle total de vos documents avec des API ultra-rapides et à faible encombrement pour les applications d'entreprise.",
                Bullet1 = "Générez, chargez, modifiez et enregistrez des feuilles de calcul XLSX, des fichiers PDF, des images et des fichiers DOCX à l'aide de C# .NET, VB.NET ou Java",
                Bullet2 = "Affichez, modifiez, imprimez, remplissez et soumettez des documents dans JavaScript PDF Viewer et PDF Editor",
                Bullet3 = "Entièrement compatible avec Windows, macOS et Linux",
                Bullet4 = "Aucune dépendance à Excel, Word ou Acrobat",
                Bullet5 = "Déploiement sur une variété de services basés sur le cloud, y compris Azure, AWS et AWS Lambda",
                Bullet6 = "Produit disponible individuellement ou en lot",
                Pt1Hdr = "Haut vitesse, faible encombrement, aucune dépendance",
                Pt1Txt = "L'API de document .NET 6 est conçue pour générer rapidement des documents volumineux et optimisés, tout en restant léger et extensible, vous offrant une plus grande flexibilité et créativité dans le développement de vos applications.",
                Pt2Hdr = "Support complète de .NET pour Windows, Linux et Mac",
                Pt2Txt = "Développez pour n'importe quelle plate-forme .NET ou système d'exploitation majeur avec une base de code unique. À utiliser dans vos applications pour .NET, C#, VB.NET .NET Framework, Mono, Xamarin.iOS et Xamarin.Android.",
                Pt3Hdr = "Complet, hautement programmable",
                Pt3Txt = "Faites-en plus avec vos feuilles de calcul Excel, vos documents Word, vos PDF et vos images grâce à nos API riches en fonctionnalités. Créez, chargez, modifiez, enregistrez et convertissez vos documents commerciaux avec des outils intuitifs.",
                Pt4Hdr = "Zéro dépendances",
                Pt4Txt = "Générez et modifiez des documents numériques dans vos applications sans aucune dépendance à Adobe Acrobat, Microsoft Word ou Excel. Créez vos documents encore plus rapidement sans aucune dépendance.",
                Pt5Hdr = "Déployer des applications sur le cloud",
                Pt5Txt = "Soyez partout avec un déploiement basé sur le cloud à l'aide de NuGet et Document Solutions. Vous pouvez désormais déployer sur Azure, AWS et AWS Lambda en quelques étapes courtes.",
                Pt6Hdr = "Prend en charge des centaines de fonctionnalités",
                Pt6Txt = "Générez des documents complets et des importations presque sans pertes! Ces API complètent votre boîte à outils grâce à des fonctionnalités hautes performances, notamment des tableaux croisés dynamiques Excel, des signatures numériques PDF et des commentaires de texte Word.",
            }},
            { "Russian", new Texts() {
                Caption = "Document API - для быстрой и эффективной работы с офисными документами под .NET 6 и Java",
                SubCap = "Полный контроль над офисным документооборотом с помощью сверх-быстрых, нетребовательных к памяти библиотек Document API",
                Bullet1 = "Создание, загрузка, редактирование и сохранение файлов XLSX, PDF, DOCX и изображений из кода на C#, VB.NET и Java",
                Bullet2 = "Просмотр, редактирование, печать, заполнение и пересылка PDF файлов с помощью просмотрщика и редактора на чистом JavaScript",
                Bullet3 = "Полная совместимость с операционными системами Windows, macOS и Linux",
                Bullet4 = "Отсутствие зависимостей от Excel, Word и Acrobat",
                Bullet5 = "Развертывание на Azure, AWS и AWS Lambda",
                Bullet6 = "Продукты доступны индивидуально или составе единой студии",
                Pt1Hdr = "Быстрые, маленькие библиотеки без внешних зависимостей",
                Pt1Txt = "Наши API созданы для генерации сложных оптимизированных документов с большой скоростью и малыми требованиями к памяти, для лёгкой и удобной разработки аппликаций.",
                Pt2Hdr = "Полная поддержка .NET под Windows, Linux и macOS",
                Pt2Txt = "Единый код для всех платформ и операционных систем, поддержка разработки на C# и VB.NET под .NET 6, .NET Framework, Mono, Xamarin.iOS и Xamarin.Android.",
                Pt3Hdr = "Функциональность и программируемость",
                Pt3Txt = "Полная поддержка функциональности MS Excel, MS Word и PDF документов и изображений. Создание, загрузка, редактирование, сохранение и конвертация документов.",
                Pt4Hdr = "Отсутствие внешних зависимостей",
                Pt4Txt = "Быстрое и эффективное создание и редактирование электронных документов без зависимостей от Adobe Acrobat, Microsoft Word и Excel.",
                Pt5Hdr = "Разворачивание аппликаций в \"облаке\"",
                Pt5Txt = "Простое и удобное разворачивание аппликаций в Azure, AWS и AWS Lambda. Все зависимости от Document Solutions могут разрешаться через NuGet.",
                Pt6Hdr = "Широкая поддержка возможностей форматов",
                Pt6Txt = "Доступ практически ко всем возможностям, заложенным в спецификациях форматов документов - сводные таблицы Excel, множественные цифровые подписи PDF, комментарии и многое другое.",
            }}
        };


        static PointF DrawText1(string lang, GcPdfDocument doc, GcPdfGraphics g, PointF ip, TextFormat tf, TextFormat tfCap)
        {
            var txts = _texts[lang];
            g.BeginLayer(lang);

            var tl = g.CreateTextLayout();
            tl.MaxWidth = 72 * 5;

            tl.AppendLine(txts.Caption, tfCap);
            tl.AppendLine(tf);
            tl.AppendLine(txts.SubCap, tf);
            tl.AppendLine(tf);
            g.DrawTextLayout(tl, ip);

            // Bullet list:
            ip.Y += tl.ContentHeight;
            tl.Clear();
            const string bullet = "\x2022\x2003";
            tl.FirstLineIndent = -g.MeasureString(bullet, tf).Width;
            tl.ParagraphSpacing += 4;

            tl.Append(bullet, tf);
            tl.AppendLine(txts.Bullet1, tf);
            tl.Append(bullet, tf);
            tl.AppendLine(txts.Bullet2, tf);
            tl.Append(bullet, tf);
            tl.AppendLine(txts.Bullet3, tf);
            tl.Append(bullet, tf);
            tl.AppendLine(txts.Bullet4, tf);
            tl.Append(bullet, tf);
            tl.AppendLine(txts.Bullet5, tf);
            tl.Append(bullet, tf);
            tl.AppendLine(txts.Bullet6, tf);
            g.DrawTextLayout(tl, ip);

            g.EndLayer();

            ip.Y += tl.ContentHeight;
            return ip;
        }

        static void DrawText2(string lang, GcPdfDocument doc, GcPdfGraphics g, PointF ip, TextFormat tfHdr, TextFormat tfBlack)
        {
            var picIcons = GCDRAW.Image.FromFile(Path.Combine("Resources", "ImagesBis", "2020-icon-sprites-dx.png"));
            var iconSize = new SizeF(picIcons.Width / 12, picIcons.Height / 2);

            var tl = g.CreateTextLayout();
            tl.FirstLineIndent = 0;
            tl.MaxWidth = (doc.Pages[0].Bounds.Width - ip.X * 2 - 36) / 3;
            var ip1 = ip;
            var txts = _texts[lang];

            drawItem(ip, 0, txts.Pt1Hdr, txts.Pt1Txt);
            ip.X += tl.MaxWidth.Value + 18;
            ip1.Y = Math.Max(ip1.Y, ip.Y + tl.ContentHeight);
            drawItem(ip, 1, txts.Pt2Hdr, txts.Pt2Txt);
            ip.X += tl.MaxWidth.Value + 18;
            ip1.Y = Math.Max(ip1.Y, ip.Y + tl.ContentHeight);
            drawItem(ip, 2, txts.Pt3Hdr, txts.Pt3Txt);

            ip1.Y = Math.Max(ip1.Y, ip.Y + tl.ContentHeight);
            ip1.Y += iconSize.Height + 18;
            ip = ip1;
            drawItem(ip, 3, txts.Pt4Hdr, txts.Pt4Txt);
            ip.X += tl.MaxWidth.Value + 18;
            drawItem(ip, 4, txts.Pt5Hdr, txts.Pt5Txt);
            ip.X += tl.MaxWidth.Value + 18;
            drawItem(ip, 5, txts.Pt6Hdr, txts.Pt6Txt);

            void drawItem(PointF p, int imgIdx, string header, string text)
            {
                if (lang == c_lyrEN)
                {
                    // Draw icons just once:
                    var xImg = p.X + tl.MaxWidth.Value / 2 - iconSize.Width / 2;
                    var xImgAll = xImg - imgIdx * iconSize.Width;
                    var imgRc = new RectangleF(
                        xImgAll,
                        p.Y,
                        iconSize.Width * 6,
                        iconSize.Height);
                    var rcClip = new RectangleF(new PointF(xImg, p.Y), iconSize);
                    rcClip.Width -= 1;
                    g.DrawImage(picIcons, imgRc, rcClip, ImageAlign.StretchImage);
                }
                tl.Clear();
                tl.AppendLine(header, tfHdr);
                tl.AppendLine(text, tfBlack);
                g.BeginLayer(lang);
                g.DrawTextLayout(tl, new PointF(p.X, p.Y + iconSize.Height + 8));
                g.EndLayer();
            }
        }
    }
}