///|
/// How the browser fits a diagram's user space into the page. A point
/// in user units is scaled by `scale` and moved by `dx` and `dy` to its
/// place inside the `` element, whose top left corner is at `left`
/// and `top` in the viewport; together they are `getScreenCTM()`.
pub struct Placement {
  scale : Double
  dx : Double
  dy : Double
  left : Double
  top : Double
}

///|
/// The placement of a diagram whose `` has `width="100%"`, the view
/// box `0 0 width height` and a `max-width` style (in pixels), fitted
/// the way `preserveAspectRatio` does by default: scaled uniformly and
/// centred.
///
/// The element is as wide as its container allows, at most `max_width`,
/// and as tall as the view box's proportions make it, in whole layout
/// units. When that is taller than the viewport has room for, the page
/// gets a scrollbar and the container narrows.
pub fn Context::placement(
  self : Context,
  width : Double,
  height : Double,
  max_width~ : Double,
) -> Placement {
  let page = self.page
  let mut container = self.container_width()
  // Below the ``, an inline element, the line it stands on keeps
  // room for descenders in the font of the block mermaid renders into.
  let line_descent = match self.config.text("fontFamily") {
    Some(family) => @font.Stack::for_family(family).line_descent(16.0)
    // No font of its own: the default serif of the page.
    None => 4.0
  }
  let max_width = floor_layout_unit(max_width)
  let used = (container : Double) => {
    let used_width = container.min(max_width)
    (used_width, floor_layout_unit(used_width * height / width))
  }
  // The page scrolls when its content, rounded to whole pixels, is
  // taller than the viewport; the scrollbar stays even if the narrower
  // content would fit again.
  if (used(container).1 + line_descent + 2.0 * page.margin + 0.5).floor() >
    page.viewport_height {
    container -= page.scrollbar
  }
  let (physical_width, physical_height) = used(container)
  // The view box is read into single precision; the rest is Blink's
  // `SVGPreserveAspectRatio::ComputeTransform` in doubles.
  let logical_width = Float::from_double(width).to_double()
  let logical_height = Float::from_double(height).to_double()
  if logical_width / logical_height < physical_width / physical_height {
    let scale = physical_height / logical_height
    let shift = -(logical_width -
      physical_width * logical_height / physical_height) /
      2.0
    { scale, dx: shift * scale, dy: 0.0, left: page.margin, top: page.margin, }
  } else {
    let scale = physical_width / logical_width
    let shift = -(logical_height -
      physical_height * logical_width / physical_width) /
      2.0
    { scale, dx: 0.0, dy: shift * scale, left: page.margin, top: page.margin, }
  }
}