///|
/// SVG DOM API
///
/// Provides SVG-specific DOM methods:
/// - getBBox() for bounding box retrieval
/// - Factory methods for SVG geometry objects
// =============================================================================
// getBBox Options
// =============================================================================
///|
/// Options for getBBox() method
/// Corresponds to SVGBoundingBoxOptions in the spec
pub struct GetBBoxOptions {
fill : Bool // Include fill geometry (default true)
stroke : Bool // Include stroke geometry (default false)
markers : Bool // Include markers (default false)
clipped : Bool // Apply clipping (default false)
} derive(Eq, Debug)
///|
/// Default getBBox options (fill only)
pub fn GetBBoxOptions::default() -> GetBBoxOptions {
{ fill: true, stroke: false, markers: false, clipped: false }
}
///|
/// Create getBBox options with all geometry
pub fn GetBBoxOptions::all() -> GetBBoxOptions {
{ fill: true, stroke: true, markers: true, clipped: false }
}
// =============================================================================
// DomTree SVG Extensions
// =============================================================================
///|
/// Get the bounding box of an SVG element
/// Returns the tight bounding box in user space coordinates
pub fn DomTree::get_bbox(
self : DomTree,
node_id : NodeId,
options? : GetBBoxOptions,
) -> SVGRect? {
let _opts = options.unwrap_or(GetBBoxOptions::default())
// Get cached rect from layout
match self.get_cached_rect(node_id) {
Some(rect) => Some(SVGRect::from_rect(rect))
None => None
}
}
///|
/// Get the Current Transformation Matrix (CTM)
/// Returns the transformation from user space to viewport
pub fn DomTree::get_ctm(self : DomTree, node_id : NodeId) -> SVGMatrix? {
// For now, return identity matrix if node exists
// Full implementation would traverse parent transforms
match self.get_node_info(node_id) {
Ok(_) => Some(SVGMatrix::identity())
Err(_) => None
}
}
///|
/// Get the Screen CTM
/// Returns the transformation from user space to screen pixels
pub fn DomTree::get_screen_ctm(self : DomTree, node_id : NodeId) -> SVGMatrix? {
// For now, same as getCTM
// Full implementation would include viewport transforms
self.get_ctm(node_id)
}
// =============================================================================
// Factory Methods (SVGSVGElement equivalents)
// =============================================================================
///|
/// Create a new SVGRect object
/// Equivalent to SVGSVGElement.createSVGRect()
pub fn create_svg_rect() -> SVGRect {
SVGRect::empty()
}
///|
/// Create a new SVGRect with values
pub fn create_svg_rect_with(
x : Double,
y : Double,
width : Double,
height : Double,
) -> SVGRect {
SVGRect::new(x, y, width, height)
}
///|
/// Create a new SVGPoint object
/// Equivalent to SVGSVGElement.createSVGPoint()
pub fn create_svg_point() -> SVGPoint {
SVGPoint::origin()
}
///|
/// Create a new SVGPoint with values
pub fn create_svg_point_with(x : Double, y : Double) -> SVGPoint {
SVGPoint::new(x, y)
}
///|
/// Create a new SVGMatrix object (identity matrix)
/// Equivalent to SVGSVGElement.createSVGMatrix()
pub fn create_svg_matrix() -> SVGMatrix {
SVGMatrix::identity()
}
///|
/// Create a new SVGMatrix with values
pub fn create_svg_matrix_with(
a : Double,
b : Double,
c : Double,
d : Double,
e : Double,
f : Double,
) -> SVGMatrix {
SVGMatrix::new(a, b, c, d, e, f)
}
// =============================================================================
// SVGNumber (simple wrapper for animated numbers)
// =============================================================================
///|
/// SVGNumber - represents an animated number value
pub struct SVGNumber {
value : Double
} derive(Eq, Debug)
///|
/// Create a new SVGNumber
pub fn SVGNumber::new(value : Double) -> SVGNumber {
{ value, }
}
///|
/// Create a zero SVGNumber
pub fn create_svg_number() -> SVGNumber {
SVGNumber::new(0.0)
}
// =============================================================================
// SVGLength (length with unit)
// =============================================================================
///|
/// SVG Length unit types
pub enum SVGLengthType {
Unknown
Number // Unitless number
Percentage
Ems
Exs
Px
Cm
Mm
In
Pt
Pc
} derive(Eq, Debug)
///|
/// SVGLength - represents a length value with unit
pub struct SVGLength {
unit_type : SVGLengthType
value : Double // Value in user units
value_in_specified_units : Double // Value in specified units
} derive(Eq, Debug)
///|
/// Create a new SVGLength (unitless)
pub fn SVGLength::new(value : Double) -> SVGLength {
{ unit_type: Number, value, value_in_specified_units: value }
}
///|
/// Create a zero SVGLength
pub fn create_svg_length() -> SVGLength {
SVGLength::new(0.0)
}
///|
/// Convert SVGLength to user units (pixels)
pub fn SVGLength::to_user_units(
self : SVGLength,
dpi? : Double,
font_size? : Double,
) -> Double {
let dpi_val = dpi.unwrap_or(96.0)
let font_size_val = font_size.unwrap_or(16.0)
match self.unit_type {
Unknown | Number | Px => self.value_in_specified_units
Percentage => self.value_in_specified_units / 100.0 // Needs context
Ems => self.value_in_specified_units * font_size_val
Exs => self.value_in_specified_units * font_size_val * 0.5 // Approximation
Pt => self.value_in_specified_units * dpi_val / 72.0
Pc => self.value_in_specified_units * dpi_val / 6.0
In => self.value_in_specified_units * dpi_val
Cm => self.value_in_specified_units * dpi_val / 2.54
Mm => self.value_in_specified_units * dpi_val / 25.4
}
}
// =============================================================================
// SVGAngle (angle with unit)
// =============================================================================
///|
/// SVG Angle unit types
pub enum SVGAngleType {
Unknown
Unspecified // Unitless (degrees)
Deg
Rad
Grad
Turn
} derive(Eq, Debug)
///|
/// SVGAngle - represents an angle value with unit
pub struct SVGAngle {
unit_type : SVGAngleType
value : Double // Value in degrees
value_in_specified_units : Double // Value in specified units
} derive(Eq, Debug)
///|
/// Create a new SVGAngle (degrees)
pub fn SVGAngle::new(value : Double) -> SVGAngle {
{ unit_type: Deg, value, value_in_specified_units: value }
}
///|
/// Create a zero SVGAngle
pub fn create_svg_angle() -> SVGAngle {
SVGAngle::new(0.0)
}
///|
/// Convert SVGAngle to radians
pub fn SVGAngle::to_radians(self : SVGAngle) -> Double {
let pi = 3.14159265358979323846
match self.unit_type {
Unknown | Unspecified | Deg => self.value * pi / 180.0
Rad => self.value_in_specified_units
Grad => self.value_in_specified_units * pi / 200.0
Turn => self.value_in_specified_units * 2.0 * pi
}
}
///|
/// Convert SVGAngle to degrees
pub fn SVGAngle::to_degrees(self : SVGAngle) -> Double {
let pi = 3.14159265358979323846
match self.unit_type {
Unknown | Unspecified | Deg => self.value_in_specified_units
Rad => self.value_in_specified_units * 180.0 / pi
Grad => self.value_in_specified_units * 0.9
Turn => self.value_in_specified_units * 360.0
}
}
// =============================================================================
// SVGTransform (single transform)
// =============================================================================
///|
/// SVG Transform types
pub enum SVGTransformType {
Unknown
Matrix
Translate
Scale
Rotate
SkewX
SkewY
} derive(Eq, Debug)
///|
/// SVGTransform - represents a single transformation
pub struct SVGTransform {
transform_type : SVGTransformType
matrix : SVGMatrix
angle : Double // For rotate, skewX, skewY
} derive(Eq, Debug)
///|
pub impl Show for GetBBoxOptions with fn output(self, logger) {
logger.write_string("{fill: \{self.fill}, stroke: \{self.stroke}, ")
logger.write_string("markers: \{self.markers}, clipped: \{self.clipped}}")
}
///|
pub impl Show for SVGNumber with fn output(self, logger) {
logger.write_string("{value: \{self.value}}")
}
///|
pub impl Show for SVGLengthType with fn output(self, logger) {
let name = match self {
Unknown => "Unknown"
Number => "Number"
Percentage => "Percentage"
Ems => "Ems"
Exs => "Exs"
Px => "Px"
Cm => "Cm"
Mm => "Mm"
In => "In"
Pt => "Pt"
Pc => "Pc"
}
logger.write_string(name)
}
///|
pub impl Show for SVGLength with fn output(self, logger) {
logger.write_string("{unit_type: \{self.unit_type}, value: \{self.value}, ")
logger.write_string(
"value_in_specified_units: \{self.value_in_specified_units}}",
)
}
///|
pub impl Show for SVGAngleType with fn output(self, logger) {
let name = match self {
Unknown => "Unknown"
Unspecified => "Unspecified"
Deg => "Deg"
Rad => "Rad"
Grad => "Grad"
Turn => "Turn"
}
logger.write_string(name)
}
///|
pub impl Show for SVGAngle with fn output(self, logger) {
logger.write_string("{unit_type: \{self.unit_type}, value: \{self.value}, ")
logger.write_string(
"value_in_specified_units: \{self.value_in_specified_units}}",
)
}
///|
pub impl Show for SVGTransformType with fn output(self, logger) {
let name = match self {
Unknown => "Unknown"
Matrix => "Matrix"
Translate => "Translate"
Scale => "Scale"
Rotate => "Rotate"
SkewX => "SkewX"
SkewY => "SkewY"
}
logger.write_string(name)
}
///|
pub impl Show for SVGTransform with fn output(self, logger) {
logger.write_string(
"{transform_type: \{self.transform_type}, matrix: \{self.matrix}, angle: \{self.angle}}",
)
}
///|
/// Create an SVGTransform from a matrix
pub fn SVGTransform::from_matrix(matrix : SVGMatrix) -> SVGTransform {
{ transform_type: Matrix, matrix, angle: 0.0 }
}
///|
/// Create a translate transform
pub fn SVGTransform::translate(tx : Double, ty : Double) -> SVGTransform {
{
transform_type: Translate,
matrix: SVGMatrix::translate(tx, ty),
angle: 0.0,
}
}
///|
/// Create a scale transform
pub fn SVGTransform::scale(sx : Double, sy : Double) -> SVGTransform {
{ transform_type: Scale, matrix: SVGMatrix::scale(sx, sy), angle: 0.0 }
}
///|
/// Create a rotate transform (angle in degrees)
pub fn SVGTransform::rotate(
angle : Double,
cx? : Double,
cy? : Double,
) -> SVGTransform {
let cx_val = cx.unwrap_or(0.0)
let cy_val = cy.unwrap_or(0.0)
let rad = angle * 3.14159265358979323846 / 180.0
// Rotate around (cx, cy): translate(-cx,-cy) * rotate * translate(cx,cy)
let matrix = if cx_val == 0.0 && cy_val == 0.0 {
SVGMatrix::rotate(rad)
} else {
SVGMatrix::translate(cx_val, cy_val)
.multiply(SVGMatrix::rotate(rad))
.multiply(SVGMatrix::translate(-cx_val, -cy_val))
}
{ transform_type: Rotate, matrix, angle }
}
///|
/// Create a skewX transform (angle in degrees)
pub fn SVGTransform::skew_x(angle : Double) -> SVGTransform {
let rad = angle * 3.14159265358979323846 / 180.0
{ transform_type: SkewX, matrix: SVGMatrix::skew_x(rad), angle }
}
///|
/// Create a skewY transform (angle in degrees)
pub fn SVGTransform::skew_y(angle : Double) -> SVGTransform {
let rad = angle * 3.14159265358979323846 / 180.0
{ transform_type: SkewY, matrix: SVGMatrix::skew_y(rad), angle }
}
///|
/// Create a new SVGTransform
pub fn create_svg_transform() -> SVGTransform {
SVGTransform::from_matrix(SVGMatrix::identity())
}
///|
/// Create a translation SVGTransform
pub fn create_svg_transform_translate(tx : Double, ty : Double) -> SVGTransform {
SVGTransform::translate(tx, ty)
}
///|
/// Create a scale SVGTransform
pub fn create_svg_transform_scale(sx : Double, sy : Double) -> SVGTransform {
SVGTransform::scale(sx, sy)
}
///|
/// Create a rotation SVGTransform
pub fn create_svg_transform_rotate(angle : Double) -> SVGTransform {
SVGTransform::rotate(angle)
}