31

Can anyone please provide sample code for displaying a PDF in React Native? iOS and Android.

This is what I've tried:

  render: function() {
    return <WebView
      source={require('./my.pdf')}
      />
  }

^ This results in a Red Screen of Death with the "Unable to resolve module" error.

  render: function() {
    return <WebView
      source={{uri: 'my.pdf'}}
      />
  }

^ This gives an "Error Loading Page" message in the WebView. "The requested URL was not found on this server"

I know iOS is capable of showing a PDF in a UIWebView. I assume Android can do the same. There must be something in how the source is specified that I'm missing.

Axeva
  • 4,697
  • 6
  • 40
  • 64
  • I presume you've found this already, which is an android only solution https://github.com/cnjon/react-native-pdf-view though it appears they're working on iOS. They've bridged to native for their solution. You may gain insight if you took the time to dig in. I'd love to hear whether it's possible to load pdfs locally into webview though. – Chris Geirman Mar 09 '16 at 19:37
  • @ChrisGeirman I've seen that. I'm a native iOS developer by trade, so I could setup a bridge there as well. I know I could make it work that way. Just hoping I don't need to jump thru those hoops. – Axeva Mar 09 '16 at 19:40
  • I hear you. If you do, perhaps consider contributing to the above package so the next guy (maybe me!!) won't have to :) – Chris Geirman Mar 09 '16 at 19:46
  • **Android solution:** [Link to detailed solution](http://stackoverflow.com/questions/38662309/how-to-save-pdf-to-android-file-system-and-then-view-pdf-react-native/38804845#38804845). Enjoy! – Larney Aug 07 '16 at 12:37

5 Answers5

37

Okay, for future generations, here's how I solved this problem:

Updated September 13, 2017:

There is a new NPM module that makes this entire process much easier. I would suggest using it going forward instead of my original answer below:

react-native-pdf

Once installed, rendering the PDF is as easy as this:

export default class YourClass extends Component {
  constructor(props) {
    super(props);
    this.pdf = null;
  }

  render() {
    let yourPDFURI = {uri:'bundle-assets://pdf/YourPDF.pdf', cache: true};

    return <View style={{flex: 1}}>
      <Pdf ref={(pdf)=>{this.pdf = pdf;}}
        source={yourPDFURI}
        style={{flex: 1}}
        onError={(error)=>{console.log(error);}} />
    </View>
  }
}

Just put your actual pdf in the android/app/src/main/assets/pdf folder of your project.

Original Answer:

iOS

render: function() {
  return <WebView source={{uri: 'My.pdf'}}/>
}

The trick is that you need to include My.pdf into your project in Xcode and make sure it's added to your build target.

Just copying it into your React Native project folder wasn't enough. It had to be part of the Xcode project itself.

Android

It appears that Android did not provide a native PDF viewer until 5.0 (Lollipop). To get around this, I've had to make use of three key techniques:

  1. Pull the PDF out of my APK bundle and store it in the files folder for my app. This SO answer was very helpful in accomplishing this:

Android: How to copy files from 'assets' folder to sdcard?

I tweaked the code a bit so that the file wasn't going to an sdcard but to my app's files folder. Here's what I added to my MainActivity.java

@Override
protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);

  AssetManager assetManager = getAssets();
  String[] files = null;

  try {
      files = assetManager.list("pdf");
  } catch (IOException e) {
      Log.e("tag", "Failed to get asset file list.", e);
  }

  if (files != null) for (String filename : files) {
      InputStream in = null;
      OutputStream out = null;

      try {
        in = assetManager.open("pdf/" + filename);

        File outFile = new File(getFilesDir(), filename);
        out = new FileOutputStream(outFile);
        copyFile(in, out);
        Log.e("tag", "Copy was a success: " + outFile.getPath());
      } catch(IOException e) {
        Log.e("tag", "Failed to copy asset file: " + "pdf/" + filename, e);
      }
      finally {
          if (in != null) {
              try {
                  in.close();
              } catch (IOException e) {
                  // NOOP
              }
          }
          if (out != null) {
              try {
                  out.close();
              } catch (IOException e) {
                  // NOOP
              }
          }
      }
  }
}

private void copyFile(InputStream in, OutputStream out) throws IOException {
    byte[] buffer = new byte[1024];
    int read;
    while((read = in.read(buffer)) != -1){
      out.write(buffer, 0, read);
    }
}

I also made sure my PDF is in the assets/pdf folder under android/app/src/main

  1. I then utilized the react-native-fs package to get the absolute URL to my PDF, which is now in the files folder:

    var RNFS = require('react-native-fs');
    var absolutePath = RNFS.DocumentDirectoryPath + '/My.pdf';
    
  2. With all of this in place, I used react-native-pdf-view to actually load and display the PDF:

    import PDFView from 'react-native-pdf-view';
    
    render: function() {
      var absolutePath = RNFS.DocumentDirectoryPath + '/My.pdf';
    
      return <PDFView
        ref={(pdf)=>{this.pdfView = pdf;}}
        src={absolutePath}
        style={ActharStyles.fullCover} />
    }
    

Good luck!

Axeva
  • 4,697
  • 6
  • 40
  • 64
  • 1
    Thank you for sharing this, it helped, but I am facing issue that I am not able to scroll the pdf , did you face the issue ? – Bhumit Mehta Jul 28 '16 at 13:04
  • I did not, sorry. I would suggest opening a new Question with sample code and the community will try to help. Good luck! – Axeva Jul 28 '16 at 18:01
  • **Android solution:** [Link to detailed solution](http://stackoverflow.com/questions/38662309/how-to-save-pdf-to-android-file-system-and-then-view-pdf-react-native/38804845#38804845). Enjoy! – Larney Aug 07 '16 at 12:37
  • @Axeva do you have any idea how to support zoom? – zianwar Sep 06 '16 at 16:49
  • @zianwar on which platform? iOS or Android? – Axeva Sep 06 '16 at 17:00
  • @Axeva ideally both, but i need iOS now? – zianwar Sep 07 '16 at 10:11
  • Unfortunately, there is no text search in react-native-pdf library. – Oleksandr Prokhorenko Oct 09 '17 at 14:50
  • would have loved to use the react-native-pdf but I am new to React and React Native, and I can't run the react-native link commands due to Expo. Detaching the project from Expo seems like a huge deal and me and my team really would like to keep Expo. :( – Ghost Jun 04 '18 at 13:11
  • This solution made my expo project very complicated, I ended up using rn-pdf-reader-js instead. – Felipe Valdes Feb 15 '19 at 20:18
  • is there a way that i can disable horizontal and vertical swipe in react-native-pdf? i want to change page only when user presses the button. – Soban Arshad Jan 02 '20 at 18:05
18

A simple solution for this problem is to set <WebView> source/uri to this:

https://drive.google.com/viewerng/viewer?embedded=true&url={your pdf url}’
Michael Falck Wedelgård
  • 2,943
  • 1
  • 27
  • 39
  • 5
    Actually a better solution is to use this: https://github.com/mozilla/PDF.js/wiki/Viewer-options since it support jumps to page numbers. And as far as I know the google drive viewer has limitation on uses/day – Michael Falck Wedelgård Sep 16 '18 at 20:30
  • If I need to pass some headers to PDF url, how I can do it? – indapublic Oct 04 '18 at 06:02
  • 1
    Here's an example to display just a PDD: http://mozilla.github.io/pdf.js/web/viewer.html?file=compressed.tracemonkey-pldi-09.pdf Here's one displaying the same PDF but opens at page 3: http://mozilla.github.io/pdf.js/web/viewer.html?file=compressed.tracemonkey-pldi-09.pdf#page=3 – Michael Falck Wedelgård Oct 05 '18 at 07:06
2

The Amazon S3 Presigned URL contains query string contains "?" and "&" which confuses the outer URL.

So you have to Encode the S3 Presigned URL before passing it to the Google Doc Viewer.

Like this:

var encodedUrl = encodeURIComponent(presigned_url);

var iFrameUrl = 'https://docs.google.com/gview?url=' + encodedUrl;

1

Just add style={{flex:1}} in the opening View Tag of your return and you should be fine.

gmds
  • 19,325
  • 4
  • 32
  • 58
1
In case of pdf url this will do

openLink(url) {
    return Linking.canOpenURL(url)
      .then(supported => {
        if (!supported) {
          console.log("Can't handle url: " + url);
          this.showFlashMessage('Not supported in your device');
        } else {
          return Linking.openURL(url);
        }
      })
      .catch(err => console.error('An error occurred', err));
  }
ArkaneKhan
  • 379
  • 2
  • 12