static void xps_draw_linear_gradient(fz_context *ctx, xps_document *doc, const fz_matrix *ctm, const fz_rect *area, struct stop *stops, int count, fz_xml *root, int spread) { float x0, y0, x1, y1; int i, mi, ma; float dx, dy, x, y, k; fz_point p1, p2; fz_matrix inv; fz_rect local_area = *area; char *start_point_att = fz_xml_att(root, "StartPoint"); char *end_point_att = fz_xml_att(root, "EndPoint"); x0 = y0 = 0; x1 = y1 = 1; if (start_point_att) xps_parse_point(ctx, doc, start_point_att, &x0, &y0); if (end_point_att) xps_parse_point(ctx, doc, end_point_att, &x1, &y1); p1.x = x0; p1.y = y0; p2.x = x1; p2.y = y1; fz_transform_rect(&local_area, fz_invert_matrix(&inv, ctm)); x = p2.x - p1.x; y = p2.y - p1.y; k = ((local_area.x0 - p1.x) * x + (local_area.y0 - p1.y) * y) / (x * x + y * y); mi = floorf(k); ma = ceilf(k); k = ((local_area.x1 - p1.x) * x + (local_area.y0 - p1.y) * y) / (x * x + y * y); mi = fz_mini(mi, floorf(k)); ma = fz_maxi(ma, ceilf(k)); k = ((local_area.x0 - p1.x) * x + (local_area.y1 - p1.y) * y) / (x * x + y * y); mi = fz_mini(mi, floorf(k)); ma = fz_maxi(ma, ceilf(k)); k = ((local_area.x1 - p1.x) * x + (local_area.y1 - p1.y) * y) / (x * x + y * y); mi = fz_mini(mi, floorf(k)); ma = fz_maxi(ma, ceilf(k)); dx = x1 - x0; dy = y1 - y0; if (spread == SPREAD_REPEAT) { for (i = mi; i < ma; i++) xps_draw_one_linear_gradient(ctx, doc, ctm, stops, count, 0, x0 + i * dx, y0 + i * dy, x1 + i * dx, y1 + i * dy); } else if (spread == SPREAD_REFLECT) { if ((mi % 2) != 0) mi--; for (i = mi; i < ma; i += 2) { xps_draw_one_linear_gradient(ctx, doc, ctm, stops, count, 0, x0 + i * dx, y0 + i * dy, x1 + i * dx, y1 + i * dy); xps_draw_one_linear_gradient(ctx, doc, ctm, stops, count, 0, x0 + (i + 2) * dx, y0 + (i + 2) * dy, x1 + i * dx, y1 + i * dy); } } else { xps_draw_one_linear_gradient(ctx, doc, ctm, stops, count, 1, x0, y0, x1, y1); } }
static void xps_parse_arc_segment(fz_context *doc, fz_path *path, fz_xml *root, int stroking, int *skipped_stroke) { /* ArcSegment pretty much follows the SVG algorithm for converting an * arc in endpoint representation to an arc in centerpoint * representation. Once in centerpoint it can be given to the * graphics library in the form of a postscript arc. */ float rotation_angle; int is_large_arc, is_clockwise; float point_x, point_y; float size_x, size_y; int is_stroked; char *point_att = fz_xml_att(root, "Point"); char *size_att = fz_xml_att(root, "Size"); char *rotation_angle_att = fz_xml_att(root, "RotationAngle"); char *is_large_arc_att = fz_xml_att(root, "IsLargeArc"); char *sweep_direction_att = fz_xml_att(root, "SweepDirection"); char *is_stroked_att = fz_xml_att(root, "IsStroked"); if (!point_att || !size_att || !rotation_angle_att || !is_large_arc_att || !sweep_direction_att) { fz_warn(doc, "ArcSegment element is missing attributes"); return; } is_stroked = 1; if (is_stroked_att && !strcmp(is_stroked_att, "false")) is_stroked = 0; if (!is_stroked) *skipped_stroke = 1; point_x = point_y = 0; size_x = size_y = 0; xps_parse_point(point_att, &point_x, &point_y); xps_parse_point(size_att, &size_x, &size_y); rotation_angle = fz_atof(rotation_angle_att); is_large_arc = !strcmp(is_large_arc_att, "true"); is_clockwise = !strcmp(sweep_direction_att, "Clockwise"); if (stroking && !is_stroked) { fz_moveto(doc, path, point_x, point_y); return; } xps_draw_arc(doc, path, size_x, size_y, rotation_angle, is_large_arc, is_clockwise, point_x, point_y); }
static void xps_parse_poly_line_segment(fz_context *doc, fz_path *path, fz_xml *root, int stroking, int *skipped_stroke) { char *points_att = fz_xml_att(root, "Points"); char *is_stroked_att = fz_xml_att(root, "IsStroked"); int is_stroked; float x, y; char *s; if (!points_att) { fz_warn(doc, "PolyLineSegment element has no points"); return; } is_stroked = 1; if (is_stroked_att && !strcmp(is_stroked_att, "false")) is_stroked = 0; if (!is_stroked) *skipped_stroke = 1; s = points_att; while (*s != 0) { while (*s == ' ') s++; s = xps_parse_point(s, &x, &y); if (stroking && !is_stroked) fz_moveto(doc, path, x, y); else fz_lineto(doc, path, x, y); } }
static void xps_parse_path_figure(fz_context *doc, fz_path *path, fz_xml *root, int stroking) { fz_xml *node; char *is_closed_att; char *start_point_att; char *is_filled_att; int is_closed = 0; int is_filled = 1; float start_x = 0; float start_y = 0; int skipped_stroke = 0; is_closed_att = fz_xml_att(root, "IsClosed"); start_point_att = fz_xml_att(root, "StartPoint"); is_filled_att = fz_xml_att(root, "IsFilled"); if (is_closed_att) is_closed = !strcmp(is_closed_att, "true"); if (is_filled_att) is_filled = !strcmp(is_filled_att, "true"); if (start_point_att) xps_parse_point(start_point_att, &start_x, &start_y); if (!stroking && !is_filled) /* not filled, when filling */ return; fz_moveto(doc, path, start_x, start_y); for (node = fz_xml_down(root); node; node = fz_xml_next(node)) { if (!strcmp(fz_xml_tag(node), "ArcSegment")) xps_parse_arc_segment(doc, path, node, stroking, &skipped_stroke); if (!strcmp(fz_xml_tag(node), "PolyBezierSegment")) xps_parse_poly_bezier_segment(doc, path, node, stroking, &skipped_stroke); if (!strcmp(fz_xml_tag(node), "PolyLineSegment")) xps_parse_poly_line_segment(doc, path, node, stroking, &skipped_stroke); if (!strcmp(fz_xml_tag(node), "PolyQuadraticBezierSegment")) xps_parse_poly_quadratic_bezier_segment(doc, path, node, stroking, &skipped_stroke); } if (is_closed) { if (stroking && skipped_stroke) fz_lineto(doc, path, start_x, start_y); /* we've skipped using fz_moveto... */ else fz_closepath(doc, path); /* no skipped segments, safe to closepath properly */ } }
static void xps_parse_poly_quadratic_bezier_segment(fz_context *doc, fz_path *path, fz_xml *root, int stroking, int *skipped_stroke) { char *points_att = fz_xml_att(root, "Points"); char *is_stroked_att = fz_xml_att(root, "IsStroked"); float x[2], y[2]; int is_stroked; fz_point pt; char *s; int n; if (!points_att) { fz_warn(doc, "PolyQuadraticBezierSegment element has no points"); return; } is_stroked = 1; if (is_stroked_att && !strcmp(is_stroked_att, "false")) is_stroked = 0; if (!is_stroked) *skipped_stroke = 1; s = points_att; n = 0; while (*s != 0) { while (*s == ' ') s++; s = xps_parse_point(s, &x[n], &y[n]); n ++; if (n == 2) { if (stroking && !is_stroked) { fz_moveto(doc, path, x[1], y[1]); } else { pt = fz_currentpoint(doc, path); fz_curveto(doc, path, (pt.x + 2 * x[0]) / 3, (pt.y + 2 * y[0]) / 3, (x[1] + 2 * x[0]) / 3, (y[1] + 2 * y[0]) / 3, x[1], y[1]); } n = 0; } } }
static void xps_parse_poly_bezier_segment(fz_context *doc, fz_path *path, fz_xml *root, int stroking, int *skipped_stroke) { char *points_att = fz_xml_att(root, "Points"); char *is_stroked_att = fz_xml_att(root, "IsStroked"); float x[3], y[3]; int is_stroked; char *s; int n; if (!points_att) { fz_warn(doc, "PolyBezierSegment element has no points"); return; } is_stroked = 1; if (is_stroked_att && !strcmp(is_stroked_att, "false")) is_stroked = 0; if (!is_stroked) *skipped_stroke = 1; s = points_att; n = 0; while (*s != 0) { while (*s == ' ') s++; s = xps_parse_point(s, &x[n], &y[n]); n ++; if (n == 3) { if (stroking && !is_stroked) fz_moveto(doc, path, x[2], y[2]); else fz_curveto(doc, path, x[0], y[0], x[1], y[1], x[2], y[2]); n = 0; } } }
static void xps_draw_radial_gradient(fz_context *ctx, xps_document *doc, const fz_matrix *ctm, const fz_rect *area, struct stop *stops, int count, fz_xml *root, int spread) { float x0, y0, r0; float x1, y1, r1; float xrad = 1; float yrad = 1; float invscale; int i, ma = 1; fz_matrix local_ctm = *ctm; fz_matrix inv; fz_rect local_area = *area; char *center_att = fz_xml_att(root, "Center"); char *origin_att = fz_xml_att(root, "GradientOrigin"); char *radius_x_att = fz_xml_att(root, "RadiusX"); char *radius_y_att = fz_xml_att(root, "RadiusY"); x0 = y0 = 0.0; x1 = y1 = 1.0; xrad = 1.0; yrad = 1.0; if (origin_att) xps_parse_point(ctx, doc, origin_att, &x0, &y0); if (center_att) xps_parse_point(ctx, doc, center_att, &x1, &y1); if (radius_x_att) xrad = fz_atof(radius_x_att); if (radius_y_att) yrad = fz_atof(radius_y_att); xrad = fz_max(0.01f, xrad); yrad = fz_max(0.01f, yrad); /* scale the ctm to make ellipses */ if (fz_abs(xrad) > FLT_EPSILON) { fz_pre_scale(&local_ctm, 1, yrad/xrad); } if (yrad != 0.0) { invscale = xrad / yrad; y0 = y0 * invscale; y1 = y1 * invscale; } r0 = 0; r1 = xrad; fz_transform_rect(&local_area, fz_invert_matrix(&inv, &local_ctm)); ma = fz_maxi(ma, ceilf(hypotf(local_area.x0 - x0, local_area.y0 - y0) / xrad)); ma = fz_maxi(ma, ceilf(hypotf(local_area.x1 - x0, local_area.y0 - y0) / xrad)); ma = fz_maxi(ma, ceilf(hypotf(local_area.x0 - x0, local_area.y1 - y0) / xrad)); ma = fz_maxi(ma, ceilf(hypotf(local_area.x1 - x0, local_area.y1 - y0) / xrad)); if (spread == SPREAD_REPEAT) { for (i = ma - 1; i >= 0; i--) xps_draw_one_radial_gradient(ctx, doc, &local_ctm, stops, count, 0, x0, y0, r0 + i * xrad, x1, y1, r1 + i * xrad); } else if (spread == SPREAD_REFLECT) { if ((ma % 2) != 0) ma++; for (i = ma - 2; i >= 0; i -= 2) { xps_draw_one_radial_gradient(ctx, doc, &local_ctm, stops, count, 0, x0, y0, r0 + i * xrad, x1, y1, r1 + i * xrad); xps_draw_one_radial_gradient(ctx, doc, &local_ctm, stops, count, 0, x0, y0, r0 + (i + 2) * xrad, x1, y1, r1 + i * xrad); } } else { xps_draw_one_radial_gradient(ctx, doc, &local_ctm, stops, count, 1, x0, y0, r0, x1, y1, r1); } }