// Fixed vocabulary, typed data and escaped text; arbitrary XML is not accepted.
///|
priv struct XmlOutput {
data : @buffer.Buffer
}
///|
fn XmlOutput::new() -> XmlOutput {
{ data: @buffer.Buffer(), }
}
///|
fn XmlOutput::write_string(self : XmlOutput, s : String) -> Unit raise OdsError {
let bytes = @utf8.encode(s[:])
if self.data.length() + bytes.length() > 16777216 {
raise Invalid("content XML exceeds 16 MiB")
}
self.data.write_bytes(bytes[:])
}
///|
fn XmlOutput::to_string(self : XmlOutput) -> String {
@utf8.decode_lossy(self.data.to_bytes()[:])
}
///|
fn xml_escape(s : String) -> String {
let b = StringBuilder()
for c in s {
match c {
'&' => b.write_string("&")
'<' => b.write_string("<")
'>' => b.write_string(">")
'"' => b.write_string(""")
'\'' => b.write_string("'")
'\n' => b.write_string("
")
'\r' => b.write_string("
")
'\t' => b.write_string(" ")
_ => b.write_char(c)
}
}
b.to_string()
}
///|
fn text_paragraph(s : String) -> String {
let b = StringBuilder()
b.write_string("")
for c in s {
match c {
' ' => b.write_string("")
'\t' => b.write_string("")
'\n' => b.write_string("")
_ => b.write_string(xml_escape(c.to_string()))
}
}
b.write_string("")
b.to_string()
}
///|
fn pad(n : Int, size : Int) -> String {
let s = n.to_string()
let b = StringBuilder()
for _ in 0..<(size - s.length()) {
b.write_string("0")
}
b.write_string(s)
b.to_string()
}
///|
fn Cell::xml(self : Cell, extra : String) -> String {
let b = StringBuilder()
b.write_string(" ""
Text(s) => {
b.write_string(
" office:value-type=\"string\" office:string-value=\"\{xml_escape(s)}\"",
)
s
}
Number(n) => {
let s = n.to_string()
b.write_string(" office:value-type=\"float\" office:value=\"\{s}\"")
s
}
Boolean(v) => {
let s = if v { "true" } else { "false" }
b.write_string(
" office:value-type=\"boolean\" office:boolean-value=\"\{s}\"",
)
s
}
Date(y, m, d) => {
let s = "\{pad(y, 4)}-\{pad(m, 2)}-\{pad(d, 2)}"
b.write_string(" office:value-type=\"date\" office:date-value=\"\{s}\"")
s
}
}
b.write_string(">")
if self.value != Empty {
b.write_string(text_paragraph(display))
}
b.write_string("")
b.to_string()
}