altium_format/records/pcb/
text.rs1use std::io::Read;
4
5use altium_format_derive::AltiumRecord;
6
7use super::primitive::{
8 PcbPrimitiveCommon, PcbRectangularBase, PcbTextJustification, PcbTextKind, PcbTextStrokeFont,
9};
10use crate::error::{AltiumError, Result};
11use crate::traits::{FromBinary, ToBinary};
12use crate::types::{Coord, CoordPoint, CoordRect};
13
14#[derive(Debug, Clone, Default)]
21struct FontName(String);
22
23impl FromBinary for FontName {
24 fn read_from<R: Read>(reader: &mut R) -> Result<Self> {
25 let mut buf = [0u8; 32];
26 reader.read_exact(&mut buf)?;
27
28 let mut code_units = Vec::new();
30 for chunk in buf.chunks_exact(2) {
31 let code = u16::from_le_bytes([chunk[0], chunk[1]]);
32 if code == 0 {
33 break;
34 }
35 code_units.push(code);
36 }
37
38 let value = String::from_utf16(&code_units)
40 .map_err(|e| AltiumError::Encoding(format!("Invalid UTF-16 font name: {}", e)))?;
41 Ok(FontName(value))
42 }
43}
44
45impl ToBinary for FontName {
46 fn write_to<W: std::io::Write>(&self, writer: &mut W) -> Result<()> {
47 use crate::io::writer::write_font_name;
48
49 write_font_name(writer, &self.0)
50 }
51
52 fn binary_size(&self) -> usize {
53 32
54 }
55}
56
57#[derive(Debug, Clone, Default, AltiumRecord)]
58#[altium(format = "binary")]
59struct PcbTextBaseBinary {
60 #[altium(flatten)]
61 common: PcbPrimitiveCommon,
62 #[altium(coord_point)]
63 corner1: CoordPoint,
64 #[altium(coord)]
65 height: Coord,
66 stroke_font: PcbTextStrokeFont,
67 rotation: f64,
68 mirrored: bool,
69 #[altium(coord)]
70 stroke_width: Coord,
71}
72
73#[derive(Debug, Clone, Default, AltiumRecord)]
74#[altium(format = "binary")]
75struct PcbTextExtendedBinary {
76 _unknown1: u16,
77 _unknown2: u8,
78 text_kind: PcbTextKind,
79 font_bold: bool,
80 font_italic: bool,
81 font_name: FontName,
82 #[altium(coord)]
83 barcode_lr_margin: Coord,
84 #[altium(coord)]
85 barcode_tb_margin: Coord,
86 _unknown3: i32,
87 _unknown4: i32,
88 _unknown5: u8,
89 _unknown6: u8,
90 _unknown7: i32,
91 _unknown8: u16,
92 _unknown9: i32,
93 _unknown10: i32,
94 font_inverted: bool,
95 #[altium(coord)]
96 font_inverted_border: Coord,
97 wide_strings_index: i32,
98 _unknown11: i32,
99 font_inverted_rect: bool,
100 #[altium(coord)]
101 font_inverted_rect_width: Coord,
102 #[altium(coord)]
103 font_inverted_rect_height: Coord,
104 font_inverted_rect_justification: PcbTextJustification,
105 #[altium(coord)]
106 font_inverted_rect_text_offset: Coord,
107}
108
109#[derive(Debug, Clone, Default)]
111pub struct PcbText {
112 pub base: PcbRectangularBase,
114 pub mirrored: bool,
116 pub text_kind: PcbTextKind,
118 pub stroke_font: PcbTextStrokeFont,
120 pub stroke_width: Coord,
122 pub font_bold: bool,
124 pub font_italic: bool,
126 pub font_name: String,
128 pub barcode_lr_margin: Coord,
130 pub barcode_tb_margin: Coord,
132 pub font_inverted: bool,
134 pub font_inverted_border: Coord,
136 pub font_inverted_rect: bool,
138 pub font_inverted_rect_width: Coord,
140 pub font_inverted_rect_height: Coord,
142 pub font_inverted_rect_justification: PcbTextJustification,
144 pub font_inverted_rect_text_offset: Coord,
146 pub text: String,
148 pub wide_strings_index: i32,
150}
151
152impl FromBinary for PcbText {
153 fn read_from<R: Read>(reader: &mut R) -> Result<Self> {
154 let base = <PcbTextBaseBinary as FromBinary>::read_from(reader)?;
155
156 let mut remaining = Vec::new();
157 reader.read_to_end(&mut remaining)?;
158
159 let mut extended = PcbTextExtendedBinary::default();
160 if remaining.len() >= extended.binary_size() {
161 let mut cursor = std::io::Cursor::new(&remaining);
162 extended = <PcbTextExtendedBinary as FromBinary>::read_from(&mut cursor)?;
163 }
164
165 let corner2 = CoordPoint::new(base.corner1.x, base.corner1.y + base.height);
166
167 Ok(PcbText {
168 base: PcbRectangularBase {
169 common: base.common,
170 corner1: base.corner1,
171 corner2,
172 rotation: base.rotation,
173 },
174 mirrored: base.mirrored,
175 text_kind: extended.text_kind,
176 stroke_font: base.stroke_font,
177 stroke_width: base.stroke_width,
178 font_bold: extended.font_bold,
179 font_italic: extended.font_italic,
180 font_name: extended.font_name.0,
181 barcode_lr_margin: extended.barcode_lr_margin,
182 barcode_tb_margin: extended.barcode_tb_margin,
183 font_inverted: extended.font_inverted,
184 font_inverted_border: extended.font_inverted_border,
185 font_inverted_rect: extended.font_inverted_rect,
186 font_inverted_rect_width: extended.font_inverted_rect_width,
187 font_inverted_rect_height: extended.font_inverted_rect_height,
188 font_inverted_rect_justification: extended.font_inverted_rect_justification,
189 font_inverted_rect_text_offset: extended.font_inverted_rect_text_offset,
190 text: String::new(), wide_strings_index: extended.wide_strings_index,
192 })
193 }
194}
195
196use std::io::Write;
197
198impl ToBinary for PcbText {
199 fn write_to<W: Write>(&self, writer: &mut W) -> Result<()> {
200 use byteorder::{LittleEndian, WriteBytesExt};
201
202 self.base.common.write_to(writer)?;
204
205 writer.write_i32::<LittleEndian>(self.base.corner1.x.to_raw())?;
207 writer.write_i32::<LittleEndian>(self.base.corner1.y.to_raw())?;
208
209 let height = self.base.corner2.y.to_raw() - self.base.corner1.y.to_raw();
211 writer.write_i32::<LittleEndian>(height)?;
212
213 writer.write_i16::<LittleEndian>(self.stroke_font.to_i16())?;
215
216 writer.write_f64::<LittleEndian>(self.base.rotation)?;
218
219 writer.write_u8(if self.mirrored { 1 } else { 0 })?;
221
222 writer.write_i32::<LittleEndian>(self.stroke_width.to_raw())?;
224
225 writer.write_u16::<LittleEndian>(0)?; writer.write_u8(0)?; writer.write_u8(self.text_kind.to_byte())?;
231
232 writer.write_u8(if self.font_bold { 1 } else { 0 })?;
234 writer.write_u8(if self.font_italic { 1 } else { 0 })?;
235
236 FontName(self.font_name.clone()).write_to(writer)?;
238
239 writer.write_i32::<LittleEndian>(self.barcode_lr_margin.to_raw())?;
241 writer.write_i32::<LittleEndian>(self.barcode_tb_margin.to_raw())?;
242
243 writer.write_i32::<LittleEndian>(0)?; writer.write_i32::<LittleEndian>(0)?; writer.write_u8(0)?; writer.write_u8(0)?; writer.write_i32::<LittleEndian>(0)?; writer.write_u16::<LittleEndian>(0)?; writer.write_i32::<LittleEndian>(0)?; writer.write_i32::<LittleEndian>(0)?; writer.write_u8(if self.font_inverted { 1 } else { 0 })?;
255 writer.write_i32::<LittleEndian>(self.font_inverted_border.to_raw())?;
256
257 writer.write_i32::<LittleEndian>(self.wide_strings_index)?;
259
260 writer.write_i32::<LittleEndian>(0)?;
262
263 writer.write_u8(if self.font_inverted_rect { 1 } else { 0 })?;
265 writer.write_i32::<LittleEndian>(self.font_inverted_rect_width.to_raw())?;
266 writer.write_i32::<LittleEndian>(self.font_inverted_rect_height.to_raw())?;
267 writer.write_u8(self.font_inverted_rect_justification.to_byte())?;
268 writer.write_i32::<LittleEndian>(self.font_inverted_rect_text_offset.to_raw())?;
269
270 Ok(())
271 }
272
273 fn binary_size(&self) -> usize {
274 200
276 }
277}
278
279impl PcbText {
280 #[allow(clippy::too_many_arguments)]
292 pub fn new(
293 x: f64,
294 y: f64,
295 text: &str,
296 height: f64,
297 stroke_width: f64,
298 rotation: f64,
299 mirrored: bool,
300 layer: crate::types::Layer,
301 ) -> Self {
302 use super::primitive::{PcbFlags, PcbPrimitiveCommon};
303
304 let corner1 = CoordPoint::from_mms(x, y);
305 let corner2 = CoordPoint::from_mms(x, y + height);
306
307 PcbText {
308 base: PcbRectangularBase {
309 common: PcbPrimitiveCommon {
310 layer,
311 flags: PcbFlags::UNLOCKED | PcbFlags::UNKNOWN8,
312 unique_id: None,
313 },
314 corner1,
315 corner2,
316 rotation,
317 },
318 mirrored,
319 text_kind: PcbTextKind::Stroke,
320 stroke_font: PcbTextStrokeFont::Default,
321 stroke_width: Coord::from_mms(stroke_width),
322 font_bold: false,
323 font_italic: false,
324 font_name: String::new(),
325 barcode_lr_margin: Coord::from_raw(0),
326 barcode_tb_margin: Coord::from_raw(0),
327 font_inverted: false,
328 font_inverted_border: Coord::from_raw(0),
329 font_inverted_rect: false,
330 font_inverted_rect_width: Coord::from_raw(0),
331 font_inverted_rect_height: Coord::from_raw(0),
332 font_inverted_rect_justification: PcbTextJustification::BottomLeft,
333 font_inverted_rect_text_offset: Coord::from_raw(0),
334 text: text.to_string(),
335 wide_strings_index: -1,
336 }
337 }
338
339 pub fn height(&self) -> Coord {
341 self.base.height()
342 }
343
344 pub fn calculate_bounds(&self) -> CoordRect {
346 self.base.calculate_bounds()
347 }
348}