Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

"Canvas area exceeds the maximum limit (width * height > 16777216)." on iOS #1149

Closed
4 tasks done
thibauld opened this issue Nov 9, 2022 · 11 comments
Closed
4 tasks done
Labels
question Further information is requested

Comments

@thibauld
Copy link

thibauld commented Nov 9, 2022

Before you start - checklist

  • I followed instructions in documentation written for my React-PDF version
  • I have checked if this bug is not already reported
  • I have checked if an issue is not listed in Known issues
  • If I have a problem with PDF rendering, I checked if my PDF renders properly in PDF.js demo

Description

On iOS mobile + Safari (bug has been reported on iPhone 12, 13, 14), it can happen that the PDF does not display at all.
We tracked down the issue and here is the reason:

Safari simply cannot draw large canvas elements. The limit is set at 16.777.216 pixels. Create a canvas with more pixels and iOS Safari will tell us it exceeds the memory limit.
When we open this page on iOS Safari, it’ll show the following warning in the developer console.

More information about the issue: https://pqina.nl/blog/canvas-area-exceeds-the-maximum-limit/

Steps to reproduce

Here is some dummy code to produce the error:

const canvas = document.createElement('canvas');
canvas.width = 4097;
canvas.height = 4096;

const ctx = canvas.getContext('2d');
ctx.fillRect(0, 0, 100, 100);

Here is our actual code:

import React, { useState } from "react";
import PropTypes from "prop-types";
import { Document, Page } from "react-pdf/dist/esm/entry.webpack";
import "react-pdf/dist/esm/Page/AnnotationLayer.css";

const PDFViewer = ({ file, renderRightNav, renderLeftNav }) => {
  const [pages, setPages] = useState(null);
  const [pageNumber, setPageNumber] = useState(1);

  const onDocumentLoadSuccess = ({ numPages }) => {
    setPages(numPages);
  };

  const handleNext = () => {
    setPageNumber(pageNumber + 1);
  };

  const handlePrev = () => {
    setPageNumber(pageNumber - 1);
  };

  return (
    <div className="pdf-viewer">
      <Document file={file} onLoadSuccess={onDocumentLoadSuccess}>
        <Page pageNumber={pageNumber} />
      </Document>
      <p className="page-number">Page {pageNumber} of {pages}</p>
      {renderRightNav(handleNext, pageNumber === pages)}
      {renderLeftNav(handlePrev, pageNumber === 1)}
    </div>
  );
};

PDFViewer.propTypes = {
  file           : PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
  renderRightNav : PropTypes.func,
  renderLeftNav  : PropTypes.func
};

export default PDFViewer;

Expected behavior

The PDF should display fine. Like this:
image

Actual behavior

PDF doesn't display and we're getting the following error in the console:
image

image

Attaching the incriminated PDF that doesn't display on Safari / iOS:
Trashless Business Plan 10-28-22.pdf

Additional information

No response

Environment

  • Browser (if applicable): Safari on iOS
  • React-PDF version: v6
  • React version: 18
  • Webpack version (if applicable):
@thibauld thibauld added the bug Something isn't working label Nov 9, 2022
@wojtekmaj
Copy link
Owner

I don't see what I could do about this. I could only... Add another warning?

@laura-23p
Copy link

@wojtekmaj could you set the canvas on iOS Safari to be of max 16.777.216 pixels? So that the limit is never exceeded

@wojtekmaj wojtekmaj changed the title "Canvas Area Exceeds The Maximum Limit" error "Canvas area exceeds the maximum limit (width * height > 16777216)." on iOS Nov 11, 2022
@gitname
Copy link

gitname commented Nov 16, 2022

There's an NPM package that people can use to detect the maximum canvas size supported by the web browser:

Users of react-pdf may be able to use that package to influence the size-related props they pass to the <Page> element, thereby influencing the size of the underlying <canvas> element.


I have never used it, myself.

In my case, I was running into issues on iOS when trying to render a PDF using this element:

<Page width={8.5 * 300} /* ... */ /> // in which case, the height becomes 11 * 300, since the pages are Letter-sized

As a temporary workaround, I am using the following "smaller" element, which works on iOS:

<Page width={8.5 * 72} /* ... */ /> // in which case, the height becomes 11 * 72, since the pages are Letter-sized

I plan to employ the above package in the future, but haven't gotten around to it yet (due to some higher priorities).

Edit: Changed Document to Page (was a typo in original message).

@wojtekmaj
Copy link
Owner

wojtekmaj commented Nov 22, 2022

In 45a19b6, I've added a way to override devicePixelRatio canvas is rendering with. By default, it matches screen device pixel ratio, so for example 3 on most iPhones, 1 on standard resolution office monitor.

By setting this value manually we can lower the resolution of the canvas rendered, vastly reducing the number of pixels rendered, without changing actual physical canvas size.

For example, sample document on an iPhone renders 4,508,910 pixels, setting devicePixelRatio to 2 more than halves it to 2,003,960 pixels, and lowering it all the way to 1, while quite making things quite blurry, renders only 500,990 pixels.

Bonus tip: you could cap devicePixelRatio by passing e.g. Math.min(2, window.devicePixelRatio), preventing obscenely large pixel density, while maintaining good looks on most devices.

This might help you out!

@wojtekmaj wojtekmaj added question Further information is requested and removed bug Something isn't working labels Nov 22, 2022
@laura-23p
Copy link

@wojtekmaj thank you! We will try this and let you know.

@github-actions
Copy link
Contributor

This issue is stale because it has been open 90 days with no activity. Remove stale label or comment or this issue will be closed in 14 days.

@github-actions github-actions bot added the stale label Feb 27, 2023
@github-actions
Copy link
Contributor

This issue was closed because it has been stalled for 14 days with no activity.

@wojtekmaj
Copy link
Owner

Just a heads up: there's also maxImageSize option you can pass to options that might be helpful, but I didn't experiment with it a lot.

@focusaway
Copy link

If anyone is still having problems, I managed to solve it by changing the renderMode to "svg"

@wojtekmaj
Copy link
Owner

@focusaway SVG render mode is deprecated, and will be removed in a future version.

@sheilatavares
Copy link

For example, sample document on an iPhone renders 4,508,910 pixels, setting devicePixelRatio to 2 more than halves it to 2,003,960 pixels, and lowering it all the way to 1, while quite making things quite blurry, renders only 500,990 pixels.

I had been trying to find a solution to this for many days, and I succeeded thanks to your tip. Thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

6 participants