Skip to content

Instantly share code, notes, and snippets.

@yurydelendik
Last active October 1, 2024 08:02
Show Gist options
  • Save yurydelendik/f2b846dae7cb29c86d23 to your computer and use it in GitHub Desktop.
Save yurydelendik/f2b846dae7cb29c86d23 to your computer and use it in GitHub Desktop.
PDF.js get/show hightlight
function getHightlightCoords() {
var pageIndex = PDFViewerApplication.pdfViewer.currentPageNumber - 1;
var page = PDFViewerApplication.pdfViewer.getPageView(pageIndex);
var pageRect = page.canvas.getClientRects()[0];
var selectionRects = window.getSelection().getRangeAt(0).getClientRects();
var viewport = page.viewport;
var selected = selectionRects.map(function (r) {
return viewport.convertToPdfPoint(r.left - pageRect.x, r.top - pageRect.y).concat(
viewport.convertToPdfPoint(r.right - pageRect.x, r.bottom - pageRect.y));
});
return {page: pageIndex, coords: selected};
}
function showHighlight(selected) {
var pageIndex = selected.page;
var page = PDFViewerApplication.pdfViewer.getPageView(pageIndex);
var pageElement = page.canvas.parentElement;
var viewport = page.viewport;
selected.coords.forEach(function (rect) {
var bounds = viewport.convertToViewportRectangle(rect);
var el = document.createElement('div');
el.setAttribute('style', 'position: absolute; background-color: pink;' +
'left:' + Math.min(bounds[0], bounds[2]) + 'px; top:' + Math.min(bounds[1], bounds[3]) + 'px;' +
'width:' + Math.abs(bounds[0] - bounds[2]) + 'px; height:' + Math.abs(bounds[1] - bounds[3]) + 'px;');
pageElement.appendChild(el);
});
}
@yurydelendik
Copy link
Author

PDFViewerApplication.pdfViewer.pages is undefined now.

@chitgoks pages is now "private" and replaced by getPageView()

@yurydelendik
Copy link
Author

Hey,do you have a working demo on how this functionality works?

You can paste and play with the scripts in the browser's console.

@eaxle
Copy link

eaxle commented Nov 19, 2015

@yurydelendik Instead of page.canvas.parentElement , I have used page.canvas.parentNode.nextSibling;
so that the highlighted div are inside the div with textLayer class and I have increased the opacity of that div(with class='textLayer') from 0.2 to 0.5 .Does that do any harm to the normal flow of the application?

@CGRemakes
Copy link

Is there a particular event in pdf.js that the function needs to be linked to? EDIT: I just attached it to onmouseup to test it. Works pretty well, but it's a bit inconsistent how it handles highlights with wrapped text. I find the need to for the i % 2 is more browser specific. In Firefox, it results in lines being skipped. I changed mine to if( !window.chrome || i % 2 == 0 ). I find selectionRects for IE only works in very simple cases (no wraps, weird jogs in text, etc).

@MasterTheRock
Copy link

Hi, I try to understand to coordinate return by function convertToPdfPoint, my goal is to get coordinate in the pdf file to do some modification of the pdf at this coordinate. The problem is convertToPdfPoint seems to return odd Y coordinate, while X seems to be fine.

Let say I want to get a point in left top corner of a page, when calling the function like this : convertToPdfPoint(1.5,2) it return (3.5, 592)
The scale and rotation are not the problem as it return consistent coordinate when changing scale and rotation, but always with Y as highest value on the top of the page and lowest value at the bottom. If all pages have the same length I can do the math, but this is not true in real life.

Should I find an other function or other calculation to get the information I am seeking ?

EDIT : I found out that normal coordinate 0,0 in pdf is in bottom-left, so it's why I got the coordinate that way. I need it the other way arround because the code I'm using set 0,0 at top-left, but that's an other story. Hope this could help someone else !

@chitgoks
Copy link

IE doesnt work well with window.getSelection() in the textLayer portion. chrome and firefox are okay.

@FetFrumos
Copy link

I am begginer in java script. Please create demo on how this functionality. Thanks you.

@chitgoks
Copy link

old post. but i would like to ask regarding this issue of
window.getSelection().getRangeAt(0).getClientRects()

with IE and Edge?

@mr-easy
Copy link

mr-easy commented Jul 27, 2020

The original solution wasn't working for me. Here is the one that worked good for me:

function HighlightSelectedText() { 
  let pageIndex = window.PDFViewerApplication.pdfViewer.currentPageNumber-1;
  let page = window.PDFViewerApplication.pdfViewer.getPageView(pageIndex);
  let pageElement = page.canvas.parentElement; 
  let pageRect = page.canvas.getClientRects()[0];
  let selectionRects = window.getSelection().getRangeAt(0).getClientRects();
  let viewport = page.viewport;
  selectionRects = Array.from(selectionRects);
  let selected = selectionRects.forEach(function (r) {
    let rect = viewport.convertToPdfPoint(r.left - pageRect.left, r.top - pageRect.top).concat(viewport.convertToPdfPoint(r.right - pageRect.left, r.bottom - pageRect.top));
    let bounds = viewport.convertToViewportRectangle(rect);
    let el = document.createElement('div');
    el.setAttribute('style', 'position: absolute; background-color: rgba(238, 170, 0, .2);' +
      'left:' + Math.min(bounds[0], bounds[2]) + 'px; top:' + Math.min(bounds[1], bounds[3]) + 'px;' +
      'width:' + Math.abs(bounds[0] - bounds[2]) + 'px; height:' + Math.abs(bounds[1] - bounds[3]) + 'px;');
    pageElement.appendChild(el);
  });
}

Hope it helps :)

Does anyone know how can I transform these coordinates to a position in the canvas? So that instead of adding divs, I can draw rectangles on the canvas. Thanks in advance.

@ricardojlrufino
Copy link

image

function getSelectionCoords() {
    var pdfViewer = window.appPdfViewer;    
    var pageIndex = pdfViewer.currentPageNumber - 1; 
    var page = pdfViewer.getPageView(pageIndex);
    var pageRect = page.canvas.getClientRects()[0];
    var selectionRects = window.getSelection().getRangeAt(0).getClientRects();
    var viewport = page.viewport;
    var selectionRectsList = Object.values(selectionRects);
    var selected = selectionRectsList.map(function (r) {
      return viewport.convertToPdfPoint(r.left - pageRect.x, r.top - pageRect.y).concat(
         viewport.convertToPdfPoint(r.right - pageRect.x, r.bottom - pageRect.y)); 
    });
    return {page: pageIndex, coords: selected};
}



function showHighlight() {
    var selected = getSelectionCoords();
    var pageIndex = selected.page; 
    var pdfViewer = window.appPdfViewer;  
    var page = pdfViewer.getPageView(pageIndex);
    var pageElement = page.canvas.parentElement;
    var viewport = page.viewport;

    selected.coords.forEach(function (rect) {

      // Individual Seletions...
      var x1 = Math.min(bounds[0], bounds[2]);
      var y1 = Math.min(bounds[1], bounds[3]);
      var width =  Math.abs(bounds[0] - bounds[2]);
      var hight = Math.abs(bounds[1] - bounds[3]);
      var el = createRectDiv([x1, y1, width, hight]);
      pageElement.appendChild(el);

    });
   
}

function createRectDiv(boundBox){
    var randomColor = Math.floor(Math.random()*16777215).toString(16);
    console.log(randomColor);
    var el = document.createElement('div');
    el.setAttribute('class', 'hiDiv')
    el.setAttribute('style', 'position: absolute; background-color: #'+randomColor+'; opacity: 0.5;' + 
    'left:' + boundBox[0] + 'px; top:' + boundBox[1] + 'px;' +
    'width:' + boundBox[2] + 'px; height:' + boundBox[3] + 'px;');
    return el;
}


window.addEventListener('mouseup', function() {

    var length = window.getSelection().toString().length;
    if(length > 0){
        showHighlight();
    }else{
        // Clear All ?!
    }
  console.log();
});



@shessafridi
Copy link

I am getting this weird overflow with this approach any idea why this might be the case?

the pdf viewer I am using is ngx-extended-pdf-viewer

image

@vquilon
Copy link

vquilon commented Nov 27, 2021

This is my updated solution. With this example you can continue and add some buttons, with pagination or zoom scalation. Have FUN!

HTML

<link rel="stylesheet" href="https://mozilla.github.io/pdf.js/web/viewer.css">
<script defer src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.11.338/pdf.min.js"></script>

<div id="pdfContainer" class="pdf-content"></div>

CSS

#pdfContainer {
  display: flex;
  justify-content: center;
}

JAVASCRIPT

window.onload = function () {
  function getOutputScale() {
    var pixelRatio = 'devicePixelRatio' in window ? window.devicePixelRatio : 1;
    return {
      sx: pixelRatio,
      sy: pixelRatio,
      scaled: pixelRatio != 1
    };
  }

  // atob() is used to convert base64 encoded PDF to binary-like data.
  var pdfBase64 = atob(
    "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/Z3Gk7/G6V9ibkJ/vwHTv4SDJP83MP0FMxH6e62lf40ehf5O4/R3mk9/h96F/g7+jv4e36NL84eDS/7K9PeYj6N/pfHkj3td8oezS/6419FfNE5/r/cmf8zN0d+hR5f+fzAfR/9S4/QvNT/540xP/6iCBUmbgFXBLv9ZQdPe5zmuny687h02rh/C39+EaZxQpX+/AfDBAUYKZW5kc3RyZWFtCmVuZG9iagoKMTEgMCBvYmoKPDwvVHlwZS9Gb250L1N1YnR5cGUvVHJ1ZVR5cGUvQmFzZUZvbnQvQkFBQUFBK1RpbWVzTmV3Um9tYW5QU01UCi9GaXJzdENoYXIgMAovTGFzdENoYXIgNjQKL1dpZHRoc1s3NzcgNjEwIDUwMCAyNzcgMzg5IDI1MCA0NDMgMjc3IDQ0MyA1MDAgNTAwIDQ0MyA1MDAgNzc3IDUwMCAyNTAKNTU2IDMzMyA1MDAgMjc3IDcyMiA1NTYgMzMzIDMzMyAzMzMgNTAwIDcyMiAyNTAgNTAwIDUwMCA2MTAgNTAwCjMzMyAzMzMgNTAwIDUwMCA3MjIgNTU2IDM4OSA5NDMgNTAwIDQwOCA2NjYgNTAwIDUwMCA1MDAgNTAwIDUwMAo3MjIgNDQzIDI3NyA2MTAgMjc3IDcyMiA3MjIgNjY2IDE4MCA3MjIgODg5IDI3NyA1MDAgMzMzIDMzMyA1MDAKNTAwIF0KL0ZvbnREZXNjcmlwdG9yIDkgMCBSCi9Ub1VuaWNvZGUgMTAgMCBSCj4+CmVuZG9iagoKMTIgMCBvYmoKPDwvRjEgMTEgMCBSCj4+CmVuZG9iagoKMTMgMCBvYmoKPDwvRm9udCAxMiAwIFIKL1hPYmplY3Q8PC9UcjQgNCAwIFI+PgovRXh0R1N0YXRlPDwvRUdTNSA1IDAgUj4+Ci9Qcm9jU2V0Wy9QREYvVGV4dC9JbWFnZUMvSW1hZ2VJL0ltYWdlQl0KPj4KZW5kb2JqCgoxIDAgb2JqCjw8L1R5cGUvUGFnZS9QYXJlbnQgNiAwIFIvUmVzb3VyY2VzIDEzIDAgUi9NZWRpYUJveFswIDAgNjEyIDc5Ml0vR3JvdXA8PC9TL1RyYW5zcGFyZW5jeS9DUy9EZXZpY2VSR0IvSSB0cnVlPj4vQ29udGVudHMgMiAwIFI+PgplbmRvYmoKCjYgMCBvYmoKPDwvVHlwZS9QYWdlcwovUmVzb3VyY2VzIDEzIDAgUgovTWVkaWFCb3hbIDAgMCA2MTIgNzkyIF0KL0tpZHNbIDEgMCBSIF0KL0NvdW50IDE+PgplbmRvYmoKCjE0IDAgb2JqCjw8L1R5cGUvQ2F0YWxvZy9QYWdlcyA2IDAgUgovT3BlbkFjdGlvblsxIDAgUiAvWFlaIG51bGwgbnVsbCAwXQovTGFuZyhlbi1VUykKPj4KZW5kb2JqCgoxNSAwIG9iago8PC9BdXRob3I8RkVGRjAwNTYwMDY5MDA3NjAwNjkwMDZFMDAyMDAwNTAwMDYxMDA2QzAwNjkwMDYxMDA3NDAwNjg+Ci9DcmVhdG9yPEZFRkYwMDU3MDA3MjAwNjkwMDc0MDA2NTAwNzI+Ci9Qcm9kdWNlcjxGRUZGMDA0QzAwNjkwMDYyMDA3MjAwNjUwMDRGMDA2NjAwNjYwMDY5MDA2MzAwNjUwMDIwMDAzNDAwMkUwMDMwPgovQ3JlYXRpb25EYXRlKEQ6MjAxMzA2MDYxODMxMzItMDcnMDAnKT4+CmVuZG9iagoKeHJlZgowIDE2CjAwMDAwMDAwMDAgNjU1MzUgZiAKMDAwMDAzNDA0OSAwMDAwMCBuIAowMDAwMDAwMDE5IDAwMDAwIG4gCjAwMDAwMDI5MzkgMDAwMDAgbiAKMDAwMDAwMjk2MCAwMDAwMCBuIAowMDAwMDAzMTM3IDAwMDAwIG4gCjAwMDAwMzQxOTIgMDAwMDAgbiAKMDAwMDAwMzE3NyAwMDAwMCBuIAowMDAwMDMyNjgyIDAwMDAwIG4gCjAwMDAwMzI3MDQgMDAwMDAgbiAKMDAwMDAzMjkwMyAwMDAwMCBuIAowMDAwMDMzNDcxIDAwMDAwIG4gCjAwMDAwMzM4OTEgMDAwMDAgbiAKMDAwMDAzMzkyNCAwMDAwMCBuIAowMDAwMDM0MjkxIDAwMDAwIG4gCjAwMDAwMzQzODggMDAwMDAgbiAKdHJhaWxlcgo8PC9TaXplIDE2L1Jvb3QgMTQgMCBSCi9JbmZvIDE1IDAgUgovSUQgWyA8QjFEMEZFNDk3OEU0QUFFQUQ5MEM5NkRDRDJFM0U0ODQ+CjxCMUQwRkU0OTc4RTRBQUVBRDkwQzk2RENEMkUzRTQ4ND4gXQovRG9jQ2hlY2tzdW0gLzAyODgzQjZCODM1QzZGMEMwQTVCRTUyNDgzMURFRkQwCj4+CnN0YXJ0eHJlZgozNDYyOQolJUVPRgo="
  );

  window.myState = {
    pdfDoc: null,
    pageNum: 1,
    pageRendering: false,
    pageNumPending: null,
    scale: 0.8,
    canvas: null,
    ctx: null,
    textLayer: null,
  };
  myState.canvas = document.createElement("canvas");
  var $pdfContainer = document.querySelector("#pdfContainer");
  $pdfContainer.appendChild(myState.canvas);
  myState.ctx = myState.canvas.getContext('2d');
  
  // Create TextLayer Div
  var canvasOffset = myState.canvas.getBoundingClientRect();
  var $textLayerDiv = document.createElement("div");
  $textLayerDiv.classList.add("textLayer");
  $pdfContainer.append($textLayerDiv);
  myState.textLayer = $textLayerDiv;


  /**
   * Get page info from document, resize canvas accordingly, and render page.
   * @param num Page number.
   */
  function renderPage(num) {
    myState.pageRendering = true;
    // Using promise to fetch the page
    myState.pdfDoc.getPage(num).then(function(page) {
      var viewport = page.getViewport({scale: myState.scale});
     

      // The following few lines of code set up scaling on the ctx if we are on a HiDPI display
      var outputScale = getOutputScale();
  
      
      myState.canvas.width = Math.floor(viewport.width);
      myState.canvas.height = Math.floor(viewport.height);
      myState.canvas.style.width = Math.floor(viewport.width) + "px";
      myState.canvas.style.height = Math.floor(viewport.height) + "px";

      $textLayerDiv.style.width = Math.floor(viewport.width) + "px";
      $textLayerDiv.style.height = Math.floor(viewport.height) + "px";
      
      let canvasOffset = myState.canvas.getBoundingClientRect();
      $textLayerDiv.style.top = `${canvasOffset.top}px`;
      $textLayerDiv.style.left = `${canvasOffset.left}px`;

      const transform = outputScale !== 1 ? [outputScale, 0, 0, outputScale, 0, 0] : null;
      
      // Render PDF page into canvas context
      var renderContext = {
          canvasContext: myState.ctx,
          viewport: viewport,
          transform: transform,
      };

      var renderTask = page.render(renderContext);
      
      renderTask.promise.then(() => {
        myState.pageRendering = false;
        if (myState.pageNumPending !== null) {
          // New page rendering is pending
          renderPage(myState.pageNumPending);
          myState.pageNumPending = null;
        }
        
        return page.getTextContent().then((textContent) => {      
          var textLayer =  pdfjsLib.renderTextLayer({
            textContent: textContent,
            container: $textLayerDiv,
            viewport: viewport
          });
          textLayer._render();
          //textLayer.setTextContent(textContent);
          //textLayer.render();
        });
      });
    });

    // Update page counters
    // document.getElementById('page_num').textContent = num;
  }
  
  /**
   * If another page rendering in progress, waits until the rendering is
   * finised. Otherwise, executes rendering immediately.
   */
  function queueRenderPage(num) {
    if (myState.pageRendering) {
      myState.pageNumPending = num;
    } else {
      renderPage(num);
    }
  }

  /**
   * Displays previous page.
   */
  function onPrevPage() {
    if (myState.pageNum <= 1) {
      return;
    }
    myState.pageNum--;
    queueRenderPage(myState.pageNum);
  }
  /**
   * Displays next page.
   */
  function onNextPage() {
    if (myState.pageNum >= myState.pdfDoc.numPages) {
      return;
    }
    myState.pageNum++;
    queueRenderPage(myState.pageNum);
  }
  // document.getElementById('next').addEventListener('click', onNextPage);
  // document.getElementById('prev').addEventListener('click', onPrevPage);
  
  /**
   * Asynchronously downloads PDF.
   */
  pdfjsLib.getDocument({data: pdfBase64}).promise.then(function(pdfDoc_) {
    myState.pdfDoc = pdfDoc_;
    // document.getElementById('page_count').textContent = pdfDoc.numPages;
    
    // Initial/first page rendering
    renderPage(myState.pageNum);
  });
  
  
  
  // Text Higlihter
  function getSelectionCoords() {
   
    let pageIndex = myState.pageNum;
    let selectedPromise = myState.pdfDoc.getPage(pageIndex).then((_page) => {
      let pageRect = myState.canvas.getClientRects()[0];
      let selectionRects = window.getSelection().getRangeAt(0).getClientRects();
      let viewport = _page.getViewport({scale: myState.scale});
      let selectionRectsList = Object.values(selectionRects);
      
      let selected = selectionRectsList.map(function (r) {
        return viewport.convertToPdfPoint(r.left - pageRect.x, r.top - pageRect.y).concat(
           viewport.convertToPdfPoint(r.right - pageRect.x, r.bottom - pageRect.y)); 
      });
      return selected;
    });
    
 
    return {pageIndex: pageIndex, coordsPromise: selectedPromise};
  }

  function showHighlight() {
      var {pageIndex, coordsPromise} = getSelectionCoords();
      myState.pdfDoc.getPage(pageIndex).then((page) => {
        var viewport = page.getViewport({scale: myState.scale});;
        coordsPromise.then((coords) => {
          let highlightColor = generateColor();
          
          coords.forEach(function (rect) {
            let bounds = viewport.convertToViewportRectangle(rect);

            var x1 = Math.min(bounds[0], bounds[2]);
            var y1 = Math.min(bounds[1], bounds[3]);
            var width =  Math.abs(bounds[0] - bounds[2]);
            var hight = Math.abs(bounds[1] - bounds[3]);
            
            var el = createRectDiv([x1, y1, width, hight], highlightColor);
            myState.textLayer.appendChild(el);
          });
        });
      });
  }
  
  const generateColor = () => {
    return Math.floor(Math.random()*16777215).toString(16);
  }
  function createRectDiv(boundBox, highlightColor){
      // console.log(randomColor);
      var el = document.createElement('div');
      el.setAttribute('class', 'hiDiv')
      el.setAttribute('style', 'position: absolute; background-color: #'+highlightColor+'; opacity: 0.5;' + 
      'left:' + boundBox[0] + 'px; top:' + boundBox[1] + 'px;' +
      'width:' + boundBox[2] + 'px; height:' + boundBox[3] + 'px;');
      return el;
  }

  window.addEventListener('mouseup', function() {
    var length = window.getSelection().toString().length;
    if(length > 0){
        showHighlight();
    }else {
        // Clear All ?!
    }
    console.log();
  });
};

@abhilashsajeev
Copy link

@vquilon @ricardojlrufino Is it possible to download the file with the highlights?

@vquilon
Copy link

vquilon commented Dec 30, 2021

@abhilashsajeev If you want to make annotations in the PDF file you need to modify the binary data of the file. To do that you need another library like https://pdf-lib.js.org/.

@achapman009
Copy link

achapman009 commented Jun 29, 2022

I am getting this weird overflow with this approach any idea why this might be the case?

the pdf viewer I am using is ngx-extended-pdf-viewer

image

@shessafridi I am having this same issue. Did you ever resolve this?

@vquilon
Copy link

vquilon commented Jun 29, 2022

I am getting this weird overflow with this approach any idea why this might be the case?
the pdf viewer I am using is ngx-extended-pdf-viewer
image

@shessafridi I am having this same issue. Did you ever resolve this?

This seems to be a problem in the style of hidden text layer. I mean, maybe you have other CSS that modify the text layers, for example the font-size, I recommend you, to use my solution from zero, no extra CSS or code.

I have a minimal solution in a Codepen. Only highgliht the texts, but in the javascript you can copy the text or other operations.

Minimalistic PDF selector

@nurullah
Copy link

nurullah commented Jul 8, 2022

I am getting this weird overflow with this approach any idea why this might be the case?
the pdf viewer I am using is ngx-extended-pdf-viewer
image

@shessafridi I am having this same issue. Did you ever resolve this?

As far as i remember this problem is caused by the textLayerMode property. You can try this property by giving 2 value.

const pdfViewer = new pdfjsViewer.PDFViewer({
  container,  
  eventBus,  
  linkService,  
  textLayerMode: 2  
})

@falxen
Copy link

falxen commented Jul 16, 2022

@vquilon Thanks for your codepen. https://codepen.io/vquilon/pen/zYdgjVV
Unfortunately, it doesn't seem to highlight anything for me - what paragraph or words is it meant to highlight? I was hoping to start off with your code: I'm trying to highlight a part of the PDF, when given the start index and end index (of how the text appears in the PDF).

@achapman009
Copy link

I am getting this weird overflow with this approach any idea why this might be the case?
the pdf viewer I am using is ngx-extended-pdf-viewer
image

@shessafridi I am having this same issue. Did you ever resolve this?

As far as i remember this problem is caused by the textLayerMode property. You can try this property by giving 2 value.

const pdfViewer = new pdfjsViewer.PDFViewer({
  container,  
  eventBus,  
  linkService,  
  textLayerMode: 2  
})

This is the correct answer. Fixed all issues.

@arty-ms
Copy link

arty-ms commented Nov 2, 2023

This solution is not useful for several reasons:

  1. It is an overlay on the top of the text, so it changes the view(color) of the text.
  2. On the tablets it will work with problems.
  3. The pdf.js TextLayer is rendered with incorrect fonts. That's why your highlight may be in incorrect place.
    The smooth way would be to make highlight using canvas. You need to construct charsMap of all characters rendered on the canvas by pdf.js. Then on mouse move you will need to find the char in charsMap and re-draw text with background on the top of pdf.js canvas. This drawing should be done in separate custom canvas.

@Mowmowj
Copy link

Mowmowj commented Jan 12, 2024

This solution is not useful for several reasons:

  1. It is an overlay on the top of the text, so it changes the view(color) of the text.
  2. On the tablets it will work with problems.
  3. The pdf.js TextLayer is rendered with incorrect fonts. That's why your highlight may be in incorrect place.
    The smooth way would be to make highlight using canvas. You need to construct charsMap of all characters rendered on the canvas by pdf.js. Then on mouse move you will need to find the char in charsMap and re-draw text with background on the top of pdf.js canvas. This drawing should be done in separate custom canvas.

Hi, I am working on this recently, I would like to discuss this with you.
About your problem :
No.1 you could add a svg layer under text layer to use rect to render the highlighted element then the text layer will not be changed.
No.2 curious to know why can't it work on tablet? cuz touch or something? (as i know ios selection have problem.)
No.3 you are right the text layer position is not 100% same with canvas text position. the canvas solution is great as i find the "apryse"(but it not open source and need pay) seems to use this solution. But I am not sure how much work need to do of this by self.

@dieggop
Copy link

dieggop commented Apr 26, 2024

Is possible to get only position of click on document? in points?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment