| /* |
| * 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 */ |