Fabric.js pdf editor is not working in Adobe Accrobate

I created a PDF editor in my Angular / laravel application using: "fabric": "^6.5.3" and "pdf-lib": "^1.17.1". The thing is when I download the pdf, it is readable with all its edits in Chrome or Microsoft Edge, but when I try to read it using Adobe Acrobat Reader, the last page shows the following error: There was an error processing a page. There was a problem reading this document (18). But only if the last page is edited with a shape image or drawing, not with simple text or line. So why is that happening? here is the function responsible for downloading the edited pdf:

 async downloadPdfWithEdits() {
if (!this.pdfDoc) return;
const pdfDoc = await PDFDocument.create();
const existingPdfBytes = await fetch(this.pdfURL).then(res => res.arrayBuffer());
const existingPdf = await PDFDocument.load(existingPdfBytes);

for (let i = 1; i <= this.totalPages; i++) {
  const [copiedPage] = await pdfDoc.copyPages(existingPdf, [i - 1]);

  const canvasState = this.pageStates[i];
  if (canvasState) {
    //console.log(`Loading canvas state for page ${i}:`, canvasState);
    // Create an offscreen canvas element
    const tempCanvasElement = document.createElement('canvas');
    tempCanvasElement.width = copiedPage.getWidth();
    tempCanvasElement.height = copiedPage.getHeight();

    // Ensure it is added to the DOM (hidden) to avoid "ctx" undefined errors
    document.body.appendChild(tempCanvasElement);

    const tempCanvas = new fabric.StaticCanvas(tempCanvasElement);
    console.log("Fabric.js canvas:", tempCanvas);
    try {
      await new Promise<void>((resolve, reject) => {
        tempCanvas.loadFromJSON(canvasState, () => {
          // 🔥 Ensure the canvas matches the PDF page size
          tempCanvas.setDimensions({ width: this.pdfCanvas.nativeElement.width, height: this.pdfCanvas.nativeElement.height, });

          tempCanvas.renderAll();
          console.log("Fabric.js canvas:", tempCanvas);

          setTimeout(async () => {
            try {
             // const imageData = tempCanvas.toDataURL({ format: 'png', multiplier: 1 });
              const imageData = tempCanvas.toDataURL({ format: 'png', quality: 0.9, multiplier: 1 });

              const img = await pdfDoc.embedPng(imageData);

              const { width, height } = copiedPage.getSize(); // Get original PDF page size

              copiedPage.drawImage(img, {
                x: 0,
                y: 0,
                width: width,  // Match width
                height: height // Match height
              });

              console.log(`✅ Edits applied to page ${i}`);
              resolve();
            } catch (error) {
              console.error("❌ Error embedding PNG image:", error);
              reject(error);
            }
          }, 500);
        });
      });

    } catch (error) {
      console.error(`❌ Error processing page ${i}:`, error);
    }

    tempCanvas.dispose();
    document.body.removeChild(tempCanvasElement);
  } else {
    console.log(`⚠️ No edits found for page ${i}`);
  }

  pdfDoc.addPage(copiedPage);
}

try {
  const pdfBytes = await pdfDoc.save();
  const blob = new Blob([pdfBytes], { type: 'application/pdf' });
  const link = document.createElement('a');
  link.href = URL.createObjectURL(blob);
  link.download = 'edited.pdf';
  document.body.appendChild(link);
  link.click();
  document.body.removeChild(link);

  console.log('✅ PDF downloaded successfully');
} catch (error) {
  console.error('❌ Error saving and downloading PDF:', error);
}

}

Upvotes: 0

Views: 56

Answers (1)

mkl
mkl

Reputation: 96009

On the last page of your PDF you use an XObject Image-8784015711:

q
    1 0 0 1 0 0 cm
    1 0 0 1 0 0 cm
    612 0 0 792 0 0 cm
    1 0 0 1 0 0 cm
    /Image-8784015711 Do
Q

but in the XObject resources

<<
/Type /Page
/Annots [ 307 0 R 308 0 R 309 0 R ]
/Contents [ 64 0 R 310 0 R 65 0 R 321 0 R ]
/MediaBox [ 0 0 612 792 ]
/Resources <<
/Font <<
/F92 312 0 R
>>
/ProcSet [ /PDF /Text ]
/XObject <<
/Image-735569487 320 0 R
/Image-8784015711 336 0 R
>>
/ExtGState <<
>>
>>
/Parent 1 0 R
>>

that name points to an object stream, not an XObject stream.

336 0 obj
<<
/Filter /FlateDecode
/Type /ObjStm
/N 50
/First 382
/Length 2815
>>
stream
...
endstream
endobj

Thus, that page content is indeed broken.

I don't know the libs used here and, therefore, cannot tell whether this is the result of wrong usage of them or a bug in them, but this should indicate what people who know the libs should look for.

Upvotes: 0

Related Questions