Просмотр исходного кода

ComPDFKIt.Tool(win) - 提交ComPDFKit.Tool代码

TangJinZhou 9 месяцев назад
Родитель
Сommit
b360dec488
98 измененных файлов с 33387 добавлено и 0 удалено
  1. 256 0
      Demo/Examples/ComPDFKit.Tool/AlignmentsHelp.cs
  2. 2067 0
      Demo/Examples/ComPDFKit.Tool/CPDFToolManager.cs
  3. 930 0
      Demo/Examples/ComPDFKit.Tool/CPDFViewerTool.Annot.cs
  4. 138 0
      Demo/Examples/ComPDFKit.Tool/CPDFViewerTool.AnnotEdit.cs
  5. 1647 0
      Demo/Examples/ComPDFKit.Tool/CPDFViewerTool.Command.cs
  6. 124 0
      Demo/Examples/ComPDFKit.Tool/CPDFViewerTool.CreateWidget.cs
  7. 149 0
      Demo/Examples/ComPDFKit.Tool/CPDFViewerTool.CustomizeTool.cs
  8. 1341 0
      Demo/Examples/ComPDFKit.Tool/CPDFViewerTool.DataMethod.cs
  9. 366 0
      Demo/Examples/ComPDFKit.Tool/CPDFViewerTool.FindReplace.cs
  10. 263 0
      Demo/Examples/ComPDFKit.Tool/CPDFViewerTool.MultiSelectedRect.cs
  11. 265 0
      Demo/Examples/ComPDFKit.Tool/CPDFViewerTool.PageSelected.cs
  12. 82 0
      Demo/Examples/ComPDFKit.Tool/CPDFViewerTool.SelectImage.cs
  13. 213 0
      Demo/Examples/ComPDFKit.Tool/CPDFViewerTool.SelectText.cs
  14. 195 0
      Demo/Examples/ComPDFKit.Tool/CPDFViewerTool.SelectedRect.cs
  15. 1523 0
      Demo/Examples/ComPDFKit.Tool/CPDFViewerTool.TextEdit.cs
  16. 875 0
      Demo/Examples/ComPDFKit.Tool/CPDFViewerTool.WidgetTool.cs
  17. 20 0
      Demo/Examples/ComPDFKit.Tool/CPDFViewerTool.xaml
  18. 970 0
      Demo/Examples/ComPDFKit.Tool/CPDFViewerTool.xaml.cs
  19. 213 0
      Demo/Examples/ComPDFKit.Tool/ComPDFKit.Tool.csproj
  20. 47 0
      Demo/Examples/ComPDFKit.Tool/CustomCommands.cs
  21. 962 0
      Demo/Examples/ComPDFKit.Tool/DrawTool/AnnotEdit.cs
  22. 300 0
      Demo/Examples/ComPDFKit.Tool/DrawTool/CaretVisual.cs
  23. 1877 0
      Demo/Examples/ComPDFKit.Tool/DrawTool/CreateAnnotTool.cs
  24. 314 0
      Demo/Examples/ComPDFKit.Tool/DrawTool/CreateCustomizeTool.cs
  25. 293 0
      Demo/Examples/ComPDFKit.Tool/DrawTool/CreateWidgetTool.cs
  26. 1224 0
      Demo/Examples/ComPDFKit.Tool/DrawTool/MultiSelectedRect.cs
  27. 40 0
      Demo/Examples/ComPDFKit.Tool/DrawTool/MultipleSelectedRect.cs
  28. 1107 0
      Demo/Examples/ComPDFKit.Tool/DrawTool/PageSelectedRect.cs
  29. 331 0
      Demo/Examples/ComPDFKit.Tool/DrawTool/SelectImage.cs
  30. 363 0
      Demo/Examples/ComPDFKit.Tool/DrawTool/SelectText.cs
  31. 728 0
      Demo/Examples/ComPDFKit.Tool/DrawTool/SelectedRect.cs
  32. 830 0
      Demo/Examples/ComPDFKit.Tool/DrawTool/SelectedRect.protected.cs
  33. 108 0
      Demo/Examples/ComPDFKit.Tool/Help/CommonHelper.cs
  34. 45 0
      Demo/Examples/ComPDFKit.Tool/Help/ImportWin32.cs
  35. 228 0
      Demo/Examples/ComPDFKit.Tool/Help/PDFHelp.cs
  36. 3141 0
      Demo/Examples/ComPDFKit.Tool/Help/ParamConverter.cs
  37. 297 0
      Demo/Examples/ComPDFKit.Tool/PDFTextSearch.cs
  38. 19 0
      Demo/Examples/ComPDFKit.Tool/Properties/AssemblyInfo.cs
  39. 45 0
      Demo/Examples/ComPDFKit.Tool/SettingParam/AnnotParam.cs
  40. 53 0
      Demo/Examples/ComPDFKit.Tool/SettingParam/AnnotParam/CircleParam.cs
  41. 65 0
      Demo/Examples/ComPDFKit.Tool/SettingParam/AnnotParam/FreeTextParam.cs
  42. 53 0
      Demo/Examples/ComPDFKit.Tool/SettingParam/AnnotParam/HighlightParam.cs
  43. 58 0
      Demo/Examples/ComPDFKit.Tool/SettingParam/AnnotParam/InkParam.cs
  44. 98 0
      Demo/Examples/ComPDFKit.Tool/SettingParam/AnnotParam/LineMeasureParam.cs
  45. 65 0
      Demo/Examples/ComPDFKit.Tool/SettingParam/AnnotParam/LineParam.cs
  46. 41 0
      Demo/Examples/ComPDFKit.Tool/SettingParam/AnnotParam/LinkParam.cs
  47. 96 0
      Demo/Examples/ComPDFKit.Tool/SettingParam/AnnotParam/PolyLineMeasureParam.cs
  48. 122 0
      Demo/Examples/ComPDFKit.Tool/SettingParam/AnnotParam/PolygonMeasureParam.cs
  49. 72 0
      Demo/Examples/ComPDFKit.Tool/SettingParam/AnnotParam/RedactParam.cs
  50. 40 0
      Demo/Examples/ComPDFKit.Tool/SettingParam/AnnotParam/SoundParam.cs
  51. 53 0
      Demo/Examples/ComPDFKit.Tool/SettingParam/AnnotParam/SquareParam.cs
  52. 54 0
      Demo/Examples/ComPDFKit.Tool/SettingParam/AnnotParam/SquigglyParam.cs
  53. 47 0
      Demo/Examples/ComPDFKit.Tool/SettingParam/AnnotParam/StampParam.cs
  54. 36 0
      Demo/Examples/ComPDFKit.Tool/SettingParam/AnnotParam/StickyNoteParam.cs
  55. 51 0
      Demo/Examples/ComPDFKit.Tool/SettingParam/AnnotParam/StrikeoutParam.cs
  56. 51 0
      Demo/Examples/ComPDFKit.Tool/SettingParam/AnnotParam/UnderlineParam.cs
  57. 1954 0
      Demo/Examples/ComPDFKit.Tool/SettingParam/DefaultSettingParam.cs
  58. 34 0
      Demo/Examples/ComPDFKit.Tool/SettingParam/FormParam/CheckBoxParam.cs
  59. 58 0
      Demo/Examples/ComPDFKit.Tool/SettingParam/FormParam/ComboBoxParam.cs
  60. 58 0
      Demo/Examples/ComPDFKit.Tool/SettingParam/FormParam/ListBoxParam.cs
  61. 49 0
      Demo/Examples/ComPDFKit.Tool/SettingParam/FormParam/PushButtonParam.cs
  62. 33 0
      Demo/Examples/ComPDFKit.Tool/SettingParam/FormParam/RadioButtonParam.cs
  63. 29 0
      Demo/Examples/ComPDFKit.Tool/SettingParam/FormParam/SignatureParam.cs
  64. 44 0
      Demo/Examples/ComPDFKit.Tool/SettingParam/FormParam/TextBoxParam.cs
  65. 69 0
      Demo/Examples/ComPDFKit.Tool/SettingParam/FormParam/WidgetParm.cs
  66. 35 0
      Demo/Examples/ComPDFKit.Tool/SettingParam/PDFEditParam/ImageEditParam.cs
  67. 28 0
      Demo/Examples/ComPDFKit.Tool/SettingParam/PDFEditParam/PDFEditParam.cs
  68. 45 0
      Demo/Examples/ComPDFKit.Tool/SettingParam/PDFEditParam/TextEditParam.cs
  69. 231 0
      Demo/Examples/ComPDFKit.Tool/UndoManger/AnnotHistory.cs
  70. 220 0
      Demo/Examples/ComPDFKit.Tool/UndoManger/AnnotHistory/CircleAnnotHistory.cs
  71. 260 0
      Demo/Examples/ComPDFKit.Tool/UndoManger/AnnotHistory/FreeTextAnnotHistory.cs
  72. 162 0
      Demo/Examples/ComPDFKit.Tool/UndoManger/AnnotHistory/HighlightAnnotHistory.cs
  73. 165 0
      Demo/Examples/ComPDFKit.Tool/UndoManger/AnnotHistory/InkAnnotHistory.cs
  74. 234 0
      Demo/Examples/ComPDFKit.Tool/UndoManger/AnnotHistory/LineAnnotHistory.cs
  75. 243 0
      Demo/Examples/ComPDFKit.Tool/UndoManger/AnnotHistory/LineMeasureAnnotHistory.cs
  76. 200 0
      Demo/Examples/ComPDFKit.Tool/UndoManger/AnnotHistory/LinkAnnotHistory.cs
  77. 239 0
      Demo/Examples/ComPDFKit.Tool/UndoManger/AnnotHistory/PolyLineMeasureAnnotHistory.cs
  78. 261 0
      Demo/Examples/ComPDFKit.Tool/UndoManger/AnnotHistory/PolygonMeasureAnnotHistory.cs
  79. 248 0
      Demo/Examples/ComPDFKit.Tool/UndoManger/AnnotHistory/RedactAnnotHistory.cs
  80. 55 0
      Demo/Examples/ComPDFKit.Tool/UndoManger/AnnotHistory/ReferenceObject.cs
  81. 170 0
      Demo/Examples/ComPDFKit.Tool/UndoManger/AnnotHistory/SoundAnnotHistory.cs
  82. 219 0
      Demo/Examples/ComPDFKit.Tool/UndoManger/AnnotHistory/SquareAnnotHistory.cs
  83. 163 0
      Demo/Examples/ComPDFKit.Tool/UndoManger/AnnotHistory/SquigglyAnnotHistory.cs
  84. 217 0
      Demo/Examples/ComPDFKit.Tool/UndoManger/AnnotHistory/StampAnnotHistory.cs
  85. 161 0
      Demo/Examples/ComPDFKit.Tool/UndoManger/AnnotHistory/StickyNoteAnnotHistory.cs
  86. 161 0
      Demo/Examples/ComPDFKit.Tool/UndoManger/AnnotHistory/StrikeoutAnnotHistory.cs
  87. 161 0
      Demo/Examples/ComPDFKit.Tool/UndoManger/AnnotHistory/UnderlineAnnotHistory.cs
  88. 40 0
      Demo/Examples/ComPDFKit.Tool/UndoManger/FindReplaceHistory/FindReplaceHistory.cs
  89. 239 0
      Demo/Examples/ComPDFKit.Tool/UndoManger/FormHistory/CheckBoxHistory.cs
  90. 317 0
      Demo/Examples/ComPDFKit.Tool/UndoManger/FormHistory/ComboBoxHistory.cs
  91. 318 0
      Demo/Examples/ComPDFKit.Tool/UndoManger/FormHistory/ListBoxHistory.cs
  92. 327 0
      Demo/Examples/ComPDFKit.Tool/UndoManger/FormHistory/PushButtonHistory.cs
  93. 242 0
      Demo/Examples/ComPDFKit.Tool/UndoManger/FormHistory/RadioButtonHistory.cs
  94. 210 0
      Demo/Examples/ComPDFKit.Tool/UndoManger/FormHistory/SignatureHistory.cs
  95. 294 0
      Demo/Examples/ComPDFKit.Tool/UndoManger/FormHistory/TextBoxHistory.cs
  96. 46 0
      Demo/Examples/ComPDFKit.Tool/UndoManger/GroupHistory.cs
  97. 123 0
      Demo/Examples/ComPDFKit.Tool/UndoManger/MultiAnnotHistory.cs
  98. 34 0
      Demo/Examples/ComPDFKit.Tool/UndoManger/PDFEditHistory/PDFEditHistory.cs

+ 256 - 0
Demo/Examples/ComPDFKit.Tool/AlignmentsHelp.cs

@@ -0,0 +1,256 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Markup;
+
+namespace ComPDFKit.Tool
+{
+    /// <summary>
+    /// Help class for calculating the alignment position of the rectangle
+    /// </summary>
+    public class AlignmentsHelp
+    {
+        /// <summary>
+        /// Set the source rectangle to be left-aligned in the target rectangle
+        /// </summary>
+        /// <param name="src">
+        /// Origin rectangle
+        /// </param>
+        /// <param name="dst">
+        /// Target rectangle
+        /// </param>
+        /// <returns>
+        /// X Y direction distance required for alignment of the source rectangle
+        /// </returns>
+        public static Point SetAlignLeft(Rect src, Rect dst)
+        {
+            Point movePoint = new Point(dst.Left - src.Left, 0);
+            return movePoint;
+        }
+
+        /// <summary>
+        /// Set the source rectangle to be horizontally centered in the target rectangle
+        /// </summary>
+        /// <param name="src">
+        /// Origin rectangle
+        /// </param>
+        /// <param name="dst">
+        /// Target rectangle
+        /// </param>
+        /// <returns>
+        /// X Y direction distance required for alignment of the source rectangle
+        /// </returns>
+        public static Point SetAlignHorizonCenter (Rect src, Rect dst)
+        {
+            Point movePoint = new Point((dst.Left + dst.Right - src.Left - src.Right) / 2, 0);
+            return movePoint;
+        }
+
+        /// <summary>
+        /// Right-align the source rectangle in the target rectangle
+        /// </summary>
+        /// <param name="src">
+        /// Origin rectangle
+        /// </param>
+        /// <param name="dst">
+        /// Target rectangle
+        /// </param>
+        /// <returns>
+        /// X Y direction distance required for alignment of the source rectangle
+        /// </returns>
+        public static Point SetAlignRight(Rect src, Rect dst)
+        {
+            Point movePoint = new Point(dst.Right - src.Width - src.Left, 0);
+            return movePoint;
+        }
+
+        /// <summary>
+        /// Set the source rectangle to be top-aligned in the target rectangle
+        /// </summary>
+        /// <param name="src">
+        /// Origin rectangle
+        /// </param>
+        /// <param name="dst">
+        /// Target rectangle
+        /// </param>
+        /// <returns>
+        /// X Y direction distance required for alignment of the source rectangle
+        /// </returns>
+        public static Point SetAlignTop(Rect src, Rect dst)
+        {
+            Point movePoint = new Point(0, dst.Top - src.Top);
+            return movePoint;
+        }
+
+        /// <summary>
+        /// Set the source rectangle to be vertically centered in the target rectangle
+        /// </summary>
+        /// <param name="src">
+        /// Origin rectangle
+        /// </param>
+        /// <param name="dst">
+        /// Target rectangle
+        /// </param>
+        /// <returns>
+        /// X Y direction distance required for alignment of the source rectangle
+        /// </returns>
+        public static Point SetAlignVerticalCenter(Rect src, Rect dst)
+        {
+            Point movePoint = new Point(0, (dst.Bottom + dst.Top - src.Top - src.Bottom) / 2);
+            return movePoint;
+        }
+
+        /// <summary>
+        /// Set the source rectangle to be horizontally and vertically centered in the target rectangle
+        /// </summary>
+        /// <param name="src">
+        /// Origin rectangle
+        /// </param>
+        /// <param name="dst">
+        /// Target rectangle
+        /// </param>
+        /// <returns>
+        /// X Y direction distance required for alignment of the source rectangle
+        /// </returns>
+        public static Point SetAlignHorizonVerticalCenter(Rect src, Rect dst)
+        {
+            Point movePoint = new Point((dst.Left + dst.Right - src.Left - src.Right) / 2, (dst.Bottom + dst.Top - src.Top - src.Bottom) / 2);
+            return movePoint;
+        }
+
+        /// <summary>
+        /// Set the source rectangle to be bottom-aligned in the target rectangle
+        /// </summary>
+        /// <param name="src">
+        /// Origin rectangle
+        /// </param>
+        /// <param name="dst">
+        /// Target rectangle
+        /// </param>
+        /// <returns>
+        /// X Y direction distance required for alignment of the source rectangle
+        /// </returns>
+        public static Point SetAlignBottom(Rect src, Rect dst)
+        {
+            Point movePoint = new Point(0, dst.Bottom - src.Height - src.Top);
+            return movePoint;
+        }
+
+        /// <summary>
+        /// Set the source rectangle to be horizontally distributed and aligned in the target rectangle
+        /// </summary>
+        /// <param name="src">
+        /// Array of source rectangles needed
+        /// </param>
+        /// <param name="dst">
+        /// Target rectangle
+        /// </param>
+        /// <returns>
+        /// Dictionary of XY direction distance required for alignment of each source rectangle
+        /// </returns>
+        public static Dictionary<Rect, Point> SetDistributeHorizontal(List<Rect> src, Rect dst)
+        {
+            Dictionary<Rect, Point> dictionary = new Dictionary<Rect, Point>();
+            List<double> Leftlist = new List<double>();
+
+            // Sort the data according to the leftmost position of each rectangle, not the array order
+            foreach (Rect srcRect in src)
+            {
+                Leftlist.Add(srcRect.Left + srcRect.Width / 2);
+            }
+            double[] datalist = Leftlist.ToArray();
+            Sort(datalist, 0, Leftlist.Count - 1);
+
+            double startX = dst.Left;
+            double endX = dst.Right;
+            double interval = (endX - startX) / Leftlist.Count;
+            for (int i = 0; i < datalist.Count(); i++)
+            {
+                int index = Leftlist.IndexOf(datalist[i]);
+                Point movePoint = new Point(startX + i * interval - src[index].Left - src[index].Width / 2, 0);
+                dictionary.Add(src[index], movePoint);
+            }
+            return dictionary;
+        }
+
+        /// <summary>
+        /// Vertically distribute the source rectangles within the target rectangle (sorting based on the leftmost position of each rectangle, not the array order)
+        /// </summary>
+        /// <param name="src">
+        /// Array of source rectangles needed
+        /// </param>
+        /// <param name="dst">
+        /// Target rectangle
+        /// </param>
+        /// <returns>
+        /// Dictionary of XY direction distance required for alignment of each source rectangle
+        /// </returns>
+        public static Dictionary<Rect, Point> SetDistributeVertical(List<Rect> src, Rect dst)
+        {
+            Dictionary<Rect, Point> dictionary = new Dictionary<Rect, Point>();
+            List<double> Leftlist = new List<double>();
+
+            // Sort the data according to the leftmost position of each rectangle, not the array order
+            foreach (Rect srcRect in src)
+            {
+                Leftlist.Add(srcRect.Left + srcRect.Width / 2);
+            }
+            double[] datalist = Leftlist.ToArray();
+            Sort(datalist, 0, Leftlist.Count - 1);
+
+            double startY = dst.Top;
+            double endY = dst.Bottom;
+            double interval = (endY - startY) / Leftlist.Count;
+            for (int i = 0; i < datalist.Count(); i++)
+            {
+                int index = Leftlist.IndexOf(datalist[i]);
+                Point movePoint = new Point(0, startY + i * interval - src[index].Top - src[index].Height / 2);
+                dictionary.Add(src[index], movePoint);
+            }
+            return dictionary;
+        }
+
+        #region Quick sort
+
+        private static int SortUnit(double[] array, int low, int high)
+        {
+            double key = array[low];
+            while (low < high)
+            {
+                // Search backward for values smaller than the key
+                while (array[high] >= key && high > low)
+                    --high;
+                // Put the value smaller than key on the left
+                array[low] = array[high];
+                // Search forward for values larger than the key
+                while (array[low] <= key && high > low)
+                    ++low;
+                // Put the value larger than key on the right
+                array[high] = array[low];
+            }
+            // Left is smaller than key, right is larger than key.
+            // Put the key in the current position of the cursor
+            // At this point, 'low' equals 'high
+            array[low] = key;
+            return high;
+        }
+
+        private static void Sort(double[] array, int low, int high)
+        {
+            if (low >= high)
+                return;
+            // Finish a single unit sort
+            int index = SortUnit(array, low, high);
+            // Sort the left unit
+            Sort(array, low, index - 1);
+            // Sort the right unit
+            Sort(array, index + 1, high);
+        }
+
+        #endregion
+    }
+}

Разница между файлами не показана из-за своего большого размера
+ 2067 - 0
Demo/Examples/ComPDFKit.Tool/CPDFToolManager.cs


+ 930 - 0
Demo/Examples/ComPDFKit.Tool/CPDFViewerTool.Annot.cs

@@ -0,0 +1,930 @@
+using ComPDFKit.PDFAnnotation;
+using ComPDFKit.PDFDocument;
+using ComPDFKit.PDFPage;
+using ComPDFKit.Tool.DrawTool;
+using ComPDFKitViewer;
+using ComPDFKitViewer.Annot;
+using ComPDFKitViewer.BaseObject;
+using ComPDFKitViewer.Widget;
+using ComPDFKitViewer.Layer;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Input;
+using System.Windows;
+using ComPDFKit.Import;
+using ComPDFKit.PDFAnnotation.Form;
+using System.Windows.Controls;
+using System.Windows.Media;
+using static ComPDFKit.PDFAnnotation.CTextAttribute.CFontNameHelper;
+using ComPDFKitViewer.Helper;
+using Microsoft.SqlServer.Server;
+using ComPDFKit.Tool.Help;
+using System.Xml.Linq;
+using ComPDFKit.Tool.SettingParam;
+using ComPDFKit.Tool.UndoManger;
+using System.Windows.Controls.Primitives;
+using ComPDFKit.Measure;
+using System.Dynamic;
+
+namespace ComPDFKit.Tool
+{
+    public class MeasureEventArgs : EventArgs
+    {
+        public CPDFMeasureType Type { get; set; }
+        public double RulerBase { get; set; } = 1;
+        public string RulerBaseUnit { get; set; } = CPDFMeasure.CPDF_CM;
+        public double RulerTranslate { get; set; } = 1;
+        public string RulerTranslateUnit { get; set; } = CPDFMeasure.CPDF_CM;
+        public double Precision { get; set; } = 0.01;
+        public double Angle { get; set; }
+        public Point MousePos { get; set; }
+        public string Round { get; set; }
+        public string Distance { get; set; }
+        public string Area { get; set; }
+    }
+
+    public partial class CPDFViewerTool
+    {
+        Border textBorder;
+        TextBox textui;
+        BaseAnnot caheMoveAnnot;
+        BaseAnnot cacheHitTestAnnot;
+        bool isCacheRedaction = false;
+        int createAnnotTag = -1;
+        int selectedPageIndex = -1;
+        int selectedAnnotIndex = -1;
+        bool canSave = true;
+        bool isHitTestLink = false;
+        bool isHitTestRedact = false;
+        public event EventHandler<MeasureEventArgs> MeasureChanged;
+
+        public void InvokeMeasureChangeEvent(object sender, MeasureEventArgs e)
+        {
+            MeasureChanged?.Invoke(sender, e);
+        }
+
+        public bool GetIsHitTestLink()
+        {
+            return isHitTestLink;
+        }
+
+        public void SetIsHitTestLink(bool canHitTestLink)
+        {
+            isHitTestLink = canHitTestLink;
+        }
+
+        public void SetIsOnlyHitTestRedact(bool canHitTestRedact)
+        {
+            isHitTestRedact = canHitTestRedact;
+        }
+
+        public bool IsCanSave()
+        {
+            return canSave;
+        }
+
+        public void SetIsCanSave(bool save)
+        {
+            canSave = save;
+        }
+
+        public BaseAnnot GetCacheHitTestAnnot()
+        {
+            return cacheHitTestAnnot;
+        }
+
+        protected bool AnnotMoveHitTest()
+        {
+            BaseAnnot baseAnnot = PDFViewer.AnnotHitTest();
+            if (baseAnnot != null)
+            {
+                if (baseAnnot.CurrentType == C_ANNOTATION_TYPE.C_ANNOTATION_REDACT)
+                {
+                    if (isCacheRedaction)
+                    {
+                        (caheMoveAnnot as RedactionAnnot).SetIsMouseHover(false);
+                        (caheMoveAnnot as RedactionAnnot).Draw();
+                    }
+                    isCacheRedaction = true;
+                }
+                else
+                {
+                    if (isCacheRedaction)
+                    {
+                        (caheMoveAnnot as RedactionAnnot).SetIsMouseHover(false);
+                        (caheMoveAnnot as RedactionAnnot).Draw();
+                    }
+                    isCacheRedaction = false;
+                }
+                caheMoveAnnot = baseAnnot;
+                return true;
+            }
+            return false;
+        }
+
+        protected bool AnnotHitTest()
+        {
+            BaseAnnot baseAnnot = PDFViewer.AnnotHitTest(true);
+            if (baseAnnot != null)
+            {
+                if ((baseAnnot as BaseWidget) != null)
+                {
+                    cacheHitTestAnnot = null;
+                    return false;
+                }
+                cacheHitTestAnnot = baseAnnot;
+                return true;
+            }
+            cacheHitTestAnnot = baseAnnot;
+            return false;
+        }
+
+        public void SelectedAnnotForIndex(int pageIndex, int annotIndex)
+        {
+            CleanSelectedRect();
+            CleanSelectedMultiRect();
+            CleanEditAnnot();
+            cacheHitTestAnnot = null;
+            selectedPageIndex = pageIndex;
+            selectedAnnotIndex = annotIndex;
+        }
+
+        private bool UnCheckAnnotViewerModel()
+        {
+            if (currentModel == ToolType.CreateAnnot || currentModel == ToolType.WidgetEdit)
+            {
+                return false;
+            }
+            return true;
+        }
+
+        private void InsertAnnotView()
+        {
+            CreateAnnotTool createAnnotTool = new CreateAnnotTool(GetMeasureSetting(), GetDefaultDrawParam(), GetDefaultSettingParam());
+            int annotViewindex = PDFViewer.GetMaxViewIndex();
+            PDFViewer.InsertView(annotViewindex, createAnnotTool);
+            createAnnotTag = createAnnotTool.GetResTag();
+            createAnnotTool.UpdateAnnotHandler += CreateAnnotTool_UpdateAnnotHandler;
+            createAnnotTool.CreateFreetextCanceled += CreateAnnotTool_CreateFreetextCanceled;
+            createAnnotTool.MeasureChanged += CreateAnnotTool_MeasureChanged;
+        }
+
+        private void CreateAnnotTool_CreateFreetextCanceled(object sender, AnnotParam e)
+        {
+            dynamic notifyData = null;
+            notifyData = new ExpandoObject();
+            notifyData.Action = HistoryAction.Remove;
+            notifyData.PageIndex = e.PageIndex;
+            notifyData.AnnotIndex = e.AnnotIndex;
+            notifyData.AnnotType = e.GetType();
+            notifyData.CurrentParam = e;
+            AnnotChanged?.Invoke(this, notifyData);
+        }
+
+        private void CreateAnnotTool_MeasureChanged(object sender, MeasureEventArgs e)
+        {
+            InvokeMeasureChangeEvent(sender, e);
+        }
+
+        private void CreateAnnotTool_UpdateAnnotHandler(object sender, bool e)
+        {
+            PDFViewer.EnableZoom(e);
+            PDFViewer.CanHorizontallyScroll = e;
+            PDFViewer.CanVerticallyScroll = e;
+            if (e)
+            {
+                PDFViewer.UpdateAnnotFrame();
+            }
+        }
+
+        public void SetAnnotIsProportionalScaling(bool isProportionalScaling)
+        {
+            if (UnCheckAnnotViewerModel())
+            {
+                return;
+            }
+            BaseLayer baseLayer = PDFViewer.GetViewForTag(createAnnotTag);
+            (baseLayer as CreateAnnotTool).SetIsProportionalScaling(isProportionalScaling);
+        }
+
+        public double GetMoveLength()
+        {
+            if (UnCheckAnnotViewerModel())
+            {
+                return 0;
+            }
+            BaseLayer baseLayer = PDFViewer.GetViewForTag(createAnnotTag);
+            return (baseLayer as CreateAnnotTool).GetMoveLength();
+        }
+
+        public CPDFAnnotation StartDrawAnnot(C_ANNOTATION_TYPE annotType)
+        {
+            if (UnCheckAnnotViewerModel())
+            {
+                return null;
+            }
+
+            Point point = Mouse.GetPosition(this);
+            BaseLayer baseLayer = PDFViewer.GetViewForTag(createAnnotTag);
+            PDFViewer.GetPointPageInfo(point, out int index, out Rect paintRect, out Rect pageBound);
+            if (index < 0)
+            {
+                return null;
+            }
+            CPDFDocument cPDFDocument = PDFViewer.GetDocument();
+            CPDFPage cPDFPage = cPDFDocument.PageAtIndex(index);
+            if (annotType == C_ANNOTATION_TYPE.C_ANNOTATION_STAMP)
+            {
+                DefaultSettingParam defaultSettingParam = GetDefaultSettingParam();
+                StampParam stampParam = defaultSettingParam.StampParamDef;
+                stampParam.Rotation = cPDFPage.Rotation;
+                defaultSettingParam.SetAnnotParam(stampParam);
+            }
+            Point cropPoint = new Point();
+            if (PDFViewer.GetIsCrop())
+            {
+                CRect cRect = cPDFPage.GetCropBounds();
+                cropPoint.X = DpiHelper.PDFNumToStandardNum(cRect.left);
+                cropPoint.Y = DpiHelper.PDFNumToStandardNum(cRect.top);
+            }
+            SetScrollAndZoomTypeForAnnot(annotType);
+            switch (annotType)
+            {
+                case C_ANNOTATION_TYPE.C_ANNOTATION_LINE:
+                case C_ANNOTATION_TYPE.C_ANNOTATION_POLYGON:
+                case C_ANNOTATION_TYPE.C_ANNOTATION_POLYLINE:
+                    canSave = false;
+                    break;
+                default:
+                    break;
+            }
+            return (baseLayer as CreateAnnotTool).StartDraw(point, cropPoint, cPDFPage, paintRect, pageBound, annotType, PDFViewer, PDFViewer.GetZoom());
+        }
+
+        public void MultipleClick()
+        {
+            if (UnCheckAnnotViewerModel())
+            {
+                return;
+            }
+            Point point = Mouse.GetPosition(this);
+            BaseLayer baseLayer = PDFViewer.GetViewForTag(createAnnotTag);
+            PDFViewer.GetPointPageInfo(point, out int index, out Rect paintRect, out Rect pageBound);
+            if (index < 0)
+            {
+                return;
+            }
+            (baseLayer as CreateAnnotTool).MultipleClick(point);
+        }
+
+        public void SetScrollAndZoomTypeForAnnot(C_ANNOTATION_TYPE annotType)
+        {
+            bool enableScroll = false;
+            bool enableZoom = false;
+            switch (annotType)
+            {
+                case C_ANNOTATION_TYPE.C_ANNOTATION_LINK:
+                case C_ANNOTATION_TYPE.C_ANNOTATION_HIGHLIGHT:
+                case C_ANNOTATION_TYPE.C_ANNOTATION_UNDERLINE:
+                case C_ANNOTATION_TYPE.C_ANNOTATION_SQUIGGLY:
+                case C_ANNOTATION_TYPE.C_ANNOTATION_STRIKEOUT:
+                    enableScroll = true;
+                    enableZoom = true;
+                    break;
+                default:
+                    break;
+            }
+            PDFViewer.CanHorizontallyScroll = enableScroll;
+            PDFViewer.CanVerticallyScroll = enableScroll;
+            PDFViewer.EnableZoom(enableZoom);
+        }
+
+        public void MoveDrawAnnot()
+        {
+            if (UnCheckAnnotViewerModel())
+            {
+                return;
+            }
+            Point point = Mouse.GetPosition(this);
+            BaseLayer baseLayer = PDFViewer.GetViewForTag(createAnnotTag);
+            (baseLayer as CreateAnnotTool).MoveDraw(point, PDFViewer.GetZoom());
+        }
+
+        public void CreateTextBox()
+        {
+            if (UnCheckAnnotViewerModel())
+            {
+                return;
+            }
+            Point point = Mouse.GetPosition(this);
+            BaseLayer baseLayer = PDFViewer.GetViewForTag(createAnnotTag);
+           (baseLayer as CreateAnnotTool).CreateTextBox();
+        }
+
+        public Rect EndDrawAnnot()
+        {
+            if (UnCheckAnnotViewerModel())
+            {
+                return new Rect();
+            }
+            BaseLayer baseLayer = PDFViewer.GetViewForTag(createAnnotTag);
+            return (baseLayer as CreateAnnotTool).EndDraw();
+        }
+
+        private void SetMarkupContent(CPDFMarkupAnnotation markupAnnot, string markupContent)
+        {
+            if (markupAnnot == null || markupAnnot.IsValid() == false)
+            {
+                return;
+            }
+
+            try
+            {
+                DefaultSettingParam defaultParam = GetDefaultSettingParam();
+                if (defaultParam != null)
+                {
+                    switch (markupAnnot.Type)
+                    {
+                        case C_ANNOTATION_TYPE.C_ANNOTATION_HIGHLIGHT:
+                            if (string.IsNullOrEmpty(defaultParam.HighlightParamDef.Content) == false)
+                            {
+                                markupContent = defaultParam.HighlightParamDef.Content;
+                            }
+                            break;
+                        case C_ANNOTATION_TYPE.C_ANNOTATION_UNDERLINE:
+                            if (string.IsNullOrEmpty(defaultParam.UnderlineParamDef.Content) == false)
+                            {
+                                markupContent = defaultParam.UnderlineParamDef.Content;
+                            }
+                            break;
+                        case C_ANNOTATION_TYPE.C_ANNOTATION_SQUIGGLY:
+                            if (string.IsNullOrEmpty(defaultParam.SquigglyParamDef.Content) == false)
+                            {
+                                markupContent = defaultParam.SquigglyParamDef.Content;
+                            }
+                            break;
+                        case C_ANNOTATION_TYPE.C_ANNOTATION_STRIKEOUT:
+                            if (string.IsNullOrEmpty(defaultParam.StrikeoutParamDef.Content) == false)
+                            {
+                                markupContent = defaultParam.StrikeoutParamDef.Content;
+                            }
+                            break;
+                        default:
+                            return;
+                    }
+                }
+
+                if (string.IsNullOrEmpty(markupContent) == false)
+                {
+                    markupAnnot.SetContent(markupContent);
+                }
+            }
+            catch (Exception ex)
+            {
+
+            }
+        }
+
+        public bool CreateAnnotForSelectText(TextSelectInfo textSelectInfo, C_ANNOTATION_TYPE annotType, out GroupHistory historyData)
+        {
+            historyData = null;
+            if (UnCheckAnnotViewerModel())
+            {
+                return false;
+            }
+            Dictionary<int, List<Rect>> PagesRectList = textSelectInfo.ConvertToSelectRectDict();
+            CPDFDocument cPDFDocument = PDFViewer.GetDocument();
+            GroupHistory historyGroup = new GroupHistory();
+            foreach (int pageIndex in PagesRectList.Keys)
+            {
+                List<Rect> pageSelectRectList = PagesRectList[pageIndex];
+                if (pageSelectRectList.Count > 0)
+                {
+                    CPDFPage docPage = cPDFDocument.PageAtIndex(pageIndex);
+                    docPage.ReleaseAllAnnotations();
+                    CPDFAnnotation annotCore = docPage.CreateAnnot(annotType);
+                    annotCore.SetCreationDate(PDFHelp.GetCurrentPdfTime());
+                    annotCore.SetModifyDate(PDFHelp.GetCurrentPdfTime());
+                    if (annotCore == null || annotCore is CPDFWidget)
+                    {
+                        return false;
+                    }
+                    List<CRect> coreRectList = new List<CRect>();
+                    foreach (Rect copyRect in pageSelectRectList)
+                    {
+                        coreRectList.Add(new CRect((float)copyRect.Left, (float)copyRect.Top, (float)copyRect.Right, (float)copyRect.Bottom));
+                    }
+                    CreateDefaultAnnot(annotCore, annotType, null);
+                    string markupContent = textSelectInfo.PageSelectText[pageIndex];
+
+                    switch (annotType)
+                    {
+                        case C_ANNOTATION_TYPE.C_ANNOTATION_HIGHLIGHT:
+                            (annotCore as CPDFHighlightAnnotation).SetQuardRects(coreRectList);
+                            SetMarkupContent(annotCore as CPDFMarkupAnnotation, markupContent);
+                            break;
+                        case C_ANNOTATION_TYPE.C_ANNOTATION_UNDERLINE:
+                            (annotCore as CPDFUnderlineAnnotation).SetQuardRects(coreRectList);
+                            SetMarkupContent(annotCore as CPDFMarkupAnnotation, markupContent);
+                            break;
+                        case C_ANNOTATION_TYPE.C_ANNOTATION_SQUIGGLY:
+                            (annotCore as CPDFSquigglyAnnotation).SetQuardRects(coreRectList);
+                            SetMarkupContent(annotCore as CPDFMarkupAnnotation, markupContent);
+                            break;
+                        case C_ANNOTATION_TYPE.C_ANNOTATION_STRIKEOUT:
+                            (annotCore as CPDFStrikeoutAnnotation).SetQuardRects(coreRectList);
+                            SetMarkupContent(annotCore as CPDFMarkupAnnotation, markupContent);
+                            break;
+                        case C_ANNOTATION_TYPE.C_ANNOTATION_REDACT:
+                            (annotCore as CPDFRedactAnnotation).SetQuardRects(coreRectList);
+                            break;
+                        default:
+                            break;
+                    }
+
+                    int Left = (int)pageSelectRectList.Select(x => x.Left).Min();
+                    int Top = (int)pageSelectRectList.Select(x => x.Top).Min();
+                    int Right = (int)pageSelectRectList.Select(x => x.Right).Max();
+                    int Bottom = (int)pageSelectRectList.Select(x => x.Bottom).Max();
+                    annotCore.SetRect(new CRect(Left, Bottom, Right, Top));
+
+                    //if (annotCore.GetIsLocked() != underlineArgs.Locked)
+                    //{
+                    //    annotCore.SetIsLocked(underlineArgs.Locked);
+                    //}
+                    annotCore.UpdateAp();
+                    switch (annotType)
+                    {
+                        case C_ANNOTATION_TYPE.C_ANNOTATION_HIGHLIGHT:
+                            {
+                                HighlightAnnotHistory highlightAnnotHistory = new HighlightAnnotHistory();
+                                AnnotParam annotParam = ParamConverter.AnnotConverter(cPDFDocument, annotCore);
+                                highlightAnnotHistory.CurrentParam = (HighlightParam)annotParam;
+                                highlightAnnotHistory.PDFDoc = cPDFDocument;
+                                highlightAnnotHistory.Action = HistoryAction.Add;
+                                historyGroup.Histories.Add(highlightAnnotHistory);
+
+                            }
+                            break;
+                        case C_ANNOTATION_TYPE.C_ANNOTATION_UNDERLINE:
+                            {
+                                UnderlineAnnotHistory underlineAnnotHistory = new UnderlineAnnotHistory();
+                                AnnotParam annotParam = ParamConverter.AnnotConverter(cPDFDocument, annotCore);
+                                underlineAnnotHistory.CurrentParam = (UnderlineParam)annotParam;
+                                underlineAnnotHistory.PDFDoc = cPDFDocument;
+                                underlineAnnotHistory.Action = HistoryAction.Add;
+                                historyGroup.Histories.Add(underlineAnnotHistory);
+                            }
+                            break;
+                        case C_ANNOTATION_TYPE.C_ANNOTATION_SQUIGGLY:
+                            {
+                                SquigglyAnnotHistory squigglyAnnotHistory = new SquigglyAnnotHistory();
+                                AnnotParam annotParam = ParamConverter.AnnotConverter(cPDFDocument, annotCore);
+                                squigglyAnnotHistory.CurrentParam = (SquigglyParam)annotParam;
+                                squigglyAnnotHistory.PDFDoc = cPDFDocument;
+                                squigglyAnnotHistory.Action = HistoryAction.Add;
+                                historyGroup.Histories.Add(squigglyAnnotHistory);
+                            }
+                            break;
+                        case C_ANNOTATION_TYPE.C_ANNOTATION_STRIKEOUT:
+                            {
+                                StrikeoutAnnotHistory strikeoutHistory = new StrikeoutAnnotHistory();
+                                AnnotParam annotParam = ParamConverter.AnnotConverter(cPDFDocument, annotCore);
+                                strikeoutHistory.CurrentParam = (StrikeoutParam)annotParam;
+                                strikeoutHistory.PDFDoc = cPDFDocument;
+                                strikeoutHistory.Action = HistoryAction.Add;
+                                historyGroup.Histories.Add(strikeoutHistory);
+                            }
+                            break;
+                        case C_ANNOTATION_TYPE.C_ANNOTATION_REDACT:
+                            {
+                                RedactAnnotHistory redactHistory = new RedactAnnotHistory();
+                                AnnotParam annotParam = ParamConverter.AnnotConverter(cPDFDocument, annotCore);
+                                redactHistory.Action = HistoryAction.Add;
+                                redactHistory.CurrentParam = (RedactParam)annotParam;
+                                redactHistory.PDFDoc = cPDFDocument;
+                                redactHistory.Action = HistoryAction.Add;
+                                historyGroup.Histories.Add(redactHistory);
+                            }
+                            break;
+                        default:
+                            break;
+                    }
+                    annotCore.ReleaseAnnot();
+                }
+            }
+
+            if (historyGroup.Histories.Count > 0)
+            {
+                GetCPDFViewer()?.UndoManager?.AddHistory(historyGroup);
+            }
+            PDFViewer.UpdateAnnotFrame();
+            historyData = historyGroup;
+            return true;
+        }
+
+        public Point GetStartPoint()
+        {
+            BaseLayer baseLayer = PDFViewer.GetViewForTag(createAnnotTag);
+            return (baseLayer as CreateAnnotTool).GetStartPoint();
+        }
+
+        public Point GetEndPoint()
+        {
+            BaseLayer baseLayer = PDFViewer.GetViewForTag(createAnnotTag);
+            return (baseLayer as CreateAnnotTool).GetEndPoint();
+        }
+
+        public List<Point> GetInkDrawPoints()
+        {
+            BaseLayer baseLayer = PDFViewer.GetViewForTag(createAnnotTag);
+            return (baseLayer as CreateAnnotTool).GetInkDrawPoints();
+        }
+
+        public List<Point> GetMeasureDrawPoints()
+        {
+            BaseLayer baseLayer = PDFViewer.GetViewForTag(createAnnotTag);
+            return (baseLayer as CreateAnnotTool).GetMeasureDrawPoints();
+        }
+
+        public Rect GetDrawAnnotMaxRect()
+        {
+            if (UnCheckAnnotViewerModel())
+            {
+                return new Rect();
+            }
+            Point point = Mouse.GetPosition(this);
+            BaseLayer baseLayer = PDFViewer.GetViewForTag(createAnnotTag);
+            return (baseLayer as CreateAnnotTool).GetMaxRect();
+        }
+
+        public void ClearDrawAnnot()
+        {
+            if (UnCheckAnnotViewerModel())
+            {
+                return;
+            }
+            Point point = Mouse.GetPosition(this);
+            BaseLayer baseLayer = PDFViewer.GetViewForTag(createAnnotTag);
+            (baseLayer as CreateAnnotTool).ClearDraw();
+        }
+
+        protected void UpdateTextPop()
+        {
+            if (textBorder != null)
+            {
+                BaseAnnot currentAnnot = textui.GetValue(PopupAttachDataProperty) as BaseAnnot;
+                if (currentAnnot == null)
+                {
+                    return;
+                }
+
+                AnnotLayer annotLayer = PDFViewer.GetViewForTag(PDFViewer.GetAnnotViewTag()) as AnnotLayer;
+
+                bool isOk = annotLayer.GetUpdate(ref currentAnnot);
+                if (!isOk)
+                {
+                    return;
+                }
+                AnnotData annotData = currentAnnot.GetAnnotData(); 
+
+                if (annotData.PaintRect == Rect.Empty)
+                {
+                    return;
+                }
+                //SetFormRotateTransform(textui, annotData);
+                // Set the width and height of the TextBox, rotation, and other control position information
+                RotateTransform rotateTrans = new RotateTransform();
+                rotateTrans.Angle = -90 * annotData.Rotation;
+                rotateTrans.CenterX = annotData.PaintRect.Width / 2;
+                rotateTrans.CenterY = annotData.PaintRect.Height / 2;
+                Rect rotateRect = rotateTrans.TransformBounds(annotData.PaintRect);
+
+                textBorder.Width = rotateRect.Width;
+                textui.MinHeight = Math.Max(0, textui.FontSize + 8);
+                textui.MaxHeight = Math.Max(0, annotData.PaintOffset.Bottom - annotData.PaintRect.Top - 8);
+                textBorder.SetValue(Canvas.LeftProperty, annotData.PaintRect.Left + rotateTrans.CenterX - rotateRect.Width / 2);
+                textBorder.SetValue(Canvas.TopProperty, annotData.PaintRect.Top + rotateTrans.CenterY - rotateRect.Height / 2);
+            }
+        }
+
+        protected void BuildPopTextUI(BaseAnnot textAnnot)
+        {
+            try
+            {
+                if (textAnnot != null && textAnnot.CurrentType == C_ANNOTATION_TYPE.C_ANNOTATION_FREETEXT)
+                {
+                    AnnotData annotData = textAnnot.GetAnnotData();
+                    CPDFFreeTextAnnotation textWidget = annotData.Annot as CPDFFreeTextAnnotation;
+                    if (textWidget == null)
+                    {
+                        return;
+                    }
+                    textAnnot.CleanDraw();
+                    CRect drawRect = textWidget.GetRect();
+                    double Width = DpiHelper.PDFNumToStandardNum(drawRect.width());
+                    double Height = DpiHelper.PDFNumToStandardNum(drawRect.height());
+                    textui = new TextBox();
+                    textui.Name = "PdfViewerTextBox";
+                    textBorder = new Border();
+                    textBorder.Child = textui;
+                    textBorder.MinWidth = Width * PDFViewer.GetZoom();
+                    textBorder.MinHeight = Height * PDFViewer.GetZoom();
+
+                    // Calculate the maximum value
+                    double PDFWidth = PDFViewer.GetCurrentRenderPageForIndex(annotData.PageIndex).PaintRect.Width;
+                    double PDFHeight = PDFViewer.GetCurrentRenderPageForIndex(annotData.PageIndex).PaintRect.Height;
+
+                    textBorder.MaxWidth = (PDFWidth - annotData.VisualRect.Left - annotData.CropLeft);
+                    textBorder.MaxHeight = (PDFHeight - annotData.VisualRect.Top - annotData.CropTop);
+                    CTextAttribute textAttribute = textWidget.FreeTextDa;
+                    byte transparency = textWidget.GetTransparency();
+                    textui.FontSize = DpiHelper.PDFNumToStandardNum(textAttribute.FontSize * annotData.CurrentZoom);
+                    Color textColor = Color.FromArgb(
+                        transparency,
+                        textAttribute.FontColor[0],
+                        textAttribute.FontColor[1],
+                        textAttribute.FontColor[2]);
+
+                    Color borderColor = Colors.Transparent;
+                    Color backgroundColor = Colors.Transparent;
+                    byte[] colorArray = new byte[3];
+                    if (textWidget.Transparency > 0)
+                    {
+                        borderColor = Color.FromArgb(textWidget.Transparency, textWidget.LineColor[0], textWidget.LineColor[1], textWidget.LineColor[2]);
+                    }
+
+                    if (textWidget.HasBgColor)
+                    {
+                        backgroundColor = Color.FromArgb(textWidget.Transparency, textWidget.BgColor[0], textWidget.BgColor[1], textWidget.BgColor[2]);
+                    }
+
+                    textui.Foreground = new SolidColorBrush(textColor);
+                    textui.Background = new SolidColorBrush(backgroundColor);
+
+                    textBorder.Padding = new Thickness(0);
+                    textBorder.BorderBrush = new SolidColorBrush(borderColor);
+                    textBorder.BorderThickness = new Thickness(DpiHelper.PDFNumToStandardNum(textWidget.GetBorderWidth() * annotData.CurrentZoom));
+                    textui.BorderThickness = new Thickness(0);
+                    textui.Text = textWidget.Content;
+
+                    string fontName = string.Empty;
+                    string fontFamily = string.Empty;
+                    CPDFFont.GetFamlyStyleName(textWidget.FreeTextDa.FontName, ref fontFamily, ref fontName);
+                    textui.FontFamily = new FontFamily(fontFamily);
+
+                    textui.AcceptsReturn = true;
+                    textui.TextWrapping = TextWrapping.Wrap;
+                    textui.TextAlignment = TextAlignment.Left;
+
+                    switch (textWidget.Alignment)
+                    {
+                        case C_TEXT_ALIGNMENT.ALIGNMENT_LEFT:
+                            textui.TextAlignment = TextAlignment.Left;
+                            break;
+                        case C_TEXT_ALIGNMENT.ALIGNMENT_RIGHT:
+                            textui.TextAlignment = TextAlignment.Right;
+                            break;
+                        case C_TEXT_ALIGNMENT.ALIGNMENT_CENTER:
+                            textui.TextAlignment = TextAlignment.Center;
+                            break;
+                        default:
+                            break;
+                    }
+
+                    //SetFormRotateTransform(textui, annotData);
+                    // Set the width and height of the TextBox, rotation, and other control position information
+                    RotateTransform rotateTrans = new RotateTransform();
+                    rotateTrans.Angle = -90 * annotData.Rotation;
+                    rotateTrans.CenterX = annotData.PaintRect.Width / 2;
+                    rotateTrans.CenterY = annotData.PaintRect.Height / 2;
+                    Rect rotateRect = rotateTrans.TransformBounds(annotData.PaintRect);
+
+                    textBorder.Width = rotateRect.Width;
+                    textui.MinHeight = Math.Max(0, textui.FontSize + 8);
+                    textui.MaxHeight = Math.Max(0, annotData.PaintOffset.Bottom - annotData.PaintRect.Top - 8);
+                    textBorder.SetValue(Canvas.LeftProperty, annotData.PaintRect.Left + rotateTrans.CenterX - rotateRect.Width / 2);
+                    textBorder.SetValue(Canvas.TopProperty, annotData.PaintRect.Top + rotateTrans.CenterY - rotateRect.Height / 2);
+
+                    rotateTrans.Angle = 90 * annotData.Rotation;
+                    rotateTrans.CenterX = rotateRect.Width / 2;
+                    rotateTrans.CenterY = rotateRect.Height / 2;
+                    textBorder.RenderTransform = rotateTrans;
+
+                    textui.Loaded += (object sender, RoutedEventArgs e) =>
+                    {
+                        textui.Focus();
+                        textui.CaretIndex = textui.Text.Length;
+                        textui.SetValue(PopupAttachDataProperty, textAnnot);
+                    };
+
+                    CPDFViewer viewer = GetCPDFViewer();
+                    textui.LostFocus += (object sender, RoutedEventArgs e) =>
+                    {
+                        BaseAnnot currentAnnot = textui.GetValue(PopupAttachDataProperty) as BaseAnnot;
+                        if (currentAnnot != null)
+                        {
+                            AnnotData widgetData = currentAnnot.GetAnnotData();
+                            CPDFFreeTextAnnotation updateFreeText = widgetData.Annot as CPDFFreeTextAnnotation;
+
+                            if (textui.Text == string.Empty && updateFreeText.GetBorderWidth() == 0)
+                            {
+                                dynamic notifyData = null;
+                                notifyData = new ExpandoObject();
+                                notifyData.Action = HistoryAction.Remove;
+                                notifyData.PageIndex = widgetData.PageIndex;
+                                notifyData.AnnotIndex = widgetData.AnnotIndex;
+                                notifyData.AnnotType = widgetData.AnnotType;
+                                notifyData.CurrentParam = ParamConverter.CPDFDataConverterToAnnotParam(viewer.GetDocument(), widgetData.PageIndex, updateFreeText);
+                                updateFreeText.RemoveAnnot();
+                                AnnotChanged?.Invoke(this, notifyData);
+                            }
+                            else
+                            {
+                                FreeTextAnnotHistory history = null;
+                                if (updateFreeText.Content != textui.Text)
+                                {
+                                    history = new FreeTextAnnotHistory();
+                                    history.PDFDoc = viewer.GetDocument();
+                                    history.PreviousParam = ParamConverter.CPDFDataConverterToAnnotParam(viewer.GetDocument(), updateFreeText.Page.PageIndex, updateFreeText);
+                                    history.Action = HistoryAction.Update;
+                                    updateFreeText.SetContent(textui.Text);
+                                }
+
+                                RotateTransform rotateTranss = new RotateTransform();
+                                rotateTranss.Angle = -90 * annotData.Rotation;
+                                rotateTranss.CenterX = textBorder.ActualWidth / 2;
+                                rotateTranss.CenterY = textBorder.ActualHeight / 2;
+                                Rect textRect = rotateTranss.TransformBounds(new Rect(0, 0, textBorder.ActualWidth, textBorder.ActualHeight));
+
+                                Rect changeRect = new Rect(
+                                    annotData.ClientRect.Left,
+                                    annotData.ClientRect.Top,
+                                    DpiHelper.StandardNumToPDFNum(textRect.Width / annotData.CurrentZoom),
+                                    DpiHelper.StandardNumToPDFNum(textRect.Height / annotData.CurrentZoom));
+
+                                updateFreeText.SetRect(new CRect(
+                                     (float)changeRect.Left,
+                                      (float)changeRect.Bottom,
+                                       (float)changeRect.Right,
+                                        (float)changeRect.Top
+                                    ));
+                                updateFreeText.UpdateAp();
+
+                                if (history != null)
+                                {
+                                    history.CurrentParam = ParamConverter.CPDFDataConverterToAnnotParam(viewer.GetDocument(), updateFreeText.Page.PageIndex, updateFreeText);
+                                    viewer.UndoManager?.AddHistory(history);
+                                    viewer.UndoManager?.InvokeHistoryChanged(this, new KeyValuePair<ComPDFKitViewer.Helper.UndoAction, IHistory>(ComPDFKitViewer.Helper.UndoAction.Custom, history));
+                                }
+                                viewer.UpdateAnnotFrame();
+                            }
+                            RemovePopTextUI();
+                        } 
+                    };
+
+                    BaseLayer createAnnotTool = PDFViewer?.GetView(createAnnotTag) as CreateAnnotTool;
+                    if (createAnnotTool != null)
+                    {
+                        createAnnotTool.Children.Add(textBorder);
+                        createAnnotTool.Arrange();
+                    }
+
+                    textui.LayoutUpdated += (object sender, EventArgs e) =>
+                    {
+                        createAnnotTool.Arrange();
+                    };
+                }
+            }
+            catch (Exception ex)
+            {
+
+            }
+
+        }
+
+        public void UpdatePopTextUI(BaseAnnot textAnnot)
+        {
+            try
+            {
+                if (textui != null && textBorder != null && textAnnot != null && textAnnot.CurrentType == C_ANNOTATION_TYPE.C_ANNOTATION_FREETEXT)
+                {
+                    AnnotData annotData = textAnnot.GetAnnotData();
+                    CPDFFreeTextAnnotation textWidget = annotData.Annot as CPDFFreeTextAnnotation;
+                    if (textWidget == null)
+                    {
+                        return;
+                    }
+                    textAnnot.CleanDraw();
+
+                    CTextAttribute textAttribute = textWidget.FreeTextDa;
+                    byte transparency = textWidget.GetTransparency();
+                    textui.FontSize = DpiHelper.PDFNumToStandardNum(textAttribute.FontSize * annotData.CurrentZoom);
+                    Color textColor = Color.FromArgb(
+                        transparency,
+                        textAttribute.FontColor[0],
+                        textAttribute.FontColor[1],
+                        textAttribute.FontColor[2]);
+
+                    Color borderColor = Colors.Transparent;
+                    Color backgroundColor = Colors.White;
+                    byte[] colorArray = new byte[3];
+                    if (textWidget.Transparency > 0)
+                    {
+                        borderColor = Color.FromRgb(textWidget.LineColor[0], textWidget.LineColor[1], textWidget.LineColor[2]);
+                    }
+
+                    if (textWidget.HasBgColor)
+                    {
+                        backgroundColor = Color.FromRgb(textWidget.BgColor[0], textWidget.BgColor[1], textWidget.BgColor[2]);
+                    }
+
+                    textui.Foreground = new SolidColorBrush(textColor);
+                    textui.Background = new SolidColorBrush(backgroundColor);
+
+                    textBorder.Padding = new Thickness(0);
+                    textBorder.BorderBrush = new SolidColorBrush(borderColor);
+                    textBorder.BorderThickness = new Thickness(DpiHelper.PDFNumToStandardNum(textWidget.GetBorderWidth() * annotData.CurrentZoom));
+                    textui.BorderThickness = new Thickness(0);
+                    textui.Text = textWidget.Content;
+
+                    textui.FontFamily = new FontFamily(GetFontName(textAttribute.FontName));
+                    textui.FontWeight = IsBold(textAttribute.FontName) ? FontWeights.Bold : FontWeights.Normal;
+                    textui.FontStyle = IsItalic(textAttribute.FontName) ? FontStyles.Italic : FontStyles.Normal;
+
+                    textui.AcceptsReturn = true;
+                    textui.TextWrapping = TextWrapping.Wrap;
+                    //textui.VerticalContentAlignment = VerticalAlignment.Center;
+                    textui.TextAlignment = TextAlignment.Left;
+
+                    switch (textWidget.Alignment)
+                    {
+                        case C_TEXT_ALIGNMENT.ALIGNMENT_LEFT:
+                            textui.TextAlignment = TextAlignment.Left;
+                            break;
+                        case C_TEXT_ALIGNMENT.ALIGNMENT_RIGHT:
+                            textui.TextAlignment = TextAlignment.Right;
+                            break;
+                        case C_TEXT_ALIGNMENT.ALIGNMENT_CENTER:
+                            textui.TextAlignment = TextAlignment.Center;
+                            break;
+                        default:
+                            break;
+                    }
+
+                    //SetFormRotateTransform(textui, annotData);
+                    // Set the width and height of the TextBox, rotation, and other control position information
+                    RotateTransform rotateTrans = new RotateTransform();
+                    rotateTrans.Angle = -90 * annotData.Rotation;
+                    rotateTrans.CenterX = annotData.PaintRect.Width / 2;
+                    rotateTrans.CenterY = annotData.PaintRect.Height / 2;
+                    Rect rotateRect = rotateTrans.TransformBounds(annotData.PaintRect);
+
+                    textBorder.Width = rotateRect.Width;
+                    textui.MinHeight = Math.Max(0, textui.FontSize + 8);
+                    textui.MaxHeight = Math.Max(0, annotData.PaintOffset.Bottom - annotData.PaintRect.Top - 8);
+                    textBorder.SetValue(Canvas.LeftProperty, annotData.PaintRect.Left + rotateTrans.CenterX - rotateRect.Width / 2);
+                    textBorder.SetValue(Canvas.TopProperty, annotData.PaintRect.Top + rotateTrans.CenterY - rotateRect.Height / 2);
+
+                    rotateTrans.Angle = 90 * annotData.Rotation;
+                    rotateTrans.CenterX = rotateRect.Width / 2;
+                    rotateTrans.CenterY = rotateRect.Height / 2;
+                    textBorder.RenderTransform = rotateTrans;
+                }
+            }
+            catch
+            {
+
+            }
+        }
+
+        public void RemovePopTextUI()
+        {
+            if (textBorder == null)
+            {
+                return;
+            }
+            BaseLayer removeLayer = PDFViewer?.GetView(createAnnotTag) as CreateAnnotTool;
+            removeLayer.Children.Remove(textBorder);
+        }
+
+        public bool HitTestBorder()
+        {
+            if (textBorder == null)
+            {
+                return false;
+            }
+
+            Point pagePosition = Mouse.GetPosition(textBorder);
+            HitTestResult hitTestResult = VisualTreeHelper.HitTest(textBorder, pagePosition);
+            if (hitTestResult != null && hitTestResult.VisualHit != null)
+            {
+                return true;
+            }
+            return false;
+        }
+    }
+}

+ 138 - 0
Demo/Examples/ComPDFKit.Tool/CPDFViewerTool.AnnotEdit.cs

@@ -0,0 +1,138 @@
+using ComPDFKit.Tool.DrawTool;
+using ComPDFKit.Tool.Help;
+using ComPDFKit.Viewer.Layer;
+using ComPDFKitViewer.Layer;
+using System;
+using System.Windows;
+using System.Windows.Input;
+
+namespace ComPDFKit.Tool
+{
+    public partial class CPDFViewerTool
+    {
+        bool IsDrawEditAnnot = false;
+        int annotEditViewTag = -1;
+        public event EventHandler<SelectedAnnotData> AnnotEditDataChanging;
+        public event EventHandler<SelectedAnnotData> AnnotEditDataChanged;
+        public event EventHandler<object> AnnotChanged;
+        private void InsertAnnotEditView()
+        {
+            int selectedRectViewIndex = PDFViewer.GetMaxViewIndex();
+            CustomizeLayer customizeLayer = new CustomizeLayer();
+            AnnotEdit annotEdit = new AnnotEdit(GetDefaultDrawParam());
+            annotEdit.DataChanged += AnnotEdit_DataChanged;
+            annotEdit.DataChanging += AnnotEdit_DataChanging;
+            customizeLayer.Children.Add(annotEdit);
+            PDFViewer.InsertView(selectedRectViewIndex, customizeLayer);
+            annotEditViewTag = customizeLayer.GetResTag();
+        }
+
+        private void AnnotEdit_DataChanging(object sender, SelectedAnnotData e)
+        {
+            AnnotEditDataChanging?.Invoke(this, e);
+        }
+
+        private void AnnotEdit_DataChanged(object sender, SelectedAnnotData e)
+        {
+            AnnotEditDataChanged?.Invoke(this, e);
+        }
+
+        public void StartDrawEditAnnot()
+        {
+            Point point = Mouse.GetPosition(this);
+            BaseLayer baseLayer = PDFViewer.GetViewForTag(annotEditViewTag);
+            AnnotEdit annotEdit = CommonHelper.FindVisualChild<AnnotEdit>(baseLayer as CustomizeLayer);
+            if (annotEdit != null)
+            {
+                annotEdit.Draw();
+                annotEdit.OnMouseLeftButtonDown(point);
+                IsDrawEditAnnot = true;
+            }
+        }
+
+        public bool DrawMoveEditAnnot()
+        {
+            bool DrawTag = false;
+            Point point = Mouse.GetPosition(this);
+            BaseLayer baseLayer = PDFViewer.GetViewForTag(annotEditViewTag);
+            AnnotEdit annotEdit = CommonHelper.FindVisualChild<AnnotEdit>(baseLayer as CustomizeLayer);
+            if (annotEdit != null)
+            {
+                annotEdit.OnMouseMove(point, out DrawTag);
+                if (DrawTag)
+                {
+                    annotEdit.Draw();
+                }
+            }
+            return DrawTag;
+        }
+
+        public void DrawEndEditAnnot()
+        {
+            Point point = Mouse.GetPosition(this);
+            BaseLayer baseLayer = PDFViewer.GetViewForTag(annotEditViewTag);
+            AnnotEdit annotEdit = CommonHelper.FindVisualChild<AnnotEdit>(baseLayer as CustomizeLayer);
+            if (annotEdit != null)
+            {
+                annotEdit.Draw();
+                annotEdit.OnMouseLeftButtonUp(point);
+            }
+        }
+
+        public void CleanEditAnnot()
+        {
+            Point point = Mouse.GetPosition(this);
+            BaseLayer baseLayer = PDFViewer.GetViewForTag(annotEditViewTag);
+            AnnotEdit annotEdit = CommonHelper.FindVisualChild<AnnotEdit>(baseLayer as CustomizeLayer);
+            if (annotEdit != null)  
+            {
+                annotEdit.ClearDraw();
+                IsDrawEditAnnot = false;
+            }
+        }
+
+        private void DrawEditAnnotLayer()
+        {
+            BaseLayer baseLayer = PDFViewer.GetViewForTag(annotEditViewTag);
+            AnnotEdit annotEdit = CommonHelper.FindVisualChild<AnnotEdit>(baseLayer as CustomizeLayer);
+            if (annotEdit != null)
+            {
+                annotEdit.Draw();
+            }
+        }
+
+        /// <summary>
+        /// Press the mouse on the selected rectangle
+        /// </summary>
+        /// <returns></returns>
+        private bool DrawEditAnnotDownEvent()
+        {
+            BaseLayer baseLayer = PDFViewer.GetViewForTag(annotEditViewTag);
+
+            AnnotEdit selectedRect = CommonHelper.FindVisualChild<AnnotEdit>(baseLayer as CustomizeLayer);
+            if (selectedRect != null)
+            {
+                if (selectedRect.GetHitIndex(Mouse.GetPosition(this)) != -1)
+                {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        private void SetEditAnnotObject()
+        {
+            if (cacheHitTestAnnot == null)
+            {
+                return;
+            }
+            Point point = Mouse.GetPosition(this);
+            BaseLayer baseLayer = PDFViewer.GetViewForTag(annotEditViewTag);
+            AnnotEdit annotEdit = CommonHelper.FindVisualChild<AnnotEdit>(baseLayer as CustomizeLayer);
+            if (annotEdit != null)
+            {
+                annotEdit.SetAnnotObject(cacheHitTestAnnot.GetAnnotData());
+            }
+        }
+    }   
+}

Разница между файлами не показана из-за своего большого размера
+ 1647 - 0
Demo/Examples/ComPDFKit.Tool/CPDFViewerTool.Command.cs


+ 124 - 0
Demo/Examples/ComPDFKit.Tool/CPDFViewerTool.CreateWidget.cs

@@ -0,0 +1,124 @@
+using ComPDFKit.Import;
+using ComPDFKit.PDFAnnotation;
+using ComPDFKit.PDFAnnotation.Form;
+using ComPDFKit.PDFDocument;
+using ComPDFKit.PDFPage;
+using ComPDFKit.Tool.DrawTool;
+using ComPDFKitViewer.BaseObject;
+using ComPDFKitViewer.Helper;
+using ComPDFKitViewer.Layer;
+using ComPDFKitViewer.Widget;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Input;
+
+namespace ComPDFKit.Tool
+{
+    public partial class CPDFViewerTool
+    {
+        BaseWidget cacheMoveWidget;
+        int createWidgetTag = -1;
+
+        public BaseWidget GetCacheHitTestWidget()
+        {
+            return cacheMoveWidget;
+        }
+        private void InsertWidgetView()
+        {
+            CreateWidgetTool createAnnotTool = new CreateWidgetTool();
+            int annotViewindex = PDFViewer.GetMaxViewIndex();
+            PDFViewer.InsertView(annotViewindex, createAnnotTool);
+            createWidgetTag = createAnnotTool.GetResTag();
+        }
+
+        protected bool AnnotWidgetHitTest()
+        {
+            BaseAnnot baseAnnot = PDFViewer.AnnotHitTest();
+            if (baseAnnot != null)
+            {
+                if ((baseAnnot as BaseWidget) != null)
+                {
+                    cacheMoveWidget = baseAnnot as BaseWidget;
+                    return true;
+                }
+            }
+            cacheMoveWidget = null;
+            return false;
+        }
+
+        public CPDFAnnotation StartDrawWidget(C_WIDGET_TYPE WidgetType)
+        {
+            Point point = Mouse.GetPosition(this);
+            BaseLayer baseLayer = PDFViewer.GetViewForTag(createWidgetTag);
+            PDFViewer.GetPointPageInfo(point, out int index, out Rect paintRect, out Rect pageBound);
+            if (index < 0)
+            {
+                return null;
+            }
+            CPDFDocument cPDFDocument = PDFViewer.GetDocument();
+            CPDFPage cPDFPage = cPDFDocument.PageAtIndex(index);
+            Point cropPoint = new Point();
+            if (PDFViewer.GetIsCrop())
+            {
+                CRect cRect = cPDFPage.GetCropBounds();
+                cropPoint.X = DpiHelper.PDFNumToStandardNum(cRect.left);
+                cropPoint.Y = DpiHelper.PDFNumToStandardNum(cRect.top);
+            }
+            return (baseLayer as CreateWidgetTool).StartDraw(point, cropPoint, cPDFPage, paintRect, pageBound, WidgetType);
+        }
+
+        public void SetDrawWidgetType(C_WIDGET_TYPE WidgetType)
+        {
+            Point point = Mouse.GetPosition(this);
+            BaseLayer baseLayer = PDFViewer.GetViewForTag(createWidgetTag);
+            CPDFDocument cPDFDocument = PDFViewer.GetDocument();
+            (baseLayer as CreateWidgetTool).SetDrawType(WidgetType);
+        }
+
+        /// <summary>
+        /// Widget move in create mode
+        /// </summary>
+        /// <param name="Hide">
+        /// Identify whether the default size style of the widget form is displayed
+        /// </param>
+        public void MoveDrawWidget(bool Hide)
+        {
+            Point point = Mouse.GetPosition(this);
+            BaseLayer baseLayer = PDFViewer.GetViewForTag(createWidgetTag);
+            PDFViewer.GetPointPageInfo(point, out int index, out Rect paintRect, out Rect pageBound);
+            (baseLayer as CreateWidgetTool).MoveDraw(point, PDFViewer.GetZoom(), PDFViewer.ActualWidth, PDFViewer.ActualHeight, Hide, paintRect);
+        }
+
+        public Rect EndDrawWidget()
+        {
+            Point point = Mouse.GetPosition(this);
+            BaseLayer baseLayer = PDFViewer.GetViewForTag(createWidgetTag);
+            return (baseLayer as CreateWidgetTool).EndDraw();
+        }
+
+        public void ClearDrawWidget()
+        {
+            Point point = Mouse.GetPosition(this);
+            BaseLayer baseLayer = PDFViewer.GetViewForTag(createWidgetTag);
+            if (baseLayer is CreateWidgetTool)
+            {
+                (baseLayer as CreateWidgetTool).ClearDraw();
+                (baseLayer as CreateWidgetTool).SetDrawType(C_WIDGET_TYPE.WIDGET_NONE);
+            }
+        }
+
+        public void ReDrawWidget()
+        {
+            Point point = Mouse.GetPosition(this);
+            BaseLayer baseLayer = PDFViewer.GetViewForTag(createWidgetTag);
+            if (baseLayer is CreateWidgetTool)
+            {
+                (baseLayer as CreateWidgetTool).ReDrawWidget(PDFViewer.GetZoom());
+            }
+        }
+    }
+}

+ 149 - 0
Demo/Examples/ComPDFKit.Tool/CPDFViewerTool.CustomizeTool.cs

@@ -0,0 +1,149 @@
+using ComPDFKit.Tool.DrawTool;
+using ComPDFKitViewer.Layer;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Input;
+using System.Windows;
+using ComPDFKit.PDFAnnotation;
+using ComPDFKit.PDFPage;
+using ComPDFKit.PDFDocument;
+using ComPDFKitViewer.BaseObject;
+using ComPDFKit.PDFAnnotation.Form;
+using System.Windows.Media;
+
+namespace ComPDFKit.Tool
+{
+    public partial class CPDFViewerTool
+    {
+        /// <summary>
+        ///Identity whether to draw the custom tool
+        /// </summary>
+        protected bool isDrawAnnot { get; set; }
+
+        public event EventHandler<List<AnnotParam>> DeleteChanged;
+
+        int customizeToolViewTag = -1;
+
+        private bool UnCheckCustomizeToolViewerModel()
+        {
+            if (currentModel == ToolType.Customize)
+            {
+                return false;
+            }
+            return true;
+        }
+
+        private void InsertCustomizeToolView()
+        {
+            CreateCustomizeTool createAnnotTool = new CreateCustomizeTool();
+            createAnnotTool.PDFViewer = PDFViewer;
+            int customizeToolViewindex = PDFViewer.GetMaxViewIndex();
+            createAnnotTool.DeleteChanged += CreateAnnotTool_DeleteChanged;
+            PDFViewer.InsertView(customizeToolViewindex, createAnnotTool);
+            customizeToolViewTag= createAnnotTool.GetResTag();
+        }
+
+        private void CreateAnnotTool_DeleteChanged(object sender, List<AnnotParam> e)
+        {
+            DeleteChanged?.Invoke(this, e);
+        }
+
+        /// <summary>
+        /// Begin to draw the custom tool
+        /// </summary>
+        public void DrawStartCustomizeTool(CustomizeToolType ToolType)
+        {
+            if (UnCheckCustomizeToolViewerModel())
+            {
+                return;
+            }
+            Point point = Mouse.GetPosition(this);
+            BaseLayer baseLayer = PDFViewer.GetViewForTag(customizeToolViewTag);
+            PDFViewer.GetPointPageInfo(point, out int index, out Rect paintRect, out Rect pageBound);
+            if (index < 0)
+            {
+                return;
+            }
+            CPDFDocument cPDFDocument = PDFViewer.GetDocument();
+            CPDFPage cPDFPage = cPDFDocument.PageAtIndex(index);
+            BaseLayer layer = PDFViewer.GetViewForTag(PDFViewer.GetAnnotViewTag());
+            if (ToolType == CustomizeToolType.kErase)
+            {
+                (baseLayer as CreateCustomizeTool).SetAnnotLayer(layer as AnnotLayer);
+            }
+           (baseLayer as CreateCustomizeTool).StartDraw(point, cPDFPage, paintRect, pageBound, ToolType);
+        }
+
+        /// <summary>
+        /// Custom tool move call
+        /// </summary>
+        public void DrawMoveCustomizeTool()
+        {
+            if (UnCheckCustomizeToolViewerModel())
+            {
+                return;
+            }
+            Point point = Mouse.GetPosition(this);
+            BaseLayer baseLayer = PDFViewer.GetViewForTag(customizeToolViewTag);
+            (baseLayer as CreateCustomizeTool).MoveDraw(point, PDFViewer.GetZoom());
+        }
+
+        /// <summary>
+        /// Custom tool end call
+        /// </summary>
+        public void DrawEndCustomizeTool()
+        {
+            if (UnCheckCustomizeToolViewerModel())
+            {
+                return;
+            }
+            Point point = Mouse.GetPosition(this);
+            BaseLayer baseLayer = PDFViewer.GetViewForTag(customizeToolViewTag);
+            (baseLayer as CreateCustomizeTool).EndDraw();
+            return;
+        }
+
+        /// <summary>
+        /// Clear the custom tool
+        /// </summary>
+        public void CleanCustomizeTool()
+        {
+            BaseLayer baseLayer = PDFViewer.GetViewForTag(customizeToolViewTag);
+            (baseLayer as CreateCustomizeTool).ClearDraw();
+        }
+
+        /// <summary>
+        /// Set the thickness of the custom tool
+        /// </summary>
+        public void SetEraseZoom(double eraseZoom)
+        {
+            BaseLayer baseLayer = PDFViewer.GetViewForTag(customizeToolViewTag);
+            (baseLayer as CreateCustomizeTool)?.SetEraseZoom(eraseZoom);
+        }
+
+        /// <summary>
+        /// Clear the custom tool
+        /// </summary>
+        public void SetDefEraseThickness(double defEraseThickness)
+        {
+            BaseLayer baseLayer = PDFViewer.GetViewForTag(customizeToolViewTag);
+            (baseLayer as CreateCustomizeTool)?.SetDefEraseThickness(defEraseThickness);
+        }
+
+        /// <summary>
+        ///  Clear the custom tool
+        /// </summary>
+        public void SetEraseBrush(SolidColorBrush drawBrush)
+        {
+            if(PDFViewer!=null)
+            {
+                BaseLayer baseLayer = PDFViewer.GetViewForTag(customizeToolViewTag);
+                (baseLayer as CreateCustomizeTool)?.SetEraseBrush(drawBrush);
+            }
+        }
+    }
+}
+    

Разница между файлами не показана из-за своего большого размера
+ 1341 - 0
Demo/Examples/ComPDFKit.Tool/CPDFViewerTool.DataMethod.cs


+ 366 - 0
Demo/Examples/ComPDFKit.Tool/CPDFViewerTool.FindReplace.cs

@@ -0,0 +1,366 @@
+using ComPDFKit.PDFPage;
+using ComPDFKit.PDFPage.Edit;
+using ComPDFKit.Tool.Help;
+using ComPDFKit.Tool.UndoManger.FindReplaceHistory;
+using ComPDFKit.Viewer.Helper;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.CompilerServices;
+using System.Text;
+using System.Threading.Tasks;
+using System.Xml.Linq;
+
+namespace ComPDFKit.Tool
+{
+    public partial class CPDFViewerTool
+    {
+        // find text
+        private string findText;
+
+        // find options
+        private C_Search_Options options;
+
+        // current selection index in editTextFindSelectionList
+        private int currentSelectionIndex = -1;
+
+        // current selection
+        private CPDFEditTextFindSelection currentTextFindSelection;
+
+        // EditTextFindSelection list in single page
+        private List<CPDFEditTextFindSelection> editTextFindSelectionList;
+
+        // Whether to allow circular search to avoid infinite loops when there are no matches in the entire document
+        private bool canLoop = true;
+
+        // When replacement occurs, set this variable to true
+        private bool isReplaced = false;
+
+        // Set this variable to true when there is a page jump during the search process
+        private bool isOtherPage = false;
+
+        // current selection
+        private CPDFEditTextFindSelection editTextFindSelection;
+
+
+        private CPDFEditPage findSelectionEditPage;
+
+        private int nextPageIndex = 0;
+
+        private int tempPageIndex = -1;
+
+        private int currentPageIndex
+        {
+            get
+            {
+                if (findSelectionEditPage != null)
+                {
+                    return findSelectionEditPage.GetPageIndex();
+                }
+                else
+                {
+                    return -1;
+                }
+            }
+        }
+
+        public bool CheckPageVisiable()
+        {
+            return (currentPageIndex >= PDFViewer.CurrentRenderFrame.Renders.First().Key &&
+                currentPageIndex <= PDFViewer.CurrentRenderFrame.Renders.Last().Key);
+        }
+
+        public void StartFindText(string findText, C_Search_Options options)
+        {
+            tempPageIndex = PDFViewer.CurrentRenderFrame.PageIndex;
+
+            // Check if the current mode is content editing
+            if (currentModel != ToolType.ContentEdit)
+            {
+                SetToolType(ToolType.ContentEdit);
+            }
+
+            // Check if the findText is empty
+            if (string.IsNullOrEmpty(findText))
+            {
+                return;
+            }
+
+            if (this.findText != findText)
+            {
+
+            }
+
+            this.findText = findText;
+            this.options = options;
+            canLoop = true;
+            FindText();
+        }
+
+        internal void FindText([CallerMemberName] string caller = "")
+        {
+            // Start searching from the currently displayed page
+            CPDFPage pdfPage = GetCPDFViewer().GetDocument().PageAtIndex(nextPageIndex);
+            findSelectionEditPage = pdfPage.GetEditPage();
+            findSelectionEditPage.BeginEdit(CPDFEditType.EditText | CPDFEditType.EditImage);
+
+            // If the passed value is null, it will cause the program to freeze
+            if (string.IsNullOrEmpty(findText))
+            {
+                return;
+            }
+
+            findSelectionEditPage.FindText(findText, options);
+
+            // Get all search content of the current page for subsequent search and highlighting
+            editTextFindSelectionList = findSelectionEditPage.GetTextFindSelectionList();
+
+            if (caller == "FindPrevious")
+            {
+                currentSelectionIndex = editTextFindSelectionList.Count;
+            }
+            else
+            {
+                currentSelectionIndex = -1;
+            }
+            isReplaced = false;
+        }
+
+        public bool FindPrevious()
+        { 
+            if (!CheckPageVisiable())
+            {
+                FindText();
+            }
+
+            int tempSelectionIndex = currentSelectionIndex - 1;
+
+            if (tempSelectionIndex >= 0)
+            {
+                currentSelectionIndex = tempSelectionIndex;
+            }
+            // The result of the previous option does not exist, try to find the result of another page
+            else
+            {
+                // When there is no cross-page search, you need to preset the cross-page status
+                if (!isOtherPage && editTextFindSelectionList?.Count != 0)
+                {
+                    nextPageIndex = currentPageIndex;
+                    isOtherPage = true;
+                }
+
+                if (currentPageIndex != 0)
+                {
+                    tempPageIndex = currentPageIndex - 1;
+                }
+                else
+                {
+                    if (canLoop)
+                    {
+                        tempPageIndex = PDFViewer.GetDocument().PageCount - 1;
+                        canLoop = false;
+                    }
+                    else
+                    {
+                        tempPageIndex -= 1;
+                    }
+                }
+
+                // Search upwards
+                if (tempPageIndex >= 0)
+                {
+                    nextPageIndex = tempPageIndex;
+                    FindText();
+                    currentSelectionIndex = editTextFindSelectionList.Count;
+                    return FindPrevious();
+                }
+
+                // Failed to find, set the search status to the initial state
+                else
+                {
+                    isOtherPage = false;
+                    canLoop = true;
+                    return false;
+                }
+            }
+
+            editTextFindSelection = editTextFindSelectionList[currentSelectionIndex];
+            GoToFoundDestination(editTextFindSelection);
+            isOtherPage = false;
+            canLoop = true;
+            return true;
+        }
+
+        public bool FindNext()
+        {
+            if (currentPageIndex == -1)
+            {
+                return false;
+            }
+
+            if (!CheckPageVisiable())
+            {
+                FindText();
+            }
+
+            int tempSelectionIndex = 0;
+            if (isReplaced)
+            {
+                tempSelectionIndex = currentSelectionIndex;
+                isReplaced = false;
+            }
+            else
+            {
+                tempSelectionIndex = currentSelectionIndex + 1;
+            }
+
+            if (tempSelectionIndex < editTextFindSelectionList?.Count)
+            {
+                currentSelectionIndex = tempSelectionIndex;
+            }
+            else
+            {
+                // Search across pages downwards, only allow looping when reaching the bottom for the first time
+                if (currentPageIndex != PDFViewer.GetDocument().PageCount - 1)
+                {
+                    tempPageIndex = currentPageIndex + 1;
+                }
+                else
+                {
+                    if (canLoop)
+                    {
+                        tempPageIndex = 0;
+                        canLoop = false;
+                    }
+                    else
+                    {
+                        tempPageIndex += 1;
+                    }
+                }
+
+                if (tempPageIndex < PDFViewer.GetDocument().PageCount)
+                {
+                    nextPageIndex = tempPageIndex;
+                    FindText();
+                    currentSelectionIndex = -1;
+                    return FindNext();
+                }
+                // Failed to find, set the search status to the initial state
+                else
+                {
+                    canLoop = true;
+                    return false;
+                }
+            }
+
+            editTextFindSelection = editTextFindSelectionList[currentSelectionIndex];
+
+            GoToFoundDestination(editTextFindSelection);
+
+            canLoop = true;
+            return true;
+        }
+
+        private void GoToFoundDestination(CPDFEditTextFindSelection editTextFindSelection, bool isReplacing = false)
+        {
+            float Position_X = editTextFindSelection.RectList[0].left;
+            float Position_Y = editTextFindSelection.RectList[0].top;
+            editTextFindSelection.GetTextArea(isReplacing).GetLastSelectChars();
+
+            SelectedEditAreaForIndex(editTextFindSelection.GetPageIndex(), editTextFindSelection.GetTextAreaIndex());
+            PDFViewer.GoToPage(editTextFindSelection.GetPageIndex(), new System.Windows.Point(Position_X, Position_Y));
+        }
+
+        public bool ReplaceText(string text)
+        {
+            if (findSelectionEditPage != null)
+            {
+                // Automatically search down when continuously triggered for replacement
+                if (isReplaced)
+                {
+                    FindNext();
+                }
+
+                // An overflow error occurred: Just translated and started searching, but no content was selected
+                if (currentSelectionIndex < 0 || currentSelectionIndex > editTextFindSelectionList.Count)
+                {
+                    return false;
+                }
+
+                // Current search age has no replaceable content error
+                if (editTextFindSelectionList.Count == 0)
+                {
+                    return false;
+                }
+
+                // Get the current search result
+                CPDFEditTextFindSelection editTextFindSelection = editTextFindSelectionList[currentSelectionIndex];
+                var area = editTextFindSelection.GetTextArea();
+                var rect = area.SelectLineRects[0];
+                CPDFEditTextArea editTextArea = findSelectionEditPage.ReplaceText(editTextFindSelection, text);
+                editTextFindSelection.GetTextArea(true).GetLastSelectChars();
+                if (editTextArea != null)
+                {
+                    findSelectionEditPage.EndEdit();
+                    findSelectionEditPage.FindText(findText, options);
+                    editTextFindSelectionList = findSelectionEditPage.GetTextFindSelectionList();
+                    {
+                        FindReplaceHistory findReplaceHistory = new FindReplaceHistory();
+                        findReplaceHistory.EditPage = findSelectionEditPage;
+                        findReplaceHistory.PageIndex = findSelectionEditPage.GetPageIndex();
+                        PDFViewer.UndoManager.AddHistory(findReplaceHistory);
+                    }
+                }
+
+                isReplaced = true;
+
+                GoToFoundDestination(editTextFindSelection, true);
+                return true;
+            }
+
+            return false;
+        }
+
+        public int ReplaceAllText(string replaceText)
+        {
+            int changeCount = 0;
+            for (int pageIndex = 0; pageIndex < PDFViewer.GetDocument().PageCount; pageIndex++)
+            {
+                CPDFPage page = PDFViewer.GetDocument().PageAtIndex(pageIndex);
+                CPDFEditPage editPage = page.GetEditPage();
+                editPage.BeginEdit(CPDFEditType.EditText | CPDFEditType.EditImage);
+                editPage.FindText(findText, options);
+                List<CPDFEditTextFindSelection> editTextFindSelectionList = editPage.GetTextFindSelectionList();
+
+                for (int i = editTextFindSelectionList.Count - 1; i >= 0; i--)
+                {
+                    if (editTextFindSelectionList.Count == 0)
+                    {
+                        continue;
+                    }
+                    changeCount += editTextFindSelectionList.Count;
+                    editPage.FindText(findText, options);
+
+                    editPage.ReplaceText(editTextFindSelectionList[i], replaceText);
+                }
+            }
+
+            ResetFindPlaceStatus();
+
+            SelectedEditAreaForIndex(-1, -1);
+            PDFViewer.UpdateRenderFrame();
+            StartFindText(findText, options);
+            return 0;
+        }
+
+        private void ResetFindPlaceStatus()
+        {
+            currentSelectionIndex = -1;
+            tempPageIndex = -1;
+
+            isOtherPage = false;
+            isReplaced = false;
+            canLoop = true; 
+        }
+    }
+}

+ 263 - 0
Demo/Examples/ComPDFKit.Tool/CPDFViewerTool.MultiSelectedRect.cs

@@ -0,0 +1,263 @@
+using ComPDFKit.Tool.DrawTool;
+using ComPDFKit.Tool.Help;
+using ComPDFKit.Viewer.Layer;
+using ComPDFKitViewer.Layer;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Input;
+
+namespace ComPDFKit.Tool
+{
+    public class MultiSelectedData
+    {
+        public int PageIndex { get; set; }
+
+        public List<int> MultiObjectIndex { get; set; }
+
+        /// <summary>
+        /// Current multi-select type
+        /// </summary>
+        public SelectedType ObjectType { get; set; }
+
+        /// <summary>
+        /// Move offset value of the whole
+        /// </summary>
+        public Point MoveOffset { get; set; }
+
+        public float ZoomX { get; set; }
+
+        public float ZoomY { get; set; }
+    }
+
+    public partial class CPDFViewerTool
+    {
+        int multiSelectedRectViewTag = -1;
+        List<int> editAreaMultiIndex = new List<int>();
+        int multiPage = -1;
+        public event EventHandler<MultiSelectedData> MultiDataChanging;
+        public event EventHandler<MultiSelectedData> MultiDataChanged;
+        private void InsertMultiSelectedRectView()
+        {
+            int selectedRectViewIndex = PDFViewer.GetMaxViewIndex();
+            CustomizeLayer customizeLayer = new CustomizeLayer();
+            MultiSelectedRect multiSelectedRect = new MultiSelectedRect(GetDefaultDrawParam(), SelectedType.None);
+            multiSelectedRect.SetDrawMoveType(DrawMoveType.kReferenceLine);
+            customizeLayer.Children.Add(multiSelectedRect);
+            multiSelectedRect.DataChanged += MultiSelectedRect_DataChanged;
+            multiSelectedRect.DataChanging += MultiSelectedRect_DataChanging;
+            PDFViewer.InsertView(selectedRectViewIndex, customizeLayer);
+            multiSelectedRectViewTag = customizeLayer.GetResTag();
+            //multiSelectedRect.Children.Add(multiSelectedRect);
+        }
+
+        private void MultiSelectedRect_DataChanging(object sender, Point e)
+        {
+            MultiSelectedData multiSelectedAnnotData = new MultiSelectedData();
+            MultiSelectedRect multiSelectedRect = CommonHelper.FindVisualChild<MultiSelectedRect>(PDFViewer.GetViewForTag(multiSelectedRectViewTag));
+            if (isOpen && multiSelectedRect != null)
+            {
+                multiSelectedAnnotData.ZoomX = multiSelectedRect.GetZoomX();
+                multiSelectedAnnotData.ZoomY = multiSelectedRect.GetZoomY();
+                multiSelectedAnnotData.MoveOffset = e;
+                multiSelectedAnnotData.ObjectType = multiSelectedRect.GetSelectedType();
+                multiSelectedAnnotData.MultiObjectIndex = new List<int>();
+                multiSelectedAnnotData.MultiObjectIndex.AddRange(editAreaMultiIndex);
+                multiSelectedAnnotData.PageIndex = multiPage;
+                MultiDataChanging?.Invoke(this, multiSelectedAnnotData);
+            }
+        }
+
+        private void MultiSelectedRect_DataChanged(object sender, Point e)
+        {
+            MultiSelectedData multiSelectedAnnotData = new MultiSelectedData();
+            MultiSelectedRect multiSelectedRect = CommonHelper.FindVisualChild<MultiSelectedRect>(PDFViewer.GetViewForTag(multiSelectedRectViewTag));
+            if (isOpen && multiSelectedRect != null)
+            {
+                multiSelectedAnnotData.ZoomX = multiSelectedRect.GetZoomX();
+                multiSelectedAnnotData.ZoomY = multiSelectedRect.GetZoomY();
+                multiSelectedAnnotData.MoveOffset = e;
+                multiSelectedAnnotData.ObjectType = multiSelectedRect.GetSelectedType();
+                multiSelectedAnnotData.MultiObjectIndex = new List<int>();
+                multiSelectedAnnotData.MultiObjectIndex.AddRange(editAreaMultiIndex);
+                multiSelectedAnnotData.PageIndex = multiPage;
+                MultiDataChanged?.Invoke(this, multiSelectedAnnotData);
+            }
+        }
+
+        bool isOpen = false;
+        public void OpenSelectedMulti(bool open)
+        {
+            if (!open)
+            {
+                CleanSelectedMultiRect();
+            }
+            isOpen = open;
+        }
+
+        public bool HitTestMultiSelectedRect()
+        {
+            MultiSelectedRect multiSelectedRect = CommonHelper.FindVisualChild<MultiSelectedRect>(PDFViewer.GetViewForTag(multiSelectedRectViewTag));
+            if (isOpen && multiSelectedRect != null)
+            {
+                if (multiSelectedRect.GetHitControlIndex(Mouse.GetPosition(this)) != PointControlType.None)
+                {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        public void SelectedMultiRect(Rect selectedRects, Rect MaxRect, SelectedType type)
+        {
+            MultiSelectedRect multiSelectedRect = CommonHelper.FindVisualChild<MultiSelectedRect>(PDFViewer.GetViewForTag(multiSelectedRectViewTag));
+            if (isOpen && multiSelectedRect != null)
+            {
+
+                GetSelectedEditAreaForIndex(out int pageIndex, out int editAreaIndex);
+                if (multiPage != pageIndex && editAreaMultiIndex.Count > 0)
+                {
+                    foreach (int item in editAreaMultiIndex)
+                    {
+                        SelectedRect OldRect = GetEditAreaForIndex(multiPage, item);
+                        if (OldRect != null)
+                        {
+                            OldRect.Draw();
+                        }
+                    }
+                    editAreaMultiIndex.Clear();
+                    multiSelectedRect.ClearDraw();
+                    multiSelectedRect.CleanMulitSelectedRect();
+                    multiPage = pageIndex;
+                }
+                multiPage = pageIndex;
+                editAreaMultiIndex.Add(editAreaIndex);
+
+                multiSelectedRect.SetSelectedType(type);
+                SelectedRect selectedRect = new SelectedRect(GetDefaultDrawParam(), type);
+                selectedRect.SetDrawMoveType(DrawMoveType.kReferenceLine);
+                selectedRect.SetRect(selectedRects,currentZoom);
+                selectedRect.SetMaxRect(MaxRect);
+                multiSelectedRect.Children.Add(selectedRect);
+                multiSelectedRect.SetMulitSelectedRect(selectedRect);
+
+                multiSelectedRect.SetRect(selectedRects);
+                multiSelectedRect.SetMaxRect(MaxRect);
+                multiSelectedRect.Draw();
+            }
+
+        }
+
+        public void HideDrawSelectedMultiRect()
+        {
+            foreach (int item in editAreaMultiIndex)
+            {
+                SelectedRect OldRect = GetEditAreaForIndex(multiPage, item);
+                if (OldRect != null)
+                {
+                    OldRect.HideDraw();
+                }
+            }
+        }
+
+        public void CleanSelectedMultiRect()
+        {
+            MultiSelectedRect multiSelectedRect = CommonHelper.FindVisualChild<MultiSelectedRect>(PDFViewer.GetViewForTag(multiSelectedRectViewTag));
+            if (multiSelectedRect != null)
+            {
+                multiSelectedRect.Children.Clear();
+                multiSelectedRect.CleanMulitSelectedRect();
+                editAreaMultiIndex.Clear();
+            }
+        }
+
+        public void DrawStartSelectedMultiRect()
+        {
+            MultiSelectedRect multiSelectedRect = CommonHelper.FindVisualChild<MultiSelectedRect>(PDFViewer.GetViewForTag(multiSelectedRectViewTag));
+
+            if (multiSelectedRect != null)
+            {
+                Point point = Mouse.GetPosition(this);
+                multiSelectedRect.Draw();
+                multiSelectedRect.OnMouseLeftButtonDown(point);
+            }
+        }
+
+        public void DrawMoveSelectedMultiRect()
+        {
+            MultiSelectedRect multiSelectedRect = CommonHelper.FindVisualChild<MultiSelectedRect>(PDFViewer.GetViewForTag(multiSelectedRectViewTag));
+
+            if (multiSelectedRect != null)
+            {
+                Point point = Mouse.GetPosition(this);
+                multiSelectedRect.OnMouseMove(point, out bool Tag, PDFViewer.ActualWidth, PDFViewer.ActualHeight);
+            }
+        }
+
+        public void DrawEndSelectedMultiRect()
+        {
+            MultiSelectedRect multiSelectedRect = CommonHelper.FindVisualChild<MultiSelectedRect>(PDFViewer.GetViewForTag(multiSelectedRectViewTag));
+
+            if (multiSelectedRect != null)
+            {
+                Point point = Mouse.GetPosition(this);
+                multiSelectedRect.OnMouseLeftButtonUp(point);
+            }
+        }
+
+        public void ReDrawSelectedMultiRect()
+        {
+            MultiSelectedRect multiSelectedRect = CommonHelper.FindVisualChild<MultiSelectedRect>(PDFViewer.GetViewForTag(multiSelectedRectViewTag));
+
+            if (multiSelectedRect != null)
+            {
+                multiSelectedRect.ClearDraw();
+                multiSelectedRect.CleanMulitSelectedRect();
+
+                Point point = Mouse.GetPosition(this);
+
+                switch (multiSelectedRect.GetSelectedType())
+                {
+                    case SelectedType.Annot:
+                        SelectAnnot();
+                        break;
+                    case SelectedType.PDFEdit:
+                        SelectPDFEdit(multiSelectedRect);
+                        break;
+                    default:
+                        break;
+                }
+                multiSelectedRect.Draw();
+            }
+        }
+
+        private void SelectAnnot()
+        {
+            CleanSelectedRect();
+        }
+
+        private void SelectPDFEdit(MultiSelectedRect multiSelectedRect)
+        {
+            foreach (int item in editAreaMultiIndex)
+            {
+                SelectedRect OldRect = GetEditAreaForIndex(multiPage, item);
+                if (OldRect != null)
+                {
+                    multiSelectedRect.SetSelectedType(SelectedType.PDFEdit);
+                    SelectedRect selectedRect = new SelectedRect(GetDefaultDrawParam(), SelectedType.PDFEdit);
+                    selectedRect.SetDrawMoveType(DrawMoveType.kReferenceLine);
+                    selectedRect.SetRect(OldRect.GetRect(),currentZoom);
+                    selectedRect.SetMaxRect(OldRect.GetMaxRect());
+                    multiSelectedRect.Children.Add(selectedRect);
+                    multiSelectedRect.SetMulitSelectedRect(selectedRect);
+                    multiSelectedRect.SetRect(OldRect.GetRect());
+                    multiSelectedRect.SetMaxRect(OldRect.GetMaxRect());
+                    OldRect.HideDraw();
+                }
+            }
+        }
+    }
+}

+ 265 - 0
Demo/Examples/ComPDFKit.Tool/CPDFViewerTool.PageSelected.cs

@@ -0,0 +1,265 @@
+using ComPDFKit.Import;
+using ComPDFKit.PDFDocument;
+using ComPDFKit.PDFPage;
+using ComPDFKit.Tool.DrawTool;
+using ComPDFKit.Tool.Help;
+using ComPDFKit.Viewer.Layer;
+using ComPDFKitViewer;
+using ComPDFKitViewer.Helper;
+using ComPDFKitViewer.Layer;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Input;
+
+namespace ComPDFKit.Tool
+{
+    public class PageSelectedData
+    {
+        public int PageIndex { get; set; }
+        public Rect DrawRect { get; set; }
+        public Rect RawRect { get; set; }
+        public Rect SelectRect { get; set; }
+    }
+    public partial class CPDFViewerTool
+    {
+        int pageSelectedRectViewTag = -1;
+        int pageIndex = -1;
+        public event EventHandler<PageSelectedData> PageSelectedChanging;
+        public event EventHandler<PageSelectedData> PageSelectedChanged;
+        private void InsertPageSelectedRectView()
+        {
+            int selectedRectViewIndex = PDFViewer.GetMaxViewIndex();
+            CustomizeLayer customizeLayer = new CustomizeLayer();
+            PageSelectedRect pageSelectedRect = new PageSelectedRect(GetDefaultDrawParam());
+            pageSelectedRect.SetDrawMoveType(DrawMoveType.kReferenceLine);
+            customizeLayer.Children.Add(pageSelectedRect);
+            pageSelectedRect.DataChanged += PageSelectedRect_DataChanged;
+            pageSelectedRect.DataChanging += PageSelectedRect_DataChanging; ;
+            PDFViewer.InsertView(selectedRectViewIndex, customizeLayer);
+            pageSelectedRectViewTag = customizeLayer.GetResTag();
+        }
+
+        private void PageSelectedRect_DataChanging(object sender, Rect e)
+        {
+            PageSelectedRect pageSelectedRect = CommonHelper.FindVisualChild<PageSelectedRect>(PDFViewer.GetViewForTag(pageSelectedRectViewTag));
+
+            if (pageSelectedRect != null)
+            {
+                PageSelectedData pageSelectedData = new PageSelectedData();
+                pageSelectedData.PageIndex = pageIndex;
+                pageSelectedData.DrawRect = e;
+                PageSelectedChanged?.Invoke(this, pageSelectedData);
+            }
+        }
+
+        private void PageSelectedRect_DataChanged(object sender, Rect e)
+        {
+            PageSelectedRect pageSelectedRect = CommonHelper.FindVisualChild<PageSelectedRect>(PDFViewer.GetViewForTag(pageSelectedRectViewTag));
+
+            if (pageSelectedRect != null)
+            {
+                Canvas canvas = CommonHelper.FindVisualChild<Canvas>(pageSelectedRect);
+                if (canvas != null)
+                {
+                    UserControl userControl = CommonHelper.FindVisualChild<UserControl>(canvas);
+                    if (userControl != null)
+                    {
+                        userControl.Visibility = Visibility.Collapsed;
+                        if (e.Width > 0 && e.Height > 0)
+                        {
+                            userControl.Visibility = Visibility.Visible;
+                            userControl.Measure(pageSelectedRect.GetMaxRect().Size);
+                            Size desireSize = userControl.DesiredSize;
+                            if (e.Bottom + desireSize.Height + 3 > Math.Min(pageSelectedRect.GetMaxRect().Bottom, PDFViewer.ViewportHeight))
+                            {
+                                userControl.SetValue(Canvas.LeftProperty, e.Right - desireSize.Width);
+                                double topPos = (int)Math.Max(e.Top - desireSize.Height - 3, 0);
+
+                                userControl.SetValue(Canvas.TopProperty, topPos);
+                            }
+                            else
+                            {
+                                userControl.SetValue(Canvas.LeftProperty, e.Right - desireSize.Width);
+                                userControl.SetValue(Canvas.TopProperty, e.Bottom + 3);
+                            }
+                            if (e.Right - desireSize.Width < 0)
+                            {
+                                userControl.SetValue(Canvas.LeftProperty, 3D);
+                            }
+                        }
+                    }
+                    PageSelectedData pageSelectedData = new PageSelectedData();
+                    pageSelectedData.PageIndex = pageIndex;
+                    pageSelectedData.DrawRect = e;
+                    pageSelectedData.RawRect = Rect.Empty;
+                    pageSelectedData.SelectRect = Rect.Empty;
+                    try
+                    {
+                        Rect maxRect = pageSelectedRect.GetMaxRect();
+                        Rect pageRect = new Rect(e.X - maxRect.X, e.Y - maxRect.Y, e.Width, e.Height);
+                        pageSelectedData.SelectRect = pageRect;
+                        double zoom = PDFViewer.GetZoom();
+                        pageRect = new Rect(
+                            pageRect.X / zoom / 96D * 72D, 
+                            pageRect.Y / zoom / 96D * 72D, 
+                            pageRect.Width / zoom / 96D * 72D,
+                            pageRect.Height / zoom / 96D * 72D);
+
+                        pageSelectedData.RawRect = pageRect;
+                    }
+                    catch (Exception ex)
+                    {
+
+                    }
+
+                    PageSelectedChanged?.Invoke(this, pageSelectedData);
+                }
+            }
+        }
+
+        public void DrawStartPageSelectedRect()
+        {
+            PageSelectedRect pageSelectedRect = CommonHelper.FindVisualChild<PageSelectedRect>(PDFViewer.GetViewForTag(pageSelectedRectViewTag));
+
+            if (pageSelectedRect != null)
+            {
+                Point point = Mouse.GetPosition(this);
+                pageSelectedRect.Draw();
+                pageSelectedRect.OnMouseLeftButtonDown(point);
+                PDFViewer.CanHorizontallyScroll = false;
+                PDFViewer.CanVerticallyScroll = false;
+                PDFViewer.EnableZoom(false);
+            }
+        }
+
+        public void DrawMovePageSelectedRect()
+        {
+            PageSelectedRect pageSelectedRect = CommonHelper.FindVisualChild<PageSelectedRect>(PDFViewer.GetViewForTag(pageSelectedRectViewTag));
+
+            if (pageSelectedRect != null)
+            {
+                Point point = Mouse.GetPosition(this);
+                pageSelectedRect.OnMouseMove(point, out bool Tag, PDFViewer.ActualWidth, PDFViewer.ActualHeight);
+            }
+        }
+
+        public void DrawEndPageSelectedRect()
+        {
+            PageSelectedRect pageSelectedRect = CommonHelper.FindVisualChild<PageSelectedRect>(PDFViewer.GetViewForTag(pageSelectedRectViewTag));
+
+            if (pageSelectedRect != null)
+            {
+                Point point = Mouse.GetPosition(this);
+                pageSelectedRect.OnMouseLeftButtonUp(point);
+            }
+        }
+
+        public void SelectedPageSelectedRect(Rect selectedRects, Rect MaxRect)
+        {
+            PageSelectedRect pageSelectedRect = CommonHelper.FindVisualChild<PageSelectedRect>(PDFViewer.GetViewForTag(pageSelectedRectViewTag));
+            if (pageSelectedRect != null)
+            {
+                pageSelectedRect.SetRect(selectedRects);
+                pageSelectedRect.SetMaxRect(MaxRect);
+                pageSelectedRect.SetPDFViewerActualSize(PDFViewer.ActualWidth, PDFViewer.ActualHeight);
+                pageSelectedRect.Draw();
+            }
+        }
+
+        public bool HitTestPageSelectedRect()
+        {
+            PageSelectedRect pageSelectedRect = CommonHelper.FindVisualChild<PageSelectedRect>(PDFViewer.GetViewForTag(pageSelectedRectViewTag));
+            if (pageSelectedRect != null)
+            {
+                if (pageSelectedRect.GetHitControlIndex(Mouse.GetPosition(this)) != PointControlType.None)
+                {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        public void CleanPageSelectedRect()
+        {
+            PageSelectedRect pageSelectedRect = CommonHelper.FindVisualChild<PageSelectedRect>(PDFViewer.GetViewForTag(pageSelectedRectViewTag));
+            if (pageSelectedRect != null)
+            {
+                pageSelectedRect.ClearDraw(); 
+                Canvas canvas = CommonHelper.FindVisualChild<Canvas>(pageSelectedRect);
+                if (canvas != null)
+                {
+                    UserControl userControl = CommonHelper.FindVisualChild<UserControl>(canvas);
+                    if (userControl != null)
+                    {
+                        userControl.Visibility=Visibility.Collapsed;
+                    }
+                }
+                pageIndex = -1;
+                PDFViewer.CanHorizontallyScroll = true;
+                PDFViewer.CanVerticallyScroll = true;
+                PDFViewer.EnableZoom(true);
+            }
+        }
+            
+        public void CreatePageSelectdRect()
+        {
+            PageSelectedRect pageSelectedRect = CommonHelper.FindVisualChild<PageSelectedRect>(PDFViewer.GetViewForTag(pageSelectedRectViewTag));
+            if (pageSelectedRect != null)
+            {
+                Point point = Mouse.GetPosition(this);
+                BaseLayer baseLayer = PDFViewer.GetViewForTag(createAnnotTag);
+                PDFViewer.GetPointPageInfo(point, out int index, out Rect paintRect, out Rect pageBound);
+                if (index < 0)
+                {
+                    return;
+                }
+                pageIndex = index;
+                CPDFDocument cPDFDocument = PDFViewer.GetDocument();
+                CPDFPage cPDFPage = cPDFDocument.PageAtIndex(index);
+
+                Point cropPoint = new Point();
+                if (PDFViewer.GetIsCrop())
+                {
+                    CRect cRect = cPDFPage.GetCropBounds();
+                    cropPoint.X = DpiHelper.PDFNumToStandardNum(cRect.left);
+                    cropPoint.Y = DpiHelper.PDFNumToStandardNum(cRect.top);
+                }
+                pageSelectedRect.CreateRect(point, cropPoint, pageBound, PDFViewer.ActualWidth, PDFViewer.ActualHeight);
+                pageSelectedRect.Draw();
+                PDFViewer.CanHorizontallyScroll = false;
+                PDFViewer.CanVerticallyScroll = false;
+                PDFViewer.EnableZoom(false);
+            }
+        }
+
+        public void SetPageSelectdUserControl(UserControl control)
+        {
+            PageSelectedRect pageSelectedRect = CommonHelper.FindVisualChild<PageSelectedRect>(PDFViewer.GetViewForTag(pageSelectedRectViewTag));
+            if (pageSelectedRect != null)
+            {
+                pageSelectedRect.Children.Clear();
+                Canvas popCanvas = new Canvas();
+                pageSelectedRect.Children.Add(popCanvas);
+                popCanvas.Children.Add(control);
+                control.Visibility = Visibility.Collapsed;
+                pageSelectedRect.Arrange();
+            }
+        }
+
+        public void RemovePageSelectdUserControl()
+        {
+            PageSelectedRect pageSelectedRect = CommonHelper.FindVisualChild<PageSelectedRect>(PDFViewer.GetViewForTag(pageSelectedRectViewTag));
+            if (pageSelectedRect != null)
+            {
+                pageSelectedRect.Children.Clear();
+                pageIndex = -1;
+            }
+        }
+    }
+}

+ 82 - 0
Demo/Examples/ComPDFKit.Tool/CPDFViewerTool.SelectImage.cs

@@ -0,0 +1,82 @@
+using ComPDFKit.Tool.DrawTool;
+using ComPDFKitViewer.Helper;
+using ComPDFKitViewer.Layer;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+
+namespace ComPDFKit.Tool
+{
+    public partial class CPDFViewerTool
+    {
+        int selectImageTag = -1;
+        private void InsertSelectImageView()
+        {
+            SelectImage createAnnotTool = new SelectImage();
+            int SelectTextindex = PDFViewer.GetMaxViewIndex();
+            PDFViewer.InsertView(SelectTextindex, createAnnotTool);
+            selectImageTag = createAnnotTool.GetResTag();
+        }
+        public bool DrawMoveSelectImage()
+        {
+            bool isSelect = false;
+            if (PDFViewer.CurrentRenderFrame == null)
+            {
+                return isSelect;
+            }
+            BaseLayer baseLayer = PDFViewer.GetViewForTag(selectImageTag);
+            PDFViewer.GetMousePointToPage(out int pageindex, out Point pagepoint);
+            isSelect=(baseLayer as SelectImage).ProcessMouseMoveForSelectImage(
+                new Point(DpiHelper.StandardNumToPDFNum(pagepoint.X / PDFViewer.CurrentRenderFrame.ZoomFactor),
+                DpiHelper.StandardNumToPDFNum(pagepoint.Y / PDFViewer.CurrentRenderFrame.ZoomFactor)),
+                pageindex, PDFViewer);
+            (baseLayer as SelectImage).Draw(PDFViewer); 
+            return isSelect;
+        }
+
+        public bool DrawDownSelectImage(bool isNeedClear)
+        {
+            bool isSelect = false;
+            if (PDFViewer.CurrentRenderFrame == null)
+            {
+                return isSelect;
+            }
+            BaseLayer baseLayer = PDFViewer.GetViewForTag(selectImageTag);
+            PDFViewer.GetMousePointToPage(out int pageindex, out Point pagepoint);
+            isSelect=(baseLayer as SelectImage).ProcessMouseDownForSelectImage(
+                new Point(DpiHelper.StandardNumToPDFNum(pagepoint.X / PDFViewer.CurrentRenderFrame.ZoomFactor),
+                DpiHelper.StandardNumToPDFNum(pagepoint.Y / PDFViewer.CurrentRenderFrame.ZoomFactor)),
+                pageindex, PDFViewer, isNeedClear);
+            (baseLayer as SelectImage).Draw(PDFViewer);
+            return isSelect;
+        }
+
+
+        public void ReDrawSelectImage()
+        {
+            BaseLayer baseLayer = PDFViewer.GetViewForTag(selectImageTag);
+            (baseLayer as SelectImage).Draw(PDFViewer);
+        }
+
+        public void CleanDrawSelectImage()
+        {
+            BaseLayer baseLayer = PDFViewer.GetViewForTag(selectImageTag);
+            (baseLayer as SelectImage).CleanDraw(PDFViewer);
+        }
+
+        public PageImageItem GetSelectImage()
+        {
+            BaseLayer baseLayer = PDFViewer.GetViewForTag(selectImageTag);
+            return (baseLayer as SelectImage).GetHoverImageItem();
+        }
+
+        public Dictionary<int, List<PageImageItem>> GetSelectImageItems()
+        {
+            BaseLayer baseLayer = PDFViewer.GetViewForTag(selectImageTag);
+            return (baseLayer as SelectImage).GetSelectImageItems();
+        }
+    }
+}

+ 213 - 0
Demo/Examples/ComPDFKit.Tool/CPDFViewerTool.SelectText.cs

@@ -0,0 +1,213 @@
+using ComPDFKit.Tool.DrawTool;
+using ComPDFKitViewer.Layer;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Input;
+using System.Windows;
+using ComPDFKitViewer.Helper;
+using ComPDFKitViewer;
+using ComPDFKit.Tool.Help;
+using System.Windows.Media;
+using System.Xml.Linq;
+
+namespace ComPDFKit.Tool
+{
+    public partial class CPDFViewerTool
+    {
+        int selectTextTag = -1;
+        private void InsertSelectTextView()
+        {
+            SelectText createAnnotTool = new SelectText();
+            int SelectTextindex = PDFViewer.GetMaxViewIndex();
+            PDFViewer.InsertView(SelectTextindex, createAnnotTool);
+            selectTextTag = createAnnotTool.GetResTag();
+        }
+
+        public bool IsText()
+        {
+            PDFViewer.GetMousePointToPage(out int pageindex, out Point pagepoint);
+            return PDFHelp.IsTextAtPos(PDFViewer.GetDocument(), pageindex, new Point(DpiHelper.StandardNumToPDFNum(pagepoint.X / PDFViewer.CurrentRenderFrame.ZoomFactor), DpiHelper.StandardNumToPDFNum(pagepoint.Y / PDFViewer.CurrentRenderFrame.ZoomFactor)));
+        }
+
+        public void DrawStartSelectText()
+        {
+            if (PDFViewer.CurrentRenderFrame == null)
+            {
+                return;
+            }
+            BaseLayer baseLayer = PDFViewer.GetViewForTag(selectTextTag);
+            PDFViewer.GetMousePointToPage(out int pageindex, out Point pagepoint);
+            (baseLayer as SelectText).StartDraw(new Point(DpiHelper.StandardNumToPDFNum(pagepoint.X / PDFViewer.CurrentRenderFrame.ZoomFactor), DpiHelper.StandardNumToPDFNum(pagepoint.Y / PDFViewer.CurrentRenderFrame.ZoomFactor)), pageindex);
+        }
+
+        public void DrawMoveSelectText(bool DoubleClick)
+        {
+            if (PDFViewer.CurrentRenderFrame == null)
+            {
+                return;
+            }
+            BaseLayer baseLayer = PDFViewer.GetViewForTag(selectTextTag);
+            PDFViewer.GetMousePointToPage(out int pageindex, out Point pagepoint);
+            (baseLayer as SelectText).MoveDraw(new Point(DpiHelper.StandardNumToPDFNum(pagepoint.X / PDFViewer.CurrentRenderFrame.ZoomFactor), DpiHelper.StandardNumToPDFNum(pagepoint.Y / PDFViewer.CurrentRenderFrame.ZoomFactor)), pageindex, PDFViewer, new Point(10, 10), DoubleClick);
+        }
+
+        public void DrawEndSelectText()
+        {
+            BaseLayer baseLayer = PDFViewer.GetViewForTag(selectTextTag);
+            (baseLayer as SelectText).EndDraw();
+        }
+
+        public bool GetMousePointToTextSelectInfo()
+        {
+            BaseLayer baseLayer = PDFViewer.GetViewForTag(selectTextTag);
+            TextSelectInfo selectTextInfo = (baseLayer as SelectText).GetTextSelectInfo();
+            PDFViewer.GetMousePointToPage(out int pageindex, out Point pagepoint);
+            bool PressOnSelectedText = false;
+            if (selectTextInfo.PageSelectTextRectList.Count <= 0)
+            {
+                PressOnSelectedText = false;
+                return PressOnSelectedText;
+            }
+            if (selectTextInfo.PageSelectTextRectList.ContainsKey(pageindex))
+            {
+                foreach (TextDrawRect textRect in selectTextInfo.PageSelectTextRectList[pageindex])
+                {
+                    Rect RawPaintRect = new Rect(
+                        DpiHelper.PDFNumToStandardNum(textRect.DrawRect.Left) * currentZoom,
+                           DpiHelper.PDFNumToStandardNum(textRect.DrawRect.Top) * currentZoom,
+                             DpiHelper.PDFNumToStandardNum(textRect.DrawRect.Width) * currentZoom,
+                             DpiHelper.PDFNumToStandardNum(textRect.DrawRect.Height) * currentZoom);
+
+                    if (RawPaintRect.Contains(pagepoint))
+                    {
+                        PressOnSelectedText = true;
+                        break;
+                    }
+                }
+            }
+            return PressOnSelectedText;
+        }
+
+        public TextSelectInfo GetTextSelectInfo()
+        {
+            BaseLayer baseLayer = PDFViewer.GetViewForTag(selectTextTag);
+            return (baseLayer as SelectText).GetTextSelectInfo();
+        }
+
+        public void ReDrawSelectText()
+        {
+            BaseLayer baseLayer = PDFViewer.GetViewForTag(selectTextTag);
+            if ((baseLayer as SelectText).HasSelectTextInfo())
+            {
+                (baseLayer as SelectText).Draw(PDFViewer);
+            }
+            else
+            {
+                (baseLayer as SelectText).CleanDraw(PDFViewer);
+            }
+        }
+
+        public void ReDrawSearchText()
+        {
+            BaseLayer baseLayer = PDFViewer.GetViewForTag(selectTextTag);
+            if ((baseLayer as SelectText).HasSearchInfo())
+            {
+                (baseLayer as SelectText).Draw(PDFViewer);
+            }
+            else
+            {
+                (baseLayer as SelectText).CleanDraw(PDFViewer);
+            }
+        }
+
+        public void RemoveSelectTextData()
+        {
+            BaseLayer baseLayer = PDFViewer.GetViewForTag(selectTextTag);
+            if ((baseLayer as SelectText).HasSelectTextInfo())
+            {
+                (baseLayer as SelectText).RemoveSelectDataInfo();
+                (baseLayer as SelectText).Draw(PDFViewer);
+            }
+        }
+
+        /// <summary>
+        /// Set all search results
+        /// </summary>
+        /// <param name="searchTexts"></param>
+        public void SetPageSelectText(List<TextSearchItem> searchTexts)
+        {
+            if (searchTexts.Count > 0)
+            {
+                TextSelectInfo searchInfo = new TextSelectInfo();
+                searchInfo.StartPage = searchTexts.Min(x => x.PageIndex);
+                searchInfo.EndPage = searchTexts.Max(x => x.PageIndex);
+                searchInfo.PageRotate = searchTexts[0].PageRotate;
+                searchInfo.RotateRecord = true;
+                List<int> pageIndexList = (from u in searchTexts select u.PageIndex).Distinct().ToList();
+                foreach (int pageIndex in pageIndexList)
+                {
+                    List<TextSearchItem> pageTexts = searchTexts.Where(x => x.PageIndex == pageIndex).ToList();
+                    foreach (TextSearchItem textItem in pageTexts)
+                    {
+                        if (!searchInfo.PageSelectTextRectList.ContainsKey(pageIndex))
+                        {
+                            searchInfo.PageSelectTextRectList[pageIndex] = new List<TextDrawRect>();
+                        }
+
+                        searchInfo.PageSelectTextRectList[pageIndex].Add(new TextDrawRect()
+                        {
+                            Text = textItem.TextContent,
+                            DrawRect = textItem.TextRect,
+                            PaintBrush = textItem.PaintBrush,
+                            DrawActiveSearch = false,
+                            SearchInfo = textItem
+                        });
+                    }
+                }
+
+                BaseLayer baseLayer = PDFViewer.GetViewForTag(selectTextTag);
+                (baseLayer as SelectText).SetSearchInfo(searchInfo);
+            }
+        }
+
+        /// <summary>
+        /// Clear the previously cached search results
+        /// </summary>
+        public void CleanSearchInfo()
+        {
+            BaseLayer baseLayer = PDFViewer.GetViewForTag(selectTextTag);
+            (baseLayer as SelectText).CleanSearchInfo();
+        }
+
+        public void HighLightSearchText(List<TextSearchItem> selectTexts)
+        {
+            BaseLayer baseLayer = PDFViewer.GetViewForTag(selectTextTag);
+            if ((baseLayer as SelectText).HasSearchInfo())
+            {
+                TextSelectInfo searchTextInfo = (baseLayer as SelectText).GetSearchInfo();
+                if (searchTextInfo.PageSelectTextRectList != null)
+                {
+                    foreach (int pageIndex in searchTextInfo.PageSelectTextRectList.Keys)
+                    {
+                        List<TextDrawRect> drawSearchList = searchTextInfo.PageSelectTextRectList[pageIndex];
+                        foreach (TextDrawRect drawRect in drawSearchList)
+                        {
+                            drawRect.DrawActiveSearch = selectTexts.Contains(drawRect.SearchInfo);
+                            if (drawRect.DrawActiveSearch)
+                            {
+                                drawRect.PaintBrush = drawRect.SearchInfo.PaintBrush;
+                            }
+                            else
+                            {
+                                drawRect.PaintBrush = drawRect.SearchInfo.BorderBrush;
+                            }
+                        }
+                    }
+                }
+            } 
+        }
+    }
+}

+ 195 - 0
Demo/Examples/ComPDFKit.Tool/CPDFViewerTool.SelectedRect.cs

@@ -0,0 +1,195 @@
+using ComPDFKit.Tool.DrawTool;
+using ComPDFKitViewer.Layer;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Input;
+using System.Windows;
+using ComPDFKit.Viewer.Layer;
+using System.Windows.Media;
+using ComPDFKit.Tool.Help;
+using ComPDFKitViewer;
+using ComPDFKit.PDFAnnotation;
+
+namespace ComPDFKit.Tool
+{
+    public partial class CPDFViewerTool
+    {
+        bool isDrawSelectRect = false;
+        int selectedRectViewTag = -1;
+        public event EventHandler<SelectedAnnotData> SelectedDataChanging;
+        public event EventHandler<SelectedAnnotData> SelectedDataChanged;
+
+        private void InsertSelectedRectView()
+        {
+            int selectedRectViewIndex = PDFViewer.GetMaxViewIndex();
+            CustomizeLayer customizeLayer = new CustomizeLayer();
+            SelectedRect selectedRect = new SelectedRect(GetDefaultDrawParam(), SelectedType.Annot);
+            selectedRect.SetDrawMoveType(DrawMoveType.kDefault);
+            customizeLayer.Children.Add(selectedRect);
+            selectedRect.DataChanged += SelectedRect_DataChanged;
+            selectedRect.DataChanging += SelectedRect_DataChanging;
+            PDFViewer.InsertView(selectedRectViewIndex, customizeLayer);
+            selectedRectViewTag= customizeLayer.GetResTag();
+        }
+
+        private void SelectedRect_DataChanging(object sender, SelectedAnnotData e)
+        {
+            SelectedDataChanging?.Invoke(this, e);
+        }
+
+        private void SelectedRect_DataChanged(object sender, SelectedAnnotData e)
+        {
+            SelectedDataChanged?.Invoke(this, e);
+        }
+
+        /// <summary>
+        /// Start to draw the rectangle
+        /// </summary>
+        public void DrawStartSelectedRect()
+        {
+            Point point = Mouse.GetPosition(this);
+            BaseLayer baseLayer = PDFViewer.GetViewForTag(selectedRectViewTag);
+
+            SelectedRect selectedRect = CommonHelper.FindVisualChild<SelectedRect>(baseLayer as CustomizeLayer);
+            if (selectedRect != null)
+            {
+                selectedRect.Draw();
+                selectedRect.OnMouseLeftButtonDown(point);
+                isDrawSelectRect = true;
+            }
+        }
+
+        public Cursor GetMoveSelectedRectCursor()
+        {
+            Point point = Mouse.GetPosition(this);
+            BaseLayer baseLayer = PDFViewer.GetViewForTag(selectedRectViewTag);
+
+            SelectedRect selectedRect = CommonHelper.FindVisualChild<SelectedRect>(baseLayer as CustomizeLayer);
+            if (selectedRect != null)
+            {
+                return selectedRect.GetCursor(point, this.Cursor);
+            }
+            return this.Cursor;
+        }
+
+        /// <summary>
+        /// Draw the rectangle when dragging
+        /// </summary>
+        public bool DrawMoveSelectedRect()
+        {
+            bool DrawTag = false;
+            Point point = Mouse.GetPosition(this);
+            BaseLayer baseLayer = PDFViewer.GetViewForTag(selectedRectViewTag);
+
+            SelectedRect selectedRect = CommonHelper.FindVisualChild<SelectedRect>(baseLayer as CustomizeLayer);
+            if (selectedRect != null)
+            {
+                selectedRect.OnMouseMove(point, out DrawTag,PDFViewer.ActualWidth,PDFViewer.ActualHeight);
+                selectedRect.Draw();
+            }
+            return DrawTag;
+        }
+
+        /// <summary>
+        /// End of drawing the rectangle
+        /// </summary>
+        public void DrawEndSelectedRect()
+        {
+            Point point = Mouse.GetPosition(this);
+            BaseLayer baseLayer = PDFViewer.GetViewForTag(selectedRectViewTag);
+
+            SelectedRect selectedRect = CommonHelper.FindVisualChild<SelectedRect>(baseLayer as CustomizeLayer);
+            if (selectedRect != null)
+            {
+                selectedRect.OnMouseLeftButtonUp(point);
+                selectedRect.Draw();
+            }
+        }
+
+        /// <summary>
+        /// Clear the rectangle drawing
+        /// </summary>
+        public void CleanSelectedRect()
+        {
+            Point point = Mouse.GetPosition(this);
+            BaseLayer baseLayer = PDFViewer.GetViewForTag(selectedRectViewTag);
+
+            SelectedRect selectedRect = CommonHelper.FindVisualChild<SelectedRect>(baseLayer as CustomizeLayer);
+            if (selectedRect != null)
+            {
+                selectedRect.ClearDraw();
+                isDrawSelectRect = false;
+            }
+        }
+
+        private void SelectedAnnot()
+        {
+            if (!isHitTestLink&& cacheHitTestAnnot?.CurrentType== C_ANNOTATION_TYPE.C_ANNOTATION_LINK)
+            {
+                return ;
+            }
+            if (isHitTestRedact && cacheHitTestAnnot?.CurrentType!= C_ANNOTATION_TYPE.C_ANNOTATION_REDACT)
+            {
+                return ;
+            }
+            BaseLayer baseLayer = PDFViewer.GetViewForTag(selectedRectViewTag);
+            SelectedRect selectedRect = CommonHelper.FindVisualChild<SelectedRect>(baseLayer as CustomizeLayer);
+            if (selectedRect != null)
+            {
+                selectedRect.SetAnnotData(cacheHitTestAnnot.GetAnnotData());
+            }
+        }
+
+        private void SelectedAnnot(AnnotData annotData)
+        {
+            BaseLayer baseLayer = PDFViewer.GetViewForTag(selectedRectViewTag);
+            SelectedRect selectedRect = CommonHelper.FindVisualChild<SelectedRect>(baseLayer as CustomizeLayer);
+            if (selectedRect != null)
+            {
+                if (annotData==null)
+                {
+                    selectedRect.ClearDraw();
+                }
+                else
+                {
+                    selectedRect.SetAnnotData(annotData);
+                }
+            }
+        }
+
+        /// <summary>
+        /// Refresh the drawing
+        /// </summary>
+        private void DrawSelectedLayer()
+        {
+            BaseLayer baseLayer = PDFViewer.GetViewForTag(selectedRectViewTag);
+            SelectedRect selectedRect = CommonHelper.FindVisualChild<SelectedRect>(baseLayer as CustomizeLayer);
+            if (selectedRect != null)
+            {
+                selectedRect.Draw();
+            }
+        }
+        
+        /// <summary>
+        /// Identify whether the mouse is on the rectangle
+        /// </summary>
+        /// <returns></returns>
+        private bool DrawSelectRectDownEvent()
+        {
+            BaseLayer baseLayer = PDFViewer.GetViewForTag(selectedRectViewTag);
+
+            SelectedRect selectedRect = CommonHelper.FindVisualChild<SelectedRect>(baseLayer as CustomizeLayer);
+            if (selectedRect != null)
+            {
+                if (selectedRect.GetHitControlIndex(Mouse.GetPosition(this)) != PointControlType.None)
+                {
+                    return true;
+                }
+            }
+            return false;
+        }
+    }
+}

Разница между файлами не показана из-за своего большого размера
+ 1523 - 0
Demo/Examples/ComPDFKit.Tool/CPDFViewerTool.TextEdit.cs


+ 875 - 0
Demo/Examples/ComPDFKit.Tool/CPDFViewerTool.WidgetTool.cs

@@ -0,0 +1,875 @@
+using ComPDFKit.PDFAnnotation;
+using ComPDFKit.PDFAnnotation.Form;
+using ComPDFKit.PDFDocument;
+using ComPDFKit.PDFDocument.Action;
+using ComPDFKit.PDFPage;
+using ComPDFKit.Tool.DrawTool;
+using ComPDFKit.Viewer.Layer;
+using ComPDFKitViewer;
+using ComPDFKitViewer.BaseObject;
+using ComPDFKitViewer.Widget;
+using ComPDFKitViewer.Layer;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Controls.Primitives;
+using System.Windows.Documents;
+using System.Windows.Media;
+using System.Xml.Linq;
+using static ComPDFKit.PDFAnnotation.CTextAttribute.CFontNameHelper;
+using ComPDFKit.Tool.UndoManger;
+using ComPDFKit.Tool.Help;
+using ComPDFKitViewer.Helper;
+
+namespace ComPDFKit.Tool
+{
+    public class WidgetClickArgs: EventArgs
+    {
+        public bool Handled { get; set; }
+        public BaseWidget Widget { get; set; }
+        public UIElement UI {  get; set; }
+    }
+    public partial class CPDFViewerTool
+    {
+        public static DependencyProperty PopupAttachDataProperty = DependencyProperty.Register("PopupAttachData", typeof(BaseAnnot), typeof(CPDFViewerTool));
+
+        public event EventHandler<WidgetClickArgs> WidgetActionHandler;
+
+        private CustomizeLayer formPopLayer=null;
+        // Inner default pop-up control
+        private bool isInternalPopup;
+
+        public bool ShowFormHitPop(BaseWidget hitForm)
+        {
+            List<C_WIDGET_TYPE> formTypeList = new List<C_WIDGET_TYPE>()
+            {
+                C_WIDGET_TYPE.WIDGET_TEXTFIELD,
+                C_WIDGET_TYPE.WIDGET_LISTBOX,
+                C_WIDGET_TYPE.WIDGET_COMBOBOX
+            };
+
+            if(hitForm != null && formTypeList.Contains(hitForm.GetFormType()))
+            {
+                UIElement newUI = null;
+              
+                switch(hitForm.GetFormType())
+                {
+                    case C_WIDGET_TYPE.WIDGET_TEXTFIELD:
+                        newUI=BuildPopTextUI(hitForm);
+                        break;
+                    case C_WIDGET_TYPE.WIDGET_LISTBOX:
+                        newUI=BuildPopListBoxUI(hitForm);
+                        break;
+                    case C_WIDGET_TYPE.WIDGET_COMBOBOX:
+                        newUI=BuildPopComboBoxUI(hitForm);
+                        break;
+                    default:
+                        break;
+                }
+                if (newUI!=null)
+                {
+                    ShowFormHitPop(newUI,hitForm);
+                    isInternalPopup = true;
+                }
+            }
+            return false;
+        }
+
+        public bool ShowFormHitPop(UIElement customui,BaseWidget hitForm)
+        {
+            if (customui!= null && hitForm!=null)
+            {
+                isInternalPopup = false;
+                customui.SetValue(PopupAttachDataProperty, hitForm);
+                HideWidgetHitPop();
+                if(formPopLayer==null)
+                {
+                    formPopLayer= new CustomizeLayer();
+                }
+                int selectedRectViewIndex = PDFViewer.GetMaxViewIndex();
+                formPopLayer.Children.Clear();
+                formPopLayer.Children.Add(customui);
+                formPopLayer.Arrange();
+                PDFViewer.InsertView(selectedRectViewIndex, formPopLayer);
+            }
+            return false;
+        }
+
+        /// <summary>
+        /// Remove Form pop-up control
+        /// </summary>
+        public void HideWidgetHitPop()
+        {
+            PDFViewer?.RemoveView(formPopLayer);
+        }
+
+        private string GetFontName(string pdfFontName)
+        {
+            string fontName;
+            switch (GetFontType(pdfFontName))
+            {
+                case FontType.Courier:
+                    fontName = "Courier New";
+                    break;
+                case FontType.Helvetica:
+                    fontName = "Arial";
+                    break;
+                case FontType.Times_Roman:
+                    fontName = "Times New Roman";
+                    break;
+                default:
+                    fontName = "Arial";
+                    break;
+            }
+            return fontName;
+        }
+
+        private void SetFormRotateTransform(FrameworkElement formui,AnnotData annotData)
+        {
+            RotateTransform rotateTrans = new RotateTransform();
+            rotateTrans.Angle = -90 * annotData.Rotation;
+            rotateTrans.CenterX = annotData.PaintRect.Width / 2;
+            rotateTrans.CenterY = annotData.PaintRect.Height / 2;
+            Rect rotateRect = rotateTrans.TransformBounds(annotData.PaintRect);
+
+            formui.Width = rotateRect.Width;
+            formui.Height = rotateRect.Height;
+            formui.SetValue(Canvas.LeftProperty, annotData.PaintRect.Left + rotateTrans.CenterX - rotateRect.Width / 2);
+            formui.SetValue(Canvas.TopProperty, annotData.PaintRect.Top + rotateTrans.CenterY - rotateRect.Height / 2);
+
+            rotateTrans.Angle = 90 * annotData.Rotation;
+            rotateTrans.CenterX = rotateRect.Width / 2;
+            rotateTrans.CenterY = rotateRect.Height / 2;
+            formui.RenderTransform = rotateTrans;
+        }
+
+        protected UIElement BuildPopTextUI(BaseWidget textForm)
+        {
+            try
+            {
+                if (textForm != null && textForm.GetFormType() == C_WIDGET_TYPE.WIDGET_TEXTFIELD)
+                {
+                    AnnotData annotData = textForm.GetAnnotData();
+                    CPDFTextWidget textWidget = annotData.Annot as CPDFTextWidget;
+                    if (textWidget == null)
+                    {
+                       return null;
+                    }
+
+                    TextBox textui = new TextBox();
+                    CTextAttribute textAttribute = textWidget.GetTextAttribute();
+                    byte transparency = textWidget.GetTransparency();
+                    textui.FontSize = textAttribute.FontSize* annotData.CurrentZoom/72D*96D;
+                    Color textColor = Color.FromArgb(
+                        transparency,
+                        textAttribute.FontColor[0],
+                        textAttribute.FontColor[1],
+                        textAttribute.FontColor[2]);
+
+                    Color borderColor = Colors.Transparent;
+                    Color backgroundColor = Colors.White;
+                    byte[] colorArray = new byte[3];
+                    if (textWidget.GetWidgetBorderRGBColor(ref colorArray))
+                    {
+                        borderColor = Color.FromRgb(colorArray[0], colorArray[1], colorArray[2]);
+                    }
+
+                    if (textWidget.GetWidgetBgRGBColor(ref colorArray))
+                    {
+                        backgroundColor = Color.FromRgb(colorArray[0], colorArray[1], colorArray[2]);
+                    }
+
+                    textui.Foreground = new SolidColorBrush(textColor);
+                    textui.BorderBrush = new SolidColorBrush(borderColor);
+                    textui.Background = new SolidColorBrush(backgroundColor);
+
+                    textui.BorderThickness = new Thickness(textWidget.GetBorderWidth()*annotData.CurrentZoom);
+                    textui.Text = textWidget.Text;
+
+                    textui.FontFamily = new FontFamily(GetFontName(textAttribute.FontName));
+                    textui.FontWeight = IsBold(textAttribute.FontName) ? FontWeights.Bold : FontWeights.Normal;
+                    textui.FontStyle = IsItalic(textAttribute.FontName) ? FontStyles.Italic : FontStyles.Normal;
+
+
+                    if (textWidget.IsMultiLine)
+                    {
+                        textui.AcceptsReturn = true;
+                        textui.TextWrapping = TextWrapping.Wrap;
+                    }
+                    else
+                    {
+                        textui.VerticalContentAlignment = VerticalAlignment.Center;
+                    }
+
+                    switch (textWidget.Alignment)
+                    {
+                        case C_TEXT_ALIGNMENT.ALIGNMENT_LEFT:
+                            textui.TextAlignment = TextAlignment.Left;
+                            break;
+                        case C_TEXT_ALIGNMENT.ALIGNMENT_RIGHT:
+                            textui.TextAlignment = TextAlignment.Right;
+                            break;
+                        case C_TEXT_ALIGNMENT.ALIGNMENT_CENTER:
+                            textui.TextAlignment = TextAlignment.Center;
+                            break;
+                        default:
+                            break;
+                    }
+
+                    SetFormRotateTransform(textui, annotData);
+                    textui.Loaded += (object sender, RoutedEventArgs e) =>
+                    {
+                        textui.Focus();
+                        textui.CaretIndex = textui.Text.Length;
+                    };
+                    CPDFViewer viewer = GetCPDFViewer();
+                    textui.LostFocus += (object sender, RoutedEventArgs e) =>
+                    {
+                        WidgetClickArgs eventparam = new WidgetClickArgs();
+                        BaseWidget currentForm = textui.GetValue(PopupAttachDataProperty) as BaseWidget;
+                        eventparam.Widget = currentForm;
+                        eventparam.UI = textui;
+                        WidgetActionHandler?.Invoke(this, eventparam);
+
+                        if (currentForm != null && eventparam.Handled == false)
+                        {
+                            AnnotData formData = currentForm.GetAnnotData();
+                            CPDFTextWidget updateWidget = formData.Annot as CPDFTextWidget;
+
+                            if (updateWidget!=null && updateWidget.Text!=textui.Text)
+                            {
+                                CPDFDocument doc= viewer.GetDocument();
+                                TextBoxHistory textHistory=new TextBoxHistory();
+                                textHistory.Action= HistoryAction.Update;
+                                textHistory.PDFDoc = doc;
+                                textHistory.PreviousParam = ParamConverter.CPDFDataConverterToAnnotParam(doc, formData.PageIndex, formData.Annot);
+                                updateWidget.SetText(textui.Text);
+                                textHistory.CurrentParam = ParamConverter.CPDFDataConverterToAnnotParam(doc, formData.PageIndex, formData.Annot);
+                                updateWidget.UpdateFormAp();
+                                viewer?.UpdateAnnotFrame();
+                                viewer?.UndoManager.AddHistory(textHistory);
+                            }
+                        }
+                    };
+
+                    return textui;
+                }
+            }
+            catch (Exception ex)
+            {
+
+            }
+            return null;
+        }
+
+        protected UIElement BuildPopListBoxUI(BaseWidget listboxForm)
+        {
+            try
+            {
+                if (listboxForm != null && listboxForm.GetFormType() == C_WIDGET_TYPE.WIDGET_LISTBOX)
+                {
+                    AnnotData annotData = listboxForm.GetAnnotData();
+                    ListBox listui = new ListBox();
+                    CPDFListBoxWidget listWidget = annotData.Annot as CPDFListBoxWidget;
+                    if (listWidget == null)
+                    {
+                        return null;
+                    }
+
+                    CTextAttribute textAttribute = listWidget.GetTextAttribute();
+                    byte transparency = listWidget.GetTransparency();
+                    listui.FontSize = textAttribute.FontSize * annotData.CurrentZoom;
+                    Color textColor = Color.FromArgb(
+                        transparency,
+                        textAttribute.FontColor[0],
+                        textAttribute.FontColor[1],
+                        textAttribute.FontColor[2]);
+
+                    Color borderColor = Colors.Transparent;
+                    Color backgroundColor = Colors.White;
+                    byte[] colorArray = new byte[3];
+                    if (listWidget.GetWidgetBorderRGBColor(ref colorArray))
+                    {
+                        borderColor = Color.FromRgb(colorArray[0], colorArray[1], colorArray[2]);
+                    }
+
+                    if (listWidget.GetWidgetBgRGBColor(ref colorArray))
+                    {
+                        backgroundColor = Color.FromRgb(colorArray[0], colorArray[1], colorArray[2]);
+                    }
+
+                    listui.Foreground = new SolidColorBrush(textColor);
+                    listui.BorderBrush = new SolidColorBrush(borderColor);
+                    listui.Background = new SolidColorBrush(backgroundColor);
+                    listui.BorderThickness = new Thickness(listWidget.GetBorderWidth() * annotData.CurrentZoom);
+                    listui.FontFamily = new FontFamily(GetFontName(textAttribute.FontName));
+                    listui.FontStyle = IsItalic(textAttribute.FontName) ? FontStyles.Italic : FontStyles.Normal;
+                    listui.FontWeight = IsBold(textAttribute.FontName) ? FontWeights.Bold : FontWeights.Normal;
+
+                    CWidgetItem selectItem = listWidget.GetSelectedItem();
+                    CWidgetItem[] listItems = listWidget.LoadWidgetItems();
+
+                    if (listItems != null)
+                    {
+                        foreach (CWidgetItem item in listItems)
+                        {
+                            ListBoxItem addItem = new ListBoxItem()
+                            {
+                                FontSize = listui.FontSize,
+                                Content = item.Text
+                            };
+                            listui.Items.Add(addItem);
+                            if (selectItem == null)
+                            {
+                                continue;
+                            }
+                            if (selectItem.Value == item.Value && selectItem.Text == item.Text)
+                            {
+                                listui.SelectedItem = addItem;
+                            }
+                        }
+                    }
+
+                    SetFormRotateTransform(listui, annotData);
+
+                    CPDFViewer viewer = GetCPDFViewer();
+                    listui.SelectionChanged += (object sender, SelectionChangedEventArgs e) =>
+                    {
+                        WidgetClickArgs eventparam = new WidgetClickArgs();
+                        BaseWidget currentForm = listui.GetValue(PopupAttachDataProperty) as BaseWidget;
+                        eventparam.Widget = currentForm;
+                        eventparam.UI = listui;
+                        WidgetActionHandler?.Invoke(this, eventparam);
+
+                        if (currentForm != null && eventparam.Handled == false)
+                        {
+                            AnnotData formData = currentForm.GetAnnotData();
+                            CPDFListBoxWidget updateWidget = formData.Annot as CPDFListBoxWidget;
+                            if (updateWidget != null)
+                            {
+                                int selectIndex = -1;
+                                if (listItems != null && listItems.Length > 0)
+                                {
+                                    for (int i = 0; i < listItems.Length; i++)
+                                    {
+                                        CWidgetItem item = listItems[i];
+                                     
+                                        if (selectItem != null && selectItem.Text == item.Text && selectItem.Value == item.Value)
+                                        {
+                                            selectIndex = i;
+                                            break;
+                                        }
+                                    }
+                                }
+
+                                if (selectIndex != listui.SelectedIndex)
+                                {
+                                    CPDFDocument doc = viewer.GetDocument();
+                                    ListBoxHistory listboxHistory = new ListBoxHistory();
+                                    listboxHistory.Action = ComPDFKitViewer.Helper.HistoryAction.Update;
+                                    listboxHistory.PDFDoc = doc;
+                                    listboxHistory.PreviousParam = ParamConverter.CPDFDataConverterToAnnotParam(doc, formData.PageIndex, formData.Annot);
+                                    updateWidget.SelectItem(listui.SelectedIndex);
+                                    listboxHistory.CurrentParam = ParamConverter.CPDFDataConverterToAnnotParam(doc, formData.PageIndex, formData.Annot);
+                                    updateWidget?.UpdateFormAp();
+                                    viewer?.UpdateAnnotFrame();
+                                    viewer?.UndoManager.AddHistory(listboxHistory);
+                                }
+                            }
+                        }
+                    };
+
+                    return listui;
+                }
+            }
+            catch (Exception ex)
+            {
+
+            }
+           
+            return null;
+        }
+
+        protected UIElement BuildPopComboBoxUI(BaseWidget comboboxForm)
+        {
+            try
+            {
+                if (comboboxForm != null && comboboxForm.GetFormType() == C_WIDGET_TYPE.WIDGET_COMBOBOX)
+                {
+                    AnnotData annotData = comboboxForm.GetAnnotData();
+                    ComboBox comboboxui=new ComboBox();
+                    CPDFComboBoxWidget comboboxWidget=annotData.Annot as CPDFComboBoxWidget;
+                    if(comboboxWidget==null)
+                    {
+                        return null;
+                    }
+
+                    CWidgetItem[] comboboxItems = comboboxWidget.LoadWidgetItems();
+                    CWidgetItem selectItem= comboboxWidget.GetSelectedItem();
+                    CTextAttribute textAttribute = comboboxWidget.GetTextAttribute();
+                    byte transparency = comboboxWidget.GetTransparency();
+                    comboboxui.FontSize = textAttribute.FontSize*annotData.CurrentZoom/72D*96D;
+                    Color textColor = Color.FromArgb(
+                        transparency,
+                        textAttribute.FontColor[0],
+                        textAttribute.FontColor[1],
+                        textAttribute.FontColor[2]);
+
+                    Color borderColor = Colors.Transparent;
+                    Color backgroundColor = Colors.White;
+                    byte[] colorArray = new byte[3];
+                    if (comboboxWidget.GetWidgetBorderRGBColor(ref colorArray))
+                    {
+                        borderColor = Color.FromRgb(colorArray[0], colorArray[1], colorArray[2]);
+                    }
+
+                    if (comboboxWidget.GetWidgetBgRGBColor(ref colorArray))
+                    {
+                        backgroundColor = Color.FromRgb(colorArray[0], colorArray[1], colorArray[2]);
+                    }
+
+                    if (comboboxItems != null)
+                    {
+                        foreach (CWidgetItem item in comboboxItems)
+                        {
+                            ComboBoxItem comboItem = new ComboBoxItem();
+                            comboItem.FontSize = comboboxui.FontSize;
+                            comboItem.Content = item.Text;
+                            comboboxui.Items.Add(comboItem);
+
+                            if (selectItem == null)
+                            {
+                                continue;
+                            }
+                            if (selectItem.Value == item.Value && selectItem.Text == item.Text)
+                            {
+                                comboboxui.SelectedItem = comboItem;
+                            }
+                        }
+                    }
+
+                    comboboxui.Text = comboboxWidget.GetSelectedItem().Text;
+                    comboboxui.Foreground = new SolidColorBrush(textColor);
+                    comboboxui.BorderBrush = new SolidColorBrush(borderColor);
+                    comboboxui.Background = new SolidColorBrush(backgroundColor);
+                    comboboxui.BorderThickness = new Thickness(comboboxWidget.GetBorderWidth()*annotData.CurrentZoom);
+                    comboboxui.FontFamily = new FontFamily(GetFontName(textAttribute.FontName));
+                    comboboxui.FontStyle = IsItalic(textAttribute.FontName) ? FontStyles.Italic : FontStyles.Normal;
+                    comboboxui.FontWeight = IsBold(textAttribute.FontName) ? FontWeights.Bold : FontWeights.Normal;
+                    SetFormRotateTransform(comboboxui, annotData);
+
+                    CPDFViewer viewer = GetCPDFViewer();
+                    comboboxui.SelectionChanged += (object sender, SelectionChangedEventArgs e) =>
+                    {
+                        WidgetClickArgs eventparam = new WidgetClickArgs();
+                        BaseWidget currentForm= comboboxui.GetValue(PopupAttachDataProperty) as BaseWidget;
+                        eventparam.Widget = currentForm;
+                        eventparam.UI = comboboxui;
+                        WidgetActionHandler?.Invoke(this, eventparam);
+
+                        if (currentForm != null && eventparam.Handled == false)
+                        {
+                            AnnotData formData = currentForm.GetAnnotData();
+                            CPDFComboBoxWidget updateWidget = formData.Annot as CPDFComboBoxWidget;
+                            if (updateWidget != null)
+                            {
+                                int selectIndex = -1;
+                                if (comboboxItems != null && comboboxItems.Length > 0)
+                                {
+                                    for (int i = 0; i < comboboxItems.Length; i++)
+                                    {
+                                        CWidgetItem item = comboboxItems[i];
+
+                                        if (selectItem != null && selectItem.Text == item.Text && selectItem.Value == item.Value)
+                                        {
+                                            selectIndex = i;
+                                            break;
+                                        }
+                                    }
+                                }
+
+                                if (selectIndex != comboboxui.SelectedIndex)
+                                {
+                                    CPDFDocument doc = viewer.GetDocument();
+                                    ComboBoxHistory comboboxHistory = new ComboBoxHistory();
+                                    comboboxHistory.Action = ComPDFKitViewer.Helper.HistoryAction.Update;
+                                    comboboxHistory.PDFDoc = doc;
+                                    comboboxHistory.PreviousParam = ParamConverter.CPDFDataConverterToAnnotParam(doc, formData.PageIndex, formData.Annot);
+                                    updateWidget.SelectItem(comboboxui.SelectedIndex);
+                                    comboboxHistory.CurrentParam = ParamConverter.CPDFDataConverterToAnnotParam(doc, formData.PageIndex, formData.Annot);
+                                    updateWidget?.UpdateFormAp();
+                                    viewer?.UpdateAnnotFrame();
+                                    viewer?.UndoManager.AddHistory(comboboxHistory);
+                                }
+                            }
+                        }
+                    };
+
+                    return comboboxui;
+                }
+            }
+            catch(Exception ex)
+            {
+
+            }
+            return null;
+        }
+
+        private void UpdateTextUI(TextBox textui,BaseWidget hitform)
+        {
+            if(textui!=null && hitform!=null)
+            {
+                AnnotData annotData = hitform.GetAnnotData();
+                CPDFTextWidget textWidget = annotData.Annot as CPDFTextWidget;
+                if (textWidget == null)
+                {
+                    return;
+                }
+                CTextAttribute textAttribute = textWidget.GetTextAttribute();
+                textui.FontSize = textAttribute.FontSize * annotData.CurrentZoom;
+                SetFormRotateTransform(textui, annotData);
+                textui.SetValue(PopupAttachDataProperty, hitform);
+                formPopLayer?.Arrange();
+            }
+        }
+
+        private void UpdateListBoxUI(ListBox listboxui,BaseWidget hitform)
+        {
+            if(listboxui!=null &&hitform!=null)
+            {
+                AnnotData annotData = hitform.GetAnnotData();
+                CPDFListBoxWidget listWidget=annotData.Annot as CPDFListBoxWidget;
+                if(listWidget == null)
+                {
+                    return;
+                }
+
+                CTextAttribute textAttribute = listWidget.GetTextAttribute();
+                listboxui.FontSize = textAttribute.FontSize * annotData.CurrentZoom;
+                listboxui.BorderThickness = new Thickness(listWidget.GetBorderWidth() * annotData.CurrentZoom);
+                foreach(ListBoxItem item in listboxui.Items)
+                {
+                    item.FontSize = listboxui.FontSize;
+                }
+                SetFormRotateTransform(listboxui, annotData);
+                listboxui.SetValue(PopupAttachDataProperty, hitform);
+                formPopLayer?.Arrange();
+            }
+        }
+
+        private void UpdateComboboxUI(ComboBox comboboxui,BaseWidget hitform)
+        {
+            if (comboboxui != null && hitform != null)
+            {
+                AnnotData annotData = hitform.GetAnnotData();
+                CPDFComboBoxWidget comboboxWidget = annotData.Annot as CPDFComboBoxWidget;
+                if (comboboxWidget == null)
+                {
+                    return;
+                }
+
+                CTextAttribute textAttribute = comboboxWidget.GetTextAttribute();
+                comboboxui.FontSize = textAttribute.FontSize * annotData.CurrentZoom;
+                comboboxui.BorderThickness = new Thickness(comboboxWidget.GetBorderWidth() * annotData.CurrentZoom);
+               
+                foreach (ComboBoxItem item in comboboxui.Items)
+                {
+                    item.FontSize = comboboxui.FontSize;
+                }
+                SetFormRotateTransform(comboboxui, annotData);
+                comboboxui.SetValue(PopupAttachDataProperty, hitform);
+                formPopLayer?.Arrange();
+            }
+        }
+
+        protected void UpdateFormHitPop()
+        {
+            if(formPopLayer==null || formPopLayer.Children.Count==0 || !isInternalPopup)
+            {
+                return;
+            }
+            FrameworkElement popui = formPopLayer.Children[0] as FrameworkElement;
+            if(popui==null)
+            {
+                return;
+            }
+            try
+            {
+                BaseWidget hitForm = popui.GetValue(PopupAttachDataProperty) as BaseWidget;
+                if(hitForm==null)
+                {
+                    return;
+                }
+                AnnotLayer annotLayer = PDFViewer.GetViewForTag(PDFViewer.GetAnnotViewTag()) as AnnotLayer;
+                BaseAnnot baseAnnot = hitForm;
+                annotLayer.GetUpdate(ref baseAnnot);
+                hitForm = baseAnnot as BaseWidget;
+
+                if (hitForm.GetFormType()==C_WIDGET_TYPE.WIDGET_TEXTFIELD && (popui is TextBox))
+                {
+                    UpdateTextUI((TextBox)popui, hitForm);
+                }
+
+                if (hitForm.GetFormType() == C_WIDGET_TYPE.WIDGET_LISTBOX && (popui is ListBox))
+                {
+                    UpdateListBoxUI((ListBox)popui,hitForm);
+                }
+
+                if (hitForm.GetFormType() == C_WIDGET_TYPE.WIDGET_COMBOBOX && (popui is ComboBox))
+                {
+                    UpdateComboboxUI((ComboBox)popui, hitForm);
+                }
+            }
+            catch(Exception ex)
+            {
+
+            }
+        }
+
+        protected void FormClickProcess()
+        {
+            BaseWidget currentForm = PDFViewer?.AnnotHitTest() as BaseWidget;
+            if (currentForm != null)
+            {
+                List<C_WIDGET_TYPE> formTypeList = new List<C_WIDGET_TYPE>()
+                {
+                    C_WIDGET_TYPE.WIDGET_CHECKBOX,
+                    C_WIDGET_TYPE.WIDGET_RADIOBUTTON,
+                    C_WIDGET_TYPE.WIDGET_PUSHBUTTON
+                };
+
+                if(formTypeList.Contains(currentForm.GetFormType()))
+                {
+                    WidgetClickArgs eventparam = new WidgetClickArgs();
+                    eventparam.Widget = currentForm;
+                    WidgetActionHandler?.Invoke(this, eventparam);
+                    if (eventparam.Handled == false)
+                    {
+                        switch (currentForm.GetFormType())
+                        {
+                            case C_WIDGET_TYPE.WIDGET_RADIOBUTTON:
+                                FormRadioButtonClick(currentForm);
+                                break;
+                            case C_WIDGET_TYPE.WIDGET_CHECKBOX:
+                                FormCheckBoxClick(currentForm);
+                                break;
+                            case C_WIDGET_TYPE.WIDGET_PUSHBUTTON:
+                                FormPushButtonClick(currentForm);
+                                break;
+                            default:
+                                break;
+                        }
+                    }
+                }
+            }
+        }
+
+        private void FormRadioButtonClick(BaseWidget clickForm)
+        {
+            if(clickForm!=null && clickForm.GetFormType()==C_WIDGET_TYPE.WIDGET_RADIOBUTTON)
+            {
+                AnnotData formData = clickForm.GetAnnotData();
+                CPDFRadioButtonWidget updateWidget = formData.Annot as CPDFRadioButtonWidget;
+                CPDFViewer viewer = GetCPDFViewer();
+                CPDFDocument doc = viewer?.GetDocument();
+                if (viewer != null && doc != null)
+                {
+                    if (updateWidget.IsChecked() == false)
+                    {
+                        GroupHistory historyGroup=new GroupHistory();
+
+                        for (int i = 0; i < doc.PageCount; i++)
+                        {
+                            SetFormButtonChecked(i, doc, updateWidget.GetFieldName(), false, historyGroup);
+                        }
+                        RadioButtonHistory history=new RadioButtonHistory();
+                        history.PreviousParam = ParamConverter.CPDFDataConverterToAnnotParam(doc, updateWidget.Page.PageIndex, updateWidget);
+                        history.PDFDoc= doc;
+                        history.Action=HistoryAction.Update;
+                        updateWidget.SetChecked(true);
+                        updateWidget.UpdateFormAp();
+                        viewer.UpdateAnnotFrame();
+                        history.CurrentParam = ParamConverter.CPDFDataConverterToAnnotParam(doc, updateWidget.Page.PageIndex, updateWidget);
+                        historyGroup?.Histories.Add(history);
+                        viewer?.UndoManager?.AddHistory(historyGroup);
+                    }
+                }
+            }
+        }
+
+        private void FormCheckBoxClick(BaseWidget clickForm) 
+        {
+            if (clickForm != null && clickForm.GetFormType() == C_WIDGET_TYPE.WIDGET_CHECKBOX)
+            {
+                AnnotData formData = clickForm.GetAnnotData();
+                CPDFCheckBoxWidget updateWidget = formData.Annot as CPDFCheckBoxWidget;
+                bool isCheck = !updateWidget.IsChecked();
+                CPDFViewer viewer = GetCPDFViewer();
+                CPDFDocument doc = viewer?.GetDocument();
+                if (viewer != null && doc != null)
+                {
+                    GroupHistory historyGroup = new GroupHistory();
+
+                    for (int i = 0; i < doc.PageCount; i++)
+                    {
+                        SetFormButtonChecked(i, doc, updateWidget.GetFieldName(), isCheck, historyGroup);
+                    }
+                    if(historyGroup.Histories.Count > 0)
+                    {
+                        viewer?.UndoManager?.AddHistory(historyGroup);
+                    }
+                    updateWidget.UpdateFormAp();
+                    viewer.UpdateAnnotFrame();
+                }
+            }
+        }
+
+        private void FormPushButtonClick(BaseWidget clickForm)
+        {
+            if (clickForm != null && clickForm.GetFormType() == C_WIDGET_TYPE.WIDGET_PUSHBUTTON)
+            {
+                AnnotData formData = clickForm.GetAnnotData();
+                CPDFPushButtonWidget updateWidget = formData.Annot as CPDFPushButtonWidget;
+                CPDFViewer viewer = GetCPDFViewer();
+                CPDFDocument doc = viewer?.GetDocument();
+                if (viewer != null && doc != null)
+                {
+                    CPDFAction action = updateWidget.GetButtonAction();
+                    PDFActionHandler(action, doc, viewer);
+                }
+            }
+        }
+
+        internal void SetFormButtonChecked(int pageIndex, CPDFDocument currentDoc, string fieldName, bool isCheck,GroupHistory historyGroup)
+        {
+            if (pageIndex < 0 || currentDoc==null || pageIndex>=currentDoc.PageCount)
+            {
+                return;
+            }
+            CPDFPage docPage = currentDoc.PageAtIndex(pageIndex, false);
+            if (docPage == null)
+            {
+                return;
+            }
+            List<CPDFAnnotation> docAnnots = docPage.GetAnnotations();
+
+            if (docAnnots != null && docAnnots.Count > 0)
+            {
+                foreach (CPDFAnnotation annotCore in docAnnots)
+                {
+                    if (annotCore == null || annotCore.Type != C_ANNOTATION_TYPE.C_ANNOTATION_WIDGET)
+                    {
+                        continue;
+                    }
+                    CPDFWidget widget = (CPDFWidget)annotCore;
+                    if (widget != null)
+                    {
+                        if (widget.WidgetType == C_WIDGET_TYPE.WIDGET_RADIOBUTTON && widget.GetFieldName() == fieldName)
+                        {
+                            RadioButtonHistory history = new RadioButtonHistory();
+                            history.PDFDoc = currentDoc;
+                            history.Action = HistoryAction.Update;
+                            history.PreviousParam = ParamConverter.CPDFDataConverterToAnnotParam(currentDoc, pageIndex, widget);
+                            widget.ResetForm();
+                            history.CurrentParam = ParamConverter.CPDFDataConverterToAnnotParam(currentDoc, pageIndex, widget);
+                            historyGroup?.Histories.Add(history);
+                        }
+
+                        if (widget.WidgetType == C_WIDGET_TYPE.WIDGET_CHECKBOX && widget.GetFieldName() == fieldName)
+                        {
+                            CheckBoxHistory history = new CheckBoxHistory();
+                            history.PDFDoc = currentDoc;
+                            history.Action = HistoryAction.Update;
+                            history.PreviousParam = ParamConverter.CPDFDataConverterToAnnotParam(currentDoc, pageIndex, widget);
+                            (widget as CPDFCheckBoxWidget)?.SetChecked(isCheck);
+                            widget.UpdateFormAp();
+                            history.CurrentParam = ParamConverter.CPDFDataConverterToAnnotParam(currentDoc, pageIndex, widget);
+                            historyGroup?.Histories.Add(history);
+                        }
+                    }
+                }
+            }
+        }
+
+        internal void PDFActionHandler(CPDFAction action,CPDFDocument currentDoc,CPDFViewer viewer)
+        {
+            if (action != null && currentDoc!=null)
+            {
+                try
+                {
+                    switch (action.ActionType)
+                    {
+                        case C_ACTION_TYPE.ACTION_TYPE_NAMED:
+                            {
+                                CPDFNamedAction namedAction = action as CPDFNamedAction;
+                                string name = namedAction.GetName();
+                                int pageIndex = -1;
+                                if (name.ToLower() == "firstpage")
+                                {
+                                    pageIndex = 0;
+                                }
+                                if (name.ToLower() == "lastpage")
+                                {
+                                    pageIndex = currentDoc.PageCount - 1;
+                                }
+                                if (name.ToLower() == "nextpage" && viewer.CurrentRenderFrame != null)
+                                {
+                                    pageIndex = Math.Min(viewer.CurrentRenderFrame.PageIndex + 1, currentDoc.PageCount - 1);
+                                }
+                                if (name.ToLower() == "prevpage" && viewer.CurrentRenderFrame != null)
+                                {
+                                    pageIndex = Math.Max(viewer.CurrentRenderFrame.PageIndex - 1, 0);
+                                }
+                                if (pageIndex != -1)
+                                {
+                                    viewer.GoToPage(pageIndex, new Point(0, 0));
+                                }
+                            }
+                            break;
+                        case C_ACTION_TYPE.ACTION_TYPE_GOTO:
+                            {
+                                CPDFGoToAction gotoAction = action as CPDFGoToAction;
+                                CPDFDestination dest = gotoAction.GetDestination(currentDoc);
+                                if (dest != null)
+                                {
+                                    viewer.GoToPage(dest.PageIndex, new Point(0, 0));
+                                }
+                            }
+                            break;
+                        case C_ACTION_TYPE.ACTION_TYPE_GOTOR:
+                            {
+                                CPDFGoToRAction gotorAction = action as CPDFGoToRAction;
+                                CPDFDestination dest = gotorAction.GetDestination(currentDoc);
+                                if (dest != null)
+                                {
+                                    viewer.GoToPage(dest.PageIndex, new Point(0, 0));
+                                }
+                            }
+                            break;
+                        case C_ACTION_TYPE.ACTION_TYPE_URI:
+                            {
+                                CPDFUriAction uriAction = action as CPDFUriAction;
+                                string uri = uriAction.GetUri();
+                                if (!string.IsNullOrEmpty(uri))
+                                {
+                                    Process.Start(uri);
+                                }
+                            }
+                            break;
+                        default:
+                            break;
+                    }
+                }
+                catch(Exception ex)
+                {
+
+                }
+            }
+        }
+    }
+}

+ 20 - 0
Demo/Examples/ComPDFKit.Tool/CPDFViewerTool.xaml

@@ -0,0 +1,20 @@
+<UserControl x:Class="ComPDFKit.Tool.CPDFViewerTool"
+             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
+             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
+             xmlns:local="clr-namespace:ComPDFKit.Tool" 
+             xmlns:compdfkitviewer="clr-namespace:ComPDFKitViewer;assembly=ComPDFKit.Viewer"
+             mc:Ignorable="d" 
+             Focusable="True"
+             Loaded="CPDFViewerTool_Loaded"
+             Unloaded="CPDFViewerTool_Unloaded"
+             >
+    <ScrollViewer Focusable="False" CanContentScroll="True"
+                  HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"
+                  MouseUp="ScrollViewer_MouseUp" MouseDown="ScrollViewer_MouseDown">
+        <compdfkitviewer:CPDFViewer x:Name="PDFViewer">
+
+        </compdfkitviewer:CPDFViewer>
+    </ScrollViewer>
+</UserControl>

+ 970 - 0
Demo/Examples/ComPDFKit.Tool/CPDFViewerTool.xaml.cs

@@ -0,0 +1,970 @@
+using ComPDFKit.PDFAnnotation;
+using ComPDFKit.PDFDocument;
+using ComPDFKit.PDFDocument.Action;
+using ComPDFKit.PDFPage;
+using ComPDFKit.Tool.DrawTool;
+using ComPDFKit.Tool.SettingParam;
+using ComPDFKit.Viewer.Helper;
+using ComPDFKitViewer;
+using ComPDFKitViewer.Annot;
+using ComPDFKitViewer.BaseObject;
+using ComPDFKitViewer.Helper;
+using ComPDFKitViewer.Layer;
+using ComPDFKitViewer.Widget;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Input;
+using System.Windows.Media;
+using UndoAction = ComPDFKitViewer.Helper.UndoAction;
+
+namespace ComPDFKit.Tool
+{
+    public struct MouseEventObject
+    {
+        public MouseEventArgs mouseButtonEventArgs;
+        public MouseHitTestType hitTestType;
+        public C_ANNOTATION_TYPE annotType;
+        /// <summary>
+        /// Identifies whether the object is created
+        /// </summary>
+        public bool IsCreate;
+        public bool IsDrawing;
+        public bool IsMersured;
+        public object Data;
+    }
+
+    public enum MouseHitTestType
+    {
+        Unknown,
+        Text,
+        Annot,
+        SelectRect,
+        Widget,
+        TextEdit,
+        ImageEdit,
+        ImageSelect,
+        MultiTextEdit,
+        SelectedPageRect,
+    }
+
+    public enum ToolType
+    {
+        None = -1,
+        Viewer,
+        Pan,
+        CreateAnnot,
+        WidgetEdit,
+        ContentEdit,
+        Customize,
+        SelectedPage,
+    }
+
+    public partial class CPDFViewerTool : UserControl
+    {
+        public bool IsDocumentModified
+        {
+            get => isDocumentModified;
+            set
+            {
+                isDocumentModified = value;
+                DocumentModifiedChanged?.Invoke(this, EventArgs.Empty);
+            }
+        }
+
+        public CPDFViewerTool()
+        {
+            InitializeComponent();
+            BindCommand();
+            Application.Current.Exit += Current_Exit;
+            InsertSelectImageView();
+            InsertAnnotView();
+            InsertAnnotEditView();
+            InsertWidgetView();
+            InsertSelectedRectView();
+            InsertMultiSelectedRectView();
+            InsertCustomizeToolView();
+            InsertSelectTextView();
+            InsertTextEditView();
+            InsertPageSelectedRectView();
+        }
+
+        private void Current_Exit(object sender, ExitEventArgs e)
+        {
+            GetCPDFViewer().Dispose();
+        }
+
+        protected override Visual GetVisualChild(int index)
+        {
+            return base.GetVisualChild(index);
+        }
+
+        protected override int VisualChildrenCount => base.VisualChildrenCount;
+
+        public event EventHandler<MouseEventObject> MouseLeftButtonDownHandler;
+        public event EventHandler<MouseEventObject> MouseLeftButtonUpHandler;
+        public event EventHandler<MouseEventObject> MouseMoveHandler;
+        public event EventHandler<MouseEventObject> MouseRightButtonDownHandler;
+        public event EventHandler DrawChanged;
+        public event EventHandler DocumentModifiedChanged;
+
+        private ToolType currentModel = ToolType.Viewer;
+
+        DefaultSettingParam defaultSettingParam = new DefaultSettingParam();
+        DefaultDrawParam defaultDrawParam = new DefaultDrawParam();
+        MeasureSetting measureSetting = new MeasureSetting();
+
+        Point Point = new Point();
+        Point CachePoint = new Point();
+
+        private bool isMultiSelected;
+        private bool isDocumentModified = false;
+
+        public void SetIsMultiSelected(bool isMulti)
+        {
+            isMultiSelected = isMulti;
+        }
+
+        public DefaultSettingParam GetDefaultSettingParam()
+        {
+            return defaultSettingParam;
+        }
+
+        public DefaultDrawParam GetDefaultDrawParam()
+        {
+            return defaultDrawParam;
+        }
+
+        public MeasureSetting GetMeasureSetting()
+        {
+            return measureSetting;
+        }
+
+        public bool IsSelectRectMousePoint()
+        {
+            if (DrawSelectRectDownEvent() && cacheHitTestAnnot != null)
+            {
+                return true;
+            }
+            return false;
+        }
+
+        private void LinkAnnotAction(BaseAnnot annot)
+        {
+            AnnotData data = annot.GetAnnotData();
+            CPDFLinkAnnotation linkAnnot = data.Annot as CPDFLinkAnnotation;
+            CPDFAction action = linkAnnot.GetLinkAction();
+            if (action != null)
+            {
+                ActionProcess(action);
+            }
+            else
+            {
+                CPDFDestination dest = linkAnnot.GetDestination(PDFViewer.GetDocument());
+                if (dest != null)
+                {
+                    CPDFGoToAction gotoAction = new CPDFGoToAction();
+                    gotoAction.SetDestination(PDFViewer.GetDocument(), dest);
+                    ActionProcess(gotoAction);
+                }
+            }
+        }
+
+        public void ActionProcess(CPDFAction action)
+        {
+            if (action == null)
+            {
+                return;
+            }
+
+            switch (action.ActionType)
+            {
+                case C_ACTION_TYPE.ACTION_TYPE_NAMED:
+                    {
+                        CPDFNamedAction namedAction = action as CPDFNamedAction;
+                        string namedStr = namedAction.GetName();
+                        switch (namedStr)
+                        {
+                            case "FirstPage":
+                                {
+                                    PDFViewer?.GoToPage(0, new Point(0, 0));
+                                    break;
+                                }
+                            case "LastPage":
+                                {
+                                    PDFViewer?.GoToPage(PDFViewer.GetDocument().PageCount - 1, new Point(0, 0));
+                                    break;
+                                }
+                            case "NextPage":
+                                if (PDFViewer != null)
+                                {
+                                    int nextIndex = PDFViewer.CurrentRenderFrame.PageIndex + 1;
+                                    if (nextIndex < PDFViewer.GetDocument().PageCount)
+                                    {
+                                        PDFViewer.GoToPage(nextIndex, new Point(0, 0));
+                                    }
+                                }
+                                break;
+                            case "PrevPage":
+                                if (PDFViewer != null)
+                                {
+                                    int prevIndex = PDFViewer.CurrentRenderFrame.PageIndex - 1;
+                                    if (prevIndex >= 0)
+                                    {
+                                        PDFViewer.GoToPage(prevIndex, new Point(0, 0));
+                                    }
+                                }
+                                break;
+                            default:
+                                break;
+                        }
+                        break;
+                    }
+
+                case C_ACTION_TYPE.ACTION_TYPE_GOTO:
+                    if (PDFViewer != null)
+                    {
+                        CPDFGoToAction gotoAction = action as CPDFGoToAction;
+                        CPDFDestination dest = gotoAction.GetDestination(PDFViewer.GetDocument());
+                        if (dest != null)
+                        {
+                            Size pageSize = DataConversionForWPF.CSizeConversionForSize(PDFViewer.GetDocument().GetPageSize(dest.PageIndex));
+                            PDFViewer.GoToPage(dest.PageIndex, new Point(dest.Position_X, pageSize.Height - dest.Position_Y));
+                        }
+                    }
+                    break;
+
+                case C_ACTION_TYPE.ACTION_TYPE_GOTOR:
+                    if (PDFViewer != null)
+                    {
+                        CPDFGoToRAction gotorAction = action as CPDFGoToRAction;
+                        CPDFDestination dest = gotorAction.GetDestination(PDFViewer.GetDocument());
+                        if (dest != null)
+                        {
+                            Size pageSize = DataConversionForWPF.CSizeConversionForSize(PDFViewer.GetDocument().GetPageSize(dest.PageIndex));
+                            PDFViewer.GoToPage(dest.PageIndex, new Point(dest.Position_X, pageSize.Height - dest.Position_Y));
+                        }
+                    }
+                    break;
+
+                case C_ACTION_TYPE.ACTION_TYPE_URI:
+                    {
+                        CPDFUriAction uriAction = action as CPDFUriAction;
+                        string uri = uriAction.GetUri();
+                        try
+                        {
+                            if (!string.IsNullOrEmpty(uri))
+                            {
+                                Process.Start(uri);
+                            }
+                        }
+                        catch (Exception ex)
+                        {
+
+                        }
+                    }
+                    break;
+
+                default:
+                    break;
+            }
+        }
+
+        protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
+        {
+            if (PDFViewer == null || PDFViewer.CurrentRenderFrame == null)
+            {
+                return;
+            }
+            if (!HitTestBorder())
+            {
+                RemovePopTextUI();
+            }
+            Focus();
+            Mouse.Capture(this, CaptureMode.SubTree);
+            MouseEventObject mouseEventObject = new MouseEventObject
+            {
+                mouseButtonEventArgs = e,
+                hitTestType = MouseHitTestType.Unknown,
+                annotType = C_ANNOTATION_TYPE.C_ANNOTATION_NONE,
+                IsCreate = false
+            };
+            if (isDrawSelectRect)
+            {
+                if (e.ClickCount == 2)
+                {
+                    // Refresh the currently selected annotation object to ensure it is the latest
+                    AnnotHitTest();
+                    if (cacheHitTestAnnot is FreeTextAnnot)
+                    {
+                        BuildPopTextUI(cacheHitTestAnnot);
+                        isDrawSelectRect = false;
+                        mouseEventObject.annotType = cacheHitTestAnnot.GetAnnotData().AnnotType;
+                        mouseEventObject.IsMersured = cacheHitTestAnnot.GetAnnotData().Annot.IsMersured();
+                        MouseLeftButtonDownHandler?.Invoke(this, mouseEventObject);
+                        return;
+                    }
+                    if (cacheHitTestAnnot is StickyNoteAnnot)
+                    {
+                        (cacheHitTestAnnot as StickyNoteAnnot).PopStickyNote();
+                    }
+                }
+                // Click inside the selected rectangle area
+                if (DrawSelectRectDownEvent() && cacheHitTestAnnot != null)
+                {
+                    mouseEventObject.hitTestType = MouseHitTestType.SelectRect;
+                    mouseEventObject.annotType = cacheHitTestAnnot.GetAnnotData().AnnotType;
+                    mouseEventObject.IsMersured = cacheHitTestAnnot.GetAnnotData().Annot.IsMersured();
+                    MouseLeftButtonDownHandler?.Invoke(this, mouseEventObject);
+                    return;
+                }
+                else
+                {
+                    CleanSelectedRect();
+                }
+            }
+
+            if (IsDrawEditAnnot)
+            {
+                // Click inside the selected rectangle area
+                if (DrawEditAnnotDownEvent() && cacheHitTestAnnot != null)
+                {
+                    mouseEventObject.hitTestType = MouseHitTestType.SelectRect;
+                    mouseEventObject.annotType = cacheHitTestAnnot.GetAnnotData().AnnotType;
+                    mouseEventObject.IsMersured = cacheHitTestAnnot.GetAnnotData().Annot.IsMersured();
+                    MouseLeftButtonDownHandler?.Invoke(this, mouseEventObject);
+                    return;
+                }
+            }
+
+            Point = e.GetPosition(this);
+
+            // Annotation selection effect
+            if ((currentModel == ToolType.Pan
+                || currentModel == ToolType.CreateAnnot)
+                && AnnotHitTest()
+                && IsCanSave()
+                && !PDFViewer.GetIsShowStampMouse()
+                )
+            {
+                //if (!IsCacheRedaction)
+                {
+                    if (cacheHitTestAnnot?.CurrentType == C_ANNOTATION_TYPE.C_ANNOTATION_LINK && currentModel != ToolType.CreateAnnot)
+                    {
+                        LinkAnnotAction(cacheHitTestAnnot);
+                    }
+                    else
+                    {
+                        List<C_ANNOTATION_TYPE> list = new List<C_ANNOTATION_TYPE>()
+                        {
+                            C_ANNOTATION_TYPE.C_ANNOTATION_LINE,
+                            C_ANNOTATION_TYPE.C_ANNOTATION_POLYLINE,
+                            C_ANNOTATION_TYPE.C_ANNOTATION_POLYGON,
+                        };
+                        if (cacheHitTestAnnot != null && list.Contains(cacheHitTestAnnot.CurrentType))
+                        {
+                            mouseEventObject.IsMersured = cacheHitTestAnnot.GetAnnotData().Annot.IsMersured();
+                            SetEditAnnotObject();
+                        }
+                        else
+                        {
+                            SelectedAnnot();
+                            CleanDrawSelectImage();
+                        }
+                        isDrawSelectRect = true;
+                    }
+                }
+                mouseEventObject.hitTestType = MouseHitTestType.Annot;
+                if (cacheHitTestAnnot != null)
+                {
+                    mouseEventObject.annotType = cacheHitTestAnnot.GetAnnotData().AnnotType;
+                }
+                else
+                {
+                    mouseEventObject.annotType = C_ANNOTATION_TYPE.C_ANNOTATION_NONE;
+                }
+            }
+
+            // Form selected effect
+            else if ((currentModel == ToolType.Pan || currentModel == ToolType.Viewer) && AnnotWidgetHitTest())
+            {
+                mouseEventObject.hitTestType = MouseHitTestType.Widget;
+                mouseEventObject.annotType = cacheMoveWidget.GetAnnotData().AnnotType;
+                FormClickProcess();
+            }
+            else if ((currentModel == ToolType.Pan || currentModel == ToolType.Viewer))
+            {
+                if (DrawDownSelectImage(true))
+                {
+                    mouseEventObject.hitTestType = MouseHitTestType.ImageSelect;
+                }
+                else
+                {
+                    ReDrawSelectImage();
+                }
+            }
+
+            // Form creation mode
+            else if (currentModel == ToolType.WidgetEdit)
+            {
+                if (AnnotWidgetHitTest())
+                {
+                    cacheHitTestAnnot = PDFViewer?.AnnotHitTest() as BaseWidget;
+                    SelectedAnnot();
+                    mouseEventObject.hitTestType = MouseHitTestType.Annot;
+
+                    mouseEventObject.annotType = cacheMoveWidget.GetAnnotData().AnnotType;
+                }
+            }
+            // Content editing mode
+            else if (currentModel == ToolType.ContentEdit)
+            {
+                OpenSelectedMulti(isMultiSelected);
+
+                if (!PDFViewer.GetIsShowStampMouse())
+                {
+                    DrawTextEditDownEvent(true);
+                }
+
+                if (lastSelectedRect != null)
+                {
+                    SelectedMultiRect(lastSelectedRect.GetRect(), lastSelectedRect.GetMaxRect(), SelectedType.PDFEdit);
+                    HideDrawSelectedMultiRect();
+                    lastSelectedRect.DataChanged -= SelectedRect_DataChanged;
+                    lastSelectedRect.DataChanged += SelectedRect_DataChanged;
+                }
+                else
+                {
+                    if (HitTestMultiSelectedRect())
+                    {
+                        mouseEventObject.hitTestType = MouseHitTestType.MultiTextEdit;
+                    }
+                    else
+                    {
+                        CleanSelectedMultiRect();
+
+                    }
+                }
+            }
+
+            else if (currentModel == ToolType.SelectedPage)
+            {
+                if (HitTestPageSelectedRect())
+                {
+
+                }
+                else
+                {
+                    CleanPageSelectedRect();
+                    CreatePageSelectdRect();
+                }
+                mouseEventObject.hitTestType = MouseHitTestType.SelectedPageRect;
+            }
+            MouseLeftButtonDownHandler?.Invoke(this, mouseEventObject);
+        }
+
+        protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e)
+        {
+            if (PDFViewer == null || PDFViewer.CurrentRenderFrame == null)
+            {
+                return;
+            }
+            MouseEventObject mouseEventObject = new MouseEventObject
+            {
+                mouseButtonEventArgs = e,
+                hitTestType = MouseHitTestType.Unknown,
+                annotType = C_ANNOTATION_TYPE.C_ANNOTATION_NONE,
+                IsCreate = false
+            };
+            if (isDrawSelectRect || IsDrawEditAnnot)
+            {
+                mouseEventObject.hitTestType = MouseHitTestType.SelectRect;
+                if (cacheHitTestAnnot != null)
+                {
+                    mouseEventObject.annotType = cacheHitTestAnnot.GetAnnotData().AnnotType;
+                }
+                else
+                {
+                    mouseEventObject.annotType = C_ANNOTATION_TYPE.C_ANNOTATION_NONE;
+                }
+                MouseLeftButtonUpHandler?.Invoke(this, mouseEventObject);
+                ReleaseMouseCapture();
+                return;
+            }
+            MouseLeftButtonUpHandler?.Invoke(this, mouseEventObject);
+            ReleaseMouseCapture();
+        }
+
+        protected override void OnMouseMove(MouseEventArgs e)
+        {
+            if (PDFViewer == null || PDFViewer.CurrentRenderFrame == null)
+            {
+                return;
+            }
+
+            Cursor oldCursor = this.Cursor;
+            Cursor newCursor = this.Cursor;
+            MouseEventObject mouseEventObject = new MouseEventObject
+            {
+                mouseButtonEventArgs = e,
+                hitTestType = MouseHitTestType.Unknown,
+                annotType = C_ANNOTATION_TYPE.C_ANNOTATION_NONE,
+                IsCreate = false
+            };
+
+            if (Mouse.LeftButton != MouseButtonState.Pressed)
+            {
+                List<ToolType> allowModeList = new List<ToolType>()
+                    {
+                        ToolType.Pan,
+                        ToolType.Viewer
+                    };
+                if (allowModeList.Contains(currentModel))
+                {
+                    newCursor = Cursors.Arrow;
+                    if (caheMoveAnnot is BaseWidget)
+                    {
+                        BaseWidget widget = (BaseWidget)caheMoveAnnot;
+                        if (widget.GetFormType() == PDFAnnotation.Form.C_WIDGET_TYPE.WIDGET_PUSHBUTTON && PDFViewer != null)
+                        {
+                            newCursor = Cursors.Hand;
+                        }
+                    }
+                    if (caheMoveAnnot is LinkAnnot)
+                    {
+                        newCursor = Cursors.Hand;
+                    }
+                }
+            }
+
+            if (!isDrawSelectRect && !IsDrawEditAnnot)
+            {
+                if (AnnotMoveHitTest())
+                {
+                    if (isCacheRedaction)
+                    {
+                        (caheMoveAnnot as RedactionAnnot).SetIsMouseHover(true);
+                        (caheMoveAnnot as RedactionAnnot).Draw();
+                    }
+
+                    mouseEventObject.annotType = caheMoveAnnot.GetAnnotData().AnnotType;
+                    mouseEventObject.IsMersured = caheMoveAnnot.GetAnnotData().Annot.IsMersured();
+                }
+                else
+                {
+                    if (isCacheRedaction)
+                    {
+                        isCacheRedaction = false;
+                        (caheMoveAnnot as RedactionAnnot).SetIsMouseHover(false);
+                        (caheMoveAnnot as RedactionAnnot).Draw();
+                    }
+
+                    caheMoveAnnot = null;
+                    if ((currentModel == ToolType.Pan || currentModel == ToolType.Viewer))
+                    {
+                        DrawMoveSelectImage();
+                    }
+                }
+            }
+            else
+            {
+                if (AnnotMoveHitTest())
+                {
+                    if (DrawSelectRectDownEvent() == false && Mouse.LeftButton != MouseButtonState.Pressed)
+                    {
+                        mouseEventObject.annotType = caheMoveAnnot.GetAnnotData().AnnotType;
+                    }
+
+                    if (isCacheRedaction)
+                    {
+                        (caheMoveAnnot as RedactionAnnot)?.SetIsMouseHover(true);
+                        (caheMoveAnnot as RedactionAnnot)?.Draw();
+                    }
+                }
+                else
+                {
+                    if (isCacheRedaction)
+                    {
+                        (caheMoveAnnot as RedactionAnnot)?.SetIsMouseHover(false);
+                        (caheMoveAnnot as RedactionAnnot)?.Draw();
+                    }
+                }
+
+                if (mouseEventObject.annotType == C_ANNOTATION_TYPE.C_ANNOTATION_NONE)
+                {
+                    mouseEventObject.hitTestType = MouseHitTestType.SelectRect;
+                    if (cacheHitTestAnnot != null)
+                    {
+                        mouseEventObject.annotType = cacheHitTestAnnot.GetAnnotData().AnnotType;
+                        mouseEventObject.IsMersured = cacheHitTestAnnot.GetAnnotData().Annot.IsMersured();
+                    }
+                    else
+                    {
+                        mouseEventObject.annotType = C_ANNOTATION_TYPE.C_ANNOTATION_NONE;
+                    }
+                }
+            }
+
+            MouseMoveHandler?.Invoke(this, mouseEventObject);
+            PDFViewer.SetCustomMousePoint(Mouse.GetPosition(this).Y, Mouse.GetPosition(this).X);
+            if (oldCursor != newCursor)
+            {
+                this.Cursor = newCursor;
+            }
+        }
+
+        protected override void OnMouseRightButtonDown(MouseButtonEventArgs e)
+        {
+            MouseEventObject mouseEventObject = new MouseEventObject
+            {
+                mouseButtonEventArgs = e,
+                hitTestType = MouseHitTestType.Unknown,
+                annotType = C_ANNOTATION_TYPE.C_ANNOTATION_NONE,
+                IsCreate = false
+            };
+
+            if (currentModel == ToolType.Pan || currentModel == ToolType.Viewer)
+            {
+                if (GetMousePointToTextSelectInfo())
+                {
+                    mouseEventObject.hitTestType = MouseHitTestType.Text;
+                }
+            }
+
+            if (isDrawSelectRect)
+            {
+                // Click inside the selected rectangle area
+                if (DrawSelectRectDownEvent() && cacheHitTestAnnot != null)
+                {
+                    mouseEventObject.hitTestType = MouseHitTestType.SelectRect;
+                    mouseEventObject.annotType = cacheHitTestAnnot.GetAnnotData().AnnotType;
+                    MouseRightButtonDownHandler?.Invoke(this, mouseEventObject);
+                    return;
+                }
+            }
+
+            if (IsDrawEditAnnot)
+            {
+                // Click inside the selected rectangle area
+                if (DrawEditAnnotDownEvent() && cacheHitTestAnnot != null)
+                {
+                    mouseEventObject.hitTestType = MouseHitTestType.SelectRect;
+                    mouseEventObject.annotType = cacheHitTestAnnot.GetAnnotData().AnnotType;
+                    MouseRightButtonDownHandler?.Invoke(this, mouseEventObject);
+                    return;
+                }
+            }
+
+            Point = e.GetPosition(this);
+
+            // Annotation selection effect
+            if ((currentModel == ToolType.Pan || currentModel == ToolType.CreateAnnot))
+            {
+                if (AnnotHitTest())
+                {
+
+                    if (!isCacheRedaction)
+                    {
+                        if (cacheHitTestAnnot?.CurrentType == C_ANNOTATION_TYPE.C_ANNOTATION_LINK && currentModel != ToolType.CreateAnnot)
+                        {
+                            LinkAnnotAction(cacheHitTestAnnot);
+                        }
+                        else
+                        {
+                            List<C_ANNOTATION_TYPE> list = new List<C_ANNOTATION_TYPE>()
+                        {
+                            C_ANNOTATION_TYPE.C_ANNOTATION_LINE,
+                            C_ANNOTATION_TYPE.C_ANNOTATION_POLYLINE,
+                            C_ANNOTATION_TYPE.C_ANNOTATION_POLYGON,
+                        };
+                            if (cacheHitTestAnnot != null && list.Contains(cacheHitTestAnnot.CurrentType))
+                            {
+                                SetEditAnnotObject();
+                                DrawEditAnnotLayer();
+                            }
+                            else
+                            {
+                                SelectedAnnot();
+                                DrawSelectedLayer();
+                            }
+                            isDrawSelectRect = true;
+                        }
+                    }
+                    mouseEventObject.hitTestType = MouseHitTestType.Annot;
+                    if (cacheHitTestAnnot != null)
+                    {
+                        mouseEventObject.annotType = cacheHitTestAnnot.GetAnnotData().AnnotType;
+                    }
+                    else
+                    {
+                        mouseEventObject.annotType = C_ANNOTATION_TYPE.C_ANNOTATION_NONE;
+                    }
+                }
+                else
+                {
+                    CleanSelectedRect();
+                }
+            }
+
+            // Form selection effect
+            if ((currentModel == ToolType.Pan || currentModel == ToolType.Viewer) && AnnotWidgetHitTest())
+            {
+                mouseEventObject.hitTestType = MouseHitTestType.Widget;
+                mouseEventObject.annotType = cacheMoveWidget.GetAnnotData().AnnotType;
+                FormClickProcess();
+            }
+
+            // Form creation mode
+            if (currentModel == ToolType.WidgetEdit)
+            {
+                if (AnnotWidgetHitTest())
+                {
+                    cacheHitTestAnnot = PDFViewer?.AnnotHitTest() as BaseWidget;
+                    SelectedAnnot();
+                    DrawSelectedLayer();
+                    mouseEventObject.hitTestType = MouseHitTestType.Annot;
+
+                    mouseEventObject.annotType = cacheMoveWidget.GetAnnotData().AnnotType;
+                }
+                else
+                {
+                    CleanSelectedRect();
+                }
+            }
+
+            // Content editing mode
+            if (currentModel == ToolType.ContentEdit)
+            {
+                if (e.ClickCount == 1)
+                {
+                    DrawTextEditDownEvent(false);
+                    if (GetLastSelectedRect() != null)
+                    {
+                        EditAreaObject editAreaObject = GetEditAreaObjectForRect(lastSelectedRect);
+                        switch (editAreaObject.cPDFEditArea.Type)
+                        {
+                            case CPDFEditType.EditText:
+                                mouseEventObject.hitTestType = MouseHitTestType.TextEdit;
+                                break;
+                            case CPDFEditType.EditImage:
+                                mouseEventObject.hitTestType = MouseHitTestType.ImageEdit;
+                                break;
+                            default:
+                                break;
+                        }
+                    }
+                }
+            }
+            else
+            {
+                if ((currentModel == ToolType.Viewer || currentModel == ToolType.Pan) && mouseEventObject.hitTestType == MouseHitTestType.Unknown && DrawDownSelectImage(false))
+                {
+                    mouseEventObject.hitTestType = MouseHitTestType.ImageSelect;
+                }
+            }
+            MouseRightButtonDownHandler?.Invoke(this, mouseEventObject);
+        }
+
+        public bool GetIsCropMode()
+        {
+            if (lastSelectedRect != null)
+            {
+                return lastSelectedRect.GetCurrentDrawPointType() == DrawPointType.Crop;
+            }
+            return false;
+        }
+
+        public void SetCropMode(bool crop)
+        {
+            if (lastSelectedRect != null)
+            {
+                List<PointControlType> ignoreList = new List<PointControlType>();
+                if (crop)
+                {
+                    lastSelectedRect.SetCurrentDrawPointType(DrawPointType.Crop);
+                    ignoreList.Add(PointControlType.Body);
+                    if (editArea.TryGetValue(lastSelectedRect, out EditAreaObject editAreaObject))
+                    {
+                        cropIndex = editAreaObject.EditAreaIndex;
+                    }
+                    lastSelectedRect.DataChanged -= LastSelectedRect_DataChanged;
+                }
+                else
+                {
+                    lastSelectedRect.SetCurrentDrawPointType(DrawPointType.Square);
+                    cropIndex = -1;
+                    lastSelectedRect.DataChanged += LastSelectedRect_DataChanged;
+                }
+                lastSelectedRect.SetIgnorePoints(ignoreList);
+                lastSelectedRect.Draw();
+            }
+        }
+
+        internal void SetToolType(ToolType model)
+        {
+            currentModel = model;
+            CPDFViewer pdfViewer = GetCPDFViewer();
+
+            if (pdfViewer != null)
+            {
+                if (currentModel == ToolType.WidgetEdit)
+                {
+                    pdfViewer.IsHideFormShow = true;
+                }
+                else
+                {
+                    pdfViewer.IsHideFormShow = false;
+                }
+            }
+        }
+
+        public ToolType GetToolType()
+        {
+            return currentModel;
+        }
+
+        public void SavePoint()
+        {
+            CachePoint = Point;
+        }
+
+        public void CleanPoint()
+        {
+            CachePoint = new Point();
+        }
+
+        private void CPDFViewerTool_Loaded(object sender, RoutedEventArgs e)
+        {
+            PDFViewer.DrawChanged += PDFViewer_DrawChanged;
+            PDFViewer.UndoManager.HistoryChanged += UndoManager_HistoryChanged;
+            PDFViewer.MouseEnter += PDFViewer_MouseEnter;
+            PDFViewer.MouseLeave += PDFViewer_MouseLeave;
+        }
+
+        private void PDFViewer_MouseLeave(object sender, MouseEventArgs e)
+        {
+            PDFViewer.IsVisibilityMouse(false);
+        }
+
+        private void PDFViewer_MouseEnter(object sender, MouseEventArgs e)
+        {
+            PDFViewer.IsVisibilityMouse(true);
+        }
+
+        private void UndoManager_HistoryChanged(object sender, KeyValuePair<UndoAction, IHistory> data)
+        {
+            IsDocumentModified = true;
+        }
+
+        private void CPDFViewerTool_Unloaded(object sender, RoutedEventArgs e)
+        {
+            PDFViewer.DrawChanged -= PDFViewer_DrawChanged;
+        }
+
+        private void PDFViewer_DrawChanged(object sender, EventArgs e)
+        {
+            SizeChangeds();
+            DrawChanged?.Invoke(this, e);
+        }
+
+        public void SizeChangeds()
+        {
+            if (IsLoaded)
+            {
+                if (cacheHitTestAnnot != null)
+                {
+                    BaseLayer baseLayer1 = PDFViewer.GetViewForTag(PDFViewer.GetAnnotViewTag());
+                    bool Update = (baseLayer1 as AnnotLayer).GetUpdate(ref cacheHitTestAnnot);
+                    if (Update)
+                    {
+                        if (IsDrawEditAnnot)
+                        {
+                            SetEditAnnotObject();
+                            DrawEditAnnotLayer();
+                        }
+
+                        if (isDrawSelectRect)
+                        {
+                            List<C_ANNOTATION_TYPE> list = new List<C_ANNOTATION_TYPE>()
+                            {
+                                C_ANNOTATION_TYPE.C_ANNOTATION_LINE,
+                                C_ANNOTATION_TYPE.C_ANNOTATION_POLYLINE,
+                                C_ANNOTATION_TYPE.C_ANNOTATION_POLYGON,
+                            };
+
+                            if (cacheHitTestAnnot != null && list.Contains(cacheHitTestAnnot.CurrentType))
+                            {
+                                SetEditAnnotObject();
+                                DrawEditAnnotLayer();
+                            }
+                            else
+                            {
+                                SelectedAnnot();
+                            }
+                            DrawSelectedLayer();
+                        }
+                    }
+                    else
+                    {
+                        SelectedAnnot(null);
+                    }
+                }
+                else if (selectedPageIndex != -1 && selectedAnnotIndex != -1)
+                {
+                    BaseLayer baseLayer1 = PDFViewer.GetViewForTag(PDFViewer.GetAnnotViewTag());
+                    cacheHitTestAnnot = (baseLayer1 as AnnotLayer).GetSelectedAnnot(ref selectedPageIndex, ref selectedAnnotIndex);
+                    if (cacheHitTestAnnot != null)
+                    {
+                        List<C_ANNOTATION_TYPE> list = new List<C_ANNOTATION_TYPE>()
+                        {
+                            C_ANNOTATION_TYPE.C_ANNOTATION_LINE,
+                            C_ANNOTATION_TYPE.C_ANNOTATION_POLYLINE,
+                            C_ANNOTATION_TYPE.C_ANNOTATION_POLYGON,
+                        };
+                        if (cacheHitTestAnnot != null && list.Contains(cacheHitTestAnnot.CurrentType))
+                        {
+                            SetEditAnnotObject();
+                            DrawEditAnnotLayer();
+                        }
+                        else
+                        {
+                            SelectedAnnot();
+                        }
+                        DrawSelectedLayer();
+                        isDrawSelectRect = true;
+                    }
+                }
+                if (PDFViewer.CurrentRenderFrame != null)
+                {
+                    currentZoom = PDFViewer.CurrentRenderFrame.ZoomFactor;
+                    if (PDFViewer.CurrentRenderFrame.IsCacheEditPage == true && currentModel == ToolType.ContentEdit)
+                    {
+                        SetEditTextRect(PDFViewer.CurrentRenderFrame);
+                        if (selectedEditPageIndex != -1 && selectedEditAreaIndex != -1)
+                        {
+                            DrawSelectedEditAreaForIndex();
+                        }
+                    }
+                }
+                ReDrawSelectedMultiRect();
+                ReDrawWidget();
+                ReDrawSelectText();
+                ReDrawSelectImage();
+                UpdateFormHitPop();
+                UpdateTextPop();
+            }
+        }
+
+        private void ScrollViewer_MouseUp(object sender, MouseButtonEventArgs e)
+        {
+
+        }
+
+        private void ScrollViewer_MouseDown(object sender, MouseButtonEventArgs e)
+        {
+
+        }
+    }
+}

+ 213 - 0
Demo/Examples/ComPDFKit.Tool/ComPDFKit.Tool.csproj

@@ -0,0 +1,213 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProjectGuid>{A061EE7A-6704-4BD9-86EE-48ED5DF75E2F}</ProjectGuid>
+    <OutputType>Library</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <RootNamespace>ComPDFKit.Tool</RootNamespace>
+    <AssemblyName>ComPDFKit.Tool</AssemblyName>
+    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+    <FileAlignment>512</FileAlignment>
+    <Deterministic>true</Deterministic>
+    <TargetFrameworkProfile />
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>bin\Debug\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>bin\Release\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'DemoTest|AnyCPU'">
+    <OutputPath>bin\DemoTest\</OutputPath>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
+    <DebugSymbols>true</DebugSymbols>
+    <OutputPath>bin\x64\Debug\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <DebugType>full</DebugType>
+    <PlatformTarget>x64</PlatformTarget>
+    <LangVersion>7.3</LangVersion>
+    <ErrorReport>prompt</ErrorReport>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
+    <OutputPath>bin\x64\Release\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <Optimize>true</Optimize>
+    <DebugType>pdbonly</DebugType>
+    <PlatformTarget>x64</PlatformTarget>
+    <LangVersion>7.3</LangVersion>
+    <ErrorReport>prompt</ErrorReport>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'DemoTest|x64'">
+    <OutputPath>bin\x64\DemoTest\</OutputPath>
+    <PlatformTarget>x64</PlatformTarget>
+    <LangVersion>7.3</LangVersion>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
+    <DebugSymbols>true</DebugSymbols>
+    <OutputPath>bin\x86\Debug\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <DebugType>full</DebugType>
+    <PlatformTarget>x86</PlatformTarget>
+    <LangVersion>7.3</LangVersion>
+    <ErrorReport>prompt</ErrorReport>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
+    <OutputPath>bin\x86\Release\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <Optimize>true</Optimize>
+    <DebugType>pdbonly</DebugType>
+    <PlatformTarget>x86</PlatformTarget>
+    <LangVersion>7.3</LangVersion>
+    <ErrorReport>prompt</ErrorReport>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'DemoTest|x86'">
+    <OutputPath>bin\x86\DemoTest\</OutputPath>
+    <PlatformTarget>x86</PlatformTarget>
+    <LangVersion>7.3</LangVersion>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="PresentationCore" />
+    <Reference Include="PresentationFramework" />
+    <Reference Include="System" />
+    <Reference Include="System.Core" />
+    <Reference Include="System.Xaml" />
+    <Reference Include="System.Xml.Linq" />
+    <Reference Include="System.Data.DataSetExtensions" />
+    <Reference Include="Microsoft.CSharp" />
+    <Reference Include="System.Data" />
+    <Reference Include="System.Net.Http" />
+    <Reference Include="System.Xml" />
+    <Reference Include="WindowsBase" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="AlignmentsHelp.cs" />
+    <Compile Include="CPDFViewerTool.Annot.cs" />
+    <Compile Include="CPDFViewerTool.AnnotEdit.cs" />
+    <Compile Include="CPDFViewerTool.Command.cs" />
+    <Compile Include="CPDFViewerTool.CreateWidget.cs" />
+    <Compile Include="CPDFViewerTool.CustomizeTool.cs" />
+    <Compile Include="CPDFViewerTool.DataMethod.cs" />
+    <Compile Include="CPDFViewerTool.FindReplace.cs" />
+    <Compile Include="CPDFViewerTool.MultiSelectedRect.cs" />
+    <Compile Include="CPDFViewerTool.PageSelected.cs" />
+    <Compile Include="CPDFViewerTool.SelectImage.cs" />
+    <Compile Include="CPDFViewerTool.WidgetTool.cs" />
+    <Compile Include="CPDFViewerTool.SelectedRect.cs" />
+    <Compile Include="CPDFViewerTool.SelectText.cs" />
+    <Compile Include="CPDFViewerTool.TextEdit.cs" />
+    <Compile Include="CPDFViewerTool.xaml.cs">
+      <DependentUpon>CPDFViewerTool.xaml</DependentUpon>
+    </Compile>
+    <Compile Include="CustomCommands.cs" />
+    <Compile Include="DrawTool\AnnotEdit.cs" />
+    <Compile Include="DrawTool\CaretVisual.cs" />
+    <Compile Include="DrawTool\CreateAnnotTool.cs" />
+    <Compile Include="DrawTool\CreateCustomizeTool.cs" />
+    <Compile Include="DrawTool\CreateWidgetTool.cs" />
+    <Compile Include="DrawTool\MultiSelectedRect.cs" />
+    <Compile Include="DrawTool\PageSelectedRect.cs" />
+    <Compile Include="DrawTool\SelectedRect.cs" />
+    <Compile Include="DrawTool\SelectedRect.protected.cs" />
+    <Compile Include="DrawTool\SelectImage.cs" />
+    <Compile Include="DrawTool\SelectText.cs" />
+    <Compile Include="Help\CommonHelper.cs" />
+    <Compile Include="Help\ImportWin32.cs" />
+    <Compile Include="Help\ParamConverter.cs" />
+    <Compile Include="Help\PDFHelp.cs" />
+    <Compile Include="PDFTextSearch.cs" />
+    <Compile Include="Properties\AssemblyInfo.cs" />
+    <Compile Include="CPDFToolManager.cs" />
+    <Compile Include="SettingParam\AnnotParam.cs" />
+    <Compile Include="SettingParam\AnnotParam\CircleParam.cs" />
+    <Compile Include="SettingParam\AnnotParam\FreeTextParam.cs" />
+    <Compile Include="SettingParam\AnnotParam\HighlightParam.cs" />
+    <Compile Include="SettingParam\AnnotParam\InkParam.cs" />
+    <Compile Include="SettingParam\AnnotParam\LineMeasureParam.cs" />
+    <Compile Include="SettingParam\AnnotParam\LineParam.cs" />
+    <Compile Include="SettingParam\AnnotParam\LinkParam.cs" />
+    <Compile Include="SettingParam\AnnotParam\PolygonMeasureParam.cs" />
+    <Compile Include="SettingParam\AnnotParam\PolyLineMeasureParam.cs" />
+    <Compile Include="SettingParam\AnnotParam\RedactParam.cs" />
+    <Compile Include="SettingParam\AnnotParam\SoundParam.cs" />
+    <Compile Include="SettingParam\AnnotParam\SquareParam.cs" />
+    <Compile Include="SettingParam\AnnotParam\SquigglyParam.cs" />
+    <Compile Include="SettingParam\AnnotParam\StampParam.cs" />
+    <Compile Include="SettingParam\AnnotParam\StickyNoteParam.cs" />
+    <Compile Include="SettingParam\AnnotParam\StrikeoutParam.cs" />
+    <Compile Include="SettingParam\AnnotParam\UnderlineParam.cs" />
+    <Compile Include="SettingParam\DefaultSettingParam.cs" />
+    <Compile Include="SettingParam\FormParam\CheckBoxParam.cs" />
+    <Compile Include="SettingParam\FormParam\SignatureParam.cs" />
+    <Compile Include="SettingParam\FormParam\ComboBoxParam.cs" />
+    <Compile Include="SettingParam\FormParam\WidgetParm.cs" />
+    <Compile Include="SettingParam\FormParam\ListBoxParam.cs" />
+    <Compile Include="SettingParam\FormParam\PushButtonParam.cs" />
+    <Compile Include="SettingParam\FormParam\RadioButtonParam.cs" />
+    <Compile Include="SettingParam\FormParam\TextBoxParam.cs" />
+    <Compile Include="SettingParam\PDFEditParam\ImageEditParam.cs" />
+    <Compile Include="SettingParam\PDFEditParam\PDFEditParam.cs" />
+    <Compile Include="SettingParam\PDFEditParam\TextEditParam.cs" />
+    <Compile Include="UndoManger\AnnotHistory.cs" />
+    <Compile Include="UndoManger\AnnotHistory\CircleAnnotHistory.cs" />
+    <Compile Include="UndoManger\AnnotHistory\FreeTextAnnotHistory.cs" />
+    <Compile Include="UndoManger\AnnotHistory\HighlightAnnotHistory.cs" />
+    <Compile Include="UndoManger\AnnotHistory\InkAnnotHistory.cs" />
+    <Compile Include="UndoManger\AnnotHistory\LineAnnotHistory.cs" />
+    <Compile Include="UndoManger\AnnotHistory\LineMeasureAnnotHistory.cs" />
+    <Compile Include="UndoManger\AnnotHistory\LinkAnnotHistory.cs" />
+    <Compile Include="UndoManger\AnnotHistory\PolygonMeasureAnnotHistory.cs" />
+    <Compile Include="UndoManger\AnnotHistory\PolyLineMeasureAnnotHistory.cs" />
+    <Compile Include="UndoManger\AnnotHistory\SoundAnnotHistory.cs" />
+    <Compile Include="UndoManger\FindReplaceHistory\FindReplaceHistory.cs" />
+    <Compile Include="UndoManger\FormHistory\CheckBoxHistory.cs" />
+    <Compile Include="UndoManger\FormHistory\ComboBoxHistory.cs" />
+    <Compile Include="UndoManger\FormHistory\ListBoxHistory.cs" />
+    <Compile Include="UndoManger\FormHistory\PushButtonHistory.cs" />
+    <Compile Include="UndoManger\FormHistory\RadioButtonHistory.cs" />
+    <Compile Include="UndoManger\FormHistory\SignatureHistory.cs" />
+    <Compile Include="UndoManger\FormHistory\TextBoxHistory.cs" />
+    <Compile Include="UndoManger\GroupHistory.cs" />
+    <Compile Include="UndoManger\MultiAnnotHistory.cs" />
+    <Compile Include="UndoManger\AnnotHistory\RedactAnnotHistory.cs" />
+    <Compile Include="UndoManger\AnnotHistory\ReferenceObject.cs" />
+    <Compile Include="UndoManger\AnnotHistory\SquareAnnotHistory.cs" />
+    <Compile Include="UndoManger\AnnotHistory\SquigglyAnnotHistory.cs" />
+    <Compile Include="UndoManger\AnnotHistory\StampAnnotHistory.cs" />
+    <Compile Include="UndoManger\AnnotHistory\StickyNoteAnnotHistory.cs" />
+    <Compile Include="UndoManger\AnnotHistory\StrikeoutAnnotHistory.cs" />
+    <Compile Include="UndoManger\AnnotHistory\UnderlineAnnotHistory.cs" />
+    <Compile Include="UndoManger\PDFEditHistory\PDFEditHistory.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <Page Include="CPDFViewerTool.xaml">
+      <SubType>Designer</SubType>
+      <Generator>MSBuild:Compile</Generator>
+    </Page>
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\..\..\..\compdfkit_windows\ComPDFKit\ComPDFKitCSharp\ComPDFKit.NET.csproj">
+      <Project>{56e518ad-c126-4b48-9a09-0a64c87020e4}</Project>
+      <Name>ComPDFKit.NET</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\..\..\..\compdfkit_windows_rebuild\ComPDFKit.Viewer\ComPDFKit.Viewer.csproj">
+      <Project>{783263cf-0da3-4095-9df8-2c4a6b3ff908}</Project>
+      <Name>ComPDFKit.Viewer</Name>
+    </ProjectReference>
+  </ItemGroup>
+  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+</Project>

+ 47 - 0
Demo/Examples/ComPDFKit.Tool/CustomCommands.cs

@@ -0,0 +1,47 @@
+using System.Collections.Generic;
+using System.Linq;
+using System.Windows.Input;
+
+namespace ComPDFKit.Tool
+{
+    public static class CustomCommands
+    {
+        internal enum CustomCommandId
+        {
+            PasteMatchStyle
+        }
+
+        public class CustomCommand: RoutedUICommand
+        {
+            internal CustomCommandId Id;
+
+            public CustomCommand(string name, string text) : base(text, name, typeof(CustomCommands))
+            {
+                
+            }
+        }
+        
+        private static List<CustomCommand> Commands { get; set; }=new List<CustomCommand>();
+        /// <summary>
+        /// Pastes text matching style.
+        /// </summary>
+        public static RoutedUICommand PasteWithoutStyle => EnsureCommand(CustomCommandId.PasteMatchStyle);
+
+        private static RoutedUICommand EnsureCommand(CustomCommandId idCommand)
+        {
+            CustomCommand command = null;
+            if (Commands!=null && Commands.Count>0)
+            {
+                command = Commands.AsEnumerable().FirstOrDefault(x => x.Id == idCommand);
+            }
+
+            if(command == null)
+            {
+                command= new CustomCommand("PasteWithoutStyle", "Paste Without Style");
+                command.Id = idCommand;
+                Commands?.Add(command);
+            }
+            return command;
+        }
+    }
+}

+ 962 - 0
Demo/Examples/ComPDFKit.Tool/DrawTool/AnnotEdit.cs

@@ -0,0 +1,962 @@
+using ComPDFKit.Import;
+using ComPDFKit.Measure;
+using ComPDFKit.PDFAnnotation;
+using ComPDFKit.Tool.SettingParam;
+using ComPDFKit.Viewer.Helper;
+using ComPDFKitViewer;
+using ComPDFKitViewer.Annot;
+using ComPDFKitViewer.Helper;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Net;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Media;
+using System.Windows.Media.TextFormatting;
+using static ComPDFKit.Tool.Help.ImportWin32;
+
+namespace ComPDFKit.Tool.DrawTool
+{
+    internal class AnnotEdit : DrawingVisual
+    {
+        #region public Attributes
+
+        /// <summary>
+        /// Data changing event
+        /// </summary>
+        public event EventHandler<SelectedAnnotData> DataChanging;
+
+        /// <summary>
+        /// Data changed event
+        /// </summary>
+        public event EventHandler<SelectedAnnotData> DataChanged;
+
+        #endregion
+
+        #region private Attributes
+
+        protected DefaultDrawParam drawParam = new DefaultDrawParam();
+
+        /// <summary>
+        /// Max drawing range
+        /// </summary>
+        protected Rect maxRect { get; set; } = new Rect(0, 0, 0, 0);
+
+        /// <summary>
+        /// Is mouse down
+        /// </summary>
+        protected bool isMouseDown { get; set; }
+
+        protected DrawingContext drawDC { get; set; }
+
+        /// <summary>
+        /// Current annotation's control points collection (96 DPI coordinate points, minus page offset)
+        /// </summary>
+        private PointCollection activePoints { get; set; } = new PointCollection();
+
+        /// <summary>
+        /// Current annotation's drawing range box (96 DPI coordinate points, minus page offset)
+        /// </summary>
+        private Rect activeRect { get; set; } = new Rect();
+
+        private int hitIndex = -1;
+
+        protected AnnotData annotData { get; set; }
+
+        /// <summary>
+        /// Position information recorded when the mouse is pressed 
+        /// </summary>
+        protected Point mouseDownPoint { get; set; }
+
+        /// <summary>
+        /// Mouse last draw position information
+        /// </summary>
+        protected Point mouseEndDrawPoint { get; set; }
+
+        /// <summary>
+        /// Move offset during movement
+        /// </summary>
+        protected Point moveOffset { get; set; } = new Point(0, 0);
+
+        /// <summary>
+        /// Point size
+        /// </summary>
+        protected int pointSize { get; set; } = 6;
+
+        protected SelectedAnnotData annotEditData = new SelectedAnnotData();
+
+        //Measure
+        private PointCollection leftLine { get; set; } = new PointCollection();
+        private PointCollection rightLine { get; set; } = new PointCollection();
+        private PointCollection crossLine { get; set; } = new PointCollection();
+
+        private Point[] moveLeftLine;
+        private Point[] moveRightLine;
+        private Point[] moveCrossLine;
+
+        #endregion
+
+        /// <summary>
+        /// Re-locate child elements
+        /// </summary>
+        public void Arrange()
+        {
+            foreach (Visual child in Children)
+            {
+                if (!(child is UIElement))
+                {
+                    continue;
+                }
+                UIElement checkChild = child as UIElement;
+                try
+                {
+                    double left = Canvas.GetLeft(checkChild);
+                    double top = Canvas.GetTop(checkChild);
+                    double width = (double)checkChild.GetValue(FrameworkElement.WidthProperty);
+                    double height = (double)checkChild.GetValue(FrameworkElement.HeightProperty);
+                    checkChild.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
+                    checkChild.Arrange(new Rect(
+                        double.IsNaN(left) ? 0 : left,
+                        double.IsNaN(top) ? 0 : top,
+                        double.IsNaN(width) ? checkChild.DesiredSize.Width : width,
+                        double.IsNaN(height) ? checkChild.DesiredSize.Height : height));
+                }
+                catch (Exception ex)
+                {
+
+                }
+            }
+        }
+        public AnnotEdit(DefaultDrawParam defaultDrawParam) : base()
+        {
+            drawParam = defaultDrawParam;
+        }
+
+        public bool SetAnnotObject(AnnotData Data)
+        {
+            activePoints.Clear();
+            if (Data == null)
+            {
+                return false;
+            }
+            annotData = Data;
+
+            annotEditData = new SelectedAnnotData();
+            annotEditData.annotData = annotData;
+
+            SetMaxRect(annotData.PaintOffset);
+            switch (annotData.Annot.Type)
+            {
+                case C_ANNOTATION_TYPE.C_ANNOTATION_NONE:
+                    break;
+                case C_ANNOTATION_TYPE.C_ANNOTATION_UNKOWN:
+                    break;
+                case C_ANNOTATION_TYPE.C_ANNOTATION_TEXT:
+                    break;
+                case C_ANNOTATION_TYPE.C_ANNOTATION_LINK:
+                    break;
+                case C_ANNOTATION_TYPE.C_ANNOTATION_FREETEXT:
+                    break;
+                case C_ANNOTATION_TYPE.C_ANNOTATION_LINE:
+                    for (int i = 0; i < (annotData.Annot as CPDFLineAnnotation).Points.Length; i++)
+                    {
+                        Point point = DpiHelper.PDFPointToStandardPoint(new Point((annotData.Annot as CPDFLineAnnotation).Points[i].x, (annotData.Annot as CPDFLineAnnotation).Points[i].y));
+                        point = new Point(
+                            point.X * annotData.CurrentZoom + annotData.PaintOffset.X - annotData.CropLeft * annotData.CurrentZoom,
+                            point.Y * annotData.CurrentZoom + annotData.PaintOffset.Y - annotData.CropTop * annotData.CurrentZoom
+                            );
+                        activePoints.Add(point);
+                    }
+                    if ((annotData.Annot as CPDFLineAnnotation).IsMersured())
+                    {
+                        CRect rawRect = annotData.Annot.GetRect();
+                        Rect rect = DataConversionForWPF.CRectConversionForRect(rawRect);
+                        rect = DpiHelper.PDFRectToStandardRect(rect);
+                        activeRect = new Rect(
+                            rect.X * annotData.CurrentZoom + annotData.PaintOffset.X - annotData.CropLeft * annotData.CurrentZoom,
+                            rect.Y * annotData.CurrentZoom + annotData.PaintOffset.Y - annotData.CropTop * annotData.CurrentZoom,
+                            rect.Width * annotData.CurrentZoom,
+                            rect.Height * annotData.CurrentZoom
+                            );
+                        CalcMeasurePoints(annotData.Annot as CPDFLineAnnotation);
+                    }
+                    break;
+                case C_ANNOTATION_TYPE.C_ANNOTATION_SQUARE:
+                    break;
+                case C_ANNOTATION_TYPE.C_ANNOTATION_CIRCLE:
+                    break;
+                case C_ANNOTATION_TYPE.C_ANNOTATION_POLYGON:
+                    for (int i = 0; i < (annotData.Annot as CPDFPolygonAnnotation).Points.Count; i++)
+                    {
+                        Point point = DpiHelper.PDFPointToStandardPoint(new Point((annotData.Annot as CPDFPolygonAnnotation).Points[i].x, (annotData.Annot as CPDFPolygonAnnotation).Points[i].y));
+                        point = new Point(
+                            point.X * annotData.CurrentZoom + annotData.PaintOffset.X - annotData.CropLeft * annotData.CurrentZoom,
+                            point.Y * annotData.CurrentZoom + annotData.PaintOffset.Y - annotData.CropTop * annotData.CurrentZoom
+                            );
+                        activePoints.Add(point);
+                    }
+                    if ((annotData.Annot as CPDFPolygonAnnotation).IsMersured())
+                    {
+                        CRect rawRect = annotData.Annot.GetRect();
+                        Rect rect = DataConversionForWPF.CRectConversionForRect(rawRect);
+                        rect = DpiHelper.PDFRectToStandardRect(rect);
+                        activeRect = new Rect(
+                            rect.X * annotData.CurrentZoom + annotData.PaintOffset.X - annotData.CropLeft * annotData.CurrentZoom,
+                            rect.Y * annotData.CurrentZoom + annotData.PaintOffset.Y - annotData.CropTop * annotData.CurrentZoom,
+                            rect.Width * annotData.CurrentZoom,
+                            rect.Height * annotData.CurrentZoom
+                            );
+                    }
+                    break;
+                case C_ANNOTATION_TYPE.C_ANNOTATION_POLYLINE:
+                    for (int i = 0; i < (annotData.Annot as CPDFPolylineAnnotation).Points.Count; i++)
+                    {
+                        Point point = DpiHelper.PDFPointToStandardPoint(new Point((annotData.Annot as CPDFPolylineAnnotation).Points[i].x, (annotData.Annot as CPDFPolylineAnnotation).Points[i].y));
+                        point = new Point(
+                            point.X * annotData.CurrentZoom + annotData.PaintOffset.X - annotData.CropLeft * annotData.CurrentZoom,
+                            point.Y * annotData.CurrentZoom + annotData.PaintOffset.Y - annotData.CropTop * annotData.CurrentZoom
+                            );
+                        activePoints.Add(point);
+                    }
+                    if ((annotData.Annot as CPDFPolylineAnnotation).IsMersured())
+                    {
+                        CRect rawRect = annotData.Annot.GetRect();
+                        Rect rect = DataConversionForWPF.CRectConversionForRect(rawRect);
+                        rect = DpiHelper.PDFRectToStandardRect(rect);
+                        activeRect = new Rect(
+                            rect.X * annotData.CurrentZoom + annotData.PaintOffset.X - annotData.CropLeft * annotData.CurrentZoom,
+                            rect.Y * annotData.CurrentZoom + annotData.PaintOffset.Y - annotData.CropTop * annotData.CurrentZoom,
+                            rect.Width * annotData.CurrentZoom,
+                            rect.Height * annotData.CurrentZoom
+                            );
+                    }
+                    break;
+                case C_ANNOTATION_TYPE.C_ANNOTATION_HIGHLIGHT:
+                    break;
+                case C_ANNOTATION_TYPE.C_ANNOTATION_UNDERLINE:
+                    break;
+                case C_ANNOTATION_TYPE.C_ANNOTATION_SQUIGGLY:
+                    break;
+                case C_ANNOTATION_TYPE.C_ANNOTATION_STRIKEOUT:
+                    break;
+                case C_ANNOTATION_TYPE.C_ANNOTATION_STAMP:
+                    break;
+                case C_ANNOTATION_TYPE.C_ANNOTATION_CARET:
+                    break;
+                case C_ANNOTATION_TYPE.C_ANNOTATION_INK:
+                    break;
+                case C_ANNOTATION_TYPE.C_ANNOTATION_POPUP:
+                    break;
+                case C_ANNOTATION_TYPE.C_ANNOTATION_FILEATTACHMENT:
+                    break;
+                case C_ANNOTATION_TYPE.C_ANNOTATION_SOUND:
+                    break;
+                case C_ANNOTATION_TYPE.C_ANNOTATION_MOVIE:
+                    break;
+                case C_ANNOTATION_TYPE.C_ANNOTATION_WIDGET:
+                    break;
+                case C_ANNOTATION_TYPE.C_ANNOTATION_SCREEN:
+                    break;
+                case C_ANNOTATION_TYPE.C_ANNOTATION_PRINTERMARK:
+                    break;
+                case C_ANNOTATION_TYPE.C_ANNOTATION_TRAPNET:
+                    break;
+                case C_ANNOTATION_TYPE.C_ANNOTATION_WATERMARK:
+                    break;
+                case C_ANNOTATION_TYPE.C_ANNOTATION_3D:
+                    break;
+                case C_ANNOTATION_TYPE.C_ANNOTATION_RICHMEDIA:
+                    break;
+                case C_ANNOTATION_TYPE.C_ANNOTATION_REDACT:
+                    break;
+                case C_ANNOTATION_TYPE.C_ANNOTATION_INTERCHANGE:
+                    break;
+                default:
+                    break;
+            }
+            return true;
+        }
+
+        public void SetMaxRect(Rect rect)
+        {
+            maxRect = rect;
+        }
+
+        public int GetHitIndex(Point point)
+        {
+            for (int i = 0; i < activePoints.Count; i++)
+            {
+                Vector checkVector = activePoints[i] - point;
+                if (checkVector.Length < pointSize)
+                {
+                    return i;
+                }
+            }
+            return -1;
+        }
+
+        /// <summary>
+        /// Calculate the control points of the measurement annotation
+        /// </summary>
+        /// <param name="Annot">
+        /// Measurement annotation object
+        /// </param>
+        private void CalcMeasurePoints(CPDFLineAnnotation Annot)
+        {
+            leftLine.Clear();
+            crossLine.Clear();
+            rightLine.Clear();
+            CPDFDistanceMeasure LineMeasure = Annot.GetDistanceMeasure();
+            double LeadLength = LineMeasure.GetLeadLength() / 72D * 96D;
+            double LeadOffset = LineMeasure.GetLeadOffset() / 72D * 96D;
+            double LeadExtension = LineMeasure.GetLeadExtension() / 72D * 96D;
+
+            List<Point> orderPoints = activePoints.AsEnumerable().OrderBy(x => x.X).ToList();
+            Vector lineVector = (activePoints[1] - activePoints[0]) * annotData.CurrentZoom;
+            lineVector.Normalize();
+            Vector leadEndVector = lineVector * (Math.Abs(LeadLength) + Math.Abs(LeadOffset) + Math.Abs(LeadExtension) * annotData.CurrentZoom);
+            Vector leadStartVector = lineVector * (Math.Abs(LeadOffset)) * annotData.CurrentZoom;
+            Vector leadCrossVector = lineVector * (Math.Abs(LeadLength)  * annotData.CurrentZoom + Math.Abs(LeadOffset));
+            Matrix rotateMatrix = new Matrix();
+            double angle = LeadLength < 0 ? 90 : -90;
+            rotateMatrix.Rotate(angle);
+            Point leftEndPoint = rotateMatrix.Transform(leadEndVector) + activePoints[0];
+            Point leftStartPoint = rotateMatrix.Transform(leadStartVector) + activePoints[0];
+            Point leftCrossPoint = rotateMatrix.Transform(leadCrossVector) + activePoints[0];
+
+            leftLine.Add(leftStartPoint);
+            leftLine.Add(leftCrossPoint);
+            crossLine.Add(leftCrossPoint);
+
+            lineVector = activePoints[1] - activePoints[0];
+            rightLine.Add(leftStartPoint + lineVector);
+            rightLine.Add(leftCrossPoint + lineVector);
+            crossLine.Add(leftCrossPoint + lineVector);
+            activePoints.Insert(1, new Point(
+                (crossLine[0].X + crossLine[1].X) / 2,
+                (crossLine[0].Y + crossLine[1].Y) / 2
+                ));
+        }
+
+        #region Event
+
+        public virtual void OnMouseLeftButtonDown(Point downPoint)
+        {
+            isMouseDown = true;
+            hitIndex = -1;
+            mouseDownPoint = downPoint;
+            moveOffset = new Point(0, 0);
+            HitTestResult hitResult = VisualTreeHelper.HitTest(this, downPoint);
+            if (hitResult != null && hitResult.VisualHit is DrawingVisual)
+            {
+                hitIndex = GetHitIndex(downPoint);
+            }
+        }
+
+        public virtual void OnMouseMove(Point mousePoint, out bool Tag)
+        {
+            Tag = false;
+            if (isMouseDown)
+            {
+                Tag = isMouseDown;
+                mouseEndDrawPoint = mousePoint;
+
+                Point newOffset = new Point(
+                    mouseEndDrawPoint.X - mouseDownPoint.X,
+                    mouseEndDrawPoint.Y - mouseDownPoint.Y
+                    );
+
+                Point movePoint = CheckMoveOffSet(activePoints.ToList(), maxRect, newOffset);
+                if(movePoint.X==0)
+                {
+                    newOffset.X=moveOffset.X;
+                }
+                if(movePoint.Y==0)
+                {
+                    newOffset.Y=moveOffset.Y;
+                }
+                moveOffset = newOffset;
+                Draw();
+                InvokeDataChangEvent(false);
+            }
+        }
+
+        public virtual void OnMouseLeftButtonUp(Point upPoint)
+        {
+            isMouseDown = false;
+            Draw();
+            if (annotData!=null&&annotData.AnnotType == C_ANNOTATION_TYPE.C_ANNOTATION_LINE)
+            {
+                if ((annotData.Annot as CPDFLineAnnotation).IsMersured())
+                {
+                    activePoints.Clear();
+
+                    if (moveLeftLine == null)
+                    {
+                        moveLeftLine = leftLine.ToArray();
+                    }
+                    if (moveRightLine == null)
+                    {
+                        moveRightLine = rightLine.ToArray();
+                    }
+                    if (moveCrossLine == null)
+                    {
+                        moveCrossLine = crossLine.ToArray();
+                    }
+                    activePoints.Add(moveLeftLine[0]);
+                    activePoints.Add(moveLeftLine[1]);
+                    activePoints.Add(moveRightLine[0]);
+                    activePoints.Add(moveRightLine[1]);
+                    activePoints.Add(moveCrossLine[0]);
+                    activePoints.Add(moveCrossLine[1]);
+                }
+            }
+            moveLeftLine = null;
+            moveRightLine = null;
+            moveCrossLine = null;
+            if (moveOffset!= new Point(0, 0))
+            {
+                InvokeDataChangEvent(true);
+            }
+            moveOffset = new Point(0, 0);
+            mouseDownPoint = new Point();
+
+            mouseEndDrawPoint = new Point();
+            hitIndex = -1;
+        }
+
+        /// <summary>
+        /// Used to notify events during/after drawing data
+        /// </summary>
+        /// <param name="isFinish">
+        /// Is the data change complete
+        /// </param>
+        protected void InvokeDataChangEvent(bool isFinish)
+        {
+            PointCollection ActivePoints1 = new PointCollection();
+            for (int i = 0; i < activePoints.Count; i++)
+            {
+                Point currentPoint = activePoints[i];
+                if (hitIndex == -1)
+                {
+                    if (annotData.AnnotType == C_ANNOTATION_TYPE.C_ANNOTATION_LINE)
+                    {
+                        if (!(annotData.Annot as CPDFLineAnnotation).IsMersured())
+                        {
+                            currentPoint.X += moveOffset.X;
+                            currentPoint.Y += moveOffset.Y;
+                        }
+                    }
+                    else
+                    {
+                        currentPoint.X += moveOffset.X;
+                        currentPoint.Y += moveOffset.Y;
+                    }
+                }
+                ActivePoints1.Add(currentPoint);
+            }
+            annotEditData.Points = ActivePoints1;
+            if (isFinish)
+            {
+                DataChanged?.Invoke(this, annotEditData);
+            }
+            else
+            {
+                DataChanging?.Invoke(this, annotEditData);
+            }
+        }
+
+        #endregion
+
+        #region Draw
+
+        public void Draw()
+        {
+            Dispatcher.Invoke(() =>
+            {
+                if (annotData == null)
+                {
+                    return;
+                }
+                drawDC = RenderOpen();
+
+                switch (annotData.Annot.Type)
+                {
+                    case C_ANNOTATION_TYPE.C_ANNOTATION_LINE:
+                        {
+                            if ((annotData.Annot as CPDFLineAnnotation).IsMersured())
+                            {
+                                DrawLineMeasure(drawDC);
+                            }
+                            else
+                            {
+                                DrawLine(drawDC);
+                            }
+                        }
+                        break;
+                    case C_ANNOTATION_TYPE.C_ANNOTATION_POLYLINE:
+                        DrawPolyLineMeasure(drawDC);
+                        break;
+                    case C_ANNOTATION_TYPE.C_ANNOTATION_POLYGON:
+                        DrawPolygonMeasure(drawDC);
+                        break;
+                    default:
+                        break;
+                }
+
+                drawDC?.Close();
+                drawDC = null;
+            });
+        }
+
+        private Point CheckPointBound(Point checkPoint,Rect bound)
+        {
+            if (checkPoint.X < bound.Left)
+            {
+                checkPoint.X = bound.Left;
+            }
+            if (checkPoint.X > bound.Right)
+            {
+                checkPoint.X = bound.Right;
+            }
+            if (checkPoint.Y < bound.Top)
+            {
+                checkPoint.Y = bound.Top;
+            }
+            if (checkPoint.Y > bound.Bottom)
+            {
+                checkPoint.Y = bound.Bottom;
+            }
+
+            return checkPoint;
+        }
+
+        private Point CheckMoveOffSet(List<Point> checkPoints, Rect bound, Point moveOffset)
+        {
+            double left = 0;
+            double top = 0;
+            double right = 0;
+            double bottom = 0;
+
+            if (checkPoints != null && checkPoints.Count > 0)
+            {
+                left = checkPoints.AsEnumerable().Select(p => p.X).Min();
+                right = checkPoints.AsEnumerable().Select(p => p.X).Max();
+
+                top = checkPoints.AsEnumerable().Select(p => p.Y).Min();
+                bottom = checkPoints.AsEnumerable().Select(p => p.Y).Max();
+            }
+
+            Point movePoint = moveOffset;
+
+            if(left+moveOffset.X<bound.Left || right+moveOffset.X>bound.Right)
+            {
+                movePoint.X = 0;
+            }
+
+            if (top + moveOffset.Y < bound.Top || bottom + moveOffset.Y > bound.Bottom)
+            {
+                movePoint.Y = 0;
+            }
+
+            return movePoint;
+        }
+
+        private void DrawLine(DrawingContext drawingContext)
+        {
+            PathGeometry drawPath = new PathGeometry();
+            PathFigure drawFigure = new PathFigure();
+
+            PolyLineSegment polySegment = new PolyLineSegment();
+            if (hitIndex != -1 && hitIndex < activePoints.Count)
+            {
+                if (mouseEndDrawPoint!=new Point() && !activePoints.Contains(mouseEndDrawPoint))
+                {
+                    activePoints[hitIndex] = CheckPointBound(mouseEndDrawPoint, maxRect);
+                }
+            }
+
+            Point StartPoint = activePoints[0];
+            if (hitIndex == -1)
+            {
+                StartPoint.X += moveOffset.X;
+                StartPoint.Y += moveOffset.Y;
+            }
+            
+            drawFigure.StartPoint = StartPoint;
+            for (int i = 1; i < activePoints.Count; i++)
+            {
+                Point currentPoint = activePoints[i];
+                if (hitIndex == -1)
+                {
+                    currentPoint.X += moveOffset.X;
+                    currentPoint.Y += moveOffset.Y;
+                }
+                polySegment.Points.Add(currentPoint);
+            }
+
+            if (polySegment.Points.Count > 0)
+            {
+                drawFigure.Segments.Add(polySegment);
+            }
+
+            if (drawFigure.Segments.Count > 0)
+            {
+                drawPath.Figures.Add(drawFigure);
+            }
+
+            foreach (Point controlPoint in activePoints)
+            {
+                Point drawPoint = new Point(
+                    controlPoint.X,
+                    controlPoint.Y);
+                if (hitIndex == -1)
+                {
+                    drawPoint.X += moveOffset.X;
+                    drawPoint.Y += moveOffset.Y;
+                }
+                drawingContext?.DrawEllipse(drawParam.EditControlLineBrush, drawParam.EditControlLinePen, (drawPoint), pointSize, pointSize);
+            }
+            if (isMouseDown)
+            {
+                drawingContext?.DrawGeometry(null, drawParam.EditLinePen, drawPath);
+            }
+        }
+
+        private void DrawLineMeasure(DrawingContext drawingContext)
+        {
+            foreach (Point controlPoint in activePoints)
+            {
+                Point drawPoint = new Point(
+                    controlPoint.X,
+                    controlPoint.Y);
+                if (hitIndex == -1)
+                {
+                    drawPoint.X += moveOffset.X;
+                    drawPoint.Y += moveOffset.Y;
+                }
+                drawingContext?.DrawEllipse(drawParam.EditControlLineBrush, drawParam.EditControlLinePen, (drawPoint), pointSize, pointSize);
+            }
+            Rect drawRect = activeRect;
+            drawRect.X += moveOffset.X;
+            drawRect.Y += moveOffset.Y;
+            if (isMouseDown)
+            {
+                PointCollection drawLeftPoints = new PointCollection();
+                PointCollection drawRightPoints = new PointCollection();
+                PointCollection drawCrossPoints = new PointCollection();
+                moveLeftLine = leftLine.ToArray();
+                moveRightLine = rightLine.ToArray();
+                moveCrossLine = crossLine.ToArray();
+                switch (hitIndex)
+                { 
+                    case 0://Left
+                        {
+                            moveLeftLine[0].X += moveOffset.X;
+                            moveLeftLine[0].Y += moveOffset.Y;
+
+                            moveLeftLine[0].X = Math.Max(maxRect.Left, moveLeftLine[0].X);
+                            moveLeftLine[0].X = Math.Min(maxRect.Right, moveLeftLine[0].X);
+                            moveLeftLine[0].Y = Math.Max(maxRect.Top, moveLeftLine[0].Y);
+                            moveLeftLine[0].Y = Math.Min(maxRect.Bottom, moveLeftLine[0].Y);
+
+                            Vector newVector = moveLeftLine[0] - rightLine[0];
+                            Vector leftVector = leftLine[1] - leftLine[0];
+
+                            double angle = leftLine[0].Y < crossLine[0].Y ? -90 : 90;
+                            newVector.Normalize();
+                            newVector = newVector * leftVector.Length;
+                            Matrix rotateMatrix = new Matrix();
+                            rotateMatrix.Rotate(angle);
+                            moveLeftLine[1] = moveLeftLine[0] + newVector * rotateMatrix;
+                            moveRightLine[0] = rightLine[0];
+                            moveRightLine[1] = moveRightLine[0] + newVector * rotateMatrix;
+                            moveCrossLine[0] = moveLeftLine[1];
+                            moveCrossLine[1] = moveRightLine[1];
+                        }
+                        break;
+                    case 1:// Center
+                        {
+                            Point centerPoint = new Point(
+                            (crossLine[0].X + crossLine[1].X) / 2,
+                            (crossLine[0].Y + crossLine[1].Y) / 2
+                            );
+                            Point movePoint = new Point(centerPoint.X, centerPoint.Y);
+                            movePoint.X += moveOffset.X;
+                            movePoint.Y += moveOffset.Y;
+                            Vector ruleVector = crossLine[1] - crossLine[0];
+
+                            bool rateMove = true;
+                            if (ruleVector.X == 0)
+                            {
+                                movePoint.Y = centerPoint.Y;
+                                rateMove = false;
+                            }
+                            if (ruleVector.Y == 0)
+                            {
+                                movePoint.X = centerPoint.X;
+                                rateMove = false;
+                            }
+                            if (rateMove)
+                            {
+                                Vector moveVector = movePoint - centerPoint;
+                                double moveLength = moveVector.Length;
+                                double ruleLength = ruleVector.Length;
+                                ruleVector.Normalize();
+                                moveVector.Normalize();
+                                Vector crossVector = new Vector(-ruleVector.Y, ruleVector.X);
+                                crossVector.Normalize();
+
+                                if (Math.Abs(Vector.AngleBetween(moveVector, crossVector)) > 90)
+                                {
+                                    crossVector.Negate();
+                                }
+                                Point saveCenter = crossVector * moveLength + centerPoint;
+                                double halfLenght = ruleLength / 2;
+                                Point SaveRight = ruleVector * halfLenght + saveCenter;
+                                Point saveLeft = saveCenter - ruleVector * halfLenght;
+
+                                moveCrossLine[0] = saveLeft;
+                                moveCrossLine[1] = SaveRight;
+                                moveLeftLine[1] = saveLeft;
+                                moveRightLine[1] = SaveRight;
+                                moveLeftLine[0] = leftLine[0];
+                                moveRightLine[0] = rightLine[0];
+                            }
+                            else
+                            {
+                                Point moveOffset = new Point(
+                                    movePoint.X - centerPoint.X,
+                                    movePoint.Y - centerPoint.Y);
+                                moveCrossLine[0].X += moveOffset.X;
+                                moveCrossLine[0].Y += moveOffset.Y;
+                                moveCrossLine[1].X += moveOffset.X;
+                                moveCrossLine[1].Y += moveOffset.Y;
+
+                                moveLeftLine[1].X += moveOffset.X;
+                                moveLeftLine[1].Y += moveOffset.Y;
+
+                                moveRightLine[1].X += moveOffset.X;
+                                moveRightLine[1].Y += moveOffset.Y;
+                            }
+
+                        }
+                        break;
+                    case 2://Right
+                        {
+                            moveRightLine[0].X += moveOffset.X;
+                            moveRightLine[0].Y += moveOffset.Y;
+
+                            Vector newVector = moveRightLine[0] - leftLine[0];
+                            Vector leftVector = rightLine[1] - rightLine[0];
+
+                            double angle = (rightLine[0].Y + leftLine[0].Y) / 2 > (crossLine[0].Y + crossLine[1].Y) / 2 ? -90 : 90;
+                            newVector.Normalize();
+                            newVector = newVector * leftVector.Length;
+                            Matrix rotateMatrix = new Matrix();
+                            rotateMatrix.Rotate(angle);
+                            moveLeftLine[1] = moveLeftLine[0] + newVector * rotateMatrix;
+                            moveLeftLine[0] = leftLine[0];
+                            moveRightLine[1] = moveRightLine[0] + newVector * rotateMatrix;
+                            moveCrossLine[0] = moveLeftLine[1];
+                            moveCrossLine[1] = moveRightLine[1];
+                        }
+                        break;
+                    case -1:
+                        moveLeftLine[0].X += moveOffset.X;
+                        moveLeftLine[0].Y += moveOffset.Y;
+                        moveLeftLine[1].X += moveOffset.X;
+                        moveLeftLine[1].Y += moveOffset.Y;
+
+                        moveRightLine[0].X += moveOffset.X;
+                        moveRightLine[0].Y += moveOffset.Y;
+                        moveRightLine[1].X += moveOffset.X;
+                        moveRightLine[1].Y += moveOffset.Y;
+
+                        moveCrossLine[0] = moveLeftLine[1];
+                        moveCrossLine[1] = moveRightLine[1];
+                        break;
+                    default:
+                        break;
+                }
+                //Left
+                drawLeftPoints.Add(new Point(
+                    moveLeftLine[0].X,
+                    moveLeftLine[0].Y));
+                drawLeftPoints.Add(new Point(
+                   moveLeftLine[1].X,
+                   moveLeftLine[1].Y));
+
+                //Right
+                drawRightPoints.Add(new Point(
+                    moveRightLine[0].X,
+                    moveRightLine[0].Y));
+                drawRightPoints.Add(new Point(
+                   moveRightLine[1].X,
+                   moveRightLine[1].Y));
+
+                //Middle
+                drawCrossPoints.Add(new Point(
+                    moveCrossLine[0].X,
+                    moveCrossLine[0].Y));
+
+                drawCrossPoints.Add(new Point(
+                    moveCrossLine[1].X,
+                    moveCrossLine[1].Y));
+
+
+                drawingContext?.DrawLine(drawParam.EditLinePen, drawLeftPoints[0], drawLeftPoints[1]);
+                drawingContext?.DrawLine(drawParam.EditLinePen, drawRightPoints[0], drawRightPoints[1]);
+                drawingContext?.DrawLine(drawParam.EditLinePen, drawCrossPoints[0], drawCrossPoints[1]);
+            }
+            else
+            {
+                drawingContext?.DrawRectangle(null, drawParam.EditLinePen, drawRect);
+            }
+        }
+
+        private void DrawPolyLineMeasure(DrawingContext drawingContext)
+        {
+            PathGeometry drawPath = new PathGeometry();
+            PathFigure drawFigure = new PathFigure();
+
+            PolyLineSegment polySegment = new PolyLineSegment();
+            if (hitIndex != -1 && hitIndex < activePoints.Count)
+            {
+                if (mouseEndDrawPoint != new Point())
+                {
+                    activePoints[hitIndex] = mouseEndDrawPoint;
+                }
+            }
+
+            Point StartPoint = activePoints[0];
+            if (hitIndex == -1)
+            {
+                StartPoint.X += moveOffset.X;
+                StartPoint.Y += moveOffset.Y;
+            }
+            drawFigure.StartPoint = StartPoint;
+            for (int i = 1; i < activePoints.Count; i++)
+            {
+                Point currentPoint = activePoints[i];
+                if (hitIndex == -1)
+                {
+                    currentPoint.X += moveOffset.X;
+                    currentPoint.Y += moveOffset.Y;
+                }
+                polySegment.Points.Add(currentPoint);
+            }
+
+            if (polySegment.Points.Count > 0)
+            {
+                drawFigure.Segments.Add(polySegment);
+            }
+
+            if (drawFigure.Segments.Count > 0)
+            {
+                drawPath.Figures.Add(drawFigure);
+            }
+            foreach (Point controlPoint in activePoints)
+            {
+                Point drawPoint = new Point(
+                    controlPoint.X,
+                    controlPoint.Y);
+                if (hitIndex == -1)
+                {
+                    drawPoint.X += moveOffset.X;
+                    drawPoint.Y += moveOffset.Y;
+                }
+                drawingContext?.DrawEllipse(drawParam.EditControlLineBrush, drawParam.EditControlLinePen, (drawPoint), pointSize, pointSize);
+            }
+            Rect drawRect = activeRect;
+            drawRect.X += moveOffset.X;
+            drawRect.Y += moveOffset.Y;
+            if (isMouseDown)
+            {
+                drawingContext?.DrawGeometry(null, drawParam.EditLinePen, drawPath);
+            }
+            else
+            {
+                drawingContext?.DrawRectangle(null, drawParam.EditLinePen, drawRect);
+            }
+        }
+
+        private void DrawPolygonMeasure(DrawingContext drawingContext)
+        {
+            PathGeometry drawPath = new PathGeometry();
+            PathFigure drawFigure = new PathFigure();
+
+            PolyLineSegment polySegment = new PolyLineSegment();
+            if (hitIndex != -1 && hitIndex < activePoints.Count)
+            {
+                if (mouseEndDrawPoint!=new Point())
+                {
+                    activePoints[hitIndex] = mouseEndDrawPoint;
+                }
+            }
+
+            Point StartPoint = activePoints[0];
+            if (hitIndex == -1)
+            {
+                StartPoint.X += moveOffset.X;
+                StartPoint.Y += moveOffset.Y;
+            }
+            drawFigure.StartPoint = StartPoint;
+            for (int i = 1; i < activePoints.Count; i++)
+            {
+                Point currentPoint = activePoints[i];
+                if (hitIndex == -1)
+                {
+                    currentPoint.X += moveOffset.X;
+                    currentPoint.Y += moveOffset.Y;
+                }
+                polySegment.Points.Add(currentPoint);
+            }
+
+            if (polySegment.Points.Count > 0)
+            {
+                polySegment.Points.Add(drawFigure.StartPoint);
+                drawFigure.Segments.Add(polySegment);
+            }
+
+            if (drawFigure.Segments.Count > 0)
+            {
+                drawPath.Figures.Add(drawFigure);
+            }
+            foreach (Point controlPoint in activePoints)
+            {
+                Point drawPoint = new Point(
+                    controlPoint.X,
+                    controlPoint.Y);
+                if (hitIndex == -1)
+                {
+                    drawPoint.X += moveOffset.X;
+                    drawPoint.Y += moveOffset.Y;
+                }
+                drawingContext?.DrawEllipse(drawParam.EditControlLineBrush, drawParam.EditControlLinePen, (drawPoint), pointSize, pointSize);
+            }
+            Rect drawRect = activeRect;
+            drawRect.X += moveOffset.X;
+            drawRect.Y += moveOffset.Y;
+            if (isMouseDown)
+            {
+                drawingContext?.DrawGeometry(null, drawParam.EditLinePen, drawPath);
+            }
+            else
+            {
+                drawingContext?.DrawRectangle(null, drawParam.EditLinePen, drawRect);
+            }
+        }
+
+        public virtual void ClearDraw()
+        {
+            drawDC = RenderOpen();
+            drawDC?.Close();
+            drawDC = null;
+        }
+
+        #endregion
+    }
+}

+ 300 - 0
Demo/Examples/ComPDFKit.Tool/DrawTool/CaretVisual.cs

@@ -0,0 +1,300 @@
+using ComPDFKit.Import;
+using ComPDFKit.PDFPage;
+using ComPDFKit.PDFPage.Edit;
+using ComPDFKit.Tool.SettingParam;
+using ComPDFKit.Viewer.Helper;
+using ComPDFKitViewer.Helper;
+using System;
+using System.Collections.Generic;
+using System.Timers;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Media;
+using System.Windows.Threading;
+
+namespace ComPDFKit.Tool.DrawTool
+{
+    /// <summary>
+    /// Use to draw the cursor and selected effect of text editing
+    /// </summary>
+    internal class CaretVisual : DrawingVisual
+    {
+        #region Attributes
+
+        private Timer caretTimer;
+
+        protected DefaultDrawParam drawParam = new DefaultDrawParam();
+
+        private bool lastShow = true;
+
+        private Point caretHeight = new Point(0, 0);
+
+        private Point cursorPoint = new Point(0, 0);
+
+        private double currentZoom = 1;
+
+        private Rect paintOffset { get; set; }
+
+        /// <summary>
+        /// Selected text's collection of drawn rectangles.
+        /// </summary>
+        List<Rect> selectRects { get; set; } = new List<Rect>();
+
+        #endregion
+
+        #region  Timer
+
+        public void StartTimer()
+        {
+            if (caretTimer.Enabled == false)
+            {
+                caretTimer.Start();
+            }
+        }
+
+        public void StopTimer()
+        {
+            caretTimer.Stop();
+            cursorPoint = new Point(0, 0);
+            caretHeight = new Point(0, 0);
+            currentZoom = 0;
+            paintOffset = new Rect();
+        }
+
+        private void CaretTimerElapsed(object sender, ElapsedEventArgs e)
+        {
+            if ((cursorPoint - caretHeight).Length > 0)
+            {
+                Dispatcher.InvokeAsync(() =>
+                {
+                    Draw(lastShow);
+                });
+            }
+        }
+
+        #endregion
+
+        /// <summary>
+        /// Re-locate the child elements
+        /// </summary>
+        public void Arrange()
+        {
+            foreach (Visual child in Children)
+            {
+                if (!(child is UIElement))
+                {
+                    continue;
+                }
+                UIElement checkChild = child as UIElement;
+                try
+                {
+                    double left = Canvas.GetLeft(checkChild);
+                    double top = Canvas.GetTop(checkChild);
+                    double width = (double)checkChild.GetValue(FrameworkElement.WidthProperty);
+                    double height = (double)checkChild.GetValue(FrameworkElement.HeightProperty);
+                    checkChild.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
+                    checkChild.Arrange(new Rect(
+                        double.IsNaN(left) ? 0 : left,
+                        double.IsNaN(top) ? 0 : top,
+                        double.IsNaN(width) ? checkChild.DesiredSize.Width : width,
+                        double.IsNaN(height) ? checkChild.DesiredSize.Height : height));
+                }
+                catch (Exception ex)
+                {
+
+                }
+            }
+        }
+
+        public CaretVisual(DefaultDrawParam defaultDrawParam)
+        {
+            caretTimer = new Timer(500);
+            caretTimer.Elapsed += CaretTimerElapsed;
+            caretTimer.AutoReset = true;
+            drawParam = defaultDrawParam;
+        }
+
+        /// <summary>
+        /// Set the selected text effect area in text editing
+        /// </summary>
+        /// <param name="SelectLineRects">
+        /// Rect of PDF
+        /// </param>
+        public void SetSelectRect(List<Rect> SelectLineRects)
+        {
+            selectRects.Clear();
+            for (int i = 0; i < SelectLineRects.Count; i++)
+            {
+                selectRects.Add(DpiHelper.PDFRectToStandardRect(SelectLineRects[i]));
+            }
+        }
+
+        public void SetZoom(double Zoom)
+        {
+            currentZoom = Zoom;
+        }
+
+        /// <summary>
+        /// Set the current page drawing area (standard DPI)
+        /// </summary>
+        /// <param name="paintOffset">
+        /// The current page drawing area (standard DPI)
+        /// </param>
+        public void SetPaintOffset(Rect paintOffset)
+        {
+            this.paintOffset = paintOffset;
+        }
+
+        /// <summary>
+        /// Set the current cursor position
+        /// </summary>
+        /// <param name="EditArea">
+        /// Data object being edited
+        /// </param>
+        /// <param name="Zoom">
+        /// Current zoom factor
+        /// </param>
+        /// <param name="paintRect">
+        /// Current text box drawing area (standard DPI)
+        /// </param>
+        /// <param name="paintOffset">
+        /// Current page drawing area (standard DPI)
+        /// </param>
+        /// <returns>
+        /// Set error return false
+        /// </returns>
+        public bool SetCaretVisualArea(CPDFEditArea EditArea, double Zoom, Rect paintOffset, Point mousePoint)
+        {
+            if (EditArea.Type == CPDFEditType.EditText)
+            {
+                cursorPoint = new Point(0, 0);
+                // Mouse coordinates on the current page (PDF).
+                Point pagePoint = new Point(((mousePoint.X - paintOffset.X) / Zoom),
+                    ((mousePoint.Y - paintOffset.Y) / Zoom));
+
+                // Call the SDK interface to set the coordinates and get the current cursor position and height
+                pagePoint = DpiHelper.StandardPointToPDFPoint(pagePoint);
+                (EditArea as CPDFEditTextArea).SelectCharItemAtPos(DataConversionForWPF.PointConversionForCPoint(pagePoint));
+
+                CPoint caretCPoint = new CPoint(0, 0);
+                CPoint HighCpoint = new CPoint(0, 0);
+                (EditArea as CPDFEditTextArea).GetTextCursorPoints(ref caretCPoint, ref HighCpoint);
+
+                Point caretPoint = DataConversionForWPF.CPointConversionForPoint(caretCPoint);
+                Point pointHigh = DataConversionForWPF.CPointConversionForPoint(HighCpoint); 
+                // Converting SDK return values into data required for drawing
+                //CaretHeight = DpiHelper.PDFNumToStandardNum((caretPoint - pointHigh).Length);
+                //Rect caretRect = new Rect(caretPoint, pointHigh);
+                caretHeight = DpiHelper.PDFPointToStandardPoint(pointHigh);
+                cursorPoint = DpiHelper.PDFPointToStandardPoint(caretPoint);
+                currentZoom = Zoom;
+                this.paintOffset = paintOffset;
+                return true;
+            }
+
+            return false;
+        }
+
+        /// <summary>
+        /// Set the current cursor position
+        /// </summary>
+        /// <param name="paintOffset">
+        /// The lowPoint obtained from the SDK 
+        /// </param>
+        /// <param name="mousePoint">
+        ///The lowPoint obtained from the SDK.
+        /// </param>
+        /// <returns>
+        /// Set error return false
+        /// </returns>
+        public bool SetCaretVisualArea(Point lowPoint, Point highPoint)
+        {
+            caretHeight = DpiHelper.PDFPointToStandardPoint(lowPoint);
+            cursorPoint = DpiHelper.PDFPointToStandardPoint(highPoint);
+            return true;
+        }
+
+        /// <summary>
+        /// The low point of the cursor (96 DPI). 
+        /// </summary>
+        /// <returns>
+        /// The low point of the cursor (96 DPI).
+        /// </returns>
+        public Point GetCaretLowPoint()
+        {
+            return cursorPoint;
+        }
+
+        /// <summary> 
+        /// The high point of the cursor (96 DPI).
+        /// </summary>
+        /// <returns>
+        /// The high point of the cursor (96 DPI).
+        /// </returns>
+        public Point GetCaretHighPoint()
+        {
+            return caretHeight;
+        }
+
+        /// <summary>
+        /// Draw method.
+        /// </summary>
+        /// <param name="isLastShow">
+        ///  Indicates whether to immediately refresh
+        /// </param>
+        /// <param name="isShowCaret"> 
+        /// Indicates whether to draw the cursor.
+        /// </param>
+        public void Draw(bool isLastShow, bool isShowCaret = true)
+        {
+            using (DrawingContext dc = RenderOpen())
+            {
+                RectangleGeometry clipGeometry = new RectangleGeometry();
+
+                Point CaretPos = new Point(
+                   cursorPoint.X * currentZoom + paintOffset.X,
+                    cursorPoint.Y * currentZoom + paintOffset.Y);
+
+                Point CaretHeight = new Point(
+                   caretHeight.X * currentZoom + paintOffset.X,
+                    caretHeight.Y * currentZoom + paintOffset.Y);
+                if (isLastShow)
+                {
+                    if (isShowCaret)
+                    {
+                        dc.DrawLine(drawParam.CaretPen, CaretPos, CaretHeight);
+                    }
+                }
+                foreach (Rect selectRect in selectRects)
+                {
+                    Rect paintRect = new Rect(
+                        (int)(selectRect.X * currentZoom + paintOffset.X),
+                        (int)(selectRect.Y * currentZoom + paintOffset.Y),
+                        (int)(selectRect.Width * currentZoom),
+                        (int)(selectRect.Height * currentZoom));
+
+                    dc?.DrawRectangle(drawParam.CaretBrush, null, paintRect);
+                }
+                dc.Close();
+            }
+            lastShow = !isLastShow;
+        }
+
+        public void CleanDraw()
+        {
+            DrawingContext dc = RenderOpen();
+            dc.Close();
+        }
+
+        public void CleanSelectRectDraw()
+        {
+            selectRects.Clear();
+            Draw(true);
+        }
+
+        public void StopCaret()
+        {
+            caretTimer.Stop();
+        }
+    }
+}

Разница между файлами не показана из-за своего большого размера
+ 1877 - 0
Demo/Examples/ComPDFKit.Tool/DrawTool/CreateAnnotTool.cs


+ 314 - 0
Demo/Examples/ComPDFKit.Tool/DrawTool/CreateCustomizeTool.cs

@@ -0,0 +1,314 @@
+using ComPDFKit.Import;
+using ComPDFKit.PDFAnnotation;
+using ComPDFKit.PDFDocument;
+using ComPDFKit.PDFPage;
+using ComPDFKit.Tool.Help;
+using ComPDFKit.Tool.UndoManger;
+using ComPDFKit.Viewer.Layer;
+using ComPDFKitViewer;
+using ComPDFKitViewer.Annot;
+using ComPDFKitViewer.BaseObject;
+using ComPDFKitViewer.Helper;
+using ComPDFKitViewer.Layer;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Media;
+
+namespace ComPDFKit.Tool.DrawTool
+{
+    public enum CustomizeToolType
+    {
+        kUnknown,
+        kErase,
+    }
+    internal class CreateCustomizeTool : CustomizeLayer
+    {
+        /// <summary>
+        /// Start drawing point
+        /// </summary>
+        protected Point mouseStartPoint { get; set; }
+
+        /// <summary>
+        /// End drawing point
+        /// </summary>
+        protected Point mouseEndPoint { get; set; }
+
+        /// <summary>
+        /// Identifies whether the mouse is pressed
+        /// </summary>
+        protected bool isMouseDown { get; set; }
+
+        /// <summary>
+        /// Zoom factor
+        /// </summary>
+        private double zoomFactor { get; set; } = 1;
+
+        /// <summary>
+        /// Rect for drawing
+        /// </summary>
+        protected Rect drawRect { get; set; }
+
+        /// <summary>
+        /// Max drawing rect
+        /// </summary>
+        protected Rect maxRect { get; set; }
+
+        /// <summary>
+        /// Original page range rectangle (calculate offset in continuous mode)
+        /// </summary>
+        protected Rect pageBound { get; set; }
+
+        /// <summary>
+        /// Standard DPI rectangle (without removing half of the pen thickness)
+        /// </summary>
+        protected Rect DPIRect { get; set; }
+        public SolidColorBrush FillBrush;
+        public Pen DrawPen;
+        public event EventHandler<List<AnnotParam>> DeleteChanged;
+        protected DrawingContext drawDC { get; set; }
+        private CustomizeToolType customizeToolType { get; set; } = CustomizeToolType.kUnknown;
+        private CPDFPage currentPage = null;
+        private AnnotLayer annotLayer { get; set; } = null;
+
+        private double defEraseThickness = 5;
+
+        private double eraseZoom = 1;
+        private SolidColorBrush eraseBrush { get; set; } = new SolidColorBrush(Colors.LightGray);
+        internal CPDFViewer PDFViewer { get; set; }
+        public CreateCustomizeTool()
+        {
+
+        }
+
+        public void SetDefEraseThickness(double defEraseThickness)
+        {
+            this.defEraseThickness = defEraseThickness;
+        }
+
+        public void SetEraseZoom(double eraseZoom)
+        {
+            this.eraseZoom = eraseZoom;
+        }
+
+        public void SetEraseBrush(SolidColorBrush drawBrush)
+        {
+            if(drawBrush == null)
+            {
+                return;
+            }
+            eraseBrush = drawBrush;
+        }
+
+        public void SetAnnotLayer(AnnotLayer layer)
+        {
+            annotLayer = layer;
+        }
+
+        public void StartDraw(Point downPoint, CPDFPage cPDFPage, Rect maxRect, Rect pageBound, CustomizeToolType ToolType)
+        {
+            customizeToolType = ToolType;
+            mouseStartPoint = downPoint;
+            isMouseDown = true;
+            this.maxRect = maxRect;
+            this.pageBound = pageBound;
+            DPIRect = new Rect();
+            currentPage = cPDFPage;
+        }
+
+        public void MoveDraw(Point downPoint, double zoom)
+        {
+            if (isMouseDown)
+            {
+                mouseEndPoint = downPoint;
+                zoomFactor = zoom;
+                DrawTool();
+            }
+        }
+
+        public void EndDraw()
+        {
+            if (isMouseDown)
+            {
+                isMouseDown = false;
+                mouseStartPoint = new Point();
+                mouseEndPoint = new Point();
+                pageBound = new Rect();
+                DPIRect = new Rect();
+                currentPage = null;
+                annotLayer = null;
+            }
+        }
+
+        public void DrawTool()
+        {
+            Dispatcher.Invoke(() =>
+            {
+                drawDC = Open();
+
+                switch (customizeToolType)
+                {
+                    case CustomizeToolType.kUnknown:
+                        break;
+                    case CustomizeToolType.kErase:
+                        DrawErase(drawDC);
+                        break;
+                    default:
+                        break;
+                }
+
+                Present();
+            });
+        }
+
+        public void ClearDraw()
+        {
+            Open();
+            Present();
+        }
+
+        /// <summary>
+        /// Erase the hand-drawn
+        /// </summary>
+        /// <returns>
+        /// 1 delete 0 erase -1 not intersect
+        /// </returns>
+        private int ErasePoint(InkAnnot AnnotCore, Rect eraseRect)
+        {
+            Rect rawErase = eraseRect;
+            List<List<Point>> addPointList = new List<List<Point>>();
+            List<List<Point>> RawPointList = new List<List<Point>>();
+            CPDFInkAnnotation AnnotInk = (AnnotCore.GetAnnotData().Annot as CPDFInkAnnotation);
+            bool isErasePoint = false;
+            if (AnnotInk.InkPath != null)
+            {
+                foreach (var Item in AnnotInk.InkPath)
+                {
+                    List<Point> PointList = new List<Point>();
+                    foreach (var RawPoint in Item)
+                    {
+                        PointList.Add(DpiHelper.PDFPointToStandardPoint(new Point(RawPoint.x, RawPoint.y)));
+                    }
+                    RawPointList.Add(PointList);
+                }
+            }
+            foreach (List<Point> Item in RawPointList)
+            {
+                List<Point> addItem = new List<Point>();
+
+                foreach (Point checkPoint in Item)
+                {
+                    if (rawErase.Contains(checkPoint) == false)
+                    {
+                        addItem.Add(checkPoint);
+                    }
+                    else
+                    {
+                        isErasePoint = true;
+                        if (addItem.Count > 2)
+                        {
+                            addPointList.Add(addItem);
+                        }
+                        addItem = new List<Point>();
+                    }
+                }
+                if (addItem.Count > 2)
+                {
+                    addPointList.Add(addItem);
+                }
+            }
+            RawPointList = addPointList;
+
+            if (addPointList.Count == 0)
+            {
+                //delete annot
+                return 1;
+            }
+
+            List<List<CPoint>> inkPathList = new List<List<CPoint>>();
+            CPDFInkAnnotation annotInk = (AnnotCore.GetAnnotData().Annot as CPDFInkAnnotation);
+            foreach (List<Point> inkNode in RawPointList)
+            {
+                List<CPoint> inkPath = new List<CPoint>();
+                foreach (Point addPoint in inkNode)
+                {
+                    inkPath.Add(new CPoint((float)DpiHelper.StandardNumToPDFNum(addPoint.X), (float)DpiHelper.StandardNumToPDFNum(addPoint.Y)));
+                }
+
+                inkPathList.Add(inkPath);
+            }
+
+            if (isErasePoint)
+            {
+                if (!annotInk.IsValid())
+                {
+                    return -1;
+                }
+             
+                annotInk.SetInkPath(inkPathList);
+                annotInk.UpdateAp();
+                AnnotCore.Draw();
+            }
+
+            return isErasePoint ? 0 : -1;
+        }
+        private void DrawErase(DrawingContext drawingContext)
+        {
+            Rect drawRect = new Rect(mouseEndPoint.X - defEraseThickness* eraseZoom, mouseEndPoint.Y - defEraseThickness * eraseZoom, defEraseThickness * 2* eraseZoom, defEraseThickness * 2* eraseZoom); 
+            Rect eraseRect = new Rect((drawRect.Left - pageBound.Left)/zoomFactor,
+                       (drawRect.Top - pageBound.Top)/zoomFactor,
+                        drawRect.Width / zoomFactor,
+                        drawRect.Height / zoomFactor);
+            drawingContext?.DrawEllipse(eraseBrush, null, new Point(mouseEndPoint.X, mouseEndPoint.Y), defEraseThickness* eraseZoom, defEraseThickness* eraseZoom);
+            if (annotLayer==null)
+            {
+                return;
+            }
+            List<BaseAnnot> annotControlList= annotLayer.GetAnnotListForType(C_ANNOTATION_TYPE.C_ANNOTATION_INK);
+            GroupHistory historyGroup=new GroupHistory();
+            CPDFDocument pdfDoc = PDFViewer?.GetDocument();
+            List<AnnotParam> paramList = new List<AnnotParam>();
+            foreach (var item in annotControlList)
+            {
+                InkAnnot ink = item as InkAnnot;
+                int Tag = ErasePoint(ink, eraseRect);
+                if (Tag == 1)
+                {
+                    CPDFAnnotation delAnnot = item.GetAnnotData().Annot;
+                    AnnotHistory annotHistory = ParamConverter.CreateHistory(delAnnot);
+                    AnnotParam annotParam = null;
+                    if (pdfDoc != null)
+                    {
+                        annotParam = ParamConverter.CPDFDataConverterToAnnotParam(pdfDoc, delAnnot.Page.PageIndex, delAnnot);
+                        annotHistory.CurrentParam = annotParam;
+                        annotHistory.Action=HistoryAction.Remove;
+                        annotHistory.PDFDoc=pdfDoc;
+                        historyGroup.Histories.Add(annotHistory);
+                    }
+
+                    if (delAnnot.RemoveAnnot())
+                    {
+                        if(annotParam != null)
+                        {
+                            paramList.Add(annotParam);
+                        }
+                        historyGroup.Histories.Add(annotHistory);
+                    }
+                }
+            }
+
+            if(historyGroup.Histories.Count > 0 && PDFViewer!=null)
+            {
+                PDFViewer.UndoManager.AddHistory(historyGroup);
+            }
+            if(paramList.Count > 0)
+            {
+                DeleteChanged?.Invoke(this, paramList);
+            }
+        }
+    }
+}

+ 293 - 0
Demo/Examples/ComPDFKit.Tool/DrawTool/CreateWidgetTool.cs

@@ -0,0 +1,293 @@
+using ComPDFKit.PDFAnnotation;
+using ComPDFKit.PDFAnnotation.Form;
+using ComPDFKit.PDFPage;
+using ComPDFKit.Viewer.Layer;
+using ComPDFKitViewer.BaseObject;
+using ComPDFKitViewer.Helper;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net.NetworkInformation;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Media;
+using ComPDFKit.Tool.SettingParam;
+using ComPDFKit.Tool.Help;
+using System.Windows.Annotations;
+
+namespace ComPDFKit.Tool.DrawTool
+{
+    internal class CreateWidgetTool : CustomizeLayer
+    {
+        protected CPDFAnnotation cPDFAnnotation { get; set; }
+
+        /// <summary>
+        /// End point of the drawing
+        /// </summary>
+        protected Point mouseEndPoint { get; set; }
+
+        /// <summary>
+        /// Crop point
+        /// </summary>
+        protected Point cropPoint { get; set; }
+
+        protected bool hideDraw { get; set; } = false;
+
+        /// <summary>
+        ///Identify whether the annotation is being created
+        /// </summary>
+        protected bool isDrawAnnot { get; set; }
+
+        /// <summary>
+        /// Max drawing range rectangle
+        /// </summary>
+        protected Rect maxRect { get; set; }
+
+        /// <summary>
+        /// Start drawing point
+        /// </summary>
+        protected Point mouseStartPoint { get; set; }
+
+        /// <summary>
+        /// Original page range rectangle (calculate offset in continuous mode)
+        /// </summary>
+        protected Rect pageBound { get; set; }
+
+        /// <summary>
+        /// Standard DPI rectangle (without removing half of the pen thickness)
+        /// </summary>
+        protected Rect DPIRect { get; set; }
+
+        /// <summary>
+        /// Used to set the current zoom ratio
+        /// </summary>
+        private double zoomFactor { get; set; } = 1;
+
+        protected DrawingContext drawDC { get; set; }
+
+        private double minWidth = 15;
+
+        private double minHeight = 15;
+
+        private double defaultWidth { get; set; } = 0;
+
+        private double defaultHeight { get; set; } = 0;
+
+        private double PDFViewerActualWidth { get; set; } = 0;
+        private double PDFViewerActualHeight { get; set; } = 0;
+
+        private C_WIDGET_TYPE currentWidgetType = C_WIDGET_TYPE.WIDGET_UNKNOWN;
+
+        protected DefaultDrawParam drawParam = new DefaultDrawParam();
+        
+        public void SetDrawType(C_WIDGET_TYPE WidgetType)
+        {
+            currentWidgetType = WidgetType;
+        }
+
+        public void ReDrawWidget(double zoom)
+        {
+            zoomFactor = zoom;
+            Draw();
+        }
+
+        public CPDFAnnotation StartDraw(Point downPoint, Point cropPoint, CPDFPage cPDFPage, Rect maxRect, Rect pageBound, C_WIDGET_TYPE WidgetType)
+        {
+            if (WidgetType== C_WIDGET_TYPE.WIDGET_NONE)
+            {
+                return null;
+            }
+            mouseStartPoint = downPoint;
+            isDrawAnnot = true;
+            this.maxRect = maxRect;
+            int newIndex=cPDFPage.GetAnnotCount();
+            cPDFAnnotation = cPDFPage.CreateWidget(WidgetType);
+            if(cPDFAnnotation!=null)
+            {
+                cPDFAnnotation.SetCreationDate(PDFHelp.GetCurrentPdfTime());
+                cPDFAnnotation.SetModifyDate(PDFHelp.GetCurrentPdfTime());
+                List<CPDFAnnotation> annotList= cPDFPage.GetAnnotations();
+               cPDFAnnotation = annotList[newIndex];
+            }
+            this.cropPoint = cropPoint;
+            this.pageBound = pageBound;
+            DPIRect = new Rect();
+            return cPDFAnnotation;
+        }
+
+        public void MoveDraw(Point downPoint, double zoom, double width, double height, bool Hide, Rect maxRect)
+        {
+            hideDraw = Hide;
+            PDFViewerActualWidth = width;
+            PDFViewerActualHeight = height;
+
+            mouseEndPoint = downPoint;
+            zoomFactor = zoom;
+            if (!isDrawAnnot)
+            {
+                this.maxRect = maxRect;
+            }
+            Draw();
+        }
+
+        public Rect EndDraw()
+        {
+            if (isDrawAnnot)
+            {
+                Rect rect = DPIRect;
+                if (rect.Width<=1&& rect.Height <= 1)
+                {
+                    rect = new Rect(mouseStartPoint.X, mouseStartPoint.Y, defaultWidth * zoomFactor, defaultHeight * zoomFactor);
+                }
+                if (rect.Width < minWidth)
+                {
+                    rect.Width = defaultWidth * zoomFactor;
+                }
+                if (rect.Height < minHeight)
+                {
+                    rect.Height = defaultHeight * zoomFactor;
+                }
+                rect.Intersect(maxRect);
+                
+                Rect StandardRect = new Rect(
+                        (rect.Left - pageBound.X + (cropPoint.X * zoomFactor)) / zoomFactor,
+                        (rect.Top - pageBound.Y + (cropPoint.Y * zoomFactor)) / zoomFactor,
+                        rect.Width / zoomFactor, rect.Height / zoomFactor);
+                isDrawAnnot = false;
+                mouseStartPoint = new Point();
+                mouseEndPoint = new Point();
+                pageBound = new Rect();
+                DPIRect = new Rect();
+                cPDFAnnotation = null;
+                return DpiHelper.StandardRectToPDFRect(StandardRect);
+            }
+            return new Rect();
+        }
+
+        public override void Draw()
+        {
+            Dispatcher.Invoke(() =>
+            {
+                drawDC = Open();
+
+                if (hideDraw||currentWidgetType == C_WIDGET_TYPE.WIDGET_NONE||(cPDFAnnotation == null && isDrawAnnot))
+                {
+                    Present();
+                    return;
+                }
+
+                Point DrawPoint = new Point();
+                if (isDrawAnnot)
+                {
+                    DrawPoint = mouseStartPoint;
+                }
+                else
+                {
+                    DrawPoint = mouseEndPoint;
+                }
+                if (!maxRect.Contains(DrawPoint))
+                {
+                    Present();
+                    return;
+                }
+
+                drawDC?.DrawLine(drawParam.CreateWidgetPen, new Point(0, DrawPoint.Y), new Point(PDFViewerActualWidth, DrawPoint.Y));
+                drawDC?.DrawLine(drawParam.CreateWidgetPen, new Point(DrawPoint.X, 0), new Point(DrawPoint.X, PDFViewerActualHeight));
+                if (!isDrawAnnot)
+                {
+                    switch (currentWidgetType)
+                    {
+                        case C_WIDGET_TYPE.WIDGET_PUSHBUTTON:
+                            DefaultPushButton();
+                            break;
+                        case C_WIDGET_TYPE.WIDGET_CHECKBOX:
+                        case C_WIDGET_TYPE.WIDGET_RADIOBUTTON:
+                            DefaultRadioButtonOrCheckBox();
+                            break;
+                        case C_WIDGET_TYPE.WIDGET_TEXTFIELD:
+                        case C_WIDGET_TYPE.WIDGET_COMBOBOX:
+                            DefaultTextBoxOrComboBox();
+                            break;
+                        case C_WIDGET_TYPE.WIDGET_LISTBOX:
+                            DefaultListBox();
+                            break;
+                        case C_WIDGET_TYPE.WIDGET_SIGNATUREFIELDS:
+                            DefaultSign();
+                            break;
+                        case C_WIDGET_TYPE.WIDGET_UNKNOWN:
+                            break;
+                        default:
+                            break;
+                    }
+                    Rect rect = new Rect(mouseEndPoint.X, mouseEndPoint.Y, defaultWidth * zoomFactor, defaultHeight * zoomFactor);
+
+                    DPIRect = rect;
+                    drawDC?.DrawRectangle(null, drawParam.CreateWidgetPen, rect);
+                }
+                else
+                {
+                    Rect rect = new Rect(mouseStartPoint, mouseEndPoint);
+                    double mLeft = rect.Left;
+                    double mRight = rect.Right;
+                    double mUp = rect.Top;
+                    double mDown = rect.Bottom;
+                    if (rect.Left < maxRect.Left)
+                    {
+                        mLeft = maxRect.Left;
+                    }
+                    if (rect.Right > maxRect.Right)
+                    {
+                        mRight = maxRect.Right;
+                    }
+                    if (rect.Top < maxRect.Top)
+                    {
+                        mUp = maxRect.Top;
+                    }
+                    if (rect.Bottom > maxRect.Bottom)
+                    {
+                        mDown = maxRect.Bottom;
+                    }
+                    DPIRect = new Rect(mLeft, mUp, mRight - mLeft, mDown - mUp);
+                    drawDC?.DrawRectangle(null, drawParam.CreateWidgetPen, DPIRect);
+                }
+                Present();
+            });
+        }
+
+        private void DefaultRadioButtonOrCheckBox()
+        {
+            defaultWidth = 30;
+            defaultHeight = 30;
+        }
+
+        private void DefaultTextBoxOrComboBox()
+        {
+            defaultWidth = 200;
+            defaultHeight = 40;
+        }
+
+        private void DefaultListBox()
+        {
+            defaultWidth = 200;
+            defaultHeight = 130;
+        }
+
+        private void DefaultPushButton()
+        {
+            defaultWidth = 200;
+            defaultHeight = 50;
+        }
+
+        private void DefaultSign()
+        {
+            defaultWidth = 200;
+            defaultHeight = 80;
+        }
+        public virtual void ClearDraw()
+        {
+            Open();
+            Present();
+        }
+    }
+}

Разница между файлами не показана из-за своего большого размера
+ 1224 - 0
Demo/Examples/ComPDFKit.Tool/DrawTool/MultiSelectedRect.cs


+ 40 - 0
Demo/Examples/ComPDFKit.Tool/DrawTool/MultipleSelectedRect.cs

@@ -0,0 +1,40 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Media;
+
+namespace ComPDFKit.Tool.DrawTool
+{
+    public partial class MultipleSelectedRect: DrawingVisual
+    {
+        protected DrawingContext drawDC { get; set; }
+
+        /// <summary>
+        /// 数据改变中事件
+        /// </summary>
+        public event EventHandler<SelectedAnnotData> MultipleDataChanging;
+
+        /// <summary>
+        /// 数据改变完成事件
+        /// </summary>
+        public event EventHandler<SelectedAnnotData> MultipleDataChanged;
+
+        protected bool isHover = false;
+
+        protected bool isSelected = false;
+
+        protected SelectedType selectedType = SelectedType.None;
+
+        public void SetIsHover(bool hover)
+        {
+            isHover = hover;
+        }
+
+        public void SetIsSelected(bool selected)
+        {
+            isSelected = selected;
+        }
+    }
+}

Разница между файлами не показана из-за своего большого размера
+ 1107 - 0
Demo/Examples/ComPDFKit.Tool/DrawTool/PageSelectedRect.cs


+ 331 - 0
Demo/Examples/ComPDFKit.Tool/DrawTool/SelectImage.cs

@@ -0,0 +1,331 @@
+using ComPDFKit.Import;
+using ComPDFKit.PDFPage;
+using ComPDFKit.Viewer.Helper;
+using ComPDFKit.Viewer.Layer;
+using ComPDFKitViewer;
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Input;
+using System.Windows.Media;
+using ComPDFKit.Tool.SettingParam;
+
+namespace ComPDFKit.Tool.DrawTool
+{
+    public class PageImageItem
+    {
+        public int PageIndex;
+        /// <summary>
+        /// PDF DPI(72)
+        /// </summary>
+        public Rect PaintRect;
+        public int PageRotate;
+        public int ImageIndex;
+
+        public PageImageItem Clone()
+        {
+            PageImageItem cloneItem = new PageImageItem();
+
+            cloneItem.PageIndex = PageIndex;
+            cloneItem.PaintRect = PaintRect;
+            cloneItem.PageRotate = PageRotate;
+            cloneItem.ImageIndex = ImageIndex;
+
+            return cloneItem;
+        }
+    }
+    internal class SelectImage : CustomizeLayer
+    {
+        protected DrawingContext drawDC { get; set; }
+
+        protected DefaultDrawParam drawParam = new DefaultDrawParam();
+
+        private Dictionary<int, List<PageImageItem>> pageImageDict = new Dictionary<int, List<PageImageItem>>();
+
+        private PageImageItem hoverImageItem = null;
+
+        /// <summary>
+        /// Identify whether the image selection effect is being drawn
+        /// </summary>
+        protected bool isDrawSelectImage { get; set; }
+
+        public override void Draw()
+        {
+
+        }
+
+        public void Draw(CPDFViewer cPDFViewer)
+        {
+            Dispatcher.Invoke(() =>
+            {
+                drawDC = Open();
+                List<RenderData> renderDatas = cPDFViewer.CurrentRenderFrame.GetRenderDatas();
+                foreach (RenderData item in renderDatas)
+                {
+                    if (pageImageDict.ContainsKey(item.PageIndex))
+                    {
+                        List<PageImageItem> PaintImageList = pageImageDict[item.PageIndex];
+                        foreach (PageImageItem SelectItem in PaintImageList)
+                        {
+                            DrawItem(SelectItem, item, cPDFViewer, drawDC, false);
+                        }
+                    }
+                    if (hoverImageItem != null && hoverImageItem.PageIndex == item.PageIndex)
+                    {
+                        DrawItem(hoverImageItem, item, cPDFViewer, drawDC, true);
+                    }
+                }
+                Present();
+            });
+        }
+
+        public void CleanDraw(CPDFViewer cPDFViewer)
+        {
+            Dispatcher.Invoke(() =>
+            {
+                drawDC = Open();
+                Present();
+                ClearImageItem();
+            });
+        }
+
+        private void DrawItem(PageImageItem SelectItem, RenderData renderData, CPDFViewer cPDFViewer, DrawingContext dc, bool isHover)
+        {
+            Rect drawRect = SelectItem.PaintRect;
+            if (cPDFViewer.GetDocument() != null)
+            {
+                CPDFPage rawPage = cPDFViewer.GetDocument().PageAtIndex(renderData.PageIndex);
+                if (rawPage != null)
+                {
+                    int rotation = rawPage.Rotation - SelectItem.PageRotate;
+                    if (rotation != 0)
+                    {
+                        Size rawSize = renderData.RenderRect.Size;
+                        Matrix matrix = new Matrix();
+                        matrix.RotateAt(-rotation * 90, rawSize.Width / 2, rawSize.Height / 2);
+                        Rect checkRect = new Rect(0, 0, rawSize.Width, rawSize.Height);
+                        checkRect.Transform(matrix);
+                        matrix = new Matrix();
+                        matrix.RotateAt(rotation * 90, checkRect.Width / 96D * 72D / 2, checkRect.Height / 96D * 72D / 2);
+                        checkRect = new Rect(0, 0, checkRect.Width / 96D * 72D, checkRect.Height / 96D * 72D);
+
+                        drawRect.Transform(matrix);
+                        checkRect.Transform(matrix);
+
+                        drawRect = new Rect(drawRect.Left - checkRect.Left,
+                            drawRect.Top - checkRect.Top,
+                            drawRect.Width, drawRect.Height);
+                    }
+                }
+            }
+            Rect BorderRect = renderData.PageBound;
+            Rect RawPaintRect = new Rect(drawRect.Left / 72 * cPDFViewer.GetZoom() * 96 - renderData.CropLeft * cPDFViewer.GetZoom(),
+                drawRect.Top / 72 * cPDFViewer.GetZoom() * 96 - renderData.CropTop * cPDFViewer.GetZoom(),
+                drawRect.Width / 72 * cPDFViewer.GetZoom() * 96,
+                drawRect.Height / 72 * cPDFViewer.GetZoom() * 96);
+
+            RawPaintRect.X += BorderRect.X;
+            RawPaintRect.Y += BorderRect.Y;
+
+            RectangleGeometry clipGeometry = new RectangleGeometry();
+            clipGeometry.Rect = renderData.PageBound;
+            dc.PushClip(clipGeometry);
+            if (isHover)
+            {
+                dc.DrawRectangle(null, drawParam.ViewerImagePen, RawPaintRect);
+            }
+            else
+            {
+                dc.DrawRectangle(drawParam.ViewerImageBackgroundBrush, null, RawPaintRect);
+            }
+            dc.Pop();
+        }
+
+        public bool ProcessMouseDownForSelectImage(Point pdfPoint, int pageIndex, CPDFViewer cPDFViewer, bool isNeedClear)
+        {
+            return ProcessSelectImageAtPos(cPDFViewer, pdfPoint, pageIndex, false, isNeedClear);
+        }
+
+        public bool ProcessMouseMoveForSelectImage(Point pdfPoint, int pageIndex, CPDFViewer cPDFViewer)
+        {
+            return ProcessSelectImageAtPos(cPDFViewer, pdfPoint, pageIndex, true, true);
+        }
+
+        private bool ProcessSelectImageAtPos(CPDFViewer cPDFViewer, Point pdfPoint, int pageIndex, bool isHover, bool isNeedClear)
+        {
+            bool result = false;
+            if (cPDFViewer == null || cPDFViewer.GetDocument() == null || pageIndex < 0)
+            {
+                return result;
+            }
+            CPDFPage rawPage = cPDFViewer.GetDocument().PageAtIndex(pageIndex);
+            CPDFImgSelection imageRanges = rawPage.GetImgSelection();
+            List<CRect> checkList = imageRanges.GetImageRects();
+
+            bool findItem = false;
+            for (int i = 0; i < checkList.Count; i++)
+            {
+                Rect checkRect = DataConversionForWPF.CRectConversionForRect(checkList[i]);
+                if (checkRect.Contains(pdfPoint))
+                {
+                    findItem = true;
+                    if (isHover)
+                    {
+                        if (hoverImageItem == null || hoverImageItem.PageIndex != pageIndex || hoverImageItem.ImageIndex != i)
+                        {
+                            hoverImageItem = new PageImageItem()
+                            {
+                                PageIndex = pageIndex,
+                                PaintRect = checkRect,
+                                PageRotate = rawPage.Rotation,
+                                ImageIndex = i
+                            };
+                            result = true;
+                        }
+                    }
+                    else
+                    {
+                        if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl))
+                        {
+                            if (ContainImageItem(pageIndex, i))
+                            {
+                                result = RemoveImageItem(pageIndex, i);
+                            }
+                            else
+                            {
+                                if (pageImageDict != null && pageImageDict.Count > 0 && pageImageDict.Keys.Contains(pageIndex) == false)
+                                {
+                                    ClearImageItem();
+                                }
+                                result = AddPageImageItem(pageIndex, checkRect, rawPage.Rotation, i);
+                            }
+                        }
+                        else
+                        {
+                            if (!ContainImageItem(pageIndex, i) || isNeedClear)
+                            {
+                                ClearImageItem();
+                                result = AddPageImageItem(pageIndex, checkRect, rawPage.Rotation, i);
+                            }
+                            else
+                            {
+                                result = true;
+                            }
+                        }
+
+                        hoverImageItem = null;
+                    }
+                    break;
+                }
+            }
+            if (findItem == false && hoverImageItem != null)
+            {
+                hoverImageItem = null;
+                result = false;
+            }
+            if (findItem == false && !isHover && pageImageDict != null && pageImageDict.Count > 0 && isNeedClear)
+            {
+                ClearImageItem();
+                result = true;
+            }
+
+            return result;
+        }
+
+        private bool AddPageImageItem(int pageIndex, Rect paintRect, int pageRotate, int ImageIndex)
+        {
+            bool result = false;
+            if (pageImageDict.ContainsKey(pageIndex) && pageImageDict[pageIndex] != null)
+            {
+                List<PageImageItem> imageItems = pageImageDict[pageIndex];
+                if (ContainImageItem(pageIndex, ImageIndex) == false)
+                {
+                    imageItems.Add(new PageImageItem()
+                    {
+                        PageIndex = pageIndex,
+                        PaintRect = paintRect,
+                        PageRotate = pageRotate,
+                        ImageIndex = ImageIndex
+                    });
+                    result = true;
+                }
+            }
+            else
+            {
+                List<PageImageItem> imageItems = new List<PageImageItem>();
+                imageItems.Add(new PageImageItem()
+                {
+                    PageIndex = pageIndex,
+                    PaintRect = paintRect,
+                    PageRotate = pageRotate,
+                    ImageIndex = ImageIndex
+                });
+
+                pageImageDict.Add(pageIndex, imageItems);
+                result = true;
+            }
+            return result;
+        }
+
+        private bool ContainImageItem(int pageIndex, int ImageIndex)
+        {
+            bool result = false;
+            if (pageImageDict != null && pageImageDict.ContainsKey(pageIndex))
+            {
+                List<PageImageItem> imageItems = pageImageDict[pageIndex];
+                if (imageItems != null && imageItems.Count > 0 && imageItems.Where(x => x.ImageIndex == ImageIndex).Count() > 0)
+                {
+                    result = true;
+                }
+            }
+            return result;
+        }
+        private bool RemoveImageItem(int pageIndex, int ImageIndex)
+        {
+            bool result = false;
+            if (pageImageDict.ContainsKey(pageIndex) && pageImageDict[pageIndex] != null)
+            {
+                List<PageImageItem> imageItems = pageImageDict[pageIndex];
+
+                if (imageItems.Count > 0)
+                {
+                    List<PageImageItem> delItems = imageItems.Where(x => x.ImageIndex == ImageIndex).ToList();
+                    if (delItems.Count > 0)
+                    {
+                        result = true;
+                    }
+                    foreach (PageImageItem delItem in delItems)
+                    {
+                        imageItems.Remove(delItem);
+                    }
+                }
+            }
+            return result;
+        }
+        public bool ClearImageItem()
+        {
+            bool result = false;
+            if (pageImageDict.Count > 0)
+            {
+                pageImageDict.Clear();
+                result = true;
+            }
+            hoverImageItem = null;
+            return result;
+        }
+
+        public PageImageItem GetHoverImageItem()
+        {
+            return hoverImageItem.Clone();
+        }
+
+        public Dictionary<int, List<PageImageItem>> GetSelectImageItems()
+        {
+            return pageImageDict;
+        }
+    }
+}

+ 363 - 0
Demo/Examples/ComPDFKit.Tool/DrawTool/SelectText.cs

@@ -0,0 +1,363 @@
+using ComPDFKit.Tool.Help;
+using ComPDFKit.Viewer.Layer;
+using ComPDFKitViewer;
+using ComPDFKitViewer.Helper;
+using System.Collections.Generic;
+using System.Linq;
+using System.Windows;
+using System.Windows.Media;
+
+namespace ComPDFKit.Tool.DrawTool
+{
+    public class TextDrawRect
+    {
+        public string Text { get; set; }
+
+        /// <summary>
+        /// Current text rectangle (PDF DPI)
+        /// </summary>
+        public Rect DrawRect { get; set; }
+        public SolidColorBrush PaintBrush { get; set; } = new SolidColorBrush(Color.FromArgb(0x46, 0x46, 0x82, 0xB4));
+
+        #region Properties Used for Searching
+
+        internal bool DrawActiveSearch { get; set; } = false;
+
+        internal TextSearchItem SearchInfo { get; set; }
+
+        #endregion
+    }
+
+    public class TextSelectInfo
+    {
+        public int StartPage = -1;
+        public int EndPage = -1;
+        /// <summary>
+        /// Original page coordinates
+        /// </summary>
+        public Point StartPoint = new Point();
+        /// <summary>
+        /// Original page coordinates
+        /// </summary>
+        public Point EndPoint = new Point();
+        public int PageRotate;
+        public bool RotateRecord;
+        public Dictionary<int, string> PageSelectText = new Dictionary<int, string>();
+        public Dictionary<int, List<TextDrawRect>> PageSelectTextRectList = new Dictionary<int, List<TextDrawRect>>();
+        public Dictionary<int, KeyValuePair<Point, Point>> PageSelectPointList = new Dictionary<int, KeyValuePair<Point, Point>>();
+        internal Dictionary<int, List<Rect>> ConvertToSelectRectDict()
+        {
+            Dictionary<int, List<Rect>> TextRectDict = new Dictionary<int, List<Rect>>();
+
+            if (PageSelectTextRectList != null)
+            {
+                foreach (int key in PageSelectTextRectList.Keys)
+                {
+                    List<TextDrawRect> textDrawRects = PageSelectTextRectList[key];
+                    List<Rect> rectList = new List<Rect>();
+
+                    foreach (TextDrawRect drawItem in textDrawRects)
+                    {
+                        rectList.Add(drawItem.DrawRect);
+                    }
+                    TextRectDict[key] = rectList;
+                }
+            }
+
+            return TextRectDict;
+
+        }
+    }
+
+    internal class SelectText : CustomizeLayer
+    {
+        protected DrawingContext drawDC { get; set; }
+
+        private TextSelectInfo textInfo { get; set; } = new TextSelectInfo();
+        private TextSelectInfo searchInfo { get; set; } = new TextSelectInfo();
+
+        /// <summary>
+        ///Identify whether the text selection effect is being drawn
+        /// </summary>
+        protected bool isDrawSelectText { get; set; }
+
+        private TextSelectInfo SortTextSelectInfo(TextSelectInfo textInfo)
+        {
+            if (textInfo == null || textInfo.StartPage <= textInfo.EndPage)
+            {
+                return textInfo;
+            }
+
+            TextSelectInfo SortItem = new TextSelectInfo();
+            SortItem.StartPage = textInfo.EndPage;
+            SortItem.StartPoint = textInfo.EndPoint;
+            SortItem.EndPage = textInfo.StartPage;
+            SortItem.EndPoint = textInfo.StartPoint;
+            SortItem.PageSelectPointList = textInfo.PageSelectPointList;
+            SortItem.PageSelectTextRectList = textInfo.PageSelectTextRectList;
+            SortItem.PageSelectText = textInfo.PageSelectText;
+            SortItem.PageRotate = textInfo.PageRotate;
+            SortItem.RotateRecord = textInfo.RotateRecord;
+            return SortItem;
+        }
+
+        private void SetTextSelectRange(TextSelectInfo SelectTextItem, CPDFViewer cPDFViewer, Point tolerance, bool DoubleClick = false)
+        {
+            if (SelectTextItem == null)
+            {
+                return;
+            }
+
+            // Remove data that does not need to be displayed
+            for (int i = 0; i < SelectTextItem.PageSelectText.Count; i++)
+            {
+                var item = SelectTextItem.PageSelectText.ElementAt(i);
+                if (item.Key < SelectTextItem.StartPage || item.Key > SelectTextItem.EndPage)
+                {
+                    SelectTextItem.PageSelectText.Remove(item.Key);
+                }
+            }
+
+            for (int i = 0; i < SelectTextItem.PageSelectPointList.Count; i++)
+            {
+                var item = SelectTextItem.PageSelectPointList.ElementAt(i);
+                if (item.Key < SelectTextItem.StartPage || item.Key > SelectTextItem.EndPage)
+                {
+                    SelectTextItem.PageSelectPointList.Remove(item.Key);
+                }
+            }
+
+            for (int i = 0; i < SelectTextItem.PageSelectTextRectList.Count; i++)
+            {
+                var item = SelectTextItem.PageSelectTextRectList.ElementAt(i);
+                if (item.Key < SelectTextItem.StartPage || item.Key > SelectTextItem.EndPage)
+                {
+                    SelectTextItem.PageSelectTextRectList.Remove(item.Key);
+                } 
+            }
+
+            // Add or update data
+            for (int i = SelectTextItem.StartPage; i <= SelectTextItem.EndPage; i++)
+            {
+                PageViewData RenderPage = cPDFViewer.GetPageNodeByPageIndex(i);
+                if (RenderPage == null)
+                {
+                    continue;
+                }
+                Point StartPoint = new Point(0, 0);
+                Point EndPoint = new Point(RenderPage.RawSize.Width, RenderPage.RawSize.Height);
+                if (i == SelectTextItem.StartPage)
+                {
+                    StartPoint = SelectTextItem.StartPoint;
+                }
+                if (i == SelectTextItem.EndPage)
+                {
+                    EndPoint = SelectTextItem.EndPoint;
+                }
+                bool ReSelect = true;
+                if (SelectTextItem.PageSelectPointList.ContainsKey(i))
+                {
+                    KeyValuePair<Point, Point> PrevPointRange = SelectTextItem.PageSelectPointList[i];
+                    if (PrevPointRange.Key == StartPoint && PrevPointRange.Value == EndPoint)
+                    {
+                        ReSelect = false;
+                    }
+                }
+                if (ReSelect)
+                {
+                    if (DoubleClick == true)
+                    {
+                        Rect uiRect = Rect.Empty;
+                        SelectTextItem.PageSelectText[i] = PDFHelp.GetDoubleClickText(cPDFViewer.GetDocument(), i, StartPoint, ref uiRect);
+                        SelectTextItem.PageSelectPointList[i] = new KeyValuePair<Point, Point>(StartPoint, EndPoint);
+                        SelectTextItem.PageSelectTextRectList[i] = new List<TextDrawRect>() { new TextDrawRect() { DrawRect = uiRect, Text = SelectTextItem.PageSelectText[i] } };
+                    }
+                    else
+                    {
+                        SelectTextItem.PageSelectText[i] = PDFHelp.GetSelectText(cPDFViewer.GetDocument(), i, StartPoint, EndPoint, tolerance);
+                        SelectTextItem.PageSelectPointList[i] = new KeyValuePair<Point, Point>(StartPoint, EndPoint);
+                        SelectTextItem.PageSelectTextRectList[i] = PDFHelp.GetSelectTextRect(cPDFViewer.GetDocument(), i, StartPoint, EndPoint, tolerance);
+                    }
+                }
+            }
+        }
+
+        public SelectText()
+        {
+
+        }
+
+        public void StartDraw(Point pagePoint, int pageIndex)
+        {
+            isDrawSelectText = true;
+            textInfo = new TextSelectInfo()
+            {
+                StartPage = pageIndex,
+                EndPage = pageIndex,
+                StartPoint = pagePoint,
+                EndPoint = pagePoint
+            };
+        }
+
+        public void MoveDraw(Point downPoint, int pageIndex, CPDFViewer cPDFViewer, Point tolerance, bool DoubleClick)
+        {
+            if (isDrawSelectText)
+            {
+                textInfo.EndPage = pageIndex;
+                textInfo.EndPoint = downPoint;
+
+                SetTextSelectRange(SortTextSelectInfo(textInfo), cPDFViewer, tolerance, DoubleClick);
+
+                Draw(cPDFViewer);
+            }
+        }
+
+        public TextSelectInfo GetTextSelectInfo()
+        {
+            return textInfo;
+        }
+
+        public TextSelectInfo GetSearchInfo()
+        {
+            return searchInfo;
+        }
+        public void SetSearchInfo(TextSelectInfo searchInfo)
+        {
+            this.searchInfo = searchInfo;
+        }
+
+        public void CleanSearchInfo()
+        {
+            searchInfo = new TextSelectInfo();
+        }
+
+        public void RemoveSelectDataInfo()
+        {
+            textInfo = new TextSelectInfo();
+        }
+
+        public bool HasSelectTextInfo()
+        {
+            if (textInfo?.PageSelectTextRectList.Count > 0)
+            {
+                return true;
+            }
+            return false;
+        }
+
+        public bool HasSearchInfo()
+        {
+            if (searchInfo?.PageSelectTextRectList.Count > 0)
+            {
+                return true;
+            }
+            return false;
+        }
+
+        public void EndDraw()
+        {
+            isDrawSelectText = false;
+        }
+
+        public void Draw(CPDFViewer cPDFViewer)
+        {
+            Dispatcher.Invoke(() =>
+            {
+                drawDC = Open();
+                DrawSelectRange(drawDC, textInfo, cPDFViewer);
+                DrawSelectRange(drawDC, searchInfo, cPDFViewer);
+                Present();
+            });
+        }
+
+        public void CleanDraw(CPDFViewer cPDFViewer)
+        {
+            Dispatcher.Invoke(() =>
+            {
+                drawDC = Open();
+                DrawSelectRange(drawDC, searchInfo, cPDFViewer);
+                Present();
+            });
+        }
+
+        public override void Draw()
+        {
+
+        }
+
+        private void DrawSelectRange(DrawingContext dc, TextSelectInfo SelectTextItem, CPDFViewer cPDFViewer)
+        {
+            List<RenderData> renderDatas=new List<RenderData>();
+            if (cPDFViewer.CurrentRenderFrame!=null)
+            {
+                renderDatas = cPDFViewer.CurrentRenderFrame.GetRenderDatas();
+            }
+
+            foreach (RenderData PaintRange in renderDatas)
+            {
+                if (SelectTextItem.PageSelectTextRectList.ContainsKey(PaintRange.PageIndex))
+                {
+                    List<TextDrawRect> PaintRectList = SelectTextItem.PageSelectTextRectList[PaintRange.PageIndex];
+                    foreach (TextDrawRect SelectRect in PaintRectList)
+                    {
+                        Rect drawRect = SelectRect.DrawRect;
+                        Rect textRect = drawRect;
+                        if (cPDFViewer.GetDocument() != null && SelectTextItem.RotateRecord)
+                        {
+                            var rawPage = cPDFViewer.GetDocument().PageAtIndex(PaintRange.PageIndex);
+                            if (rawPage != null)
+                            {
+                                int rotation = rawPage.Rotation - SelectTextItem.PageRotate;
+                                if (rotation != 0)
+                                {
+                                    Size rawSize = new Size(PaintRange.PageBound.Width, PaintRange.PageBound.Height);
+                                    Matrix matrix = new Matrix();
+                                    matrix.RotateAt(-rotation * 90, rawSize.Width / 2, rawSize.Height / 2);
+                                    Rect checkRect = new Rect(0, 0, rawSize.Width, rawSize.Height);
+                                    checkRect.Transform(matrix);
+                                    matrix = new Matrix();
+                                    matrix.RotateAt(rotation * 90, checkRect.Width / 96D * 72D / 2, checkRect.Height / 96D * 72D / 2);
+                                    checkRect = new Rect(0, 0, checkRect.Width / 96D * 72D, checkRect.Height / 96D * 72D);
+                                    textRect.Transform(matrix);
+                                    checkRect.Transform(matrix);
+                                    drawRect = new Rect(textRect.Left - checkRect.Left,
+                                        textRect.Top - checkRect.Top,
+                                        textRect.Width, textRect.Height);
+                                }
+                            }
+                        }
+                        Rect BorderRect = PaintRange.PageBound;
+                        Rect RawPaintRect =DpiHelper.PDFRectToStandardRect( 
+                            new Rect(
+                            drawRect.Left * cPDFViewer.CurrentRenderFrame.ZoomFactor - DpiHelper.StandardNumToPDFNum(PaintRange.CropLeft) * cPDFViewer.CurrentRenderFrame.ZoomFactor,
+                            drawRect.Top * cPDFViewer.CurrentRenderFrame.ZoomFactor - DpiHelper.StandardNumToPDFNum(PaintRange.CropTop) * cPDFViewer.CurrentRenderFrame.ZoomFactor,
+                            drawRect.Width * cPDFViewer.CurrentRenderFrame.ZoomFactor,
+                            drawRect.Height * cPDFViewer.CurrentRenderFrame.ZoomFactor));
+
+                        RawPaintRect.X += BorderRect.X;
+                        RawPaintRect.Y += BorderRect.Y;
+
+                        RectangleGeometry clipGeometry = new RectangleGeometry();
+                        clipGeometry.Rect = BorderRect;
+                        dc.PushClip(clipGeometry);
+                        Rect paintRect = RawPaintRect;
+                        dc.DrawRectangle(SelectRect.PaintBrush, null, paintRect);
+
+                        TextSearchItem searchInfo = SelectRect.SearchInfo;
+                        if (SelectRect.DrawActiveSearch && searchInfo.BorderThickness > 0 && searchInfo.BorderBrush != Brushes.Transparent)
+                        {
+                            Rect outRect = new Rect(paintRect.Left - searchInfo.BorderThickness / 2 - searchInfo.Padding.Left,
+                                paintRect.Top - searchInfo.BorderThickness / 2 - searchInfo.Padding.Top,
+                                paintRect.Width + searchInfo.BorderThickness + searchInfo.Padding.Left + searchInfo.Padding.Right,
+                                paintRect.Height + searchInfo.BorderThickness + searchInfo.Padding.Top + searchInfo.Padding.Bottom);
+
+                            Pen borderPen = new Pen(searchInfo.BorderBrush, searchInfo.BorderThickness);
+                            dc.DrawRectangle(null, borderPen, outRect);
+                        }
+                        dc.Pop();
+                    }
+                }
+            }
+        }
+    }
+}

+ 728 - 0
Demo/Examples/ComPDFKit.Tool/DrawTool/SelectedRect.cs

@@ -0,0 +1,728 @@
+using ComPDFKit.PDFAnnotation;
+using ComPDFKit.Tool.SettingParam;
+using ComPDFKit.Viewer.Layer;
+using ComPDFKitViewer;
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Diagnostics;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Controls.Primitives;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Media3D;
+using System.Xml.Linq;
+using static System.Net.Mime.MediaTypeNames;
+
+namespace ComPDFKit.Tool.DrawTool
+{
+    public enum PointControlType
+    {
+        None = -1,
+        LeftTop,
+        LeftMiddle,
+        LeftBottom,
+        MiddlBottom,
+        RightBottom,
+        RightMiddle,
+        RightTop,
+        MiddleTop,
+        Rotate,
+        Body,
+        Line
+    }
+
+    public enum SelectedType
+    {
+        None = -1,
+        Annot,
+        PDFEdit
+    }
+
+
+    public enum DrawPointType
+    {
+        Circle,
+        Square,
+        Crop
+    }
+
+    public enum DrawMoveType
+    {
+        kDefault,
+        kReferenceLine,
+    }
+
+    public class SelectedAnnotData
+    {
+        /// <summary>
+        /// Current size of the rectangle
+        /// </summary>
+        public Rect Square { get; set; }
+
+        /// <summary>
+        /// Current points of the rectangle
+        /// </summary>
+        public PointCollection Points { get; set; }
+
+        public AnnotData annotData { get; set; }
+
+    }
+
+    public partial class SelectedRect : DrawingVisual
+    {
+
+        /// <summary>
+        /// Re-layout child elements
+        /// </summary>
+        public void Arrange()
+        {
+            foreach (Visual child in Children)
+            {
+                if (!(child is UIElement))
+                {
+                    continue;
+                }
+                UIElement checkChild = child as UIElement;
+                try
+                {
+                    double left = Canvas.GetLeft(checkChild);
+                    double top = Canvas.GetTop(checkChild);
+                    double width = (double)checkChild.GetValue(FrameworkElement.WidthProperty);
+                    double height = (double)checkChild.GetValue(FrameworkElement.HeightProperty);
+                    checkChild.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
+                    checkChild.Arrange(new Rect(
+                        double.IsNaN(left) ? 0 : left,
+                        double.IsNaN(top) ? 0 : top,
+                        double.IsNaN(width) ? checkChild.DesiredSize.Width : width,
+                        double.IsNaN(height) ? checkChild.DesiredSize.Height : height));
+                }
+                catch (Exception ex)
+                {
+
+                }
+            }
+        }
+        protected DefaultDrawParam DrawParam = new DefaultDrawParam();
+
+        protected DrawingContext drawDC { get; set; }
+
+        /// <summary>
+        /// Data changing event
+        /// </summary>
+        public event EventHandler<SelectedAnnotData> DataChanging;
+
+        /// <summary>
+        /// Data changed event
+        /// </summary>
+        public event EventHandler<SelectedAnnotData> DataChanged;
+
+        protected bool isHover = false;
+
+        protected bool isSelected = false;
+
+        protected SelectedType selectedType = SelectedType.None;
+
+        public SelectedType GetSelectedType()
+        {
+            return selectedType;
+        }
+
+        public void SetIsHover(bool hover)
+        {
+            isHover = hover;
+        }
+
+        public bool GetIsHover()
+        {
+           return isHover;
+        }
+
+        public void SetIsSelected(bool selected)
+        {
+            isSelected = selected;
+        }
+
+        public bool GetIsSelected()
+        {
+           return isSelected;
+        }
+
+        public void SetCurrentDrawPointType(DrawPointType type)
+        {
+            currentDrawPointType = type;
+        }
+
+        public DrawPointType GetCurrentDrawPointType()
+        {
+            return currentDrawPointType;
+        }
+
+        public virtual void OnMouseLeftButtonDown(Point downPoint)
+        {
+            isMouseDown = true;
+            hitControlType = PointControlType.None;
+            mouseDownPoint = downPoint;
+            moveOffset = new Point(0, 0);
+            HitTestResult hitResult = VisualTreeHelper.HitTest(this, downPoint);
+            if (hitResult != null && hitResult.VisualHit is DrawingVisual)
+            {
+                hitControlType = GetHitControlIndex(downPoint);
+                if (hitControlType != PointControlType.None)
+                {
+                    cacheRect = drawRect;
+                }
+            }
+        }
+
+        public virtual void OnMouseLeftButtonUp(Point upPoint)
+        {
+            if (isMouseDown && hitControlType != PointControlType.None)
+            {
+                isMouseDown = false;
+                cacheRect = SetDrawRect = drawRect;
+                Draw();
+                if ((int)upPoint.X != (int)mouseDownPoint.X || (int)upPoint.Y != (int)mouseDownPoint.Y)
+                {
+                    InvokeDataChangEvent(true);
+                }
+            }
+            moveOffset = new Point(0, 0);
+        }
+
+        public virtual void OnMouseMove(Point mousePoint, out bool Tag, double width, double height)
+        {
+            PDFViewerActualWidth = width;
+            PDFViewerActualHeight = height;
+            Tag = false;
+            if (isMouseDown && hitControlType != PointControlType.None)
+            {
+                Tag = isMouseDown;
+                if (CalcHitPointMove(mousePoint))
+                {
+                    Draw();
+                    if ((int)mousePoint.X != (int)mouseDownPoint.X || (int)mousePoint.Y != (int)mouseDownPoint.Y)
+                    {
+                        InvokeDataChangEvent(false);
+                    }
+                }
+            }
+        }
+
+        public Cursor GetCursor(Point downPoint, Cursor cursor)
+        {
+            if (isMouseDown)
+            {
+                return cursor;
+            }
+            hitControlType = GetHitControlIndex(downPoint);
+            switch (hitControlType)
+            {
+                case PointControlType.LeftTop:
+                case PointControlType.RightBottom:
+                    return Cursors.SizeNWSE;
+
+                case PointControlType.LeftMiddle:
+                case PointControlType.RightMiddle:
+                    return Cursors.SizeWE;
+
+                case PointControlType.LeftBottom:
+                case PointControlType.RightTop:
+                    return Cursors.SizeNESW;
+
+                case PointControlType.MiddlBottom:
+                case PointControlType.MiddleTop:
+                    return Cursors.SizeNS;
+                case PointControlType.Body:
+                    return Cursors.Arrow;
+                case PointControlType.Line:
+                    return Cursors.SizeAll;
+                default:
+                    return Cursors.Arrow;
+            }
+        }
+
+        public SelectedRect(DefaultDrawParam defaultDrawParam, SelectedType type) : base()
+        {
+            DrawParam = defaultDrawParam;
+            currentDrawPointType = DrawPointType.Square;
+            selectedType = type;
+        }
+
+        public void Draw()
+        {
+            Dispatcher.Invoke(() =>
+            {
+                Rect currentRect = SetDrawRect;
+                drawDC = RenderOpen();
+                switch (currentDrawMoveType)
+                {
+                    case DrawMoveType.kDefault:
+                        currentRect = drawRect;
+                        CalcControlPoint(currentRect);
+                        break;
+                    case DrawMoveType.kReferenceLine:
+                        CalcControlPoint(currentRect);
+                        if (isMouseDown == true)
+                        {
+                            SolidColorBrush moveBrush = DrawParam.AnnotMoveBrush;
+                            Pen movepen = DrawParam.AnnotMovePen;
+                            GetMoveBrushAndPen(ref moveBrush, ref movepen);
+                            DrawMoveBounds(drawDC, hitControlType, movepen, moveBrush, drawRect);
+                        }
+                        break;
+                    default:
+                        break;
+                }
+
+                SolidColorBrush solidColorBrush = DrawParam.AnnotRectFillBrush;
+                Pen pen = DrawParam.AnnotRectLinePen;
+                GetBrushAndPen(ref solidColorBrush, ref pen);
+                drawDC?.DrawRectangle(solidColorBrush, pen, currentRect);
+
+                SolidColorBrush PointBrush = DrawParam.AnnotPointBorderBrush;
+                Pen PointPen = DrawParam.AnnotPointPen;
+                GetPointBrushAndPen(ref PointBrush, ref PointPen);
+
+                switch (currentDrawPointType)
+                {
+                    case DrawPointType.Circle:
+                        DrawCirclePoint(drawDC, GetIgnorePoints(), pointSize, PointPen, PointBrush);
+                        break;
+                    case DrawPointType.Square:
+                        DrawSquarePoint(drawDC, GetIgnorePoints(), pointSize, PointPen, PointBrush);
+                        break;
+                    case DrawPointType.Crop:
+                        DrawCropPoint(drawDC, GetIgnorePoints(), pointSize, PointPen, PointBrush);
+                        break;
+                }
+                drawDC?.Close();
+                drawDC = null;
+            });
+        }
+
+        private void GetMoveBrushAndPen(ref SolidColorBrush colorBrush, ref Pen pen)
+        {
+            switch (selectedType)
+            {
+                case SelectedType.None:
+                    break;
+                case SelectedType.Annot:
+                    colorBrush = DrawParam.AnnotMoveBrush;
+                    pen = DrawParam.AnnotMovePen;
+                    break;
+                case SelectedType.PDFEdit:
+                    colorBrush = DrawParam.PDFEditMoveBrush;
+                    pen = DrawParam.PDFEditMovePen;
+                    break;
+                default:
+                    break;
+            }
+        }
+
+        private void GetPointBrushAndPen(ref SolidColorBrush colorBrush, ref Pen pen)
+        {
+            switch (selectedType)
+            {
+                case SelectedType.None:
+                    break;
+                case SelectedType.Annot:
+                    colorBrush = DrawParam.AnnotPointBorderBrush;
+                    pen = DrawParam.AnnotPointPen;
+                    break;
+                case SelectedType.PDFEdit:
+                    if (isHover)
+                    {
+                        colorBrush = DrawParam.PDFEditRectFillHoverBrush;
+                        pen = DrawParam.PDFEditPointHoverPen;
+                    }
+                    else if (currentDrawPointType == DrawPointType.Crop)
+                    {
+                        colorBrush = DrawParam.SPDFEditCropBorderBrush;//new SolidColorBrush((DrawParam.SPDFEditPointPen.Brush as SolidColorBrush).Color);
+                        pen = DrawParam.SPDFEditPointPen.Clone();
+                        pen.DashStyle = DashStyles.Solid;
+                    }
+                    else
+                    {
+                        if (isSelected)
+                        {
+                            colorBrush = DrawParam.SPDFEditPointBorderBrush;
+                            pen = DrawParam.SPDFEditPointPen;
+                        }
+                        else
+                        {
+                            colorBrush = DrawParam.PDFEditPointBorderBrush;
+                            pen = DrawParam.PDFEditPointPen;
+                        }
+                    }
+                    break;
+                default:
+                    break;
+            }
+        }
+
+        private void GetBrushAndPen(ref SolidColorBrush colorBrush, ref Pen pen)
+        {
+            switch (selectedType)
+            {
+                case SelectedType.None:
+                    break;
+                case SelectedType.Annot:
+                    if (isHover)
+                    {
+                        colorBrush = DrawParam.AnnotRectFillBrush;
+                        pen = DrawParam.AnnotRectHoverPen;
+                    }
+                    else
+                    {
+                        colorBrush = DrawParam.AnnotRectFillBrush;
+                        pen = DrawParam.AnnotRectLinePen;
+                    }
+                    break;
+                case SelectedType.PDFEdit:
+                    if (isHover)
+                    {
+                        colorBrush = DrawParam.PDFEditRectFillHoverBrush;
+                        pen = DrawParam.PDFEditRectLineHoverPen;
+                    }
+                    else
+                    {
+                        if (isSelected)
+                        {
+                            colorBrush = DrawParam.SPDFEditRectFillBrush;
+                            pen = DrawParam.SPDFEditRectLinePen;
+                        }
+                        else
+                        {
+                            colorBrush = DrawParam.PDFEditRectFillBrush;
+                            pen = DrawParam.PDFEditRectLinePen;
+                        }
+                    }
+                    break;
+                default:
+                    break;
+            }
+        }
+        public virtual void ClearDraw()
+        {
+            SetDrawRect = drawRect = new Rect();
+            drawDC = RenderOpen();
+            drawDC?.Close();
+            drawDC = null;
+        }
+
+        /// <summary>
+        /// Hide the drawing
+        /// </summary>
+        public virtual void HideDraw()
+        {
+            drawDC = RenderOpen();
+            drawDC?.Close();
+        }
+
+        public void SetRect(Rect newRect,double zoom)
+        {
+            if(newRect == Rect.Empty || newRect == null)
+            {
+                return;
+            }
+            newRect = new Rect((int)(newRect.X - rectPadding* zoom), (int)(newRect.Y - rectPadding* zoom),(int)( newRect.Width + 2 * rectPadding* zoom), (int)(newRect.Height + 2 * rectPadding* zoom));
+            currentZoom = zoom;
+            SetDrawRect = drawRect = newRect;
+            drawCenterPoint = new Point(drawRect.Left + drawRect.Width / 2, drawRect.Top + drawRect.Height / 2);
+        }
+
+        /// <summary>
+        /// Get the original set Rect, not the calculated fill
+        /// </summary>
+        /// <param name="newRect">
+        /// The new rect to set
+        /// </param>
+        public Rect GetRect()
+        {
+            Rect rect = new Rect(drawRect.X + rectPadding * currentZoom, drawRect.Y + rectPadding * currentZoom, Math.Max(rectMinWidth, drawRect.Width - 2 * rectPadding * currentZoom), Math.Max(RectMinHeight, drawRect.Height - 2 * rectPadding * currentZoom));
+            return rect;
+        }
+
+        public void SetRectPadding(double rectPadding)
+        {
+            this.rectPadding = rectPadding;
+        }
+
+        public double GetRectPadding()
+        {
+            return rectPadding;
+        }
+
+        public Rect GetDrawRect()
+        {
+            return drawRect;
+        }
+
+        public void SetMaxRect(Rect rect)
+        {
+            maxRect = rect;
+        }
+        public Rect GetMaxRect()
+        {
+            return maxRect;
+        }
+
+        public void SetAnnotData(AnnotData annotData)
+        {
+            SetIgnorePoints(new List<PointControlType>());
+            SetIsProportionalScaling(false);
+            isProportionalScaling = false;
+            switch (annotData.AnnotType)
+            {
+                case C_ANNOTATION_TYPE.C_ANNOTATION_HIGHLIGHT:
+                case C_ANNOTATION_TYPE.C_ANNOTATION_UNDERLINE:
+                case C_ANNOTATION_TYPE.C_ANNOTATION_SQUIGGLY:
+                case C_ANNOTATION_TYPE.C_ANNOTATION_STRIKEOUT:
+                case C_ANNOTATION_TYPE.C_ANNOTATION_RICHMEDIA:
+                case C_ANNOTATION_TYPE.C_ANNOTATION_MOVIE:
+                case C_ANNOTATION_TYPE.C_ANNOTATION_REDACT:
+                    DisableAll();
+                    break;
+
+                case C_ANNOTATION_TYPE.C_ANNOTATION_TEXT:
+                case C_ANNOTATION_TYPE.C_ANNOTATION_SOUND:
+                    SetIgnorePointsAll();
+                    break;
+
+                case C_ANNOTATION_TYPE.C_ANNOTATION_STAMP:
+                    SetIsProportionalScaling(true);
+                    break;
+
+                case C_ANNOTATION_TYPE.C_ANNOTATION_LINK:
+                    SetIgnorePointsAll();
+                    break;
+
+                default:
+                    break;
+            }
+            SetMaxRect(annotData.PaintOffset);
+            SetRect(annotData.PaintRect, annotData.CurrentZoom);
+            selectedRectData = new SelectedAnnotData();
+            selectedRectData.annotData = annotData;
+        }
+
+        public void SetIsProportionalScaling(bool isProportionalScaling)
+        {
+            this.isProportionalScaling = isProportionalScaling;
+            ignorePoints.Clear();
+            if (isProportionalScaling)
+            {
+                ignorePoints.Add(PointControlType.LeftMiddle);
+                ignorePoints.Add(PointControlType.MiddlBottom);
+                ignorePoints.Add(PointControlType.RightMiddle);
+                ignorePoints.Add(PointControlType.MiddleTop);
+                ignorePoints.Add(PointControlType.Rotate);
+            }
+        }
+
+        public void SetDrawType(DrawPointType drawType)
+        {
+            currentDrawPointType = drawType;
+        }
+
+        public void SetDrawMoveType(DrawMoveType drawType)
+        {
+            currentDrawMoveType = drawType;
+        }
+
+        /// <summary>
+        /// Set the types that need to be ignored
+        /// </summary>
+        /// <param name="types">
+        /// The collection of point types that need to be ignored
+        /// </param>
+        public void SetIgnorePoints(List<PointControlType> types)
+        {
+            ignorePoints.Clear();
+            foreach (PointControlType type in types)
+            {
+                ignorePoints.Add(type);
+            }
+        }
+
+        /// <summary>
+        /// Ignore all points
+        /// </summary>
+        public void SetIgnorePointsAll()
+        {
+            ignorePoints.Clear();
+            ignorePoints.Add(PointControlType.LeftTop);
+            ignorePoints.Add(PointControlType.LeftMiddle);
+            ignorePoints.Add(PointControlType.LeftBottom);
+            ignorePoints.Add(PointControlType.MiddlBottom);
+            ignorePoints.Add(PointControlType.RightBottom);
+            ignorePoints.Add(PointControlType.RightMiddle);
+            ignorePoints.Add(PointControlType.RightTop);
+            ignorePoints.Add(PointControlType.MiddleTop);
+        }
+
+        /// <summary>
+        /// Disable all functions
+        /// </summary>
+        public void DisableAll()
+        {
+            ignorePoints.Clear();
+            ignorePoints.Add(PointControlType.LeftTop);
+            ignorePoints.Add(PointControlType.LeftMiddle);
+            ignorePoints.Add(PointControlType.LeftBottom);
+            ignorePoints.Add(PointControlType.MiddlBottom);
+            ignorePoints.Add(PointControlType.RightBottom);
+            ignorePoints.Add(PointControlType.RightMiddle);
+            ignorePoints.Add(PointControlType.RightTop);
+            ignorePoints.Add(PointControlType.MiddleTop);
+            ignorePoints.Add(PointControlType.Rotate);
+            ignorePoints.Add(PointControlType.Body);
+            ignorePoints.Add(PointControlType.Line);
+        }
+
+        /// <summary>
+        /// Set the left alignment in the set maximum rectangle
+        /// </summary>
+        public virtual void SetAlignLeftForMaxRect()
+        {
+            DrawAlignRect(AlignmentsHelp.SetAlignLeft(drawRect, maxRect));
+        }
+
+        /// <summary>
+        /// Set horizontal center alignment in the set maximum rectangle
+        /// </summary>
+        public virtual void SetAlignHorizonCenterForMaxRect()
+        {
+            DrawAlignRect(AlignmentsHelp.SetAlignHorizonCenter(drawRect, maxRect));
+        }
+
+        /// <summary>
+        /// Set horizontal right alignment in the set maximum rectangle
+        /// </summary>
+        public virtual void SetAlignRightForMaxRect()
+        {
+            DrawAlignRect(AlignmentsHelp.SetAlignRight(drawRect, maxRect));
+        }
+
+        /// <summary>
+        /// Set the top alignment in the set maximum rectangle
+        /// </summary>
+        public virtual void SetAlignTopForMaxRect()
+        {
+            DrawAlignRect(AlignmentsHelp.SetAlignTop(drawRect, maxRect));
+        }
+
+        /// <summary>
+        /// Set vertical center alignment in the set maximum rectangle
+        /// </summary>
+        public virtual void SetAlignVerticalCenterForMaxRect()
+        {
+            DrawAlignRect(AlignmentsHelp.SetAlignVerticalCenter(drawRect, maxRect));
+        }
+
+        /// <summary>
+        /// Set vertical center alignment in the set maximum rectangle
+        /// </summary>
+        public virtual void SetAlignHorizonVerticalCenterForMaxRect()
+        {
+            DrawAlignRect(AlignmentsHelp.SetAlignHorizonVerticalCenter(drawRect, maxRect));
+        }
+
+        /// <summary>
+        /// Set the bottom alignment in the set maximum rectangle
+        /// </summary>
+        public virtual void SetAlignBottomForMaxRect()
+        {
+            DrawAlignRect(AlignmentsHelp.SetAlignBottom(drawRect, maxRect));
+        }
+
+
+        /// <summary>
+        /// Get which control point the coordinate is on
+        /// </summary>
+        /// <param name="clickPoint">
+        /// The point to check
+        /// </param>
+        /// <returns>
+        /// The control point type
+        /// </returns>
+        public PointControlType GetHitControlIndex(Point point, bool isIgnore=true)
+        {
+            HitTestResult hitResult = VisualTreeHelper.HitTest(this, point);
+            if (hitResult != null && hitResult.VisualHit is DrawingVisual)
+            {
+                List<PointControlType> ignoreList = GetIgnorePoints();
+
+                List<Point> IgnorePointsList = new List<Point>();
+                foreach (PointControlType type in ignoreList)
+                {
+                    if ((int)type < controlPoints.Count)
+                    {
+                        IgnorePointsList.Add(controlPoints[(int)type]);
+                    }
+                }
+                for (int i = 0; i < controlPoints.Count; i++)
+                {
+                    Point checkPoint = controlPoints[i];
+
+                    if (isIgnore&&IgnorePointsList.Contains(checkPoint))
+                    {
+                        continue;
+                    }
+                    switch (currentDrawPointType)
+                    {
+                        case DrawPointType.Circle:
+                            Vector checkVector = checkPoint - point;
+                            if (checkVector.Length < pointSize)
+                            {
+                                return (PointControlType)i;
+                            }
+                            break;
+                        case DrawPointType.Square:
+
+                            Rect checkRect = new Rect(Math.Max(checkPoint.X - pointSize,0), Math.Max(checkPoint.Y - pointSize,0), pointSize * 2, pointSize * 2);
+                            if (checkRect.Contains(point))
+                            {
+                                return (PointControlType)i;
+                            }
+                            break;
+                        
+                        case DrawPointType.Crop:
+                            Rect cropRect = new Rect(Math.Max(checkPoint.X - pointSize, 0), Math.Max(checkPoint.Y - pointSize, 0), pointSize * 2, pointSize * 2);
+                            if (cropRect.Contains(point))
+                            {
+                                return (PointControlType)i;
+                            }
+                            break;
+                        default:
+                            break;
+                    }
+                }
+                if (drawRect.Contains(point))
+                {
+                    double rectWidth = (drawRect.Width - 2 * rectPadding > 0)? drawRect.Width - 2 * rectPadding: 0;
+                    double rectHeight = (drawRect.Height - 2 * rectPadding > 0)? drawRect.Height - 2 * rectPadding: 0;
+                    Rect rect = new Rect(Math.Max(drawRect.X + rectPadding,0),Math.Max( drawRect.Y + rectPadding,0), rectWidth, rectHeight);
+                    if (rect.Contains(point))
+                    {
+                        if (!ignoreList.Contains(PointControlType.Body))
+                        {
+                            return PointControlType.Body;
+                        }
+                    }
+                    if (!ignoreList.Contains(PointControlType.Body))
+                    {
+                        return PointControlType.Line;
+                    }
+                }
+            }
+            return PointControlType.None;
+        }
+    }
+}

+ 830 - 0
Demo/Examples/ComPDFKit.Tool/DrawTool/SelectedRect.protected.cs

@@ -0,0 +1,830 @@
+using System;
+using System.Collections.Generic;
+using System.Windows;
+using System.Windows.Media;
+
+namespace ComPDFKit.Tool.DrawTool
+{
+    public partial class SelectedRect
+    {
+        #region Properties
+
+        /// <summary>
+        /// Current control point drawing style.
+        /// </summary>
+        protected DrawPointType currentDrawPointType { get; set; }
+
+        /// <summary>
+        /// Current drag drawing style.
+        /// </summary>
+        protected DrawMoveType currentDrawMoveType { get; set; }
+
+        /// <summary>
+        /// Current click hit control point.
+        /// </summary>
+        protected PointControlType hitControlType { get; set; }
+
+        /// <summary>
+        /// Mouse down position information.
+        /// </summary>
+        protected Point mouseDownPoint { get; set; }
+
+        /// <summary>
+        /// Whether the mouse is pressed.
+        /// </summary>
+        protected bool isMouseDown { get; set; }
+
+        /// <summary>
+        /// Whether proportional scaling is required.
+        /// </summary>
+        protected bool isProportionalScaling { get; set; } = false;
+
+        /// <summary>
+        /// Current control point size.
+        /// </summary>
+        protected int pointSize { get; set; } = 4;
+
+        /// <summary>
+        /// Rectangular minimum width.
+        /// </summary>
+        protected int rectMinWidth { get; set; } = 10;
+
+        /// <summary>
+        /// Rectangular minimum height.
+        /// </summary>
+        protected int RectMinHeight { get; set; } = 10;
+
+        /// <summary>
+        /// Current set of ignore points.
+        /// </summary>
+        protected List<PointControlType> ignorePoints { get; set; } = new List<PointControlType>();
+
+        /// <summary>
+        /// Current set of drawing rectangles (original data).
+        /// </summary>
+        protected Rect SetDrawRect { get; set; } = new Rect(0, 0, 0, 0);
+
+        /// <summary>
+        /// Current drawing rectangle (calculated during operation).
+        /// </summary>
+        protected Rect drawRect { get; set; } = new Rect(0, 0, 0, 0);
+
+        /// <summary>
+        /// Maximum range that can be drawn.
+        /// </summary>
+        protected Rect maxRect { get; set; } = new Rect(0, 0, 0, 0);
+
+        /// <summary>
+        /// Current center point of the drawing rectangle.
+        /// </summary>
+        protected Point drawCenterPoint { get; private set; } = new Point(0, 0);
+
+        /// <summary>
+        /// When the mouse is pressed, the cached rectangle.
+        /// </summary>
+        protected Rect cacheRect { get; set; } = new Rect(0, 0, 0, 0);
+
+        /// <summary>
+        /// Current control point coordinates.
+        /// </summary>
+        protected List<Point> controlPoints { get; set; } = new List<Point>();
+
+        /// <summary>
+        /// Move offset during movement.
+        /// </summary>
+        protected Point moveOffset { get; set; } = new Point(0, 0);
+
+        /// <summary>
+        /// Current actual display width and height of PDFVIewer.
+        /// </summary>
+        protected double PDFViewerActualWidth { get; set; } = 0;
+
+        protected double PDFViewerActualHeight { get; set; } = 0;
+
+        protected double rectPadding = 6;
+
+        protected double currentZoom = 1;
+
+        protected SelectedAnnotData selectedRectData = new SelectedAnnotData();
+
+        protected bool disable = false;
+
+        #endregion
+
+        #region Functions
+
+        /// <summary>
+        /// Calcuate the control points
+        /// </summary>
+        /// <param name="currentRect">
+        /// Control points in the target rectangle
+        /// </param>
+        protected void CalcControlPoint(Rect currentRect)
+        {
+            controlPoints.Clear();
+            int centerX = (int)(currentRect.Left + currentRect.Right) / 2;
+            int centerY = (int)(currentRect.Top + currentRect.Bottom) / 2;
+
+            controlPoints.Add(new Point(currentRect.Left, currentRect.Top));
+            controlPoints.Add(new Point(currentRect.Left, centerY));
+            controlPoints.Add(new Point(currentRect.Left, currentRect.Bottom));
+            controlPoints.Add(new Point(centerX, currentRect.Bottom));
+            controlPoints.Add(new Point(currentRect.Right, currentRect.Bottom));
+            controlPoints.Add(new Point(currentRect.Right, centerY));
+            controlPoints.Add(new Point(currentRect.Right, currentRect.Top));
+            controlPoints.Add(new Point(centerX, currentRect.Top));
+        }
+
+        /// <summary>
+        /// Calcuate the offset of the current rectangle in the maximum rectangle range
+        /// </summary>
+        /// <param name="currentRect">
+        /// The rectangle cached when pressed
+        /// </param>
+        /// <param name="offsetPoint">
+        /// Equivalent to the offset value when pressed
+        /// </param>
+        /// <param name="maxRect">
+        /// The maximum rectangle range.
+        /// </param>
+        /// <returns></returns>
+        protected Point CalcMoveBound(Rect currentRect, Point offsetPoint, Rect maxRect)
+        {
+            double cLeft = currentRect.Left;
+            double cRight = currentRect.Right;
+            double cUp = currentRect.Top;
+            double cDown = currentRect.Bottom;
+
+            double TmpLeft = cLeft + offsetPoint.X;
+            double TmpRight = cRight + offsetPoint.X;
+            double TmpUp = cUp + offsetPoint.Y;
+            double TmpDown = cDown + offsetPoint.Y;
+            if (TmpLeft < maxRect.Left)
+            {
+                TmpRight = (cRight - cLeft) + maxRect.Left;
+                TmpLeft = maxRect.Left;
+            }
+            if (TmpUp < maxRect.Top)
+            {
+                TmpDown = (cDown - cUp) + maxRect.Top;
+                TmpUp = maxRect.Top;
+            }
+            if (TmpRight > maxRect.Right)
+            {
+                TmpLeft = maxRect.Right - (cRight - cLeft);
+                TmpRight = maxRect.Right;
+            }
+            if (TmpDown > maxRect.Bottom)
+            {
+                TmpUp = maxRect.Bottom - (cDown - cUp);
+                TmpDown = maxRect.Bottom;
+            }
+            offsetPoint = new Point(TmpLeft - cLeft, TmpUp - cUp);
+            return offsetPoint;
+        }
+
+        /// <summary>
+        /// Calculate the movement of the hit point
+        /// </summary>
+        /// <param name="mousePoint">
+        /// Current mouse position
+        /// </param>
+        /// <returns>
+        /// Whether the movement is successful
+        /// </returns>
+        protected bool CalcHitPointMove(Point mousePoint)
+        {
+            if (isMouseDown == false || hitControlType == PointControlType.None)
+            {
+                return false;
+            }
+            return NormalScaling(mousePoint);
+        }
+
+        private Size GetProportionalScalingSize(double width, double height)
+        {
+            double minHeight = RectMinHeight + 2 * rectPadding * currentZoom;
+            double minWidth = rectMinWidth + 2 * rectPadding * currentZoom;
+            if (minWidth > width || minHeight > height)
+            {
+                if (cacheRect.Width >= cacheRect.Height)
+                {
+                    width = cacheRect.Width * minHeight / cacheRect.Height;
+                    height = minHeight;
+                }
+                else
+                {
+                    height = cacheRect.Height * minWidth / cacheRect.Width;
+                    width = minWidth;
+                }
+            }
+
+            return new Size(width, height);
+        }
+
+        /// <summary>
+        /// Draw the algorithm in the form of normal scaling (drag a point, only scale in one direction).
+        /// </summary>
+        /// <param name="mousePoint">Current mouse position.</param>
+        /// <returns></returns>
+        protected bool NormalScaling(Point mousePoint)
+        {
+            double left = 0, right = 0, top = 0, bottom = 0;
+            double minHeight = RectMinHeight + 2 * rectPadding * currentZoom;
+            double minWidth = rectMinWidth + 2 * rectPadding * currentZoom;
+
+            Point centerPoint = new Point((cacheRect.Right + cacheRect.Left) / 2, (cacheRect.Bottom + cacheRect.Top) / 2);
+            Point moveVector = (Point)(mousePoint - centerPoint);
+            moveVector = ProportionalScalingOffsetPos(moveVector);
+
+            switch (hitControlType)
+            {
+                case PointControlType.LeftTop:
+                    {
+                        left = centerPoint.X + moveVector.X;
+                        right = cacheRect.Right;
+                        top = centerPoint.Y + moveVector.Y;
+                        bottom = cacheRect.Bottom;
+                        if (isProportionalScaling)
+                        {
+                            Size size = GetProportionalScalingSize(right - left, bottom - top);
+                            left = right - size.Width;
+                            top = bottom - size.Height;
+                            if(left < maxRect.Left)
+                            {
+                                double tmpWidth = right - left;
+                                left = maxRect.Left;
+                                double width = right - left;
+                                double height = (bottom - top) * width / tmpWidth;
+                                top = bottom - height;
+                            }
+
+                            if (top < maxRect.Top)
+                            {
+                                double tmpHeight = bottom - top;
+                                top = maxRect.Top;
+                                double height = bottom - top;
+                                double width = (right - left) * height / tmpHeight;
+                                left = right - width;
+                            }
+                        }
+                        else
+                        {
+                            if (left + minWidth > right)
+                            {
+                                left = right - minWidth;
+                            }
+
+                            if (top + minHeight > bottom)
+                            {
+                                top = bottom - minHeight;
+                            }
+                        }
+                    }
+                    break;
+
+                case PointControlType.LeftMiddle:
+                    {
+                        left = centerPoint.X + moveVector.X;
+                        right = cacheRect.Right;
+                        top = cacheRect.Top;
+                        bottom = cacheRect.Bottom;
+                        if (left + minWidth > right)
+                        {
+                            left = right - minWidth;
+                        }
+                    }
+                    break;
+
+                case PointControlType.LeftBottom:
+                    {
+                        left = centerPoint.X + moveVector.X;
+                        right = cacheRect.Right;
+                        top = cacheRect.Top;
+                        bottom = centerPoint.Y + moveVector.Y;
+                        if (isProportionalScaling)
+                        {
+                            Size size = GetProportionalScalingSize(right - left, bottom - top);
+                            left = right - size.Width;
+                            bottom = top + size.Height;
+                            if (left < maxRect.Left)
+                            {
+                                double tmpWidth = right - left;
+                                left = maxRect.Left;
+                                double width = right - left;
+                                double height = (bottom - top) * width / tmpWidth;
+                                bottom = top + height;
+                            }
+
+                            if (bottom > maxRect.Bottom)
+                            {
+                                double tmpHeight = bottom - top;
+                                bottom = maxRect.Bottom;
+                                double height = bottom - top;
+                                double width = (right - left) * height / tmpHeight;
+                                left = right - width;
+                            }
+                        }
+                        else
+                        {
+                            if (left + minWidth > right)
+                            {
+                                left = right - minWidth;
+                            }
+                            
+                            if (top + minHeight > bottom)
+                            {
+                                bottom = top + minHeight;
+                            }
+                        }
+                    }
+                    break;
+
+                case PointControlType.MiddlBottom:
+                    {
+                        left = cacheRect.Left;
+                        right = cacheRect.Right;
+                        top = cacheRect.Top;
+                        bottom = centerPoint.Y + moveVector.Y;
+                        if (top + minHeight > bottom)
+                        {
+                            bottom = top + minHeight;
+                        }
+                    }
+                    break;
+
+                case PointControlType.RightBottom:
+                    {
+                        left = cacheRect.Left;
+                        right = centerPoint.X + moveVector.X;
+                        top = cacheRect.Top;
+                        bottom = centerPoint.Y + moveVector.Y;
+                        if (isProportionalScaling)
+                        {
+                            Size size = GetProportionalScalingSize(right - left, bottom - top);
+                            right = left + size.Width;
+                            bottom = top + size.Height;
+                            if (right > maxRect.Right)
+                            {
+                                double tmpWidth = right - left;
+                                right = maxRect.Right;
+                                double width = right - left;
+                                double height = (bottom - top) * width / tmpWidth;
+                                bottom = top + height;
+                            }
+
+                            if (bottom > maxRect.Bottom)
+                            {
+                                double tmpHeight = bottom - top;
+                                bottom = maxRect.Bottom;
+                                double height = bottom - top;
+                                double width = (right - left) * height / tmpHeight;
+                                right = left + width;
+                            }
+                        }
+                        else
+                        {
+                            if (left + minWidth > right)
+                            {
+                                right = left + minWidth;
+                            }
+
+                            if (top + minHeight > bottom)
+                            {
+                                bottom = top + minHeight;
+                            }
+                        }
+                    }
+                    break;
+
+                case PointControlType.RightMiddle:
+                    {
+                        left = cacheRect.Left;
+                        right = centerPoint.X + moveVector.X;
+                        top = cacheRect.Top;
+                        bottom = cacheRect.Bottom;
+                        if (left + minWidth > right)
+                        {
+                            right = left + minWidth;
+                        }
+                    }
+                    break;
+
+                case PointControlType.RightTop:
+                    {
+                        left = cacheRect.Left;
+                        right = centerPoint.X + moveVector.X;
+                        top = centerPoint.Y + moveVector.Y;
+                        bottom = cacheRect.Bottom;
+                        if (isProportionalScaling)
+                        {
+                            Size size = GetProportionalScalingSize(right - left, bottom - top);
+                            right = left + size.Width;
+                            top = bottom - size.Height;
+                            if (right > maxRect.Right)
+                            {
+                                double tmpWidth = right - left;
+                                right = maxRect.Right;
+                                double width = right - left;
+                                double height = (bottom - top) * width / tmpWidth;
+                                top =  bottom - height;
+                            }
+
+                            if (top < maxRect.Top)
+                            {
+                                double tmpHeight = bottom - top;
+                                top = maxRect.Top;
+                                double height = bottom - top;
+                                double width = (right - left) * height / tmpHeight;
+                                right = left + width;
+                            }
+                        }
+                        else
+                        {
+                            if (left + minWidth > right)
+                            {
+                                right = left + minWidth;
+                            }
+
+                            if (top + minHeight > bottom)
+                            {
+                                top = bottom - minHeight;
+                            }
+                        }
+                    }
+                    break;
+
+                case PointControlType.MiddleTop:
+                    {
+                        left = cacheRect.Left;
+                        right = cacheRect.Right;
+                        top = centerPoint.Y + moveVector.Y;
+                        bottom = cacheRect.Bottom;
+                        if (top + minHeight > bottom)
+                        {
+                            top = bottom - minHeight;
+                        }
+                    }
+                    break;
+
+                case PointControlType.Body:
+                case PointControlType.Line:
+                    {
+                        Point OffsetPos = CalcMoveBound(cacheRect, ((Point)(mousePoint - mouseDownPoint)), maxRect);
+                        left = cacheRect.Left + OffsetPos.X;
+                        right = cacheRect.Right + OffsetPos.X;
+                        top = cacheRect.Top + OffsetPos.Y;
+                        bottom = cacheRect.Bottom + OffsetPos.Y;
+                    }
+                    break;
+
+                default:
+                    break;
+            }
+
+            if (left < maxRect.Left)
+            {
+                left = maxRect.Left;
+            }
+
+            if (top < maxRect.Top)
+            {
+                top = maxRect.Top;
+            }
+
+            if (right > maxRect.Right)
+            {
+                right = maxRect.Right;
+            }
+
+            if (bottom > maxRect.Bottom)
+            {
+                bottom = maxRect.Bottom;
+            }
+
+            drawRect = new Rect(left, top, right - left, bottom - top);
+            moveOffset = new Point(drawRect.X - cacheRect.X, drawRect.Y - cacheRect.Y);
+            return true;
+        }
+
+        /// <summary>
+        /// Proportional scaling offset calibration
+        /// </summary>
+        /// <param name="movePoint">
+        /// The current movement point
+        /// </param>
+        /// <returns>
+        /// The offset point after the proportional scaling
+        /// </returns>
+        protected Point ProportionalScalingOffsetPos(Point movePoint)
+        {
+            if (isProportionalScaling)
+            {
+                Point offsetPos = movePoint;
+                double ratioX = cacheRect.Width > 0 ? cacheRect.Height / cacheRect.Width : 1;
+                double ratioY = cacheRect.Height > 0 ? cacheRect.Width / cacheRect.Height : 1;
+                switch (hitControlType)
+                {
+                    case PointControlType.LeftTop:
+                    case PointControlType.RightBottom:
+                        offsetPos = new Point(movePoint.X, Math.Abs(movePoint.X) * ratioX * (movePoint.X < 0 ? -1 : 1));
+                        break;
+                    case PointControlType.LeftBottom:
+                    case PointControlType.RightTop:
+                        offsetPos = new Point(movePoint.X, Math.Abs(movePoint.X) * ratioX * (movePoint.X < 0 ? 1 : -1));
+                        break;
+                    case PointControlType.LeftMiddle:
+                        offsetPos = new Point(movePoint.X, Math.Abs(movePoint.X) * ratioX * (movePoint.X < 0 ? 1 : -1));
+                        break;
+                    case PointControlType.RightMiddle:
+                        offsetPos = new Point(movePoint.X, Math.Abs(movePoint.X) * ratioX * (movePoint.X < 0 ? -1 : 1));
+                        break;
+                    case PointControlType.MiddlBottom:
+                        offsetPos = new Point(Math.Abs(movePoint.Y) * ratioY * (movePoint.Y < 0 ? 1 : -1), movePoint.Y);
+                        break;
+                    case PointControlType.MiddleTop:
+                        offsetPos = new Point(Math.Abs(movePoint.Y) * ratioY * (movePoint.Y < 0 ? -1 : 1), movePoint.Y);
+                        break;
+                    default:
+                        break;
+                }
+                return offsetPos;
+            }
+            else
+            {
+                return movePoint;
+            }
+        }
+
+        /// <summary>
+        /// Inner drawing circle point
+        /// </summary>
+        /// <param name="drawingContext">
+        /// Drawing context
+        /// </param>
+        /// <param name="ignoreList">
+        /// Collection of positions that need to be drawn
+        /// </param>
+        /// <param name="PointSize">
+        /// Size of the point
+        /// </param>
+        /// <param name="PointPen">
+        /// Brush for drawing points
+        /// </param>
+        /// <param name="BorderBrush">
+        /// Border brush for drawing points
+        /// </param>
+        protected void DrawCirclePoint(DrawingContext drawingContext, List<PointControlType> ignoreList, int PointSize, Pen PointPen, SolidColorBrush BorderBrush)
+        {
+            GeometryGroup controlGroup = new GeometryGroup();
+            controlGroup.FillRule = FillRule.Nonzero;
+
+            List<Point> ignorePointsList = new List<Point>();
+            // Get specific points
+            foreach (PointControlType type in ignoreList)
+            {
+                if ((int)type < controlPoints.Count)
+                {
+                    ignorePointsList.Add(controlPoints[(int)type]);
+                }
+            }
+            for (int i = 0; i < controlPoints.Count; i++)
+            {
+                Point controlPoint = controlPoints[i];
+
+                if (ignorePointsList.Contains(controlPoint))
+                {
+                    continue;
+                }
+                EllipseGeometry circlPoint = new EllipseGeometry(controlPoint, PointSize, PointSize);
+                controlGroup.Children.Add(circlPoint);
+            }
+            drawingContext?.DrawGeometry(BorderBrush, PointPen, controlGroup);
+        }
+
+        /// <summary>
+        /// Inner drawing square
+        /// </summary>
+        /// <param name="drawingContext">
+        /// Drawing context
+        /// </param>
+        /// <param name="ControlPoints">
+        /// Collection of positions that need to be drawn
+        /// </param>
+        /// <param name="PointSize">
+        /// Size of the point
+        /// </param>
+        /// <param name="PointPen">
+        /// Brush for drawing points
+        /// </param>
+        /// <param name="BorderBrush">
+        /// Border brush for drawing points
+        /// </param>
+        protected void DrawSquarePoint(DrawingContext drawingContext, List<PointControlType> ignoreList, int PointSize, Pen PointPen, SolidColorBrush BorderBrush)
+        {
+            GeometryGroup controlGroup = new GeometryGroup();
+            controlGroup.FillRule = FillRule.Nonzero;
+            List<Point> ignorePointsList = new List<Point>();
+            // Get specific points
+            foreach (PointControlType type in ignoreList)
+            {
+                if ((int)type < controlPoints.Count)
+                {
+                    ignorePointsList.Add(controlPoints[(int)type]);
+                }
+            }
+            for (int i = 0; i < controlPoints.Count; i++)
+            {
+                Point controlPoint = controlPoints[i];
+                if (ignorePointsList.Contains(controlPoint))
+                {
+                    continue;
+                }
+                RectangleGeometry rectPoint = new RectangleGeometry(new Rect(controlPoint.X - PointSize, controlPoint.Y - PointSize,
+                    PointSize * 2, PointSize * 2), 1, 1);
+                controlGroup.Children.Add(rectPoint);
+            }
+            drawingContext?.DrawGeometry(BorderBrush, PointPen, controlGroup);
+        }
+        
+        protected void DrawCropPoint(DrawingContext drawingContext, List<PointControlType> ignoreList, int PointSize, Pen PointPen, SolidColorBrush BorderBrush)
+        {
+            GeometryGroup controlGroup = new GeometryGroup();
+            controlGroup.FillRule = FillRule.Nonzero;
+            
+            //Left Top Corner
+            if (!ignoreList.Contains(PointControlType.LeftTop))
+            {
+                drawingContext?.DrawRectangle(BorderBrush, null, new Rect(controlPoints[0].X - PointSize, controlPoints[0].Y - PointSize, PointSize, PointSize * 4));
+                drawingContext?.DrawRectangle(BorderBrush, null, new Rect(controlPoints[0].X - PointSize, controlPoints[0].Y - PointSize, PointSize * 4, PointSize));
+            }
+            
+            //Left Center
+            if (!ignoreList.Contains(PointControlType.LeftMiddle))
+            {
+                drawingContext?.DrawRectangle(BorderBrush, null, new Rect(controlPoints[1].X - PointSize, (controlPoints[1].Y + controlPoints[1].Y - PointSize * 5) / 2, PointSize, PointSize * 5));
+            }
+
+            //Left Bottom Corner
+            if (!ignoreList.Contains(PointControlType.LeftBottom))
+            {
+                drawingContext?.DrawRectangle(BorderBrush, null, new Rect(controlPoints[2].X - PointSize, controlPoints[2].Y - PointSize * 3, PointSize, PointSize * 4));
+                drawingContext?.DrawRectangle(BorderBrush, null, new Rect(controlPoints[2].X - PointSize, controlPoints[2].Y, PointSize * 4, PointSize));
+            }
+            
+            //Bottom Center
+            if (!ignoreList.Contains(PointControlType.MiddlBottom))
+            {
+                drawingContext?.DrawRectangle(BorderBrush, null, new Rect((controlPoints[3].X + controlPoints[3].X - PointSize * 5) / 2, controlPoints[3].Y, PointSize * 5, PointSize));
+            }
+
+            //Bottom Right Corner
+            if (!ignoreList.Contains(PointControlType.RightBottom))
+            {
+                drawingContext?.DrawRectangle(BorderBrush, null, new Rect(controlPoints[4].X , controlPoints[4].Y - PointSize * 3, PointSize, PointSize * 4));
+                drawingContext?.DrawRectangle(BorderBrush, null, new Rect(controlPoints[4].X - PointSize * 3, controlPoints[4].Y, PointSize * 4, PointSize));
+            }
+            
+            //Right Center
+            if (!ignoreList.Contains(PointControlType.RightMiddle))
+            {
+                drawingContext?.DrawRectangle(BorderBrush, null, new Rect(controlPoints[5].X, (controlPoints[5].Y + controlPoints[5].Y - PointSize * 5) / 2, PointSize, PointSize * 5));
+            }
+            
+            //Right Top Corner
+            if (!ignoreList.Contains(PointControlType.RightTop))
+            {
+                drawingContext?.DrawRectangle(BorderBrush, null, new Rect(controlPoints[6].X, controlPoints[6].Y - PointSize, PointSize, PointSize * 4));
+                drawingContext?.DrawRectangle(BorderBrush, null, new Rect(controlPoints[6].X - PointSize * 4, controlPoints[6].Y - PointSize, PointSize * 4, PointSize));
+            }
+            
+            //Top Center
+            if (!ignoreList.Contains(PointControlType.MiddleTop))
+            {
+                drawingContext?.DrawRectangle(BorderBrush, null, new Rect((controlPoints[7].X + controlPoints[7].X - PointSize * 5) / 2, controlPoints[7].Y-PointSize, PointSize * 5, PointSize));
+            }
+            drawingContext?.DrawGeometry(BorderBrush, PointPen, controlGroup);
+        }
+
+        /// <summary>
+        /// Draw the reference line in the moving state
+        /// </summary>
+        /// <param name="drawDc">
+        ///  Draw context handle
+        /// </param>
+        /// <param name="controltype">
+        /// Current selected control point type
+        /// </param>
+        /// <param name="activePen">
+        /// Brush for drawing lines
+        /// </param>
+        /// <param name="moveBrush">
+        /// Brush for drawing rectangles
+        /// </param>
+        /// <param name="moveRect">
+        /// Current rectangle to draw
+        /// </param>
+        protected void DrawMoveBounds(DrawingContext drawDc, PointControlType controltype, Pen activePen, Brush moveBrush, Rect moveRect)
+        {
+            switch (controltype)
+            {
+                case PointControlType.LeftTop:
+                    drawDc?.DrawLine(activePen, new Point(0, moveRect.Top), new Point(PDFViewerActualWidth, moveRect.Top));
+                    drawDc?.DrawLine(activePen, new Point(moveRect.Left, 0), new Point(moveRect.Left, PDFViewerActualHeight));
+                    break;
+                case PointControlType.LeftMiddle:
+                    drawDc?.DrawLine(activePen, new Point(moveRect.Left, 0), new Point(moveRect.Left, PDFViewerActualHeight));
+                    break;
+                case PointControlType.LeftBottom:
+                    drawDc?.DrawLine(activePen, new Point(0, moveRect.Bottom), new Point(PDFViewerActualWidth, moveRect.Bottom));
+                    drawDc?.DrawLine(activePen, new Point(moveRect.Left, 0), new Point(moveRect.Left, PDFViewerActualHeight));
+                    break;
+                case PointControlType.MiddlBottom:
+                    drawDc?.DrawLine(activePen, new Point(0, moveRect.Bottom), new Point(PDFViewerActualWidth, moveRect.Bottom));
+                    break;
+                case PointControlType.RightBottom:
+                    drawDc?.DrawLine(activePen, new Point(0, moveRect.Bottom), new Point(PDFViewerActualWidth, moveRect.Bottom));
+                    drawDc?.DrawLine(activePen, new Point(moveRect.Right, 0), new Point(moveRect.Right, PDFViewerActualHeight));
+                    break;
+                case PointControlType.RightMiddle:
+                    drawDc?.DrawLine(activePen, new Point(moveRect.Right, 0), new Point(moveRect.Right, PDFViewerActualHeight));
+                    break;
+                case PointControlType.RightTop:
+                    drawDc?.DrawLine(activePen, new Point(0, moveRect.Top), new Point(PDFViewerActualWidth, moveRect.Top));
+                    drawDc?.DrawLine(activePen, new Point(moveRect.Right, 0), new Point(moveRect.Right, PDFViewerActualHeight));
+                    break;
+                case PointControlType.MiddleTop:
+                    drawDc?.DrawLine(activePen, new Point(0, moveRect.Top), new Point(PDFViewerActualWidth, moveRect.Top));
+                    break;
+                case PointControlType.Rotate:
+                    break;
+                case PointControlType.Body:
+                case PointControlType.Line:
+                    drawDc?.DrawLine(activePen, new Point(0, moveRect.Top), new Point(PDFViewerActualWidth, moveRect.Top));
+                    drawDc?.DrawLine(activePen, new Point(0, moveRect.Bottom), new Point(PDFViewerActualWidth, moveRect.Bottom));
+                    drawDc?.DrawLine(activePen, new Point(moveRect.Left, 0), new Point(moveRect.Left, PDFViewerActualHeight));
+                    drawDc?.DrawLine(activePen, new Point(moveRect.Right, 0), new Point(moveRect.Right, PDFViewerActualHeight));
+                    break;
+                default:
+                    break;
+            }
+            drawDc?.DrawRectangle(moveBrush, null, moveRect);
+        }
+
+        /// <summary>
+        /// Notify the event during/after the drawing data
+        /// </summary>
+        /// <param name="isFinish">
+        /// Identifies whether the data change is complete
+        /// </param>
+        protected void InvokeDataChangEvent(bool isFinish)
+        {
+            selectedRectData.Square = GetRect();
+            if (isFinish)
+            {
+                DataChanged?.Invoke(this, selectedRectData);
+            }
+            else
+            {
+                DataChanging?.Invoke(this, selectedRectData);
+            }
+        }
+
+        /// <summary>
+        /// Align the rectangle drawing
+        /// </summary>
+        /// <param name="RectMovePoint">
+        /// Move distance required for the aligned algorithm to obtain the rectangle
+        /// </param>
+        private void DrawAlignRect(Point RectMovePoint)
+        {
+            double TmpLeft, TmpRight, TmpUp, TmpDown;
+            Point OffsetPos = CalcMoveBound(drawRect, RectMovePoint, maxRect);
+            TmpLeft = drawRect.Left + OffsetPos.X;
+            TmpRight = drawRect.Right + OffsetPos.X;
+            TmpUp = drawRect.Top + OffsetPos.Y;
+            TmpDown = drawRect.Bottom + OffsetPos.Y;
+            SetDrawRect = drawRect = new Rect(TmpLeft, TmpUp, TmpRight - TmpLeft, TmpDown - TmpUp);
+            Draw();
+        }
+
+
+        /// <summary>
+        /// Get the current set of ignore points
+        /// </summary>
+        /// <returns>
+        /// Data set of ignored points
+        /// </returns>
+        private List<PointControlType> GetIgnorePoints()
+        {
+            List<PointControlType> IgnorePointsList = new List<PointControlType>();
+            foreach (PointControlType type in ignorePoints)
+            {
+                IgnorePointsList.Add(type);
+            }
+            return IgnorePointsList;
+        }
+
+        #endregion
+    }
+}

+ 108 - 0
Demo/Examples/ComPDFKit.Tool/Help/CommonHelper.cs

@@ -0,0 +1,108 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Media;
+using System.Windows;
+
+namespace ComPDFKit.Tool.Help
+{
+    public static class CommonHelper
+    {
+        /// <summary>
+        /// Find the parent control of the target type of the object
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="obj"></param>
+        /// <returns></returns>
+        public static T FindVisualParent<T>(DependencyObject obj) where T : class
+        {
+            try
+            {
+                while (obj != null)
+                {
+                    if (obj is T)
+                        return obj as T;
+
+                    obj = VisualTreeHelper.GetParent(obj);
+                }
+                return null;
+            }
+            catch { return null; }
+        }
+
+        /// <summary>
+        /// Find the child control of the target type of the object
+        /// </summary>
+        /// <typeparam name="childItem">
+        /// The type of the child control to find
+        /// </typeparam>
+        /// <param name="obj">
+        /// The object to find
+        /// </param>
+        /// <returns>
+        /// The child control of the target type of the object
+        /// </returns>
+        public static childItem FindVisualChild<childItem>(DependencyObject obj)
+            where childItem : DependencyObject
+        {
+            try
+            {
+                for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
+                {
+                    DependencyObject child = VisualTreeHelper.GetChild(obj, i);
+                    if (child != null && child is childItem)
+                        return (childItem)child;
+                    else
+                    {
+                        childItem childOfChild = FindVisualChild<childItem>(child);
+                        if (childOfChild != null)
+                            return childOfChild;
+                    }
+                }
+                return null;
+            }
+            catch { return null; }
+        }
+
+        /// <summary>
+        /// Find the child control of the target type of the object
+        /// </summary>
+        /// <typeparam name="childItem">
+        /// The type of the child control to find
+        /// </typeparam>
+        /// <param name="obj">
+        /// The object to find
+        /// </param>
+        /// <returns>
+        /// The child control of the target type of the object
+        /// </returns>
+        public static List<childItem> FindVisualChildList<childItem>(DependencyObject obj)
+            where childItem : DependencyObject
+        {
+            List <childItem> children = new List <childItem>();
+            try
+            {
+                for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
+                {
+                    DependencyObject child = VisualTreeHelper.GetChild(obj, i);
+                    if (child != null && child is childItem)
+                    {
+                        children.Add((childItem)child);
+                    }
+                    else
+                    {
+                        childItem childOfChild = FindVisualChild<childItem>(child);
+                        if (childOfChild != null)
+                            children.Add(childOfChild);
+                    }
+                }
+                return children;
+            }
+            catch { return children; }
+        }
+
+
+    }
+}

+ 45 - 0
Demo/Examples/ComPDFKit.Tool/Help/ImportWin32.cs

@@ -0,0 +1,45 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace ComPDFKit.Tool.Help
+{
+    internal class ImportWin32
+    {
+        private const string ImeDll = "imm32.dll";
+        [StructLayout(LayoutKind.Sequential)]
+        internal struct RECT
+        {
+            public int left;
+            public int top;
+            public int right;
+            public int bottom;
+        }
+        [StructLayout(LayoutKind.Sequential)]
+        internal struct POINT
+        {
+            public int x;
+            public int y;
+        }
+        [StructLayout(LayoutKind.Sequential)]
+        internal struct CompositionForm
+        {
+            public int dwStyle;
+            public POINT ptCurrentPos;
+            public RECT rcArea;
+        }
+        [DllImport(ImeDll)]
+        internal static extern IntPtr ImmGetContext(IntPtr hWnd);
+        [DllImport(ImeDll)]
+        [return: MarshalAs(UnmanagedType.Bool)]
+        internal static extern bool ImmReleaseContext(IntPtr hWnd, IntPtr hIMC);
+        [DllImport(ImeDll)]
+        [return: MarshalAs(UnmanagedType.Bool)]
+        internal static extern bool ImmSetCompositionWindow(IntPtr hIMC, ref CompositionForm form);
+        [DllImport(ImeDll)]
+        internal static extern IntPtr ImmGetDefaultIMEWnd(IntPtr hWnd);
+    }
+}

+ 228 - 0
Demo/Examples/ComPDFKit.Tool/Help/PDFHelp.cs

@@ -0,0 +1,228 @@
+using ComPDFKit.Import;
+using ComPDFKit.PDFAnnotation;
+using ComPDFKit.PDFDocument;
+using ComPDFKit.PDFPage;
+using ComPDFKit.Tool.DrawTool;
+using ComPDFKit.Viewer.Helper;
+using System;
+using System.Collections.Generic;
+using System.Reflection;
+using System.Windows;
+using System.Windows.Media.Imaging;
+using System.Windows.Media;
+using System.IO;
+
+namespace ComPDFKit.Tool.Help
+{
+    public class PDFHelp
+    {
+        public static List<TextDrawRect> GetSelectTextRect(CPDFDocument document, int pageIndex, Point startPoint, Point endPoint, Point tolerance)
+        {
+            CPDFPage page = document.PageAtIndex(pageIndex);
+            if (page != null)
+            {
+                CPDFTextPage textPage = page.GetTextPage();
+                if (textPage != null)
+                {
+                    List<CRect> selectList = textPage.GetCharsRectAtPos(
+                       DataConversionForWPF.PointConversionForCPoint(startPoint),
+                        DataConversionForWPF.PointConversionForCPoint(endPoint),
+                        new CPoint(10, 10)
+                        );
+                    List<TextDrawRect> drawList = new List<TextDrawRect>();
+                    foreach (CRect rawRect in selectList)
+                    {
+                        drawList.Add(new TextDrawRect() { DrawRect = DataConversionForWPF.CRectConversionForRect( rawRect), Text = "" });
+                    }
+                    return drawList;
+                }
+            }
+            return new List<TextDrawRect>();
+        }
+     
+        public static string GetSelectText(CPDFDocument document, int pageIndex, Point startPoint, Point endPoint,Point tolerance)
+        {
+            if (document != null)
+            {
+                CPDFPage page = document.PageAtIndex(pageIndex);
+                if (page != null && page.IsValid())
+                {
+                    CPDFTextPage textPage = page.GetTextPage();
+                    if (textPage != null && textPage.IsValid())
+                    {
+                        return textPage.GetSelectText(
+                             DataConversionForWPF.PointConversionForCPoint(startPoint),
+                             DataConversionForWPF.PointConversionForCPoint(endPoint),
+                             DataConversionForWPF.PointConversionForCPoint(tolerance)
+                             );
+                    }
+                }
+            }
+            return string.Empty;
+        }
+
+        public static string GetDoubleClickText(CPDFDocument document, int pageIndex, Point startPoint, ref Rect uiRect)
+        {
+            CPDFPage page = document.PageAtIndex(pageIndex);
+            if (page != null)
+            {
+                CPDFTextPage textPage = page.GetTextPage();
+                if (textPage != null)
+                {
+                    CRect wordRect = new CRect();
+                   string Word =  textPage.GetSelectionForWordAtPos(
+                         DataConversionForWPF.PointConversionForCPoint(startPoint), 
+                        new CPoint(5, 5), ref wordRect);
+                    uiRect= DataConversionForWPF.CRectConversionForRect(wordRect);
+                    return Word;
+                }
+            }
+            return string.Empty;
+        }
+
+        public static string GetCurrentPdfTime()
+        {
+            DateTime localTime = DateTime.Now;
+            DateTime utcTime = DateTime.UtcNow;
+            int checkHour = (localTime - utcTime).Hours;
+            if (checkHour > 0)
+            {
+                return "D:" + localTime.ToString("yyyyMMddHHmmss") + "+" + checkHour.ToString("D2") + "'00'";
+            }
+            if (checkHour < 0)
+            {
+                return "D:" + localTime.ToString("yyyyMMddHHmmss") + "-" + Math.Abs(checkHour).ToString("D2") + "'00'";
+            }
+            return "D:" + localTime.ToString("yyyyMMddHHmmss") + "Z00'00'";
+        }
+
+        public static bool IsTextAtPos(CPDFDocument document, int pageIndex, Point pagePoint)
+        {
+
+            if (document==null)
+            {
+                return false;
+            }
+            CPDFPage page = document.PageAtIndex(pageIndex);
+            if (page == null)
+            {
+                return false;
+            }
+            CPDFTextPage textPage = page.GetTextPage();
+            if (textPage == null)
+            {
+                return false;
+            }
+            var x = textPage.GetCharIndexAtPos(
+                DataConversionForWPF.PointConversionForCPoint(pagePoint),
+                new CPoint(10, 10)
+                );
+            return x != -1;
+        }
+
+        public static void ImagePathToByte(string imagePath, ref byte[] imageData, ref int imageWidth, ref int imageHeight)
+        {
+            if (!File.Exists(imagePath))
+                return;
+
+            imagePath = Path.GetFullPath(imagePath);
+            BitmapFrame frame = null;
+            BitmapDecoder decoder = BitmapDecoder.Create(new Uri(imagePath), BitmapCreateOptions.None, BitmapCacheOption.Default);
+            if (decoder != null && decoder.Frames.Count > 0)
+            {
+                frame = decoder.Frames[0];
+            }
+            if (frame != null)
+            {
+                imageData = new byte[frame.PixelWidth * frame.PixelHeight * 4];
+                if (frame.Format != PixelFormats.Bgra32)
+                {
+                    FormatConvertedBitmap covert = new FormatConvertedBitmap(frame, PixelFormats.Bgra32, frame.Palette, 0);
+                    covert.CopyPixels(imageData, frame.PixelWidth * 4, 0);
+                }
+                else
+                {
+                    frame.CopyPixels(imageData, frame.PixelWidth * 4, 0);
+                }
+
+                imageWidth = frame.PixelWidth;
+                imageHeight = frame.PixelHeight;
+            }
+        }
+
+        public static void ImageStreamToByte(Stream imageStream, ref byte[] imageData, ref int imageWidth, ref int imageHeight)
+        {
+            imageStream.Seek(0, SeekOrigin.Begin);
+            BitmapDecoder decoder = BitmapDecoder.Create(imageStream,BitmapCreateOptions.None,BitmapCacheOption.Default);
+            BitmapFrame frame = null;
+            if (decoder != null && decoder.Frames.Count > 0)
+            {
+                frame = decoder.Frames[0];
+            }
+
+            if (frame != null)
+            {
+                imageData = new byte[frame.PixelWidth * frame.PixelHeight * 4];
+                if (frame.Format != PixelFormats.Bgra32)
+                {
+                    FormatConvertedBitmap covert = new FormatConvertedBitmap(frame, PixelFormats.Bgra32, frame.Palette, 0);
+                    covert.CopyPixels(imageData, frame.PixelWidth * 4, 0);
+                }
+                else
+                {
+                    frame.CopyPixels(imageData, frame.PixelWidth * 4, 0);
+                }
+
+                imageWidth = frame.PixelWidth;
+                imageHeight = frame.PixelHeight;
+            }
+        }
+    }
+
+    public static class PDFDateHelp
+    {
+        /// <summary>
+        /// Gets image for the appearance stream of the text stamp.
+        /// </summary>
+        /// <param name="stampText">Text</param>
+        /// <param name="date">Date</param>
+        /// <param name="shape">Shape</param>
+        /// <param name="color">Color</param>
+        /// <param name="stampWidth">Width of stamp</param>
+        /// <param name="stampHeight">Height of stamp</param>
+        /// <param name="imageWidth">Width of image</param>
+        /// <param name="imageHeight">Height of image</param>
+        /// <param name="rotation">Rotation angle</param>
+        /// <returns>Image data</returns>
+        public static byte[] GetTempTextStampImage(string stampText, string date, C_TEXTSTAMP_SHAPE shape, C_TEXTSTAMP_COLOR color, out int stampWidth, out int stampHeight, out int imageWidth, out int imageHeight, int rotation = 0)
+        {
+            CPDFDocument doc = CPDFDocument.CreateDocument();
+            string Path = null;
+            doc.InsertPage(0, 800, 1200, Path);
+            CPDFPage page = doc.PageAtIndex(0, false);
+            CPDFStampAnnotation stampAnnot = page.CreateAnnot(C_ANNOTATION_TYPE.C_ANNOTATION_STAMP) as CPDFStampAnnotation;
+
+            stampAnnot.SetTextStamp(stampText, date, shape, color, rotation);
+            stampAnnot.UpdateAp();
+
+            var flags = BindingFlags.NonPublic | BindingFlags.Static;
+            var dpiProperty = typeof(SystemParameters).GetProperty("Dpi", flags);
+            int dpi = (int)dpiProperty.GetValue(null, null);
+
+            CRect rect = stampAnnot.GetRect();
+            stampWidth = (int)rect.width();
+            stampHeight = (int)rect.height();
+            imageWidth = (int)(rect.width() * dpi / 72D * 2);
+            imageHeight = (int)(rect.height() * dpi / 72D * 2);
+
+            byte[] imageData = new byte[imageWidth * imageHeight * 4];
+            stampAnnot.RenderAnnot(imageWidth, imageHeight, imageData);
+
+            stampAnnot.ReleaseAnnot();
+            doc.ReleasePages(0);
+            doc.Release();
+
+            return imageData;
+        }
+    }
+}

Разница между файлами не показана из-за своего большого размера
+ 3141 - 0
Demo/Examples/ComPDFKit.Tool/Help/ParamConverter.cs


+ 297 - 0
Demo/Examples/ComPDFKit.Tool/PDFTextSearch.cs

@@ -0,0 +1,297 @@
+using ComPDFKit.Import;
+using ComPDFKit.NativeMethod;
+using ComPDFKit.PDFDocument;
+using ComPDFKit.PDFPage;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Text;
+using System.Text.RegularExpressions;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Windows.Media;
+using System.Windows;
+
+namespace ComPDFKit.Tool
+{
+    /// <summary>
+    /// This class is about search result object.
+    /// </summary>
+    public class TextSearchItem
+    {
+        /// <summary>
+        /// Page index
+        /// </summary>
+        public int PageIndex;
+        /// <summary>
+        /// The bounds of the selection on the page(PDF 72DPI)
+        /// </summary>
+        public Rect TextRect;
+        /// <summary>
+        /// The text contains in the selection
+        /// </summary>
+        public string TextContent;
+        /// <summary>
+        /// Page rotation angle
+        /// </summary>
+        public int PageRotate;
+
+        public void CreatePaintBrush(Color color)
+        {
+            Application.Current.Dispatcher.Invoke(() =>
+            {
+                this.PaintBrush = new SolidColorBrush(color);
+            });
+        }
+
+        public SolidColorBrush PaintBrush { get; private set; } = Brushes.Transparent;
+
+
+        public void CreateBorderBrush(Color color)
+        {
+            Application.Current.Dispatcher.Invoke(() =>
+            {
+                this.BorderBrush = new SolidColorBrush(color);
+            });
+        }
+        public SolidColorBrush BorderBrush { get; private set; } = Brushes.Transparent;
+
+
+        public int BorderThickness;
+        public Thickness Padding;
+    }
+
+    /// <summary>
+    /// Result of text search 
+    /// </summary>
+    public class TextSearchResult
+    {
+        /// <summary>
+        /// Start page index
+        /// </summary>
+        public int StartPage;
+        /// <summary>
+        /// End page index
+        /// </summary>
+        public int EndPage;
+        /// <summary>
+        /// Percentage of search process
+        /// </summary>
+        public double Percent;
+        /// <summary>
+        /// Current page index
+        /// </summary>
+        public int CurrentPage;
+        /// <summary>
+        /// The number of search result
+        /// </summary>
+        public int TotalCount;
+        /// <summary>
+        /// The maximum value of search result
+        /// </summary>
+        public int PageMaxCount;
+        /// <summary>
+        /// Details of current search result
+        /// </summary>
+        public Dictionary<int, List<TextSearchItem>> Items = new Dictionary<int, List<TextSearchItem>>();
+    }
+
+    /// <summary>
+    /// This class is about text search.
+    /// </summary>
+    public class PDFTextSearch
+    {
+        /// <summary>
+        /// A notification that a find operation finished working on a page of a document.
+        /// </summary>
+        public event EventHandler<TextSearchResult> SearchPercentHandler;
+
+        /// <summary>
+        /// A notification that a find operation cancels.
+        /// </summary>
+        public event EventHandler<TextSearchResult> SearchCancelHandler;
+
+        /// <summary>
+        /// A notification that a find operation finished of a document.
+        /// </summary>
+        public event EventHandler<TextSearchResult> SearchCompletedHandler;
+
+        /// <summary>
+        /// Associates a CPDFDocument.
+        /// </summary>
+        public CPDFDocument TextSearchDocument;
+
+        private CPDFDocument mSearchDocument;
+        private bool isCancel;
+        private string searchKeywords;
+        private string password;
+        private C_Search_Options searchOption;
+        private int startPage;
+        private int endPage;
+
+        /// <summary>
+        /// Whether to allow to search.
+        /// </summary>
+        public bool CanDoSearch { get; private set; } = true;
+
+        /// <summary>
+        /// Constructor function.
+        /// </summary>
+        public PDFTextSearch()
+        {
+
+        }
+
+        private void DoWork()
+        {
+            endPage = endPage == -1 ? TextSearchDocument.PageCount - 1 : Math.Min(TextSearchDocument.PageCount - 1, endPage);
+            TextSearchResult searchResult = new TextSearchResult();
+            searchResult.StartPage = startPage;
+            searchResult.EndPage = endPage;
+            double searchPercent = 100;
+            try
+            {
+
+                mSearchDocument = CPDFDocument.InitWithFilePath(TextSearchDocument.FilePath);
+                if (password != null && password != string.Empty)
+                {
+                    mSearchDocument.UnlockWithPassword(password);
+                }
+                password = string.Empty;
+            }
+            catch (Exception ex)
+            {
+
+            }
+            if (mSearchDocument != null)
+            {
+                try
+                {
+                    int pageMaxCount = 0;
+                    int recordCount = 0;
+                    searchPercent = 0;
+                    for (int i = startPage; i <= endPage; i++)
+                    {
+                        CPDFTextSearcher mPDFTextSearcher = new CPDFTextSearcher();
+                        CPDFPage pageCore = mSearchDocument.PageAtIndex(i);
+                        CPDFTextPage textPage = pageCore.GetTextPage();
+                        int startIndex = 0;
+
+                        List<TextSearchItem> textSearchItems = new List<TextSearchItem>();
+                        if (mPDFTextSearcher.FindStart(textPage, searchKeywords, searchOption, startIndex))
+                        {
+                            CRect textRect = new CRect();
+                            string textContent = "";
+                            while (mPDFTextSearcher.FindNext(pageCore, textPage, ref textRect, ref textContent, ref startIndex))
+                            {
+                                if (textContent == "")
+                                {
+                                    textContent = searchKeywords;
+                                }
+                                textSearchItems.Add(new TextSearchItem()
+                                {
+                                    PageIndex = i,
+                                    TextRect = new Rect(textRect.left, textRect.top, textRect.width(), textRect.height()),
+                                    TextContent = textContent,
+                                    PageRotate = pageCore.Rotation
+                                });
+                                var matchResult = Regex.Matches(textContent, searchKeywords, RegexOptions.IgnoreCase);
+                                if (matchResult != null)
+                                {
+                                    recordCount += matchResult.Count;
+                                }
+                            }
+                        }
+                        mPDFTextSearcher.FindClose();
+                        if (textSearchItems.Count > 0)
+                        {
+                            searchResult.Items.Add(i, textSearchItems);
+                        }
+                        pageMaxCount = Math.Max(pageMaxCount, textSearchItems.Count);
+                        searchResult.TotalCount = recordCount;
+                        searchResult.PageMaxCount = pageMaxCount;
+                        if (SearchPercentHandler != null)
+                        {
+                            searchPercent = (int)((i + 1 - startPage) * 100 / (endPage + 1 - startPage));
+                            searchResult.Percent = searchPercent;
+                            searchResult.CurrentPage = i;
+                            SearchPercentHandler.Invoke(this, searchResult);
+                        }
+                        mSearchDocument.ReleasePages(i);
+                        if (isCancel)
+                        {
+                            break;
+                        }
+                    }
+                    searchPercent = 100;
+                }
+                catch (Exception ex)
+                {
+
+                }
+                mSearchDocument.Release();
+            }
+            try
+            {
+                if (SearchCompletedHandler != null && !isCancel)
+                {
+                    searchResult.Percent = searchPercent;
+                    SearchCompletedHandler.Invoke(this, searchResult);
+                }
+                if (SearchCancelHandler != null && isCancel)
+                {
+                    SearchCancelHandler.Invoke(this, searchResult);
+                }
+            }
+            catch (Exception ex)
+            {
+
+            }
+            CanDoSearch = true;
+            isCancel = false;
+        }
+
+        /// <summary>
+        /// Cancles a search.
+        /// </summary>
+
+        public void CancleSearch()
+        {
+            isCancel = true;
+        }
+
+        /// <summary>
+        /// Searches the specified string in the document.
+        /// </summary>
+        /// <param name="search">Search the specified string</param>
+        /// <param name="option">Search options</param>
+        /// <param name="pwd">Document password</param>
+        /// <param name="startPage">Start page index</param>
+        /// <param name="endPage">End page index</param>
+        /// <returns>Returns true on success, false on failure</returns>
+        public bool SearchText(string search, C_Search_Options option, string pwd = "", int startPage = 0, int endPage = -1)
+        {
+            if (CPDFSDKVerifier.TextSearch == false)
+            {
+                Trace.WriteLine("Your license does not support this feature, please upgrade your license privilege.");
+                return false;
+            }
+
+            if (CanDoSearch)
+            {
+                searchKeywords = search;
+                password = pwd;
+                searchOption = option;
+                this.startPage = startPage;
+                this.endPage = endPage;
+                isCancel = false;
+                CanDoSearch = false;
+                Thread taskThread = new Thread(DoWork);
+                taskThread.Start();
+                return true;
+            }
+            return false;
+        }
+    }
+}

+ 19 - 0
Demo/Examples/ComPDFKit.Tool/Properties/AssemblyInfo.cs

@@ -0,0 +1,19 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+[assembly: AssemblyTitle("ComPDFKit.Tool")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("PDF Technologies, Inc.")]
+[assembly: AssemblyProduct("ComPDFKit.Tool")]
+[assembly: AssemblyCopyright("Copyright © 2014-2024 PDF Technologies, Inc. All Rights Reserved.")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+[assembly: ComVisible(false)]
+
+[assembly: Guid("a061ee7a-6704-4bd9-86ee-48ed5df75e2f")]
+
+[assembly: AssemblyVersion("2.0.0.0")]
+[assembly: AssemblyFileVersion("2.0.0.0")]

+ 45 - 0
Demo/Examples/ComPDFKit.Tool/SettingParam/AnnotParam.cs

@@ -0,0 +1,45 @@
+using ComPDFKit.Import;
+using ComPDFKit.PDFAnnotation;
+using System.Windows;
+
+namespace ComPDFKit.Tool
+{
+	public class AnnotParam
+    {
+        public string Content { get; set; } = string.Empty;
+        public string Author { get; set; } = string.Empty;
+        public string CreateTime { get; set; } = string.Empty;
+        public string UpdateTime { get; set; } = string.Empty;
+        public C_ANNOTATION_TYPE CurrentType {  get; set; }
+        public bool Locked { get; set; }
+
+        /// <summary>
+        /// The rectangle of the annotation within the PDF (PDF DPI)
+        /// </summary>
+        public CRect ClientRect { get; set; }
+        public int PageIndex {  get; set; }
+        public int AnnotIndex {  get; set; }
+        public byte Transparency {  get; set; }
+        public virtual bool CopyTo(AnnotParam transfer)
+        {
+            if (transfer == null)
+            {
+               return false;
+            }
+
+            transfer.Content = Content;
+            transfer.Author = Author;
+            transfer.CreateTime = CreateTime;
+            transfer.UpdateTime = UpdateTime;
+            transfer.CurrentType = CurrentType;
+            transfer.Locked = Locked;
+            transfer.ClientRect = ClientRect;
+            transfer.CurrentType=CurrentType;
+            transfer.PageIndex = PageIndex;
+            transfer.Transparency = Transparency;
+            transfer.AnnotIndex = AnnotIndex;
+
+            return true;
+        }
+    }
+}

+ 53 - 0
Demo/Examples/ComPDFKit.Tool/SettingParam/AnnotParam/CircleParam.cs

@@ -0,0 +1,53 @@
+using ComPDFKit.PDFAnnotation;
+using System.Windows.Media;
+
+namespace ComPDFKit.Tool
+{
+    public class CircleParam : AnnotParam
+    {
+        public CircleParam()
+        {
+            CurrentType = C_ANNOTATION_TYPE.C_ANNOTATION_CIRCLE;
+        }
+
+        public byte[] LineColor { get; set; }
+        public byte[] BgColor { get; set; }
+        public bool HasBgColor { get; set; }
+        public double LineWidth { get; set; }
+        public float[] LineDash { get; set; }
+
+        public C_BORDER_STYLE BorderStyle { get; set; }
+
+        public override bool CopyTo(AnnotParam transfer)
+        {
+            CircleParam circleTransfer = transfer as CircleParam;
+            if (circleTransfer == null)
+            {
+                return false;
+            }
+
+            if (!base.CopyTo(circleTransfer))
+            {
+                return false;
+            }
+
+            if (LineColor != null)
+            {
+                circleTransfer.LineColor = (byte[])LineColor.Clone();
+            }
+            if (BgColor != null)
+            {
+                circleTransfer.BgColor = (byte[])BgColor.Clone();
+            }
+
+            if (LineDash != null)
+            {
+                circleTransfer.LineDash = (float[])LineDash.Clone();
+            }
+            circleTransfer.LineWidth = LineWidth;
+            circleTransfer.HasBgColor = HasBgColor;
+            circleTransfer.BorderStyle = BorderStyle;
+            return true;
+        }
+    }
+}

+ 65 - 0
Demo/Examples/ComPDFKit.Tool/SettingParam/AnnotParam/FreeTextParam.cs

@@ -0,0 +1,65 @@
+using ComPDFKit.PDFAnnotation;
+using System.Windows;
+using System.Windows.Media;
+
+namespace ComPDFKit.Tool
+{
+	public class FreeTextParam:AnnotParam
+    {
+        public  FreeTextParam ()
+        {
+            CurrentType = C_ANNOTATION_TYPE.C_ANNOTATION_FREETEXT;
+        }
+
+        public byte[] LineColor { get; set; }
+        public byte[] BgColor { get; set; }
+        public bool HasBgColor {  get; set; }
+        public byte[] FontColor { get; set; }
+        public double LineWidth {  get; set; }
+        public bool IsBold { get; set; }
+        public bool IsItalic { get; set; }
+        public string FontName { get; set; } = string.Empty;
+        public double FontSize { get; set; }
+        public C_TEXT_ALIGNMENT Alignment {  get; set; }
+
+        public override bool CopyTo(AnnotParam transfer)
+        {
+            
+            FreeTextParam freetextTransfer = transfer as FreeTextParam;
+            if (freetextTransfer == null)
+            {
+                return false;
+            }
+
+            if (!base.CopyTo(freetextTransfer))
+            {
+                return false;
+            }
+
+            if(LineColor != null)
+            {
+                freetextTransfer.LineColor = (byte[])LineColor.Clone();
+            }
+           
+            if(BgColor != null)
+            {
+                freetextTransfer.BgColor = (byte[])BgColor.Clone();
+            }
+          
+            if(FontColor != null)
+            {
+                freetextTransfer.FontColor = (byte[])FontColor.Clone();
+            }
+
+            freetextTransfer.HasBgColor = HasBgColor;
+            freetextTransfer.LineWidth = LineWidth;
+            freetextTransfer.IsBold = IsBold;
+            freetextTransfer.IsItalic = IsItalic;
+            freetextTransfer.FontName = FontName;
+            freetextTransfer.FontSize = FontSize;
+            freetextTransfer.Alignment = Alignment;
+
+            return true;
+        }
+    }
+}

+ 53 - 0
Demo/Examples/ComPDFKit.Tool/SettingParam/AnnotParam/HighlightParam.cs

@@ -0,0 +1,53 @@
+using ComPDFKit.Import;
+using ComPDFKit.PDFAnnotation;
+using System.Collections.Generic;
+using System.Windows;
+using System.Windows.Media;
+
+namespace ComPDFKit.Tool
+{
+	public class HighlightParam:AnnotParam
+    {
+
+        public  HighlightParam()
+        {
+            CurrentType = C_ANNOTATION_TYPE.C_ANNOTATION_HIGHLIGHT;
+        }
+
+        public byte[] HighlightColor { get; set; }
+
+        public List<CRect> QuardRects {  get; set; }
+
+        public override bool CopyTo(AnnotParam transfer)
+        {
+            HighlightParam highlightTransfer = transfer as HighlightParam;
+            if (highlightTransfer == null)
+            {
+                return false;
+            }
+
+            if (!base.CopyTo(highlightTransfer))
+            {
+                return false;
+            }
+
+            if(HighlightColor!=null )
+            {
+                highlightTransfer.HighlightColor = (byte[])HighlightColor.Clone();
+            }
+
+            if (QuardRects != null)
+            {
+                List<CRect> rectList = new List<CRect>();
+                foreach (CRect saveRect in QuardRects)
+                {
+                    rectList.Add(saveRect);
+                }
+
+                highlightTransfer.QuardRects = rectList;
+            }
+
+            return true;
+        }
+    }
+}

+ 58 - 0
Demo/Examples/ComPDFKit.Tool/SettingParam/AnnotParam/InkParam.cs

@@ -0,0 +1,58 @@
+using ComPDFKit.Import;
+using ComPDFKit.PDFAnnotation;
+using System.Collections.Generic;
+using System.Windows.Media;
+
+namespace ComPDFKit.Tool
+{
+	public class InkParam:AnnotParam
+    {
+        public InkParam ()
+        {
+            CurrentType = C_ANNOTATION_TYPE.C_ANNOTATION_INK;
+        }
+
+        public byte[] InkColor { get; set; }
+        public double Thickness { get; set; }
+        public List<List<CPoint>> InkPath { get; set; }
+
+        public override bool CopyTo(AnnotParam transfer)
+        {
+            InkParam inkTransfer = transfer as InkParam;
+            if (inkTransfer == null)
+            {
+                return false;
+            }
+
+            if (!base.CopyTo(inkTransfer))
+            {
+                return false;
+            }
+
+            if(InkColor != null)
+            {
+                inkTransfer.InkColor = (byte[])InkColor.Clone();
+            }
+            
+            inkTransfer.Thickness = Thickness;
+
+            if(InkPath != null)
+            {
+                List<List<CPoint>> inkPoints = new List<List<CPoint>>();
+                foreach (List<CPoint> points in InkPath)
+                {
+                    List<CPoint> pointList = new List<CPoint>();
+                    foreach (CPoint point in points)
+                    {
+                        pointList.Add(point);
+                    }
+                    inkPoints.Add(pointList);
+                }
+
+                inkTransfer.InkPath = inkPoints;
+            }
+
+            return true;
+        }
+    }
+}

+ 98 - 0
Demo/Examples/ComPDFKit.Tool/SettingParam/AnnotParam/LineMeasureParam.cs

@@ -0,0 +1,98 @@
+using ComPDFKit.Import;
+using ComPDFKit.Measure;
+using ComPDFKit.PDFAnnotation;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Media;
+
+namespace ComPDFKit.Tool
+{
+    public class LineMeasureParam : AnnotParam
+    {
+        public LineMeasureParam() 
+        {
+            CurrentType = C_ANNOTATION_TYPE.C_ANNOTATION_LINE;
+        }
+        public byte[] LineColor { get; set; }
+        public float LineWidth { get; set; }
+        public float[] LineDash { get; set; }
+        public CPoint HeadPoint { get; set; }
+        public CPoint TailPoint { get; set; }
+        public C_LINE_TYPE HeadLineType { get; set; }
+        public C_LINE_TYPE TailLineType { get; set; }
+        public string FontName { get; set; }
+        public double FontSize { get; set; }
+        public byte[] FontColor { get; set; }
+        public bool IsBold { get; set; }
+        public bool IsItalic { get; set; }
+        public C_BORDER_STYLE BorderStyle { get; set; }
+        public CPDFMeasureInfo measureInfo { get; set; }
+
+        public override bool CopyTo(AnnotParam transfer)
+        {
+            LineMeasureParam polygonTransfer = transfer as LineMeasureParam;
+            if (polygonTransfer == null)
+            {
+                return false;
+            }
+
+            if (!base.CopyTo(polygonTransfer))
+            {
+                return false;
+            }
+
+            if (LineColor != null)
+            {
+                polygonTransfer.LineColor = (byte[])LineColor.Clone();
+            }
+
+            if (LineDash != null)
+            {
+                polygonTransfer.LineDash = (float[])LineDash.Clone();
+            }
+
+            if (FontColor != null)
+            {
+                polygonTransfer.FontColor = (byte[])FontColor.Clone();
+            }
+
+            if (measureInfo != null)
+            {
+                CPDFMeasureInfo cPDFMeasureInfo = new CPDFMeasureInfo()
+                {
+                    Factor = measureInfo.Factor,
+                    Unit = measureInfo.Unit,
+                    DecimalSymbol = measureInfo.DecimalSymbol,
+                    ThousandSymbol = measureInfo.ThousandSymbol,
+                    Display = measureInfo.Display,
+                    Precision = measureInfo.Precision,
+                    UnitPrefix = measureInfo.UnitPrefix,
+                    UnitSuffix = measureInfo.UnitSuffix,
+                    UnitPosition = measureInfo.UnitPosition,
+                    RulerBase = measureInfo.RulerBase,
+                    RulerBaseUnit = measureInfo.RulerBaseUnit,
+                    RulerTranslateUnit = measureInfo.RulerTranslateUnit,
+                    RulerTranslate= measureInfo.RulerTranslate,
+                    CaptionType = measureInfo.CaptionType,
+                };
+                polygonTransfer.measureInfo = cPDFMeasureInfo;
+            }
+
+            polygonTransfer.LineWidth = LineWidth;
+            polygonTransfer.HeadLineType = HeadLineType;
+            polygonTransfer.TailLineType = TailLineType;
+            polygonTransfer.FontName = FontName;
+            polygonTransfer.FontSize = FontSize;
+            polygonTransfer.IsBold = IsBold;
+            polygonTransfer.IsItalic = IsItalic;
+            polygonTransfer.HeadPoint = HeadPoint;
+            polygonTransfer.TailPoint = TailPoint;
+            polygonTransfer.BorderStyle = BorderStyle;
+
+            return true;
+        }
+    }
+}

+ 65 - 0
Demo/Examples/ComPDFKit.Tool/SettingParam/AnnotParam/LineParam.cs

@@ -0,0 +1,65 @@
+using ComPDFKit.Import;
+using ComPDFKit.PDFAnnotation;
+using System.Windows;
+using System.Windows.Media;
+
+namespace ComPDFKit.Tool
+{
+	public class LineParam:AnnotParam
+    {
+        public LineParam ()
+        {
+            CurrentType = C_ANNOTATION_TYPE.C_ANNOTATION_LINE;
+        }
+
+        public byte[] LineColor {  get; set; }
+        public byte[] BgColor { get; set; }
+        public bool HasBgColor {  get; set; }
+        public double LineWidth { get; set; }
+        public float[] LineDash { get; set; }
+        public C_BORDER_STYLE BorderStyle { get; set; }
+        public C_LINE_TYPE HeadLineType {  get; set; }
+        public C_LINE_TYPE TailLineType {  get; set; }
+        public CPoint HeadPoint { get; set; }
+        public CPoint TailPoint { get; set; }
+
+        public override bool CopyTo(AnnotParam transfer)
+        {
+            LineParam lineTransfer = transfer as LineParam;
+            if (lineTransfer == null)
+            {
+                return false;
+            }
+
+            if (!base.CopyTo(lineTransfer))
+            {
+                return false;
+            }
+
+            if (LineColor!=null)
+            {
+                lineTransfer.LineColor = (byte[])LineColor.Clone();
+            }
+          
+            if(BgColor!=null)
+            {
+                lineTransfer.BgColor = (byte[])BgColor.Clone();
+            }
+            
+            if(LineDash!=null)
+            {
+                lineTransfer.LineDash = (float[])LineDash.Clone();
+            }
+
+            lineTransfer.HasBgColor = HasBgColor;
+            lineTransfer.LineWidth = LineWidth;
+            lineTransfer.BorderStyle = BorderStyle;
+            lineTransfer.HeadLineType = HeadLineType;
+            lineTransfer.TailLineType = TailLineType;
+            lineTransfer.HeadPoint = HeadPoint;
+            lineTransfer.TailPoint = TailPoint;
+
+            return true;
+        }
+    }
+}

+ 41 - 0
Demo/Examples/ComPDFKit.Tool/SettingParam/AnnotParam/LinkParam.cs

@@ -0,0 +1,41 @@
+using ComPDFKit.Import;
+using ComPDFKit.PDFAnnotation;
+using ComPDFKit.PDFDocument.Action;
+using System.Windows;
+
+namespace ComPDFKit.Tool
+{
+	public class LinkParam:AnnotParam
+    {
+        public  LinkParam ()
+        {
+            CurrentType = C_ANNOTATION_TYPE.C_ANNOTATION_LINK;
+        }
+
+        public string Uri { get; set; } = string.Empty;
+        public int DestinationPageIndex {  get; set; }
+        public CPoint DestinationPosition { get; set; }
+        public C_ACTION_TYPE Action { get; set; }
+
+        public override bool CopyTo(AnnotParam transfer)
+        {
+            LinkParam linkTransfer = transfer as LinkParam;
+            if (linkTransfer == null)
+            {
+                return false;
+            }
+
+            if (!base.CopyTo(linkTransfer))
+            {
+                return false;
+            }
+
+            linkTransfer.Uri = Uri;
+            linkTransfer.DestinationPageIndex = DestinationPageIndex;
+            linkTransfer.DestinationPosition = DestinationPosition;
+            linkTransfer.Action = Action;
+
+            return true;
+        }
+    }
+}

+ 96 - 0
Demo/Examples/ComPDFKit.Tool/SettingParam/AnnotParam/PolyLineMeasureParam.cs

@@ -0,0 +1,96 @@
+using ComPDFKit.Import;
+using ComPDFKit.Measure;
+using ComPDFKit.PDFAnnotation;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Media;
+
+namespace ComPDFKit.Tool
+{
+    public class PolyLineMeasureParam : AnnotParam
+    {
+        public PolyLineMeasureParam()
+        {
+            CurrentType = C_ANNOTATION_TYPE.C_ANNOTATION_POLYLINE;
+        }
+        public byte[] LineColor { get; set; }
+        public float LineWidth { get; set; }
+        public float[] LineDash { get; set; }
+        public List<CPoint> SavePoints { get; set; }
+        public string FontName { get; set; }
+        public double FontSize { get; set; }
+        public byte[] FontColor { get; set; }
+        public bool IsBold { get; set; }
+        public bool IsItalic { get; set; }
+        public C_BORDER_STYLE BorderStyle { get; set; }
+        public CPDFMeasureInfo measureInfo { get; set; }
+
+        public override bool CopyTo(AnnotParam transfer)
+        {
+            PolyLineMeasureParam polygonTransfer = transfer as PolyLineMeasureParam;
+            if (polygonTransfer == null)
+            {
+                return false;
+            }
+
+            if (!base.CopyTo(polygonTransfer))
+            {
+                return false;
+            }
+
+            if (LineColor != null)
+            {
+                polygonTransfer.LineColor = (byte[])LineColor.Clone();
+            }
+
+            if (LineDash != null)
+            {
+                polygonTransfer.LineDash = (float[])LineDash.Clone();
+            }
+
+            if (SavePoints != null)
+            {
+                polygonTransfer.SavePoints.CopyTo(SavePoints.ToArray());
+            }
+
+            if (FontColor != null)
+            {
+                polygonTransfer.FontColor = (byte[])FontColor.Clone();
+            }
+
+            if (measureInfo != null)
+            {
+                CPDFMeasureInfo cPDFMeasureInfo = new CPDFMeasureInfo()
+                {
+                    Factor = measureInfo.Factor,
+                    Unit = measureInfo.Unit,
+                    DecimalSymbol = measureInfo.DecimalSymbol,
+                    ThousandSymbol = measureInfo.ThousandSymbol,
+                    Display = measureInfo.Display,
+                    Precision = measureInfo.Precision,
+                    UnitPrefix = measureInfo.UnitPrefix,
+                    UnitSuffix = measureInfo.UnitSuffix,
+                    UnitPosition = measureInfo.UnitPosition,
+                    RulerBase = measureInfo.RulerBase,
+                    RulerBaseUnit = measureInfo.RulerBaseUnit,
+                    RulerTranslateUnit = measureInfo.RulerTranslateUnit,
+                    CaptionType = measureInfo.CaptionType,
+                    RulerTranslate= measureInfo.RulerTranslate,
+                };
+                polygonTransfer.measureInfo = cPDFMeasureInfo;
+            }
+
+            polygonTransfer.LineWidth = LineWidth;
+            polygonTransfer.FontName = FontName;
+            polygonTransfer.FontSize = FontSize;
+            polygonTransfer.IsBold = IsBold;
+            polygonTransfer.IsItalic = IsItalic;
+            polygonTransfer.BorderStyle = BorderStyle;
+
+            return true;
+        }
+    }
+}

+ 122 - 0
Demo/Examples/ComPDFKit.Tool/SettingParam/AnnotParam/PolygonMeasureParam.cs

@@ -0,0 +1,122 @@
+using ComPDFKit.Import;
+using ComPDFKit.Measure;
+using ComPDFKit.PDFAnnotation;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Media;
+
+namespace ComPDFKit.Tool
+{
+    public class PolygonMeasureParam : AnnotParam
+    {
+        public PolygonMeasureParam() 
+        {
+            CurrentType = C_ANNOTATION_TYPE.C_ANNOTATION_POLYGON;
+        }    
+        public byte[] FillColor { get; set; }
+        public bool HasFillColor { get; set; }
+        public byte[] LineColor { get; set; }
+        public float LineWidth { get; set; }
+        public float[] LineDash { get; set; }
+        public List<CPoint> SavePoints { get; set; } 
+        public byte[] EndLineColor { get; set; }
+        public double EndLineWidth { get; set; }
+        public double EndTransparency { get; set; }
+        public DashStyle EndLineDash { get; set; }
+        public string FontName { get; set; }
+        public double FontSize { get; set; }
+        public byte[] FontColor { get; set; }
+        public bool IsBold { get; set; }
+        public bool IsItalic { get; set; }
+        public C_BORDER_STYLE BorderStyle { get; set; }
+        public CPDFMeasureInfo measureInfo { get; set; }
+
+        public override bool CopyTo(AnnotParam transfer)
+        {
+            PolygonMeasureParam polygonTransfer = transfer as PolygonMeasureParam;
+            if (polygonTransfer == null)
+            {
+                return false;
+            }
+
+            if (!base.CopyTo(polygonTransfer))
+            {
+                return false;
+            }
+
+            if (FillColor != null)
+            {
+                polygonTransfer.FillColor = (byte[])FillColor.Clone();
+            }
+
+            if (SavePoints != null)
+            {
+                polygonTransfer.SavePoints.CopyTo(SavePoints.ToArray());
+            }
+
+            if (LineColor != null)
+            {
+                polygonTransfer.LineColor = (byte[])LineColor.Clone();
+            }
+
+            if (LineDash != null)
+            {
+                polygonTransfer.LineDash = (float[])LineDash.Clone(); 
+            }
+
+            if (EndLineColor != null)
+            {
+                polygonTransfer.EndLineColor = (byte[])EndLineColor.Clone();
+            }
+
+            if (EndLineDash != null)
+            {
+                polygonTransfer.EndLineDash = EndLineDash.Clone();
+            }
+
+            if (FontColor != null)
+            {
+                polygonTransfer.FontColor = (byte[])FontColor.Clone();
+            }
+
+            if (measureInfo != null)
+            {
+                CPDFMeasureInfo cPDFMeasureInfo =new CPDFMeasureInfo() 
+                {
+                    Factor=measureInfo.Factor,
+                    Unit=measureInfo.Unit,
+                    DecimalSymbol=measureInfo.DecimalSymbol,
+                    ThousandSymbol=measureInfo.ThousandSymbol,
+                    Display=measureInfo.Display,
+                    Precision=measureInfo.Precision,
+                    UnitPrefix=measureInfo.UnitPrefix,
+                    UnitSuffix=measureInfo.UnitSuffix,
+                    UnitPosition=measureInfo.UnitPosition,
+                    RulerBase=measureInfo.RulerBase,
+                    RulerBaseUnit=measureInfo.RulerBaseUnit,
+                    RulerTranslateUnit=measureInfo.RulerTranslateUnit,
+                    CaptionType=measureInfo.CaptionType,
+                    RulerTranslate=measureInfo.RulerTranslate,
+                };
+                polygonTransfer.measureInfo = cPDFMeasureInfo;
+            }
+
+            polygonTransfer.HasFillColor = HasFillColor;
+            polygonTransfer.LineWidth = LineWidth;
+            polygonTransfer.EndLineWidth = EndLineWidth;
+            polygonTransfer.EndTransparency = EndTransparency;
+            polygonTransfer.FontName = FontName;
+            polygonTransfer.FontSize = FontSize;
+            polygonTransfer.IsBold = IsBold;
+            polygonTransfer.IsItalic = IsItalic;
+            polygonTransfer.BorderStyle = BorderStyle;
+
+            return true;
+        }
+    }
+}

+ 72 - 0
Demo/Examples/ComPDFKit.Tool/SettingParam/AnnotParam/RedactParam.cs

@@ -0,0 +1,72 @@
+using ComPDFKit.Import;
+using ComPDFKit.PDFAnnotation;
+using System.Collections.Generic;
+using System.Windows;
+using System.Windows.Media;
+
+namespace ComPDFKit.Tool
+{
+	public class RedactParam:AnnotParam
+	{
+		public RedactParam ()
+        {
+            CurrentType = C_ANNOTATION_TYPE.C_ANNOTATION_REDACT;
+        }
+
+		public byte[] LineColor { get; set; }
+		public byte[] BgColor { get; set; }
+		public byte[] FontColor { get; set; }
+		public string FontName { get; set; } = string.Empty;
+		public string OverlayText { get; set; } = string.Empty;
+		public double FontSize { get; set; }
+		public C_TEXT_ALIGNMENT Alignment { get; set; }
+		public List<CRect> QuardRects { get; set; }
+
+		public override bool CopyTo(AnnotParam transfer)
+		{
+			RedactParam redactTransfer = transfer as RedactParam;
+			if (redactTransfer == null)
+			{
+				return false;
+			}
+
+			if (!base.CopyTo(redactTransfer))
+			{
+				return false;
+			}
+
+			if(LineColor!=null)
+			{
+				redactTransfer.LineColor= (byte[])LineColor.Clone();
+			}
+			
+			if(BgColor!=null)
+			{
+				redactTransfer.BgColor= (byte[])BgColor.Clone();
+			}
+			
+			if(FontColor!=null)
+			{ 
+				redactTransfer.FontColor= (byte[])FontColor.Clone();
+			}
+			
+			redactTransfer.FontName = FontName;
+			redactTransfer.FontSize = FontSize;
+			redactTransfer.Alignment = Alignment;
+			redactTransfer.OverlayText = OverlayText;
+
+			if (QuardRects != null)
+			{
+				List<CRect> rectList = new List<CRect>();
+				foreach (CRect saveRect in QuardRects)
+				{
+					rectList.Add(saveRect);
+				}
+
+				redactTransfer.QuardRects = rectList;
+			}
+
+			return true;
+		}
+	}
+}

+ 40 - 0
Demo/Examples/ComPDFKit.Tool/SettingParam/AnnotParam/SoundParam.cs

@@ -0,0 +1,40 @@
+using ComPDFKit.PDFAnnotation;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace ComPDFKit.Tool.SettingParam
+{
+    public class SoundParam : AnnotParam
+    {
+        public  SoundParam ()
+        {
+            CurrentType = C_ANNOTATION_TYPE.C_ANNOTATION_SOUND;
+        }
+
+
+        public string SoundFilePath { get; set; } = string.Empty;
+        public MemoryStream ImageStream { get; set; }
+
+        public override bool CopyTo(AnnotParam transfer)
+        {
+            SoundParam soundTransfer = transfer as SoundParam;
+            if (soundTransfer == null)
+            {
+                return false;
+            }
+
+            if (!base.CopyTo(soundTransfer))
+            {
+                return false;
+            }
+
+            soundTransfer.SoundFilePath = SoundFilePath;
+            soundTransfer.ImageStream = ImageStream;
+            return true;
+        }
+    }
+}

+ 53 - 0
Demo/Examples/ComPDFKit.Tool/SettingParam/AnnotParam/SquareParam.cs

@@ -0,0 +1,53 @@
+using ComPDFKit.PDFAnnotation;
+using System.Windows.Media;
+
+namespace ComPDFKit.Tool
+{
+	public class SquareParam:AnnotParam
+    {
+        public SquareParam ()
+        {
+            CurrentType = C_ANNOTATION_TYPE.C_ANNOTATION_SQUARE;
+        }
+
+        public byte[] LineColor { get; set; }
+        public byte[] BgColor { get; set; }
+        public bool HasBgColor {  get; set; }
+        public double LineWidth { get; set; }
+        public float[] LineDash { get; set; }
+        public C_BORDER_STYLE BorderStyle { get; set; }
+        public override bool CopyTo(AnnotParam transfer)
+        {
+            SquareParam squareTransfer = transfer as SquareParam;
+            if (squareTransfer == null)
+            {
+                return false;
+            }
+
+            if (!base.CopyTo(squareTransfer))
+            {
+                return false;
+            }
+
+            if(LineColor!=null)
+            {
+                squareTransfer.LineColor = (byte[])LineColor.Clone();
+            }
+
+            if(BgColor!=null)
+            {
+                squareTransfer.BgColor = (byte[])BgColor.Clone();
+            }
+            
+            if(LineDash!=null)
+            {
+                squareTransfer.LineDash = (float[])LineDash.Clone();
+            }
+            squareTransfer.LineWidth = LineWidth;
+            squareTransfer.HasBgColor = HasBgColor;
+            squareTransfer.BorderStyle = BorderStyle;
+
+            return true;
+        }
+    }
+}

+ 54 - 0
Demo/Examples/ComPDFKit.Tool/SettingParam/AnnotParam/SquigglyParam.cs

@@ -0,0 +1,54 @@
+using ComPDFKit.Import;
+using ComPDFKit.PDFAnnotation;
+using System.Collections.Generic;
+using System.Windows;
+using System.Windows.Media;
+
+namespace ComPDFKit.Tool
+{
+	public class SquigglyParam:AnnotParam
+    {
+        public  SquigglyParam ()
+        {
+            CurrentType = C_ANNOTATION_TYPE.C_ANNOTATION_SQUIGGLY;
+        }
+        public byte[] SquigglyColor { get; set; }
+
+        public List<CRect> QuardRects { get; set; }
+
+        public override bool CopyTo(AnnotParam transfer)
+        {
+            SquigglyParam squigglyTransfer = transfer as SquigglyParam;
+            if (squigglyTransfer == null)
+            {
+                return false;
+            }
+
+            if (!base.CopyTo(squigglyTransfer))
+            {
+                return false;
+            }
+
+            squigglyTransfer.SquigglyColor = SquigglyColor;
+
+            if(SquigglyColor!=null)
+            {
+                squigglyTransfer.SquigglyColor= (byte[])SquigglyColor.Clone();
+            }
+
+            if (QuardRects != null)
+            {
+                List<CRect> rectList = new List<CRect>();
+                foreach (CRect saveRect in QuardRects)
+                {
+                    rectList.Add(saveRect);
+                }
+
+                squigglyTransfer.QuardRects = rectList;
+            }
+
+            return true;
+        }
+
+    }
+}

+ 47 - 0
Demo/Examples/ComPDFKit.Tool/SettingParam/AnnotParam/StampParam.cs

@@ -0,0 +1,47 @@
+using ComPDFKit.PDFAnnotation;
+using System.IO;
+
+namespace ComPDFKit.Tool
+{
+	public class StampParam:AnnotParam
+	{
+		public  StampParam ()
+        {
+            CurrentType = C_ANNOTATION_TYPE.C_ANNOTATION_STAMP;
+        }
+
+
+		public string StampText { get; set; } = string.Empty;
+		public string DateText {  get; set; }= string.Empty;
+		public C_STAMP_TYPE StampType { get; set; }
+		public C_TEXTSTAMP_SHAPE TextStampShape {  get; set; }
+		public C_TEXTSTAMP_COLOR TextStampColor {  get; set; }
+		public MemoryStream ImageStream { get; set; }
+		public int Rotation { get; set; }	
+
+
+        public override bool CopyTo(AnnotParam transfer)
+		{
+			StampParam stampTransfer = transfer as StampParam;
+			if (stampTransfer == null)
+			{
+				return false;
+			}
+
+			if (!base.CopyTo(stampTransfer))
+			{
+				return false;
+			}
+
+		    stampTransfer.StampText = StampText;
+			stampTransfer.DateText = DateText;
+			stampTransfer.StampType = StampType;
+			stampTransfer.TextStampColor = TextStampColor;
+			stampTransfer.TextStampShape = TextStampShape;
+			stampTransfer.ImageStream = ImageStream;
+			stampTransfer.Rotation = Rotation;
+
+			return true;
+		}
+	}
+}

+ 36 - 0
Demo/Examples/ComPDFKit.Tool/SettingParam/AnnotParam/StickyNoteParam.cs

@@ -0,0 +1,36 @@
+using ComPDFKit.PDFAnnotation;
+using System.Windows.Media;
+
+namespace ComPDFKit.Tool
+{
+	public class StickyNoteParam:AnnotParam
+    {
+        public  StickyNoteParam ()
+        {
+            CurrentType = C_ANNOTATION_TYPE.C_ANNOTATION_TEXT;
+        }
+
+        public byte[] StickyNoteColor { get; set; }
+
+        public override bool CopyTo(AnnotParam transfer)
+        {
+            StickyNoteParam stickynoteTransfer = transfer as StickyNoteParam;
+            if (stickynoteTransfer == null)
+            {
+                return false;
+            }
+
+            if (!base.CopyTo(stickynoteTransfer))
+            {
+                return false;
+            }
+
+            if(StickyNoteColor != null)
+            {
+                stickynoteTransfer.StickyNoteColor = (byte[])StickyNoteColor.Clone();
+            }
+
+            return true;
+        }
+    }
+}

+ 51 - 0
Demo/Examples/ComPDFKit.Tool/SettingParam/AnnotParam/StrikeoutParam.cs

@@ -0,0 +1,51 @@
+using ComPDFKit.Import;
+using ComPDFKit.PDFAnnotation;
+using System.Collections.Generic;
+using System.Windows;
+using System.Windows.Media;
+
+namespace ComPDFKit.Tool
+{
+	public class StrikeoutParam:AnnotParam
+    {
+		public  StrikeoutParam ()
+        {
+            CurrentType = C_ANNOTATION_TYPE.C_ANNOTATION_STRIKEOUT;
+        }
+		public byte[] StrikeoutColor { get; set; }
+
+		public List<CRect> QuardRects { get; set; }
+
+		public override bool CopyTo(AnnotParam transfer)
+		{
+			StrikeoutParam strikeoutTransfer = transfer as StrikeoutParam;
+			if (strikeoutTransfer == null)
+			{
+				return false;
+			}
+
+			if (!base.CopyTo(strikeoutTransfer))
+			{
+				return false;
+			}
+
+			if(StrikeoutColor != null)
+			{
+                strikeoutTransfer.StrikeoutColor = (byte[])StrikeoutColor.Clone();
+            }
+
+			if (QuardRects != null)
+			{
+				List<CRect> rectList = new List<CRect>();
+				foreach (CRect saveRect in QuardRects)
+				{
+					rectList.Add(saveRect);
+				}
+
+				strikeoutTransfer.QuardRects = rectList;
+			}
+
+			return true;
+		}
+	}
+}

+ 51 - 0
Demo/Examples/ComPDFKit.Tool/SettingParam/AnnotParam/UnderlineParam.cs

@@ -0,0 +1,51 @@
+using ComPDFKit.Import;
+using ComPDFKit.PDFAnnotation;
+using System.Collections.Generic;
+using System.Windows;
+using System.Windows.Media;
+
+namespace ComPDFKit.Tool
+{
+	public class UnderlineParam:AnnotParam
+	{
+		public UnderlineParam ()
+        {
+            CurrentType = C_ANNOTATION_TYPE.C_ANNOTATION_UNDERLINE;
+        }
+		public byte[] UnderlineColor { get; set; }
+
+		public List<CRect> QuardRects { get; set; }
+
+		public override bool CopyTo(AnnotParam transfer)
+		{
+			UnderlineParam underlineTransfer = transfer as UnderlineParam;
+			if (underlineTransfer == null)
+			{
+				return false;
+			}
+
+			if (!base.CopyTo(underlineTransfer))
+			{
+				return false;
+			}
+
+			if(UnderlineColor != null)
+			{
+                underlineTransfer.UnderlineColor = (byte[])UnderlineColor.Clone();
+            }
+
+			if (QuardRects != null)
+			{
+				List<CRect> rectList = new List<CRect>();
+				foreach (CRect saveRect in QuardRects)
+				{
+					rectList.Add(saveRect);
+				}
+
+				underlineTransfer.QuardRects = rectList;
+			}
+
+			return true;
+		}
+	}
+}

Разница между файлами не показана из-за своего большого размера
+ 1954 - 0
Demo/Examples/ComPDFKit.Tool/SettingParam/DefaultSettingParam.cs


+ 34 - 0
Demo/Examples/ComPDFKit.Tool/SettingParam/FormParam/CheckBoxParam.cs

@@ -0,0 +1,34 @@
+using ComPDFKit.PDFAnnotation;
+using ComPDFKit.PDFAnnotation.Form;
+
+namespace ComPDFKit.Tool
+{
+	public class CheckBoxParam:WidgetParm
+	{
+		public  CheckBoxParam ()
+        {
+            CurrentType = C_ANNOTATION_TYPE.C_ANNOTATION_WIDGET;
+			WidgetType = C_WIDGET_TYPE.WIDGET_CHECKBOX;
+        }
+
+		public C_CHECK_STYLE CheckStyle { get; set; }
+		public bool IsChecked { get; set; }
+		public override bool CopyTo(AnnotParam transfer)
+		{
+			CheckBoxParam checkboxTransfer = transfer as CheckBoxParam;
+			if (checkboxTransfer == null)
+			{
+				return false;
+			}
+			if (!base.CopyTo(checkboxTransfer))
+			{
+				return false;
+			}
+
+			checkboxTransfer.CheckStyle = CheckStyle;
+			checkboxTransfer.IsChecked = IsChecked;
+
+			return true;
+		}
+	}
+}

+ 58 - 0
Demo/Examples/ComPDFKit.Tool/SettingParam/FormParam/ComboBoxParam.cs

@@ -0,0 +1,58 @@
+using ComPDFKit.PDFAnnotation;
+using ComPDFKit.PDFAnnotation.Form;
+using System.Collections.Generic;
+
+namespace ComPDFKit.Tool
+{
+	public class ComboBoxParam:WidgetParm
+	{
+		public  ComboBoxParam ()
+        {
+            CurrentType = C_ANNOTATION_TYPE.C_ANNOTATION_WIDGET;
+            WidgetType = C_WIDGET_TYPE.WIDGET_COMBOBOX;
+        }
+
+		public bool IsItalic { get; set; }
+		public bool IsBold { get; set; }
+		public Dictionary<string,string> OptionItems { get; set; }=new Dictionary<string,string>();	
+		public List<int> SelectItemsIndex { get; set; } = new List<int>();
+
+		public override bool CopyTo(AnnotParam transfer)
+		{
+			ComboBoxParam comboboxTransfer = transfer as ComboBoxParam;
+			if (comboboxTransfer == null)
+			{
+				return false;
+			}
+			if (!base.CopyTo(comboboxTransfer))
+			{
+				return false;
+			}
+			
+			comboboxTransfer.IsItalic = IsItalic;
+			comboboxTransfer.IsBold = IsBold;
+			if(OptionItems != null)
+			{
+				Dictionary<string, string> optionDict=new Dictionary<string, string>();
+				foreach (string dictKey in OptionItems.Keys)
+				{
+					optionDict[dictKey] = OptionItems[dictKey];
+				}
+
+				comboboxTransfer.OptionItems = optionDict;
+			}
+
+			if(SelectItemsIndex != null)
+			{
+				List<int> selectList=new List<int>();
+				foreach (int index in SelectItemsIndex)
+				{
+					selectList.Add(index);
+				}
+				comboboxTransfer.SelectItemsIndex = selectList;
+			}
+
+			return true;
+		}
+	}
+}

+ 58 - 0
Demo/Examples/ComPDFKit.Tool/SettingParam/FormParam/ListBoxParam.cs

@@ -0,0 +1,58 @@
+using ComPDFKit.PDFAnnotation;
+using ComPDFKit.PDFAnnotation.Form;
+using System.Collections.Generic;
+
+namespace ComPDFKit.Tool
+{
+	public class ListBoxParam:WidgetParm
+	{
+		public  ListBoxParam ()
+        {
+            CurrentType = C_ANNOTATION_TYPE.C_ANNOTATION_WIDGET;
+            WidgetType = C_WIDGET_TYPE.WIDGET_LISTBOX;
+        }
+
+		public bool IsItalic { get; set; }
+		public bool IsBold { get; set; }
+		public Dictionary<string, string> OptionItems { get; set; }=new Dictionary<string, string>();	
+		public List<int> SelectItemsIndex { get; set; }=new List<int>();	
+
+		public override bool CopyTo(AnnotParam transfer)
+		{
+			ListBoxParam listboxTransfer = transfer as ListBoxParam;
+			if (listboxTransfer == null)
+			{
+				return false;
+			}
+			if (!base.CopyTo(listboxTransfer))
+			{
+				return false;
+			}
+
+			listboxTransfer.IsItalic = IsItalic;
+			listboxTransfer.IsBold = IsBold;
+			if (OptionItems != null)
+			{
+				Dictionary<string, string> optionDict = new Dictionary<string, string>();
+				foreach (string dictKey in OptionItems.Keys)
+				{
+					optionDict[dictKey] = OptionItems[dictKey];
+				}
+
+				listboxTransfer.OptionItems = optionDict;
+			}
+
+			if (SelectItemsIndex != null)
+			{
+				List<int> selectList = new List<int>();
+				foreach (int index in SelectItemsIndex)
+				{
+					selectList.Add(index);
+				}
+				listboxTransfer.SelectItemsIndex = selectList;
+			}
+
+			return true;
+		}
+	}
+}

+ 49 - 0
Demo/Examples/ComPDFKit.Tool/SettingParam/FormParam/PushButtonParam.cs

@@ -0,0 +1,49 @@
+using ComPDFKit.Import;
+using ComPDFKit.PDFAnnotation;
+using ComPDFKit.PDFAnnotation.Form;
+using ComPDFKit.PDFDocument.Action;
+using System.Windows;
+
+namespace ComPDFKit.Tool
+{
+	public class PushButtonParam:WidgetParm
+	{
+		public PushButtonParam ()
+        {
+            CurrentType = C_ANNOTATION_TYPE.C_ANNOTATION_WIDGET;
+            WidgetType = C_WIDGET_TYPE.WIDGET_PUSHBUTTON;
+        }
+
+		public bool IsBold {  get; set; }
+		public bool IsItalic { get; set; }
+		public string Text { get; set; } = string.Empty;
+		public string Uri { get; set; } = string.Empty;
+		public int DestinationPageIndex { get; set; }
+		public CPoint DestinationPosition { get; set; }
+		public C_ACTION_TYPE Action { get; set; }
+
+		public override bool CopyTo(AnnotParam transfer)
+		{
+			PushButtonParam pushbuttonTransfer = transfer as PushButtonParam;
+			if (pushbuttonTransfer == null)
+			{
+				return false;
+			}
+
+			if (!base.CopyTo(pushbuttonTransfer))
+			{
+				return false;
+			}
+
+			pushbuttonTransfer.IsBold = IsBold;
+			pushbuttonTransfer.IsItalic = IsItalic;
+			pushbuttonTransfer.Text = Text;
+			pushbuttonTransfer.Uri = Uri;
+			pushbuttonTransfer.DestinationPageIndex = DestinationPageIndex;
+			pushbuttonTransfer.DestinationPosition = DestinationPosition;
+			pushbuttonTransfer.Action = Action;
+
+			return true;
+		}
+	}
+}

+ 33 - 0
Demo/Examples/ComPDFKit.Tool/SettingParam/FormParam/RadioButtonParam.cs

@@ -0,0 +1,33 @@
+using ComPDFKit.PDFAnnotation;
+using ComPDFKit.PDFAnnotation.Form;
+
+namespace ComPDFKit.Tool
+{
+	public class RadioButtonParam:WidgetParm
+	{
+		public  RadioButtonParam ()
+        {
+            CurrentType = C_ANNOTATION_TYPE.C_ANNOTATION_WIDGET;
+            WidgetType = C_WIDGET_TYPE.WIDGET_RADIOBUTTON;
+        }
+		public C_CHECK_STYLE CheckStyle {  get; set; }
+		public bool IsChecked {  get; set; }
+		public override bool CopyTo(AnnotParam transfer)
+		{
+			RadioButtonParam radioTransfer = transfer as RadioButtonParam;
+			if (radioTransfer == null)
+			{
+				return false;
+			}
+			if (!base.CopyTo(radioTransfer))
+			{
+				return false;
+			}
+
+			radioTransfer.CheckStyle = CheckStyle;
+			radioTransfer.IsChecked = IsChecked;
+
+			return true;
+		}
+	}
+}

+ 29 - 0
Demo/Examples/ComPDFKit.Tool/SettingParam/FormParam/SignatureParam.cs

@@ -0,0 +1,29 @@
+using ComPDFKit.PDFAnnotation;
+using ComPDFKit.PDFAnnotation.Form;
+
+namespace ComPDFKit.Tool
+{
+	public class SignatureParam:WidgetParm
+	{
+		public  SignatureParam ()
+        {
+            CurrentType = C_ANNOTATION_TYPE.C_ANNOTATION_WIDGET;
+            WidgetType = C_WIDGET_TYPE.WIDGET_SIGNATUREFIELDS;
+        }
+
+		public override bool CopyTo(AnnotParam transfer)
+		{
+			SignatureParam signTransfer = transfer as SignatureParam;
+			if (signTransfer == null)
+			{
+				return false;
+			}
+			if (!base.CopyTo(signTransfer))
+			{
+				return false;
+			}
+
+			return true;
+		}
+	}
+}

+ 44 - 0
Demo/Examples/ComPDFKit.Tool/SettingParam/FormParam/TextBoxParam.cs

@@ -0,0 +1,44 @@
+using ComPDFKit.PDFAnnotation;
+using ComPDFKit.PDFAnnotation.Form;
+using System.Windows;
+
+namespace ComPDFKit.Tool
+{
+	public class TextBoxParam:WidgetParm
+	{
+		public  TextBoxParam ()
+        {
+            CurrentType = C_ANNOTATION_TYPE.C_ANNOTATION_WIDGET;
+            WidgetType = C_WIDGET_TYPE.WIDGET_TEXTFIELD;
+        }
+
+		public string Text { get; set; } = string.Empty;
+		public bool IsMultiLine {  get; set; }
+		public bool IsPassword { get; set; }
+		public bool IsItalic {  get; set; }
+		public bool IsBold { get; set; }
+		public C_TEXT_ALIGNMENT Alignment { get; set; }
+
+		public override bool CopyTo(AnnotParam transfer)
+		{
+			TextBoxParam textTransfer = transfer as TextBoxParam;
+			if (textTransfer == null)
+			{
+				return false;
+			}
+			if (!base.CopyTo(textTransfer))
+			{
+				return false;
+			}
+
+			textTransfer.Text = Text;
+			textTransfer.IsMultiLine = IsMultiLine;
+			textTransfer.IsPassword = IsPassword;
+			textTransfer.IsItalic = IsItalic;
+			textTransfer.IsBold = IsBold;
+			textTransfer.Alignment = Alignment;
+
+			return true;
+		}
+	}
+}

+ 69 - 0
Demo/Examples/ComPDFKit.Tool/SettingParam/FormParam/WidgetParm.cs

@@ -0,0 +1,69 @@
+using ComPDFKit.PDFAnnotation;
+using ComPDFKit.PDFAnnotation.Form;
+using System.Windows.Media;
+
+namespace ComPDFKit.Tool
+{
+	public class WidgetParm:AnnotParam
+	{
+		public C_WIDGET_TYPE WidgetType {  get; set; }
+		public C_BORDER_STYLE BorderStyle { get; set; }
+		public byte[] LineColor { get; set; }
+		public bool HasLineColor {  get; set; }
+		public byte[] BgColor { get; set; }
+		public bool HasBgColor {  get; set; }
+		public byte[] FontColor { get; set; }
+        public string FontName { get; set; } = string.Empty; 
+        public double FontSize { get; set; }
+        public double LineWidth { get; set; }
+		public string FieldName { get; set; } = string.Empty;
+
+        /// <summary> 
+		/// Note the use of ParamConverter.GetFormFlags for obtaining the result
+        /// </summary>
+        public int Flags {  get; set; }
+		public bool IsReadOnly {  get; set; }
+		public bool IsHidden {  get; set; }
+		public override bool CopyTo(AnnotParam transfer)
+		{
+			WidgetParm formTransfer = transfer as WidgetParm;
+			if (formTransfer == null)
+			{
+				return false;
+			}
+			if (!base.CopyTo(formTransfer))
+			{
+				return false;
+			}
+
+			formTransfer.WidgetType = WidgetType;
+			formTransfer.BorderStyle = BorderStyle;
+			if(LineColor!=null)
+			{
+				formTransfer.LineColor = (byte[])LineColor.Clone();
+			}
+			
+			if(BgColor!=null)
+			{
+				formTransfer.BgColor = (byte[])BgColor.Clone();
+			}
+			
+			if(FontColor!=null)
+			{
+				formTransfer.FontColor = (byte[])FontColor.Clone();
+			}
+
+			formTransfer.HasBgColor = HasBgColor;
+			formTransfer.HasLineColor = HasLineColor;
+			formTransfer.LineWidth = LineWidth;
+			formTransfer.FieldName = FieldName;
+			formTransfer.FontName = FontName;
+			formTransfer.FontSize = FontSize;
+			formTransfer.Flags = Flags;
+			formTransfer.IsReadOnly = IsReadOnly;
+			formTransfer.IsHidden = IsHidden;
+
+			return true;
+		}
+	}
+}

+ 35 - 0
Demo/Examples/ComPDFKit.Tool/SettingParam/PDFEditParam/ImageEditParam.cs

@@ -0,0 +1,35 @@
+using ComPDFKit.Import;
+using ComPDFKit.PDFPage;
+
+namespace ComPDFKit.Tool
+{
+    public class ImageEditParam:PDFEditParam
+    {
+        public ImageEditParam() 
+        {
+            EditType = CPDFEditType.EditImage;
+        }
+        public CRect ClipRect { get; set; }
+        public int Rotate { get; set; }
+
+        public override bool CopyTo(PDFEditParam transfer)
+        {
+            ImageEditParam imageTransfer = transfer as ImageEditParam;
+            if (imageTransfer == null)
+            {
+                return false;
+            }
+
+            if (!base.CopyTo(imageTransfer))
+            {
+                return false;
+            }
+
+           
+            imageTransfer.ClipRect = ClipRect;
+            imageTransfer.Rotate = Rotate;
+
+            return true;
+        }
+    }
+}

+ 28 - 0
Demo/Examples/ComPDFKit.Tool/SettingParam/PDFEditParam/PDFEditParam.cs

@@ -0,0 +1,28 @@
+using ComPDFKit.Import;
+using ComPDFKit.PDFPage;
+
+namespace ComPDFKit.Tool
+{
+    public class PDFEditParam
+    {
+        public int PageIndex {  get; set; }
+        public int EditIndex {  get; set; }
+        public CPDFEditType EditType { get; set; }
+        public byte Transparency { get; set; }
+        public CRect ClientRect { get; set; }
+        public virtual bool CopyTo(PDFEditParam transfer)
+        {
+            if (transfer == null)
+            {
+                return false;
+            }
+            transfer.PageIndex = PageIndex;
+            transfer.EditIndex = EditIndex;
+            transfer.EditType = EditType;
+            transfer.Transparency = Transparency;
+            transfer.ClientRect = ClientRect;
+
+            return true;
+        }
+    }
+}

+ 45 - 0
Demo/Examples/ComPDFKit.Tool/SettingParam/PDFEditParam/TextEditParam.cs

@@ -0,0 +1,45 @@
+using ComPDFKit.PDFPage;
+using ComPDFKit.PDFPage.Edit;
+
+namespace ComPDFKit.Tool
+{
+    public class TextEditParam:PDFEditParam
+    {
+        public TextEditParam()
+        {
+            EditType = CPDFEditType.EditText;
+        }
+        public double FontSize { get; set; }
+        public byte[] FontColor { get; set; }
+        public TextAlignType TextAlign { get; set; }
+        public string FontName { get; set; } = string.Empty;
+        public bool IsItalic { get; set; }
+        public bool IsBold { get; set; }
+
+        public override bool CopyTo(PDFEditParam transfer)
+        {
+            TextEditParam texteditTransfer = transfer as TextEditParam;
+            if (texteditTransfer == null)
+            {
+                return false;
+            }
+
+            if (!base.CopyTo(texteditTransfer))
+            {
+                return false;
+            }
+
+            texteditTransfer.FontSize = FontSize;
+            if (FontColor != null)
+            {
+                texteditTransfer.FontColor = (byte[])FontColor.Clone();
+            }
+            texteditTransfer.TextAlign = TextAlign;
+            texteditTransfer.FontName = FontName;
+            texteditTransfer.IsItalic = IsItalic;
+            texteditTransfer.IsBold = IsBold;
+
+            return true;
+        }
+    }
+}

+ 231 - 0
Demo/Examples/ComPDFKit.Tool/UndoManger/AnnotHistory.cs

@@ -0,0 +1,231 @@
+using ComPDFKit.Import;
+using ComPDFKit.PDFAnnotation;
+using ComPDFKit.PDFDocument;
+using ComPDFKit.PDFPage;
+using ComPDFKitViewer.Helper;
+using System.Collections.Generic;
+
+namespace ComPDFKit.Tool.UndoManger
+{
+	public class AnnotHistory : IHistory
+    {
+        internal CPDFAnnotation Annot { get; set; }
+        public CPDFDocument PDFDoc { get; set; }
+
+        public HistoryAction Action { get; set; }
+
+        internal int HistoryIndex { get; set; } = 0;
+
+        public AnnotParam CurrentParam { get; set; }
+        public AnnotParam PreviousParam { get; set; }
+
+        public virtual int GetPageIndex()
+        {
+            return -1;
+        }
+
+        public virtual int GetAnnotIndex()
+        {
+            return -1;
+        }
+
+        public virtual void SetAnnotIndex(int newIndex)
+        {
+
+        }
+
+        public AnnotHistory() 
+        { 
+
+        }
+
+        public bool Redo()
+        {
+            bool result=false;
+            switch(Action)
+            {
+                case HistoryAction.Add:
+                    result = Add();
+                    break;
+                case HistoryAction.Remove:
+                    result = Remove();
+                    break;
+                case HistoryAction.Update:
+                    result = Update(false);
+                    break;
+                default:
+                    break;
+            }
+            return result;
+        }
+
+        public bool Undo()
+        {
+            bool result = false;
+            switch (Action)
+            {
+                case HistoryAction.Add:
+                    result = Remove();
+                    break;
+                case HistoryAction.Remove:
+                    result = Add();
+                    break;
+                case HistoryAction.Update:
+                    result = Update(true);
+                    break;
+                default:
+                    break;
+            }
+            return result;
+        }
+
+        public void Check(IHistory checkItem, bool undo, bool redo, bool add)
+        {
+            List<AnnotHistory> checkAnnotList = new List<AnnotHistory>();
+            if (checkItem is MultiAnnotHistory)
+            {
+                MultiAnnotHistory multiHistory = (MultiAnnotHistory)checkItem;
+                foreach (AnnotHistory checkAnnot in multiHistory.Histories)
+                {
+                    if (checkAnnot == null)
+                    {
+                        continue;
+                    }
+                    checkAnnotList.Add(checkAnnot);
+                }
+            }
+
+            if (checkItem is AnnotHistory)
+            {
+                checkAnnotList.Add((AnnotHistory)checkItem);
+            }
+
+            foreach (AnnotHistory checkAnnot in checkAnnotList)
+            {
+                if (add && checkAnnot.Action == HistoryAction.Remove)
+                {
+                    //remove
+                    SubCurrentIndex(checkAnnot.GetPageIndex(), checkAnnot.GetAnnotIndex());
+                }
+
+                if (undo && checkAnnot.Action == HistoryAction.Add)
+                {
+                    //remove
+                    SubCurrentIndex(checkAnnot.GetPageIndex(), checkAnnot.GetAnnotIndex());
+                }
+
+                if (redo && checkAnnot.Action == HistoryAction.Remove)
+                {
+                    //remove
+                    SubCurrentIndex(checkAnnot.GetPageIndex(), checkAnnot.GetAnnotIndex());
+                }
+
+                if (undo && checkAnnot.Action == HistoryAction.Remove)
+                {
+                    //add
+                    UpdateCurrentIndex(checkAnnot.GetPageIndex(), checkAnnot.HistoryIndex, checkAnnot.GetAnnotIndex());
+                }
+
+                if (redo && checkAnnot.Action == HistoryAction.Add)
+                {
+                    //add
+                    UpdateCurrentIndex(checkAnnot.GetPageIndex(), checkAnnot.HistoryIndex, checkAnnot.GetAnnotIndex());
+                }
+            }
+        }
+
+        internal void SubCurrentIndex(int pageIndex, int annotIndex)
+        {
+            if (GetPageIndex() == pageIndex)
+            {
+                int oldIndex = GetAnnotIndex();
+                if (oldIndex > annotIndex || oldIndex<=-1)
+                {
+                    SetAnnotIndex(oldIndex - 1);
+                    HistoryIndex = oldIndex - 1;
+                }
+               
+                if(oldIndex==annotIndex)
+                {
+                    SetAnnotIndex(-1);
+                    HistoryIndex = -1;
+                }
+            }
+        }
+
+        internal void UpdateCurrentIndex(int pageIndex, int prevIndex, int annotIndex)
+        {
+            if (GetPageIndex() == pageIndex && HistoryIndex == prevIndex)
+            {
+                SetAnnotIndex(annotIndex);
+                HistoryIndex = annotIndex;
+            }
+        }
+
+        internal virtual bool Add()
+        {
+            return false;
+        }
+
+        internal virtual bool Remove()
+        {
+            return false;
+        }
+
+        internal virtual bool Update(bool isUndo)
+        {
+            return false;
+        }
+
+        internal bool MakeAnnotValid(AnnotParam currentParam)
+        {
+            if (currentParam == null || PDFDoc == null || !PDFDoc.IsValid())
+            {
+                return false;
+            }
+           
+            CPDFPage pdfPage = PDFDoc.PageAtIndex(currentParam.PageIndex);
+            if (pdfPage != null)
+            {
+                List<CPDFAnnotation> annotList = pdfPage.GetAnnotations();
+                if (annotList != null && annotList.Count > currentParam.AnnotIndex)
+                {
+                    Annot = annotList[currentParam.AnnotIndex];
+                    if (Annot != null && Annot.IsValid())
+                    {
+                        return true;
+                    }
+                }
+            }
+
+            return false;
+        }
+
+        internal bool CheckArrayEqual<T>(T[] compareA, T[] compareB)
+        {
+            if (compareA == compareB)
+            {
+                return true;
+            }
+
+            if(compareA!=null && compareB!=null)
+            {
+                if(compareA.Length != compareB.Length) 
+                { 
+                    return false; 
+                }
+
+                for (int i = 0; i < compareA.Length; i++)
+                {
+                    if (!compareA[i].Equals(compareB[i]))
+                    {
+                        return false;
+                    }
+                }
+                return true;
+            }
+
+            return false;
+        }
+    }
+}

+ 220 - 0
Demo/Examples/ComPDFKit.Tool/UndoManger/AnnotHistory/CircleAnnotHistory.cs

@@ -0,0 +1,220 @@
+using ComPDFKit.PDFAnnotation;
+using ComPDFKit.PDFPage;
+using ComPDFKit.Tool.Help;
+using System.Collections.Generic;
+
+namespace ComPDFKit.Tool.UndoManger
+{
+    public class CircleAnnotHistory:AnnotHistory
+    {
+        public override int GetAnnotIndex()
+        {
+            if(CurrentParam!=null)
+            {
+                return CurrentParam.AnnotIndex;
+            }
+            return base.GetAnnotIndex();
+        }
+
+        public override int GetPageIndex()
+        {
+            if(CurrentParam!=null)
+            {
+                return CurrentParam.PageIndex;
+            }
+            return base.GetPageIndex();
+        }
+
+        public override void SetAnnotIndex(int newIndex)
+        {
+            if (CurrentParam != null)
+            {
+                CurrentParam.AnnotIndex = newIndex;
+            }
+
+            if (PreviousParam != null)
+            {
+                PreviousParam.AnnotIndex = newIndex;
+            }
+        }
+
+        internal override bool Add()
+        {
+            CircleParam currentParam = CurrentParam as CircleParam;
+            
+            if (currentParam == null || PDFDoc == null || !PDFDoc.IsValid())
+            {
+                return false;
+            }
+
+            CPDFPage pdfPage = PDFDoc.PageAtIndex(currentParam.PageIndex);
+            CPDFCircleAnnotation circleAnnot = pdfPage?.CreateAnnot(C_ANNOTATION_TYPE.C_ANNOTATION_CIRCLE) as CPDFCircleAnnotation;
+            if (circleAnnot != null)
+            {
+                int annotIndex = pdfPage.GetAnnotCount() - 1;
+                
+                if(currentParam.LineColor!=null && currentParam.LineColor.Length==3)
+                {
+                    circleAnnot.SetLineColor(currentParam.LineColor);
+                }
+
+                if (currentParam.HasBgColor)
+                {
+                    if (currentParam.BgColor != null && currentParam.BgColor.Length == 3)
+                    {
+                        circleAnnot.SetBgColor(currentParam.BgColor);
+                    }
+                }
+
+                circleAnnot.SetTransparency((byte)currentParam.Transparency);
+                circleAnnot.SetLineWidth((byte)currentParam.LineWidth);
+                circleAnnot.SetRect(currentParam.ClientRect);
+
+                List<float> floatArray = new List<float>();
+                if (currentParam.LineDash != null)
+                {
+                    foreach (float num in currentParam.LineDash)
+                    {
+                        floatArray.Add(num);
+                    }
+                }
+                circleAnnot.SetBorderStyle(currentParam.BorderStyle, floatArray.ToArray());
+
+                if (!string.IsNullOrEmpty(currentParam.Author))
+                {
+                    circleAnnot.SetAuthor(currentParam.Author);
+                }
+
+                if (!string.IsNullOrEmpty(currentParam.Content))
+                {
+                    circleAnnot.SetContent(currentParam.Content);
+                }
+                circleAnnot.SetIsLocked(currentParam.Locked);
+                circleAnnot.SetCreationDate(PDFHelp.GetCurrentPdfTime());
+                circleAnnot.UpdateAp();
+                circleAnnot.ReleaseAnnot();
+                
+                if (currentParam != null)
+                {
+                    currentParam.AnnotIndex = annotIndex;
+                }
+                if (PreviousParam != null)
+                {
+                    PreviousParam.AnnotIndex = annotIndex;
+                }
+
+                return true;
+            }
+
+            return false;
+        }
+
+        internal override bool Update(bool isUndo)
+        {
+            if (CurrentParam as CircleParam == null || PreviousParam as CircleParam == null)
+            {
+                return false;
+            }
+
+            if (MakeAnnotValid(CurrentParam))
+            {
+                CPDFCircleAnnotation circleAnnot = Annot as CPDFCircleAnnotation;
+                if (circleAnnot == null || !circleAnnot.IsValid())
+                {
+                    return false;
+                }
+
+                CircleParam updateParam = (isUndo ? PreviousParam : CurrentParam) as CircleParam;
+                CircleParam checkParam = (isUndo ? CurrentParam : PreviousParam) as CircleParam;
+
+                if (!CheckArrayEqual(updateParam.LineColor, checkParam.LineColor))
+                {
+                    if(updateParam.LineColor != null && updateParam.LineColor.Length==3)
+                    {
+                        circleAnnot.SetLineColor(updateParam.LineColor);
+                    }
+                }
+
+                if (!CheckArrayEqual(updateParam.BgColor, checkParam.BgColor))
+                {
+                    if (updateParam.HasBgColor ==false)
+                    {
+                        circleAnnot.ClearBgColor();
+                    }
+                    else
+                    {
+                        if (updateParam.BgColor != null && updateParam.BgColor.Length == 3)
+                        {
+                            circleAnnot.SetBgColor(updateParam.BgColor);
+                        }
+                    }
+                }
+
+                if (updateParam.Transparency != checkParam.Transparency)
+                {
+                    circleAnnot.SetTransparency((byte)updateParam.Transparency);
+                }
+
+                if (updateParam.LineWidth != checkParam.LineWidth)
+                {
+                    circleAnnot.SetLineWidth((byte)updateParam.LineWidth);
+                }
+
+                if (!updateParam.ClientRect.Equals(checkParam.ClientRect))
+                {
+                    circleAnnot.SetRect(updateParam.ClientRect);
+                }
+
+                if(updateParam.LineDash!=null)
+                {
+                    List<float> floatArray = new List<float>();
+                    if (updateParam.LineDash != null && updateParam.LineDash.Length > 0)
+                    {
+                        foreach (double num in updateParam.LineDash)
+                        {
+                            floatArray.Add((float)num);
+                        }
+                    }
+                    circleAnnot.SetBorderStyle(updateParam.BorderStyle, floatArray.ToArray());
+                }
+                else
+                {
+                    float[] dashArray = new float[0];
+                    circleAnnot.SetBorderStyle(updateParam.BorderStyle, dashArray);
+                }
+
+                if (updateParam.Author != checkParam.Author)
+                {
+                    circleAnnot.SetAuthor(updateParam.Author);
+                }
+
+                if (updateParam.Content != checkParam.Content)
+                {
+                    circleAnnot.SetContent(updateParam.Content);
+                }
+
+                if (updateParam.Locked != checkParam.Locked)
+                {
+                    circleAnnot.SetIsLocked(updateParam.Locked);
+                }
+
+                circleAnnot.SetModifyDate(PDFHelp.GetCurrentPdfTime());
+                circleAnnot.UpdateAp();
+
+                return true;
+            }
+
+            return false;
+        }
+
+        internal override bool Remove()
+        {
+            if (MakeAnnotValid(CurrentParam))
+            {
+                Annot.RemoveAnnot();
+                return true;
+            }
+            return false;
+        }
+    }
+}

+ 260 - 0
Demo/Examples/ComPDFKit.Tool/UndoManger/AnnotHistory/FreeTextAnnotHistory.cs

@@ -0,0 +1,260 @@
+using ComPDFKit.PDFAnnotation;
+using ComPDFKit.PDFPage;
+using ComPDFKit.Tool.Help;
+using static ComPDFKit.PDFAnnotation.CTextAttribute.CFontNameHelper;
+
+namespace ComPDFKit.Tool.UndoManger
+{
+    public class FreeTextAnnotHistory:AnnotHistory
+    {
+
+        public override int GetAnnotIndex()
+        {
+            if (CurrentParam != null)
+            {
+                return CurrentParam.AnnotIndex;
+            }
+            return base.GetAnnotIndex();
+        }
+
+        public override int GetPageIndex()
+        {
+            if (CurrentParam != null)
+            {
+                return CurrentParam.PageIndex;
+            }
+            return base.GetPageIndex();
+        }
+
+        public override void SetAnnotIndex(int newIndex)
+        {
+            if (CurrentParam != null)
+            {
+                CurrentParam.AnnotIndex = newIndex;
+            }
+
+            if (PreviousParam != null)
+            {
+                PreviousParam.AnnotIndex = newIndex;
+            }
+        }
+
+        internal override bool Add()
+        {
+            FreeTextParam currentParam = CurrentParam as FreeTextParam;
+            if (currentParam == null || PDFDoc == null || !PDFDoc.IsValid())
+            {
+                return false;
+            }
+            CPDFPage pdfPage = PDFDoc.PageAtIndex(currentParam.PageIndex);
+            CPDFFreeTextAnnotation textAnnot = pdfPage?.CreateAnnot(C_ANNOTATION_TYPE.C_ANNOTATION_FREETEXT) as CPDFFreeTextAnnotation;
+            if (textAnnot != null)
+            {
+                int annotIndex = pdfPage.GetAnnotCount() - 1;
+
+                if (currentParam.LineColor!=null && currentParam.LineColor.Length==3)
+                {
+                    textAnnot.SetLineColor(currentParam.LineColor);
+                }
+               
+               
+                if(currentParam.HasBgColor)
+                {
+                    if (currentParam.BgColor != null && currentParam.BgColor.Length == 3)
+                    {
+                        textAnnot.SetBgColor(currentParam.BgColor);
+                    }
+                }
+
+                textAnnot.SetTransparency((byte)currentParam.Transparency);
+                textAnnot.SetLineWidth((byte)currentParam.LineWidth);
+
+                textAnnot.SetFreetextAlignment(currentParam.Alignment);
+
+                CTextAttribute textAttr = new CTextAttribute();
+                byte[] fontColor = new byte[3];
+
+                if (currentParam.FontColor != null && currentParam.FontColor.Length == 3)
+                {
+                    fontColor = currentParam.FontColor;
+                }
+                textAttr.FontColor = fontColor;
+                textAttr.FontSize = (float)currentParam.FontSize;
+                textAttr.FontName = ObtainFontName(
+					GetFontType(currentParam.FontName),
+                    currentParam.IsBold,
+                    currentParam.IsItalic);
+
+                textAnnot.SetFreetextDa(textAttr);
+
+                textAnnot.SetRect(currentParam.ClientRect);
+
+                if (!string.IsNullOrEmpty(currentParam.Author))
+                {
+                    textAnnot.SetAuthor(currentParam.Author);
+                }
+
+                if (!string.IsNullOrEmpty(currentParam.Content))
+                {
+                    textAnnot.SetContent(currentParam.Content);
+                }
+                textAnnot.SetIsLocked(currentParam.Locked);
+                textAnnot.SetCreationDate(PDFHelp.GetCurrentPdfTime());
+                textAnnot.UpdateAp();
+                textAnnot.ReleaseAnnot();
+
+                if (CurrentParam != null)
+                {
+                    currentParam.AnnotIndex = annotIndex;
+                }
+                if (PreviousParam != null)
+                {
+                    PreviousParam.AnnotIndex = annotIndex;
+                }
+
+                return true;
+            }
+            return false;
+        }
+
+        internal override bool Update(bool isUndo)
+        {
+            if (CurrentParam as FreeTextParam == null || PreviousParam as FreeTextParam == null)
+            {
+                return false;
+            }
+
+            if (MakeAnnotValid(CurrentParam))
+            {
+                CPDFFreeTextAnnotation textAnnot = Annot as CPDFFreeTextAnnotation;
+                if (textAnnot == null || !textAnnot.IsValid())
+                {
+                    return false;
+                }
+
+                FreeTextParam updateParam = (isUndo ? PreviousParam : CurrentParam) as FreeTextParam;
+                FreeTextParam checkParam = (isUndo ? CurrentParam : PreviousParam) as FreeTextParam;
+
+                if(!CheckArrayEqual(updateParam.LineColor,checkParam.LineColor))
+                {
+                    if(updateParam.LineColor != null && updateParam.LineColor.Length==3)
+                    {
+                        textAnnot.SetLineColor(updateParam.LineColor);
+                    }
+                }
+
+                if (!CheckArrayEqual(updateParam.BgColor, checkParam.BgColor))
+                {
+                    if (updateParam.HasBgColor)
+                    {
+                        if (updateParam.BgColor != null && updateParam.BgColor.Length == 3)
+                        {
+                            textAnnot.SetBgColor(updateParam.BgColor);
+                        }
+                    }
+                    else
+                    {
+                        textAnnot.ClearBgColor();
+                    }
+                }
+
+                if(updateParam.Transparency != checkParam.Transparency)
+                {
+                    textAnnot.SetTransparency((byte)updateParam.Transparency);
+                }
+
+                if(updateParam.LineWidth != checkParam.LineWidth)
+                {
+                    textAnnot.SetLineWidth((float)updateParam.LineWidth);
+                }
+
+                if(updateParam.Alignment != checkParam.Alignment)
+                {
+                    textAnnot.SetFreetextAlignment(updateParam.Alignment);
+                }
+
+                if(updateParam.FontName != checkParam.FontName)
+                {
+                    CTextAttribute textAttr = textAnnot.FreeTextDa;
+                    bool isBold = IsBold(textAttr.FontName);
+                    bool isItalic = IsItalic(textAttr.FontName);
+                    FontType fontType = GetFontType(updateParam.FontName);
+                    textAttr.FontName = ObtainFontName(fontType, isBold, isItalic);
+                    textAnnot.SetFreetextDa(textAttr);
+                }
+
+                if(updateParam.FontSize != checkParam.FontSize)
+                {
+                    CTextAttribute textAttr = textAnnot.FreeTextDa;
+                    textAttr.FontSize = (float)updateParam.FontSize; 
+                    textAnnot.SetFreetextDa(textAttr);
+                }
+
+                if(!CheckArrayEqual(updateParam.FontColor, checkParam.FontColor))
+                {
+                    if(updateParam.FontColor != null && updateParam.FontColor.Length==3)
+                    {
+                        CTextAttribute textAttr = textAnnot.FreeTextDa;
+                        textAttr.FontColor = updateParam.FontColor;
+                        textAnnot.SetFreetextDa(textAttr);
+                    }
+                }
+               
+                if(updateParam.IsBold != checkParam.IsBold)
+                {
+                    CTextAttribute textAttr = textAnnot.FreeTextDa;
+                    bool isItalic = IsItalic(textAttr.FontName);
+                    FontType fontType = GetFontType(textAttr.FontName);
+                    textAttr.FontName = ObtainFontName(fontType, updateParam.IsBold, isItalic);
+                    textAnnot.SetFreetextDa(textAttr);
+                }
+
+                if(updateParam.IsItalic != checkParam.IsItalic)
+                {
+                    CTextAttribute textAttr = textAnnot.FreeTextDa;
+                    bool isBold = IsBold(textAttr.FontName);
+                    FontType fontType = GetFontType(textAttr.FontName);
+                    textAttr.FontName = ObtainFontName(fontType, isBold, updateParam.IsItalic);
+                    textAnnot.SetFreetextDa(textAttr);
+                }
+
+                if (!updateParam.ClientRect.Equals(checkParam.ClientRect))
+                {
+                    textAnnot.SetRect(updateParam.ClientRect);
+                }
+
+                if (updateParam.Author != checkParam.Author)
+                {
+                    textAnnot.SetAuthor(updateParam.Author);
+                }
+
+                if (updateParam.Content != checkParam.Content)
+                {
+                    textAnnot.SetContent(updateParam.Content);
+                }
+
+                if (updateParam.Locked != checkParam.Locked)
+                {
+                    textAnnot.SetIsLocked(updateParam.Locked);
+                }
+
+                textAnnot.SetModifyDate(PDFHelp.GetCurrentPdfTime());
+                textAnnot.UpdateAp();
+
+                return true;
+            }
+
+            return false;
+        }
+
+        internal override bool Remove()
+        {
+            if (MakeAnnotValid(CurrentParam))
+            {
+                Annot.RemoveAnnot();
+                return true;
+            }
+            return false;
+        }
+    }
+}

+ 162 - 0
Demo/Examples/ComPDFKit.Tool/UndoManger/AnnotHistory/HighlightAnnotHistory.cs

@@ -0,0 +1,162 @@
+using ComPDFKit.Import;
+using ComPDFKit.PDFAnnotation;
+using ComPDFKit.PDFPage;
+using ComPDFKit.Tool.Help;
+using System.Collections.Generic;
+
+namespace ComPDFKit.Tool.UndoManger
+{
+    public class HighlightAnnotHistory:AnnotHistory
+    {
+        public override int GetAnnotIndex()
+        {
+            if (CurrentParam != null)
+            {
+                return CurrentParam.AnnotIndex;
+            }
+            return base.GetAnnotIndex();
+        }
+
+        public override int GetPageIndex()
+        {
+            if (CurrentParam != null)
+            {
+                return CurrentParam.PageIndex;
+            }
+            return base.GetPageIndex();
+        }
+
+        public override void SetAnnotIndex(int newIndex)
+        {
+            if (CurrentParam != null)
+            {
+                CurrentParam.AnnotIndex = newIndex;
+            }
+
+            if (PreviousParam != null)
+            {
+                PreviousParam.AnnotIndex = newIndex;
+            }
+        }
+
+
+        internal override bool Add()
+        {
+            HighlightParam currentParam = CurrentParam as HighlightParam;
+            if (currentParam == null || PDFDoc == null || !PDFDoc.IsValid())
+			{
+				return false;
+			}
+			CPDFPage pdfPage = PDFDoc.PageAtIndex(currentParam.PageIndex);
+			CPDFHighlightAnnotation highlightAnnot = pdfPage?.CreateAnnot(C_ANNOTATION_TYPE.C_ANNOTATION_HIGHLIGHT) as CPDFHighlightAnnotation;
+			if (highlightAnnot != null)
+			{
+				int annotIndex = pdfPage.GetAnnotCount() - 1;
+				
+				highlightAnnot.SetTransparency((byte)currentParam.Transparency);
+				
+				if (currentParam.QuardRects != null)
+				{
+                    highlightAnnot.SetQuardRects(currentParam.QuardRects);
+                }
+
+                if (currentParam.HighlightColor != null && currentParam.HighlightColor.Length==3)
+				{
+                    highlightAnnot.SetColor(currentParam.HighlightColor);
+                }
+
+				if (!string.IsNullOrEmpty(currentParam.Author))
+				{
+					highlightAnnot.SetAuthor(currentParam.Author);
+				}
+
+				if (!string.IsNullOrEmpty(currentParam.Content))
+				{
+					highlightAnnot.SetContent(currentParam.Content);
+				}
+                highlightAnnot.SetRect(currentParam.ClientRect);
+                highlightAnnot.SetIsLocked(currentParam.Locked);
+				highlightAnnot.SetCreationDate(PDFHelp.GetCurrentPdfTime());
+				highlightAnnot.UpdateAp();
+				highlightAnnot.ReleaseAnnot();
+
+				if (currentParam != null)
+				{
+                    currentParam.AnnotIndex = annotIndex;
+				}
+				if (PreviousParam != null)
+				{
+					PreviousParam.AnnotIndex = annotIndex;
+				}
+
+				return true;
+			}
+			return false;
+		}
+
+		internal override bool Update(bool isUndo)
+		{
+			if (CurrentParam as HighlightParam == null || PreviousParam as HighlightParam == null)
+			{
+				return false;
+			}
+
+			if (MakeAnnotValid(CurrentParam))
+			{
+				CPDFHighlightAnnotation highlightAnnot = Annot as CPDFHighlightAnnotation;
+				if (highlightAnnot == null || !highlightAnnot.IsValid())
+				{
+					return false;
+				}
+
+				HighlightParam updateParam = (isUndo ? PreviousParam : CurrentParam) as HighlightParam;
+				HighlightParam checkParam = (isUndo ? CurrentParam : PreviousParam) as HighlightParam;
+
+                if (!CheckArrayEqual(updateParam.HighlightColor, checkParam.HighlightColor))
+                {
+					if(updateParam.HighlightColor != null && updateParam.HighlightColor.Length==3)
+					{
+                        highlightAnnot.SetColor(updateParam.HighlightColor);
+                    }
+				}
+
+				if (updateParam.Transparency != checkParam.Transparency)
+				{
+					highlightAnnot.SetTransparency((byte)updateParam.Transparency);
+				}
+
+				if (updateParam.Author != checkParam.Author)
+				{
+					highlightAnnot.SetAuthor(updateParam.Author);
+				}
+
+				if (updateParam.Content != checkParam.Content)
+				{
+					highlightAnnot.SetContent(updateParam.Content);
+				}
+
+				if (updateParam.Locked != checkParam.Locked)
+				{
+					highlightAnnot.SetIsLocked(updateParam.Locked);
+				}
+
+				highlightAnnot.SetModifyDate(PDFHelp.GetCurrentPdfTime());
+				highlightAnnot.UpdateAp();
+
+				return true;
+			}
+
+			return false;
+		}
+
+		internal override bool Remove()
+		{
+			if (MakeAnnotValid(CurrentParam))
+			{
+				Annot.RemoveAnnot();
+				return true;
+			}
+			return false;
+		}
+	}
+}

+ 165 - 0
Demo/Examples/ComPDFKit.Tool/UndoManger/AnnotHistory/InkAnnotHistory.cs

@@ -0,0 +1,165 @@
+using ComPDFKit.Import;
+using ComPDFKit.PDFAnnotation;
+using ComPDFKit.PDFPage;
+using ComPDFKit.Tool.Help;
+using System.Collections.Generic;
+
+namespace ComPDFKit.Tool.UndoManger
+{
+    public class InkAnnotHistory:AnnotHistory
+    {
+        public override int GetAnnotIndex()
+        {
+            if (CurrentParam != null)
+            {
+                return CurrentParam.AnnotIndex;
+            }
+            return base.GetAnnotIndex();
+        }
+
+        public override int GetPageIndex()
+        {
+            if (CurrentParam != null)
+            {
+                return CurrentParam.PageIndex;
+            }
+            return base.GetPageIndex();
+        }
+
+        public override void SetAnnotIndex(int newIndex)
+        {
+            if (CurrentParam != null)
+            {
+                CurrentParam.AnnotIndex = newIndex;
+            }
+
+            if (PreviousParam != null)
+            {
+                PreviousParam.AnnotIndex = newIndex;
+            }
+        }
+
+        internal override bool Add()
+        {
+            InkParam currentParam = CurrentParam as InkParam;
+            if (currentParam == null || PDFDoc == null || !PDFDoc.IsValid())
+            {
+                return false;
+            }
+
+            CPDFPage pdfPage = PDFDoc.PageAtIndex(currentParam.PageIndex);
+            CPDFInkAnnotation inkAnnot = pdfPage?.CreateAnnot(C_ANNOTATION_TYPE.C_ANNOTATION_INK) as CPDFInkAnnotation;
+            if(inkAnnot != null)
+            {
+                int annotIndex = pdfPage.GetAnnotCount() - 1;
+                if(currentParam.InkColor!=null && currentParam.InkColor.Length==3)
+                {
+                    inkAnnot.SetInkColor(currentParam.InkColor);
+                }
+               
+                inkAnnot.SetThickness((float)currentParam.Thickness);
+                inkAnnot.SetInkPath(currentParam.InkPath);
+                inkAnnot.SetTransparency(currentParam.Transparency);
+                if (!string.IsNullOrEmpty(currentParam.Author))
+                {
+                    inkAnnot.SetAuthor(currentParam.Author);
+                }
+
+                if (!string.IsNullOrEmpty(currentParam.Content))
+                {
+                    inkAnnot.SetContent(currentParam.Content);
+                }
+                inkAnnot.SetIsLocked(currentParam.Locked);
+                inkAnnot.SetCreationDate(PDFHelp.GetCurrentPdfTime());
+                inkAnnot.UpdateAp();
+                inkAnnot.ReleaseAnnot();
+
+                if (currentParam != null)
+                {
+                    currentParam.AnnotIndex = annotIndex;
+                }
+                if (PreviousParam != null)
+                {
+                    PreviousParam.AnnotIndex = annotIndex;
+                }
+                return true;
+            }
+
+            return false;
+        }
+
+        internal override bool Update(bool isUndo)
+        {
+            if (CurrentParam as InkParam == null || PreviousParam as InkParam == null)
+            {
+                return false;
+            }
+
+            if (MakeAnnotValid(CurrentParam))
+            {
+                CPDFInkAnnotation inkAnnot = Annot as CPDFInkAnnotation;
+                if (inkAnnot == null || !inkAnnot.IsValid())
+                {
+                    return false;
+                }
+
+                InkParam updateParam = (isUndo ? PreviousParam : CurrentParam) as InkParam;
+                InkParam checkParam = (isUndo ? CurrentParam : PreviousParam)as InkParam;
+
+                if (!CheckArrayEqual(updateParam.InkColor,checkParam.InkColor))
+                {
+                    if(updateParam.InkColor != null && updateParam.InkColor.Length==3)
+                    {
+                        inkAnnot.SetInkColor(updateParam.InkColor);
+                    }
+                }
+
+                if (updateParam.Thickness != checkParam.Thickness)
+                {
+                    inkAnnot.SetThickness((byte)updateParam.Thickness);
+                }
+
+                if (updateParam.InkPath!=null)
+                {
+                    inkAnnot.SetInkPath(updateParam.InkPath);
+                }
+
+                if (updateParam.Transparency != checkParam.Transparency)
+                {
+                    inkAnnot.SetTransparency((byte)updateParam.Transparency);
+                }
+
+                if (updateParam.Author != checkParam.Author)
+                {
+                    inkAnnot.SetAuthor(updateParam.Author);
+                }
+
+                if (updateParam.Content != checkParam.Content)
+                {
+                    inkAnnot.SetContent(updateParam.Content);
+                }
+
+                if (updateParam.Locked != checkParam.Locked)
+                {
+                    inkAnnot.SetIsLocked(updateParam.Locked);
+                }
+
+                inkAnnot.SetModifyDate(PDFHelp.GetCurrentPdfTime());
+                inkAnnot.UpdateAp();
+                return true;
+            }
+
+            return false;
+        }
+
+        internal override bool Remove()
+        {
+            if (MakeAnnotValid(CurrentParam))
+            {
+                Annot.RemoveAnnot();
+                return true;
+            }
+            return false;
+        }
+    }
+}

+ 234 - 0
Demo/Examples/ComPDFKit.Tool/UndoManger/AnnotHistory/LineAnnotHistory.cs

@@ -0,0 +1,234 @@
+using ComPDFKit.PDFAnnotation;
+using ComPDFKit.PDFPage;
+using ComPDFKit.Tool.Help;
+using System.Collections.Generic;
+
+namespace ComPDFKit.Tool.UndoManger
+{
+    public class LineAnnotHistory:AnnotHistory
+    {
+
+        public override int GetAnnotIndex()
+        {
+            if (CurrentParam != null)
+            {
+                return CurrentParam.AnnotIndex;
+            }
+            return base.GetAnnotIndex();
+        }
+
+        public override int GetPageIndex()
+        {
+            if (CurrentParam != null)
+            {
+                return CurrentParam.PageIndex;
+            }
+            return base.GetPageIndex();
+        }
+
+        public override void SetAnnotIndex(int newIndex)
+        {
+            if (CurrentParam != null)
+            {
+                CurrentParam.AnnotIndex = newIndex;
+            }
+
+            if (PreviousParam != null)
+            {
+                PreviousParam.AnnotIndex = newIndex;
+            }
+        }
+
+        internal override bool Add()
+        {
+            LineParam currentParam = CurrentParam as LineParam;
+            if (currentParam==null || PDFDoc==null || !PDFDoc.IsValid())
+            {
+               return false;
+            }
+
+            CPDFPage pdfPage = PDFDoc.PageAtIndex(currentParam.PageIndex);
+            CPDFLineAnnotation lineAnnot = pdfPage?.CreateAnnot(C_ANNOTATION_TYPE.C_ANNOTATION_LINE) as CPDFLineAnnotation;
+            if (lineAnnot != null)
+            {
+                int annotIndex = pdfPage.GetAnnotCount() - 1;
+                if(currentParam.HeadLineType!=C_LINE_TYPE.LINETYPE_NONE || currentParam.TailLineType!=C_LINE_TYPE.LINETYPE_NONE)
+                {
+                    lineAnnot.SetLineType(currentParam.HeadLineType, currentParam.TailLineType);
+                }
+
+                if(currentParam.LineColor!=null && currentParam.LineColor.Length==3)
+                {
+                    lineAnnot.SetLineColor(currentParam.LineColor);
+                }
+
+                if(currentParam.HasBgColor)
+                {
+                    if (currentParam.BgColor != null && currentParam.BgColor.Length == 3)
+                    {
+                        lineAnnot.SetBgColor(currentParam.BgColor);
+                    }
+                }
+
+                lineAnnot.SetTransparency((byte)currentParam.Transparency);
+                lineAnnot.SetLineWidth((byte)currentParam.LineWidth);
+                lineAnnot.SetLinePoints(currentParam.HeadPoint, currentParam.TailPoint);
+                lineAnnot.SetRect(currentParam.ClientRect);
+
+                List<float> floatArray = new List<float>();
+                if (currentParam.LineDash != null)
+                {
+                    foreach (float num in currentParam.LineDash)
+                    {
+                        floatArray.Add(num);
+                    }
+                }
+                lineAnnot.SetBorderStyle(C_BORDER_STYLE.BS_DASHDED, floatArray.ToArray());
+
+                if (!string.IsNullOrEmpty(currentParam.Author))
+                {
+                    lineAnnot.SetAuthor(currentParam.Author);
+                }
+
+                if(!string.IsNullOrEmpty(currentParam.Content))
+                {
+                    lineAnnot.SetContent(currentParam.Content);
+                }
+                lineAnnot.SetIsLocked(currentParam.Locked);
+                lineAnnot.SetCreationDate(PDFHelp.GetCurrentPdfTime());
+                lineAnnot.UpdateAp();
+                lineAnnot.ReleaseAnnot();
+
+                if (CurrentParam != null)
+                {
+                    currentParam.AnnotIndex = annotIndex;
+                }
+                if (PreviousParam != null)
+                {
+                    PreviousParam.AnnotIndex = annotIndex;
+                }
+                return true;
+            }
+
+            return false;
+        }
+
+        internal override bool Update(bool isUndo)
+        {
+            if (CurrentParam as LineParam == null || PreviousParam as LineParam == null)
+            {
+                return false;
+            }
+
+            if (MakeAnnotValid(CurrentParam))
+            {
+                CPDFLineAnnotation lineAnnot = Annot as CPDFLineAnnotation;
+                if (lineAnnot == null || !lineAnnot.IsValid())
+                {
+                    return false;
+                }
+
+                LineParam updateParam = (isUndo ? PreviousParam : CurrentParam)as LineParam;
+                LineParam checkParam = (isUndo ? CurrentParam : PreviousParam) as LineParam;
+
+                if(updateParam.HeadLineType!=checkParam.HeadLineType || updateParam.TailLineType!=checkParam.TailLineType)
+                {
+                    lineAnnot.SetLineType(updateParam.HeadLineType, updateParam.TailLineType);
+                }
+
+                if (!CheckArrayEqual(updateParam.LineColor,checkParam.LineColor))
+                {
+                    if(updateParam.LineColor != null && updateParam.LineColor.Length==3)
+                    {
+                        lineAnnot.SetLineColor(updateParam.LineColor);
+                    }
+                }
+
+                if(!CheckArrayEqual(updateParam.BgColor, checkParam.BgColor))
+                {
+                    if (updateParam.HasBgColor)
+                    {
+                        if (updateParam.BgColor != null && updateParam.BgColor.Length == 3)
+                        {
+                            lineAnnot.SetBgColor(updateParam.BgColor);
+                        }
+                    }
+                    else
+                    {
+                        lineAnnot.ClearBgColor();
+                    }
+                }
+
+                if(updateParam.Transparency!=checkParam.Transparency)
+                {
+                    lineAnnot.SetTransparency((byte)updateParam.Transparency);
+                }
+
+                if(updateParam.LineWidth!=checkParam.LineWidth)
+                {
+                    lineAnnot.SetLineWidth((byte)updateParam.LineWidth);
+                }
+
+                if(!updateParam.HeadPoint.Equals(checkParam.HeadPoint) || !updateParam.TailPoint.Equals(checkParam.TailPoint))
+                {
+                    lineAnnot.SetLinePoints(updateParam.HeadPoint,updateParam.TailPoint);
+                }
+
+                if(!updateParam.ClientRect.Equals(checkParam.ClientRect))
+                {
+                    lineAnnot.SetRect(updateParam.ClientRect);
+                }
+
+                if (updateParam.LineDash != null)
+                {
+                    List<float> floatArray = new List<float>();
+                    if (updateParam.LineDash != null && updateParam.LineDash.Length > 0)
+                    {
+                        foreach (double num in updateParam.LineDash)
+                        {
+                            floatArray.Add((float)num);
+                        }
+                    }
+                    lineAnnot.SetBorderStyle(updateParam.BorderStyle, floatArray.ToArray());
+                }
+                else
+                {
+                    float[] dashArray = new float[0];
+                    lineAnnot.SetBorderStyle(updateParam.BorderStyle, dashArray);
+                }
+
+                if (updateParam.Author != checkParam.Author)
+                {
+                    lineAnnot.SetAuthor(updateParam.Author);
+                }
+
+                if(updateParam.Content != checkParam.Content)
+                {
+                    lineAnnot.SetContent(updateParam.Content);
+                }
+
+                if(updateParam.Locked != checkParam.Locked) 
+                {
+                    lineAnnot.SetIsLocked(updateParam.Locked);
+                }
+
+                lineAnnot.SetModifyDate(PDFHelp.GetCurrentPdfTime());
+                lineAnnot.UpdateAp();
+
+                return true;
+            }
+
+            return false;
+        }
+
+        internal override bool Remove()
+        {
+            if (MakeAnnotValid(CurrentParam))
+            {
+                Annot.RemoveAnnot();
+                return true;
+            }
+            return false;
+        }
+    }
+}

+ 243 - 0
Demo/Examples/ComPDFKit.Tool/UndoManger/AnnotHistory/LineMeasureAnnotHistory.cs

@@ -0,0 +1,243 @@
+using ComPDFKit.Measure;
+using ComPDFKit.PDFAnnotation;
+using ComPDFKit.PDFPage;
+using ComPDFKit.Tool.Help;
+using ComPDFKitViewer.Annot;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using static ComPDFKit.PDFAnnotation.CTextAttribute;
+
+namespace ComPDFKit.Tool.UndoManger
+{
+    internal class LineMeasureAnnotHistory : AnnotHistory
+    {
+        public override int GetAnnotIndex()
+        {
+            if (CurrentParam != null)
+            {
+                return CurrentParam.AnnotIndex;
+            }
+            return base.GetAnnotIndex();
+        }
+
+        public override int GetPageIndex()
+        {
+            if (CurrentParam != null)
+            {
+                return CurrentParam.PageIndex;
+            }
+            return base.GetPageIndex();
+        }
+        public override void SetAnnotIndex(int newIndex)
+        {
+            if (CurrentParam != null)
+            {
+                CurrentParam.AnnotIndex = newIndex;
+            }
+
+            if (PreviousParam != null)
+            {
+                PreviousParam.AnnotIndex = newIndex;
+            }
+        }
+        internal override bool Add()
+        {
+            LineMeasureParam currentParam = CurrentParam as LineMeasureParam;
+            if (CurrentParam == null || PDFDoc == null || !PDFDoc.IsValid())
+            {
+                return false;
+            }
+
+            CPDFPage pdfPage = PDFDoc.PageAtIndex(currentParam.PageIndex);
+            CPDFLineAnnotation polygonAnnot = pdfPage?.CreateAnnot(C_ANNOTATION_TYPE.C_ANNOTATION_LINE) as CPDFLineAnnotation;
+            if (polygonAnnot != null)
+            {
+                int annotIndex = pdfPage.GetAnnotCount() - 1;
+
+                if (currentParam.HeadLineType != C_LINE_TYPE.LINETYPE_NONE || currentParam.TailLineType != C_LINE_TYPE.LINETYPE_NONE)
+                {
+                    polygonAnnot.SetLineType(currentParam.HeadLineType, currentParam.TailLineType);
+                }
+
+                if (currentParam.LineColor != null && currentParam.LineColor.Length == 3)
+                {
+                    polygonAnnot.SetLineColor(currentParam.LineColor);
+                }
+
+                polygonAnnot.SetTransparency((byte)currentParam.Transparency);
+                polygonAnnot.SetLineWidth(currentParam.LineWidth);
+
+                polygonAnnot.SetLinePoints(currentParam.HeadPoint, currentParam.TailPoint);
+                polygonAnnot.SetRect(currentParam.ClientRect);
+
+                if (currentParam.LineDash != null)
+                {
+                    if (currentParam.LineDash.Length == 0)
+                    {
+                        polygonAnnot.SetBorderStyle(C_BORDER_STYLE.BS_SOLID, new float[0]);
+                    }
+                    else
+                    {
+                        List<float> floatArray = new List<float>();
+                        foreach (float num in currentParam.LineDash)
+                        {
+                            floatArray.Add(num);
+                        }
+                        polygonAnnot.SetBorderStyle(C_BORDER_STYLE.BS_DASHDED, floatArray.ToArray());
+                    }
+                }
+
+                CTextAttribute textAttribute = new CTextAttribute();
+                textAttribute.FontColor = currentParam.FontColor;
+                textAttribute.FontSize = (float)currentParam.FontSize;
+                textAttribute.FontName = CFontNameHelper.ObtainFontName(CFontNameHelper.GetFontType(currentParam.FontName),
+                            currentParam.IsBold,
+                            currentParam.IsItalic);
+                polygonAnnot.SetTextAttribute(textAttribute);
+                if (currentParam.measureInfo != null)
+                {
+                    CPDFDistanceMeasure polygonMeasure = polygonAnnot.GetDistanceMeasure();
+                    if (polygonMeasure != null)
+                    {
+                        polygonMeasure.SetMeasureInfo(currentParam.measureInfo);
+                        polygonMeasure.SetMeasureScale(currentParam.measureInfo.RulerBase, currentParam.measureInfo.RulerBaseUnit,
+                                                       currentParam.measureInfo.RulerTranslate, currentParam.measureInfo.RulerTranslateUnit);
+                        polygonMeasure.UpdateAnnotMeasure();
+                    }
+                }
+                if (!string.IsNullOrEmpty(currentParam.Author))
+                {
+                    polygonAnnot.SetAuthor(currentParam.Author);
+                }
+
+                if (!string.IsNullOrEmpty(currentParam.Content))
+                {
+                    polygonAnnot.SetContent(currentParam.Content);
+                }
+                polygonAnnot.SetIsLocked(currentParam.Locked);
+                polygonAnnot.SetCreationDate(PDFHelp.GetCurrentPdfTime());
+                polygonAnnot.UpdateAp();
+                polygonAnnot.ReleaseAnnot();
+
+                if (currentParam != null)
+                {
+                    currentParam.AnnotIndex = annotIndex;
+                }
+                if (PreviousParam != null)
+                {
+                    PreviousParam.AnnotIndex = annotIndex;
+                }
+                return true;
+            }
+            return false;
+        }
+        internal override bool Update(bool isUndo)
+        {
+            if (CurrentParam as LineMeasureParam == null || PreviousParam as LineMeasureParam == null)
+            {
+                return false;
+            }
+            if (MakeAnnotValid(CurrentParam))
+            {
+                CPDFLineAnnotation polygonAnnot = Annot as CPDFLineAnnotation;
+                if (polygonAnnot == null || !polygonAnnot.IsValid() || !polygonAnnot.IsMersured())
+                {
+                    return false;
+                }
+
+                LineMeasureParam updateParam = (isUndo ? PreviousParam : CurrentParam) as LineMeasureParam;
+                LineMeasureParam checkParam = (isUndo ? CurrentParam : PreviousParam) as LineMeasureParam;
+                if (!CheckArrayEqual(updateParam.LineColor, checkParam.LineColor))
+                {
+                    if (updateParam.LineColor != null && updateParam.LineColor.Length == 3)
+                    {
+                        polygonAnnot.SetLineColor(updateParam.LineColor);
+                    }
+                }
+
+                if (updateParam.Transparency != checkParam.Transparency)
+                {
+                    polygonAnnot.SetTransparency((byte)updateParam.Transparency);
+                }
+
+                if (updateParam.LineWidth != checkParam.LineWidth)
+                {
+                    polygonAnnot.SetLineWidth((byte)updateParam.LineWidth);
+                }
+
+                if (!CheckArrayEqual(updateParam.LineDash, checkParam.LineDash))
+                {
+                    if (updateParam.LineDash.Length == 0)
+                    {
+                        polygonAnnot.SetBorderStyle(C_BORDER_STYLE.BS_SOLID, new float[0]);
+                    }
+                    else
+                    {
+                        List<float> floatArray = new List<float>();
+                        foreach (float num in updateParam.LineDash)
+                        {
+                            floatArray.Add(num);
+                        }
+                        polygonAnnot.SetBorderStyle(C_BORDER_STYLE.BS_DASHDED, floatArray.ToArray());
+                    }
+                }
+                if (!updateParam.HeadPoint.Equals(checkParam.HeadPoint) || !updateParam.TailPoint.Equals(checkParam.TailPoint))
+                {
+                    polygonAnnot.SetLinePoints(updateParam.HeadPoint, updateParam.TailPoint);
+                }
+
+                if (!updateParam.ClientRect.Equals(checkParam.ClientRect))
+                {
+                    polygonAnnot.SetRect(updateParam.ClientRect);
+                }
+
+
+                if (updateParam.Author != checkParam.Author)
+                {
+                    polygonAnnot.SetAuthor(updateParam.Author);
+                }
+
+                if (updateParam.Content != checkParam.Content)
+                {
+                    polygonAnnot.SetContent(updateParam.Content);
+                }
+
+                if (updateParam.Locked != checkParam.Locked)
+                {
+                    polygonAnnot.SetIsLocked(updateParam.Locked);
+                }
+                if (updateParam.measureInfo != checkParam.measureInfo)
+                {
+                    CPDFDistanceMeasure polygonMeasure = polygonAnnot.GetDistanceMeasure();
+                    if (polygonMeasure != null)
+                    {
+                        polygonMeasure.SetMeasureInfo(updateParam.measureInfo);
+                        polygonMeasure.SetMeasureScale(updateParam.measureInfo.RulerBase, updateParam.measureInfo.RulerBaseUnit,
+                                                       updateParam.measureInfo.RulerTranslate, updateParam.measureInfo.RulerTranslateUnit);
+                        polygonMeasure.UpdateAnnotMeasure();
+                    }
+                }
+                polygonAnnot.SetModifyDate(PDFHelp.GetCurrentPdfTime());
+
+                polygonAnnot.UpdateAp();
+
+                return true;
+            }
+            return false;
+        }
+
+        internal override bool Remove()
+        {
+            if (MakeAnnotValid(CurrentParam))
+            {
+                Annot.RemoveAnnot();
+                return true;
+            }
+            return false;
+        }
+
+    }
+}

+ 200 - 0
Demo/Examples/ComPDFKit.Tool/UndoManger/AnnotHistory/LinkAnnotHistory.cs

@@ -0,0 +1,200 @@
+using ComPDFKit.PDFAnnotation;
+using ComPDFKit.PDFDocument;
+using ComPDFKit.PDFDocument.Action;
+using ComPDFKit.PDFPage;
+using ComPDFKit.Tool.Help;
+
+namespace ComPDFKit.Tool.UndoManger
+{
+    public class LinkAnnotHistory:AnnotHistory
+    {
+        public override int GetAnnotIndex()
+        {
+            if (CurrentParam != null)
+            {
+                return CurrentParam.AnnotIndex;
+            }
+            return base.GetAnnotIndex();
+        }
+
+        public override int GetPageIndex()
+        {
+            if (CurrentParam != null)
+            {
+                return CurrentParam.PageIndex;
+            }
+            return base.GetPageIndex();
+        }
+
+        public override void SetAnnotIndex(int newIndex)
+        {
+            if (CurrentParam != null)
+            {
+                CurrentParam.AnnotIndex = newIndex;
+            }
+
+            if (PreviousParam != null)
+            {
+                PreviousParam.AnnotIndex = newIndex;
+            }
+        }
+
+        internal override bool Add()
+        {
+            LinkParam currentParam = CurrentParam as LinkParam;
+            if (currentParam == null || PDFDoc == null || !PDFDoc.IsValid())
+            {
+                return false;
+            }
+
+            CPDFPage pdfPage = PDFDoc.PageAtIndex(currentParam.PageIndex);
+            CPDFLinkAnnotation linkAnnot = pdfPage?.CreateAnnot(C_ANNOTATION_TYPE.C_ANNOTATION_LINK) as CPDFLinkAnnotation;
+            if (linkAnnot != null)
+            {
+                int annotIndex = pdfPage.GetAnnotCount() - 1;
+
+                switch(currentParam.Action)
+                {
+                    case C_ACTION_TYPE.ACTION_TYPE_GOTO:
+                        {
+                            CPDFGoToAction gotoAction = new CPDFGoToAction();
+                            CPDFDestination destination = new CPDFDestination();
+                            destination.Position_X = currentParam.DestinationPosition.x;
+                            destination.Position_Y = currentParam.DestinationPosition.y;
+                            destination.PageIndex = currentParam.DestinationPageIndex;
+                            gotoAction.SetDestination(PDFDoc,destination);
+                            linkAnnot.SetLinkAction(gotoAction);
+                        }
+                        break;
+                    case C_ACTION_TYPE.ACTION_TYPE_URI:
+                        {
+                            CPDFUriAction uriAction = new CPDFUriAction();
+                            if(!string.IsNullOrEmpty(currentParam.Uri))
+                            {
+                                uriAction.SetUri(currentParam.Uri);
+                            }
+                            linkAnnot.SetLinkAction(uriAction);
+                        }
+                        break;
+                    default:
+                        break;
+                }
+
+                linkAnnot.SetRect(currentParam.ClientRect);
+
+                if (!string.IsNullOrEmpty(currentParam.Author))
+                {
+                    linkAnnot.SetAuthor(currentParam.Author);
+                }
+
+                if (!string.IsNullOrEmpty(currentParam.Content))
+                {
+                    linkAnnot.SetContent(currentParam.Content);
+                }
+
+                linkAnnot.SetIsLocked(currentParam.Locked);
+                linkAnnot.SetCreationDate(PDFHelp.GetCurrentPdfTime());
+                linkAnnot.UpdateAp();
+                linkAnnot.ReleaseAnnot();
+
+                if (currentParam != null)
+                {
+                    currentParam.AnnotIndex = annotIndex;
+                }
+
+                if (PreviousParam != null)
+                {
+                    PreviousParam.AnnotIndex = annotIndex;
+                }
+
+                return true;
+            }
+
+            return false;
+        }
+
+        internal override bool Update(bool isUndo)
+        {
+            if (CurrentParam as LinkParam == null || PreviousParam as LinkParam == null)
+            {
+                return false;
+            }
+
+            if (MakeAnnotValid(CurrentParam))
+            {
+                CPDFLinkAnnotation linkAnnot = Annot as CPDFLinkAnnotation;
+                if (linkAnnot == null || !linkAnnot.IsValid())
+                {
+                    return false;
+                }
+
+                LinkParam updateParam = (isUndo ? PreviousParam : CurrentParam) as LinkParam;
+                LinkParam checkParam = (isUndo ? CurrentParam : PreviousParam) as LinkParam;
+
+                switch (updateParam.Action)
+                {
+                    case C_ACTION_TYPE.ACTION_TYPE_GOTO:
+                        {
+                            CPDFGoToAction gotoAction = new CPDFGoToAction();
+                            CPDFDestination destination = new CPDFDestination();
+                            destination.Position_X = updateParam.DestinationPosition.x;
+                            destination.Position_Y = updateParam.DestinationPosition.y;
+                            destination.PageIndex = updateParam.DestinationPageIndex;
+                            gotoAction.SetDestination(PDFDoc, destination);
+                            linkAnnot.SetLinkAction(gotoAction);
+                        }
+                        break;
+                    case C_ACTION_TYPE.ACTION_TYPE_URI:
+                        {
+                            CPDFUriAction uriAction = new CPDFUriAction();
+                            if(!string.IsNullOrEmpty(updateParam.Uri))
+                            {
+                                uriAction.SetUri(updateParam.Uri);
+                            }
+                            linkAnnot.SetLinkAction(uriAction);
+                        }
+                        break;
+                    default:
+                        break;
+                }
+
+                if (!updateParam.ClientRect.Equals(checkParam.ClientRect))
+                {
+                    linkAnnot.SetRect(updateParam.ClientRect);
+                }
+
+                if (updateParam.Author != checkParam.Author)
+                {
+                    linkAnnot.SetAuthor(updateParam.Author);
+                }
+
+                if (updateParam.Content != checkParam.Content)
+                {
+                    linkAnnot.SetContent(updateParam.Content);
+                }
+
+                if (updateParam.Locked != checkParam.Locked)
+                {
+                    linkAnnot.SetIsLocked(updateParam.Locked);
+                }
+
+                linkAnnot.SetModifyDate(PDFHelp.GetCurrentPdfTime());
+                linkAnnot.UpdateAp();
+
+                return true;
+            }
+
+            return false;
+        }
+
+        internal override bool Remove()
+        {
+            if (MakeAnnotValid(CurrentParam))
+            {
+                Annot.RemoveAnnot();
+                return true;
+            }
+            return false;
+        }
+    }
+}

+ 239 - 0
Demo/Examples/ComPDFKit.Tool/UndoManger/AnnotHistory/PolyLineMeasureAnnotHistory.cs

@@ -0,0 +1,239 @@
+using ComPDFKit.Measure;
+using ComPDFKit.PDFAnnotation;
+using ComPDFKit.PDFPage;
+using ComPDFKit.Tool.Help;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using static ComPDFKit.PDFAnnotation.CTextAttribute;
+
+namespace ComPDFKit.Tool.UndoManger
+{
+    internal class PolyLineMeasureAnnotHistory : AnnotHistory
+    {
+        public override int GetAnnotIndex()
+        {
+            if (CurrentParam != null)
+            {
+                return CurrentParam.AnnotIndex;
+            }
+            return base.GetAnnotIndex();
+        }
+
+        public override int GetPageIndex()
+        {
+            if (CurrentParam != null)
+            {
+                return CurrentParam.PageIndex;
+            }
+            return base.GetPageIndex();
+        }
+        public override void SetAnnotIndex(int newIndex)
+        {
+            if (CurrentParam != null)
+            {
+                CurrentParam.AnnotIndex = newIndex;
+            }
+
+            if (PreviousParam != null)
+            {
+                PreviousParam.AnnotIndex = newIndex;
+            }
+        }
+        internal override bool Add()
+        {
+            PolyLineMeasureParam currentParam = CurrentParam as PolyLineMeasureParam;
+            if (CurrentParam == null || PDFDoc == null || !PDFDoc.IsValid())
+            {
+                return false;
+            }
+
+            CPDFPage pdfPage = PDFDoc.PageAtIndex(currentParam.PageIndex);
+            CPDFPolylineAnnotation polygonAnnot = pdfPage?.CreateAnnot(C_ANNOTATION_TYPE.C_ANNOTATION_POLYLINE) as CPDFPolylineAnnotation;
+            if (polygonAnnot != null)
+            {
+                int annotIndex = pdfPage.GetAnnotCount() - 1;
+
+                if (currentParam.LineColor != null && currentParam.LineColor.Length == 3)
+                {
+                    polygonAnnot.SetLineColor(currentParam.LineColor);
+                }
+
+                polygonAnnot.SetTransparency((byte)currentParam.Transparency);
+                polygonAnnot.SetLineWidth(currentParam.LineWidth);
+
+                polygonAnnot.SetPoints(currentParam.SavePoints);
+                polygonAnnot.SetRect(currentParam.ClientRect);
+
+                if (currentParam.LineDash != null)
+                {
+                    if (currentParam.LineDash.Length == 0)
+                    {
+                        polygonAnnot.SetBorderStyle(C_BORDER_STYLE.BS_SOLID, new float[0]);
+                    }
+                    else
+                    {
+                        List<float> floatArray = new List<float>();
+                        foreach (float num in currentParam.LineDash)
+                        {
+                            floatArray.Add(num);
+                        }
+                        polygonAnnot.SetBorderStyle(C_BORDER_STYLE.BS_DASHDED, floatArray.ToArray());
+                    }
+                }
+
+                CTextAttribute textAttribute = new CTextAttribute();
+                textAttribute.FontColor = currentParam.FontColor;
+                textAttribute.FontSize = (float)currentParam.FontSize;
+                textAttribute.FontName = CFontNameHelper.ObtainFontName(CFontNameHelper.GetFontType(currentParam.FontName),
+                            currentParam.IsBold,
+                            currentParam.IsItalic);
+                polygonAnnot.SetTextAttribute(textAttribute);
+                if (currentParam.measureInfo != null)
+                {
+                    CPDFPerimeterMeasure polygonMeasure = polygonAnnot.GetPerimeterMeasure();
+                    if (polygonMeasure != null)
+                    {
+                        polygonMeasure.SetMeasureInfo(currentParam.measureInfo);
+                        polygonMeasure.SetMeasureScale(currentParam.measureInfo.RulerBase, currentParam.measureInfo.RulerBaseUnit,
+                                                       currentParam.measureInfo.RulerTranslate, currentParam.measureInfo.RulerTranslateUnit);
+                        polygonMeasure.UpdateAnnotMeasure();
+                    }
+                }
+                if (!string.IsNullOrEmpty(currentParam.Author))
+                {
+                    polygonAnnot.SetAuthor(currentParam.Author);
+                }
+
+                if (!string.IsNullOrEmpty(currentParam.Content))
+                {
+                    polygonAnnot.SetContent(currentParam.Content);
+                }
+                polygonAnnot.SetIsLocked(currentParam.Locked);
+                polygonAnnot.SetCreationDate(PDFHelp.GetCurrentPdfTime());
+                polygonAnnot.UpdateAp();
+                polygonAnnot.ReleaseAnnot();
+
+                if (currentParam != null)
+                {
+                    currentParam.AnnotIndex = annotIndex;
+                }
+                if (PreviousParam != null)
+                {
+                    PreviousParam.AnnotIndex = annotIndex;
+                }
+                return true;
+            }
+            return false;
+        }
+        internal override bool Update(bool isUndo)
+        {
+            if (CurrentParam as PolyLineMeasureParam == null || PreviousParam as PolyLineMeasureParam == null)
+            {
+                return false;
+            }
+            if (MakeAnnotValid(CurrentParam))
+            {
+                CPDFPolylineAnnotation polygonAnnot = Annot as CPDFPolylineAnnotation;
+                if (polygonAnnot == null || !polygonAnnot.IsValid() || !polygonAnnot.IsMersured())
+                {
+                    return false;
+                }
+
+                PolyLineMeasureParam updateParam = (isUndo ? PreviousParam : CurrentParam) as PolyLineMeasureParam;
+                PolyLineMeasureParam checkParam = (isUndo ? CurrentParam : PreviousParam) as PolyLineMeasureParam;
+                if (!CheckArrayEqual(updateParam.LineColor, checkParam.LineColor))
+                {
+                    if (updateParam.LineColor != null && updateParam.LineColor.Length == 3)
+                    {
+                        polygonAnnot.SetLineColor(updateParam.LineColor);
+                    }
+                }
+
+                if (updateParam.Transparency != checkParam.Transparency)
+                {
+                    polygonAnnot.SetTransparency((byte)updateParam.Transparency);
+                }
+
+                if (updateParam.LineWidth != checkParam.LineWidth)
+                {
+                    polygonAnnot.SetLineWidth((byte)updateParam.LineWidth);
+                }
+
+                if (!CheckArrayEqual(updateParam.LineDash, checkParam.LineDash))
+                {
+                    if (updateParam.LineDash.Length == 0)
+                    {
+                        polygonAnnot.SetBorderStyle(C_BORDER_STYLE.BS_SOLID, new float[0]);
+                    }
+                    else
+                    {
+                        List<float> floatArray = new List<float>();
+                        foreach (float num in updateParam.LineDash)
+                        {
+                            floatArray.Add(num);
+                        }
+                        polygonAnnot.SetBorderStyle(C_BORDER_STYLE.BS_DASHDED, floatArray.ToArray());
+                    }
+                }
+
+                if (!updateParam.SavePoints.SequenceEqual(checkParam.SavePoints))
+                {
+                    polygonAnnot.SetPoints(updateParam.SavePoints);
+                }
+
+                if (!updateParam.ClientRect.Equals(checkParam.ClientRect))
+                {
+                    polygonAnnot.SetRect(updateParam.ClientRect);
+                }
+
+
+                if (updateParam.Author != checkParam.Author)
+                {
+                    polygonAnnot.SetAuthor(updateParam.Author);
+                }
+
+                if (updateParam.Content != checkParam.Content)
+                {
+                    polygonAnnot.SetContent(updateParam.Content);
+                }
+
+                if (updateParam.Locked != checkParam.Locked)
+                {
+                    polygonAnnot.SetIsLocked(updateParam.Locked);
+                }
+
+                if (updateParam.measureInfo != checkParam.measureInfo)
+                {
+                    CPDFPerimeterMeasure polygonMeasure = polygonAnnot.GetPerimeterMeasure();
+                    if (polygonMeasure != null)
+                    {
+                        polygonMeasure.SetMeasureInfo(updateParam.measureInfo);
+                        polygonMeasure.SetMeasureScale(updateParam.measureInfo.RulerBase, updateParam.measureInfo.RulerBaseUnit,
+                                                       updateParam.measureInfo.RulerTranslate, updateParam.measureInfo.RulerTranslateUnit);
+                        polygonMeasure.UpdateAnnotMeasure();
+                    }
+                }
+                polygonAnnot.SetModifyDate(PDFHelp.GetCurrentPdfTime());
+
+                polygonAnnot.UpdateAp();
+
+                return true;
+            }
+            return false;
+        }
+
+        internal override bool Remove()
+        {
+            if (MakeAnnotValid(CurrentParam))
+            {
+                Annot.RemoveAnnot();
+                return true;
+            }
+            return false;
+        }
+
+    }
+}

+ 261 - 0
Demo/Examples/ComPDFKit.Tool/UndoManger/AnnotHistory/PolygonMeasureAnnotHistory.cs

@@ -0,0 +1,261 @@
+using ComPDFKit.Measure;
+using ComPDFKit.PDFAnnotation;
+using ComPDFKit.PDFPage;
+using ComPDFKit.Tool.Help;
+using ComPDFKitViewer.Annot;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using static ComPDFKit.PDFAnnotation.CTextAttribute;
+
+namespace ComPDFKit.Tool.UndoManger
+{
+    internal class PolygonMeasureAnnotHistory : AnnotHistory
+    {
+        public override int GetAnnotIndex()
+        {
+            if (CurrentParam != null)
+            {
+                return CurrentParam.AnnotIndex;
+            }
+            return base.GetAnnotIndex();
+        }
+
+        public override int GetPageIndex()
+        {
+            if (CurrentParam != null)
+            {
+                return CurrentParam.PageIndex;
+            }
+            return base.GetPageIndex();
+        }
+        public override void SetAnnotIndex(int newIndex)
+        {
+            if (CurrentParam != null)
+            {
+                CurrentParam.AnnotIndex = newIndex;
+            }
+
+            if (PreviousParam != null)
+            {
+                PreviousParam.AnnotIndex = newIndex;
+            }
+        }
+        internal override bool Add()
+        {
+            PolygonMeasureParam currentParam = CurrentParam as PolygonMeasureParam;
+            if (CurrentParam == null || PDFDoc == null || !PDFDoc.IsValid())
+            {
+                return false;
+            }
+
+            CPDFPage pdfPage = PDFDoc.PageAtIndex(currentParam.PageIndex);
+            CPDFPolygonAnnotation polygonAnnot = pdfPage?.CreateAnnot(C_ANNOTATION_TYPE.C_ANNOTATION_POLYGON) as CPDFPolygonAnnotation;
+            if (polygonAnnot != null)
+            {
+                int annotIndex = pdfPage.GetAnnotCount() - 1;
+
+                if (currentParam.HasFillColor)
+                {
+                    if (currentParam.FillColor != null && currentParam.FillColor.Length == 3)
+                    {
+                        polygonAnnot.SetBgColor(currentParam.FillColor);
+                    }
+                }
+
+                if (currentParam.LineColor != null && currentParam.LineColor.Length == 3)
+                {
+                    polygonAnnot.SetLineColor(currentParam.LineColor);
+                }
+
+                polygonAnnot.SetTransparency((byte)currentParam.Transparency);
+                polygonAnnot.SetLineWidth(currentParam.LineWidth);
+
+                polygonAnnot.SetPoints(currentParam.SavePoints);
+                polygonAnnot.SetRect(currentParam.ClientRect);
+
+                if (currentParam.LineDash != null)
+                {
+                    if (currentParam.LineDash.Length == 0)
+                    {
+                        polygonAnnot.SetBorderStyle(C_BORDER_STYLE.BS_SOLID, new float[0]);
+                    }
+                    else
+                    {
+                        List<float> floatArray = new List<float>();
+                        foreach (float num in currentParam.LineDash)
+                        {
+                            floatArray.Add(num);
+                        }
+                        polygonAnnot.SetBorderStyle(C_BORDER_STYLE.BS_DASHDED, floatArray.ToArray());
+                    }
+                }
+
+                CTextAttribute textAttribute = new CTextAttribute();
+                textAttribute.FontColor = currentParam.FontColor;
+                textAttribute.FontSize = (float)currentParam.FontSize;
+                textAttribute.FontName = CFontNameHelper.ObtainFontName(CFontNameHelper.GetFontType(currentParam.FontName),
+                            currentParam.IsBold,
+                            currentParam.IsItalic);
+                polygonAnnot.SetTextAttribute(textAttribute);
+                if (currentParam.measureInfo != null)
+                {
+                    CPDFAreaMeasure polygonMeasure = polygonAnnot.GetAreaMeasure();
+                    if (polygonMeasure != null)
+                    {
+                        polygonMeasure.SetMeasureInfo(currentParam.measureInfo);
+                        polygonMeasure.SetMeasureScale(currentParam.measureInfo.RulerBase, currentParam.measureInfo.RulerBaseUnit,
+                                                       currentParam.measureInfo.RulerTranslate, currentParam.measureInfo.RulerTranslateUnit);
+                        polygonMeasure.UpdateAnnotMeasure();
+                    }
+                }
+                if (!string.IsNullOrEmpty(currentParam.Author))
+                {
+                    polygonAnnot.SetAuthor(currentParam.Author);
+                }
+
+                if (!string.IsNullOrEmpty(currentParam.Content))
+                {
+                    polygonAnnot.SetContent(currentParam.Content);
+                }
+                polygonAnnot.SetIsLocked(currentParam.Locked);
+                polygonAnnot.SetCreationDate(PDFHelp.GetCurrentPdfTime());
+                polygonAnnot.UpdateAp();
+                polygonAnnot.ReleaseAnnot();
+
+                if (currentParam != null)
+                {
+                    currentParam.AnnotIndex = annotIndex;
+                }
+                if (PreviousParam != null)
+                {
+                    PreviousParam.AnnotIndex = annotIndex;
+                }
+                return true;
+            }
+            return false;
+        }
+        internal override bool Update(bool isUndo)
+        {
+            if (CurrentParam as PolygonMeasureParam == null || PreviousParam as PolygonMeasureParam == null)
+            {
+                return false;
+            }
+            if (MakeAnnotValid(CurrentParam))
+            {
+                CPDFPolygonAnnotation polygonAnnot = Annot as CPDFPolygonAnnotation;
+                if (polygonAnnot == null || !polygonAnnot.IsValid() || !polygonAnnot.IsMersured())
+                {
+                    return false;
+                }
+
+                PolygonMeasureParam updateParam = (isUndo ? PreviousParam : CurrentParam) as PolygonMeasureParam;
+                PolygonMeasureParam checkParam = (isUndo ? CurrentParam : PreviousParam) as PolygonMeasureParam;
+                if (!CheckArrayEqual(updateParam.LineColor, checkParam.LineColor))
+                {
+                    if (updateParam.LineColor != null && updateParam.LineColor.Length == 3)
+                    {
+                        polygonAnnot.SetLineColor(updateParam.LineColor);
+                    }
+                }
+
+                if (!CheckArrayEqual(updateParam.FillColor, checkParam.FillColor))
+                {
+                    if (updateParam.HasFillColor)
+                    {
+                        if (updateParam.FillColor != null && updateParam.FillColor.Length == 3)
+                        {
+                            polygonAnnot.SetBgColor(updateParam.FillColor);
+                        }
+                    }
+                    else
+                    {
+                        polygonAnnot.ClearBgColor();
+                    }
+                }
+
+                if (updateParam.Transparency != checkParam.Transparency)
+                {
+                    polygonAnnot.SetTransparency((byte)updateParam.Transparency);
+                }
+
+                if (updateParam.LineWidth != checkParam.LineWidth)
+                {
+                    polygonAnnot.SetLineWidth((byte)updateParam.LineWidth);
+                }
+
+                if (!CheckArrayEqual(updateParam.LineDash, checkParam.LineDash))
+                {
+                    if (updateParam.LineDash.Length == 0)
+                    {
+                        polygonAnnot.SetBorderStyle(C_BORDER_STYLE.BS_SOLID, new float[0]);
+                    }
+                    else
+                    {
+                        List<float> floatArray = new List<float>();
+                        foreach (float num in updateParam.LineDash)
+                        {
+                            floatArray.Add(num);
+                        }
+                        polygonAnnot.SetBorderStyle(C_BORDER_STYLE.BS_DASHDED, floatArray.ToArray());
+                    }
+                }
+
+                if (!updateParam.SavePoints.SequenceEqual(checkParam.SavePoints))
+                {
+                    polygonAnnot.SetPoints(updateParam.SavePoints);
+                }
+
+                if (!updateParam.ClientRect.Equals(checkParam.ClientRect))
+                {
+                    polygonAnnot.SetRect(updateParam.ClientRect);
+                }
+
+
+                if (updateParam.Author != checkParam.Author)
+                {
+                    polygonAnnot.SetAuthor(updateParam.Author);
+                }
+
+                if (updateParam.Content != checkParam.Content)
+                {
+                    polygonAnnot.SetContent(updateParam.Content);
+                }
+
+                if (updateParam.Locked != checkParam.Locked)
+                {
+                    polygonAnnot.SetIsLocked(updateParam.Locked);
+                }
+                if (updateParam.measureInfo != checkParam.measureInfo)
+                {
+                    CPDFAreaMeasure polygonMeasure = polygonAnnot.GetAreaMeasure();
+                    if (polygonMeasure != null)
+                    {
+                        polygonMeasure.SetMeasureInfo(updateParam.measureInfo);
+                        polygonMeasure.SetMeasureScale(updateParam.measureInfo.RulerBase, updateParam.measureInfo.RulerBaseUnit,
+                                                       updateParam.measureInfo.RulerTranslate, updateParam.measureInfo.RulerTranslateUnit);
+                        polygonMeasure.UpdateAnnotMeasure();
+                    }
+                }
+                polygonAnnot.SetModifyDate(PDFHelp.GetCurrentPdfTime());
+
+                polygonAnnot.UpdateAp();
+
+                return true;
+            }
+            return false;
+        }
+
+        internal override bool Remove()
+        {
+            if (MakeAnnotValid(CurrentParam))
+            {
+                Annot.RemoveAnnot();
+                return true;
+            }
+            return false;
+        }
+    }
+}

+ 248 - 0
Demo/Examples/ComPDFKit.Tool/UndoManger/AnnotHistory/RedactAnnotHistory.cs

@@ -0,0 +1,248 @@
+using ComPDFKit.PDFAnnotation;
+using ComPDFKit.PDFPage;
+using ComPDFKit.Tool.Help;
+using static ComPDFKit.PDFAnnotation.CTextAttribute.CFontNameHelper;
+
+namespace ComPDFKit.Tool.UndoManger
+{
+    public class RedactAnnotHistory:AnnotHistory
+	{
+        public override int GetAnnotIndex()
+        {
+            if (CurrentParam != null)
+            {
+                return CurrentParam.AnnotIndex;
+            }
+            return base.GetAnnotIndex();
+        }
+
+        public override int GetPageIndex()
+        {
+            if (CurrentParam != null)
+            {
+                return CurrentParam.PageIndex;
+            }
+            return base.GetPageIndex();
+        }
+
+        public override void SetAnnotIndex(int newIndex)
+        {
+            if (CurrentParam != null)
+            {
+                CurrentParam.AnnotIndex = newIndex;
+            }
+
+            if (PreviousParam != null)
+            {
+                PreviousParam.AnnotIndex = newIndex;
+            }
+        }
+
+        internal override bool Add()
+        {
+            RedactParam currentParam = CurrentParam as RedactParam;
+            if (currentParam == null || PDFDoc == null || !PDFDoc.IsValid())
+			{
+				return false;
+			}
+
+			CPDFPage pdfPage = PDFDoc.PageAtIndex(currentParam.PageIndex);
+			CPDFRedactAnnotation redactAnnot = pdfPage?.CreateAnnot(C_ANNOTATION_TYPE.C_ANNOTATION_REDACT) as CPDFRedactAnnotation;
+			if (redactAnnot != null)
+			{
+				int annotIndex = pdfPage.GetAnnotCount() - 1;
+
+                if (currentParam.LineColor != null && currentParam.LineColor.Length == 3)
+                {
+                    redactAnnot.SetOutlineColor(currentParam.LineColor);
+                }
+
+                if (currentParam.BgColor != null && currentParam.BgColor.Length == 3)
+                {
+                    redactAnnot.SetFillColor(currentParam.BgColor);
+                }
+
+                redactAnnot.SetTextAlignment(currentParam.Alignment);
+
+				CTextAttribute textAttr = new CTextAttribute();
+                byte[] fontColor = new byte[3];
+
+                if (currentParam.FontColor != null && currentParam.FontColor.Length == 3)
+                {
+                    fontColor = currentParam.FontColor;
+                }
+                textAttr.FontColor = fontColor;
+				textAttr.FontSize = (float)currentParam.FontSize;
+				textAttr.FontName = ObtainFontName(
+					GetFontType(currentParam.FontName),
+					false,
+					false);
+
+				redactAnnot.SetTextAttribute(textAttr);
+				
+				if(currentParam.QuardRects != null)
+				{
+                    redactAnnot.SetQuardRects(currentParam.QuardRects);
+                }
+				
+				redactAnnot.SetRect(currentParam.ClientRect);
+
+				redactAnnot.SetBorderWidth(1);
+				redactAnnot.SetTransparency((byte)currentParam.Transparency);
+
+				if(!string.IsNullOrEmpty(currentParam.OverlayText))
+				{
+					redactAnnot.SetOverlayText(currentParam.OverlayText);
+				}
+
+				if (!string.IsNullOrEmpty(currentParam.Author))
+				{
+					redactAnnot.SetAuthor(currentParam.Author);
+				}
+
+				if (!string.IsNullOrEmpty(currentParam.Content))
+				{
+					redactAnnot.SetContent(currentParam.Content);
+				}
+
+				redactAnnot.SetIsLocked(currentParam.Locked);
+				redactAnnot.SetCreationDate(PDFHelp.GetCurrentPdfTime());
+				redactAnnot.UpdateAp();
+				redactAnnot.ReleaseAnnot();
+
+				if (CurrentParam != null)
+                {
+                    currentParam.AnnotIndex = annotIndex;
+				}
+				if (PreviousParam != null)
+				{
+					PreviousParam.AnnotIndex = annotIndex;
+				}
+
+				return true;
+			}
+
+			return false;
+		}
+
+		internal override bool Update(bool isUndo)
+		{
+			if (CurrentParam as RedactParam == null || PreviousParam as RedactParam == null)
+			{
+				return false;
+			}
+
+			if (MakeAnnotValid(CurrentParam))
+			{
+				CPDFRedactAnnotation redactAnnot = Annot as CPDFRedactAnnotation;
+				if (redactAnnot == null || !redactAnnot.IsValid())
+				{
+					return false;
+				}
+
+				RedactParam updateParam = (isUndo ? PreviousParam : CurrentParam) as RedactParam;
+				RedactParam checkParam = (isUndo ? CurrentParam : PreviousParam) as RedactParam;
+
+                if (!CheckArrayEqual(updateParam.LineColor, checkParam.LineColor))
+                {
+                    if (updateParam.LineColor != null && updateParam.LineColor.Length == 3)
+                    {
+                        redactAnnot.SetOutlineColor(updateParam.LineColor);
+                    }
+                }
+
+                if (!CheckArrayEqual(updateParam.BgColor, checkParam.BgColor))
+                {
+                    if (updateParam.BgColor != null && updateParam.BgColor.Length == 3)
+                    {
+                        redactAnnot.SetFillColor(updateParam.BgColor);
+                    }
+                }
+
+                if (updateParam.Alignment != checkParam.Alignment)
+				{
+                    redactAnnot.SetTextAlignment(updateParam.Alignment);
+                }
+
+                if (updateParam.FontName != checkParam.FontName)
+				{
+					CTextAttribute textAttr = redactAnnot.GetTextAttribute();
+					bool isBold = IsBold(textAttr.FontName);
+					bool isItalic = IsItalic(textAttr.FontName);
+					FontType fontType = GetFontType(updateParam.FontName);
+					textAttr.FontName = ObtainFontName(fontType, isBold, isItalic);
+					redactAnnot.SetTextAttribute(textAttr);
+				}
+
+				if (updateParam.FontSize != checkParam.FontSize)
+				{
+					CTextAttribute textAttr = redactAnnot.GetTextAttribute();
+					textAttr.FontSize = (float)updateParam.FontSize;
+					redactAnnot.SetTextAttribute(textAttr);
+				}
+
+                if (!CheckArrayEqual(updateParam.FontColor, checkParam.FontColor))
+                {
+                    if (updateParam.FontColor != null && updateParam.FontColor.Length == 3)
+                    {
+                        CTextAttribute textAttr = redactAnnot.GetTextAttribute();
+                        textAttr.FontColor = updateParam.FontColor;
+                        redactAnnot.SetTextAttribute(textAttr);
+                    }
+                }
+
+                if (updateParam.QuardRects != null)
+				{
+					redactAnnot.SetQuardRects(updateParam.QuardRects);
+				}
+
+				if (!updateParam.ClientRect.Equals(checkParam.ClientRect))
+                {
+                    redactAnnot.SetRect(updateParam.ClientRect);
+                }
+
+				if (updateParam.Transparency != checkParam.Transparency)
+				{
+					redactAnnot.SetTransparency((byte)updateParam.Transparency);
+				}
+
+				if(updateParam.OverlayText != checkParam.OverlayText)
+				{
+					redactAnnot.SetOverlayText(updateParam.OverlayText);
+				}
+
+				if (updateParam.Author != checkParam.Author)
+				{
+					redactAnnot.SetAuthor(updateParam.Author);
+				}
+
+				if (updateParam.Content != checkParam.Content)
+				{
+					redactAnnot.SetContent(updateParam.Content);
+				}
+
+				if (updateParam.Locked != checkParam.Locked)
+				{
+					redactAnnot.SetIsLocked(updateParam.Locked);
+				}
+
+				redactAnnot.SetModifyDate(PDFHelp.GetCurrentPdfTime());
+				redactAnnot.UpdateAp();
+
+				return true;
+			}
+
+			return false;
+		}
+
+		internal override bool Remove()
+		{
+			if (MakeAnnotValid(CurrentParam))
+			{
+				Annot.RemoveAnnot();
+				return true;
+			}
+			return false;
+		}
+	}
+}

+ 55 - 0
Demo/Examples/ComPDFKit.Tool/UndoManger/AnnotHistory/ReferenceObject.cs

@@ -0,0 +1,55 @@
+using System;
+
+namespace ComPDFKit.Tool.UndoManger
+{
+	public class ReferenceObject<T>
+    {
+        public class ReferenceChange<T>
+        {
+            public T oldValue { get; set; }
+            public T newValue { get; set; }
+        }
+
+        private static event EventHandler<ReferenceChange<T>> Changed;
+        
+        public T Data { get;private set; }
+
+        public ReferenceObject()
+        {
+            Changed += ReferenceObject_Changed;
+        }
+
+         ~ReferenceObject()
+        {
+            Changed -= ReferenceObject_Changed;
+        }
+        private void ReferenceObject_Changed(object sender, ReferenceChange<T> newData)
+        {
+            if(Data==null && newData.oldValue==null)
+            {
+                Data = newData.newValue;
+                return;
+            }
+            if (Data.Equals(newData.oldValue))
+            {
+                Data = newData.newValue;
+            }
+        }
+
+        public void Initialize(T newData)
+        {
+            Data = newData;
+        }
+
+        public void Update(T newData)
+        {
+            ReferenceChange<T> changeData = new ReferenceChange<T>()
+            {
+                oldValue = Data,
+                newValue = newData
+            };
+
+            Changed?.Invoke(this, changeData);
+        }
+    }
+}

+ 170 - 0
Demo/Examples/ComPDFKit.Tool/UndoManger/AnnotHistory/SoundAnnotHistory.cs

@@ -0,0 +1,170 @@
+using ComPDFKit.PDFAnnotation;
+using ComPDFKit.PDFPage;
+using ComPDFKit.Tool.Help;
+using ComPDFKit.Tool.SettingParam;
+using ComPDFKitViewer.Annot;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Media.Imaging;
+using System.Windows.Media;
+
+namespace ComPDFKit.Tool.UndoManger
+{
+    public class SoundAnnotHistory : AnnotHistory
+    {
+        public override int GetAnnotIndex()
+        {
+            if (CurrentParam != null)
+            {
+                return CurrentParam.AnnotIndex;
+            }
+            return base.GetAnnotIndex();
+        }
+
+        public override int GetPageIndex()
+        {
+            if (CurrentParam != null)
+            {
+                return CurrentParam.PageIndex;
+            }
+            return base.GetPageIndex();
+        }
+
+        public override void SetAnnotIndex(int newIndex)
+        {
+            if (CurrentParam != null)
+            {
+                CurrentParam.AnnotIndex = newIndex;
+            }
+
+            if (PreviousParam != null)
+            {
+                PreviousParam.AnnotIndex = newIndex;
+            }
+        }
+
+        internal override bool Add()
+        {
+            SoundParam currentParam = CurrentParam as SoundParam;
+
+            if (currentParam == null || PDFDoc == null || !PDFDoc.IsValid())
+            {
+                return false;
+            }
+
+            CPDFPage pdfPage = PDFDoc.PageAtIndex(currentParam.PageIndex);
+            CPDFSoundAnnotation soundAnnot = pdfPage?.CreateAnnot(C_ANNOTATION_TYPE.C_ANNOTATION_SOUND) as CPDFSoundAnnotation;
+            if (soundAnnot != null)
+            {
+                int annotIndex = pdfPage.GetAnnotCount() - 1;
+                soundAnnot.SetTransparency((byte)currentParam.Transparency);
+                soundAnnot.SetRect(currentParam.ClientRect);
+
+                byte[] imageData = null;
+                int imageWidth = 0;
+                int imageHeight = 0;
+                PDFHelp.ImageStreamToByte(currentParam.ImageStream, ref imageData, ref imageWidth, ref imageHeight);
+                if (imageData != null && imageWidth > 0 && imageHeight > 0)
+                {
+                    if (!string.IsNullOrEmpty(currentParam.SoundFilePath))
+                    {
+                        soundAnnot.SetSoundPath(imageData, imageWidth, imageHeight, currentParam.SoundFilePath);
+                    }
+                }
+
+                if (!string.IsNullOrEmpty(currentParam.Author))
+                {
+                    soundAnnot.SetAuthor(currentParam.Author);
+                }
+
+                if (!string.IsNullOrEmpty(currentParam.Content))
+                {
+                    soundAnnot.SetContent(currentParam.Content);
+                }
+                soundAnnot.SetIsLocked(currentParam.Locked);
+                soundAnnot.SetCreationDate(PDFHelp.GetCurrentPdfTime());
+                soundAnnot.UpdateAp();
+                soundAnnot.ReleaseAnnot();
+
+                if (currentParam != null)
+                {
+                    currentParam.AnnotIndex = annotIndex;
+                }
+                if (PreviousParam != null)
+                {
+                    PreviousParam.AnnotIndex = annotIndex;
+                }
+
+                return true;
+            }
+
+            return false;
+        }
+
+        internal override bool Update(bool isUndo)
+        {
+            if (CurrentParam as SoundParam == null || PreviousParam as SoundParam == null)
+            {
+                return false;
+            }
+
+            if (MakeAnnotValid(CurrentParam))
+            {
+                CPDFSoundAnnotation circleAnnot = Annot as CPDFSoundAnnotation;
+                if (circleAnnot == null || !circleAnnot.IsValid())
+                {
+                    return false;
+                }
+
+                SoundParam updateParam = (isUndo ? PreviousParam : CurrentParam) as SoundParam;
+                SoundParam checkParam = (isUndo ? CurrentParam : PreviousParam) as SoundParam;
+
+                if (updateParam.Transparency != checkParam.Transparency)
+                {
+                    circleAnnot.SetTransparency((byte)updateParam.Transparency);
+                }
+
+                if (!updateParam.ClientRect.Equals(checkParam.ClientRect))
+                {
+                    circleAnnot.SetRect(updateParam.ClientRect);
+                }
+
+                if (updateParam.Author != checkParam.Author)
+                {
+                    circleAnnot.SetAuthor(updateParam.Author);
+                }
+
+                if (updateParam.Content != checkParam.Content)
+                {
+                    circleAnnot.SetContent(updateParam.Content);
+                }
+
+                if (updateParam.Locked != checkParam.Locked)
+                {
+                    circleAnnot.SetIsLocked(updateParam.Locked);
+                }
+
+                circleAnnot.SetModifyDate(PDFHelp.GetCurrentPdfTime());
+                //circleAnnot.UpdateAp();
+
+                return true;
+            }
+
+            return false;
+        }
+
+        internal override bool Remove()
+        {
+            if (MakeAnnotValid(CurrentParam))
+            {
+                Annot.RemoveAnnot();
+                return true;
+            }
+            return false;
+        }
+    }
+}

+ 219 - 0
Demo/Examples/ComPDFKit.Tool/UndoManger/AnnotHistory/SquareAnnotHistory.cs

@@ -0,0 +1,219 @@
+using ComPDFKit.PDFAnnotation;
+using ComPDFKit.PDFPage;
+using ComPDFKit.Tool.Help;
+using ComPDFKitViewer.Annot;
+using System.Collections.Generic;
+
+namespace ComPDFKit.Tool.UndoManger
+{
+    public class SquareAnnotHistory:AnnotHistory
+    {
+        public override int GetAnnotIndex()
+        {
+            if (CurrentParam != null)
+            {
+                return CurrentParam.AnnotIndex;
+            }
+            return base.GetAnnotIndex();
+        }
+
+        public override int GetPageIndex()
+        {
+            if (CurrentParam != null)
+            {
+                return CurrentParam.PageIndex;
+            }
+            return base.GetPageIndex();
+        }
+
+        public override void SetAnnotIndex(int newIndex)
+        {
+            if (CurrentParam != null)
+            {
+                CurrentParam.AnnotIndex = newIndex;
+            }
+
+            if (PreviousParam != null)
+            {
+                PreviousParam.AnnotIndex = newIndex;
+            }
+        }
+
+        internal override bool Add()
+        {
+            SquareParam currentParam = CurrentParam as SquareParam;
+            if (currentParam == null || PDFDoc == null || !PDFDoc.IsValid())
+            {
+                return false;
+            }
+
+            CPDFPage pdfPage = PDFDoc.PageAtIndex(currentParam.PageIndex);
+            CPDFSquareAnnotation squareAnnot = pdfPage?.CreateAnnot(C_ANNOTATION_TYPE.C_ANNOTATION_SQUARE) as CPDFSquareAnnotation;
+            if (squareAnnot != null)
+            {
+                int annotIndex = pdfPage.GetAnnotCount() - 1;
+                if (currentParam.LineColor != null && currentParam.LineColor.Length == 3)
+                {
+                    squareAnnot.SetLineColor(currentParam.LineColor);
+                }
+
+                if (currentParam.HasBgColor)
+                {
+                    if (currentParam.BgColor != null && currentParam.BgColor.Length == 3)
+                    {
+                        squareAnnot.SetBgColor(currentParam.BgColor);
+                    }
+                }
+
+                squareAnnot.SetTransparency((byte)currentParam.Transparency);
+                squareAnnot.SetLineWidth((byte)currentParam.LineWidth);
+                squareAnnot.SetRect(currentParam.ClientRect);
+
+                List<float> floatArray = new List<float>();
+                if (currentParam.LineDash != null)
+                {
+                    foreach (float num in currentParam.LineDash)
+                    {
+                        floatArray.Add(num);
+                    }
+                }
+                squareAnnot.SetBorderStyle(currentParam.BorderStyle, floatArray.ToArray());
+
+                if (!string.IsNullOrEmpty(currentParam.Author))
+                {
+                    squareAnnot.SetAuthor(currentParam.Author);
+                }
+
+                if (!string.IsNullOrEmpty(currentParam.Content))
+                {
+                    squareAnnot.SetContent(currentParam.Content);
+                }
+                squareAnnot.SetIsLocked(currentParam.Locked);
+                squareAnnot.SetCreationDate(PDFHelp.GetCurrentPdfTime());
+                squareAnnot.UpdateAp();
+                squareAnnot.ReleaseAnnot();
+
+                if (currentParam != null)
+                {
+                    currentParam.AnnotIndex = annotIndex;
+                }
+                if (PreviousParam != null)
+                {
+                    PreviousParam.AnnotIndex = annotIndex;
+                }
+
+                return true;
+            }
+
+            return false;
+        }
+
+        internal override bool Update(bool isUndo)
+        {
+            if (CurrentParam as SquareParam == null || PreviousParam as SquareParam == null)
+            {
+                return false;
+            }
+
+            if (MakeAnnotValid(CurrentParam))
+            {
+                CPDFSquareAnnotation squareAnnot = Annot as CPDFSquareAnnotation;
+                if (squareAnnot == null || !squareAnnot.IsValid())
+                {
+                    return false;
+                }
+
+                SquareParam updateParam = (isUndo ? PreviousParam : CurrentParam) as SquareParam;
+                SquareParam checkParam = (isUndo ? CurrentParam : PreviousParam) as SquareParam;
+
+                if (!CheckArrayEqual(updateParam.LineColor, checkParam.LineColor))
+                {
+                    if (updateParam.LineColor != null && updateParam.LineColor.Length == 3)
+                    {
+                        squareAnnot.SetLineColor(updateParam.LineColor);
+                    }
+                }
+
+                if (!CheckArrayEqual(updateParam.BgColor, checkParam.BgColor))
+                {
+                    if (updateParam.HasBgColor)
+                    {
+                        if (updateParam.BgColor != null && updateParam.BgColor.Length == 3)
+                        {
+                            squareAnnot.SetBgColor(updateParam.BgColor);
+                        }
+                    }
+                    else
+                    {
+                        squareAnnot.ClearBgColor();
+                    }
+                }
+
+                if (updateParam.Transparency != checkParam.Transparency)
+                {
+                    squareAnnot.SetTransparency((byte)updateParam.Transparency);
+                }
+
+                if (updateParam.LineWidth != checkParam.LineWidth)
+                {
+                    squareAnnot.SetLineWidth((byte)updateParam.LineWidth);
+                }
+
+                if (!updateParam.ClientRect.Equals(checkParam.ClientRect))
+                {
+                    squareAnnot.SetRect(updateParam.ClientRect);
+                }
+
+                if (updateParam.LineDash != null)
+                {
+                    List<float> floatArray = new List<float>();
+                    if (updateParam.LineDash != null && updateParam.LineDash.Length > 0)
+                    {
+                        foreach (double num in updateParam.LineDash)
+                        {
+                            floatArray.Add((float)num);
+                        }
+                    }
+                    squareAnnot.SetBorderStyle(updateParam.BorderStyle, floatArray.ToArray());
+                }
+                else
+                {
+                    float[] dashArray = new float[0];
+                    squareAnnot.SetBorderStyle(updateParam.BorderStyle, dashArray);
+                }
+
+                if (updateParam.Author != checkParam.Author)
+                {
+                    squareAnnot.SetAuthor(updateParam.Author);
+                }
+
+                if (updateParam.Content != checkParam.Content)
+                {
+                    squareAnnot.SetContent(updateParam.Content);
+                }
+
+                if (updateParam.Locked != checkParam.Locked)
+                {
+                    squareAnnot.SetIsLocked(updateParam.Locked);
+                }
+
+                squareAnnot.SetModifyDate(PDFHelp.GetCurrentPdfTime());
+                squareAnnot.UpdateAp();
+
+                return true;
+            }
+
+            return false;
+        }
+
+        internal override bool Remove()
+        {
+            if(MakeAnnotValid(CurrentParam))
+            {
+                Annot.RemoveAnnot();
+                return true;
+            }
+            return false;
+        }
+    }
+}

+ 163 - 0
Demo/Examples/ComPDFKit.Tool/UndoManger/AnnotHistory/SquigglyAnnotHistory.cs

@@ -0,0 +1,163 @@
+using ComPDFKit.PDFAnnotation;
+using ComPDFKit.PDFPage;
+using ComPDFKit.Tool.Help;
+
+namespace ComPDFKit.Tool.UndoManger
+{
+    public class SquigglyAnnotHistory:AnnotHistory
+	{
+        public override int GetAnnotIndex()
+        {
+            if (CurrentParam != null)
+            {
+                return CurrentParam.AnnotIndex;
+            }
+            return base.GetAnnotIndex();
+        }
+
+        public override int GetPageIndex()
+        {
+            if (CurrentParam != null)
+            {
+                return CurrentParam.PageIndex;
+            }
+            return base.GetPageIndex();
+        }
+
+        public override void SetAnnotIndex(int newIndex)
+        {
+            if (CurrentParam != null)
+            {
+                CurrentParam.AnnotIndex = newIndex;
+            }
+
+            if (PreviousParam != null)
+            {
+                PreviousParam.AnnotIndex = newIndex;
+            }
+        }
+
+        internal override bool Add()
+		{
+            SquigglyParam currentParam = CurrentParam as SquigglyParam;
+
+
+            if (currentParam == null || PDFDoc == null || !PDFDoc.IsValid())
+			{
+				return false;
+			}
+			CPDFPage pdfPage = PDFDoc.PageAtIndex(currentParam.PageIndex);
+			CPDFSquigglyAnnotation squigglyAnnot = pdfPage?.CreateAnnot(C_ANNOTATION_TYPE.C_ANNOTATION_SQUIGGLY) as CPDFSquigglyAnnotation;
+			if (squigglyAnnot != null)
+			{
+				int annotIndex = pdfPage.GetAnnotCount() - 1;
+
+				squigglyAnnot.SetTransparency((byte)currentParam.Transparency);
+				squigglyAnnot.SetRect(currentParam.ClientRect);
+				
+				if (currentParam.QuardRects != null)
+				{
+                    squigglyAnnot.SetQuardRects(currentParam.QuardRects);
+                }
+
+                if (currentParam.SquigglyColor != null && currentParam.SquigglyColor.Length == 3)
+                {
+                    squigglyAnnot.SetColor(currentParam.SquigglyColor);
+                }
+
+
+                if (!string.IsNullOrEmpty(currentParam.Author))
+				{
+					squigglyAnnot.SetAuthor(currentParam.Author);
+				}
+
+				if (!string.IsNullOrEmpty(currentParam.Content))
+				{
+					squigglyAnnot.SetContent(currentParam.Content);
+				}
+				squigglyAnnot.SetIsLocked(currentParam.Locked);
+				squigglyAnnot.SetCreationDate(PDFHelp.GetCurrentPdfTime());
+				squigglyAnnot.UpdateAp();
+				squigglyAnnot.ReleaseAnnot();
+
+				if (currentParam != null)
+                {
+                    currentParam.AnnotIndex = annotIndex;
+				}
+				if (PreviousParam != null)
+				{
+					PreviousParam.AnnotIndex = annotIndex;
+				}
+
+				return true;
+			}
+
+			return false;
+		}
+
+		internal override bool Update(bool isUndo)
+		{
+			if (CurrentParam as SquigglyParam == null || PreviousParam as SquigglyParam == null)
+			{
+				return false;
+			}
+
+			if (MakeAnnotValid(CurrentParam))
+			{
+				CPDFSquigglyAnnotation squigglyAnnot = Annot as CPDFSquigglyAnnotation;
+				if (squigglyAnnot == null || !squigglyAnnot.IsValid())
+				{
+					return false;
+				}
+
+				SquigglyParam updateParam = (isUndo ? PreviousParam : CurrentParam) as SquigglyParam;
+				SquigglyParam checkParam = (isUndo ? CurrentParam : PreviousParam) as SquigglyParam;
+
+                if (!CheckArrayEqual(updateParam.SquigglyColor, checkParam.SquigglyColor))
+                {
+                    if (updateParam.SquigglyColor != null && updateParam.SquigglyColor.Length == 3)
+                    {
+                        squigglyAnnot.SetColor(updateParam.SquigglyColor);
+                    }
+                }
+
+                if (updateParam.Transparency != checkParam.Transparency)
+				{
+					squigglyAnnot.SetTransparency((byte)updateParam.Transparency);
+				}
+
+				if (updateParam.Author != checkParam.Author)
+				{
+					squigglyAnnot.SetAuthor(updateParam.Author);
+				}
+
+				if (updateParam.Content != checkParam.Content)
+				{
+					squigglyAnnot.SetContent(updateParam.Content);
+				}
+
+				if (updateParam.Locked != checkParam.Locked)
+				{
+					squigglyAnnot.SetIsLocked(updateParam.Locked);
+				}
+
+				squigglyAnnot.SetModifyDate(PDFHelp.GetCurrentPdfTime());
+				squigglyAnnot.UpdateAp();
+
+				return true;
+			}
+
+			return false;
+		}
+
+		internal override bool Remove()
+		{
+			if (MakeAnnotValid(CurrentParam))
+			{
+				Annot.RemoveAnnot();
+				return true;
+			}
+			return false;
+		}
+	}
+}

+ 217 - 0
Demo/Examples/ComPDFKit.Tool/UndoManger/AnnotHistory/StampAnnotHistory.cs

@@ -0,0 +1,217 @@
+using ComPDFKit.PDFAnnotation;
+using ComPDFKit.PDFPage;
+using ComPDFKit.Tool.Help;
+using System.IO;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+
+namespace ComPDFKit.Tool.UndoManger
+{
+    public class StampAnnotHistory:AnnotHistory
+	{
+        public override int GetAnnotIndex()
+        {
+            if (CurrentParam != null)
+            {
+                return CurrentParam.AnnotIndex;
+            }
+            return base.GetAnnotIndex();
+        }
+
+        public override int GetPageIndex()
+        {
+            if (CurrentParam != null)
+            {
+                return CurrentParam.PageIndex;
+            }
+            return base.GetPageIndex();
+        }
+
+        public override void SetAnnotIndex(int newIndex)
+        {
+            if (CurrentParam != null)
+            {
+                CurrentParam.AnnotIndex = newIndex;
+            }
+
+            if (PreviousParam != null)
+            {
+                PreviousParam.AnnotIndex = newIndex;
+            }
+        }
+
+        internal override bool Add()
+		{
+            StampParam currentParam = CurrentParam as StampParam;
+
+            if (currentParam == null || PDFDoc == null || !PDFDoc.IsValid())
+			{
+				return false;
+			}
+
+			if(currentParam.StampType==C_STAMP_TYPE.UNKNOWN_STAMP)
+			{
+				return false;
+			}
+
+			if(currentParam.StampType==C_STAMP_TYPE.IMAGE_STAMP && currentParam.ImageStream==null)
+			{
+				return false;
+			}
+
+			CPDFPage pdfPage = PDFDoc.PageAtIndex(currentParam.PageIndex);
+			CPDFStampAnnotation stampAnnot = pdfPage?.CreateAnnot(C_ANNOTATION_TYPE.C_ANNOTATION_STAMP) as CPDFStampAnnotation;
+           
+            if (stampAnnot != null)
+			{
+				int annotIndex = pdfPage.GetAnnotCount() - 1;
+				switch(currentParam.StampType)
+				{
+					case C_STAMP_TYPE.STANDARD_STAMP:
+						{
+							string stampText = currentParam.StampText;
+							if (stampText == null)
+							{
+								stampText = string.Empty;
+							}
+							stampAnnot.SetStandardStamp(stampText, pdfPage.Rotation);
+                            stampAnnot.SetRect(currentParam.ClientRect);
+                        }
+						break;
+					case C_STAMP_TYPE.TEXT_STAMP:
+						{
+							string dateText = currentParam.DateText;
+							string stampText = currentParam.StampText;
+							if (dateText == null)
+							{
+								dateText = string.Empty;
+							}
+							if (stampText == null)
+							{
+								stampText = string.Empty;
+							}
+							stampAnnot.SetTextStamp(
+								stampText,
+								dateText,
+								currentParam.TextStampShape,
+								currentParam.TextStampColor,
+								pdfPage.Rotation);
+                            stampAnnot.SetRect(currentParam.ClientRect);
+                        }
+						break;
+					case C_STAMP_TYPE.IMAGE_STAMP:
+						{
+                            byte[] imageData = null;
+                            int imageWidth = 0;
+                            int imageHeight = 0;
+                            PDFHelp.ImageStreamToByte(currentParam.ImageStream, ref imageData, ref imageWidth, ref imageHeight);
+							if (imageData != null && imageWidth > 0 && imageHeight > 0)
+							{
+                                stampAnnot.SetRect(currentParam.ClientRect);
+                                stampAnnot.SetImageStamp(
+                                    imageData,
+                                    imageWidth,
+                                    imageHeight,
+                                    pdfPage.Rotation);
+                            }
+						}
+						break;
+
+					default:
+						break;
+				}
+
+				stampAnnot.SetTransparency((byte)currentParam.Transparency);
+				
+
+				if (!string.IsNullOrEmpty(currentParam.Author))
+				{
+					stampAnnot.SetAuthor(currentParam.Author);
+				}
+
+				if (!string.IsNullOrEmpty(currentParam.Content))
+				{
+					stampAnnot.SetContent(currentParam.Content);
+				}
+				stampAnnot.SetIsLocked(currentParam.Locked);
+				stampAnnot.SetCreationDate(PDFHelp.GetCurrentPdfTime());
+				stampAnnot.UpdateAp();
+				stampAnnot.ReleaseAnnot();
+
+				if (currentParam != null)
+                {
+                    currentParam.AnnotIndex = annotIndex;
+				}
+				if (PreviousParam != null)
+				{
+					PreviousParam.AnnotIndex = annotIndex;
+				}
+
+				return true;
+			}
+
+			return false;
+		}
+
+		internal override bool Update(bool isUndo)
+		{
+			if (CurrentParam as StampParam == null || PreviousParam as StampParam == null)
+			{
+				return false;
+			}
+
+			if (MakeAnnotValid(CurrentParam))
+			{
+				CPDFStampAnnotation stampAnnot = Annot as CPDFStampAnnotation;
+				if (stampAnnot == null || !stampAnnot.IsValid())
+				{
+					return false;
+				}
+
+				StampParam updateParam = (isUndo ? PreviousParam : CurrentParam) as StampParam;
+				StampParam checkParam = (isUndo ? CurrentParam : PreviousParam) as StampParam;
+
+				if (updateParam.Transparency != checkParam.Transparency)
+				{
+					stampAnnot.SetTransparency((byte)updateParam.Transparency);
+				}
+
+				if (!updateParam.ClientRect.Equals(checkParam.ClientRect))
+                {
+                    stampAnnot.SetRect(updateParam.ClientRect);
+                }
+
+				if (updateParam.Author != checkParam.Author)
+				{
+					stampAnnot.SetAuthor(updateParam.Author);
+				}
+
+				if (updateParam.Content != checkParam.Content)
+				{
+					stampAnnot.SetContent(updateParam.Content);
+				}
+
+				if (updateParam.Locked != checkParam.Locked)
+				{
+					stampAnnot.SetIsLocked(updateParam.Locked);
+				}
+
+				stampAnnot.SetModifyDate(PDFHelp.GetCurrentPdfTime());
+				stampAnnot.UpdateAp();
+
+				return true;
+			}
+			return false;
+		}
+
+		internal override bool Remove()
+		{
+			if (MakeAnnotValid(CurrentParam))
+			{
+				Annot.RemoveAnnot();
+				return true;
+			}
+			return false;
+		}
+	}
+}

+ 161 - 0
Demo/Examples/ComPDFKit.Tool/UndoManger/AnnotHistory/StickyNoteAnnotHistory.cs

@@ -0,0 +1,161 @@
+using ComPDFKit.PDFAnnotation;
+using ComPDFKit.PDFPage;
+using ComPDFKit.Tool.Help;
+
+namespace ComPDFKit.Tool.UndoManger
+{
+    public class StickyNoteAnnotHistory:AnnotHistory
+    {
+        public override int GetAnnotIndex()
+        {
+            if (CurrentParam != null)
+            {
+                return CurrentParam.AnnotIndex;
+            }
+            return base.GetAnnotIndex();
+        }
+
+        public override int GetPageIndex()
+        {
+            if (CurrentParam != null)
+            {
+                return CurrentParam.PageIndex;
+            }
+            return base.GetPageIndex();
+        }
+
+        public override void SetAnnotIndex(int newIndex)
+        {
+            if (CurrentParam != null)
+            {
+                CurrentParam.AnnotIndex = newIndex;
+            }
+
+            if (PreviousParam != null)
+            {
+                PreviousParam.AnnotIndex = newIndex;
+            }
+        }
+
+        internal override bool Add()
+        {
+            StickyNoteParam currentParam = CurrentParam as StickyNoteParam;
+            if (currentParam == null || PDFDoc == null || !PDFDoc.IsValid())
+            {
+                return false;
+            }
+
+            CPDFPage pdfPage = PDFDoc.PageAtIndex(currentParam.PageIndex);
+            CPDFTextAnnotation stickynoteAnnot = pdfPage?.CreateAnnot(C_ANNOTATION_TYPE.C_ANNOTATION_TEXT) as CPDFTextAnnotation;
+            if(stickynoteAnnot != null)
+            {
+                int annotIndex = pdfPage.GetAnnotCount() - 1;
+              
+                if(currentParam.StickyNoteColor!=null && currentParam.StickyNoteColor.Length==3)
+                {
+                    stickynoteAnnot.SetColor(currentParam.StickyNoteColor);
+                }
+               
+                stickynoteAnnot.SetTransparency((byte)currentParam.Transparency);
+                stickynoteAnnot.SetRect(currentParam.ClientRect);
+
+                if (!string.IsNullOrEmpty(currentParam.Author))
+                {
+                    stickynoteAnnot.SetAuthor(currentParam.Author);
+                }
+
+                if (!string.IsNullOrEmpty(currentParam.Content))
+                {
+                    stickynoteAnnot.SetContent(currentParam.Content);
+                }
+                stickynoteAnnot.SetIsLocked(currentParam.Locked);
+                stickynoteAnnot.SetCreationDate(PDFHelp.GetCurrentPdfTime());
+                stickynoteAnnot.UpdateAp();
+                stickynoteAnnot.ReleaseAnnot();
+
+                if (currentParam != null)
+                {
+                    currentParam.AnnotIndex = annotIndex;
+                }
+                if (PreviousParam != null)
+                {
+                    PreviousParam.AnnotIndex = annotIndex;
+                }
+
+                return true;
+            }
+
+            return false;
+        }
+
+        internal override bool Update(bool isUndo)
+        {
+            if (CurrentParam as StickyNoteParam == null || PreviousParam as StickyNoteParam == null)
+            {
+                return false;
+            }
+
+            if (MakeAnnotValid(CurrentParam))
+            {
+                CPDFTextAnnotation stickynoteAnnot = Annot as CPDFTextAnnotation;
+                if (stickynoteAnnot == null || !stickynoteAnnot.IsValid())
+                {
+                    return false;
+                }
+
+                StickyNoteParam updateParam = (isUndo ? PreviousParam : CurrentParam) as StickyNoteParam;
+                StickyNoteParam checkParam = (isUndo ? CurrentParam : PreviousParam) as StickyNoteParam;
+
+                if (!CheckArrayEqual(updateParam.StickyNoteColor, checkParam.StickyNoteColor))
+                {
+                    if (updateParam.StickyNoteColor != null && updateParam.StickyNoteColor.Length == 3)
+                    {
+                        stickynoteAnnot.SetColor(updateParam.StickyNoteColor);
+                    }
+                }
+
+                if (updateParam.Transparency != checkParam.Transparency)
+                {
+                    stickynoteAnnot.SetTransparency((byte)updateParam.Transparency);
+                }
+
+                if (!updateParam.ClientRect.Equals(checkParam.ClientRect))
+                {
+                    stickynoteAnnot.SetRect(updateParam.ClientRect);
+                }
+
+                if (updateParam.Author != checkParam.Author)
+                {
+                    stickynoteAnnot.SetAuthor(updateParam.Author);
+                }
+
+                if (updateParam.Content != checkParam.Content)
+                {
+                    stickynoteAnnot.SetContent(updateParam.Content);
+                }
+
+                if (updateParam.Locked != checkParam.Locked)
+                {
+                    stickynoteAnnot.SetIsLocked(updateParam.Locked);
+                }
+
+                stickynoteAnnot.SetModifyDate(PDFHelp.GetCurrentPdfTime());
+                stickynoteAnnot.UpdateAp();
+
+                return true;
+            }
+
+            return false;
+        }
+
+        internal override bool Remove()
+        {
+            if (MakeAnnotValid(CurrentParam))
+            {
+                Annot.RemoveAnnot();
+                return true;
+            }
+            return false;
+        }
+    }
+}

+ 161 - 0
Demo/Examples/ComPDFKit.Tool/UndoManger/AnnotHistory/StrikeoutAnnotHistory.cs

@@ -0,0 +1,161 @@
+using ComPDFKit.PDFAnnotation;
+using ComPDFKit.PDFPage;
+using ComPDFKit.Tool.Help;
+
+namespace ComPDFKit.Tool.UndoManger
+{
+    public class StrikeoutAnnotHistory:AnnotHistory
+	{
+        public override int GetAnnotIndex()
+        {
+            if (CurrentParam != null)
+            {
+                return CurrentParam.AnnotIndex;
+            }
+            return base.GetAnnotIndex();
+        }
+
+        public override int GetPageIndex()
+        {
+            if (CurrentParam != null)
+            {
+                return CurrentParam.PageIndex;
+            }
+            return base.GetPageIndex();
+        }
+
+        public override void SetAnnotIndex(int newIndex)
+        {
+            if (CurrentParam != null)
+            {
+                CurrentParam.AnnotIndex = newIndex;
+            }
+
+            if (PreviousParam != null)
+            {
+                PreviousParam.AnnotIndex = newIndex;
+            }
+        }
+
+        internal override bool Add()
+		{
+            StrikeoutParam currentParam = CurrentParam as StrikeoutParam;
+
+            if (currentParam == null || PDFDoc == null || !PDFDoc.IsValid())
+			{
+				return false;
+			}
+			CPDFPage pdfPage = PDFDoc.PageAtIndex(currentParam.PageIndex);
+			CPDFStrikeoutAnnotation strikeoutAnnot = pdfPage?.CreateAnnot(C_ANNOTATION_TYPE.C_ANNOTATION_STRIKEOUT) as CPDFStrikeoutAnnotation;
+			if (strikeoutAnnot != null)
+			{
+				int annotIndex = pdfPage.GetAnnotCount() - 1;
+
+				strikeoutAnnot.SetTransparency((byte)currentParam.Transparency);
+				strikeoutAnnot.SetRect(currentParam.ClientRect);
+				
+				if (currentParam.QuardRects != null)
+				{
+                    strikeoutAnnot.SetQuardRects(currentParam.QuardRects);
+                }
+
+                if (currentParam.StrikeoutColor != null && currentParam.StrikeoutColor.Length == 3)
+                {
+                    strikeoutAnnot.SetColor(currentParam.StrikeoutColor);
+                }
+
+                if (!string.IsNullOrEmpty(currentParam.Author))
+				{
+					strikeoutAnnot.SetAuthor(currentParam.Author);
+				}
+
+				if (!string.IsNullOrEmpty(currentParam.Content))
+				{
+					strikeoutAnnot.SetContent(currentParam.Content);
+				}
+				strikeoutAnnot.SetIsLocked(currentParam.Locked);
+				strikeoutAnnot.SetCreationDate(PDFHelp.GetCurrentPdfTime());
+				strikeoutAnnot.UpdateAp();
+				strikeoutAnnot.ReleaseAnnot();
+
+				if (currentParam != null)
+                {
+                    currentParam.AnnotIndex = annotIndex;
+				}
+				if (PreviousParam != null)
+				{
+					PreviousParam.AnnotIndex = annotIndex;
+				}
+
+				return true;
+			}
+
+			return false;
+		}
+
+		internal override bool Update(bool isUndo)
+		{
+			if (CurrentParam as StrikeoutParam == null || PreviousParam as StrikeoutParam == null)
+			{
+				return false;
+			}
+
+			if (MakeAnnotValid(CurrentParam))
+			{
+				CPDFStrikeoutAnnotation strikeoutAnnot = Annot as CPDFStrikeoutAnnotation;
+				if (strikeoutAnnot == null || !strikeoutAnnot.IsValid())
+				{
+					return false;
+				}
+
+				StrikeoutParam updateParam = (isUndo ? PreviousParam : CurrentParam) as StrikeoutParam;
+				StrikeoutParam checkParam = (isUndo ? CurrentParam : PreviousParam) as StrikeoutParam;
+
+                if (!CheckArrayEqual(updateParam.StrikeoutColor, checkParam.StrikeoutColor))
+                {
+                    if (updateParam.StrikeoutColor != null && updateParam.StrikeoutColor.Length == 3)
+                    {
+                        strikeoutAnnot.SetColor(updateParam.StrikeoutColor);
+                    }
+                }
+
+                if (updateParam.Transparency != checkParam.Transparency)
+				{
+					strikeoutAnnot.SetTransparency((byte)updateParam.Transparency);
+				}
+
+				if (updateParam.Author != checkParam.Author)
+				{
+					strikeoutAnnot.SetAuthor(updateParam.Author);
+				}
+
+				if (updateParam.Content != checkParam.Content)
+				{
+					strikeoutAnnot.SetContent(updateParam.Content);
+				}
+
+				if (updateParam.Locked != checkParam.Locked)
+				{
+					strikeoutAnnot.SetIsLocked(updateParam.Locked);
+				}
+
+				strikeoutAnnot.SetModifyDate(PDFHelp.GetCurrentPdfTime());
+				strikeoutAnnot.UpdateAp();
+
+				return true;
+			}
+
+			return false;
+		}
+
+		internal override bool Remove()
+		{
+			if (MakeAnnotValid(CurrentParam))
+			{
+				Annot.RemoveAnnot();
+				return true;
+			}
+			return false;
+		}
+	}
+}

+ 161 - 0
Demo/Examples/ComPDFKit.Tool/UndoManger/AnnotHistory/UnderlineAnnotHistory.cs

@@ -0,0 +1,161 @@
+using ComPDFKit.PDFAnnotation;
+using ComPDFKit.PDFPage;
+using ComPDFKit.Tool.Help;
+
+namespace ComPDFKit.Tool.UndoManger
+{
+    public class UnderlineAnnotHistory:AnnotHistory
+	{
+        public override int GetAnnotIndex()
+        {
+            if (CurrentParam != null)
+            {
+                return CurrentParam.AnnotIndex;
+            }
+            return base.GetAnnotIndex();
+        }
+
+        public override int GetPageIndex()
+        {
+            if (CurrentParam != null)
+            {
+                return CurrentParam.PageIndex;
+            }
+            return base.GetPageIndex();
+        }
+
+        public override void SetAnnotIndex(int newIndex)
+        {
+            if (CurrentParam != null)
+            {
+                CurrentParam.AnnotIndex = newIndex;
+            }
+
+            if (PreviousParam != null)
+            {
+                PreviousParam.AnnotIndex = newIndex;
+            }
+        }
+
+        internal override bool Add()
+		{
+            UnderlineParam currentParam = CurrentParam as UnderlineParam;
+
+            if (currentParam == null || PDFDoc == null || !PDFDoc.IsValid())
+			{
+				return false;
+			}
+			CPDFPage pdfPage = PDFDoc.PageAtIndex(currentParam.PageIndex);
+			CPDFUnderlineAnnotation underlineAnnot = pdfPage?.CreateAnnot(C_ANNOTATION_TYPE.C_ANNOTATION_UNDERLINE) as CPDFUnderlineAnnotation;
+			if (underlineAnnot != null)
+			{
+				int annotIndex = pdfPage.GetAnnotCount() - 1;
+
+				underlineAnnot.SetTransparency((byte)currentParam.Transparency);
+				underlineAnnot.SetRect(currentParam.ClientRect);
+				
+				if (currentParam.QuardRects != null)
+				{
+                    underlineAnnot.SetQuardRects(currentParam.QuardRects);
+                }
+
+                if (currentParam.UnderlineColor != null && currentParam.UnderlineColor.Length == 3)
+                {
+                    underlineAnnot.SetColor(currentParam.UnderlineColor);
+                }
+
+                if (!string.IsNullOrEmpty(currentParam.Author))
+				{
+					underlineAnnot.SetAuthor(currentParam.Author);
+				}
+
+				if (!string.IsNullOrEmpty(currentParam.Content))
+				{
+					underlineAnnot.SetContent(currentParam.Content);
+				}
+				underlineAnnot.SetIsLocked(currentParam.Locked);
+				underlineAnnot.SetCreationDate(PDFHelp.GetCurrentPdfTime());
+				underlineAnnot.UpdateAp();
+				underlineAnnot.ReleaseAnnot();
+
+				if (currentParam != null)
+                {
+                    currentParam.AnnotIndex = annotIndex;
+				}
+				if (PreviousParam != null)
+				{
+					PreviousParam.AnnotIndex = annotIndex;
+				}
+
+				return true;
+			}
+
+			return false;
+		}
+
+		internal override bool Update(bool isUndo)
+		{
+			if (CurrentParam as UnderlineParam == null || PreviousParam as UnderlineParam == null)
+			{
+				return false;
+			}
+
+			if (MakeAnnotValid(CurrentParam))
+			{
+				CPDFUnderlineAnnotation underlineAnnot = Annot as CPDFUnderlineAnnotation;
+				if (underlineAnnot == null || !underlineAnnot.IsValid())
+				{
+					return false;
+				}
+
+				UnderlineParam updateParam = (isUndo ? PreviousParam : CurrentParam) as UnderlineParam;
+				UnderlineParam checkParam = (isUndo ? CurrentParam : PreviousParam) as UnderlineParam;
+
+                if (!CheckArrayEqual(updateParam.UnderlineColor, checkParam.UnderlineColor))
+                {
+                    if (updateParam.UnderlineColor != null && updateParam.UnderlineColor.Length == 3)
+                    {
+                        underlineAnnot.SetColor(updateParam.UnderlineColor);
+                    }
+                }
+
+                if (updateParam.Transparency != checkParam.Transparency)
+				{
+					underlineAnnot.SetTransparency((byte)updateParam.Transparency);
+				}
+
+				if (updateParam.Author != checkParam.Author)
+				{
+					underlineAnnot.SetAuthor(updateParam.Author);
+				}
+
+				if (updateParam.Content != checkParam.Content)
+				{
+					underlineAnnot.SetContent(updateParam.Content);
+				}
+
+				if (updateParam.Locked != checkParam.Locked)
+				{
+					underlineAnnot.SetIsLocked(updateParam.Locked);
+				}
+
+				underlineAnnot.SetModifyDate(PDFHelp.GetCurrentPdfTime());
+				underlineAnnot.UpdateAp();
+
+				return true;
+			}
+
+			return false;
+		}
+
+		internal override bool Remove()
+		{
+			if (MakeAnnotValid(CurrentParam))
+			{
+				Annot.RemoveAnnot();
+				return true;
+			}
+			return false;
+		}
+	}
+}

+ 40 - 0
Demo/Examples/ComPDFKit.Tool/UndoManger/FindReplaceHistory/FindReplaceHistory.cs

@@ -0,0 +1,40 @@
+using ComPDFKit.PDFPage;
+using ComPDFKitViewer.Helper;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace ComPDFKit.Tool.UndoManger.FindReplaceHistory
+{
+    public class FindReplaceHistory : IHistory
+    {
+        public CPDFEditPage EditPage { get; set; } 
+
+        public int PageIndex { get; set; }
+
+        void IHistory.Check(IHistory changeItem, bool undo, bool redo, bool add)
+        {
+            
+        }
+
+        bool IHistory.Redo()
+        {
+            if (EditPage != null && EditPage.IsValid())
+            {
+                return EditPage.Redo();
+            }
+            return false;
+        }
+
+        bool IHistory.Undo()
+        {
+            if (EditPage != null && EditPage.IsValid())
+            { 
+                return EditPage.Undo();
+            }
+            return false;
+        }
+    }
+}

+ 239 - 0
Demo/Examples/ComPDFKit.Tool/UndoManger/FormHistory/CheckBoxHistory.cs

@@ -0,0 +1,239 @@
+using ComPDFKit.PDFAnnotation;
+using ComPDFKit.PDFAnnotation.Form;
+using ComPDFKit.PDFPage;
+using ComPDFKit.Tool.Help;
+
+namespace ComPDFKit.Tool.UndoManger
+{
+    public class CheckBoxHistory:AnnotHistory
+	{
+        public override int GetAnnotIndex()
+        {
+            if (CurrentParam != null)
+            {
+                return CurrentParam.AnnotIndex;
+            }
+            return base.GetAnnotIndex();
+        }
+
+        public override int GetPageIndex()
+        {
+            if (CurrentParam != null)
+            {
+                return CurrentParam.PageIndex;
+            }
+            return base.GetPageIndex();
+        }
+
+        public override void SetAnnotIndex(int newIndex)
+        {
+            if (CurrentParam != null)
+            {
+                CurrentParam.AnnotIndex = newIndex;
+            }
+
+            if (PreviousParam != null)
+            {
+                PreviousParam.AnnotIndex = newIndex;
+            }
+        }
+
+        internal override bool Add()
+		{
+            CheckBoxParam currentParam = CurrentParam as CheckBoxParam;
+
+            if (currentParam == null || PDFDoc == null || !PDFDoc.IsValid())
+			{
+				return false;
+			}
+			CPDFPage pdfPage = PDFDoc.PageAtIndex(currentParam.PageIndex);
+			CPDFCheckBoxWidget checkboxWidget = pdfPage.CreateWidget(C_WIDGET_TYPE.WIDGET_CHECKBOX) as CPDFCheckBoxWidget;
+			if (checkboxWidget != null)
+			{
+				int annotIndex = pdfPage.GetAnnotCount() - 1;
+
+				if (!string.IsNullOrEmpty(currentParam.FieldName))
+				{
+					checkboxWidget.SetFieldName(currentParam.FieldName);
+				}
+
+                checkboxWidget.SetWidgetCheckStyle(currentParam.CheckStyle);
+				checkboxWidget.SetChecked(currentParam.IsChecked);
+
+				if(currentParam.HasLineColor)
+				{
+                    if (currentParam.LineColor != null && currentParam.LineColor.Length == 3)
+                    {
+                        checkboxWidget.SetWidgetBorderRGBColor(currentParam.LineColor);
+                    }
+                }
+
+                if (currentParam.HasBgColor)
+                {
+                    if (currentParam.BgColor != null && currentParam.BgColor.Length == 3)
+                    {
+                        checkboxWidget.SetWidgetBgRGBColor(currentParam.BgColor);
+                    }
+                }
+
+                checkboxWidget.SetBorderWidth((float)currentParam.LineWidth);
+				checkboxWidget.SetWidgetBorderStyle(currentParam.BorderStyle);
+
+                if (currentParam.FontColor != null && currentParam.FontColor.Length == 3)
+                {
+                    CTextAttribute textAttr = checkboxWidget.GetTextAttribute();
+					textAttr.FontColor = currentParam.FontColor;
+                    checkboxWidget.SetTextAttribute(textAttr);
+                }
+
+                checkboxWidget.SetRect(currentParam.ClientRect);
+
+				checkboxWidget.SetFlags(currentParam.Flags);
+				checkboxWidget.SetIsLocked(currentParam.Locked);
+				checkboxWidget.SetIsReadOnly(currentParam.IsReadOnly);
+				checkboxWidget.SetIsHidden(currentParam.IsHidden);
+
+				checkboxWidget.SetCreationDate(PDFHelp.GetCurrentPdfTime());
+				checkboxWidget.UpdateFormAp();
+				checkboxWidget.ReleaseAnnot();
+
+				if (CurrentParam != null)
+                {
+                    currentParam.AnnotIndex = annotIndex;
+				}
+				if (PreviousParam != null)
+				{
+					PreviousParam.AnnotIndex = annotIndex;
+				}
+
+				return true;
+			}
+
+			return false;
+		}
+
+		internal override bool Update(bool isUndo)
+		{
+			if (CurrentParam as CheckBoxParam == null || PreviousParam as CheckBoxParam == null)
+			{
+				return false;
+			}
+
+			if (MakeAnnotValid(CurrentParam))
+			{
+				CPDFCheckBoxWidget checkboxWidget = Annot as CPDFCheckBoxWidget;
+				if (checkboxWidget == null || !checkboxWidget.IsValid())
+				{
+					return false;
+				}
+
+				CheckBoxParam updateParam = (isUndo ? PreviousParam : CurrentParam) as CheckBoxParam;
+				CheckBoxParam checkParam = (isUndo ? CurrentParam : PreviousParam) as CheckBoxParam;
+
+				if (updateParam.FieldName != checkParam.FieldName)
+				{
+					checkboxWidget.SetFieldName(updateParam.FieldName);
+				}
+
+				if (updateParam.CheckStyle != checkParam.CheckStyle)
+				{
+					checkboxWidget.SetWidgetCheckStyle(updateParam.CheckStyle);
+				}
+
+				if (updateParam.IsChecked != checkParam.IsChecked)
+				{
+					checkboxWidget.SetChecked(updateParam.IsChecked);
+				}
+
+                if (!CheckArrayEqual(updateParam.LineColor, checkParam.LineColor))
+                {
+                    if (updateParam.HasLineColor)
+                    {
+                        if (updateParam.LineColor != null && updateParam.LineColor.Length == 3)
+                        {
+                            checkboxWidget.SetWidgetBorderRGBColor(updateParam.LineColor);
+                        }
+                    }
+                    else
+                    {
+                        checkboxWidget.ClearWidgetBorderRGBColor();
+                    }
+                }
+
+                if (!CheckArrayEqual(updateParam.BgColor, checkParam.BgColor))
+                {
+                    if (updateParam.HasBgColor)
+                    {
+                        if (updateParam.BgColor != null && updateParam.BgColor.Length == 3)
+                        {
+                            checkboxWidget.SetWidgetBgRGBColor(updateParam.BgColor);
+                        }
+                    }
+                    else
+                    {
+                        checkboxWidget.ClearWidgetBgRGBColor();
+                    }
+                }
+
+                if (updateParam.LineWidth != checkParam.LineWidth)
+				{
+					checkboxWidget.SetBorderWidth((float)updateParam.LineWidth);
+				}
+
+				if (updateParam.BorderStyle != checkParam.BorderStyle)
+				{
+					checkboxWidget.SetWidgetBorderStyle(updateParam.BorderStyle);
+				}
+
+                if (!CheckArrayEqual(updateParam.FontColor, checkParam.FontColor))
+				{
+					CTextAttribute textAttr = checkboxWidget.GetTextAttribute();
+					textAttr.FontColor = updateParam.FontColor;
+                    checkboxWidget.SetTextAttribute(textAttr);
+				}
+
+				if (!updateParam.ClientRect.Equals(checkParam.ClientRect))
+                {
+                    checkboxWidget.SetRect(updateParam.ClientRect);
+                }
+
+				if (updateParam.Flags != checkParam.Flags)
+				{
+					checkboxWidget.SetFlags(updateParam.Flags);
+				}
+
+				if (updateParam.Locked != checkParam.Locked)
+				{
+					checkboxWidget.SetIsLocked(updateParam.Locked);
+				}
+
+				if (updateParam.IsReadOnly != checkParam.IsReadOnly)
+				{
+					checkboxWidget.SetIsReadOnly(updateParam.IsReadOnly);
+				}
+
+				if (updateParam.IsHidden != checkParam.IsHidden)
+				{
+					checkboxWidget.SetIsHidden(updateParam.IsHidden);
+				}
+
+				checkboxWidget.SetModifyDate(PDFHelp.GetCurrentPdfTime());
+				checkboxWidget.UpdateFormAp();
+
+				return true;
+			}
+
+			return false;
+		}
+
+		internal override bool Remove()
+		{
+			if (MakeAnnotValid(CurrentParam))
+			{
+				Annot.RemoveAnnot();
+				return true;
+			}
+			return false;
+		}
+	}
+}

+ 317 - 0
Demo/Examples/ComPDFKit.Tool/UndoManger/FormHistory/ComboBoxHistory.cs

@@ -0,0 +1,317 @@
+using ComPDFKit.PDFAnnotation;
+using ComPDFKit.PDFAnnotation.Form;
+using ComPDFKit.PDFPage;
+using ComPDFKit.Tool.Help;
+using static ComPDFKit.PDFAnnotation.CTextAttribute.CFontNameHelper;
+
+namespace ComPDFKit.Tool.UndoManger
+{
+    public class ComboBoxHistory:AnnotHistory
+	{
+        public override int GetAnnotIndex()
+        {
+            if (CurrentParam != null)
+            {
+                return CurrentParam.AnnotIndex;
+            }
+            return base.GetAnnotIndex();
+        }
+
+        public override int GetPageIndex()
+        {
+            if (CurrentParam != null)
+            {
+                return CurrentParam.PageIndex;
+            }
+            return base.GetPageIndex();
+        }
+
+        public override void SetAnnotIndex(int newIndex)
+        {
+            if (CurrentParam != null)
+            {
+                CurrentParam.AnnotIndex = newIndex;
+            }
+
+            if (PreviousParam != null)
+            {
+                PreviousParam.AnnotIndex = newIndex;
+            }
+        }
+
+        internal override bool Add()
+        {
+            ComboBoxParam currentParam = CurrentParam as ComboBoxParam;
+            if (currentParam == null || PDFDoc == null || !PDFDoc.IsValid())
+			{
+				return false;
+			}
+			CPDFPage pdfPage = PDFDoc.PageAtIndex(currentParam.PageIndex);
+			CPDFComboBoxWidget comboboxWidget = pdfPage.CreateWidget(C_WIDGET_TYPE.WIDGET_COMBOBOX) as CPDFComboBoxWidget;
+			if (comboboxWidget != null)
+			{
+				int annotIndex = pdfPage.GetAnnotCount() - 1;
+
+				if (!string.IsNullOrEmpty(currentParam.FieldName))
+				{
+					comboboxWidget.SetFieldName(currentParam.FieldName);
+				}
+				
+				if(currentParam.HasLineColor)
+				{
+                    if (currentParam.LineColor != null && currentParam.LineColor.Length == 3)
+                    {
+                        comboboxWidget.SetWidgetBorderRGBColor(currentParam.LineColor);
+                    }
+                }
+
+				if(currentParam.HasBgColor)
+				{
+                    if (currentParam.BgColor != null && currentParam.BgColor.Length == 3)
+					{
+                        comboboxWidget.SetWidgetBgRGBColor(currentParam.BgColor);
+                    }
+                }
+				
+				comboboxWidget.SetBorderWidth((float)currentParam.LineWidth);
+				comboboxWidget.SetWidgetBorderStyle(currentParam.BorderStyle);
+
+				CTextAttribute textAttr = new CTextAttribute();
+				byte[] fontColor = new byte[3];
+                if (currentParam.FontColor != null && currentParam.FontColor.Length == 3)
+                {
+					fontColor = currentParam.FontColor;
+                }
+                textAttr.FontColor = fontColor;
+				textAttr.FontSize = (float)currentParam.FontSize;
+				textAttr.FontName = ObtainFontName(
+					GetFontType(currentParam.FontName),
+					currentParam.IsBold,
+					currentParam.IsItalic);
+
+				comboboxWidget.SetTextAttribute(textAttr);
+
+				if(currentParam.OptionItems!=null && currentParam.OptionItems.Count > 0)
+				{
+					int addIndex = 0;
+					foreach (string key in currentParam.OptionItems.Keys)
+					{
+						comboboxWidget.AddOptionItem(addIndex, currentParam.OptionItems[key], key);
+						addIndex++;
+					}
+				}
+
+				if(currentParam.SelectItemsIndex!=null && currentParam.SelectItemsIndex.Count > 0)
+				{
+					comboboxWidget.SelectItem(currentParam.SelectItemsIndex[0]);
+				}
+
+				comboboxWidget.SetRect(currentParam.ClientRect);
+
+				comboboxWidget.SetFlags(currentParam.Flags);
+				comboboxWidget.SetIsLocked(currentParam.Locked);
+				comboboxWidget.SetIsReadOnly(currentParam.IsReadOnly);
+				comboboxWidget.SetIsHidden(currentParam.IsHidden);
+
+				comboboxWidget.SetCreationDate(PDFHelp.GetCurrentPdfTime());
+				comboboxWidget.UpdateFormAp();
+				comboboxWidget.ReleaseAnnot();
+
+				if (CurrentParam != null)
+                {
+                    currentParam.AnnotIndex = annotIndex;
+				}
+				if (PreviousParam != null)
+				{
+					PreviousParam.AnnotIndex = annotIndex;
+				}
+
+				return true;
+			}
+
+			return false;
+		}
+
+		internal override bool Update(bool isUndo)
+		{
+			if (CurrentParam as ComboBoxParam == null || PreviousParam as ComboBoxParam == null)
+			{
+				return false;
+			}
+
+			if (MakeAnnotValid(CurrentParam))
+			{
+				CPDFComboBoxWidget comboboxWidget = Annot as CPDFComboBoxWidget;
+				if (comboboxWidget == null || !comboboxWidget.IsValid())
+				{
+					return false;
+				}
+
+				ComboBoxParam updateParam = (isUndo ? PreviousParam : CurrentParam) as ComboBoxParam;
+				ComboBoxParam checkParam = (isUndo ? CurrentParam : PreviousParam) as ComboBoxParam;
+
+				if (updateParam.FieldName != checkParam.FieldName)
+				{
+					comboboxWidget.SetFieldName(updateParam.FieldName);
+				}
+
+                if (!CheckArrayEqual(updateParam.LineColor, checkParam.LineColor))
+                {
+                    if (updateParam.HasLineColor)
+                    {
+                        if (updateParam.LineColor != null && updateParam.LineColor.Length == 3)
+                        {
+                            comboboxWidget.SetWidgetBorderRGBColor(updateParam.LineColor);
+                        }
+                    }
+                    else
+                    {
+                        comboboxWidget.ClearWidgetBorderRGBColor();
+                    }
+                }
+
+                if (!CheckArrayEqual(updateParam.BgColor, checkParam.BgColor))
+                {
+                    if (updateParam.HasBgColor)
+                    {
+                        if (updateParam.BgColor != null && updateParam.BgColor.Length == 3)
+                        {
+                            comboboxWidget.SetWidgetBgRGBColor(updateParam.BgColor);
+                        }
+                    }
+                    else
+                    {
+                        comboboxWidget.ClearWidgetBgRGBColor();
+                    }
+                }
+
+                if (updateParam.LineWidth!=checkParam.LineWidth)
+				{
+					comboboxWidget.SetBorderWidth((float)updateParam.LineWidth);
+				}
+
+				if(updateParam.BorderStyle!=checkParam.BorderStyle)
+				{
+					comboboxWidget.SetWidgetBorderStyle(updateParam.BorderStyle);
+				}
+
+				if (updateParam.FontName != checkParam.FontName)
+				{
+					CTextAttribute textAttr = comboboxWidget.GetTextAttribute();
+					bool isBold = IsBold(textAttr.FontName);
+					bool isItalic = IsItalic(textAttr.FontName);
+					FontType fontType = GetFontType(updateParam.FontName);
+					textAttr.FontName = ObtainFontName(fontType, isBold, isItalic);
+					comboboxWidget.SetTextAttribute(textAttr);
+				}
+
+				if (updateParam.FontSize != checkParam.FontSize)
+				{
+					CTextAttribute textAttr = comboboxWidget.GetTextAttribute();
+					textAttr.FontSize = (float)updateParam.FontSize;
+					comboboxWidget.SetTextAttribute(textAttr);
+				}
+
+				if (!CheckArrayEqual(updateParam.FontColor,checkParam.FontColor))
+				{
+                    if (updateParam.FontColor != null && updateParam.FontColor.Length == 3)
+					{
+                        CTextAttribute textAttr = comboboxWidget.GetTextAttribute();
+                        textAttr.FontColor = updateParam.FontColor;
+                        comboboxWidget.SetTextAttribute(textAttr);
+                    }
+				}
+
+				if (updateParam.IsBold != checkParam.IsBold)
+				{
+					CTextAttribute textAttr = comboboxWidget.GetTextAttribute();
+					bool isItalic = IsItalic(textAttr.FontName);
+					FontType fontType = GetFontType(textAttr.FontName);
+					textAttr.FontName = ObtainFontName(fontType, updateParam.IsBold, isItalic);
+					comboboxWidget.SetTextAttribute(textAttr);
+				}
+
+				if (updateParam.IsItalic != checkParam.IsItalic)
+				{
+					CTextAttribute textAttr = comboboxWidget.GetTextAttribute();
+					bool isBold = IsBold(textAttr.FontName);
+					FontType fontType = GetFontType(textAttr.FontName);
+					textAttr.FontName = ObtainFontName(fontType, isBold, updateParam.IsItalic);
+					comboboxWidget.SetTextAttribute(textAttr);
+				}
+
+				if (updateParam.OptionItems != null)
+				{
+					int optionsCount = comboboxWidget.GetItemsCount();
+					for (int i = 0; i < optionsCount; i++)
+					{
+						comboboxWidget.RemoveOptionItem(0);
+					}
+
+					int addIndex = 0;
+					foreach (string key in updateParam.OptionItems.Keys)
+					{
+						comboboxWidget.AddOptionItem(addIndex, updateParam.OptionItems[key], key);
+						addIndex++;
+					}
+				}
+
+                if (updateParam.SelectItemsIndex != null)
+                {
+                    if (updateParam.SelectItemsIndex.Count > 0)
+                    {
+                        comboboxWidget.SelectItem(updateParam.SelectItemsIndex[0]);
+                    }
+                }
+
+
+                if (updateParam.SelectItemsIndex != null && updateParam.SelectItemsIndex.Count > 0)
+				{
+					comboboxWidget.SelectItem(updateParam.SelectItemsIndex[0]);
+				}
+
+				if (!updateParam.ClientRect.Equals(checkParam.ClientRect))
+                {
+                    comboboxWidget.SetRect(updateParam.ClientRect);
+                }
+
+				if (updateParam.Flags != checkParam.Flags)
+				{
+					comboboxWidget.SetFlags(updateParam.Flags);
+				}
+
+				if (updateParam.Locked != checkParam.Locked)
+				{
+					comboboxWidget.SetIsLocked(updateParam.Locked);
+				}
+
+				if (updateParam.IsReadOnly != checkParam.IsReadOnly)
+				{
+					comboboxWidget.SetIsReadOnly(updateParam.IsReadOnly);
+				}
+
+				if (updateParam.IsHidden != checkParam.IsHidden)
+				{
+					comboboxWidget.SetIsHidden(updateParam.IsHidden);
+				}
+
+				comboboxWidget.SetModifyDate(PDFHelp.GetCurrentPdfTime());
+				comboboxWidget.UpdateFormAp();
+
+				return true;
+			}
+
+			return false;
+		}
+
+		internal override bool Remove()
+		{
+			if (MakeAnnotValid(CurrentParam))
+			{
+				Annot.RemoveAnnot();
+				return true;
+			}
+			return false;
+		}
+	}
+}

+ 318 - 0
Demo/Examples/ComPDFKit.Tool/UndoManger/FormHistory/ListBoxHistory.cs

@@ -0,0 +1,318 @@
+using ComPDFKit.PDFAnnotation;
+using ComPDFKit.PDFAnnotation.Form;
+using ComPDFKit.PDFPage;
+using ComPDFKit.Tool.Help;
+using static ComPDFKit.PDFAnnotation.CTextAttribute.CFontNameHelper;
+
+namespace ComPDFKit.Tool.UndoManger
+{
+    public class ListBoxHistory:AnnotHistory
+	{
+        public override int GetAnnotIndex()
+        {
+            if (CurrentParam != null)
+            {
+                return CurrentParam.AnnotIndex;
+            }
+            return base.GetAnnotIndex();
+        }
+
+        public override int GetPageIndex()
+        {
+            if (CurrentParam != null)
+            {
+                return CurrentParam.PageIndex;
+            }
+            return base.GetPageIndex();
+        }
+
+        public override void SetAnnotIndex(int newIndex)
+        {
+            if (CurrentParam != null)
+            {
+                CurrentParam.AnnotIndex = newIndex;
+            }
+
+            if (PreviousParam != null)
+            {
+                PreviousParam.AnnotIndex = newIndex;
+            }
+        }
+
+        internal override bool Add()
+        {
+            ListBoxParam currentParam = CurrentParam as ListBoxParam;
+            if (currentParam == null || PDFDoc == null || !PDFDoc.IsValid())
+			{
+				return false;
+			}
+			CPDFPage pdfPage = PDFDoc.PageAtIndex(currentParam.PageIndex);
+			CPDFListBoxWidget listboxWidget = pdfPage.CreateWidget(C_WIDGET_TYPE.WIDGET_LISTBOX) as CPDFListBoxWidget;
+			if (listboxWidget != null)
+			{
+				int annotIndex = pdfPage.GetAnnotCount() - 1;
+
+				if (!string.IsNullOrEmpty(currentParam.FieldName))
+				{
+					listboxWidget.SetFieldName(currentParam.FieldName);
+				}
+
+				if(currentParam.HasLineColor)
+				{
+                    if (currentParam.LineColor != null && currentParam.LineColor.Length == 3)
+                    {
+                        listboxWidget.SetWidgetBorderRGBColor(currentParam.LineColor);
+                    }
+                }
+
+				if(currentParam.HasBgColor)
+				{
+                    if (currentParam.BgColor != null && currentParam.BgColor.Length == 3)
+					{
+                        listboxWidget.SetWidgetBgRGBColor(currentParam.BgColor);
+                    }
+                }
+				
+				listboxWidget.SetBorderWidth((float)currentParam.LineWidth);
+				listboxWidget.SetWidgetBorderStyle(currentParam.BorderStyle);
+
+				CTextAttribute textAttr = new CTextAttribute();
+				byte[] fontColor = new byte[3];
+                if (currentParam.FontColor != null && currentParam.FontColor.Length == 3)
+                {
+                    fontColor = currentParam.FontColor;
+                }
+                textAttr.FontColor = fontColor;
+				textAttr.FontSize = (float)currentParam.FontSize;
+				textAttr.FontName = ObtainFontName(
+					GetFontType(currentParam.FontName),
+					currentParam.IsBold,
+					currentParam.IsItalic);
+
+				listboxWidget.SetTextAttribute(textAttr);
+
+				if (currentParam.OptionItems != null && currentParam.OptionItems.Count > 0)
+				{
+					int addIndex = 0;
+					foreach (string key in currentParam.OptionItems.Keys)
+					{
+						listboxWidget.AddOptionItem(addIndex, currentParam.OptionItems[key], key);
+						addIndex++;
+					}
+				}
+
+				if (currentParam.SelectItemsIndex != null && currentParam.SelectItemsIndex.Count > 0)
+				{
+					listboxWidget.SelectItem(currentParam.SelectItemsIndex[0]);
+				}
+
+				listboxWidget.SetRect(currentParam.ClientRect);
+
+				listboxWidget.SetFlags(currentParam.Flags);
+				listboxWidget.SetIsLocked(currentParam.Locked);
+				listboxWidget.SetIsReadOnly(currentParam.IsReadOnly);
+				listboxWidget.SetIsHidden(currentParam.IsHidden);
+
+				listboxWidget.SetCreationDate(PDFHelp.GetCurrentPdfTime());
+				listboxWidget.UpdateFormAp();
+				listboxWidget.ReleaseAnnot();
+
+				if (CurrentParam != null)
+				{
+                    currentParam.AnnotIndex = annotIndex;
+				}
+				if (PreviousParam != null)
+				{
+					PreviousParam.AnnotIndex = annotIndex;
+				}
+
+				return true;
+			}
+
+			return false;
+		}
+
+		internal override bool Update(bool isUndo)
+		{
+			if (CurrentParam as ListBoxParam == null || PreviousParam as ListBoxParam == null)
+			{
+				return false;
+			}
+
+			if (MakeAnnotValid(CurrentParam))
+			{
+				CPDFListBoxWidget listboxWidget = Annot as CPDFListBoxWidget;
+				if (listboxWidget == null || !listboxWidget.IsValid())
+				{
+					return false;
+				}
+
+				ListBoxParam updateParam = (isUndo ? PreviousParam : CurrentParam) as ListBoxParam;
+				ListBoxParam checkParam = (isUndo ? CurrentParam : PreviousParam) as ListBoxParam;
+
+				if (updateParam.FieldName != checkParam.FieldName)
+				{
+					listboxWidget.SetFieldName(updateParam.FieldName);
+				}
+
+                if (!CheckArrayEqual(updateParam.LineColor, checkParam.LineColor))
+                {
+                    if (updateParam.HasLineColor)
+                    {
+                        if (updateParam.LineColor != null && updateParam.LineColor.Length == 3)
+                        {
+                            listboxWidget.SetWidgetBorderRGBColor(updateParam.LineColor);
+                        }
+                    }
+                    else
+                    {
+                        listboxWidget.ClearWidgetBorderRGBColor();
+                    }
+                }
+
+                if (!CheckArrayEqual(updateParam.BgColor, checkParam.BgColor))
+                {
+                    if (updateParam.HasBgColor)
+                    {
+                        if (updateParam.BgColor != null && updateParam.BgColor.Length == 3)
+                        {
+                            listboxWidget.SetWidgetBgRGBColor(updateParam.BgColor);
+                        }
+                    }
+                    else
+                    {
+                        listboxWidget.ClearWidgetBgRGBColor();
+                    }
+                }
+
+                if (updateParam.LineWidth != checkParam.LineWidth)
+				{
+					listboxWidget.SetBorderWidth((float)updateParam.LineWidth);
+				}
+
+				if (updateParam.BorderStyle != checkParam.BorderStyle)
+				{
+					listboxWidget.SetWidgetBorderStyle(updateParam.BorderStyle);
+				}
+
+				if (updateParam.FontName != checkParam.FontName)
+				{
+					CTextAttribute textAttr = listboxWidget.GetTextAttribute();
+					bool isBold = IsBold(textAttr.FontName);
+					bool isItalic = IsItalic(textAttr.FontName);
+					FontType fontType = GetFontType(updateParam.FontName);
+					textAttr.FontName = ObtainFontName(fontType, isBold, isItalic);
+					listboxWidget.SetTextAttribute(textAttr);
+				}
+
+				if (updateParam.FontSize != checkParam.FontSize)
+				{
+					CTextAttribute textAttr = listboxWidget.GetTextAttribute();
+					textAttr.FontSize = (float)updateParam.FontSize;
+					listboxWidget.SetTextAttribute(textAttr);
+				}
+
+                if (!CheckArrayEqual(updateParam.FontColor, checkParam.FontColor))
+                {
+                    if (updateParam.FontColor != null && updateParam.FontColor.Length == 3)
+                    {
+                        CTextAttribute textAttr = listboxWidget.GetTextAttribute();
+                        textAttr.FontColor = updateParam.FontColor;
+                        listboxWidget.SetTextAttribute(textAttr);
+                    }
+                }
+
+
+                if (updateParam.IsBold != checkParam.IsBold)
+				{
+					CTextAttribute textAttr = listboxWidget.GetTextAttribute();
+					bool isItalic = IsItalic(textAttr.FontName);
+					FontType fontType = GetFontType(textAttr.FontName);
+					textAttr.FontName = ObtainFontName(fontType, updateParam.IsBold, isItalic);
+					listboxWidget.SetTextAttribute(textAttr);
+				}
+
+				if (updateParam.IsItalic != checkParam.IsItalic)
+				{
+					CTextAttribute textAttr = listboxWidget.GetTextAttribute();
+					bool isBold = IsBold(textAttr.FontName);
+					FontType fontType = GetFontType(textAttr.FontName);
+					textAttr.FontName = ObtainFontName(fontType, isBold, updateParam.IsItalic);
+					listboxWidget.SetTextAttribute(textAttr);
+				}
+
+				if (updateParam.OptionItems != null)
+				{
+					int optionsCount = listboxWidget.GetItemsCount();
+					for (int i = 0; i < optionsCount; i++)
+					{
+						listboxWidget.RemoveOptionItem(0);
+					}
+
+					int addIndex = 0;
+					foreach (string key in updateParam.OptionItems.Keys)
+					{
+						listboxWidget.AddOptionItem(addIndex, updateParam.OptionItems[key], key);
+						addIndex++;
+					}
+				}
+
+				if (updateParam.SelectItemsIndex != null)
+				{
+					if (updateParam.SelectItemsIndex.Count > 0)
+					{
+						listboxWidget.SelectItem(updateParam.SelectItemsIndex[0]);
+					}
+				}
+
+
+				if (updateParam.SelectItemsIndex != null && updateParam.SelectItemsIndex.Count > 0)
+				{
+					listboxWidget.SelectItem(updateParam.SelectItemsIndex[0]);
+				}
+
+				if (!updateParam.ClientRect.Equals(checkParam.ClientRect))
+				{
+					listboxWidget.SetRect(updateParam.ClientRect);
+				}
+
+				if (updateParam.Flags != checkParam.Flags)
+				{
+					listboxWidget.SetFlags(updateParam.Flags);
+				}
+
+				if (updateParam.Locked != checkParam.Locked)
+				{
+					listboxWidget.SetIsLocked(updateParam.Locked);
+				}
+
+				if (updateParam.IsReadOnly != checkParam.IsReadOnly)
+				{
+					listboxWidget.SetIsReadOnly(updateParam.IsReadOnly);
+				}
+
+				if (updateParam.IsHidden != checkParam.IsHidden)
+				{
+					listboxWidget.SetIsHidden(updateParam.IsHidden);
+				}
+
+				listboxWidget.SetModifyDate(PDFHelp.GetCurrentPdfTime());
+				listboxWidget.UpdateFormAp();
+
+				return true;
+			}
+
+			return false;
+		}
+
+		internal override bool Remove()
+		{
+			if (MakeAnnotValid(CurrentParam))
+			{
+				Annot.RemoveAnnot();
+				return true;
+			}
+			return false;
+		}
+	}
+}

+ 327 - 0
Demo/Examples/ComPDFKit.Tool/UndoManger/FormHistory/PushButtonHistory.cs

@@ -0,0 +1,327 @@
+using ComPDFKit.PDFAnnotation;
+using ComPDFKit.PDFAnnotation.Form;
+using ComPDFKit.PDFDocument;
+using ComPDFKit.PDFDocument.Action;
+using ComPDFKit.PDFPage;
+using ComPDFKit.Tool.Help;
+using static ComPDFKit.PDFAnnotation.CTextAttribute.CFontNameHelper;
+
+namespace ComPDFKit.Tool.UndoManger
+{
+    public class PushButtonHistory:AnnotHistory
+	{
+        public override int GetAnnotIndex()
+        {
+            if (CurrentParam != null)
+            {
+                return CurrentParam.AnnotIndex;
+            }
+            return base.GetAnnotIndex();
+        }
+
+        public override int GetPageIndex()
+        {
+            if (CurrentParam != null)
+            {
+                return CurrentParam.PageIndex;
+            }
+            return base.GetPageIndex();
+        }
+
+        public override void SetAnnotIndex(int newIndex)
+        {
+            if (CurrentParam != null)
+            {
+                CurrentParam.AnnotIndex = newIndex;
+            }
+
+            if (PreviousParam != null)
+            {
+                PreviousParam.AnnotIndex = newIndex;
+            }
+        }
+
+        internal override bool Add()
+		{
+            PushButtonParam currentParam = CurrentParam as PushButtonParam;
+
+            if (currentParam == null || PDFDoc == null || !PDFDoc.IsValid())
+			{
+				return false;
+			}
+			CPDFPage pdfPage = PDFDoc.PageAtIndex(currentParam.PageIndex);
+			CPDFPushButtonWidget pushbuttonWidget = pdfPage.CreateWidget(C_WIDGET_TYPE.WIDGET_PUSHBUTTON) as CPDFPushButtonWidget;
+			if (pushbuttonWidget != null)
+			{
+				int annotIndex = pdfPage.GetAnnotCount() - 1;
+
+				if (!string.IsNullOrEmpty(currentParam.FieldName))
+				{
+					pushbuttonWidget.SetFieldName(currentParam.FieldName);
+				}
+
+                if (currentParam.HasLineColor)
+                {
+                    if (currentParam.LineColor != null && currentParam.LineColor.Length == 3)
+                    {
+                        pushbuttonWidget.SetWidgetBorderRGBColor(currentParam.LineColor);
+                    }
+                }
+
+				if(currentParam.HasBgColor)
+				{
+                    if (currentParam.BgColor != null && currentParam.BgColor.Length == 3)
+                    {
+                        pushbuttonWidget.SetWidgetBgRGBColor(currentParam.BgColor);
+                    }
+                }
+
+				if(!string.IsNullOrEmpty(currentParam.Text))
+				{
+					pushbuttonWidget.SetButtonTitle(currentParam.Text);
+				}
+
+				pushbuttonWidget.SetBorderWidth((float)currentParam.LineWidth);
+				pushbuttonWidget.SetWidgetBorderStyle(currentParam.BorderStyle);
+
+				CTextAttribute textAttr = new CTextAttribute();
+				byte[] fontColor = new byte[3];
+				if(currentParam.FontColor != null && currentParam.FontColor.Length==3)
+				{
+					fontColor = currentParam.FontColor;
+                }
+				textAttr.FontColor = fontColor;
+				textAttr.FontSize = (float)currentParam.FontSize;
+				textAttr.FontName = ObtainFontName(
+					GetFontType(currentParam.FontName),
+					currentParam.IsBold,
+					currentParam.IsItalic);
+
+				pushbuttonWidget.SetTextAttribute(textAttr);
+
+				switch (currentParam.Action)
+				{
+					case C_ACTION_TYPE.ACTION_TYPE_GOTO:
+						{
+							CPDFGoToAction gotoAction = new CPDFGoToAction();
+							CPDFDestination destination = new CPDFDestination();
+							destination.Position_X = currentParam.DestinationPosition.x;
+							destination.Position_Y = currentParam.DestinationPosition.y;
+							destination.PageIndex = currentParam.DestinationPageIndex;
+							gotoAction.SetDestination(PDFDoc, destination);
+							pushbuttonWidget.SetButtonAction(gotoAction);
+						}
+						break;
+					case C_ACTION_TYPE.ACTION_TYPE_URI:
+						{
+							CPDFUriAction uriAction = new CPDFUriAction();
+							uriAction.SetUri(currentParam.Uri);
+							pushbuttonWidget.SetButtonAction(uriAction);
+						}
+						break;
+					default:
+						break;
+				}
+
+				pushbuttonWidget.SetRect(currentParam.ClientRect);
+				pushbuttonWidget.SetFlags(currentParam.Flags);
+				pushbuttonWidget.SetIsLocked(currentParam.Locked);
+				pushbuttonWidget.SetIsReadOnly(currentParam.IsReadOnly);
+				pushbuttonWidget.SetIsHidden(currentParam.IsHidden);
+
+				pushbuttonWidget.SetCreationDate(PDFHelp.GetCurrentPdfTime());
+				pushbuttonWidget.UpdateFormAp();
+				pushbuttonWidget.ReleaseAnnot();
+
+				if (CurrentParam != null)
+				{
+                    currentParam.AnnotIndex = annotIndex;
+				}
+				if (PreviousParam != null)
+				{
+					PreviousParam.AnnotIndex = annotIndex;
+				}
+
+				return true;
+
+			}
+
+			return false;
+		}
+
+		internal override bool Update(bool isUndo)
+		{
+			if (CurrentParam as PushButtonParam == null || PreviousParam as PushButtonParam == null)
+			{
+				return false;
+			}
+
+			if (MakeAnnotValid(CurrentParam))
+			{
+				CPDFPushButtonWidget pushbutton = Annot as CPDFPushButtonWidget;
+				if (pushbutton == null || !pushbutton.IsValid())
+				{
+					return false;
+				}
+
+				PushButtonParam updateParam = (isUndo ? PreviousParam : CurrentParam) as PushButtonParam;
+				PushButtonParam checkParam = (isUndo ? CurrentParam : PreviousParam) as PushButtonParam;
+
+				if (updateParam.FieldName != checkParam.FieldName)
+				{
+					pushbutton.SetFieldName(updateParam.FieldName);
+				}
+
+                if (!CheckArrayEqual(updateParam.LineColor, checkParam.LineColor))
+                {
+                    if (updateParam.HasLineColor)
+                    {
+                        if (updateParam.LineColor != null && updateParam.LineColor.Length == 3)
+                        {
+                            pushbutton.SetWidgetBorderRGBColor(updateParam.LineColor);
+                        }
+                    }
+                    else
+                    {
+                        pushbutton.ClearWidgetBorderRGBColor();
+                    }
+                }
+
+                if (!CheckArrayEqual(updateParam.BgColor, checkParam.BgColor))
+                {
+                    if (updateParam.HasBgColor)
+                    {
+                        if (updateParam.BgColor != null && updateParam.BgColor.Length == 3)
+                        {
+                            pushbutton.SetWidgetBgRGBColor(updateParam.BgColor);
+                        }
+                    }
+                    else
+                    {
+                        pushbutton.ClearWidgetBgRGBColor();
+                    }
+                }
+
+                if (updateParam.LineWidth != checkParam.LineWidth)
+				{
+					pushbutton.SetBorderWidth((float)updateParam.LineWidth);
+				}
+
+				if (updateParam.BorderStyle != checkParam.BorderStyle)
+				{
+					pushbutton.SetWidgetBorderStyle(updateParam.BorderStyle);
+				}
+
+				if (updateParam.FontName != checkParam.FontName)
+				{
+					CTextAttribute textAttr = pushbutton.GetTextAttribute();
+					bool isBold = IsBold(textAttr.FontName);
+					bool isItalic = IsItalic(textAttr.FontName);
+					FontType fontType = GetFontType(updateParam.FontName);
+					textAttr.FontName = ObtainFontName(fontType, isBold, isItalic);
+					pushbutton.SetTextAttribute(textAttr);
+				}
+
+				if (updateParam.FontSize != checkParam.FontSize)
+				{
+					CTextAttribute textAttr = pushbutton.GetTextAttribute();
+					textAttr.FontSize = (float)updateParam.FontSize;
+					pushbutton.SetTextAttribute(textAttr);
+				}
+
+                if (!CheckArrayEqual(updateParam.FontColor, checkParam.FontColor))
+                {
+                    if (updateParam.FontColor != null && updateParam.FontColor.Length == 3)
+                    {
+                        CTextAttribute textAttr = pushbutton.GetTextAttribute();
+                        textAttr.FontColor = updateParam.FontColor;
+                        pushbutton.SetTextAttribute(textAttr);
+                    }
+                }
+
+                if (updateParam.IsBold != checkParam.IsBold)
+				{
+					CTextAttribute textAttr = pushbutton.GetTextAttribute();
+					bool isItalic = IsItalic(textAttr.FontName);
+					FontType fontType = GetFontType(textAttr.FontName);
+					textAttr.FontName = ObtainFontName(fontType, updateParam.IsBold, isItalic);
+					pushbutton.SetTextAttribute(textAttr);
+				}
+
+				if (updateParam.IsItalic != checkParam.IsItalic)
+				{
+					CTextAttribute textAttr = pushbutton.GetTextAttribute();
+					bool isBold = IsBold(textAttr.FontName);
+					FontType fontType = GetFontType(textAttr.FontName);
+					textAttr.FontName = ObtainFontName(fontType, isBold, updateParam.IsItalic);
+					pushbutton.SetTextAttribute(textAttr);
+				}
+
+				switch (updateParam.Action)
+				{
+					case C_ACTION_TYPE.ACTION_TYPE_GOTO:
+						{
+							CPDFGoToAction gotoAction = new CPDFGoToAction();
+							CPDFDestination destination = new CPDFDestination();
+							destination.Position_X = updateParam.DestinationPosition.x;
+							destination.Position_Y = updateParam.DestinationPosition.y;
+							destination.PageIndex = updateParam.DestinationPageIndex;
+							gotoAction.SetDestination(PDFDoc, destination);
+							pushbutton.SetButtonAction(gotoAction);
+						}
+						break;
+					case C_ACTION_TYPE.ACTION_TYPE_URI:
+						{
+							CPDFUriAction uriAction = new CPDFUriAction();
+							uriAction.SetUri(updateParam.Uri);
+							pushbutton.SetButtonAction(uriAction);
+						}
+						break;
+					default:
+						break;
+				}
+
+				if (!updateParam.ClientRect.Equals(checkParam.ClientRect))
+				{
+					pushbutton.SetRect(updateParam.ClientRect);
+				}
+
+				if (updateParam.Flags != checkParam.Flags)
+				{
+					pushbutton.SetFlags(updateParam.Flags);
+				}
+
+				if (updateParam.Locked != checkParam.Locked)
+				{
+					pushbutton.SetIsLocked(updateParam.Locked);
+				}
+
+				if (updateParam.IsReadOnly != checkParam.IsReadOnly)
+				{
+					pushbutton.SetIsReadOnly(updateParam.IsReadOnly);
+				}
+
+				if (updateParam.IsHidden != checkParam.IsHidden)
+				{
+					pushbutton.SetIsHidden(updateParam.IsHidden);
+				}
+
+				pushbutton.SetModifyDate(PDFHelp.GetCurrentPdfTime());
+				pushbutton.UpdateFormAp();
+
+				return true;
+			}
+			return false;
+		}
+
+		internal override bool Remove()
+		{
+			if (MakeAnnotValid(CurrentParam))
+			{
+				Annot.RemoveAnnot();
+				return true;
+			}
+			return false;
+		}
+	}
+}

+ 242 - 0
Demo/Examples/ComPDFKit.Tool/UndoManger/FormHistory/RadioButtonHistory.cs

@@ -0,0 +1,242 @@
+using ComPDFKit.PDFAnnotation;
+using ComPDFKit.PDFAnnotation.Form;
+using ComPDFKit.PDFPage;
+using ComPDFKit.Tool.Help;
+
+namespace ComPDFKit.Tool.UndoManger
+{
+    public class RadioButtonHistory:AnnotHistory
+	{
+        public override int GetAnnotIndex()
+        {
+            if (CurrentParam != null)
+            {
+                return CurrentParam.AnnotIndex;
+            }
+            return base.GetAnnotIndex();
+        }
+
+        public override int GetPageIndex()
+        {
+            if (CurrentParam != null)
+            {
+                return CurrentParam.PageIndex;
+            }
+            return base.GetPageIndex();
+        }
+
+        public override void SetAnnotIndex(int newIndex)
+        {
+            if (CurrentParam != null)
+            {
+                CurrentParam.AnnotIndex = newIndex;
+            }
+
+            if (PreviousParam != null)
+            {
+                PreviousParam.AnnotIndex = newIndex;
+            }
+        }
+
+        internal override bool Add()
+		{
+			RadioButtonParam currentParam = CurrentParam as RadioButtonParam;
+
+            if (currentParam  == null || PDFDoc == null || !PDFDoc.IsValid())
+			{
+				return false;
+			}
+			CPDFPage pdfPage = PDFDoc.PageAtIndex(currentParam.PageIndex);
+			CPDFRadioButtonWidget radioWidget=pdfPage.CreateWidget(C_WIDGET_TYPE.WIDGET_RADIOBUTTON) as CPDFRadioButtonWidget;
+			if (radioWidget != null)
+			{
+				int annotIndex = pdfPage.GetAnnotCount() - 1;
+
+				if(!string.IsNullOrEmpty(currentParam.FieldName))
+				{
+					radioWidget.SetFieldName(currentParam.FieldName);
+				}
+
+				radioWidget.SetWidgetCheckStyle(currentParam.CheckStyle);
+				radioWidget.SetChecked(currentParam.IsChecked);
+
+                if (currentParam.HasLineColor)
+                {
+                    if (currentParam.LineColor != null && currentParam.LineColor.Length == 3)
+                    {
+                        radioWidget.SetWidgetBorderRGBColor(currentParam.LineColor);
+                    }
+                }
+
+                if (currentParam.HasBgColor)
+                {
+                    if (currentParam.BgColor != null && currentParam.BgColor.Length == 3)
+                    {
+                        radioWidget.SetWidgetBgRGBColor(currentParam.BgColor);
+                    }
+                }
+
+                radioWidget.SetBorderWidth((float)currentParam.LineWidth);
+				radioWidget.SetWidgetBorderStyle(currentParam.BorderStyle);
+
+				if(currentParam.FontColor!=null && currentParam.FontColor.Length==3)
+				{
+					CTextAttribute textAttr = radioWidget.GetTextAttribute();
+					textAttr.FontColor = currentParam.FontColor;
+					radioWidget.SetTextAttribute(textAttr);
+				}
+
+				radioWidget.SetRect(currentParam.ClientRect);
+
+				radioWidget.SetFlags(currentParam.Flags);
+				radioWidget.SetIsLocked(currentParam.Locked);
+				radioWidget.SetIsReadOnly(currentParam.IsReadOnly);
+				radioWidget.SetIsHidden(currentParam.IsHidden);
+
+				radioWidget.SetCreationDate(PDFHelp.GetCurrentPdfTime());
+				radioWidget.UpdateFormAp();
+				radioWidget.ReleaseAnnot();
+
+				if (CurrentParam != null)
+				{
+                    currentParam.AnnotIndex = annotIndex;
+				}
+				if (PreviousParam != null)
+				{
+					PreviousParam.AnnotIndex = annotIndex;
+				}
+
+				return true;
+			}
+
+			return false;
+		}
+
+		internal override bool Update(bool isUndo)
+		{
+			if (CurrentParam as RadioButtonParam == null || PreviousParam as RadioButtonParam == null)
+			{
+				return false;
+			}
+
+			if (MakeAnnotValid(CurrentParam))
+			{
+				CPDFRadioButtonWidget radioWidget = Annot as CPDFRadioButtonWidget;
+				if (radioWidget == null || !radioWidget.IsValid())
+				{
+					return false;
+				}
+
+				RadioButtonParam updateParam = (isUndo ? PreviousParam : CurrentParam) as RadioButtonParam;
+				RadioButtonParam checkParam = (isUndo ? CurrentParam : PreviousParam) as RadioButtonParam;
+
+				if(updateParam.FieldName != checkParam.FieldName)
+				{
+					radioWidget.SetFieldName(updateParam.FieldName);
+				}
+
+				if(updateParam.CheckStyle != checkParam.CheckStyle) 
+				{ 
+					radioWidget.SetWidgetCheckStyle(updateParam.CheckStyle);
+				}
+
+				if(updateParam.IsChecked != checkParam.IsChecked)
+				{
+					radioWidget.SetChecked(updateParam.IsChecked);
+				}
+
+                if (!CheckArrayEqual(updateParam.LineColor, checkParam.LineColor))
+                {
+                    if (updateParam.HasLineColor)
+                    {
+                        if (updateParam.LineColor != null && updateParam.LineColor.Length == 3)
+                        {
+                            radioWidget.SetWidgetBorderRGBColor(updateParam.LineColor);
+                        }
+                    }
+                    else
+                    {
+                        radioWidget.ClearWidgetBorderRGBColor();
+                    }
+                }
+
+                if (!CheckArrayEqual(updateParam.BgColor, checkParam.BgColor))
+                {
+                    if (updateParam.HasBgColor)
+                    {
+                        if (updateParam.BgColor != null && updateParam.BgColor.Length == 3)
+                        {
+                            radioWidget.SetWidgetBgRGBColor(updateParam.BgColor);
+                        }
+                    }
+                    else
+                    {
+                        radioWidget.ClearWidgetBgRGBColor();
+                    }
+                }
+
+                if (updateParam.LineWidth != checkParam.LineWidth)
+				{
+					radioWidget.SetBorderWidth((float)updateParam.LineWidth);
+				}
+
+				if (updateParam.BorderStyle != checkParam.BorderStyle)
+				{
+					radioWidget.SetWidgetBorderStyle(updateParam.BorderStyle);
+				}
+
+				if(!CheckArrayEqual(updateParam.FontColor, checkParam.FontColor))
+				{
+					if(updateParam.FontColor!=null && updateParam.FontColor.Length==3)
+					{
+                        CTextAttribute textAttr = radioWidget.GetTextAttribute();
+                        textAttr.FontColor = updateParam.FontColor;
+                        radioWidget.SetTextAttribute(textAttr);
+                    }
+				}
+
+				if(!updateParam.ClientRect.Equals(checkParam.ClientRect))
+				{
+					radioWidget.SetRect(updateParam.ClientRect);
+				}
+
+				if(updateParam.Flags!=checkParam.Flags)
+				{
+					radioWidget.SetFlags(updateParam.Flags);
+				}
+
+				if(updateParam.Locked!=checkParam.Locked)
+				{
+					radioWidget.SetIsLocked(updateParam.Locked);
+				}
+
+				if(updateParam.IsReadOnly!=checkParam.IsReadOnly)
+				{
+					radioWidget.SetIsReadOnly(updateParam.IsReadOnly);
+				}
+
+				if(updateParam.IsHidden!=checkParam.IsHidden)
+				{
+					radioWidget.SetIsHidden(updateParam.IsHidden);
+				}
+
+				radioWidget.SetModifyDate(PDFHelp.GetCurrentPdfTime());
+				radioWidget.UpdateFormAp();
+
+				return true;
+			}
+
+			return false;
+		}
+
+		internal override bool Remove()
+		{
+			if (MakeAnnotValid(CurrentParam))
+			{
+				Annot.RemoveAnnot();
+				return true;
+			}
+			return false;
+		}
+	}
+}

+ 210 - 0
Demo/Examples/ComPDFKit.Tool/UndoManger/FormHistory/SignatureHistory.cs

@@ -0,0 +1,210 @@
+using ComPDFKit.PDFAnnotation.Form;
+using ComPDFKit.PDFPage;
+using ComPDFKit.Tool.Help;
+
+namespace ComPDFKit.Tool.UndoManger
+{
+    public class SignatureHistory:AnnotHistory
+	{
+        public override int GetAnnotIndex()
+        {
+            if (CurrentParam != null)
+            {
+                return CurrentParam.AnnotIndex;
+            }
+            return base.GetAnnotIndex();
+        }
+
+        public override int GetPageIndex()
+        {
+            if (CurrentParam != null)
+            {
+                return CurrentParam.PageIndex;
+            }
+            return base.GetPageIndex();
+        }
+
+        public override void SetAnnotIndex(int newIndex)
+        {
+            if (CurrentParam != null)
+            {
+                CurrentParam.AnnotIndex = newIndex;
+            }
+
+            if (PreviousParam != null)
+            {
+                PreviousParam.AnnotIndex = newIndex;
+            }
+        }
+
+        internal override bool Add()
+		{
+            SignatureParam currentParam = CurrentParam as SignatureParam;
+
+            if (currentParam == null || PDFDoc == null || !PDFDoc.IsValid())
+			{
+				return false;
+			}
+			CPDFPage pdfPage = PDFDoc.PageAtIndex(currentParam.PageIndex);
+			CPDFSignatureWidget signWidget = pdfPage.CreateWidget(C_WIDGET_TYPE.WIDGET_SIGNATUREFIELDS) as CPDFSignatureWidget;
+			if (signWidget != null)
+			{
+				int annotIndex = pdfPage.GetAnnotCount() - 1;
+
+				if (!string.IsNullOrEmpty(currentParam.FieldName))
+				{
+					signWidget.SetFieldName(currentParam.FieldName);
+				}
+
+				if(currentParam.HasLineColor)
+				{
+                    if (currentParam.LineColor != null && currentParam.LineColor.Length == 3)
+                    {
+                        signWidget.SetWidgetBorderRGBColor(currentParam.LineColor);
+                    }
+                }
+
+                if (currentParam.HasBgColor)
+                {
+                    if (currentParam.BgColor != null && currentParam.BgColor.Length == 3)
+                    {
+                        signWidget.SetWidgetBgRGBColor(currentParam.BgColor);
+                    }
+                }
+
+                signWidget.SetBorderWidth((float)currentParam.LineWidth);
+				signWidget.SetWidgetBorderStyle(currentParam.BorderStyle);
+
+				signWidget.SetRect(currentParam.ClientRect);
+
+				signWidget.SetFlags(currentParam.Flags);
+				signWidget.SetIsLocked(currentParam.Locked);
+				signWidget.SetIsReadOnly(currentParam.IsReadOnly);
+				signWidget.SetIsHidden(currentParam.IsHidden);
+
+				signWidget.SetCreationDate(PDFHelp.GetCurrentPdfTime());
+				signWidget.UpdateFormAp();
+				signWidget.ReleaseAnnot();
+
+				if (currentParam != null)
+				{
+                    currentParam.AnnotIndex = annotIndex;
+				}
+				if (PreviousParam != null)
+				{
+					PreviousParam.AnnotIndex = annotIndex;
+				}
+
+				return true;
+			}
+
+			return false;
+		}
+
+		internal override bool Update(bool isUndo)
+		{
+			if (CurrentParam as SignatureParam == null || PreviousParam as SignatureParam == null)
+			{
+				return false;
+			}
+
+			if (MakeAnnotValid(CurrentParam))
+			{
+				CPDFSignatureWidget signWidget = Annot as CPDFSignatureWidget;
+				if (signWidget == null || !signWidget.IsValid())
+				{
+					return false;
+				}
+
+				SignatureParam updateParam =( isUndo ? PreviousParam : CurrentParam) as SignatureParam;
+				SignatureParam checkParam = (isUndo ? CurrentParam : PreviousParam) as SignatureParam;
+
+				if (updateParam.FieldName != checkParam.FieldName)
+				{
+					signWidget.SetFieldName(updateParam.FieldName);
+				}
+
+                if (!CheckArrayEqual(updateParam.LineColor, checkParam.LineColor))
+                {
+                    if (updateParam.HasLineColor)
+                    {
+                        if (updateParam.LineColor != null && updateParam.LineColor.Length == 3)
+                        {
+                            signWidget.SetWidgetBorderRGBColor(updateParam.LineColor);
+                        }
+                    }
+                    else
+                    {
+                        signWidget.ClearWidgetBorderRGBColor();
+                    }
+                }
+
+                if (!CheckArrayEqual(updateParam.BgColor, checkParam.BgColor))
+                {
+                    if (updateParam.HasBgColor)
+                    {
+                        if (updateParam.BgColor != null && updateParam.BgColor.Length == 3)
+                        {
+                            signWidget.SetWidgetBgRGBColor(updateParam.BgColor);
+                        }
+                    }
+                    else
+                    {
+                        signWidget.ClearWidgetBgRGBColor();
+                    }
+                }
+
+                if (updateParam.LineWidth != checkParam.LineWidth)
+				{
+					signWidget.SetBorderWidth((float)updateParam.LineWidth);
+				}
+
+				if(updateParam.BorderStyle != checkParam.BorderStyle)
+				{
+					signWidget.SetWidgetBorderStyle(updateParam.BorderStyle);
+				}
+
+				if(!updateParam.ClientRect.Equals(checkParam.ClientRect))
+                {
+                    signWidget.SetRect(updateParam.ClientRect);
+                }
+
+				if (updateParam.Flags != checkParam.Flags)
+				{
+					signWidget.SetFlags(updateParam.Flags);
+				}
+
+				if (updateParam.Locked != checkParam.Locked)
+				{
+					signWidget.SetIsLocked(updateParam.Locked);
+				}
+
+				if (updateParam.IsReadOnly != checkParam.IsReadOnly)
+				{
+					signWidget.SetIsReadOnly(updateParam.IsReadOnly);
+				}
+
+				if (updateParam.IsHidden != checkParam.IsHidden)
+				{
+					signWidget.SetIsHidden(updateParam.IsHidden);
+				}
+
+				signWidget.SetModifyDate(PDFHelp.GetCurrentPdfTime());
+				signWidget.UpdateFormAp();
+
+				return true;
+			}
+			return false;
+		}
+
+		internal override bool Remove()
+		{
+			if (MakeAnnotValid(CurrentParam))
+			{
+				Annot.RemoveAnnot();
+				return true;
+			}
+			return false;
+		}
+	}
+}

+ 294 - 0
Demo/Examples/ComPDFKit.Tool/UndoManger/FormHistory/TextBoxHistory.cs

@@ -0,0 +1,294 @@
+using ComPDFKit.PDFAnnotation;
+using ComPDFKit.PDFAnnotation.Form;
+using ComPDFKit.PDFPage;
+using ComPDFKit.Tool.Help;
+using static ComPDFKit.PDFAnnotation.CTextAttribute.CFontNameHelper;
+
+namespace ComPDFKit.Tool.UndoManger
+{
+    public class TextBoxHistory:AnnotHistory
+	{
+        public override int GetAnnotIndex()
+        {
+            if (CurrentParam != null)
+            {
+                return CurrentParam.AnnotIndex;
+            }
+            return base.GetAnnotIndex();
+        }
+
+        public override int GetPageIndex()
+        {
+            if (CurrentParam != null)
+            {
+                return CurrentParam.PageIndex;
+            }
+            return base.GetPageIndex();
+        }
+
+        public override void SetAnnotIndex(int newIndex)
+        {
+            if (CurrentParam != null)
+            {
+                CurrentParam.AnnotIndex = newIndex;
+            }
+
+            if (PreviousParam != null)
+            {
+                PreviousParam.AnnotIndex = newIndex;
+            }
+        }
+
+        internal override bool Add()
+        {
+            TextBoxParam currentParam = CurrentParam as TextBoxParam;
+            if (currentParam == null || PDFDoc == null || !PDFDoc.IsValid())
+			{
+				return false;
+			}
+			CPDFPage pdfPage = PDFDoc.PageAtIndex(currentParam.PageIndex);
+			CPDFTextWidget textWidget = pdfPage.CreateWidget(C_WIDGET_TYPE.WIDGET_TEXTFIELD) as CPDFTextWidget;
+			if(textWidget != null)
+			{
+				int annotIndex = pdfPage.GetAnnotCount() - 1;
+
+				if (!string.IsNullOrEmpty(currentParam.FieldName))
+				{
+					textWidget.SetFieldName(currentParam.FieldName);
+				}
+
+                if (currentParam.HasLineColor)
+                {
+                    if (currentParam.LineColor != null && currentParam.LineColor.Length == 3)
+                    {
+                        textWidget.SetWidgetBorderRGBColor(currentParam.LineColor);
+                    }
+                }
+
+				if(currentParam.HasBgColor)
+				{
+                    if (currentParam.BgColor != null && currentParam.BgColor.Length == 3)
+                    {
+                        textWidget.SetWidgetBgRGBColor(currentParam.BgColor);
+                    }
+                }
+
+				if(!string.IsNullOrEmpty(currentParam.Text))
+				{
+					textWidget.SetText(currentParam.Text);
+				}
+
+				CTextAttribute textAttr = new CTextAttribute();
+				byte[] fontColor = new byte[3];
+				if(currentParam.FontColor != null && currentParam.FontColor.Length==3)
+				{
+					fontColor = currentParam.FontColor;
+                }
+				textAttr.FontColor = fontColor;
+				textAttr.FontSize = (float)currentParam.FontSize;
+				textAttr.FontName = ObtainFontName(
+					GetFontType(currentParam.FontName),
+					currentParam.IsBold,
+					currentParam.IsItalic);
+
+				textWidget.SetTextAttribute(textAttr);
+                textWidget.SetJustification(currentParam.Alignment);
+
+				textWidget.SetBorderWidth((float)currentParam.LineWidth);
+				textWidget.SetWidgetBorderStyle(currentParam.BorderStyle);
+				textWidget.SetMultiLine(currentParam.IsMultiLine);
+
+				textWidget.SetRect(currentParam.ClientRect);
+
+				textWidget.SetFlags(currentParam.Flags);
+				textWidget.SetIsLocked(currentParam.Locked);
+				textWidget.SetIsReadOnly(currentParam.IsReadOnly);
+				textWidget.SetIsHidden(currentParam.IsHidden);
+
+				textWidget.SetCreationDate(PDFHelp.GetCurrentPdfTime());
+				textWidget.UpdateFormAp();
+				textWidget.ReleaseAnnot();
+
+				if (CurrentParam != null)
+				{
+                    currentParam.AnnotIndex = annotIndex;
+				}
+				if (PreviousParam != null)
+				{
+					PreviousParam.AnnotIndex = annotIndex;
+				}
+
+				return true;
+			}
+
+			return false;
+		}
+
+		internal override bool Update(bool isUndo)
+		{
+			if (CurrentParam as TextBoxParam == null || PreviousParam as TextBoxParam == null)
+			{
+				return false;
+			}
+
+			if (MakeAnnotValid(CurrentParam))
+			{
+				CPDFTextWidget textWidget = Annot as CPDFTextWidget;
+				if (textWidget == null || !textWidget.IsValid())
+				{
+					return false;
+				}
+
+				TextBoxParam updateParam = (isUndo ? PreviousParam : CurrentParam) as TextBoxParam;
+				TextBoxParam checkParam = (isUndo ? CurrentParam : PreviousParam) as TextBoxParam;
+
+				if(updateParam.FieldName!=checkParam.FieldName)
+				{
+					textWidget.SetFieldName(updateParam.FieldName);
+				}
+
+                if (!CheckArrayEqual(updateParam.LineColor, checkParam.LineColor))
+                {
+                    if (updateParam.HasLineColor)
+                    {
+                        if (updateParam.LineColor != null && updateParam.LineColor.Length == 3)
+                        {
+                            textWidget.SetWidgetBorderRGBColor(updateParam.LineColor);
+                        }
+                    }
+                    else
+                    {
+                        textWidget.ClearWidgetBorderRGBColor();
+                    }
+                }
+
+                if (!CheckArrayEqual(updateParam.BgColor, checkParam.BgColor))
+                {
+                    if (updateParam.HasBgColor)
+                    {
+                        if (updateParam.BgColor != null && updateParam.BgColor.Length == 3)
+                        {
+                            textWidget.SetWidgetBgRGBColor(updateParam.BgColor);
+                        }
+                    }
+                    else
+                    {
+                        textWidget.ClearWidgetBgRGBColor();
+                    }
+                }
+
+                if (updateParam.Text!=checkParam.Text)
+				{
+					textWidget.SetText(updateParam.Text);
+				}
+
+				if (updateParam.FontName != checkParam.FontName)
+				{
+					CTextAttribute textAttr = textWidget.GetTextAttribute();
+					bool isBold = IsBold(textAttr.FontName);
+					bool isItalic = IsItalic(textAttr.FontName);
+					FontType fontType = GetFontType(updateParam.FontName);
+					textAttr.FontName = ObtainFontName(fontType, isBold, isItalic);
+					textWidget.SetTextAttribute(textAttr);
+				}
+
+				if (updateParam.FontSize != checkParam.FontSize)
+				{
+					CTextAttribute textAttr = textWidget.GetTextAttribute();
+					textAttr.FontSize = (float)updateParam.FontSize;
+					textWidget.SetTextAttribute(textAttr);
+				}
+
+                if (!CheckArrayEqual(updateParam.FontColor, checkParam.FontColor))
+				{
+					if(updateParam.FontColor != null && updateParam.FontColor.Length==3)
+					{
+                        CTextAttribute textAttr = textWidget.GetTextAttribute();
+                        textAttr.FontColor = updateParam.FontColor;
+                        textWidget.SetTextAttribute(textAttr);
+                    }
+				}
+
+				if (updateParam.IsBold != checkParam.IsBold)
+				{
+					CTextAttribute textAttr = textWidget.GetTextAttribute();
+					bool isItalic = IsItalic(textAttr.FontName);
+					FontType fontType = GetFontType(textAttr.FontName);
+					textAttr.FontName = ObtainFontName(fontType, updateParam.IsBold, isItalic);
+					textWidget.SetTextAttribute(textAttr);
+				}
+
+				if (updateParam.IsItalic != checkParam.IsItalic)
+				{
+					CTextAttribute textAttr = textWidget.GetTextAttribute();
+					bool isBold = IsBold(textAttr.FontName);
+					FontType fontType = GetFontType(textAttr.FontName);
+					textAttr.FontName = ObtainFontName(fontType, isBold, updateParam.IsItalic);
+					textWidget.SetTextAttribute(textAttr);
+				}
+
+				if (updateParam.Alignment != checkParam.Alignment)
+				{
+                    textWidget.SetJustification(updateParam.Alignment);
+                }
+
+				if(updateParam.LineWidth != checkParam.LineWidth)
+				{
+					textWidget.SetBorderWidth((float)updateParam.LineWidth);
+				}
+
+				if(updateParam.BorderStyle != checkParam.BorderStyle)
+				{
+					textWidget.SetWidgetBorderStyle(updateParam.BorderStyle);
+				}
+
+				if(updateParam.IsMultiLine!=checkParam.IsMultiLine)
+				{
+					textWidget.SetMultiLine(updateParam.IsMultiLine);
+				}
+
+				if (!updateParam.ClientRect.Equals(checkParam.ClientRect))
+                {
+                    textWidget.SetRect(updateParam.ClientRect);
+                }
+
+				if (updateParam.Flags != checkParam.Flags)
+				{
+					textWidget.SetFlags(updateParam.Flags);
+				}
+
+				if (updateParam.Locked != checkParam.Locked)
+				{
+					textWidget.SetIsLocked(updateParam.Locked);
+				}
+
+				if (updateParam.IsReadOnly != checkParam.IsReadOnly)
+				{
+					textWidget.SetIsReadOnly(updateParam.IsReadOnly);
+				}
+
+				if (updateParam.IsHidden != checkParam.IsHidden)
+				{
+					textWidget.SetIsHidden(updateParam.IsHidden);
+				}
+
+				textWidget.SetModifyDate(PDFHelp.GetCurrentPdfTime());
+				textWidget.UpdateFormAp();
+
+				return true;
+			}
+
+			return false;
+		}
+
+		internal override bool Remove()
+		{
+			if (MakeAnnotValid(CurrentParam))
+			{
+				Annot.RemoveAnnot();
+				return true;
+			}
+			return false;
+		}
+	}
+}

+ 46 - 0
Demo/Examples/ComPDFKit.Tool/UndoManger/GroupHistory.cs

@@ -0,0 +1,46 @@
+using ComPDFKitViewer.Helper;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace ComPDFKit.Tool.UndoManger
+{
+    public class GroupHistory : IHistory
+    {
+        public List<IHistory> Histories { get; set; } = new List<IHistory>();
+        public bool Redo()
+        {
+            if (Histories != null && Histories.Count > 0)
+            {
+                bool success = true;
+                foreach (IHistory history in Histories)
+                {
+                    success &= history.Redo();
+                }
+                return success;
+            }
+            return false;
+        }
+
+        public bool Undo()
+        {
+            if (Histories != null && Histories.Count > 0)
+            {
+                bool success = true;
+                foreach (IHistory history in Histories)
+                {
+                    success &= history.Undo();
+                }
+                return success;
+            }
+            return false;
+        }
+
+        public void Check(IHistory checkItem, bool undo, bool redo, bool add)
+        {
+            
+        }
+    }
+}

+ 123 - 0
Demo/Examples/ComPDFKit.Tool/UndoManger/MultiAnnotHistory.cs

@@ -0,0 +1,123 @@
+using ComPDFKitViewer.Helper;
+using System.Collections.Generic;
+
+namespace ComPDFKit.Tool.UndoManger
+{
+	public class MultiAnnotHistory : IHistory
+    {
+        public List<AnnotHistory> Histories { get; set; } = new List<AnnotHistory>();
+        public bool Redo()
+        {
+            if (Histories != null && Histories.Count > 0)
+            {
+                bool success=true;
+                foreach (AnnotHistory history in Histories)
+                {
+                    success &= history.Redo();
+                }
+                return success;
+            }
+            return false;
+        }
+
+        public bool Undo()
+        {
+            if (Histories != null && Histories.Count > 0)
+            {
+                bool success = true;
+                foreach (AnnotHistory history in Histories)
+                {
+                    success &= history.Undo();
+                }
+                return success;
+            }
+            return false;
+        }
+
+        public void Check(IHistory checkItem, bool undo, bool redo, bool add)
+        {
+            List<AnnotHistory> checkAnnotList= new List<AnnotHistory>();
+            if (checkItem is MultiAnnotHistory)
+            {
+                MultiAnnotHistory multiHistory = (MultiAnnotHistory)checkItem;
+                foreach (AnnotHistory checkAnnot in multiHistory.Histories)
+                {
+                    if (checkAnnot == null)
+                    {
+                        continue;
+                    }
+                    checkAnnotList.Add(checkAnnot);
+                }
+            }
+
+            if (checkItem is AnnotHistory)
+            {
+                checkAnnotList.Add((AnnotHistory)checkItem);
+            }
+
+            foreach (AnnotHistory checkAnnot in checkAnnotList)
+            {
+                if (add && checkAnnot.Action == HistoryAction.Remove)
+                {
+                    //remove
+                    SubCurrentIndex(checkAnnot.GetPageIndex(),checkAnnot.GetAnnotIndex());
+                }
+
+                if (undo && checkAnnot.Action == HistoryAction.Add)
+                {
+                    //remove
+                    SubCurrentIndex(checkAnnot.GetPageIndex(), checkAnnot.GetAnnotIndex());
+                }
+
+                if (redo && checkAnnot.Action == HistoryAction.Remove)
+                {
+                    //remove
+                    SubCurrentIndex(checkAnnot.GetPageIndex(), checkAnnot.GetAnnotIndex());
+                }
+
+                if(undo && checkAnnot.Action == HistoryAction.Remove) 
+                {
+                    //add
+                    UpdateCurrentIndex(checkAnnot.GetPageIndex(), checkAnnot.HistoryIndex, checkAnnot.GetAnnotIndex());
+                }
+
+                if(redo && checkAnnot.Action == HistoryAction.Add)
+                {
+                    //add
+                    UpdateCurrentIndex(checkAnnot.GetPageIndex(), checkAnnot.HistoryIndex, checkAnnot.GetAnnotIndex());
+                }
+            }
+        }
+
+        internal void SubCurrentIndex(int pageIndex, int annotIndex)
+        {
+            foreach (AnnotHistory annotHistory in Histories)
+            {
+                if (annotHistory.GetPageIndex() == pageIndex)
+                {
+                    int oldIndex = annotHistory.GetAnnotIndex();
+                    if (oldIndex > annotIndex || oldIndex<=-1)
+                    {
+                        annotHistory.SetAnnotIndex(oldIndex-1);
+                    }
+
+                    if (oldIndex == annotIndex)
+                    {
+                        annotHistory.SetAnnotIndex(-1);
+                    }
+                }
+            }
+        }
+
+        internal void UpdateCurrentIndex(int pageIndex,int prevIndex, int annotIndex)
+        {
+            foreach (AnnotHistory annotHistory in Histories)
+            {
+                if (annotHistory.GetPageIndex() == pageIndex && annotHistory.HistoryIndex==prevIndex)
+                {
+                    annotHistory.SetAnnotIndex(annotIndex);
+                }
+            }
+        }
+    }
+}

+ 34 - 0
Demo/Examples/ComPDFKit.Tool/UndoManger/PDFEditHistory/PDFEditHistory.cs

@@ -0,0 +1,34 @@
+using ComPDFKit.PDFPage;
+using ComPDFKitViewer.Helper;
+
+namespace ComPDFKit.Tool.UndoManger
+{
+	public class PDFEditHistory : IHistory
+	{
+		public CPDFEditPage EditPage { get; set; }
+		public int PageIndex {  get; set; }
+		public bool Redo()
+		{
+			if(EditPage!=null && EditPage.IsValid())
+			{
+				return EditPage.Redo();
+			}
+			
+			return false;
+		}
+
+		public bool Undo()
+		{
+			if(EditPage!=null && EditPage.IsValid())
+			{
+				return EditPage.Undo();
+			}
+			return false;
+		}
+
+		public void Check(IHistory changeItem, bool undo, bool redo, bool add)
+        {
+
+		}
+	}
+}