1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
//! Multi-line text entity
use super::{Entity, EntityCommon, LineSpacingStyle};
use crate::types::{BoundingBox3D, Color, Handle, LineWeight, Transparency, Vector3};
/// Attachment point for MText
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum AttachmentPoint {
/// Top left
TopLeft = 1,
/// Top center
TopCenter = 2,
/// Top right
TopRight = 3,
/// Middle left
MiddleLeft = 4,
/// Middle center
MiddleCenter = 5,
/// Middle right
MiddleRight = 6,
/// Bottom left
BottomLeft = 7,
/// Bottom center
BottomCenter = 8,
/// Bottom right
BottomRight = 9,
}
/// Drawing direction for MText
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum DrawingDirection {
/// Left to right
LeftToRight = 1,
/// Top to bottom
TopToBottom = 2,
/// By style
ByStyle = 3,
}
/// Column layout for an [`MText`] entity (stored in R2018+ DWG, non-annotative).
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct MTextColumnData {
/// Column type: 0 = no columns, 1 = static columns, 2 = dynamic columns.
pub column_type: i16,
/// Number of columns. For dynamic, non-auto-height columns this is the
/// number of per-column [`heights`](Self::heights); the DWG writer derives
/// the on-disk count from `heights.len()` in that case to keep the object
/// stream in sync, so keep the two consistent for dynamic columns.
pub column_count: i32,
/// Whether the column flow is reversed.
pub flow_reversed: bool,
/// Whether the column height is computed automatically.
pub auto_height: bool,
/// Column width.
pub width: f64,
/// Gutter width between columns.
pub gutter: f64,
/// Per-column heights. Only stored for dynamic, non-auto-height columns.
pub heights: Vec<f64>,
}
impl MTextColumnData {
/// Create empty (no-columns) column data.
pub fn new() -> Self {
MTextColumnData {
column_type: 0,
column_count: 0,
flow_reversed: false,
auto_height: false,
width: 0.0,
gutter: 0.0,
heights: Vec::new(),
}
}
}
impl Default for MTextColumnData {
fn default() -> Self {
Self::new()
}
}
/// A multi-line text entity
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct MText {
/// Common entity data
pub common: EntityCommon,
/// Text content (may contain formatting codes)
pub value: String,
/// Insertion point
pub insertion_point: Vector3,
/// Text height
pub height: f64,
/// Reference rectangle width
pub rectangle_width: f64,
/// Reference rectangle height (optional)
pub rectangle_height: Option<f64>,
/// Rotation angle in radians
pub rotation: f64,
/// Text style name
pub style: String,
/// Attachment point
pub attachment_point: AttachmentPoint,
/// Drawing direction
pub drawing_direction: DrawingDirection,
/// Line spacing factor
pub line_spacing_factor: f64,
/// Line spacing style (DXF 73): AtLeast (1) or Exactly (2).
pub line_spacing_style: LineSpacingStyle,
/// Normal vector
pub normal: Vector3,
/// Background fill flags (BL 90): bit 0x01 = use background fill color,
/// 0x02 = use drawing window color, 0x10 = text frame (R2018+).
pub background_fill_flags: i32,
/// Background fill scale factor (BD 45). Default 1.5.
pub background_scale: f64,
/// Background fill color (CMC 63).
pub background_color: Color,
/// Background fill transparency (BL 441).
pub background_transparency: i32,
/// Whether this MTEXT is annotative (R2018+). When `false`, the DWG stores
/// a block of redundant fields followed by column data.
pub is_annotative: bool,
/// Column layout data (R2018+).
pub column_data: MTextColumnData,
}
impl MText {
/// Create a new MText entity
pub fn new() -> Self {
MText {
common: EntityCommon::new(),
value: String::new(),
insertion_point: Vector3::ZERO,
height: 1.0,
rectangle_width: 10.0,
rectangle_height: None,
rotation: 0.0,
style: "STANDARD".to_string(),
attachment_point: AttachmentPoint::TopLeft,
drawing_direction: DrawingDirection::LeftToRight,
line_spacing_factor: 1.0,
line_spacing_style: LineSpacingStyle::AtLeast,
normal: Vector3::UNIT_Z,
background_fill_flags: 0,
background_scale: 1.5,
background_color: Color::ByLayer,
background_transparency: 0,
is_annotative: true,
column_data: MTextColumnData::new(),
}
}
/// Create a new MText with value and position
pub fn with_value(value: impl Into<String>, position: Vector3) -> Self {
MText {
value: value.into(),
insertion_point: position,
..Self::new()
}
}
/// Set the text height
pub fn with_height(mut self, height: f64) -> Self {
self.height = height;
self
}
/// Set the rectangle width
pub fn with_width(mut self, width: f64) -> Self {
self.rectangle_width = width;
self
}
}
impl Default for MText {
fn default() -> Self {
Self::new()
}
}
impl Entity for MText {
fn handle(&self) -> Handle {
self.common.handle
}
fn set_handle(&mut self, handle: Handle) {
self.common.handle = handle;
}
fn layer(&self) -> &str {
&self.common.layer
}
fn set_layer(&mut self, layer: String) {
self.common.layer = layer;
}
fn color(&self) -> Color {
self.common.color
}
fn set_color(&mut self, color: Color) {
self.common.color = color;
}
fn line_weight(&self) -> LineWeight {
self.common.line_weight
}
fn set_line_weight(&mut self, weight: LineWeight) {
self.common.line_weight = weight;
}
fn transparency(&self) -> Transparency {
self.common.transparency
}
fn set_transparency(&mut self, transparency: Transparency) {
self.common.transparency = transparency;
}
fn is_invisible(&self) -> bool {
self.common.invisible
}
fn set_invisible(&mut self, invisible: bool) {
self.common.invisible = invisible;
}
fn bounding_box(&self) -> BoundingBox3D {
let height = self.rectangle_height.unwrap_or(self.height * 2.0);
BoundingBox3D::new(
self.insertion_point,
Vector3::new(
self.insertion_point.x + self.rectangle_width,
self.insertion_point.y + height,
self.insertion_point.z,
),
)
}
fn translate(&mut self, offset: Vector3) {
super::translate::translate_mtext(self, offset);
}
fn entity_type(&self) -> &'static str {
"MTEXT"
}
fn apply_transform(&mut self, transform: &crate::types::Transform) {
super::transform::transform_mtext(self, transform);
}
fn apply_mirror(&mut self, transform: &crate::types::Transform) {
super::mirror::mirror_mtext(self, transform);
}
}