Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion crates/utopia-ingest/src/parsers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -485,6 +485,10 @@ pub(crate) fn docx_xml_to_text(

/// PPTX: extract a:t text in the presentation's logical slide order. Tables under a:tbl
/// become Markdown grids through the same renderer as DOCX and spreadsheet tables.
///
/// 第一页是这份演示的开头,写在任何标题之前;第二页起每页一节(`## Slide N`)。封面上的
/// 日期说的是整份演示,而一个日期管到哪看它所在的标题(0064):封面也是一节的话,它的
/// 日期只管封面,后面每一页的陈述都没有见证。Word 的标题块本来就在第一个标题之前,这里一样
pub fn pptx(bytes: &[u8]) -> anyhow::Result<String> {
let mut archive =
zip::ZipArchive::new(Cursor::new(bytes.to_vec())).context("Failed to unzip pptx")?;
Expand Down Expand Up @@ -515,7 +519,12 @@ pub fn pptx(bytes: &[u8]) -> anyhow::Result<String> {
for (num, name) in slides {
let xml = pptx_part(&mut archive, &name)?;
let text = pptx_xml_to_text(&xml)?;
if !text.trim().is_empty() {
if text.trim().is_empty() {
continue;
}
if num == 1 {
out.push_str(&format!("{text}\n"));
} else {
out.push_str(&format!("\n## Slide {num}\n{text}\n"));
}
}
Expand Down
2 changes: 1 addition & 1 deletion crates/utopia-ingest/tests/pptx_breaks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,5 +56,5 @@ fn runs_without_a_break_still_join() {
let text = read(
r#"<a:p><a:r><a:t>Hel</a:t></a:r><a:r><a:t>lo</a:t></a:r></a:p><a:p><a:r><a:t>world</a:t></a:r></a:p>"#,
);
assert!(text.contains("## Slide 1\nHello\nworld\n"), "{text}");
assert!(text.starts_with("Hello\nworld\n"), "{text}");
}
16 changes: 14 additions & 2 deletions crates/utopia-ingest/tests/pptx_order.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,12 @@ fn parsed(parts: &Parts) -> String {
fn logical_order_and_page_numbers_do_not_depend_on_zip_or_part_order() {
let mut parts = deck();
let expected = parsed(&parts);
// 第一页是开头,没有标题;第二页起每页一节
assert!(
expected.contains("## Slide 1\nSECOND-CREATED & 中 <raw>"),
expected.starts_with("SECOND-CREATED & 中 <raw>"),
"{expected}"
);
assert!(!expected.contains("## Slide 1"), "{expected}");
assert!(expected.contains("## Slide 2\nFIRST-CREATED"), "{expected}");
assert!(!expected.contains("UNLISTED"));
assert!(!expected.contains("NOTES"));
Expand All @@ -66,7 +68,7 @@ fn logical_order_and_page_numbers_do_not_depend_on_zip_or_part_order() {
.replace("rIdA", "rIdB")
.replace("temp", "rIdA");
let normal = parsed(&parts);
assert!(normal.contains("## Slide 1\nFIRST-CREATED"));
assert!(normal.starts_with("FIRST-CREATED"), "{normal}");
assert!(normal.contains("## Slide 2\nSECOND-CREATED"));
}

Expand Down Expand Up @@ -216,3 +218,13 @@ fn empty_presentations_and_manifest_free_legacy_packages_keep_their_behavior() {
no_root.retain(|(n, _)| n != "_rels/.rels");
assert_eq!(parsed(&no_root), parsed(&deck()));
}

/// 封面写在任何标题之前:它说的日期管整份演示,不只管它自己那一页(0064 的范围看标题)。
/// 第一页没有字的演示没有开头,其余各页照旧各是一节
#[test]
fn the_first_slide_opens_the_deck_and_later_slides_are_sections() {
let text = parsed(&deck());
let first_heading = text.find("## ").expect("later slides are sections");
assert!(text[..first_heading].contains("SECOND-CREATED"), "{text}");
assert!(text[first_heading..].starts_with("## Slide 2\n"), "{text}");
}
31 changes: 25 additions & 6 deletions crates/utopia-server/src/time_resolution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -642,6 +642,17 @@ pub async fn resolve_document(state: &AppState, document_id: Uuid) -> anyhow::Re
Ok(())
}

/// 见证的名字:日期的名字加它的字(「提报日期 2026年9月4日」)。没有名字的光是字;封面上
/// 一个光秃秃的日期,模型常把日期自己又填成名字,那就只写一遍
fn witness_name(name: &str, words: &str) -> String {
let (name, words) = (name.trim(), words.trim());
if name.is_empty() || name == words {
words.to_string()
} else {
format!("{name} {words}")
}
}

/// 这篇文档的陈述各由它所在那一节里文本说话的那一刻作证(0064 决定 3)。
///
/// 一条陈述在哪一节,看它的引文在它那一块里的位置、那一块正文里的标题;管着它的是范围
Expand Down Expand Up @@ -680,12 +691,7 @@ async fn attest_statements(
let path = headings_at(&text, usize::try_from(start.unwrap_or(0)).unwrap_or(0));
let witness = now_in_force(&context.entries, &path).and_then(|now| {
let (at, _) = parts_to_time(&now.from)?;
let by = if now.name.is_empty() {
now.words.clone()
} else {
format!("{} {}", now.name, now.words)
};
Some((at, by))
Some((at, witness_name(&now.name, &now.words)))
});
// 每条证据记下自己这一节的日期(没说的记成空):事实上只留最早的那一个,证据所在
// 的文档删了之后要照还在的证据重算,得知道每一条各是哪天
Expand Down Expand Up @@ -730,6 +736,19 @@ mod attestation_tests;
#[cfg(test)]
mod tests {
use super::*;

#[test]
fn a_bare_date_is_named_once() {
assert_eq!(
witness_name("提报日期", "2026年9月4日"),
"提报日期 2026年9月4日"
);
assert_eq!(witness_name("", "2024年10月10日"), "2024年10月10日");
assert_eq!(
witness_name("2024年10月10日", " 2024年10月10日"),
"2024年10月10日"
);
}
use utopia_extract::time::NamedPeriod;

fn parts(y: i32, m: Option<u32>, d: Option<u32>) -> DateParts {
Expand Down
Loading