///|
pub struct CoreProperties {
mut title : String
mut subject : String
mut creator : String
mut keywords : String
mut description : String
mut last_modified_by : String
mut language : String
mut identifier : String
mut revision : String
mut content_status : String
mut category : String
mut version : String
mut created : String
mut modified : String
}
///|
fn CoreProperties::new() -> CoreProperties {
{
title: "",
subject: "",
creator: "mbtexcel",
keywords: "",
description: "",
last_modified_by: "mbtexcel",
language: "",
identifier: "",
revision: "",
content_status: "",
category: "",
version: "",
created: "",
modified: "",
}
}
///|
pub fn CoreProperties::with_values(
title? : String,
subject? : String,
creator? : String,
keywords? : String,
description? : String,
last_modified_by? : String,
language? : String,
identifier? : String,
revision? : String,
content_status? : String,
category? : String,
version? : String,
created? : String,
modified? : String,
) -> CoreProperties {
let props = CoreProperties::new()
match title {
Some(value) => props.title = value
None => ()
}
match subject {
Some(value) => props.subject = value
None => ()
}
match creator {
Some(value) => props.creator = value
None => ()
}
match keywords {
Some(value) => props.keywords = value
None => ()
}
match description {
Some(value) => props.description = value
None => ()
}
match last_modified_by {
Some(value) => props.last_modified_by = value
None => ()
}
match language {
Some(value) => props.language = value
None => ()
}
match identifier {
Some(value) => props.identifier = value
None => ()
}
match revision {
Some(value) => props.revision = value
None => ()
}
match content_status {
Some(value) => props.content_status = value
None => ()
}
match category {
Some(value) => props.category = value
None => ()
}
match version {
Some(value) => props.version = value
None => ()
}
match created {
Some(value) => props.created = value
None => ()
}
match modified {
Some(value) => props.modified = value
None => ()
}
props
}
///|
pub struct AppProperties {
mut application : String
mut doc_security : Int?
mut scale_crop : Bool?
mut company : String
mut links_up_to_date : Bool?
mut hyperlinks_changed : Bool?
mut app_version : String
}
///|
fn AppProperties::new() -> AppProperties {
{
application: "mbtexcel",
doc_security: None,
scale_crop: None,
company: "",
links_up_to_date: None,
hyperlinks_changed: None,
app_version: "",
}
}
///|
pub fn AppProperties::with_values(
application? : String,
doc_security? : Int,
scale_crop? : Bool,
company? : String,
links_up_to_date? : Bool,
hyperlinks_changed? : Bool,
app_version? : String,
) -> AppProperties {
let props = AppProperties::new()
match application {
Some(value) => props.application = value
None => ()
}
match doc_security {
Some(value) => props.doc_security = Some(value)
None => ()
}
match scale_crop {
Some(value) => props.scale_crop = Some(value)
None => ()
}
match company {
Some(value) => props.company = value
None => ()
}
match links_up_to_date {
Some(value) => props.links_up_to_date = Some(value)
None => ()
}
match hyperlinks_changed {
Some(value) => props.hyperlinks_changed = Some(value)
None => ()
}
match app_version {
Some(value) => props.app_version = value
None => ()
}
props
}
///|
fn bool_text(value : Bool) -> String {
if value {
"true"
} else {
"false"
}
}
///|
fn write_core_properties_xml(
props : CoreProperties,
budget? : WritePartBudget,
) -> String raise XlsxError {
let sb = LimitedXmlBuilder::new(budget)
sb.write_view("\n")
sb.write_view(
"\n")
if props.title != "" {
sb.write_view(" ")
sb.write_xml_text(props.title)
sb.write_view("\n")
}
if props.subject != "" {
sb.write_view(" ")
sb.write_xml_text(props.subject)
sb.write_view("\n")
}
if props.creator != "" {
sb.write_view(" ")
sb.write_xml_text(props.creator)
sb.write_view("\n")
}
if props.keywords != "" {
sb.write_view(" ")
sb.write_xml_text(props.keywords)
sb.write_view("\n")
}
if props.description != "" {
sb.write_view(" ")
sb.write_xml_text(props.description)
sb.write_view("\n")
}
if props.last_modified_by != "" {
sb.write_view(" ")
sb.write_xml_text(props.last_modified_by)
sb.write_view("\n")
}
if props.language != "" {
sb.write_view(" ")
sb.write_xml_text(props.language)
sb.write_view("\n")
}
if props.identifier != "" {
sb.write_view(" ")
sb.write_xml_text(props.identifier)
sb.write_view("\n")
}
if props.revision != "" {
sb.write_view(" ")
sb.write_xml_text(props.revision)
sb.write_view("\n")
}
if props.content_status != "" {
sb.write_view(" ")
sb.write_xml_text(props.content_status)
sb.write_view("\n")
}
if props.category != "" {
sb.write_view(" ")
sb.write_xml_text(props.category)
sb.write_view("\n")
}
if props.version != "" {
sb.write_view(" ")
sb.write_xml_text(props.version)
sb.write_view("\n")
}
if props.created != "" {
sb.write_view(" ")
sb.write_xml_text(props.created)
sb.write_view("\n")
}
if props.modified != "" {
sb.write_view(" ")
sb.write_xml_text(props.modified)
sb.write_view("\n")
}
sb.write_view("")
sb.to_string()
}
///|
fn write_app_properties_xml(
props : AppProperties,
budget? : WritePartBudget,
) -> String raise XlsxError {
let sb = LimitedXmlBuilder::new(budget)
sb.write_view("\n")
sb.write_view(
"\n",
)
if props.application != "" {
sb.write_view(" ")
sb.write_xml_text(props.application)
sb.write_view("\n")
}
match props.scale_crop {
Some(value) => {
sb.write_view(" ")
sb.write_view(bool_text(value))
sb.write_view("\n")
}
None => ()
}
match props.doc_security {
Some(value) => {
sb.write_view(" ")
sb.write_view("\{value}")
sb.write_view("\n")
}
None => ()
}
if props.company != "" {
sb.write_view(" ")
sb.write_xml_text(props.company)
sb.write_view("\n")
}
match props.links_up_to_date {
Some(value) => {
sb.write_view(" ")
sb.write_view(bool_text(value))
sb.write_view("\n")
}
None => ()
}
match props.hyperlinks_changed {
Some(value) => {
sb.write_view(" ")
sb.write_view(bool_text(value))
sb.write_view("\n")
}
None => ()
}
if props.app_version != "" {
sb.write_view(" ")
sb.write_xml_text(props.app_version)
sb.write_view("\n")
}
sb.write_view("")
sb.to_string()
}
///|
fn parse_bool(text : StringView) -> Bool {
let value = text.to_owned()
value == "true" || value == "1"
}
///|
fn parse_core_properties(xml : StringView) -> CoreProperties raise XlsxError {
let props = CoreProperties::new()
match extract_tag_body(xml, "dc:title") {
Some(value) => props.title = unescape_xml_text(value)
None => ()
}
match extract_tag_body(xml, "dc:subject") {
Some(value) => props.subject = unescape_xml_text(value)
None => ()
}
match extract_tag_body(xml, "dc:creator") {
Some(value) => props.creator = unescape_xml_text(value)
None => ()
}
match extract_tag_body(xml, "cp:keywords") {
Some(value) => props.keywords = unescape_xml_text(value)
None => ()
}
match extract_tag_body(xml, "dc:description") {
Some(value) => props.description = unescape_xml_text(value)
None => ()
}
match extract_tag_body(xml, "cp:lastModifiedBy") {
Some(value) => props.last_modified_by = unescape_xml_text(value)
None => ()
}
match extract_tag_body(xml, "dc:language") {
Some(value) => props.language = unescape_xml_text(value)
None => ()
}
match extract_tag_body(xml, "dc:identifier") {
Some(value) => props.identifier = unescape_xml_text(value)
None => ()
}
match extract_tag_body(xml, "cp:revision") {
Some(value) => props.revision = unescape_xml_text(value)
None => ()
}
match extract_tag_body(xml, "cp:contentStatus") {
Some(value) => props.content_status = unescape_xml_text(value)
None => ()
}
match extract_tag_body(xml, "cp:category") {
Some(value) => props.category = unescape_xml_text(value)
None => ()
}
match extract_tag_body(xml, "cp:version") {
Some(value) => props.version = unescape_xml_text(value)
None => ()
}
match extract_tag_body(xml, "dcterms:created") {
Some(value) => props.created = unescape_xml_text(value)
None => ()
}
match extract_tag_body(xml, "dcterms:modified") {
Some(value) => props.modified = unescape_xml_text(value)
None => ()
}
props
}
///|
fn parse_app_properties(xml : StringView) -> AppProperties raise XlsxError {
let props = AppProperties::new()
match extract_tag_body(xml, "Application") {
Some(value) => props.application = unescape_xml_text(value)
None => ()
}
match extract_tag_body(xml, "ScaleCrop") {
Some(value) => props.scale_crop = Some(parse_bool(value))
None => ()
}
match extract_tag_body(xml, "DocSecurity") {
Some(value) =>
props.doc_security = Some(
@string.parse_int(unescape_xml_text(value), base=10) catch {
_ => 0
},
)
None => ()
}
match extract_tag_body(xml, "Company") {
Some(value) => props.company = unescape_xml_text(value)
None => ()
}
match extract_tag_body(xml, "LinksUpToDate") {
Some(value) => props.links_up_to_date = Some(parse_bool(value))
None => ()
}
match extract_tag_body(xml, "HyperlinksChanged") {
Some(value) => props.hyperlinks_changed = Some(parse_bool(value))
None => ()
}
match extract_tag_body(xml, "AppVersion") {
Some(value) => props.app_version = unescape_xml_text(value)
None => ()
}
props
}
///|
let custom_props_namespace = "http://schemas.openxmlformats.org/officeDocument/2006/custom-properties"
///|
let custom_props_vt_namespace = "http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes"
///|
let custom_props_fmt_id = "{D5CDD505-2E9C-101B-9397-08002B2CF9AE}"
///|
fn extract_tag_value(xml : StringView, tag_name : StringView) -> String? {
let open = "<\{tag_name.to_owned()}>"
let close = "\{tag_name.to_owned()}>"
let text = xml.to_owned()
match text.find(open) {
None => None
Some(start) => {
let rest = text[start + open.length():]
match rest.find(close) {
Some(end) => {
let slice = rest[:end]
Some(slice.to_owned())
}
None => None
}
}
}
}
///|
fn parse_custom_property_value(
xml : StringView,
) -> CustomPropertyValue? raise XlsxError {
match extract_tag_value(xml, "vt:lpwstr") {
Some(value) => return Some(Text(unescape_xml_text(value)))
None => ()
}
match extract_tag_value(xml, "vt:bool") {
Some(value) => return Some(Boolean(parse_cell_bool(value)))
None => ()
}
match extract_tag_value(xml, "vt:filetime") {
Some(value) => return Some(DateTime(value))
None => ()
}
let int_tags = [
"vt:i1", "vt:i2", "vt:i4", "vt:i8", "vt:int", "vt:ui1", "vt:ui2", "vt:ui4", "vt:ui8",
"vt:uint",
]
for tag in int_tags {
match extract_tag_value(xml, tag) {
Some(value) =>
return Some(
Integer(
@string.parse_int(value, base=10) catch {
_ =>
raise InvalidWorkbookProperty(msg="custom property int invalid")
},
),
)
None => ()
}
}
let float_tags = ["vt:r4", "vt:r8", "vt:decimal"]
for tag in float_tags {
match extract_tag_value(xml, tag) {
Some(value) =>
return Some(
Float(
@string.parse_double(value) catch {
_ =>
raise InvalidWorkbookProperty(
msg="custom property number invalid",
)
},
),
)
None => ()
}
}
None
}
///|
fn write_custom_property_value(
sb : LimitedXmlBuilder,
value : CustomPropertyValue,
) -> Unit raise XlsxError {
match value {
Integer(number) => {
sb.write_view("")
sb.write_view("\{number}")
sb.write_view("")
}
Float(number) => {
sb.write_view("")
sb.write_view("\{number}")
sb.write_view("")
}
Boolean(value) => {
sb.write_view("")
sb.write_view(if value { "true" } else { "false" })
sb.write_view("")
}
Text(text) => {
sb.write_view("")
sb.write_xml_text(text)
sb.write_view("")
}
DateTime(text) => {
sb.write_view("")
sb.write_xml_text(text)
sb.write_view("")
}
}
}
///|
fn write_custom_properties_xml(
props : ArrayView[CustomProperty],
budget? : WritePartBudget,
) -> String raise XlsxError {
let sb = LimitedXmlBuilder::new(budget)
sb.write_view("\n")
sb.write_view("\n")
let mut pid = 2
for prop in props {
match prop.value {
None => ()
Some(value) => {
sb.write_view(" ")
write_custom_property_value(sb, value)
sb.write_view("\n")
pid = pid + 1
}
}
}
sb.write_view("")
sb.to_string()
}
///|
fn parse_custom_properties(
xml : StringView,
) -> Array[CustomProperty] raise XlsxError {
let properties : Array[CustomProperty] = []
let mut first = true
for chunk in xml.split("") {
Some(pos) => pos
None => raise InvalidXml(msg="property tag not closed")
}
let tag = text[:end]
let name = match attr_value(tag, "name") {
Some(value) => unescape_xml_text(value)
None => raise InvalidXml(msg="property name missing")
}
let close_tag = ""
let body = match text.find(close_tag) {
Some(pos) => text[end + 1:pos]
None => ""
}
let value = parse_custom_property_value(body)
properties.push({ name, value })
}
properties
}