ARCHICADのGSMファイルにGDLで画像を埋め込む方法

# はじめに
以下のYoutube動画で解説されている方法にメモを加えたものになります。
[https://www.youtube.com/watch?v=nLOsXR_m8-A](https://www.youtube.com/watch?v=nLOsXR_m8-A)
# 環境
OS：Windows 10
ARCHICAD：24
# GDL(Geometric Description Language)とは
本題ではないので軽く紹介だけ。
建築用BIMソフト[ARCHICAD](https://graphisoft.com/jp/solutions/products/archicad)に、新規にライブラリを作ったり、オブジェクトを改造したりするのに使える言語です。
これを書くことでオブジェクトの描画や、オブジェクトのインターフェース画面の改造などいろいろなことができます。
公式のリファレンスガイドは[こちら](https://help.graphisoft.com/AC/24/JPN/GDL.pdf)
## どこに書くのか
<img width="40%" alt="オブジェクトを開く" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/343311/fd00e3f4-19e1-0c58-3621-bafc15f9b852.png">

これで「新規オブジェクト」または「オブジェクトを開く」で以下のタブを開くことができます。

<img width="40%" alt="オブジェクトを開いた" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/343311/9b4ebd38-fa2a-6403-b497-4df1cff10909.png">

上記の赤枠部分をクリックすることでスクリプトを書き込むことができます。
2Dは平面図や立面図、3Dは3D図、インターフェースは選択したオブジェクトの設定画面…といった具合にそれぞれで設定していきます。

# 画像の埋め込み
GSMオブジェクトに画像を埋め込むと「埋め込みライブラリ」で画像ファイルを別途持っておく必要がなくなるので便利になります。
埋め込みを行うタイミングと、スクリプト内に書き込むタイミングはどちらが先でも大丈夫です。スクリプト書き込み→画像埋め込み→スクリプト修正、としても大丈夫です。
## GSMファイル→XMLファイルに変換
まず、ARCHICADインストール時についてくる「LP_XMLConverter」というツールを使って画像を埋め込みたい対象のGSMファイルをXMLファイルに変換します。
```Gsm2Xml.py
import os
import subprocess

converterPath = "C:\Program Files\GRAPHISOFT\ARCHICAD 24\LP_XMLConverter"

srcDir = os.path.dirname(os.path.abspath(__file__)) + "\Binary"
outDir = os.path.dirname(os.path.abspath(__file__)) + "\Xml"

command = "l2x"
result = subprocess.run([converterPath, command, srcDir, outDir])
```
※参考にしたYoutube動画で書かれているPythonスクリプトのパスを私の環境に合わせて書き換えたものです。
LP_XMLConverter.exeの場所は念のため確認しておいてください。
以下のように対象となるGSMファイルを指定のフォルダ内に置いてGsm2Xml.pyを実行してください。
test1, test2はこのあと埋め込む画像ファイルです。
下の例ではtargetObject.gsm一つだけですが、複数置くことで複数変換することができます。

>    Gsm2Xml.py
>├─Binary
>│      targetObject.gsm
>├─Img
>│      test1.png
>│      test2.png
>└─Xml
>

するとXmlフォルダ内にtargetObject.xmlファイルが作られます。これに対して埋め込みコードを記入します。

## 画像を埋め込む
targetObject.xmlファイルを開き、埋め込みたい画像の情報を`<GDLPict>`タグで書き込みます。
書き込む場所は`<Symbol>...</Symbol>`で囲まれている中であればどこでも大丈夫ですが、他のタグに割り込まないように気を付けましょう。
```targetObject.xml
<GDLPict MIME="image/png" path="test1.png" SectVersion="19" SectionFlags="0" SubIdent="1" />
<GDLPict MIME="image/png" path="test2.png" SectVersion="19" SectionFlags="0" SubIdent="2" />
```
- MIME
    - この例ではpng画像なので`MIME="image/png"`としていますが、jpg画像なら`MIME="image/jpeg"`、svg画像なら`MIME="image/svg"`とします。
- path
    - 画像の名前です。画像までのパスはコマンド実行時に指定します。
- SectVersion
    - 19固定です。18, 20の例もありますが何の数字なのかはググっても解決せず…古いARCHICADだと小さい数字にしにないといけないようだけど…。
- SectionFlags
    - ベクター画像かどうかのフラグ。svg画像などベクター画像の場合は`SectionFlags="1"`、ラスター画像であれば`SectionFlags="0"`とします。
- SubIdent
    - スクリプトで各画像を示すための番号です。各画像で一意になるようにしましょう。
## XMLファイル→GSMファイルに変換
`<GDLPict>`で画像の情報を書き込んだtargetObject.xmlを、画像が埋め込まれたGSMファイルに変換します。
コードは以下です。
```Xml2Gsm.py
import os
import subprocess

converterPath = "C:\Program Files\GRAPHISOFT\ARCHICAD 24\LP_XMLConverter"

srcDir = os.path.dirname(os.path.abspath(__file__)) + "\Xml"
outDir = os.path.dirname(os.path.abspath(__file__)) + "\RevBinary"

command = "x2l"
img = "-img"
imgDir = os.path.dirname(os.path.abspath(__file__)) + "\Img"

result = subprocess.run([converterPath, command, img, imgDir, srcDir, outDir])
```
これを実行すると画像データが埋め込まれたGSMファイルがRevBinaryフォルダ内に作られます。
## 埋め込まれた画像を使う
埋め込まれた画像は通常の埋め込みライブラリと同じように配置・サイズの変更ができるようになります。
たとえば、先ほどのtest1.png, test2.pngをインターフェース画面で使用したい場合は以下のようになります。それぞれの画像が`1`、`2`といった数字で表されています。
```
ui_pict 1, 280,  50, 57, 39, 1
ui_pict 2, 280, 180, 57, 39, 1
ui_pict <SubIdentの数字>, <x座標>, <y座標>[, <width>, <height>[, <mask>]]
```
## 埋め込んだ画像を修正したい
埋め込む画像を間違えたときは最初からやり直してもいいし、返還後のGSMファイルをまた変換してもいいです。
RevBianryのGSMファイルをXMLファイルに変換すると、GDLPict部分にCDATAセクションが増えており、この部分が画像情報だということが分かるかと思います。間違えた画像ファイルをGDLPictタグごと削除し、もう一度書き直してからGSMファイルに変換しなおしましょう。

# おわりに
やってみたけど動かない、間違っているなどあればお知らせください。