///|
fn build_substring_local(s : String, start : Int, end : Int) -> String {
let buf = StringBuilder::new()
let mut i = start
let len = s.length()
while i < end && i < len {
buf.write_char(Int::unsafe_to_char(s[i].to_int()))
i = i + 1
}
buf.to_string()
}
///|
fn string_ends_with_local(s : String, suffix : String) -> Bool {
if suffix.length() > s.length() {
return false
}
let start = s.length() - suffix.length()
for i in 0.. String {
let mut start = 0
let mut end = s.length()
while start < end {
let c = Int::unsafe_to_char(s[start].to_int())
if c == ' ' || c == '\t' || c == '\n' || c == '\r' {
start = start + 1
} else {
break
}
}
while end > start {
let c = Int::unsafe_to_char(s[end - 1].to_int())
if c == ' ' || c == '\t' || c == '\n' || c == '\r' {
end = end - 1
} else {
break
}
}
build_substring_local(s, start, end)
}
///|
fn is_space_char(c : Char) -> Bool {
c == ' ' || c == '\t' || c == '\n' || c == '\r'
}
///|
fn char_at(s : String, i : Int) -> Char {
Int::unsafe_to_char(s[i].to_int())
}
///|
fn parse_simple_number(s : String) -> Double {
let s = trim_string_local(s)
if s.length() == 0 {
return 0.0
}
let mut result = 0.0
let mut sign = 1.0
let mut i = 0
let len = s.length()
let first = char_at(s, i)
if first == '-' {
sign = -1.0
i = i + 1
} else if first == '+' {
i = i + 1
}
while i < len {
let c = char_at(s, i)
if c >= '0' && c <= '9' {
result = result * 10.0 + (c.to_int() - 48).to_double()
i = i + 1
} else {
break
}
}
if i < len && char_at(s, i) == '.' {
i = i + 1
let mut frac = 0.1
while i < len {
let c = char_at(s, i)
if c >= '0' && c <= '9' {
result = result + (c.to_int() - 48).to_double() * frac
frac = frac * 0.1
i = i + 1
} else {
break
}
}
}
sign * result
}
///|
fn parse_length_simple(s : String) -> Double {
let s = trim_string_local(s)
let cleaned = if string_ends_with_local(s, "px") {
build_substring_local(s, 0, s.length() - 2)
} else if string_ends_with_local(s, "em") || string_ends_with_local(s, "pt") {
build_substring_local(s, 0, s.length() - 2)
} else if string_ends_with_local(s, "%") {
build_substring_local(s, 0, s.length() - 1)
} else {
s
}
parse_simple_number(cleaned)
}
///|
fn replace_viewport_units(value : String, vw : Double, vh : Double) -> String {
let len = value.length()
let buf = StringBuilder::new()
let mut i = 0
while i < len {
let c = char_at(value, i)
let is_number_start = (c >= '0' && c <= '9') ||
c == '.' ||
c == '-' ||
c == '+'
if is_number_start {
let start = i
i = i + 1
while i < len {
let nc = char_at(value, i)
if (nc >= '0' && nc <= '9') || nc == '.' {
i = i + 1
} else {
break
}
}
let num_str = build_substring_local(value, start, i)
if i + 1 < len && char_at(value, i) == 'v' {
let unit = char_at(value, i + 1)
if unit == 'w' || unit == 'h' {
let num = parse_simple_number(num_str)
let val = if unit == 'w' {
num * vw / 100.0
} else {
num * vh / 100.0
}
buf.write_string(val.to_string())
i = i + 2
continue
}
}
buf.write_string(num_str)
continue
}
buf.write_char(c)
i = i + 1
}
buf.to_string()
}
///|
fn match_at(s : String, idx : Int, pat : String) -> Bool {
if idx + pat.length() > s.length() {
return false
}
for i in 0.. String? {
let len = svg.length()
let mut end = len
for i in 0..' {
end = i
break
}
}
let mut i = 0
while i < end {
if match_at(svg, i, name) {
let mut j = i + name.length()
while j < end && is_space_char(char_at(svg, j)) {
j = j + 1
}
if j < end && char_at(svg, j) == '=' {
j = j + 1
while j < end && is_space_char(char_at(svg, j)) {
j = j + 1
}
if j >= end {
return None
}
let quote = char_at(svg, j)
if quote == '"' || quote == '\'' {
j = j + 1
let start = j
while j < end && char_at(svg, j) != quote {
j = j + 1
}
if j > start {
return Some(build_substring_local(svg, start, j))
}
return None
} else {
let start = j
while j < end &&
!is_space_char(char_at(svg, j)) &&
char_at(svg, j) != '>' {
j = j + 1
}
if j > start {
return Some(build_substring_local(svg, start, j))
}
return None
}
}
}
i = i + 1
}
None
}
///|
fn extract_svg_size(svg : String) -> (Double, Double) {
let width = match find_attr_value(svg, "width") {
Some(v) => parse_length_simple(v)
None => 0.0
}
let height = match find_attr_value(svg, "height") {
Some(v) => parse_length_simple(v)
None => 0.0
}
(width, height)
}
///|
fn render_image(
x : Double,
y : Double,
width : Double,
height : Double,
href : String,
transform : Transform,
ctx : RenderState,
preserve_aspect_ratio : PreserveAspectRatio,
preserve_aspect_ratio_is_set : Bool,
sampling : ImageSampling,
opacity : Double,
) -> Unit {
if width <= 0.0 || height <= 0.0 {
return
}
if href.length() == 0 {
return
}
let embedded_svg = match decode_data_svg(href) {
Some(svg) => Some(svg)
None => {
let trimmed = trim_string(href)
if string_starts_with(trimmed, "