/*
 * Copyright © 2026  Behdad Esfahbod
 *
 *  This is part of HarfBuzz, a text shaping library.
 *
 * Permission is hereby granted, without written agreement and without
 * license or royalty fees, to use, copy, modify, and distribute this
 * software and its documentation for any purpose, provided that the
 * above copyright notice and the following two paragraphs appear in
 * all copies of this software.
 *
 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
 * DAMAGE.
 *
 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 *
 * Author(s): Behdad Esfahbod
 */

#ifndef HB_NO_RASTER_SVG

#include "hb.hh"

#include "hb-raster-svg-parse.hh"

#include <math.h>
bool
hb_raster_svg_parse_transform (hb_svg_str_t s, hb_svg_transform_t *out)
{
  hb_svg_float_parser_t fp (s);

  while (fp.p < fp.end)
  {
    fp.skip_ws_comma ();
    if (fp.p >= fp.end) break;

    const char *start = fp.p;
    while (fp.p < fp.end && *fp.p != '(') fp.p++;
    hb_svg_str_t func_name = {start, (unsigned) (fp.p - start)};

    while (func_name.len && (func_name.data[func_name.len - 1] == ' ' ||
                             func_name.data[func_name.len - 1] == '\t'))
      func_name.len--;

    if (fp.p >= fp.end) break;
    fp.p++;

    hb_svg_transform_t t;

    if (func_name.eq_ascii_ci ("matrix"))
    {
      t.xx = fp.next_float ();
      t.yx = fp.next_float ();
      t.xy = fp.next_float ();
      t.yy = fp.next_float ();
      t.dx = fp.next_float ();
      t.dy = fp.next_float ();
    }
    else if (func_name.eq_ascii_ci ("translate"))
    {
      t.dx = fp.next_float ();
      fp.skip_ws_comma ();
      if (fp.p < fp.end && *fp.p != ')')
        t.dy = fp.next_float ();
    }
    else if (func_name.eq_ascii_ci ("scale"))
    {
      t.xx = fp.next_float ();
      fp.skip_ws_comma ();
      if (fp.p < fp.end && *fp.p != ')')
        t.yy = fp.next_float ();
      else
        t.yy = t.xx;
    }
    else if (func_name.eq_ascii_ci ("rotate"))
    {
      float angle = fp.next_float () * (float) M_PI / 180.f;
      float cs = cosf (angle), sn = sinf (angle);
      fp.skip_ws_comma ();
      if (fp.p < fp.end && *fp.p != ')')
      {
        float cx = fp.next_float ();
        float cy = fp.next_float ();
        t.xx = cs;  t.yx = sn;
        t.xy = -sn; t.yy = cs;
        t.dx = cx - cs * cx + sn * cy;
        t.dy = cy - sn * cx - cs * cy;
      }
      else
      {
        t.xx = cs;  t.yx = sn;
        t.xy = -sn; t.yy = cs;
      }
    }
    else if (func_name.eq_ascii_ci ("skewX"))
    {
      float angle = fp.next_float () * (float) M_PI / 180.f;
      t.xy = tanf (angle);
    }
    else if (func_name.eq_ascii_ci ("skewY"))
    {
      float angle = fp.next_float () * (float) M_PI / 180.f;
      t.yx = tanf (angle);
    }

    while (fp.p < fp.end && *fp.p != ')') fp.p++;
    if (fp.p < fp.end) fp.p++;

    out->multiply (t);
  }
  return true;
}

static void
svg_arc_to_cubics (hb_draw_funcs_t *dfuncs, void *draw_data, hb_draw_state_t *st,
                   float cx, float cy,
                   float rx, float ry,
                   float phi, float theta1, float dtheta)
{
  int n_segs = (int) ceilf (fabsf (dtheta) / ((float) M_PI / 2.f));
  if (n_segs < 1) n_segs = 1;
  float seg_angle = dtheta / n_segs;

  float cos_phi = cosf (phi);
  float sin_phi = sinf (phi);

  for (int i = 0; i < n_segs; i++)
  {
    float t1 = theta1 + i * seg_angle;
    float t2 = t1 + seg_angle;
    float alpha = sinf (seg_angle) *
                  (sqrtf (4.f + 3.f * tanf (seg_angle / 2.f) * tanf (seg_angle / 2.f)) - 1.f) / 3.f;

    float cos_t1 = cosf (t1), sin_t1 = sinf (t1);
    float cos_t2 = cosf (t2), sin_t2 = sinf (t2);

    float e1x = rx * cos_t1, e1y = ry * sin_t1;
    float e2x = rx * cos_t2, e2y = ry * sin_t2;
    float d1x = -rx * sin_t1, d1y = ry * cos_t1;
    float d2x = -rx * sin_t2, d2y = ry * cos_t2;

    float cp1x = e1x + alpha * d1x;
    float cp1y = e1y + alpha * d1y;
    float cp2x = e2x - alpha * d2x;
    float cp2y = e2y - alpha * d2y;

    float r_cp1x = cos_phi * cp1x - sin_phi * cp1y + cx;
    float r_cp1y = sin_phi * cp1x + cos_phi * cp1y + cy;
    float r_cp2x = cos_phi * cp2x - sin_phi * cp2y + cx;
    float r_cp2y = sin_phi * cp2x + cos_phi * cp2y + cy;
    float r_e2x  = cos_phi * e2x  - sin_phi * e2y  + cx;
    float r_e2y  = sin_phi * e2x  + cos_phi * e2y  + cy;

    hb_draw_cubic_to (dfuncs, draw_data, st,
                      r_cp1x, r_cp1y,
                      r_cp2x, r_cp2y,
                      r_e2x, r_e2y);
  }
}

static void
svg_arc_endpoint_to_center (float x1, float y1, float x2, float y2,
                            float rx, float ry, float phi_deg,
                            bool large_arc, bool sweep,
                            float *cx_out, float *cy_out,
                            float *theta1_out, float *dtheta_out,
                            float *rx_out, float *ry_out)
{
  float phi = phi_deg * (float) M_PI / 180.f;
  float cos_phi = cosf (phi), sin_phi = sinf (phi);

  float mx = (x1 - x2) / 2.f;
  float my = (y1 - y2) / 2.f;
  float x1p =  cos_phi * mx + sin_phi * my;
  float y1p = -sin_phi * mx + cos_phi * my;

  rx = fabsf (rx); ry = fabsf (ry);
  if (rx < 1e-10f || ry < 1e-10f)
  {
    *cx_out = (x1 + x2) / 2.f;
    *cy_out = (y1 + y2) / 2.f;
    *theta1_out = 0;
    *dtheta_out = 0;
    *rx_out = rx; *ry_out = ry;
    return;
  }

  float x1p2 = x1p * x1p, y1p2 = y1p * y1p;
  float rx2 = rx * rx, ry2 = ry * ry;
  float lambda = x1p2 / rx2 + y1p2 / ry2;
  if (lambda > 1.f)
  {
    float sl = sqrtf (lambda);
    rx *= sl; ry *= sl;
    rx2 = rx * rx; ry2 = ry * ry;
  }

  float num = rx2 * ry2 - rx2 * y1p2 - ry2 * x1p2;
  float den = rx2 * y1p2 + ry2 * x1p2;
  float sq = (den > 0.f) ? sqrtf (hb_max (num / den, 0.f)) : 0.f;
  if (large_arc == sweep) sq = -sq;

  float cxp =  sq * rx * y1p / ry;
  float cyp = -sq * ry * x1p / rx;

  float cx = cos_phi * cxp - sin_phi * cyp + (x1 + x2) / 2.f;
  float cy = sin_phi * cxp + cos_phi * cyp + (y1 + y2) / 2.f;

  auto angle = [] (float ux, float uy, float vx, float vy) -> float {
    float dot = ux * vx + uy * vy;
    float len = sqrtf ((ux * ux + uy * uy) * (vx * vx + vy * vy));
    if (!(len > 0.f) || !std::isfinite (len))
      return 0.f;
    float a = acosf (hb_clamp (dot / len, -1.f, 1.f));
    if (ux * vy - uy * vx < 0.f) a = -a;
    return a;
  };

  float theta1 = angle (1.f, 0.f, (x1p - cxp) / rx, (y1p - cyp) / ry);
  float dtheta = angle ((x1p - cxp) / rx, (y1p - cyp) / ry,
                        (-x1p - cxp) / rx, (-y1p - cyp) / ry);

  if (!sweep && dtheta > 0.f) dtheta -= 2.f * (float) M_PI;
  if (sweep && dtheta < 0.f)  dtheta += 2.f * (float) M_PI;

  *cx_out = cx; *cy_out = cy;
  *theta1_out = theta1; *dtheta_out = dtheta;
  *rx_out = rx; *ry_out = ry;
}

void
hb_raster_svg_parse_path_data (hb_svg_str_t d, hb_draw_funcs_t *dfuncs, void *draw_data)
{
  hb_draw_state_t st = HB_DRAW_STATE_DEFAULT;
  hb_svg_float_parser_t fp (d);

  float cur_x = 0, cur_y = 0;
  float start_x = 0, start_y = 0;
  float last_cx = 0, last_cy = 0;
  char last_cmd = 0;
  char cmd = 0;
  unsigned segments = 0;

  while (fp.p < fp.end)
  {
    if (unlikely (segments++ >= HB_SVG_MAX_PATH_SEGMENTS))
      break;
    fp.skip_ws_comma ();
    if (fp.p >= fp.end) break;

    char c = *fp.p;
    if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'))
    {
      cmd = c;
      fp.p++;
    }

    switch (cmd)
    {
    case 'M': case 'm':
    {
      float x = fp.next_float ();
      float y = fp.next_float ();
      if (cmd == 'm') { x += cur_x; y += cur_y; }
      hb_draw_move_to (dfuncs, draw_data, &st, x, y);
      cur_x = start_x = x;
      cur_y = start_y = y;
      cmd = (cmd == 'M') ? 'L' : 'l';
      last_cmd = 'M';
      continue;
    }
    case 'L': case 'l':
    {
      float x = fp.next_float ();
      float y = fp.next_float ();
      if (cmd == 'l') { x += cur_x; y += cur_y; }
      hb_draw_line_to (dfuncs, draw_data, &st, x, y);
      cur_x = x; cur_y = y;
      break;
    }
    case 'H': case 'h':
    {
      float x = fp.next_float ();
      if (cmd == 'h') x += cur_x;
      hb_draw_line_to (dfuncs, draw_data, &st, x, cur_y);
      cur_x = x;
      break;
    }
    case 'V': case 'v':
    {
      float y = fp.next_float ();
      if (cmd == 'v') y += cur_y;
      hb_draw_line_to (dfuncs, draw_data, &st, cur_x, y);
      cur_y = y;
      break;
    }
    case 'C': case 'c':
    {
      float x1 = fp.next_float ();
      float y1 = fp.next_float ();
      float x2 = fp.next_float ();
      float y2 = fp.next_float ();
      float x  = fp.next_float ();
      float y  = fp.next_float ();
      if (cmd == 'c')
      { x1 += cur_x; y1 += cur_y; x2 += cur_x; y2 += cur_y; x += cur_x; y += cur_y; }
      hb_draw_cubic_to (dfuncs, draw_data, &st, x1, y1, x2, y2, x, y);
      last_cx = x2; last_cy = y2;
      cur_x = x; cur_y = y;
      break;
    }
    case 'S': case 's':
    {
      float cx1, cy1;
      if (last_cmd == 'C' || last_cmd == 'c' || last_cmd == 'S' || last_cmd == 's')
      { cx1 = 2 * cur_x - last_cx; cy1 = 2 * cur_y - last_cy; }
      else
      { cx1 = cur_x; cy1 = cur_y; }
      float x2 = fp.next_float ();
      float y2 = fp.next_float ();
      float x  = fp.next_float ();
      float y  = fp.next_float ();
      if (cmd == 's')
      { x2 += cur_x; y2 += cur_y; x += cur_x; y += cur_y; }
      hb_draw_cubic_to (dfuncs, draw_data, &st, cx1, cy1, x2, y2, x, y);
      last_cx = x2; last_cy = y2;
      cur_x = x; cur_y = y;
      break;
    }
    case 'Q': case 'q':
    {
      float x1 = fp.next_float ();
      float y1 = fp.next_float ();
      float x  = fp.next_float ();
      float y  = fp.next_float ();
      if (cmd == 'q')
      { x1 += cur_x; y1 += cur_y; x += cur_x; y += cur_y; }
      hb_draw_quadratic_to (dfuncs, draw_data, &st, x1, y1, x, y);
      last_cx = x1; last_cy = y1;
      cur_x = x; cur_y = y;
      break;
    }
    case 'T': case 't':
    {
      float cx1, cy1;
      if (last_cmd == 'Q' || last_cmd == 'q' || last_cmd == 'T' || last_cmd == 't')
      { cx1 = 2 * cur_x - last_cx; cy1 = 2 * cur_y - last_cy; }
      else
      { cx1 = cur_x; cy1 = cur_y; }
      float x = fp.next_float ();
      float y = fp.next_float ();
      if (cmd == 't') { x += cur_x; y += cur_y; }
      hb_draw_quadratic_to (dfuncs, draw_data, &st, cx1, cy1, x, y);
      last_cx = cx1; last_cy = cy1;
      cur_x = x; cur_y = y;
      break;
    }
    case 'A': case 'a':
    {
      float rx = fp.next_float ();
      float ry = fp.next_float ();
      float x_rot = fp.next_float ();
      bool large_arc = fp.next_flag ();
      bool sweep = fp.next_flag ();
      float x = fp.next_float ();
      float y = fp.next_float ();
      if (cmd == 'a') { x += cur_x; y += cur_y; }

      if (fabsf (x - cur_x) < 1e-6f && fabsf (y - cur_y) < 1e-6f)
      {
        cur_x = x; cur_y = y;
        break;
      }

      float cx, cy, theta1, dtheta, adj_rx, adj_ry;
      svg_arc_endpoint_to_center (cur_x, cur_y, x, y,
                                  rx, ry, x_rot,
                                  large_arc, sweep,
                                  &cx, &cy, &theta1, &dtheta,
                                  &adj_rx, &adj_ry);

      float phi = x_rot * (float) M_PI / 180.f;
      svg_arc_to_cubics (dfuncs, draw_data, &st,
                         cx, cy, adj_rx, adj_ry, phi, theta1, dtheta);
      cur_x = x; cur_y = y;
      break;
    }
    case 'Z': case 'z':
      hb_draw_close_path (dfuncs, draw_data, &st);
      cur_x = start_x;
      cur_y = start_y;
      break;

    default:
      fp.p++;
      continue;
    }

    last_cmd = cmd;
  }
}

static void
svg_rect_to_path (float x, float y, float w, float h, float rx, float ry,
                  hb_draw_funcs_t *dfuncs, void *draw_data)
{
  hb_draw_state_t st = HB_DRAW_STATE_DEFAULT;
  if (rx < 0.f || ry < 0.f)
    return;

  if (rx <= 0 && ry <= 0)
  {
    hb_draw_move_to (dfuncs, draw_data, &st, x, y);
    hb_draw_line_to (dfuncs, draw_data, &st, x + w, y);
    hb_draw_line_to (dfuncs, draw_data, &st, x + w, y + h);
    hb_draw_line_to (dfuncs, draw_data, &st, x, y + h);
    hb_draw_close_path (dfuncs, draw_data, &st);
    return;
  }

  if (rx <= 0) rx = ry;
  if (ry <= 0) ry = rx;
  rx = hb_min (rx, w / 2);
  ry = hb_min (ry, h / 2);

  float kx = rx * 0.5522847498f;
  float ky = ry * 0.5522847498f;

  hb_draw_move_to (dfuncs, draw_data, &st, x + rx, y);
  hb_draw_line_to (dfuncs, draw_data, &st, x + w - rx, y);
  hb_draw_cubic_to (dfuncs, draw_data, &st,
                    x + w - rx + kx, y,
                    x + w, y + ry - ky,
                    x + w, y + ry);
  hb_draw_line_to (dfuncs, draw_data, &st, x + w, y + h - ry);
  hb_draw_cubic_to (dfuncs, draw_data, &st,
                    x + w, y + h - ry + ky,
                    x + w - rx + kx, y + h,
                    x + w - rx, y + h);
  hb_draw_line_to (dfuncs, draw_data, &st, x + rx, y + h);
  hb_draw_cubic_to (dfuncs, draw_data, &st,
                    x + rx - kx, y + h,
                    x, y + h - ry + ky,
                    x, y + h - ry);
  hb_draw_line_to (dfuncs, draw_data, &st, x, y + ry);
  hb_draw_cubic_to (dfuncs, draw_data, &st,
                    x, y + ry - ky,
                    x + rx - kx, y,
                    x + rx, y);
  hb_draw_close_path (dfuncs, draw_data, &st);
}

static void
svg_circle_to_path (float cx, float cy, float r,
                    hb_draw_funcs_t *dfuncs, void *draw_data)
{
  hb_draw_state_t st = HB_DRAW_STATE_DEFAULT;
  float k = r * 0.5522847498f;

  hb_draw_move_to (dfuncs, draw_data, &st, cx + r, cy);
  hb_draw_cubic_to (dfuncs, draw_data, &st,
                    cx + r, cy + k,
                    cx + k, cy + r,
                    cx, cy + r);
  hb_draw_cubic_to (dfuncs, draw_data, &st,
                    cx - k, cy + r,
                    cx - r, cy + k,
                    cx - r, cy);
  hb_draw_cubic_to (dfuncs, draw_data, &st,
                    cx - r, cy - k,
                    cx - k, cy - r,
                    cx, cy - r);
  hb_draw_cubic_to (dfuncs, draw_data, &st,
                    cx + k, cy - r,
                    cx + r, cy - k,
                    cx + r, cy);
  hb_draw_close_path (dfuncs, draw_data, &st);
}

static void
svg_ellipse_to_path (float cx, float cy, float rx, float ry,
                     hb_draw_funcs_t *dfuncs, void *draw_data)
{
  hb_draw_state_t st = HB_DRAW_STATE_DEFAULT;
  float kx = rx * 0.5522847498f;
  float ky = ry * 0.5522847498f;

  hb_draw_move_to (dfuncs, draw_data, &st, cx + rx, cy);
  hb_draw_cubic_to (dfuncs, draw_data, &st,
                    cx + rx, cy + ky,
                    cx + kx, cy + ry,
                    cx, cy + ry);
  hb_draw_cubic_to (dfuncs, draw_data, &st,
                    cx - kx, cy + ry,
                    cx - rx, cy + ky,
                    cx - rx, cy);
  hb_draw_cubic_to (dfuncs, draw_data, &st,
                    cx - rx, cy - ky,
                    cx - kx, cy - ry,
                    cx, cy - ry);
  hb_draw_cubic_to (dfuncs, draw_data, &st,
                    cx + kx, cy - ry,
                    cx + rx, cy - ky,
                    cx + rx, cy);
  hb_draw_close_path (dfuncs, draw_data, &st);
}

static void
svg_line_to_path (float x1, float y1, float x2, float y2,
                  hb_draw_funcs_t *dfuncs, void *draw_data)
{
  hb_draw_state_t st = HB_DRAW_STATE_DEFAULT;
  hb_draw_move_to (dfuncs, draw_data, &st, x1, y1);
  hb_draw_line_to (dfuncs, draw_data, &st, x2, y2);
}

static void
svg_polygon_to_path (hb_svg_str_t points, bool close,
                     hb_draw_funcs_t *dfuncs, void *draw_data)
{
  hb_draw_state_t st = HB_DRAW_STATE_DEFAULT;
  hb_svg_float_parser_t fp (points);
  bool first = true;
  while (fp.has_more ())
  {
    float x = fp.next_float ();
    float y = fp.next_float ();
    if (first)
    {
      hb_draw_move_to (dfuncs, draw_data, &st, x, y);
      first = false;
    }
    else
      hb_draw_line_to (dfuncs, draw_data, &st, x, y);
  }
  if (close && !first)
    hb_draw_close_path (dfuncs, draw_data, &st);
}

void
hb_raster_svg_shape_path_emit (hb_draw_funcs_t *dfuncs, void *draw_data, void *user_data)
{
  hb_svg_shape_emit_data_t *shape = (hb_svg_shape_emit_data_t *) user_data;
  switch (shape->type)
  {
  case hb_svg_shape_emit_data_t::SHAPE_PATH:
    hb_raster_svg_parse_path_data (shape->str_data, dfuncs, draw_data);
    break;
  case hb_svg_shape_emit_data_t::SHAPE_RECT:
    svg_rect_to_path (shape->params[0], shape->params[1],
                      shape->params[2], shape->params[3],
                      shape->params[4], shape->params[5],
                      dfuncs, draw_data);
    break;
  case hb_svg_shape_emit_data_t::SHAPE_CIRCLE:
    svg_circle_to_path (shape->params[0], shape->params[1], shape->params[2],
                        dfuncs, draw_data);
    break;
  case hb_svg_shape_emit_data_t::SHAPE_ELLIPSE:
    svg_ellipse_to_path (shape->params[0], shape->params[1],
                         shape->params[2], shape->params[3],
                         dfuncs, draw_data);
    break;
  case hb_svg_shape_emit_data_t::SHAPE_LINE:
    svg_line_to_path (shape->params[0], shape->params[1],
                      shape->params[2], shape->params[3],
                      dfuncs, draw_data);
    break;
  case hb_svg_shape_emit_data_t::SHAPE_POLYLINE:
    svg_polygon_to_path (shape->str_data, false, dfuncs, draw_data);
    break;
  case hb_svg_shape_emit_data_t::SHAPE_POLYGON:
    svg_polygon_to_path (shape->str_data, true, dfuncs, draw_data);
    break;
  }
}

bool
hb_raster_svg_parse_shape_tag (hb_svg_xml_parser_t &parser,
                     hb_svg_shape_emit_data_t *shape)
{
  hb_svg_attr_view_t attrs (parser);
  hb_svg_style_props_t style_props;
  svg_parse_style_props (attrs.get ("style"), &style_props);
  hb_svg_str_t tag = parser.tag_name;
  if (tag.eq ("path"))
  {
    hb_svg_str_t d = svg_pick_attr_or_style (parser, style_props.d, "d");
    if (!d.len) return false;
    shape->type = hb_svg_shape_emit_data_t::SHAPE_PATH;
    shape->str_data = d;
    return true;
  }
  if (tag.eq ("rect"))
  {
    float w = svg_parse_float (svg_pick_attr_or_style (parser, style_props.width, "width"));
    float h = svg_parse_float (svg_pick_attr_or_style (parser, style_props.height, "height"));
    if (w <= 0 || h <= 0) return false;
    float rx = svg_parse_float (svg_pick_attr_or_style (parser, style_props.rx, "rx"));
    float ry = svg_parse_float (svg_pick_attr_or_style (parser, style_props.ry, "ry"));
    if (rx < 0.f || ry < 0.f) return false;
    shape->type = hb_svg_shape_emit_data_t::SHAPE_RECT;
    shape->params[0] = svg_parse_float (svg_pick_attr_or_style (parser, style_props.x, "x"));
    shape->params[1] = svg_parse_float (svg_pick_attr_or_style (parser, style_props.y, "y"));
    shape->params[2] = w;
    shape->params[3] = h;
    shape->params[4] = rx;
    shape->params[5] = ry;
    return true;
  }
  if (tag.eq ("circle"))
  {
    float r = svg_parse_float (svg_pick_attr_or_style (parser, style_props.r, "r"));
    if (r <= 0) return false;
    shape->type = hb_svg_shape_emit_data_t::SHAPE_CIRCLE;
    shape->params[0] = svg_parse_float (svg_pick_attr_or_style (parser, style_props.cx, "cx"));
    shape->params[1] = svg_parse_float (svg_pick_attr_or_style (parser, style_props.cy, "cy"));
    shape->params[2] = r;
    return true;
  }
  if (tag.eq ("ellipse"))
  {
    float rx = svg_parse_float (svg_pick_attr_or_style (parser, style_props.rx, "rx"));
    float ry = svg_parse_float (svg_pick_attr_or_style (parser, style_props.ry, "ry"));
    if (rx <= 0 || ry <= 0) return false;
    shape->type = hb_svg_shape_emit_data_t::SHAPE_ELLIPSE;
    shape->params[0] = svg_parse_float (svg_pick_attr_or_style (parser, style_props.cx, "cx"));
    shape->params[1] = svg_parse_float (svg_pick_attr_or_style (parser, style_props.cy, "cy"));
    shape->params[2] = rx;
    shape->params[3] = ry;
    return true;
  }
  if (tag.eq ("line"))
  {
    shape->type = hb_svg_shape_emit_data_t::SHAPE_LINE;
    shape->params[0] = svg_parse_float (svg_pick_attr_or_style (parser, style_props.x1, "x1"));
    shape->params[1] = svg_parse_float (svg_pick_attr_or_style (parser, style_props.y1, "y1"));
    shape->params[2] = svg_parse_float (svg_pick_attr_or_style (parser, style_props.x2, "x2"));
    shape->params[3] = svg_parse_float (svg_pick_attr_or_style (parser, style_props.y2, "y2"));
    return true;
  }
  if (tag.eq ("polyline"))
  {
    hb_svg_str_t points = svg_pick_attr_or_style (parser, style_props.points, "points");
    if (!points.len) return false;
    shape->type = hb_svg_shape_emit_data_t::SHAPE_POLYLINE;
    shape->str_data = points;
    return true;
  }
  if (tag.eq ("polygon"))
  {
    hb_svg_str_t points = svg_pick_attr_or_style (parser, style_props.points, "points");
    if (!points.len) return false;
    shape->type = hb_svg_shape_emit_data_t::SHAPE_POLYGON;
    shape->str_data = points;
    return true;
  }
  return false;
}

#endif /* !HB_NO_RASTER_SVG */
