forked from 1b-agent/opencadstudio-upstream
fix(text): integrate ignored fields across text-bearing entities
- MultiLeader: stop double-scaling when ctx.text_height (WCS-baked) is set;
use it as-is and only multiply by scale_factor + anno on the fallback path.
ctx.text_width gets the same treatment.
- Text / Attribute: width_factor and oblique_angle are entity-authoritative.
The previous entity*style multiplication / addition mis-rendered any text
whose TXSTYLE had a non-default width_factor or oblique_angle (RR 0.6,
STYLE2 15°, Romans/ARIEL/A32 0.65-0.8 in the sample file).
- MText: honour TXSTYLE is_backward (negative width_factor mirror) and
is_upside_down (180° rotation). measure_mtext_chars uses positive
magnitude so anchor math stays correct under backward text.
- Tolerance: use tol.dimension_gap when set instead of the hard-coded
0.35 * height heuristic.
- Table: cell text height / font / alignment resolve through
CellContent -> CellStyle -> TableStyle.{title|header|data}_row_style
-> compiled-in defaults. Row classification respects the suppressed
flags. Cell + content rotation applied.
Co-Authored-By: Claude Opus 4.7 <[email protected]>
This commit is contained in:
1 parent
ace99541d0
commit
62876b1081
7 files changed
+198
-55
No files matched your search
@@ -111,11 +111,16 @@ fn build_attr_truck(input: AttrTextInputs<'_>, document: &acadrust::CadDocument)
|
||||
|
||||
let resolved = resolve_text_style(input.text_style, document);
|
||||
|
||||
// width_factor sign also flips on text_generation_flags bit 2 (backward).
|
||||
// The TextStyle's own is_backward flag is multiplicative — if both are set
|
||||
// they cancel (mirror twice → forward), matching AutoCAD.
|
||||
let base_wf =
|
||||
((input.width_factor as f32).max(0.01) * resolved.width_factor.max(0.01)).clamp(0.01, 100.0);
|
||||
// The entity stores the FINAL width factor / oblique angle (same rule
|
||||
// as TEXT). Use it as-is; fall back to the style only when the parser
|
||||
// reports a default-omitted 0.0.
|
||||
let base_wf = if input.width_factor.abs() > 1e-9 {
|
||||
(input.width_factor as f32).clamp(0.01, 100.0)
|
||||
} else {
|
||||
resolved.width_factor.max(0.01)
|
||||
};
|
||||
// text_generation_flags bit 2 (backward) flips width-factor sign; the
|
||||
// TextStyle's own is_backward is XOR-combined so mirror-twice cancels.
|
||||
let attr_backward = (input.text_generation_flags & 2) != 0;
|
||||
let mut width_factor = base_wf;
|
||||
if attr_backward ^ resolved.is_backward {
|
||||
@@ -132,7 +137,11 @@ fn build_attr_truck(input: AttrTextInputs<'_>, document: &acadrust::CadDocument)
|
||||
} else {
|
||||
input.rotation as f32
|
||||
};
|
||||
let oblique_angle = input.oblique_angle as f32 + resolved.oblique_angle;
|
||||
let oblique_angle = if input.oblique_angle.abs() > 1e-9 {
|
||||
input.oblique_angle as f32
|
||||
} else {
|
||||
resolved.oblique_angle
|
||||
};
|
||||
|
||||
// Anchor selection mirrors Text: only Left/Baseline uses insertion_point;
|
||||
// every other alignment uses alignment_point.
|
||||
|
||||
+16
-4
@@ -79,12 +79,19 @@ fn to_truck(t: &MText, document: &acadrust::CadDocument) -> TruckEntity {
|
||||
let resolved_style = resolve_text_style(&t.style, document);
|
||||
let font_name = resolved_style.font_name;
|
||||
let font = cxf::get_font(&font_name);
|
||||
let style_width_factor = resolved_style.width_factor.max(0.01);
|
||||
// MText has no entity-level width/oblique override (those are MText format
|
||||
// codes \W and \Q inside the value string, handled by `strip_mtext_codes`).
|
||||
// Default rendering inherits both from the style. `is_backward` flips the
|
||||
// width-factor sign so text reads right-to-left.
|
||||
let base_wf = resolved_style.width_factor.max(0.01);
|
||||
let style_width_factor = if resolved_style.is_backward { -base_wf } else { base_wf };
|
||||
let style_oblique = resolved_style.oblique_angle;
|
||||
let plain = strip_mtext_codes(&t.value);
|
||||
let explicit_lines = split_mtext_lines(&plain);
|
||||
let lines: Vec<String> = if t.rectangle_width > 0.0 {
|
||||
let scale = t.height as f32 / 9.0 * style_width_factor;
|
||||
// Measurement uses the magnitude only; the sign on style_width_factor
|
||||
// is a render-time mirror flag, not a width scalar.
|
||||
let scale = t.height as f32 / 9.0 * style_width_factor.abs();
|
||||
let max_w = t.rectangle_width as f32;
|
||||
explicit_lines
|
||||
.iter()
|
||||
@@ -131,7 +138,12 @@ fn to_truck(t: &MText, document: &acadrust::CadDocument) -> TruckEntity {
|
||||
_ => 0.0,
|
||||
};
|
||||
let vertical_text = matches!(t.drawing_direction, DrawingDirection::TopToBottom);
|
||||
let rot = t.rotation as f32;
|
||||
// is_upside_down flips the text 180° around the insertion point.
|
||||
let rot = if resolved_style.is_upside_down {
|
||||
t.rotation as f32 + std::f32::consts::PI
|
||||
} else {
|
||||
t.rotation as f32
|
||||
};
|
||||
let (cos_r, sin_r) = (rot.cos(), rot.sin());
|
||||
let ins_x = t.insertion_point.x;
|
||||
let ins_y = t.insertion_point.y;
|
||||
@@ -151,7 +163,7 @@ fn to_truck(t: &MText, document: &acadrust::CadDocument) -> TruckEntity {
|
||||
(line_y * (-sin_r), line_y * cos_r)
|
||||
};
|
||||
let line_w = if h_anchor > 0.0 {
|
||||
let scale = t.height as f32 / 9.0 * style_width_factor;
|
||||
let scale = t.height as f32 / 9.0 * style_width_factor.abs();
|
||||
measure_mtext_chars(line, scale, font)
|
||||
} else {
|
||||
0.0
|
||||
|
||||
+26
-12
@@ -187,13 +187,14 @@ fn to_truck(ml: &MultiLeader, document: &acadrust::CadDocument) -> Option<TruckE
|
||||
// here — this path is consumed by snap / truck export which work in WCS.
|
||||
if ml.content_type == LeaderContentType::MText && !ml.context.text_string.is_empty() {
|
||||
let ctx = &ml.context;
|
||||
let raw_height = if ctx.text_height > 0.0 {
|
||||
ctx.text_height
|
||||
} else {
|
||||
ml.text_height
|
||||
} as f32;
|
||||
// `ctx.text_height` (when > 0) is already in WCS — scale_factor is
|
||||
// baked in. Only the fallback path needs to multiply.
|
||||
let scale_factor = ml.scale_factor as f32;
|
||||
let height = raw_height * scale_factor;
|
||||
let height = if ctx.text_height > 0.0 {
|
||||
ctx.text_height as f32
|
||||
} else {
|
||||
ml.text_height as f32 * scale_factor
|
||||
};
|
||||
|
||||
let ins = &ctx.text_location;
|
||||
let ins_x = ins.x;
|
||||
@@ -202,12 +203,11 @@ fn to_truck(ml: &MultiLeader, document: &acadrust::CadDocument) -> Option<TruckE
|
||||
// Prefer text_direction (carries through rotations/mirrors); fall back
|
||||
// to text_rotation when no direction has been set.
|
||||
let td = ctx.text_direction;
|
||||
let rot = if td.x.abs() > 1e-9 || td.y.abs() > 1e-9 {
|
||||
let mut rot = if td.x.abs() > 1e-9 || td.y.abs() > 1e-9 {
|
||||
(td.y as f32).atan2(td.x as f32)
|
||||
} else {
|
||||
ctx.text_rotation as f32
|
||||
};
|
||||
let (cos_r, sin_r) = (rot.cos(), rot.sin());
|
||||
snap_pts.push(node([ins_x, ins_y, z]));
|
||||
|
||||
// Resolve text style via handle when available, falling back to STANDARD.
|
||||
@@ -225,16 +225,28 @@ fn to_truck(ml: &MultiLeader, document: &acadrust::CadDocument) -> Option<TruckE
|
||||
let style = crate::entities::text_support::resolve_text_style(&style_name, document);
|
||||
let font_name = style.font_name;
|
||||
let font = crate::scene::cxf::get_font(&font_name);
|
||||
let width_factor = style.width_factor.max(0.01);
|
||||
// MultiLeader text inherits width factor / oblique from the style.
|
||||
// is_backward flips the sign on width_factor; is_upside_down adds π
|
||||
// to the text rotation — same convention as Text / MText.
|
||||
let base_wf = style.width_factor.max(0.01);
|
||||
let width_factor = if style.is_backward { -base_wf } else { base_wf };
|
||||
let oblique = style.oblique_angle;
|
||||
if style.is_upside_down {
|
||||
rot += std::f32::consts::PI;
|
||||
}
|
||||
let (cos_r, sin_r) = (rot.cos(), rot.sin());
|
||||
|
||||
// Strip MText format codes (e.g. `{\fArial Black|b0|i0|c162|p34;...}`),
|
||||
// then split on \P / \n / \N and optionally word-wrap to text_width.
|
||||
let plain = crate::entities::text_support::strip_mtext_codes(&ctx.text_string);
|
||||
let explicit_lines = crate::entities::text_support::split_mtext_lines(&plain);
|
||||
let lines: Vec<String> = if ctx.text_width > 0.0 {
|
||||
let scale = height / 9.0 * width_factor;
|
||||
let max_w = ctx.text_width as f32 * scale_factor;
|
||||
// Width-factor sign is a render-time mirror flag; measurement uses
|
||||
// the magnitude only.
|
||||
let scale = height / 9.0 * width_factor.abs();
|
||||
// ctx.text_width is in the same WCS frame as ctx.text_height —
|
||||
// do not re-multiply by scale_factor.
|
||||
let max_w = ctx.text_width as f32;
|
||||
explicit_lines
|
||||
.iter()
|
||||
.flat_map(|line| {
|
||||
@@ -261,7 +273,9 @@ fn to_truck(ml: &MultiLeader, document: &acadrust::CadDocument) -> Option<TruckE
|
||||
let v_offset =
|
||||
v_offset_for_attachment(ctx.text_left_attachment, n_lines, height, line_h);
|
||||
|
||||
let scale = height / 9.0 * width_factor;
|
||||
// Line-width measurement uses positive magnitude; mirror sign feeds
|
||||
// tessellate_text_ex below.
|
||||
let scale = height / 9.0 * width_factor.abs();
|
||||
for (i, line) in lines.iter().enumerate() {
|
||||
let li = i as f32;
|
||||
let line_y_local = -li * line_h + v_offset;
|
||||
|
||||
+91
-16
@@ -14,7 +14,7 @@ fn v3(v: &acadrust::types::Vector3) -> Vec3 {
|
||||
}
|
||||
|
||||
impl TruckConvertible for Table {
|
||||
fn to_truck(&self, _document: &acadrust::CadDocument) -> Option<TruckEntity> {
|
||||
fn to_truck(&self, document: &acadrust::CadDocument) -> Option<TruckEntity> {
|
||||
if self.rows.is_empty() || self.columns.is_empty() {
|
||||
return None;
|
||||
}
|
||||
@@ -121,9 +121,42 @@ impl TruckConvertible for Table {
|
||||
// is gone — col/row offsets still feed cell drawing below.
|
||||
let _ = (total_w, total_h);
|
||||
|
||||
// Cell text — lifted into Lines points via 2D strokes
|
||||
let text_height = 0.18_f32;
|
||||
let margin = text_height * 0.5_f32;
|
||||
// Cell text — resolve defaults via TableStyle, then layer per-cell
|
||||
// overrides on top. Resolution order (text height, text style, alignment):
|
||||
// 1. CellContent.* (per-content explicit override)
|
||||
// 2. CellStyle.* (per-cell explicit override)
|
||||
// 3. TableStyle.<row_kind>_row_style.* (table-wide default for this row class)
|
||||
// 4. compiled-in fallback (0.18 / "txt" / MiddleCenter)
|
||||
//
|
||||
// Row classification: row 0 is Title (when not suppressed), row 1 is
|
||||
// Header (when not suppressed), everything else is Data. The two
|
||||
// suppressed flags shift the leading rows down to Data.
|
||||
let lookup_style = |h: acadrust::Handle| -> Option<&acadrust::tables::TextStyle> {
|
||||
document.text_styles.iter().find(|s| s.handle == h)
|
||||
};
|
||||
let table_style: Option<&acadrust::objects::TableStyle> =
|
||||
self.table_style_handle.and_then(|h| {
|
||||
document.objects.get(&h).and_then(|obj| match obj {
|
||||
acadrust::objects::ObjectType::TableStyle(ts) => Some(ts),
|
||||
_ => None,
|
||||
})
|
||||
});
|
||||
let title_suppressed = table_style.map(|t| t.title_suppressed).unwrap_or(false);
|
||||
let header_suppressed = table_style.map(|t| t.header_suppressed).unwrap_or(false);
|
||||
|
||||
let font_for_handle = |handle: Option<acadrust::Handle>| -> Option<String> {
|
||||
handle.and_then(|h| lookup_style(h)).and_then(|s| {
|
||||
let file = s.font_file.trim();
|
||||
if !file.is_empty() {
|
||||
let basename = file.rsplit(['/', '\\']).next().unwrap_or(file);
|
||||
let stem = basename.split('.').next().unwrap_or(basename).trim();
|
||||
if !stem.is_empty() {
|
||||
return Some(stem.to_string());
|
||||
}
|
||||
}
|
||||
None
|
||||
})
|
||||
};
|
||||
|
||||
for (ri, row) in self.rows.iter().enumerate() {
|
||||
let row_top = row_offsets[ri];
|
||||
@@ -133,6 +166,22 @@ impl TruckConvertible for Table {
|
||||
.unwrap_or(row_top + row.height as f32);
|
||||
let row_mid = (row_top + row_bot) * 0.5;
|
||||
|
||||
// Pick the appropriate row_style from TableStyle for this row's role.
|
||||
let row_style: Option<&acadrust::objects::RowCellStyle> = table_style
|
||||
.map(|ts| {
|
||||
let kind = match (title_suppressed, header_suppressed, ri) {
|
||||
(false, _, 0) => 0, // title
|
||||
(false, false, 1) => 1, // header
|
||||
(true, false, 0) => 1, // header pulled up
|
||||
_ => 2, // data
|
||||
};
|
||||
match kind {
|
||||
0 => &ts.title_row_style,
|
||||
1 => &ts.header_row_style,
|
||||
_ => &ts.data_row_style,
|
||||
}
|
||||
});
|
||||
|
||||
for (ci, cell) in row.cells.iter().enumerate() {
|
||||
let text = cell.text_value();
|
||||
if text.is_empty() {
|
||||
@@ -143,16 +192,39 @@ impl TruckConvertible for Table {
|
||||
let col_width = self.columns.get(ci).map(|c| c.width as f32).unwrap_or(1.0);
|
||||
let col_right = col_left + col_width;
|
||||
|
||||
// Alignment: CellStyle.alignment i32 encodes 1-9 (AutoCAD convention):
|
||||
// 1=TopLeft 2=TopCenter 3=TopRight
|
||||
// 4=MiddleLeft 5=MiddleCenter 6=MiddleRight
|
||||
// 7=BottomLeft 8=BottomCenter 9=BottomRight
|
||||
// 0/default = MiddleCenter (5)
|
||||
let align = cell.style.as_ref().map_or(5, |s| s.alignment);
|
||||
// Resolve text height: content → cell-style → row-style → 0.18.
|
||||
let content = cell.contents.first();
|
||||
let cell_h = content
|
||||
.map(|c| c.text_height)
|
||||
.filter(|h| *h > 1e-6)
|
||||
.or_else(|| cell.style.as_ref().map(|s| s.text_height).filter(|h| *h > 1e-6))
|
||||
.or_else(|| row_style.map(|s| s.text_height).filter(|h| *h > 1e-6))
|
||||
.map(|h| h as f32)
|
||||
.unwrap_or(0.18);
|
||||
let margin = cell_h * 0.5_f32;
|
||||
|
||||
// Resolve text-style handle: content → cell-style → row-style.
|
||||
let style_handle = content
|
||||
.and_then(|c| c.text_style_handle)
|
||||
.or_else(|| cell.style.as_ref().and_then(|s| s.text_style_handle))
|
||||
.or_else(|| row_style.and_then(|s| s.text_style_handle));
|
||||
let font_owned =
|
||||
font_for_handle(style_handle).unwrap_or_else(|| "txt".to_string());
|
||||
let font_name: &str = &font_owned;
|
||||
|
||||
// Alignment resolution: cell.style.alignment (1-9) overrides;
|
||||
// otherwise fall back to row_style.alignment, then MiddleCenter.
|
||||
let align = cell
|
||||
.style
|
||||
.as_ref()
|
||||
.map(|s| s.alignment)
|
||||
.filter(|a| *a != 0)
|
||||
.or_else(|| row_style.map(|s| s.alignment as i32))
|
||||
.unwrap_or(5);
|
||||
let horiz = ((align - 1).rem_euclid(3)) + 1; // 1=left, 2=center, 3=right
|
||||
let vert = ((align - 1) / 3) + 1; // 1=top, 2=middle, 3=bottom
|
||||
|
||||
let text_w = cxf::measure_text(text, text_height, 1.0, "txt");
|
||||
let text_w = cxf::measure_text(text, cell_h, 1.0, font_name);
|
||||
|
||||
let x_offset = match horiz {
|
||||
1 => col_left + margin, // left
|
||||
@@ -161,19 +233,22 @@ impl TruckConvertible for Table {
|
||||
};
|
||||
let y_offset = match vert {
|
||||
1 => row_top + margin, // top
|
||||
3 => row_bot - margin - text_height, // bottom
|
||||
_ => row_mid - text_height * 0.5, // middle (default)
|
||||
3 => row_bot - margin - cell_h, // bottom
|
||||
_ => row_mid - cell_h * 0.5, // middle (default)
|
||||
};
|
||||
|
||||
let text_origin = origin + h * x_offset + v_down * y_offset;
|
||||
|
||||
// Content rotation (radians) on top of table cell rotation.
|
||||
let rot = content.map(|c| c.rotation as f32).unwrap_or(0.0)
|
||||
+ cell.rotation as f32;
|
||||
let strokes = cxf::tessellate_text_ex(
|
||||
[text_origin.x, text_origin.y],
|
||||
text_height,
|
||||
0.0,
|
||||
cell_h,
|
||||
rot,
|
||||
1.0,
|
||||
0.0,
|
||||
"txt",
|
||||
font_name,
|
||||
text,
|
||||
);
|
||||
for stroke in strokes {
|
||||
|
||||
+13
-6
@@ -61,12 +61,15 @@ fn to_truck(t: &Text, document: &acadrust::CadDocument) -> TruckEntity {
|
||||
let snap_pt = Vec3::new(wsx as f32, wsy as f32, wsz as f32);
|
||||
let resolved_style = resolve_text_style(&t.style, document);
|
||||
let font_name = resolved_style.font_name;
|
||||
let base_wf = (if t.width_factor > 0.0 {
|
||||
t.width_factor as f32
|
||||
// AutoCAD text geometry rule: the entity stores the FINAL width factor /
|
||||
// oblique angle, copied from the style at creation and persisting through
|
||||
// style edits. Use it as-is. Only fall back to the style when the entity
|
||||
// value is missing (the parser reports 0.0 for default-omitted fields).
|
||||
let base_wf = if t.width_factor.abs() > 1e-9 {
|
||||
(t.width_factor as f32).clamp(0.01, 100.0)
|
||||
} else {
|
||||
1.0
|
||||
} * resolved_style.width_factor.max(0.01))
|
||||
.clamp(0.01, 100.0);
|
||||
resolved_style.width_factor.max(0.01)
|
||||
};
|
||||
// is_backward mirrors text left-right via negative width factor.
|
||||
let width_factor = if resolved_style.is_backward {
|
||||
-base_wf
|
||||
@@ -79,7 +82,11 @@ fn to_truck(t: &Text, document: &acadrust::CadDocument) -> TruckEntity {
|
||||
} else {
|
||||
t.rotation as f32
|
||||
};
|
||||
let oblique_angle = t.oblique_angle as f32 + resolved_style.oblique_angle;
|
||||
let oblique_angle = if t.oblique_angle.abs() > 1e-9 {
|
||||
t.oblique_angle as f32
|
||||
} else {
|
||||
resolved_style.oblique_angle
|
||||
};
|
||||
let anchor = match (
|
||||
&t.horizontal_alignment,
|
||||
&t.vertical_alignment,
|
||||
|
||||
@@ -130,7 +130,13 @@ fn tessellate_tolerance(tol: &Tolerance) -> Vec<Vec<[f32; 2]>> {
|
||||
} else {
|
||||
2.5_f32
|
||||
};
|
||||
let gap = (h * 0.35).max(0.1);
|
||||
// DIMGAP — stored on the entity (resolved from the dim style at creation).
|
||||
// Fall back to AutoCAD's 0.35 × height convention only when missing.
|
||||
let gap = if tol.dimension_gap > 1e-6 {
|
||||
tol.dimension_gap as f32
|
||||
} else {
|
||||
(h * 0.35).max(0.1)
|
||||
};
|
||||
let cell_h = h + 2.0 * gap;
|
||||
let char_w = h * 0.65;
|
||||
let min_cell_w = h * 1.4;
|
||||
|
||||
+30
-10
@@ -1552,12 +1552,18 @@ pub fn tessellate_multileader(
|
||||
// text_left_attachment (vertical), with text_rotation/text_direction applied.
|
||||
if ml.content_type == LeaderContentType::MText && !ml.context.text_string.is_empty() {
|
||||
let ctx = &ml.context;
|
||||
let raw_height = if ctx.text_height > 0.0 {
|
||||
ctx.text_height
|
||||
// `ctx.text_height` (when > 0) is the already-resolved WCS text height
|
||||
// stored in the per-instance context — style × scale_factor × anno
|
||||
// scale are all already baked in. Multiplying by `effective_scale`
|
||||
// again would double-scale (e.g., a context height of 100 in a file
|
||||
// with scale_factor=20 was rendering at 2000 units — 20× too big).
|
||||
// Only the fallback path (file omits the context value) needs
|
||||
// scale_factor + annotation scale applied.
|
||||
let height = if ctx.text_height > 0.0 {
|
||||
ctx.text_height as f32
|
||||
} else {
|
||||
ml.text_height
|
||||
} as f32;
|
||||
let height = raw_height * effective_scale;
|
||||
ml.text_height as f32 * effective_scale
|
||||
};
|
||||
|
||||
let ins = &ctx.text_location;
|
||||
// Subtract world_offset in f64 before casting to f32: drawings often
|
||||
@@ -1595,7 +1601,6 @@ pub fn tessellate_multileader(
|
||||
if ml.text_direction_negative {
|
||||
rot += std::f32::consts::PI;
|
||||
}
|
||||
let (cos_r, sin_r) = (rot.cos(), rot.sin());
|
||||
|
||||
// Resolve text style via handle when available, falling back to STANDARD.
|
||||
let style_name = ctx
|
||||
@@ -1612,16 +1617,29 @@ pub fn tessellate_multileader(
|
||||
let style = crate::entities::text_support::resolve_text_style(&style_name, document);
|
||||
let font_name = style.font_name;
|
||||
let font = crate::scene::cxf::get_font(&font_name);
|
||||
let width_factor = style.width_factor.max(0.01);
|
||||
// MultiLeader text takes its width factor / oblique from the style
|
||||
// (no entity-level override exists for MText-content multileaders).
|
||||
// is_backward flips the sign on width_factor; is_upside_down adds π
|
||||
// to the text rotation — same convention as Text / MText.
|
||||
let base_wf = style.width_factor.max(0.01);
|
||||
let width_factor = if style.is_backward { -base_wf } else { base_wf };
|
||||
let oblique = style.oblique_angle;
|
||||
if style.is_upside_down {
|
||||
rot += std::f32::consts::PI;
|
||||
}
|
||||
let (cos_r, sin_r) = (rot.cos(), rot.sin());
|
||||
|
||||
// Strip MText format codes (e.g. `{\fArial Black|b0|i0|c162|p34;...}`),
|
||||
// then split on \P / \n / \N and optionally word-wrap to text_width.
|
||||
let plain = crate::entities::text_support::strip_mtext_codes(&ctx.text_string);
|
||||
let explicit_lines = crate::entities::text_support::split_mtext_lines(&plain);
|
||||
let lines: Vec<String> = if ctx.text_width > 0.0 {
|
||||
let scale = height / 9.0 * width_factor;
|
||||
let max_w = ctx.text_width as f32 * effective_scale;
|
||||
// Measurement uses positive width magnitude; the sign on
|
||||
// width_factor is a render-time mirror flag.
|
||||
let scale = height / 9.0 * width_factor.abs();
|
||||
// ctx.text_width lives in the same WCS frame as ctx.text_height;
|
||||
// it does not need a second multiplication by effective_scale.
|
||||
let max_w = ctx.text_width as f32;
|
||||
explicit_lines
|
||||
.iter()
|
||||
.flat_map(|line| {
|
||||
@@ -1676,7 +1694,9 @@ pub fn tessellate_multileader(
|
||||
};
|
||||
let v_offset = v_offset_for_attachment(vertical_attach, n_lines, height, line_h);
|
||||
|
||||
let scale = height / 9.0 * width_factor;
|
||||
// line_widths use positive magnitude for measurement; tessellate_text_ex
|
||||
// gets the signed width_factor below to honour the backward flag.
|
||||
let scale = height / 9.0 * width_factor.abs();
|
||||
let line_widths: Vec<f32> = lines
|
||||
.iter()
|
||||
.map(|line| crate::entities::text_support::measure_mtext_chars(line, scale, font))
|
||||
|
||||
Reference in new issue
Block a user