npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@noxan/react-pdf-highlighter-extended

v8.1.0

Published

Set of modern React components for PDF highlighting

Downloads

79

Readme

react-pdf-highlighter-extended

Node.js CI npm version

react-pdf-highlighter-extended is a React library that provides a highly customisable annotation experience for PDF documents on the web, with text and rectangular highlights both supported. It leverages PDF.js as its viewer. The highlight data format is also independent of the viewport, making it suitable for saving on a server.

This originally started as a fork of react-pdf-highlighter but so much has been refactored and redesigned that it would be a burden to pull it to the original repo. Some of these changes include: addition of HighlightContext, PdfHighlighterContext, and MonitoredHighlightContainer; zoom support; exposed styling on all components; and numerous bugfixes. Efforts will be made to try to ensure feature parity with the original repo, but there are no guarantees that syntax and usage will be the same.

Table of Contents

Documentation

If you just want to use this library, you can find comprehensive docs for all aspects by visiting the official documentation page.

If you wish to contribute, most internal components are documented in code, though not to the same depth.

Example

For a live demo check https://danielarnould.github.io/react-pdf-highlighter-extended/example-app/.

To run the example app locally:

git clone https://github.com/DanielArnould/react-pdf-highlighter-extended.git
npm install
npm run dev

Installation

npm install react-pdf-highlighter-extended --save

Usage

Here are some simple usage examples of this library to help you get started with your application. Please note that these examples and explanations are not exhaustive and many additional props are not shown. To see more extensive usage, have a look at the example app or refer to the documentation.

PdfLoader

The PdfLoader creates a container to load PDF documents with PDF.js.

Example loading document with string

const url = "https://arxiv.org/pdf/1708.08021.pdf";

<PdfLoader document={url}>
  {(pdfDocument) => (/* PdfHighlighter component goes here */)}
</PdfLoader>;

Example loading document with DocumentInitParameters

You can also pass any extra parameters to the PdfLoader document that are accepted by PDF.js. For example, you could use this to specify HTTP headers for retrieving your document.

const pdfDocument: Partial<DocumentInitParameters> = {
  url: "https://arxiv.org/pdf/1708.08021.pdf",
  httpHeaders: {
      "Authorization": "Bearer JWT_TOKEN_HERE"
  },
  password: "PDF_PASSWORD_HERE"
}

<PdfLoader document={pdfDocument}>
  {(pdfDocument) => (/* PdfHighlighter component goes here */)}
</PdfLoader>

PdfHighlighter

The PdfHighlighter provides a PDF.js viewer along with various helpful event listeners and niceties for creating a fully-fledged and robust highlighter. It does NOT render any highlights on its own. Instead, it expects a user-defined Highlight Container as its child, which will be rendered and given context for each individual highlights. Please also note for styling that the PDF.js viewer renders its pages with absolute positioning.

Example

const myPdfHighlighter = () => {
  const [highlights, setHighlights] = useState<Array<Highlight>>([]);

  /** Refs for PdfHighlighter utilities
   * These contain numerous helpful functions, such as scrollToHighlight,
   * getCurrentSelection, setTip, and many more
   */
  const highlighterUtilsRef = useRef<PdfHighlighterUtils>();

return (
  <PdfLoader document={url}>
    {(pdfDocument) => (
      <PdfHighlighter
        enableAreaSelection={(event) => event.altKey}
        pdfDocument={pdfDocument}
        utilsRef={(_pdfHighlighterUtils) => {
          highlighterUtilsRef.current = _pdfHighlighterUtils;
        }}
        selectionTip={<ExpandableTip />} // Component will render as a tip upon any selection
        highlights={highlights}
      >
        {/* User-defined HighlightContainer component goes here */}
      </PdfHighlighter>
    )}
  </PdfLoader>
);
}

User-defined HighlightContainer

You must create your own Highlight Container which will be rendered as needed for each highlight inside the PdfHighlighter. This container will receive the context it needs through the useHighlightContainerContext hook. Additionally, you can access numerous useful utility functions through the usePdfHighlighterContext hook. This library also provides two ready-to-use componenets, TextHighlight and AreaHighlight, which you can place inside your container to easily render some standard highlight styles.

Example

interface MyHighlightContainerProps {
  editHighlight: (idToUpdate: string, edit: Partial<Highlight>) => void; // This could update highlights in the parent
}

const MyHighlightContainer = ({
  editHighlight,
}: MyHighlightContainerProps) => {

  const {
    highlight, // The highlight being rendred
    viewportToScaled, // Convert a highlight position to platform agnostic coords (useful for saving edits)
    screenshot, // Screenshot a bounding rectangle
    isScrolledTo, // Whether the highlight has been auto-scrolled to
    highlightBindings, // Whether the highlight has been auto-scrolled to
  } = useHighlightContainerContext();

  const {
    currentTip,
    setTip,
    toggleEditInProgress,
    isEditInProgress
  } = useTipViewerUtils();

  const { toggleEditInProgress } =
    usePdfHighlighterContext();

  const isTextHighlight = !Boolean(
    highlight.content && highlight.content.image
  );

  const component = isTextHighlight ? (
    <TextHighlight
      isScrolledTo={isScrolledTo}
      highlight={highlight}
    />
  ) : (
    <AreaHighlight
      isScrolledTo={isScrolledTo}
      highlight={highlight}
      onChange={(boundingRect) => {
        const edit = {
          position: {
            boundingRect: viewportToScaled(boundingRect),
            rects: [],
          },
          content: {
            image: screenshot(boundingRect),
          },
        };

        editHighlight(highlight.id, edit);
        toggleEditInProgress(false);
      }}
      bounds={highlightBindings.textLayer}
      onEditStart={() => toggleEditInProgress(true)}
    />
  );

  return (component);
};

Example with categories and custom highlights

The power of a user-defined highlight container is that you can customise your highlight rendering as much as you want. For example, here is how you could extend your application to support highlights with categories.

export interface MyCustomHighlight extends Highlight {
    category: string
}

You could then use this in your HighlightContainer to render highlights with different colors depending on their category.

// Same logic as above examples

  const {
    highlight,
    viewportToScaled,
    screenshot,
    isScrolledTo,
    highlightBindings,
  } = useHighlightContainerContext<MyCustomHighlight>();

const category = highlight.category;
let highlightColor = "rgba(199,227,114,1)";

if (category === "red") {
  highlightColor = "rgba(239,90,104,1)";
} else if (category === "blue") {
  highlightColor = "rgba(154,208,220,1)";
}

const component = isTextHighlight ? (
  <TextHighlight highlight={highlight} style={{ background: highlightColor }} />
) : (
  <AreaHighlight
    highlight={highlight}
    style={{
      background: highlightColor,
    }}
  />
);

// Same return as above examples

Example with comments and MonitoredHighlightContainer

Very often you might want to display a popup, tip, or comment if the user hovers over a highlight. To facilitate this, this library offers a MonitoredHighlightContainer, which you can wrap around your rendered highlight to create a mouse listener both over the highlight and any popup content you might display above it. Combining this with custom highlights allows you to associate all sorts of displayable information with your highlights.

export interface MyCustomHighlight extends Highlight {
    comment?: string
}

const MyHighlightPopup = (highlight: ViewportHighlight<MyCustomHighlight>) => {
  return highlight.comment ? (
    <div>{highlight.comment}</div>
  ) : (
    <div>Highlight has no comment</div>
  );
};

const MyHighlightContainer = ({
  editHighlight,
}: MyHighlightContainerProps) => {

  // Same hooks as above example
  // Same logic as above example

  const highlightTip: Tip = {
    position: highlight.position,
    content: <HighlightPopup highlight={highlight} />
  };

  return (
    <MonitoredHighlightContainer
      highlightTip={highlightTip}
      key={highlight.id}
      children={component}
    />
  );
};

Tips

At any point you can use getTip() and setTip() from the usePdfHighlighterContext() hook or the utilsRef property on PdfHighlighter. To set a tip, simply provide the position of a highlight, ghost highlight, or selection and the component you wish to render. This will automatically place your tip in the middle and slightly above your given highlight (or below if necessary). However, since the PdfHighlighter is not aware of the state of your tip, you must tell it to update its position if your tip ever changes size and you want it to remain above/below your given highlight. Fortunately, you can do this quite easily with updateTipPosition() within a PdfHighlighterContext.

Example

const MyExpandableTip = () => {
  const [compact, setCompact] = useState(true);

  const { getCurrentSelection, updateTipPosition } =
    usePdfHighlighterContext();

  useLayoutEffect(() => {
    updateTipPosition!();
  }, [compact]);

  return (
    <div className="Tip">
      {compact ? (
        <button
          onClick={() => {
            setCompact(false);
            if (getCurrentSelection()) {
              getCurrentSelection().makeGhostHighlight();
            }
          }}
        >
          Expand Tip
        </button>
      ) : (
        <div style={{ padding: "50px" }}>Expanded content</div>
      )}
    </div>
  );
};

Contribute

If you have a bug to report, please add it as an issue with clear steps to reproduce it.

If you have a feature request, please add it as an issue or make a pull request. If you do wish to make a pull request, consider checking whether your feature has already been implemented or tested in the original react-pdf-highlighter.