static void rsvg_node_image_draw (RsvgNode * self, RsvgDrawingCtx * ctx, int dominate) { RsvgNodeImage *z = (RsvgNodeImage *) self; unsigned int aspect_ratio = z->preserve_aspect_ratio; gdouble x, y, w, h; cairo_surface_t *surface = z->surface; if (surface == NULL) return; x = _rsvg_css_normalize_length (&z->x, ctx, 'h'); y = _rsvg_css_normalize_length (&z->y, ctx, 'v'); w = _rsvg_css_normalize_length (&z->w, ctx, 'h'); h = _rsvg_css_normalize_length (&z->h, ctx, 'v'); rsvg_state_reinherit_top (ctx, z->super.state, dominate); rsvg_push_discrete_layer (ctx); if (!rsvg_current_state (ctx)->overflow && (aspect_ratio & RSVG_ASPECT_RATIO_SLICE)) { rsvg_add_clipping_rect (ctx, x, y, w, h); } rsvg_preserve_aspect_ratio (aspect_ratio, (double) cairo_image_surface_get_width (surface), (double) cairo_image_surface_get_height (surface), &w, &h, &x, &y); rsvg_render_surface (ctx, surface, x, y, w, h); rsvg_pop_discrete_layer (ctx); }
static void _rsvg_node_line_draw (RsvgNode * overself, RsvgDrawingCtx * ctx, int dominate) { cairo_path_t *path; RsvgPathBuilder builder; RsvgNodeLine *self = (RsvgNodeLine *) overself; double x1, y1, x2, y2; rsvg_path_builder_init (&builder, 4); x1 = _rsvg_css_normalize_length (&self->x1, ctx, 'h'); y1 = _rsvg_css_normalize_length (&self->y1, ctx, 'v'); x2 = _rsvg_css_normalize_length (&self->x2, ctx, 'h'); y2 = _rsvg_css_normalize_length (&self->y2, ctx, 'v'); rsvg_path_builder_move_to (&builder, x1, y1); rsvg_path_builder_line_to (&builder, x2, y2); path = rsvg_path_builder_finish (&builder); rsvg_state_reinherit_top (ctx, overself->state, dominate); rsvg_render_path (ctx, path); rsvg_cairo_path_destroy (path); }
static void _rsvg_node_text_draw (RsvgNode * self, RsvgDrawingCtx * ctx, int dominate) { double x, y; gboolean lastwasspace = TRUE; RsvgNodeText *text = (RsvgNodeText *) self; rsvg_state_reinherit_top (ctx, self->state, dominate); x = _rsvg_css_normalize_length (&text->x, ctx, 'h'); y = _rsvg_css_normalize_length (&text->y, ctx, 'v'); x += _rsvg_css_normalize_length (&text->dx, ctx, 'h'); y += _rsvg_css_normalize_length (&text->dy, ctx, 'v'); if (rsvg_current_state (ctx)->text_anchor != TEXT_ANCHOR_START) { double length = 0; _rsvg_node_text_length_children (self, ctx, &length, &lastwasspace); if (rsvg_current_state (ctx)->text_anchor == TEXT_ANCHOR_END) x -= length; if (rsvg_current_state (ctx)->text_anchor == TEXT_ANCHOR_MIDDLE) x -= length / 2; } lastwasspace = TRUE; _rsvg_node_text_type_children (self, ctx, &x, &y, &lastwasspace); }
static int _rsvg_node_text_length_children (RsvgNode * self, RsvgDrawingCtx * ctx, gdouble * x, gboolean * lastwasspace) { guint i; int out = FALSE; for (i = 0; i < self->children->len; i++) { RsvgNode *node = g_ptr_array_index (self->children, i); rsvg_state_push (ctx); rsvg_state_reinherit_top (ctx, node->state, 0); if (!strcmp (node->type->str, "RSVG_NODE_CHARS")) { RsvgNodeChars *chars = (RsvgNodeChars *) node; GString *str = _rsvg_text_chomp (rsvg_current_state (ctx), chars->contents, lastwasspace); *x += rsvg_text_length_text_as_string (ctx, str->str); g_string_free (str, TRUE); } else if (!strcmp (node->type->str, "tspan")) { RsvgNodeText *tspan = (RsvgNodeText *) node; out = _rsvg_node_text_length_tspan (tspan, ctx, x, lastwasspace); } else if (!strcmp (node->type->str, "tref")) { RsvgNodeTref *tref = (RsvgNodeTref *) node; out = _rsvg_node_text_length_tref (tref, ctx, x, lastwasspace); } rsvg_state_pop (ctx); if (out) break; } return out; }
static void rsvg_node_path_draw (RsvgNode * self, RsvgDrawingCtx * ctx, int dominate) { RsvgNodePath *path = (RsvgNodePath *) self; if (!path->path) return; rsvg_state_reinherit_top (ctx, self->state, dominate); rsvg_render_path (ctx, path->path); }
static void _rsvg_node_poly_draw (RsvgNode * self, RsvgDrawingCtx * ctx, int dominate) { RsvgNodePoly *poly = (RsvgNodePoly *) self; if (poly->path == NULL) return; rsvg_state_reinherit_top (ctx, self->state, dominate); rsvg_render_path (ctx, poly->path); }
static void _rsvg_node_ellipse_draw (RsvgNode * self, RsvgDrawingCtx * ctx, int dominate) { RsvgNodeEllipse *ellipse = (RsvgNodeEllipse *) self; cairo_path_t *path; double cx, cy, rx, ry; RsvgPathBuilder builder; cx = _rsvg_css_normalize_length (&ellipse->cx, ctx, 'h'); cy = _rsvg_css_normalize_length (&ellipse->cy, ctx, 'v'); rx = _rsvg_css_normalize_length (&ellipse->rx, ctx, 'h'); ry = _rsvg_css_normalize_length (&ellipse->ry, ctx, 'v'); if (rx <= 0 || ry <= 0) return; /* approximate an ellipse using 4 bezier curves */ rsvg_path_builder_init (&builder, 19); rsvg_path_builder_move_to (&builder, cx + rx, cy); rsvg_path_builder_curve_to (&builder, cx + rx, cy - RSVG_ARC_MAGIC * ry, cx + RSVG_ARC_MAGIC * rx, cy - ry, cx, cy - ry); rsvg_path_builder_curve_to (&builder, cx - RSVG_ARC_MAGIC * rx, cy - ry, cx - rx, cy - RSVG_ARC_MAGIC * ry, cx - rx, cy); rsvg_path_builder_curve_to (&builder, cx - rx, cy + RSVG_ARC_MAGIC * ry, cx - RSVG_ARC_MAGIC * rx, cy + ry, cx, cy + ry); rsvg_path_builder_curve_to (&builder, cx + RSVG_ARC_MAGIC * rx, cy + ry, cx + rx, cy + RSVG_ARC_MAGIC * ry, cx + rx, cy); rsvg_path_builder_close_path (&builder); path = rsvg_path_builder_finish (&builder); rsvg_state_reinherit_top (ctx, self->state, dominate); rsvg_render_path (ctx, path); rsvg_cairo_path_destroy (path); }
static void _rsvg_node_circle_draw (RsvgNode * self, RsvgDrawingCtx * ctx, int dominate) { cairo_path_t *path; RsvgNodeCircle *circle = (RsvgNodeCircle *) self; double cx, cy, r; RsvgPathBuilder builder; cx = _rsvg_css_normalize_length (&circle->cx, ctx, 'h'); cy = _rsvg_css_normalize_length (&circle->cy, ctx, 'v'); r = _rsvg_css_normalize_length (&circle->r, ctx, 'o'); if (r <= 0) return; /* approximate a circle using 4 bezier curves */ rsvg_path_builder_init (&builder, 19); rsvg_path_builder_move_to (&builder, cx + r, cy); rsvg_path_builder_curve_to (&builder, cx + r, cy + r * RSVG_ARC_MAGIC, cx + r * RSVG_ARC_MAGIC, cy + r, cx, cy + r); rsvg_path_builder_curve_to (&builder, cx - r * RSVG_ARC_MAGIC, cy + r, cx - r, cy + r * RSVG_ARC_MAGIC, cx - r, cy); rsvg_path_builder_curve_to (&builder, cx - r, cy - r * RSVG_ARC_MAGIC, cx - r * RSVG_ARC_MAGIC, cy - r, cx, cy - r); rsvg_path_builder_curve_to (&builder, cx + r * RSVG_ARC_MAGIC, cy - r, cx + r, cy - r * RSVG_ARC_MAGIC, cx + r, cy); rsvg_path_builder_close_path (&builder); path = rsvg_path_builder_finish (&builder); rsvg_state_reinherit_top (ctx, self->state, dominate); rsvg_render_path (ctx, path); rsvg_cairo_path_destroy (path); }
/* generic function for drawing all of the children of a particular node */ void _rsvg_node_draw_children (RsvgNode * self, RsvgDrawingCtx * ctx, int dominate) { guint i; if (dominate != -1) { rsvg_state_reinherit_top (ctx, self->state, dominate); rsvg_push_discrete_layer (ctx); } for (i = 0; i < self->children->len; i++) { rsvg_state_push (ctx); rsvg_node_draw (g_ptr_array_index (self->children, i), ctx, 0); rsvg_state_pop (ctx); } if (dominate != -1) rsvg_pop_discrete_layer (ctx); }
static void _rsvg_node_text_type_tspan (RsvgNodeText * self, RsvgDrawingCtx * ctx, gdouble * x, gdouble * y, gboolean * lastwasspace, gboolean usetextonly) { double dx, dy, length = 0; rsvg_state_reinherit_top (ctx, self->super.state, 0); dx = _rsvg_css_normalize_length (&self->dx, ctx, 'h'); dy = _rsvg_css_normalize_length (&self->dy, ctx, 'v'); if (rsvg_current_state (ctx)->text_anchor != TEXT_ANCHOR_START) { gboolean lws = *lastwasspace; _rsvg_node_text_length_children (&self->super, ctx, &length, &lws, usetextonly); if (rsvg_current_state (ctx)->text_anchor == TEXT_ANCHOR_MIDDLE) length /= 2; } if (self->x.factor != 'n') { *x = _rsvg_css_normalize_length (&self->x, ctx, 'h'); if (!PANGO_GRAVITY_IS_VERTICAL (rsvg_current_state (ctx)->text_gravity)) { *x -= length; if (rsvg_current_state (ctx)->text_anchor == TEXT_ANCHOR_MIDDLE) dx /= 2; if (rsvg_current_state (ctx)->text_anchor == TEXT_ANCHOR_END) dx = 0; } } *x += dx; if (self->y.factor != 'n') { *y = _rsvg_css_normalize_length (&self->y, ctx, 'v'); if (PANGO_GRAVITY_IS_VERTICAL (rsvg_current_state (ctx)->text_gravity)) { *y -= length; if (rsvg_current_state (ctx)->text_anchor == TEXT_ANCHOR_MIDDLE) dy /= 2; if (rsvg_current_state (ctx)->text_anchor == TEXT_ANCHOR_END) dy = 0; } } *y += dy; _rsvg_node_text_type_children (&self->super, ctx, x, y, lastwasspace, usetextonly); }
static int _rsvg_node_text_length_children (RsvgNode * self, RsvgDrawingCtx * ctx, gdouble * length, gboolean * lastwasspace, gboolean usetextonly) { guint i; int out = FALSE; for (i = 0; i < self->children->len; i++) { RsvgNode *node = g_ptr_array_index (self->children, i); RsvgNodeType type = RSVG_NODE_TYPE (node); rsvg_state_push (ctx); rsvg_state_reinherit_top (ctx, node->state, 0); if (type == RSVG_NODE_TYPE_CHARS) { RsvgNodeChars *chars = (RsvgNodeChars *) node; GString *str = _rsvg_text_chomp (rsvg_current_state (ctx), chars->contents, lastwasspace); *length += rsvg_text_length_text_as_string (ctx, str->str); g_string_free (str, TRUE); } else { if (usetextonly || type == RSVG_NODE_TYPE_TEXT_PATH) { out = _rsvg_node_text_length_children(node, ctx, length, lastwasspace, usetextonly); } else { if (type == RSVG_NODE_TYPE_TSPAN) { RsvgNodeText *tspan = (RsvgNodeText *) node; out = _rsvg_node_text_length_tspan (tspan, ctx, length, lastwasspace, usetextonly); } else if (type == RSVG_NODE_TYPE_TREF) { RsvgNodeTref *tref = (RsvgNodeTref *) node; out = _rsvg_node_text_length_tref (tref, ctx, length, lastwasspace, usetextonly); } } } rsvg_state_pop (ctx); if (out) break; } return out; }
static void _rsvg_node_switch_draw (RsvgNode * self, RsvgDrawingCtx * ctx, int dominate) { guint i; rsvg_state_reinherit_top (ctx, self->state, dominate); rsvg_push_discrete_layer (ctx); for (i = 0; i < self->children->len; i++) { RsvgNode *drawable = g_ptr_array_index (self->children, i); if (drawable->state->cond_true) { rsvg_state_push (ctx); rsvg_node_draw (g_ptr_array_index (self->children, i), ctx, 0); rsvg_state_pop (ctx); break; /* only render the 1st one */ } } rsvg_pop_discrete_layer (ctx); }
static void _rsvg_node_text_draw (RsvgNode * self, RsvgDrawingCtx * ctx, int dominate) { double x, y, dx, dy, length = 0; gboolean lastwasspace = TRUE; RsvgNodeText *text = (RsvgNodeText *) self; rsvg_state_reinherit_top (ctx, self->state, dominate); x = _rsvg_css_normalize_length (&text->x, ctx, 'h'); y = _rsvg_css_normalize_length (&text->y, ctx, 'v'); dx = _rsvg_css_normalize_length (&text->dx, ctx, 'h'); dy = _rsvg_css_normalize_length (&text->dy, ctx, 'v'); if (rsvg_current_state (ctx)->text_anchor != TEXT_ANCHOR_START) { _rsvg_node_text_length_children (self, ctx, &length, &lastwasspace, FALSE); if (rsvg_current_state (ctx)->text_anchor == TEXT_ANCHOR_MIDDLE) length /= 2; } if (PANGO_GRAVITY_IS_VERTICAL (rsvg_current_state (ctx)->text_gravity)) { y -= length; if (rsvg_current_state (ctx)->text_anchor == TEXT_ANCHOR_MIDDLE) dy /= 2; if (rsvg_current_state (ctx)->text_anchor == TEXT_ANCHOR_END) dy = 0; } else { x -= length; if (rsvg_current_state (ctx)->text_anchor == TEXT_ANCHOR_MIDDLE) dx /= 2; if (rsvg_current_state (ctx)->text_anchor == TEXT_ANCHOR_END) dx = 0; } x += dx; y += dy; lastwasspace = TRUE; _rsvg_node_text_type_children (self, ctx, &x, &y, &lastwasspace, FALSE); }
static void _rsvg_node_text_type_tspan (RsvgNodeText * self, RsvgDrawingCtx * ctx, gdouble * x, gdouble * y, gboolean * lastwasspace) { rsvg_state_reinherit_top (ctx, self->super.state, 0); if (self->x.factor != 'n') { *x = _rsvg_css_normalize_length (&self->x, ctx, 'h'); if (rsvg_current_state (ctx)->text_anchor != TEXT_ANCHOR_START) { double length = 0; gboolean lws = *lastwasspace; _rsvg_node_text_length_children (&self->super, ctx, &length, &lws); if (rsvg_current_state (ctx)->text_anchor == TEXT_ANCHOR_END) *x -= length; if (rsvg_current_state (ctx)->text_anchor == TEXT_ANCHOR_MIDDLE) *x -= length / 2; } } if (self->y.factor != 'n') *y = _rsvg_css_normalize_length (&self->y, ctx, 'v'); *x += _rsvg_css_normalize_length (&self->dx, ctx, 'h'); *y += _rsvg_css_normalize_length (&self->dy, ctx, 'v'); _rsvg_node_text_type_children (&self->super, ctx, x, y, lastwasspace); }
static void _rsvg_node_rect_draw (RsvgNode * self, RsvgDrawingCtx * ctx, int dominate) { double x, y, w, h, rx, ry; double half_w, half_h; RsvgPathBuilder builder; cairo_path_t *path; RsvgNodeRect *rect = (RsvgNodeRect *) self; x = _rsvg_css_normalize_length (&rect->x, ctx, 'h'); y = _rsvg_css_normalize_length (&rect->y, ctx, 'v'); /* FIXME: negative w/h/rx/ry is an error, per http://www.w3.org/TR/SVG11/shapes.html#RectElement * For now we'll just take the absolute value. */ w = fabs (_rsvg_css_normalize_length (&rect->w, ctx, 'h')); h = fabs (_rsvg_css_normalize_length (&rect->h, ctx, 'v')); rx = fabs (_rsvg_css_normalize_length (&rect->rx, ctx, 'h')); ry = fabs (_rsvg_css_normalize_length (&rect->ry, ctx, 'v')); if (w == 0. || h == 0.) return; if (rect->got_rx) rx = rx; else rx = ry; if (rect->got_ry) ry = ry; else ry = rx; half_w = w / 2; half_h = h / 2; if (rx > half_w) rx = half_w; if (ry > half_h) ry = half_h; if (rx == 0) ry = 0; else if (ry == 0) rx = 0; if (rx == 0) { /* Easy case, no rounded corners */ rsvg_path_builder_init (&builder, 11); rsvg_path_builder_move_to (&builder, x, y); rsvg_path_builder_line_to (&builder, x + w, y); rsvg_path_builder_line_to (&builder, x + w, y + h); rsvg_path_builder_line_to (&builder, x, y + h); rsvg_path_builder_line_to (&builder, x, y); rsvg_path_builder_close_path (&builder); } else { double top_x1, top_x2, top_y; double bottom_x1, bottom_x2, bottom_y; double left_x, left_y1, left_y2; double right_x, right_y1, right_y2; /* Hard case, rounded corners * * (top_x1, top_y) (top_x2, top_y) * *--------------------------------* * / \ * * (left_x, left_y1) * (right_x, right_y1) * | | * | | * | | * | | * | | * | | * | | * | | * | | * * (left_x, left_y2) * (right_x, right_y2) * \ / * *--------------------------------* * (bottom_x1, bottom_y) (bottom_x2, bottom_y) */ top_x1 = x + rx; top_x2 = x + w - rx; top_y = y; bottom_x1 = top_x1; bottom_x2 = top_x2; bottom_y = y + h; left_x = x; left_y1 = y + ry; left_y2 = y + h - ry; right_x = x + w; right_y1 = left_y1; right_y2 = left_y2; rsvg_path_builder_init (&builder, 32); /* an estimate; the arc segments may grow the array anyway */ rsvg_path_builder_move_to (&builder, top_x1, top_y); rsvg_path_builder_line_to (&builder, top_x2, top_y); rsvg_path_builder_arc (&builder, top_x2, top_y, rx, ry, 0, FALSE, TRUE, right_x, right_y1); rsvg_path_builder_line_to (&builder, right_x, right_y2); rsvg_path_builder_arc (&builder, right_x, right_y2, rx, ry, 0, FALSE, TRUE, bottom_x2, bottom_y); rsvg_path_builder_line_to (&builder, bottom_x1, bottom_y); rsvg_path_builder_arc (&builder, bottom_x1, bottom_y, rx, ry, 0, FALSE, TRUE, left_x, left_y2); rsvg_path_builder_line_to (&builder, left_x, left_y1); rsvg_path_builder_arc (&builder, left_x, left_y1, rx, ry, 0, FALSE, TRUE, top_x1, top_y); rsvg_path_builder_close_path (&builder); } path = rsvg_path_builder_finish (&builder); rsvg_state_reinherit_top (ctx, self->state, dominate); rsvg_render_path (ctx, path); rsvg_cairo_path_destroy (path); }
static void rsvg_node_use_draw (RsvgNode * self, RsvgDrawingCtx * ctx, int dominate) { RsvgNodeUse *use = (RsvgNodeUse *) self; RsvgNode *child; RsvgState *state; double affine[6]; double x, y, w, h; x = _rsvg_css_normalize_length (&use->x, ctx, 'h'); y = _rsvg_css_normalize_length (&use->y, ctx, 'v'); w = _rsvg_css_normalize_length (&use->w, ctx, 'h'); h = _rsvg_css_normalize_length (&use->h, ctx, 'v'); rsvg_state_reinherit_top (ctx, self->state, dominate); child = use->link; /* If it can find nothing to draw... draw nothing */ if (!child) return; else if (rsvg_node_is_ancestor (child, self)) /* or, if we're <use>'ing ourself */ return; state = rsvg_current_state (ctx); if (strcmp (child->type->str, "symbol")) { _rsvg_affine_translate (affine, x, y); _rsvg_affine_multiply (state->affine, affine, state->affine); rsvg_push_discrete_layer (ctx); rsvg_state_push (ctx); rsvg_node_draw (child, ctx, 1); rsvg_state_pop (ctx); rsvg_pop_discrete_layer (ctx); } else { RsvgNodeSymbol *symbol = (RsvgNodeSymbol *) child; if (symbol->vbox.active) { rsvg_preserve_aspect_ratio (symbol->preserve_aspect_ratio, symbol->vbox.w, symbol->vbox.h, &w, &h, &x, &y); _rsvg_affine_translate (affine, x, y); _rsvg_affine_multiply (state->affine, affine, state->affine); _rsvg_affine_scale (affine, w / symbol->vbox.w, h / symbol->vbox.h); _rsvg_affine_multiply (state->affine, affine, state->affine); _rsvg_affine_translate (affine, -symbol->vbox.x, -symbol->vbox.y); _rsvg_affine_multiply (state->affine, affine, state->affine); _rsvg_push_view_box (ctx, symbol->vbox.w, symbol->vbox.h); rsvg_push_discrete_layer (ctx); if (!state->overflow || (!state->has_overflow && child->state->overflow)) rsvg_add_clipping_rect (ctx, symbol->vbox.x, symbol->vbox.y, symbol->vbox.w, symbol->vbox.h); } else { _rsvg_affine_translate (affine, x, y); _rsvg_affine_multiply (state->affine, affine, state->affine); rsvg_push_discrete_layer (ctx); } rsvg_state_push (ctx); _rsvg_node_draw_children (child, ctx, 1); rsvg_state_pop (ctx); rsvg_pop_discrete_layer (ctx); if (symbol->vbox.active) _rsvg_pop_view_box (ctx); } }
static void rsvg_node_svg_draw (RsvgNode * self, RsvgDrawingCtx * ctx, int dominate) { RsvgNodeSvg *sself; RsvgState *state; gdouble affine[6], affine_old[6], affine_new[6]; guint i; double nx, ny, nw, nh; sself = (RsvgNodeSvg *) self; nx = _rsvg_css_normalize_length (&sself->x, ctx, 'h'); ny = _rsvg_css_normalize_length (&sself->y, ctx, 'v'); nw = _rsvg_css_normalize_length (&sself->w, ctx, 'h'); nh = _rsvg_css_normalize_length (&sself->h, ctx, 'v'); rsvg_state_reinherit_top (ctx, self->state, dominate); state = rsvg_current_state (ctx); for (i = 0; i < 6; i++) affine_old[i] = state->affine[i]; if (sself->vbox.active) { double x = nx, y = ny, w = nw, h = nh; rsvg_preserve_aspect_ratio (sself->preserve_aspect_ratio, sself->vbox.w, sself->vbox.h, &w, &h, &x, &y); affine[0] = w / sself->vbox.w; affine[1] = 0; affine[2] = 0; affine[3] = h / sself->vbox.h; affine[4] = x - sself->vbox.x * w / sself->vbox.w; affine[5] = y - sself->vbox.y * h / sself->vbox.h; _rsvg_affine_multiply (state->affine, affine, state->affine); _rsvg_push_view_box (ctx, sself->vbox.w, sself->vbox.h); } else { affine[0] = 1; affine[1] = 0; affine[2] = 0; affine[3] = 1; affine[4] = nx; affine[5] = ny; _rsvg_affine_multiply (state->affine, affine, state->affine); _rsvg_push_view_box (ctx, nw, nh); } for (i = 0; i < 6; i++) affine_new[i] = state->affine[i]; rsvg_push_discrete_layer (ctx); /* Bounding box addition must be AFTER the discrete layer push, which must be AFTER the transformation happens. */ if (!state->overflow && self->parent) { for (i = 0; i < 6; i++) state->affine[i] = affine_old[i]; rsvg_add_clipping_rect (ctx, nx, ny, nw, nh); for (i = 0; i < 6; i++) state->affine[i] = affine_new[i]; } for (i = 0; i < self->children->len; i++) { rsvg_state_push (ctx); rsvg_node_draw (g_ptr_array_index (self->children, i), ctx, 0); rsvg_state_pop (ctx); } rsvg_pop_discrete_layer (ctx); _rsvg_pop_view_box (ctx); }