From 82cccec6f39eba4de754ed8c11f309bfa3875fcc Mon Sep 17 00:00:00 2001 From: liaoxianglian Date: Mon, 29 Sep 2025 19:37:16 +0800 Subject: [PATCH 1/2] =?UTF-8?q?=E9=85=8D=E5=90=88QET=E5=B0=86margin?= =?UTF-8?q?=E6=94=B9=E4=B8=BA0=E7=9A=84=E4=BB=A3=E7=A0=81=EF=BC=8C?= =?UTF-8?q?=E8=BD=AC=E6=8D=A2=E5=90=8E=E7=9A=84=E5=9D=90=E6=A0=87=E4=B8=BA?= =?UTF-8?q?=E5=AF=B9=E9=BD=90=E7=82=B9=E5=9D=90=E6=A0=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/qelmt/dynamictext.rs | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/src/qelmt/dynamictext.rs b/src/qelmt/dynamictext.rs index 3cd8b2b..c4cd25b 100644 --- a/src/qelmt/dynamictext.rs +++ b/src/qelmt/dynamictext.rs @@ -148,20 +148,32 @@ impl From<&DynamicText> for XMLElement { println!("对齐方式:{}, {}", txt.h_alignment, txt.v_alignment); println!("文本宽度: {}", txt_width); println!("文本高度:{}", pt_size); - println!("初始位置: x={}, y={}", txt.align_x, txt.align_y); + println!("初始左下角位置: x={}, y={}", txt.x, txt.y); + println!("初始对齐位置: x={}, y={}", txt.align_x, txt.align_y); println!("旋转角度: {}", txt.rotation); // println!("cjk_char_count: {}", cjk_char_count); // 计算基础位置(不考虑旋转) // txt.x和txt.y现在是对齐点坐标,需要根据对齐方式计算出左上角位置 // 首先根据水平对齐方式计算左边界的x坐标 - let left_x = txt.x; + // let left_x = txt.x; + let left_x = match txt.h_alignment { + HAlignment::Left => txt.x - 0.5, + HAlignment::Center => txt.align_x - 1.0, + HAlignment::Right => txt.align_x - 2.0 + }; // 根据垂直对齐方式计算顶部的y坐标 + // let top_y = match txt.v_alignment { + // VAlignment::Top => txt.y + pt_size / 2.0, + // VAlignment::Center => txt.y - pt_size / 2.0, + // VAlignment::Bottom => txt.y - pt_size + pt_size / 2.0, + // }; + let top_y = match txt.v_alignment { - VAlignment::Top => txt.y + pt_size / 2.0, - VAlignment::Center => txt.y - pt_size / 2.0, - VAlignment::Bottom => txt.y - pt_size + pt_size / 2.0, + VAlignment::Top => txt.align_y - 0.5, + VAlignment::Center => txt.align_y + 0.5, + VAlignment::Bottom => txt.y + 1.0 }; println!("左上角位置: x={}, y={}", left_x, top_y); @@ -170,14 +182,16 @@ impl From<&DynamicText> for XMLElement { let cjk_offset:f64 = (cjk_char_count as f64) * pt_size * 0.75; // 左对齐是不需要中文偏移的,居中对齐需要偏移,右对齐要偏移 - let base_x = left_x + 0.5 - (pt_size / 8.0) - 4.05; + let base_x = left_x;// + 0.5 - (pt_size / 8.0) - 4.05; // - match txt.h_alignment { // HAlignment::Left => 0.0, // HAlignment::Center => cjk_offset / 2.0, // HAlignment::Right => cjk_offset, // }; - let base_y = top_y + 0.5 - (7.0 / 5.0 * pt_size + 26.0 / 5.0) + pt_size; + // let base_y = top_y + 0.5 - (7.0 / 5.0 * pt_size + 26.0 / 5.0) + pt_size; + let base_y = top_y; + // 如果有旋转角度,应用旋转变换 let (x_pos, y_pos) = if txt.rotation != 0.0 { @@ -244,6 +258,8 @@ impl ScaleEntity for DynamicText { fn scale(&mut self, fact_x: f64, fact_y: f64) { self.x *= fact_x; self.y *= fact_y; + self.align_x *= fact_x; + self.align_y *= fact_y; //self.font.pixel_size *= fact; self.font.point_size *= fact_x; } From 78c6ea15ac53ec3931d4cfcdb45d0e9ce060ecd2 Mon Sep 17 00:00:00 2001 From: liaoxianglian Date: Tue, 30 Sep 2025 17:08:46 +0800 Subject: [PATCH 2/2] =?UTF-8?q?=E5=9C=A8elmt=E4=B8=AD=E4=B8=BA=E6=96=87?= =?UTF-8?q?=E6=9C=AC=E6=B7=BB=E5=8A=A0=E5=AF=B9=E9=BD=90=E7=82=B9=E5=9D=90?= =?UTF-8?q?=E6=A0=87=EF=BC=8C=E5=9C=A8qet=E4=B8=AD=E5=B0=86=E4=BB=A5?= =?UTF-8?q?=E5=AF=B9=E9=BD=90=E7=82=B9=E5=9D=90=E6=A0=87=E4=B8=BA=E5=9F=BA?= =?UTF-8?q?=E5=87=86=E8=AE=A1=E7=AE=97=E5=B7=A6=E4=B8=8A=E8=A7=92=E5=9D=90?= =?UTF-8?q?=E6=A0=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/qelmt/dynamictext.rs | 122 ++++++++------------------------------- src/qelmt/mod.rs | 24 +++++++- 2 files changed, 45 insertions(+), 101 deletions(-) diff --git a/src/qelmt/dynamictext.rs b/src/qelmt/dynamictext.rs index c4cd25b..7ff5571 100644 --- a/src/qelmt/dynamictext.rs +++ b/src/qelmt/dynamictext.rs @@ -64,86 +64,14 @@ impl From<&DynamicText> for XMLElement { // "guess" the width by number of characters and font-size: // let graphene_count = txt.text.graphemes(true).count(); - let mut cjk_char_count = 0; - // let mut graphene_count = 0; - // for grapheme in txt.text.graphemes(true) { - // graphene_count += 1; - // if grapheme.chars().any(|c| { - // // 检查是否为中文字符(包括中日韩统一表意文字) - // (c >= '\u{4E00}' && c <= '\u{9FFF}') || // CJK统一表意文字 - // (c >= '\u{3400}' && c <= '\u{4DBF}') || // CJK扩展A - // (c >= '\u{20000}' && c <= '\u{2A6DF}') || // CJK扩展B - // (c >= '\u{2A700}' && c <= '\u{2B73F}') || // CJK扩展C - // (c >= '\u{2B740}' && c <= '\u{2B81F}') || // CJK扩展D - // (c >= '\u{2B820}' && c <= '\u{2CEAF}') || // CJK扩展E - // (c >= '\u{F900}' && c <= '\u{FAFF}') || // CJK兼容表意文字 - // (c >= '\u{2F800}' && c <= '\u{2FA1F}') // CJK兼容表意文字补充 - // }) { - // cjk_char_count += 1; - // } - // } let txt_width = if txt.reference_rectangle_width > 2.0 { txt.reference_rectangle_width } else { // 不同的字母,宽度与高度的比例并不相同,0.75只是一个平均值 (graphene_count as f64) * pt_size * 0.75 - // 根据字符类型计算宽度,考虑不同字符的宽高比例 - // let mut total_width = 0.0; - // for grapheme in txt.text.graphemes(true) { - // let char_width = if grapheme.chars().any(|c| { - // // 检查是否为中文字符(包括中日韩统一表意文字) - // (c >= '\u{4E00}' && c <= '\u{9FFF}') || // CJK统一表意文字 - // (c >= '\u{3400}' && c <= '\u{4DBF}') || // CJK扩展A - // (c >= '\u{20000}' && c <= '\u{2A6DF}') || // CJK扩展B - // (c >= '\u{2A700}' && c <= '\u{2B73F}') || // CJK扩展C - // (c >= '\u{2B740}' && c <= '\u{2B81F}') || // CJK扩展D - // (c >= '\u{2B820}' && c <= '\u{2CEAF}') || // CJK扩展E - // (c >= '\u{F900}' && c <= '\u{FAFF}') || // CJK兼容表意文字 - // (c >= '\u{2F800}' && c <= '\u{2FA1F}') // CJK兼容表意文字补充 - // }) { - // // 中文字符:宽高比 1:1 - // cjk_char_count += 1; - // pt_size * 1.5 - // } else if grapheme.chars().any(|c| matches!(c, 'i' | 'l' | 'I' | ',' | ';' | ':' | '!')) { - // pt_size * 0.3 - // } else if grapheme.chars().any(|c| matches!(c, 'W' | 'M' | '@' )) { - // pt_size * 1.0 - // } else if grapheme.chars().any(|c| matches!(c, 'w' |'m' | '%' )) { - // pt_size * 0.9 - // }else if grapheme.chars().any(|c| matches!(c, 'f' |'j' | 't' | 'r' | 'J' | 'T' | 'F' | '/' | '\\' | 's' | 'z' )) { - // // 其他标点符号:宽高比约 0.4:1 - // pt_size * 0.5 - // } else { - // // 中等宽度字母(默认):宽高比约 0.6:1 - // pt_size * 0.7 - // }; - // total_width += char_width; - // } - // total_width }; - // let x_pos = { - // let x_pos = txt.x + 0.5 - (pt_size / 8.0) - 4.05; - // // match txt.h_alignment { - // // HAlignment::Left => x_pos, - // // HAlignment::Center => x_pos - txt_width / 2.0, - // // HAlignment::Right => x_pos - txt_width, - // // } - // x_pos // 直接返回基础x_pos,不进行水平对齐调整 - // }; - // // let y_pos = txt.y + 0.5 - (7.0 / 5.0 * pt_size + 26.0 / 5.0) + pt_size; - // let y_pos = { - // let base_y_pos = txt.y + 0.5 - (7.0 / 5.0 * pt_size + 26.0 / 5.0) + pt_size; - // match txt.v_alignment { - // VAlignment::Top => base_y_pos, - // VAlignment::Center => base_y_pos - pt_size / 2.0, - // VAlignment::Bottom => base_y_pos - pt_size, - // } - // }; - - // 如果采用计算对齐的方式,可能还要考虑字体宽度的偏差。。 - println!("文本:{}", txt.text); println!("对齐方式:{}, {}", txt.h_alignment, txt.v_alignment); println!("文本宽度: {}", txt_width); @@ -151,48 +79,42 @@ impl From<&DynamicText> for XMLElement { println!("初始左下角位置: x={}, y={}", txt.x, txt.y); println!("初始对齐位置: x={}, y={}", txt.align_x, txt.align_y); println!("旋转角度: {}", txt.rotation); - // println!("cjk_char_count: {}", cjk_char_count); // 计算基础位置(不考虑旋转) - // txt.x和txt.y现在是对齐点坐标,需要根据对齐方式计算出左上角位置 - // 首先根据水平对齐方式计算左边界的x坐标 - // let left_x = txt.x; - let left_x = match txt.h_alignment { - HAlignment::Left => txt.x - 0.5, + // txt.x和txt.y现在是左下角位置 + let left_x = txt.x; + // align_x 是对齐点的x坐标,后面加的数字是考虑到QET中boundingRect的没有完全贴合文本内容 + let mut align_x = match txt.h_alignment { + HAlignment::Left => txt.align_x - 0.5, HAlignment::Center => txt.align_x - 1.0, HAlignment::Right => txt.align_x - 2.0 }; // 根据垂直对齐方式计算顶部的y坐标 - // let top_y = match txt.v_alignment { - // VAlignment::Top => txt.y + pt_size / 2.0, - // VAlignment::Center => txt.y - pt_size / 2.0, - // VAlignment::Bottom => txt.y - pt_size + pt_size / 2.0, - // }; - let top_y = match txt.v_alignment { + VAlignment::Top => txt.y + pt_size / 2.0, + VAlignment::Center => txt.y - pt_size / 2.0, + VAlignment::Bottom => txt.y - pt_size + pt_size / 2.0, + }; + + // align_y 是对齐点的y坐标,后面加的数字是考虑到QET中boundingRect的没有完全贴合文本内容 + let mut align_y = match txt.v_alignment { VAlignment::Top => txt.align_y - 0.5, VAlignment::Center => txt.align_y + 0.5, - VAlignment::Bottom => txt.y + 1.0 + VAlignment::Bottom => txt.align_y + 1.0 }; - println!("左上角位置: x={}, y={}", left_x, top_y); + if(txt.h_alignment == HAlignment::Left && txt.v_alignment==VAlignment::Bottom) { + align_x = txt.x-0.5; + align_y = txt.y+1.0; + } - // 根据是否包含中文字符调整字体大小相关的偏移量 - let cjk_offset:f64 = (cjk_char_count as f64) * pt_size * 0.75; - // 左对齐是不需要中文偏移的,居中对齐需要偏移,右对齐要偏移 - let base_x = left_x;// + 0.5 - (pt_size / 8.0) - 4.05; - // - match txt.h_alignment { - // HAlignment::Left => 0.0, - // HAlignment::Center => cjk_offset / 2.0, - // HAlignment::Right => cjk_offset, - // }; + println!("左上角位置: x={}, y={}", left_x, top_y); - // let base_y = top_y + 0.5 - (7.0 / 5.0 * pt_size + 26.0 / 5.0) + pt_size; + let base_x = left_x; let base_y = top_y; - // 如果有旋转角度,应用旋转变换 let (x_pos, y_pos) = if txt.rotation != 0.0 { let rotation_rad = txt.rotation.to_radians(); @@ -226,6 +148,8 @@ impl From<&DynamicText> for XMLElement { dtxt_xml.add_attribute("frame", txt.frame); dtxt_xml.add_attribute("text_width", txt.text_width); dtxt_xml.add_attribute("color", txt.color.display_rgb()); + dtxt_xml.add_attribute("align_x", two_dec(align_x)); + dtxt_xml.add_attribute("align_y", two_dec(align_y)); //If I ever add support for other text_from types, element and composite text //I'll need to add more smarts here, as there may be some other children components @@ -348,7 +272,7 @@ impl<'a> DTextBuilder<'a> { txt.text_height, txt.value.clone(), HAlignment::from(txt.horizontal_text_justification), - VAlignment::from(txt.vertical_text_justification), + VAlignment::from_text_entity(txt.vertical_text_justification, false), 0.0, // as Placeholder: no "reference_rectangle_width" with Text!!! txt.second_alignment_point.x, -txt.second_alignment_point.y @@ -411,7 +335,7 @@ impl<'a> DTextBuilder<'a> { attrib.text_height, attrib.text_tag.clone(), HAlignment::from(attrib.horizontal_text_justification), - VAlignment::from(attrib.vertical_text_justification), + VAlignment::from_text_entity(attrib.vertical_text_justification, true), 0.0, // as Placeholder: not need to check if Attrib has something similar attrib.second_alignment_point.x, -attrib.second_alignment_point.y diff --git a/src/qelmt/mod.rs b/src/qelmt/mod.rs index 3d4da0c..f2b75ce 100644 --- a/src/qelmt/mod.rs +++ b/src/qelmt/mod.rs @@ -1331,7 +1331,7 @@ impl Display for ItemType { } } -#[derive(Debug)] +#[derive(Debug, PartialEq)] enum HAlignment { Left, Center, @@ -1384,7 +1384,7 @@ impl From for HAlignment { } } -#[derive(Debug)] +#[derive(Debug, PartialEq)] enum VAlignment { Top, Center, @@ -1434,6 +1434,26 @@ impl From for VAlignment { } } +impl VAlignment { + /// 根据文本实体类型和垂直对齐方式创建VAlignment + /// 对于Attrib实体,baseline对齐到底部 + /// 对于MText和Text实体,baseline对齐到中间 + pub fn from_text_entity(value: VerticalTextJustification, is_attrib: bool) -> Self { + match value { + VerticalTextJustification::Top => VAlignment::Top, + VerticalTextJustification::Middle => VAlignment::Center, + VerticalTextJustification::Bottom => VAlignment::Bottom, + VerticalTextJustification::Baseline => { + if is_attrib { + VAlignment::Bottom // Attrib的baseline对齐到底部 + } else { + VAlignment::Center // MText和Text的baseline对齐到中间 + } + } + } + } +} + #[derive(Debug)] enum LineEnd { None,