Picks up the DXF over-annotative MULTILEADER fix (companion to the MTEXT
one): enable_annotation_scale now defaults false and DXF reads code 293.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Picks up the DXF over-annotative-MTEXT fix: acadrust now defaults
MTEXT.is_annotative to false, so imported DXF MTEXT is no longer flagged
annotative and over-scaled in annotation-scaled viewports.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Make objects genuinely annotative — carrying real per-scale
representations that interoperate with other CAD apps — rather than just
a native flag. `create_annotation_context` synthesizes the extension-dict
chain (AcDbContextDataManager -> ACDB_ANNOTATIONSCALES -> per-scale leaf)
from the entity's placement; the editable Annotative toggle now covers
Text and block references as well as MText/MLeader; and a new
"Annotation Object Scale" dialog (OBJECTSCALE) adds/removes an object's
per-scale memberships. Requires the acadrust ObjectContextData encoder
(Cargo.lock bump).
Also fixes a render bug where objects carrying an *empty*
ACDB_ANNOTATIONSCALES (a single-representation marker with no per-scale
reps) were treated as annotative and (mis)scaled by the annotation factor
in scaled paper viewports, ballooning the text: an object is now
annotative-by-context only when its scale collection is non-empty, so
such objects render at their base geometry.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The "Dim line color" row on Leader and Dimension entities was
read-only: an earlier attempt wired it to Leader.override_color, which
acadrust never serialises, so the pick was lost on save. Store it
instead as a standard ACAD_DSTYLE per-object dimension-style override
(DXF code 176, an ACI index) through the existing dim_override codec,
so it round-trips through both DWG and DXF like the other dim
overrides. RGB picks collapse to the nearest ACI, matching the rest of
the dim-colour stack (dimension styles are index-only through the file
layer). The renderer prefers the override over the style's DIMCLRD.
The write branch is guarded to leaders and dimensions so a mixed
selection cannot stamp the override onto other entity types.
Bumps acadrust to c9fe982, which carries parsed XDATA onto dimensions
on DXF read (it was dropping common.extended_data), so the dimension
override persists on DXF save as well as DWG.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Pulls the acadrust fix that emits XDATA for every entity on DXF save (not
just hatches), so the leader dimension overrides and hyperlinks now
round-trip through DXF as well as DWG.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Picks up the DWG reader reflecting CANNOSCALE from the AcDbVariableDictionary
into the header, so the status-bar annotation-scale pill shows the drawing's
real model-space scale (e.g. 1:70) instead of the "1:1" default.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Resolve, per selected entity, whether it is annotative (from its text /
dimension / multileader style, or its own flag) and — when annotative — the
name(s) of the annotation scale(s) applied to it, shown as a read-only
"Annotative scale" row (Text / MText / Leader / MLeader).
The applied scale is read from the entity's per-scale object-context leaves
via the new acadrust CadDocument.context_scales map (context leaf ->
AcDbScale handle), walked from the entity's extension dictionary
(AcDbContextDataManager -> ACDB_ANNOTATIONSCALES). Bumps acadrust to pick up
that parsing.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Pulls acadrust 4ac3d68: entities with proxy/preview vector graphics
(MULTILEADER, WIPEOUT) omit the R2013+ has_ds_data bit, so reading it
desynced the record and produced garbage MultiLeader text_location
(~2e39 → NaN glyphs). The labels now parse with finite locations and
render through the existing MText SDF path.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
First real Windows build surfaced errors that a Linux host couldn't catch:
* Enable the missing `windows` feature gates (Win32_System_LibraryLoader,
Win32_System_SystemServices, Win32_Security — the last brings in the
`Ex` registry APIs) so the imports resolve.
* Depend on `windows-core`: the `#[implement]` macro expands to
`windows_core::` paths, which need the crate nameable in the root.
* `IClassFactory::LockServer` takes `Win32::Foundation::BOOL`, not a
`core` BOOL.
* Name the returned image type via a new `dwg_thumbnailer::RgbaImage`
re-export instead of a direct `image` dependency.
Verified with `cargo check --target x86_64-pc-windows-gnu -p
dwg-thumbnailer-win` (and the native check still passes).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Round-trip a raster preview through the DWG's embedded preview slot and
surface it to the OS file managers, so drawings show their contents in
Explorer / Finder / Nautilus instead of a generic document icon.
OCS:
* io::thumbnail — rasterize the scene to a BMP DIB on save (embedded via
acadrust's new Preview type) and read it back on open for the Start
page recent list; `--dwg-thumbnail IN OUT SIZE` CLI extracts a badged
PNG for external thumbnailers.
* io::file_association::install_thumbnailer — self-install the OS
integration on startup: Linux writes a .thumbnailer + hicolor mimetype
icons; Windows registers the IThumbnailProvider DLL under HKCU.
Shared core:
* crates/dwg-thumbnailer — lean (image-only) preview extractor + the
`badge_dwg` full-width "DWG" band, used by every platform so the
ribbon is single-sourced. Ships an rlib+staticlib.
* crates/dwg-thumbnailer-win — IThumbnailProvider COM in-proc server
(cfg(windows), CI-built as a workspace member).
* macos/ — QuickLook thumbnail extension (Swift + C-ABI bridge to the
core staticlib), assembled into a .appex.
Icons & packaging (single SVG source -> per-platform assets in CI):
* assets/mimetypes/image-vnd.{dwg,dxf}.svg — themed file icons.
* packaging: WiX ships dwg/dxf.ico + the thumbnail DLL and points the
ProgIds at them; Info.plist gets CFBundleTypeIconFile + the QuickLook
extension in Contents/PlugIns.
* release.yml — generate .ico (Windows) and .icns (macOS) from the SVGs,
build the QuickLook .appex, and bundle everything.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Generate a small wire-raster thumbnail when saving a DWG and embed it via
acadrust's preview API, so OCS drawings show a thumbnail in file browsers
and other CAD applications. Read the embedded preview back cheaply (file
header + preview bytes only, no full parse) to show a thumbnail per file
in the Start page's Recent Documents panel.
- io/thumbnail.rs: CPU rasterizer (current-layout wires -> BMP DIB) plus
a preview-only reader/decoder (BMP/PNG -> iced handle) for the Start
page.
- Embedded on save via stamp_thumbnail on both save paths.
- recent_thumbs cache filled on config load / recents change (never from
a view); a thumbnail is drawn beside each recent file.
Requires acadrust at 3111694 (preview read/write), already pinned in
Cargo.lock.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
App preferences were spread across six flat plain-text files (settings.txt,
recent.txt, recent_limit.txt, statusbar.txt, ribbon.txt, plot.txt), each with a
bespoke line parser and no grouping. Replace them with a single grouped JSON,
serialized via serde (enabling the derive feature; serde_json was already a
dependency).
- New app/config.rs: AppConfig { settings, recent, statusbar, ribbon, plot } →
<config>/settings.json (pretty JSON), load()/save().
- Make UserSettings, StatusBarConfig/StatusPill, CollapseMode and
PlotDialogState serde-derived; #[serde(skip)] marks the plot dialog's runtime
fields so only print preferences persist. Their bespoke .txt load/save is
removed.
- App gains current_config/apply_config/save_config: one AppConfig::load at
startup distributes into live state; every settings-change site
(persist_settings_if_changed, recent add/remove/limit, status-bar pill
toggle, ribbon density, plot commit) routes through save_config, which diffs
against the last write to avoid thrashing.
- Clean start: the old flat files are ignored (no migration). ocad.pgp stays a
separate hand-editable file.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Picks up acadrust 716d4b4: DWG reader synthesizes the mandatory
*Model_Space/*Paper_Space block records when a file's BLOCK_HEADER for
them never materialised, so model-space geometry is no longer dropped on
save (file shrank + reopened blank).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Opening a DXF whose active viewport is stored uppercase (*ACTIVE),
editing and re-saving produced a file with two full-window model-space
viewports — the file's own *ACTIVE plus a fresh *Active we add on save.
The reserved viewport name is case-insensitive, so the two didn't
collapse and other CAD apps drew them one on top of the other, which
read as the copied objects being linked / "reacting as one".
save_model_tiles_to_vports dropped existing active viewports with a
case-sensitive `name != "*Active"` filter, so an uppercase *ACTIVE
survived and sat next to the new entry. Route every *Active comparison
(save filter + camera/tile restore) through a case-insensitive
is_active_vport_name helper so the file's record is recognised and
replaced instead of duplicated.
Also bumps acadrust to #99f75fe (merged PR #1): the ASCII DXF writer now
emits shortest round-trippable floats instead of the full 16-digit
expansion, halving the round-tripped file size.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Pulls acadrust f1f4dbf, which makes the DXF reader parse hatch pattern
definition lines (and types group code 79 correctly) so custom/dashed
patterns load identically to the DWG instead of collapsing to flat
horizontal lines.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Pulls acadrust d32c9cf so DXF import reads RGB layer colours (code 420)
instead of collapsing every true-colour layer to Index(7)/white; ByLayer
entities then resolve to the correct colour.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Two related steps for the text pipeline:
- TEXT parsing now goes through acadrust's parse_plain_text (like MTEXT
uses parse_mtext). text::acad_text_encode parses the value's %% codes —
including %%u/%%o underline/overline and %%nnn — and re-encodes to the
stroke tessellator's \L/\O + resolved-Unicode grammar, so OCS no longer
tokenizes TEXT %% codes itself. (ATTRIB/TOLERANCE/DIMENSION still use the
local tokenizer for now.) Bumps the acadrust pin to the commit that
completes the TEXT/MTEXT control-code set.
- SDF text (OCS_TEXT_SDF) now covers MTEXT and every text-producing
entity, not just top-level TEXT. TextStroke carries a GlyphRun (layout
params); the collector walks convert()'s TruckObject::Text runs and
applies annotation scale exactly as tessellate does for strokes.
layout_glyph_quads shares lff::tokenize_run so %% specials resolve and
\L/\O decorations are skipped (decoration LINES in SDF are still TODO).
Verified: stroke rendering unchanged with the flag off; SDF glyphs render
correctly for TEXT and MTEXT with OCS_TEXT_SDF=1. 154 lib tests green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
OCS maintained a second MTEXT inline-code parser that duplicated
acadrust's `mtext_format::parse_mtext`. Replace it with an adapter
(`adapt_mtext_paragraphs`) that maps acadrust's structured MTextDocument
onto the existing layout types (`MTextLine`/`RunState`); the layout
engine (`layout_mtext`, `mtext_line_count`, and their callers) is
unchanged. Removed the hand-rolled `parse_mtext_paragraphs*` and its
helpers.
Validated during migration by a differential test (adapter vs the old
parser matched byte-for-byte over a 20-case corpus) and confirmed
rendering identically in the app; the differential test is replaced with
adapter-only unit tests. Relies on acadrust's new `MTextScalar` to tell
absolute `\H` from relative `\Hx` (bumped the acadrust pin).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Pull in acadrust 4d51947, which makes SatDocument::record() an O(1) hit
instead of a linear scan. Tessellating an ACIS-heavy drawing was
O(records²) per solid; this drops the post-load mesh build for Clinic.dwg
(~9900 xref'd solids) from ~2.2s to ~0.85s of parallel tessellation, with
identical geometry.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Opening a drawing that xrefs several 3D-heavy DWGs (e.g. Clinic.dwg with
5 discipline files) took ~137s, effectively all of it inside the DWG
parser's AcDs SAB blob extraction.
Bump the acadrust pin to ceef045, which bounds the AcDs end-marker search
so a missing marker family no longer scans the whole buffer per record.
Total xref resolve for Clinic.dwg drops from ~97s to ~4s.
Also make the post-xref mesh pass incremental: the host solids were
already tessellated by the background loader and the merge assigns fresh
handles to every imported entity, so `populate_missing_meshes_from_document`
keeps the cached host meshes and tessellates only the merged xref solids
instead of clearing and rebuilding the whole document.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Picks up the acadrust change that writes xref blocks as external
references instead of serializing their resolved contents as owned
entities. An xref attached with XATTACH now survives a save + reopen as
a reference instead of being bound/exploded into loose objects.
Closes#268
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A spline stored by fit points (DWG R2013+ scenario 2) has no control points
or knots, so the renderer — which built its curve from control points —
produced nothing and the spline was invisible. When a spline has fewer than
two control points but at least two fit points, interpolate a smooth
Catmull-Rom curve through the fit points and emit it as a dense polyline.
Open ends use reflected phantom points; closed/periodic splines wrap.
Pairs with the acadrust bump (e8e422a) that decodes the R2013+ fit-point
scenario correctly — together they make the fit-point spline in
Annotation-2D-Mesh-Solid-BIM.dwg render.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Fixes entity transparency dropping to opaque on DWG read (e.g. the 85%
area-highlight solids in Clinic_Architectural.dwg). The ENC codec read/wrote
the transparency BL before the rgb BL (real order is rgb then transparency)
and stored the on-disk opacity byte without inverting to the internal
transparency-amount representation.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Fixes BIM 3D solids rendering at wrong positions — the AcDs SAB blob→solid
pairing now follows handle order instead of object-stream order, so every
solid gets its own geometry and body transform. Corrects the regression that
broke 3D solids in 0.7.3 (they were correct in 0.7.2) and places wall/floor
solids that previously piled near the origin.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Pulls the acadrust fixes that make a saved DWG containing 3D solids open
cleanly in a strict conformance reader: the AcDsPrototype datastore section is
now built correctly (segment-header ds_version, datidx, and the handle-sorted
search index) so 3D solids keep their geometry instead of being dropped, and a
dim-style/block handle collision on read is repaired so layouts round-trip.
Validated end-to-end on two real 107- and 273-solid files.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
OCS models 3-D solids with truck, but extrude/boolean results carried no ACIS
body, so other CAD apps dropped them on open. Add an exact planar exporter and
run it at save time.
- scene::convert::acis_export::planar_solid_to_sat walks a truck Solid whose
faces are all planes bounded by straight edges into a vertex/face-ring mesh
and builds an exact ACIS body via acadrust's build_planar_body. Curved
faces/edges return None (left for the NURBS path). A signed-volume check
flips the winding if the shell comes out inside-out.
- OpenCADStudio::sync_truck_solids_to_acis fills in ACIS geometry for every
cached truck solid that still has none, just before a Save / Save-As, so the
written DWG/DXF carries real modeler geometry.
- Bump acadrust to 9142a50 (build_planar_body + classic-ACIS AcDs roundtrip fix).
Booleans (already cached as truck solids) now export exact geometry. Extruded
polygons will too once the EXTRUDE command caches its truck solid (a separate
change kept out of this commit because command_driver.rs holds unrelated
in-progress edits).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Pulls the acadrust fix that links R2013+ 3DSOLID/REGION/BODY geometry to its
AcDs SAB blob (writes the has_ds_data flag, corrects the AcDsPrototype header,
and pairs blobs in object-stream order). DWG files saved with 3D solids now
reopen with their geometry intact in AutoCAD/BricsCAD instead of being dropped
as an empty data stream.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Bump acadrust to ef0e231 (region/body history handle + viewport clip
boundary now stored) and populate the rows the builders left blank:
- Solid3D / Body "Show History": derived from the history handle's
presence (there is no separate stored bool — the DWG carries only the
H 350 history link).
- Body "History": the history handle, matching how Solid3D already shows
it.
- Viewport "Clipped": Yes when the clip-boundary handle is non-NULL.
Hatch Origin X/Y and Boundary retention stay blank by design: they are
not part of the persisted AcDbHatch record in DWG or DXF (retention is a
BHATCH command setting), so there is nothing to read.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Bump acadrust to a46cb8b (spline knot parameterization + mtext line-space
style now stored) and populate rows the builders left blank:
Entity-local: spline Knot Parameterization + closed-spline Area (shoelace),
mtext Line space style, hatch Spacing (pattern-line offset), leader Text
offset (annotation offset), region Area + Perimeter (wire-loop approx).
Doc-dependent (resolved in app/properties.rs where the document is
reachable): insert Block Unit + Unit factor (block record units vs
INSUNITS), underlay Name + Path (definition object), leader Text style /
vertical placement / overall scale and tolerance Text style (dimension
style), multileader Max points + segment angles (mleader style).
Rows needing an annotation-scale / sheet-set subsystem or an internal
PDF/DWF layer parser are intentionally left blank.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Pull in this session's acadrust work — the HELIX / ACAD_TABLE read+write,
underlay reader/writer/definition, spline tolerances & tangents, etc. The new
EntityType::Helix variant breaks three exhaustive matches; add its arm to
entity_type_name, the UI-name / DXF-name maps and entity_type_key.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
#249 — plugin XDATA written via write_record survived only in memory: the
acadrust DWG writer dropped ExtendedData::records on save. Bump acadrust to
e88a9a6 (records now encode to EED and decode back on read) and fix the host
side that fed it:
- ensure_app_id allocates a real APPID handle; a null handle serializes as
0 and the EED reference can't resolve, so the XDATA vanished on reopen.
- write_record / remove_record drop stale raw_dwg_eed for the target app so
an edit made after a save/reopen wins over the pre-edit bytes.
#250 — out-of-process plugins got a throwaway document_mut() snapshot, so
edits to existing entities were silently discarded and deletion wasn't
expressible at all. Add the missing mutation surface:
- UpdateEntity / RemoveEntity IPC requests + HostApi::update_entity /
remove_entity (default in-process impls, RPC overrides on the client that
invalidate the stale document cache).
- Scene::update_entity replaces the entity in place, preserving its handle
and owning block, and reseeds only its derived caches; remove reuses the
cache-coherent erase_entities (which also honours layer locks).
- document_mut() is documented as a local-only snapshot out-of-process.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Pull in acadrust 4ae2ceb, which fixes two R2013+ (AC1027) DWG writer
bugs that made OCS-saved files unreadable in AutoCAD/TrueView/BricsCAD:
- #182: R2013 header string stream was unlocatable because the file
wrote maintenance_version 0 (omitting the R2010+ extra RL).
- #225: 3DSOLID/REGION/BODY omitted the R2013+ revision block, corrupting
the entity stream and dropping 3D solids on load.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Pull in acadrust ad0471e, which corrects the entity ENC color decode
for book colors. R2007 files whose hatches carry a book color (e.g.
the santeen.dwg from issue 46) desynced on read, dropping all 270
SOLID hatch boundaries; they now load and render.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Pulls the acadrust translate fix: MOVE now carries a block's attributes
and the whole MultiLeader (text/base/block-content anchors + break
points), not just the insert point / leader line. No OCS code change —
the transform path already calls translate and re-tessellates.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Each row in the attribute editor now shows the attribute's prompt — the
text defined on the block's ATTDEF — falling back to the tag when the
block defines no prompt. The prompt is read from the block definition,
since attribute instances carry only tag + value.
Bumps the acadrust pin to pull the matching reader fix: its DWG object
reader was discarding the ATTDEF prompt, so every prompt came back empty.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Pulls the quadratic→linear AcDs blob-extraction fix in acadrust. 3D-heavy
DWGs (and master files that xref them) that took 28s+ to parse — minutes
for a multi-discipline set — now open in well under a second each.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>