pdf.addImage(myPanel)
What this actually does is take a low resolution screenshot (72 DPI) of the component and add it as an image to the pdf. So when you print the pdf, the quality is pretty bad. After some digging I found this post which seemed to be exactly what I was looking for. I thought that simply doing this would do the trick:
var image:ImageSnapshot = ImageSnapshot.captureImage(myPanel, 300, new PNGEncoder());
The problem is caused by the fact that Alive PDF doesn't support transparent PNGs which is what is returned when using the PNGEncoder class which is part of Flex.
A little more digging (several hours worth actually) turned up this post which again sounded like my solution. Applying the code fix from that got me almost there. I created my own NonTransparentPNGEncoder which was basically a copy of the built in PNGEncoder with this new version of internalEncode() method:
private function internalEncode(source:Object, width:int, height:int,
transparent:Boolean = false):ByteArray{// The source is either a BitmapData or a ByteArray.var sourceBitmapData:BitmapData = source as BitmapData;var sourceByteArray:ByteArray = source as ByteArray;if (sourceByteArray)sourceByteArray.position = 0;// Create output byte arrayvar png:ByteArray = new ByteArray();// Write PNG signaturepng.writeUnsignedInt(0x89504E47);png.writeUnsignedInt(0x0D0A1A0A);// Build IHDR chunkvar IHDR:ByteArray = new ByteArray();IHDR.writeInt(width);IHDR.writeInt(height);IHDR.writeByte(8); // bit depth per channelIHDR.writeByte(2); // color type: RGBAIHDR.writeByte(0); // compression methodIHDR.writeByte(0); // filter methodIHDR.writeByte(0); // interlace methodwriteChunk(png, 0x49484452, IHDR);// Build IDAT chunkvar IDAT:ByteArray = new ByteArray();for (var y:int = 0; y <>{IDAT.writeByte(0); // no filtervar x:int;var pixel:uint;for (var j:int = 0; j <>{pixel = sourceBitmapData.getPixel(j, y);IDAT.writeByte(pixel >> 16 & 0xFF);IDAT.writeByte(pixel >> 8 & 0xFF);IDAT.writeByte(pixel & 0xFF);}}IDAT.compress();writeChunk(png, 0x49444154, IDAT);// Build IEND chunkwriteChunk(png, 0x49454E44, null);// return PNGpng.position = 0;return png;}
One final change and I finally had it working:
pdf.addPage();var image:ImageSnapshot = ImageSnapshot.captureImage(page, 300, new NonTransparentPNGEncoder());var resize:Resize = new Resize ( Mode.FIT_TO_PAGE, Position.CENTERED );pdf.addImageStream(image.data, ColorSpace.DEVICE_RGB, resize);
Then I just save my pdf to a ByteArray and use the FileReference to save it.
var bytes:ByteArray = pdf.save(Method.LOCAL);var file:FileReference = new FileReference();file.save(bytes, filename);
Keep in mind that although the panels are high resolution, they are still bitmap graphics. So, you will not be able to copy and paste any of your text out of the resulting PDF.
In order for all this to work, I am using 0.1.5 Beta version of AlivePDF, Flex 3.4 and Flash Player 10.
UPDATE: One last note. Because this is generating a 300DPI version of your component, it takes some time. A few seconds for a few page document.
7 comments:
Thanks, this post was really helpful! I copied the mx.graphics.codec.PNGEncoder and replaced the internalEncode function with the one you gave. Except that your for-loops look like they were parsed incorrectly by the blog. Here is what I used:
for (var y:int = 0; y < height; y++)
{
IDAT.writeByte(0); // no filter
var x:int;
var pixel:uint;
for (x = 0; x < width; x++)
{
pixel = sourceBitmapData.getPixel(x, y);
IDAT.writeByte(pixel >> 16 & 0xFF);
IDAT.writeByte(pixel >> 8 & 0xFF);
IDAT.writeByte(pixel & 0xFF);
}
}
You might also want to check out Eugene's Async PNG encoder which is able to export even huge bitmaps: http://blog.inspirit.ru/?p=378
And for exporting full featured PDFs you should have a look at the brand new PurePDF by Alessandro Crugnola: http://www.sephiroth.it/weblog/archives/2010/02/purepdf_a_complete_actionscript_pdf_l.php
Why not resize the display object, addImage, resize back?
can someone help me out with this?
i edited the PNGEncoder class as specified (instead of creating my own custom class)
and used the following code:
claimPDF.addPage();
var image:ImageSnapshot = ImageSnapshot.captureImage(combination_chart, 300, new PNGEncoder());
claimPDF.addImageStream(image.data);
var bytes:ByteArray = claimPDF.save(Method.LOCAL);
var f:FileReference = new FileReference();
f.save(bytes,"report.pdf");
the above gives me the following error:
Implicit coercion of a value of type mx.graphics.codec:PNGEncoder to an unrelated type mx.graphics.codec:IImageEncoder
thanks!
@Scott - What version of the Flex SDK are you using?
Thank You very much for this! Its a brilliant thing that you combined all these code and came out with this gem of a solution! And thanks to Jamie too for putting in the correction. Fantastic!!!
I just use this:
// set up a matrix for capturing the target at the correct dpi
if (dpi <= 0) dpi = Capabilities.screenDPI;
var matrix:Matrix = new Matrix(dpi/Capabilities.screenDPI, 0, 0, dpi/Capabilities.screenDPI);
var data:BitmapData = ImageSnapshot.captureBitmapData(target, matrix);
// add target image in cm @72 dpi, but fill image @300 dpi
pdf.addImage(new Bitmap(data), new Resize(Mode.NONE, Position.LEFT), x, y, cmTargetWidth, cmTargetHeight, 0, 1.0, true, ImageFormat.PNG);
Post a Comment