///|
fn is_paint_server_context(parent_tag : String) -> Bool {
parent_tag == "svg" || parent_tag == "defs" || parent_tag == "symbol"
}
///|
fn parse_pattern_from_attrs(
attrs : Array[(String, String)],
ctx : SVGParseContext,
parent_tag : String,
) -> Unit {
if !is_paint_server_context(parent_tag) {
return
}
parse_pattern_with_content(attrs, [], ctx)
}
///|
fn parse_pattern(
parser : SVGParser,
attrs : Array[(String, String)],
ctx : SVGParseContext,
tag_name : String,
parent_tag : String,
) -> Unit {
if !is_paint_server_context(parent_tag) {
skip_to_end_tag(parser, tag_name)
return
}
let temp = SVGNode::new(Group)
apply_attributes(temp, attrs)
let style = resolve_element_computed_style(
temp,
tag_name,
attrs,
ctx,
ctx.current_style(),
true,
)
parse_children(parser, temp, tag_name, attrs, ctx, style)
parse_pattern_with_content(attrs, temp.children, ctx)
}
///|
fn parse_marker_from_attrs(
attrs : Array[(String, String)],
ctx : SVGParseContext,
_parent_tag : String,
) -> Unit {
let node = SVGNode::new(Group)
apply_attributes(node, attrs)
let _ = resolve_element_computed_style(
node,
"marker",
attrs,
ctx,
ctx.current_style(),
true,
)
parse_marker_with_content(attrs, node, ctx)
}
///|
fn parse_marker(
parser : SVGParser,
attrs : Array[(String, String)],
ctx : SVGParseContext,
tag_name : String,
_parent_tag : String,
) -> Unit {
let temp = SVGNode::new(Group)
apply_attributes(temp, attrs)
let style = resolve_element_computed_style(
temp,
tag_name,
attrs,
ctx,
ctx.current_style(),
true,
)
parse_children(parser, temp, tag_name, attrs, ctx, style)
parse_marker_with_content(attrs, temp, ctx)
}
///|
fn parse_marker_with_content(
attrs : Array[(String, String)],
content : SVGNode,
ctx : SVGParseContext,
) -> Unit {
let id = match get_attr(attrs, "id") {
Some(v) => v
None => ""
}
if id.length() == 0 {
return
}
let mut ref_x = 0.0
let mut ref_y = 0.0
let mut marker_width = 3.0
let mut marker_height = 3.0
let mut marker_units = MarkerUnits::StrokeWidth
let mut orient = MarkerOrient::Angle(0.0)
let mut view_box : ViewBox? = None
let mut preserve_aspect_ratio = PreserveAspectRatio::default()
let mut overflow_visible = false
if get_attr(attrs, "refX") is Some(v) {
ref_x = parse_length(v)
}
if get_attr(attrs, "refY") is Some(v) {
ref_y = parse_length(v)
}
if get_attr(attrs, "markerWidth") is Some(v) {
marker_width = parse_length(v)
}
if get_attr(attrs, "markerHeight") is Some(v) {
marker_height = parse_length(v)
}
if get_attr(attrs, "markerUnits") is Some(v) {
marker_units = parse_marker_units(v)
}
if get_attr(attrs, "orient") is Some(v) {
orient = parse_marker_orient(v)
}
if get_attr(attrs, "viewBox") is Some(v) {
view_box = parse_view_box(v)
}
if get_attr(attrs, "preserveAspectRatio") is Some(v) {
preserve_aspect_ratio = parse_preserve_aspect_ratio(v)
}
if get_attr(attrs, "overflow") is Some(v) {
overflow_visible = trim_string(v) == "visible"
}
content.viewport_width = Some(marker_width)
content.viewport_height = Some(marker_height)
// Marker viewBox is handled by marker transform, not by content node.
content.view_box = None
content.preserve_aspect_ratio = PreserveAspectRatio::default()
content.preserve_aspect_ratio_is_set = false
// Marker viewport clipping is handled at render time.
content.clip_overflow = false
let clip_overflow = !overflow_visible
let marker = {
..Marker::new(id, content),
ref_x,
ref_y,
marker_width,
marker_height,
marker_units,
orient,
view_box,
preserve_aspect_ratio,
clip_overflow,
}
ctx.markers.add(marker)
}
///|
fn parse_marker_units(value : String) -> MarkerUnits {
match trim_string(value) {
"userSpaceOnUse" => UserSpaceOnUse_
"strokeWidth" => StrokeWidth
_ => StrokeWidth
}
}
///|
fn parse_marker_orient(value : String) -> MarkerOrient {
let v = trim_string(value)
if v == "auto" {
Auto
} else if v == "auto-start-reverse" {
AutoStartReverse
} else {
let angle = if string_ends_with(v, "deg") {
parse_number(build_substring(v, 0, v.length() - 3))
} else {
parse_number(v)
}
Angle(angle)
}
}
///|
fn parse_pattern_with_content(
attrs : Array[(String, String)],
content : Array[SVGNode],
ctx : SVGParseContext,
) -> Unit {
let id = match get_attr(attrs, "id") {
Some(v) => v
None => ""
}
if id.length() == 0 {
return
}
let reference = match get_href_attr(attrs) {
Some(v) => parse_url_ref(v)
None => None
}
let mut x = 0.0
let mut y = 0.0
let mut width = 0.0
let mut height = 0.0
if get_attr(attrs, "x") is Some(v) {
let (val, _) = parse_length_or_percent(v)
x = val
}
if get_attr(attrs, "y") is Some(v) {
let (val, _) = parse_length_or_percent(v)
y = val
}
if get_attr(attrs, "width") is Some(v) {
let (val, _) = parse_length_or_percent(v)
width = val
}
if get_attr(attrs, "height") is Some(v) {
let (val, _) = parse_length_or_percent(v)
height = val
}
let pattern_units = match get_attr(attrs, "patternUnits") {
Some(v) => parse_pattern_units(v)
None => ObjectBoundingBox
}
let pattern_content_units = match get_attr(attrs, "patternContentUnits") {
Some(v) => parse_pattern_units(v)
None => UserSpaceOnUse
}
let transform = match get_attr(attrs, "patternTransform") {
Some(v) => parse_transform(v)
None => Transform::identity()
}
let view_box = match get_attr(attrs, "viewBox") {
Some(v) => parse_view_box(v)
None => None
}
let preserve_aspect_ratio = match get_attr(attrs, "preserveAspectRatio") {
Some(v) => parse_preserve_aspect_ratio(v)
None => PreserveAspectRatio::default()
}
let pattern = {
..Pattern::new(id, width, height, content),
x,
y,
pattern_units,
pattern_content_units,
transform,
view_box,
preserve_aspect_ratio,
}
let mut specified = 0
if get_attr(attrs, "x") is Some(_) {
specified = specified | 1
}
if get_attr(attrs, "y") is Some(_) {
specified = specified | 2
}
if get_attr(attrs, "width") is Some(_) {
specified = specified | 4
}
if get_attr(attrs, "height") is Some(_) {
specified = specified | 8
}
if get_attr(attrs, "patternUnits") is Some(_) {
specified = specified | 16
}
if get_attr(attrs, "patternContentUnits") is Some(_) {
specified = specified | 32
}
if get_attr(attrs, "patternTransform") is Some(_) {
specified = specified | 64
}
if get_attr(attrs, "viewBox") is Some(_) {
specified = specified | 128
}
if get_attr(attrs, "preserveAspectRatio") is Some(_) {
specified = specified | 256
}
if !content.is_empty() {
specified = specified | 512
}
ctx.patterns.add_definition(pattern, reference, specified)
}