forked from 1b-agent/opencadstudio-upstream
fix(render): DXF leader hookline width + SHAPE name fallback; bump acadrust
Companion to the acadrust DXF-fidelity sweep (bump to 379ce12): - Legacy LEADER hookline: underline the annotation using the leader's own stored text width (DXF code 41) when the MTEXT has no computed extents, so the landing spans the text instead of a short stub. - SHAPE: resolve the glyph by name when the shape number is 0 (DXF SHAPE carries only the name), and search shape-file text styles when the entity has no direct style reference. Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
1 parent
b7dc62ffa5
commit
ce49f4da0a
3 files changed
+77
-30
No files matched your search
Generated
+2
-2
@@ -74,7 +74,7 @@ checksum = "366ffbaa4442f4684d91e2cd7c5ea7c4ed8add41959a31447066e279e432b618"
|
||||
[[package]]
|
||||
name = "acadrust"
|
||||
version = "0.4.0"
|
||||
source = "git+https://github.com/OpenAEC-Foundation/acadifc?branch=main#2e63e7053f0f245004b41c4b576a0529748f3f41"
|
||||
source = "git+https://github.com/OpenAEC-Foundation/acadifc?branch=main#379ce12e55a8b5f3c32b9678cc348f0b4dfb82c7"
|
||||
dependencies = [
|
||||
"ahash 0.8.12",
|
||||
"anyhow",
|
||||
@@ -3180,7 +3180,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b577e2d69827c4740cba2b52efaad1c4cc7c73042860b199710b3575c68438d"
|
||||
dependencies = [
|
||||
"bytecount",
|
||||
"memchr 1.0.2",
|
||||
"memchr 2.8.3",
|
||||
"nom 8.0.0",
|
||||
]
|
||||
|
||||
|
||||
+17
-7
@@ -761,14 +761,24 @@ impl LeaderTess for Leader {
|
||||
.or_else(|| style.map(|s| s.dimtad))
|
||||
.unwrap_or(0);
|
||||
if dimtad != 0 {
|
||||
if let Some(acadrust::entities::EntityType::MText(mt)) =
|
||||
document.get_entity(self.annotation_handle)
|
||||
{
|
||||
if mt.extents_width > 0.0 {
|
||||
let to_far_edge =
|
||||
sign * (mt.insertion_point.x - last.x) + mt.extents_width;
|
||||
land_len = land_len.max(to_far_edge);
|
||||
let mt = match document.get_entity(self.annotation_handle) {
|
||||
Some(acadrust::entities::EntityType::MText(mt)) => {
|
||||
Some((mt.extents_width, mt.insertion_point.x))
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
// Underline width: the annotation MTEXT's laid-out extents when
|
||||
// known, otherwise the leader's own stored annotation width
|
||||
// (DXF code 41) — a DXF-loaded MTEXT often has no computed
|
||||
// extents (extents_width == 0), which left the hook too short.
|
||||
let width = mt
|
||||
.map(|(w, _)| w)
|
||||
.filter(|w| *w > 0.0)
|
||||
.unwrap_or(self.text_width);
|
||||
if width > 0.0 {
|
||||
let ins_x = mt.map(|(_, x)| x).unwrap_or(last.x);
|
||||
let to_far_edge = sign * (ins_x - last.x) + width;
|
||||
land_len = land_len.max(to_far_edge);
|
||||
}
|
||||
}
|
||||
points.push([f64::NAN; 3]);
|
||||
|
||||
+58
-21
@@ -63,32 +63,69 @@ fn shx_polylines(
|
||||
shape: &Shape,
|
||||
document: &acadrust::CadDocument,
|
||||
) -> Option<crate::scene::text::shx::ShapePolylines> {
|
||||
let style = shape
|
||||
.style_handle
|
||||
.and_then(|h| document.text_styles.iter().find(|s| s.handle == h))
|
||||
.or_else(|| {
|
||||
let name = shape.style_name.trim();
|
||||
(!name.is_empty())
|
||||
.then(|| {
|
||||
document
|
||||
.text_styles
|
||||
.iter()
|
||||
.find(|s| s.name.eq_ignore_ascii_case(name))
|
||||
})
|
||||
.flatten()
|
||||
})?;
|
||||
let font = style.font_file.trim();
|
||||
if font.is_empty() {
|
||||
return None;
|
||||
}
|
||||
let base = document
|
||||
.source_path
|
||||
.as_deref()
|
||||
.map(std::path::Path::new)
|
||||
.and_then(|p| p.parent());
|
||||
let resolved = crate::io::resolve_image_file(font, base)?;
|
||||
let num = u16::try_from(shape.shape_number).ok()?;
|
||||
crate::scene::text::shx::shape_polylines(&resolved, num)
|
||||
let name = shape.shape_name.trim();
|
||||
|
||||
// Resolve the glyph from one style's shape (.shx) file, preferring the
|
||||
// shape NUMBER but falling back to the NAME — a DXF SHAPE carries only the
|
||||
// name (code 2), so its `shape_number` is 0.
|
||||
let try_style = |style: &acadrust::tables::TextStyle| {
|
||||
let font = style.font_file.trim();
|
||||
if font.is_empty() {
|
||||
return None;
|
||||
}
|
||||
let resolved = crate::io::resolve_image_file(font, base)?;
|
||||
if let Ok(num) = u16::try_from(shape.shape_number) {
|
||||
if num != 0 {
|
||||
if let Some(p) = crate::scene::text::shx::shape_polylines(&resolved, num) {
|
||||
return Some(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
if !name.is_empty() {
|
||||
return crate::scene::text::shx::shape_polylines_by_name(&resolved, name);
|
||||
}
|
||||
None
|
||||
};
|
||||
|
||||
// 1. Direct style referenced by the SHAPE (handle, then name).
|
||||
let direct = shape
|
||||
.style_handle
|
||||
.and_then(|h| document.text_styles.iter().find(|s| s.handle == h))
|
||||
.or_else(|| {
|
||||
let sn = shape.style_name.trim();
|
||||
(!sn.is_empty())
|
||||
.then(|| {
|
||||
document
|
||||
.text_styles
|
||||
.iter()
|
||||
.find(|s| s.name.eq_ignore_ascii_case(sn))
|
||||
})
|
||||
.flatten()
|
||||
});
|
||||
if let Some(style) = direct {
|
||||
if let Some(p) = try_style(style) {
|
||||
return Some(p);
|
||||
}
|
||||
}
|
||||
|
||||
// 2. No usable style reference (the DXF case): search every shape-file
|
||||
// style — one with a real font file and an empty table name — for a
|
||||
// glyph named like this shape.
|
||||
if !name.is_empty() {
|
||||
for style in document.text_styles.iter() {
|
||||
if style.name.is_empty() && !style.font_file.trim().is_empty() {
|
||||
if let Some(p) = try_style(style) {
|
||||
return Some(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
impl TruckConvertible for Shape {
|
||||
|
||||
Reference in new issue
Block a user