Thursday, December 27, 2012
Templated PDF Printing for Appcelerator based iOS Apps
Author:
Content
- Motivation
- Idea of a solution
- Detailed explanation
- IOS Module
- HTML-Template
- Inline-Images
- HTML Page-Formatting
Motivation
With Javascript being the language of choice within the appcelerator framework,a huge variety of libraries and techniques for common problems, the catch being,
that many of these depend on the context of a web browser to funtion properly.
When it comes to creating PDF data within Appcelerator apps, so far the approach
had therefore been to small pure JS libs, such as jsPDF (http://jspdf.com/).
Yet, because of it's lowlevel approach (of drawing rectangles and lines) and
rendering text, it can be cumbersome to create a fully layouted document.
Idea of a solution
As an alternative, I propose to make use of Appcelerators module framework, i.e.,making use of native code, to pass an html string to an IOS module that will
render the content in a UIWebview object and subsequently to a PDF file, that
is returned to the application layer.
IOS Module
The module used for this solution has a method "setHTMLString", that can becalled from the application layer with the HTML string to be rendered in
a UIWebview object. Upon loading, the method "webViewDidFinishLoad" is called,
where the page then is rendered to a PDF file, subsequently firing an event
"pdfready", including a path to the file. This is necessary, as events in
Appcelerator can only carry JSON serializable data. Because the UIWebkit library
is not thread safe, all the calls in "setHTMLString" and "webViewDidFinishLoad"
have to be queued on the main thread by using "dispatch_async". For details check
the source code from github, referenced below. For general information about
developing iOS modules, check https://wiki.appcelerator.org/display/guides/iOS+Module+Development+Guide
HTML Template
Since we are passing an HTML string from the application layer and basicallyrender it in a browser window, all known HTML traits apply and the document
can be layouted, as applicable. But, since there usually will be no webserver
running to pull resources from, you have to inline any CSS or JS code into the
HTML string, you pass to the IOS module. Based on such a template you might
create your final HTML string by replacing certain placeholders, such as
<!--PLACEHOLDER--> with content data. Or you choose to just construct your
HTML string, as needed. Consider the following Appcelerator code:
var html2pdf = require('com.factisresearch.html2pdf');
Ti.API.info("module is => " + html2pdf);
html2pdf.addEventListener('pdfready', function(e) {
var emailDialog = Ti.UI.createEmailDialog();
emailDialog.addAttachment(Ti.Filesystem.getFile(e.pdf));
emailDialog.open();
});
var html = '<html><body><p>Hello World!</p></body></html>';
html2pdf.setHtmlString(html);
Inline Images
Of course it should be possible to add images to the document. Because generallyit will not be possible to pull resources, the images also have to be embedded
into the HTML string. How this can be done is explained here:
http://www.techerator.com/2011/12/how-to-embed-images-directly-into-your-html/
and in countless other places. Consider the following code:
var html2pdf = require('com.factisresearch.html2pdf');
Ti.API.info("module is => " + html2pdf);
html2pdf.addEventListener('pdfready', function(e) {
var emailDialog = Ti.UI.createEmailDialog();
emailDialog.addAttachment(Ti.Filesystem.getFile(e.pdf));
emailDialog.open();
});
var html = '<html><body>';
var imageData = Titanium.Filesystem.getFile(Ti.Filesystem.getResourcesDirectory(), 'Default.png').read();
var iv = Ti.UI.createImageView({
image: imageData
});
var image = iv.toImage();
var imageB64 = Ti.Utils.base64encode(image);
html += '<p><img src="data:image/png;base64,'+imageB64+'"></p></body></html>';
html2pdf.setHtmlString(html);
HTML Page-Formatting
The IOS module described above uses static page sizes and bounds for the printablearea. You may however want to customize this for different pages. On this account
CSS offers a range of customization options, such as the "@page" attribute, where
margins can be defined or the "page-break-before" style, that can set on an html
element to enforce the subsequent content to be displayed on a new page in the
resulting pdf.
-------------------------------
var html2pdf = require('com.factisresearch.html2pdf');
Ti.API.info("module is => " + html2pdf);
html2pdf.addEventListener('pdfready', function(e) {
var emailDialog = Ti.UI.createEmailDialog();
emailDialog.addAttachment(Ti.Filesystem.getFile(e.pdf));
emailDialog.open();
});
var html = '<html><body>';
var works = queryDBForWorks();
for (var i in works) {
var entry = {};
var rs = db.getWorkInfo(works[i]);
html += ('<h2 '+(i == 0 ? '>' : 'style="page-break-before:always;">')+''+rs.fieldByName('TITLE')+' - '+rs.fieldByName('ARTIST')+'</h2>');
html += ('<img src="data:image/jpg;base64,'
+Ti.Utils.base64encode(rs.fieldByName('THUMBNAIL'))+'">');
html += ('<p>'+''+rs.fieldByName('TEXT')+'</p></div>');
rs.close();
}
html2pdf.setHtmlString(html);
You may pull an implementation, along with an example from
https://github.com/fscz/html2pdf
Subscribe to:
Posts (Atom)