# Undo/Redo

# 简介

Foxit PDF SDK for Web 提供了一个名为 "undo-redo" 的插件,它为 PDF 文档添加了撤销和重做功能。用户可以通过 Ctrl+ZCtrl+Y 组合键来撤销和重做以下操作:

  • 添加注释
  • 删除注释
  • 修改注释
  • 等等

此外,该插件还提供了编程接口,允许开发者在自定义应用中实现撤销和重做功能。

# 加载插件

请参考 Addons 简介 一章中的方法。插件加载后, undo-redo 功能便会自动生效, 用户可以通过 Ctrl+ZCtrl+Y (Mac 中为 Cmd+ZCmd+Y)来撤销和重做上一步操作。

# 编程接口

除了提供快捷键之外,undo-redo 插件还提供了一系列编程接口方便开发者实现自定义功能。在开始调用接口之前,我们先获取 undo-redo 插件的对象实例:

async function obtainAddonInstanceExample() {
    const undoRedoAddon = await pdfui.getAddonInstance("UndoRedoAddon");
    if (undoRedoAddon) {
        // 这里就可以开始使用 undoRedoAddon 接口了。
    } else {
        console.error("UndoRedo Addon instance not found.");
    }
}

关于 UndoRedoAddon 详细的接口描述,可以参考 API Reference (opens new window)

获取到 UndoRedoAddon 实例后,接下来,我们开始使用它吧。

# 1. 撤销操作

undoRedoAddon.undo() 接口用于撤销操作,它可以撤销用户的最后一次操作。请查看例子:

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

# 2. 重做操作

undoRedoAddon.redo() 接口用于重做操作需要,它可以重新执行最后一次被撤销的用户操作。请查看例子:

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

# 3. 撤销所有操作

UndoRedoAddon.undoAll() 接口用于撤销所有操作,他可以撤销用户所有操作,将文档还原到最初状态。 请查看例子:

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

虽然所有操作被撤销,但是依然可以一步一步地重做。

# 4. 记录操作

UndoRedoAddon 还提供了一个 invoke 方法, 通过它,可以传递一个回调函数,该函数接受一个 PDFDoc (opens new window) 对象,这是一个被包装过的对象,它相对于未包装过的 PDFDoc 不同之处在于它可以记录操作,这些被记录的操作可以被撤销和重做。

下面是一个简单的例子,我们在回调函数中记录了创建注释和修改注释的操作。这些操作被记录后,我们还可以通过 undo/redo 接口来撤销和重做:

function wait() {
    // 这个函数的作用是让下面的每个步骤执行完后停顿一定时间,方便查看效果。
    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, // 初始化外边框颜色为:红色
            });
            await wait();
            // 创建完成后将外边框颜色设置为紫色
            await square.setBorderColor(0xff00ff);
        });

        await wait();

        // 撤销后,颜色变回 红色
        await undoRedoAddon.undo();

        await wait();

        // 重做后,颜色变为 紫色
        await undoRedoAddon.redo();

        await wait();

        // 撤销所有,square Annotation 将被删除

        await undoRedoAddon.undoAll();
    } else {
        console.error("UndoRedo Addon instance not found.");
    }
}

# 支持 Undo/Redo 的 API 列表

下面列举了 Foxit PDF SDK for Web 当前支持撤销和重做操作的 API,

# 1. 支持的对象获取接口

这里列举了一系列接口,这些接口在 UIXAddon.invoke 接口回调函数中调用后会返回一个被包装过的对象。开发者可以调用这个被包装过对象上的方法来记录操作:

  1. PDFDoc (opens new window) 接口
    • PDFDoc.getPageByIndex(index): 通过索引获取文档中的页面。
    • PDFDoc.getPageById(id): 通过页面的唯一标识符获取页面。
    • PDFDoc.getAnnots(): 获取文档中的所有注释。
  2. PDFPage (opens new window) 接口
    • PDFPage.getAnnots(): 获取页面上的所有注释。
    • PDFPage.getAnnotsByObjectNumArray(objectNums): 通过对象编号数组获取页面上的注释。
    • PDFPage.getAnnotsByIdArray(annotIds): 通过注释标识符数组获取页面上的注释。
    • PDFPage.getMarkupAnnots(): 获取页面上的标记注释。
    • PDFPage.getAnnotTree(): 获取页面上注释的树状结构。

我们举个例子:

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.");
    }
}

# 2. 支持的操作接口

这些接口被调用后,其操作将会被自动记录,记录后的操作可以被撤销和重做。要注意的是,这些接口必须在上一段中列举的对象获取接口返回的对象上调用。

  1. PDFDoc (opens new window) 接口
    • PDFDoc.addAnnot
  2. PDFPage (opens new window) 接口
    • PDFPage.addAnnot
    • PDFPage.removeAnnotById
    • PDFPage.removeAnnotByObjectNumber
  3. Annot (opens new window) 接口(所有注释类的超类):
    • Annot.setContent
    • Annot.setRect
    • Annot.setBorderColor
    • Annot.setBorderInfo
    • Annot.setFlags
  4. Markup (opens new window) 接口(所有Markup注释的超类, 包括: FreeText(callout, textbox, typewriter), ink, line, note, polygon, polyline,redact,sound,square,stamp, TextMarkup(highlight,squiggly,strikeout,underline) ):
    • Markup.setOpacity
    • Markup.setSubject
    • Markup.setTitle
    • Markup.addReply
    • Markup.addReviewState
    • Markup.addMarkedState
    • Markup.setFillColor
  5. 特定的注释类型: