CopyParagraphs.cs
//
// This code is part of GrapeCity Documents for Word samples.
// Copyright (c) GrapeCity, Inc. All rights reserved.
//
using System;
using System.IO;
using System.Drawing;
using System.Linq;
using GrapeCity.Documents.Word;

namespace GcWordWeb.Samples
{
    // This sample shows how to copy or join paragraphs in a document.
    // Currently, GcWord does not yet provide a built-in way to perform
    // copy/paste operations, this sample provides a limited temporary
    // solution.
    public class CopyParagraphs
    {
        public GcWordDocument CreateDocx()
        {
            const string p1start = "This is the first paragraph of the original document";
            const string p2start = "This is the second paragraph of the original document";
            const string p3start = "This is the third paragraph of the original document";
            const string p4start = "This is the fourth paragraph of the original document";

            var doc = new GcWordDocument();

            // Load an existing DOCX file:
            var path = Path.Combine("Resources", "WordDocs", "SampleParagraphs.docx");
            doc.Load(path);

            Paragraph p1 = null, p2 = null, p3 = null, p4 = null;
            foreach (var p in doc.Body.Paragraphs)
            {
                var t = p.GetRange().Text;
                if (t.StartsWith(p1start))
                    p1 = p;
                else if (t.StartsWith(p2start))
                    p2 = p;
                else if (t.StartsWith(p3start))
                    p3 = p;
                else if (t.StartsWith(p4start))
                    p4 = p;
            }
            if (p1 == null || p2 == null || p3 == null || p4 == null)
                throw new Exception("Unexpected: could not find paragraphs.");

            var swapResult = Helper.SwapParagraphs(p1, p3);

            swapResult.Item1.GetRange().Runs.Insert("Second swapped paragraph (paragraph 3): ", InsertLocation.Start);
            swapResult.Item2.GetRange().Runs.Insert("First swapped paragraph (paragraph 1): ", InsertLocation.Start);

            var joinResult = Helper.JoinParagraphs(doc.Body.Paragraphs.Add(), swapResult.Item1, swapResult.Item2);
            joinResult.GetRange().Runs.Insert("Jointed first and 3rd paragraphs: ", InsertLocation.Start);

            // Add a note at the end of the document:
            doc.Body.Sections.Last.GetRange().Paragraphs.Add($"Created by GcWord on {DateTime.Now}.");

            // Done:
            return doc;
        }

        // A static helper class that provides methods to copy or move
        // GcWord content objects such as paragraphs and runs.
        // 
        // AddRun, AddPicture, AddField, AddText, AddParagraph methods accept parameter
        // "withFormatting" which determines whether to copy just the content, 
        // or content and formatting. Note that if this parameter is true, 
        // direct formatting is applied to the newly created objects,
        // so the connection to the original document style is broken 
        // (updating the style will not affect the new objects).
        static public class Helper
        {
            // Swaps two paragraphs by inserting a new paragraph before each one,
            // copying the content (and optionally formatting) from the other paragraph,
            // and the removing the old paragraphs.
            static public (Paragraph, Paragraph) SwapParagraphs(Paragraph p1, Paragraph p2, bool withFormatting = true)
            {
                if (p1.ParentBody != p2.ParentBody)
                    throw new Exception("Both paragraphs must belong the same parent body.");

                var newP2 = p1.GetRange().Paragraphs.Insert(InsertLocation.Before);
                CopyParagraph(newP2, p2, withFormatting);
                var newP1 = p2.GetRange().Paragraphs.Insert(InsertLocation.Before);
                CopyParagraph(newP1, p1, withFormatting);
                p1.Delete();
                p2.Delete();
                return (newP1, newP2);
            }

            // Copies the contents (and optionally formatting) of two paragraphs into a third one.
            static public Paragraph JoinParagraphs(Paragraph target, Paragraph p1, Paragraph p2, bool withFormatting = true)
            {
                if (p1.ParentBody != p2.ParentBody)
                    throw new Exception("Both paragraphs must belong the same parent body.");

                CopyParagraph(target, p1, withFormatting);
                CopyParagraph(target, p2, withFormatting);

                return target;
            }

            // Copy child objects from one content object to another.
            static private void CopyChildren(ContentObject target, ContentObject source, bool withFormatting)
            {
                foreach (ContentObject child in source.Children)
                {
                    switch (child)
                    {
                        case Run run:
                            AddRun(target, run, withFormatting);
                            break;
                        case SimpleField field:
                            AddField(target, field, withFormatting);
                            break;
                        case Picture picture:
                            AddPicture(target, picture, withFormatting);
                            break;
                        case Paragraph paragraph:
                            AddParagraph(target, paragraph, withFormatting);
                            break;
                        case Text text:
                            AddText(target, text, withFormatting);
                            break;
                        default:
                            System.Diagnostics.Debug.Assert(false, "Unexpected: unknown content object type.");
                            break;
                    }
                }
            }

            // Joins two paragraphs.
            static private Paragraph JoinParagraphs(Paragraph first, Paragraph second, bool withFormatting = false)
            {
                if (first.ParentBody != second.ParentBody)
                    throw new Exception("Left and right paragraphs must belong the same parent body.");

                var newParagraph = first.ParentBody.Paragraphs.Add();
                CopyParagraph(newParagraph, first, withFormatting);
                // Note that second paragraph formatting overrides first paragraph formatting:
                CopyParagraph(newParagraph, second, withFormatting);

                return newParagraph;
            }

            // Adds a copy of a paragraph to a body.
            static private void AddParagraph(Body body, Paragraph source, bool withFormatting = false)
            {
                var newParagraph = body.Paragraphs.Add();
                CopyParagraph(newParagraph, source, withFormatting);
            }

            // Adds a copy of a paragraph to a content object.
            static private void AddParagraph(ContentObject target, Paragraph source, bool withFormatting = false)
            {
                var newParagraph = target.GetRange().Paragraphs.Add();
                CopyParagraph(newParagraph, source, withFormatting);
            }

            // Adds a copy of a run to a content object.
            static private void AddRun(ContentObject target, Run source, bool withFormatting = false)
            {
                var newRun = target.GetRange().Runs.Add();
                CopyRun(newRun, source, withFormatting);
            }

            // Copies a paragraph to another paragraph.
            static private void CopyParagraph(Paragraph target, Paragraph source, bool withFormatting = false)
            {
                CopyRevisionId(target.RevisionId, source.RevisionId);
                CopyChildren(target, source, withFormatting);
                if (withFormatting)
                    CopyParagraphFormat(target, source);
            }

            // Adds a copy of a text to a content object.
            static private void AddText(ContentObject target, Text source, bool withFormatting)
            {
                var newText = target.GetRange().Texts.Add(source.Value);
                CopyText(newText, source, withFormatting);
            }
            
            // Copies a text to another text.
            static private void CopyText(Text target, Text source, bool withFormatting)
            {
                target.PreserveSpace = source.PreserveSpace;
                CopyChildren(target, source, withFormatting);
            }

            // Adds a copy of a field to a content object.
            static private void AddField(ContentObject target, SimpleField source, bool withFormatting)
            {
                var newField = target.GetRange().SimpleFields.Add(source.Code);
                CopySimpleField(newField, source, withFormatting);
            }
            
            // Copies a simple field to another simple field.
            static private void CopySimpleField(SimpleField target, SimpleField source, bool withFormatting)
            {
                target.Code = source.Code;
                target.CustomData = source.CustomData;
                target.Locked = source.Locked;
                //parse children
                CopyChildren(target, source, withFormatting);
            }

            // Copies a run to another run.
            static private void CopyRun(Run target, Run source, bool withFormatting)
            {
                CopyRevisionId(target.RevisionId, source.RevisionId);
                CopyChildren(target, source, withFormatting);
                if (withFormatting)
                {
                    target.Style = target.Style;
                    CopyFont(target.Font, source.Font);
                }
            }

            // Copies a revision ID.
            static private void CopyRevisionId(RevisionId target, RevisionId source)
            {
                target.AdditionId = source.AdditionId;
                target.DeletionId = source.DeletionId;
                target.PropertiesId = source.PropertiesId;
            }

            // Adds a copy of a picture to a content object.
            static private void AddPicture(ContentObject target, Picture source, bool withFormatting)
            {
                var newPicture = target.GetRange().Pictures.Add();
                CopyPicture(newPicture, source, withFormatting);
            }

            // Copies a picture to another picture.
            static private void CopyPicture(Picture target, Picture source, bool withFormatting)
            {
                CopyImageData(target.ImageData, source.ImageData, withFormatting);
                target.Name = source.Name;
                target.Title = source.Title;
                CopyChildren(target, source, withFormatting);

                if (!withFormatting)
                    return;

                target.AlternativeText = source.AlternativeText;
                target.Hidden = source.Hidden;

                CopyShapeRotation(target.Rotation, source.Rotation);
                CopyShapeSize(target.Size, source.Size);
                CopyWrapFormat(target.WrapFormat, source.WrapFormat);
            }

            // Copies image data to another image data.
            static private void CopyImageData(ImageData target, ImageData source, bool withFormatting)
            {
                target.Compression = source.Compression;
                target.Source = source.Source;
                target.SetImage(source.ToStream(), source.ContentType);
                if (!withFormatting)
                    return;
                CopyEdgeExtent(target.Crop, source.Crop);
            }

            // Copies paragraph formatting to another paragraph.
            static private void CopyParagraphFormat(Paragraph target, Paragraph source)
            {
                target.Style = source.Style;
                target.Mark.Style = source.Mark.Style;

                CopyParagraphFormatting(target.Format, source.Format);
                //
                target.ListFormat.Template = source.ListFormat.Template;
                target.ListFormat.LevelNumber = source.ListFormat.LevelNumber;
            }

            // Copies a font to another font.
            static private void CopyFont(Font target, Font source)
            {
                target.AllCaps = source.AllCaps;
                target.AlwaysHidden = source.AlwaysHidden;
                target.Animation = source.Animation;
                target.Bidi = source.Bidi;
                target.Bold = source.Bold;
                target.BoldBi = source.BoldBi;
                CopyBorder(target.Border, source.Border);
                CopyWordColor(target.Color, source.Color);
                target.ContextualAlternates = source.ContextualAlternates;
                target.DisableCharacterSpaceGrid = source.DisableCharacterSpaceGrid;
                target.DoubleStrikeThrough = source.DoubleStrikeThrough;
                CopyEastAsianLayout(target.EastAsianLayout, source.EastAsianLayout);
                target.Emboss = source.Emboss;
                target.EmphasisMark = source.EmphasisMark;
                target.Engrave = source.Engrave;
                target.FitTextId = source.FitTextId;
                target.FitTextWidth = source.FitTextWidth;
                target.Hidden = source.Hidden;
                target.HighlightColor = source.HighlightColor;
                target.HintType = source.HintType;
                target.Italic = source.Italic;
                target.ItalicBi = source.ItalicBi;
                target.Kerning = source.Kerning;
                target.Ligatures = source.Ligatures;
                target.LocaleName = source.LocaleName;
                target.LocaleNameBi = source.LocaleNameBi;
                //
                target.LocaleNameFarEast = source.LocaleNameFarEast;
                target.Name = source.Name;
                target.NameAscii = source.NameAscii;
                target.NameBi = source.NameBi;
                target.NameFarEast = source.NameFarEast;
                target.NameOther = source.NameOther;
                target.NoProofing = source.NoProofing;
                target.NumberForm = source.NumberForm;
                target.NumberSpacing = source.NumberSpacing;
                target.Outline = source.Outline;
                target.Position = source.Position;
                target.RightToLeft = source.RightToLeft;
                target.Scaling = source.Scaling;

                CopyShading(target.Shading, source.Shading);
                target.Shadow = source.Shadow;
                target.Size = source.Size;
                target.SizeBi = source.SizeBi;
                target.SmallCaps = source.SmallCaps;
                target.Spacing = source.Spacing;
                target.StrikeThrough = source.StrikeThrough;
                target.StylisticSets = source.StylisticSets;

                target.ThemeAscii = source.ThemeAscii;
                target.ThemeBi = source.ThemeBi;
                target.ThemeFarEast = source.ThemeFarEast;
                target.ThemeOther = source.ThemeOther;
                target.Underline = source.Underline;
                CopyWordColor(target.UnderlineColor, source.UnderlineColor);
                target.VerticalPosition = source.VerticalPosition;
                target.WebHidden = source.WebHidden;
            }

            // Copies East Asian layout.
            static private void CopyEastAsianLayout(EastAsianLayout target, EastAsianLayout source)
            {
                target.FitVerticalInLine = source.FitVerticalInLine;
                target.HorizontalInVertical = source.HorizontalInVertical;
                target.TwoLinesInOne = source.TwoLinesInOne;
                target.TwoLinesInOneBrackets = source.TwoLinesInOneBrackets;
            }

            // Copies paragraph formatting.
            static private void CopyParagraphFormatting(ParagraphFormat target, ParagraphFormat source)
            {
                target.Alignment = source.Alignment;
                target.BaseLineAlignment = source.BaseLineAlignment;
                target.Bidi = source.Bidi;

                target.DisableLineHeightGrid = source.DisableLineHeightGrid;
                target.FarEastLineBreakControl = source.FarEastLineBreakControl;
                target.HalfWidthPunctuationOnTopOfLine = source.HalfWidthPunctuationOnTopOfLine;
                target.HangingPunctuation = source.HangingPunctuation;
                target.Hyphenation = source.Hyphenation;

                target.KeepTogether = source.KeepTogether;
                target.KeepWithNext = source.KeepWithNext;
                target.NoLineNumber = source.NoLineNumber;
                target.OutlineLevel = source.OutlineLevel;
                target.PageBreakBefore = source.PageBreakBefore;
                target.TextboxTightWrap = source.TextboxTightWrap;
                target.TextFlowDirection = source.TextFlowDirection;
                target.WidowControl = source.WidowControl;
                target.WordWrap = source.WordWrap;

                CopyShading(target.Shading, source.Shading);
                CopySpacing(target.Spacing, source.Spacing);
                CopyIndentation(target.Indentation, source.Indentation);
                //copy TabStops
                foreach (var tabStop in source.TabStops)
                {
                    if (tabStop.Leader != TabStopLeader.None)
                        target.TabStops.Add(tabStop.Position, tabStop.Alignment);
                    else
                        target.TabStops.Add(tabStop.Position, tabStop.Alignment, tabStop.Leader);
                }
                //copy borders
                CopyBorder(target.Borders.Inside, source.Borders.Inside);
                CopyBorder(target.Borders.Left, source.Borders.Left);
                CopyBorder(target.Borders.Top, source.Borders.Top);
                CopyBorder(target.Borders.Right, source.Borders.Right);
                CopyBorder(target.Borders.Bottom, source.Borders.Bottom);
            }

            static private void CopyIndentation(Indentation target, Indentation source)
            {
                target.AutoAdjustRightIndent = source.AutoAdjustRightIndent;
                target.CharacterUnitFirstLineIndent = source.CharacterUnitFirstLineIndent;
                target.CharacterUnitLeftIndent = source.CharacterUnitLeftIndent;
                target.CharacterUnitRightIndent = source.CharacterUnitRightIndent;
                target.FirstLineIndent = source.FirstLineIndent;
                target.LeftIndent = source.LeftIndent;
                target.MirrorIndents = source.MirrorIndents;
                target.RightIndent = source.RightIndent;
            }

            static private void CopySpacing(Spacing target, Spacing source)
            {
                target.AddSpaceBetweenFarEastAndAlpha = source.AddSpaceBetweenFarEastAndAlpha;
                target.AddSpaceBetweenFarEastAndDigit = source.AddSpaceBetweenFarEastAndDigit;
                target.LineSpacing = source.LineSpacing;
                target.LineSpacingRule = source.LineSpacingRule;
                target.LineUnitAfter = source.LineUnitAfter;
                target.LineUnitBefore = source.LineUnitBefore;
                target.NoSpaceBetweenParagraphsOfSameStyle = source.NoSpaceBetweenParagraphsOfSameStyle;
                target.SpaceAfter = source.SpaceAfter;
                target.SpaceAfterAuto = source.SpaceAfterAuto;
                target.SpaceBefore = source.SpaceBefore;
                target.SpaceBeforeAuto = source.SpaceBeforeAuto;
            }

            static private void CopyShading(Shading target, Shading source)
            {

                CopyWordColor(target.BackgroundPatternColor, source.BackgroundPatternColor);
                CopyWordColor(target.ForegroundPatternColor, source.ForegroundPatternColor);
                target.Texture = source.Texture;
            }

            static private void CopyBorder(Border target, Border source)
            {
                CopyWordColor(target.Color, source.Color);
                target.FrameEffect = source.FrameEffect;
                target.LineStyle = source.LineStyle;
                target.LineWidth = source.LineWidth;
                target.Shadow = source.Shadow;
                target.Space = source.Space;
                target.Visible = source.Visible;
            }

            static private void CopyWordColor(WordColor target, WordColor source)
            {
                target.RGB = source.RGB;
                // Note: current GcWord OM does not provide means to find where a concrete color
                // comes from, so copying theme colors will overwrite RGB even if the RGB value
                // should be used. So for this sample, we just ignore theme colors:
                // target.ThemeColor = source.ThemeColor;
                // target.ThemeShade = source.ThemeShade;
                // target.ThemeTint = source.ThemeTint;
            }

            static private void CopyWrapFormat(WrapFormat target, WrapFormat source)
            {
                target.BehindText = source.BehindText;
                target.DistanceBottom = source.DistanceBottom;
                target.DistanceLeft = source.DistanceLeft;
                target.DistanceRight = source.DistanceRight;
                target.DistanceTop = source.DistanceTop;
                target.Side = source.Side;
                target.Type = source.Type;
                //clone list
                if (source.WrapPolygon != null)
                    target.WrapPolygon = source.WrapPolygon.ToList();
            }

            static private void CopyShapeRotation(ShapeRotation target, ShapeRotation source)
            {
                target.Angle = source.Angle;
                target.HorizontalFlip = source.HorizontalFlip;
                target.VerticalFlip = source.VerticalFlip;
            }

            static private void CopyShapeSize(ShapeSize target, ShapeSize source)
            {
                CopyEdgeExtent(target.EffectExtent, source.EffectExtent);
                CopyShapeWidth(target.Width, source.Width);
                CopyShapeHeight(target.Height, source.Height);
            }

            static private void CopyShapeWidth(ShapeWidth target, ShapeWidth source)
            {
                target.Relative = source.Relative;
                target.RelativeTo = source.RelativeTo;
                target.Value = source.Value;
            }

            static private void CopyShapeHeight(ShapeHeight target, ShapeHeight source)
            {
                target.Relative = source.Relative;
                target.RelativeTo = source.RelativeTo;
                target.Value = source.Value;
            }

            static private void CopyEdgeExtent(EdgeExtent target, EdgeExtent source)
            {
                target.AllEdges = source.AllEdges;
                target.BottomEdge = source.BottomEdge;
                target.LeftEdge = source.LeftEdge;
                target.RightEdge = source.RightEdge;
                target.TopEdge = source.TopEdge;
            }
        }
    }
}