use pdf_writer::Content;
const KAPPA: f64 = 0.552_284_8;
pub(super) fn rounded_rect_path(
content: &mut Content,
x: f64,
y: f64,
w: f64,
h: f64,
radii: [f64; 4],
) {
if !(w > 0.0 && h > 0.0) {
return;
}
let half_min = (w / 2.0).min(h / 2.0);
let tl = radii[0].max(0.0).min(half_min);
let tr = radii[1].max(0.0).min(half_min);
let br = radii[2].max(0.0).min(half_min);
let bl = radii[3].max(0.0).min(half_min);
let ktl = (KAPPA * tl) as f32;
let ktr = (KAPPA * tr) as f32;
let kbr = (KAPPA * br) as f32;
let kbl = (KAPPA * bl) as f32;
let (tl, tr, br, bl) = (tl as f32, tr as f32, br as f32, bl as f32);
let (x, y, w, h) = (x as f32, y as f32, w as f32, h as f32);
content.move_to(x + tl, y);
content.line_to(x + w - tr, y);
if tr > 0.0 {
content.cubic_to(x + w - tr + ktr, y, x + w, y + tr - ktr, x + w, y + tr);
}
content.line_to(x + w, y + h - br);
if br > 0.0 {
content.cubic_to(
x + w,
y + h - br + kbr,
x + w - br + kbr,
y + h,
x + w - br,
y + h,
);
}
content.line_to(x + bl, y + h);
if bl > 0.0 {
content.cubic_to(x + bl - kbl, y + h, x, y + h - bl + kbl, x, y + h - bl);
}
content.line_to(x, y + tl);
if tl > 0.0 {
content.cubic_to(x, y + tl - ktl, x + tl - ktl, y, x + tl, y);
}
content.close_path();
}
pub(super) fn ellipse_path(
content: &mut Content,
x: f64,
y: f64,
w: f64,
h: f64,
rx_override: Option<f64>,
ry_override: Option<f64>,
) {
if !(w > 0.0 && h > 0.0) {
return;
}
let rx = rx_override.unwrap_or(w / 2.0);
let ry = ry_override.unwrap_or(h / 2.0);
let cx = x + w / 2.0;
let cy = y + h / 2.0;
let kx = (KAPPA * rx) as f32;
let ky = (KAPPA * ry) as f32;
let (cx, cy, rx, ry) = (cx as f32, cy as f32, rx as f32, ry as f32);
content.move_to(cx + rx, cy);
content.cubic_to(cx + rx, cy + ky, cx + kx, cy + ry, cx, cy + ry); content.cubic_to(cx - kx, cy + ry, cx - rx, cy + ky, cx - rx, cy); content.cubic_to(cx - rx, cy - ky, cx - kx, cy - ry, cx, cy - ry); content.cubic_to(cx + kx, cy - ry, cx + rx, cy - ky, cx + rx, cy); content.close_path();
}
pub(super) fn poly_path(content: &mut Content, points: &[f64], closed: bool) -> bool {
let (Some(&x0), Some(&y0)) = (points.first(), points.get(1)) else {
return false;
};
content.move_to(x0 as f32, y0 as f32);
let mut i = 2;
while i + 1 < points.len() {
let (Some(&px), Some(&py)) = (points.get(i), points.get(i + 1)) else {
break;
};
content.line_to(px as f32, py as f32);
i += 2;
}
if closed {
content.close_path();
}
true
}
pub(super) struct GlyphPen<'a> {
content: &'a mut Content,
origin_x: f32,
baseline_y: f32,
scale: f32,
cur: (f32, f32),
}
impl<'a> GlyphPen<'a> {
pub(super) fn new(
content: &'a mut Content,
origin_x: f32,
baseline_y: f32,
scale: f32,
) -> Self {
Self {
content,
origin_x,
baseline_y,
scale,
cur: (0.0, 0.0),
}
}
#[inline]
fn map(&self, fx: f32, fy: f32) -> (f32, f32) {
(
self.origin_x + fx * self.scale,
self.baseline_y - fy * self.scale,
)
}
}
impl ttf_parser::OutlineBuilder for GlyphPen<'_> {
fn move_to(&mut self, x: f32, y: f32) {
let (px, py) = self.map(x, y);
self.content.move_to(px, py);
self.cur = (px, py);
}
fn line_to(&mut self, x: f32, y: f32) {
let (px, py) = self.map(x, y);
self.content.line_to(px, py);
self.cur = (px, py);
}
fn quad_to(&mut self, x1: f32, y1: f32, x: f32, y: f32) {
let (cx, cy) = self.map(x1, y1);
let (px, py) = self.map(x, y);
let (p0x, p0y) = self.cur;
let c1x = p0x + 2.0 / 3.0 * (cx - p0x);
let c1y = p0y + 2.0 / 3.0 * (cy - p0y);
let c2x = px + 2.0 / 3.0 * (cx - px);
let c2y = py + 2.0 / 3.0 * (cy - py);
self.content.cubic_to(c1x, c1y, c2x, c2y, px, py);
self.cur = (px, py);
}
fn curve_to(&mut self, x1: f32, y1: f32, x2: f32, y2: f32, x: f32, y: f32) {
let (c1x, c1y) = self.map(x1, y1);
let (c2x, c2y) = self.map(x2, y2);
let (px, py) = self.map(x, y);
self.content.cubic_to(c1x, c1y, c2x, c2y, px, py);
self.cur = (px, py);
}
fn close(&mut self) {
self.content.close_path();
}
}