wojtekmaj / react-pdf

Display PDFs in your React app as easily as if they were images.
https://projects.wojtekmaj.pl/react-pdf
MIT License
9.45k stars 886 forks source link

Not rendering, no errors, loads fine #1904

Open johnnyshankman opened 4 days ago

johnnyshankman commented 4 days ago

Before you start - checklist

Description

Here's my whole component:

import { Document, pdfjs } from 'react-pdf';

pdfjs.GlobalWorkerOptions.workerSrc = `//unpkg.com/pdfjs-dist@${pdfjs.version}/build/pdf.worker.min.mjs`;

const LazyMedia = ({
}) => {
  ...
  return (
      <Document
        file={src}
        onLoadSuccess={handleLoad}
        onLoadError={handleError}
      />
    );
}

I get the following markdown with nothing inside the outer shell of what Document renders.

Screenshot 2024-10-25 at 11 40 18 AM

I get no console errors at all. I get the successful load callback firing.

This is the PDF

https://attachments.are.na/14176873/cf54adc6f26086c414b26974fbd4a884.pdf?1638225377

Steps to reproduce

See above

Expected behavior

I should see errors or some DOM or something

Actual behavior

Just an empty shell with silence in the console.

Additional information

No response

Environment

"react-pdf": "^9.1.1",
johnnyshankman commented 4 days ago

i can confirm with console logs that the global worksrc is set before my Document element is rendered:

It logs the following

//unpkg.com/pdfjs-dist@4.4.168/build/pdf.worker.min.mjs

Which does exist: https://unpkg.com/pdfjs-dist@4.4.168/build/pdf.worker.min.mjs

So why am i getting nothing?

johnnyshankman commented 4 days ago

Ahhhh I see, I may have missed some Vite-specific instructions, let me double check those aren't the missing key.

Copying cMaps

johnnyshankman commented 4 days ago

Still nothing, pretty frustrating.

I swapped to using the URL syntax + using the vite config updates

import { Document, pdfjs } from 'react-pdf';

pdfjs.GlobalWorkerOptions.workerSrc = new URL(
  'pdfjs-dist/build/pdf.worker.min.mjs',
  import.meta.url,
).toString();

const LazyMedia = ({
}) => {
  return (
      <Document
        file={src}
        onLoadSuccess={handleLoad}
        onLoadError={handleError}
      />
    );
}

And my vite config is perfect. I can see the import is working and leading to the worker.

So what's the issue? Why am I silently failing? I can't really do anything with this. Same exact behavior just an empty div with the react PDF classes on it.

I don't get it.

logs with the src and the worker url: Screenshot 2024-10-25 at 12 17 51 PM

proof the worker is actually the right file in source panel: Screenshot 2024-10-25 at 12 18 02 PM

johnnyshankman commented 4 days ago

I can confirm it's not the PDF as I've now re-done it using an example PDF y'all provided.

johnnyshankman commented 4 days ago

I can also confirm there is no load error. The onLoadSuccess handler is firing as expected and the onLoadError handler is not firing.

johnnyshankman commented 4 days ago

To unblock myself I've tried downgrading to ^8.0.0 and now I'm getting real errors in the log. Something about 9x is silently failing.

johnnyshankman commented 4 days ago

Ugh on 8.x i can't get the vite instructions to work so i opted back for unpkg:

pdfjs.GlobalWorkerOptions.workerSrc = `//unpkg.com/pdfjs-dist@${pdfjs.version}/build/pdf.worker.min.js`;

now i silently fail again. wtf is going on? this is so hard to use.

johnnyshankman commented 4 days ago

Same goes for the legacy build. I give up this is supposed to be plug and play and it's a gigantic migraine. I'll wait to hear from you, I've put in way more time than I should've to try to solve this.

johnnyshankman commented 4 days ago

Opted to just write my own component using the pdfjs-dist bc I couldn't figure this out:

import { ArrowLeft, ArrowRight } from '@phosphor-icons/react';
import * as pdfjsLib from 'pdfjs-dist';
import React, { useEffect, useRef, useState } from 'react';
import 'pdfjs-dist/build/pdf.worker.entry';

interface PDFViewerProps {
  src: string;
  onLoad: () => void;
  onError: () => void;
  className?: string;
}

pdfjsLib.GlobalWorkerOptions.workerSrc = `https://cdnjs.cloudflare.com/ajax/libs/pdf.js/${pdfjsLib.version}/pdf.worker.min.js`;

const PDFViewer: React.FC<PDFViewerProps> = ({ src, onLoad, onError, className }) => {
  const canvasRef = useRef<HTMLCanvasElement>(null);
  const [pdf, setPdf] = useState<pdfjsLib.PDFDocumentProxy | null>(null);
  const [currentPage, setCurrentPage] = useState(1);
  const [totalPages, setTotalPages] = useState(0);

  useEffect(() => {
    const loadPDF = async () => {
      try {
        const loadingTask = pdfjsLib.getDocument(src);
        const pdfDocument = await loadingTask.promise;
        setPdf(pdfDocument);
        setTotalPages(pdfDocument.numPages);
        renderPage(pdfDocument, 1);
        onLoad();
      } catch (error) {
        console.error('Error loading PDF:', error);
        onError();
      }
    };

    loadPDF();
  }, [src, onLoad, onError]);

  const renderPage = async (pdfDoc: pdfjsLib.PDFDocumentProxy, pageNumber: number) => {
    const page = await pdfDoc.getPage(pageNumber);
    const scale = 1.5;
    const viewport = page.getViewport({ scale });

    const canvas = canvasRef.current;
    if (!canvas) return;

    const context = canvas.getContext('2d');
    if (!context) return;

    canvas.height = viewport.height;
    canvas.width = viewport.width;

    const renderContext = {
      canvasContext: context,
      viewport: viewport,
    };

    await page.render(renderContext).promise;
  };

  const goToPreviousPage = () => {
    if (currentPage > 1) {
      setCurrentPage((prevPage) => prevPage - 1);
    }
  };

  const goToNextPage = () => {
    if (currentPage < totalPages) {
      setCurrentPage((prevPage) => prevPage + 1);
    }
  };

  useEffect(() => {
    if (pdf) {
      renderPage(pdf, currentPage);
    }
  }, [pdf, currentPage]);

  return (
    <div className={`${className || ''} rcl--relative`}>
      <canvas
        ref={canvasRef}
        className={`${className || ''} rcl--relative`}
      />
      <div className="rcl--absolute rcl--bottom-4 rcl--right-4 rcl--flex rcl--items-center rcl--rounded-md rcl--bg-black rcl--bg-opacity-50 rcl--px-4 rcl--py-1">
        <Button
          onClick={goToPreviousPage}
          disabled={currentPage === 1}
        >
          <ArrowLeft />
        </Button>
        <span className="rcl--mx-2 rcl--select-none rcl--text-sm rcl--text-white">{`${currentPage} of ${totalPages}`}</span>
        <Button
          onClick={goToNextPage}
          disabled={currentPage === totalPages}
        >
          <ArrowRight />
        </Button>
      </div>
    </div>
  );
};

export default PDFViewer;