Attach Underlay ribbon menu, DWF/DGN attach dialog, DGN true colours

- Insert > Reference: the PDF Attach button becomes an Attach Underlay menu
  (PDF / DWF / DGN); its face runs the last one chosen.
- DWFATTACH, DGNATTACH and ATTACH on a .dwf/.dwfx/.dgn open the Attach
  dialog for that kind: sheets or models with thumbnails (one chosen),
  placement, path type, file details with the sheet / model size, and for
  DGN the conversion units (sub units offer 1 / sub units per master as the
  scale).
- DGN V8 true colours come from the ExtendedColors record packed in the
  non-model attributes stream; the colour word's high part numbers them.
- A DGN model's extent includes each text's box (its length along the
  baseline and 1.5 x its height), as the reference sizes a model.
- DWFx text uses the family named in its embedded (deobfuscated) font.
- DWF contrast pivots at 0.73 once colours are turned over for a dark
  background.
- Hairline strokes are drawn solid one pixel wide, and underlay rasters are
  made at or below their size on screen so those lines stay unbroken.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
ramox81andClaude Opus 5.5 committed 2026-09-28 17:08:06 +03:00
1 parent fecbcd8138
commit 5b6cbff5b2
33 files changed
+745 -59

No files matched your search

+12 -1
View File
@@ -53,7 +53,18 @@ impl CadModule for InsertModule {
title: "Reference",
tools: vec![
RibbonItem::LargeTool(xattach::tool()),
RibbonItem::LargeTool(pdf_attach::tool()),
// PDF, DWF or DGN; the face runs the last one chosen.
RibbonItem::LargeDropdown {
id: "UNDERLAY_ATTACH",
label: "Attach Underlay",
icon: pdf_attach::ICON,
items: vec![
("PDFATTACH", "Attach PDF", pdf_attach::ICON),
("DWFATTACH", "Attach DWF", pdf_attach::ICON),
("DGNATTACH", "Attach DGN", pdf_attach::ICON),
],
default: "PDFATTACH",
},
RibbonItem::LargeTool(xclip::tool()),
RibbonItem::LargeTool(xadjust::tool()),
RibbonItem::LabeledTool(underlay_layers::tool()),
+16 -19
View File
@@ -23,20 +23,11 @@ use glam::DVec3;
use crate::command::{CadCommand, CmdOption, CmdResult, InputKind, WorkingPlane};
use crate::scene::model::wire_model::WireModel;
use crate::modules::{IconKind, ModuleEvent, ToolDef};
use crate::modules::IconKind;
pub const ICON: IconKind =
IconKind::Svg(include_bytes!("../../../assets/icons/underlay_layers.svg"));
pub fn tool() -> ToolDef {
ToolDef {
id: "PDFATTACH",
label: "Attach PDF",
icon: ICON,
event: ModuleEvent::Command("PDFATTACH".to_string()),
}
}
/// Units the Unit option offers: keyword, the name the prompt shows.
const UNITS: [(&str, &str); 9] = [
("MM", "Millimeters"),
@@ -172,11 +163,6 @@ impl PdfAttachCommand {
}
}
/// PDFATTACH with a file already chosen: starts at the page prompt.
pub fn with_file(path: &str, insunits: i16) -> Self {
Self::with_kind_file(UnderlayType::Pdf, path, insunits)
}
/// DWFATTACH / DGNATTACH / PDFATTACH with the file chosen: starts at the
/// page, sheet or model prompt.
pub fn with_kind_file(kind: UnderlayType, path: &str, insunits: i16) -> Self {
@@ -216,23 +202,34 @@ impl PdfAttachCommand {
}
}
/// The dialog's attach with the insertion point asked on screen: pages
/// chosen, the path to store, and any scale / rotation already given.
pub fn from_dialog(
/// The DWF / DGN (or PDF) Attach dialog: the sheet or model chosen and,
/// for DGN, whether sub units were chosen (their scale is then offered).
#[allow(clippy::too_many_arguments)]
pub fn from_kind_dialog(
kind: UnderlayType,
read_path: &str,
stored_path: &str,
pages: &[String],
scale: Option<f64>,
rotation_deg: Option<f64>,
sub_units: bool,
insunits: i16,
) -> Self {
let mut command = Self::with_file(read_path, insunits);
let mut command = Self::with_kind_file(kind, read_path, insunits);
command.stored_path = Some(stored_path.to_string());
if let Some((first, rest)) = pages.split_first() {
command.page = first.clone();
command.extra_pages = rest.to_vec();
}
command.rect = command.page_rect(&command.page);
if kind == UnderlayType::Dgn && sub_units {
let sub = crate::scene::model::underlay_vector::sheet(kind, read_path, &command.page)
.map(|s| s.sub_per_master)
.filter(|v| *v > 0.0)
.unwrap_or(1.0);
command.default_scale = 1.0 / sub;
command.scale = command.default_scale;
}
command.preset_scale = scale;
if let Some(s) = scale {
command.scale = s;