///|
pub(all) struct RichTextFont {
bold : Bool
italic : Bool
strike : Bool
outline : Bool
shadow : Bool
condense : Bool
extended : Bool
underline : String?
size : Double?
color : String?
color_theme : Int?
color_indexed : Int?
color_tint : Double?
charset : Int?
family_number : Int?
scheme : String?
vert_align : String?
family : String?
} derive(Debug)
///|
pub(all) struct RichTextRun {
text : String
font : RichTextFont?
} derive(Debug)
///|
let max_cell_chars = 32767
///|
fn count_utf16_units(text : StringView) -> Int {
let mut count = 0
for c in text {
let units = if c.to_int() > 0xffff { 2 } else { 1 }
count = count + units
}
count
}
///|
fn truncate_utf16_units(text : StringView, limit : Int) -> String {
if limit <= 0 {
return ""
}
let sb = StringBuilder::new()
let mut count = 0
for c in text {
let units = if c.to_int() > 0xffff { 2 } else { 1 }
if count + units > limit {
break
}
sb.write_char(c)
count = count + units
}
sb.to_string()
}
///|
fn rich_text_plain_text(runs : ArrayView[RichTextRun]) -> String {
let sb = StringBuilder::new()
for run in runs {
sb.write_view(run.text)
}
sb.to_string()
}
///|
fn rich_text_total_units(runs : ArrayView[RichTextRun]) -> Int {
let mut total = 0
for run in runs {
total = total + count_utf16_units(run.text)
}
total
}
///|
fn rich_text_truncate_runs(
runs : ArrayView[RichTextRun],
limit : Int,
) -> Array[RichTextRun] {
let truncated : Array[RichTextRun] = []
let mut remaining = limit
for run in runs {
if remaining <= 0 {
break
}
let units = count_utf16_units(run.text)
if units <= remaining {
truncated.push(run)
remaining = remaining - units
} else {
let text = truncate_utf16_units(run.text, remaining)
truncated.push({ text, font: run.font })
break
}
}
truncated
}
///|
test "rich text truncate utf16 handles non-positive limit" {
inspect(truncate_utf16_units("abc", 0), content="")
inspect(truncate_utf16_units("abc", -1), content="")
}
///|
test "rich text truncate runs supports partial and exact limits" {
let runs : Array[RichTextRun] = [
{ text: "AB", font: None },
{ text: "C", font: None },
]
debug_inspect(
rich_text_truncate_runs(runs, 1),
content=(
#|[{ text: "A", font: None }]
),
)
debug_inspect(
rich_text_truncate_runs(runs, 2),
content=(
#|[{ text: "AB", font: None }]
),
)
}