Search code examples
reactjsreact-nativereact-native-webviewreact-pdf

React PDF not rendering PDF in IOS 15.5


I am using react-pdf to render a pdf in a react application which is then rendered via WebView in ios and android.

The pdf render works in android.

In ios, I am getting this error Total canvas memory use exceeds the maximum limit (224 MB).

           <Document
                file={pdfBlob}
                onLoadSuccess={onDocumentLoadSuccess}
                renderMode="canvas"
                loading={<AppLoader />}
              >
                <Page
                  object-fit="fill"
                  pageNumber={currentPage}
                  width={isMobile && 350}
                  loading={<AppLoader />}
                  onRenderSuccess={() => {
                    setPdfRenderLoading(false);
                  }}
                  renderTextLayer={false}
                />
                <IconWrapper isMobile={isMobile}>
                  <IconButton
                    disabled={!canGoToPrev()}
                    colorScheme="blue"
                    aria-label="go-previous"
                    icon={<ChevronLeftIcon fontSize={fontSizes.md} />}
                    backgroundColor={appColors.brandGrey['50']}
                    color={appColors.brandGrey['900']}
                    mr={2}
                    onClick={handlePrevPage}
                  />
                  <AppText title={`${currentPage} of ${totalPages}`} />
                  <IconButton
                    disabled={!canGoToNext()}
                    colorScheme="blue"
                    aria-label="go-next"
                    icon={<ChevronRightIcon fontSize={fontSizes.md} />}
                    backgroundColor={appColors.brandGrey['50']}
                    color={appColors.brandGrey['900']}
                    ml={2}
                    onClick={handleNextPage}
                  />
                </IconWrapper>
              </Document>

I am fetching the pdf from s3 and storing it as a blob to prevent fetching everytime the page rerenders

My dependencies

"dependencies": {
    "@chakra-ui/icons": "^1.1.7",
    "@chakra-ui/react": "^1.8.8",
    "@emotion/react": "^11",
    "@emotion/styled": "^11",
    "@fontsource/nunito": "^4.5.8",
    "@testing-library/jest-dom": "^5.16.4",
    "@testing-library/react": "^13.1.1",
    "@testing-library/user-event": "^13.5.0",
    "axios": "^0.27.2",
    "chakra-react-select": "^3.3.1",
    "eslint-config-prettier": "^8.5.0",
    "eslint-plugin-prettier": "^4.0.0",
    "eslint-plugin-simple-import-sort": "^7.0.0",
    "eslint-plugin-unused-imports": "^2.0.0",
    "framer-motion": "^6",
    "prettier": "^2.6.2",
    "react": "^18.1.0",
    "react-dom": "^18.1.0",
    "react-pdf": "^5.7.2",
    "react-query": "^3.38.1",
    "react-router-dom": "^6.3.0",
    "react-scripts": "5.0.1",
    "use-debounce": "^8.0.1",
    "web-vitals": "^2.1.4",
    "zustand": "^4.0.0-rc.1"
  }

The entire block is displayed inside a webview using react-native-webview

The pdf starts to render, but get stucks and then the canvas error pops up, is there a fix for this in react-pdf


Solution

  • IOS prevents painting the canvas for such memory."Total canvas memory use exceeds the maximum limit (384 MB)." Hence React PDF is unable to paint the image.

    A quick fix would be to change renderMode to svg which just renders the SVG.

    SVG takes a bit more time to load than the Canvas. As the Canvas can be viewed to the user and then be painted / rendered, Whereas the SVG is completely rendered before the user can view.

    This can be a deal breaker for large PDFs or PDFs with image rich content as rendering SVGs will take a longer time to render

               <Document
                    file={pdfBlob}
                    onLoadSuccess={onDocumentLoadSuccess}
                    renderMode="svg" // Changed
                    loading={<AppLoader />}
                  >
                    <Page
                      object-fit="fill"
                      pageNumber={currentPage}
                      width={isMobile && 350}
                      loading={<AppLoader />}
                      onRenderSuccess={() => {
                        setPdfRenderLoading(false);
                      }}
                      renderTextLayer={false}
                    />
                    <IconWrapper isMobile={isMobile}>
                      <IconButton
                        disabled={!canGoToPrev()}
                        colorScheme="blue"
                        aria-label="go-previous"
                        icon={<ChevronLeftIcon fontSize={fontSizes.md} />}
                        backgroundColor={appColors.brandGrey['50']}
                        color={appColors.brandGrey['900']}
                        mr={2}
                        onClick={handlePrevPage}
                      />
                      <AppText title={`${currentPage} of ${totalPages}`} />
                      <IconButton
                        disabled={!canGoToNext()}
                        colorScheme="blue"
                        aria-label="go-next"
                        icon={<ChevronRightIcon fontSize={fontSizes.md} />}
                        backgroundColor={appColors.brandGrey['50']}
                        color={appColors.brandGrey['900']}
                        ml={2}
                        onClick={handleNextPage}
                      />
                    </IconWrapper>
                  </Document>