Skip to main content

pptx_rs/shape/
textbox.rs

1//! `TextBox`:纯文本框(`<p:sp>` + `prstGeom="rect"` + `txBody`)。
2//!
3//! 与 [`AutoShape`] 区别在于:[`TextBox`] 强制 `prstGeom=rect`、bodyPr
4//! 默认无填充无外框,更接近"传统文本框"语义。
5//!
6//! # 与 python-pptx 的对应
7//!
8//! - `pptx.shapes.textbox.TextBox` ←→ [`TextBox`];
9//! - `Slide.shapes.add_textbox(left, top, width, height)` 返回 [`TextBox`]。
10//!
11//! # 文本替换语义
12//!
13//! [`TextBox::set_text`] 的语义是"替换全部段落"——每行(`\n` 分隔)变成一段;
14//! 旧的字体/颜色属性会被丢弃。如需保留属性,请直接 mutate
15//! [`TextBox::text_frame_mut`]。
16//!
17//! # 示例
18//!
19//! ```no_run
20//! use pptx_rs::shape::TextBox;
21//!
22//! let mut tb = TextBox::new("MyTextBox");
23//! tb.set_text("Hello\nWorld");
24//! assert_eq!(tb.text(), "Hello\nWorld");
25//! ```
26
27use crate::oxml::shape::Sp as OxmlSp;
28use crate::oxml::simpletypes::PresetGeometry;
29use crate::oxml::txbody::{Paragraph, TextBody};
30use crate::shape::autoshape::AutoShape;
31use crate::shape::base::Shape;
32use crate::units::Emu;
33
34/// 文本框(python-pptx 的 `shapes.add_textbox` 对应)。
35#[derive(Clone, Debug, Default)]
36pub struct TextBox {
37    /// 内部包装的 [`AutoShape`]。
38    pub(crate) shape: AutoShape,
39}
40
41impl TextBox {
42    /// 新建一个文本框。
43    pub fn new(name: impl Into<String>) -> Self {
44        let mut s = AutoShape::new(name, PresetGeometry::Rectangle);
45        // 默认 textbox 的 bodyPr 是无填充、无外框
46        s.sp_mut().text = TextBody::new();
47        TextBox { shape: s }
48    }
49
50    /// 从 oxml [`OxmlSp`] 构造。
51    pub fn from_sp(sp: OxmlSp) -> Self {
52        TextBox {
53            shape: AutoShape::from_sp(sp),
54        }
55    }
56
57    /// 文本帧不可变引用。
58    pub fn text_frame(&self) -> &TextBody {
59        &self.shape.sp.text
60    }
61    /// 文本帧可变引用。
62    pub fn text_frame_mut(&mut self) -> &mut TextBody {
63        &mut self.shape.sp_mut().text
64    }
65
66    /// 替换全部文本(每行一个段落)。
67    ///
68    /// 替换后旧的 Run 属性(字体、颜色、加粗)会被清除;如需保留请直接操作
69    /// [`TextBox::text_frame_mut`]。
70    pub fn set_text(&mut self, text: &str) -> &mut TextBody {
71        let tb = self.text_frame_mut();
72        tb.paragraphs.clear();
73        for line in text.split('\n') {
74            let mut p = Paragraph::new();
75            p.runs.push(crate::oxml::txbody::Run::new(line));
76            tb.paragraphs.push(p);
77        }
78        tb
79    }
80
81    /// 取文本(把全部段落拼起来,行间 `\n`)。
82    pub fn text(&self) -> String {
83        let mut out = String::new();
84        let mut first = true;
85        for p in &self.text_frame().paragraphs {
86            if !first {
87                out.push('\n');
88            }
89            first = false;
90            for r in &p.runs {
91                out.push_str(&r.text);
92            }
93        }
94        out
95    }
96
97    /// 单词换行便捷方法。
98    pub fn set_word_wrap(&mut self, v: bool) {
99        self.text_frame_mut().set_word_wrap(v);
100    }
101
102    /// 自动调整便捷方法。
103    pub fn set_auto_size(&mut self, v: crate::oxml::simpletypes::MsoAutoSize) {
104        self.text_frame_mut().set_auto_size(v);
105    }
106
107    /// 垂直对齐便捷方法。
108    pub fn set_vertical_anchor(&mut self, v: crate::oxml::simpletypes::MsoAnchor) {
109        self.text_frame_mut().set_vertical_anchor(v);
110    }
111
112    // --------------------- 委派 AutoShape 的效果/锁定/样式 API ---------------------
113    //
114    // `TextBox` 内部就是 `AutoShape` + `prstGeom=rect` + `txBox=1`,因此所有
115    // 形状效果、锁定、主题样式相关的高阶 API 都直接转发到内部 AutoShape。
116    // 这样用户对 `TextBox` 也能用 `set_outer_shadow` / `lock_select` / `set_style`
117    // 等便捷方法,与 `AutoShape` 行为完全一致。
118
119    /// 设置外阴影(`<a:outerShdw>`)。详见 [`crate::shape::AutoShape::set_outer_shadow`]。
120    pub fn set_outer_shadow(&mut self, shadow: crate::oxml::sppr::ShadowEffect) {
121        self.shape.set_outer_shadow(shadow);
122    }
123
124    /// 设置内阴影(`<a:innerShdw>`)。详见 [`crate::shape::AutoShape::set_inner_shadow`]。
125    pub fn set_inner_shadow(&mut self, shadow: crate::oxml::sppr::ShadowEffect) {
126        self.shape.set_inner_shadow(shadow);
127    }
128
129    /// 设置发光(`<a:glow>`)。详见 [`crate::shape::AutoShape::set_glow`]。
130    pub fn set_glow(&mut self, glow: crate::oxml::sppr::GlowEffect) {
131        self.shape.set_glow(glow);
132    }
133
134    /// 设置柔化边缘(`<a:softEdge>`)。详见 [`crate::shape::AutoShape::set_soft_edge`]。
135    pub fn set_soft_edge(&mut self, rad: i64) {
136        self.shape.set_soft_edge(rad);
137    }
138
139    /// 设置反射(`<a:reflection>`)。详见 [`crate::shape::AutoShape::set_reflection`]。
140    pub fn set_reflection(&mut self, reflection: crate::oxml::sppr::ReflectionEffect) {
141        self.shape.set_reflection(reflection);
142    }
143
144    /// 清除所有效果。详见 [`crate::shape::AutoShape::clear_effects`]。
145    pub fn clear_effects(&mut self) {
146        self.shape.clear_effects();
147    }
148
149    /// 设置三维旋转。详见 [`crate::shape::AutoShape::set_3d_rotation`]。
150    pub fn set_3d_rotation(&mut self, lat_deg: f64, lon_deg: f64, rev_deg: f64) {
151        self.shape.set_3d_rotation(lat_deg, lon_deg, rev_deg);
152    }
153
154    /// 设置三维拉伸。详见 [`crate::shape::AutoShape::set_3d_extrusion`]。
155    pub fn set_3d_extrusion(&mut self, height_emu: i32, color: Option<crate::oxml::color::Color>) {
156        self.shape.set_3d_extrusion(height_emu, color);
157    }
158
159    /// 设置三维棱台。详见 [`crate::shape::AutoShape::set_3d_bevel`]。
160    pub fn set_3d_bevel(&mut self, top_w: i32, top_h: i32, bottom_w: i32, bottom_h: i32) {
161        self.shape.set_3d_bevel(top_w, top_h, bottom_w, bottom_h);
162    }
163
164    /// 设置三维材质预设。详见 [`crate::shape::AutoShape::set_3d_material`]。
165    pub fn set_3d_material(&mut self, material: crate::oxml::sppr::MaterialPreset) {
166        self.shape.set_3d_material(material);
167    }
168
169    /// 清除所有三维效果。详见 [`crate::shape::AutoShape::clear_3d`]。
170    pub fn clear_3d(&mut self) {
171        self.shape.clear_3d();
172    }
173
174    /// 取三维场景引用。详见 [`crate::shape::AutoShape::scene_3d`]。
175    pub fn scene_3d(&self) -> Option<&crate::oxml::sppr::Scene3d> {
176        self.shape.scene_3d()
177    }
178
179    /// 取三维场景可变引用。详见 [`crate::shape::AutoShape::scene_3d_mut`]。
180    pub fn scene_3d_mut(&mut self) -> &mut Option<crate::oxml::sppr::Scene3d> {
181        self.shape.scene_3d_mut()
182    }
183
184    /// 取形状三维属性引用。详见 [`crate::shape::AutoShape::sp_3d`]。
185    pub fn sp_3d(&self) -> Option<&crate::oxml::sppr::Sp3d> {
186        self.shape.sp_3d()
187    }
188
189    /// 取形状三维属性可变引用。详见 [`crate::shape::AutoShape::sp_3d_mut`]。
190    pub fn sp_3d_mut(&mut self) -> &mut Option<crate::oxml::sppr::Sp3d> {
191        self.shape.sp_3d_mut()
192    }
193
194    /// 读取形状锁定(`<a:spLocks>`)。详见 [`crate::shape::AutoShape::locks`]。
195    pub fn locks(&self) -> Option<&crate::oxml::shape::ShapeLocks> {
196        self.shape.locks()
197    }
198
199    /// 读取形状锁定的可变引用。详见 [`crate::shape::AutoShape::locks_mut`]。
200    pub fn locks_mut(&mut self) -> &mut crate::oxml::shape::ShapeLocks {
201        self.shape.locks_mut()
202    }
203
204    /// 便捷锁定:禁止选中。详见 [`crate::shape::AutoShape::lock_select`]。
205    pub fn lock_select(&mut self, locked: bool) {
206        self.shape.lock_select(locked);
207    }
208
209    /// 便捷锁定:禁止移动。详见 [`crate::shape::AutoShape::lock_move`]。
210    pub fn lock_move(&mut self, locked: bool) {
211        self.shape.lock_move(locked);
212    }
213
214    /// 便捷锁定:禁止缩放。详见 [`crate::shape::AutoShape::lock_resize`]。
215    pub fn lock_resize(&mut self, locked: bool) {
216        self.shape.lock_resize(locked);
217    }
218
219    /// 便捷锁定:禁止旋转。详见 [`crate::shape::AutoShape::lock_rotate`]。
220    pub fn lock_rotate(&mut self, locked: bool) {
221        self.shape.lock_rotate(locked);
222    }
223
224    /// 便捷锁定:禁止组合。详见 [`crate::shape::AutoShape::lock_group`]。
225    pub fn lock_group(&mut self, locked: bool) {
226        self.shape.lock_group(locked);
227    }
228
229    /// 清除所有锁定。详见 [`crate::shape::AutoShape::clear_locks`]。
230    pub fn clear_locks(&mut self) {
231        self.shape.clear_locks();
232    }
233
234    /// 统一锁定入口:按 [`crate::LockType`] 设置指定锁定。
235    /// 详见 [`crate::shape::AutoShape::set_lock`]。
236    pub fn set_lock(&mut self, lock_type: crate::oxml::shape::LockType, locked: bool) {
237        self.shape.set_lock(lock_type, locked);
238    }
239
240    /// 读取主题样式引用(`<p:style>`)。详见 [`crate::shape::AutoShape::style`]。
241    pub fn style(&self) -> Option<&crate::oxml::shape::ShapeStyle> {
242        self.shape.style()
243    }
244
245    /// 设置主题样式引用。详见 [`crate::shape::AutoShape::set_style`]。
246    pub fn set_style(&mut self, style: crate::oxml::shape::ShapeStyle) {
247        self.shape.set_style(style);
248    }
249
250    /// 清除主题样式引用。详见 [`crate::shape::AutoShape::clear_style`]。
251    pub fn clear_style(&mut self) {
252        self.shape.clear_style();
253    }
254}
255
256impl Shape for TextBox {
257    fn id(&self) -> u32 {
258        self.shape.id()
259    }
260    fn set_id(&mut self, id: u32) {
261        self.shape.set_id(id);
262    }
263    fn name(&self) -> &str {
264        self.shape.name()
265    }
266    fn set_name(&mut self, name: String) {
267        self.shape.set_name(name);
268    }
269    fn shape_type(&self) -> &'static str {
270        "text_box"
271    }
272
273    fn left(&self) -> Emu {
274        self.shape.left()
275    }
276    fn set_left(&mut self, emu: Emu) {
277        self.shape.set_left(emu);
278    }
279    fn top(&self) -> Emu {
280        self.shape.top()
281    }
282    fn set_top(&mut self, emu: Emu) {
283        self.shape.set_top(emu);
284    }
285    fn width(&self) -> Emu {
286        self.shape.width()
287    }
288    fn set_width(&mut self, emu: Emu) {
289        self.shape.set_width(emu);
290    }
291    fn height(&self) -> Emu {
292        self.shape.height()
293    }
294    fn set_height(&mut self, emu: Emu) {
295        self.shape.set_height(emu);
296    }
297
298    fn rotation(&self) -> f64 {
299        self.shape.rotation()
300    }
301    fn set_rotation(&mut self, deg: f64) {
302        self.shape.set_rotation(deg);
303    }
304}