fix(image): resolve raster image paths so DWG-embedded images render (#98)
A RasterImage stores its path on the linked ImageDefinition, usually as the original author's absolute path (e.g. a Windows/VMware share) that doesn't exist locally, so the image silently failed to load. On open, resolve each raster image: use the stored path if it exists, else fall back to the same file name next to the drawing, and write the resolved path onto the entity. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
b7c5103891
commit
a819a47c08
1 changed files with 63 additions and 0 deletions
|
|
@ -101,6 +101,7 @@ pub fn load_file(path: &Path) -> Result<CadDocument, String> {
|
|||
.map_err(|e| e.to_string())?;
|
||||
fix_viewport_status_flags(&mut doc);
|
||||
fix_current_style_names(&mut doc);
|
||||
resolve_raster_image_paths(&mut doc, path.parent());
|
||||
Ok(doc)
|
||||
}
|
||||
"dxf" => {
|
||||
|
|
@ -111,12 +112,74 @@ pub fn load_file(path: &Path) -> Result<CadDocument, String> {
|
|||
fix_dxf_dimension_rotations(&mut doc);
|
||||
fix_viewport_status_flags(&mut doc);
|
||||
fix_current_style_names(&mut doc);
|
||||
resolve_raster_image_paths(&mut doc, path.parent());
|
||||
Ok(doc)
|
||||
}
|
||||
_ => Err(format!("Unsupported file format: .{ext}")),
|
||||
}
|
||||
}
|
||||
|
||||
/// A RasterImage entity stores its file path on the linked ImageDefinition,
|
||||
/// often as the original author's absolute path (e.g. a Windows / VMware share)
|
||||
/// that doesn't exist on this machine. Resolve each image to a usable path:
|
||||
/// the stored path if it exists, otherwise the same file name next to the
|
||||
/// drawing — and write the result onto the entity so the renderer finds it.
|
||||
fn resolve_raster_image_paths(doc: &mut CadDocument, base_dir: Option<&Path>) {
|
||||
use acadrust::objects::ObjectType;
|
||||
use acadrust::EntityType;
|
||||
use std::collections::HashMap;
|
||||
|
||||
let defs: HashMap<acadrust::Handle, String> = doc
|
||||
.objects
|
||||
.iter()
|
||||
.filter_map(|(h, o)| match o {
|
||||
ObjectType::ImageDefinition(d) => Some((*h, d.file_name.clone())),
|
||||
_ => None,
|
||||
})
|
||||
.collect();
|
||||
|
||||
for e in doc.entities_mut() {
|
||||
if let EntityType::RasterImage(img) = e {
|
||||
let raw = if !img.file_path.trim().is_empty() {
|
||||
img.file_path.clone()
|
||||
} else {
|
||||
img.definition_handle
|
||||
.and_then(|h| defs.get(&h).cloned())
|
||||
.unwrap_or_default()
|
||||
};
|
||||
if raw.trim().is_empty() {
|
||||
continue;
|
||||
}
|
||||
if let Some(resolved) = resolve_image_file(&raw, base_dir) {
|
||||
img.file_path = resolved;
|
||||
} else {
|
||||
// At least surface the stored path so the renderer can try it.
|
||||
img.file_path = raw;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Resolve a (possibly foreign / absolute) image path to an existing file:
|
||||
/// as stored, then relative to the drawing folder, then just the file name
|
||||
/// next to the drawing.
|
||||
fn resolve_image_file(raw: &str, base_dir: Option<&Path>) -> Option<String> {
|
||||
if Path::new(raw).is_file() {
|
||||
return Some(raw.to_string());
|
||||
}
|
||||
let base_dir = base_dir?;
|
||||
let joined = base_dir.join(raw);
|
||||
if joined.is_file() {
|
||||
return Some(joined.to_string_lossy().into_owned());
|
||||
}
|
||||
let name = raw.rsplit(|c| c == '/' || c == '\\').next().unwrap_or(raw);
|
||||
let cand = base_dir.join(name);
|
||||
if cand.is_file() {
|
||||
return Some(cand.to_string_lossy().into_owned());
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
// ── Directory listing (used by the custom Save As dialog) ────────────────
|
||||
|
||||
/// Read `dir` and return sorted entries: `(display_name, is_dir, full_path)`.
|
||||
|
|
|
|||
Loading…
Reference in a new issue