diff --git a/crates/textimport/src/xlsx.rs b/crates/textimport/src/xlsx.rs index d0795699..3f20f3de 100644 --- a/crates/textimport/src/xlsx.rs +++ b/crates/textimport/src/xlsx.rs @@ -35,11 +35,23 @@ fn first_sheet_target(wb: &str, rels: &str) -> Option { Some(if target.starts_with("xl/") { target } else { format!("xl/{target}") }) } +/// The text of a shared string (``) or inline string (``): its own `` and the `` of its +/// `` runs. Phonetic guides (``, ``) are the reading, not the cell text. +fn string_item(si: &El) -> String { + si.els() + .map(|e| match e.name.as_str() { + "t" => e.text(), + "r" => e.els().filter(|t| t.name == "t").map(El::text).collect(), + _ => String::new(), + }) + .collect() +} + fn value(c: &El, shared: &[String]) -> String { let v = || c.child("v").map(El::text).unwrap_or_default(); match c.attr("t") { Some("s") => v().trim().parse::().ok().and_then(|i| shared.get(i).cloned()).unwrap_or_default(), - Some("inlineStr") => c.child("is").map(El::text).unwrap_or_default(), + Some("inlineStr") => c.child("is").map(string_item).unwrap_or_default(), // A cached formula string is text even when it looks like a number. Some("str") => v(), Some("b") => { @@ -90,7 +102,7 @@ pub fn records(bytes: &[u8], sheet: Option<&str>) -> Result>, Im let shared: Vec = part(&mut zip, "xl/sharedStrings.xml")? .map(|x| parse(&x)) .transpose()? - .map(|sst| sst.els().filter(|e| e.name == "si").map(El::text).collect()) + .map(|sst| sst.els().filter(|e| e.name == "si").map(string_item).collect()) .unwrap_or_default(); let mut cells: Vec<(usize, usize, String)> = Vec::new(); if let Some(data) = sheet_xml.child("sheetData") { @@ -198,7 +210,7 @@ pub fn import(bytes: &[u8]) -> Result { let shared: Vec = part(&mut zip, "xl/sharedStrings.xml")? .map(|x| parse(&x)) .transpose()? - .map(|sst| sst.els().filter(|e| e.name == "si").map(El::text).collect()) + .map(|sst| sst.els().filter(|e| e.name == "si").map(string_item).collect()) .unwrap_or_default(); let mut cells: Vec<((usize, usize), String)> = Vec::new(); if let Some(data) = sheet.child("sheetData") { @@ -355,6 +367,31 @@ mod tests { assert!(records(&book, Some("Missing")).is_err()); } + #[test] + fn phonetic_guide_text_is_not_cell_text() { + let book = sheets(&[ + ( + "xl/workbook.xml", + r#""#, + ), + ("xl/_rels/workbook.xml.rels", r#""#), + ( + "xl/sharedStrings.xml", + r#"日本ニホン東京トウキョウ"#, + ), + ( + "xl/worksheets/sheet1.xml", + r#"01大阪オオサカ"#, + ), + ]); + let want = ["日本", "東京", "大阪"].map(String::from).to_vec(); + assert_eq!(records(&book, None).unwrap(), vec![want.clone()]); + let imp = import(&book).unwrap(); + let t = imp.story.tables.values().next().expect("a table"); + let got: Vec = (0..3).map(|c| t.cell(0, c).unwrap().text.text.clone()).collect(); + assert_eq!(got, want); + } + fn one_value(cell: &str) -> Vec { workbook_with(&format!( r#"Value{cell}"#