Commit message: None
CVE: CVE-2014-2013
CWE: CWE-119
Vulnerability description: Stack-based buffer overflow in the xps_parse_color function in xps/xps-common.c in MuPDF 1.3 and earlier allows remote attackers to execute arbitrary code via a large number of entries in the ContextColor value of the Fill attribute in a Path element.

Project: ghostscript
File: xps-common.c

static int count_commas(char *s)
{
	int n = 0;
	while (*s)
	{
		if (*s == ',')
			n ++;
		s ++;
	}
	return n;
}

Project: ghostscript
File: xps-common.c

static inline int unhex(int a)
{
	if (a >= 'A' && a <= 'F') return a - 'A' + 0xA;
	if (a >= 'a' && a <= 'f') return a - 'a' + 0xA;
	if (a >= '0' && a <= '9') return a - '0';
	return 0;
}

Project: ghostscript
File: xps-common.c

xps_end_opacity(xps_document *doc, char *base_uri, xps_resource *dict,
	char *opacity_att, fz_xml *opacity_mask_tag)
{
	if (!opacity_att && !opacity_mask_tag)
		return;

	if (doc->opacity_top > 0)
		doc->opacity_top--;

	if (opacity_mask_tag)
	{
		if (strcmp(fz_xml_tag(opacity_mask_tag), "SolidColorBrush"))
			fz_pop_clip(doc->dev);
	}
}

Project: ghostscript
File: xps-common.c

xps_lookup_alternate_content(fz_xml *node)
{
	for (node = fz_xml_down(node); node; node = fz_xml_next(node))
	{
		if (!strcmp(fz_xml_tag(node), "mc:Choice") && fz_xml_att(node, "Requires"))
		{
			char list[64];
			char *next = list, *item;
			fz_strlcpy(list, fz_xml_att(node, "Requires"), sizeof(list));
			while ((item = fz_strsep(&next, " \t\r\n")) != NULL && (!*item || !strcmp(item, "xps")));
			if (!item)
				return fz_xml_down(node);
		}
		else if (!strcmp(fz_xml_tag(node), "mc:Fallback"))
			return fz_xml_down(node);
	}
	return NULL;
}

Project: ghostscript
File: xps-common.c

xps_parse_element(xps_document *doc, const fz_matrix *ctm, const fz_rect *area, char *base_uri, xps_resource *dict, fz_xml *node)
{
	if (doc->cookie && doc->cookie->abort)
		return;
	if (!strcmp(fz_xml_tag(node), "Path"))
		xps_parse_path(doc, ctm, base_uri, dict, node);
	if (!strcmp(fz_xml_tag(node), "Glyphs"))
		xps_parse_glyphs(doc, ctm, base_uri, dict, node);
	if (!strcmp(fz_xml_tag(node), "Canvas"))
		xps_parse_canvas(doc, ctm, area, base_uri, dict, node);
	if (!strcmp(fz_xml_tag(node), "mc:AlternateContent"))
	{
		node = xps_lookup_alternate_content(node);
		if (node)
			xps_parse_element(doc, ctm, area, base_uri, dict, node);
	}
	/* skip unknown tags (like Foo.Resources and similar) */
}

Project: ghostscript
File: xps-common.c

xps_parse_rectangle(xps_document *doc, char *text, fz_rect *rect)
{
	float args[4];
	char *s = text;
	int i;

	args[0] = 0; args[1] = 0;
	args[2] = 1; args[3] = 1;

	for (i = 0; i < 4 && *s; i++)
	{
		args[i] = fz_atof(s);
		while (*s && *s != ',')
			s++;
		if (*s == ',')
			s++;
	}

	rect->x0 = args[0];
	rect->y0 = args[1];
	rect->x1 = args[0] + args[2];
	rect->y1 = args[1] + args[3];
}

Project: ghostscript
File: xps-glyphs.c

static inline int is_real_num_char(int c)
{
	return (c >= '0' && c <= '9') ||
		c == 'e' || c == 'E' || c == '+' || c == '-' || c == '.';
}

Project: ghostscript
File: xps-glyphs.c

xps_count_font_encodings(fz_font *font)
{
	FT_Face face = font->ft_face;
	return face->num_charmaps;
}

Project: ghostscript
File: xps-glyphs.c

xps_deobfuscate_font_resource(xps_document *doc, xps_part *part)
{
	unsigned char buf[33];
	unsigned char key[16];
	char *p;
	int i;

	p = strrchr(part->name, '/');
	if (!p)
		p = part->name;

	for (i = 0; i < 32 && *p; p++)
	{
		if (ishex(*p))
			buf[i++] = *p;
	}
	buf[i] = 0;

	if (i != 32)
	{
		fz_warn(doc->ctx, "cannot extract GUID from obfuscated font part name");
		return;
	}

	for (i = 0; i < 16; i++)
		key[i] = unhex(buf[i*2+0]) * 16 + unhex(buf[i*2+1]);

	for (i = 0; i < 16; i++)
	{
		part->data[i] ^= key[15-i];
		part->data[i+16] ^= key[15-i];
	}
}

Project: ghostscript
File: xps-glyphs.c

xps_encode_font_char(fz_font *font, int code)
{
	FT_Face face = font->ft_face;
	int gid = FT_Get_Char_Index(face, code);
	if (gid == 0 && face->charmap && face->charmap->platform_id == 3 && face->charmap->encoding_id == 0)
		gid = FT_Get_Char_Index(face, 0xF000 | code);
	return gid;
}

Project: ghostscript
File: xps-glyphs.c

xps_identify_font_encoding(fz_font *font, int idx, int *pid, int *eid)
{
	FT_Face face = font->ft_face;
	*pid = face->charmaps[idx]->platform_id;
	*eid = face->charmaps[idx]->encoding_id;
}

Project: ghostscript
File: xps-glyphs.c

xps_lookup_font(xps_document *doc, char *name)
{
	xps_font_cache *cache;
	for (cache = doc->font_table; cache; cache = cache->next)
		if (!xps_strcasecmp(cache->name, name))
			return fz_keep_font(doc->ctx, cache->font);
	return NULL;
}

Project: ghostscript
File: xps-glyphs.c

xps_measure_font_glyph(xps_document *doc, fz_font *font, int gid, xps_glyph_metrics *mtx)
{
	int mask = FT_LOAD_NO_SCALE | FT_LOAD_IGNORE_TRANSFORM;
	FT_Face face = font->ft_face;
	FT_Fixed hadv, vadv;
	fz_context *ctx = doc->ctx;

	fz_lock(ctx, FZ_LOCK_FREETYPE);
	FT_Get_Advance(face, gid, mask, &hadv);
	FT_Get_Advance(face, gid, mask | FT_LOAD_VERTICAL_LAYOUT, &vadv);
	fz_unlock(ctx, FZ_LOCK_FREETYPE);

	mtx->hadv = hadv / (float)face->units_per_EM;
	mtx->vadv = vadv / (float)face->units_per_EM;
	mtx->vorg = face->ascender / (float) face->units_per_EM;
}

Project: ghostscript
File: xps-glyphs.c

xps_parse_cluster_mapping(char *s, int *code_count, int *glyph_count)
{
	if (*s == '(')
		s = xps_parse_digits(s + 1, code_count);
	if (*s == ':')
		s = xps_parse_digits(s + 1, glyph_count);
	if (*s == ')')
		s ++;
	return s;
}

Project: ghostscript
File: xps-glyphs.c

xps_parse_glyph_index(char *s, int *glyph_index)
{
	if (*s >= '0' && *s <= '9')
		s = xps_parse_digits(s, glyph_index);
	return s;
}

Project: ghostscript
File: xps-glyphs.c

xps_parse_glyph_metrics(char *s, float *advance, float *uofs, float *vofs)
{
	if (*s == ',')
		s = xps_parse_real_num(s + 1, advance);
	if (*s == ',')
		s = xps_parse_real_num(s + 1, uofs);
	if (*s == ',')
		s = xps_parse_real_num(s + 1, vofs);
	return s;
}

Project: ghostscript
File: xps-glyphs.c

xps_parse_glyphs_imp(xps_document *doc, const fz_matrix *ctm,
	fz_font *font, float size, float originx, float originy,
	int is_sideways, int bidi_level,
	char *indices, char *unicode)
{
	xps_glyph_metrics mtx;
	fz_text *text;
	fz_matrix tm;
	float e, f;
	float x = originx;
	float y = originy;
	char *us = unicode;
	char *is = indices;
	int un = 0;

	if (!unicode && !indices)
		fz_warn(doc->ctx, "glyphs element with neither characters nor indices");

	if (us)
	{
		if (us[0] == '{' && us[1] == '}')
			us = us + 2;
		un = strlen(us);
	}

	if (is_sideways)
	{
		fz_pre_scale(fz_rotate(&tm, 90), -size, size);
	}
	else
		fz_scale(&tm, size, -size);

	text = fz_new_text(doc->ctx, font, &tm, is_sideways);

	while ((us && un > 0) || (is && *is))
	{
		int char_code = '?';
		int code_count = 1;
		int glyph_count = 1;

		if (is && *is)
		{
			is = xps_parse_cluster_mapping(is, &code_count, &glyph_count);
		}

		if (code_count < 1)
			code_count = 1;
		if (glyph_count < 1)
			glyph_count = 1;

		/* TODO: add code chars with cluster mappings for text extraction */

		while (code_count--)
		{
			if (us && un > 0)
			{
				int t = fz_chartorune(&char_code, us);
				us += t; un -= t;
			}
		}

		while (glyph_count--)
		{
			int glyph_index = -1;
			float u_offset = 0;
			float v_offset = 0;
			float advance;

			if (is && *is)
				is = xps_parse_glyph_index(is, &glyph_index);

			if (glyph_index == -1)
				glyph_index = xps_encode_font_char(font, char_code);

			xps_measure_font_glyph(doc, font, glyph_index, &mtx);
			if (is_sideways)
				advance = mtx.vadv * 100;
			else if (bidi_level & 1)
				advance = -mtx.hadv * 100;
			else
				advance = mtx.hadv * 100;

			if (font->ft_bold)
				advance *= 1.02f;

			if (is && *is)
			{
				is = xps_parse_glyph_metrics(is, &advance, &u_offset, &v_offset);
				if (*is == ';')
					is ++;
			}

			if (bidi_level & 1)
				u_offset = -mtx.hadv * 100 - u_offset;

			u_offset = u_offset * 0.01f * size;
			v_offset = v_offset * 0.01f * size;

			if (is_sideways)
			{
				e = x + u_offset + (mtx.vorg * size);
				f = y - v_offset + (mtx.hadv * 0.5f * size);
			}
			else
			{
				e = x + u_offset;
				f = y - v_offset;
			}

			fz_add_text(doc->ctx, text, glyph_index, char_code, e, f);

			x += advance * 0.01f * size;
		}
	}

	return text;
}

Project: ghostscript
File: xps-glyphs.c

xps_parse_real_num(char *s, float *number)
{
	char buf[64];
	char *p = buf;
	while (is_real_num_char(*s))
		*p++ = *s++;
	*p = 0;
	if (buf[0])
		*number = fz_atof(buf);
	return s;
}

Project: ghostscript
File: xps-glyphs.c

xps_select_best_font_encoding(xps_document *doc, fz_font *font)
{
	static struct { int pid, eid; } xps_cmap_list[] =
	{
		{ 3, 10 },		/* Unicode with surrogates */
		{ 3, 1 },		/* Unicode without surrogates */
		{ 3, 5 },		/* Wansung */
		{ 3, 4 },		/* Big5 */
		{ 3, 3 },		/* Prc */
		{ 3, 2 },		/* ShiftJis */
		{ 3, 0 },		/* Symbol */
		{ 1, 0 },
		{ -1, -1 },
	};

	int i, k, n, pid, eid;

	n = xps_count_font_encodings(font);
	for (k = 0; xps_cmap_list[k].pid != -1; k++)
	{
		for (i = 0; i < n; i++)
		{
			xps_identify_font_encoding(font, i, &pid, &eid);
			if (pid == xps_cmap_list[k].pid && eid == xps_cmap_list[k].eid)
			{
				xps_select_font_encoding(font, i);
				return;
			}
		}
	}

	fz_warn(doc->ctx, "cannot find a suitable cmap");
}

Project: ghostscript
File: xps-glyphs.c

xps_select_font_encoding(fz_font *font, int idx)
{
	FT_Face face = font->ft_face;
	FT_Set_Charmap(face, face->charmaps[idx]);
}

Project: ghostscript
File: xps-gradient.c

static int cmp_stop(const void *a, const void *b)
{
	const struct stop *astop = a;
	const struct stop *bstop = b;
	float diff = astop->offset - bstop->offset;
	if (diff < 0)
		return -1;
	if (diff > 0)
		return 1;
	return astop->index - bstop->index;
}

Project: ghostscript
File: xps-gradient.c

static inline float lerp(float a, float b, float x)
{
	return a + (b - a) * x;
}

Project: ghostscript
File: xps-gradient.c

static inline float point_inside_circle(float px, float py, float x, float y, float r)
{
	float dx = px - x;
	float dy = py - y;
	return dx * dx + dy * dy <= r * r;
}

Project: ghostscript
File: xps-gradient.c

xps_draw_linear_gradient(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(start_point_att, &x0, &y0);
	if (end_point_att)
		xps_parse_point(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(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(doc, ctm, stops, count, 0, x0 + i * dx, y0 + i * dy, x1 + i * dx, y1 + i * dy);
			xps_draw_one_linear_gradient(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(doc, ctm, stops, count, 1, x0, y0, x1, y1);
	}
}

Project: ghostscript
File: xps-gradient.c

xps_draw_one_linear_gradient(xps_document *doc, const fz_matrix *ctm,
	struct stop *stops, int count,
	int extend,
	float x0, float y0, float x1, float y1)
{
	fz_shade *shade;

	/* TODO: this (and the stuff in pdf_shade) should move to res_shade.c */
	shade = fz_malloc_struct(doc->ctx, fz_shade);
	FZ_INIT_STORABLE(shade, 1, fz_free_shade_imp);
	shade->colorspace = fz_device_rgb(doc->ctx);
	shade->bbox = fz_infinite_rect;
	shade->matrix = fz_identity;
	shade->use_background = 0;
	shade->use_function = 1;
	shade->type = FZ_LINEAR;
	shade->u.l_or_r.extend[0] = extend;
	shade->u.l_or_r.extend[1] = extend;

	xps_sample_gradient_stops(shade, stops, count);

	shade->u.l_or_r.coords[0][0] = x0;
	shade->u.l_or_r.coords[0][1] = y0;
	shade->u.l_or_r.coords[0][2] = 0;
	shade->u.l_or_r.coords[1][0] = x1;
	shade->u.l_or_r.coords[1][1] = y1;
	shade->u.l_or_r.coords[1][2] = 0;

	fz_fill_shade(doc->dev, shade, ctm, doc->opacity[doc->opacity_top]);

	fz_drop_shade(doc->ctx, shade);
}

Project: ghostscript
File: xps-gradient.c

xps_draw_radial_gradient(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(origin_att, &x0, &y0);
	if (center_att)
		xps_parse_point(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(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(doc, &local_ctm, stops, count, 0, x0, y0, r0 + i * xrad, x1, y1, r1 + i * xrad);
			xps_draw_one_radial_gradient(doc, &local_ctm, stops, count, 0, x0, y0, r0 + (i + 2) * xrad, x1, y1, r1 + i * xrad);
		}
	}
	else
	{
		xps_draw_one_radial_gradient(doc, &local_ctm, stops, count, 1, x0, y0, r0, x1, y1, r1);
	}
}

Project: ghostscript
File: xps-gradient.c

xps_parse_gradient_brush(xps_document *doc, const fz_matrix *ctm, const fz_rect *area,
	char *base_uri, xps_resource *dict, fz_xml *root,
	void (*draw)(xps_document *, const fz_matrix*, const fz_rect *, struct stop *, int, fz_xml *, int))
{
	fz_xml *node;

	char *opacity_att;
	char *spread_att;
	char *transform_att;

	fz_xml *transform_tag = NULL;
	fz_xml *stop_tag = NULL;

	struct stop stop_list[MAX_STOPS];
	int stop_count;
	fz_matrix transform;
	int spread_method;

	opacity_att = fz_xml_att(root, "Opacity");
	spread_att = fz_xml_att(root, "SpreadMethod");
	transform_att = fz_xml_att(root, "Transform");

	for (node = fz_xml_down(root); node; node = fz_xml_next(node))
	{
		if (!strcmp(fz_xml_tag(node), "LinearGradientBrush.Transform"))
			transform_tag = fz_xml_down(node);
		if (!strcmp(fz_xml_tag(node), "RadialGradientBrush.Transform"))
			transform_tag = fz_xml_down(node);
		if (!strcmp(fz_xml_tag(node), "LinearGradientBrush.GradientStops"))
			stop_tag = fz_xml_down(node);
		if (!strcmp(fz_xml_tag(node), "RadialGradientBrush.GradientStops"))
			stop_tag = fz_xml_down(node);
	}

	xps_resolve_resource_reference(doc, dict, &transform_att, &transform_tag, NULL);

	spread_method = SPREAD_PAD;
	if (spread_att)
	{
		if (!strcmp(spread_att, "Pad"))
			spread_method = SPREAD_PAD;
		if (!strcmp(spread_att, "Reflect"))
			spread_method = SPREAD_REFLECT;
		if (!strcmp(spread_att, "Repeat"))
			spread_method = SPREAD_REPEAT;
	}

	transform = fz_identity;
	if (transform_att)
		xps_parse_render_transform(doc, transform_att, &transform);
	if (transform_tag)
		xps_parse_matrix_transform(doc, transform_tag, &transform);
	fz_concat(&transform, &transform, ctm);

	if (!stop_tag) {
		fz_warn(doc->ctx, "missing gradient stops tag");
		return;
	}

	stop_count = xps_parse_gradient_stops(doc, base_uri, stop_tag, stop_list, MAX_STOPS);
	if (stop_count == 0)
	{
		fz_warn(doc->ctx, "no gradient stops found");
		return;
	}

	xps_begin_opacity(doc, &transform, area, base_uri, dict, opacity_att, NULL);

	draw(doc, &transform, area, stop_list, stop_count, root, spread_method);

	xps_end_opacity(doc, base_uri, dict, opacity_att, NULL);
}

Project: ghostscript
File: xps-gradient.c

xps_parse_linear_gradient_brush(xps_document *doc, const fz_matrix *ctm, const fz_rect *area,
	char *base_uri, xps_resource *dict, fz_xml *root)
{
	xps_parse_gradient_brush(doc, ctm, area, base_uri, dict, root, xps_draw_linear_gradient);
}

Project: ghostscript
File: xps-gradient.c

xps_sample_gradient_stops(fz_shade *shade, struct stop *stops, int count)
{
	float offset, d;
	int i, k;

	k = 0;
	for (i = 0; i < 256; i++)
	{
		offset = i / 255.0f;
		while (k + 1 < count && offset > stops[k+1].offset)
			k++;

		d = (offset - stops[k].offset) / (stops[k+1].offset - stops[k].offset);

		shade->function[i][0] = lerp(stops[k].r, stops[k+1].r, d);
		shade->function[i][1] = lerp(stops[k].g, stops[k+1].g, d);
		shade->function[i][2] = lerp(stops[k].b, stops[k+1].b, d);
		shade->function[i][3] = lerp(stops[k].a, stops[k+1].a, d);
	}
}

Project: ghostscript
File: xps-path.c

angle_between(const fz_point u, const fz_point v)
{
	float det = u.x * v.y - u.y * v.x;
	float sign = (det < 0 ? -1 : 1);
	float magu = u.x * u.x + u.y * u.y;
	float magv = v.x * v.x + v.y * v.y;
	float udotv = u.x * v.x + u.y * v.y;
	float t = udotv / (magu * magv);
	/* guard against rounding errors when near |1| (where acos will return NaN) */
	if (t < -1) t = -1;
	if (t > 1) t = 1;
	return sign * acosf(t);
}

Project: ghostscript
File: xps-path.c

xps_draw_arc_segment(fz_context *doc, fz_path *path, const fz_matrix *mtx, float th0, float th1, int iscw)
{
	float t, d;
	fz_point p;

	while (th1 < th0)
		th1 += (float)M_PI * 2;

	d = (float)M_PI / 180; /* 1-degree precision */

	if (iscw)
	{
		for (t = th0 + d; t < th1 - d/2; t += d)
		{
			p.x = cosf(t);
			p.y = sinf(t);
			fz_transform_point(&p, mtx);
			fz_lineto(doc, path, p.x, p.y);
		}
	}
	else
	{
		th0 += (float)M_PI * 2;
		for (t = th0 - d; t > th1 + d/2; t -= d)
		{
			p.x = cosf(t);
			p.y = sinf(t);
			fz_transform_point(&p, mtx);
			fz_lineto(doc, path, p.x, p.y);
		}
	}
}

Project: ghostscript
File: xps-path.c

xps_parse_abbreviated_geometry(xps_document *doc, char *geom, int *fill_rule)
{
	fz_path *path;
	char **args;
	char **pargs;
	char *s = geom;
	fz_point pt;
	int i, n;
	int cmd, old;
	float x1, y1, x2, y2, x3, y3;
	float smooth_x, smooth_y; /* saved cubic bezier control point for smooth curves */
	int reset_smooth;

	path = fz_new_path(doc->ctx);

	args = fz_malloc_array(doc->ctx, strlen(geom) + 1, sizeof(char*));
	pargs = args;

	while (*s)
	{
		if ((*s >= 'A' && *s <= 'Z') || (*s >= 'a' && *s <= 'z'))
		{
			*pargs++ = s++;
		}
		else if ((*s >= '0' && *s <= '9') || *s == '.' || *s == '+' || *s == '-' || *s == 'e' || *s == 'E')
		{
			*pargs++ = s;
			while ((*s >= '0' && *s <= '9') || *s == '.' || *s == '+' || *s == '-' || *s == 'e' || *s == 'E')
				s ++;
		}
		else
		{
			s++;
		}
	}

	*pargs = s;

	n = pargs - args;
	i = 0;

	old = 0;

	reset_smooth = 1;
	smooth_x = 0;
	smooth_y = 0;

	while (i < n)
	{
		cmd = args[i][0];
		if (cmd == '+' || cmd == '.' || cmd == '-' || (cmd >= '0' && cmd <= '9'))
			cmd = old; /* it's a number, repeat old command */
		else
			i ++;

		if (reset_smooth)
		{
			smooth_x = 0;
			smooth_y = 0;
		}

		reset_smooth = 1;

		switch (cmd)
		{
		case 'F':
			if (i >= n) break;
			*fill_rule = atoi(args[i]);
			i ++;
			break;

		case 'M':
			if (i + 1 >= n) break;
			fz_moveto(doc->ctx, path, fz_atof(args[i]), fz_atof(args[i+1]));
			i += 2;
			break;
		case 'm':
			if (i + 1 >= n) break;
			pt = fz_currentpoint(doc->ctx, path);
			fz_moveto(doc->ctx, path, pt.x + fz_atof(args[i]), pt.y + fz_atof(args[i+1]));
			i += 2;
			break;

		case 'L':
			if (i + 1 >= n) break;
			fz_lineto(doc->ctx, path, fz_atof(args[i]), fz_atof(args[i+1]));
			i += 2;
			break;
		case 'l':
			if (i + 1 >= n) break;
			pt = fz_currentpoint(doc->ctx, path);
			fz_lineto(doc->ctx, path, pt.x + fz_atof(args[i]), pt.y + fz_atof(args[i+1]));
			i += 2;
			break;

		case 'H':
			if (i >= n) break;
			pt = fz_currentpoint(doc->ctx, path);
			fz_lineto(doc->ctx, path, fz_atof(args[i]), pt.y);
			i += 1;
			break;
		case 'h':
			if (i >= n) break;
			pt = fz_currentpoint(doc->ctx, path);
			fz_lineto(doc->ctx, path, pt.x + fz_atof(args[i]), pt.y);
			i += 1;
			break;

		case 'V':
			if (i >= n) break;
			pt = fz_currentpoint(doc->ctx, path);
			fz_lineto(doc->ctx, path, pt.x, fz_atof(args[i]));
			i += 1;
			break;
		case 'v':
			if (i >= n) break;
			pt = fz_currentpoint(doc->ctx, path);
			fz_lineto(doc->ctx, path, pt.x, pt.y + fz_atof(args[i]));
			i += 1;
			break;

		case 'C':
			if (i + 5 >= n) break;
			x1 = fz_atof(args[i+0]);
			y1 = fz_atof(args[i+1]);
			x2 = fz_atof(args[i+2]);
			y2 = fz_atof(args[i+3]);
			x3 = fz_atof(args[i+4]);
			y3 = fz_atof(args[i+5]);
			fz_curveto(doc->ctx, path, x1, y1, x2, y2, x3, y3);
			i += 6;
			reset_smooth = 0;
			smooth_x = x3 - x2;
			smooth_y = y3 - y2;
			break;

		case 'c':
			if (i + 5 >= n) break;
			pt = fz_currentpoint(doc->ctx, path);
			x1 = fz_atof(args[i+0]) + pt.x;
			y1 = fz_atof(args[i+1]) + pt.y;
			x2 = fz_atof(args[i+2]) + pt.x;
			y2 = fz_atof(args[i+3]) + pt.y;
			x3 = fz_atof(args[i+4]) + pt.x;
			y3 = fz_atof(args[i+5]) + pt.y;
			fz_curveto(doc->ctx, path, x1, y1, x2, y2, x3, y3);
			i += 6;
			reset_smooth = 0;
			smooth_x = x3 - x2;
			smooth_y = y3 - y2;
			break;

		case 'S':
			if (i + 3 >= n) break;
			pt = fz_currentpoint(doc->ctx, path);
			x1 = fz_atof(args[i+0]);
			y1 = fz_atof(args[i+1]);
			x2 = fz_atof(args[i+2]);
			y2 = fz_atof(args[i+3]);
			fz_curveto(doc->ctx, path, pt.x + smooth_x, pt.y + smooth_y, x1, y1, x2, y2);
			i += 4;
			reset_smooth = 0;
			smooth_x = x2 - x1;
			smooth_y = y2 - y1;
			break;

		case 's':
			if (i + 3 >= n) break;
			pt = fz_currentpoint(doc->ctx, path);
			x1 = fz_atof(args[i+0]) + pt.x;
			y1 = fz_atof(args[i+1]) + pt.y;
			x2 = fz_atof(args[i+2]) + pt.x;
			y2 = fz_atof(args[i+3]) + pt.y;
			fz_curveto(doc->ctx, path, pt.x + smooth_x, pt.y + smooth_y, x1, y1, x2, y2);
			i += 4;
			reset_smooth = 0;
			smooth_x = x2 - x1;
			smooth_y = y2 - y1;
			break;

		case 'Q':
			if (i + 3 >= n) break;
			pt = fz_currentpoint(doc->ctx, path);
			x1 = fz_atof(args[i+0]);
			y1 = fz_atof(args[i+1]);
			x2 = fz_atof(args[i+2]);
			y2 = fz_atof(args[i+3]);
			fz_curveto(doc->ctx, path,
				(pt.x + 2 * x1) / 3, (pt.y + 2 * y1) / 3,
				(x2 + 2 * x1) / 3, (y2 + 2 * y1) / 3,
				x2, y2);
			i += 4;
			break;
		case 'q':
			if (i + 3 >= n) break;
			pt = fz_currentpoint(doc->ctx, path);
			x1 = fz_atof(args[i+0]) + pt.x;
			y1 = fz_atof(args[i+1]) + pt.y;
			x2 = fz_atof(args[i+2]) + pt.x;
			y2 = fz_atof(args[i+3]) + pt.y;
			fz_curveto(doc->ctx, path,
				(pt.x + 2 * x1) / 3, (pt.y + 2 * y1) / 3,
				(x2 + 2 * x1) / 3, (y2 + 2 * y1) / 3,
				x2, y2);
			i += 4;
			break;

		case 'A':
			if (i + 6 >= n) break;
			xps_draw_arc(doc->ctx, path,
				fz_atof(args[i+0]), fz_atof(args[i+1]), fz_atof(args[i+2]),
				atoi(args[i+3]), atoi(args[i+4]),
				fz_atof(args[i+5]), fz_atof(args[i+6]));
			i += 7;
			break;
		case 'a':
			if (i + 6 >= n) break;
			pt = fz_currentpoint(doc->ctx, path);
			xps_draw_arc(doc->ctx, path,
				fz_atof(args[i+0]), fz_atof(args[i+1]), fz_atof(args[i+2]),
				atoi(args[i+3]), atoi(args[i+4]),
				fz_atof(args[i+5]) + pt.x, fz_atof(args[i+6]) + pt.y);
			i += 7;
			break;

		case 'Z':
		case 'z':
			fz_closepath(doc->ctx, path);
			break;

		default:
			/* eek */
			fz_warn(doc->ctx, "ignoring invalid command '%c'", cmd);
			/* Skip any trailing numbers to avoid an infinite loop */
			while (i < n && (args[i][0] == '+' || args[i][0] == '.' || args[i][0] == '-' || (args[i][0] >= '0' && args[i][0] <= '9')))
				i ++;
			break;
		}

		old = cmd;
	}

	fz_free(doc->ctx, args);
	return path;
}

Project: ghostscript
File: xps-path.c

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);
}

Project: ghostscript
File: xps-path.c

xps_parse_float_array(char *s, int num, float *x)
{
	int k = 0;

	if (s == NULL || *s == 0)
		return NULL;

	while (*s)
	{
		while (*s == 0x0d || *s == '\t' || *s == ' ' || *s == 0x0a)
			s++;
		x[k] = (float)strtod(s, &s);
		while (*s == 0x0d || *s == '\t' || *s == ' ' || *s == 0x0a)
			s++;
		if (*s == ',')
			s++;
		if (++k == num)
			break;
	}
	return s;
}

Project: ghostscript
File: xps-path.c

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

Project: ghostscript
File: xps-path.c

xps_parse_path_geometry(xps_document *doc, xps_resource *dict, fz_xml *root, int stroking, int *fill_rule)
{
	fz_xml *node;

	char *figures_att;
	char *fill_rule_att;
	char *transform_att;

	fz_xml *transform_tag = NULL;
	fz_xml *figures_tag = NULL; /* only used by resource */

	fz_matrix transform;
	fz_path *path;

	figures_att = fz_xml_att(root, "Figures");
	fill_rule_att = fz_xml_att(root, "FillRule");
	transform_att = fz_xml_att(root, "Transform");

	for (node = fz_xml_down(root); node; node = fz_xml_next(node))
	{
		if (!strcmp(fz_xml_tag(node), "PathGeometry.Transform"))
			transform_tag = fz_xml_down(node);
	}

	xps_resolve_resource_reference(doc, dict, &transform_att, &transform_tag, NULL);
	xps_resolve_resource_reference(doc, dict, &figures_att, &figures_tag, NULL);

	if (fill_rule_att)
	{
		if (!strcmp(fill_rule_att, "NonZero"))
			*fill_rule = 1;
		if (!strcmp(fill_rule_att, "EvenOdd"))
			*fill_rule = 0;
	}

	transform = fz_identity;
	if (transform_att)
		xps_parse_render_transform(doc, transform_att, &transform);
	if (transform_tag)
		xps_parse_matrix_transform(doc, transform_tag, &transform);

	if (figures_att)
		path = xps_parse_abbreviated_geometry(doc, figures_att, fill_rule);
	else
		path = fz_new_path(doc->ctx);

	if (figures_tag)
		xps_parse_path_figure(doc->ctx, path, figures_tag, stroking);

	for (node = fz_xml_down(root); node; node = fz_xml_next(node))
	{
		if (!strcmp(fz_xml_tag(node), "PathFigure"))
			xps_parse_path_figure(doc->ctx, path, node, stroking);
	}

	if (transform_att || transform_tag)
		fz_transform_path(doc->ctx, path, &transform);

	return path;
}

Project: ghostscript
File: xps-path.c

xps_parse_point(char *s_in, float *x, float *y)
{
	char *s_out = s_in;
	float xy[2];

	s_out = xps_parse_float_array(s_out, 2, &xy[0]);
	*x = xy[0];
	*y = xy[1];
	return s_out;
}

Project: ghostscript
File: xps-path.c

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;
		}
	}
}

Project: ghostscript
File: xps-path.c

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);
	}
}

Project: ghostscript
File: xps-path.c

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;
		}
	}
}