Ionic, Angular and Android PDFs

Hi Guys,

So we’ve done weeks of research and experimenting but cannot get PDFs to display on an Android device, which is crucial to our app.

It works fine on iOS using the in app browser.

We’re storing PDF data as blob in a pouch/ webSQL. Ideally we want to display this in the App, but happy to open this externally if this is our only option.

The In App Browser on Android will not display PDFs. I don’t believe we can point users to the Google drive PDF renderer method due to the PDF data being a blob and not a file hosting on a URL.

We looked at PDF.js but again, I believe this isn’t supported yet in Angular 2.

Does anyone have experience of PDFs on Android with an Ionic 2 project - we really need some help!

Thank you.

Ben

1 Like

In the past I solved this situation downloading the pdf and then opening it with any installed pdf reader. But I needed to create a custom Cordova plugin (no source code available, sorry).

Another workaround is open an InAppBrowser window and use Google Docs:

http://docs.google.com/gview?embedded=true&url=YOUR_ENCODED_PDF_URL

1 Like

Thanks although we need this to work offline so the google docs option isn’t available to us. Happy to open in external app, any tips here?

You’ll need to create a plugin that gets a list of apps that can open a url or mime type. Something like this:

private void chooseAndOpen(String url, String mimeType, CallbackContext callbackContext) {
    Intent intentCheck = new Intent(Intent.ACTION_VIEW);
    if (mimeType.length() > 0) {
        intentCheck.setDataAndType(Uri.parse(url), mimeType);
    }
    else {
        intentCheck.setData(Uri.parse(url));
    }
    intentCheck.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    PackageManager packageManager = context.getPackageManager();
    List<ResolveInfo> appsList = packageManager.queryIntentActivities(intentCheck, 0);
    if (appsList.size() > 0) {
        Intent chooserIntent = Intent.createChooser(intentCheck, "Choose an app to open this file");
        this.cordova.startActivityForResult(this, chooserIntent, 100);
        callbackContext.success();
    }
    else {
        callbackContext.error("There are no installed apps that can open this file");
    }
}

Just to update - we’ve got this working. We’ve used PDF.js and our own custom viewer which is working nicely in both iOS and Android, displaying PDFs in app, without inAppBrowser or an external PDF viewer.

https://mozilla.github.io/pdf.js/

Thanks for the pointers.

3 Likes

Can you please share the steps how to import PDFJs in ionic2?

Be sure to checkout: https://mozilla.github.io/pdf.js/getting_started/

You can use it out the box. The main gotcha is to make sure you include the worker.js and import it before the ionic bundle.

We built our own, basic viewer.

Hope that helps!

Can you show some of your source code about how you import PDFJs in ionic2 typescript?

1 Like

Have u create PDF View in ionic 2

How to download and upload any type of file to remote server in ionic 2???
Can anybody help??

@fruitstudios
can you share with us a demo or steps to use pdf.js with ionic ?
thank you at advance

Hi, I tried during weeks, and I finally got a solution to download a pdf from a service and show it in iOS and Android, works perfect in both platforms:

Install Plugins

We need to install InAppBrowser, File and FileOpener plugins. Remember to include all this plugins in your app.modules.ts.

The component

This is the structure of my pdf-viewer.ts component:

import { Platform } from 'ionic-angular';
import { File } from '@ionic-native/file';
import { InAppBrowser } from '@ionic-native/in-app-browser';
import { FileOpener } from '@ionic-native/file-opener';
//include api-service.ts to get the pdf
import { ApiService } from '../../providers/api-service'; 

@Component({
	selector: 'pdf-viewer',
	templateUrl: 'pdf-viewer.html'
})
@Pipe({ name: 'safe' })

export class myComponent {

  constructor(
        ...
        private file: File,
        private fileOpener: FileOpener,
        private plt: Platform,
        ...
  ) { 
        ...
  }
  ...
  blobPdf; // Here we will store our Blob

  openPdf(){
		this.apiService.getPdf().subscribe(
			data => {
				if (this.plt.is('ios')) {
				// Use inAppBrowser plugin,
				// easier and faster in iOS
					this.pdfobj.active = false;
					this.pdfobj.fileURL = URL.createObjectURL(data);
					var browser = this.iab.create(
						this.pdfobj.fileURL, 
						'_blank',
						'location=no,' +
						'toolbar=yes,' +
						'enableViewportScale=yes,' +
						'closebuttoncaption=Cerrar PDF,' +
						'hardwareback=no'
					);
					browser.show();
				} else if (this.plt.is('android')) {
					// lets save and then open the file
					this.blobPdf = data; // Lets store the pdf Blob
					let filedir = this.file.dataDirectory;
					this.file.writeFile( //save PDF
						filedir, 
						"comprobante.pdf", 
						this.blobPdf, 
						{replace:true}
					).then(() =>{
						this.fileOpener.open( //open in native PDF
							filedir + 'comprobante.pdf', 
							'application/pdf'
						).then(() => {
							this.pdfobj.active = false;
						}).catch(e => console.log('Open error', e));
					}).catch(e => console.log('Save error',e));
				} 
			},
			(error) => {
				this.appService.manageError(error);
			}
		);
	}

The service

This the code for appi-service.ts file:

import { Injectable } 							from '@angular/core';
import { Http, Headers, Response, RequestOptions, ResponseContentType } from '@angular/http';
import { Observable } 							from 'rxjs/Rx';

import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';

@Injectable()

export class ApiService {
	
	constructor ( 
		public http: Http
	){}

	handleError(error, errorType) {
    return Observable.throw(error);
	}
	
	getPdf (): Observable<Array<any>> {		
		var url = YOURSERVICEURL + '/getabeautypdfinbase64/'; 
		var headersXML = new Headers({ 
			'Content-Type': 'application/json',
		});
		let options = new RequestOptions({
			headers: headersXML,
			responseType: ResponseContentType.Blob //here is the magic!!!
		});
		return this.http.get(url, options)
			.map(
				(res) => {
					//CHANGE THE BASE64 DATA INTO A PDF BLOB
          return new Blob([res.blob()], { type: 'application/pdf' })
        })
			.catch(this.handleError);
	}
}

And ready, this way you can open a Base64 pdf from a service in iOS and Android

Hi @fruitstudios can you just point me out in how to do this:

That I can not find info about.

Thanks

In: src/index.html

Thanks for your response.

Are you using the Library directly? I have import it into my project as I wrote here, so no “pdf.worker.js” import, sadly it does not work for me. I stills throws a error about the web worker.

Hi,

I did the same implementation than you. I use the function file.writeFile to save the document and then I open it. It works fantastic in ios and android, but I’ve found a problem when I installed crosswalk-webview plugin, then the application is not able to write the files for Android, ios is still working ok. There is no error in the javascript console, neither in Android studio.

Are you using the plugin crosswalk-webview and is it working for you?
Btw, I’ve created an issue https://github.com/crosswalk-project/cordova-plugin-crosswalk-webview/issues/143

Thanks

1 Like

Hi, I am working in a App for my job, and in bug revision we found the same problem, today I will work on this, I am sure we will find a solution, as soon as I get it I will share with you, Until now I can tell you that it only happens in some versions of android, these have no problem.

I was trying and trying but didn’t find a solution. I have the problem in all my android versions. A possible workaround would be to have the pdf url and open it using windo.open _system. Let me know when you have something.

Thank you man!