DWF/DGN underlays: clipping, layers, text, 3D DGN, colours and zoom extents

- PDFOSNAP, DWFOSNAP and DGNOSNAP are separate; UOSNAP sets them all and
  reads 2 while they differ. Enable Snap switches the selected kind's.
- DWFCLIP / DGNCLIP clip their own kind with the kind's prompts and message;
  CLIP routes DWF and DGN underlays there; Enter at a corner asks again.
- The right-edge underlay tools show for DWF and DGN too (import is PDF
  only).
- Underlay layers: DGN levels (V8 level table, "Default" listed as "0"; V7
  used levels named from the level name table), hidden through the layer
  override data with the DGN placeholder string first; a plotted DWF has
  none.
- V8 colour read from its colour word (by level, index, or a true colour
  drawn as its nearest index); 3D V8 elements drawn in plan (lines, line
  strings, ellipses and arcs by their orientation, text); a model's extent
  includes its text.
- DWF 6 TrueType text (font, height, rotation from the font opcode) and DWFx
  glyph runs are drawn; the default W2D colour map is the measured one.
- Contrast pulls lightness towards a per-kind pivot as measured (PDF 0.5,
  DWF 0.243, DGN 0.65).
- Zoom extents includes raster images and underlays whose frames are hidden.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
ramox81andClaude Opus 5.5 committed 2026-09-28 16:10:01 +03:00
1 parent 1cbe89ef62
commit 56ecf786f1
41 files changed
+593 -147

No files matched your search

+7 -2
View File
@@ -709,9 +709,14 @@ impl OpenCADStudio {
Err(_) => self.command_line.push_error(&format!("{path} not found.")),
}
}
"PDFCLIP" => {
"PDFCLIP" | "DWFCLIP" | "DGNCLIP" => {
use crate::command::CadCommand;
let command = crate::modules::insert::pdf_clip::PdfClipCommand::new();
let kind = match cmd {
"DWFCLIP" => codec::entities::UnderlayType::Dwf,
"DGNCLIP" => codec::entities::UnderlayType::Dgn,
_ => codec::entities::UnderlayType::Pdf,
};
let command = crate::modules::insert::pdf_clip::PdfClipCommand::for_kind(kind);
self.command_line.push_info(&command.prompt());
self.tabs[i].active_cmd = Some(Box::new(command));
}
+14 -11
View File
@@ -73,8 +73,7 @@ impl OpenCADStudio {
crate::entities::underlay::display_path(&self.pdf_stored_path(path, state.path_type.0));
}
/// ULAYERS: every PDF underlay of the drawing, the selected one first
/// shown.
/// ULAYERS: every underlay of the drawing, the selected one first shown.
pub(in crate::app) fn open_underlay_layers_dialog(&mut self, i: usize) {
let document = &self.tabs[i].scene.document;
let selected: Vec<codec::Handle> = self.tabs[i]
@@ -88,9 +87,6 @@ impl OpenCADStudio {
let codec::EntityType::Underlay(u) = entity else {
continue;
};
if u.underlay_type != codec::entities::UnderlayType::Pdf {
continue;
}
let Some(def) = crate::entities::underlay::definition(u, document) else {
continue;
};
@@ -99,10 +95,13 @@ impl OpenCADStudio {
if same > 0 {
name = format!("{name} ({})", same + 1);
}
let mut layers: Vec<String> = crate::scene::model::pdf_layers::layers(&def.file_path)
.into_iter()
.map(|l| l.name)
.collect();
let mut layers: Vec<String> = match u.underlay_type {
codec::entities::UnderlayType::Pdf => crate::scene::model::pdf_layers::layers(&def.file_path)
.into_iter()
.map(|l| l.name)
.collect(),
kind => crate::scene::model::underlay_vector::layer_names(kind, &def.file_path),
};
layers.sort_by_key(|n| n.to_lowercase());
layers.dedup();
targets.push(LayerTarget {
@@ -113,7 +112,7 @@ impl OpenCADStudio {
});
}
if targets.is_empty() {
self.command_line.push_error("No PDF underlays found.");
self.command_line.push_error("No underlays found.");
return;
}
let current = targets
@@ -152,7 +151,7 @@ impl OpenCADStudio {
/// Writes the layers each underlay turns off, as one undo step.
fn apply_underlay_layers(&mut self, i: usize, targets: Vec<LayerTarget>) {
use crate::scene::model::pdf_layers::{hidden_layers, LAYER_OVERRIDE_APP};
use crate::scene::model::pdf_layers::{hidden_layers, DGN_OVERRIDE_PLACEHOLDER, LAYER_OVERRIDE_APP};
let changed: Vec<LayerTarget> = targets
.into_iter()
.filter(|t| match self.tabs[i].scene.document.get_entity(t.handle) {
@@ -182,6 +181,10 @@ impl OpenCADStudio {
data.remove_record(LAYER_OVERRIDE_APP);
if !target.hidden.is_empty() {
let mut record = codec::xdata::ExtendedDataRecord::new(LAYER_OVERRIDE_APP);
// A DGN underlay's names follow a placeholder string.
if u.underlay_type == codec::entities::UnderlayType::Dgn {
record.add_value(codec::xdata::XDataValue::String(DGN_OVERRIDE_PLACEHOLDER.to_string()));
}
for name in &target.hidden {
record.add_value(codec::xdata::XDataValue::String(name.clone()));
}
+17 -8
View File
@@ -1,17 +1,20 @@
//! The right-edge PDF underlay and xref tools: their state comes from the
//! The right-edge underlay and xref tools: their state comes from the
//! selection, their buttons edit it.
use super::*;
use codec::entities::{Underlay, UnderlayDisplayFlags, UnderlayType};
use codec::entities::{Underlay, UnderlayDisplayFlags};
use crate::ui::ribbon::UnderlayContext;
impl OpenCADStudio {
/// The selected PDF underlays, or nothing when anything else is selected.
/// The selected underlays when they are all of one kind, or nothing
/// when anything else (or another kind) is selected.
fn selected_pdf_underlays(&self, i: usize) -> Vec<(codec::Handle, Underlay)> {
let mut out = Vec::new();
let mut out: Vec<(codec::Handle, Underlay)> = Vec::new();
for (handle, entity) in self.tabs[i].scene.selected_entities() {
match entity {
codec::EntityType::Underlay(u) if u.underlay_type == UnderlayType::Pdf => {
codec::EntityType::Underlay(u)
if out.first().is_none_or(|(_, first)| first.underlay_type == u.underlay_type) =>
{
out.push((handle, u.clone()));
}
_ => return Vec::new(),
@@ -51,9 +54,10 @@ impl OpenCADStudio {
} else {
self.selected_pdf_underlays(i).first().map(|(_, u)| {
UnderlayContext {
kind: u.underlay_type,
monochrome: u.flags.contains(UnderlayDisplayFlags::MONOCHROME),
shown: u.flags.contains(UnderlayDisplayFlags::ON),
snap: crate::scene::model::pdf_vector::pdf_osnap(),
snap: crate::scene::model::pdf_vector::underlay_osnap(u.underlay_type),
}
})
};
@@ -106,9 +110,14 @@ impl OpenCADStudio {
.is_some_and(|(_, u)| u.flags.contains(UnderlayDisplayFlags::ON));
self.edit_selected_underlays(i, "PDFUNDERLAY", |u| u.set_on(on));
}
// Enable Snap switches PDFOSNAP, DWFOSNAP or DGNOSNAP.
"_PDFULSNAP" => {
let on = !crate::scene::model::pdf_vector::pdf_osnap();
crate::scene::model::pdf_vector::set_pdf_osnap(on);
let Some((_, underlay)) = self.selected_pdf_underlays(i).into_iter().next() else {
return Some(Task::none());
};
let kind = underlay.underlay_type;
let on = !crate::scene::model::pdf_vector::underlay_osnap(kind);
crate::scene::model::pdf_vector::set_underlay_osnap(kind, on);
self.tabs[i].scene.reseed_underlays();
self.sync_underlay_tab();
}
+15 -4
View File
@@ -1183,16 +1183,27 @@ impl OpenCADStudio {
}
return Some(self.finish_dispatch(cmd));
}
// ponytail: one flag for every underlay kind; split it if a drawing
// needs PDF snaps on while DWF/DGN snaps are off.
if matches!(name.as_str(), "PDFOSNAP" | "DWFOSNAP" | "DGNOSNAP" | "UOSNAP") {
let current = i16::from(crate::scene::model::pdf_vector::pdf_osnap());
use crate::scene::model::pdf_vector as pv;
use codec::entities::UnderlayType;
// UOSNAP sets every kind and reads 2 while they differ.
let kind = match name.as_str() {
"PDFOSNAP" => Some(UnderlayType::Pdf),
"DWFOSNAP" => Some(UnderlayType::Dwf),
"DGNOSNAP" => Some(UnderlayType::Dgn),
_ => None,
};
let current = kind.map_or_else(pv::uosnap, |kind| i16::from(pv::underlay_osnap(kind)));
if let Some(value) = &value {
match value.parse::<i16>().ok().filter(|value| (0..=1).contains(value)) {
Some(mode) => {
if current != mode {
crate::scene::model::pdf_vector::set_pdf_osnap(mode == 1);
match kind {
Some(kind) => pv::set_underlay_osnap(kind, mode == 1),
None => pv::set_uosnap(mode == 1),
}
self.tabs[i].scene.reseed_underlays();
self.sync_underlay_tab();
}
}
None => self.command_line.push_error("Requires 0 or 1 only"),
+1 -1
View File
@@ -1613,7 +1613,7 @@ bg={bg_ms:.1}ms n={view_count}"
} else if let Some(ctx) = self.ribbon.underlay_context() {
let ctx = ctx.clone();
crate::ui::side_toolbar::view_with_active(
&crate::ui::ribbon::pdf_underlay_tools(),
&crate::ui::ribbon::pdf_underlay_tools(ctx.kind),
&move |id| match id {
"_PDFULMONO" => ctx.monochrome,
"_PDFULSHOW" => ctx.shown,