AlivePDF is an easy to use library to create PDF’s directly in Flash Player. There are several posts, i.e. here and here, describing how you can insert a hires image of any DisplayObject, i.e. a chart. These posts tell you how to modify a PNGEncoder in a way that it discards transparency – but in case your DisplayObject had some actual transparency, it won’t end up looking very good – transparent parts turn black,  semi-transparent dark – what you really want is to give the DisplayObject a solid white background.

You can specify solid background colour on nearly any Flex component or you can always place the component in a white Canvas (or on white Rect in Flex 4).

Finally, the obvious approach (as we already had to hack it anyway) is to modify the mx.graphics.codec.PNGEncoder to replace transparency with white. There are no doubt great PNGEncoders out there, but I didn’t quickly find one that would discard transparency and extend IImageEncoder interface, so I jumped at the (rare) opportunity to have a play with >> and & operators – and have done one for you. The downside is that it’s not very fast – that’s likely caused by my poor coding, please feel free to suggest how to optimise it.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
////////////////////////////////////////////////////////////////////////////////
//
// NonTransparentPNGEncoder.as
// STEHIL.COM, 2010
// example usage with AlivePDF:
// var p:PDF = new PDF( Orientation.PORTRAIT, Unit.MM, Size.A4 );
// p.addPage();
// var linechartSnap:ImageSnapshot = ImageSnapshot.captureImage(linechart, IMAGE_DPI, new NonTransparentPNGEncoder());
// p.addImageStream(linechartSnap.data, ColorSpace.DEVICE_RGB, null, 0, 150, 180, 80);
//
// based on mx.graphics.codec.PNGEncoder
//
//  ADOBE SYSTEMS INCORPORATED
//  Copyright 2007 Adobe Systems Incorporated
//  All Rights Reserved.
//
//  NOTICE: Adobe permits you to use, modify, and distribute this file
//  in accordance with the terms of the license agreement accompanying it.
//
////////////////////////////////////////////////////////////////////////////////

package com.stehil.util
{
 
  import flash.display.BitmapData;
  import flash.utils.ByteArray;
 
  import mx.graphics.codec.IImageEncoder;
 
  /**
   *  The PNGEncoder class converts raw bitmap images into encoded
   *  images using Portable Network Graphics (PNG) lossless compression.
   *
   *  <p>For the PNG specification, see http://www.w3.org/TR/PNG/</p>.
   */

  public class NonTransparentPNGEncoder implements IImageEncoder
  {
   
    //--------------------------------------------------------------------------
    //
    //  Class constants
    //
    //--------------------------------------------------------------------------
   
    /**
     *  @private
     *  The MIME type for a PNG image.
     */

    private static const CONTENT_TYPE:String = "image/png";
   
    //--------------------------------------------------------------------------
    //
    //  Constructor
    //
    //--------------------------------------------------------------------------
   
    /**
     *  Constructor.
     */

    public function NonTransparentPNGEncoder()
    {
      super();
     
      initializeCRCTable();
    }
   
    //--------------------------------------------------------------------------
    //
    //  Variables
    //
    //--------------------------------------------------------------------------
   
    /**
     *  @private
     *  Used for computing the cyclic redundancy checksum
     *  at the end of each chunk.
     */

    private var crcTable:Array;
   
    //--------------------------------------------------------------------------
    //
    //  Properties
    //
    //--------------------------------------------------------------------------
   
    //----------------------------------
    //  contentType
    //----------------------------------
   
    /**
     *  The MIME type for the PNG encoded image.
     *  The value is <code>"image/png"</code>.
     */

    public function get contentType():String
    {
      return CONTENT_TYPE;
    }
   
    //--------------------------------------------------------------------------
    //
    //  Methods
    //
    //--------------------------------------------------------------------------
   
    /**
     *  Converts the pixels of a BitmapData object
     *  to a PNG-encoded ByteArray object.
     *
     *  @param bitmapData The input BitmapData object.
     *
     *  @return Returns a ByteArray object containing PNG-encoded image data.
     */

    public function encode(bitmapData:BitmapData):ByteArray
    {
      return internalEncode(bitmapData, bitmapData.width, bitmapData.height,
        bitmapData.transparent);
    }
   
    /**
     *  Converts a ByteArray object containing raw pixels
     *  in 32-bit ARGB (Alpha, Red, Green, Blue) format
     *  to a new PNG-encoded ByteArray object.
     *  The original ByteArray is left unchanged.
     *
     *  @param byteArray The input ByteArray object containing raw pixels.
     *  This ByteArray should contain
     *  <code>4 * width * height</code> bytes.
     *  Each pixel is represented by 4 bytes, in the order ARGB.
     *  The first four bytes represent the top-left pixel of the image.
     *  The next four bytes represent the pixel to its right, etc.
     *  Each row follows the previous one without any padding.
     *
     *  @param width The width of the input image, in pixels.
     *
     *  @param height The height of the input image, in pixels.
     *
     *  @param transparent If <code>false</code>, alpha channel information
     *  is ignored but you still must represent each pixel
     *  as four bytes in ARGB format.
     *
     *  @return Returns a ByteArray object containing PNG-encoded image data.
     */

    public function encodeByteArray(byteArray:ByteArray, width:int, height:int,
                    transparent:Boolean = true):ByteArray
    {
      return internalEncode(byteArray, width, height, transparent);
    }
   
    /**
     *  @private
     */

    private function initializeCRCTable():void
    {
      crcTable = [];
     
      for (var n:uint = 0; n < 256; n++)
      {
        var c:uint = n;
        for (var k:uint = 0; k < 8; k++)
        {
          if (c & 1)
            c = uint(uint(0xedb88320) ^ uint(c >>> 1));
          else
            c = uint(c >>> 1);
        }
        crcTable[n] = c;
      }
    }
   
    /**
     *  @private
     */

    private function internalEncode(source:Object, width:int, height:int,
                    transparent:Boolean = true):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 array
      var png:ByteArray = new ByteArray();
      // Write PNG signature
      png.writeUnsignedInt(0x89504E47);
      png.writeUnsignedInt(0x0D0A1A0A);
      // Build IHDR chunk
      var IHDR:ByteArray = new ByteArray();
      IHDR.writeInt(width);
      IHDR.writeInt(height);
      IHDR.writeByte(8); // bit depth per channel
      IHDR.writeByte(2); // color type: RGBA
      IHDR.writeByte(0); // compression method
      IHDR.writeByte(0); // filter method
      IHDR.writeByte(0); // interlace method
      writeChunk(png, 0x49484452, IHDR);
      // Build IDAT chunk
      var IDAT:ByteArray = new ByteArray();

      var alpha:uint;
      var r:uint;
      var g:uint;
      var b:uint;
     
      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.getPixel32(x, y);

          alpha = pixel >> 24 & 0xFF; //alpha=255 if no transparency
         
          r = pixel >> 16 & 0xFF;
          g = pixel >> 8 & 0xFF;
          b = pixel & 0xFF;
         
          alpha = 0xFF - alpha; //invert alpha value
          r+=alpha;
          g+=alpha;
          b+=alpha;
           
          IDAT.writeByte(limit8byte(r));
          IDAT.writeByte(limit8byte(g));
          IDAT.writeByte(limit8byte(b));          
        }
      }
      IDAT.compress();
      writeChunk(png, 0x49444154, IDAT);
      // Build IEND chunk
      writeChunk(png, 0x49454E44, null);
      // return PNG
      png.position = 0;
      return png;
    }
   
    private function limit8byte(n:uint):uint {
      if (n>0xFF)
        return 0xFF;
      else
        return n;
    }
   
   
    /**
     *  @private
     */

    private function writeChunk(png:ByteArray, type:uint, data:ByteArray):void
    {
      // Write length of data.
      var len:uint = 0;
      if (data)
        len = data.length;
      png.writeUnsignedInt(len);
     
      // Write chunk type.
      var typePos:uint = png.position;
      png.writeUnsignedInt(type);
     
      // Write data.
      if (data)
        png.writeBytes(data);
     
      // Write CRC of chunk type and data.
      var crcPos:uint = png.position;
      png.position = typePos;
      var crc:uint = 0xFFFFFFFF;
      for (var i:uint = typePos; i < crcPos; i++)
      {
        crc = uint(crcTable[(crc ^ png.readUnsignedByte()) & uint(0xFF)] ^
          uint(crc >>> 8));
      }
      crc = uint(crc ^ uint(0xFFFFFFFF));
      png.position = crcPos;
      png.writeUnsignedInt(crc);
    }
  }
 
}