Skip to main content

pptx_rs/
notes_masters.rs

1//! # 备注母版(Notes Master)—— 高阶 API(TODO-045)。
2//!
3//! 对应 OOXML 规范中的 `<p:notesMaster>` 元素,以及 python-pptx 中
4//! `NotesMaster` / `NotesMasterPart`。
5//!
6//! # 概念
7//!
8//! 备注母版是所有备注页(`<p:notesSlide>`)的"模板"——
9//! 它定义了备注页的默认占位符(日期 / 页码 / 正文 / 幻灯片图像)与文本样式。
10//! 一个演示文稿通常只有 1 个备注母版。
11//!
12//! ```text
13//!   NotesMaster  (备注母版:定义备注页的默认布局)
14//!        ↑ 引用
15//!   NotesSlide   (备注页:每张 slide 的演讲者备注)
16//! ```
17//!
18//! # 当前实现范围
19//!
20//! 本模块是**只读访问**——支持从已存在 `.pptx` 解析出备注母版并查询其形状。
21//! 写路径(创建/修改备注母版并持久化)暂未实现,与 python-pptx 行为一致
22//! (python-pptx 也仅暴露 `presentation.notes_master` 的只读访问)。
23//!
24//! # 与 python-pptx 的对应
25//!
26//! - `pptx.NotesMaster` ←→ [`NotesMasterRef`];
27//! - `presentation.notes_master` ←→ [`crate::presentation::Presentation::notes_master`]。
28
29use std::cell::RefCell;
30use std::rc::Rc;
31
32use crate::oxml::notesmaster::NotesMaster as OxmlNotesMaster;
33use crate::oxml::shape::Sp as OxmlSp;
34
35/// 单个备注母版引用(高阶句柄)。
36///
37/// 与 [`crate::slide_masters::SlideMasterRef`] 同样使用 `Rc<RefCell<OxmlNotesMaster>>`
38/// 共享 oxml 模型,方便在多视图间共享且通过编译期借用检查。
39#[derive(Debug, Clone)]
40pub struct NotesMasterRef {
41    /// 在所属 [`NotesMasters`] 中的索引。
42    #[allow(dead_code)]
43    pub(crate) idx: usize,
44    /// OPC part 路径(`/ppt/notesMasters/notesMasterN.xml`)。
45    pub(crate) partname: String,
46    /// 关系 id(在 `presentation.xml.rels` 中使用)。
47    pub(crate) rid: String,
48    /// 内部 oxml 模型。
49    pub(crate) oxml: Rc<RefCell<OxmlNotesMaster>>,
50}
51
52impl NotesMasterRef {
53    /// 取出 part 路径(如 `/ppt/notesMasters/notesMaster1.xml`)。
54    pub fn partname(&self) -> &str {
55        &self.partname
56    }
57    /// 取出关系 id(如 `rIdNotesMaster1`)。
58    pub fn rid(&self) -> &str {
59        &self.rid
60    }
61
62    /// shape 不可变快照(python-pptx `notes_master.shapes` 风格)。
63    ///
64    /// 返回克隆后的形状列表,调用方可自由操作而不影响母版内部状态。
65    pub fn shapes(&self) -> Vec<OxmlSp> {
66        self.oxml.borrow().shapes.clone()
67    }
68
69    /// shape 可变视图(返回 `RefMut`)。
70    ///
71    /// 通过 `RefMut` 可以原地修改母版内的形状;修改不会自动持久化到
72    /// `.pptx` 文件(写路径暂未实现)。
73    pub fn shapes_mut(&self) -> std::cell::RefMut<'_, Vec<OxmlSp>> {
74        std::cell::RefMut::map(self.oxml.borrow_mut(), |m| &mut m.shapes)
75    }
76
77    /// 备注母版中的形状数量。
78    pub fn len(&self) -> usize {
79        self.oxml.borrow().shapes.len()
80    }
81
82    /// 是否无形状。
83    pub fn is_empty(&self) -> bool {
84        self.oxml.borrow().shapes.is_empty()
85    }
86
87    /// 读取备注母版背景。`None` 表示未设置独立背景。
88    pub fn background(&self) -> Option<crate::oxml::slide::SlideBackground> {
89        self.oxml.borrow().background.clone()
90    }
91}
92
93/// 全部备注母版的集合。
94///
95/// 在 [`crate::presentation::Presentation`] 中由 `notes_masters` 字段持有。
96/// 一个演示文稿通常只有 0 或 1 个备注母版。
97#[derive(Debug, Default, Clone)]
98pub struct NotesMasters {
99    pub(crate) items: Vec<NotesMasterRef>,
100}
101
102impl NotesMasters {
103    /// 新建一个空集合。
104    pub fn new() -> Self {
105        NotesMasters::default()
106    }
107    /// 数量。
108    pub fn len(&self) -> usize {
109        self.items.len()
110    }
111    /// 是否为空。
112    pub fn is_empty(&self) -> bool {
113        self.items.is_empty()
114    }
115    /// 遍历所有备注母版引用。
116    pub fn iter(&self) -> std::slice::Iter<'_, NotesMasterRef> {
117        self.items.iter()
118    }
119    /// 按索引取不可变引用。
120    pub fn get(&self, idx: usize) -> Option<&NotesMasterRef> {
121        self.items.get(idx)
122    }
123
124    /// 取第一个备注母版(演示文稿通常只有 1 个)。
125    ///
126    /// `None` 表示该演示文稿无备注母版(`Presentation::new` 创建的空白文档无此 part)。
127    pub fn first(&self) -> Option<&NotesMasterRef> {
128        self.items.first()
129    }
130
131    /// 追加一个备注母版(仅内存模型;写路径暂未实现)。
132    pub fn push(&mut self, master: NotesMasterRef) {
133        self.items.push(master);
134    }
135}