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
Sunday, October 7, 2012
Developing Medical Software in Scala and Haskell
Author:
By the way, CUFP hosted a whole bunch of very interesting talks. They are all available on youtube, just search for "CUFP 2012".
Thursday, August 9, 2012
RedmineR - Redmine iOS App
Author:
Die Verwendung der App ist denkbar einfach: Zuerst muss "Redminer" kostenlos aus dem App Store geladen werden. Danach wird der API-Zugriffschlüssel benötigt - dieser wird in der normalen Redmine-Oberfläche unter "Mein Konto > API-Zugriffsschlüssel" angezeigt. Der Zugriffschlüssel wird dann auf dem iOS-Gerät unter "Einstellungen > Redminer" bei "API-Key" eingetragen. Außerdem muss hier noch die Tracker-URL eingetragen werden.
Download und weitere Informationen: Redminer für iPhone
Screenshots:
Tuesday, July 24, 2012
Android-App: Anrufweiterleitung
Author:
Sunday, June 3, 2012
UIs for Hierachical iPad Apps
Author:
If you're in a hurry: tl;dr version
Full-blown post
Motivation
How does it work?
![]() |
| after revealing more information in the level-2 view controller (the "Teams" view controller) by swiping to the right |
Can I use FRLayeredViewController, too?
What is the API like?
Say, you have a UIViewController and you're using UINavigationController from Apple. To show a new layer to the user, you would do:
[self.navigationController pushViewController:test animated:YES];
[self.layeredNavigationController pushViewController:test
inFrontOf:self
maximumWidth:NO
animated:YES];
The maximumWidth parameter is how you tell FRLayeredNavigationController whether the new view controller is content (give it the maximal width still available) or not.
Just as UINavigationController, it also supports UIBarItems and a configurable title view. For example, a UIViewController which has been pushed onto a FRLayeredNavigationController could have the following viewWillAppear method to add a back button on the upper left corner:
- (void)viewWillAppear:(BOOL)animated
{
self.layeredNavigationItem.leftBarButtonItem =
[[UIBarButtonItem alloc]
initWithImage:[UIImage imageNamed:@"back-button.png"]
style:UIBarButtonItemStylePlain
target:self
action:@selector(backButtonClicked)];
self.layeredNavigationItem.leftBarButtonItem.style =
UIBarButtonItemStyleBordered;
}
How can I embed FRLayeredViewController in my project?
Thursday, December 1, 2011
A rant about Scala vs. Haskell
Author:
Scala's type system is powerful but also complex right from the start. With Haskell you can use most basic libraries without enabling GADTs, RankNTypes, OverlappingInstances and TypeFamilies or having to learn about them. And you can take your time to learn all these concepts. The containers API is really simple compared to Scala's collections API. On the other hand in Haskell you have to learn about monads quite early. (Oh, I miss this warm fuzzy feeling in Scala!)
We're using Scala for integration with existing Java code and libraries and that's working really well. As a "stand alone" language I still prefer Haskell and I doubt that's going to change with more Scala experience. For me as a functional programmer subtyping doesn't add much more than Java compatibilty. Maybe I have to change but IMHO it makes API design and the type system too complex. I'm still overwhelmed by the "default" design possibilities: type parameters or abstract member types? Sealed case classes or open subtyping? Implicit parameters and conversions (aka type classes) offer even more design choices! Maybe I'm just more experienced with Haskell but mostly I just decide between "data" and "class" and with higher-order functions that's enough 95% of the time. For the missing 5% we have extensions (and FlexibleX + Rank2Types + TypeFamilies + ExistentialQuantification is enough for me).
Often people complain about GHC's error messages but they're really great compared to scalac's. I also miss STM. Actors are nice, but using react doesn't feel natural and we can't just use "receive" with thousands of threads "without thinking" just like in Haskell. (We're now using react with the CPS transform plugin to hide the fact that internally everything is implemented using continuations.)
I don't miss lazyness, yet. I've used call-by-name and lazy vals on some occasions and I think I prefer strict by default with optional lazyness annotations. I just can't think "the lazy way" - even after 18 months of full-time Haskell coding.
I do miss control over side effects. When writing a higher order function I always get scared when I realize that I can only expect the caller to provide a pure function but the function could be doing all sorts of things behind my back!
One thing I'd like as a GHC extensions is explicit dictionary passing. It's nice that implicit parameters can be used implicitly but also explicitly. Of course it's risky, too, because nobody can stop you from using a different Ord instance every time you're inserting into a Set. "unsafeCoerce" seemed so evil - until erasure forced me to use foo.asInstanceOf[...] (Of course an exception is not as bad as a segfault.) Sometimes I know what I want and I'm willing to take the risk. As long as all those features are not enabled by default I think hard enough before enabling {-# LANGUAGE IncoherentInstances #-}.
Scala might be the best way to live between worlds. But you have to understand both worlds and you're still in between. It's not twice as good. If I have to live in OO world I prefer having a functional world in addition so I'd chose Scala over Java at any time. But if I can choose freely I still prefer the pure Haskell world.
Sunday, October 30, 2011
WifiAlive Android App released
Author:
WifiAlive is a very simple and low resource application aimed at maintenance of a Wifi connection. A connection is periodically (every 5 seconds) checked by trying to contact the gateway, as well as IP addresses or domain names which you can enter via the UI. WifiAlive tests each of these addresses by icmp and http head. If one of these tests succeeds, Wifi is considered up. If not, Wifi is reset (that is deactivated and activated). After Wifi (re-)activation Android on its own tries to establish a connection to an access point in reach. On a failed connection check, WifiAlive will only wait 20 seconds for a reset, scan and successful connection until the connection is reset again. Considering the time needed for a Wifi reset and a scan to complete, it can be said, that, as soon as a known network is available, there is a worst-case of roughly 30 seconds for the connection to be established. WifiAlive is thus much more simple then many of its competitors. The popular WifiFixer for example conveys about 6000 lines of code whereas WifiAlive has only around 500.
The code of WifiFixer is publicly available on github. I have checked those parts of the code which seemed important in respect of a problem I believed to have with WifiFixer 0.8.0.6. I was rather stunned by the complexity of the program, since I had in mind the solution I have just proposed. WifiFixer almost replaces the Wifi Settings Tab in Android and will continue this approach in upcoming versions. In some cases, settings from the Android Wifi Settings Tab can be overridden, it does its own management of known networks and behind the scenes does scans for networks in reach, matches them to known networks and harbours a small collection of hacks for devices that have to be dealt with in a special way - for example the Google Nexus One. WifiFixer is a great application. It helps many people with their Wifi troubles. But after I had seen its complexity and, in addition, found a bug in its matching of known to found networks on a scan, I decided that this was not the solution to my problem, as, additionally, a lot of code seemed to do things that Android normally does on its own. No offence David, I've learned a lot from looking at your code.
There are other applications, like Advanced Wifi Lock. But I couldn't get access to their code and very often the free (limited) versions did not seem to cover all aspects of Wifi maintenance that I wanted to ensure. Moreover I am already beyond trusting a random application from the market without knowing its code. The Advanced Wifi Lock application (a bare Wifi lock, if it does what the name suggests) does not solve my problems, as it sometimes takes Android a bit too much of time to re-establish a connection when re-entering the area of an access point or on walking from one access point to another. In my experience this could take up to two minutes, which was just too long for my purpose. These experiences may be better on other devices, still it feels very good to know, that as soon as a known network is in reach, there is a rough 30 second worst-case until you have a connection.
On some devices there also seem to be problems with the supplied Wifi drivers. These drivers may enter an undetermined state or cause wpa supplicant to run in endless loops. I have not delved into these problems. But it seemed to me, that, if at all, they can be resolved by doing a Wifi reset.
To sum up, I think that some applications aimed at taking care of your Wifi connection may be too complicated and question is, why such an application should do things like scanning for access points, as Android usually does a pretty good at this. Others may be doing the things I had in mind, yet there was no way for me to be sure.
That's, why I wrote WifiAlive. I hope you like it.
Feel Free to browse the code at http://darcs.factisresearch.com/pub/WifiAlive or just install the app from the android market.





