# # Undo/Redo

## # Introduction

Foxit PDF SDK for Web provides an addon called "undo-redo", which adds undo and redo functionality to PDF documents. Users can use the combination keys `Ctrl+Z` and `Ctrl+Y` to undo and redo the following actions:

- Add comments
- Delete comments
- Modify comments
- And more

Additionally, the addon also provides APIs to allow developers to implement undo and redo functionality in their custom applications.

## # Load the addon

Please refer to the methods in the Addons Introduction section. Once the addon is loaded, the undo-redo functionality will be automatically activated. Users can use `Ctrl+Z` and `Ctrl+Y` (or `Cmd+Z` and `Cmd+Y` on Mac) to undo and redo the previous actions.

## # APIs

In addition to providing shortcut keys, the undo-redo addon also offers a series of APIs to help developers implement custom features. Before calling the API, we first need to obtain the object instance of the undo-redo addon:

    async function obtainAddonInstanceExample() {
        const undoRedoAddon = await pdfui.getAddonInstance("UndoRedoAddon");
        if (undoRedoAddon) {
            // Here you can begin to use the methods of undoRedoAddon object.
        } else {
            console.error("UndoRedo Addon instance not found.");
        }
    }

For a detailed description of APIs in UndoRedoAddon class, please refer to the API Reference
(opens new window).

After getting the UndoRedoAddon instance, let's start using it.

### # Undo an operation

The `undoRedoAddon.undo()` method is used for undoing an operation, allowing the reversal of the user's last action. Please refer to the example:

    async function undoExample() {
        const undoRedoAddon = await pdfui.getAddonInstance("UndoRedoAddon");
        if (undoRedoAddon) {
            undoRedoAddon.undo();
        } else {
            console.error("UndoRedo Addon instance not found.");
        }
    }

### # Redo an operation

The `undoRedoAddon.redo()` method is used for redoing an operation, allowing the reapplication of the most recent user action that was undone. Please refer to the example:

    async function redoExample() {
        const undoRedoAddon = await pdfui.getAddonInstance("UndoRedoAddon");
        if (undoRedoAddon) {
            undoRedoAddon.redo();
        } else {
            console.error("UndoRedo Addon instance not found.");
        }
    }

### # Undo all operations

The `undoRedoAddon.undoAll()` method is used for undoing all operations, allowing the reversal of all user actions and restoring the document to its original state. Please refer to the example:

    async function undoAllExample() {
        const undoRedoAddon = await pdfui.getAddonInstance("UndoRedoAddon");
        if (undoRedoAddon) {
            undoRedoAddon.undoAll();
        } else {
            console.error("UndoRedo Addon instance not found.");
        }
    }

Although all operations have been undone, they can still be redone step by step.

### # Record operations

The UndoRedoAddon also provides an `invoke` method, through which a callback function can be passed. This function accepts a PDFDoc
(opens new window) object, which is a wrapped object. The difference between this wrapped PDFDoc and an unwrapped one is that it can record operations, and these recorded operations can be undone and redone.

Here is a simple example, where we record the operations of creating and modifying annotations within the callback function. After these operations have been recorded, we can also undo and redo them using the `undo/redo` APIs:

    function wait() {
        // The purpose of this function is to pause for a certain amount of time after each step is executed, making it easier to observe the results.
        return new Promise((resolve) => setTimeout(resolve, 2000));
    }
    async function invokeExample() {
        const undoRedoAddon = await pdfui.getAddonInstance("UndoRedoAddon");
        if (undoRedoAddon) {
            await undoRedoAddon.invoke(async (pdfDoc) => {
                const pdfPage = await pdfDoc.getPageByIndex(0);
                const [square, popup] = await pdfPage.addAnnot({
                    type: "square",
                    rect: {
                        left: 0,
                        right: 100,
                        bottom: 500,
                        top: 550,
                    },
                    color: 0xff0000, // Initialize the border color to red.

                });
                await wait();
                // Set the border color to purple after creation is completed.
                await square.setBorderColor(0xff00ff);
            });

            await wait();

            // After undoing, the color reverts back to red.
            await undoRedoAddon.undo();

            await wait();

            // After redoing, the color changes to purple.
            await undoRedoAddon.redo();

            await wait();

            // Undo all, the square annotation will be deleted.
            await undoRedoAddon.undoAll();
        } else {
            console.error("UndoRedo Addon instance not found.");
        }
    }

## # List of APIs supporting Undo/Redo

The following lists the APIs that Foxit PDF SDK for Web currently supports for undo and redo operations.

### # Supported object retrieval APIs

Here is a series of APIs that, when called within the `UIXAddon.invoke` callback function, return a wrapped object. Developers can use the methods on this wrapped object to record operations:

1. PDFDoc
   (opens new window)
   - `PDFDoc.getPageByIndex(index)`: Get a page in the document by its index.
   - `PDFDoc.getPageById(id)`: Get a page using its unique identifier.
   - `PDFDoc.getAnnots()`: Get all annotations in the document.
2. PDFPage
   (opens new window)
   - `PDFPage.getAnnots()`: Get all annotations on a page.
   - `PDFPage.getAnnotsByObjectNumArray(objectNums)`: Get annotations on a page using an array of object numbers.
   - `PDFPage.getAnnotsByIdArray(annotIds)`: Get annotations on a page using an array of annotation identifiers.
   - `PDFPage.getMarkupAnnots()`: Get markup annotations on a page.
   - `PDFPage.getAnnotTree()`: Get the tree structure of annotations on a page.

Example:

    async function getAnnotsExample() {
        const undoRedoAddon = await pdfui.getAddonInstance("UndoRedoAddon");
        if (undoRedoAddon) {
            await undoRedoAddon.invoke(async (pdfDoc) => {
                const pdfPage = await pdfDoc.getPageByIndex(0);
                const [square] = await pdfPage.getAnnotsByObjectNumArray([12002]);
                square.setBorderColor(0xFF0000);
            });
            await undoRedoAddon.undo();
        } else {
            console.error("UndoRedo Addon instance not found.");
        }
    }

### # Supported operation APIs

After these APIs are called, their operations will be automatically recorded, and the recorded operations can be undone and redone. It is important to note that these APIs must be called on the objects returned by the object retrieval APIs listed in the previous paragraph.

1. PDFDoc
   (opens new window)
   - `PDFDoc.addAnnot`
2. PDFPage
   (opens new window)
   - `PDFPage.addAnnot`
   - `PDFPage.removeAnnotById`
   - `PDFPage.removeAnnotByObjectNumber`
3. Annot
   (opens new window) (All annotation classes' superclass)：
   - `Annot.setContent`
   - `Annot.setRect`
   - `Annot.setBorderColor`
   - `Annot.setBorderInfo`
   - `Annot.setFlags`
4. Markup
   (opens new window) (All Markup annotation classes' superclass, including FreeText(callout, textbox, typewriter), ink, line, note, polygon, polyline, redact, sound, square, stamp and TextMarkup(highlight, squiggly, strikeout, underline)）:
   - `Markup.setOpacity`
   - `Markup.setSubject`
   - `Markup.setTitle`
   - `Markup.addReply`
   - `Markup.addReviewState`
   - `Markup.addMarkedState`
   - `Markup.setFillColor`
5. Specific annotation types:
   - Circle
     (opens new window)
     - `Circle.setMeasureRatio`
     - `Circle.setMeasureUnit`
     - `Circle.setMeasureConversionFactor`
     - `Circle.enableCaption`
     - `Circle.setCaptionColor`
   - Line
     (opens new window)
     - `Line.setMeasureRatio`
     - `Line.setMeasureUnit`
     - `Line.enableCaption`
     - `Line.setCaptionColor`
     - `Line.setEndPoint`
     - `Line.setStartPoint`
     - `Line.setEndingStyle`
     - `Line.setStyleFillColor`
     - `Line.setLeaderLineExtend`
     - `Line.setLeaderLineOffset`
     - `Line.setLeaderLineLength`
     - `Line.setCaptionOffset`
   - Polygon
     (opens new window)
     - `Polygon.enableCaption`
     - `Polygon.setCaptionColor`
     - `Polygon.updateVertexes`
     - `Polygon.setVertexes`
   - Polyline
     (opens new window)
     - `Polyline.setMeasureRatio`
     - `Polyline.enableCaption`
     - `Polyline.setCaptionColor`
     - `Polyline.setEndingStyle`
     - `Polyline.setStyleFillColor`
     - `Polyline.updateVertexes`
     - `Polyline.setVertexes`
   - Square
     (opens new window)
     - `Square.setMeasureRatio`
     - `Square.setMeasureUnit`
     - `Square.setMeasureConversionFactor`
   - FreeText
     (opens new window)(typewriter, textbox, callout)
     - `FreeText.setAlignment`
     - `FreeText.setInnerRect`
     - `FreeText.setCalloutLinePoints`
     - `FreeText.setCalloutLineEndingStyle`
     - `FreeText.setDefaultAppearance`
     - `FreeText.setRotation`
   - Screen
     (opens new window)
     - `Screen.setRotation`
   - Stamp
     (opens new window)
     - `Stamp.setRotation`
   - Redact
     (opens new window)
     - `Redact.setDefaultAppearance`
     - `Redact.setOverlayText`
     - `Redact.removeOvelayText`
     - `Redact.setOverlayTextAlignment`
     - `Redact.setRepeat`
     - `Redact.setAutoFontSize`
     - `Redact.setRedactApplyFillColor`
   - Ink
     (opens new window)
     - `Ink.setInkList`
   - Link
     (opens new window)
     - `Link.setHighlightingMode`
     - `Link.setAction`
   - FileAttachment
     (opens new window)
     - `FileAttachment.setIconName`
   - Note
     (opens new window)
     - `Note.setIconName`