feat(standards): ISO-25 and ANSI dimension, text and leader style presets as pinned data
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FKFStjEFzpGyLq5bMpWxTd
This commit is contained in:
co-authored by
Claude Opus 5
parent
49fc6ac6a5
commit
54fac471a9
@@ -0,0 +1,66 @@
|
||||
"""Multileader style presets: arrow, landing gap, text style and height.
|
||||
|
||||
Sized to match the dimension presets they sit beside (ISO-25: 2.5 mm arrows
|
||||
and lettering; ANSI: 3 mm), so a leader note and a dimension on the same
|
||||
sheet read at one size. Only ezdxf can author an MLEADERSTYLE object;
|
||||
ActiveX exposes no collection for it (see ``backends/contracts/styles.py``).
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import math
|
||||
from typing import Any
|
||||
|
||||
__all__ = ["MLEADER_KEYS", "MLEADER_PRESETS", "resolve_mleaderstyle"]
|
||||
|
||||
MLEADER_PRESETS: dict[str, dict[str, Any]] = {
|
||||
"iso": {"arrow_size": 2.5, "landing_gap": 1.0, "text_style": "ISOCP", "text_height": 2.5},
|
||||
"ansi": {"arrow_size": 3.0, "landing_gap": 1.5, "text_style": "ROMANS", "text_height": 3.0},
|
||||
}
|
||||
|
||||
MLEADER_KEYS: tuple[str, ...] = ("arrow_size", "landing_gap", "text_style", "text_height")
|
||||
|
||||
_ILLEGAL_NAME_CHARS = frozenset('<>/\\":;?*|,=`')
|
||||
|
||||
|
||||
def _positive(key: str, value: Any, *, allow_zero: bool = False) -> float:
|
||||
if isinstance(value, bool) or not isinstance(value, (int, float)):
|
||||
raise TypeError(f"{key}: expected a number, got {type(value).__name__}")
|
||||
number = float(value)
|
||||
if not math.isfinite(number):
|
||||
raise ValueError(f"{key}: {value!r} is not a finite number")
|
||||
if number < 0.0 or (number == 0.0 and not allow_zero):
|
||||
raise ValueError(f"{key}: {value!r} must be greater than 0")
|
||||
return number
|
||||
|
||||
|
||||
def resolve_mleaderstyle(preset: str, overrides: dict | None = None) -> dict[str, Any]:
|
||||
"""The preset's four values with ``overrides`` applied; refusals name the key."""
|
||||
key = str(preset).strip().lower() if preset is not None else ""
|
||||
if key not in MLEADER_PRESETS:
|
||||
raise ValueError(f"preset: unknown value {preset!r}; use one of {sorted(MLEADER_PRESETS)}")
|
||||
values = dict(MLEADER_PRESETS[key])
|
||||
if overrides is None:
|
||||
return values
|
||||
if not isinstance(overrides, dict):
|
||||
raise TypeError(f"overrides: expected a mapping, got {type(overrides).__name__}")
|
||||
for raw_key, value in overrides.items():
|
||||
name = str(raw_key).strip().lower()
|
||||
if name not in MLEADER_KEYS:
|
||||
raise ValueError(
|
||||
f"overrides: {raw_key!r} is not a leader style key; allowed: {list(MLEADER_KEYS)}"
|
||||
)
|
||||
if name == "text_style":
|
||||
if not isinstance(value, str) or not value.strip():
|
||||
raise ValueError("text_style: a text style name cannot be empty")
|
||||
bad = sorted(set(value.strip()) & _ILLEGAL_NAME_CHARS)
|
||||
if bad:
|
||||
raise ValueError(
|
||||
f"text_style: {value!r} contains characters DXF forbids in a name: {bad}"
|
||||
)
|
||||
values[name] = value.strip()
|
||||
elif name == "landing_gap":
|
||||
values[name] = _positive(name, value, allow_zero=True)
|
||||
else:
|
||||
values[name] = _positive(name, value)
|
||||
return values
|
||||
Reference in New Issue
Block a user