For the background to this post, and how to use the following code, see Part 1
In this section I will describe the “solution” I found to the problem of the lack of adequate printing capability in AIR apps. My solution is to use ActionScript to create a PDF on the fly on the client-side. In this case, I am creating it from Javascript since I develop HTML/Javascript apps (using the EXTJS library). This solution does NOT require EXTJS or any other JS library.
I found two AS libraries that create PDF files client-side – AlivePDF and purePDF. The latter interested me particularly becuase it is a port of iText java library which I was already familiar with. However, as discussed in my forum post here, I was unable to get purePDF to work so I switched to AlivePDF which was more successful. It is highly likely that my problems with purePDF we of my own making. However I was unable to get any help to work out what, if anything, I was doing wrong.
Now, this is really the start of the solution and very much a work in progress. I would value feedback about the overall approach and architecture I am proposing. Since I really wanted to use purePDF instead of AlivePDF, I elected to make the JS class as a layer so that it would be easier to create a version for purePDF if I ever get it to work!
So, here is the “proof of concept” source code.
First, the ActionScript class that exposes Javascript-friendly methods. Compile this as per Part 1 of this article.
package {
import flash.display.DisplayObject;
import flash.display.Sprite;
import flash.display.StageAlign;
import flash.display.StageScaleMode;
import org.alivepdf.pdf.PDF;
import org.alivepdf.saving.Method;
import org.alivepdf.fonts.* ;
import org.alivepdf.pages.Page;
import org.alivepdf.display.Display;
import org.alivepdf.layout.* ;
import org.alivepdf.links.* ;
import mx.utils.UIDUtil;
import flash.filesystem.FileStream;
import flash.filesystem.File;
import flash.filesystem.FileMode;
import flash.utils.ByteArray;
public class alivePDFWrapper extends Sprite {
private
var pdf: PDF;
private
var file: File;
public function alivePDFWrapper() {}
// The translate methods convert the integers passed from the javascript to
// whatever correct data types AlivePDF uses
private function translateSize(size: int): Size {
var s: Size = Size.A4;
switch (size) {
case 0:
s = Size.A3;
break;
case 1:
s = Size.A4;
break;
case 2:
s = Size.A5;
break;
case 3:
s = Size.LEGAL;
break;
case 4:
s = Size.LETTER;
break;
case 5:
s = Size.TABLOID;
break;
}
return s;
}
private function translateOrientation(orientation: int): String {
var o: String = Orientation.PORTRAIT;
switch (orientation) {
case 0:
o = Orientation.PORTRAIT;
break;
case 1:
o = Orientation.LANDSCAPE;
break;
}
return o;
}
private function translateUnit(unit: int): String {
var u: String = Unit.MM;
switch (unit) {
case 0:
u = Unit.MM;
break;
case 1:
u = Unit.CM;
break;
case 2:
u = Unit.INCHES;
break;
case 3:
u = Unit.POINT;
break;
}
return u;
}
private function translateDisplayZoom(dzoom: int): String {
var dz: String = Display.FULL_PAGE;
switch (dzoom) {
case 0:
dz = Display.DEFAULT;
break;
case 1:
dz = Display.FULL_PAGE;
break;
case 2:
dz = Display.FULL_WIDTH;
break;
case 3:
dz = Display.REAL;
break;
}
return dz;
}
private function translateDisplayLayout(dlayout: int): String {
var dl: String = Layout.SINGLE_PAGE;
switch (dlayout) {
case 0:
dl = Layout.SINGLE_PAGE;
break;
case 1:
dl = Layout.ONE_COLUMN;
break;
case 2:
dl = Layout.TWO_COLUMN_LEFT;
break;
case 3:
dl = Layout.TWO_COLUMN_RIGHT;
break;
}
return dl;
}
public function newPDF(size: int, orientation: int, unit: int): void {
var s: Size = translateSize(size);
var o: String = translateOrientation(orientation);
var u: String = translateUnit(unit);
pdf = new PDF(o, u, s);
//var myCoreFont:IFont = new CoreFont ( FontFamily.HELVETICA_BOLD );
var myCoreFont: IFont = new CoreFont(FontFamily.HELVETICA);
pdf.setFont(myCoreFont, 12);
}
public function setDisplayMode(displayZoom: int, displayLayout: int): void {
var dz: String = translateDisplayZoom(displayZoom);
var dl: String = translateDisplayLayout(displayLayout);
pdf.setDisplayMode(dz, dl);
}
public function newPage(size: int, orientation: int,unit: int): void {
var s: Size = translateSize(size);
var o: String = translateOrientation(orientation);
var u: String = translateUnit(unit);
var newPage: Page = new Page(o, u, s);
pdf.addPage(newPage);
}
public function addPage(): void {
pdf.addPage();
}
public function writeText(lineHeight: Number, text: String, link: ILink = null): void {
pdf.writeText(lineHeight, text, link);
}
public function saveToFileAIR(fname: String): void {
// Save to file for AIR
var fs: FileStream = new FileStream();
file = new File(fname);
fs.open(file, FileMode.WRITE);
var bytes: ByteArray = pdf.save(Method.LOCAL);
fs.writeBytes(bytes);
fs.close();
}
}
}
The Javascript interface (so far). Save this to /js folder in your AIR app.
function jsPDF() {
// Define constants
this.size = {
A3: 0,
A4: 1,
A5: 2,
LEGAL: 3,
LETTER: 4,
TABLOID: 5
}
this.unit = {
MM: 0,
CM: 1,
INCHES: 2,
POINT: 3
}
this.orientation = {
PORTRAIT: 0,
LANDSCAPE: 1
}
this.displayZoom = {
DEFAULT: 0,
FULL_PAGE: 1,
FULL_WIDTH: 2,
REAL: 3
}
this.displayLayout = {
SINGLE_PAGE: 0,
ONE_COLUMN: 1,
TWO_COLUMN_LEFT: 2,
TWO_COLUMN_RIGHT: 3
}
// Load the ActionScript Library (ie alivePDF + wrapper class)
this.ASlib = new window.runtime.alivePDFWrapper();
// Create a new PDF instance
this.newPDF = function (size, orientation, unit) {
this.ASlib.newPDF(size, orientation, unit);
}
// Set the display mode for the PDF when displayed in the reader
this.setDisplayMode = function (displayZoom, displayLayout) {
this.ASlib.setDisplayMode(displayZoom, displayLayout);
}
// Create a new page and add it to the pdf document
this.newPage = function (size, orientation, unit) {
this.ASlib.newPage(size, orientation, unit);
}
this.addPage = function () {
this.ASlib.addPage();
}
this.writeText = function (lineHeight, text, link) {
this.ASlib.writeText(lineHeight, text, link);
}
this.saveToFileAIR = function (fname) {
this.ASlib.saveToFileAIR(fname);
}
}
The AIR app:
<html>
<head>
<title>
Test alivePDF
</title>
<script type="text/javascript" src="lib/air/AIRAliases.js"></script>
<script src="lib/alivePDFWrapper.swf" type="application/x-shockwave-flash"></script>
<script src="js/jspdf.js"></script>
<script>
var mypdf = new jsPDF();
mypdf.newPDF(mypdf.size.A4, mypdf.orientation.PORTRAIT, mypdf.unit.MM);
//mypdf.setDisplayMode(mypdf.displayZoom.FULL_PAGE,mypdf.displayLayout.SINGLE_PAGE);
mypdf.setDisplayMode(mypdf.displayZoom.FULL_PAGE, mypdf.displayLayout.TWO_COLUMN_LEFT);
mypdf.newPage(mypdf.size.A4, mypdf.orientation.LANDSCAPE, mypdf.unit.MM);
mypdf.writeText(10, "Just one line on the first page!");
mypdf.newPage(mypdf.size.A5, mypdf.orientation.PORTRAIT, mypdf.unit.MM);
mypdf.writeText(10, "Lorem ipsum dolor sit amet, consectetur adipiscing elit.\n\nPraesent vel lectus lorem. Phasellus convallis, tortor a venenatis mattis, erat mi euismod tellus, in fermentum sapien nibh sit amet urna. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Praesent tellus libero, lacinia ac egestas eget, interdum quis purus. Donec ut nisl metus, sit amet viverra turpis. Mauris ultrices dapibus lacus non ultrices. Cras elementum luctus mauris, vitae eleifend diam accumsan ut. Aliquam erat volutpat. Suspendisse placerat nibh in libero tincidunt a elementum mi vehicula. Donec lobortis magna vel nibh mollis tempor. Maecenas et elit nunc. Nam non auctor orci. Aliquam vel velit vel mi adipiscing semper in ac orci. Vestibulum commodo sem eget tortor lobortis semper. Ut sit amet sapien non velit rutrum egestas sollicitudin in elit. Fusce laoreet leo a sem mattis iaculis");
mypdf.saveToFileAIR("C:/CFusionMX7/wwwroot/Sites/TestAS/test2.pdf"); // Set YOUR path here
</script>
</head>
<body>
</body>
</html>
I will continue to add functionality and test the AS and JS classes above. I will update the code above as I do. I am very interested to see if others find this idea has potential.