2011年12月13日火曜日

AIRで動的に埋め込みフォントを読み込むには?

AIRで動的に埋め込みフォントを読み込むには?大体、そもそも、そんなことが可能なのか?

Flash的にはこのようにするらしいので、たぶんAIRでも可能ではあるだろう。

AS3 Runtime Font Loading – nochump.com

ということで、とりあえずやってみる。

まず上の記事にあるように、フォントを埋め込んだFontPack.swfをFlashBuilderでActionScriptプロジェクトで作る。FlexでもAIRでもライブラリプロジェクトでもなく、ActionScriptプロジェクトだ。

FontPack.asはこんな感じ。
package {
  
  import flash.display.Sprite;
  
  public class FontPack extends Sprite {
    
    public static var vernum:String = "111213.0"; //これは必ずしも必要ではない

    [Embed(source="fonts/aquafont.ttf", fontName="aquafont", mimeType="application/x-font", embedAsCFF="true")]
    public static var AquaFont:Class;

  } //END class
  
} //END package
そして、さらに上の記事を参考に、AIRのプログラムにこのFontPack.swfを読み込む処理を追加する。こんな感じ。
protected function onClickBtnFontPackDL():void {
  var url:String = "http://path/to/FontPack.swf";
  var loader:Loader = new Loader();
  loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onCompleteLoader);
  loader.load(new URLRequest(url));
  function onCompleteLoader():void {
    var FontPack:Class = loader.contentLoaderInfo.applicationDomain.getDefinition("FontPack") as Class;
    trace("onCompleteLoader() vernum=" + FontPack.vernum);
    Font.registerFont(FontPack.AquaFont); //ここでエラーになる(ArgumentError: Error #1508: The value specified for argument font is invalid.)
    var fonts:Array = Font.enumerateFonts(false);
    for (var i:Number = 0; i < fonts.length; i++) {
      var font:Font = fonts[i];
      trace("onCompleteLoader() : [" + i + "] fontName=" + font.fontName + " fontStyle=" + font.fontStyle + " fontType=" + font.fontType);
    }
  }
}
実行するとFont.registerFont()でエラーになる。しかし、vernumのtraceが正常に表示されるため、FontPackはNULLではないし、ある程度は機能していることは分かる。

しかし、なぜエラーになるのかサ〜ッパリ分からず、1日目は詰んだ(*´Д`)


2日目。気を取り直して考えてみると、ひょっとするとセキュリティー関連の問題ではないかと思った。Flashでは画像を読み込めても、セキュリティーの問題で表示できないことがあるから。

そこでセキュリティー問題をクリアにするため、AIRプロジェクト内にFontPack.swfを配置してローカル読み込みできるようにして、urlを「http〜」でなく「FontPack.swf」として読み込む。

すると、Font.enumerateFonts()でAquaFontが列挙された。つまり、AIRでも動的に埋め込みフォントを読み込むことは可能だということがわかった。

あとは、どうやってこのセキュリティーサンドボックスを抜けてhttp経由で読み込むかだけだ。

これについては以前にも同様の経験があって、一旦URLLoaderでバイナリを取得してそのバイナリをLoaderで読み込めばいいことがわかっている。

つまり、こんな感じ。
protected function onClickBtnFontPackDL():void {
  var url:String = "http://path/to/FontPack.swf";
  var urlLoader:URLLoader = new URLLoader();
  urlLoader.dataFormat = URLLoaderDataFormat.BINARY;
  urlLoader.addEventListener(Event.COMPLETE, onCompleteUrlLoader);
  urlLoader.load(new URLRequest(url));
  function onCompleteUrlLoader():void {
    var ba:ByteArray = urlLoader.data;
    var lc:LoaderContext = new LoaderContext();
    lc.allowLoadBytesCodeExecution = true;
    var loader:Loader = new Loader();
    loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onCompleteLoader);
    loader.loadBytes(ba, lc);
    function onCompleteLoader():void {
      var FontPack:Class = loader.contentLoaderInfo.applicationDomain.getDefinition("FontPack") as Class;
      trace("onCompleteLoader() vernum=" + FontPack.vernum);
      Font.registerFont(FontPack.AquaFont);
      var fonts:Array = Font.enumerateFonts(false);
      for (var i:Number = 0; i < fonts.length; i++) {
        var font:Font = fonts[i];
        trace("onCompleteLoader() : [" + i + "] fontName=" + font.fontName + " fontStyle=" + font.fontStyle + " fontType=" + font.fontType);
      }
    }
  }
}
これで、AIRでもフォントが埋め込まれたswfをランタイムにhttp経由で読み込んでFont.registerFont()できることを確認した。