How to Drop Image Stamp Exactly at Mouse Cursor Location

WebViewer Version: 11.4.0

Do you have an issue with a specific file(s)? No
Can you reproduce using one of our samples or online demos?No
Are you using the WebViewer server? Yes
Does the issue only happen on certain browsers? No
Is your issue related to a front-end framework? No
Is your issue related to annotations? No

Please give a brief summary of your issue:
I’m currently implementing a custom image stamp panel using the WebViewer API and handling the image drop via the drop event. However, I’m facing an issue where the image stamp does not get placed exactly where the mouse pointer is located during the drop. It often appears slightly offset or in a different area of the page.

Here’s a snippet of the relevant code I’m using:

viewerElement.addEventListener('drop', (e) => {
  e.preventDefault();

  const imageData = e.dataTransfer.getData('imageData');
  if (!imageData) return;

  const viewerRect = viewerElement.getBoundingClientRect();
  const x = e.clientX - viewerRect.left;
  const y = e.clientY - viewerRect.top;

  const displayMode = viewer.getDisplayModeManager().getDisplayMode();
  const selectedPages = displayMode.getSelectedPages({ x, y });
  const pageNumber = selectedPages?.last;
  const pagePoint = displayMode.windowToPage({ x, y }, pageNumber);

  const stampAnnot = new Annotations.StampAnnotation();
  stampAnnot.PageNumber = pageNumber;
  stampAnnot.X = pagePoint.x;
  stampAnnot.Y = pagePoint.y;
  stampAnnot.Width = 200;
  stampAnnot.Height = 150;
  stampAnnot.Author = annotManager.getCurrentUser();
  stampAnnot.Rotation = documentViewer.getCompleteRotation(pageNumber) * 90;
  stampAnnot.setImageData(imageData);

  annotManager.addAnnotation(stampAnnot);
  annotManager.redrawAnnotation(stampAnnot);
});

Could you please guide me on how to ensure the stamp gets added precisely at the mouse cursor position during the drop event? If there’s a better way to calculate the page point or handle zoom/scroll offsets, your input would be greatly appreciated.

1 Like

Hello, I’m AiRon, an automated tech support bot :robot:
While you wait for one of our customer support representatives, I can help you with your question.


It seems you’re dealing with positioning issues when placing an image stamp at the mouse cursor location within WebViewer. This usually involves accounting for the viewport’s zoom and scroll positions to ensure the image is placed exactly where desired.

Some things you can try:

  • Ensure that your mouse position is correctly translated into page coordinates considering the current zoom and scroll offsets.

  • Use the documentViewer.getScrollViewElement() to get the scroll positions and add these to your mouse coordinates to adjust for the current scroll position.

  • Check the transformation applied by the display mode using displayMode.windowToPage() to ensure it accounts for the current zoom level and rotation.

  • Make sure to use the correct property for the page number from getSelectedPages as it should return an array of selected pages.




Here are some resources that might help you:



Hopefully, this helps you solve your problem while you wait for a human to get back to you.

1 Like

Hello @rishumishra2001,

Thank you for posting on our forum.

If you have not already, see our helpful guide on coordinates, particularly in regards to converting between mouse locations and window coordinates: https://docs.apryse.com/web/guides/coordinates#converting-between-mouse-locations-and-window-coordinates.

After some investigation, I was able to reproduce the issue on my end with the code provided. Rather than using the coordinates provided from the drop event, we recommend using the suggestion in the guide for getting the mouse location. When doing so, this greatly improved the accuracy of where the annotation was being placed.

Here’s my full code snippet I used for testing (slightly modified to use double click as I did not have the custom image stamp panel:

// EDIT: New helper function getMouseLocation, see guide for more details
  const getMouseLocation = e => {
    const scrollElement = documentViewer.getScrollViewElement();
    const scrollLeft = scrollElement.scrollLeft || 0;
    const scrollTop = scrollElement.scrollTop || 0;
    return {
      x: e.pageX + scrollLeft,
      y: e.pageY + scrollTop
    };
  };

  const res = await fetch('../../envs/testing/png-transparent-circle-spiral-point-pattern-spiral.png'); // replace with your image path
  const imageBlob = await res.blob();
  const reader = new FileReader();
  reader.onloadend = async () => {
    documentViewer.addEventListener('dblClick', async e => {
      // refer to getMouseLocation implementation above

      // use the getMouseLocation() coordinates
      const windowCoordinates = getMouseLocation(e);
      const base64data = reader.result;

      const viewerElement = documentViewer.getViewerElement();

      const viewerRect = viewerElement.getBoundingClientRect();
      const x = e.clientX - viewerRect.left;
      const y = e.clientY - viewerRect.top;

      const displayMode = documentViewer.getDisplayModeManager().getDisplayMode();
      const selectedPages = displayMode.getSelectedPages(windowCoordinates, windowCoordinates);
      const pageNumber = selectedPages?.last;
      const pagePoint = displayMode.windowToPage(windowCoordinates, pageNumber);

      const stampAnnot = new Annotations.StampAnnotation();
      stampAnnot.PageNumber = pageNumber;
      stampAnnot.X = pagePoint.x;
      stampAnnot.Y = pagePoint.y;
      stampAnnot.Width = 200;
      stampAnnot.Height = 150;
      stampAnnot.Author = annotationManager.getCurrentUser();
      stampAnnot.Rotation = documentViewer.getCompleteRotation(pageNumber) * 90;
      await stampAnnot.setImageData(base64data);

      annotationManager.addAnnotation(stampAnnot);
      annotationManager.redrawAnnotation(stampAnnot);

    });
  }
  reader.readAsDataURL(imageBlob);

Let us know if this code snippet works for you!

Best Regards,
Jacob Romano Carlsen
Web Development Support Engineer
Apryse Software Inc.

1 Like

I tried your code sample, but now there’s an issue — when the user drags and drops the stamp image, it gets placed slightly to the right of where the user clicked.

Here is the code I have try:

 bindDropHandler: function (viewer, annotManager, Annotations, documentViewer) {
     if (isDropListenerBound) return;

     const viewerElement = viewer.getViewerElement();

     const getMouseLocation = (e) => {
         const scrollElement = viewer.getScrollViewElement();
         const scrollLeft = scrollElement?.scrollLeft || 0;
         const scrollTop = scrollElement?.scrollTop || 0;
         return {
             x: e.pageX + scrollLeft,
             y: e.pageY + scrollTop
         };
     };

     viewerElement.addEventListener('dragover', (e) => e.preventDefault());

     viewerElement.addEventListener('drop', async (e) => {
         if (dropInProgress) return;
         dropInProgress = true;
         setTimeout(() => dropInProgress = false, 300);

         e.preventDefault();
         e.stopPropagation();

         const imageData = e.dataTransfer.getData('imageData');
         if (!imageData) return;

         const source = e.dataTransfer.getData('source');
         window.isImageAddedFromPanel = source === 'imageStampPanel';

         const windowCoordinates = getMouseLocation(e);
         const viewerElement = documentViewer.getViewerElement();
         const viewerRect = viewerElement.getBoundingClientRect();
         const x = e.clientX - viewerRect.left;
         const y = e.clientY - viewerRect.top;
         const displayMode = viewer.getDisplayModeManager().getDisplayMode();
         const selectedPages = displayMode.getSelectedPages(windowCoordinates, windowCoordinates);
         const pageNumber = selectedPages?.last;

         if (!pageNumber) {
             console.warn('Page number not found at drop location.');
             return;
         }

         const pagePoint = displayMode.windowToPage(windowCoordinates, pageNumber);

         const stampAnnot = new Annotations.StampAnnotation();
         stampAnnot.PageNumber = pageNumber;
         stampAnnot.X = pagePoint.x;
         stampAnnot.Y = pagePoint.y;
         stampAnnot.Width = 200;
         stampAnnot.Height = 150;
         stampAnnot.Author = annotManager.getCurrentUser();
         stampAnnot.Rotation = viewer.getCompleteRotation(pageNumber) * 90;
         await stampAnnot.setImageData(imageData);

         annotManager.addAnnotation(stampAnnot);
         annotManager.redrawAnnotation(stampAnnot);
     });

     isDropListenerBound = true;
 },
--------------------------------------------------------------------------------------------------------------

render: () => {
    const panelDiv = document.createElement('div');

    const heading = document.createElement('h1');
    heading.textContent = 'Image Stamp';
    heading.className = 'Custom-rubber-stamp-panel-header';
    panelDiv.appendChild(heading);

    const button = document.createElement('button');
    button.textContent = 'Upload Image Stamp';
    button.className = 'Custom-createRubberStampButton';
    panelDiv.appendChild(button);

    const imageStampList = document.createElement('div');
    imageStampList.id = 'imageStampList';
    imageStampList.style.marginTop = '10px';
    imageStampList.style.display = 'flex';
    imageStampList.style.flexWrap = 'wrap';
    imageStampList.style.gap = '10px';
    panelDiv.appendChild(imageStampList);

    const viewer = window.webViewerInstance.Core.documentViewer;
    const annotManager = window.webViewerInstance.Core.annotationManager;
    const Annotations = window.webViewerInstance.Core.Annotations;

    Apryse.bindDropHandler(viewer, annotManager, Annotations, documentViewer);

    const renderImageStamps = () => {
        imageStampList.innerHTML = '';
        const stamps = Apryse?.stampData || [];
        stamps.forEach((s) => {
            if (s.imageData) {
                const img = document.createElement('img');
                img.src = s.imageData;
                img.draggable = true;
                img.style.width = '180px';
                img.style.height = '130px';
                img.style.border = '1px solid #ccc';
                img.style.marginBottom = '10px';
                img.style.marginRight = '10px';
                img.style.cursor = 'grab';

                img.ondragstart = (ev) => {
                    ev.dataTransfer.setData('imageData', s.imageData);
                    ev.dataTransfer.setData('source', 'imageStampPanel');
                };

                imageStampList.appendChild(img);
            }
        });
    };

    button.onclick = () => {
        const fileInput = document.createElement('input');
        fileInput.type = 'file';
        fileInput.accept = '.jpg,.jpeg,.png';
        fileInput.style.display = 'none';
        panelDiv.appendChild(fileInput);

        fileInput.click();

        fileInput.onchange = (event) => {
            const file = event.target.files[0];
            if (file) {
                const reader = new FileReader();
                reader.onload = (e) => {
                    const imageData = e.target.result;

                    if (!Apryse.stampData) Apryse.stampData = [];
                    Apryse.stampData.push({ imageData });

                    renderImageStamps();
                    Apryse.AddStamp(imageData);
                };
                reader.readAsDataURL(file);
            }

            panelDiv.removeChild(fileInput);
        };
    };

    renderImageStamps();

    return panelDiv;
}

I have attached the video for your reference.

1 Like

Hello @rishumishra2001,

Thank you for the information.

You mentioned attaching a video, however there may have been an issue with uploading the video to the forum post, as I am unable to see the attached video. Would you be able to resend the video file? Alternatively, you can upload it to a file sharing service such as Google Drive or Sharepoint and provide a share link for us to use.

Additionally, from your testing are your custom UI components that you are implementing adding an unexpected offset to the cursor event? If so, and the distance is consistent, we would recommend applying an offset in the opposite direction to mitigate this.

Let us know how you would like to proceed.

Best Regards,
Jacob Romano Carlsen
Web Development Support Engineer
Apryse Software Inc.

1 Like


Here is the video for the you reference.

1 Like

It add image to the right to drop location.

1 Like

Any update on the issue

1 Like

Hello @rishumishra2001,

Thank you for the information. It is a bit difficult as the mouse location disappears, but is the offset of the placement only horizontal? Is it possible to get an approximation on how far offset the annotation is from the drop point?

In the demo video, at the far left there appears to be a scroll bar. Is there a small panel to the left of your stamp panel which is roughly the size of the offset?


If there is, we recommend adding the width of this component to the offset and seeing if that resolves your issue.

Best Regards,
Jacob Romano Carlsen
Web Development Support Engineer
Apryse Software Inc.

1 Like