Browse Source

add: 重构切换目录(注释)

wzl 2 months ago
parent
commit
f7cc95e5a8
34 changed files with 3681 additions and 8 deletions
  1. 1 1
      jsdoc.config.json
  2. 3 3
      packages/core/src/AnnotationsB/EllipseAnnotation.js
  3. 198 4
      packages/core/src/AnnotationsB/FreeTextAnnotation.js
  4. 64 0
      packages/core/src/AnnotationsB/RichTextEditor.js
  5. 23 0
      packages/webviewer-core/src/Actions/Action.js
  6. 29 0
      packages/webviewer-core/src/Actions/Dest.js
  7. 25 0
      packages/webviewer-core/src/Actions/GoTo.js
  8. 23 0
      packages/webviewer-core/src/Actions/URI.js
  9. 16 0
      packages/webviewer-core/src/Actions/index.js
  10. 944 0
      packages/webviewer-core/src/AnnotationManager.js
  11. 384 0
      packages/webviewer-core/src/Annotations/Annotation.js
  12. 22 0
      packages/webviewer-core/src/Annotations/Border.js
  13. 24 0
      packages/webviewer-core/src/Annotations/BoxControlHandle.js
  14. 20 0
      packages/webviewer-core/src/Annotations/BoxSelectionModel.js
  15. 38 0
      packages/webviewer-core/src/Annotations/ButtonWidgetAnnotation.js
  16. 19 0
      packages/webviewer-core/src/Annotations/CheckButtonWidgetAnnotation.js
  17. 23 0
      packages/webviewer-core/src/Annotations/ChoiceWidgetAnnotation.js
  18. 115 0
      packages/webviewer-core/src/Annotations/Color.js
  19. 74 0
      packages/webviewer-core/src/Annotations/ControlHandle.js
  20. 20 0
      packages/webviewer-core/src/Annotations/EllipseAnnotation.js
  21. 50 0
      packages/webviewer-core/src/Annotations/Font.js
  22. 144 0
      packages/webviewer-core/src/Annotations/Forms/Field.js
  23. 100 0
      packages/webviewer-core/src/Annotations/Forms/FieldManager.js
  24. 12 0
      packages/webviewer-core/src/Annotations/Forms/index.js
  25. 222 0
      packages/webviewer-core/src/Annotations/FreeTextAnnotation.js
  26. 39 0
      packages/webviewer-core/src/Annotations/HTMLAnnotation.js
  27. 0 0
      packages/webviewer-core/src/Annotations/Link.js
  28. 90 0
      packages/webviewer-core/src/Annotations/RichTextEditor.js
  29. 227 0
      packages/webviewer-core/src/Annotations/SelectionModel.js
  30. 40 0
      packages/webviewer-core/src/Annotations/WidgetAnnotation.js
  31. 104 0
      packages/webviewer-core/src/Annotations/WidgetFlags.js
  32. 83 0
      packages/webviewer-core/src/Annotations/index.js
  33. 392 0
      packages/webviewer-core/src/DocumentViewer.js
  34. 113 0
      packages/webviewer-core/src/EventHandler.js

+ 1 - 1
jsdoc.config.json

@@ -1,7 +1,7 @@
 {
     "source": {
       "include": [
-        "./packages/core/src/AnnotationsB"
+        "./packages/webviewer-core/src"
       ],
       "includePattern": ".+\\.js(doc|x)?$"
     },

+ 3 - 3
packages/core/src/AnnotationsB/EllipseAnnotation.js

@@ -5,9 +5,6 @@ import MarkupAnnotation from './MarkupAnnotation';
  */
 class EllipseAnnotation extends MarkupAnnotation {
 
-  /** Gets or sets the border style of an annotation. e.g Solid, Cloudy */
-  Style: 'Solid'
-
   /**
    * Represents an ellipse annotation.
    * @extends Core.Annotations.MarkupAnnotation
@@ -16,5 +13,8 @@ class EllipseAnnotation extends MarkupAnnotation {
    */
   constructor(initializer) {
     super(initializer);
+    
+    /** Gets or sets the border style of an annotation. e.g Solid, Cloudy */
+    this.Style = 'Solid';
   }
 }

+ 198 - 4
packages/core/src/AnnotationsB/FreeTextAnnotation.js

@@ -5,6 +5,30 @@ import IPathAnnotation from './IPathAnnotation';
  */
 class FreeTextAnnotation extends IPathAnnotation {
 
+  /**
+   * An enum with possible auto-size types.
+   * @property {string} NONE - Freetext stays at fixed size
+   * @property {string} AUTO - Autosize to content
+   * @property {string} FIXED_WIDTH - Width cannot be changed
+   * @property {string} FIXED_HEIGHT - Height cannot be changed
+   */
+  static AutoSizeTypes = {
+    NONE: 'NONE',
+    AUTO: 'AUTO',
+    FIXED_WIDTH: 'FIXED_WIDTH',
+    FIXED_HEIGHT: 'FIXED_HEIGHT'
+  };
+
+  /**
+   * An enum with possible freetext intents.
+   * @property {string} FreeText - Treat as regular freetext
+   * @property {string} FreeTextCallout - Treat as callout
+   */
+  static Intent = {
+    FreeText: 'FreeText',
+    FreeTextCallout: 'FreeTextCallout'
+  };
+
   /**
    * Represents a free text annotation.
    * @extents Core.Annotations.IPathAnnotation
@@ -17,12 +41,182 @@ class FreeTextAnnotation extends IPathAnnotation {
    * @property {string} Intent.FreeText - Should treat as regular freetext
    * @property {string} Intent.FreeTextCallout - Should treat as callout
    */
-  constructor(intent, initializer) {
+  constructor(intent = null, initializer = {}) {
     super(initializer);
     this.EXPORT_CALCULATED_FONT_SIZE = true;
-    this.Intent = {
-      FreeText: '',
-      FreeTextCallout: ''
+    this.Intent = intent || FreeTextAnnotation.Intent.FreeText;
+
+    Object.keys(initializer).forEach(key => {
+      if (this.hasOwnProperty(key)) {
+        this[key] = initializer[key];
+      }
+    });
+
+    /**
+     * Object that contains all the flags to customize the auto-sizing behavior
+     * @property {boolean} shrinkWidth - Whether to shrink the width of the annotation if the size of its contents are smaller in width than the annotation
+     * @property {boolean} shrinkHeight - Whether to shrink the height of the annotation if the size of its contents are smaller in height than the annotation
+     * @property {boolean} expandWidth - Whether to expand the width of the annotation if the size of the contents are larger in width than the annotation
+     * @property {boolean} expandHeight - Whether to expand the height of the annotation if the size of the contents are larger in height than the annotation
+     */
+    this.autoSizeProperties = {
+      shrinkWidth: false,
+      shrinkHeight: true,
+      expandWidth: false,
+      expandHeight: true
+    };
+
+    /** The font to use for the annotation's text. */
+    this.Font = '';
+
+    /** The font size to use for the annotation's text, specified like a CSS font size. */
+    this.FontSize = 14;
+
+    /** The horizontal alignment of the annotation's text (left, right, center) */
+    this.TextAlign = 'left';
+
+    /** The color of the text in the annotation. */
+    this.TextColor = '';
+
+    /** The vertical alignment of the annotation's text (top, bottom, center) default: top */
+    this.TextVerticalAlign = 'top';
+  }
+
+  /** Disables the Enter key for the instance of RichTextEditor */
+  disableEnterKeypress() {}
+
+  /** Enables the Enter key for the instance of RichTextEditor */
+  enableEnterKeypress() {}
+
+  /**
+   * Resize the annotation's text box to fit its contents
+   * @param {Core.Document.PageInfo} pageInfo - An object representing the page info. Contains the properties "width" and "height".
+   * @param {object} pageMatrix - The transformation matrix for the page that the annotation is on.
+   * @param {number} pageRotation - The internal degrees of rotation of the current page.
+   * @example
+const freetextAnnot = annotManager.getAnnotationsList()[0];
+const doc = docViewer.getDocument();
+const pageNumber = 1;
+const pageInfo = doc.getPageInfo(pageNumber);
+const pageMatrix = doc.getPageMatrix(pageNumber);
+const pageRotation = doc.getPageRotation(pageNumber);
+freetextAnnot.fitText(pageInfo, pageMatrix, pageRotation);
+annotManager.drawAnnotationsFromList([freetextAnnot]);
+   */
+  fitText(pageInfo, pageMatrix, pageRotation) {}
+
+  /**
+   * Gets the auto-sizing method if any.
+   * @return {string} The type of autosizing method
+   */
+  getAutoSizeType() {}
+
+  /**
+   * Gets the calculated font size when auto-sizing the text font size to fit the free text annotation's bounding box. This method retrieves the font size that has been dynamically calculated to fit the content within the bounding box of the free text annotation when Core.Annotations.FreeTextAnnotation#switchToAutoFontSize is used to enable auto-sizing of the font size. If Core.Annotations.FreeTextAnnotation#switchToAutoFontSize has not been called, it will return the current font size.
+   * @return {string} The calculated font size of the annotation, including the unit (e.g., "16px").
+   * @example
+WebViewer(...)
+.then(instance => {
+  const { annotationManager, Annotations } = instance.Core;
+
+  annotationManager.addEventListener('annotationSelected', (annotations, action) => {
+    if(action === 'selected' && annotations[0] instanceof Annotations.FreeTextAnnotation) {
+      annotations[0].switchToAutoFontSize();
+    }
+  });
+
+  annotationManager.addEventListener('annotationChanged', (annotations, action) => {
+    if(action === 'modify' && annotations[0] instanceof Annotations.FreeTextAnnotation) {
+      console.log(annotations[0].getCalculatedFontSize())
     }
+  });
+});
+   */
+  getCalculatedFontSize() {}
+
+  /**
+   * Gets the date format of the FreeText (if any)
+   * @return {string} Returns Date format
+   */
+  getDateFormat() {}
+
+  /** @return {Core.Annotations.FreeTextAnnotation.RichTextEditor} Returns the rich text editor instance associated with the annotation */
+  getEditor() {}
+
+  /**
+   * Gets the intent of the FreeText annotation e.g. FreeTextCallout
+   * @return {string} The intent of the annotation
+   */
+  getIntent() {
+    return this.Intent;
   }
+
+  /**
+   * Get the rectangle difference of the annotation bounding rect and the text rect
+   * @return {Core.Math.Rect} the rectangle difference
+   */
+  getPadding() {}
+
+  /**
+   * Gets the start style of the line. Only applicable for callout annotations
+   * @return {string} The start style
+   */
+  getStartStyle() {}
+
+  /** @return {boolean} Returns a boolean which indicates if the bounding box of the freetext will be resized automatically when its contents are being edited */
+  isAutoSized() {}
+
+  /** @return {boolean} Returns whether the current font is Automatically sized */
+  isAutoSizeFont() {}
+
+  /**
+   * Sets how the freetext annotation auto-sizes to content. If you are passing 'AUTO' as the parameter, you will need to invoke the Core.Annotations.FreeTextAnnotation#fitText method afterwards.
+   * @param {string} type - Use one of the types provided by AutoSizeTypes
+   */
+  setAutoSizeType(type) {}
+
+  /**
+   * Set FreeText as a date type annotation with the specified format. A viewer can use this information to allow easier updating of the text to represent a date, for example by using a date picker.
+   * @param {string} format - Set Annotation Date format
+   */
+  setDateFormat(format) {}
+
+  /**
+   * Sets the intent of the FreeText annotation
+   * @param {string} intent - The new intent for the annotation
+   */
+  setIntent(intent) {
+    this.Intent = intent;
+  }
+
+  /**
+   * Sets the rectangle difference of the annotation bounding rect and the text rect
+   * @param {Core.Math.Rect} rect - The new rectangle difference
+   */
+  setPadding(rect) {}
+
+  /**
+   * Sets the start style of the line. Only applicable for callout annotations.
+   * @param {string} startStyle - The starting style
+   */
+  setStartStyle(startStyle) {}
+
+  /**
+   * Sets the font size of the free text annotation to automatically adjust to fit its bounding box when resized. This method is used to switch the font size behavior of the free text annotation to "auto," which means that the font size will adapt dynamically to the size of the annotation's bounding box when it is resized.
+   * @example
+WebViewer(...)
+.then(instance => {
+  const { documentViewer, annotationManager, Annotations } = instance.Core;
+
+  documentViewer.addEventListener('annotationsLoaded', () => {
+    const annotList = annotationManager.getAnnotationsList();
+    annotList.forEach(annot => {
+      if (annot instanceof Annotations.FreeTextAnnotation) {
+        annot.switchToAutoFontSize();
+      }
+    });
+  });
+});
+   */
+  switchToAutoFontSize() {}
 }

+ 64 - 0
packages/core/src/AnnotationsB/RichTextEditor.js

@@ -0,0 +1,64 @@
+/**
+ * A class that represents controls used for editing contents of a freetext annotation
+ * @memberof Core.Annotations.FreeTextAnnotation
+ */
+class RichTextEditor {
+
+  /**
+   * Gets the Quill editor options.
+   * @return {Core.Annotations.FreeTextAnnotation.RichTextEditor.QuillEditorOptions} Quill options that are currently set.
+   */
+  static getEditorOptions() {}
+
+  /**
+   * Sets the Quill editor options.
+   * @param {Core.Annotations.FreeTextAnnotation.RichTextEditor.QuillEditorOptions} options - Quill options to be set.
+   */
+  static setEditorOptions(options) {}
+
+  /** Blurs the editor. */
+  blur() {}
+
+  /**
+   * Delete some content from the editor.
+   * @param {number} index - the start index of the range
+   * @param {number} length - the length of the range
+   */
+  deleteText(index, length) {}
+
+  /**
+   * Format text at the current selection. If the current selection has length of 0, then the format will be set active.
+   * @param {Core.Annotations.FreeTextAnnotation.RichTextEditor.Format} format 
+   * @param {Core.Annotations.FreeTextAnnotation.RichTextEditor.FormatValue} value 
+   */
+  format(format, value) {}
+
+  /**
+   * Gets the current contents in the editor
+   * @return {string} Returns the current contents in the editor
+   */
+  getContents() {}
+}
+
+/**
+ * The available formats of an editor instance
+ * @typedef {'color' | 'bold' | 'italic' | 'underline' | 'strike'} Core.Annotations.FreeTextAnnotation.RichTextEditor.Format
+ */
+
+/**
+ * The value of a format. The value should be a hex color string(e.g. #FF00FF) for the `color` format, and boolean for the rest of formats.
+ * @typedef {string | boolean} Core.Annotations.FreeTextAnnotation.RichTextEditor.FormatValue
+ */
+
+/**
+ * @typedef {object} Core.Annotations.FreeTextAnnotation.RichTextEditor.QuillEditorOptions
+ * @property {Element} bounds - DOM Element or a CSS selector for a DOM Element, within which the editor’s UI elements (i.e. tooltips, etc.) should be confined. Currently, it only considers left and right boundaries.
+ * @property {string} debug - Shortcut for debug. Note debug is a static method and will affect other instances of Quill editors on the page. Only warning and error messages are enabled by default.
+ * @property {object} formats - [WARNING: this is not fully supported by WebViewer and may result in unexpected behavior] Whitelist of formats to allow in the editor. See Formats for a complete list.
+ * @property {object} modules - Collection of modules to include and respective options. See Modules for more information.
+ * @property {string} placeholder - Placeholder text to show when editor is empty.
+ * @property {boolean} readOnly - Whether to instantiate the editor in read-only mode.
+ * @property {Element} scrollingContainer - DOM Element or a CSS selector for a DOM Element, specifying which container has the scrollbars (i.e. overflow-y: auto), if is has been changed from the default ql-editor with custom CSS. Necessary to fix scroll jumping bugs when Quill is set to auto grow its height, and another ancestor container is responsible from the scrolling.
+ * @property {string} theme - [WARNING: this is not fully supported by WebViewer and may result in unexpected behavior] Themes to use for the editor.
+ * @property {boolean} shouldScrollContainer - Whether to scroll the container when text exceeds the Text Box.
+ */

+ 23 - 0
packages/webviewer-core/src/Actions/Action.js

@@ -0,0 +1,23 @@
+/**
+ * A generic action.
+ * @memberof Core.Actions
+ */
+class Action {
+  /**
+   * @param {?object} options - A map of properties to set on the Action
+   * @property {string} name - Get the action's name.
+   */
+  constructor(options) {
+    this.name = options?.name || '';
+  }
+
+  /**
+   * Called when the action is triggered.
+   * @param {Annotations.Forms.Field|Core.DocumentViewer|Core.Bookmark} owner - The dispatcher to which this action is attached
+   * @param {object} event - The PDFJS event to use when executing the action
+   * @param {Core.DocumentViewer} documentViewer - The DocumentViewer to use as context for the action execution
+   */
+  onTriggered(owner, event, documentViewer) {}
+}
+
+export default Action;

+ 29 - 0
packages/webviewer-core/src/Actions/Dest.js

@@ -0,0 +1,29 @@
+/**
+ * A PDF Destination object, representing a location and fitting strategy.
+ * @memberof Core.Actions
+ */
+class Dest {
+  /**
+   * @param {?object} options - A map of properties to initialize on the Dest object
+   * @property {number} page - Gets and sets the 1-based page number to go to.
+   * @property {string} fit - Gets the fit style (One of XYZ, FitBH, FitH, FitBV, FitV, FitR, FitB, Fit)
+   * @property {number|undefined} top - Gets the top of the destination rectangle
+   * @property {number|undefined} left - Gets the left hand side of the destination rectangle
+   * @property {number|undefined} bottom - Gets the bottom of the destination rectangle
+   * @property {number|undefined} right - Gets the right hand side of the destination rectangle
+   * @property {number|undefined} zoom - Gets the zoom at which to view the destination
+   * @property {string|undefined} name - Gets the name of the destination
+   */
+  constructor(options) {
+    this.page = options?.page || 1;
+    this.fit = options?.fit || '';
+    this.top = options?.top;
+    this.left = options?.left;
+    this.bottom = options?.bottom;
+    this.right = options?.right;
+    this.zoom = options?.zoom;
+    this.name = options?.name;
+  }
+}
+
+export default Dest;

+ 25 - 0
packages/webviewer-core/src/Actions/GoTo.js

@@ -0,0 +1,25 @@
+import Action from './Action';
+import Dest from './Dest';
+
+/**
+ * A GoTo Action. Navigates to a specific page of the document with various fit options.
+ * @memberof Core.Actions
+ */
+class GoTo extends Action {
+
+    /**
+     * @extends Core.Actions.Action
+     * @param {?object} options - A map of properties to set on the Action.
+     * @property {Core.Actions.Dest} dest - Gets the destination object.
+     */
+    constructor(options) {
+        super(options);
+        this.dest = options?.dest ?? new Dest();
+    }
+
+    onTriggered(owner, event, documentViewer) {
+        super.onTriggered(owner, event, documentViewer);
+    }
+}
+
+export default GoTo;

+ 23 - 0
packages/webviewer-core/src/Actions/URI.js

@@ -0,0 +1,23 @@
+import Action from './Action';
+
+/**
+ * An URI Action.
+ * @memberof Core.Actions
+ */
+class URI extends Action {
+    /**
+     * @extends Core.Actions.Action
+     * @param {?object} options - A map of properties to set on the Action
+     * @property {string} uri - The URI to launch when the action is triggered
+     */
+    constructor(options) {
+        super(options);
+        this.uri = options?.uri || '';
+    }
+
+    onTriggered(owner, event, documentViewer) {
+        window.open(this.uri, '_blank');
+    }
+}
+
+export default URI;

+ 16 - 0
packages/webviewer-core/src/Actions/index.js

@@ -0,0 +1,16 @@
+/** 
+ * The namespace for anything to do with PDF actions and action dispatch. Actions can be defined by providing a JavaScript object that has the desired properties, and a name property defining the action subtype it represents. See documentation for specific action types for allowable properties.
+ * @memberof Core
+ * @namespace
+ */
+const Actions = {
+}
+
+/**
+ * The onTriggered function for actions.
+ * @typedef {function(Core.Annotations.Forms.Field|Core.DocumentViewer, object, Core.DocumentViewer)} Core.Actions.ActionOnTriggeredHandler
+ * @param {Core.Annotations.Forms.Field|Core.DocumentViewer} target - The dispatcher to which this action is attached
+ * @param {object} event - The embedded JS event that is used when executing the action
+ * @param {Core.DocumentViewer} documentViewer - The DocumentViewer to use as context for the action execution
+ */
+function ActionOnTriggeredHandler(target, event, documentViewer) {}

+ 944 - 0
packages/webviewer-core/src/AnnotationManager.js

@@ -0,0 +1,944 @@
+import EventHandler from './EventHandler';
+import annotationStore from "./annotation_store";
+
+/**
+ * Represents an object that manages the Annotations that appear on a Document's pages when displayed in a DocumentViewer.
+ * @memberof Core
+ */
+class AnnotationManager extends EventHandler {
+  /**
+   * Creates a new instance of AnnotationManager.
+   * @extends Core.EventHandler
+   * @param {Core.DocumentViewer} docViewer
+   */
+  constructor(docViewer) {
+    super();
+    this.docViewer = docViewer;
+    this.annotations = null;
+
+    this.addEventListener('annotationChange', this.handleAnnotationChange.bind(this));
+  }
+
+  /**
+   * The different action types for the annotationChanged event.
+   * @property {string} ADD - When the annotationChanged event triggered due to adding annotations
+   * @property {string} DELETE - When the annotationChanged event triggered due to deleting annotations
+   * @property {string} MODIFY - When the annotationChanged event triggered due to modifying annotations
+   */
+  static get AnnotationChangedActions() {
+    return {
+      ADD: 'ADD',
+      DELETE: 'DELETE',
+      MODIFY: 'MODIFY'
+    };
+  }
+
+  /**
+   * @property {string} ANNOTATION_SELECTED - [Core.AnnotationManager.annotationSelected]{@link Core.AnnotationManager#event:annotationSelected}
+   * @property {string} ANNOTATION_DESELECTED - [Core.AnnotationManager.annotationDeselected]{@link Core.AnnotationManager#event:annotationDeselected}
+   * @property {string} ANNOTATION_DOUBLE_CLICKED - [Core.AnnotationManager.annotationDoubleClicked]{@link Core.AnnotationManager#event:annotationDoubleClicked}
+   * @property {string} ANNOTATION_CHANGED - [Core.AnnotationManager.annotationChanged]{@link Core.AnnotationManager#event:annotationChanged}
+   * @property {string} ANNOTATION_NUMBERING_UPDATED - [Core.AnnotationManager.annotationNumberingUpdated]{@link Core.AnnotationManager#event:annotationNumberingUpdated}
+   * @property {string} UPDATE_ANNOTATION_PERMISSION - [Core.AnnotationManager.updateAnnotationPermission]{@link Core.AnnotationManager#event:updateAnnotationPermission}
+   * @property {string} ANNOTATIONS_DRAWN - [Core.AnnotationManager.annotationsDrawn]{@link Core.AnnotationManager#event:annotationsDrawn}
+   * @property {string} ANNOTATION_HIDDEN - [Core.AnnotationManager.annotationHidden]{@link Core.AnnotationManager#event:annotationHidden}
+   * @property {string} SET_NOTE_TEXT - [Core.AnnotationManager.setNoteText]{@link Core.AnnotationManager#event:setNoteText}
+   * @property {string} ADD_REPLY - [Core.AnnotationManager.addReply]{@link Core.AnnotationManager#event:addReply}
+   * @property {string} DELETE_REPLY - [Core.AnnotationManager.deleteReply]{@link Core.AnnotationManager#event:deleteReply}
+   * @property {string} FIELD_CHANGED - [Core.AnnotationManager.fieldChanged]{@link Core.AnnotationManager#event:fieldChanged}
+   * @property {string} FILE_ATTACHMENT_DATA_AVAILABLE - [Core.AnnotationManager.fileAttachmentDataAvailable]{@link Core.AnnotationManager#event:fileAttachmentDataAvailable}
+   */
+  static get Events() {
+    return {
+      ANNOTATION_SELECTED: 'ANNOTATION_SELECTED',
+      ANNOTATION_DESELECTED: 'ANNOTATION_DESELECTED',
+      ANNOTATION_DOUBLE_CLICKED: 'ANNOTATION_DOUBLE_CLICKED',
+      ANNOTATION_CHANGED: 'ANNOTATION_CHANGED',
+      ANNOTATION_NUMBERING_UPDATED: 'ANNOTATION_NUMBERING_UPDATED',
+      UPDATE_ANNOTATION_PERMISSION: 'UPDATE_ANNOTATION_PERMISSION',
+      ANNOTATIONS_DRAWN: 'ANNOTATIONS_DRAWN',
+      ANNOTATION_HIDDEN: 'ANNOTATION_HIDDEN',
+      SET_NOTE_TEXT: 'SET_NOTE_TEXT',
+      ADD_REPLY: 'ADD_REPLY',
+      DELETE_REPLY: 'DELETE_REPLY',
+      FIELD_CHANGED: 'FIELD_CHANGED',
+      FILE_ATTACHMENT_DATA_AVAILABLE: 'FILE_ATTACHMENT_DATA_AVAILABLE'
+    };
+  }
+
+  /**
+   * Represents the types of rotations available to perform on an annotation.
+   * @property {string} SNAP_ROTATION - Rotate an annotation in discrete steps, such as 45 or 90 degrees
+   * @property {string} FREEFORM_ROTATION - Rotate an annotation continuously
+   */
+  static get RotationTypes() {
+    return {
+      SNAP_ROTATION: 'SNAP_ROTATION',
+      FREEFORM_ROTATION: 'FREEFORM_ROTATION'
+    };
+  }
+
+  /**
+   * Represents the types of tabbing orders available to set on widgets in the document.
+   * @property {string} STRUCTURE - Order widgets according to the order found in the internal structure. This is default
+   * @property {string} ROW - Order widgets in terms of rows
+   * @property {string} COLUMN - Order widgets in terms of columns
+   */
+  static get TabbingOrders() {
+    return {
+      STRUCTURE: 'STRUCTURE',
+      ROW: 'ROW',
+      COLUMN: 'COLUMN'
+    };
+  }
+
+  async handleAnnotationChange(data) {
+    let annotation = data.annotation
+    this.annotationHistory.push(annotation)
+    const formTypes = ['textfield', 'checkbox', 'radiobutton', 'listbox', 'combobox', 'pushbutton', 'signatureFields']
+    const markupTypes = ['highlight', 'underline', 'strikeout', 'squiggly']
+    if (data.type === 'add') {
+      if (!Array.isArray(annotation)) {
+        annotation = [annotation]
+      }
+      const length = annotation.length
+      for (let i = 0; i < length; i++) {
+        if (Number(annotation[i].pageIndex) + 1 > this.pagesCount) continue
+        if (formTypes.includes(annotation[i].type)) {
+          annotation[i].operate = "add-form"
+        } else {
+          annotation[i].operate = "add-annot"
+          annotation[i].author = this.annotator
+        }
+
+        if (markupTypes.includes(annotation[i].type)) {
+          const quadPoints = []
+          if (!annotation[i].quadPoints[0].PointX && !annotation[i].quadPoints[0].PointY) {
+            for (let j = 0; j < annotation[i].quadPoints.length; j++) {
+              const quadPoint = annotation[i].quadPoints[j]
+              const left = quadPoint.left
+              const top = quadPoint.top
+              const right = quadPoint.right
+              const bottom = quadPoint.bottom
+
+              quadPoints.push({
+                PointX: left,
+                PointY: top,
+              })
+              quadPoints.push({
+                PointX: right,
+                PointY: top,
+              })
+              quadPoints.push({
+                PointX: left,
+                PointY: bottom,
+              })
+              quadPoints.push({
+                PointX: right,
+                PointY: bottom,
+              })
+            }
+            annotation[i].quadPoints = quadPoints
+          }
+        }
+
+        const result = await this.handleAnnotations(annotation[i])
+        if (result && result.code) return false
+        if (this.docViewer.pdfViewer) {
+          this.docViewer.pdfViewer.renderAnnotation(annotation[i], !!data.show)
+        }
+        annotation[i].targetPage = annotation[i].pageIndex * 1 + 1
+        let currentAnnotation = JSON.parse(JSON.stringify(annotation[i]))
+        if (currentAnnotation.inklist) {
+          currentAnnotation = this.handleInk(currentAnnotation)
+        }
+      }
+    } else {
+      const annotations = this.annotations[annotation.pageIndex]
+      if (!annotations) return
+      const index = findIndex(annotation.name, annotations) 
+      if (data.type === 'delete') {
+        const annot = annotations[index]
+        let result = null
+        if (annot.replies?.length) {
+          result = await this.messageHandler.sendWithPromise('RemoveFromPageIncludeReplyAnnot', {
+            pagePtr: annot.pagePtr,
+            annotPtr: annotation.annotPtr
+          })
+        } else {
+          result = await this.messageHandler.sendWithPromise('RemoveAnnot', {
+            annotPtr: annotation.annotPtr
+          })
+        }
+
+        if (result && result.code) {
+          console.log(result.message)
+          alert(result.message)
+          return
+        }
+        annotations.splice(index, 1)
+        if (!annotations.length) {
+          delete this.annotations[annotation.pageIndex]
+        }
+      } else if (data.type === 'empty') {
+        annotations.splice(index, 1)
+        if (!annotations.length) {
+          delete this.annotations[annotation.pageIndex]
+        }
+        return
+      } else {
+        annotation.doc = this.doc
+        const result = await this.messageHandler.sendWithPromise('EditAnnotation', { annotation })
+
+        if (result && result.code) {
+          console.log(result.message)
+          alert(result.message)
+          return false
+        }
+        const rawAnnotation = annotations[index]
+        annotations[index] = {
+          ...rawAnnotation,
+          ...annotation
+        }
+      }
+      annotation.targetPage = annotation.pageIndex + 1
+
+      let currentAnnotation = annotation
+      if (currentAnnotation.inklist) {
+        currentAnnotation = this.handleInk(currentAnnotation)
+      }
+    }
+
+    this.annotationChanged(this.annotations);
+    return true
+  }
+
+  async handleAnnotations(annotation, init = false) {
+    if (!this.annotations) {
+      this.annotations = {}
+    }
+    if (init) {
+      this.initAddAnnotations(annotation)
+    } else {
+    if (Number(annotation.pageIndex) + 1 > this.pagesCount) return
+    this.pushAnnotations(annotation)
+      if ('destPage' in annotation) {
+        annotation.doc = this.doc
+        annotation.pageHeight = this.pagesPtr[annotation.pageIndex].height
+      }
+      const pagePtr = this.pagesPtr[annotation.pageIndex].pagePtr
+      const result = await this.messageHandler.sendWithPromise('CreateAnnotation', {
+        doc: this.doc,
+        pagePtr,
+        annotation
+      })
+
+      if (result && result.code) {
+        console.log(result.message)
+        alert(result.message)
+        return result
+      }
+
+      const { annotPtr } = result
+
+      annotation.pagePtr = pagePtr
+      annotation.annotPtr = annotPtr
+    }
+  }
+
+  pushAnnotations(annotation) {
+    const pageIndex = annotation.pageIndex
+    const annotations = this.annotations
+    if (!annotations[pageIndex]) {
+      annotations[pageIndex] = []
+      this.docViewer.pdfViewer._pages[pageIndex].annotations = annotations[pageIndex]
+    }
+    if (!annotationStore.annotationsAll) {
+      annotationStore.annotationsAll = annotations
+    }
+    annotations[pageIndex].push(annotation)
+  }
+
+  initAddAnnotations(annotation) {
+    const pageIndex = annotation.pageIndex
+    const annotations = this.annotations
+    if (!annotations[pageIndex]) {
+      annotations[pageIndex] = []
+    }
+    if (typeof annotation.index !== 'undefined') {
+      annotations[pageIndex][annotation.index] = annotation
+    } else {
+      annotations[pageIndex].push(annotation)
+    }
+    if (!annotationStore.annotationsAll) {
+      annotationStore.annotationsAll = annotations
+    }
+  }
+
+  // 处理回复注释
+  async handleReplyAnnotation(data) {
+    const { type, annotation, reply, reviewAnnotState, markedAnnotState } = data
+    const { pagePtr, annotPtr } = annotation
+
+    const annot = this.annotations[annotation.pageIndex].find(item => item?.name === annotation.name)
+    if (!annot) return
+    
+    if (type === 'add') {
+      if (reply) { // 添加回复
+        reply.name = uuidv4()
+        await this.messageHandler.sendWithPromise('CreateReplyAnnotation', {
+          pagePtr,
+          annotPtr,
+          reply
+        })
+        const { replies, markedAnnotState, reviewAnnotState } = await this.messageHandler.sendWithPromise('GetReplyAnnotation', {
+          pagePtr,
+          annotPtr
+        })
+        if (replies.length) {
+          replies.forEach(reply => reply.name = uuidv4())
+          annot.replies = replies
+        }
+        if (reviewAnnotState) annot.reviewAnnotState = reviewAnnotState
+        if (markedAnnotState) annot.markedAnnotState = markedAnnotState
+      }
+
+      if (reviewAnnotState || markedAnnotState) { // 添加有状态回复(首次设置注释状态)
+        const newState = await this.messageHandler.sendWithPromise('CreateReplyStateAnnotation', {
+          pagePtr,
+          annotPtr,
+          state: reviewAnnotState || markedAnnotState
+        })
+        if (markedAnnotState) annot.markedAnnotState = newState
+        else annot.reviewAnnotState = newState
+      }
+
+      this.addReply(annotation, reply);
+
+    } else if (type === 'delete') {
+      await this.messageHandler.sendWithPromise('RemoveAnnot', {
+        annotPtr: reply.annotPtr
+      })
+      annot.replies = annot.replies.filter(item => item.name !== reply.name)
+      annot.replies.forEach(item => {
+        if (item.index > reply.index) {
+          item.index--
+        }
+      })
+
+      this.deleteReply(reply, annot);
+
+    } else if (type === 'edit') {
+      if (markedAnnotState || reviewAnnotState) { // 修改注释状态
+        const newState = await this.messageHandler.sendWithPromise('SetState', {
+          reviewAnnotState,
+          markedAnnotState
+        })
+        if (markedAnnotState) {
+          annot.markedAnnotState = newState
+        }
+        if (reviewAnnotState) {
+          annot.reviewAnnotState = newState
+        }
+      } else { // 修改回复内容
+        this.messageHandler.sendWithPromise('EditReplyAnnotation', {
+          reply
+        })
+        const index = annot.replies.findIndex(obj => obj.name === reply.name)
+        const replyItem = annot.replies[index]
+        annot.replies[index] = {
+          ...replyItem,
+          ...reply
+        }
+      }
+    }
+
+    this.annotationChanged(this.annotations);
+  }
+
+  async exportXfdf(download = false) {
+    let xfdfString = ''
+    const result = await this.messageHandler.sendWithPromise('XFDFExportAnnotations', {
+      doc: this.doc
+    })
+
+    if (result && result.code) {
+      console.log(result.message)
+      alert(result.message)
+      return false
+    }
+
+    xfdfString = result.Content
+
+    if (download) {
+      const name = this._docName.replace(/\.pdf$/, '.xfdf')
+      const blob = new Blob([xfdfString], { type: 'application/xml' })
+      saveAs(blob, name)
+      return true
+    } else {
+      return xfdfString
+    }
+  }
+
+  async importAnnotations(data) {
+    const source = await new Promise((resolve) => {
+      const reader = new FileReader();
+      reader.onload = (event) => {
+        const contents = event.target.result
+        resolve(contents)
+      };
+
+      if (this.webviewerServer) {
+        reader.readAsText(data);
+      } else {
+        reader.readAsArrayBuffer(data);
+      }
+    });
+    const xfdfBuffer = new Uint8Array(source)
+    const result = await this.messageHandler.sendWithPromise('XFDFImportAnnotations', {
+      doc: this.doc,
+      xfdfBuffer
+    })
+
+    if (result && result.code) {
+      console.log(result.message)
+      alert(result.message)
+      return false
+    }
+
+    this.emptyAnnotations()
+    this.reRenderAnnotations()
+    // for (let i = 0; i < len; i++) {
+    //   const item = annotationsXml[i]
+    //   const data = {
+    //     annotationType: 3,
+    //     fontSize: 10,
+    //     value: item.obj_attr.content,
+    //     rotation: 0,
+    //     position: item.obj_attr.position,
+    //     type: 'custom'
+    //   }
+    //   this.pdfViewer._pages[annotationsXml[i].obj_attr.page].pdfAnnotationLayer.deserialize(data)
+    // }
+  }
+
+  emptyAnnotations() {
+    const annotations = this.annotations
+    if (this.annotations) {
+      for (let pageIndex in annotations) {
+        this.docViewer.pdfViewer._pages[pageIndex].emptyAnnotations()
+      }
+    }
+  }
+
+  async reRenderAnnotations() {
+    this.annotations = null
+    const annotations = []
+    const pagesPtr = this.pagesPtr
+
+    for (let pageIndex = 0; pageIndex < pagesPtr.length; pageIndex++) {
+      const pagePtr = pagesPtr[pageIndex]
+      const data = await this.messageHandler.sendWithPromise('InitAnnot', {
+        pageIndex,
+        pagePtr: pagePtr.pagePtr
+      })
+      for (let annotCountNum = 0; annotCountNum < data.annotCount; annotCountNum++) {
+        const annotation = data.annotations[annotCountNum]
+        const typeInt = annotation.type
+        const type = AnnotationTypeString[typeInt]
+        annotation.pageIndex = pageIndex
+        annotation.type = type
+        let attr = null
+        if (type === 'widget') {
+          attr = await this.messageHandler.sendWithPromise('GetWidgetAnnotation', {
+            doc: this.doc,
+            annotPtr: annotation.annotPtr,
+            pagePtr: annotation.pagePtr,
+            typeInt
+          })
+        } else {
+          attr = await this.messageHandler.sendWithPromise('GetAnnotation', {
+            doc: this.doc,
+            annotPtr: annotation.annotPtr,
+            pagePtr: annotation.pagePtr,
+            typeInt,
+            scale: this.scale
+          })
+          if (attr && attr.replies && attr.replies.length) attr.replies.forEach(reply => reply.name = uuidv4())
+        }
+        Object.assign(annotation, attr)
+        annotations.push(annotation)
+      }
+    }
+
+    for (let index = 0; index < annotations.length; index++) {
+      if (!this.annotations) {
+        this.annotations = {}
+      }
+      this.pushAnnotations(annotations[index])
+
+      this.docViewer.pdfViewer.renderAnnotation(annotations[index])
+    }
+    this.annotationChanged(this.annotations);
+  }
+
+  handleInk(annotation) {
+    let rawAnnotation = JSON.parse(JSON.stringify(annotation))
+    let inklist = ''
+    let inkArray = []
+    if (Array.isArray(rawAnnotation.inklist)) {
+      inkArray = rawAnnotation.inklist
+    }
+    else {
+      inkArray.push(rawAnnotation.inklist)
+    }
+    for (let i = 0; i < inkArray.length; i++) {
+      inklist = inklist + inkArray[i].replaceAll(';', ',') + '//'
+    }
+    rawAnnotation.inklist = inklist// rawAnnotation.inklist.replaceAll(';', ',') + '//'
+    return rawAnnotation
+  }
+
+  initAnnotations(annotations, render = false) {
+    for (let index = 0; index < annotations.length; index++) {
+      const result = this.handleAnnotations(annotations[index], true)
+      if (result && result.code) return false
+      render && this.docViewer.pdfViewer.renderAnnotation(annotations[index])
+    }
+    this.annotationChanged(this.annotations);
+  }
+
+  /**
+   * Adds the specified annotation to the managed list of annotations.
+   * @param {Core.Annotations.Annotation|Array.<Core.Annotations.Annotation>} annotation - An array of annotations.
+   * @param {object} [options] - An object that can contain the following optional parameters.
+   * @param {boolean} [options.imported=false] - Whether the annotations were imported from another source or not
+   * @param {boolean} [options.isUndoRedo=false] - Whether the annotation change was caused by undo/redo or not
+   * @param {boolean} [options.autoFocus=false] - Whether the annotation's text input should be automatically focused if applicable. Relevant to FreeText and Callout annotations.
+   * @param {string} [options.source] - What type of action cause this event (i.e. "redactionApplied")
+   */
+  addAnnotation(annotation, options) {
+    if (!Array.isArray(annotation)) {
+      annotation = [annotation];
+    }
+    this.addAnnotations(annotation);
+  }
+
+  /**
+   * Adds the specified annotations to the managed list of annotations.
+   * @param {Array.<Core.Annotations.Annotation>} annotations - An array of annotations.
+   * @param {object} [options] - An object that can contain the following optional parameters.
+   * @param {boolean} [options.imported=false] - Whether the annotations were imported from another source or not
+   * @param {boolean} [options.isUndoRedo=false] - Whether the annotation change was caused by undo/redo or not
+   * @param {boolean} [options.autoFocus=false] - Whether the annotation's text input should be automatically focused if applicable. Relevant to FreeText and Callout annotations.
+   * @param {string} [options.source] - What type of action cause this event (i.e. "redactionApplied")
+   */
+  addAnnotations(annotations, options) {
+    annotations.forEach(item => {
+      this.handleAnnotationChange({ type: 'add', annotation: item })
+    })
+    for (let i = 0; i < annotations.length; i++) {
+      this.annotations.push(annotations[i]);
+    }
+  }
+
+  /**
+   * Whether or not the current user can modify the annotation.
+   * @param {Core.Annotations.Annotation} annotation - The annotation to check permissions on.
+   * @returns {boolean} Whether the annotation can be modified or not.
+   */
+  canModify(annotation) {
+    return !annotation.ReadOnly;
+  }
+
+  /**
+   * Whether or not the current user can modify the annotation's contents.
+   * @param {Core.Annotations.Annotation} annotation - The annotation to check permissions on.
+   * @returns {boolean} Whether the annotation's contents can be modified or not.
+   */
+  canModifyContents(annotation) {
+    return !annotation.ReadOnly;
+  }
+
+  /**
+   * Creates an annotation that replies to the passed in annotation. Annotation replies are sticky note annotations.
+   * @param {Core.Annotations.Annotation} annotation - The annotation to add a reply to
+   * @param {string} initialText - The initialText for the annotation, defaults to the empty string
+   * @returns {Core.Annotations.StickyAnnotation} The created annotation reply
+   */
+  createAnnotationReply(annotation, initialText = '') {
+    const reply = {
+      operate: 'add-annot',
+      type: 'reply',
+      author: this.docViewer.annotator.value,
+      title: this.docViewer.annotator.value,
+      contents: initialText,
+      date: new Date(),
+      pageIndex: selectedAnnot.value.pageIndex,
+      index: selectedAnnot.value.replies?.length || 0
+    }
+    this.handleReplyAnnotation({
+      type: 'add',
+      annotation: [annotation],
+      reply: [reply]
+    })
+    return reply;
+  }
+
+  /**
+   * Deletes the specified annotation in the managed list of annotations. If an annotation is successfully deleted, the annotationChanged event will be fired with a "delete" action.
+   * @param {Core.Annotations.Annotation|Array.<Core.Annotations.Annotation>} annotation - An instance of Annotation.
+   * @param {object} [options] - An object that can contain the following optional parameters.
+   * @param {boolean} [options.imported=false] - Whether the annotations were imported from another source or not
+   * @param {boolean} [options.isUndoRedo=false] - Whether the annotation change was caused by undo/redo or not
+   * @param {boolean} [options.autoFocus=false] - Whether the annotation's text input should be automatically focused if applicable. Relevant to FreeText and Callout annotations.
+   * @param {string} [options.source] - What type of action cause this event (i.e. "redactionApplied")
+   */
+  deleteAnnotation(annotation, options) {
+    if (!Array.isArray(annotation)) {
+      annotation = [annotation];
+    }
+    this.deleteAnnotations(annotation);
+  }
+
+  /**
+   * Deletes the specified annotations in the managed list of annotations. If an annotation is successfully deleted, the annotationChanged event will be fired with a "delete" action.
+   * @param {Array.<Core.Annotations.Annotation>} annotations - An array of annotations.
+   * @param {object} [options] - An object that can contain the following optional parameters.
+   * @param {boolean} [options.imported=false] - Whether the annotations were imported from another source or not
+   * @param {boolean} [options.isUndoRedo=false] - Whether the annotation change was caused by undo/redo or not
+   * @param {boolean} [options.autoFocus=false] - Whether the annotation's text input should be automatically focused if applicable. Relevant to FreeText and Callout annotations.
+   * @param {string} [options.source] - What type of action cause this event (i.e. "redactionApplied")
+   */
+  deleteAnnotations(annotations, options) {
+    const annotationList = this.annotations
+    annotations.forEach(annotation => {
+      const index = annotationList.findIndex(item => item.Id === annotation.Id);
+      if (index > -1) {
+        const annotPtr = annotationList[index].annotPtr
+        Module._RemoveAnnot(annotPtr)
+        annotationList.splice(index, 1)
+      }
+    })
+  }
+
+  /**
+   * Exports all annotations as an XFDF (XML) string
+   * @param {object} [options] - Options for the export. Set options.widgets or options.links or options.fields to false to disable exporting of them.
+   * @param {Array.<Core.Annotations.Annotation>} [options.annotList] - An array of annotations to only export the XFDF for those particular annotations.
+   * @param {Array.<Core.Annotations.Annotation>} [options.annotationList] - An array of annotations to only export the XFDF for those particular annotations.
+   * @param {boolean} [options.widgets] - Whether to export widget information
+   * @param {boolean} [options.links] - Whether to export links information
+   * @param {boolean} [options.fields] - Whether to export fields information
+   * @param {boolean} [options.useDisplayAuthor] - Whether to export annotations with the Display Author name from annotationManager.getDisplayAuthor()
+   * @param {boolean} [options.generateInlineAppearances] - Whether to generate custom appearance strings for annotations that have an appearance generated on export (e.g. freetext). Default: true
+   * @returns {Promise.<string>} Returns a promise that resolves with the XFDF (XML) annotations as a string
+   */
+  exportAnnotations(options) {
+    return this.exportXfdf();
+  }
+
+  /**
+   * Gets an annotation object by the annotation's ID.
+   * @param {*} id - The ID of the annotation.
+   * @param {*} annotationList - Optionally pass your own array of annotations to search in
+   */
+  getAnnotationById(id, annotationList) {
+    if (annotationList && annotationList.length) return annotationList.find(annotation => annotation.Id === id);
+    return this.annotations.find(annotation => annotation.Id === id);
+  }
+
+  /**
+   * Gets the list of all annotations managed by the AnnotationManager.
+   * @returns {Array.<Core.Annotations.Annotation>} An array of Annotations.
+   */
+  getAnnotationsList() {
+    return this.annotations;
+  }
+
+  /**
+   * Returns the user name of the current user.
+   * @returns {string} The user name of the current user.
+   */
+  getCurrentUser() {
+    return this.docViewer.annotator;
+  }
+
+  /**
+   * Annotations may set the author to a unique id which isn't suitable for display in the UI. this function gets the author name of the annotation that should be displayed.
+   * @param {string} annotationUserId - Annotation userId
+   * @returns {string} The display author name
+   */
+  getDisplayAuthor(annotationUserId) {
+    const annotation = this.annotations.find(annotation => annotation.Id === annotationUserId);
+    if (annotation) return annotation.author;
+  }
+
+  /**
+   * Hides the specified annotation.
+   * @param {Core.Annotations.Annotation} annot - The annotation to hide 
+   */
+  hideAnnotation(annot) {
+    if (!Array.isArray(annot)) {
+      annot = [annot];
+    }
+    this.hideAnnotations(annot);
+  }
+
+  /**
+   * Hides all of the annotations in the list
+   * @param {Array.<Core.Annotations.Annotation>} annots - Array of annotations to hide
+   */
+  hideAnnotations(annots) {
+    for (let i = 0; i < annots.length; i++) {
+      annots[i].hidden = true;
+    }
+    this.annotationHidden(annots, true)
+  }
+
+  /**
+   * Loads XFDF annotations into the viewer
+   * @param {string} xfdfString - The XFDF annotations as a string
+   * @param {object} [options] - The options for importing
+   * @param {number} [options.batchSize] - The number of annotations to import in each batch (default 100)
+   * @param {number} [options.batchDelay] - The amount of time in milliseconds to delay between importing each batch (default 0)
+   * @param {Core.Annotations.Annotation|Array.<Core.Annotations.Annotation>} [options.batchSize] - The type of existing annotations that will be removed before import starts (default [])
+   * @returns {Promise.<any>} Returns a promise that resolves with the annotations have been imported
+   */
+  importAnnotations(xfdfString, options) {
+    this.importAnnotations(xfdfString);
+  }
+
+  /**
+   * Redraws the specified annotation. Note that the entire annotation canvas for the page will be redrawn.
+   * @param {Core.Annotations.Annotation} annotation - The annotation to be redrawn.
+   */
+  redrawAnnotation(annotation) {
+    this.docViewer.pdfViewer.renderAnnotation(annotation)
+  }
+
+  /**
+   * Set the styles for the annotation
+   * @param {Core.Annotations.Annotation} annotation - an annotation of which the styles will be changed.
+   * @param {object|function} newStyles - if an object is used, it should contain properties of the new styles. If a function is used, the current styles will be passed as its argument and the function should return an object which contains properties of the new styles. Example of valid properties: StrokeColor, TextColor, FillColor, FontSize, Opacity, StrokeThickness, Precision, Scale, OverlayText, Style and Dashes.
+   */
+  async setAnnotationStyles(annotation, newStyles) {
+    const annotations = this.annotations[annotation.pageIndex]
+    if (!annotations) return
+    const index = findIndex(annotation.name, annotations)
+    
+    annotation.doc = this.doc
+    Object.keys(newStyles).forEach(key => {
+      if (key in annotation) annotation[key] = newStyles[key];
+    });
+    const result = await this.messageHandler.sendWithPromise('EditAnnotation', { annotation })
+
+    if (result && result.code) {
+      console.log(result.message)
+      alert(result.message)
+      return false
+    }
+    const rawAnnotation = annotations[index]
+    annotations[index] = {
+      ...rawAnnotation,
+      ...annotation
+    }
+    annotation.targetPage = annotation.pageIndex + 1
+
+    let currentAnnotation = annotation
+    if (currentAnnotation.inklist) {
+      currentAnnotation = this.handleInk(currentAnnotation)
+    }
+
+    this.annotationChanged(this.annotations);
+  }
+
+  /**
+   * Shows the annotation
+   * @param {Core.Annotations.Annotation} annot - The annotation to show
+   */
+  showAnnotation(annot) {
+    if (!Array.isArray(annot)) {
+      annot = [annot];
+    }
+    this.showAnnotations(annot);
+  }
+
+  /**
+   * Shows all of the annotations in the list
+   * @param {Array.<Core.Annotations.Annotation>} annots - Array of annotations to show
+   */
+  showAnnotations(annots) {
+    for (var i = 0; i < annots.length; i++) {
+      annots[i].hidden = false;
+    }
+    this.annotationHidden(annots, false);
+  }
+
+  /**
+   * Redraws the annotations on the same page as the specified annotation if the annotation has been added.
+   * @param {Core.Annotations.Annotation} annotation - An instance of Annotation.
+   */
+  updateAnnotation(annotation) {
+    this.redrawAnnotation(annotation);
+  }
+
+  /**
+   * Updates the annotation state. This is done by creating an annotation that replies to the passed in annotation.
+   * @param {Core.Annotations.Annotation} annotation - The annotation to add a reply to
+   * @param {string} state - Annotations may have an author-specific state associated with them. Valid states are for Review stateModel: Accepted, Rejected, Cancelled, Completed, None. Default is None. Valid states are for Marked stateModel: Marked and Unmarked. Default is Unmarked.
+   * @param {string} stateModel - Gets or sets the stateModel of the annotation. stateModel has two statemodels: Marked, Review. Default is Review.
+   * @param {string} message - Message to be set when the state is updated.
+   * @returns {Core.Annotations.StickyAnnotation} The created annotation reply
+   */
+  updateAnnotationState(annotation, state, stateModel, message) {
+    let data
+
+    if (stateModel === 'Review') {
+      if (annotation.reviewAnnotState) {
+        data = {
+          type: 'edit',
+          annotation,
+          reviewAnnotState: annotation.reviewAnnotState.annotPtr ? {
+            annotPtr: annotation.reviewAnnotState.annotPtr,
+            state
+          } : state
+        }
+      } else {
+        data = {
+          type: 'add',
+          annotation,
+          reviewAnnotState: state
+        }
+      }
+    }
+
+    if (stateModel === 'Marked') {
+      if (annotation.markedAnnotState) {
+        data = {
+          type: 'edit',
+          annotation,
+          markedAnnotState: annotation.markedAnnotState.annotPtr ? {
+            annotPtr: annotation.markedAnnotState.annotPtr,
+            state: annotation.markedAnnotState.state === 'MARKED' ? 'UNMARKED' : 'MARKED'
+          } : (annotation.markedAnnotState === 'MARKED' ? 'UNMARKED' : 'MARKED')
+        }
+      } else {
+        data = {
+          type: 'add',
+          annotation,
+          markedAnnotState: state
+        }
+      }
+    }
+
+    data && this.handleReplyAnnotation(data)
+  }
+
+  /**
+   * Triggered when a reply has been added to an annotation
+   * @event Core.AnnotationManager#addReply
+   * @param {Core.Annotations.Annotation} annotation - The annotation that was added
+   * @param {Core.Annotations.Annotation} parent - The annotation that is the direct parent of the first annotation
+   * @param {Core.Annotations.Annotation} root - The annotation that is the root parent of the first annotation (may be the same as parent)
+   */
+  addReply(annotation, reply) {
+    this.trigger('addReply', {
+      annotation: reply,
+      parent: annotation,
+      root: annotation
+    });
+  }
+
+  /**
+   * Triggered when an annotation or annotations have been changed (added, deleted, modified). Attach like annotManager.addEventListener('annotationChanged', callback)
+   * @event Core.AnnotationManager#annotationChanged
+   * @param {Array.<Core.Annotations.Annotation>} annotations - The annotations that were changed
+   * @param {string} action - The action that occurred (add, delete, modify)
+   * @param {Core.AnnotationManager.AnnotationChangedInfoObject} info - An object containing extra information about the annotationChanged event
+   */
+  annotationChanged(annotations, action, info) {
+    this.trigger('annotationChanged', {
+      annotations,
+      action,
+      info
+    });
+  }
+
+  /**
+   * Triggered after annotations have been deselected.
+   * @event Core.AnnotationManager#annotationDeselected
+   * @param {Array.<Core.Annotations.Annotation>} annotationList - List of annotations that have been deselected.
+   */
+  annotationDeselected(annotations) {
+    this.trigger('annotationDeselected', annotations);
+  }
+
+  /**
+   * Triggered after an annotation has been double clicked
+   * @event Core.AnnotationManager#annotationDoubleClicked
+   * @param {Core.Annotations.Annotation} annotation - The annotation that has been double clicked
+   */
+  annotationDoubleClicked(annotation) {
+    this.trigger('annotationDoubleClicked', annotation);
+  }
+
+  /**
+   * Triggered after annotations have been hidden/shown.
+   * @event Core.AnnotationManager#annotationHidden
+   * @param {Array.<Core.Annotations.Annotation>} annotationList - List of annotations that were hidden or shown
+   * @param {boolean} hidden - Whether the annotations have been hidden or shown
+   */
+  annotationHidden(annotationList, hidden) {
+    this.trigger('annotationHidden', {
+      annotationList,
+      hidden
+    });
+  }
+
+  /**
+   * Triggered after annotations have been drawn for a page
+   * @event Core.AnnotationManager#annotationsDrawn
+   * @param {object} pageNumber - The page number of all the annotations that were just drawn
+   */
+  annotationsDrawn() {}
+
+  /**
+   * Triggered when a reply has been deleted from an annotation
+   * @event Core.AnnotationManager#deleteReply
+   * @param {Core.Annotations.Annotation} annotation - The annotation that was deleted
+   * @param {Core.Annotations.Annotation} root - The annotation that is the root parent of the first annotation
+   */
+  deleteReply(reply, annotation) {
+    this.trigger('deleteReply', {
+      annotation: reply,
+      root: annotation
+    });
+  }
+}
+
+/**
+ * @typedef {object} Core.AnnotationManager.AnnotationChangedInfoObject
+ * @property {boolean} imported - A boolean that will be true if the annotation change is the result of importing annotations using importAnnotations, importAnnotationCommand or if the imported parameter is set to true when calling addAnnotations or deleteAnnotations
+ * @property {boolean} isUndoRedo - A boolean that will be true if the annotation change is the result of an undo or redo action
+ * @property {string} source - An optional string that indicates what type of action caused this event (i.e. "redactionApplied") Core.AnnotationManager.AnnotationChangedSources
+ */
+
+/**
+ * Sources for what triggered the annotationChanged event
+ * @typedef {string} Core.AnnotationManager.AnnotationChangedSources
+ * @property {string} MOVE - Source for when the annotation was moved
+ * @property {string} RESIZE - Source for when the annotation was resized
+ * @property {string} ROTATE - Source for when the annotation was rotated
+ * @property {string} TEXT_CHANGED - Source for when the annotation's text was changed (FreeText)
+ * @property {string} NOTE_CHANGED - Source for when the annotation's note was changed
+ * @property {string} DRAGGING_ACROSS_PAGES - Source for when the annotation was dragged across pages
+ */
+
+/**
+ * @ignore
+ * @memberof Core.AnnotationManager
+ */
+class Alignment {}
+
+export { AnnotationManager };

+ 384 - 0
packages/webviewer-core/src/Annotations/Annotation.js

@@ -0,0 +1,384 @@
+/**
+ * Represents an annotation.
+ * @memberof Core.Annotations
+ */
+class Annotation {
+
+  /**
+   * An object containing the different display formats for measurement annotations.
+   * @property {string} DECIMAL - Decimal format eg. 12.75
+   * @property {string} FRACTION - Fraction format eg. 12 3/4
+   * @property {string} ROUND - Decimal format eg. 12.8
+   * @property {string} TRUNCATE - Decimal format eg. 12.7
+   * @example
+   * const fractionMeasurementDisplayFormat = Annotations.Annotation.MeasurementDisplayFormats.FRACTION
+   */
+  static get MeasurementDisplayFormats() {
+    return {
+      DECIMAL: 'DECIMAL',
+      FRACTION: 'FRACTION',
+      ROUND: 'ROUND',
+      TRUNCATE: 'TRUNCATE'
+    };
+  }
+
+  /**
+   * An object containing the different measurement systems for measurement annotations.
+   * @property {string} METRIC - The Metric measurement system
+   * @property {string} IMPERIAL - The Imperial measurement system
+   * @property {string} TYPOGRAPHIC - The Typographic measurement system
+   * @example
+   * const metricMeasurementSystem = Annotations.Annotation.MeasurementSystems.METRIC
+   */
+  static get MeasurementSystems() {
+    return {
+      METRIC: 'METRIC',
+      IMPERIAL: 'IMPERIAL',
+      TYPOGRAPHIC: 'TYPOGRAPHIC'
+    };
+  }
+
+  /**
+   * An object containing the different display measurement units for measurement annotations.
+   * @property {string} MM - Millimeter measurement unit
+   * @property {string} CM - Centimeter measurement unit
+   * @property {string} M - Meter measurement unit
+   * @property {string} KM - Kilometer measurement unit
+   * @property {string} IN - Inch measurement unit
+   * @property {string} FT - Foot measurement unit
+   * @property {string} YD - Yard measurement unit
+   * @property {string} MI - Mile measurement unit
+   * @property {string} PT - Point measurement unit
+   * @property {string} DOUBLE_PRIME_IN - Inch" measurement unit
+   * @property {string} PRIME_FT - Foot' measurement unit
+   * @property {string} FT_IN - Foot and Inch measurement unit
+   */
+  static get MeasurementUnits() {
+    return {
+      MM: 'MM',
+      CM: 'CM',
+      M: 'M',
+      KM: 'KM',
+      IN: 'IN',
+      FT: 'FT',
+      YD: 'YD',
+      MI: 'MI',
+      PT: 'PT',
+      DOUBLE_PRIME_IN: 'DOUBLE_PRIME_IN',
+      PRIME_FT: 'PRIME_FT',
+      FT_IN: 'FT_IN'
+    };
+  }
+
+  /**
+   * The base class for all annotations
+   * @param {object} [initializer] - A map of values to auto-initialize certain properties of the annotation. You can only initialize properties defined on the annotation under the Members section (unless specified otherwise).
+   */
+  constructor(initializer) {
+    /** The author of the annotation. */
+    this.author = initializer?.author;
+
+    /**
+     * Gets or sets the annotation blend mode.
+     * 
+     * **THIS PROPERTY IS EXPERIMENTAL, NOT CURRENTLY PERSISTED AND ONLY WORKS WITHIN WEBVIEWER**
+     */
+    this.BlendMode = initializer?.BlendMode;
+
+    /** Gets or sets the annotation's stroke color. */
+    this.color = initializer?.color;
+
+    /** Gets or sets the date that the annotation was last modified. */
+    this.DateModified = initializer?.DateModified;
+
+    /** Gets or sets the name of the annotation element in XFDF.
+     * 
+     * This property cannot be initialized through the initializer.
+     */
+    this.elementName = initializer?.elementName;
+
+    /** Gets or sets whether the annotation is hidden. */
+    this.Hidden = initializer?.Hidden;
+
+    /** A unique identifier for the annotation. Corresponds to the name attribute in XFDF. */
+    this.Id = initializer?.Id;
+
+    /** The Id of the annotation that this annotation is replying to or the parent annotation it is grouped with. */
+    this.InReplyTo = initializer?.InReplyTo;
+
+    /** Gets or sets whether the annotation is invisible, only if it is an unknown annotation type. Generally for hiding annotations you should use "Hidden". */
+    this.Invisible = initializer?.Invisible;
+
+    /** Gets or sets whether the annotation has been added since the last time the AnnotCommand was retrieved. */
+    this.IsAdded = initializer?.IsAdded || false;
+
+    /** Gets or sets whether any parts of the annotation drawn outside of the rect are clickable. */
+    this.IsClickableOutsideRect = initializer?.IsClickableOutsideRect || false;
+
+    /** Gets or sets whether the annotation has been deleted since the last time the AnnotCommand was retrieved. */
+    this.IsDeleted = initializer?.IsDeleted || false;
+
+    /** Gets or sets whether the annotation has been modified since the last time the AnnotCommand was retrieved. */
+    this.IsModified = initializer?.IsModified || false;
+
+    /** Gets or sets the page number of a document that the annotation appears on. */
+    this.PageNumber = initializer?.PageNumber || 0;
+
+    /** Gets or sets whether the annotation should be displayed when printing the page. */
+    this.Printable = initializer?.Printable;
+
+    /** Gets or sets whether the annotation is readonly or not. If it's readonly it can't be edited or deleted. */
+    this.ReadOnly = initializer?.ReadOnly;
+
+    /** The type of reply. */
+    this.ReplyType = initializer?.ReplyType;
+
+    /** Gets or sets the annotation's clockwise rotation in degrees. Valid values are 0, 90, 180 and 270. Only applies to Stamp, FreeText and Caret annotations. */
+    this.Rotation = initializer?.Rotation;
+
+    /** Enables or disables the annotation's rotation control. */
+    this.RotationControlEnabled = initializer?.RotationControlEnabled;
+
+    /** Gets or sets a selection model constructor for how an annotation gets selected. */
+    this.selectionModel = initializer?.selectionModel;
+
+    /** Represents the subject of the annotation. Default is the annotation's type. */
+    this.Subject = initializer?.Subject;
+
+    /** Gets or sets whether the ToggleNoView flag is set on the annotation. */
+    this.ToggleNoView = initializer?.ToggleNoView;
+
+    /** The name of the tool that is used to create this annotation. */
+    this.ToolName = initializer?.ToolName;
+
+    /** Gets or sets the width of the annotation. */
+    this.Width = initializer?.Width || 0;
+
+    /** Gets or sets the height of the annotation. */
+    this.Height = initializer?.Height || 0;
+
+    /** Gets or sets the annotation's x-axis position. */
+    this.X = initializer?.X || 0;
+
+    /** Gets or sets the annotation's y-axis position. */
+    this.Y = initializer?.Y || 0;
+  }
+
+  /**
+   * Draws the annotation on the provide canvas context, relative to the page. The point (0,0) coresponds to the top left corner of the page.
+   * @param {CanvasRenderingContext2D} ctx - The canvas context prepared to be drawn on.
+   * @param {object} pageMatrix - The page matrix for the page that the annotation is on. You can get this object by calling getPageMatrix on the document object.
+   */
+  draw(ctx, pageMatrix) {}
+
+  /**
+   * Gets the text content for the annotation. Contents may be displayed in an annotation's popup or directly on the page (in the case of FreeTextAnnotation).
+   * @returns {string} the text content for the annotation.
+   */
+  getContents() {
+    return this.content;
+  }
+
+  /**
+   * Returns custom data associated with the given key.
+   * @param {string} key - The key for which to retrieve the associated data.
+   * @returns {string} value The custom data. If no data is available an empty string is returned.
+   */
+  getCustomData(key) {
+    return this[key];
+  }
+
+  /**
+   * Gets the height of the annotation.
+   * @returns {number} the height of the annotation.
+   */
+  getHeight() {
+    return this.Height;
+  }
+
+  /**
+   * Gets the page number of the annotation. Note: page number starts from 1.
+   * @returns {number} The annotation's page number.
+   */
+  getPageNumber() {
+    return this.PageNumber;
+  }
+
+  /**
+   * Get annotation bounding rectangle
+   * @returns {Core.Math.Rect} The annotation's bounding rectangle.
+   */
+  getRect() {
+    return this.rect;
+  }
+
+  /**
+   * Gets the padding that will be applied by default on the annotation's rectangle.
+   * @returns {number} The amount of padding
+   */
+  getRectPadding() {
+    return this.rectPadding;
+  }
+
+  /**
+   * Gets the list of replies to this annotation.
+   * @returns {Array.<Core.Annotations.Annotation>} The list of replies
+   */
+  getReplies() {
+    return this.replies;
+  }
+
+  /**
+   * Gets the status of the annotation, and returns an empty string if no status set.
+   * @returns {string} The last status update.
+   */
+  getStatus() {
+    return this.status;
+  }
+
+  /**
+   * Gets the width of the annotation.
+   * @returns {number} The width of the annotation.
+   */
+  getWidth() {
+    return this.Width;
+  }
+
+  /**
+   * Gets the x position measured in page coordinates of an annotation.
+   * @returns {number} the x position
+   */
+  getX() {
+    return this.X;
+  }
+
+  /**
+   * Gets the y position measured in page coordinates of an annotation.
+   * @returns {number} the y position
+   */
+  getY() {
+    return this.Y;
+  }
+
+  /**
+   * Checks if a content edit annotation is being edited and has focus.
+   * @param {Core.ContentEditManager} contentEditManager - An instance of contentEditManager
+   * @returns {boolean} Returns whether an annotation is on content edit focus mode or not
+   */
+  isInContentEditFocusMode(contentEditManager) {}
+
+  /**
+   * Gets whether the annotation is a reply to another annotation.
+   * @returns {boolean} Returns true if it does reply to another annotation, false otherwise.
+   */
+  isReply() {}
+
+  /**
+   * Whether the annotation is visible on the document or not **when viewing**. If the Hidden or NoView flags are set, or if the annotation is a reply to another annotation then it won't be visible.
+   * @returns {boolean} Whether the annotation is visible on the document or not when viewing
+   */
+  isVisible() {}
+
+  /**
+   * Move the annotation based on a new given Core.Math.Rect. Use this method instead of setRect when internal properties (other than x, y, width and height) need to be modified on move.
+   * @param {Core.Math.Rect} rect - the new bounding rectangle
+   */
+  move(rect) {}
+
+  /**
+   * Set the text content for the annotation. Note that this will not refresh the text in the UI.
+   * @param {string} value - the text content to be set
+   */
+  setContents(value) {
+    this.content = value;
+  }
+
+  /**
+   * Sets the custom data associated with the specified key.
+   * @param {string} key - The key under which to store this custom data. Data will automatically be saved in the exported XFDF and merged into the PDF when downloading.
+   * @param {string} value - The custom data to store
+   */
+  setCustomData(key, value) {
+    this[key] = value;
+  }
+
+  /**
+   * Sets the height of the annotation.
+   * @param {number} value - the height of the annotation.
+   */
+  setHeight(value) {
+    this.height = value;
+  }
+
+  /**
+   * Sets the page number of the annotation. Note: page numbers start from 1.
+   * @param {number} value - the page number to be set
+   */
+  setPageNumber(value) {
+    this.pageNumber = value;
+  }
+
+  /**
+   * Sets the size and location of the annotation's bounding rectangle. Use this method instead of resize when only the x, y, width and height needs to be modified. Beware: this method ignores coordinates if they are the wrong way around. (It hasn't been fixed for reasons of maintaining backward compatibility. use setRectWithNormalization instead if you want it to reverse the wrong-way-round coordinates instead of ignoring them.)
+   * @param {Core.Math.Rect} rect - the new bounding rectangle
+   */
+  setRect(rect) {
+    this.rect = rect;
+  }
+
+  /**
+   * Sets the width of the annotation.
+   * @param {number} value - the width of the annotation.
+   */
+  setWidth(value) {
+    this.width = value;
+  }
+
+  /**
+   * Sets the x position measured in page coordinates of an annotation.
+   * @param {number} value - the x position
+   */
+  setX(value) {
+    this.x = value;
+  }
+
+  /**
+   * Sets the y position measured in page coordinates of an annotation.
+   * @param {number} value - the y position
+   */
+  setY(value) {
+    this.y = value;
+  }
+}
+
+/**
+ * an object that contains content editing properties which apply to selections of text while inline editing.
+ * @typedef {Object} Core.Annotations.Annotation.ContentEditingFormat
+ * @property {boolean} bold - Whether or not the selection of text is bold
+ * @property {Core.Annotations.Color} color - The color of the selection of text
+ * @property {boolean} italic - Whether or not the selection of text is italic
+ * @property {boolean} underline - Whether or not the selection of text is underlined
+ * @example
+ * {
+ *  bold: false,
+ *  color: new instance.Core.Annotations.Color('#424242'),
+ *  italic: false,
+ *  underline: false,
+ * }
+ */
+
+/**
+ * an object that contains content style properties from a content editing annotation.
+ * @typedef {Object} Core.Annotations.Annotation.ContentStyleProperties
+ * @property {string} fontFamily - the font family of the text content
+ * @property {string} fontSize - the font size of the text content
+ * @property {string} textAlign - the alignment of the text content
+ * @example
+ * {
+ *  fontFamily: 'SourceSansProSemiBold',
+ *  fontSize: '30pt',
+ *  textAlign: 'left'
+ * }
+ */
+
+export default Annotation;

+ 22 - 0
packages/webviewer-core/src/Annotations/Border.js

@@ -0,0 +1,22 @@
+/**
+ * Represents a class that contains border information for an annotation.
+ * @memberof Core.Annotations
+ */
+class Border {
+
+  /**
+   * Creates a new instance of Border.
+   * @param {object} options The initialization options, an object with properties color, width, style, cornerRadius
+   * 
+   * @property {Core.Annotations.Color} color - The color of the border
+   * @property {number} width - The width of the border
+   * @property {string} style - The style of the border (possible types include: solid, bevelled, inset)
+   * @property {number} cornerRadius - The corner radius size
+   */
+  constructor(options) {
+    this.color = options.color;
+    this.width = options.width;
+    this.style = options.style;
+    this.cornerRadius = options.cornerRadius;
+  }
+}

+ 24 - 0
packages/webviewer-core/src/Annotations/BoxControlHandle.js

@@ -0,0 +1,24 @@
+import ControlHandle from './ControlHandle';
+
+/**
+ * A control handle based on the bounding box of the annotation used for resizing.
+ * @memberof Core.Annotations
+ */
+class BoxControlHandle extends ControlHandle {
+
+  /**
+   * Creates a new box control handle
+   * @extends Core.Annotations.ControlHandle
+   * 
+   * @param {number} width - the width of the handle
+   * @param {number} height - the height of the handle
+   * @param {number} horizontalAlignment - the horizontal position of the handle
+   * @param {number} verticalAlignment - the vertical position of the handle
+   */
+  constructor(width, height, horizontalAlignment, verticalAlignment) {
+    this.width = width;
+    this.height = height;
+    this.horizontalAlignment = horizontalAlignment;
+    this.verticalAlignment = verticalAlignment;
+  }
+}

+ 20 - 0
packages/webviewer-core/src/Annotations/BoxSelectionModel.js

@@ -0,0 +1,20 @@
+import SelectionModel from './SelectionModel';
+
+/**
+ * A selection model based on the annotation's bounding box. This is used for most generic annotations.
+ * @memberof Core.Annotations
+ */
+class BoxSelectionModel extends SelectionModel {
+
+  /**
+   * Creates a box selection model.
+   * @extends Core.Annotations.SelectionModel
+   * 
+   * @param {Core.Annotations.Annotation} annotation - The annotation
+   * @param {boolean} canModify - Indicates if the annotation can be modified
+   * @param {boolean} isSelected - boolean to denote if the annotation is selected
+   */
+  constructor(annotation, canModify, isSelected) {
+    super(annotation, canModify, isSelected);
+  }
+}

+ 38 - 0
packages/webviewer-core/src/Annotations/ButtonWidgetAnnotation.js

@@ -0,0 +1,38 @@
+import WidgetAnnotation from './WidgetAnnotation';
+
+/**
+ * Represents a Button Widget annotation, which includes push button, checkbox, and radio button.
+ * @memberof Core.Annotations
+ */
+class ButtonWidgetAnnotation extends WidgetAnnotation {
+
+  /**
+   * An enum for each checkbox/radioButton caption values When assgin the enum to checkbox/radioButton caption, the UI of the checkbox/radioButton will be changed to the corresponded shape
+   * @property {string} CROSS - name of Cross shape
+   * @property {string} TICK - name of Tick shape
+   * @property {string} CIRCLE - name of Circle shape
+   * @property {string} DIAMAND - name of Diamand shape
+   * @property {string} SQUARE - name of Square shape
+   * @property {string} STAR - name of Star shape
+   */
+  static WidgetButtonCaptions = {
+    CROSS: 'CROSS',
+    TICK: 'TICK',
+    CIRCLE: 'CIRCLE',
+    DIAMAND: 'DIAMAND',
+    SQUARE: 'SQUARE',
+    STAR: 'STAR'
+  }
+
+  /**
+   * Creates a new instance of ButtonWidgetAnnotation.
+   * @extends Core.Annotations.WidgetAnnotation
+   * 
+   * @param {Core.Annotations.Forms.Field} field - The field to associate with the widget
+   * @param {?object} options - A map of properties to set on the widget
+   */
+  constructor(field, options) {
+    super(options);
+    this.field = field;
+  }
+}

+ 19 - 0
packages/webviewer-core/src/Annotations/CheckButtonWidgetAnnotation.js

@@ -0,0 +1,19 @@
+import ButtonWidgetAnnotation from './ButtonWidgetAnnotation';
+
+/**
+ * @memberof Core.Annotations
+ */
+class CheckButtonWidgetAnnotation extends ButtonWidgetAnnotation {
+
+  /**
+   * A Checkbutton widget annotation.
+   * @extends Core.Annotations.ButtonWidgetAnnotation
+   * 
+   * @param {Core.Annotations.Forms.Field} field - The field to associate with the widget
+   * @param {?object} options - A map of properties to set on the widget
+   */
+  constructor(field, options) {
+    super(options);
+    this.field = field;
+  }
+}

+ 23 - 0
packages/webviewer-core/src/Annotations/ChoiceWidgetAnnotation.js

@@ -0,0 +1,23 @@
+import WidgetAnnotation from './WidgetAnnotation';
+
+/**
+ * Represents a Choice Widget annotation (a combo box).
+ * @memberof Core.Annotations
+ */
+class ChoiceWidgetAnnotation extends WidgetAnnotation {
+
+  /**
+   * Creates a new instance of ChoiceWidgetAnnotation.
+   * @extends Core.Annotations.WidgetAnnotation
+   * 
+   * @param {Core.Annotations.Forms.Field} field - The field to associate with the widget
+   * @param {?object} options - A map of properties to set on the widget
+   * 
+   * @property {boolean} FORCE_SELECT - Whether to force all choice widgets to be html select elements
+   */
+  constructor(field, options) {
+    super(options);
+    this.field = field;
+    this.FORCE_SELECT = false;
+  }
+}

+ 115 - 0
packages/webviewer-core/src/Annotations/Color.js

@@ -0,0 +1,115 @@
+/**
+ * Represents a class that contains a color's RGB and alpha value.
+ * @memberof Core.Annotations
+ */
+class Color {
+
+  /**
+   * Creates a new instance of Color.
+   * @param {number} r - the R (red) value (0-255)
+   * @param {number} g - the G (green) value (0-255)
+   * @param {number} b - the B (blue) value (0-255)
+   * @param {number} [a] - the A (alpha) value (0-1.0)
+   * 
+   * @property {number} R - the R (red) value (0-255)
+   * @property {number} G - the G (green) value (0-255)
+   * @property {number} B - the B (blue) value (0-255)
+   * @property {number} A - the A (alpha) value (0-1.0)
+   */
+  constructor(r = 0, g = 0, b = 0, a) {
+    this.R = Number(r);
+    this.G = Number(g);
+    this.B = Number(b);
+    this.A = (r === 0 && g === 0 && b === 0) ? 0 : (Number(a) || 1);
+  }
+
+  /**
+   * Returns the color as a hex string e.g. #FFFFFF
+   * @returns {string} The hex color string.
+   */
+  toHexString() {
+    const r = this.R;
+    const g = this.G;
+    const b = this.B;
+    const hexString = ((1 << 24) | (r << 16) | (g << 8) | b).toString(16).slice(1).toUpperCase();
+    return `#${hexString}`;
+  }
+
+  hexToRgb(hexColor) {
+    const hex = hexColor.slice(1);
+    const bigint = parseInt(hex, 16);
+    const R = (bigint >> 16) & 255;
+    const G = (bigint >> 8) & 255;
+    const B = bigint & 255;
+    return {
+      R,
+      G,
+      B
+    }
+  }
+
+  /**
+   * Outputs the current color as a CSS3 RGB color string.
+   * @returns {string} The CSS3 RGB color string.
+   * @example
+   * ex. "rgb(0,0,0)" or "rgba(0,255,0,0.5)"
+   */
+  toString() {
+    if (this.A === 1) return `rgb(${this.R}, ${this.G}, ${this.B})`;
+    return `rgba(${this.R}, ${this.G}, ${this.B}, ${this.A})`;
+  }
+
+  setColor(r, g, b, a) {
+    this.R = r
+    this.G = g
+    this.B = b
+    typeof a === 'number' && (this.A = a)
+  }
+
+  formatColor(value) {
+    const format = this.getColorFormat(value)
+    if (format === 'hex') {
+      const { R, G, B } = this.hexToRgb(value)
+      return {
+        R,
+        G,
+        B
+      }
+    } else if (format === 'rgb') {
+      const match = value.match(/(\d+),\s*(\d+),\s*(\d+)/)
+      const R = parseInt(match[1])
+      const G = parseInt(match[2])
+      const B = parseInt(match[3])
+      return {
+        R,
+        G,
+        B
+      }
+    } else {
+      console.error({
+        message: 'Invalid color value'
+      })
+    }
+  }
+
+  getColorFormat(colorString) {
+    if (colorString.startsWith('#')) {
+      return 'hex';
+    } else if (colorString.startsWith('rgb(')) {
+      return 'rgb';
+    } else {
+      return 'unknown';
+    }
+  }
+
+  convertColorToCppFormat() {
+    const R = roundToDecimalPlaces(this.R / 255)
+    const G = roundToDecimalPlaces(this.G / 255)
+    const B = roundToDecimalPlaces(this.B / 255)
+    return {
+      R,
+      G,
+      B
+    }
+  }
+}

+ 74 - 0
packages/webviewer-core/src/Annotations/ControlHandle.js

@@ -0,0 +1,74 @@
+/**
+ * Represents a base class for control handles for selected annotations that can be moved. Control handles are typically used to resize an annotation or other operations that modify the annotation.
+ * @memberof Core.Annotations
+ */
+class ControlHandle {
+  /** Defines the color for the annotation control point. */
+  static color;
+
+  /** Defines the height of all control handles. Default is 10. */
+  static handleHeight = 10;
+
+  /** Defines the width of all control handles. Default is 10. */
+  static handleWidth = 10;
+
+  /** Defines the outline color for the annotation control point. */
+  static outlineColor;
+
+  /** Defines the height of all rotation control handles. Default is 28. */
+  static rotationHandleHeight = 28;
+
+  /** Defines the width of all rotation control handles. Default is 28. */
+  static rotationHandleWidth = 28;
+
+  /** Defines a padding for selection accuracy. Default is 1. Increase this value to make selection more forgiving. */
+  static selectionAccuracyPadding = 1;
+
+  /** Defines thickness of the annotation selection outline. Default is 3. */
+  static selectionPointOutlineThickness = 3;
+
+  /** Defines the shadow blur for the annotation control point. */
+  static shadowBlur;
+
+  /** Defines the shadow color for the annotation control point. */
+  static shadowColor;
+
+  /** Defines the offset Y position for the shadow blur for the annotation control point. */
+  static shadowOffsetY;
+
+  /**
+   * Creates a new box control handle
+   * @param {number} x - The x-coordinate of the upper-left point
+   * @param {number} y - The y-coordinate of the upper-left point
+   * @param {number} width - The width of the control handle
+   * @param {number} height - The height of the control handle
+   */
+  constructor(x, y, width, height) {
+    this.x = x;
+    this.y = y;
+    this.width = width;
+    this.height = height;
+  }
+
+  /**
+   * Draws the control handle's appearance on the provided canvas context
+   * @param {CanvasRenderingContext2D} ctx - the annotation canvas context
+   * @param {Core.Annotations.Annotation} annotation - the annotation to modify
+   * @param {Core.Math.Rect} selectionBox - the selection rect
+   * @param {number} zoom - the current zoom level of the document
+   */
+  draw(ctx, annotation, selectionBox, zoom) {}
+
+  /**
+   * Determines if the provided point is a hit on the control handle. See {@link Core.Annotations.SelectionAlgorithm} for usuable selection algorithms.
+   * @param {Core.Annotations.Annotation} annotation - the annotation
+   * @param {Core.Math.Rect} selectionBox - the selection rect
+   * @param {number} zoom - the current zoom level of the document
+   * @param {number} x - the x-coordinate of the point to test, in page coordinates
+   * @param {number} y - the y-coordinate of the point to test, in page coordinates
+   * @return {boolean} true if the provided point is a hit
+   */
+  testSelection(annotation, selectionBox, zoom, x, y) {}
+}
+
+export default ControlHandle;

+ 20 - 0
packages/webviewer-core/src/Annotations/EllipseAnnotation.js

@@ -0,0 +1,20 @@
+import MarkupAnnotation from './MarkupAnnotation';
+
+/**
+ * @memberof Core.Annotations
+ */
+class EllipseAnnotation extends MarkupAnnotation {
+
+  /**
+   * Represents an ellipse annotation.
+   * @extends Core.Annotations.MarkupAnnotation
+   * 
+   * @param {object} [initializer] - A map of values to auto-initialize certain properties of the annotation. You can only initialize properties defined on the annotation under the Members section (unless specified otherwise).
+   */
+  constructor(initializer) {
+    super(initializer);
+    
+    /** Gets or sets the border style of an annotation. e.g Solid, Cloudy */
+    this.Style = 'Solid';
+  }
+}

+ 50 - 0
packages/webviewer-core/src/Annotations/Font.js

@@ -0,0 +1,50 @@
+import Color from './Color';
+
+/**
+ * @memberof Core.Annotations
+ */
+class Font {
+
+  /**
+   * A class representing a PDF font.
+   * @param {object} [params] - An object containing parameters to be initialised on the font.
+   * 
+   * @property {string} name - The font's name
+   * @property {number} [size=0] - The font's size
+   * @property {string} [type='Type1'] - The font's type (One of CIDType0, CIDType2, MMType1, TrueType, Type0, Type1, Type3)
+   * @property {(Core.Annotations.Color|array|object)} [strokeColor=new Core.Annotations.Color([0,0,0])] - The font's stroke color
+   * @property {(Core.Annotations.Color|array|object)} [fillColor=new Core.Annotations.Color([0,0,0])] - The font's fill color
+   * @property {number} calculatedSize - The calculated size of the font if size is 0
+   */
+  constructor(params = {}) {
+    this.name = params.name || '';
+    this.size = params.size || 0;
+    this.type = params.type || 'Type1';
+    this.strokeColor = params.strokeColor || new Color([0,0,0]);
+    this.fillColor = params.fillColor || new Color([0,0,0]);
+    this.calculatedSize = this.size === 0 ? this.calculateSize() : this.size;
+  }
+
+  calculateSize() {
+    return 14;
+  }
+
+  /**
+   * Is the font solid black?
+   * @return {boolean} Whether the font is solid black
+   */
+  isBlack() {}
+
+  /**
+   * Set all properties on Font using a Font or a Font-like object.
+   * @param {Core.Annotations.Font|object} options - 	The options to set on the Font
+   */
+  set(options) {}
+
+  /**
+   * Convert the font to a CSS object like one would pass to jQuery.css().
+   * @param {number} zoom 
+   * @return {object} An object appropriate to pass to jQuery.css()
+   */
+  toCSS(zoom) {}
+}

+ 144 - 0
packages/webviewer-core/src/Annotations/Forms/Field.js

@@ -0,0 +1,144 @@
+import Dispatcher from '../../Actions/Dispatcher';
+
+/**
+ * @memberof Core.Annotations.Forms
+ * @mixes Core.Actions.Dispatcher
+ * @listens Core.Annotations.Forms.Field#event:calculate
+ * @listens Core.Annotations.Forms.Field#event:commit
+ */
+class Field {
+
+  /**
+   * Represents a PDF Form field.
+   * @param {string} name - The field's full name.
+   * @param {Core.Annotations.Forms.Field|object} [options] - The options with which to construct the field. If options is a Field, the constructor returns the existing object.
+   * @param {string} [options.type] - The field's type. One of ('Tx', 'Btn', 'Ch' or 'Sig').
+   * @param {string} [options.value] - The value of the field
+   * @param {Core.Annotations.WidgetFlags} [options.flags] - The flags for the field
+   * @param {Array.<Core.Annotations.Forms.Field>} [options.children] - The field's children.
+   * @param {Array.<Core.Annotations.WidgetAnnotation>} [options.widgets] - The field's child widgets.
+   * @param {Core.Annotations.Forms.FieldManager} [options.fieldManager] - The field's field manager.
+   * @param {Core.Annotations.Font} [options.font] - The font that is used by the field.
+   * @param {number} [options.maxLen] - The field's maximum length. -1 means infinite.
+   * @param {string} [options.tooltipName] - The text to be displayed when hovering over the field.
+   * @param {Array.<object>} [options.options] - The field's options (or permanently null if the field type does not support options).
+   * 
+   * @property {string} type - Returns the field's type. One of ('Tx', 'Btn', 'Ch' or 'Sig')
+   * @property {Array.<Core.Annotations.Forms.Field>} [children=[]] - The field's children
+   * @property {Array.<Core.Annotations.WidgetAnnotation>} [widgets=[]] - The field's child widgets
+   * @property {object.<string, Array.<Core.Actions.Action>>} [actions={}] - Gets the field's actions.
+   * @property {number|string} [defaultValue=null] - The field's default value
+   * @property {number|string} [exportValue=null] - The field's export value. Defaults to the field's value if not set.
+   * @property {Core.Annotations.WidgetFlags} [flags=new Core.Annotations.WidgetFlags()] - The field's flags object
+   * @property {Core.Annotations.Font} [font=new Core.Annotations.Font()] - Returns the field's associated font object
+   * @property {boolean} [IsModified=false] - Is field is modified?
+   * @property {number} [maxLen=-1] - The field's maximum length. -1 means infinite.
+   * @property {string} name - The full name of the field.
+   * @property {Array.<object>} [options=[]] - The field's options (or permanently null if the field type does not support options).
+   * @property {string} [quadding='Left-justified'] - Returns the field's quadding. (One of 'Left-justified', 'Right-justified', 'Centered')
+   * @property {number|string} [value=null] - The field's value.
+   * @property {string} tooltipName - The text to be displayed when hovering over the field.
+   */
+  constructor(name, options) {
+    this.name = name;
+    this.type = options?.type || '';
+    this.children = options?.children || [];
+    this.widgets = options?.widgets || [];
+    this.actions = options?.actions || {};
+    this.defaultValue = options?.defaultValue || null;
+    this.exportValue = options?.exportValue || null;
+    this.flags = options?.flags || new Core.Annotations.WidgetFlags();
+    this.font = options?.font || new Core.Annotations.Font();
+    this.IsModified = options?.IsModified || false;
+    this.maxLen = options?.maxLen || -1;
+    this.quadding = options?.quadding || 'Left-justified';
+    this.options = options?.options || [];
+    this.value = options?.value || null;
+    this.tooltipName = options?.tooltipName;
+
+    Object.assign(this, Dispatcher)
+  }
+
+  /**
+   * Commits the new value to the field (triggers Validate action, and updates all widgets)
+   * @param {*} event - Ignored.
+   * @param {string|number} [value] - The new value.
+   * @param {Core.Annotations.WidgetAnnotation} [widget] - Optionally a particular widget annotation associated with the field
+   */
+  commit(event, value, widget) {}
+
+  /**
+   * Returns the field's type
+   * @return {string} - The field type as defined by the FormFieldTypes enum
+   */
+  getFieldType() {}
+
+  /**
+   * Gets the field's value
+   * @return {string|number} - The value of the field
+   */
+  getValue() {}
+
+  /** Disable visibility of all child widgets */
+  hide() {}
+
+  /**
+   * Checks whether the current field is a terminal/leaf node (no children).
+   * @return {boolean} - Whether this field is terminal/leaf node
+   */
+  isTerminal() {}
+
+  /** Refresh the appearance of each widget in the current field. */
+  refreshAppearances() {}
+
+  /**
+   * Set all field properties form the given object.
+   * @param {Core.Annotations.Forms.Field|object} options - The object to set properties from.
+   */
+  set(options) {
+    const self = this;
+    Object.keys(options).forEach(key => {
+      if (key in self) self[key] = options[key];
+    });
+  }
+
+  /**
+   * Sets the field's type. Tries to retain as much type-specific information as possible across type changes.
+   * @param {string} type - The new field type, one of Tx, Ch, Btn or Sig.
+   */
+  setType(type) {
+    this.type = type;
+  }
+
+  /**
+   * Sets the field's value, following any type-specific constraints, and updating all widgets.
+   * @param {?(string|number)} value - The value to set.
+   * @param {Core.Annotations.WidgetAnnotation} [widget] - Optionally a particular widget annotation associated with the field
+   * @fires Core.Annotations.Forms.Field#event:change
+   */
+  setValue(value, widget) {}
+
+  /** Enable visibility of all child widgets */
+  show() {}
+
+  /**
+   * A signal that the field should fire its own calculation events.
+   * @event Core.Annotations.Forms.Field#calculate
+   * @param {Core.Annotations.Forms.Field} source - The field that caused the calculation event.
+   */
+  calculate(source) {}
+
+  /**
+   * An event that represents a field's value having changed.
+   * @event Core.Annotations.Forms.Field#change
+   * @param {Core.Annotations.Forms.Field} field - The field that has changed.
+   */
+  change() {}
+
+  /**
+   * The field's value has been changed by the user. Sets a new value.
+   * @event Core.Annotations.Forms.Field#commit
+   * @param {?(string|number)} value - The new field value.
+   */
+  commit(value) {}
+}

+ 100 - 0
packages/webviewer-core/src/Annotations/Forms/FieldManager.js

@@ -0,0 +1,100 @@
+/**
+ * @memberof Core.Annotations.Forms
+ */
+class FieldManager {
+
+  /**
+   * Represents a tree of PDF Forms fields. @see {Core.Annotations.Forms.Field} Allows adding, getting and visiting fields.
+   * @param {Core.AnnotationManager} annotationManager 
+   */
+  constructor(annotationManager) {
+    this.annotationManager = annotationManager;
+  }
+
+  /**
+   * Adds the given field object to the field tree, either replacing or merging with any existing field. Note: if the field is merged, it is the equivalent of performing Annotations.Forms.Field.set on the existing field, followed by setting all the field's widgets' fields to the original field.
+   * @param {Core.Annotations.Forms.Field|object} field - The field or field-like object to add.
+   * @param {boolean} [update=false]
+   * @return {Core.Annotations.Forms.Field} The field that was inserted into the field tree (may not be the field originally provided).
+   */
+  addField(field, update = false) {}
+
+  /**
+   * iterate over every field and check if it's required and if it has a value
+   * @return {boolean} Returns True if there is no required field or all fields are filled out, false otherwise
+   */
+  areRequiredFieldsFilled() {}
+
+  /**
+   * Equivalent to the given field's value changing, if it was at position index in the calculation order.
+   * @param {?Core.Annotations.Forms.Field} field - The field to set as source for the calculation event. May be null.
+   * @param {number} [index=0] - The index at which to start running the calculation order.
+   */
+  calculate(field, index = 0) {}
+
+  /**
+   * Listener for field value changes.
+   * @param {Core.Annotations.Forms.Field} field - The field whose value changed.
+   */
+  fieldChanged(field) {}
+
+  /**
+   * Calls the specified callback for each of the root fields in the document. If there is a tree of form fields, then this will only iterate over root fields. Please use Core.Annotations.Forms.Field.children and a tree traversal algorithm to traverse the fields. If you know the name of your field, you can use Core.Annotations.Forms.FieldManager.getField.
+   * @param {function} callback - The function that will be called for each field. The callback is passed the field object.
+   * @example
+const stack = [];
+fieldManager.forEachField(function(field) {
+  stack.push(field); // Push root fields
+});
+while (stack.length > 0) {
+  const current = stack.pop();
+  if (current.isTerminal()) {
+    // Work with terminal/leaf fields
+  }
+  else {
+    // Traverse children
+    stack = stack.concat(current.children);
+  }
+}
+   */
+  forEachField(callback) {}
+
+  /**
+   * Gets a field whose full name matches the one that is given. If No field matches, and the last part of the field name is a number, returns that index of the field's widget array.
+   * @param {string} name - The full name of the field to search for.
+   * @return {Core.Annotations.Forms.Field|Core.Annotations.WidgetAnnotation} The field or widget, or null if no field matches.
+   */
+  getField(name) {}
+
+  /**
+   * Returns an array of all field objects in the document
+   * @return {Array.<Core.Annotations.Forms.Field>}
+   */
+  getFields() {}
+
+  /** Invokes the available print handler to print the document Note: This method requires Embedded JavaScript to not have been disabled with Core.disableEmbeddedJavaScript */
+  print() {}
+
+  /**
+   * Updates the function to be used for handling alert messages for field validation.
+   * @param {function} handler - The function that will handle alert messages. It will be called with a string representing the alert message.
+   */
+  setAlertHandler(handler) {}
+
+  /**
+   * Set the calculation order - use this if you need changes in one field to trigger calculation events on other fields. If the name of field that is changed is before the name of another field, that other field is recalculated. If the name of the field that has changed is not in the array, all the fields named by the arrya are recalculated in order.
+   * @param {Array.<string>} order - An array of field names.
+   */
+  setCalculationOrder(order) {}
+
+  /** Sets the function to be used to handle the print API called by embedded PDF JavaScript */
+  setPrintHandler() {}
+
+  /**
+   * Renames the field that is passed to the name passed in.
+   * @param {Core.Annotations.Forms.Field|object} field - The field that should be renamed.
+   * @param {string} name - The name of the field to be renamed. Cannot be blank.
+   * @return {Core.Annotations.Forms.Field|Core.Annotations.WidgetAnnotation} The renamed field.
+   */
+  updateFieldName(field, name) {}
+}

+ 12 - 0
packages/webviewer-core/src/Annotations/Forms/index.js

@@ -0,0 +1,12 @@
+/**
+ * Contains the tools to create and manipulate form field data
+ * @memberof Core.Annotations
+ * @property {function} getUsableInputWidth - A function to set the allowable width for fields with "scroll long text" disabled. It takes the width of the field and it should return a number which is the new allowable width
+ */
+class Forms {
+  constructor() {}
+
+  getUsableInputWidth() {
+    return 10;
+  }
+}

+ 222 - 0
packages/webviewer-core/src/Annotations/FreeTextAnnotation.js

@@ -0,0 +1,222 @@
+import IPathAnnotation from './IPathAnnotation';
+
+/** 
+ * @memberof Core.Annotations
+ */
+class FreeTextAnnotation extends IPathAnnotation {
+
+  /**
+   * An enum with possible auto-size types.
+   * @property {string} NONE - Freetext stays at fixed size
+   * @property {string} AUTO - Autosize to content
+   * @property {string} FIXED_WIDTH - Width cannot be changed
+   * @property {string} FIXED_HEIGHT - Height cannot be changed
+   */
+  static AutoSizeTypes = {
+    NONE: 'NONE',
+    AUTO: 'AUTO',
+    FIXED_WIDTH: 'FIXED_WIDTH',
+    FIXED_HEIGHT: 'FIXED_HEIGHT'
+  };
+
+  /**
+   * An enum with possible freetext intents.
+   * @property {string} FreeText - Treat as regular freetext
+   * @property {string} FreeTextCallout - Treat as callout
+   */
+  static Intent = {
+    FreeText: 'FreeText',
+    FreeTextCallout: 'FreeTextCallout'
+  };
+
+  /**
+   * Represents a free text annotation.
+   * @extents Core.Annotations.IPathAnnotation
+   * 
+   * @param {string} [intent] - The intended usage type of this freetext annotation. Could either be freetext or callout.
+   * @param {object} [initializer] - A map of values to auto-initialize certain properties of the annotation. You can only initialize properties defined on the annotation under the Members section (unless specified otherwise).
+   * 
+   * @property {boolean} EXPORT_CALCULATED_FONT_SIZE - Whether to export calculated font size for auto-sized fonts. Default: true
+   * @property {object} Intent - An enumeration of possible intents for FreeText Annotations
+   * @property {string} Intent.FreeText - Should treat as regular freetext
+   * @property {string} Intent.FreeTextCallout - Should treat as callout
+   */
+  constructor(intent = null, initializer = {}) {
+    super(initializer);
+    this.EXPORT_CALCULATED_FONT_SIZE = true;
+    this.Intent = intent || FreeTextAnnotation.Intent.FreeText;
+
+    Object.keys(initializer).forEach(key => {
+      if (this.hasOwnProperty(key)) {
+        this[key] = initializer[key];
+      }
+    });
+
+    /**
+     * Object that contains all the flags to customize the auto-sizing behavior
+     * @property {boolean} shrinkWidth - Whether to shrink the width of the annotation if the size of its contents are smaller in width than the annotation
+     * @property {boolean} shrinkHeight - Whether to shrink the height of the annotation if the size of its contents are smaller in height than the annotation
+     * @property {boolean} expandWidth - Whether to expand the width of the annotation if the size of the contents are larger in width than the annotation
+     * @property {boolean} expandHeight - Whether to expand the height of the annotation if the size of the contents are larger in height than the annotation
+     */
+    this.autoSizeProperties = {
+      shrinkWidth: false,
+      shrinkHeight: true,
+      expandWidth: false,
+      expandHeight: true
+    };
+
+    /** The font to use for the annotation's text. */
+    this.Font = '';
+
+    /** The font size to use for the annotation's text, specified like a CSS font size. */
+    this.FontSize = 14;
+
+    /** The horizontal alignment of the annotation's text (left, right, center) */
+    this.TextAlign = 'left';
+
+    /** The color of the text in the annotation. */
+    this.TextColor = '';
+
+    /** The vertical alignment of the annotation's text (top, bottom, center) default: top */
+    this.TextVerticalAlign = 'top';
+  }
+
+  /** Disables the Enter key for the instance of RichTextEditor */
+  disableEnterKeypress() {}
+
+  /** Enables the Enter key for the instance of RichTextEditor */
+  enableEnterKeypress() {}
+
+  /**
+   * Resize the annotation's text box to fit its contents
+   * @param {Core.Document.PageInfo} pageInfo - An object representing the page info. Contains the properties "width" and "height".
+   * @param {object} pageMatrix - The transformation matrix for the page that the annotation is on.
+   * @param {number} pageRotation - The internal degrees of rotation of the current page.
+   * @example
+const freetextAnnot = annotManager.getAnnotationsList()[0];
+const doc = docViewer.getDocument();
+const pageNumber = 1;
+const pageInfo = doc.getPageInfo(pageNumber);
+const pageMatrix = doc.getPageMatrix(pageNumber);
+const pageRotation = doc.getPageRotation(pageNumber);
+freetextAnnot.fitText(pageInfo, pageMatrix, pageRotation);
+annotManager.drawAnnotationsFromList([freetextAnnot]);
+   */
+  fitText(pageInfo, pageMatrix, pageRotation) {}
+
+  /**
+   * Gets the auto-sizing method if any.
+   * @return {string} The type of autosizing method
+   */
+  getAutoSizeType() {}
+
+  /**
+   * Gets the calculated font size when auto-sizing the text font size to fit the free text annotation's bounding box. This method retrieves the font size that has been dynamically calculated to fit the content within the bounding box of the free text annotation when Core.Annotations.FreeTextAnnotation#switchToAutoFontSize is used to enable auto-sizing of the font size. If Core.Annotations.FreeTextAnnotation#switchToAutoFontSize has not been called, it will return the current font size.
+   * @return {string} The calculated font size of the annotation, including the unit (e.g., "16px").
+   * @example
+WebViewer(...)
+.then(instance => {
+  const { annotationManager, Annotations } = instance.Core;
+
+  annotationManager.addEventListener('annotationSelected', (annotations, action) => {
+    if(action === 'selected' && annotations[0] instanceof Annotations.FreeTextAnnotation) {
+      annotations[0].switchToAutoFontSize();
+    }
+  });
+
+  annotationManager.addEventListener('annotationChanged', (annotations, action) => {
+    if(action === 'modify' && annotations[0] instanceof Annotations.FreeTextAnnotation) {
+      console.log(annotations[0].getCalculatedFontSize())
+    }
+  });
+});
+   */
+  getCalculatedFontSize() {}
+
+  /**
+   * Gets the date format of the FreeText (if any)
+   * @return {string} Returns Date format
+   */
+  getDateFormat() {}
+
+  /** @return {Core.Annotations.FreeTextAnnotation.RichTextEditor} Returns the rich text editor instance associated with the annotation */
+  getEditor() {}
+
+  /**
+   * Gets the intent of the FreeText annotation e.g. FreeTextCallout
+   * @return {string} The intent of the annotation
+   */
+  getIntent() {
+    return this.Intent;
+  }
+
+  /**
+   * Get the rectangle difference of the annotation bounding rect and the text rect
+   * @return {Core.Math.Rect} the rectangle difference
+   */
+  getPadding() {}
+
+  /**
+   * Gets the start style of the line. Only applicable for callout annotations
+   * @return {string} The start style
+   */
+  getStartStyle() {}
+
+  /** @return {boolean} Returns a boolean which indicates if the bounding box of the freetext will be resized automatically when its contents are being edited */
+  isAutoSized() {}
+
+  /** @return {boolean} Returns whether the current font is Automatically sized */
+  isAutoSizeFont() {}
+
+  /**
+   * Sets how the freetext annotation auto-sizes to content. If you are passing 'AUTO' as the parameter, you will need to invoke the Core.Annotations.FreeTextAnnotation#fitText method afterwards.
+   * @param {string} type - Use one of the types provided by AutoSizeTypes
+   */
+  setAutoSizeType(type) {}
+
+  /**
+   * Set FreeText as a date type annotation with the specified format. A viewer can use this information to allow easier updating of the text to represent a date, for example by using a date picker.
+   * @param {string} format - Set Annotation Date format
+   */
+  setDateFormat(format) {}
+
+  /**
+   * Sets the intent of the FreeText annotation
+   * @param {string} intent - The new intent for the annotation
+   */
+  setIntent(intent) {
+    this.Intent = intent;
+  }
+
+  /**
+   * Sets the rectangle difference of the annotation bounding rect and the text rect
+   * @param {Core.Math.Rect} rect - The new rectangle difference
+   */
+  setPadding(rect) {}
+
+  /**
+   * Sets the start style of the line. Only applicable for callout annotations.
+   * @param {string} startStyle - The starting style
+   */
+  setStartStyle(startStyle) {}
+
+  /**
+   * Sets the font size of the free text annotation to automatically adjust to fit its bounding box when resized. This method is used to switch the font size behavior of the free text annotation to "auto," which means that the font size will adapt dynamically to the size of the annotation's bounding box when it is resized.
+   * @example
+WebViewer(...)
+.then(instance => {
+  const { documentViewer, annotationManager, Annotations } = instance.Core;
+
+  documentViewer.addEventListener('annotationsLoaded', () => {
+    const annotList = annotationManager.getAnnotationsList();
+    annotList.forEach(annot => {
+      if (annot instanceof Annotations.FreeTextAnnotation) {
+        annot.switchToAutoFontSize();
+      }
+    });
+  });
+});
+   */
+  switchToAutoFontSize() {}
+}

+ 39 - 0
packages/webviewer-core/src/Annotations/HTMLAnnotation.js

@@ -0,0 +1,39 @@
+import Annotation from './Annotation';
+import Color from './Color';
+import Border from './Border';
+
+/**
+ * @memberof Core.Annotations
+ * @mixes Core.Actions.Dispatcher
+ */
+class HTMLAnnotation extends Annotation {
+
+  /**
+   * An common base class annotation that displays its contents using HTML5 instead of via Canvas.
+   * @extends Core.Annotations.Annotation
+   * 
+   * @property {Object} rect - The annotation's rectangle
+   * @property {?Element} element - Gets the annotation's outer display element, if it exists
+   * @property {?Element} innerElement - Gets the annotation's inner UI element, if it exists
+   * @property {boolean} [hidden=false] - Gets and sets whether the annotation is hidden
+   * @property {Core.Annotations.Color|object|Array.<any>} [color=new Core.Annotations.Color()] - Gets and sets the annotation's color (any type that can be used to construct Core.Annotations.Color)
+   * @property {Core.Annotations.Border|object} [border=new Core.Annotations.Border()] - Gets and sets the annotation's border (is a border, or a map of border properties)
+   * @property {Core.Annotations.Color|object|Array.<any>} [backgroundColor=new Core.Annotations.Color()] - Gets and sets the annotation's background color
+   * @property {Object} [actions={}] - Gets the actions attached to this annotation as an object with triggers as keys and arrays of actions as values
+   * @property {number} [rotation={}] - Gets and sets the annotation's rotation (must be one of 0, 90, 180, 270)
+   */
+  constructor(options) {
+    super(options);
+    this.rect = options?.rect || {};
+    this.element = options?.element || null;
+    this.innerElement = options?.innerElement || null;
+    this.hidden = options?.hidden || false;
+    this.color = options?.color || new Color();
+    this.border = options?.border || new Border();
+    this.backgroundColor = options?.backgroundColor || new Color();
+    this.actions = options?.actions || {};
+    this.rotation = options?.rotation || {};
+  }
+}
+
+export default HTMLAnnotation;

+ 0 - 0
packages/webviewer-core/src/Annotations/Link.js


+ 90 - 0
packages/webviewer-core/src/Annotations/RichTextEditor.js

@@ -0,0 +1,90 @@
+/**
+ * A class that represents controls used for editing contents of a freetext annotation
+ * @memberof Core.Annotations.FreeTextAnnotation
+ */
+class RichTextEditor {
+
+  /**
+   * Gets the Quill editor options.
+   * @return {Core.Annotations.FreeTextAnnotation.RichTextEditor.QuillEditorOptions} Quill options that are currently set.
+   */
+  static getEditorOptions() {}
+
+  /**
+   * Sets the Quill editor options.
+   * @param {Core.Annotations.FreeTextAnnotation.RichTextEditor.QuillEditorOptions} options - Quill options to be set.
+   */
+  static setEditorOptions(options) {}
+
+  /** Blurs the editor. */
+  blur() {}
+
+  /**
+   * Delete some content from the editor.
+   * @param {number} index - the start index of the range
+   * @param {number} length - the length of the range
+   */
+  deleteText(index, length) {}
+
+  /**
+   * Format text at the current selection. If the current selection has length of 0, then the format will be set active.
+   * @param {Core.Annotations.FreeTextAnnotation.RichTextEditor.Format} format 
+   * @param {Core.Annotations.FreeTextAnnotation.RichTextEditor.FormatValue} value 
+   */
+  format(format, value) {}
+
+  /**
+   * Gets the current contents in the editor
+   * @return {string} Returns the current contents in the editor
+   */
+  getContents() {}
+
+  /**
+   * Checks if the editor is being focused.
+   * @return {boolean} Whether the editor is focused
+   */
+  hasFocus() {}
+
+  /**
+   * Insert the content in the editor.
+   * @param {number} index - the start index of the range
+   * @param {string} contents - the contents that need to be updated in the editor
+   */
+  insertText(index, contents) {}
+
+  /**
+   * Sets the current selection in the editor.
+   * @param {number} index - the start index of the range
+   * @param {number} length - the length of the selection range
+   */
+  setSelection(index, length) {}
+
+  /**
+   * Sets the content in the editor.
+   * @param {string} contents - the contents that need to be updated in the editor
+   */
+  setText(contents) {}
+}
+
+/**
+ * The available formats of an editor instance
+ * @typedef {'color' | 'bold' | 'italic' | 'underline' | 'strike'} Core.Annotations.FreeTextAnnotation.RichTextEditor.Format
+ */
+
+/**
+ * The value of a format. The value should be a hex color string(e.g. #FF00FF) for the `color` format, and boolean for the rest of formats.
+ * @typedef {string | boolean} Core.Annotations.FreeTextAnnotation.RichTextEditor.FormatValue
+ */
+
+/**
+ * @typedef {object} Core.Annotations.FreeTextAnnotation.RichTextEditor.QuillEditorOptions
+ * @property {Element} bounds - DOM Element or a CSS selector for a DOM Element, within which the editor’s UI elements (i.e. tooltips, etc.) should be confined. Currently, it only considers left and right boundaries.
+ * @property {string} debug - Shortcut for debug. Note debug is a static method and will affect other instances of Quill editors on the page. Only warning and error messages are enabled by default.
+ * @property {object} formats - [WARNING: this is not fully supported by WebViewer and may result in unexpected behavior] Whitelist of formats to allow in the editor. See Formats for a complete list.
+ * @property {object} modules - Collection of modules to include and respective options. See Modules for more information.
+ * @property {string} placeholder - Placeholder text to show when editor is empty.
+ * @property {boolean} readOnly - Whether to instantiate the editor in read-only mode.
+ * @property {Element} scrollingContainer - DOM Element or a CSS selector for a DOM Element, specifying which container has the scrollbars (i.e. overflow-y: auto), if is has been changed from the default ql-editor with custom CSS. Necessary to fix scroll jumping bugs when Quill is set to auto grow its height, and another ancestor container is responsible from the scrolling.
+ * @property {string} theme - [WARNING: this is not fully supported by WebViewer and may result in unexpected behavior] Themes to use for the editor.
+ * @property {boolean} shouldScrollContainer - Whether to scroll the container when text exceeds the Text Box.
+ */

+ 227 - 0
packages/webviewer-core/src/Annotations/SelectionModel.js

@@ -0,0 +1,227 @@
+/**
+ * Represents a class that contains information about how an annotation should behave when selected.
+ * @memberof Core.Annotations
+ */
+class SelectionModel {
+
+  /** Defines the default color for the annotation selection outline when the user is not permitted to make modifications. */
+  static defaultNoPermissionSelectionOutlineColor;
+
+  /** Defines the default color for the annotation selection outline. */
+  static defaultSelectionOutlineColor;
+
+  /** Defines padding for selection accuracy. Default is 2. Increase this value to make selection more forgiving. */
+  static selectionAccuracyPadding = 2;
+
+  /** Defines the dash size for the selection outline. Default is 4; */
+  static selectionOutlineDashSize = 4;
+
+  /** Defines the default padding between selection control points. The padding only appears when the control points are too close. */
+  static selectionOutlineExtraPadding;
+
+  /** Defines thickness of the annotation selection outline. Default is 1. */
+  static selectionOutlineThickness = 1;
+
+  /** Defines if extra padding should be shown when the selected annotation is too small. Default is true. */
+  static showPaddingWhenAnnotationIsSmall = true;
+
+  /** Defines if a dashed line style should be used for the selection outline. Default is false. */
+  static useDashedLine = false;
+
+  /**
+   * Create a new selection model.
+   * @param {Core.Annotations.Annotation} annotation - The annotation selected
+   * @param {boolean} canModify - Modification of the annotation is allowed
+   * @param {boolean} isSelected - The annotation is already selected
+   * @param {Core.DocumentViewer} docViewer - An instance of DocumentViewer.
+   */
+  constructor(annotation, canModify, isSelected, docViewer) {
+    this.annotation = annotation;
+    this.canModify = canModify;
+    this.isSelected = isSelected;
+    this.docViewer = docViewer;
+  }
+
+  /**
+   * Overwrites the functions in SelectionModel(ex: drawSelectionOutline, getDimensions, testSelection).
+   * @param {Core.Annotations.SelectionModel} selectionModelClass - The class (constructor) of the selectionModel
+   * @param {Core.Annotations.SelectionModel.SelectionModelCustomHandlers} selectionModelCustomHandlers - An object containing multiple handlers to overwrite functions on a SelectionModel.
+   * @example
+const { Annotations } = instance.Core;
+const { SelectionModel, BoxSelectionModel } = Annotations;
+SelectionModel.setCustomHandlers(BoxSelectionModel, {
+  // draws a diagonal dashed along across the middle of the selected annotation
+   drawSelectionOutline(ctx, annotation, zoom, pageMatrix, { selectionModel, originalDrawSelectionOutline }) {
+     if (!(annotation instanceof Annotations.RectangleAnnotation)) {
+         originalDrawSelectionOutline(ctx, annotation, zoom, pageMatrix);
+         return;
+       }
+      if (typeof zoom !== 'undefined') {
+         ctx.lineWidth = SelectionModel.selectionOutlineThickness / zoom;
+       } else {
+         ctx.lineWidth = SelectionModel.selectionOutlineThickness;
+       }
+       if (selectionModel.canModify()) {
+         ctx.strokeStyle = SelectionModel.defaultSelectionOutlineColor.toString();
+       } else {
+         ctx.strokeStyle = SelectionModel.defaultNoPermissionSelectionOutlineColor.toString();
+       }
+       ctx.beginPath();
+       ctx.moveTo(annotation.X, annotation.Y);
+       ctx.lineTo(annotation.X + annotation.Width, annotation.Y + annotation.Height);
+       ctx.closePath();
+       ctx.stroke();
+       const dashUnit = SelectionModel.selectionOutlineDashSize / zoom;
+       const sequence = [dashUnit, dashUnit];
+       ctx.setLineDash(sequence);
+       ctx.strokeStyle = 'rgb(255, 255, 255)';
+       ctx.stroke();
+     },
+    // Get the dimension that is extended by 8 both horizontally and vertically
+    getDimensions(annotation, { selectionModel, originalGetDimensions }) {
+     if (!(annotation instanceof Annotations.RectangleAnnotation)) {
+        return originalGetDimensions(annotation);
+     }
+     const x = annotation.X - 4;
+     const y = annotation.Y - 4;
+     const width = annotation.Width + 2 * 4;
+     const height = annotation.Height + 2 * 4;
+     return new Annotations.Rect(x, y, x + width, y + height);
+    },
+    testSelection(annotation, x, y, pageMatrix, zoom, rotation, { selectionModel, originalTestSelection }) {
+      if (annotation instanceof Annotations.RectangleAnnotation) {
+        return originalTestSelection(annotation, x, y, pageMatrix, zoom, rotation);;
+      }
+      return Annotations.SelectionAlgorithm.boundingRectTest(annotation, x, y, zoom);
+    }
+})
+   */
+  static setCustomHandlers(selectionModelClass, selectionModelCustomHandlers) {}
+
+  /**
+   * Defines a function to set selection padding on specific types of annotations.
+   * @param {function} fn - A function that returns the padding value for specific types of annotations
+   * @example
+const { Annotations } = instance.Core;
+Annotations.SelectionModel.setSelectionModelPaddingHandler((annotation) => {
+  if (annotation instanceof Annotations.FreeTextAnnotation) {
+    return 30;
+  }
+  if (annotation instanceof Annotations.RectangleAnnotation) {
+    return 20;
+  }
+  return 0;
+});
+   */
+  static setSelectionModelPaddingHandler(fn) {}
+
+  /**
+   * Indicates that the associated annotation is able to be modified
+   * @return {boolean}
+   */
+  canModify() {
+    return this.canModify;
+  }
+
+  /**
+   * Draws the selection outline of the annotation. By default, a rectangle is drawn based on the annotations x, y, width and height.
+   * @param {CanvasRenderingContext2D} ctx 
+   * @param {Core.Annotations.Annotation} annotation 
+   * @param {number} zoom 
+   */
+  drawSelectionOutline(ctx, annotation, zoom) {}
+
+  /**
+   * Returns the ControlHandle objects associated with this selection model.
+   * @return {Array.<Core.Annotations.ControlHandle>} An array of ControlHandleObject
+   */
+  getControlHandles() {}
+
+  /**
+   * Gets the dimensions {x, y, width, height} of the selection bounding box. It may be different from the annotation's bounding box. e.g. The selection bounding box may have a padding.
+   * @param {Core.Annotations.Annotation} annotation
+   * @return {Core.Math.Rect}
+   */
+  getDimensions(annotation) {}
+
+  /**
+   * Indicates that the associated annotation is already selected. This can be useful to implement different selection behaviors when an annotation is selected.
+   * @returns {boolean}
+   */
+  isSelected() {
+    return this.isSelected;
+  }
+
+  /**
+   * Hit detection for each control handle.
+   * @param {Core.Annotations.Annotation} annotation 
+   * @param {number} zoom 
+   * @param {number} x 
+   * @param {number} y 
+   * @return {Core.Annotations.ControlHandle} The control handle that was hit
+   */
+  testControlHandles(annotation, zoom, x, y) {}
+
+  /**
+   * Determines if the provided point is a hit on the selected annotationhandle. * See {@link Core.Annotations.SelectionAlgorithm} for usuable selection algorithms.
+   * @param {Core.Annotations.Annotation} annotation - the annotation
+   * @param {number} x - the x-coordinate of the point to test, in page coordinates
+   * @param {number} y - the y-coordinate of the point to test, in page coordinates
+   * @param {object} pageMatrix - the page matrix of the page the annotation is on
+   * @param {number} zoom - the zoom level of the page the annotation is on
+   * @param {Core.PageRotation} rotation - the rotation of the page the annotation is on
+   * @return {boolean} true if the provided point is a hit
+   */
+  testSelection(annotation, x, y, pageMatrix, zoom, rotation) {}
+}
+
+/**
+ * Callback that gets passed to drawSelectionOutlineHandler in setCustomHandlers.
+ * @typedef {function} Core.Annotations.SelectionModel.CustomDrawSelectionOutlineHandler
+ * @param {CanvasRenderingContext2D} ctx - A canvas context
+ * @param {Core.Annotations.Annotation} annotation - The annotation being selected
+ * @param {number} zoom - the current zoom level of the document
+ * @param {object} pageMatrix - The transformation matrix for the page that the annotation is on
+ * @param {object} options - Additional options and parameters
+ * @param {Core.Annotations.SelectionModel} options.selectionModel - The selection model of the annotation which is currently selected.
+ * @param {function} options.originalDrawSelectionOutline - The original draw function of this selection model
+ */
+function CustomDrawSelectionOutlineHandler(ctx, annotation, zoom, pageMatrix, options) {}
+
+/**
+ * Callback that gets passed to getDimensionsHandler in setCustomHandlers.
+ * @typedef {function} Core.Annotations.SelectionModel.CustomGetDimensionsHandler
+ * @param {Core.Annotations.Annotation} annotation - The annotation being selected
+ * @param {Core.Math.Rect} selectionBox
+ * @param {number} zoom - the current zoom level of the document
+ * @param {object} options - Optional options
+ * @param {Core.Annotations.SelectionModel} options.selectionModel - The selection model of the annotation which is currently selected.
+ * @param {function} options.originalGetDimensions - The original getDimensions function of this selection model
+ * @return {Core.Math.Rect}
+ */
+function CustomGetDimensionsHandler(annotation, selectionBox, zoom, options) {}
+
+/**
+ * Callback that gets passed to testSelectionHandler in setCustomHandlers.
+ * @typedef {function} Core.Annotations.SelectionModel.CustomTestSelectionHandler
+ * @param {number} x - The x-coordinate of the point to test, in page coordinates
+ * @param {number} y - The y-coordinate of the point to test, in page coordinates
+ * @param {object} pageMatrix - the page matrix of the page the annotation is on
+ * @param {number} zoom - The zoom level of the page the annotation is on
+ * @param {Core.PageRotation} rotation - The rotation of the page the annotation is on
+ * @param {object} options - Optional options
+ * @param {Core.Annotations.SelectionModel} options.selectionModel - The selection model of the annotation which is currently selected.
+ * @param {function} options.originalTestSelection - The original draw function of this selection model
+ * @return {boolean}
+ */
+function CustomTestSelectionHandler(x, y, pageMatrix, zoom, rotation, options) {}
+
+/**
+ * Callback that gets passed to testSelectionHandler in setCustomHandlers.
+ * @typedef {object} Core.Annotations.SelectionModel.SelectionModelCustomHandlers
+ * @property {Core.Annotations.SelectionModel.CustomDrawSelectionOutlineHandler} [drawSelectionOutline] - Changes how a selection model is drawn within WebViewer.
+ * @property {Core.Annotations.SelectionModel.CustomGetDimensionsHandler} [getDimensions] - Gets the rect of the selection bounding box.
+ * @property {Core.Annotations.SelectionModel.CustomTestSelectionHandler} [testSelection] - Determines if the provided point is a hit on the selected annotation handle.
+ */
+
+export default SelectionModel;

+ 40 - 0
packages/webviewer-core/src/Annotations/WidgetAnnotation.js

@@ -0,0 +1,40 @@
+import HTMLAnnotation from './HTMLAnnotation';
+/**
+ * Represents an extendable Widget annotation. These annotations contain a field value
+ * @memberof Core.Annotations
+ */
+class WidgetAnnotation extends HTMLAnnotation {
+
+  /**
+   * Creates a new instance of WidgetAnnotation.
+   * @extends Core.Annotations.HTMLAnnotation
+   * 
+   * @property {string} fieldName - Gets the name of field associated with the widget annotation.
+   * @property {?string|number} value - Gets or sets the value of the widget annotation (defaults to the field value)
+   * @property {?(string|number)} defaultValue - Gets or sets the default value of the field associated with the widget annotation.
+   * @property {Core.Annotations.WidgetFlags} fieldFlags - Gets the flags object of the field associated with this widget.
+   * @property {Core.Annotations.WidgetFlags} flags - Gets the flags object on this widget.
+   * @property {Core.Annotations.Font} font - Gets or sets the font object associated with this widget (defaults to the field's font if not set).
+   * @property {object} appearances - Gets the appearances of this field (currently keys of an object).
+   * @property {string} appearance - Gets or sets the widgets current appearance name.
+   * @property {object} captions - Gets the map of this widget's captions. (Normal is used for the face of PushButtonAnnotations)
+   * @property {function} getCustomStyles - A function that receives the widget object and should return any CSS styles that you want to override
+   * @property {function} getContainerCustomStyles - A function that receives the widget object and should return any CSS styles that you want to override for its parent container
+   */
+  constructor(options) {
+    super(options);
+    this.fieldName = options?.fieldName;
+    this.value = options?.value;
+    this.defaultValue = options?.defaultValue;
+    this.fieldFlags = options?.fieldFlags;
+    this.flags = options?.flags;
+    this.font = options?.font;
+    this.appearances = options?.appearances;
+    this.appearance = options?.appearance;
+    this.captions = options?.captions;
+    this.getCustomStyles = options?.getCustomStyles;
+    this.getContainerCustomStyles = options?.getContainerCustomStyles;
+  }
+}
+
+export default WidgetAnnotation;

+ 104 - 0
packages/webviewer-core/src/Annotations/WidgetFlags.js

@@ -0,0 +1,104 @@
+/**
+ * @memberof Core.Annotations
+ */
+class WidgetFlags {
+  /** If set, the field shall be automatically divided into as many equally spaced positions as the value of the max length. */
+  static COMB
+
+  /** If set, the field is a combo box; if clear, the field is a list box. */
+  static COMBO
+
+  /** If set, the new value shall be committed as soon as a selection is made. */
+  static COMMIT_ON_SEL_CHANGE
+
+  /** If set, the field shall not scroll (horizontally for single-line fields, vertically for multiple-line fields) to accommodate more text than fits within its annotation rectangle. */
+  static DO_NOT_SCROLL
+
+  /** If set, text entered in the field shall not be spell-checked. */
+  static DO_NOT_SPELL_CHECK
+
+  /** If set, the combo box shall include an editable text box as well as a drop-down list; if clear, it shall include only a drop-down list. */
+  static EDIT
+
+  /** If set, the text entered in the field represents the pathname of a file whose contents shall be submitted as the value of the field. */
+  static FILE_SELECT
+
+  /** If set, more than one of the field’s option items may be selected simultaneously; if clear, at most one item shall be selected. */
+  static MULTI_SELECT
+
+  /** If set, the field may contain multiple lines of text. */
+  static MULTILINE
+
+  /** If set, the field shall not be exported by a submit-form action. */
+  static NO_EXPORT
+
+  /** (Radio buttons only) If set, exactly one radio button shall be selected at all times. */
+  static NO_TOGGLE_TO_OFF
+
+  /** If set, the field is intended for entering a secure password that should not be echoed visibly to the screen. */
+  static PASSWORD
+
+  /** If set, the field is a pushbutton that does not retain a permanent value. */
+  static PUSH_BUTTON
+
+  /** If set, the field is a set of radio buttons; if clear, the field is a checkbox. */
+  static RADIO
+
+  /** If set, a group of radio buttons within a radio button field that use the same value for the on state will turn on and off in unison. */
+  static RADIOS_IN_UNISON
+
+  /** If set, the user may not change the value of the field. */
+  static READ_ONLY
+
+  /** If set, the field shall have a value at the time it is exported by a submit-form action. */
+  static REQUIRED
+
+  /** If set, the value of this field shall be a rich text string. */
+  static RICH_TEXT
+
+  /** If set, the field’s option items shall be sorted alphabetically. */
+  static SORT
+
+  /**
+   * An object that describes flags on a Widget, or other PDF objects.
+   * @param {?Object} [options] - Define the set of supported flags.
+   */
+  constructor(options = {}) {
+    const self = this;
+    Object.keys(options).forEach(key => {
+      if (key in self) self[key] = options[key];
+    });
+  }
+
+  /**
+   * Determine if the given flag is set.
+   * @param {string} flag The name of the flag to check
+   * @return {boolean} Whether the flag is set
+   */
+  get(flag) {
+    return !!this[flag];
+  }
+
+  /**
+   * Set flags to true or false. If options is an array of strings, set the flags with those names to true. If options is a number or array of numbers, set the flags from the corresponding numbers to true. (deprecated, relies on a limited interpretation of numbers as bitfields) If options is a string, set that flag to shouldSet.
+   * @param {Array.<string>|Array.<number>|string|number} options - One of the possible input types to determine which flags to set
+   * @param {boolean} [shouldSet] - If options is a string, whether to set or unset the flag
+   */
+  set(options, shouldSet = true) {
+    if (Array.isArray(options)) {
+      options.forEach(option => {
+        if (typeof option === 'string') {
+          this.flags[option] = true;
+        } else if (typeof option === 'number') {
+          const flagName = `flag${option}`;
+          this.flags[flagName] = true;
+        }
+      });
+    } else if (typeof options === 'string') {
+      this.flags[options] = shouldSet;
+    } else if (typeof options === 'number') {
+      const flagName = `flag${options}`;
+      this.flags[flagName] = true;
+    }
+  }
+}

+ 83 - 0
packages/webviewer-core/src/Annotations/index.js

@@ -0,0 +1,83 @@
+/** 
+ * The namespace for anything to do with PDF annotations.
+ * @memberof Core
+ * @namespace
+ */
+const Annotations = {
+  /**
+   * An enum representing different line end types that are available for line annotations
+   * @property {string} NONE - No line endings
+   * @property {string} OPEN_ARROW - an arrow that points outward
+   * @property {string} R_OPEN_ARROW - an arrow that points inward
+   * @property {string} CLOSED_ARROW - a triangle that points outward
+   * @property {string} R_CLOSED_ARROW - a triangle that points inward
+   * @property {string} BUTT - a vertical line
+   * @property {string} SQUARE - a square
+   * @property {string} DIAMOND - a diamond
+   * @property {string} CIRCLE - a circle
+   * @property {string} SLASH - a slash
+   */
+  LineEndType: {
+    NONE: 'NONE',
+    OPEN_ARROW: 'OPEN_ARROW',
+    R_OPEN_ARROW: 'R_OPEN_ARROW',
+    CLOSED_ARROW: 'CLOSED_ARROW',
+    R_CLOSED_ARROW: 'R_CLOSED_ARROW',
+    BUTT: 'BUTT',
+    SQUARE: 'SQUARE',
+    DIAMOND: 'DIAMOND',
+    CIRCLE: 'CIRCLE',
+    SLASH: 'SLASH',
+  },
+
+  /**
+   * An enum representing different line types that are available for line annotations
+   * @property {string} SOLID - solid line style
+   * @property {string} DASH_1_2_2 - dashed line with [1,2,2] style
+   * @property {string} DASH_3_3 - dashed line with [3,3] style
+   * @property {string} DASH_3_3_4 - dashed line with [3,3,4] style
+   * @property {string} DASH_1_3_5 - dashed line with [1,3,5] style
+   * @property {string} DASH_2_2_4 - dashed line with [2,2,4] style
+   * @property {string} DASH_4_3_16_3 - dashed line with [4,3,16,3] style
+   * @property {string} CLOUDY - cloudy line style
+   */
+  LineStyleType: {
+    SOLID: 'SOLID',
+    DASH_1_2_2: 'DASH_1_2_2',
+    DASH_3_3: 'DASH_3_3',
+    DASH_3_3_4: 'DASH_3_3_4',
+    DASH_1_3_5: 'DASH_1_3_5',
+    DASH_2_2_4: 'DASH_2_2_4',
+    DASH_4_3_16_3: 'DASH_4_3_16_3',
+    CLOUDY: 'CLOUDY',
+  },
+
+  /**
+   * Restores the draw function back to the default.
+   * @param {Core.Annotations.Annotation} annotationClass - The class (constructor) of the annotation
+   */
+  restoreDraw(annotationClass) {},
+
+  /**
+   * Change ControlHandle's draw to customize appearance on the provided canvas context.
+   * @param {Core.Annotations.ControlHandle} controlHandle - The class (constructor) of the controlHandle
+   * @param {CustomControlHandleDrawHandler} controlHandleDrawHandler - The handler will customize the appearance of the controlHandle
+   * @example
+   * Annotations.setCustomControlHandleDrawHandler(Core.Annotations.ControlHandle, function(ctx, annotation, selectionBox, zoom, {controlHandle, originalDraw}) {
+    if(controlHandle instanceof Core.Annotations.BoxControlHandle) {
+      const dim = this.getDimensions(annotation, selectionBox, zoom);
+      ctx.fillStyle = '#FFFFFF';
+      ctx.beginPath();
+      ctx.moveTo(dim.x1 + (dim.getWidth() / 2), dim.y1);
+      ctx.lineTo(dim.x1 + dim.getWidth(), dim.y1 + dim.getHeight());
+      ctx.lineTo(dim.x1, dim.y1 + dim.getHeight());
+      ctx.closePath();
+      ctx.stroke();
+      ctx.fill();
+    } else {
+      originalDraw(ctx, annotation, selectionBox, zoom);
+    }
+  })
+   */
+  setCustomControlHandleDrawHandler(controlHandle, controlHandleDrawHandler) {}
+}

+ 392 - 0
packages/webviewer-core/src/DocumentViewer.js

@@ -0,0 +1,392 @@
+import EventHandler from './EventHandler';
+
+/**
+ * Represents a full-featured control used for displaying a Document. The viewer supports text selection and text search, and several page view modes.
+ * @memberof Core
+ */
+class DocumentViewer extends EventHandler {
+  /**
+   * Creates a new empty DocumentViewer.
+   * @extends Core.EventHandler
+   * @property {object} defaults - Default values for document viewer. Set FitMode, DisplayMode or Zoom.
+   */
+  constructor(defaults) {
+    super();
+    
+    /**
+     * Contains a list of available fit modes for the DocumentViewer.
+     * @property {function} FitWidth - Zoom level is constrained such that the current page's width will exactly fill the available space.
+     * @property {function} FitPage - Zoom level is constrained such that the current page's width or height will exactly fill the available space.
+     * @property {function} Zoom - Zoom level is not constrained.
+     */
+    this.FitMode = null;
+  }
+
+  /**
+   * @property {string} ACTIVE_SEARCH_RESULT_CHANGED - Core.DocumentViewer.activeSearchResultChanged
+   * @property {string} MOUSE_LEFT_UP - Core.DocumentViewer.mouseLeftUp
+   * @property {string} MOUSE_LEFT_DOWN - Core.DocumentViewer.mouseLeftDown
+   * @property {string} CLICK - Core.DocumentViewer.click
+   * @property {string} TAP - Core.DocumentViewer.tap
+   * @property {string} LONG_TAP - Core.DocumentViewer.longTap
+   * @property {string} DBL_CLICK - Core.DocumentViewer.dblClick
+   * @property {string} KEY_DOWN - Core.DocumentViewer.keyDown
+   * @property {string} KEY_UP - Core.DocumentViewer.keyUp
+   * @property {string} MOUSE_ENTER - Core.DocumentViewer.mouseEnter
+   * @property {string} MOUSE_MOVE - Core.DocumentViewer.mouseMove
+   * @property {string} MOUSE_LEAVE - Core.DocumentViewer.mouseLeave
+   * @property {string} MOUSE_RIGHT_DOWN - Core.DocumentViewer.mouseRightDown
+   * @property {string} MOUSE_RIGHT_UP - Core.DocumentViewer.mouseRightUp
+   * @property {string} DOCUMENT_UNLOADED - Core.DocumentViewer.documentUnloaded
+   * @property {string} ANNOTATIONS_LOADED - Core.DocumentViewer.annotationsLoaded
+   * @property {string} BEFORE_DOCUMENT_LOADED - Core.DocumentViewer.beforeDocumentLoaded
+   * @property {string} DOCUMENT_LOADED - Core.DocumentViewer.documentLoaded
+   * @property {string} DISPLAY_PAGE_LOCATION - Core.DocumentViewer.displayPageLocation
+   * @property {string} NOTIFY - Core.DocumentViewer.notify
+   * @property {string} SEARCH_IN_PROGRESS - Core.DocumentViewer.searchInProgress
+   * @property {string} TEXT_SELECTED - Core.DocumentViewer.textSelected
+   * @property {string} TOOL_MODE_UPDATED - Core.DocumentViewer.toolModeUpdated
+   * @property {string} FIT_MODE_UPDATED - Core.DocumentViewer.fitModeUpdated
+   * @property {string} ROTATION_UPDATED - Core.DocumentViewer.rotationUpdated
+   * @property {string} ZOOM_UPDATED - Core.DocumentViewer.zoomUpdated
+   * @property {string} PAGE_NUMBER_UPDATED - Core.DocumentViewer.pageNumberUpdated
+   * @property {string} PAGES_UPDATED - Core.DocumentViewer.pagesUpdated
+   * @property {string} PAGE_COMPLETE - Core.DocumentViewer.pageComplete
+   * @property {string} DISPLAY_MODE_UPDATED - Core.DocumentViewer.displayModeUpdated
+   * @property {string} BEGIN_RENDERING - Core.DocumentViewer.beginRendering
+   * @property {string} FINISHED_RENDERING - Core.DocumentViewer.finishedRendering
+   * @property {string} TOOL_UPDATED - Core.DocumentViewer.toolUpdated
+   * @property {string} SNAP_MODE_INITIALIZED - Core.DocumentViewer.snapModeInitialized
+   * @property {string} BEFORE_SNAP_MODE_INITIALIZED - Core.DocumentViewer.beforeSnapModeInitialized
+   * @property {string} EMBEDDED_POP_UP_MENU - Core.DocumentViewer.embeddedPopUpMenu
+   */
+  static get Events() {}
+
+  /** Clears all search results from the viewer */
+  clearSearchResults() {}
+
+  /** Clears any selected text on the all pages. */
+  clearSelection() {}
+
+  /**
+   * Reinitializes data and clears the viewer area content.
+   * @returns {Promise.<void>} A promise that resolves when the document is finished closing
+   */
+  closeDocument() {}
+
+  /** Scrolls the viewer so that the upper-left corner of the Document's first page is in the upper-left corner of the viewer. */
+  displayFirstPage() {}
+
+  /** Scrolls the viewer so that the upper-left corner of the Document's last page is in the upper-left corner of the viewer. */
+  displayLastPage() {}
+
+  /**
+   * Scrolls the viewer so that the position of the search result is in the middle of the viewer.
+   * @param {object} result - The result of a search, Core.Search.SearchResult. Returned by onResult in Core.DocumentViewer#textSearchInit.
+   * @param {function} [jump] - The callback for navigating to the found result. This is optional and will jump to the correct location in the DocumentViewer if no parameter is passed.
+   */
+  displaySearchResult(result, jump) {}
+
+  /**
+   * Returns the AnnotationHistoryManager used by this DocumentViewer
+   * @returns {Core.AnnotationHistoryManager} An instance of AnnotationHistoryManager
+   */
+  getAnnotationHistoryManager() {}
+
+  /**
+   * Returns the AnnotationManager used by this DocumentViewer
+   * @returns {Core.AnnotationManager} An instance of AnnotationManager
+   */
+  getAnnotationManager() {}
+
+  /**
+   * Returns the number of pages in a document.
+   * @returns {number} The number of the pages in the current document.
+   */
+  getPageCount() {}
+
+  /**
+   * Get the width of a specified page in the document.
+   * @param {number} pageNumber The page number to get the width of.
+   * @return {number} The width of the page
+   */
+  getPageWidth(pageNumber) {}
+
+  /**
+   * Get the height of a specified page in the document.
+   * @param {number} pageNumber The page number to get the height of.
+   * @return {number} The height of the page
+   */
+  getPageHeight(pageNumber) {}
+
+  /**
+   * Returns the current tool.
+   * @returns {Core.Tools.Tool} The current tool.
+   */
+  getToolMode() {}
+
+  /**
+   * Returns the current zoom level.
+   * @returns {number} The current zoom level.
+   */
+  getZoomLevel() {}
+
+  /**
+   * Initialize the viewer and load the given file into the viewer.
+   * @param {string | File | ArrayBuffer | Blob | Core.Document | Core.PDFNet.PDFDoc} src - Source parameter, path/url to document or File.
+   * @param {Core.LoadDocumentOptions} [options] - Load document options.
+   * @returns {Promise.<void>} A promise that resolves when the document is finished loading
+   */
+  loadDocument(src, options) {}
+
+  /**
+   * Selects the text content of the document given two page coordinates.
+   * @param pt1 - Starting page coordinate. Requires properties x, y and pageNumber.
+   * @param pt2 - Ending page coordinate. Requires properties x, y and pageNumber.
+   */
+  select(pt1, pt2) {}
+
+  /**
+   * Sets the current page. Updates the current page and jumps to it.
+   * @param {number} pageNumber - The page number to jump to.
+   * @param {boolean} isSmoothScroll - If set to true, the viewer will scroll in a smooth way.
+   */
+  setCurrentPage(pageNumber, isSmoothScroll) {}
+
+  /**
+   * Sets how the document will scale to fit the size of the scrollviewer's viewport. Also re-renders content to the appropriate zoom level. Only renders if a document has been loaded.
+   * @param {function} fitMode - A fit mode property from Core.DocumentViewer#FitMode
+   */
+  setFitMode(fitMode) {}
+
+  /**
+   * Sets the tool mode. Also removes selection caused by text selection or search.
+   * @param {Core.Tools.Tool} tool - An instance of the toolmode
+   */
+  setToolMode(tool) {}
+
+  /**
+   * Adjusts the viewer's zoom factor
+   * @param {number} zoom - Zoom value.
+   */
+  zoomTo(zoom) {}
+
+
+
+  /**
+   * Download the document as an ArrayBuffer.
+   * @return {Promise.<ArrayBuffer>}
+   */
+  download() {}
+
+  /**
+   * Switche the display mode of the web viewer.
+   * @param {0|1|2} mode Display Mode Parameter, Single Mode: 0, Two-up Mode: 1, Cover Mode: 2
+   */
+  webViewerSwitchSpreadMode(mode) {}
+
+  /** Request full screen mode for the web viewer. */
+  requestFullScreenMode() {}
+  
+  /** Display the previous page in the web viewer. */
+  previousPage() {}
+
+  /** Display the next page in the web viewer. */
+  nextPage() {}
+
+  /**
+   * Display the specified page in the web viewer.
+   * @param {string} pageNumber The page number to display.
+   */
+  pageNumberChanged(pageNumber) {}
+
+  /** Scroll the web viewer to the specified position. */
+  scrollTo() {}
+
+  /**
+   * Switche the scroll mode of the web viewer.
+   * @param {0|1} mode Scroll Mode Parameter, Vertical Mode: 0, Horizontal Mode: 1
+   */
+  webViewerSwitchScrollMode(mode) {}
+
+  /**
+   * Searche the document for the specified text.
+   * @param {string} text The Searched Content.
+   */
+  search(text) {}
+
+  /** Zoom in the web viewer. */
+  zoomIn() {}
+
+  /** Zoom out the web viewer. */
+  zoomOut() {}
+
+  /**
+   * Change the scale of the web viewer.
+   * @param {number} scale The scale to change to.
+   */
+  webViewerScaleChanged(scale) {}
+
+  /**
+   * Add annotations to the document.
+   * @param {object} annotation The object containing the annotation to add.
+   */
+  addAnnotations(annotation) {}
+
+  /**
+   * Insert a blank page into the document.
+   * @param {number} pageIndex The index to insert the blank page at.
+   * @param {number} width The width of the blank page.
+   * @param {number} height The height of the blank page.
+   */
+  insertBlankPage(pageIndex, width, height) {}
+
+  /**
+   * Insert pages into the document.
+   * @param {File} file The file to insert.
+   * @param {number} pageIndexToInsert The index to insert the pages at.
+   * @param {Array.<number|string>} pagesIndexToInsert The index of the pages to insert.
+   */
+  insertPages(file, pageIndexToInsert, pagesIndexToInsert) {}
+
+  /**
+   * Remove pages from the document.
+   * @param {Array.<number>} pagesIndexToDelete The index of the pages to remove.
+   */
+  removePages(pagesIndexToDelete) {}
+  
+  /**
+   * Rotate pages in the document.
+   * @param {Array.<number>} pagesIndexToRotate The index of the pages to rotate.
+   * @param {number} rotation The rotation to apply to the pages.
+   */
+  rotatePages(pagesIndexToRotate, rotation) {}
+
+  /**
+   * Extract pages from the document.
+   * @param {Array.<number|string>} pagesIndexToExtract The index of the pages to extract.
+   * @return {Promise.<ArrayBuffer>} The ArrayBuffer of the extracted pages.
+   */
+  extractPages(pagesIndexToExtract) {}
+
+  /**
+   * Move pages in the document.
+   * @param {string} pagesIndexToMove The index of the pages to move.
+   * @param {number} targetPageIndex The index to move the pages to.
+   */
+  movePages(pagesIndexToMove, targetPageIndex) {}
+
+  /**
+   * Copy pages in the document.
+   * @param {Array.<number>} pagesIndexToCopy The index of the pages to copy.
+   */
+  copyPages(pagesIndexToCopy) {}
+
+  /**
+   * Set the password for the document.
+   * @param {string} password The password to set.
+   */
+  setPassword(password) {}
+
+  /** Remove the password from the document. */
+  removePassword() {}
+
+  /**
+   * Convert a point from page coordinates to window coordinates.
+   * @param {object} pagePoint The point to convert.
+   * @param {number} pagePoint.x The x coordinate of the point.
+   * @param {number} pagePoint.y The y coordinate of the point.
+   * @param {number} pageNumber The page number of the point.
+   * @return {object} The converted point.
+   */
+  pageToWindow(pagePoint, pageNumber) {}
+
+  /**
+   * Convert a point from window coordinates to page coordinates.
+   * @param {object} windowPoint The point to convert.
+   * @param {number} windowPoint.x The x coordinate of the point.
+   * @param {number} windowPoint.y The y coordinate of the point.
+   * @param {number} pageNumber The page number of the point.
+   * @return {object} The converted point.
+   */
+  windowToPage(windowPoint, pageNumber) {}
+
+  /**
+   * Get the scroll view element of the web viewer.
+   * @return {HTMLElement} The scroll view element.
+   */
+  getScrollViewElement() {}
+
+  /**
+   * Get the selected page of the web viewer.
+   * @param {object} windowCoordinates The window coordinates to convert.
+   * @param {number} windowCoordinates.x The x coordinate of the point.
+   * @param {number} windowCoordinates.y The y coordinate of the point.
+   * @return {number} The selected page.
+   */
+  getSelectedPage(windowCoordinates) {}
+
+  /**
+   * Triggered when the active search result has changed
+   * @param {object} result - The new active search result, Core.Search.SearchResult.
+   * @event Core.DocumentViewer#activeSearchResultChanged
+   */
+  activeSearchResultChanged() {}
+
+  /**
+   * Triggered when a new document has been loaded
+   * @event Core.DocumentViewer#documentLoaded
+   */
+  documentLoaded() {}
+
+  /**
+   * Triggered when all the annotations embedded in the document have been loaded
+   * @event Core.DocumentViewer#annotationsLoaded
+   */
+  annotationsLoaded() {}
+
+  /**
+   * Triggered for the click event in the DocumentViewer's viewing area
+   * @param {object} nativeEvt - Event object of the native JavaScript event
+   * @event Core.DocumentViewer#click
+   */
+  click() {}
+
+  /**
+   * Triggered when the display mode is updated
+   * @event Core.DocumentViewer#displayModeUpdated
+   * @returns {Core.VirtualDisplayMode} The new display mode of the viewer
+   */
+  displayModeUpdated() {}
+
+  /**
+   * Triggered when a Widget is clicked that triggers the Embedded JavaScript popUpMenu API
+   * @event Core.DocumentViewer#embeddedPopUpMenu
+   * @param {Core.DocumentViewer.embeddedPopUpMenuData} popUpMenuData - Data provided from the Embedded JavaScript popUpMenu API being called
+   */
+  embeddedPopUpMenu() {}
+
+  /**
+   * Triggered when the current document has been closed and unloaded
+   * @event Core.DocumentViewer#documentUnloaded
+   */
+  documentUnloaded() {}
+
+  /**
+   * Triggered when DocumentViewer finishes rendering pages
+   * @event Core.DocumentViewer#finishedRendering
+   */
+  finishedRendering() {}
+
+  /**
+   * Triggered when the page number is updated
+   * @event Core.DocumentViewer#pageNumberUpdated
+   */
+  pageNumberUpdated() {}
+
+  /**
+   * Triggered when the page number is updated
+   * @event Core.DocumentViewer#zoomUpdated
+   */
+  zoomUpdated() {}
+}

+ 113 - 0
packages/webviewer-core/src/EventHandler.js

@@ -0,0 +1,113 @@
+/**
+ * @memberof Core
+ */
+export default class EventHandler {
+  constructor() {
+    this._listeners = Object.create(null);
+  }
+
+  /**
+   * Add a handler to the given event name
+   * @param {string|number} type - The name of the event to listen to
+   * @param {function} fn - The handler to be called when the event is triggered
+   * @param {object} [options] - Optional options object for addEventListener
+   * @param {boolean} options.once - If true then the handler will be called only once
+   * @returns {object} Returns the object that 'addEventListener' is being called on
+   * @example
+   * myObject.addEventListener('eventName', (eventParameter1, eventParameter2) => {
+   *  ...
+   * });
+   */
+  addEventListener(type, fn, options = null) {
+    return this._on(type, fn, {
+      external: true,
+      once: options?.once,
+    });
+  }
+
+  /**
+   * Remove a handler of the given event name and namespace (if given) or with a function reference
+   * @param {string|number} [type] - The name of the event to remove the handler of with an optional namespace.
+   * @param {function} [fn] - The handler associated with this event to be removed. If fn is undefined, all the handlers of the given event namespace will be removed. If you are not passing in this parameter then a namespace must be used with the event name.
+   * @returns {object} Returns the object that 'removeEventListener' is being called on
+   * @example
+   * myObject.removeEventListener('eventName.namespace');
+   * myObject.removeEventListener('eventName', fn);
+   */
+  removeEventListener(type, fn) {
+    return this._off(type, fn);
+  }
+
+  /**
+   * Calls the handlers of the event name with given data
+   * @param {string|number} type - event name of which the handlers will be called.
+   * @param {*} [data] - data that will be passed to the handlers. If data is an array, it will be spread and then passed to the handlers
+   * @returns {object} Returns the object that 'trigger' is being called on
+   * @example
+   * myObject.trigger('eventName');
+   * myObject.trigger('eventName', [eventParameter1, eventParameter2]);
+   */
+  trigger(type, data, action = '') {
+    const eventListeners = this._listeners[type];
+    if (!eventListeners || eventListeners.length === 0) {
+      return;
+    }
+    let externalListeners;
+    // Making copy of the listeners array in case if it will be modified
+    // during dispatch.
+    for (const { fn, external, once } of eventListeners.slice(0)) {
+      if (once) {
+        this._off(type, fn)
+      }
+      if (external) {
+        (externalListeners ||= []).push(fn)
+        continue;
+      }
+      fn(data, action)
+    }
+    // Dispatch any "external" listeners *after* the internal ones, to give the
+    // viewer components time to handle events and update their state first.
+    const listenersToRemove = externalListeners;
+    if (externalListeners) {
+      for (const fn of externalListeners) {
+        fn(data);
+      }
+      externalListeners = null;
+    }
+    return listenersToRemove;
+  }
+
+  /**
+   * @ignore
+   */
+  _on(type, fn, options = null) {
+    const eventListeners = (this._listeners[type] ||= []);
+    const listener = {
+      fn,
+      external: options?.external === true,
+      once: options?.once === true,
+    };
+    eventListeners.push(listener);
+    return listener;
+  }
+
+  /**
+   * @ignore
+   */
+  _off(type, fn) {
+    const eventListeners = this._listeners[type];
+    if (!eventListeners) {
+      return;
+    }
+    if (!fn) {
+      const listenersToRemove = eventListeners;
+      eventListeners.length = 0;
+      return listenersToRemove;
+    }
+    for (let i = 0, ii = eventListeners.length; i < ii; i++) {
+      if (eventListeners[i].fn === fn) {
+        return eventListeners.splice(i, 1);
+      }
+    }
+  }
+}