- tests/fixtures/plot/.page-setups-metric.dxf.ocs.lock was the app's own
single-instance lock file, committed by accident in #1343; remove it and
ignore *.ocs.lock.
- load_plotsettings_into_dialog: #1343's refresh_custom_scale_fields borrowed
self while #1360 still wrote through the plot_dialog borrow; write the
style error first.
- The wasm list_printers stub returns the Result #1358 gave the native one.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
Opening a drawing that another program or machine holds without read
sharing used to fail with a raw OS error, and the memory-mapped fast
path could kill the process outright: a mapped file that shrinks
mid-parse (a writer saving over it) or a cloud placeholder that cannot
page in raises an in-page exception no Rust code can catch.
Read the DWG through a guarded fast path instead: mmap when possible,
and fall back to one in-memory snapshot read — opened with the most
permissive share mode Windows offers (FILE_SHARE_READ | WRITE |
DELETE) — whenever the map fails or an I/O error surfaces mid-parse.
A snapshot can never fault: a concurrent writer just ends the read
early. Sharing violations (os error 32/33) now answer with a message
that names the file and the remedy, and OneDrive-style placeholder
attributes skip mmap entirely.
Tables copied from other applications into the plot styles folder were
listed but refused on selection, showing as "missing" with the reason
lost on a console: the loader failed hard on a checksum or a text length
that did not add up and on any non-UTF-8 byte — and those files carry
their descriptions in Windows-1252.
- decompress_ctb reads the file as it is: a checksum or length mismatch is
reported and read past (a genuinely cut-off file is still an error), a
zlib stream with a bad trailer is retried as raw deflate, and text that
is not UTF-8 is read as Windows-1252 (pens and colours are numbers and
unaffected; other code pages only show wrong characters in descriptions).
- PlotStyleTable carries load_warnings; every load site reports them on the
command line, so a table that needed tolerance says so.
- load_named explains itself: a .stb name says named tables are not
supported yet, a .ctb missing from the folder says which folder to copy
it into (instead of "Unknown built-in plot style").
- The Plot dialog shows the reason under the Table row (style_error) —
from a selection, from a page setup that names a table, and from the
Load… picker, whose error used to be swallowed.
- A table picked with Load… from outside the plot styles folder is copied
into it (unless a file of that name is already there), so it is listed
from then on and page setups naming it resolve after a restart.
Tests: flipped checksum, text length off by one, a short payload field,
Windows-1252 descriptions, a truncated stream, .stb and missing names.
Co-Authored-By: Claude Opus 5 <[email protected]>
- #1352 pinned cadkernel/cadkernel-constraints to ramox81's fork. Its one
extra commit (cadkernel #67, SymmetricLineDirections) is now on
HakanSeven12/cadkernel main as the same SHA, so the manifests and lock
point back at the upstream repo at rev 0493b59.
- SYCONSTRAINT reports a repeated relation with the same message Fix and
Vertical use, as an error instead of plain output.
- The knot-less spline test consumes the add_entity results.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
The plot dialog asks CUPS printers for their sheets and margins; on Windows
the query was a stub, so every printer showed the paper catalogue with
unverified margins. The driver is now asked the way the plot dialog expects:
- windows_media (compiled and tested everywhere): the three parallel
DeviceCapabilities tables (ids, sizes in tenths of a millimetre, 64-WCHAR
names) become one PrinterMedia per distinct size — a rotated or
borderless twin folds onto its sheet, catalogue sheets keep their name,
unknown sizes carry the driver's — with the device's margins on every
entry and the driver's default sheet found by id or by user size.
- print_to_printer (Windows): DeviceCapabilitiesW for the tables, the
default DEVMODE for the default sheet, one portrait printer DC for the
unprintable margins (physical offsets and extents, per device — a
documented approximation). The DEVMODE fetch is shared with the print
path (with_printer_devmode).
- A direct print now asks the driver for the plot's own sheet (by id, or
as a user-defined size), so an A3 plot prints on A3 rather than shrunk
into the driver's default; a driver that refuses the sheet is asked for
the orientation alone.
- The capability cache remembers printers that could not be asked, so a
slow failing query runs once per session, not on every dialog open.
- PRINTERS <name> reports a printer's sheets, margins and default sheet.
Tests: names (NUL trim, full slot, lossy), media folding and dropping,
default by id / user size / none, DC margins, paper id lookup.
Co-Authored-By: Claude Opus 5 <[email protected]>
On Windows a plot with no printer chosen went to the PDF application's
bare "print" verb — whatever that application considered its default —
and a viewer without a "printto" verb (Edge, store readers) silently
dropped the printer the dialog had chosen. The dialog also never named the
system default, and a failed printer enumeration left only "Default" and
"PDF" with the reason written to a console nobody sees.
- The default printer is looked up by name (GetDefaultPrinterW) both for
the dialog's "System default printer (…)" entry and before every job.
- A pure route decision (plan_print_route) resolves "Default" to that
name and uses the PDF application only when it registered a "printto"
verb for .pdf (AssocQueryStringW); otherwise the job goes straight to
the spooler through the existing GDI path. The report says which:
"Sent to printer: NAME (direct print | via the PDF application)".
- list_printers reports the system's own error; the plot dialog shows it
under the printer row and the command line warns once. A desktop without
CUPS is an empty list, not an error.
- PRINTERS command: default printer, listed printers (or the error) and,
on Windows, the print route — a report to paste into an issue.
- Properties… on the Default entry opens that printer's preferences sheet.
- GDI path: the driver's dmSize is kept (its private data lives behind
it), the print raster is no longer memoised for the session, and a
spooled temporary PDF is removed.
Tests: route planning (named / blank / no default / printto or not), the
PRINTERS command; the two Windows-only GDI tests pass the resolved name.
Co-Authored-By: Claude Opus 5 <[email protected]>
On the web build, {"op":"open","path":…} resolves the name against the
browser's recent-files cache, so a file that is not already there cannot be
opened by the control channel. Accept {"op":"open","name":…,
"data_base64":…} instead: the bytes go through the same parse, caching
and recovery path as a file chosen in the browser picker
(io::open_bytes_web, now shared with pick_and_load_web).
Native builds keep path-based open and reject data_base64 with
invalid_request.
Refs #1349
crate::network and the open crate are desktop-only, but the community-font
download (font_repo.rs) and the XREF row Open action call them
unconditionally, so cargo check --target wasm32-unknown-unknown fails on
main. Gate both; on the web download_fonts returns an error, since the web
build has no network client or fonts folder.
When a display plot style maps an ACI to a fill pattern, wire fill
triangles are re-emitted as PLOTSTYLE hatches. Those took
depth_override raw — a per-block child label for block wires and 0.0
for top-level ones — while everything else in the hatch/wipeout pass
sorts at the composed depths[insert] + label * half. The fill could
land outside its host's band and get buried under its own block's
masks (same shape as the block-text bug).
Both the display and PDF paths now travel through wire_draw_depth /
the PlotWire's composed depth, and the display cache key rides the
depth generation so a DRAWORDER edit re-ranks the fills without
needing a wire rebuild.
load_file, used by headless export, xrefs and block import, finalizes a
drawing without purging, so the spline degree repair never ran there and
`--export drawing.dxf out.dwg` still hit the knot underflow. Run the repair
when any load is finalized.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Retaining a line's length added a P2PDistance constraint, whose gradient
divides by the current distance. For a zero-length line the solver got a NaN
Jacobian and its SVD fallback never returned, freezing the editor (e.g.
ECONSTRAINT on a line drawn by clicking one point twice). Hold coincident
endpoints together with per-axis differences instead.
Control-point splines read without knots are given a degree their control
points can carry (at most n - 1) and a clamped knot vector when a drawing
loads. Generating knots for fewer points than the degree underflowed during
DWG write: a panic in debug builds, an unbounded allocation in release, and
reachable from autosave. A lone control point with no knots or fit points is
purged as corrupt.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Properties… next to the printer used to open the CUPS web interface in a
browser. It now edits the driver options of the *selected* printer the
way LibreOffice's Properties… does, without leaving the plot dialog:
- CUPS platforms (Linux, any desktop; macOS): an in-line editor lists the
printer's PPD options as `lpoptions -l` reports them — media type,
colour mode, quality, duplex, trays, scaling — with readable choice
labels, Reset / Cancel / Apply. Apply keeps only the choices that differ
from the driver defaults, per printer, in the plot config, and every job
for that printer passes them as `lp -o key=value`. Where CUPS cannot
answer, the desktop's printer settings panel opens instead of a browser.
- Windows: the driver's document-properties sheet (`DocumentPropertiesW`
with prompt, owned by the main window) and, on OK, the result stored as
the user's printing preferences for that printer (`SetPrinter` level 9),
which the job honours. `printui /e` (printing preferences) replaces the
device-properties sheet as the fallback.
The desktop print portal was tried first and dropped: GTK 4's print
dialog loses the preselected printer when the default queue is listed
before it, so the dialog always opened on the wrong printer.
Dialog layout: the modal grows to 940×620; Printer / plotter and Paper
size share a line with their pickers (long queue and media names now fit);
Copies + Properties… and Custom… sit beneath; the custom-sheet editor
labels each margin (Left / Right, Top / Bottom) and shows the unit.
Also recognises `none_device`, AutoCAD's stored spelling for "no plotter",
and writes it back unchanged.
Tests: `lpoptions` parsing and keyword humanising, the editor flow (load,
stale answer ignored, set, apply, remember, reset, error, cancel), device
name round-trips including `none_device`.
Co-Authored-By: Claude Opus 5 <[email protected]>
AutoCAD lets a plotter configuration carry user-defined sheets; a drawing
then names them like any other media (`Roll_24_(609.60_x_1500.00_MM)`).
The plot dialog now defines such sheets inline, under the Size row:
name, width/height, unit, and the printable margins in that unit. Added
sheets are selected at once, offered for every device after the device's
own list, remembered in the plot config across sessions, and written to
the layout with their own margins (a printer that reports the same size
still wins with its hardware margins). A selected user-defined sheet can
be removed; the sheet itself stays selectable in the drawing that uses it.
`CustomPaper` / `Margins` live in the paper catalogue with serde support;
`PlotDevice::margins_mm` consults the user's definitions. The Plot
dialog's form helpers (button and field styles, dividers, `label :
control` rows) move to `ui::style::form` so the next dialogs share them
instead of copying.
Tests: draft validation and seeding, catalogue naming of user sheets,
device margins with user definitions, and dialog round-trips (add,
select, persist, write, replace, remove, refuse an invalid size).
Co-Authored-By: Claude Opus 5 <[email protected]>
Replace the provisional PDF margins with the values AutoCAD's Plotter
Configuration Editor lists for `DWG To PDF.pc3`. They depend on the sheet
family and variant, not the size: ISO sheets 5/17/6/18 mm (left, bottom,
right, top), ISO expand 5/10/6/11 mm, ISO full bleed 0/1/0/1 mm; ANSI and
ARCH sheets 0.23/0.70 in, expand 0.23/0.42 in, full bleed 0.03 in all
round. Inch sets are stored in millimetres like the sheet itself.
Co-Authored-By: Claude Opus 5 <[email protected]>
A page setup names its plotter, and AutoCAD looks that name up in its own
configurations: `DWG To PDF.pc3` for PDF output, the spooler name for a
system printer, `None` when nothing is assigned. Drawings saved here used
to carry the dialog's label ("Save to PDF file…"), so AutoCAD reported a
missing device on every layout plotted to PDF from Open CAD Studio.
- New `io::plot_device`: `PlotDevice { None, Pdf, Printer }` with AutoCAD's
spellings, a reader for stored names that keeps any plotter
configuration AutoCAD wrote (an HP `.pc3`, `DWF6 ePlot.pc3` — plotted to
PDF here but preserved for AutoCAD) and upgrades the legacy label.
- Printable areas: the PDF driver's margins per sheet variant (full bleed
edge to edge; standard / expand insets provisional until read off
AutoCAD's plotter configuration), and for CUPS printers the media and
margins the queue reports over IPP (`ipptool` `media-col-database`),
parsed once per printer and cached. Windows enumeration follows with
native GDI printing.
- Page setups now store the device in AutoCAD's spelling and the margins
of the chosen device/sheet, keeping the drawing's own values while
neither changed (AutoCAD's `PAPERUPDATE` behaviour). The paper-space
printable-area rectangle follows.
- Plot dialog: the default entry names the printer it resolves to; a
printer that answered lists its own sheets with a note; the media query
runs in the background and answers for a since-deselected printer are
dropped.
Tests: device names, stored-name resolution, PDF margin table, IPP media
parsing (sizes, margins, borderless folding, default sheet), PWG names,
plus dialog ↔ layout round-trips for PDF, legacy label, preserved plotter
configuration, sheet change → margins, and printer selection.
Co-Authored-By: Claude Opus 5 <[email protected]>
AutoCAD stores a layout's sheet as the plotter driver's canonical media
name (`ISO_A4_(210.00_x_297.00_MM)`, `ANSI_full_bleed_B_(11.00_x_17.00_Inches)`),
so a drawing saved here has to carry the same spelling for AutoCAD to find
the sheet in its DWG To PDF list, and a name that arrives from AutoCAD must
resolve to its dimensions and go back out untouched.
- New `io::paper_catalog` replaces the eight-entry `paper_sizes`: ISO A/B,
ANSI, ARCH (incl. E1) and JIS series with full-bleed / expand variants,
Letter / Legal / Tabloid, millimetre and inch units, a parser that
reconstructs any well-formed AutoCAD name (custom or driver-specific)
from its embedded dimensions, driver / legacy-config aliases (`A4`,
`Letter`, `Ledger`), and dimension matching that prefers the plain sheet.
- The Plot dialog stores the canonical name and shows the sheet as
`ISO A4 (210 × 297 mm)`; a sheet that only exists in the drawing is
listed first so it stays selectable.
- A sheet the user did not change keeps the drawing's own media name
(`paper_name_for_settings`), so a Windows printer's `A4` is not rewritten
to `ISO_A4_(…)` just by passing through the dialog.
- Drops the unused `PlotFormat` / `PlotOrientation` messages,
`paper_label_from_dims`, and three duplicated `match "A4" | …` tables.
Tests: catalogue spelling, parser round-trip, aliases, dimension matching,
display formatting, plus dialog ↔ layout round-trips for driver-named,
catalogue and unknown sheets.
Co-Authored-By: Claude Opus 5 <[email protected]>
- IMAGEEMBED: pack raster images into OLE2FRAME entities so a .dwg is
self-contained; 'Embed Image' entry in the XREF attach menu; control/MCP
op embed_image for external tools; dxf_name for OLE2FRAME queries.
- Scene: populate_images_from_document now rebuilds OLE2FRAME and PDF
underlay models too (they used to vanish after any XREF op).
- Printing: add PRINTER_ENUM_NETWORK pass when listing printers; GDI
spooler fallback (hayro raster at 300 DPI + StretchDIBits) for hosts
whose PDF handler registers no print verb; DEVMODE orientation with a
90-degree raster rotation fallback for v4 drivers; fix the fit-scale
precedence bug that printed blank pages.
- PDF export dialogs on Windows use the async file dialog so the native
overwrite confirmation no longer deadlocks the UI thread.
- Plot dialog: persist the chosen printer (saved on selection) and the
picked plot window across sessions; page setups with no device no
longer reset the output device.
- Community fonts: detect unresolvable .shx references on open and offer
a download from the repository's fonts/ folder (contribution guide in
fonts/README.md); custom font source URL setting for intranet font
servers; downloaded fonts resolve from the per-user fonts directory.
Right-click actions bound to the row under the cursor, split-button toolbar groups with shared borders and hover states, and a notice when an operation needs a selection first.
Path normalization with stable keys, relative/absolute/none pathtypes, wildcard matching, and reference entries with loaded, stale, missing, and unreferenced states.
Problem
The open guard rejects circles and arcs with a radius below 1e-10. A normal
open fails when a drawing contains one. The user gets the recovery prompt.
AutoCAD and nanoCAD open these drawings.
The check came from ace99541. Its comment says the tessellator cannot sample
a degenerate curve. That was true of truck's parameter_division. Commit
8c7ebdc9 removed truck. Commit 96be8214 made the kernel sample circles and
arcs by sweep angle with a floored segment count. The reason for the check
no longer exists.
Change
- Remove the near-zero radius check for circles and arcs.
- Keep the checks for non-finite values and radii above 1e10.
- Update the guard comments. The purge rejects parser desync. It does not
reject degenerate geometry. Be generous in what you accept and strict in
what you produce.
- Add tests: a zero radius passes the guard, an absurd radius does not.
- Change the automation purge test sample from radius 0 to radius 1e11.
Behavior
- Zero-radius circles and arcs load. They render as dots.
- Tessellation, hit testing, snapping, and hatch boundaries accept a zero
radius.
- The DXF writer does not change.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
Saving a plot style table wrote every value of its custom lineweight table
with two decimals, so a pen such as 0.035 mm came back as 0.04 mm after a
save and reload, and every color using it plotted with the wrong weight.
Write the lineweights in full, through the same formatting the scale factor
already uses, which keeps the decimal point on a whole value.
Co-Authored-By: Claude Opus 5 <[email protected]>
Include decoded IMAGE/OLE content in the shared PDF plotting path,
with clipping, transforms, transparency, draw order, and resource reuse.
Use named page content for export and printing.
Fixes#1266
Populate the plot-dialog printer list from the Windows spooler cache.
Validate record counts and UTF-16 name bounds before reading the buffer.
Refs #1261
The vertex-normal fallback compared the cross product's length with
f64::EPSILON, an area-scale threshold, so a valid triangle small enough
fell back to the leaning vertex normal again. Fall back only when the
cross product is zero.
Co-Authored-By: Claude Opus 5 <[email protected]>
The vertex-normal fallback compared the cross product's length with
f32::EPSILON, an area-scale threshold, so a valid facet with 1e-4 edges
(cross product 1e-8) took the leaning vertex normal again. Fall back only
when the cross product is zero.
Co-Authored-By: Claude Opus 5 <[email protected]>
PlotStyleTable::to_text wrote scale_factor with one decimal, and saving
from the plot style panel rewrites the whole loaded table. A table with
apply_factor on and scale_factor=0.25 came back as 0.2, thinning every
scaled lineweight by a fifth.
Write the factor in full, keeping the decimal point on a whole value.
Co-Authored-By: Claude Opus 5 <[email protected]>
In a .lin file the description is optional and the comma goes with it, so
"*PLAIN" followed by "A,.5,-.25" is a complete definition. parse only kept
a header that contained a comma, so such a linetype was dropped silently
and a CENTERLTYPE naming it then reported that it was not loaded.
Take the whole header as the name when there is no comma.
Co-Authored-By: Claude Opus 5 <[email protected]>
build_stl took the facet normal from the first vertex's normal whenever the
mesh had normals. On smoothed meshes (tessellated curved solids, OBJ imports
with vn) that normal leans off the triangle, while STL defines the facet
normal as the triangle's own unit normal.
Use the triangle's own normal, and keep the vertex normal only for a
triangle with no area. Meshes without normals export as before.
Co-Authored-By: Claude Opus 5 <[email protected]>