[libgxps] Move GXPSPath code to a new file
- From: Carlos Garcia Campos <carlosgc src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [libgxps] Move GXPSPath code to a new file
- Date: Thu, 17 Nov 2011 19:23:35 +0000 (UTC)
commit 8a0d792758332d484f5025b59e207153d507d978
Author: Carlos Garcia Campos <carlosgc gnome org>
Date: Thu Nov 17 16:57:52 2011 +0100
Move GXPSPath code to a new file
libgxps/Makefile.am | 2 +
libgxps/gxps-page.c | 1033 +--------------------------------------------------
libgxps/gxps-path.c | 1029 ++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 1040 insertions(+), 1024 deletions(-)
---
diff --git a/libgxps/Makefile.am b/libgxps/Makefile.am
index f8ae726..1bcbdfb 100644
--- a/libgxps/Makefile.am
+++ b/libgxps/Makefile.am
@@ -10,6 +10,7 @@ NOINST_H_FILES = \
gxps-matrix.h \
gxps-page-private.h \
gxps-parse-utils.h \
+ gxps-path.h \
gxps-private.h
INST_H_FILES = \
@@ -40,6 +41,7 @@ libgxps_la_SOURCES = \
gxps-images.c \
gxps-page.c \
gxps-parse-utils.c \
+ gxps-path.c \
$(NOINST_H_FILES) \
$(INST_H_FILES)
diff --git a/libgxps/gxps-page.c b/libgxps/gxps-page.c
index 9de988a..deb0e1b 100644
--- a/libgxps/gxps-page.c
+++ b/libgxps/gxps-page.c
@@ -25,6 +25,7 @@
#include "gxps-page-private.h"
#include "gxps-matrix.h"
#include "gxps-brush.h"
+#include "gxps-path.h"
#include "gxps-fonts.h"
#include "gxps-links.h"
#include "gxps-images.h"
@@ -198,553 +199,6 @@ gxps_page_render_parser_push (GMarkupParseContext *context,
g_markup_parse_context_push (context, &render_parser, ctx);
}
-typedef struct {
- GXPSRenderContext *ctx;
-
- gchar *data;
- gchar *clip_data;
- cairo_pattern_t *fill_pattern;
- cairo_pattern_t *stroke_pattern;
- cairo_fill_rule_t fill_rule;
- gdouble line_width;
- gdouble *dash;
- guint dash_len;
- gdouble dash_offset;
- cairo_line_cap_t line_cap;
- cairo_line_join_t line_join;
- gdouble miter_limit;
- gdouble opacity;
-
- gboolean is_stroked : 1;
- gboolean is_filled : 1;
- gboolean is_closed : 1;
-} GXPSPath;
-
-static GXPSPath *
-gxps_path_new (GXPSRenderContext *ctx)
-{
- GXPSPath *path;
-
- path = g_slice_new0 (GXPSPath);
- path->ctx = ctx;
-
- /* Default values */
- path->fill_rule = CAIRO_FILL_RULE_EVEN_ODD;
- path->line_width = 1.0;
- path->line_cap = CAIRO_LINE_CAP_BUTT;
- path->line_join = CAIRO_LINE_JOIN_MITER;
- path->miter_limit = 10.0;
- path->opacity = 1.0;
- path->is_filled = TRUE;
- path->is_stroked = TRUE;
-
- return path;
-}
-
-static void
-gxps_path_free (GXPSPath *path)
-{
- if (G_UNLIKELY (!path))
- return;
-
- g_free (path->data);
- g_free (path->clip_data);
- cairo_pattern_destroy (path->fill_pattern);
- cairo_pattern_destroy (path->stroke_pattern);
- g_free (path->dash);
-
- g_slice_free (GXPSPath, path);
-}
-
-typedef enum {
- PD_TOKEN_INVALID,
- PD_TOKEN_NUMBER,
- PD_TOKEN_COMMA,
- PD_TOKEN_COMMAND,
- PD_TOKEN_EOF
-} PathDataTokenType;
-
-typedef struct {
- gchar *iter;
- gchar *end;
- PathDataTokenType type;
- gdouble number;
- gchar command;
-} PathDataToken;
-
-static const gchar *
-path_data_token_type_to_string (PathDataTokenType type)
-{
- switch (type) {
- case PD_TOKEN_INVALID:
- return "Invalid";
- case PD_TOKEN_NUMBER:
- return "Number";
- case PD_TOKEN_COMMA:
- return "Comma";
- case PD_TOKEN_COMMAND:
- return "Command";
- case PD_TOKEN_EOF:
- return "Eof";
- default:
- g_assert_not_reached ();
- }
-}
-
-#ifdef GXPS_ENABLE_DEBUG
-static void
-print_token (PathDataToken *token)
-{
- switch (token->type) {
- case PD_TOKEN_INVALID:
- g_debug ("Invalid token");
- break;
- case PD_TOKEN_NUMBER:
- g_debug ("Token number: %f", token->number);
- break;
- case PD_TOKEN_COMMA:
- g_debug ("Token comma");
- break;
- case PD_TOKEN_COMMAND:
- g_debug ("Token command %c", token->command);
- break;
- case PD_TOKEN_EOF:
- g_debug ("Token EOF");
- break;
- default:
- g_assert_not_reached ();
- }
-}
-#endif /* GXPS_ENABLE_DEBUG */
-
-static inline gboolean
-advance_char (PathDataToken *token)
-{
- token->iter++;
-
- if (G_UNLIKELY (token->iter == token->end))
- return FALSE;
-
- return TRUE;
-}
-
-static inline gboolean
-_isspace (char c)
-{
- return c == ' ' || c == '\t';
-}
-
-static void
-skip_spaces (PathDataToken *token)
-{
- do {
- if (!_isspace (*token->iter))
- return;
- } while (advance_char (token));
-}
-
-static gboolean
-path_data_iter_next (PathDataToken *token,
- GError **error)
-{
- gchar c;
-
- skip_spaces (token);
-
- if (token->iter == token->end) {
- token->type = PD_TOKEN_EOF;
- GXPS_DEBUG (print_token (token));
-
- return TRUE;
- }
-
- c = *token->iter;
-
- if (g_ascii_isdigit (c) || c == '+' || c == '-') {
- gchar *start;
- gchar *str;
-
- start = token->iter;
- token->iter++;
- while (token->iter != token->end && (g_ascii_isdigit (*token->iter) || *token->iter == '.'))
- token->iter++;
- str = g_strndup (start, token->iter - start);
- if (!gxps_value_get_double (str, &token->number)) {
- g_set_error (error,
- GXPS_PAGE_ERROR,
- GXPS_PAGE_ERROR_RENDER,
- "Error parsing abreviated path: error converting token %s (%s) to double at %s",
- path_data_token_type_to_string (token->type),
- str, token->iter);
- g_free (str);
-
- return FALSE;
- }
- g_free (str);
- token->type = PD_TOKEN_NUMBER;
- } else if (c == ',') {
- token->type = PD_TOKEN_COMMA;
- token->iter++;
- } else if (g_ascii_isalpha (c)) {
- token->command = c;
- token->type = PD_TOKEN_COMMAND;
- token->iter++;
- } else {
- token->type = PD_TOKEN_INVALID;
- token->iter++;
- }
-
- GXPS_DEBUG (print_token (token));
-
- return TRUE;
-}
-
-static void
-path_data_parse_error (PathDataToken *token,
- PathDataTokenType expected,
- GError **error)
-{
- if (expected == PD_TOKEN_INVALID)
- g_set_error (error,
- GXPS_PAGE_ERROR,
- GXPS_PAGE_ERROR_RENDER,
- "Error parsing abreviated path: unexpected token %s at %s",
- path_data_token_type_to_string (token->type),
- token->iter);
- else
- g_set_error (error,
- GXPS_PAGE_ERROR,
- GXPS_PAGE_ERROR_RENDER,
- "Error parsing abreviated path: expected token %s, but %s found at %s",
- path_data_token_type_to_string (token->type),
- path_data_token_type_to_string (expected),
- token->iter);
-}
-
-static gboolean
-path_data_get_point (PathDataToken *token,
- gdouble *x,
- gdouble *y,
- GError **error)
-{
- *x = token->number;
-
- if (!path_data_iter_next (token, error))
- return FALSE;
- if (token->type != PD_TOKEN_COMMA) {
- path_data_parse_error (token, PD_TOKEN_COMMA, error);
- return FALSE;
- }
-
- if (!path_data_iter_next (token, error))
- return FALSE;
- if (token->type != PD_TOKEN_NUMBER) {
- path_data_parse_error (token, PD_TOKEN_NUMBER, error);
- return FALSE;
- }
- *y = token->number;
-
- return TRUE;
-}
-
-static gboolean
-path_data_parse (const gchar *data,
- cairo_t *cr,
- GError **error)
-{
- PathDataToken token;
- gdouble control_point_x;
- gdouble control_point_y;
-
- token.iter = (gchar *)data;
- token.end = token.iter + strlen (data);
-
- if (!path_data_iter_next (&token, error))
- return FALSE;
- if (G_UNLIKELY (token.type != PD_TOKEN_COMMAND))
- return TRUE;
-
- control_point_x = control_point_y = 0;
-
- do {
- gchar command = token.command;
- gboolean is_rel = FALSE;
-
- if (!path_data_iter_next (&token, error))
- return FALSE;
-
- switch (command) {
- /* Move */
- case 'm':
- is_rel = TRUE;
- case 'M':
- while (token.type == PD_TOKEN_NUMBER) {
- gdouble x, y;
-
- if (!path_data_get_point (&token, &x, &y, error))
- return FALSE;
-
- GXPS_DEBUG (g_message ("%s (%f, %f)", is_rel ? "rel_move_to" : "move_to", x, y));
-
- if (is_rel)
- cairo_rel_move_to (cr, x, y);
- else
- cairo_move_to (cr, x, y);
-
- if (!path_data_iter_next (&token, error))
- return FALSE;
- }
- control_point_x = control_point_y = 0;
- break;
- /* Line */
- case 'l':
- is_rel = TRUE;
- case 'L':
- while (token.type == PD_TOKEN_NUMBER) {
- gdouble x, y;
-
- if (!path_data_get_point (&token, &x, &y, error))
- return FALSE;
-
- GXPS_DEBUG (g_message ("%s (%f, %f)", is_rel ? "rel_line_to" : "line_to", x, y));
-
- if (is_rel)
- cairo_rel_line_to (cr, x, y);
- else
- cairo_line_to (cr, x, y);
-
- if (!path_data_iter_next (&token, error))
- return FALSE;
- }
- control_point_x = control_point_y = 0;
- break;
- /* Horizontal Line */
- case 'h':
- is_rel = TRUE;
- case 'H':
- while (token.type == PD_TOKEN_NUMBER) {
- gdouble x, y;
- gdouble offset;
-
- offset = token.number;
-
- GXPS_DEBUG (g_message ("%s (%f)", is_rel ? "rel_hline_to" : "hline_to", offset));
-
- cairo_get_current_point (cr, &x, &y);
- x = is_rel ? x + offset : offset;
- cairo_line_to (cr, x, y);
-
- if (!path_data_iter_next (&token, error))
- return FALSE;
- }
- control_point_x = control_point_y = 0;
- break;
- /* Vertical Line */
- case 'v':
- is_rel = TRUE;
- case 'V':
- while (token.type == PD_TOKEN_NUMBER) {
- gdouble x, y;
- gdouble offset;
-
- offset = token.number;
-
- GXPS_DEBUG (g_message ("%s (%f)", is_rel ? "rel_vline_to" : "vline_to", offset));
-
- cairo_get_current_point (cr, &x, &y);
- y = is_rel ? y + offset : offset;
- cairo_line_to (cr, x, y);
-
- if (!path_data_iter_next (&token, error))
- return FALSE;
- }
- control_point_x = control_point_y = 0;
- break;
- /* Cubic BÃzier curve */
- case 'c':
- is_rel = TRUE;
- case 'C':
- while (token.type == PD_TOKEN_NUMBER) {
- gdouble x1, y1, x2, y2, x3, y3;
-
- if (!path_data_get_point (&token, &x1, &y1, error))
- return FALSE;
-
- if (!path_data_iter_next (&token, error))
- return FALSE;
- if (!path_data_get_point (&token, &x2, &y2, error))
- return FALSE;
-
- if (!path_data_iter_next (&token, error))
- return FALSE;
- if (!path_data_get_point (&token, &x3, &y3, error))
- return FALSE;
-
- GXPS_DEBUG (g_message ("%s (%f, %f, %f, %f, %f, %f)", is_rel ? "rel_curve_to" : "curve_to",
- x1, y1, x2, y2, x3, y3));
-
- if (is_rel)
- cairo_rel_curve_to (cr, x1, y1, x2, y2, x3, y3);
- else
- cairo_curve_to (cr, x1, y1, x2, y2, x3, y3);
-
- control_point_x = x3 - x2;
- control_point_y = y3 - y2;
-
- if (!path_data_iter_next (&token, error))
- return FALSE;
- }
- break;
- /* Quadratic BÃzier curve */
- case 'q':
- is_rel = TRUE;
- case 'Q':
- while (token.type == PD_TOKEN_NUMBER) {
- gdouble x1, y1, x2, y2;
- gdouble x, y;
-
- if (!path_data_get_point (&token, &x1, &y1, error))
- return FALSE;
-
- if (!path_data_iter_next (&token, error))
- return FALSE;
- if (!path_data_get_point (&token, &x2, &y2, error))
- return FALSE;
-
- GXPS_DEBUG (g_message ("%s (%f, %f, %f, %f)", is_rel ? "rel_quad_curve_to" : "quad_curve_to",
- x1, y1, x2, y2));
-
- cairo_get_current_point (cr, &x, &y);
- x1 += is_rel ? x : 0;
- y1 += is_rel ? y : 0;
- x2 += is_rel ? x : 0;
- y2 += is_rel ? y : 0;
- cairo_curve_to (cr,
- 2.0 / 3.0 * x1 + 1.0 / 3.0 * x,
- 2.0 / 3.0 * y1 + 1.0 / 3.0 * y,
- 2.0 / 3.0 * x1 + 1.0 / 3.0 * x2,
- 2.0 / 3.0 * y1 + 1.0 / 3.0 * y2,
- x2, y2);
-
- if (!path_data_iter_next (&token, error))
- return FALSE;
- }
- control_point_x = control_point_y = 0;
- break;
- /* Smooth Cubic BÃzier curve */
- case 's':
- is_rel = TRUE;
- case 'S':
- while (token.type == PD_TOKEN_NUMBER) {
- gdouble x2, y2, x3, y3;
-
- if (!path_data_get_point (&token, &x2, &y2, error))
- return FALSE;
-
- if (!path_data_iter_next (&token, error))
- return FALSE;
- if (!path_data_get_point (&token, &x3, &y3, error))
- return FALSE;
-
- GXPS_DEBUG (g_message ("%s (%f, %f, %f, %f, %f, %f)", is_rel ? "rel_smooth_curve_to" : "smooth_curve_to",
- control_point_x, control_point_y, x2, y2, x3, y3));
-
- if (is_rel) {
- cairo_rel_curve_to (cr, control_point_x, control_point_y, x2, y2, x3, y3);
- } else {
- gdouble x, y;
-
- cairo_get_current_point (cr, &x, &y);
- cairo_curve_to (cr, x + control_point_x, y + control_point_y, x2, y2, x3, y3);
- }
-
- control_point_x = x3 - x2;
- control_point_y = y3 - y2;
-
- if (!path_data_iter_next (&token, error))
- return FALSE;
- }
- break;
- /* Elliptical Arc */
- case 'a':
- is_rel = TRUE;
- case 'A':
- while (token.type == PD_TOKEN_NUMBER) {
- gdouble xr, yr, rx, farc, fsweep, x, y;
-
- if (!path_data_get_point (&token, &xr, &yr, error))
- return FALSE;
-
- if (!path_data_iter_next (&token, error))
- return FALSE;
- if (token.type != PD_TOKEN_NUMBER) {
- path_data_parse_error (&token, PD_TOKEN_NUMBER, error);
- return FALSE;
- }
- rx = token.number;
-
- if (!path_data_iter_next (&token, error))
- return FALSE;
- if (token.type != PD_TOKEN_NUMBER) {
- path_data_parse_error (&token, PD_TOKEN_NUMBER, error);
- return FALSE;
- }
- farc = token.number;
-
- if (!path_data_iter_next (&token, error))
- return FALSE;
- if (token.type != PD_TOKEN_NUMBER) {
- path_data_parse_error (&token, PD_TOKEN_NUMBER, error);
- return FALSE;
- }
- fsweep = token.number;
-
- if (!path_data_iter_next (&token, error))
- return FALSE;
- if (!path_data_get_point (&token, &x, &y, error))
- return FALSE;
-
- GXPS_DEBUG (g_message ("%s (%f, %f, %f, %f, %f, %f, %f)", is_rel ? "rel_arc" : "arc",
- xr, yr, rx, farc, fsweep, x, y));
- GXPS_DEBUG (g_debug ("Unsupported command in path: %c", command));
-
- if (!path_data_iter_next (&token, error))
- return FALSE;
- }
- control_point_x = control_point_y = 0;
- break;
- /* Close */
- case 'z':
- is_rel = TRUE;
- case 'Z':
- cairo_close_path (cr);
- GXPS_DEBUG (g_message ("close_path"));
- control_point_x = control_point_y = 0;
- break;
- /* Fill Rule */
- case 'F': {
- gint fill_rule;
-
- fill_rule = (gint)token.number;
- cairo_set_fill_rule (cr,
- (fill_rule == 0) ?
- CAIRO_FILL_RULE_EVEN_ODD :
- CAIRO_FILL_RULE_WINDING);
- GXPS_DEBUG (g_message ("set_fill_rule (%s)", (fill_rule == 0) ? "EVEN_ODD" : "WINDING"));
-
- if (!path_data_iter_next (&token, error))
- return FALSE;
- }
- control_point_x = control_point_y = 0;
- break;
- default:
- g_assert_not_reached ();
- }
- } while (token.type == PD_TOKEN_COMMAND);
-
- return TRUE;
-}
-
static gboolean
gxps_dash_array_parse (const gchar *dash,
gdouble **dashes_out,
@@ -806,61 +260,6 @@ gxps_line_join_parse (const gchar *join)
return CAIRO_LINE_JOIN_MITER;
}
-static cairo_fill_rule_t
-gxps_fill_rule_parse (const gchar *rule)
-{
- if (strcmp (rule, "EvenOdd") == 0)
- return CAIRO_FILL_RULE_EVEN_ODD;
- else if (strcmp (rule, "NonZero") == 0)
- return CAIRO_FILL_RULE_WINDING;
- return CAIRO_FILL_RULE_EVEN_ODD;
-}
-
-static gboolean
-gxps_points_parse (const gchar *points,
- gdouble **coords,
- guint *n_points)
-{
- gchar **items;
- guint i, j = 0;
- gboolean retval = TRUE;
-
- *n_points = 0;
- items = g_strsplit (points, " ", -1);
- if (!items)
- return FALSE;
-
- for (i = 0; items[i] != NULL; i++) {
- if (*items[i] != '\0') /* don't count empty string */
- (*n_points)++;
- }
-
- if (*n_points == 0)
- return FALSE;
-
- *coords = g_malloc (*n_points * 2 * sizeof (gdouble));
-
- for (i = 0; items[i] != NULL; i++) {
- gdouble x, y;
-
- if (*items[i] == '\0')
- continue;
-
- if (!gxps_point_parse (items[i], &x, &y)) {
- g_free (*coords);
- retval = FALSE;
- break;
- }
-
- coords[0][j++] = x;
- coords[0][j++] = y;
- }
-
- g_strfreev (items);
-
- return retval;
-}
-
typedef struct {
GXPSRenderContext *ctx;
@@ -964,420 +363,6 @@ static GMarkupParser canvas_parser = {
NULL
};
-static void
-path_geometry_start_element (GMarkupParseContext *context,
- const gchar *element_name,
- const gchar **names,
- const gchar **values,
- gpointer user_data,
- GError **error)
-{
- GXPSPath *path = (GXPSPath *)user_data;
-
- if (strcmp (element_name, "PathGeometry.Transform") == 0) {
- GXPSMatrix *matrix;
-
- matrix = gxps_matrix_new (path->ctx);
- gxps_matrix_parser_push (context, matrix);
- } else if (strcmp (element_name, "PathFigure") == 0) {
- gint i;
- gboolean has_start_point = FALSE;
-
- for (i = 0; names[i] != NULL; i++) {
- if (strcmp (names[i], "StartPoint") == 0) {
- gdouble x, y;
-
- if (!gxps_point_parse (values[i], &x, &y)) {
- gxps_parse_error (context,
- path->ctx->page->priv->source,
- G_MARKUP_ERROR_INVALID_CONTENT,
- "PathFigure", "StartPoint",
- values[i], error);
- return;
- }
-
- GXPS_DEBUG (g_message ("move_to (%f, %f)", x, y));
- cairo_move_to (path->ctx->cr, x, y);
- has_start_point = TRUE;
- } else if (strcmp (names[i], "IsClosed") == 0) {
- gboolean is_closed;
-
- if (!gxps_value_get_boolean (values[i], &is_closed)) {
- gxps_parse_error (context,
- path->ctx->page->priv->source,
- G_MARKUP_ERROR_INVALID_CONTENT,
- "PathFigure", "IsClosed",
- values[i], error);
- return;
- }
- path->is_closed = is_closed;
- } else if (strcmp (names[i], "IsFilled") == 0) {
- gboolean is_filled;
-
- if (!gxps_value_get_boolean (values[i], &is_filled)) {
- gxps_parse_error (context,
- path->ctx->page->priv->source,
- G_MARKUP_ERROR_INVALID_CONTENT,
- "PathFigure", "IsFilled",
- values[i], error);
- return;
- }
- path->is_filled = is_filled;
- }
- }
-
- if (!has_start_point) {
- gxps_parse_error (context,
- path->ctx->page->priv->source,
- G_MARKUP_ERROR_MISSING_ATTRIBUTE,
- "PathFigure", "StartPoint",
- NULL, error);
- return;
- }
- } else if (strcmp (element_name, "PolyLineSegment") == 0) {
- gint i, j;
- const gchar *points_str = NULL;
- gdouble *points = NULL;
- guint n_points;
- gboolean is_stroked = TRUE;
-
- for (i = 0; names[i] != NULL; i++) {
- if (strcmp (names[i], "Points") == 0) {
- points_str = values[i];
- } else if (strcmp (names[i], "IsStroked") == 0) {
- if (!gxps_value_get_boolean (values[i], &is_stroked)) {
- gxps_parse_error (context,
- path->ctx->page->priv->source,
- G_MARKUP_ERROR_INVALID_CONTENT,
- "PolyLineSegment", "IsStroked",
- points_str, error);
- return;
- }
- }
- }
-
- if (!is_stroked)
- return;
-
- if (!points_str) {
- gxps_parse_error (context,
- path->ctx->page->priv->source,
- G_MARKUP_ERROR_MISSING_ATTRIBUTE,
- "PolyLineSegment", "Points",
- NULL, error);
- return;
- }
-
- if (!gxps_points_parse (points_str, &points, &n_points)) {
- gxps_parse_error (context,
- path->ctx->page->priv->source,
- G_MARKUP_ERROR_INVALID_CONTENT,
- "PolyLineSegment", "Points",
- points_str, error);
- return;
- }
-
- for (j = 0; j < n_points * 2; j += 2) {
- GXPS_DEBUG (g_message ("line_to (%f, %f)", points[j], points[j + 1]));
- cairo_line_to (path->ctx->cr, points[j], points[j + 1]);
- }
-
- g_free (points);
- } else if (strcmp (element_name, "PolyBezierSegment") == 0) {
- gint i, j;
- const gchar *points_str = NULL;
- gdouble *points = NULL;
- guint n_points;
- gboolean is_stroked = TRUE;
-
- for (i = 0; names[i] != NULL; i++) {
- if (strcmp (names[i], "Points") == 0) {
- points_str = values[i];
-
- } else if (strcmp (names[i], "IsStroked") == 0) {
- if (!gxps_value_get_boolean (values[i], &is_stroked)) {
- gxps_parse_error (context,
- path->ctx->page->priv->source,
- G_MARKUP_ERROR_INVALID_CONTENT,
- "PolyBezierSegment", "IsStroked",
- points_str, error);
- return;
- }
- }
- }
-
- if (!is_stroked)
- return;
-
- if (!points_str) {
- gxps_parse_error (context,
- path->ctx->page->priv->source,
- G_MARKUP_ERROR_MISSING_ATTRIBUTE,
- "PolyBezierSegment", "Points",
- NULL, error);
- return;
- }
-
- if (!gxps_points_parse (points_str, &points, &n_points)) {
- gxps_parse_error (context,
- path->ctx->page->priv->source,
- G_MARKUP_ERROR_INVALID_CONTENT,
- "PolyBezierSegment", "Points",
- points_str, error);
- return;
- }
-
- for (j = 0; j < n_points * 2; j += 6) {
- GXPS_DEBUG (g_message ("curve_to (%f, %f, %f, %f, %f, %f)",
- points[j], points[j + 1],
- points[j + 2], points[j + 3],
- points[j + 4], points[j + 5]));
- cairo_curve_to (path->ctx->cr,
- points[j], points[j + 1],
- points[j + 2], points[j + 3],
- points[j + 4], points[j + 5]);
- }
-
- g_free (points);
- } else if (strcmp (element_name, "PolyQuadraticBezierSegment") == 0) {
- gint i, j;
- const gchar *points_str = NULL;
- gdouble *points = NULL;
- guint n_points;
- gboolean is_stroked = TRUE;
-
- for (i = 0; names[i] != NULL; i++) {
- if (strcmp (names[i], "Points") == 0) {
- points_str = values[i];
-
- } else if (strcmp (names[i], "IsStroked") == 0) {
- if (!gxps_value_get_boolean (values[i], &is_stroked)) {
- gxps_parse_error (context,
- path->ctx->page->priv->source,
- G_MARKUP_ERROR_INVALID_CONTENT,
- "PolyQuadraticBezierSegment", "IsStroked",
- points_str, error);
- return;
- }
- }
- }
-
- if (!is_stroked)
- return;
-
- if (!points_str) {
- gxps_parse_error (context,
- path->ctx->page->priv->source,
- G_MARKUP_ERROR_MISSING_ATTRIBUTE,
- "PolyQuadraticBezierSegment", "Points",
- NULL, error);
- return;
- }
-
- if (!gxps_points_parse (points_str, &points, &n_points)) {
- gxps_parse_error (context,
- path->ctx->page->priv->source,
- G_MARKUP_ERROR_INVALID_CONTENT,
- "PolyQuadraticBezierSegment", "Points",
- points_str, error);
- return;
- }
-
- for (j = 0; j < n_points * 2; j += 4) {
- gdouble x1, y1, x2, y2;
- gdouble x, y;
-
- x1 = points[j];
- y1 = points[j + 1];
- x2 = points[j + 2];
- y2 = points[j + 3];
-
- GXPS_DEBUG (g_message ("quad_curve_to (%f, %f, %f, %f)", x1, y1, x2, y2));
- cairo_get_current_point (path->ctx->cr, &x, &y);
- cairo_curve_to (path->ctx->cr,
- 2.0 / 3.0 * x1 + 1.0 / 3.0 * x,
- 2.0 / 3.0 * y1 + 1.0 / 3.0 * y,
- 2.0 / 3.0 * x1 + 1.0 / 3.0 * x2,
- 2.0 / 3.0 * y1 + 1.0 / 3.0 * y2,
- x2, y2);
- }
-
- g_free (points);
- } else if (strcmp (element_name, "ArcSegment") == 0) {
- GXPS_DEBUG (g_debug ("Unsupported PathGeometry: ArcSegment"));
- }
-}
-
-static void
-path_geometry_end_element (GMarkupParseContext *context,
- const gchar *element_name,
- gpointer user_data,
- GError **error)
-{
- GXPSPath *path = (GXPSPath *)user_data;
-
- if (strcmp (element_name, "PathGeometry.Transform") == 0) {
- GXPSMatrix *matrix;
-
- matrix = g_markup_parse_context_pop (context);
- GXPS_DEBUG (g_message ("transform (%f, %f, %f, %f) [%f, %f]",
- matrix->matrix.xx, matrix->matrix.yx,
- matrix->matrix.xy, matrix->matrix.yy,
- matrix->matrix.x0, matrix->matrix.y0));
- cairo_transform (path->ctx->cr, &matrix->matrix);
-
- gxps_matrix_free (matrix);
- } else if (strcmp (element_name, "PathFigure") == 0) {
- if (path->is_closed) {
- GXPS_DEBUG (g_message ("close_path"));
- cairo_close_path (path->ctx->cr);
- }
-
- if (path->is_filled && path->fill_pattern) {
- GXPS_DEBUG (g_message ("fill"));
- cairo_set_source (path->ctx->cr, path->fill_pattern);
- if (path->is_stroked && path->stroke_pattern)
- cairo_fill_preserve (path->ctx->cr);
- else
- cairo_fill (path->ctx->cr);
- }
-
- if (path->stroke_pattern) {
- GXPS_DEBUG (g_message ("stroke"));
- cairo_set_source (path->ctx->cr, path->stroke_pattern);
- cairo_set_line_width (path->ctx->cr, path->line_width);
- if (path->dash && path->dash_len > 0)
- cairo_set_dash (path->ctx->cr, path->dash, path->dash_len, path->dash_offset);
- cairo_set_line_join (path->ctx->cr, path->line_join);
- cairo_set_miter_limit (path->ctx->cr, path->miter_limit);
- cairo_stroke (path->ctx->cr);
- }
- }
-}
-
-static GMarkupParser path_geometry_parser = {
- path_geometry_start_element,
- path_geometry_end_element,
- NULL,
- NULL
-};
-
-static void
-path_start_element (GMarkupParseContext *context,
- const gchar *element_name,
- const gchar **names,
- const gchar **values,
- gpointer user_data,
- GError **error)
-{
- GXPSPath *path = (GXPSPath *)user_data;
-
- if (strcmp (element_name, "Path.Fill") == 0) {
- GXPSBrush *brush;
-
- brush = gxps_brush_new (path->ctx);
- gxps_brush_parser_push (context, brush);
- } else if (strcmp (element_name, "Path.Stroke") == 0) {
- GXPSBrush *brush;
-
- brush = gxps_brush_new (path->ctx);
- gxps_brush_parser_push (context, brush);
- } else if (strcmp (element_name, "Path.Data") == 0) {
- } else if (strcmp (element_name, "PathGeometry") == 0) {
- gint i;
-
- for (i = 0; names[i] != NULL; i++) {
- if (strcmp (names[i], "Figures") == 0) {
- path->data = g_strdup (values[i]);
- } else if (strcmp (names[i], "FillRule") == 0) {
- path->fill_rule = gxps_fill_rule_parse (values[i]);
- GXPS_DEBUG (g_message ("set_fill_rule (%s)", values[i]));
- } else if (strcmp (names[i], "Transform") == 0) {
- cairo_matrix_t matrix;
-
- if (!gxps_matrix_parse (values[i], &matrix)) {
- gxps_parse_error (context,
- path->ctx->page->priv->source,
- G_MARKUP_ERROR_INVALID_CONTENT,
- "PathGeometry", "Transform",
- values[i], error);
- return;
- }
- GXPS_DEBUG (g_message ("transform (%f, %f, %f, %f) [%f, %f]",
- matrix.xx, matrix.yx,
- matrix.xy, matrix.yy,
- matrix.x0, matrix.y0));
- cairo_transform (path->ctx->cr, &matrix);
- }
- }
-
- if (!path->data) {
- cairo_set_fill_rule (path->ctx->cr, path->fill_rule);
- if (path->clip_data) {
- if (!path_data_parse (path->clip_data, path->ctx->cr, error))
- return;
- GXPS_DEBUG (g_message ("clip"));
- cairo_clip (path->ctx->cr);
- }
- g_markup_parse_context_push (context, &path_geometry_parser, path);
- }
- } else if (strcmp (element_name, "Path.RenderTransform") == 0) {
- GXPSMatrix *matrix;
-
- matrix = gxps_matrix_new (path->ctx);
- gxps_matrix_parser_push (context, matrix);
- } else {
- GXPS_DEBUG (g_debug ("Unsupported path child %s", element_name));
- }
-}
-
-static void
-path_end_element (GMarkupParseContext *context,
- const gchar *element_name,
- gpointer user_data,
- GError **error)
-{
- GXPSPath *path = (GXPSPath *)user_data;
-
- if (strcmp (element_name, "Path.Fill") == 0) {
- GXPSBrush *brush;
-
- brush = g_markup_parse_context_pop (context);
- path->fill_pattern = cairo_pattern_reference (brush->pattern);
- gxps_brush_free (brush);
- } else if (strcmp (element_name, "Path.Stroke") == 0) {
- GXPSBrush *brush;
-
- brush = g_markup_parse_context_pop (context);
- path->stroke_pattern = cairo_pattern_reference (brush->pattern);
- gxps_brush_free (brush);
- } else if (strcmp (element_name, "Path.Data") == 0) {
- } else if (strcmp (element_name, "PathGeometry") == 0) {
- if (!path->data)
- g_markup_parse_context_pop (context);
- } else if (strcmp (element_name, "Path.RenderTransform") == 0) {
- GXPSMatrix *matrix;
-
- matrix = g_markup_parse_context_pop (context);
- GXPS_DEBUG (g_message ("transform (%f, %f, %f, %f) [%f, %f]",
- matrix->matrix.xx, matrix->matrix.yx,
- matrix->matrix.xy, matrix->matrix.yy,
- matrix->matrix.x0, matrix->matrix.y0));
- cairo_transform (path->ctx->cr, &matrix->matrix);
-
- gxps_matrix_free (matrix);
- } else {
-
- }
-}
-
-static GMarkupParser path_parser = {
- path_start_element,
- path_end_element,
- NULL,
- NULL
-};
-
typedef struct {
GXPSRenderContext *ctx;
gdouble em_size;
@@ -2118,7 +1103,7 @@ render_start_element (GMarkupParseContext *context,
if (path->opacity != 1.0)
cairo_push_group (ctx->cr);
- g_markup_parse_context_push (context, &path_parser, path);
+ gxps_path_parser_push (context, path);
} else if (strcmp (element_name, "Glyphs") == 0) {
GXPSGlyphs *glyphs;
gchar *font_uri = NULL;
@@ -2311,7 +1296,7 @@ render_start_element (GMarkupParseContext *context,
}
GXPS_DEBUG (g_message ("set_opacity (%f)", canvas->opacity));
} else if (strcmp (names[i], "Clip") == 0) {
- if (!path_data_parse (values[i], ctx->cr, error)) {
+ if (!gxps_path_parse (values[i], ctx->cr, error)) {
gxps_parse_error (context,
ctx->page->priv->source,
G_MARKUP_ERROR_INVALID_CONTENT,
@@ -2361,7 +1346,7 @@ render_end_element (GMarkupParseContext *context,
cairo_set_fill_rule (ctx->cr, path->fill_rule);
if (path->clip_data) {
- if (!path_data_parse (path->clip_data, ctx->cr, error)) {
+ if (!gxps_path_parse (path->clip_data, ctx->cr, error)) {
if (path->opacity != 1.0)
cairo_pattern_destroy (cairo_pop_group (ctx->cr));
gxps_path_free (path);
@@ -2371,7 +1356,7 @@ render_end_element (GMarkupParseContext *context,
cairo_clip (ctx->cr);
}
- if (!path_data_parse (path->data, ctx->cr, error)) {
+ if (!gxps_path_parse (path->data, ctx->cr, error)) {
if (path->opacity != 1.0)
cairo_pattern_destroy (cairo_pop_group (ctx->cr));
gxps_path_free (path);
@@ -2439,7 +1424,7 @@ render_end_element (GMarkupParseContext *context,
}
if (glyphs->clip_data) {
- if (!path_data_parse (glyphs->clip_data, ctx->cr, error)) {
+ if (!gxps_path_parse (glyphs->clip_data, ctx->cr, error)) {
if (glyphs->opacity_mask)
cairo_pattern_destroy (cairo_pop_group (ctx->cr));
if (glyphs->opacity != 1.0)
@@ -2646,7 +1631,7 @@ links_start_element (GMarkupParseContext *context,
return;
} else if (strcmp (names[i], "Clip") == 0) {
/* FIXME: do we really need clips? */
- if (!path_data_parse (values[i], ctx->cr, error))
+ if (!gxps_path_parse (values[i], ctx->cr, error))
return;
GXPS_DEBUG (g_message ("clip"));
cairo_clip (ctx->cr);
@@ -2773,7 +1758,7 @@ links_end_element (GMarkupParseContext *context,
cairo_rectangle_t area;
if (path_link->data)
- path_data_parse (path_link->data, ctx->cr, error);
+ gxps_path_parse (path_link->data, ctx->cr, error);
cairo_path_extents (ctx->cr, &x1, &y1, &x2, &y2);
cairo_user_to_device (ctx->cr, &x1, &y1);
@@ -3013,7 +1998,7 @@ anchors_end_element (GMarkupParseContext *context,
cairo_rectangle_t *rect;
if (path_anchor->data)
- path_data_parse (path_anchor->data, ctx->cr, error);
+ gxps_path_parse (path_anchor->data, ctx->cr, error);
cairo_path_extents (ctx->cr, &x1, &y1, &x2, &y2);
cairo_user_to_device (ctx->cr, &x1, &y1);
diff --git a/libgxps/gxps-path.c b/libgxps/gxps-path.c
new file mode 100644
index 0000000..e046ac7
--- /dev/null
+++ b/libgxps/gxps-path.c
@@ -0,0 +1,1029 @@
+/* GXPSPath
+ *
+ * Copyright (C) 2011 Carlos Garcia Campos <carlosgc gnome org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#include <string.h>
+
+#include "gxps-path.h"
+#include "gxps-matrix.h"
+#include "gxps-brush.h"
+#include "gxps-parse-utils.h"
+#include "gxps-debug.h"
+
+typedef enum {
+ PD_TOKEN_INVALID,
+ PD_TOKEN_NUMBER,
+ PD_TOKEN_COMMA,
+ PD_TOKEN_COMMAND,
+ PD_TOKEN_EOF
+} PathDataTokenType;
+
+typedef struct {
+ gchar *iter;
+ gchar *end;
+ PathDataTokenType type;
+ gdouble number;
+ gchar command;
+} PathDataToken;
+
+GXPSPath *
+gxps_path_new (GXPSRenderContext *ctx)
+{
+ GXPSPath *path;
+
+ path = g_slice_new0 (GXPSPath);
+ path->ctx = ctx;
+
+ /* Default values */
+ path->fill_rule = CAIRO_FILL_RULE_EVEN_ODD;
+ path->line_width = 1.0;
+ path->line_cap = CAIRO_LINE_CAP_BUTT;
+ path->line_join = CAIRO_LINE_JOIN_MITER;
+ path->miter_limit = 10.0;
+ path->opacity = 1.0;
+ path->is_filled = TRUE;
+ path->is_stroked = TRUE;
+
+ return path;
+}
+
+void
+gxps_path_free (GXPSPath *path)
+{
+ if (G_UNLIKELY (!path))
+ return;
+
+ g_free (path->data);
+ g_free (path->clip_data);
+ cairo_pattern_destroy (path->fill_pattern);
+ cairo_pattern_destroy (path->stroke_pattern);
+ g_free (path->dash);
+
+ g_slice_free (GXPSPath, path);
+}
+
+static const gchar *
+path_data_token_type_to_string (PathDataTokenType type)
+{
+ switch (type) {
+ case PD_TOKEN_INVALID:
+ return "Invalid";
+ case PD_TOKEN_NUMBER:
+ return "Number";
+ case PD_TOKEN_COMMA:
+ return "Comma";
+ case PD_TOKEN_COMMAND:
+ return "Command";
+ case PD_TOKEN_EOF:
+ return "Eof";
+ default:
+ g_assert_not_reached ();
+ }
+}
+
+#ifdef GXPS_ENABLE_DEBUG
+static void
+print_token (PathDataToken *token)
+{
+ switch (token->type) {
+ case PD_TOKEN_INVALID:
+ g_debug ("Invalid token");
+ break;
+ case PD_TOKEN_NUMBER:
+ g_debug ("Token number: %f", token->number);
+ break;
+ case PD_TOKEN_COMMA:
+ g_debug ("Token comma");
+ break;
+ case PD_TOKEN_COMMAND:
+ g_debug ("Token command %c", token->command);
+ break;
+ case PD_TOKEN_EOF:
+ g_debug ("Token EOF");
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+}
+#endif /* GXPS_ENABLE_DEBUG */
+
+static inline gboolean
+advance_char (PathDataToken *token)
+{
+ token->iter++;
+
+ if (G_UNLIKELY (token->iter == token->end))
+ return FALSE;
+
+ return TRUE;
+}
+
+static inline gboolean
+_isspace (char c)
+{
+ return c == ' ' || c == '\t';
+}
+
+static void
+skip_spaces (PathDataToken *token)
+{
+ do {
+ if (!_isspace (*token->iter))
+ return;
+ } while (advance_char (token));
+}
+
+static gboolean
+path_data_iter_next (PathDataToken *token,
+ GError **error)
+{
+ gchar c;
+
+ skip_spaces (token);
+
+ if (token->iter == token->end) {
+ token->type = PD_TOKEN_EOF;
+ GXPS_DEBUG (print_token (token));
+
+ return TRUE;
+ }
+
+ c = *token->iter;
+
+ if (g_ascii_isdigit (c) || c == '+' || c == '-') {
+ gchar *start;
+ gchar *str;
+
+ start = token->iter;
+ token->iter++;
+ while (token->iter != token->end && (g_ascii_isdigit (*token->iter) || *token->iter == '.'))
+ token->iter++;
+ str = g_strndup (start, token->iter - start);
+ if (!gxps_value_get_double (str, &token->number)) {
+ g_set_error (error,
+ GXPS_PAGE_ERROR,
+ GXPS_PAGE_ERROR_RENDER,
+ "Error parsing abreviated path: error converting token %s (%s) to double at %s",
+ path_data_token_type_to_string (token->type),
+ str, token->iter);
+ g_free (str);
+
+ return FALSE;
+ }
+ g_free (str);
+ token->type = PD_TOKEN_NUMBER;
+ } else if (c == ',') {
+ token->type = PD_TOKEN_COMMA;
+ token->iter++;
+ } else if (g_ascii_isalpha (c)) {
+ token->command = c;
+ token->type = PD_TOKEN_COMMAND;
+ token->iter++;
+ } else {
+ token->type = PD_TOKEN_INVALID;
+ token->iter++;
+ }
+
+ GXPS_DEBUG (print_token (token));
+
+ return TRUE;
+}
+
+static void
+path_data_parse_error (PathDataToken *token,
+ PathDataTokenType expected,
+ GError **error)
+{
+ if (expected == PD_TOKEN_INVALID)
+ g_set_error (error,
+ GXPS_PAGE_ERROR,
+ GXPS_PAGE_ERROR_RENDER,
+ "Error parsing abreviated path: unexpected token %s at %s",
+ path_data_token_type_to_string (token->type),
+ token->iter);
+ else
+ g_set_error (error,
+ GXPS_PAGE_ERROR,
+ GXPS_PAGE_ERROR_RENDER,
+ "Error parsing abreviated path: expected token %s, but %s found at %s",
+ path_data_token_type_to_string (token->type),
+ path_data_token_type_to_string (expected),
+ token->iter);
+}
+
+static gboolean
+path_data_get_point (PathDataToken *token,
+ gdouble *x,
+ gdouble *y,
+ GError **error)
+{
+ *x = token->number;
+
+ if (!path_data_iter_next (token, error))
+ return FALSE;
+ if (token->type != PD_TOKEN_COMMA) {
+ path_data_parse_error (token, PD_TOKEN_COMMA, error);
+ return FALSE;
+ }
+
+ if (!path_data_iter_next (token, error))
+ return FALSE;
+ if (token->type != PD_TOKEN_NUMBER) {
+ path_data_parse_error (token, PD_TOKEN_NUMBER, error);
+ return FALSE;
+ }
+ *y = token->number;
+
+ return TRUE;
+}
+
+gboolean
+gxps_path_parse (const gchar *data,
+ cairo_t *cr,
+ GError **error)
+{
+ PathDataToken token;
+ gdouble control_point_x;
+ gdouble control_point_y;
+
+ token.iter = (gchar *)data;
+ token.end = token.iter + strlen (data);
+
+ if (!path_data_iter_next (&token, error))
+ return FALSE;
+ if (G_UNLIKELY (token.type != PD_TOKEN_COMMAND))
+ return TRUE;
+
+ control_point_x = control_point_y = 0;
+
+ do {
+ gchar command = token.command;
+ gboolean is_rel = FALSE;
+
+ if (!path_data_iter_next (&token, error))
+ return FALSE;
+
+ switch (command) {
+ /* Move */
+ case 'm':
+ is_rel = TRUE;
+ case 'M':
+ while (token.type == PD_TOKEN_NUMBER) {
+ gdouble x, y;
+
+ if (!path_data_get_point (&token, &x, &y, error))
+ return FALSE;
+
+ GXPS_DEBUG (g_message ("%s (%f, %f)", is_rel ? "rel_move_to" : "move_to", x, y));
+
+ if (is_rel)
+ cairo_rel_move_to (cr, x, y);
+ else
+ cairo_move_to (cr, x, y);
+
+ if (!path_data_iter_next (&token, error))
+ return FALSE;
+ }
+ control_point_x = control_point_y = 0;
+ break;
+ /* Line */
+ case 'l':
+ is_rel = TRUE;
+ case 'L':
+ while (token.type == PD_TOKEN_NUMBER) {
+ gdouble x, y;
+
+ if (!path_data_get_point (&token, &x, &y, error))
+ return FALSE;
+
+ GXPS_DEBUG (g_message ("%s (%f, %f)", is_rel ? "rel_line_to" : "line_to", x, y));
+
+ if (is_rel)
+ cairo_rel_line_to (cr, x, y);
+ else
+ cairo_line_to (cr, x, y);
+
+ if (!path_data_iter_next (&token, error))
+ return FALSE;
+ }
+ control_point_x = control_point_y = 0;
+ break;
+ /* Horizontal Line */
+ case 'h':
+ is_rel = TRUE;
+ case 'H':
+ while (token.type == PD_TOKEN_NUMBER) {
+ gdouble x, y;
+ gdouble offset;
+
+ offset = token.number;
+
+ GXPS_DEBUG (g_message ("%s (%f)", is_rel ? "rel_hline_to" : "hline_to", offset));
+
+ cairo_get_current_point (cr, &x, &y);
+ x = is_rel ? x + offset : offset;
+ cairo_line_to (cr, x, y);
+
+ if (!path_data_iter_next (&token, error))
+ return FALSE;
+ }
+ control_point_x = control_point_y = 0;
+ break;
+ /* Vertical Line */
+ case 'v':
+ is_rel = TRUE;
+ case 'V':
+ while (token.type == PD_TOKEN_NUMBER) {
+ gdouble x, y;
+ gdouble offset;
+
+ offset = token.number;
+
+ GXPS_DEBUG (g_message ("%s (%f)", is_rel ? "rel_vline_to" : "vline_to", offset));
+
+ cairo_get_current_point (cr, &x, &y);
+ y = is_rel ? y + offset : offset;
+ cairo_line_to (cr, x, y);
+
+ if (!path_data_iter_next (&token, error))
+ return FALSE;
+ }
+ control_point_x = control_point_y = 0;
+ break;
+ /* Cubic BÃzier curve */
+ case 'c':
+ is_rel = TRUE;
+ case 'C':
+ while (token.type == PD_TOKEN_NUMBER) {
+ gdouble x1, y1, x2, y2, x3, y3;
+
+ if (!path_data_get_point (&token, &x1, &y1, error))
+ return FALSE;
+
+ if (!path_data_iter_next (&token, error))
+ return FALSE;
+ if (!path_data_get_point (&token, &x2, &y2, error))
+ return FALSE;
+
+ if (!path_data_iter_next (&token, error))
+ return FALSE;
+ if (!path_data_get_point (&token, &x3, &y3, error))
+ return FALSE;
+
+ GXPS_DEBUG (g_message ("%s (%f, %f, %f, %f, %f, %f)", is_rel ? "rel_curve_to" : "curve_to",
+ x1, y1, x2, y2, x3, y3));
+
+ if (is_rel)
+ cairo_rel_curve_to (cr, x1, y1, x2, y2, x3, y3);
+ else
+ cairo_curve_to (cr, x1, y1, x2, y2, x3, y3);
+
+ control_point_x = x3 - x2;
+ control_point_y = y3 - y2;
+
+ if (!path_data_iter_next (&token, error))
+ return FALSE;
+ }
+ break;
+ /* Quadratic BÃzier curve */
+ case 'q':
+ is_rel = TRUE;
+ case 'Q':
+ while (token.type == PD_TOKEN_NUMBER) {
+ gdouble x1, y1, x2, y2;
+ gdouble x, y;
+
+ if (!path_data_get_point (&token, &x1, &y1, error))
+ return FALSE;
+
+ if (!path_data_iter_next (&token, error))
+ return FALSE;
+ if (!path_data_get_point (&token, &x2, &y2, error))
+ return FALSE;
+
+ GXPS_DEBUG (g_message ("%s (%f, %f, %f, %f)", is_rel ? "rel_quad_curve_to" : "quad_curve_to",
+ x1, y1, x2, y2));
+
+ cairo_get_current_point (cr, &x, &y);
+ x1 += is_rel ? x : 0;
+ y1 += is_rel ? y : 0;
+ x2 += is_rel ? x : 0;
+ y2 += is_rel ? y : 0;
+ cairo_curve_to (cr,
+ 2.0 / 3.0 * x1 + 1.0 / 3.0 * x,
+ 2.0 / 3.0 * y1 + 1.0 / 3.0 * y,
+ 2.0 / 3.0 * x1 + 1.0 / 3.0 * x2,
+ 2.0 / 3.0 * y1 + 1.0 / 3.0 * y2,
+ x2, y2);
+
+ if (!path_data_iter_next (&token, error))
+ return FALSE;
+ }
+ control_point_x = control_point_y = 0;
+ break;
+ /* Smooth Cubic BÃzier curve */
+ case 's':
+ is_rel = TRUE;
+ case 'S':
+ while (token.type == PD_TOKEN_NUMBER) {
+ gdouble x2, y2, x3, y3;
+
+ if (!path_data_get_point (&token, &x2, &y2, error))
+ return FALSE;
+
+ if (!path_data_iter_next (&token, error))
+ return FALSE;
+ if (!path_data_get_point (&token, &x3, &y3, error))
+ return FALSE;
+
+ GXPS_DEBUG (g_message ("%s (%f, %f, %f, %f, %f, %f)", is_rel ? "rel_smooth_curve_to" : "smooth_curve_to",
+ control_point_x, control_point_y, x2, y2, x3, y3));
+
+ if (is_rel) {
+ cairo_rel_curve_to (cr, control_point_x, control_point_y, x2, y2, x3, y3);
+ } else {
+ gdouble x, y;
+
+ cairo_get_current_point (cr, &x, &y);
+ cairo_curve_to (cr, x + control_point_x, y + control_point_y, x2, y2, x3, y3);
+ }
+
+ control_point_x = x3 - x2;
+ control_point_y = y3 - y2;
+
+ if (!path_data_iter_next (&token, error))
+ return FALSE;
+ }
+ break;
+ /* Elliptical Arc */
+ case 'a':
+ is_rel = TRUE;
+ case 'A':
+ while (token.type == PD_TOKEN_NUMBER) {
+ gdouble xr, yr, rx, farc, fsweep, x, y;
+
+ if (!path_data_get_point (&token, &xr, &yr, error))
+ return FALSE;
+
+ if (!path_data_iter_next (&token, error))
+ return FALSE;
+ if (token.type != PD_TOKEN_NUMBER) {
+ path_data_parse_error (&token, PD_TOKEN_NUMBER, error);
+ return FALSE;
+ }
+ rx = token.number;
+
+ if (!path_data_iter_next (&token, error))
+ return FALSE;
+ if (token.type != PD_TOKEN_NUMBER) {
+ path_data_parse_error (&token, PD_TOKEN_NUMBER, error);
+ return FALSE;
+ }
+ farc = token.number;
+
+ if (!path_data_iter_next (&token, error))
+ return FALSE;
+ if (token.type != PD_TOKEN_NUMBER) {
+ path_data_parse_error (&token, PD_TOKEN_NUMBER, error);
+ return FALSE;
+ }
+ fsweep = token.number;
+
+ if (!path_data_iter_next (&token, error))
+ return FALSE;
+ if (!path_data_get_point (&token, &x, &y, error))
+ return FALSE;
+
+ GXPS_DEBUG (g_message ("%s (%f, %f, %f, %f, %f, %f, %f)", is_rel ? "rel_arc" : "arc",
+ xr, yr, rx, farc, fsweep, x, y));
+ GXPS_DEBUG (g_debug ("Unsupported command in path: %c", command));
+
+ if (!path_data_iter_next (&token, error))
+ return FALSE;
+ }
+ control_point_x = control_point_y = 0;
+ break;
+ /* Close */
+ case 'z':
+ is_rel = TRUE;
+ case 'Z':
+ cairo_close_path (cr);
+ GXPS_DEBUG (g_message ("close_path"));
+ control_point_x = control_point_y = 0;
+ break;
+ /* Fill Rule */
+ case 'F': {
+ gint fill_rule;
+
+ fill_rule = (gint)token.number;
+ cairo_set_fill_rule (cr,
+ (fill_rule == 0) ?
+ CAIRO_FILL_RULE_EVEN_ODD :
+ CAIRO_FILL_RULE_WINDING);
+ GXPS_DEBUG (g_message ("set_fill_rule (%s)", (fill_rule == 0) ? "EVEN_ODD" : "WINDING"));
+
+ if (!path_data_iter_next (&token, error))
+ return FALSE;
+ }
+ control_point_x = control_point_y = 0;
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+ } while (token.type == PD_TOKEN_COMMAND);
+
+ return TRUE;
+}
+
+static gboolean
+gxps_points_parse (const gchar *points,
+ gdouble **coords,
+ guint *n_points)
+{
+ gchar **items;
+ guint i, j = 0;
+ gboolean retval = TRUE;
+
+ *n_points = 0;
+ items = g_strsplit (points, " ", -1);
+ if (!items)
+ return FALSE;
+
+ for (i = 0; items[i] != NULL; i++) {
+ if (*items[i] != '\0') /* don't count empty string */
+ (*n_points)++;
+ }
+
+ if (*n_points == 0)
+ return FALSE;
+
+ *coords = g_malloc (*n_points * 2 * sizeof (gdouble));
+
+ for (i = 0; items[i] != NULL; i++) {
+ gdouble x, y;
+
+ if (*items[i] == '\0')
+ continue;
+
+ if (!gxps_point_parse (items[i], &x, &y)) {
+ g_free (*coords);
+ retval = FALSE;
+ break;
+ }
+
+ coords[0][j++] = x;
+ coords[0][j++] = y;
+ }
+
+ g_strfreev (items);
+
+ return retval;
+}
+
+static void
+path_geometry_start_element (GMarkupParseContext *context,
+ const gchar *element_name,
+ const gchar **names,
+ const gchar **values,
+ gpointer user_data,
+ GError **error)
+{
+ GXPSPath *path = (GXPSPath *)user_data;
+
+ if (strcmp (element_name, "PathGeometry.Transform") == 0) {
+ GXPSMatrix *matrix;
+
+ matrix = gxps_matrix_new (path->ctx);
+ gxps_matrix_parser_push (context, matrix);
+ } else if (strcmp (element_name, "PathFigure") == 0) {
+ gint i;
+ gboolean has_start_point = FALSE;
+
+ for (i = 0; names[i] != NULL; i++) {
+ if (strcmp (names[i], "StartPoint") == 0) {
+ gdouble x, y;
+
+ if (!gxps_point_parse (values[i], &x, &y)) {
+ gxps_parse_error (context,
+ path->ctx->page->priv->source,
+ G_MARKUP_ERROR_INVALID_CONTENT,
+ "PathFigure", "StartPoint",
+ values[i], error);
+ return;
+ }
+
+ GXPS_DEBUG (g_message ("move_to (%f, %f)", x, y));
+ cairo_move_to (path->ctx->cr, x, y);
+ has_start_point = TRUE;
+ } else if (strcmp (names[i], "IsClosed") == 0) {
+ gboolean is_closed;
+
+ if (!gxps_value_get_boolean (values[i], &is_closed)) {
+ gxps_parse_error (context,
+ path->ctx->page->priv->source,
+ G_MARKUP_ERROR_INVALID_CONTENT,
+ "PathFigure", "IsClosed",
+ values[i], error);
+ return;
+ }
+ path->is_closed = is_closed;
+ } else if (strcmp (names[i], "IsFilled") == 0) {
+ gboolean is_filled;
+
+ if (!gxps_value_get_boolean (values[i], &is_filled)) {
+ gxps_parse_error (context,
+ path->ctx->page->priv->source,
+ G_MARKUP_ERROR_INVALID_CONTENT,
+ "PathFigure", "IsFilled",
+ values[i], error);
+ return;
+ }
+ path->is_filled = is_filled;
+ }
+ }
+
+ if (!has_start_point) {
+ gxps_parse_error (context,
+ path->ctx->page->priv->source,
+ G_MARKUP_ERROR_MISSING_ATTRIBUTE,
+ "PathFigure", "StartPoint",
+ NULL, error);
+ return;
+ }
+ } else if (strcmp (element_name, "PolyLineSegment") == 0) {
+ gint i, j;
+ const gchar *points_str = NULL;
+ gdouble *points = NULL;
+ guint n_points;
+ gboolean is_stroked = TRUE;
+
+ for (i = 0; names[i] != NULL; i++) {
+ if (strcmp (names[i], "Points") == 0) {
+ points_str = values[i];
+ } else if (strcmp (names[i], "IsStroked") == 0) {
+ if (!gxps_value_get_boolean (values[i], &is_stroked)) {
+ gxps_parse_error (context,
+ path->ctx->page->priv->source,
+ G_MARKUP_ERROR_INVALID_CONTENT,
+ "PolyLineSegment", "IsStroked",
+ points_str, error);
+ return;
+ }
+ }
+ }
+
+ if (!is_stroked)
+ return;
+
+ if (!points_str) {
+ gxps_parse_error (context,
+ path->ctx->page->priv->source,
+ G_MARKUP_ERROR_MISSING_ATTRIBUTE,
+ "PolyLineSegment", "Points",
+ NULL, error);
+ return;
+ }
+
+ if (!gxps_points_parse (points_str, &points, &n_points)) {
+ gxps_parse_error (context,
+ path->ctx->page->priv->source,
+ G_MARKUP_ERROR_INVALID_CONTENT,
+ "PolyLineSegment", "Points",
+ points_str, error);
+ return;
+ }
+
+ for (j = 0; j < n_points * 2; j += 2) {
+ GXPS_DEBUG (g_message ("line_to (%f, %f)", points[j], points[j + 1]));
+ cairo_line_to (path->ctx->cr, points[j], points[j + 1]);
+ }
+
+ g_free (points);
+ } else if (strcmp (element_name, "PolyBezierSegment") == 0) {
+ gint i, j;
+ const gchar *points_str = NULL;
+ gdouble *points = NULL;
+ guint n_points;
+ gboolean is_stroked = TRUE;
+
+ for (i = 0; names[i] != NULL; i++) {
+ if (strcmp (names[i], "Points") == 0) {
+ points_str = values[i];
+
+ } else if (strcmp (names[i], "IsStroked") == 0) {
+ if (!gxps_value_get_boolean (values[i], &is_stroked)) {
+ gxps_parse_error (context,
+ path->ctx->page->priv->source,
+ G_MARKUP_ERROR_INVALID_CONTENT,
+ "PolyBezierSegment", "IsStroked",
+ points_str, error);
+ return;
+ }
+ }
+ }
+
+ if (!is_stroked)
+ return;
+
+ if (!points_str) {
+ gxps_parse_error (context,
+ path->ctx->page->priv->source,
+ G_MARKUP_ERROR_MISSING_ATTRIBUTE,
+ "PolyBezierSegment", "Points",
+ NULL, error);
+ return;
+ }
+
+ if (!gxps_points_parse (points_str, &points, &n_points)) {
+ gxps_parse_error (context,
+ path->ctx->page->priv->source,
+ G_MARKUP_ERROR_INVALID_CONTENT,
+ "PolyBezierSegment", "Points",
+ points_str, error);
+ return;
+ }
+
+ for (j = 0; j < n_points * 2; j += 6) {
+ GXPS_DEBUG (g_message ("curve_to (%f, %f, %f, %f, %f, %f)",
+ points[j], points[j + 1],
+ points[j + 2], points[j + 3],
+ points[j + 4], points[j + 5]));
+ cairo_curve_to (path->ctx->cr,
+ points[j], points[j + 1],
+ points[j + 2], points[j + 3],
+ points[j + 4], points[j + 5]);
+ }
+
+ g_free (points);
+ } else if (strcmp (element_name, "PolyQuadraticBezierSegment") == 0) {
+ gint i, j;
+ const gchar *points_str = NULL;
+ gdouble *points = NULL;
+ guint n_points;
+ gboolean is_stroked = TRUE;
+
+ for (i = 0; names[i] != NULL; i++) {
+ if (strcmp (names[i], "Points") == 0) {
+ points_str = values[i];
+
+ } else if (strcmp (names[i], "IsStroked") == 0) {
+ if (!gxps_value_get_boolean (values[i], &is_stroked)) {
+ gxps_parse_error (context,
+ path->ctx->page->priv->source,
+ G_MARKUP_ERROR_INVALID_CONTENT,
+ "PolyQuadraticBezierSegment", "IsStroked",
+ points_str, error);
+ return;
+ }
+ }
+ }
+
+ if (!is_stroked)
+ return;
+
+ if (!points_str) {
+ gxps_parse_error (context,
+ path->ctx->page->priv->source,
+ G_MARKUP_ERROR_MISSING_ATTRIBUTE,
+ "PolyQuadraticBezierSegment", "Points",
+ NULL, error);
+ return;
+ }
+
+ if (!gxps_points_parse (points_str, &points, &n_points)) {
+ gxps_parse_error (context,
+ path->ctx->page->priv->source,
+ G_MARKUP_ERROR_INVALID_CONTENT,
+ "PolyQuadraticBezierSegment", "Points",
+ points_str, error);
+ return;
+ }
+
+ for (j = 0; j < n_points * 2; j += 4) {
+ gdouble x1, y1, x2, y2;
+ gdouble x, y;
+
+ x1 = points[j];
+ y1 = points[j + 1];
+ x2 = points[j + 2];
+ y2 = points[j + 3];
+
+ GXPS_DEBUG (g_message ("quad_curve_to (%f, %f, %f, %f)", x1, y1, x2, y2));
+ cairo_get_current_point (path->ctx->cr, &x, &y);
+ cairo_curve_to (path->ctx->cr,
+ 2.0 / 3.0 * x1 + 1.0 / 3.0 * x,
+ 2.0 / 3.0 * y1 + 1.0 / 3.0 * y,
+ 2.0 / 3.0 * x1 + 1.0 / 3.0 * x2,
+ 2.0 / 3.0 * y1 + 1.0 / 3.0 * y2,
+ x2, y2);
+ }
+
+ g_free (points);
+ } else if (strcmp (element_name, "ArcSegment") == 0) {
+ GXPS_DEBUG (g_debug ("Unsupported PathGeometry: ArcSegment"));
+ }
+}
+
+static void
+path_geometry_end_element (GMarkupParseContext *context,
+ const gchar *element_name,
+ gpointer user_data,
+ GError **error)
+{
+ GXPSPath *path = (GXPSPath *)user_data;
+
+ if (strcmp (element_name, "PathGeometry.Transform") == 0) {
+ GXPSMatrix *matrix;
+
+ matrix = g_markup_parse_context_pop (context);
+ GXPS_DEBUG (g_message ("transform (%f, %f, %f, %f) [%f, %f]",
+ matrix->matrix.xx, matrix->matrix.yx,
+ matrix->matrix.xy, matrix->matrix.yy,
+ matrix->matrix.x0, matrix->matrix.y0));
+ cairo_transform (path->ctx->cr, &matrix->matrix);
+
+ gxps_matrix_free (matrix);
+ } else if (strcmp (element_name, "PathFigure") == 0) {
+ if (path->is_closed) {
+ GXPS_DEBUG (g_message ("close_path"));
+ cairo_close_path (path->ctx->cr);
+ }
+
+ if (path->is_filled && path->fill_pattern) {
+ GXPS_DEBUG (g_message ("fill"));
+ cairo_set_source (path->ctx->cr, path->fill_pattern);
+ if (path->is_stroked && path->stroke_pattern)
+ cairo_fill_preserve (path->ctx->cr);
+ else
+ cairo_fill (path->ctx->cr);
+ }
+
+ if (path->stroke_pattern) {
+ GXPS_DEBUG (g_message ("stroke"));
+ cairo_set_source (path->ctx->cr, path->stroke_pattern);
+ cairo_set_line_width (path->ctx->cr, path->line_width);
+ if (path->dash && path->dash_len > 0)
+ cairo_set_dash (path->ctx->cr, path->dash, path->dash_len, path->dash_offset);
+ cairo_set_line_join (path->ctx->cr, path->line_join);
+ cairo_set_miter_limit (path->ctx->cr, path->miter_limit);
+ cairo_stroke (path->ctx->cr);
+ }
+ }
+}
+
+static GMarkupParser path_geometry_parser = {
+ path_geometry_start_element,
+ path_geometry_end_element,
+ NULL,
+ NULL
+};
+
+static cairo_fill_rule_t
+gxps_fill_rule_parse (const gchar *rule)
+{
+ if (strcmp (rule, "EvenOdd") == 0)
+ return CAIRO_FILL_RULE_EVEN_ODD;
+ else if (strcmp (rule, "NonZero") == 0)
+ return CAIRO_FILL_RULE_WINDING;
+ return CAIRO_FILL_RULE_EVEN_ODD;
+}
+
+static void
+path_start_element (GMarkupParseContext *context,
+ const gchar *element_name,
+ const gchar **names,
+ const gchar **values,
+ gpointer user_data,
+ GError **error)
+{
+ GXPSPath *path = (GXPSPath *)user_data;
+
+ if (strcmp (element_name, "Path.Fill") == 0) {
+ GXPSBrush *brush;
+
+ brush = gxps_brush_new (path->ctx);
+ gxps_brush_parser_push (context, brush);
+ } else if (strcmp (element_name, "Path.Stroke") == 0) {
+ GXPSBrush *brush;
+
+ brush = gxps_brush_new (path->ctx);
+ gxps_brush_parser_push (context, brush);
+ } else if (strcmp (element_name, "Path.Data") == 0) {
+ } else if (strcmp (element_name, "PathGeometry") == 0) {
+ gint i;
+
+ for (i = 0; names[i] != NULL; i++) {
+ if (strcmp (names[i], "Figures") == 0) {
+ path->data = g_strdup (values[i]);
+ } else if (strcmp (names[i], "FillRule") == 0) {
+ path->fill_rule = gxps_fill_rule_parse (values[i]);
+ GXPS_DEBUG (g_message ("set_fill_rule (%s)", values[i]));
+ } else if (strcmp (names[i], "Transform") == 0) {
+ cairo_matrix_t matrix;
+
+ if (!gxps_matrix_parse (values[i], &matrix)) {
+ gxps_parse_error (context,
+ path->ctx->page->priv->source,
+ G_MARKUP_ERROR_INVALID_CONTENT,
+ "PathGeometry", "Transform",
+ values[i], error);
+ return;
+ }
+ GXPS_DEBUG (g_message ("transform (%f, %f, %f, %f) [%f, %f]",
+ matrix.xx, matrix.yx,
+ matrix.xy, matrix.yy,
+ matrix.x0, matrix.y0));
+ cairo_transform (path->ctx->cr, &matrix);
+ }
+ }
+
+ if (!path->data) {
+ cairo_set_fill_rule (path->ctx->cr, path->fill_rule);
+ if (path->clip_data) {
+ if (!gxps_path_parse (path->clip_data, path->ctx->cr, error))
+ return;
+ GXPS_DEBUG (g_message ("clip"));
+ cairo_clip (path->ctx->cr);
+ }
+ g_markup_parse_context_push (context, &path_geometry_parser, path);
+ }
+ } else if (strcmp (element_name, "Path.RenderTransform") == 0) {
+ GXPSMatrix *matrix;
+
+ matrix = gxps_matrix_new (path->ctx);
+ gxps_matrix_parser_push (context, matrix);
+ } else {
+ GXPS_DEBUG (g_debug ("Unsupported path child %s", element_name));
+ }
+}
+
+static void
+path_end_element (GMarkupParseContext *context,
+ const gchar *element_name,
+ gpointer user_data,
+ GError **error)
+{
+ GXPSPath *path = (GXPSPath *)user_data;
+
+ if (strcmp (element_name, "Path.Fill") == 0) {
+ GXPSBrush *brush;
+
+ brush = g_markup_parse_context_pop (context);
+ path->fill_pattern = cairo_pattern_reference (brush->pattern);
+ gxps_brush_free (brush);
+ } else if (strcmp (element_name, "Path.Stroke") == 0) {
+ GXPSBrush *brush;
+
+ brush = g_markup_parse_context_pop (context);
+ path->stroke_pattern = cairo_pattern_reference (brush->pattern);
+ gxps_brush_free (brush);
+ } else if (strcmp (element_name, "Path.Data") == 0) {
+ } else if (strcmp (element_name, "PathGeometry") == 0) {
+ if (!path->data)
+ g_markup_parse_context_pop (context);
+ } else if (strcmp (element_name, "Path.RenderTransform") == 0) {
+ GXPSMatrix *matrix;
+
+ matrix = g_markup_parse_context_pop (context);
+ GXPS_DEBUG (g_message ("transform (%f, %f, %f, %f) [%f, %f]",
+ matrix->matrix.xx, matrix->matrix.yx,
+ matrix->matrix.xy, matrix->matrix.yy,
+ matrix->matrix.x0, matrix->matrix.y0));
+ cairo_transform (path->ctx->cr, &matrix->matrix);
+
+ gxps_matrix_free (matrix);
+ } else {
+
+ }
+}
+
+static GMarkupParser path_parser = {
+ path_start_element,
+ path_end_element,
+ NULL,
+ NULL
+};
+
+void
+gxps_path_parser_push (GMarkupParseContext *context,
+ GXPSPath *path)
+{
+ g_markup_parse_context_push (context, &path_parser, path);
+}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]