diff --git a/backends/com_backend.py b/backends/com_backend.py index d111acb..a26a835 100644 --- a/backends/com_backend.py +++ b/backends/com_backend.py @@ -314,30 +314,66 @@ def _same_dimvar(old: Any, new: Any) -> bool: return str(old) == str(new) -def _com_dimensions_using(doc, style_name: str) -> list[str]: - """Handles of every dimension, in every layout, whose StyleName is ``style_name``. +#: ``SelectionSet.Select`` mode ``acSelectionSetAll`` (AcSelect enum): every +#: entity in the database, in every layout — ``ssget "X"``. +_AC_SELECTION_SET_ALL = 5 - ``IAcadBlock.Item`` is declared ``IAcadEntity*`` (acax25enu.tlb), so with a - makepy cache present every member comes back narrowed to ``IAcadEntity`` - and ``.StyleName`` — a member of the dimension classes, not of the entity - base — raises ``AttributeError``. Measured live (AutoCAD 2026): a rotated - dimension drawn with ``PROBE-ANSI`` answered ``[]`` because a blanket - ``except`` turned that AttributeError into "no dimension uses it". Each - item is therefore un-narrowed (``_com_unnarrow``), and nothing is - swallowed: a read that fails is an error the caller sees, not an empty - list reported as fact. +#: Characters an ``ssget`` filter string treats as wildcards (AutoCAD's +#: ``wcmatch`` grammar). Symbol names may carry most of them: a dimension +#: style called ``A.B`` also matched ``A_B``, ``N#1`` matched ``N21``, +#: ``P[1]`` matched ``P1`` and ``S@X`` matched ``SAX`` until the name was +#: escaped (measured live, AutoCAD 2026). +_WCMATCH_SPECIALS = frozenset("*?#@.~[]-,`") + + +def _wcmatch_literal(text: str) -> str: + """``text`` spelled so an ``ssget`` filter matches it literally. + + Every wildcard-significant character is escaped with a backtick, the + grammar's own escape. A symbol name can never contain a backtick itself + (AutoCAD refuses one), so the escaped form is unambiguous; the match stays + case-insensitive, as any symbol-table name comparison is. """ - wanted = style_name.lower() - handles: list[str] = [] - for index in range(doc.Layouts.Count): - block = doc.Layouts.Item(index).Block - for position in range(block.Count): - obj = _com_unnarrow(block.Item(position)) - if "Dimension" not in str(obj.ObjectName): - continue - if str(obj.StyleName).lower() == wanted: - handles.append(str(obj.Handle)) - return handles + return "".join("`" + char if char in _WCMATCH_SPECIALS else char for char in text) + + +def _com_dimensions_using(doc, style_name: str) -> list[str]: + """Handles of every DIMENSION, in every layout, whose style is ``style_name``. + + One filtered ``SelectionSet.Select(acSelectionSetAll)`` — the ``ssget "X"`` + route with ``(0 . "DIMENSION") (3 .