21

I want to open local (SD card) PDF file in a WebView.

I already tried this:

webview = (WebView) findViewById(R.id.webview);
webview.getSettings().setJavaScriptEnabled(true);
webview.getSettings().setPluginsEnabled(true);
webview.getSettings().setAllowFileAccess(true);
File file = new File(Environment.getExternalStorageDirectory() + "/test.pdf");

final Uri uri = Uri.fromFile(file);

webview.loadUrl(uri.toString());

But it's still not opening it, so let me know how I can open a PDF in WebView?

Floern
  • 33,559
  • 24
  • 104
  • 119
Girish Patel
  • 1,270
  • 4
  • 16
  • 30

9 Answers9

29

I know, this question is old.

But I really like the approach of Xamarin to make use of the pdf.js from Mozilla. It works on older Android versions, you don't need a special PDF Viewer app for this and you can easily display a PDF inside of your apps views hierarchy.

Git for this: https://mozilla.github.io/pdf.js/

Additional default options (like standard zoom): https://github.com/mozilla/pdf.js/wiki/Viewer-options

Just add the pdfjs files to your Assets directory:

enter image description here

And call it the following way:

// Assuming you got your pdf file:
File file = new File(Environment.getExternalStorageDirectory() + "/test.pdf");

webview = (WebView) findViewById(R.id.webview);
WebSettings settings = webview.getSettings();
settings.setJavaScriptEnabled(true);
settings.setAllowFileAccessFromFileURLs(true);
settings.setAllowUniversalAccessFromFileURLs(true);
settings.setBuiltInZoomControls(true);
webview.setWebChromeClient(new WebChromeClient());
webview.loadUrl("file:///android_asset/pdfjs/web/viewer.html?file=" + file.getAbsolutePath() + "#zoom=page-width");

Cool thing: If you want to reduce the amount of functionalities / controls. Go to the Assets/pdfjs/web/viewer.html file and mark certain controls as hidden. With

style="display: none;"

E.g. If you don't like the right toolbar:

<div id="toolbarViewerRight" style="display: none;">...</div>

Update

'URL scheme "file" is not supported'

Might occur for newer versions of pdfjs. With version 1.8.188 this error does not appear.

Lepidopteron
  • 6,056
  • 5
  • 41
  • 53
  • Awesome answer :) – Ely Dantas Apr 21 '17 at 17:02
  • Can pdf inside Android Project assets folder be opnened? – CGR Jun 16 '17 at 00:10
  • @CGR You will need a file‘s reference, which also needs to be available for the script, so maybe you need to copy the file locally from the assets folder first: https://stackoverflow.com/a/38504987 – Lepidopteron Jun 16 '17 at 04:57
  • 2
    Lepidopteron thanks a lot, it worked great. I have pdf in assets, I copied it in private folder inside my app and opened it from there - no problems, no permissions mess. Used latest pdfjs 1.8.188 – Boris Gafurov Oct 19 '17 at 15:41
  • @BorisGafurov Glad it helped you! :-) – Lepidopteron Oct 19 '17 at 15:42
  • @Pihu Yes, it will – Lepidopteron Dec 12 '17 at 10:33
  • But i am unable to run this on Nougat Devices – Pihu Dec 12 '17 at 10:59
  • Yeah, it runs finally now.. But it is very slow. – Pihu Dec 12 '17 at 11:20
  • pdf.js zip file is around 40MB....I have to increase my app size by this much amout? – pallav bohara Mar 21 '18 at 11:40
  • @pallavbohara the apk zips the files once again, so it will not increase the apk size of 40MB, but rather a smaller value. I did not validate the impact on the apk's size, tho. – Lepidopteron Mar 22 '18 at 12:14
  • @NiyasNazar it should, so far I did not stumble over an API, which did not work anymore. If you do, please do edit my answer and add your observations made. – Lepidopteron Nov 16 '18 at 07:30
  • @Lepidopteron How would you make it work that way, while on the internet you can find so many threads about pdf.js not supporting the `file:///` fetching? Exact err message is 'URL scheme "file" is not supported' and you can find lots of threads about it. How does this work for you? – Android developer Jun 11 '19 at 08:39
  • @RadekKłos this approach renders the PDF with the framework - this requires some work if you‘d like to implement it yourself. Whilst the „file:///„ approach would make use of the system rendering. – Lepidopteron Jun 11 '19 at 14:28
  • 1
    @RadekKłos I was wondering the same and so I tried an older version of pdfjs, 1.8.188 version, mentioned in a comment above. And it worked. Instead it doesn't work with the latest version 2.1.266. So the URL scheme "file" not supported is a recent change. – DSoldo Sep 06 '19 at 14:53
  • @Lepidopteron - I'm hoping you or someone is still keeping an eye on this. I'm still having issues getting this to work in a native Android app. Using 1.8.188 version. Initially I was getting issues about SystemJS not being defined, however I npm install systemjs to the pdfjs package in my Android project. However now I'm getitng a different error: Failed to load file:///android_asset/pdfjs/web/app.js: Cross origin requests are only supported for protocol schemes: http, data, chrome, https.", source: file:///android_asset/pdfjs/node_modules/systemjs/dist/system.js (4) – niall8s Jul 07 '20 at 14:04
  • Ah now this issue is back to the unsupported file protocol scheme regarding trying to access the l10n.js stuff for localisation. Did anyone exclude this somehow? – niall8s Jul 07 '20 at 16:37
5

As @Sameer replied in your comment above, the only solution to view PDF in webview is through Google Docs' online viewer which will render and send back a readable version to your app.

Previously discussed here

Community
  • 1
  • 1
James Wong
  • 4,529
  • 4
  • 48
  • 65
  • @androidsanta nop, it was to be loaded from web. – James Wong Apr 30 '14 at 04:09
  • This work only with a remote PDF. If you want to open a local file like @girishce26, you have to use the CSmith's solution or this external plugin https://github.com/JoanZapata/android-pdfview – tryp Apr 13 '16 at 12:05
3

After going through several posts I came across this simple answer on Quora which pretty much do the work. Following are steps:-

Add this dependency in your gradle file:

compile 'com.github.barteksc:android-pdf-viewer:2.0.3'

activity_main.xml

 <com.github.barteksc.pdfviewer.PDFView
    android:id="@+id/pdfView"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
     />

MainActivity.java

package pdfviewer.pdfviewer;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import com.github.barteksc.pdfviewer.PDFView;
import com.github.barteksc.pdfviewer.listener.OnLoadCompleteListener;
import com.github.barteksc.pdfviewer.listener.OnPageChangeListener;
import com.github.barteksc.pdfviewer.scroll.DefaultScrollHandle;
import com.shockwave.pdfium.PdfDocument;

import java.util.List;

public class MainActivity extends Activity implements OnPageChangeListener,OnLoadCompleteListener{
    private static final String TAG = MainActivity.class.getSimpleName();
    public static final String SAMPLE_FILE = "sample_pdf.pdf";
    PDFView pdfView;
    Integer pageNumber = 0;
    String pdfFileName;

    @Override    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);


        pdfView= (PDFView)findViewById(R.id.pdfView);
        displayFromAsset(SAMPLE_FILE);
    }

    private void displayFromAsset(String assetFileName) {
        pdfFileName = assetFileName;

        pdfView.fromAsset(SAMPLE_FILE)
                .defaultPage(pageNumber)
                .enableSwipe(true)

                .swipeHorizontal(false)
                .onPageChange(this)
                .enableAnnotationRendering(true)
                .onLoad(this)
                .scrollHandle(new DefaultScrollHandle(this))
                .load();
    }


    @Override    public void onPageChanged(int page, int pageCount) {
        pageNumber = page;
        setTitle(String.format("%s %s / %s", pdfFileName, page + 1, pageCount));
    }


    @Override    public void loadComplete(int nbPages) {
        PdfDocument.Meta meta = pdfView.getDocumentMeta();
        printBookmarksTree(pdfView.getTableOfContents(), "-");

    }

    public void printBookmarksTree(List<PdfDocument.Bookmark> tree, String sep) {
        for (PdfDocument.Bookmark b : tree) {

            Log.e(TAG, String.format("%s %s, p %d", sep, b.getTitle(), b.getPageIdx()));

            if (b.hasChildren()) {
                printBookmarksTree(b.getChildren(), sep + "-");
            }
        }
    }

}

You have to make sure that your asset folder contains sample_pdf.pdf (Library also support opening pdf from Uri and SDCard)

enter image description here

Happy coding :)

pallav bohara
  • 6,199
  • 6
  • 24
  • 45
2

You cannot. Using an Intent, you can open the PDF in an external viewer application like Acrobat Reader:

try
{
 Intent intentUrl = new Intent(Intent.ACTION_VIEW);
 intentUrl.setDataAndType(uri, "application/pdf");
 intentUrl.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
 mActivity.startActivity(intentUrl);
}
catch (ActivityNotFoundException e)
{
 Toast.makeText(mActivity, "No PDF Viewer Installed", Toast.LENGTH_LONG).show();
}
CSmith
  • 13,318
  • 3
  • 39
  • 42
1

From android OS 5.0(lollipop) on-wards you can use PdfRenderer instead of webview/library. You can use this class to show pdf's within the app.

If you want to support OS lower than that you can use a library/other approach mentioned in other answer as there is no native support.

Read more about it from the docs, you can also refer this example

Aniruddha K.M
  • 7,361
  • 3
  • 43
  • 52
1

WebView don't provide direct functionality to open PDF from local storage. You can open pdf in external PDFViewer applications.

Wait it will take user to different application.

Now without using any extra library to open a PDF we can use PdfRenderer class to render a PDF.

PdfRenderer can render pdf on bitmap. Bitmap can be drawn to Image View to show the PDF in your application.

// create a new renderer
 PdfRenderer renderer = new PdfRenderer(getSeekableFileDescriptor());

 // let us just render all pages
 final int pageCount = renderer.getPageCount();
 for (int i = 0; i < pageCount; i++) {
     Page page = renderer.openPage(i);

     // say we render for showing on the screen
     page.render(mBitmap, null, null, Page.RENDER_MODE_FOR_DISPLAY);

     // do stuff with the bitmap

     // close the page
     page.close();
 }

 // close the renderer
 renderer.close();

Read more about it from the docs.

  • While this link may answer the question, it is better to include the essential parts of the answer here and provide the link for reference. Link-only answers can become invalid if the linked page changes. Also, the same link already exists in a better-expanded answer. - [From Review](/review/late-answers/33395794) – Adinia Dec 16 '22 at 10:36
  • @Adinia, I will edit the answer with proper explanation. – Himanshu Sharma Dec 27 '22 at 04:38
0

you can use the SkewPdfView library for loading pdf from remote or local urls.

First of all, Add following in your root build.gradle:

allprojects {
    repositories {
        ...
        maven { url 'https://jitpack.io' }
    }
}

and add the SkewPdfView Library as:

dependencies {
    implementation 'com.github.naya-aastra:SkewPdfView:1.1'
}

Now Include SkewPdfView in your layout:

<com.nayaastra.skewpdfview.SkewPdfView
    android:id="@+id/skewPdfView"
    android:layout_width="match_parent"
    android:layout_height="match_parent"/>

Save all the local files in assets folder and then use SkewPdfView as follows. Load a PDF file programmatically as:

SkewPdfView skewPdfView;
skewPdfView = findViewById(R.id.skewPdfView);
String pdfLink = "LINK TO ASSET FILE";
skewPdfView.loadPdf(pdfLink);

P.S. link of SkewPdfView library

SkewPdfView Github Page

Boken
  • 4,825
  • 10
  • 32
  • 42
-1

WebView can easily be used for displaying PDF file from a web url but if you have local PDF file, then it becomes a pain.

In my case i first stored a reference of my local file:-

File file = new File(getExternalFilesDir(null),"your_pdf_file.pdf");

Then i obtained file path URI of my local PDF file using FileProvider and started an intent for opening it using existing applications that can open PDF documents:-

try{
    Intent intent = new Intent(Intent.ACTION_VIEW);
    intent.setDataAndType(FileProvider.getUriForFile(BaseActivity.this,BuildConfig.APPLICATION_ID +".provider",file), "application/pdf");
    intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
    intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
    startActivity(intent);
    } catch (ActivityNotFoundException e) {
    Toast.makeText(BaseActivity.this,"No Application Available to View PDF",Toast.LENGTH_SHORT).show();
    }

Also to use FileProvider API, you need to declare it in manifest as:-

        <provider
            android:name="androidx.core.content.FileProvider"
            android:authorities="${applicationId}.provider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/file_paths" />
        </provider>

And declare file_paths.xml under XML resource folder as:-

<paths>
    <external-path name="external_files" path="."/>
</paths>
Rishabh
  • 1,827
  • 1
  • 12
  • 9
-2

WebView can not open a .pdf file. The most popular solution (google docs' url + your url in WebView) just shows you converted by google docs pictures. However there is still no simple way to open .pdf from url.

Grecha
  • 119
  • 1
  • 11