///|
///|
/// `gen_path` "renders" the provided text string into an SVG path
/// using the provided font information. It performs the necessary
/// path translations in order to combine the individual glyphs into
/// a "super glyph" that can be rendered as a whole.
///
/// Note that `gen_path` operates on the string as a left-justified whole
/// and aligns the entire path relative to `alignment` accordingly.
///
/// If you want multiple lines centered horizontally, use `gen_paths` instead.
///
/// Note that the SVG standard states that `positive-Y` is "down" with origin
/// coordinates in the upper-left, however, all SVG fonts appear to have their
/// origin in the lower left with `positive-Y` moving "up". This package
/// attempts to convert the SVG paths such that the font is equally readable
/// in both the `y_up=false` SVG canvas environment or in a 3D `y_up=true`
/// rendering environment.
pub fn Font::gen_path(
self : Font,
text : String,
alignment? : Alignment = Unchanged,
y_up? : Bool = false,
) -> Glyph raise FontError {
let mut xmin = 0.0
let mut ymin = 0.0
let mut xmax = 0.0
let mut ymax = 0.0
let mut x = 0.0
let mut y = 0.0
let y_scale = if y_up { 1.0 } else { -1.0 }
let char = Buffer()
let gerber_lp = Buffer()
let d = Buffer()
//
let mut first = true
for c in text {
match self.glyphs.get(c.to_string()) {
Some(glyph) => {
char.write_string_utf16le(glyph.char)
gerber_lp.write_string_utf16le(glyph.gerber_lp)
let (glyph_ymin, glyph_ymax) = if y_scale < 0 {
(-glyph.ymax, -glyph.ymin)
} else {
(glyph.ymin, glyph.ymax)
}
if first {
xmin = glyph.xmin
xmax = glyph.xmax
ymin = glyph_ymin
ymax = glyph_ymax
first = false
} else {
if x + glyph.xmin < xmin {
xmin = x + glyph.xmin
}
if x + glyph.xmax > xmax {
xmax = x + glyph.xmax
}
if y + glyph_ymin < ymin {
ymin = y + glyph_ymin
}
if y + glyph_ymax > ymax {
ymax = y + glyph_ymax
}
}
d.write_string_utf16le(translate_path(glyph.d, x, y, invert_y=!y_up))
if glyph.horiz_adv_x > 0.0 {
x = x + glyph.horiz_adv_x
} else {
x = x + self.horiz_adv_x
}
}
None => {
char.write_char_utf16le(c)
match c {
'\n' => { // advance line and return to far left
x = 0.0
y = y - y_scale * (self.units_per_em - self.descent)
}
'\r' => // advance line only
y = y - y_scale * (self.units_per_em - self.descent)
// anything else - advance to the right by default width
_ => x = x + self.horiz_adv_x
}
}
}
}
let char = char.contents().to_unchecked_string()
let gerber_lp = gerber_lp.contents().to_unchecked_string()
let mut d = d.contents().to_unchecked_string()
let topy = if y_up { ymax } else { ymin }
let boty = if y_up { ymin } else { ymax }
let (dx, dy) = match alignment {
Unchanged => (0.0, 0.0)
TopLeft => (-xmin, -topy)
TopCenter => (-(xmin + xmax) / 2, -topy)
TopRight => (-xmax, -topy)
CenterLeft => (-xmin, -(boty + topy) / 2)
Center => (-(xmin + xmax) / 2, -(boty + topy) / 2)
CenterRight => (-xmax, -(boty + topy) / 2)
BaselineLeft => (-xmin, 0)
BaselineCenter => (-(xmin + xmax) / 2, 0)
BaselineRight => (-xmax, 0)
BottomLeft => (-xmin, -boty)
BottomCenter => (-(xmin + xmax) / 2, -boty)
BottomRight => (-xmax, -boty)
RatioXY(rx, ry) => (-mix(xmin, xmax, rx), -mix(boty, topy, 1.0 - ry))
}
if alignment != Unchanged {
d = translate_path(d, dx, dy)
xmin += dx
ymin += dy
xmax += dx
ymax += dy
}
{ char, horiz_adv_x: 0, gerber_lp, d, xmin, ymin, xmax, ymax }
}
///|
fn mix(a : Double, b : Double, t : Double) -> Double {
(1.0 - t) * a + t * b
}