DXFを解析するとき、最初に確認したいのはLINEやCIRCLEではありません。
ファイルのバージョン、図面範囲、挿入基点、次に利用できるハンドルなど、図面全体へ関係する情報が記録されたHEADERセクションです。
HEADERを読むと、図形データの解析に入る前に次のような判断ができます。
- 対応しているDXFバージョンか
- 図面範囲が異常に大きくないか
- 2次元図面に想定外のZ座標がないか
- 新しいオブジェクトへ割り当てるハンドル候補は何か
- 挿入基点や現在の画層など、図面全体の設定は何か
この記事では、HEADER変数の基本構造と、実務で確認しやすい$ACADVER、$EXTMIN、$EXTMAX、$HANDSEED、$INSBASEを整理します。
HEADERセクションの役割
AutodeskのDXF仕様では、HEADERセクションに図面へ関連付けられた変数の設定値が保存されます。
基本構造は次のとおりです。
0
SECTION
2
HEADER
9
$ACADVER
1
AC1032
9
$EXTMIN
10
0.0
20
0.0
30
0.0
0
ENDSEC
コード0とSECTIONがセクションの開始、コード2とHEADERがセクション名を表します。
HEADER内部では、グループコード9が変数名の開始を示します。コード9の後に、変数ごとの型に対応した一つ以上のタグが続きます。
コード9
変数名
値を表すグループコード
値
次のコード9またはENDSECが現れるまでが、その変数の値です。
HEADER変数は一律の型ではない
すべてのHEADER変数を「変数名と一つの文字列」として扱うことはできません。
変数によって値の型とタグ数が異なります。
| 変数 | 主なコード | 値の形式 |
|---|---|---|
| $ACADVER | 1 | 文字列 |
| $CLAYER | 8 | 文字列 |
| $EXTMIN | 10・20・30 | 3次元点 |
| $EXTMAX | 10・20・30 | 3次元点 |
| $INSBASE | 10・20・30 | 3次元点 |
| $HANDSEED | 5 | ハンドル |
| $LTSCALE | 40 | 実数 |
| $LUNITS | 70 | 整数 |
たとえば、次のような辞書へ単純変換すると問題が起こります。
header[code] = value
$EXTMINにはコード10、20、30の三つが必要です。また、変数名を保持しなければ、同じコード70を使う複数の設定を区別できません。
最初は変数名ごとの順序付きタグ列として保存し、その後に型を解釈する方が安全です。
{ "$ACADVER": [(1, "AC1032")], "$EXTMIN": [ (10, 0.0), (20, 0.0), (30, 0.0), ],}
$ACADVERでDXFのバージョンを判定する
$ACADVERは、AutoCAD図面データベースのバージョンを表します。値はAC1032のような文字列です。
AutodeskのDXF Referenceには、代表的な対応として次の値が示されています。
| $ACADVER | 対応バージョン |
|---|---|
AC1009 | AutoCAD R11・R12 |
AC1012 | AutoCAD R13 |
AC1014 | AutoCAD R14 |
AC1015 | AutoCAD 2000 |
AC1018 | AutoCAD 2004 |
AC1021 | AutoCAD 2007 |
AC1024 | AutoCAD 2010 |
AC1027 | AutoCAD 2013 |
AC1032 | AutoCAD 2018 |
DXFのバージョンによって、利用できるセクション、エンティティ、サブクラス、グループコードが異なります。
そのためパーサーでは、最初に$ACADVERを読み、対応範囲を判定します。
SUPPORTED_VERSIONS = { "AC1015", "AC1018", "AC1021", "AC1024", "AC1027", "AC1032",}def check_version(acadver: str) -> None: if acadver not in SUPPORTED_VERSIONS: raise ValueError( f"unsupported DXF version: {acadver}" )
未知の値を見つけたとき、既知の最新バージョンとして黙って処理するのは危険です。
実務では、次のいずれかを明示的に選びます。
- 読み込みを中止する
- 警告を出して既知のタグだけ読む
- Rawタグを保持し、未知データを失わずに通過させる
$EXTMINと$EXTMAXは図面範囲を表す
$EXTMINは図面範囲の左下側、$EXTMAXは右上側の点をWCS座標で表します。
どちらもコード10、20、30を使用します。
9
$EXTMIN
10
-25.0
20
-10.0
30
0.0
9
$EXTMAX
10
420.0
20
297.0
30
0.0
この例では、概念上の範囲は次のとおりです。
X方向:-25.0 ~ 420.0
Y方向:-10.0 ~ 297.0
Z方向: 0.0 ~ 0.0
範囲の大きさは各軸について、\[ L_x=x_{\max}-x_{\min} \]\[ L_y=y_{\max}-y_{\min} \]\[ L_z=z_{\max}-z_{\min} \]
で求められます。
この例では、\[ L_x=420-(-25)=445 \]\[ L_y=297-(-10)=307 \]
です。
ただし、ここで得られる数値の物理単位は、別途確認が必要です。すでに公開している$INSUNITSの解説と組み合わせ、mm、inch、unitlessなどを判断します。
図面範囲を検査に利用する
$EXTMINと$EXTMAXは、ファイル受け入れ時の簡易検査に利用できます。
たとえば、次の状態は確認対象です。
- 最小値が最大値を超えている
- 2次元図面なのにZ方向の範囲が大きい
- 想定する装置寸法に対して範囲が極端に大きい
- 原点から大きく離れた座標に図形が存在する
- 図面範囲と実際のエンティティ範囲が大きく異なる
ただし、HEADERの範囲値だけを絶対的な正解として扱わない方が安全です。
CADで図面を編集した直後の状態、他形式からの変換、生成ソフトの実装などによって、実体と一致しないデータを受け取る可能性があります。
堅牢な検査では、次の二つを分けます。
HEADERに保存された範囲
実際のエンティティから再計算した範囲
差が許容値を超えた場合に警告を出せば、飛び離れた図形や変換異常も発見しやすくなります。
図面範囲と図面限界は異なる
$EXTMIN・$EXTMAXと混同しやすい変数に、$LIMMIN・$LIMMAXがあります。
| 変数 | 意味 |
|---|---|
| $EXTMIN・$EXTMAX | 図面の外接範囲 |
| $LIMMIN・$LIMMAX | 作図用に設定された図面限界 |
図面限界は作業領域の設定であり、実際の図形が必ずその中に収まるとは限りません。
また、$LIMMINと$LIMMAXはコード10・20によるXY値ですが、$EXTMINと$EXTMAXはコード10・20・30によるXYZ値です。
独自ビューアの表示範囲を決める場合は、図面限界ではなく、実際の図形範囲または検証済みのextentsを使用します。
$INSBASEは図面の挿入基点
$INSBASEは、BASEコマンドで設定されたWCS上の挿入基点です。
9
$INSBASE
10
100.0
20
50.0
30
0.0
この値は、図面全体を別の図面へブロックや外部参照として挿入するときの基準に関係します。
$INSBASEを無視して、常にWCS原点を基準に合成すると、取り込んだ図面が意図した位置からずれる可能性があります。
一方、$INSBASEは各INSERTエンティティが持つ挿入点とは別の情報です。
$INSBASE
→ 図面全体の基点
INSERTのコード10・20・30
→ 個々のブロック参照の挿入点
図面統合処理では、この二つを明確に分離します。
$HANDSEEDは次に利用できるハンドル
$HANDSEEDは、次に利用可能なハンドルを示します。値にはグループコード5が使用されます。
9
$HANDSEED
5
2AF
ハンドルは16進文字列です。この例の2AFは10進数では687ですが、通常は数値計算へ使わず文字列として保持します。
DXFへ新しいオブジェクトを追加するときは、既存ハンドルと重複しない値を割り当てる必要があります。
ただし、$HANDSEEDだけを信頼して即座に採番するより、既存オブジェクトのハンドル索引も確認する方が安全です。
$HANDSEEDを取得
↓
既存ハンドルとの重複を確認
↓
未使用ハンドルを割り当てる
↓
次の候補へ$HANDSEEDを更新
不正なDXFや独自生成されたDXFでは、$HANDSEEDが既存ハンドル以下になっている可能性も考慮します。
PythonでHEADER変数を抽出する
次の関数は、HEADER内のタグ列をコード9ごとに分割します。
from typing import AnyTag = tuple[int, Any]Header = dict[str, list[Tag]]def parse_header(tags: list[Tag]) -> Header: header: Header = {} current_name: str | None = None for code, value in tags: if code == 9: current_name = str(value) if current_name in header: raise ValueError( f"duplicate header variable: {current_name}" ) header[current_name] = [] continue if current_name is not None: header[current_name].append((code, value)) return header
点データは、必要なコードを個別に取得します。
def read_point3( header: Header, name: str,) -> tuple[float, float, float]: values = dict(header[name]) required = (10, 20, 30) if not all(code in values for code in required): raise ValueError( f"incomplete point variable: {name}" ) return ( float(values[10]), float(values[20]), float(values[30]), )
単一値は、期待するグループコードも検証します。
def read_single( header: Header, name: str, expected_code: int,) -> Any: tags = header[name] if len(tags) != 1: raise ValueError( f"{name} must have exactly one value" ) code, value = tags[0] if code != expected_code: raise ValueError( f"{name}: expected code " f"{expected_code}, got {code}" ) return value
使用例は次のとおりです。
acadver = read_single(header, "$ACADVER", 1)handseed = read_single(header, "$HANDSEED", 5)extmin = read_point3(header, "$EXTMIN")extmax = read_point3(header, "$EXTMAX")
このコードは構造理解用の最小例です。実務用パーサーでは、DXFの型規則に基づく文字列・整数・実数への変換、セクション境界の検証、未知変数の保持も追加します。
HEADER検査結果を構造化する
図面受付や変換処理へ利用するなら、原文と解釈結果を分けて保存します。
{
"header": {
"acadver": "AC1032",
"extmin_wcs": [-25.0, -10.0, 0.0],
"extmax_wcs": [420.0, 297.0, 0.0],
"insbase_wcs": [100.0, 50.0, 0.0],
"handseed": "2AF"
},
"validation": {
"version_supported": true,
"extents_order_valid": true,
"handseed_format_valid": true,
"warnings": []
}
}
この構造なら、次の処理へ同じ結果を渡せます。
- DXFアップロード時の自動検査
- 対応バージョンの判定
- サムネイル表示範囲の決定
- 異常座標の検出
- 図面統合時の基点処理
- オブジェクト追加時のハンドル採番
- 検査結果のCSV・JSON出力
実装で起こりやすい失敗
HEADERの全変数を単一値として読む。
点を表す変数には複数のタグがあります。変数名ごとのタグ列を保持します。
コード9を通常の文字列データとして扱う。
HEADER内のコード9は、後続する変数値の区切りです。
$ACADVERを表示用情報だけに使う。
バージョンは、後続セクションやエンティティをどの仕様で解釈するか判断する入力です。
$EXTMAXを図面サイズと考える。
最大座標だけでは大きさを求められません。$EXTMINとの差を使用します。
extentsを無条件に信用する。
必要に応じて、実際のエンティティから再計算した範囲と比較します。
$INSBASEとINSERTの挿入点を混同する。
前者は図面全体、後者は個々のブロック参照に関係する値です。
$HANDSEEDを次のハンドルとして無検証で使う。
既存ハンドルの索引を作り、重複しないことを確認します。
未知のHEADER変数を削除する。
別のCADや新しいDXFバージョンで必要な情報かもしれません。解釈できない変数もRawタグとして保持します。
まとめ
DXFのHEADERセクションには、図面全体へ関係する変数が保存されています。
各変数はコード9で始まり、その後に変数固有の型と個数のタグが続きます。
$ACADVER
→ DXFデータベースのバージョン
$EXTMIN・$EXTMAX
→ WCS上の図面範囲
$INSBASE
→ 図面全体の挿入基点
$HANDSEED
→ 次に利用できるハンドル
パーサーでは、最初から個別の値へ決め打ちせず、変数名と順序付きタグ列をRawデータとして保存します。
その後、
HEADERタグ列
↓
コード9で変数を分割
↓
変数ごとの型を検証
↓
バージョン・範囲・基点・ハンドルを解釈
↓
ENTITIESやOBJECTSの解析へ進む
という段階に分けると、未知の変数を失わず、バージョン差にも対応しやすくなります。
HEADERを単なる設定一覧ではなく、DXF全体を解析する前の入力条件と検査データとして扱うことが、堅牢なCADデータ処理への第一歩です。

