Customize the context menu in PDF Viewer

28 Feb 202624 minutes to read

The PDF Viewer supports adding custom options to the context menu using the addCustomMenu() method; define custom actions with customContextMenuSelect(). See the addCustomMenu and customContextMenuSelect() API.

Add a custom option

The following example adds custom options to the context menu.

import { PdfViewerComponent, Toolbar, Magnification, Navigation, LinkAnnotation, BookmarkView, ThumbnailView, Print,
         TextSelection, TextSearch, Annotation, FormFields, FormDesigner,PageOrganizer } from "@syncfusion/ej2-vue-pdfviewer";
import { MenuComponent } from "@syncfusion/ej2-vue-navigations";
import { CheckBoxComponent } from "@syncfusion/ej2-vue-buttons";

export default {
    components: {
        'ejs-pdfviewer': PdfViewerComponent,
        'ejs-menu': MenuComponent,
        'ejs-checkbox': CheckBoxComponent
    },
    data: function() {
        return {
			documentPath:'https://cdn.syncfusion.com/content/pdf/pdf-succinctly.pdf',
            resourceUrl: 'https://cdn.syncfusion.com/ej2/31.2.2/dist/ej2-pdfviewer-lib',
            menuItems : [
            {
                text: 'Search In Google',
                id: 'search_in_google',
                iconCss: 'e-icons e-search'
            },
            {
                text: 'Lock Annotation',
                iconCss: 'e-icons e-lock',
                id: 'lock_annotation'
            },
            {
                text: 'Unlock Annotation',
                iconCss: 'e-icons e-unlock',
                id: 'unlock_annotation'
            },
            {
                text: 'Lock Form Field',
                iconCss: 'e-icons e-lock',
                id: 'read_only_true'
            },
            {
                text: 'Unlock Form Field',
                iconCss: 'e-icons e-unlock',
                id: 'read_only_false'
            },
        ]
        }
    },
	provide: {
      PdfViewer: [Toolbar, Magnification, Navigation, LinkAnnotation, BookmarkView, ThumbnailView, Print, TextSelection, TextSearch, Annotation, FormFields, FormDesigner,PageOrganizer]
    },
    methods: {
    documentLoad: function (args) {
        var viewer = this.$refs.pdfviewer.ej2Instances;
        viewer.addCustomMenu(this.menuItems, false, false);
    },
  }
}
import {
  PdfViewerComponent as EjsPdfviewer, Toolbar, Magnification, Navigation, LinkAnnotation, BookmarkView, ThumbnailView, Print,
  TextSelection, TextSearch, Annotation, FormFields, FormDesigner, PageOrganizer
} from "@syncfusion/ej2-vue-pdfviewer";
import { MenuComponent as EjsMenu } from "@syncfusion/ej2-vue-navigations";
import { CheckBoxComponent as EjsCheckbox } from "@syncfusion/ej2-vue-buttons";
import { provide, ref } from "vue";

const pdfviewer = ref(null);
const documentPath = 'https://cdn.syncfusion.com/content/pdf/pdf-succinctly.pdf';
const resourceUrl = 'https://cdn.syncfusion.com/ej2/31.2.2/dist/ej2-pdfviewer-lib';
const menuItems = [
  {
    text: 'Search In Google',
    id: 'search_in_google',
    iconCss: 'e-icons e-search'
  },
  {
    text: 'Lock Annotation',
    iconCss: 'e-icons e-lock',
    id: 'lock_annotation'
  },
  {
    text: 'Unlock Annotation',
    iconCss: 'e-icons e-unlock',
    id: 'unlock_annotation'
  },
  {
    text: 'Lock Form Field',
    iconCss: 'e-icons e-lock',
    id: 'read_only_true'
  },
  {
    text: 'Unlock Form Field',
    iconCss: 'e-icons e-unlock',
    id: 'read_only_false'
  },
]

provide('PdfViewer', [Toolbar, Magnification, Navigation, LinkAnnotation, BookmarkView, ThumbnailView, Print, TextSelection, TextSearch, Annotation, FormFields, FormDesigner, PageOrganizer])

const documentLoad = function (args) {
  const viewer = pdfviewer.value.ej2Instances;
  viewer.addCustomMenu(this.menuItems, false, false);
}

Customize the default vs custom menu

Toggle the display of the default context menu. When the addCustomMenu parameter is true, the default menu is hidden; when false, default menu items are displayed alongside custom items.

import {
  PdfViewerComponent as EjsPdfviewer, Toolbar, Magnification, Navigation, LinkAnnotation, BookmarkView, ThumbnailView, Print,
  TextSelection, TextSearch, Annotation, FormFields, FormDesigner, PageOrganizer
} from "@syncfusion/ej2-vue-pdfviewer";
import { MenuComponent as EjsMenu } from "@syncfusion/ej2-vue-navigations";
import { CheckBoxComponent as EjsCheckbox, SwitchComponent as EjsSwitch } from "@syncfusion/ej2-vue-buttons";
import { provide, ref } from "vue";

const pdfviewer = ref(null);
const documentPath = 'https://cdn.syncfusion.com/content/pdf/pdf-succinctly.pdf';
const resourceUrl = 'https://cdn.syncfusion.com/ej2/31.2.2/dist/ej2-pdfviewer-lib';
const menuItems = [
  {
    text: 'Search In Google',
    id: 'search_in_google',
    iconCss: 'e-icons e-search'
  },
  {
    text: 'Lock Annotation',
    iconCss: 'e-icons e-lock',
    id: 'lock_annotation'
  },
  {
    text: 'Unlock Annotation',
    iconCss: 'e-icons e-unlock',
    id: 'unlock_annotation'
  },
  {
    text: 'Lock Form Field',
    iconCss: 'e-icons e-lock',
    id: 'read_only_true'
  },
  {
    text: 'Unlock Form Field',
    iconCss: 'e-icons e-unlock',
    id: 'read_only_false'
  },
]


provide('PdfViewer', [Toolbar, Magnification, Navigation, LinkAnnotation, BookmarkView, ThumbnailView,
  Print, TextSelection, TextSearch, Annotation, FormFields, FormDesigner, PageOrganizer])

const documentLoad = function (args) {
  const viewer = pdfviewer.value.ej2Instances;
  viewer.addCustomMenu(this.menuItems, true);
}
import { PdfViewerComponent, Toolbar, Magnification, Navigation, LinkAnnotation, BookmarkView, ThumbnailView, Print,
         TextSelection, TextSearch, Annotation, FormFields, FormDesigner,PageOrganizer } from "@syncfusion/ej2-vue-pdfviewer";
import { MenuComponent } from "@syncfusion/ej2-vue-navigations";
import { CheckBoxComponent, SwitchComponent } from "@syncfusion/ej2-vue-buttons";

export default {
    components: {
        'ejs-pdfviewer': PdfViewerComponent,
        'ejs-switch': SwitchComponent,
        'ejs-menu': MenuComponent,
        'ejs-checkbox': CheckBoxComponent
    },
    data: function() {
        return {
			documentPath:'https://cdn.syncfusion.com/content/pdf/pdf-succinctly.pdf',
            resourceUrl: 'https://cdn.syncfusion.com/ej2/31.2.2/dist/ej2-pdfviewer-lib',
            menuItems : [
            {
                text: 'Search In Google',
                id: 'search_in_google',
                iconCss: 'e-icons e-search'
            },
            {
                text: 'Lock Annotation',
                iconCss: 'e-icons e-lock',
                id: 'lock_annotation'
            },
            {
                text: 'Unlock Annotation',
                iconCss: 'e-icons e-unlock',
                id: 'unlock_annotation'
            },
            {
                text: 'Lock Form Field',
                iconCss: 'e-icons e-lock',
                id: 'read_only_true'
            },
            {
                text: 'Unlock Form Field',
                iconCss: 'e-icons e-unlock',
                id: 'read_only_false'
            },
        ]
        }
    },
	provide: {
      PdfViewer: [ Toolbar, Magnification, Navigation, LinkAnnotation, BookmarkView, ThumbnailView,
                   Print, TextSelection, TextSearch, Annotation, FormFields, FormDesigner,PageOrganizer]
    },
    methods: {
    documentLoad: function (args) {
        var viewer = this.$refs.pdfviewer.ej2Instances;
        viewer.addCustomMenu(this.menuItems, true);
    },
  }
}

Show or hide custom items before opening

Use customContextMenuBeforeOpen() to hide or show custom options dynamically.

<template>
  <div id="app">
    <ul>
      <ejs-checkbox label='Hide Default Context Menu' id="enable" @change="contextmenuHelper"
        checked="false"></ejs-checkbox>
      <ejs-checkbox label='Add Custom option at bottom' id="position" @change="contextmenuHelper"
        checked="false"></ejs-checkbox>
    </ul>
    <ejs-pdfviewer id="pdfViewer" ref="pdfviewer" :resourceUrl="resourceUrl" :documentPath="documentPath"
      :documentLoad="documentLoad" :customContextMenuBeforeOpen="customContextMenuBeforeOpen"
      :customContextMenuSelect="customContextMenuSelect">
    </ejs-pdfviewer>
  </div>
</template>

<script>
import { PdfViewerComponent, Toolbar, Magnification, Navigation, LinkAnnotation, BookmarkView, ThumbnailView, Print, TextSelection, TextSearch, Annotation, FormFields, FormDesigner, PageOrganizer } from "@syncfusion/ej2-vue-pdfviewer";
import { MenuComponent } from "@syncfusion/ej2-vue-navigations";
import { CheckBoxComponent } from "@syncfusion/ej2-vue-buttons";

export default {
  name: "App",
  components: {
    'ejs-pdfviewer': PdfViewerComponent,
    'ejs-menu': MenuComponent,
    'ejs-checkbox': CheckBoxComponent
  },
  data: function () {
    return {
      documentPath: 'https://cdn.syncfusion.com/content/pdf/pdf-succinctly.pdf',
      resourceUrl: 'https://cdn.syncfusion.com/ej2/31.2.2/dist/ej2-pdfviewer-lib',
      menuItems: [
        {
          text: 'Search In Google',
          id: 'search_in_google',
          iconCss: 'e-icons e-search'
        },
        {
          text: 'Lock Annotation',
          iconCss: 'e-icons e-lock',
          id: 'lock_annotation'
        },
        {
          text: 'Unlock Annotation',
          iconCss: 'e-icons e-unlock',
          id: 'unlock_annotation'
        },
        {
          text: 'Lock Form Field',
          iconCss: 'e-icons e-lock',
          id: 'read_only_true'
        },
        {
          text: 'Unlock Form Field',
          iconCss: 'e-icons e-unlock',
          id: 'read_only_false'
        },
      ]
    }
  },
  provide: {
    PdfViewer: [Toolbar, Magnification, Navigation, LinkAnnotation, BookmarkView, ThumbnailView, Print,
      TextSelection, TextSearch, Annotation, FormFields, FormDesigner, PageOrganizer]
  },
  methods: {
    documentLoad: function (args) {
      let viewer = this.$refs.pdfviewer.ej2Instances;
      viewer.addCustomMenu(this.menuItems, false, false);
    },
    customContextMenuSelect: function (args) {
      let viewer = this.$refs.pdfviewer.ej2Instances;
      switch (args.id) {
        case 'search_in_google':
          for (let i = 0; i < viewer.textSelectionModule.selectionRangeArray.length; i++) {
            let content = viewer.textSelectionModule.selectionRangeArray[i].textContent;
            if ((viewer.textSelectionModule.isTextSelection) && (/\S/.test(content))) {
              window.open('http://google.com/search?q=' + content);
            }
          }
          break;
        case 'lock_annotation':
          this.lockAnnotations(args);
          break;
        case 'unlock_annotation':
          this.unlockAnnotations(args);
          break;
        case 'read_only_true':
          this.setReadOnlyTrue(args);
          break;
        case 'read_only_false':
          this.setReadOnlyFalse(args);
          break;
        default:
          break;
      }
    },

    customContextMenuBeforeOpen: function (args) {
      let viewer = this.$refs.pdfviewer.ej2Instances;
      for (let i = 0; i < args.ids.length; i++) {
        let search = document.getElementById(args.ids[i]);
        if (search) {
          search.style.display = 'none';
          if (args.ids[i] === 'search_in_google' && (viewer.textSelectionModule) && viewer.textSelectionModule.isTextSelection) {
            search.style.display = 'block';
          } else if (args.ids[i] === "lock_annotation" || args.ids[i] === "unlock_annotation") {
            let isLockOption = args.ids[i] === "lock_annotation";
            for (let j = 0; j < viewer.selectedItems.annotations.length; j++) {
              let selectedAnnotation = viewer.selectedItems.annotations[j];
              if (selectedAnnotation && selectedAnnotation.annotationSettings) {
                let shouldDisplay = (isLockOption && !selectedAnnotation.annotationSettings.isLock) ||
                  (!isLockOption && selectedAnnotation.annotationSettings.isLock);
                search.style.display = shouldDisplay ? 'block' : 'none';
              }
            }
          } else if ((args.ids[i] === "read_only_true" || args.ids[i] === "read_only_false") && viewer.selectedItems.formFields.length !== 0) {
            let isReadOnlyOption = args.ids[i] === "read_only_true";
            for (let j = 0; j < viewer.selectedItems.formFields.length; j++) {
              let selectedFormFields = viewer.selectedItems.formFields[j];
              if (selectedFormFields) {
                let selectedFormField = viewer.selectedItems.formFields[j].isReadonly;
                let displayMenu = (isReadOnlyOption && !selectedFormField) || (!isReadOnlyOption && selectedFormField);
                search.style.display = displayMenu ? 'block' : 'none';
              }
            }
          } else if (args.ids[i] === 'formfield properties' && viewer.selectedItems.formFields.length !== 0) {
            search.style.display = 'block';
          }
        }
      }
    },

    lockAnnotations: function (args) {
      let viewer = this.$refs.pdfviewer.ej2Instances;
      for (let i = 0; i < viewer.annotationCollection.length; i++) {
        if (viewer.annotationCollection[i].uniqueKey === viewer.selectedItems.annotations[0].id) {
          viewer.annotationCollection[i].annotationSettings.isLock = true;
          viewer.annotationCollection[i].isCommentLock = true;
          viewer.annotation.editAnnotation(viewer.annotationCollection[i]);
        }
        args.cancel = false;
      }
    },

    unlockAnnotations: function (args) {
      let viewer = this.$refs.pdfviewer.ej2Instances;
      for (let i = 0; i < viewer.annotationCollection.length; i++) {
        if (viewer.annotationCollection[i].uniqueKey === viewer.selectedItems.annotations[0].id) {
          viewer.annotationCollection[i].annotationSettings.isLock = false;
          viewer.annotationCollection[i].isCommentLock = false;
          viewer.annotation.editAnnotation(viewer.annotationCollection[i]);
        }
        args.cancel = false;
      }
    },

    setReadOnlyTrue: function (args) {
      let viewer = this.$refs.pdfviewer.ej2Instances;
      let selectedFormFields = viewer.selectedItems.formFields;
      for (let i = 0; i < selectedFormFields.length; i++) {
        let selectedFormField = selectedFormFields[i];
        if (selectedFormField) {
          viewer.formDesignerModule.updateFormField(selectedFormField, {
            isReadOnly: true,
          });
        }
        args.cancel = false;
      }
    },

    setReadOnlyFalse: function (args) {
      let viewer = this.$refs.pdfviewer.ej2Instances;
      let selectedFormFields = viewer.selectedItems.formFields;
      for (let i = 0; i < selectedFormFields.length; i++) {
        let selectedFormField = selectedFormFields[i];
        if (selectedFormField) {
          viewer.formDesignerModule.updateFormField(selectedFormField, {
            isReadOnly: false,
          });
        }
        args.cancel = false;
      }
    },

    contextmenuHelper: function (args) {
      let viewer = this.$refs.pdfviewer.ej2Instances;
      viewer.addCustomMenu(this.menuItems, enable.checked, position.checked);
    },
  }
};
</script>
<template>
  <div id="app">
    <ul>
      <ejs-checkbox label='Hide Default Context Menu' id="enable" @change="contextmenuHelper"
        checked="false"></ejs-checkbox>
      <ejs-checkbox label='Add Custom option at bottom' id="position" @change="contextmenuHelper"
        checked="false"></ejs-checkbox>
    </ul>
    <ejs-pdfviewer id="pdfViewer" ref="pdfviewer" :serviceUrl="serviceUrl" :documentPath="documentPath"
      :documentLoad="documentLoad" :customContextMenuBeforeOpen="customContextMenuBeforeOpen"
      :customContextMenuSelect="customContextMenuSelect">
    </ejs-pdfviewer>
  </div>
</template>
<script setup>
import {
  PdfViewerComponent as EjsPdfviewer, Toolbar, Magnification, Navigation, LinkAnnotation, BookmarkView, ThumbnailView, Print,
  TextSelection, TextSearch, Annotation, FormFields, FormDesigner, PageOrganizer
} from "@syncfusion/ej2-vue-pdfviewer";
import { MenuComponent as EjsMenu } from "@syncfusion/ej2-vue-navigations";
import { CheckBoxComponent as EjsCheckbox } from "@syncfusion/ej2-vue-buttons";
import { provide, ref } from "vue";

const pdfviewer = ref(null);
const documentPath = 'https://cdn.syncfusion.com/content/pdf/pdf-succinctly.pdf';
const serviceUrl = "https://document.syncfusion.com/web-services/pdf-viewer/api/pdfviewer";
const menuItems = [
  {
    text: 'Search In Google',
    id: 'search_in_google',
    iconCss: 'e-icons e-search'
  },
  {
    text: 'Lock Annotation',
    iconCss: 'e-icons e-lock',
    id: 'lock_annotation'
  },
  {
    text: 'Unlock Annotation',
    iconCss: 'e-icons e-unlock',
    id: 'unlock_annotation'
  },
  {
    text: 'Lock Form Field',
    iconCss: 'e-icons e-lock',
    id: 'read_only_true'
  },
  {
    text: 'Unlock Form Field',
    iconCss: 'e-icons e-unlock',
    id: 'read_only_false'
  },
]

provide('PdfViewer', [Toolbar, Magnification, Navigation, LinkAnnotation, BookmarkView, ThumbnailView, Print,
  TextSelection, TextSearch, Annotation, FormFields, FormDesigner, PageOrganizer])

const documentLoad = function (args) {
  let viewer = pdfviewer.value.ej2Instances;
  viewer.addCustomMenu(menuItems, false, false);
}
const customContextMenuSelect = function (args) {
  let viewer = pdfviewer.value.ej2Instances;
  switch (args.id) {
    case 'search_in_google':
      for (let i = 0; i < viewer.textSelectionModule.selectionRangeArray.length; i++) {
        let content = viewer.textSelectionModule.selectionRangeArray[i].textContent;
        if ((viewer.textSelectionModule.isTextSelection) && (/\S/.test(content))) {
          window.open('http://google.com/search?q=' + content);
        }
      }
      break;
    case 'lock_annotation':
      lockAnnotations(args);
      break;
    case 'unlock_annotation':
      unlockAnnotations(args);
      break;
    case 'read_only_true':
      setReadOnlyTrue(args);
      break;
    case 'read_only_false':
      setReadOnlyFalse(args);
      break;
    default:
      break;
  }
}

const customContextMenuBeforeOpen = function (args) {
  let viewer = pdfviewer.value.ej2Instances;
  for (let i = 0; i < args.ids.length; i++) {
    let search = document.getElementById(args.ids[i]);
    if (search) {
      search.style.display = 'none';
      if (args.ids[i] === 'search_in_google' && (viewer.textSelectionModule) && viewer.textSelectionModule.isTextSelection) {
        search.style.display = 'block';
      } else if (args.ids[i] === "lock_annotation" || args.ids[i] === "unlock_annotation") {
        let isLockOption = args.ids[i] === "lock_annotation";
        for (let j = 0; j < viewer.selectedItems.annotations.length; j++) {
          let selectedAnnotation = viewer.selectedItems.annotations[j];
          if (selectedAnnotation && selectedAnnotation.annotationSettings) {
            let shouldDisplay = (isLockOption && !selectedAnnotation.annotationSettings.isLock) ||
              (!isLockOption && selectedAnnotation.annotationSettings.isLock);
            search.style.display = shouldDisplay ? 'block' : 'none';
          }
        }
      } else if ((args.ids[i] === "read_only_true" || args.ids[i] === "read_only_false") && viewer.selectedItems.formFields.length !== 0) {
        let isReadOnlyOption = args.ids[i] === "read_only_true";
        for (let j = 0; j < viewer.selectedItems.formFields.length; j++) {
          let selectedFormFields = viewer.selectedItems.formFields[j];
          if (selectedFormFields) {
            let selectedFormField = viewer.selectedItems.formFields[j].isReadonly;
            let displayMenu = (isReadOnlyOption && !selectedFormField) || (!isReadOnlyOption && selectedFormField);
            search.style.display = displayMenu ? 'block' : 'none';
          }
        }
      } else if (args.ids[i] === 'formfield properties' && viewer.selectedItems.formFields.length !== 0) {
        search.style.display = 'block';
      }
    }
  }
}

const lockAnnotations = function (args) {
  let viewer = pdfviewer.value.ej2Instances;
  for (let i = 0; i < viewer.annotationCollection.length; i++) {
    if (viewer.annotationCollection[i].uniqueKey === viewer.selectedItems.annotations[0].id) {
      viewer.annotationCollection[i].annotationSettings.isLock = true;
      viewer.annotationCollection[i].isCommentLock = true;
      viewer.annotation.editAnnotation(viewer.annotationCollection[i]);
    }
    args.cancel = false;
  }
}

const unlockAnnotations = function (args) {
  let viewer = pdfviewer.value.ej2Instances;
  for (let i = 0; i < viewer.annotationCollection.length; i++) {
    if (viewer.annotationCollection[i].uniqueKey === viewer.selectedItems.annotations[0].id) {
      viewer.annotationCollection[i].annotationSettings.isLock = false;
      viewer.annotationCollection[i].isCommentLock = false;
      viewer.annotation.editAnnotation(viewer.annotationCollection[i]);
    }
    args.cancel = false;
  }
}

const setReadOnlyTrue = function (args) {
  let viewer = pdfviewer.value.ej2Instances;
  let selectedFormFields = viewer.selectedItems.formFields;
  for (let i = 0; i < selectedFormFields.length; i++) {
    let selectedFormField = selectedFormFields[i];
    if (selectedFormField) {
      viewer.formDesignerModule.updateFormField(selectedFormField, {
        isReadOnly: true,
      });
    }
    args.cancel = false;
  }
}

const setReadOnlyFalse = function (args) {
  let viewer = pdfviewer.value.ej2Instances;
  let selectedFormFields = viewer.selectedItems.formFields;
  for (let i = 0; i < selectedFormFields.length; i++) {
    let selectedFormField = selectedFormFields[i];
    if (selectedFormField) {
      viewer.formDesignerModule.updateFormField(selectedFormField, {
        isReadOnly: false,
      });
    }
    args.cancel = false;
  }
}

const contextmenuHelper = function (args) {
  let viewer = pdfviewer.value.ej2Instances;
  viewer.addCustomMenu(menuItems, enable.checked, position.checked);
}

</script>
<template>
  <div id="app">
    <ul>
      <ejs-checkbox label='Hide Default Context Menu' id="enable" @change="contextmenuHelper"
        checked="false"></ejs-checkbox>
      <ejs-checkbox label='Add Custom option at bottom' id="position" @change="contextmenuHelper"
        checked="false"></ejs-checkbox>
    </ul>
    <ejs-pdfviewer id="pdfViewer" ref="pdfviewer" :serviceUrl="serviceUrl" :documentPath="documentPath"
      :documentLoad="documentLoad" :customContextMenuBeforeOpen="customContextMenuBeforeOpen"
      :customContextMenuSelect="customContextMenuSelect">
    </ejs-pdfviewer>
  </div>
</template>
<script>
import {
  PdfViewerComponent, Toolbar, Magnification, Navigation, LinkAnnotation, BookmarkView, ThumbnailView, Print,
  TextSelection, TextSearch, Annotation, FormFields, FormDesigner, PageOrganizer
} from "@syncfusion/ej2-vue-pdfviewer";
import { MenuComponent } from "@syncfusion/ej2-vue-navigations";
import { CheckBoxComponent } from "@syncfusion/ej2-vue-buttons";

export default {
  name: "App",
  components: {
    'ejs-pdfviewer': PdfViewerComponent,
    'ejs-menu': MenuComponent,
    'ejs-checkbox': CheckBoxComponent
  },
  data: function () {
    return {
      documentPath: 'https://cdn.syncfusion.com/content/pdf/pdf-succinctly.pdf',
      serviceUrl: "https://document.syncfusion.com/web-services/pdf-viewer/api/pdfviewer",
      menuItems: [
        {
          text: 'Search In Google',
          id: 'search_in_google',
          iconCss: 'e-icons e-search'
        },
        {
          text: 'Lock Annotation',
          iconCss: 'e-icons e-lock',
          id: 'lock_annotation'
        },
        {
          text: 'Unlock Annotation',
          iconCss: 'e-icons e-unlock',
          id: 'unlock_annotation'
        },
        {
          text: 'Lock Form Field',
          iconCss: 'e-icons e-lock',
          id: 'read_only_true'
        },
        {
          text: 'Unlock Form Field',
          iconCss: 'e-icons e-unlock',
          id: 'read_only_false'
        },
      ]
    }
  },
  provide: {
    PdfViewer: [Toolbar, Magnification, Navigation, LinkAnnotation, BookmarkView, ThumbnailView, Print,
      TextSelection, TextSearch, Annotation, FormFields, FormDesigner, PageOrganizer]
  },
  methods: {
    documentLoad: function (args) {
      let viewer = this.$refs.pdfviewer.ej2Instances;
      viewer.addCustomMenu(this.menuItems, false, false);
    },
    customContextMenuSelect: function (args) {
      let viewer = this.$refs.pdfviewer.ej2Instances;
      switch (args.id) {
        case 'search_in_google':
          for (let i = 0; i < viewer.textSelectionModule.selectionRangeArray.length; i++) {
            let content = viewer.textSelectionModule.selectionRangeArray[i].textContent;
            if ((viewer.textSelectionModule.isTextSelection) && (/\S/.test(content))) {
              window.open('http://google.com/search?q=' + content);
            }
          }
          break;
        case 'lock_annotation':
          this.lockAnnotations(args);
          break;
        case 'unlock_annotation':
          this.unlockAnnotations(args);
          break;
        case 'read_only_true':
          this.setReadOnlyTrue(args);
          break;
        case 'read_only_false':
          this.setReadOnlyFalse(args);
          break;
        default:
          break;
      }
    },

    customContextMenuBeforeOpen: function (args) {
      let viewer = this.$refs.pdfviewer.ej2Instances;
      for (let i = 0; i < args.ids.length; i++) {
        let search = document.getElementById(args.ids[i]);
        if (search) {
          search.style.display = 'none';
          if (args.ids[i] === 'search_in_google' && (viewer.textSelectionModule) && viewer.textSelectionModule.isTextSelection) {
            search.style.display = 'block';
          } else if (args.ids[i] === "lock_annotation" || args.ids[i] === "unlock_annotation") {
            let isLockOption = args.ids[i] === "lock_annotation";
            for (let j = 0; j < viewer.selectedItems.annotations.length; j++) {
              let selectedAnnotation = viewer.selectedItems.annotations[j];
              if (selectedAnnotation && selectedAnnotation.annotationSettings) {
                let shouldDisplay = (isLockOption && !selectedAnnotation.annotationSettings.isLock) ||
                  (!isLockOption && selectedAnnotation.annotationSettings.isLock);
                search.style.display = shouldDisplay ? 'block' : 'none';
              }
            }
          } else if ((args.ids[i] === "read_only_true" || args.ids[i] === "read_only_false") && viewer.selectedItems.formFields.length !== 0) {
            let isReadOnlyOption = args.ids[i] === "read_only_true";
            for (let j = 0; j < viewer.selectedItems.formFields.length; j++) {
              let selectedFormFields = viewer.selectedItems.formFields[j];
              if (selectedFormFields) {
                let selectedFormField = viewer.selectedItems.formFields[j].isReadonly;
                let displayMenu = (isReadOnlyOption && !selectedFormField) || (!isReadOnlyOption && selectedFormField);
                search.style.display = displayMenu ? 'block' : 'none';
              }
            }
          } else if (args.ids[i] === 'formfield properties' && viewer.selectedItems.formFields.length !== 0) {
            search.style.display = 'block';
          }
        }
      }
    },

    lockAnnotations: function (args) {
      let viewer = this.$refs.pdfviewer.ej2Instances;
      for (let i = 0; i < viewer.annotationCollection.length; i++) {
        if (viewer.annotationCollection[i].uniqueKey === viewer.selectedItems.annotations[0].id) {
          viewer.annotationCollection[i].annotationSettings.isLock = true;
          viewer.annotationCollection[i].isCommentLock = true;
          viewer.annotation.editAnnotation(viewer.annotationCollection[i]);
        }
        args.cancel = false;
      }
    },

    unlockAnnotations: function (args) {
      let viewer = this.$refs.pdfviewer.ej2Instances;
      for (let i = 0; i < viewer.annotationCollection.length; i++) {
        if (viewer.annotationCollection[i].uniqueKey === viewer.selectedItems.annotations[0].id) {
          viewer.annotationCollection[i].annotationSettings.isLock = false;
          viewer.annotationCollection[i].isCommentLock = false;
          viewer.annotation.editAnnotation(viewer.annotationCollection[i]);
        }
        args.cancel = false;
      }
    },

    setReadOnlyTrue: function (args) {
      let viewer = this.$refs.pdfviewer.ej2Instances;
      let selectedFormFields = viewer.selectedItems.formFields;
      for (let i = 0; i < selectedFormFields.length; i++) {
        let selectedFormField = selectedFormFields[i];
        if (selectedFormField) {
          viewer.formDesignerModule.updateFormField(selectedFormField, {
            isReadOnly: true,
          });
        }
        args.cancel = false;
      }
    },

    setReadOnlyFalse: function (args) {
      let viewer = this.$refs.pdfviewer.ej2Instances;
      let selectedFormFields = viewer.selectedItems.formFields;
      for (let i = 0; i < selectedFormFields.length; i++) {
        let selectedFormField = selectedFormFields[i];
        if (selectedFormField) {
          viewer.formDesignerModule.updateFormField(selectedFormField, {
            isReadOnly: false,
          });
        }
        args.cancel = false;
      }
    },

    contextmenuHelper: function (args) {
      let viewer = this.$refs.pdfviewer.ej2Instances;
      viewer.addCustomMenu(this.menuItems, enable.checked, position.checked);
    },
  }
};
</script>

The following is the output of custom context menu with customization.

<template>
  <div id="app">
    <ul>
      <ejs-checkbox label='Hide Default Context Menu' id="enable" @change="contextmenuHelper"
        checked="false"></ejs-checkbox>
      <ejs-checkbox label='Add Custom option at bottom' id="position" @change="contextmenuHelper"
        checked="false"></ejs-checkbox>
    </ul>
    <ejs-pdfviewer id="pdfViewer" ref="pdfViewer" :resourceUrl="resourceUrl" :documentPath="documentPath"
      :documentLoad="documentLoad" :customContextMenuBeforeOpen="customContextMenuBeforeOpen"
      :customContextMenuSelect="customContextMenuSelect">
    </ejs-pdfviewer>
  </div>
</template>

<script setup>
import { provide, ref } from "vue";
import {
  PdfViewerComponent as EjsPdfviewer, Toolbar, Magnification, Navigation, LinkAnnotation,
  BookmarkView, ThumbnailView, Print, TextSelection, TextSearch,
  Annotation, FormDesigner, FormFields, PageOrganizer
} from '@syncfusion/ej2-vue-pdfviewer';
import { CheckBoxComponent as EjsCheckbox } from "@syncfusion/ej2-vue-buttons";

const pdfViewer = ref(null);
const documentPath = "https://cdn.syncfusion.com/content/pdf/pdf-succinctly.pdf";
const resourceUrl = "https://cdn.syncfusion.com/ej2/25.1.35/dist/ej2-pdfviewer-lib";
const menuItems = ref([{
  text: 'Search In Google',
  id: 'search_in_google',
  iconCss: 'e-icons e-search'
},
{
  text: 'Lock Annotation',
  iconCss: 'e-icons e-lock',
  id: 'lock_annotation'
},
{
  text: 'Unlock Annotation',
  iconCss: 'e-icons e-unlock',
  id: 'unlock_annotation'
},
{
  text: 'Lock Form Fields',
  iconCss: 'e-icons e-lock',
  id: 'read_only_true'
},
{
  text: 'Unlock Form Fields',
  iconCss: 'e-icons e-unlock',
  id: 'read_only_false'
},
]);

provide('PdfViewer', [Toolbar, Magnification, Navigation, LinkAnnotation, BookmarkView, ThumbnailView,
  Print, TextSelection, TextSearch, Annotation, FormFields, FormDesigner, PageOrganizer]);

const documentLoad = function () {
  const viewer = pdfViewer.value.ej2Instances;
  viewer.addCustomMenu(menuItems.value, false, false);
}
const customContextMenuSelect = function (args) {
  const viewer = pdfViewer.value.ej2Instances;
  switch (args.id) {
    case 'search_in_google':
      for (var i = 0; i < viewer.textSelectionModule.selectionRangeArray.length; i++) {
        var content = viewer.textSelectionModule.selectionRangeArray[i].textContent;
        if ((viewer.textSelectionModule.isTextSelection) && (/\S/.test(content))) {
          window.open('http://google.com/search?q=' + content);
        }
      }
      break;
    case 'lock_annotation':
      lockAnnotations(args);
      break;
    case 'unlock_annotation':
      unlockAnnotations(args);
      break;
    case 'read_only_true':
      setReadOnlyTrue(args);
      break;
    case 'read_only_false':
      setReadOnlyFalse(args);
      break;
    default:
      break;
  }
}

const customContextMenuBeforeOpen = function (args) {
  const viewer = pdfViewer.value.ej2Instances;
  for (var i = 0; i < args.ids.length; i++) {
    var search = document.getElementById(args.ids[i]);
    if (search) {
      search.style.display = 'none';
      if (args.ids[i] === 'search_in_google' && (viewer.textSelectionModule) && viewer.textSelectionModule.isTextSelection) {
        search.style.display = 'block';
      } else if (args.ids[i] === "lock_annotation" || args.ids[i] === "unlock_annotation") {
        var isLockOption = args.ids[i] === "lock_annotation";
        for (var j = 0; j < viewer.selectedItems.annotations.length; j++) {
          var selectedAnnotation = viewer.selectedItems.annotations[j];
          if (selectedAnnotation && selectedAnnotation.annotationSettings) {
            var shouldDisplay = (isLockOption && !selectedAnnotation.annotationSettings.isLock) ||
              (!isLockOption && selectedAnnotation.annotationSettings.isLock);
            search.style.display = shouldDisplay ? 'block' : 'none';
          }
        }
      } else if ((args.ids[i] === "read_only_true" || args.ids[i] === "read_only_false") && viewer.selectedItems.formFields.length !== 0) {
        var isReadOnlyOption = args.ids[i] === "read_only_true";
        for (var j = 0; j < viewer.selectedItems.formFields.length; j++) {
          var selectedFormFields = viewer.selectedItems.formFields[j];
          if (selectedFormFields) {
            var selectedFormField = viewer.selectedItems.formFields[j].isReadonly;
            var displayMenu = (isReadOnlyOption && !selectedFormField) || (!isReadOnlyOption && selectedFormField);
            search.style.display = displayMenu ? 'block' : 'none';
          }
        }
      } else if (args.ids[i] === 'formfield properties' && viewer.selectedItems.formFields.length !== 0) {
        search.style.display = 'block';
      }
    }
  }
}

const lockAnnotations = function (args) {
  const viewer = pdfViewer.value.ej2Instances;
  for (var i = 0; i < viewer.annotationCollection.length; i++) {
    if (viewer.annotationCollection[i].uniqueKey === viewer.selectedItems.annotations[0].id) {
      viewer.annotationCollection[i].annotationSettings.isLock = true;
      viewer.annotationCollection[i].isCommentLock = true;
      viewer.annotation.editAnnotation(viewer.annotationCollection[i]);
    }
    args.cancel = false;
  }
}

const unlockAnnotations = function (args) {
  const viewer = pdfViewer.value.ej2Instances;
  for (var i = 0; i < viewer.annotationCollection.length; i++) {
    if (viewer.annotationCollection[i].uniqueKey === viewer.selectedItems.annotations[0].id) {
      viewer.annotationCollection[i].annotationSettings.isLock = false;
      viewer.annotationCollection[i].isCommentLock = false;
      viewer.annotation.editAnnotation(viewer.annotationCollection[i]);
    }
    args.cancel = false;
  }
}

const setReadOnlyTrue = function (args) {
  const viewer = pdfViewer.value.ej2Instances;
  var selectedFormFields = viewer.selectedItems.formFields;
  for (var i = 0; i < selectedFormFields.length; i++) {
    var selectedFormField = selectedFormFields[i];
    if (selectedFormField) {
      viewer.formDesignerModule.updateFormField(selectedFormField, {
        isReadOnly: true,
      });
    }
    args.cancel = false;
  }
}

const setReadOnlyFalse = function (args) {
  const viewer = pdfViewer.value.ej2Instances;
  var selectedFormFields = viewer.selectedItems.formFields;
  for (var i = 0; i < selectedFormFields.length; i++) {
    var selectedFormField = selectedFormFields[i];
    if (selectedFormField) {
      viewer.formDesignerModule.updateFormField(selectedFormField, {
        isReadOnly: false,
      });
    }
    args.cancel = false;
  }
}

const contextmenuHelper = function () {
  const viewer = pdfViewer.value.ej2Instances;
  viewer.addCustomMenu(menuItems.value, enable.checked, position.checked);
}
</script>
<style>
@import '../node_modules/@syncfusion/ej2-base/styles/material.css';
@import '../node_modules/@syncfusion/ej2-buttons/styles/material.css';
@import '../node_modules/@syncfusion/ej2-dropdowns/styles/material.css';
@import '../node_modules/@syncfusion/ej2-inputs/styles/material.css';
@import '../node_modules/@syncfusion/ej2-navigations/styles/material.css';
@import '../node_modules/@syncfusion/ej2-popups/styles/material.css';
@import '../node_modules/@syncfusion/ej2-splitbuttons/styles/material.css';
@import '../node_modules/@syncfusion/ej2-lists/styles/material.css';
@import '../node_modules/@syncfusion/ej2-vue-pdfviewer/styles/material.css';

#pdfViewer {
  height: 640px;
}
</style>
<template>
  <div id="app">
    <ul>
      <ejs-checkbox label='Hide Default Context Menu' id="enable" @change="contextmenuHelper"
        checked="false"></ejs-checkbox>
      <ejs-checkbox label='Add Custom option at bottom' id="position" @change="contextmenuHelper"
        checked="false"></ejs-checkbox>
    </ul>
    <ejs-pdfviewer id="pdfViewer" ref="pdfviewer":resourceUrl="resourceUrl" :documentPath="documentPath" :documentLoad="documentLoad"
      :customContextMenuBeforeOpen="customContextMenuBeforeOpen" :customContextMenuSelect="customContextMenuSelect">
    </ejs-pdfviewer>
  </div>
</template>

<script>
import {
  PdfViewerComponent, Toolbar, Magnification, Navigation, LinkAnnotation,
  BookmarkView, ThumbnailView, Print, TextSelection, TextSearch,
  Annotation, FormDesigner, FormFields, PageOrganizer
} from '@syncfusion/ej2-vue-pdfviewer';
import { CheckBoxComponent } from "@syncfusion/ej2-vue-buttons";

export default {
  name: "App",
  components: {
    'ejs-pdfviewer': PdfViewerComponent,
    'ejs-checkbox': CheckBoxComponent
  },
  name: 'app',
  data() {
    return {
      documentPath: "https://cdn.syncfusion.com/content/pdf/pdf-succinctly.pdf",
      resourceUrl: "https://cdn.syncfusion.com/ej2/25.1.35/dist/ej2-pdfviewer-lib",
      menuItems: [{
        text: 'Search In Google',
        id: 'search_in_google',
        iconCss: 'e-icons e-search'
      },
      {
        text: 'Lock Annotation',
        iconCss: 'e-icons e-lock',
        id: 'lock_annotation'
      },
      {
        text: 'Unlock Annotation',
        iconCss: 'e-icons e-unlock',
        id: 'unlock_annotation'
      },
      {
        text: 'Lock Form Fields',
        iconCss: 'e-icons e-lock',
        id: 'read_only_true'
      },
      {
        text: 'Unlock Form Fields',
        iconCss: 'e-icons e-unlock',
        id: 'read_only_false'
      },
      ]
    };
  },

  provide: {
    PdfViewer: [Toolbar, Magnification, Navigation, LinkAnnotation, BookmarkView, ThumbnailView,
      Print, TextSelection, TextSearch, Annotation, FormFields, FormDesigner, PageOrganizer]
  },
  methods: {
    documentLoad: function () {
      let viewer = this.$refs.pdfviewer.ej2Instances;
      viewer.addCustomMenu(this.menuItems, false, false);
    },
    customContextMenuSelect: function (args) {
      let viewer = this.$refs.pdfviewer.ej2Instances;
      switch (args.id) {
        case 'search_in_google':
          for (let i = 0; i < viewer.textSelectionModule.selectionRangeArray.length; i++) {
            let content = viewer.textSelectionModule.selectionRangeArray[i].textContent;
            if ((viewer.textSelectionModule.isTextSelection) && (/\S/.test(content))) {
              window.open('http://google.com/search?q=' + content);
            }
          }
          break;
        case 'lock_annotation':
          this.lockAnnotations(args);
          break;
        case 'unlock_annotation':
          this.unlockAnnotations(args);
          break;
        case 'read_only_true':
          this.setReadOnlyTrue(args);
          break;
        case 'read_only_false':
          this.setReadOnlyFalse(args);
          break;
        default:
          break;
      }
    },

    customContextMenuBeforeOpen: function (args) {
      let viewer = this.$refs.pdfviewer.ej2Instances;
      for (let i = 0; i < args.ids.length; i++) {
        let search = document.getElementById(args.ids[i]);
        if (search) {
          search.style.display = 'none';
          if (args.ids[i] === 'search_in_google' && (viewer.textSelectionModule) && viewer.textSelectionModule.isTextSelection) {
            search.style.display = 'block';
          } else if (args.ids[i] === "lock_annotation" || args.ids[i] === "unlock_annotation") {
            let isLockOption = args.ids[i] === "lock_annotation";
            for (let j = 0; j < viewer.selectedItems.annotations.length; j++) {
              let selectedAnnotation = viewer.selectedItems.annotations[j];
              if (selectedAnnotation && selectedAnnotation.annotationSettings) {
                let shouldDisplay = (isLockOption && !selectedAnnotation.annotationSettings.isLock) ||
                  (!isLockOption && selectedAnnotation.annotationSettings.isLock);
                search.style.display = shouldDisplay ? 'block' : 'none';
              }
            }
          } else if ((args.ids[i] === "read_only_true" || args.ids[i] === "read_only_false") && viewer.selectedItems.formFields.length !== 0) {
            let isReadOnlyOption = args.ids[i] === "read_only_true";
            for (let j = 0; j < viewer.selectedItems.formFields.length; j++) {
              let selectedFormFields = viewer.selectedItems.formFields[j];
              if (selectedFormFields) {
                let selectedFormField = viewer.selectedItems.formFields[j].isReadonly;
                let displayMenu = (isReadOnlyOption && !selectedFormField) || (!isReadOnlyOption && selectedFormField);
                search.style.display = displayMenu ? 'block' : 'none';
              }
            }
          } else if (args.ids[i] === 'formfield properties' && viewer.selectedItems.formFields.length !== 0) {
            search.style.display = 'block';
          }
        }
      }
    },

    lockAnnotations: function (args) {
      let viewer = this.$refs.pdfviewer.ej2Instances;
      for (let i = 0; i < viewer.annotationCollection.length; i++) {
        if (viewer.annotationCollection[i].uniqueKey === viewer.selectedItems.annotations[0].id) {
          viewer.annotationCollection[i].annotationSettings.isLock = true;
          viewer.annotationCollection[i].isCommentLock = true;
          viewer.annotation.editAnnotation(viewer.annotationCollection[i]);
        }
        args.cancel = false;
      }
    },

    unlockAnnotations: function (args) {
      let viewer = this.$refs.pdfviewer.ej2Instances;
      for (let i = 0; i < viewer.annotationCollection.length; i++) {
        if (viewer.annotationCollection[i].uniqueKey === viewer.selectedItems.annotations[0].id) {
          viewer.annotationCollection[i].annotationSettings.isLock = false;
          viewer.annotationCollection[i].isCommentLock = false;
          viewer.annotation.editAnnotation(viewer.annotationCollection[i]);
        }
        args.cancel = false;
      }
    },

    setReadOnlyTrue: function (args) {
      let viewer = this.$refs.pdfviewer.ej2Instances;
      let selectedFormFields = viewer.selectedItems.formFields;
      for (let i = 0; i < selectedFormFields.length; i++) {
        let selectedFormField = selectedFormFields[i];
        if (selectedFormField) {
          viewer.formDesignerModule.updateFormField(selectedFormField, {
            isReadOnly: true,
          });
        }
        args.cancel = false;
      }
    },

    setReadOnlyFalse: function (args) {
      let viewer = this.$refs.pdfviewer.ej2Instances;
      let selectedFormFields = viewer.selectedItems.formFields;
      for (let i = 0; i < selectedFormFields.length; i++) {
        let selectedFormField = selectedFormFields[i];
        if (selectedFormField) {
          viewer.formDesignerModule.updateFormField(selectedFormField, {
            isReadOnly: false,
          });
        }
        args.cancel = false;
      }
    },

    contextmenuHelper: function () {
      let viewer = this.$refs.pdfviewer.ej2Instances;
      viewer.addCustomMenu(this.menuItems, enable.checked, position.checked);
    },
  }
}
</script>
<style>
@import '../node_modules/@syncfusion/ej2-base/styles/material.css';
@import '../node_modules/@syncfusion/ej2-buttons/styles/material.css';
@import '../node_modules/@syncfusion/ej2-dropdowns/styles/material.css';
@import '../node_modules/@syncfusion/ej2-inputs/styles/material.css';
@import '../node_modules/@syncfusion/ej2-navigations/styles/material.css';
@import '../node_modules/@syncfusion/ej2-popups/styles/material.css';
@import '../node_modules/@syncfusion/ej2-splitbuttons/styles/material.css';
@import '../node_modules/@syncfusion/ej2-lists/styles/material.css';
@import '../node_modules/@syncfusion/ej2-vue-pdfviewer/styles/material.css';

#pdfViewer {
  height: 640px;
}
</style>

NOTE

To set up the server-backed PDF Viewer, add the following service URL in App.vue:
serviceUrl: 'https://document.syncfusion.com/web-services/pdf-viewer/api/pdfviewer'
Then configure the PDF Viewer template with the :serviceUrl='service' attribute on the viewer container.

View sample in GitHub