DXFのHEADER変数を読む|$ACADVER・$EXTMIN・$HANDSEEDの解析

「DXFのHEADER変数を読む|$ACADVER・$EXTMIN・$HANDSEEDの解析」の内容を表す技術イラスト

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変数を「変数名と一つの文字列」として扱うことはできません。

変数によって値の型とタグ数が異なります。

変数主なコード値の形式
$ACADVER1文字列
$CLAYER8文字列
$EXTMIN10・20・303次元点
$EXTMAX10・20・303次元点
$INSBASE10・20・303次元点
$HANDSEED5ハンドル
$LTSCALE40実数
$LUNITS70整数

たとえば、次のような辞書へ単純変換すると問題が起こります。

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対応バージョン
AC1009AutoCAD R11・R12
AC1012AutoCAD R13
AC1014AutoCAD R14
AC1015AutoCAD 2000
AC1018AutoCAD 2004
AC1021AutoCAD 2007
AC1024AutoCAD 2010
AC1027AutoCAD 2013
AC1032AutoCAD 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データ処理への第一歩です。

参考情報

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

この記事を書いた人

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

目次