use docspec_core::Result;
use quick_xml::events::{BytesRef, BytesStart};
use super::input::XmlCursor;
use super::{parse_error, read_attribute};
use crate::symbol_fonts::SymbolFont;
pub(super) fn collect_text_content(input: &mut XmlCursor) -> Result<String> {
let mut content = String::new();
loop {
let event = input.read_owned()?;
match event {
quick_xml::events::Event::Text(text) => {
let decoded = text
.decode()
.map_err(|err| parse_error(format!("malformed document.xml: {err}")))?;
let unescaped = quick_xml::escape::unescape(&decoded)
.map_err(|err| parse_error(format!("malformed document.xml: {err}")))?;
content.push_str(&unescaped);
}
quick_xml::events::Event::CData(cdata) => {
let bytes = cdata.into_inner();
let text = core::str::from_utf8(&bytes)
.map_err(|err| parse_error(format!("malformed document.xml: {err}")))?;
content.push_str(text);
}
quick_xml::events::Event::GeneralRef(reference) => {
content.push_str(&decode_general_ref(&reference)?);
}
quick_xml::events::Event::End(end) if end.local_name().as_ref() == b"t" => {
return Ok(content);
}
quick_xml::events::Event::Start(start) => {
input.skip_subtree(&start)?;
}
quick_xml::events::Event::Empty(_)
| quick_xml::events::Event::End(_)
| quick_xml::events::Event::Comment(_)
| quick_xml::events::Event::Decl(_)
| quick_xml::events::Event::PI(_)
| quick_xml::events::Event::DocType(_) => {}
quick_xml::events::Event::Eof => return Ok(content),
}
}
}
pub(super) fn normalize_symbol_text(font: Option<SymbolFont>, text: String) -> String {
let Some(font) = font else {
return text;
};
let mut out = String::with_capacity(text.len());
for ch in text.chars() {
let key = match u32::from(ch) {
cp @ 0xF020..=0xF0FF => cp
.checked_sub(0xF000)
.and_then(|stripped| u8::try_from(stripped).ok()),
cp @ 0x0020..=0x00FF => u8::try_from(cp).ok(),
_ => None,
};
if let Some(k) = key {
if let Some(mapped) = font.convert(k) {
out.push(mapped);
}
}
}
out
}
pub(super) fn decode_general_ref(reference: &BytesRef<'_>) -> Result<String> {
let decoded = reference
.decode()
.map_err(|err| parse_error(format!("malformed document.xml: {err}")))?;
let escaped = format!("&{decoded};");
let unescaped = quick_xml::escape::unescape(&escaped)
.map_err(|err| parse_error(format!("malformed document.xml: {err}")))?;
Ok(unescaped.into_owned())
}
pub(super) fn resolve_sym_char(
run_font: Option<SymbolFont>,
tag: &BytesStart<'_>,
) -> Option<String> {
let char_hex = read_attribute(tag, b"w:char")?;
let key = crate::properties::parse_sym_char(&char_hex)?;
let font = read_attribute(tag, b"w:font")
.and_then(|name| SymbolFont::from_name(&name))
.or(run_font)?;
font.convert(key).map(String::from)
}