DXFのDICTIONARYとXRECORDを読む|OBJECTSセクションと任意データの保存構造

「DXFのDICTIONARYとXRECORDを読む|OBJECTSセクションと任意データの保存構造」の内容を表す技術イラスト

DXFに保存されている情報は、画面に表示される線、円、文字、寸法だけではありません。

図面全体の設定、グループ定義、レイアウト情報、アプリケーション固有データなど、画面上に直接描画されない情報も含まれています。

こうした非図形データの多くが格納される場所がOBJECTSセクションです。

その中でも、独自の設計情報やアプリケーションデータを扱ううえで重要なのが、

  • DICTIONARY
  • XRECORD

です。

概念的な関係は次のようになります。

OBJECTSセクション
    ↓
DICTIONARY
    ↓ 名前とハンドルで参照
XRECORD
    ↓
文字列・数値・座標・参照などの任意データ

この記事では、AutodeskのDXF仕様を基準に、DICTIONARYとXRECORDの構造、所有関係、主要グループコード、XDATAとの違い、Pythonでの解析方法を整理します。

目次

OBJECTSセクションとは

DXFの主要セクションには、次のような役割があります。

セクション主な役割
HEADER図面全体の設定値
TABLESレイヤー、線種、文字スタイルなど
BLOCKSブロック定義
ENTITIES線、円、文字などの図形
OBJECTS辞書、レイアウト、画像定義などの非図形オブジェクト

ENTITIESセクションの要素は、基本的に図面空間やモデル空間へ表示されます。

一方、OBJECTSセクションのオブジェクトは、図形として表示されることを目的としていません。

たとえば、次のような情報が含まれます。

  • 名前付きオブジェクト辞書
  • グループ定義
  • MLINEスタイル
  • レイアウト情報
  • 画像定義
  • マテリアル
  • アプリケーション固有データ
  • エンティティの拡張辞書から参照されるデータ

したがってDXFを設計データとして再利用するには、ENTITIESだけでなくOBJECTSも解析対象にする必要があります。

DICTIONARYは名前とオブジェクトを対応付ける

DICTIONARYは、名前をキーとして別のDXFオブジェクトを参照するためのコンテナです。

プログラミングの辞書型に近い構造として考えられます。

キー名
    ↓
参照先オブジェクトのハンドル

たとえば、

EKPLABZ_DATA → ハンドル2B
MACHINE_INFO → ハンドル35
CHECK_RESULT → ハンドル48

という対応関係を保持できます。

ezdxfのドキュメントでも、DICTIONARYの要素は「キーとDXFオブジェクトの組」として説明されています。

ただし、通常のプログラミング言語の辞書と異なり、値そのものを直接格納するのではありません。DXF内の別オブジェクトをハンドルで参照します。

DICTIONARYの主要グループコード

AutodeskのDXF Referenceでは、DICTIONARYの主要コードを次のように定義しています。

グループコード意味
0オブジェクト名DICTIONARY
5DICTIONARY自身のハンドル
330所有元オブジェクトへの参照
100サブクラスマーカーAcDbDictionary
280hard-ownerフラグ
281重複レコードのクローン処理
3エントリ名
350エントリオブジェクトへの参照

辞書の各要素は、コード3と350の組で表されます。

3
EKPLABZ_DATA
350
2B

これは、

名前:EKPLABZ_DATA
参照先ハンドル:2B

という意味です。

辞書に複数の要素がある場合、コード3と350が繰り返されます。

3
MACHINE_INFO
350
2B
3
CHECK_RESULT
350
35

解析時には、コード3を見つけたら、その後に対応する参照ハンドルを組にして保存します。

コード280はhard-ownerフラグ

コード280は、辞書内の要素をhard-ownedとして扱うかどうかを表します。

値意味
0hard-ownedとして扱わない
1hard-ownedとして扱う

hard-ownedの要素は、所有する辞書の寿命と強く結び付いています。

ezdxfでは、hard ownerである辞書を削除すると、その辞書が所有するオブジェクトも削除されると説明されています。

したがって、辞書要素を解析するときは、

{
  "name": "EKPLABZ_DATA",
  "target_handle": "2B",
  "hard_owned": true
}

のように、参照先だけでなく所有形式も保持する方が安全です。

参照切れを検出した場合も、単純に辞書要素を削除してはいけません。所有関係によっては別オブジェクトの寿命へ影響するためです。

コード281は重複時の処理を表す

コード281は、別図面へのコピーや結合時に同じ名前の要素が存在した場合の処理方法を表します。

Autodesk仕様では次の値が定義されています。

値意味
0適用なし
1既存要素を維持
2複製側を使用
3<xref>$0$<name>形式
4$0$<name>形式
5名前のマングルを解除

この値は、通常のDXF読み取りだけでは目立ちません。

しかし、

  • XREF
  • INSERT
  • WBLOCK
  • 図面間コピー
  • 複数DXFの統合

を扱う場合に重要になります。

独自変換ツールで辞書名が重複したとき、任意に上書きするとアプリケーションデータを失う可能性があります。コード281と対象CADの動作を確認し、衝突処理を明示的に設計します。

XRECORDは任意データを保持する

XRECORDは、アプリケーションが定義した任意データを格納するためのオブジェクトです。

DICTIONARYが名前と参照先を管理し、XRECORDが実際の値を保持する、と考えると分かりやすくなります。

DICTIONARY
    └── EKPLABZ_DATA
            └── XRECORD
                    ├── 文字列
                    ├── 整数
                    ├── 実数
                    ├── 座標
                    └── ハンドル参照

Autodesk仕様では、XRECORDのアプリケーションデータとして、コード5と105を除く1〜369のグループコードを利用できます。

コードの型規則は通常のDXFタグと同じです。

コード例値の例
1文字列
10・20・30座標
40浮動小数点数
7016ビット整数
9032ビット整数
310バイナリチャンク
330〜369ハンドル参照

重要なのは、XRECORDの内容が固定スキーマではないことです。

同じコード1でも、あるアプリケーションでは部品名、別のアプリケーションではJSON文字列を表す可能性があります。

データの意味は、作成したアプリケーション側で定義する必要があります。

XRECORDの主要グループコード

XRECORD自体の共通構造には、次のコードがあります。

グループコード意味
0オブジェクト名XRECORD
5XRECORD自身のハンドル
330所有するDICTIONARYへの参照
100サブクラスマーカーAcDbXrecord
280重複レコードのクローン処理
1〜369アプリケーションデータ

XRECORDのコード280は、DICTIONARYのコード281と同様に、重複データを統合するときの処理を表します。

一方、コード280より後ろにある任意タグ列は、アプリケーションが定義したデータです。

この二つを同じデータとして混在させず、

XRECORDの管理属性
+
アプリケーションデータ

に分離して保持します。

DICTIONARYとXRECORDのDXF例

次の例では、DICTIONARYにEKPLABZ_DATAという名前を登録し、ハンドル2BのXRECORDを参照しています。

0
DICTIONARY
5
2A
330
0
100
AcDbDictionary
280
0
281
1
3
EKPLABZ_DATA
350
2B

0
XRECORD
5
2B
330
2A
100
AcDbXrecord
280
1
1
HYDRAULIC_CYLINDER
90
3
40
80.0
40
32.0
40
500.0

XRECORDの任意データ部分をアプリケーション側で、

1  → データ種類
90 → パラメータ数
40 → シリンダ内径
40 → ロッド径
40 → ストローク

と定義したとします。

この場合は、

{
  "data_type": "HYDRAULIC_CYLINDER",
  "parameter_count": 3,
  "values": [80.0, 32.0, 500.0]
}

へ変換できます。

ただし、これは記事内のデータ設計例です。DXF仕様そのものがコード40の順番をシリンダ寸法として定義しているわけではありません。

他のソフトがこのXRECORDを理解するには、別途スキーマ定義が必要です。

同じグループコードを上書きしない

XRECORDでは、同じグループコードが複数回現れることがあります。

先ほどの例でもコード40が3回あります。

通常の辞書へ直接変換すると、

data[40] = value

のように前の値が上書きされ、最後の500.0しか残りません。

Rawデータでは、必ず順序付きタグ列として保持します。

xrecord_tags = [
    (1, "HYDRAULIC_CYLINDER"),
    (90, 3),
    (40, 80.0),
    (40, 32.0),
    (40, 500.0),
]

その後、アプリケーション固有スキーマに従って意味を付けます。

Raw Tags
    ↓
スキーマ識別
    ↓
型検証
    ↓
意味のある設計データへ変換

この二段階を分ければ、未知のXRECORDも破壊せずに保持できます。

名前付き辞書と拡張辞書

DXFの辞書には、図面全体で利用される名前付きオブジェクト辞書と、個別エンティティに関連付けられる拡張辞書があります。

名前付きオブジェクト辞書

図面全体のルートとなる辞書です。

そこから、

  • グループ定義
  • MLINEスタイル
  • レイアウト
  • 印刷関連設定
  • アプリケーション独自辞書

などが参照されます。

独自データを図面単位で管理したい場合は、名前付きオブジェクト辞書配下に専用のサブ辞書を設ける方法があります。

拡張辞書

特定のLINE、INSERT、BLOCK_RECORDなどへ追加情報を関連付けたい場合は、拡張辞書を利用できます。

エンティティ側では、コード102で囲まれたACAD_XDICTIONARYグループと、コード360のハンドル参照によって拡張辞書へ接続されます。

図形エンティティ
    ↓ コード360
拡張DICTIONARY
    ↓ 名前付き参照
XRECORD

これにより、図形の標準グループコードを変更せず、検査結果、部品ID、外部DBキーなどを関連付けられます。

XDATAとの違い

任意データを保存する方法にはXDATAもあります。

両者の基本的な違いは次のとおりです。

項目XDATAXRECORD
主な関連先エンティティへ直接付加DICTIONARYから参照
識別APPID辞書のエントリ名
主なコード範囲1000番台1〜369
データ構造XDATA専用タグ通常のDXFタグに近い
管理方法エンティティ単位辞書階層で管理可能

ezdxfでは、XRECORDはXDATAに似ていますが、XDATAのサイズや順序に関する制約を受けない任意データ用オブジェクトとして説明されています。

使い分けの目安は次のようになります。

少量の属性を図形へ直接付ける
    → XDATA

複数項目・階層・図面全体の独自データを管理する
    → DICTIONARY + XRECORD

ただし、受け渡し先のCADが独自データを保持するとは限りません。保存、別名保存、形式変換、PURGEなどを含む実機検証が必要です。

PythonでDICTIONARYを解析する

順序付きタグ列から、コード3と350の組を抽出する例です。

from dataclasses import dataclass


@dataclass
class DictionaryEntry:
    name: str
    target_handle: str


def parse_dictionary_entries(
    tags: list[tuple[int, object]],
) -> list[DictionaryEntry]:
    entries: list[DictionaryEntry] = []
    pending_name: str | None = None

    for code, value in tags:
        if code == 3:
            pending_name = str(value)

        elif code in (350, 360) and pending_name is not None:
            entries.append(
                DictionaryEntry(
                    name=pending_name,
                    target_handle=str(value).upper(),
                )
            )
            pending_name = None

    if pending_name is not None:
        raise ValueError(
            f"dictionary entry has no target: {pending_name}"
        )

    return entries

実装では、次の不整合も検出します。

  • コード3に対応する参照がない
  • 同じ名前が複数回現れる
  • 参照先ハンドルが存在しない
  • 参照先がOBJECTSセクションにない
  • 参照先が想定したXRECORDではない
  • XRECORDのownerが辞書と一致しない

辞書名は大文字・小文字の扱いがソフトウェアによって異なる可能性があるため、比較用の正規化値と原文を分けて保存すると安全です。

XRECORDを構造化する

XRECORDは、まず管理属性と任意データを分離します。

COMMON_CODES = {
    0,    # object type
    5,    # handle
    100,  # subclass marker
    280,  # cloning flag
    330,  # owner handle
}


def extract_xrecord_payload(
    tags: list[tuple[int, object]],
) -> list[tuple[int, object]]:
    return [
        (code, value)
        for code, value in tags
        if code not in COMMON_CODES
    ]

ただし、単にコードだけで除外すると、アプリケーションデータ内で同じ範囲のコードを使用する設計と衝突する可能性があります。

より堅牢なパーサーでは、

  • オブジェクト共通部
  • AcDbXrecordサブクラス部
  • その後の任意タグ列

という出現位置も考慮します。

解析後は、スキーマ名とバージョンを含む構造へ変換すると再利用しやすくなります。

{
  "schema": "hydraulic-cylinder",
  "schema_version": 1,
  "source": {
    "dictionary_key": "EKPLABZ_DATA",
    "xrecord_handle": "2B"
  },
  "data": {
    "bore_mm": 80.0,
    "rod_mm": 32.0,
    "stroke_mm": 500.0
  }
}

スキーマバージョンを持たせれば、将来項目を追加したときも旧データとの区別ができます。

実装で起こりやすい失敗

ENTITIESセクションだけを読み込む。
非図形データやアプリケーション固有情報を取得できません。

DICTIONARYを値の保存場所だと考える。
DICTIONARYが保持する中心情報は、名前と別オブジェクトへの参照です。

コード3と350を別々の配列へ入れる。
対応関係が崩れる可能性があります。読み取り時点でエントリとして組にします。

XRECORDを通常の辞書へ変換する。
同じコードが繰り返されるため、順序付きタグ列を保持します。

未知のXRECORDを削除する。
他のCADやアプリケーションが必要とするデータかもしれません。理解できないデータもRaw層で保持します。

コードの意味をファイル全体で固定する。
XRECORDの任意タグは、作成したアプリケーションのスキーマによって意味が決まります。

参照切れを任意のオブジェクトへ付け替える。
まず辞書名、参照元、参照先、所有形式を診断情報として出力し、修復処理を分離します。

まとめ

DXFのOBJECTSセクションには、図形として表示されない設定や管理データが保存されています。

その中でDICTIONARYは、

コード3   → エントリ名
コード350 → 参照先オブジェクト
コード280 → hard-ownerフラグ
コード281 → 重複時の処理

という構造を持ちます。

XRECORDは、DICTIONARYから参照される任意データ用オブジェクトです。

DICTIONARY
    ↓ 名前とハンドル
XRECORD
    ↓ 順序付きDXFタグ
アプリケーション固有データ

XRECORDの任意データには、コード5と105を除く1〜369の範囲を利用できます。ただし、その意味はアプリケーション側で定義しなければなりません。

パーサーを設計するときは、

DXF原文
    ↓
OBJECTS抽出
    ↓
ハンドル索引
    ↓
DICTIONARYの名前・参照を解決
    ↓
XRECORDのタグ列を保持
    ↓
スキーマに基づいて設計データ化

という処理へ分離します。

DICTIONARYとXRECORDを扱えるようになると、DXFは単なる図形交換ファイルではなく、図形、設計属性、検査結果、外部ID、計算条件をまとめて扱える構造化データへ発展します。

参考情報

参考になったらシェアしてください
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

機械設計・油圧・CAD・Python・AIなど、ものづくりに関わる技術を扱っています。工学知識を整理・構造化し、設計や自動化に再利用できる形へ変えていくことを目指しています。

目次