How to dynamically position and draw an editableContent text on a pdf file using pdf-lib drawText()

151 Views Asked by At

I need your help guys, I am currently working on a PDF editor using pdf.js and pdf-lib, I am using the pdf.js to render the pdf on a page within my application and the pdf-lib strictly for modify and downloading the modified pdf document.

I have been able to add text to a loaded PDF with an editableContent and also able to download the pdf document with the modified content. But the problem is that the modified content on the downloaded pdf document is not properly positioned as of when it was added.

I have tried calculating the position of the editableContent on the pdf document by getting the x, y, width, and height of the pdf document by using getCropBox() function in pdf-lib but still can't get it working.

Here is the modification function in my code:

const { PDFDocument, rgb } = window.PDFLib;

let pdfBytes;
const editableContents = [];

async function loadAndDisplayPdf() {
  const pdfUrl = 'software-development-proposal-template-1702352345704.pdf';

  // Asynchronously download PDF as an ArrayBuffer
  pdfBytes = await fetch(pdfUrl).then(response => response.arrayBuffer());

  // Load PDF using pdf.js
  const loadingTask = pdfjsLib.getDocument({ data: pdfBytes });
  const pdf = await loadingTask.promise;

  // Render the first page
  const pageNumber = 1;
  const page = await pdf.getPage(pageNumber);

  // Get viewport
  const scale = 1.5;
  const viewport = page.getViewport({ scale });

  // Prepare canvas
  const canvas = document.createElement('canvas');
  const context = canvas.getContext('2d');
  canvas.width = viewport.width;
  canvas.height = viewport.height;

  // Render PDF to canvas
  await page.render({ canvasContext: context, viewport }).promise;

  // Display the canvas
  const pdfContainer = document.getElementById('pdf-container');
  pdfContainer.innerHTML = '';
  pdfContainer.appendChild(canvas);

  // Add double-click event to canvas for adding editable content
  canvas.addEventListener('dblclick', addEditableContent);
}

function addEditableContent(event) {
  // Check if the double-click is on the canvas
  if (event.target.tagName.toLowerCase() === 'canvas') {
    // Get coordinates of double-click
    const x = event.clientX + window.scrollX;
    const y = event.clientY + window.scrollY;

    let customFontSize = 6;

    // Create an editable div
    const editableContent = document.createElement('div');
    editableContent.contentEditable = true;
    editableContent.style.position = 'absolute'; // Keep it as 'absolute'
    editableContent.style.left = `${x}px`;
    editableContent.style.top = `${y}px`;
    editableContent.style.fontSize = `${customFontSize}`
    editableContent.innerText = 'Editable Text';

    // Append the editable content to the container (same container as the canvas)
    const canvasContainer = document.getElementById('pdf-container');
    canvasContainer.appendChild(editableContent);

    // Save the editable content and its position
    editableContents.push({ element: editableContent, x, y });

    // Focus on the editable content
    editableContent.focus();
  }
}

// Function to convert pixels to points
function pixelsToPoints(pixels, dpi = 96) {
    const inchesX = pixels.x / dpi;
    const inchesY = pixels.y / dpi;
    const pointsX = inchesX * (72 / 1.4); // 1 inch = 72 points
    const pointsY = inchesY * (72 / 1.4); // 1 inch = 72 points
    return { x: pointsX, y: pointsY };
  }
  

async function modifyAndDownloadPdf() {
  if (!pdfBytes) {
    console.error('PDF not loaded');
    return;
  }

  // Use pdf-lib to modify the PDF
  const pdfDoc = await PDFDocument.load(pdfBytes);

  editableContents.forEach(({ element, x, y }) => {
    const editText = element.innerText;
    const firstPage = pdfDoc.getPages()[0];

    // Dynamically set the position based on the editableContent position in points
    const { x: pointsX, y: pointsY } = pixelsToPoints({ x, y });
    
    firstPage.drawText(`${editText}`, {
      x: pointsX - 85.1,
      y: firstPage.getSize().height - pointsY, // Invert Y-axis for correct positioning
      size: 6,
      color: rgb(0.95, 0.1, 0.1),
    });
  });

  // Save the modified PDF
  const modifiedPdfBytes = await pdfDoc.save();

  // Provide download link for the modified PDF
  const downloadLink = document.createElement('a');
  downloadLink.href = URL.createObjectURL(new Blob([modifiedPdfBytes], { type: 'application/pdf' }));
  downloadLink.download = 'modified-pdf.pdf';
  downloadLink.click();
}

const downloadBtn = document.getElementById('modifyAndDownloadPdf');
const loadPDFBtn = document.getElementById('loadAndDisplayPdf');

downloadBtn.addEventListener('click', () => {
  modifyAndDownloadPdf();
});

loadPDFBtn.addEventListener('click', () => {
  loadAndDisplayPdf();
});
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <script type="module" src="https://unpkg.com/jspdf@latest/dist/jspdf.umd.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.6.347/pdf.js"></script>
  <script type="module" src="https://cdn.jsdelivr.net/npm/pdf-lib/dist/pdf-lib.min.js"></script>
  <title>PDF Viewer and Editor</title>
</head>
<body>
  <nav style="background-color: #f1f1f1; padding: 10px; margin-bottom: 10px;">
    <div class="nav-container" style="display: flex; justify-content: space-around;">
        <div class="nav-left">
            <div class="brand">JustDoc</div>
        </div>
        <div class="nav-middle">
            <div class="brand">Others</div>
        </div>
        <div class="nav-right">
            Right side
        </div>
    </div>
  </nav>
  <div class="container" style="background-color: #f2f2f2; display: flex; justify-content: space-between;">
    <div class="tools" style="
        height: 500px; 
        background-color: #ffff; 
        width: 150px; 
        padding: 10px; 
        margin: 10px;
        border-radius: 5px;
    ">
        My Tools
    </div>
    <div id="pdf-container" 
        style=" 
            height: 550px; 
            overflow: auto; 
            overflow-y: scroll;
            margin: 10px;
    "></div>
    <div class="tools" style="
        height: 500px; 
        background-color: #ffff; 
        width: 150px; 
        padding: 10px; 
        margin: 10px;
        border-radius: 5px;
    ">
        <div class="title">
            <h4>Editor Tool</h4>
        </div>
        <div class="tools-container">
            <li>
                <button id="loadAndDisplayPdf">Load and Display PDF</button>
            </li>
            <li>
                <button id="modifyAndDownloadPdf">Download PDF</button>
            </li>
        </div>
    </div>
  </div>
  <script type="module" src="./js/main.js"></script>
</body>
</html>

0

There are 0 best solutions below