annotation: TEXT/MTEXT store the model height of their default scale

Annotative text in DWG/DXF holds the model height of its default
(native) scale representation; other scales draw it at the ratio of the
two. OCS read the stored value as paper height and multiplied it by the
annotation scale again, so text from AutoCAD came out far too small
(#1734), and annotative text OCS wrote came out too small in AutoCAD.

- Rendering uses the active/native context ratio (1 when the object has
  no representation for the displayed scale).
- TEXT with an annotative style asks for the paper height, stores the
  model height of the creation scale and gets that context; MTEXT
  starts from the model height; ANNOUPDATE converts a fixed style
  height the same way.
- Properties: the Height row of annotative TEXT and MTEXT is the paper
  height at the displayed scale, with the model height beside it, and
  an edit converts back. MTEXT shows Annotative when it has contexts.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
Hakan SevenandClaude Opus 5.5 committed 2026-10-15 08:53:31 +03:00
1 parent b221ce5e33
commit 39c3e9af58
8 files changed
+183 -101

No files matched your search

+2
View File
@@ -1608,12 +1608,14 @@ impl OpenCADStudio {
}
self.push_undo_snapshot(i, "ANNOUPDATE");
let scale = self.tabs[i].scene.creation_annotation_scale_handle();
let paper_to_model = self.tabs[i].scene.creation_annotation_multiplier();
let mut updated = 0usize;
for handle in &handles {
if crate::scene::annotative::update_entity_from_annotation_style(
&mut self.tabs[i].scene.document,
*handle,
scale,
paper_to_model,
) {
updated += 1;
}
+10
View File
@@ -2697,6 +2697,16 @@ impl OpenCADStudio {
.header
.current_text_style_name
.clone();
// An annotative style's default height is on paper; the editor
// and the entity work in the model height of this scale.
let height = if crate::scene::annotative::text_style_is_annotative(
&self.tabs[i].scene.document,
&style,
) {
height * self.tabs[i].scene.creation_annotation_multiplier()
} else {
height
};
let new_cmd = MTextCommand::with_defaults(height, style);
self.command_line.push_info(&new_cmd.prompt());
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
+4 -4
View File
@@ -1721,7 +1721,7 @@ impl super::OpenCADStudio {
annotative,
);
if annotative {
if let Some(scale) = self.tabs[i].scene.current_annotation_scale_handle() {
if let Some(scale) = self.tabs[i].scene.creation_annotation_scale_handle() {
crate::scene::annotative::create_annotation_context(
&mut self.tabs[i].scene.document,
h,
@@ -1755,7 +1755,7 @@ impl super::OpenCADStudio {
self.push_undo_snapshot(i, "MTEXT");
let handle = self.commit_entity_handle(plane.place_entity(EntityType::MText(mt)));
if annotative {
let scale = self.tabs[i].scene.current_annotation_scale_handle();
let scale = self.tabs[i].scene.creation_annotation_scale_handle();
if let (Some(handle), Some(scale)) = (handle, scale) {
crate::scene::annotative::create_annotation_context(
&mut self.tabs[i].scene.document,
@@ -1837,7 +1837,7 @@ impl super::OpenCADStudio {
annotative,
);
if annotative {
if let Some(scale) = self.tabs[i].scene.current_annotation_scale_handle() {
if let Some(scale) = self.tabs[i].scene.creation_annotation_scale_handle() {
crate::scene::annotative::create_annotation_context(
&mut self.tabs[i].scene.document,
h,
@@ -1872,7 +1872,7 @@ impl super::OpenCADStudio {
let handle = self.commit_entity_handle(plane.place_entity(EntityType::MText(mt)));
self.tabs[i].dirty = true;
if annotative {
let scale = self.tabs[i].scene.current_annotation_scale_handle();
let scale = self.tabs[i].scene.creation_annotation_scale_handle();
if let (Some(handle), Some(scale)) = (handle, scale) {
crate::scene::annotative::create_annotation_context(
&mut self.tabs[i].scene.document,
+46 -44
View File
@@ -1538,7 +1538,7 @@ impl OpenCADStudio {
codec::EntityType::MText(t) => set_row_value(
&mut sections,
"annotative",
yes_no_choice(t.is_annotative),
yes_no_choice(t.is_annotative || is_anno),
),
codec::EntityType::Dimension(codec::entities::Dimension::Arc(_)) => {
set_row(
@@ -1600,54 +1600,56 @@ impl OpenCADStudio {
// text derives its paper height from the two endpoints,
// while annotative text exposes that paper height and a
// separate calculated model height.
if let codec::EntityType::Text(text) = entity {
let aligned = matches!(
text.horizontal_alignment,
codec::entities::TextHorizontalAlignment::Aligned
);
let annotative = crate::scene::annotative::is_annotative(doc, entity);
let model_factor = if annotative {
annotation_scale_handle
.and_then(|handle| match doc.objects.get(&handle) {
Some(codec::objects::ObjectType::Scale(scale)) => Some(
scale.inverse_factor()
/ self.tabs[i].scene.annotation_scale_unit_factor(),
),
_ => None,
})
.unwrap_or(self.tabs[i].scene.annotation_scale as f64)
} else {
1.0
};
let paper_height = if aligned {
crate::entities::text::text_run_placement_at_scale(text, doc, model_factor as f32)
.height as f64
} else {
text.height
if let Some(factor) =
self.tabs[i].scene.annotative_text_height_factor(entity.common().handle)
{
// Annotative text stores the model height of its default scale;
// the Height row edits the paper height at the displayed scale.
let (stored, model_factor, aligned) = match entity {
codec::EntityType::Text(text) => {
let aligned = matches!(
text.horizontal_alignment,
codec::entities::TextHorizontalAlignment::Aligned
);
let stored = if aligned {
crate::entities::text::text_run_placement_at_scale(text, doc, 1.0).height
as f64
} else {
text.height
};
(stored, factor, aligned)
}
codec::EntityType::MText(text) => (text.height, factor, false),
_ => unreachable!("annotative_text_height_factor is text-only"),
};
let ratio = crate::scene::annotative::text_context_ratio(
doc,
entity.common().handle,
annotation_scale_handle,
)
.unwrap_or(1.0);
let paper_height = stored / model_factor;
for section in sections.iter_mut() {
if let Some(row) = section.props.iter_mut().find(|row| row.field == "height") {
if annotative {
row.label = t!("Paper text height").into_owned();
}
if aligned {
row.value = crate::scene::model::object::PropValue::ReadOnly(
crate::entities::common::format_length(paper_height),
);
}
let editable = !aligned
&& !matches!(row.value, crate::scene::model::object::PropValue::ReadOnly(_));
*row = crate::entities::common::num_prop(
t!("Paper text height").as_ref(),
"height",
paper_height,
editable,
);
}
}
if annotative {
insert_row_after(
&mut sections,
"height",
crate::entities::common::ro_prop(
t!("Model text height").as_ref(),
"model_text_height",
crate::entities::common::format_length(paper_height * model_factor),
),
);
}
insert_row_after(
&mut sections,
"height",
crate::entities::common::ro_prop(
t!("Model text height").as_ref(),
"model_text_height",
crate::entities::common::format_length(stored * ratio),
),
);
}
if !group_names.is_empty() {
+27 -12
View File
@@ -276,22 +276,24 @@ impl super::OpenCADStudio {
t.style = cur_style;
}
}
if command_creation {
let annotation_multiplier = if crate::scene::annotative::text_style_is_annotative(
// TEXT asks an annotative style for the paper height; the entity
// stores the model height of the scale it is created at.
let annotative = command_creation
&& crate::scene::annotative::text_style_is_annotative(
&self.tabs[i].scene.document,
&t.style,
) {
self.tabs[i].scene.creation_annotation_multiplier()
} else {
1.0
};
);
let paper_height = t.height;
if annotative {
t.height *= self.tabs[i].scene.creation_annotation_multiplier();
}
if command_creation {
let display_height = crate::entities::text::text_run_placement_at_scale(
&t,
&self.tabs[i].scene.document,
annotation_multiplier as f32,
1.0,
)
.height as f64
* annotation_multiplier;
.height as f64;
if let Some(command) = self.tabs[i].suspended_cmd.as_mut() {
command.on_editor_display_height(display_height);
}
@@ -311,9 +313,22 @@ impl super::OpenCADStudio {
codec::entities::TextHorizontalAlignment::Aligned
)
{
self.tabs[i].scene.document.header.text_height = t.height;
self.tabs[i].scene.document.header.text_height = paper_height;
}
let handle = self.commit_entity_handle(plane.place_entity(EntityType::Text(t)));
if annotative {
let scale = self.tabs[i].scene.creation_annotation_scale_handle();
if let (Some(handle), Some(scale)) = (handle, scale) {
crate::scene::annotative::create_annotation_context(
&mut self.tabs[i].scene.document,
handle,
scale,
);
self.tabs[i]
.scene
.bump_entities(&[(handle, crate::scene::ChangeKind::Modified)]);
}
}
let _ = self.commit_entity_handle(plane.place_entity(EntityType::Text(t)));
self.tabs[i].dirty = true;
}
self.refresh_properties();
+23
View File
@@ -4179,6 +4179,29 @@ impl OpenCADStudio {
}
}
}
// Annotative text: the row holds the paper height.
"height"
if self.tabs[i]
.scene
.annotative_text_height_factor(handle)
.is_some() =>
{
let factor = self.tabs[i]
.scene
.annotative_text_height_factor(handle)
.unwrap_or(1.0);
if let (Ok(paper), Some(entity)) = (
val.trim().parse::<f64>(),
self.tabs[i].scene.document.get_entity_mut(handle),
) {
crate::scene::view::dispatch::apply_geom_prop_in_working_plane(
entity,
"height",
&(paper * factor).to_string(),
plane,
);
}
}
"hyperlink" => {
// Stored in the standard PE_URL XDATA
// record; an empty value clears it.