diegomura / react-pdf

📄 Create PDF files using React
https://react-pdf.org
MIT License
14.94k stars 1.18k forks source link

Add zoom property to PDFViewer #1352

Open cefn opened 3 years ago

cefn commented 3 years ago

In my CV renderer at https://cefn.com/cv I wanted to be able to set the zoom level on loading so people can see a bit more of the document at once. It makes the interactive controls a bit more interesting.

I was expecting to be able to pass a zoom string and have that passed to the anchor in the iframe like...

src="blob:http://localhost:3000/06813706-6d70-4381-8ac4-af1d17363b70#zoom=50"

Since the support wasn't built-in, I added the feature to my project by writing a minimal Typescript clone of the PDFViewer component from the @react-pdf/renderer module.

The source code below does everything from the original, but also handles this zoom parameter. It successfully controlled the zoom level in at least Chrome and probably Firefox from what I understand of this anchor behaviour. Copying the code below into their project might be a useful workaround for people who want this behaviour in the short term and provides a proof of concept for adding it as a feature.

In my example I use it like...

<PDFZoomViewer style={{ height: "100%", width: "100%" }} zoom="FitH">

...but you can pass "50" as a percentage and maybe some other values.

import React, {
  CSSProperties,
  JSXElementConstructor,
  ReactElement,
  useEffect
} from "react";
import { usePDF } from "@react-pdf/renderer";

import { PDFVersion } from "@react-pdf/types";

interface OnRenderProps {
  blob?: Blob;
}

interface DocumentProps {
  title?: string;
  author?: string;
  subject?: string;
  creator?: string;
  keywords?: string;
  producer?: string;
  language?: string;
  pdfVersion?: PDFVersion;
  onRender?: (props: OnRenderProps) => any;
}

interface PDFViewerProps {
  title?: string;
  width?: number | string;
  height?: number | string;
  style?: CSSProperties;
  className?: string;
  children?: React.ReactElement<DocumentProps>;
  innerRef?: React.Ref<HTMLIFrameElement>;
  zoom?: string;
}

export const PDFZoomViewer = ({
  title,
  style,
  className,
  children,
  innerRef,
  zoom,
  ...props
}: PDFViewerProps) => {
  const [instance, updateInstance] = usePDF({
    document: children as ReactElement<
      DocumentProps,
      string | JSXElementConstructor<any>
    >
  });

  useEffect(updateInstance, [children]);

  return (
    <iframe
      title={title}
      ref={innerRef}
      style={style}
      src={
        instance.url
          ? `${instance.url}${zoom ? `#zoom=${zoom}` : ""}`
          : undefined
      }
      className={className}
      {...props}
    />
  );
};
diegomura commented 3 years ago

Thanks @cefn ! Cool app! I wasn't aware you can pass those arguments to the iframe url. I'm onboard of adding this feature. Will try to tackle when I have time. It's also a good cool first ticket if anyone is interested on contributing to the project

kidroca commented 3 years ago

Keep in mind not all of the native (browser) pdf viewer support all the # parameters. Does this work in Firefox?

Also couldn't the PdfViewer component support a url prop - so you can use the usePDF hook to render a download link and the preview. The you can also append any of the # options to the url

akanshsaxena commented 3 years ago

Can I take this up?

cefn commented 3 years ago

I'm not expecting to do more on it now so go ahead! My suggestion for a clean API is to provide an anchor prop, which ends up suffixed after # and if people want to pass zoom=50 or any other value that's up to them and if the behaviour varies across browsers how they respond to different anchors it's more explicit what you have to investigate or consider.

cefn commented 3 years ago

Actually maybe fragment corresponds better to the standards than anchor ... https://en.m.wikipedia.org/wiki/URI_fragment

MaciasMx commented 1 year ago

Thanks for the solution 🤘

Scooter1337 commented 6 days ago

I patched the package myself to add a prop called PdfParams. Did it in 5 minutes, can't be that hard to add it to the component right?