swfmillで出力されるXMLを解析してみよう

By ookura - 09/02/07 - このエントリをはてなブックマークに追加このエントリをYahoo!ブックマークに追加このエントリをdel.icio.usに追加このエントリをFC2ブックマークに追加

これまでswfmillでSWFファイルをXMLに変換して内容を置換して出力する方法をいくつか紹介しましたが、
今回はSWFをXMLに変換した際のXMLの内容について解析してみた結果を説明します。
 
 


XMLへの変換

XMLへの変換はコマンドラインであれば以下のコマンドでできます。
(例:test.swfをtest.xmlに変換する場合)
FlashLite1.1以下の場合

swfmill -e cp932 swf2xml test.swf test.xml

FlashLite2.0以降の場合

swfmill swf2xml test.swf test.xml

 
 


解析に使用したSWFファイルの仕様

確認用に以下のようなSWFファイルを作成しました。
[図82]
 
・パブリッシュバージョンはFlash Lite 1.1
 
・フレームレートは12fps
 
・画面サイズは240×24px
 
・背景色は#FFFFFF
 
・X:20px,Y:20pxの位置に静止テキストを配置
(フォントサイズ12px,フォントは「_等幅」,カラーは#000000)
 
・X:20px,Y:60pxの位置にダイナミックテキストを配置
(フォントサイズ12px,フォントは「_等幅」,カラーは#000000)
 
・X:20px,Y:80pxの位置にテキスト入力を配置
(フォントサイズ12px,フォントは「_等幅」,カラーは#000000)
 
・X:20px,Y:100pxの位置に静止テキストを配置
(フォントサイズ24px,フォントは「HGP創英角ポップ体」,カラーは#000000)
 
・X:20px,Y:140pxの位置にボタンを配置。ボタンの内部には
テキスト(フォントサイズ12px,フォントは「_等幅」,カラーは#FFFFFF)と
シェイプ(サイズは80×20px,カラーは#009900,枠なし)を配置。
[図83]
アクションで以下指定。

on (release) {
    getURL("link1.html");
}

[図84]
 
・X:20px,Y:180pxの位置にビットマップ画像(2×2pxのJPEG画像を40×40px)に拡大したものを配置
 
 


 
このSWFファイルをXMLに変換すると以下のようになりました。
 
sample2476.xml
 
ではXMLの内容について順番に見ていきましょう。
 
 


XMLファイルの全体的な構造

全体的な構造については以下のようになっていました。

<?xml version="1.0" encoding="UTF-8"?>
<swf> (バージョン・圧縮状態を定義)
  <Header> (フレームレート・フレーム数を定義)
    <size/> (ステージサイズを定義)
    <tags>
      <SetBackgroundColor/> (背景色を定義)
      <DefineFont2 objectID="1"/> (等幅フォントを定義)
      <DefineEditText objectID="2" fontRef="1"/> (静止テキストを定義)
      <PlaceObject2 objectID="2"/> (静止テキストを配置)
      <DefineEditText objectID="3" fontRef="1"/> (ダイナミックテキストを定義)
      <PlaceObject2 objectID="3"/> (ダイナミックテキストを配置)
      <DefineEditText objectID="4" fontRef="1"/> (テキスト入力を定義)
      <PlaceObject2 objectID="4"/> (テキスト入力を配置)
      <DefineShape objectID="5"/> (ボタンのシェイプを定義)
      <DefineEditText objectID="6" fontRef="1"/> (静止テキストを定義)
      <DefineButton2 objectID="7"> (ボタンを定義)
        <buttons>
          <Button objectID="5"/"> (シェイプを配置)
          <Button objectID="6"/"> (静止テキストを配置)
          <Button />
        </buttons>
        <conditions> (ボタンイベントの条件を定義)
          <Condition>
            <actions/> (アクションの内容を定義)
          </Condition>
        </conditions>
      </DefineButton2>
      <PlaceObject2 objectID="7"/> (ボタンを配置)
      <DefineFont2 objectID="8"/> (アウトラインフォントを定義)
        <DefineText objectID="9"> (静止テキストを定義)
        <records>
          <TextRecord>
            <records>
              <TextRecord6 objectID="8"/> (静止テキストのフォント・カラーを指定)
            </records>
          </TextRecord>
        </records>
      </DefineText>
      <PlaceObject2 objectID="9"/> (静止テキストを配置)
      <DefineBitsJPEG2 objectID="10"/> (ビットマップ画像を定義)
      <DefineShape objectID="11"> (ビットマップ画像を配置するシェイプを定義)
        <bounds/> (シェイプサイズを指定)
        <styles>
          <StyleList>
          <fillStyles>
            <ClippedBitmap objectID="10"/> (ビットマップ画像をシェイプに配置)
            </fillStyles>
          </StyleList>
        </styles>
        <shapes/> (シェイプの形状を定義)
      </DefineShape>
      <PlaceObject2 objectID="11"> (ビットマップ画像が配置されたシェイプを配置)
      <ShowFrame/> (フレームの区切り)
      <End/> (フレーム終了)
    </tags>
  </Header>
</swf>

SWF内の各パーツはオブジェクトとして扱われ、objectIDで管理されているようです。
 
それぞれのオブジェクトが定義して配置されることの繰り返しで構成されていることがわかるかと思います。
 
 


基本要素

・パブリッシュバージョン

<swf version="4" compressed="0">

FlashLite1.1で生成するとXMLではバージョンは「4」となります。
FlashLite2.0の場合は「7」になります。
 
 
・フレームレート

<Header framerate="12" frames="1">

「framerate」の部分にそのままフレームレートが入ります。
「frames」の値は総フレーム数のようです。
 
 
・ステージサイズ

<size>
  <Rectangle left="0" right="4800" top="0" bottom="4800"/>
</size>

横サイズがrightに、縦サイズはbottomに入ります。
240pxで作成しましたので、この値は20倍されているようです。
 
 
・背景色

<SetBackgroundColor>
  <color>
    <Color red="255" green="255" blue="255"/>
  </color>
</SetBackgroundColor>

「SetBackgroundColor」でred,green,blueの各要素別に0~255の十進数で指定されています。
「#FFFFFF」(白)で指定しましたので全ての要素が「255」になっています。
 
 


テキスト

・静止テキスト・ダイナミックテキスト・テキスト入力
まずはデバイスフォントの場合です。

<DefineFont2 objectID="1" isShiftJIS="1" isUnicode="0" isANSII="0" wideGlyphOffsets="0" italic="0" bold="0" language="0" name="_等幅">
  <glyphs/>
</DefineFont2>

以下の2テキストも含めて「_等幅」フォントを指定していますので、最初にフォントが定義されます。
 
「objectID」で指定したIDがテキスト側の「fontRef」で参照されています。

<DefineEditText objectID="2" wordWrap="0" multiLine="1" password="0" readOnly="1" autoSize="0" hasLayout="1" notSelectable="1" hasBorder="0" isHTML="0" useOutlines="0" fontRef="1" fontHeight="240" align="0" leftMargin="0" rightMargin="0" indent="0" leading="40" variableName="" initialText="静止テキスト 改行&#13;&lt;&gt;&amp;&quot;' 特殊文字&#13;">
  <size>
    <Rectangle left="-40" right="1674" top="-40" bottom="320"/>
  </size>
  <color>
    <Color red="0" green="0" blue="0" alpha="255"/>
  </color>
</DefineEditText>

「DefineEditText」でテキストが定義され、そのプロパティの一つ「initialText」でテキストの内容が定義されています。
テキストの中で以下の文字は別の文字に変換(エスケープ)されます。
「<」→「&lt;」
「>」→「&gt;」
「&」→「&amp;」
「"」→「&quot;」
※シングルクォート「'」はエスケープされないようです。
改行コード→「&#013;」
静止テキストに関しては文字列の最後にも改行コードが入ります。
 
フォントサイズが指定されている場所はなく、「Rectangle」で最終的なテキストのサイズが指定されています。
 
文字色は「Color」で指定されています。
red,green,blueの要素以外に透明度を表す「alpha」も0~255の10進数で指定されています。
 

<PlaceObject2 replace="0" depth="1" objectID="2">
  <transform>
    <Transform transX="440" transY="440"/>
  </transform>
</PlaceObject2>

文字の配置位置は「PlaceObject2」で指定されます。座標は「Transform」で指定され、値は実際の座標×20+20となっています。
「depth」はレイヤーや重ね順を表しており、値が小さいほど重ね順が後ろになります。
 
 
ダイナミックテキスト・テキスト入力は静止テキストとほとんど同じで、以下のプロパティが異なっていました。

  readOnly notSelectable
静止テキスト 1 1
ダイナミックテキスト 1 0
テキスト入力 0 0

 
 


埋め込みフォントの場合はフォントのアウトライン情報を含むため、デバイスフォントと異なった形式になります。

<DefineFont2 objectID="8" isShiftJIS="1" isUnicode="0" isANSII="0" wideGlyphOffsets="0" italic="0" bold="0" language="0" name="HGP創英角ポップ体">
  <glyphs>
    <Glyph map="33440">
      <GlyphShape>
        <edges>
          <ShapeSetup x="328" y="-580" fillStyle0="1"/>
          <LineTo x="-240" y="36"/>
          <LineTo x="12" y="-144"/>
          (中略)
          <LineTo x="96" y="-92"/>
          <LineTo x="0" y="0"/>
          <ShapeSetup/>
        </edges>
      </GlyphShape>
    </Glyph>
  </glyphs>
</DefineFont2>

ここまでがフォントのアウトラインデータになります。
 

<DefineText objectID="9">
  <bounds>
    <Rectangle left="14" right="626" top="31" bottom="445"/>
  </bounds>
  <transform>
    <Transform transX="0" transY="0"/>
  </transform>
  <records>
    <TextRecord>
      <records>
        <TextRecord6 objectID="8" x="420" fontHeight="480">
          <color>
            <Color red="0" green="0" blue="0"/>
          </color>
        </TextRecord6>
        <TextRecord6>
          <glyphs>
            <TextEntry glyph="0" advance="450"/>
          </glyphs>
        </TextRecord6>
        <TextRecord6>
          <glyphs/>
        </TextRecord6>
      </records>
    </TextRecord>
  </records>
</DefineText>
<PlaceObject2 replace="0" depth="7" objectID="9">
  <transform>
    <Transform scaleX="1.000000000000000" scaleY="1.001785278320312" transX="440" transY="1640"/>
  </transform>
</PlaceObject2>

ここまでがテキストの定義と配置のXMLになります。
埋め込みフォントで指定した文字列の場合はサイズやカラー・座標の情報はありますが、テキストの文字列の内容がどのような内容であるかについてはXMLに含まれません。
 
 


ボタン

ここで配置したボタンには以下の要素が含まれています。
・シェイプ
・静止テキスト
・ボタンオブジェクト
・アクション
 
順に見ていきます。
 
・シェイプ

<DefineShape objectID="5">
  <bounds>
    <Rectangle left="0" right="1600" top="0" bottom="400"/>
  </bounds>
  <styles>
    <StyleList>
      <fillStyles>
        <Solid>
          <color>
            <Color red="0" green="153" blue="0"/>
          </color>
        </Solid>
      </fillStyles>
      <lineStyles/>
    </StyleList>
  </styles>
  <shapes>
    <Shape>
      <edges>
        <ShapeSetup x="1600" y="0" fillStyle1="1"/>
        <LineTo x="0" y="400"/>
        <LineTo x="-1600" y="0"/>
        <LineTo x="0" y="-400"/>
        <LineTo x="1600" y="0"/>
        <ShapeSetup/>
      </edges>
    </Shape>
  </shapes>
</DefineShape>

 
カラーは「Color」で、サイズは「Rectangle」で、シェイプのベクターデータは「ShapeSetup」で指定されています。
 
 
・静止テキスト

<DefineEditText objectID="6" wordWrap="0" multiLine="1" password="0" readOnly="1" autoSize="0" hasLayout="1" notSelectable="1" hasBorder="0" isHTML="0" useOutlines="0" fontRef="1" fontHeight="240" align="0" leftMargin="0" rightMargin="0" indent="0" leading="40" variableName="" initialText="ボタン&#13;">
  <size>
    <Rectangle left="-40" right="864" top="-40" bottom="320"/>
  </size>
  <color>
    <Color red="255" green="255" blue="255" alpha="255"/>
  </color>
</DefineEditText>

静止テキストについては前述の通りです。
 
 
・ボタン/アクション

<DefineButton2 objectID="7" menu="0" buttonsSize="20">
  <buttons>
    <Button hitTest="1" down="1" over="1" up="1" objectID="5" depth="1">
      <transform>
        <Transform transX="0" transY="0"/>
      </transform>
      <colorTransform>
        <ColorTransform2/>
      </colorTransform>
    </Button>
    <Button hitTest="1" down="1" over="1" up="1" objectID="6" depth="2">
      <transform>
        <Transform transX="418" transY="80"/>
      </transform>
      <colorTransform>
        <ColorTransform2/>
      </colorTransform>
    </Button>
    <Button hitTest="0" down="0" over="0" up="0"/>
  </buttons>
  <conditions>
    <Condition next="0" menuEnter="0" pointerReleaseOutside="0" pointerDragEnter="0" pointerDragLeave="0" pointerReleaseInside="1" pointerPush="0" pointerLeave="0" pointerEnter="0" key="0" menuLeave="0">
      <actions>
        <GetURL url="link1.html" target=""/>
        <EndAction/>
      </actions>
    </Condition>
  </conditions>
</DefineButton2>
<PlaceObject2 replace="0" depth="4" objectID="7">
  <transform>
    <Transform transX="400" transY="2400"/>
  </transform>
</PlaceObject2>

ボタンを定義しています。
ボタンに使用しているシェイプと静止テキストを配置し、それぞれの「アップ(up)」「オーバー(over)」「ダウン(down)」「ヒット(hitTest)」フレームでの配置状況を設定しています。
 
ボタンアクションは「conditions」で定義しています。
アクションスクリプトをそのまま記述している訳ではなく、タグで構造化されています。
「conditions」はonアクションであることを表し、イベント条件が「release」なので「pointerReleaseInside」が「1」になっています。
「action」内で実行アクションが記述されます。今回は「getURL」アクションなのでURLが指定されています。
 
最後に「PlaceObject2」でボタンを配置しています。
 
 


ビットマップ画像

<DefineBitsJPEG2 objectID="10">
  <data>
    <data>/9n/2P/Y/+AAEEpGSUYAAQEBAEgASAAA/9sAQwAGBAQEBQQGBQUGCQYFBgkLCAYGCAsMCgoLCgoMEAwMDAwMDBAMDg8QDw4MExMUFBMTHBsbGxwgICAgICAgICAg/9sAQwEHBwcNDA0YEBAYGhURFRogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg/8AAEQgAAgACAwERAAIRAQMRAf/EABQAAQAAAAAAAAAAAAAAAAAAAAf/xAAdEAACAgMAAwAAAAAAAAAAAAACAwQFAQYHABEh/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhEDEQA/AF/nvPdBuNB1q3t9aqrG2saqFLsLCXCjvkSJD44Ma5zWARsYwyyREWfec/c+B//Z</data>
  </data>
</DefineBitsJPEG2>
<DefineShape objectID="11">
  <bounds>
    <Rectangle left="400" right="1200" top="3200" bottom="4000"/>
  </bounds>
  <styles>
    <StyleList>
      <fillStyles>
        <ClippedBitmap objectID="65535">
          <matrix>
            <Transform scaleX="20.00000000000000" scaleY="20.00000000000000" transX="0" transY="0"/>
          </matrix>
        </ClippedBitmap>
        <ClippedBitmap objectID="10">
          <matrix>
            <Transform scaleX="-400.0021362304688" scaleY="-399.9969482421875" transX="1200" transY="4000"/>
          </matrix>
        </ClippedBitmap>
      </fillStyles>
      <lineStyles/>
    </StyleList>
  </styles>
  <shapes>
    <Shape>
      <edges>
        <ShapeSetup x="1200" y="3200" fillStyle1="2"/>
        <LineTo x="0" y="800"/>
        <LineTo x="-800" y="0"/>
        <LineTo x="0" y="-800"/>
        <LineTo x="800" y="0"/>
        <ShapeSetup/>
      </edges>
    </Shape>
  </shapes>
</DefineShape>
<PlaceObject2 replace="0" depth="8" objectID="11">
  <transform>
    <Transform transX="0" transY="0"/>
  </transform>
</PlaceObject2>

 
画像のバイナリデータは「DefineBitsJPEG2」の「data」で指定しています。
このデータはJPEG画像データのバイナリデータをBase64エンコードしたものと一致します。
但しFlashLite1.1の場合はエンコード前に頭にバイナリデータ「FFD9FFD8」を入れる必要があるようです。
(FlashLite2.0以上の場合は不要)
 
SWF内ではビットマップデータをそのまま配置している訳ではなく、別に用意したシェイプに組み込み、シェイプを配置しているようです。
 
SWFで扱われる画像データにはこれ以外にもGIF画像やPNG画像を表すロスレス画像もあります。
この場合は「DefineBitsLossless」でデータが定義されますが、画像バイナリデータの構造については調査中です。
 
 


XMLファイルの活用方法

ここまでのXMLの内容解析を元に、どのような活用方法があるかを考えます。
 
・テキストオブジェクトの文字列の置換
デバイスフォントでテキストオブジェクトを配置していれば、文字列置換することで内部の文字列を変更することが可能です。
静止テキストに限らずダイナミックテキストでもテキスト入力でも同様です。
ただし、改行コードや一部の特殊文字については以下のようにエンコードする必要があります。
「<」→「&lt;」
「>」→「&gt;」
「&」→「&amp;」
「"」→「&quot;」
改行コード→「&#013;」
また、静止テキストは最後に改行コード「&#013;」が入っていますので注意が必要です。
 
埋め込みフォントを指定したテキストオブジェクトに関しては元の文字列がXML内に入らないため、単純に文字列置換する方法は使えません。
フォントのデザインを重視する、動的変更の必要がない部分に限って使うことになります。
どうしても変更が必要であればあらかじめ必要と考えられるフォントを埋め込んでおき、actionscriptで差し替える方法が有効かと思われます。
なお、アウトラインフォントはデータが大きくなりますので特に必要でなければシェイプに分解しておくとサイズを抑えることができます。
 
 


・スクリプト内の文字列の変更
スクリプト自体の書き換えは難しいですが、内部の文字列の変更であれば上記の文字列置換と同様のことが可能です。
具体的にはgetURLアクションのリンク先URLやloadVariablesアクションの読み込み先URLも変更できます。
最初のフレームアクションに文字列を指定しておいて使う場合も文字列を変更できます。
 
 


・ビットマップ画像の置換
JPEG画像のようなビットマップ画像であれば入れ替えが可能です。
SWF内に埋め込んだ画像と同じものと入れ替えたい画像のバイナリデータをそれぞれ読み込んでbase64エンコードし、文字列置換するのが簡単です。
※FlashLite1.1の場合はバイナリデータの頭にバイナリデータ「FFD9FFD8」を追加する必要があります。
※GIFやPNGのようなロスレス画像も似たような原理で可能と思われますが、バイナリの形式については現状不明なのでまた調べておきます。
 
 


・文字列色・背景色・シェイプカラーの変更
変更したいカラーコードをRGBの各要素10進数の値に変換して文字列置換すれば可能です。
 
 


・フレームレートの変更
表示端末のスペックに応じてフレームレートを動的に変更することも可能です。
フレームレートは数値を変更するだけで変更できます。
 
 


・備考
置換処理に関してはテキスト・画像・配色共に、元のSWFファイル内で同じ文字列となってしまう場合は全て置換されてしまいますので、元のデータがユニークである必要があります。
テキストに関しては変数名的なものを入れておけばよいですし、
画像に関しては変数名のテキストを貼り付けた画像を用意すると分かりやすいはずです。
配色に関しては固有の配色を設定しておき、配色設定ごと置換してしまうのが簡単かと思われます。
 
 


まとめ

今回は少し長くなってしまいましたが、以上の解説でswfmillで生成されたXMLを理解することでswfmillでできること、できないことについての理解を深めていただければ幸いです。
 
 


Flashサイト動的生成関連の記事一覧

1.ケータイFlashの概要
 1-1 ケータイFlashの概要について
 1-2 ケータイFlashサイトを作る上で参考になる書籍の紹介
 
2.ケータイFlashのページ作成
 2-1 ケータイFlashのページを作ってみよう
 2-2 ケータイFlashで縦スクロールするページを作ってみよう
 2-3 ケータイFlashで縦スクロールするページを作ってみよう(3ボタン制御編)
 
3.swfmillでケータイFlashを動的生成
 3-1 swfmillでケータイFlashを動的生成してみよう(インストール編)
 3-2 swfmillでケータイFlashを動的生成してみよう(文字列置換編)
 3-3 swfmillでケータイFlashを動的生成してみよう(画像置換編)
 3-4 ケータイFlashのレイアウトを文章量に同期させよう
 3-5 swfmillで出力されるXMLを解析してみよう
 
4.ActionScriptとPHPの連携
 4-1 ActionScriptで文字列をPHPから動的に取得しよう
 4-2 ケータイFlashでテキスト入力フォームを作ろう