use super::{TextMetrics, TextStyle, WrapMode};
pub(crate) const MERMAID_CREATE_TEXT_DEFAULT_WIDTH_PX: f64 = 200.0;
pub trait TextMeasurer {
#[doc(hidden)]
#[allow(private_interfaces)]
fn builtin_operation_carrier(
&self,
_operation: crate::environment::TextMeasurementOperation,
) -> Option<crate::environment::BuiltinTextMeasurementOperationCarrier> {
None
}
#[doc(hidden)]
#[allow(private_interfaces)]
fn begin_svg_text_computed_length(
&self,
_style: &TextStyle,
) -> Option<crate::environment::BuiltinSvgComputedLength> {
None
}
fn measure(&self, text: &str, style: &TextStyle) -> TextMetrics;
fn measure_svg_text_computed_length_px(&self, text: &str, style: &TextStyle) -> f64 {
self.measure_svg_simple_text_bbox_width_px(text, style)
}
fn measure_svg_text_bbox_x(&self, text: &str, style: &TextStyle) -> (f64, f64) {
let m = self.measure(text, style);
let half = (m.width.max(0.0)) / 2.0;
(half, half)
}
fn measure_svg_text_bbox_x_with_ascii_overhang(
&self,
text: &str,
style: &TextStyle,
) -> (f64, f64) {
self.measure_svg_text_bbox_x(text, style)
}
fn measure_svg_title_bbox_x(&self, text: &str, style: &TextStyle) -> (f64, f64) {
self.measure_svg_text_bbox_x(text, style)
}
fn measure_svg_simple_text_bbox_width_px(&self, text: &str, style: &TextStyle) -> f64 {
let (l, r) = self.measure_svg_title_bbox_x(text, style);
(l + r).max(0.0)
}
fn measure_svg_raw_text_bbox_width_px(&self, text: &str, style: &TextStyle) -> f64 {
self.measure_svg_simple_text_bbox_width_px(text, style)
}
fn measure_svg_raw_text_bbox_height_px(&self, text: &str, style: &TextStyle) -> f64 {
self.measure(text, style).height.max(0.0)
}
fn measure_svg_text_bounding_client_rect_width_px(&self, text: &str, style: &TextStyle) -> f64 {
self.measure_svg_raw_text_bbox_width_px(text, style)
}
fn measure_svg_tspan_text_bbox_width_px(&self, text: &str, style: &TextStyle) -> f64 {
self.measure_svg_raw_text_bbox_width_px(text, style)
}
fn measure_svg_tspan_text_bbox_height_px(&self, text: &str, style: &TextStyle) -> f64 {
self.measure(text, style).height.max(0.0)
}
fn measure_svg_create_text_bbox_y_offset_px(&self, _text: &str, _style: &TextStyle) -> f64 {
0.0
}
fn measure_svg_create_text_middle_bbox_y_offset_px(
&self,
_text: &str,
_style: &TextStyle,
) -> f64 {
0.0
}
fn measure_svg_simple_text_bbox_width_for_wrap_px(&self, text: &str, style: &TextStyle) -> f64 {
self.measure_svg_simple_text_bbox_width_px(text, style)
}
fn measure_mermaid_calculate_text_dimensions(
&self,
text: &str,
style: &TextStyle,
) -> TextMetrics {
TextMetrics {
width: self.measure_svg_simple_text_bbox_width_for_wrap_px(text, style),
height: self.measure_svg_simple_text_bbox_height_px(text, style),
line_count: 1,
}
}
fn measure_canvas_text_width_px(&self, text: &str, style: &TextStyle) -> f64 {
self.measure_svg_text_computed_length_px(text, style)
}
fn measure_svg_simple_text_bbox_height_px(&self, text: &str, style: &TextStyle) -> f64 {
let m = self.measure(text, style);
m.height.max(0.0)
}
fn measure_wrapped(
&self,
text: &str,
style: &TextStyle,
max_width: Option<f64>,
wrap_mode: WrapMode,
) -> TextMetrics {
let _ = max_width;
let _ = wrap_mode;
self.measure(text, style)
}
fn measure_wrapped_with_raw_width(
&self,
text: &str,
style: &TextStyle,
max_width: Option<f64>,
wrap_mode: WrapMode,
) -> (TextMetrics, Option<f64>) {
(
self.measure_wrapped(text, style, max_width, wrap_mode),
None,
)
}
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub(crate) struct MermaidTextDimensions {
pub(crate) width: i64,
pub(crate) height: i64,
pub(crate) line_height: i64,
}
fn measure_mermaid_text_dimensions_for_family(
measurer: &dyn TextMeasurer,
text: &str,
style: &TextStyle,
) -> MermaidTextDimensions {
if text.is_empty() {
return MermaidTextDimensions::default();
}
let mut dimensions = MermaidTextDimensions::default();
for line in super::split_html_br_lines(text) {
let measured_line = if line.is_empty() { "\u{200b}" } else { line };
let measured = measurer.measure_mermaid_calculate_text_dimensions(measured_line, style);
let width = measured.width.max(0.0).round() as i64;
let height = measured.height.max(0.0).round() as i64;
dimensions.width = dimensions.width.max(width);
dimensions.height += height;
dimensions.line_height = dimensions.line_height.max(height);
}
dimensions
}
pub(crate) fn measure_mermaid_text_dimensions(
measurer: &dyn TextMeasurer,
text: &str,
configured_style: &TextStyle,
) -> MermaidTextDimensions {
let mut sans_style = configured_style.clone();
sans_style.font_family = Some("sans-serif".to_string());
let sans = measure_mermaid_text_dimensions_for_family(measurer, text, &sans_style);
let configured = measure_mermaid_text_dimensions_for_family(measurer, text, configured_style);
if sans.width > configured.width
&& sans.height > configured.height
&& sans.line_height > configured.line_height
{
sans
} else {
configured
}
}