DXFに保存されている情報は、画面に表示される線、円、文字、寸法だけではありません。
図面全体の設定、グループ定義、レイアウト情報、アプリケーション固有データなど、画面上に直接描画されない情報も含まれています。
こうした非図形データの多くが格納される場所がOBJECTSセクションです。
その中でも、独自の設計情報やアプリケーションデータを扱ううえで重要なのが、
DICTIONARYXRECORD
です。
概念的な関係は次のようになります。
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 |
| 5 | DICTIONARY自身のハンドル |
| 330 | 所有元オブジェクトへの参照 |
| 100 | サブクラスマーカーAcDbDictionary |
| 280 | hard-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として扱うかどうかを表します。
| 値 | 意味 |
|---|---|
| 0 | hard-ownedとして扱わない |
| 1 | hard-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 | 浮動小数点数 |
| 70 | 16ビット整数 |
| 90 | 32ビット整数 |
| 310 | バイナリチャンク |
| 330〜369 | ハンドル参照 |
重要なのは、XRECORDの内容が固定スキーマではないことです。
同じコード1でも、あるアプリケーションでは部品名、別のアプリケーションではJSON文字列を表す可能性があります。
データの意味は、作成したアプリケーション側で定義する必要があります。
XRECORDの主要グループコード
XRECORD自体の共通構造には、次のコードがあります。
| グループコード | 意味 |
|---|---|
| 0 | オブジェクト名XRECORD |
| 5 | XRECORD自身のハンドル |
| 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もあります。
両者の基本的な違いは次のとおりです。
| 項目 | XDATA | XRECORD |
|---|---|---|
| 主な関連先 | エンティティへ直接付加 | 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、計算条件をまとめて扱える構造化データへ発展します。

