図面上の引出線は、矢印と折れ線だけではありません。矢印が指す位置、注記文字、文字スタイル、着地点、折れ曲がり、尺度などが組み合わさって、一つの注記として表示されます。
DXFでは、引出線を主に次の二つの方式で表します。
LEADER:引出線と注記を別エンティティとして保存する従来方式MULTILEADER:複数の引出線とMTEXTまたはブロック内容を一つの複合構造に保存する方式
両者を同じ「頂点列」として読むと、注記文字やスタイル、複数の引出線を失います。
この記事では、AutodeskのDXF仕様を基準に、LEADERのコード340による注記参照、MULTILEADERのスタイル参照、CONTEXT_DATA、MTEXT内容、Pythonでの構造化方法を整理します。
LEADERとMULTILEADERの違い
最も重要な違いは、注記内容をどこに保持するかです。
| 項目 | LEADER | MULTILEADER |
|---|---|---|
| 引出線 | 頂点列として保持 | 複数のleader nodeを保持可能 |
| 注記文字 | 別のMTEXTなどを参照 | context内にMTEXT内容を保持 |
| 注記との接続 | コード340のハンドル参照 | エンティティ内部の複合構造 |
| スタイル | DIMSTYLE名 | MLEADERSTYLEへの参照 |
| 主なDXF名 | LEADER | MULTILEADER |
ezdxfの資料では、MULTILEADERは一つ以上の引出線と矢印を表し、LEADERとは異なり、文字またはブロック内容をエンティティ内部に持つと説明されています。
flowchart TD
A["引出線エンティティを判定"] --> B{"DXFタイプ"}
B -- LEADER --> C["頂点列とコード340を読む"]
C --> D["別エンティティの注記を解決"]
B -- MULTILEADER --> E["スタイルとCONTEXT_DATAを読む"]
E --> F["MTEXTまたはBLOCK内容を復元"]LEADERの主要グループコード
LEADERは、比較的単純な頂点列と状態値で構成されます。
| コード | 意味 |
|---|---|
| 3 | 寸法スタイル名 |
| 71 | 矢印の有無 |
| 72 | 経路形式。0は直線、1はスプライン |
| 73 | 注記の種類 |
| 75 | フック線の有無 |
| 76 | 頂点数 |
| 10・20・30 | 各頂点の座標 |
| 340 | 関連する注記エンティティへのhard reference |
| 210・220・230 | 法線ベクトル |
コード73は、LEADERがどの種類の注記と作られたかを表します。
| 値 | 注記の種類 |
|---|---|
| 0 | 文字注記 |
| 1 | 幾何公差 |
| 2 | ブロック参照 |
| 3 | 注記なし |
コード73が0でも、注記本文がLEADERのコード1へ入るわけではありません。通常はコード340が別のMTEXTなどを参照します。
LEADERと注記をハンドルで結ぶ
次の例では、LEADERのコード340がハンドル5AのMTEXTを参照しています。
0
LEADER
5
59
100
AcDbLeader
3
STANDARD
71
1
72
0
73
0
76
3
10
20.0
20
10.0
30
0.0
10
30.0
20
20.0
30
0.0
10
45.0
20
20.0
30
0.0
340
5A
0
MTEXT
5
5A
1
OIL INLET
解析結果は、LEADERとMTEXTを別々に残したまま、参照関係を追加します。
{
"type": "LEADER",
"handle": "59",
"vertices": [
[20.0, 10.0, 0.0],
[30.0, 20.0, 0.0],
[45.0, 20.0, 0.0]
],
"annotation": {
"handle": "5A",
"type": "MTEXT",
"text": "OIL INLET"
}
}
コード340の参照先が存在しない場合、文字列を空欄で補完せず、参照切れとして記録します。注記が意図的にないLEADERと、ファイル破損によって注記を失ったLEADERを区別するためです。
MULTILEADERは一つの複合エンティティ
MULTILEADERでは、注記、矢印、着地点、複数の引出線が一つのエンティティにまとまります。DXFタイプ文字列にはMULTILEADERが使われます。MLEADERという別名を受け入れるCADもありますが、ezdxfの資料では、CADアプリケーションが生成するタイプ文字列はMULTILEADERとされています。
共通部分の代表的なコードは次のとおりです。
| コード | 意味 |
|---|---|
| 340 | MLEADERSTYLEへの参照 |
| 90 | プロパティ上書きフラグ |
| 170 | 引出線の種類 |
| 290 | landingの有効状態 |
| 291 | doglegの有効状態 |
| 41 | doglegの長さ |
| 42 | 矢印サイズ |
| 172 | 内容の種類 |
| 343 | 文字スタイルへの参照 |
| 292 | 文字枠の有効状態 |
ezdxfでは内容種類を、0がなし、1がブロック、2がMTEXT、3が幾何公差として扱います。内容種類を確認してから、文字用またはブロック用のデータを読みます。
MLEADERSTYLEと個別上書きを分ける
MULTILEADERのコード340は、MLEADERSTYLEオブジェクトを参照します。スタイルには、引出線の色、線種、線の太さ、矢印、landing、dogleg、文字高さ、文字枠、内容の種類などの既定値が保存されます。
一方、MULTILEADER側にも同種のプロパティが保存され、コード90が上書き状態を表します。
MLEADERSTYLE
↓ 既定値
MULTILEADER
↓ 個別値・上書き
CONTEXT_DATA
↓ 実際の配置と内容
表示結果
実装では、次の三層を混ぜずに保持します。
- スタイルの既定値
- エンティティ固有のプロパティ
- context内の配置・注記データ
上書きフラグだけを信頼してスタイル値へ戻すと、実際の表示と一致しない場合があります。ezdxfも、上書きフラグが常に実状態を反映するとは限らないと注意しています。未知の組み合わせはRawタグも保存して、対象CADで表示を検証します。
CONTEXT_DATAを階層として読む
MULTILEADERの難しさは、同じグループコードが階層ごとに別の意味を持つ点です。
contextは、コード300のCONTEXT_DATA{で始まり、コード301の}で閉じます。その内部に、コード302のLEADER{とコード303の}で囲まれた引出線構造が続きます。
300
CONTEXT_DATA{
40
1.0
10
50.0
20
30.0
30
0.0
290
1
304
OIL INLET
12
50.0
22
30.0
32
0.0
302
LEADER{
...
303
}
301
}
context共通部の代表値は次のとおりです。
| コード | 意味 |
|---|---|
| 40 | 内容の尺度 |
| 10・20・30 | 内容の基準位置 |
| 41 | 文字高さ |
| 140 | 矢印サイズ |
| 145 | landing gap |
| 290 | MTEXT内容の有無 |
| 304 | 既定の文字内容 |
| 12・22・32 | 文字位置 |
| 13・23・33 | 文字方向 |
| 42 | 文字回転角 |
| 296 | ブロック内容の有無 |
| 341 | ブロック内容への参照 |
コード10はcontextの基準位置にも、内側の頂点にも使われます。ファイル全体をcode -> valueの辞書へ変換すると、位置情報が上書きされます。
マーカーを使って構造を切り分ける
最低限のパーサーでも、開始・終了マーカーを認識して階層を保持します。
from dataclasses import dataclass, field
from typing import Any
Tag = tuple[int, Any]
@dataclass
class MLeaderSections:
common: list[Tag] = field(default_factory=list)
context: list[Tag] = field(default_factory=list)
leaders: list[list[Tag]] = field(default_factory=list)
def split_mleader_sections(tags: list[Tag]) -> MLeaderSections:
result = MLeaderSections()
current = result.common
leader: list[Tag] | None = None
for code, value in tags:
marker = str(value) if code in (300, 301, 302, 303) else ""
if code == 300 and marker == "CONTEXT_DATA{":
current = result.context
continue
if code == 302 and marker == "LEADER{":
leader = []
result.leaders.append(leader)
current = leader
continue
if code == 303 and marker == "}":
leader = None
current = result.context
continue
if code == 301 and marker == "}":
current = result.common
continue
current.append((code, value))
return result
この例は構造分割の入口です。実際には、leader node、leader line、頂点、break pointをさらに下位オブジェクトへ分けます。未知のタグを削除せず、所属セクションと順序を保ったまま保存することが重要です。
ezdxfで内容を取得する
ezdxfでは、MULTILEADERのMTEXT内容を高水準APIから取得できます。
import ezdxf
doc = ezdxf.readfile("drawing.dxf")
msp = doc.modelspace()
for entity in msp.query("LEADER MULTILEADER"):
if entity.dxftype() == "LEADER":
annotation = entity.get_annotation()
text = ""
if annotation is not None:
text = annotation.plain_text()
print("LEADER", entity.dxf.handle, text)
continue
if entity.has_mtext_content:
text = entity.get_mtext_content()
print("MULTILEADER", entity.dxf.handle, text)
else:
print("MULTILEADER", entity.dxf.handle, "non-MTEXT content")
ライブラリのAPIは、文字内容を取得する用途では有効です。ただし、別CADで作成された複雑なMULTILEADERを完全に再描画できるとは限りません。データ抽出、参照検証、見た目の再現を別々のテストにします。
実装で起こりやすい失敗
LEADERの中に注記文字があると考える。
従来のLEADERでは、コード340で別のMTEXT、TOLERANCE、INSERTなどを参照します。
MULTILEADERを単純な頂点列へ変換する。
複数の引出線、MTEXTまたはブロック内容、スタイル、個別上書きが失われます。
同じコードを一つの辞書キーへ保存する。
コード10などは階層ごとに繰り返されます。順序付きタグ列と階層を保持します。
MLEADERSTYLEだけから表示値を決める。
個別エンティティとcontextに上書き値があります。三層を分けて解決します。
コード304だけを最終文字列とみなす。
文字内容にはMTEXTの制御記号が含まれる場合があります。プレーンテキスト化と書式保持を用途別に分けます。
抽出成功を描画互換性と同一視する。
文字配置、背景マスク、左右の接続位置、dogleg、尺度、注釈尺度まで含めた再現には、対象CADでの比較が必要です。
まとめ
DXFの引出線は、LEADERとMULTILEADERで保存構造が大きく異なります。
- LEADERは頂点列を持ち、コード340で別の注記エンティティを参照する
- MULTILEADERは複数の引出線と文字またはブロック内容を内部に保持する
- MULTILEADERのコード340はMLEADERSTYLEを参照する
- contextは
CONTEXT_DATA{、LEADER{などのマーカーで階層化される - 同じグループコードが別階層で再利用されるため、単純な辞書へ平坦化しない
- スタイル、個別プロパティ、contextを分離して保存する
この構造を保持できれば、引出線注記の検索、注記文字の一括検査、スタイル統一、参照切れ診断、図面レビュー結果のデータ化へ展開できます。
