解决Printer Command Language问题,解决多行文本旋转问题,解决多行文本位置偏移问题(没完全解决)

master
liaoxianglian 5 months ago
parent 55ca13ecb4
commit e0fb0160e5

@ -4,6 +4,7 @@ use hex_color::HexColor;
use simple_xml_builder::XMLElement; use simple_xml_builder::XMLElement;
use unicode_segmentation::UnicodeSegmentation; use unicode_segmentation::UnicodeSegmentation;
use uuid::Uuid; use uuid::Uuid;
use dxf::enums::AttachmentPoint;
/*use parley::{ /*use parley::{
Alignment, FontContext, FontWeight, InlineBox, Layout, LayoutContext, PositionedLayoutItem, Alignment, FontContext, FontWeight, InlineBox, Layout, LayoutContext, PositionedLayoutItem,
@ -85,7 +86,20 @@ impl From<&DynamicText> for XMLElement {
// } // }
// }; // };
println!("文本:{}", txt.text);
println!("对齐方式:{}, {}", txt.h_alignment, txt.v_alignment);
println!("文本宽度: {}", txt_width);
println!("文本高度:{}", pt_size);
println!("初始位置: x={}, y={}", txt.x, txt.y);
println!("旋转角度: {}", txt.rotation);
// 计算基础位置(不考虑旋转) // 计算基础位置(不考虑旋转)
// 如果是mtext是没有左下角的位置的只有对齐点的位置
// 所以需要根据对齐点和文本宽度来计算最终的位置
let base_x = txt.x + 0.5 - (pt_size / 8.0) - 4.05; let base_x = txt.x + 0.5 - (pt_size / 8.0) - 4.05;
let base_y = { let base_y = {
let base_y_pos = txt.y + 0.5 - (7.0 / 5.0 * pt_size + 26.0 / 5.0) + pt_size; let base_y_pos = txt.y + 0.5 - (7.0 / 5.0 * pt_size + 26.0 / 5.0) + pt_size;
@ -115,6 +129,9 @@ impl From<&DynamicText> for XMLElement {
(base_x, base_y) (base_x, base_y)
}; };
println!("最终位置: x={}, y={}", x_pos, y_pos);
dtxt_xml.add_attribute("x", two_dec(x_pos)); dtxt_xml.add_attribute("x", two_dec(x_pos));
dtxt_xml.add_attribute("y", two_dec(y_pos)); dtxt_xml.add_attribute("y", two_dec(y_pos));
dtxt_xml.add_attribute("z", two_dec(txt.z)); dtxt_xml.add_attribute("z", two_dec(txt.z));
@ -241,11 +258,21 @@ impl<'a> DTextBuilder<'a> {
VAlignment::from(txt.vertical_text_justification), VAlignment::from(txt.vertical_text_justification),
0.0, // as Placeholder: no "reference_rectangle_width" with Text!!! 0.0, // as Placeholder: no "reference_rectangle_width" with Text!!!
), ),
TextEntity::MText(mtxt) => ( TextEntity::MText(mtxt) => {
mtxt.insertion_point.x, let (adjusted_x, adjusted_y) = adjust_mtext_coordinates(
-mtxt.insertion_point.y, mtxt.insertion_point.x,
mtxt.insertion_point.y,
mtxt.attachment_point,
mtxt.initial_text_height,
mtxt.reference_rectangle_width
);
// 计算实际的旋转角度优先使用x_axis_direction向量
let actual_rotation = calculate_mtext_rotation(mtxt.rotation_angle, &mtxt.x_axis_direction);
(
adjusted_x,
-adjusted_y,
mtxt.insertion_point.z, mtxt.insertion_point.z,
mtxt.rotation_angle, actual_rotation,
&mtxt.text_style_name, &mtxt.text_style_name,
//I'm not sure what the proper value is here for Mtext //I'm not sure what the proper value is here for Mtext
//becuase I haven't actually finished supporting it. //becuase I haven't actually finished supporting it.
@ -269,12 +296,13 @@ impl<'a> DTextBuilder<'a> {
{ {
let mut val = mtxt.extended_text.join(""); let mut val = mtxt.extended_text.join("");
val.push_str(&mtxt.text); val.push_str(&mtxt.text);
val.replace("\\P", "\n") let processed_val = val.replace("\\P", "\n");
super::extract_text_from_pcl(&processed_val)
}, },
HAlignment::from(mtxt.attachment_point), HAlignment::from(mtxt.attachment_point),
VAlignment::from(mtxt.attachment_point), VAlignment::from(mtxt.attachment_point),
mtxt.reference_rectangle_width, mtxt.reference_rectangle_width,
), )},
TextEntity::Attrib(attrib) => ( TextEntity::Attrib(attrib) => (
attrib.location.x, attrib.location.x,
-attrib.location.y, -attrib.location.y,
@ -358,3 +386,86 @@ impl<'a> DTextBuilder<'a> {
} }
} }
} }
/// 计算MText的实际旋转角度
///
/// # Arguments
/// * `rotation_angle` - DXF中的rotation_angle字段编号50
/// * `x_axis_direction` - DXF中的x_axis_direction向量编号11,21,31
///
/// # Returns
/// 返回实际的旋转角度(弧度)
fn calculate_mtext_rotation(rotation_angle: f64, x_axis_direction: &dxf::Vector) -> f64 {
// 检查x_axis_direction是否为默认值(1.0, 0.0, 0.0)
let default_x_axis = (x_axis_direction.x - 1.0).abs() < 1e-10
&& x_axis_direction.y.abs() < 1e-10
&& x_axis_direction.z.abs() < 1e-10;
if default_x_axis {
// 如果x_axis_direction是默认值使用rotation_angle
rotation_angle
} else {
// 如果x_axis_direction不是默认值从向量计算角度
// atan2返回弧度值需要转换为度数
let angle_radians = x_axis_direction.y.atan2(x_axis_direction.x);
// 将弧度转换为度数这里rotation字段期望度数
angle_radians * 180.0 / std::f64::consts::PI
}
}
/// 将MText的对齐点坐标调整为左下角坐标
///
/// # 参数
/// * `x` - MText的insertion_point.x对齐点X坐标
/// * `y` - MText的insertion_point.y对齐点Y坐标
/// * `attachment_point` - MText的对齐方式
/// * `text_height` - 文本高度
/// * `text_width` - 文本宽度reference_rectangle_width
///
/// # 返回值
/// 返回调整后的(x, y)坐标,对应文本的左下角位置
pub fn adjust_mtext_coordinates(
x: f64,
y: f64,
attachment_point: AttachmentPoint,
text_height: f64,
text_width: f64,
) -> (f64, f64) {
let mut adjusted_x = x;
let mut adjusted_y = y;
// 根据水平对齐方式调整X坐标
match attachment_point {
// 左对齐无需调整X坐标
AttachmentPoint::TopLeft | AttachmentPoint::MiddleLeft | AttachmentPoint::BottomLeft => {
// X坐标已经是左边界无需调整
}
// 中心对齐:向左偏移文本宽度的一半
AttachmentPoint::TopCenter | AttachmentPoint::MiddleCenter | AttachmentPoint::BottomCenter => {
adjusted_x -= text_width / 2.0; // 如果文本旋转了,那就不是这么算了
}
// 右对齐:向左偏移整个文本宽度
AttachmentPoint::TopRight | AttachmentPoint::MiddleRight | AttachmentPoint::BottomRight => {
adjusted_x -= text_width;
}
}
// 根据垂直对齐方式调整Y坐标
match attachment_point {
// 底部对齐无需调整Y坐标已经是底边界
AttachmentPoint::BottomLeft | AttachmentPoint::BottomCenter | AttachmentPoint::BottomRight => {
// Y坐标已经是底边界无需调整
}
// 中心对齐:向下偏移文本高度的一半
AttachmentPoint::MiddleLeft | AttachmentPoint::MiddleCenter | AttachmentPoint::MiddleRight => {
adjusted_y -= text_height / 2.0;
}
// 顶部对齐:向下偏移整个文本高度
AttachmentPoint::TopLeft | AttachmentPoint::TopCenter | AttachmentPoint::TopRight => {
adjusted_y -= text_height;
}
}
(adjusted_x, adjusted_y)
}

@ -986,7 +986,6 @@ impl<'a> ObjectsBuilder<'a> {
//need to look up the proper way to get the color for the Attrib //need to look up the proper way to get the color for the Attrib
let mut dtext = DTextBuilder::from_attrib(attrib) let mut dtext = DTextBuilder::from_attrib(attrib)
.color({ .color({
println!("color index:{:?}", self.ent.common.color.index());
// 优先使用 ACI 颜色索引 // 优先使用 ACI 颜色索引
if let Some(aci_index) = self.ent.common.color.index() { if let Some(aci_index) = self.ent.common.color.index() {
aci_to_hex_color(aci_index) aci_to_hex_color(aci_index)
@ -1708,3 +1707,127 @@ fn aci_to_hex_color(aci: u8) -> HexColor {
let (r, g, b) = aci_to_rgb(aci); let (r, g, b) = aci_to_rgb(aci);
HexColor::rgb(r, g, b) HexColor::rgb(r, g, b)
} }
/// 从PCL格式字符串中提取文本内容
///
/// 解析Printer Command Language (PCL)格式的字符串,提取其中的文本内容。
/// 支持格式如:"\pxt1;{\T1.001;传感器S1}" -> "传感器S1"
/// 以及Unicode转义格式"{\T1.00100;\U+4F20\U+611F\U+5668S2}" -> "传感器S2"
///
/// # 参数
/// * `pcl_string` - PCL格式的字符串
///
/// # 返回值
/// 提取出的文本内容,如果解析失败则返回原字符串
pub fn extract_text_from_pcl(pcl_string: &str) -> String {
// 移除开头和结尾的空白字符
let trimmed = pcl_string.trim();
// 处理直接以大括号开始的格式:{\T1.00100;\U+4F20\U+611F\U+5668S2}
if trimmed.starts_with('{') && trimmed.ends_with('}') {
let content = &trimmed[1..trimmed.len() - 1];
// 查找\T命令
if let Some(t_pos) = content.find("\\T") {
// 查找\T命令后的分号
let t_content = &content[t_pos..];
if let Some(t_semicolon_pos) = t_content.find(';') {
let text_content = &t_content[t_semicolon_pos + 1..];
return decode_unicode_escapes(text_content);
}
}
// 如果没有找到\T命令返回大括号内的全部内容并解码Unicode
return decode_unicode_escapes(content);
}
// 检查是否以\pxt开头
if !trimmed.starts_with("\\pxt") {
return pcl_string.to_string();
}
// 查找第一个分号,这标志着\pxt命令的结束
if let Some(semicolon_pos) = trimmed.find(';') {
let remaining = &trimmed[semicolon_pos + 1..];
// 检查是否有大括号包围的内容
if remaining.starts_with('{') && remaining.ends_with('}') {
let content = &remaining[1..remaining.len() - 1];
// 查找\T命令
if let Some(t_pos) = content.find("\\T") {
// 查找\T命令后的分号
let t_content = &content[t_pos..];
if let Some(t_semicolon_pos) = t_content.find(';') {
let text_content = &t_content[t_semicolon_pos + 1..];
return decode_unicode_escapes(text_content);
}
}
// 如果没有找到\T命令返回大括号内的全部内容并解码Unicode
return decode_unicode_escapes(content);
}
}
// 如果解析失败,返回原字符串
pcl_string.to_string()
}
/// 解码Unicode转义序列如\U+4F20转换为对应的Unicode字符
///
/// # 参数
/// * `text` - 包含Unicode转义序列的文本
///
/// # 返回值
/// 解码后的文本
fn decode_unicode_escapes(text: &str) -> String {
let mut result = String::new();
let mut chars = text.chars().peekable();
while let Some(ch) = chars.next() {
if ch == '\\' {
// 检查是否是Unicode转义序列 \U+XXXX
if chars.peek() == Some(&'U') {
chars.next(); // 消费 'U'
if chars.peek() == Some(&'+') {
chars.next(); // 消费 '+'
// 收集十六进制数字
let mut hex_digits = String::new();
while let Some(&next_ch) = chars.peek() {
if next_ch.is_ascii_hexdigit() {
hex_digits.push(chars.next().unwrap());
} else {
break;
}
}
// 尝试解析十六进制数字为Unicode码点
if let Ok(code_point) = u32::from_str_radix(&hex_digits, 16) {
if let Some(unicode_char) = char::from_u32(code_point) {
result.push(unicode_char);
continue;
}
}
// 如果解析失败,保留原始字符
result.push('\\');
result.push('U');
result.push('+');
result.push_str(&hex_digits);
} else {
// 不是 \U+ 格式,保留原始字符
result.push('\\');
result.push('U');
}
} else {
// 不是Unicode转义保留反斜杠
result.push('\\');
}
} else {
result.push(ch);
}
}
result
}
Loading…
Cancel
Save