Flexでの印刷機能

Flexでの印刷機能を使ってみました。
実際のサンプルはこちら
左のお店リストを右のお気に入り枠にドラッグして、印刷ボタンを押すと印刷できます。
http://moeten.info/flash/shopcoupon/?site=maidcafe

ソースコードはこちら
※いくつかバグがあって、ドラッグドロップするときにエラーが表示されることと、プリンターによってはきれいに印刷ができません。特にAdobePDFでの出力がバグっぽいです。

いくつか作成する際に気になったこと。

・画像がまったく印刷されない。
printAsBitmapを使う

var my_pj:PrintJob = new PrintJob();
var options:PrintJobOptions = new PrintJobOptions();
options.printAsBitmap = true;
my_pj.addPage(tl3, null, options);

こちらを使用するとテキストは荒くなりますが、alpha値を持つものが印刷できるようになります。
・画像が印刷されないときがある。
→画像キャッシュライブラリを使用する。特にTileListを使用している場合はなにかと便利。
CacheableImage
CacheableImageについてはこちらのページが詳しいです。

CacheableImageライブラリは現在ページが見つかりません。
ソースに添付していますのでそちらを参照してください。
index.mxml

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute"
    creationComplete="init()" backgroundAlpha="1"
    backgroundGradientAlphas="[1,1]" backgroundGradientColors="[0xffffff,0xffccff]"
 viewSourceURL="srcview/index.html" xmlns:ns="com.d_project.qrcode.mx.*">
<mx:Script>
<![CDATA[
import mx.effects.AnimateProperty;
import mx.controls.Alert;
import mx.effects.easing.*;
[Bindable]private var server:String = "moeten.info";
[Bindable]private var site:String   = "maidcafe";
private function doMouseWheel(e:MouseEvent):void{
    var a:AnimateProperty = new AnimateProperty();
    a.easingFunction = Sine.easeOut;
    a.fromValue = this.verticalScrollPosition;
    a.duration = 300;
    a.toValue   = this.verticalScrollPosition - e.delta * 30;
    a.target = this.verticalScrollPosition;
    a.property = "verticalScrollPosition";
    if( !a.isPlaying )a.play([this]);
    //this.verticalScrollPosition = this.verticalScrollPosition + e.delta*100;
    //trace( e.delta );
}
//初期化関数
private function init():void{
//    this.addEventListener(MouseEvent.MOUSE_WHEEL, doMouseWheel);
    Error.length;
    shopDetailCanvas.visible = false;
    if(this.parameters.server) server = this.parameters.server;
    if(this.parameters.site)   site   = this.parameters.site;
    htsTid.send();
}
//次のページ
private function nextPage():void{
    if( tl.verticalScrollPosition/3+1 == Math.ceil(hts.lastResult.item.length()/3) ){
        return;
    }
    tl.verticalScrollPosition = tl.verticalScrollPosition + 3;
    ds.play([tl]);
}
//前のページ
private function prePage():void{
    if( tl.verticalScrollPosition != 0 ){
        tl.verticalScrollPosition = tl.verticalScrollPosition - 3;
        ds.play([tl]);
    }
}
//プリント
private function printPage():void{
    myCanvas.visible = true;
    var my_pj:PrintJob = new PrintJob();
     if (my_pj.start()) {
        tl3.verticalScrollPosition = 0;
        var page:int = Math.ceil( tl3.dataProvider.length/3 );
        for( var i:int = 0 ; i <  page ; i ++ ){
               tl3.verticalScrollPosition = tl3.verticalScrollPosition + 3;
          }
        tl3.verticalScrollPosition = 0;
        for( i = 0 ; i < page ; i ++ ){
            try {
                var options:PrintJobOptions = new PrintJobOptions();
                options.printAsBitmap = true;
                  my_pj.addPage(myCanvas , null, options);
                //my_pj.addPage(tl3 );
                tl3.verticalScrollPosition = tl3.verticalScrollPosition + 3;
            }catch(e:Error){
               }
        }
        my_pj.send();
    }
    myCanvas.visible = false;
}
//都道府県問い合わせ
private function onChangeTid():void{
    hts.send({"tid":tidBox.selectedItem.tid});
    htsCid.send({"tid":tidBox.selectedItem.tid});
}
//都道府県問い合わせ完了イベント
private function tidResult():void{
    hts.send({"tid":13});
}
//カテゴリー問い合わせ
private function onChangeCid():void{
    hts.send({"tid":tidBox.selectedItem.tid,
              "cid":cidBox.selectedItem.cid
            });
}
private function htsDetailResult():void{
    shopDetailCanvas.visible = true;
}
]]>
</mx:Script>
<mx:Style>
global{
  color:"0x91008f";
  backgroundAlpha:1;
  backgroundGradientAlphas:1,1;
}
ComboBox{
   selectionColor: #ff66ff;
   alternatingItemColors: #ffffff, #ffccff;
   cornerRadius: 5;
   highlightAlphas: 0.48, 0.58;
   fillAlphas: 0.82, 0.85, 0.7, 1;
   fillColors: #ffffff, #ff99ff, #ff66ff, #ffffff;
   color: #ff3399;
   textRollOverColor: #ff0066;
   themeColor: #ff00ff;
   fontWeight: normal;
   dropdownStyleName: "myComboBoxDropDowns";
}
.myComboBoxDropDowns {
    backgroundAlpha:0.8;
   cornerRadius: 7;
   borderThickness: 1;
   borderColor: #ff33ff;
}
VScrollBar {
   cornerRadius: 12;
   highlightAlphas: 1, 0.95;
   fillAlphas: 1, 1, 0.75, 1;
   fillColors: #ffffff, #ff99ff, #ffffff, #ff33ff;
   trackColors: #ff66ff, #ff66ff;
   themeColor: #ff66ff;
   borderColor: #ff33ff;
}
</mx:Style>
<!--############ HTTP ############-->
<mx:HTTPService id="hts" url="{'http://'+server+'/'+site+'/?type=coupon&amp;m=api'}"
    showBusyCursor="true" resultFormat="e4x"/>
<mx:HTTPService id="htsTid" url="{'http://'+server+'/'+site+'/?type=coupon&amp;tid=list&amp;m=api'}"
    result="tidResult();" showBusyCursor="true"    resultFormat="e4x"/>
<mx:HTTPService id="htsCid" url="{'http://'+server+'/'+site+'/?type=coupon&amp;cid=list&amp;m=api'}"
    showBusyCursor="true" resultFormat="e4x"/>
<mx:HTTPService id="htsDetail" url="{'http://'+server+'/'+site+'/?type=coupon&amp;m=api'}"
    showBusyCursor="true" resultFormat="e4x" result="htsDetailResult()"/>
<!--############ エフェクト ############-->
<mx:Dissolve id="ds" alphaFrom="0" alphaTo="1" color="0xffffff" />
<mx:Sequence id="itemsChangeEffect">
    <mx:Blur blurYTo="12" blurXTo="12" duration="300" perElementOffset="150" filter="removeItem"/>
    <mx:Parallel>
        <mx:Move duration="250" easingFunction="{Elastic.easeOut}" perElementOffset="20"/>
        <mx:RemoveItemAction startDelay="100" filter="removeItem"/>
        <mx:AddItemAction startDelay="100" filter="addItem"/>
        <mx:Blur startDelay="100" blurXFrom="18" blurYFrom="18" blurXTo="0" blurYTo="0"    duration="300" filter="addItem"/>
    </mx:Parallel>
</mx:Sequence>
<mx:Parallel id="myShow">
    <mx:Move yFrom="100" yTo="128"/>
    <mx:Fade alphaFrom="0" alphaTo="1"/>
</mx:Parallel>
<mx:Parallel id="myHide">
    <mx:Move yFrom="128" yTo="100"/>
    <mx:Fade alphaFrom="1" alphaTo="0"/>
</mx:Parallel>
<!--############ コンポーネント ############-->
<mx:LinkButton x="50" y="77" label="< 前のページ" click="prePage()" fontSize="16" rollOverColor="0xffccff"/>
<mx:Label x="185" y="79" text="{(tl.verticalScrollPosition/3+1) +' / ' + Math.ceil(hts.lastResult.item.length()/3) }"  width="156" fontSize="16"/>
<mx:LinkButton x="250" y="77" label="次のページ >" click="nextPage()" fontSize="16" rollOverColor="0xffccff"/>
<mx:Label x="450" y="90" text="お気に入りにドラッグしてね"  width="156" fontSize="12"/>
<mx:ComboBox id="tidBox" prompt="都道府県" x="49" y="10" dataProvider="{htsTid.lastResult.item}"
    fontSize="20"
    labelField="todouhuken" width="141" rowCount="10" change="onChangeTid()" height="40"/>
<mx:ComboBox id="cidBox" prompt="カテゴリー" enabled="{htsTid.lastResult}" x="212" y="10"
    fontSize="18"
    dataProvider="{htsCid.lastResult.item}" labelField="category" width="178"
    rowCount="20" change="onChangeCid()" height="40"/>
<mx:LinkButton x="580" y="16" label="お気に入りをまとめて印刷" click="printPage()" icon="@Embed('32px-Crystal_Clear_action_frameprint.png')" fontSize="14" rollOverColor="0xffccff"/>
<mx:HRule x="49" y="58" width="740" height="14"/>
<!--お店クーポンリスト-->
<mx:TileList id="tl" width="562" height="760"
    paddingBottom="2" paddingLeft="2" paddingRight="2" paddingTop="2"
    backgroundAlpha="1" borderColor="0xffccff" backgroundColor="0xffffff"
    borderThickness="3" borderStyle="solid"
    rowCount="3" columnCount="1"
    verticalScrollPolicy="off"
    cornerRadius="10" dragEnabled="true"
    rollOverColor="0xffffff" selectionColor="0xffffff"
    dataProvider="{hts.lastResult.item}" itemRenderer="shopList" x="49" y="113"/>
<!--お店お気に入りリスト-->
<mx:Label x="653" y="77" text="お気に入り" fontSize="20"/>
<mx:TileList id="tl2" width="176" height="682" dragExit="{trace('dragexit')}"
    paddingBottom="2" paddingLeft="2" paddingRight="2" paddingTop="2"
    backgroundAlpha="1" borderColor="0xffccff" backgroundColor="0xffffff"
    itemsChangeEffect="{itemsChangeEffect}"
    borderThickness="3" borderStyle="solid"
     rowHeight="130" columnWidth="170" variableRowHeight="true"
    rowCount="5" columnCount="1" dropEnabled="true" dragEnabled="true"
    cornerRadius="10" dragMoveEnabled="true"
    rollOverColor="0xffffff" selectionColor="0xffffff"
    itemRenderer="shopThum" x="619" y="113"/>
<!--ゴミ箱-->
<mx:TileList x="619" y="803" width="176" height="70" dropEnabled="true" verticalScrollPolicy="off" backgroundImage="@Embed('user-trash.png')"
    selectable="false"></mx:TileList>
<!--お店印刷用-->
<mx:Canvas width="562" id="myCanvas" height="850" verticalScrollPolicy="off" horizontalScrollPolicy="off" visible="false"
        backgroundAlpha="1" borderColor="0xffccff" backgroundColor="0xffffff"
        borderThickness="0" borderStyle="solid">
    <mx:TileList id="tl3" width="562" height="760"
        rowCount="3" columnCount="1"
        verticalScrollPolicy="off" borderThickness="0" borderStyle="solid"
        dataProvider="{tl2.dataProvider}" itemRenderer="shopList"/>
    <mx:HBox width="100%" x="0" y="740" height="100" verticalGap="0" verticalAlign="middle">
        <ns:QRCode text="{'http://'+server+'/'+site+'/i/?m=go'}" width="80" height="80" blendMode="multiply"/>
        <mx:TextArea text="お店名の左に表示される番号を入力すると簡単にお店情報へアクセスできるよ!" paddingTop="4" backgroundAlpha="0"
            width="220" fontSize="12" borderThickness="0" height="60"/>
        <ns:QRCode text="{'http://'+server+'/'+site+'/i/?m=gps_test'}" width="80" height="80" blendMode="multiply"/>
        <mx:Label text="自分の現在地を表示する"/>
    </mx:HBox>
</mx:Canvas>
<!--お店詳細-->
<mx:Canvas id="shopDetailCanvas" x="50" y="128" width="553" height="727" visible="false"
    dropShadowColor="0x000000" dropShadowEnabled="true"
    cornerRadius="20" borderColor="0xffccff" borderStyle="solid" borderThickness="3" backgroundAlpha="1" backgroundColor="0xffffff"
    showEffect="myShow" hideEffect="myHide">
    <mx:Button x="481" y="10" label="閉じる" click="{shopDetailCanvas.visible=false}"/>
    <mx:Label text="{htsDetail.lastResult.item.todouhuken} {htsDetail.lastResult.item.category}" x="20" y="14"/>
    <mx:TextInput x="20" y="40" width="517" height="25" text="{htsDetail.lastResult.item.shopname}"
        cornerRadius="5" color="0xffffff" editable="false" fontSize="16" textAlign="left"
        backgroundColor="0xff4bb9" fontWeight="bold" borderThickness="0" borderStyle="solid"/>
    <mx:Image x="49" y="73" width="153" height="199" source="{htsDetail.lastResult.item.image.image1}"/>
    <mx:Image x="210" y="73" width="153" height="199" source="{htsDetail.lastResult.item.image.image2}"/>
    <mx:Image x="371" y="73" width="153" height="199" source="{htsDetail.lastResult.item.image.image3}"/>
    <mx:TextInput text="お店情報" x="20" y="280" width="270"
        cornerRadius="5" color="0xffffff" editable="false" fontSize="16" textAlign="left"
        backgroundColor="0xff4bb9" fontWeight="bold" borderThickness="0" borderStyle="solid"/>
    <mx:Grid x="20" y="310" width="270"  height="352" verticalScrollPolicy="off" horizontalScrollPolicy="off" verticalGap="2">
        <mx:GridRow width="100%" height="100%">
            <mx:GridItem width="100%" height="100%" backgroundColor="0xffccff">
                <mx:Label text="お店紹介"/>
            </mx:GridItem>
            <mx:GridItem width="100%" height="100%">
                <mx:TextArea width="210" height="109" text="{htsDetail.lastResult.item.setumei}" editable="false"/>
            </mx:GridItem>
        </mx:GridRow>
        <mx:GridRow width="100%" height="100%">
            <mx:GridItem width="100%" height="100%" backgroundColor="0xffccff">
                <mx:Label text="公式サイト"/>
            </mx:GridItem>
            <mx:GridItem width="100%" height="100%">
                <mx:LinkButton label="{htsDetail.lastResult.item.url}"  click="{navigateToURL( new URLRequest( htsDetail.lastResult.item.url ) )}" rollOverColor="0xffccff"/>
            </mx:GridItem>
        </mx:GridRow>
        <mx:GridRow width="100%" height="100%">
            <mx:GridItem width="100%" height="100%" backgroundColor="0xffccff">
                <mx:Label text="住所"/>
            </mx:GridItem>
            <mx:GridItem width="100%" height="100%">
                <mx:Label text="{htsDetail.lastResult.item.address}"/>
            </mx:GridItem>
        </mx:GridRow>
        <mx:GridRow width="100%" height="100%">
            <mx:GridItem width="100%" height="100%" backgroundColor="0xffccff">
                <mx:Label text="電話番号"/>
            </mx:GridItem>
            <mx:GridItem width="100%" height="100%">
                <mx:Label text="{htsDetail.lastResult.item.tel}"/>
            </mx:GridItem>
        </mx:GridRow>
        <mx:GridRow width="100%" height="100%">
            <mx:GridItem width="100%" height="100%" backgroundColor="0xffccff">
                <mx:Label text="営業時間"/>
            </mx:GridItem>
            <mx:GridItem width="100%" height="100%">
                <mx:Label text="{htsDetail.lastResult.item.eigyoujikan}"/>
            </mx:GridItem>
        </mx:GridRow>
        <mx:GridRow width="100%" height="100%">
            <mx:GridItem width="100%" height="100%" backgroundColor="0xffccff">
                <mx:Label text="メニュー"/>
            </mx:GridItem>
            <mx:GridItem width="100%" height="100%">
                <mx:TextArea width="210" height="109" text="{htsDetail.lastResult.item.menu}" editable="false"/>
            </mx:GridItem>
        </mx:GridRow>
    </mx:Grid>
    <mx:TextInput text="クーポン" x="297" y="280" width="240"
        cornerRadius="5" color="0xffffff" editable="false" fontSize="16" textAlign="left"
        backgroundColor="0xff4bb9" fontWeight="bold" borderThickness="0" borderStyle="solid"/>
    <mx:TextArea width="240" height="102" x="297" y="305" text="{htsDetail.lastResult.item.coupon}" editable="false"/>
    <mx:TextInput text="アクセス" x="297" y="410" width="240"
        cornerRadius="5" color="0xffffff" editable="false" fontSize="16" textAlign="left"
        backgroundColor="0xff4bb9" fontWeight="bold" borderThickness="0" borderStyle="solid"/>
    <mx:Image id="mymap" x="297" y="440" width="240" height="222"
        source="{'http://moeten.info/flex/20080601_bookTest/bin-release/gmap.php?lat='
        + htsDetail.lastResult.item.wgps_lat + '&amp;lon=' + htsDetail.lastResult.item.wgps_lon + '&amp;w='
        + mymap.width+'&amp;h='+mymap.height+'&amp;z=16'}"
        />
</mx:Canvas>
<mx:Label x="312" y="881" text="- moeten.info -"/>
</mx:Application>

shopList.mxml

<?xml version="1.0" encoding="utf-8"?>
<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml" width="544" height="242"
    xmlns:ic="net.threeonec.imagecache.*"
    cornerRadius="10" borderThickness="1" borderStyle="solid" borderColor="0xffccff"
    verticalScrollPolicy="off" horizontalScrollPolicy="off" dropShadowEnabled="false">
<mx:Script>
<![CDATA[
import mx.core.Application;
private function showShopwDetail():void{
    Application.application.htsDetail.send({'sid':data.sid});
}
]]>
</mx:Script>
<ic:CacheableImage cacheAsBitmap="true" cachePolicy="on" x="5" y="15" width="102" height="109" source="{data.image.image1}"/>
<mx:TextInput x="0" y="0" width="100%" height="24" text="[{data.sid}]{data.shopname}"
     cornerRadius="5" color="0xffffff"
     editable="false" fontSize="16" textAlign="left" backgroundColor="0xff4bb9" fontWeight="bold" borderThickness="0" borderStyle="solid"/>
<mx:Button x="381" y="1" label="公式サイト" click="{navigateToURL( new URLRequest( data.url ) )}"/>
<mx:Button x="481" y="1" label="お店詳細" click="{showShopwDetail()}"/>
<mx:Button x="441" y="0" label="お気に入りに追加" visible="false"/>
<mx:TextArea x="120" y="25" width="156" height="205" htmlText="{data.coupon}" selectable="false"
    textAlign="left" editable="false" borderThickness="1" borderColor="0xffccff" cornerRadius="5" condenseWhite="true"
     verticalScrollPolicy="off"/>
<ic:CacheableImage id="mymap" cacheAsBitmap="true" cachePolicy="on"
    source = "{'http://moeten.info/flex/20080601_bookTest/bin-release/gmap.php?lat='
                    +data.wgps_lat + '&amp;lon=' + data.wgps_lon + '&amp;w='+mymap.width+'&amp;h='+mymap.height+'&amp;z=16'}"
     width="250" height="200" y="30" x="286"/>
<mx:TextArea x="10" y="136" text="{data.category}" width="102" textAlign="left" height="15" editable="false" borderThickness="0" backgroundColor="0xccffcc"/>
<mx:TextArea x="10" y="153" text="{data.tel}" width="102" textAlign="left" height="15" editable="false" borderThickness="0" backgroundColor="0xffffcc"/>
<mx:TextArea x="10" y="170" text="{data.eigyoujikan}" width="102" textAlign="left" height="32" editable="false" borderThickness="0" backgroundColor="0xffccff"/>
<mx:TextArea x="10" y="203" text="{data.address}" width="102" textAlign="left" height="32" editable="false" borderThickness="0" backgroundColor="0xffffcc"/>
</mx:Canvas>

shopDetail.mxml

<?xml version="1.0" encoding="utf-8"?>
<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml" width="282" height="242"
    cornerRadius="10" borderThickness="1" borderStyle="solid" borderColor="0xffccff"
    verticalScrollPolicy="off" horizontalScrollPolicy="off" dropShadowEnabled="false">
    <mx:TextInput y="3" width="266" height="20" text="{data.shopname}" click="{navigateToURL( new URLRequest(data.moeten_url))}"  buttonMode="true"
         horizontalCenter="0" cornerRadius="5" color="0xff0000" editable="false"
         textAlign="left" backgroundColor="0xffccff" fontWeight="bold" borderThickness="0" borderStyle="solid"/>
    <mx:Image x="10" y="25" width="102" height="109" source="{data.image.image1}"
        verticalAlign="middle" horizontalAlign="center"/>
    <mx:TextArea x="120" y="25" width="146" height="87" text="{String(data.coupon).replace('\r\n','')}"
        textAlign="left" editable="false" borderThickness="1" borderColor="0xffccff" cornerRadius="5"
         verticalScrollPolicy="off"
        />
    <mx:Image
        source="http://moeten.info/flex/20080601_bookTest/bin-release/gmap.php?lat={data.wgps_lat}&amp;lon={data.wgps_lon}&amp;w=146&amp;h=115&amp;z=15"
        width="146" height="115" y="120" x="118"/>
    <mx:TextArea x="10" y="136" text="{data.category}" width="102" textAlign="left" height="15" editable="false" borderThickness="0" backgroundColor="0xccffcc"/>
    <mx:TextArea x="10" y="153" text="{data.tel}" width="102" textAlign="left" height="15" editable="false" borderThickness="0" backgroundColor="0xffffcc"/>
    <mx:TextArea x="10" y="170" text="{data.eigyoujikan}" width="102" textAlign="left" height="32" editable="false" borderThickness="0" backgroundColor="0xffccff"/>
    <mx:TextArea x="10" y="203" text="{data.address}" width="102" textAlign="left" height="32" editable="false" borderThickness="0" backgroundColor="0xffffcc"/>
    <mx:LinkButton x="246" y="2" label="×" width="28" fontWeight="bold" color="0xffffff"/>
</mx:Canvas>

shopThum.mxml

<?xml version="1.0" encoding="utf-8"?>
<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml" width="184" height="128" verticalScrollPolicy="off" horizontalScrollPolicy="off" cornerRadius="5" borderColor="0xffccff" borderStyle="solid" borderThickness="1">
<mx:Script>
<![CDATA[
import mx.core.Application;
private function showShopwDetail():void{
    Application.application.htsDetail.send({'sid':data.sid});
}
]]>
</mx:Script>
<mx:TextArea y="0" width="100%" height="22" text="{data.shopname}"
     cornerRadius="5" color="0xff0000" editable="false"
     textAlign="left" backgroundColor="0xffccff" fontWeight="bold" borderThickness="0" borderStyle="solid" x="0"/>
<mx:Image x="4" y="27" width="66" height="75" source="{data.image.image1}"
    verticalAlign="middle" horizontalAlign="center"/>
<mx:LinkButton x="72" y="106" label="詳細" click="showShopwDetail()"/>
<mx:TextArea x="72" y="27" width="89" height="75" htmlText="{data.coupon}" condenseWhite="true" verticalScrollPolicy="off" textAlign="left" editable="false" selectable="false"/>
</mx:Canvas>