mirror of
https://github.com/storytold/gridcraft.git
synced 2026-11-15 10:29:11 -06:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d593a8f99d |
Generated
+1
@@ -2349,6 +2349,7 @@ dependencies = [
|
||||
"craft-offcrypto",
|
||||
"craft-opc",
|
||||
"craft-xls",
|
||||
"craft-xml",
|
||||
"gridcraft-core",
|
||||
"gridcraft-formula",
|
||||
"gridcraft-functions",
|
||||
|
||||
@@ -40,6 +40,7 @@ craft-opc = { git = "https://github.com/storytold/craft-libs", rev = "bb57d422d0
|
||||
# turn them into workbooks.
|
||||
craft-csv = { git = "https://github.com/storytold/craft-libs", rev = "bb57d422d0bc842cbde430955825553ab7b976f4", version = "0.1.0" }
|
||||
# Excel 97-2003 (.xls, BIFF8) workbooks; `crates/xlsx/src/xls.rs` maps them onto the model.
|
||||
craft-xml = { git = "https://github.com/storytold/craft-libs", rev = "bb57d422d0bc842cbde430955825553ab7b976f4", version = "0.1.0" }
|
||||
craft-xls = { git = "https://github.com/storytold/craft-libs", rev = "bb57d422d0bc842cbde430955825553ab7b976f4", version = "0.1.0" }
|
||||
|
||||
# foundation
|
||||
|
||||
@@ -23,6 +23,7 @@ craft-csv = { workspace = true }
|
||||
craft-offcrypto = { workspace = true }
|
||||
craft-opc = { workspace = true }
|
||||
craft-xls = { workspace = true }
|
||||
craft-xml = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
quick-xml = { workspace = true }
|
||||
zip = { workspace = true }
|
||||
|
||||
+19
-48
@@ -359,31 +359,9 @@ pub fn parse_streaming(xml: &[u8], split: &[&str], cb: &mut dyn FnMut(El) -> Res
|
||||
|
||||
// ---------------------------------------------------------------- writing helpers
|
||||
|
||||
/// Escapes text content.
|
||||
pub fn esc(s: &str) -> String {
|
||||
let mut o = String::with_capacity(s.len());
|
||||
push_esc(&mut o, s);
|
||||
o
|
||||
}
|
||||
|
||||
/// Appends [`esc`]`(s)` to `o`.
|
||||
pub fn push_esc(o: &mut String, s: &str) {
|
||||
if s.bytes().all(|b| matches!(b, b'\t' | b'\n' | 0x20..=0x7E) && !matches!(b, b'&' | b'<' | b'>')) {
|
||||
o.push_str(s);
|
||||
return;
|
||||
}
|
||||
o.reserve(s.len());
|
||||
for c in s.chars() {
|
||||
match c {
|
||||
'&' => o.push_str("&"),
|
||||
'<' => o.push_str("<"),
|
||||
'>' => o.push_str(">"),
|
||||
'\r' => o.push_str(" "),
|
||||
c if is_xml_char(c) => o.push(c),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
// XML 1.0 byte escaping is shared; spreadsheet string and phonetic policy stays here.
|
||||
use craft_xml::is_xml_char;
|
||||
pub use craft_xml::{esc, esc_attr, push_esc};
|
||||
|
||||
/// Appends `n` in decimal.
|
||||
pub fn push_u64(o: &mut String, mut n: u64) {
|
||||
@@ -422,29 +400,6 @@ pub fn push_a1(o: &mut String, c: gridcraft_core::CellRef) {
|
||||
push_u64(o, u64::from(c.row) + 1);
|
||||
}
|
||||
|
||||
/// Escapes an attribute value (double-quoted).
|
||||
pub fn esc_attr(s: &str) -> String {
|
||||
let mut o = String::with_capacity(s.len());
|
||||
for c in s.chars() {
|
||||
match c {
|
||||
'&' => o.push_str("&"),
|
||||
'<' => o.push_str("<"),
|
||||
'>' => o.push_str(">"),
|
||||
'"' => o.push_str("""),
|
||||
'\n' => o.push_str(" "),
|
||||
'\r' => o.push_str(" "),
|
||||
'\t' => o.push_str("	"),
|
||||
c if is_xml_char(c) => o.push(c),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
o
|
||||
}
|
||||
|
||||
fn is_xml_char(c: char) -> bool {
|
||||
matches!(c, '\t' | '\n' | '\r' | '\u{20}'..='\u{D7FF}' | '\u{E000}'..='\u{FFFD}' | '\u{10000}'..='\u{10FFFF}')
|
||||
}
|
||||
|
||||
/// ST_Xstring encoding for cell text: characters XML can't carry and carriage returns become
|
||||
/// `_xHHHH_`, and literal `_xHHHH_` sequences are protected with `_x005F_`. The result still needs
|
||||
/// [`esc`].
|
||||
@@ -805,3 +760,19 @@ mod tests {
|
||||
assert_eq!("1.5E20".parse::<f64>().unwrap(), 1.5e20);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod shared_escape_parity {
|
||||
use super::*;
|
||||
#[test]
|
||||
fn xml_content_and_attributes_preserve_grid_bytes() {
|
||||
let value = "&<>\"'\t\n\r\0\u{1f}\u{fffe}\u{ffff}é😀";
|
||||
assert_eq!(esc(value), "&<>\"'\t\n é😀");
|
||||
assert_eq!(esc_attr(value), "&<>"'	 é😀");
|
||||
let mut appended = "prefix:".to_owned();
|
||||
push_esc(&mut appended, value);
|
||||
assert_eq!(appended, format!("prefix:{}", esc(value)));
|
||||
assert_eq!(esc("plain ASCII\t\n"), "plain ASCII\t\n");
|
||||
assert_eq!(decode_xstring(&encode_xstring("_x000D_\r\0😀")), "_x000D_\r\0😀");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user