fix(task-9): DIMDSEP refuses control characters and code points above the 16-bit DXF group
check_dim_value's char branch only checked len == 1, so '\n', '\t', '\x00'
and non-BMP characters passed and the headless writer stored ord(value)
unguarded: '\n' split the dimension text over two lines, NUL read back as a
comma, and U+1F600 reached $DIMDSEP / group 278 as 128512, outside the 16-bit
range and refused by AutoCAD as "not a single character". ezdxf's audit
caught none of it. The same hole 827719f closed for names and fonts, left
open for the one variable stored as a character code.
The gate now refuses code < 32, DEL, and code > DIMDSEP_MAX_CODE (0x7FFF,
the signed short DWG stores) with a ValueError naming DIMDSEP, so nothing
reaches a backend. Pinned by a parametrized refusal test (newline, tab, NUL,
DEL, U+1F600, U+8000, U+FFFF), a positive test that the middle dot, the
Arabic decimal separator and U+7FFF still pass, and the ezdxf refusal test
now proves dimstyle_create / dimstyle_modify write nothing for these.
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
2ee18727cd
commit
4d246b17b8
@@ -12,9 +12,13 @@ Every value in ``PRESETS`` is pinned by name in
|
||||
lettering, .125 in arrowheads) in metric values: 3 mm text and arrowheads,
|
||||
text centred in a gap and horizontal, decimal point.
|
||||
|
||||
``DIMDSEP`` is a one-character *string* here. DXF stores the character code and
|
||||
ezdxf raises on a string in the header, so the translation belongs to the
|
||||
backends, not to the data.
|
||||
``DIMDSEP`` is a one-character *string* here. DXF stores the character code
|
||||
(group 278, a 16-bit value) and ezdxf raises on a string in the header, so the
|
||||
translation belongs to the backends, not to the data — but the data is where
|
||||
the refusal starts: a control character (``\\n`` splits the dimension text over
|
||||
two lines, NUL reads back as a comma) or a code point above ``0x7FFF``
|
||||
(``ezdxf`` writes it, AutoCAD refuses it as "not a single character") is
|
||||
refused here, before ``ord()`` runs in a backend.
|
||||
|
||||
Pure data plus validation: no I/O, no ezdxf, no COM, so the backends, the
|
||||
tools and the provenance tests all read one copy.
|
||||
@@ -270,6 +274,15 @@ def ezdxf_arrowhead(canonical: str) -> str:
|
||||
return canonical
|
||||
|
||||
|
||||
DIMDSEP_MAX_CODE = 0x7FFF
|
||||
"""The largest character code ``DIMDSEP`` accepts.
|
||||
|
||||
DXF group 278 is a 16-bit integer and the DWG field a signed short; AutoCAD
|
||||
refuses anything above it as "not a single character", while ezdxf would
|
||||
write it verbatim (measured: ``'\\U0001F600'`` reached the file as 128512).
|
||||
"""
|
||||
|
||||
|
||||
def _number(key: str, value: Any) -> float:
|
||||
if isinstance(value, bool) or not isinstance(value, (int, float)):
|
||||
raise TypeError(f"{key}: expected a number, got {type(value).__name__}")
|
||||
@@ -324,6 +337,16 @@ def check_dim_value(name: str, value: Any) -> Any:
|
||||
raise TypeError(f"{key}: expected a string, got {type(value).__name__}")
|
||||
if len(value) != 1: # "char"
|
||||
raise ValueError(f"{key}: {value!r} must be exactly one character")
|
||||
code = ord(value)
|
||||
if code < 32 or code == 127:
|
||||
raise ValueError(
|
||||
f"{key}: {value!r} is a control character, which a DXF cannot hold as a separator"
|
||||
)
|
||||
if code > DIMDSEP_MAX_CODE:
|
||||
raise ValueError(
|
||||
f"{key}: {value!r} (U+{code:04X}) is outside the 16-bit character code DXF group "
|
||||
f"278 stores (at most U+{DIMDSEP_MAX_CODE:04X})"
|
||||
)
|
||||
return value
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user