示例#1
0
文件: xps-doc.c 项目: krytikul/nPDF
static void
xps_parse_metadata_imp(fz_context *ctx, xps_document *doc, fz_xml *item, xps_fixdoc *fixdoc)
{
	while (item)
	{
		if (fz_xml_is_tag(item, "Relationship"))
		{
			char *target = fz_xml_att(item, "Target");
			char *type = fz_xml_att(item, "Type");
			if (target && type)
			{
				char tgtbuf[1024];
				xps_resolve_url(ctx, doc, tgtbuf, doc->base_uri, target, sizeof tgtbuf);
				if (!strcmp(type, REL_START_PART) || !strcmp(type, REL_START_PART_OXPS))
					doc->start_part = fz_strdup(ctx, tgtbuf);
				if ((!strcmp(type, REL_DOC_STRUCTURE) || !strcmp(type, REL_DOC_STRUCTURE_OXPS)) && fixdoc)
					fixdoc->outline = fz_strdup(ctx, tgtbuf);
				if (!fz_xml_att(item, "Id"))
					fz_warn(ctx, "missing relationship id for %s", target);
			}
		}

		if (fz_xml_is_tag(item, "DocumentReference"))
		{
			char *source = fz_xml_att(item, "Source");
			if (source)
			{
				char srcbuf[1024];
				xps_resolve_url(ctx, doc, srcbuf, doc->base_uri, source, sizeof srcbuf);
				xps_add_fixed_document(ctx, doc, srcbuf);
			}
		}

		if (fz_xml_is_tag(item, "PageContent"))
		{
			char *source = fz_xml_att(item, "Source");
			char *width_att = fz_xml_att(item, "Width");
			char *height_att = fz_xml_att(item, "Height");
			int width = width_att ? atoi(width_att) : 0;
			int height = height_att ? atoi(height_att) : 0;
			if (source)
			{
				char srcbuf[1024];
				xps_resolve_url(ctx, doc, srcbuf, doc->base_uri, source, sizeof srcbuf);
				xps_add_fixed_page(ctx, doc, srcbuf, width, height);
			}
		}

		if (fz_xml_is_tag(item, "LinkTarget"))
		{
			char *name = fz_xml_att(item, "Name");
			if (name)
				xps_add_link_target(ctx, doc, name);
		}

		xps_parse_metadata_imp(ctx, doc, fz_xml_down(item), fixdoc);

		item = fz_xml_next(item);
	}
}
示例#2
0
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 (fz_xml_is_tag(node, "ArcSegment"))
			xps_parse_arc_segment(doc, path, node, stroking, &skipped_stroke);
		if (fz_xml_is_tag(node, "PolyBezierSegment"))
			xps_parse_poly_bezier_segment(doc, path, node, stroking, &skipped_stroke);
		if (fz_xml_is_tag(node, "PolyLineSegment"))
			xps_parse_poly_line_segment(doc, path, node, stroking, &skipped_stroke);
		if (fz_xml_is_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 */
	}
}
示例#3
0
文件: svg-run.c 项目: muennich/mupdf
static void
svg_run_use(fz_context *ctx, fz_device *dev, svg_document *doc, fz_xml *root, const svg_state *inherit_state)
{
	svg_state local_state = *inherit_state;

	char *xlink_href_att = fz_xml_att(root, "xlink:href");
	char *x_att = fz_xml_att(root, "x");
	char *y_att = fz_xml_att(root, "y");

	float x = 0;
	float y = 0;

	svg_parse_common(ctx, doc, root, &local_state);
	if (x_att) x = svg_parse_length(x_att, local_state.viewbox_w, local_state.fontsize);
	if (y_att) y = svg_parse_length(y_att, local_state.viewbox_h, local_state.fontsize);

	fz_pre_translate(&local_state.transform, x, y);

	if (xlink_href_att && xlink_href_att[0] == '#')
	{
		fz_xml *linked = fz_tree_lookup(ctx, doc->idmap, xlink_href_att + 1);
		if (linked)
		{
			if (fz_xml_is_tag(linked, "symbol"))
				svg_run_use_symbol(ctx, dev, doc, root, linked, &local_state);
			else
				svg_run_element(ctx, dev, doc, linked, &local_state);
			return;
		}
	}

	fz_warn(ctx, "svg: cannot find linked symbol");
}
示例#4
0
void
xps_parse_visual_brush(fz_context *ctx, xps_document *doc, const fz_matrix *ctm, const fz_rect *area,
	char *base_uri, xps_resource *dict, fz_xml *root)
{
	fz_xml *node;

	char *visual_uri;
	char *visual_att;
	fz_xml *visual_tag = NULL;

	visual_att = fz_xml_att(root, "Visual");

	for (node = fz_xml_down(root); node; node = fz_xml_next(node))
	{
		if (fz_xml_is_tag(node, "VisualBrush.Visual"))
			visual_tag = fz_xml_down(node);
	}

	visual_uri = base_uri;
	xps_resolve_resource_reference(ctx, doc, dict, &visual_att, &visual_tag, &visual_uri);

	if (visual_tag)
	{
		xps_parse_tiling_brush(ctx, doc, ctm, area,
			visual_uri, dict, root, xps_paint_visual_brush, visual_tag);
	}
}
示例#5
0
文件: xps-link.c 项目: ccxvii/mupdf
static void
xps_load_links_in_element(fz_context *ctx, xps_document *doc, const fz_matrix *ctm, char *base_uri, xps_resource *dict, fz_xml *node, fz_link **link)
{
	if (fz_xml_is_tag(node, "Path"))
		xps_load_links_in_path(ctx, doc, ctm, base_uri, dict, node, link);
	else if (fz_xml_is_tag(node, "Glyphs"))
		xps_load_links_in_glyphs(ctx, doc, ctm, base_uri, dict, node, link);
	else if (fz_xml_is_tag(node, "Canvas"))
		xps_load_links_in_canvas(ctx, doc, ctm, base_uri, dict, node, link);
	else if (fz_xml_is_tag(node, "AlternateContent"))
	{
		node = xps_lookup_alternate_content(ctx, doc, node);
		if (node)
			xps_load_links_in_element(ctx, doc, ctm, base_uri, dict, node, link);
	}
}
示例#6
0
文件: xps-common.c 项目: Enzime/mupdf
void
xps_parse_brush(fz_context *ctx, 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;
	/* SolidColorBrushes are handled in a special case and will never show up here */
	if (fz_xml_is_tag(node, "ImageBrush"))
		xps_parse_image_brush(ctx, doc, ctm, area, base_uri, dict, node);
	else if (fz_xml_is_tag(node, "VisualBrush"))
		xps_parse_visual_brush(ctx, doc, ctm, area, base_uri, dict, node);
	else if (fz_xml_is_tag(node, "LinearGradientBrush"))
		xps_parse_linear_gradient_brush(ctx, doc, ctm, area, base_uri, dict, node);
	else if (fz_xml_is_tag(node, "RadialGradientBrush"))
		xps_parse_radial_gradient_brush(ctx, doc, ctm, area, base_uri, dict, node);
	else
		fz_warn(ctx, "unknown brush tag: %s", fz_xml_tag(node));
}
示例#7
0
文件: xps-common.c 项目: Enzime/mupdf
void
xps_parse_element(fz_context *ctx, 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 (fz_xml_is_tag(node, "Path"))
		xps_parse_path(ctx, doc, ctm, base_uri, dict, node);
	if (fz_xml_is_tag(node, "Glyphs"))
		xps_parse_glyphs(ctx, doc, ctm, base_uri, dict, node);
	if (fz_xml_is_tag(node, "Canvas"))
		xps_parse_canvas(ctx, doc, ctm, area, base_uri, dict, node);
	if (fz_xml_is_tag(node, "AlternateContent"))
	{
		node = xps_lookup_alternate_content(ctx, doc, node);
		if (node)
			xps_parse_element(ctx, doc, ctm, area, base_uri, dict, node);
	}
	/* skip unknown tags (like Foo.Resources and similar) */
}
示例#8
0
文件: xps-common.c 项目: Enzime/mupdf
fz_xml *
xps_lookup_alternate_content(fz_context *ctx, xps_document *doc, fz_xml *node)
{
	for (node = fz_xml_down(node); node; node = fz_xml_next(node))
	{
		if (fz_xml_is_tag(node, "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 (fz_xml_is_tag(node, "Fallback"))
			return fz_xml_down(node);
	}
	return NULL;
}
示例#9
0
文件: xps-common.c 项目: Enzime/mupdf
static void
xps_parse_matrix_transform(fz_context *ctx, xps_document *doc, fz_xml *root, fz_matrix *matrix)
{
	char *transform;

	*matrix = fz_identity;

	if (fz_xml_is_tag(root, "MatrixTransform"))
	{
		transform = fz_xml_att(root, "Matrix");
		if (transform)
			xps_parse_render_transform(ctx, doc, transform, matrix);
	}
}
示例#10
0
static xps_resource *
xps_parse_remote_resource_dictionary(fz_context *ctx, xps_document *doc, char *base_uri, char *source_att)
{
	char part_name[1024];
	char part_uri[1024];
	xps_part *part;
	xps_resource *dict = NULL;
	fz_xml_doc *xml = NULL;
	char *s;

	fz_var(xml);

	/* External resource dictionaries MUST NOT reference other resource dictionaries */
	xps_resolve_url(ctx, doc, part_name, base_uri, source_att, sizeof part_name);

	part = xps_read_part(ctx, doc, part_name);
	fz_try(ctx)
	{
		xml = fz_parse_xml(ctx, part->data, 0);
		if (!fz_xml_is_tag(fz_xml_root(xml), "ResourceDictionary"))
			fz_throw(ctx, FZ_ERROR_GENERIC, "expected ResourceDictionary element");

		fz_strlcpy(part_uri, part_name, sizeof part_uri);
		s = strrchr(part_uri, '/');
		if (s)
			s[1] = 0;

		dict = xps_parse_resource_dictionary(ctx, doc, part_uri, fz_xml_root(xml));
		if (dict)
		{
			dict->base_xml = xml; /* pass on ownership */
			xml = NULL;
		}
	}
	fz_always(ctx)
	{
		xps_drop_part(ctx, doc, part);
		fz_drop_xml(ctx, xml);
	}
	fz_catch(ctx)
	{
		fz_rethrow(ctx);
	}

	return dict;
}
示例#11
0
void
xps_parse_fixed_page(fz_context *ctx, xps_document *doc, const fz_matrix *ctm, xps_page *page)
{
	fz_xml *root, *node;
	xps_resource *dict;
	char base_uri[1024];
	fz_rect area;
	char *s;
	fz_matrix scm;

	fz_strlcpy(base_uri, page->fix->name, sizeof base_uri);
	s = strrchr(base_uri, '/');
	if (s)
		s[1] = 0;

	dict = NULL;

	doc->opacity_top = 0;
	doc->opacity[0] = 1;

	root = fz_xml_root(page->xml);
	if (!root)
		return;

	area = fz_unit_rect;
	fz_transform_rect(&area, fz_scale(&scm, page->fix->width, page->fix->height));

	fz_try(ctx)
	{
		for (node = fz_xml_down(root); node; node = fz_xml_next(node))
		{
			if (fz_xml_is_tag(node, "FixedPage.Resources") && fz_xml_down(node))
			{
				if (dict)
					fz_warn(ctx, "ignoring follow-up resource dictionaries");
				else
					dict = xps_parse_resource_dictionary(ctx, doc, base_uri, fz_xml_down(node));
			}
			xps_parse_element(ctx, doc, ctm, &area, base_uri, dict, node);
		}
	}
	fz_always(ctx)
		xps_drop_resource_dictionary(ctx, doc, dict);
	fz_catch(ctx)
		fz_rethrow(ctx);
}
示例#12
0
static fz_css_rule *
html_load_css(fz_context *ctx, fz_archive *zip, const char *base_uri, fz_css_rule *css, fz_xml *root)
{
    fz_xml *html, *head, *node;
    fz_buffer *buf;
    char path[2048];

    fz_var(buf);

    html = fz_xml_find(root, "html");
    head = fz_xml_find_down(html, "head");
    for (node = fz_xml_down(head); node; node = fz_xml_next(node))
    {
        if (fz_xml_is_tag(node, "link"))
        {
            char *rel = fz_xml_att(node, "rel");
            if (rel && !fz_strcasecmp(rel, "stylesheet"))
            {
                char *type = fz_xml_att(node, "type");
                if ((type && !strcmp(type, "text/css")) || !type)
                {
                    char *href = fz_xml_att(node, "href");
                    if (href)
                    {
                        fz_strlcpy(path, base_uri, sizeof path);
                        fz_strlcat(path, "/", sizeof path);
                        fz_strlcat(path, href, sizeof path);
                        fz_urldecode(path);
                        fz_cleanname(path);

                        buf = NULL;
                        fz_try(ctx)
                        {
                            buf = fz_read_archive_entry(ctx, zip, path);
                            fz_write_buffer_byte(ctx, buf, 0);
                            css = fz_parse_css(ctx, css, (char*)buf->data, path);
                        }
                        fz_always(ctx)
                        fz_drop_buffer(ctx, buf);
                        fz_catch(ctx)
                        fz_warn(ctx, "ignoring stylesheet %s", path);
                    }
                }
            }
示例#13
0
void
svg_parse_document_bounds(fz_context *ctx, svg_document *doc, fz_xml *root)
{
	char *version_att;
	char *w_att;
	char *h_att;
	char *viewbox_att;
	int version;

	if (!fz_xml_is_tag(root, "svg"))
		fz_throw(ctx, FZ_ERROR_GENERIC, "expected svg element (found %s)", fz_xml_tag(root));

	version_att = fz_xml_att(root, "version");
	w_att = fz_xml_att(root, "width");
	h_att = fz_xml_att(root, "height");
	viewbox_att = fz_xml_att(root, "viewBox");

	version = 10;
	if (version_att)
		version = fz_atof(version_att) * 10;

	if (version > 12)
		fz_warn(ctx, "svg document version is newer than we support");

	/* If no width or height attributes, then guess from the viewbox */
	if (w_att == NULL && h_att == NULL && viewbox_att != NULL)
	{
		float min_x, min_y, box_w, box_h;
		sscanf(viewbox_att, "%g %g %g %g", &min_x, &min_y, &box_w, &box_h);
		doc->width = box_w;
		doc->height = box_h;
	}
	else
	{
		doc->width = DEF_WIDTH;
		if (w_att)
			doc->width = svg_parse_length(w_att, doc->width, DEF_FONTSIZE);

		doc->height = DEF_HEIGHT;
		if (h_att)
			doc->height = svg_parse_length(h_att, doc->height, DEF_FONTSIZE);
	}
}
示例#14
0
void
xps_parse_path(xps_document *doc, const fz_matrix *ctm, char *base_uri, xps_resource *dict, fz_xml *root)
{
	fz_xml *node;

	char *fill_uri;
	char *stroke_uri;
	char *opacity_mask_uri;

	char *transform_att;
	char *clip_att;
	char *data_att;
	char *fill_att;
	char *stroke_att;
	char *opacity_att;
	char *opacity_mask_att;

	fz_xml *transform_tag = NULL;
	fz_xml *clip_tag = NULL;
	fz_xml *data_tag = NULL;
	fz_xml *fill_tag = NULL;
	fz_xml *stroke_tag = NULL;
	fz_xml *opacity_mask_tag = NULL;

	char *fill_opacity_att = NULL;
	char *stroke_opacity_att = NULL;

	char *stroke_dash_array_att;
	char *stroke_dash_cap_att;
	char *stroke_dash_offset_att;
	char *stroke_end_line_cap_att;
	char *stroke_start_line_cap_att;
	char *stroke_line_join_att;
	char *stroke_miter_limit_att;
	char *stroke_thickness_att;
	char *navigate_uri_att;

	fz_stroke_state *stroke = NULL;
	fz_matrix transform;
	float samples[FZ_MAX_COLORS];
	fz_colorspace *colorspace;
	fz_path *path = NULL;
	fz_path *stroke_path = NULL;
	fz_rect area;
	int fill_rule;
	int dash_len = 0;
	fz_matrix local_ctm;

	/*
	 * Extract attributes and extended attributes.
	 */

	transform_att = fz_xml_att(root, "RenderTransform");
	clip_att = fz_xml_att(root, "Clip");
	data_att = fz_xml_att(root, "Data");
	fill_att = fz_xml_att(root, "Fill");
	stroke_att = fz_xml_att(root, "Stroke");
	opacity_att = fz_xml_att(root, "Opacity");
	opacity_mask_att = fz_xml_att(root, "OpacityMask");

	stroke_dash_array_att = fz_xml_att(root, "StrokeDashArray");
	stroke_dash_cap_att = fz_xml_att(root, "StrokeDashCap");
	stroke_dash_offset_att = fz_xml_att(root, "StrokeDashOffset");
	stroke_end_line_cap_att = fz_xml_att(root, "StrokeEndLineCap");
	stroke_start_line_cap_att = fz_xml_att(root, "StrokeStartLineCap");
	stroke_line_join_att = fz_xml_att(root, "StrokeLineJoin");
	stroke_miter_limit_att = fz_xml_att(root, "StrokeMiterLimit");
	stroke_thickness_att = fz_xml_att(root, "StrokeThickness");
	navigate_uri_att = fz_xml_att(root, "FixedPage.NavigateUri");

	for (node = fz_xml_down(root); node; node = fz_xml_next(node))
	{
		if (fz_xml_is_tag(node, "Path.RenderTransform"))
			transform_tag = fz_xml_down(node);
		if (fz_xml_is_tag(node, "Path.OpacityMask"))
			opacity_mask_tag = fz_xml_down(node);
		if (fz_xml_is_tag(node, "Path.Clip"))
			clip_tag = fz_xml_down(node);
		if (fz_xml_is_tag(node, "Path.Fill"))
			fill_tag = fz_xml_down(node);
		if (fz_xml_is_tag(node, "Path.Stroke"))
			stroke_tag = fz_xml_down(node);
		if (fz_xml_is_tag(node, "Path.Data"))
			data_tag = fz_xml_down(node);
	}

	fill_uri = base_uri;
	stroke_uri = base_uri;
	opacity_mask_uri = base_uri;

	xps_resolve_resource_reference(doc, dict, &data_att, &data_tag, NULL);
	xps_resolve_resource_reference(doc, dict, &clip_att, &clip_tag, NULL);
	xps_resolve_resource_reference(doc, dict, &transform_att, &transform_tag, NULL);
	xps_resolve_resource_reference(doc, dict, &fill_att, &fill_tag, &fill_uri);
	xps_resolve_resource_reference(doc, dict, &stroke_att, &stroke_tag, &stroke_uri);
	xps_resolve_resource_reference(doc, dict, &opacity_mask_att, &opacity_mask_tag, &opacity_mask_uri);

	/*
	 * Act on the information we have gathered:
	 */

	if (!data_att && !data_tag)
		return;

	if (fill_tag && !strcmp(fz_xml_tag(fill_tag), "SolidColorBrush"))
	{
		fill_opacity_att = fz_xml_att(fill_tag, "Opacity");
		fill_att = fz_xml_att(fill_tag, "Color");
		fill_tag = NULL;
	}

	if (stroke_tag && !strcmp(fz_xml_tag(stroke_tag), "SolidColorBrush"))
	{
		stroke_opacity_att = fz_xml_att(stroke_tag, "Opacity");
		stroke_att = fz_xml_att(stroke_tag, "Color");
		stroke_tag = NULL;
	}

	if (stroke_att || stroke_tag)
	{
		if (stroke_dash_array_att)
		{
			char *s = stroke_dash_array_att;

			while (*s)
			{
				while (*s == ' ')
					s++;
				if (*s) /* needed in case of a space before the last quote */
					dash_len++;

				while (*s && *s != ' ')
					s++;
			}
		}
		stroke = fz_new_stroke_state_with_dash_len(doc->ctx, dash_len);
		stroke->start_cap = xps_parse_line_cap(stroke_start_line_cap_att);
		stroke->dash_cap = xps_parse_line_cap(stroke_dash_cap_att);
		stroke->end_cap = xps_parse_line_cap(stroke_end_line_cap_att);

		stroke->linejoin = FZ_LINEJOIN_MITER_XPS;
		if (stroke_line_join_att)
		{
			if (!strcmp(stroke_line_join_att, "Miter")) stroke->linejoin = FZ_LINEJOIN_MITER_XPS;
			if (!strcmp(stroke_line_join_att, "Round")) stroke->linejoin = FZ_LINEJOIN_ROUND;
			if (!strcmp(stroke_line_join_att, "Bevel")) stroke->linejoin = FZ_LINEJOIN_BEVEL;
		}

		stroke->miterlimit = 10;
		if (stroke_miter_limit_att)
			stroke->miterlimit = fz_atof(stroke_miter_limit_att);

		stroke->linewidth = 1;
		if (stroke_thickness_att)
			stroke->linewidth = fz_atof(stroke_thickness_att);

		stroke->dash_phase = 0;
		stroke->dash_len = 0;
		if (stroke_dash_array_att)
		{
			char *s = stroke_dash_array_att;

			if (stroke_dash_offset_att)
				stroke->dash_phase = fz_atof(stroke_dash_offset_att) * stroke->linewidth;

			while (*s)
			{
				while (*s == ' ')
					s++;
				if (*s) /* needed in case of a space before the last quote */
					stroke->dash_list[stroke->dash_len++] = fz_atof(s) * stroke->linewidth;
				while (*s && *s != ' ')
					s++;
			}
			if (dash_len > 0)
			{
				/* fz_stroke_path doesn't draw non-empty paths with phase length zero */
				float phase_len = 0;
				int i;
				for (i = 0; i < dash_len; i++)
					phase_len += stroke->dash_list[i];
				if (phase_len == 0)
					dash_len = 0;
			}
			stroke->dash_len = dash_len;
		}
	}

	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(&local_ctm, &transform, ctm);

	if (clip_att || clip_tag)
		xps_clip(doc, &local_ctm, dict, clip_att, clip_tag);

	fill_rule = 0;
	if (data_att)
		path = xps_parse_abbreviated_geometry(doc, data_att, &fill_rule);
	else if (data_tag)
	{
		path = xps_parse_path_geometry(doc, dict, data_tag, 0, &fill_rule);
		if (stroke_att || stroke_tag)
			stroke_path = xps_parse_path_geometry(doc, dict, data_tag, 1, &fill_rule);
	}
	if (!stroke_path)
		stroke_path = path;

	if (stroke_att || stroke_tag)
	{
		fz_bound_path(doc->ctx, stroke_path, stroke, &local_ctm, &area);
		if (stroke_path != path && (fill_att || fill_tag)) {
			fz_rect bounds;
			fz_bound_path(doc->ctx, path, NULL, &local_ctm, &bounds);
			fz_union_rect(&area, &bounds);
		}
	}
	else
		fz_bound_path(doc->ctx, path, NULL, &local_ctm, &area);

	/* SumatraPDF: extended link support */
	xps_extract_anchor_info(doc, &area, navigate_uri_att, fz_xml_att(root, "Name"), 0);
	navigate_uri_att = NULL;

	if (navigate_uri_att)
		xps_add_link(doc, &area, base_uri, navigate_uri_att);

	xps_begin_opacity(doc, &local_ctm, &area, opacity_mask_uri, dict, opacity_att, opacity_mask_tag);

	if (fill_att)
	{
		xps_parse_color(doc, base_uri, fill_att, &colorspace, samples);
		if (fill_opacity_att)
			samples[0] *= fz_atof(fill_opacity_att);
		xps_set_color(doc, colorspace, samples);

		fz_fill_path(doc->dev, path, fill_rule == 0, &local_ctm,
			doc->colorspace, doc->color, doc->alpha);
	}

	if (fill_tag)
	{
		fz_clip_path(doc->dev, path, &area, fill_rule == 0, &local_ctm);
		xps_parse_brush(doc, &local_ctm, &area, fill_uri, dict, fill_tag);
		fz_pop_clip(doc->dev);
	}

	if (stroke_att)
	{
		xps_parse_color(doc, base_uri, stroke_att, &colorspace, samples);
		if (stroke_opacity_att)
			samples[0] *= fz_atof(stroke_opacity_att);
		xps_set_color(doc, colorspace, samples);

		fz_stroke_path(doc->dev, stroke_path, stroke, &local_ctm,
			doc->colorspace, doc->color, doc->alpha);
	}

	if (stroke_tag)
	{
		fz_clip_stroke_path(doc->dev, stroke_path, &area, stroke, &local_ctm);
		xps_parse_brush(doc, &local_ctm, &area, stroke_uri, dict, stroke_tag);
		fz_pop_clip(doc->dev);
	}

	xps_end_opacity(doc, opacity_mask_uri, dict, opacity_att, opacity_mask_tag);

	if (stroke_path != path)
		fz_free_path(doc->ctx, stroke_path);
	fz_free_path(doc->ctx, path);
	path = NULL;
	fz_drop_stroke_state(doc->ctx, stroke);

	if (clip_att || clip_tag)
		fz_pop_clip(doc->dev);
}
示例#15
0
fz_path *
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 (fz_xml_is_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 (fz_xml_is_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;
}
示例#16
0
文件: svg-run.c 项目: muennich/mupdf
static void
svg_run_element(fz_context *ctx, fz_device *dev, svg_document *doc, fz_xml *root, const svg_state *state)
{
	if (fz_xml_is_tag(root, "svg"))
		svg_run_svg(ctx, dev, doc, root, state);

	else if (fz_xml_is_tag(root, "g"))
		svg_run_g(ctx, dev, doc, root, state);

	else if (fz_xml_is_tag(root, "title"))
		;
	else if (fz_xml_is_tag(root, "desc"))
		;

	else if (fz_xml_is_tag(root, "defs"))
		;
	else if (fz_xml_is_tag(root, "symbol"))
		;

	else if (fz_xml_is_tag(root, "use"))
		svg_run_use(ctx, dev, doc, root, state);

	else if (fz_xml_is_tag(root, "path"))
		svg_run_path(ctx, dev, doc, root, state);
	else if (fz_xml_is_tag(root, "rect"))
		svg_run_rect(ctx, dev, doc, root, state);
	else if (fz_xml_is_tag(root, "circle"))
		svg_run_circle(ctx, dev, doc, root, state);
	else if (fz_xml_is_tag(root, "ellipse"))
		svg_run_ellipse(ctx, dev, doc, root, state);
	else if (fz_xml_is_tag(root, "line"))
		svg_run_line(ctx, dev, doc, root, state);
	else if (fz_xml_is_tag(root, "polyline"))
		svg_run_polyline(ctx, dev, doc, root, state);
	else if (fz_xml_is_tag(root, "polygon"))
		svg_run_polygon(ctx, dev, doc, root, state);

#if 0
	else if (fz_xml_is_tag(root, "image"))
		svg_parse_image(ctx, doc, root);
	else if (fz_xml_is_tag(root, "text"))
		svg_run_text(ctx, dev, doc, root);
	else if (fz_xml_is_tag(root, "tspan"))
		svg_run_text_span(ctx, dev, doc, root);
	else if (fz_xml_is_tag(root, "tref"))
		svg_run_text_ref(ctx, dev, doc, root);
	else if (fz_xml_is_tag(root, "textPath"))
		svg_run_text_path(ctx, dev, doc, root);
#endif

	else
	{
		/* ignore unrecognized tags */
	}
}
示例#17
0
static int
xps_parse_gradient_stops(fz_context *ctx, xps_document *doc, char *base_uri, fz_xml *node,
	struct stop *stops, int maxcount)
{
	fz_colorspace *colorspace;
	float sample[FZ_MAX_COLORS];
	float rgb[3];
	int before, after;
	int count;
	int i;

	/* We may have to insert 2 extra stops when postprocessing */
	maxcount -= 2;

	count = 0;
	while (node && count < maxcount)
	{
		if (fz_xml_is_tag(node, "GradientStop"))
		{
			char *offset = fz_xml_att(node, "Offset");
			char *color = fz_xml_att(node, "Color");
			if (offset && color)
			{
				stops[count].offset = fz_atof(offset);
				stops[count].index = count;

				xps_parse_color(ctx, doc, base_uri, color, &colorspace, sample);

				fz_convert_color(ctx, fz_device_rgb(ctx), rgb, colorspace, sample + 1);

				stops[count].r = rgb[0];
				stops[count].g = rgb[1];
				stops[count].b = rgb[2];
				stops[count].a = sample[0];

				count ++;
			}
		}
		node = fz_xml_next(node);
	}

	if (count == 0)
	{
		fz_warn(ctx, "gradient brush has no gradient stops");
		stops[0].offset = 0;
		stops[0].r = 0;
		stops[0].g = 0;
		stops[0].b = 0;
		stops[0].a = 1;
		stops[1].offset = 1;
		stops[1].r = 1;
		stops[1].g = 1;
		stops[1].b = 1;
		stops[1].a = 1;
		return 2;
	}

	if (count == maxcount)
		fz_warn(ctx, "gradient brush exceeded maximum number of gradient stops");

	/* Postprocess to make sure the range of offsets is 0.0 to 1.0 */

	qsort(stops, count, sizeof(struct stop), cmp_stop);

	before = -1;
	after = -1;

	for (i = 0; i < count; i++)
	{
		if (stops[i].offset < 0)
			before = i;
		if (stops[i].offset > 1)
		{
			after = i;
			break;
		}
	}

	/* Remove all stops < 0 except the largest one */
	if (before > 0)
	{
		memmove(stops, stops + before, (count - before) * sizeof(struct stop));
		count -= before;
	}

	/* Remove all stops > 1 except the smallest one */
	if (after >= 0)
		count = after + 1;

	/* Expand single stop to 0 .. 1 */
	if (count == 1)
	{
		stops[1] = stops[0];
		stops[0].offset = 0;
		stops[1].offset = 1;
		return 2;
	}

	/* First stop < 0 -- interpolate value to 0 */
	if (stops[0].offset < 0)
	{
		float d = -stops[0].offset / (stops[1].offset - stops[0].offset);
		stops[0].offset = 0;
		stops[0].r = lerp(stops[0].r, stops[1].r, d);
		stops[0].g = lerp(stops[0].g, stops[1].g, d);
		stops[0].b = lerp(stops[0].b, stops[1].b, d);
		stops[0].a = lerp(stops[0].a, stops[1].a, d);
	}

	/* Last stop > 1 -- interpolate value to 1 */
	if (stops[count-1].offset > 1)
	{
		float d = (1 - stops[count-2].offset) / (stops[count-1].offset - stops[count-2].offset);
		stops[count-1].offset = 1;
		stops[count-1].r = lerp(stops[count-2].r, stops[count-1].r, d);
		stops[count-1].g = lerp(stops[count-2].g, stops[count-1].g, d);
		stops[count-1].b = lerp(stops[count-2].b, stops[count-1].b, d);
		stops[count-1].a = lerp(stops[count-2].a, stops[count-1].a, d);
	}

	/* First stop > 0 -- insert a duplicate at 0 */
	if (stops[0].offset > 0)
	{
		memmove(stops + 1, stops, count * sizeof(struct stop));
		stops[0] = stops[1];
		stops[0].offset = 0;
		count++;
	}

	/* Last stop < 1 -- insert a duplicate at 1 */
	if (stops[count-1].offset < 1)
	{
		stops[count] = stops[count-1];
		stops[count].offset = 1;
		count++;
	}

	return count;
}
示例#18
0
void
xps_parse_canvas(fz_context *ctx, xps_document *doc, const fz_matrix *ctm, const fz_rect *area, char *base_uri, xps_resource *dict, fz_xml *root)
{
	fz_device *dev = doc->dev;
	xps_resource *new_dict = NULL;
	fz_xml *node;
	char *opacity_mask_uri;

	char *transform_att;
	char *clip_att;
	char *opacity_att;
	char *opacity_mask_att;
	char *navigate_uri_att;

	fz_xml *transform_tag = NULL;
	fz_xml *clip_tag = NULL;
	fz_xml *opacity_mask_tag = NULL;

	fz_matrix transform;

	transform_att = fz_xml_att(root, "RenderTransform");
	clip_att = fz_xml_att(root, "Clip");
	opacity_att = fz_xml_att(root, "Opacity");
	opacity_mask_att = fz_xml_att(root, "OpacityMask");
	navigate_uri_att = fz_xml_att(root, "FixedPage.NavigateUri");

	for (node = fz_xml_down(root); node; node = fz_xml_next(node))
	{
		if (fz_xml_is_tag(node, "Canvas.Resources") && fz_xml_down(node))
		{
			if (new_dict)
			{
				fz_warn(ctx, "ignoring follow-up resource dictionaries");
			}
			else
			{
				new_dict = xps_parse_resource_dictionary(ctx, doc, base_uri, fz_xml_down(node));
				if (new_dict)
				{
					new_dict->parent = dict;
					dict = new_dict;
				}
			}
		}

		if (fz_xml_is_tag(node, "Canvas.RenderTransform"))
			transform_tag = fz_xml_down(node);
		if (fz_xml_is_tag(node, "Canvas.Clip"))
			clip_tag = fz_xml_down(node);
		if (fz_xml_is_tag(node, "Canvas.OpacityMask"))
			opacity_mask_tag = fz_xml_down(node);
	}

	opacity_mask_uri = base_uri;
	xps_resolve_resource_reference(ctx, doc, dict, &transform_att, &transform_tag, NULL);
	xps_resolve_resource_reference(ctx, doc, dict, &clip_att, &clip_tag, NULL);
	xps_resolve_resource_reference(ctx, doc, dict, &opacity_mask_att, &opacity_mask_tag, &opacity_mask_uri);

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

	if (navigate_uri_att)
		xps_add_link(ctx, doc, area, base_uri, navigate_uri_att);

	if (clip_att || clip_tag)
		xps_clip(ctx, doc, &transform, dict, clip_att, clip_tag);

	xps_begin_opacity(ctx, doc, &transform, area, opacity_mask_uri, dict, opacity_att, opacity_mask_tag);

	for (node = fz_xml_down(root); node; node = fz_xml_next(node))
	{
		xps_parse_element(ctx, doc, &transform, area, base_uri, dict, node);
	}

	xps_end_opacity(ctx, doc, opacity_mask_uri, dict, opacity_att, opacity_mask_tag);

	if (clip_att || clip_tag)
		fz_pop_clip(ctx, dev);

	if (new_dict)
		xps_drop_resource_dictionary(ctx, doc, new_dict);
}
示例#19
0
void
xps_parse_tiling_brush(fz_context *ctx, xps_document *doc, const fz_matrix *ctm, const fz_rect *area,
	char *base_uri, xps_resource *dict, fz_xml *root,
	void (*func)(fz_context *ctx, xps_document*, const fz_matrix*, const fz_rect*, char*, xps_resource*, fz_xml*, void*), void *user)
{
	fz_device *dev = doc->dev;
	fz_xml *node;
	struct closure c;

	char *opacity_att;
	char *transform_att;
	char *viewbox_att;
	char *viewport_att;
	char *tile_mode_att;

	fz_xml *transform_tag = NULL;

	fz_matrix transform;
	fz_rect viewbox;
	fz_rect viewport;
	float xstep, ystep;
	float xscale, yscale;
	int tile_mode;

	opacity_att = fz_xml_att(root, "Opacity");
	transform_att = fz_xml_att(root, "Transform");
	viewbox_att = fz_xml_att(root, "Viewbox");
	viewport_att = fz_xml_att(root, "Viewport");
	tile_mode_att = fz_xml_att(root, "TileMode");

	c.base_uri = base_uri;
	c.dict = dict;
	c.root = root;
	c.user = user;
	c.func = func;

	for (node = fz_xml_down(root); node; node = fz_xml_next(node))
	{
		if (fz_xml_is_tag(node, "ImageBrush.Transform"))
			transform_tag = fz_xml_down(node);
		if (fz_xml_is_tag(node, "VisualBrush.Transform"))
			transform_tag = fz_xml_down(node);
	}

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

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

	viewbox = fz_unit_rect;
	if (viewbox_att)
		xps_parse_rectangle(ctx, doc, viewbox_att, &viewbox);

	viewport = fz_unit_rect;
	if (viewport_att)
		xps_parse_rectangle(ctx, doc, viewport_att, &viewport);

	if (fabsf(viewport.x1 - viewport.x0) < 0.01f || fabsf(viewport.y1 - viewport.y0) < 0.01f)
		fz_warn(ctx, "not drawing tile for viewport size %.4f x %.4f", viewport.x1 - viewport.x0, viewport.y1 - viewport.y0);
	else if (fabsf(viewbox.x1 - viewbox.x0) < 0.01f || fabsf(viewbox.y1 - viewbox.y0) < 0.01f)
		fz_warn(ctx, "not drawing tile for viewbox size %.4f x %.4f", viewbox.x1 - viewbox.x0, viewbox.y1 - viewbox.y0);

	/* some sanity checks on the viewport/viewbox size */
	if (fabsf(viewport.x1 - viewport.x0) < 0.01f) return;
	if (fabsf(viewport.y1 - viewport.y0) < 0.01f) return;
	if (fabsf(viewbox.x1 - viewbox.x0) < 0.01f) return;
	if (fabsf(viewbox.y1 - viewbox.y0) < 0.01f) return;

	xstep = viewbox.x1 - viewbox.x0;
	ystep = viewbox.y1 - viewbox.y0;

	xscale = (viewport.x1 - viewport.x0) / xstep;
	yscale = (viewport.y1 - viewport.y0) / ystep;

	tile_mode = TILE_NONE;
	if (tile_mode_att)
	{
		if (!strcmp(tile_mode_att, "None"))
			tile_mode = TILE_NONE;
		if (!strcmp(tile_mode_att, "Tile"))
			tile_mode = TILE_TILE;
		if (!strcmp(tile_mode_att, "FlipX"))
			tile_mode = TILE_FLIP_X;
		if (!strcmp(tile_mode_att, "FlipY"))
			tile_mode = TILE_FLIP_Y;
		if (!strcmp(tile_mode_att, "FlipXY"))
			tile_mode = TILE_FLIP_X_Y;
	}

	if (tile_mode == TILE_FLIP_X || tile_mode == TILE_FLIP_X_Y)
		xstep *= 2;
	if (tile_mode == TILE_FLIP_Y || tile_mode == TILE_FLIP_X_Y)
		ystep *= 2;

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

	fz_pre_translate(&transform, viewport.x0, viewport.y0);
	fz_pre_scale(&transform, xscale, yscale);
	fz_pre_translate(&transform, -viewbox.x0, -viewbox.y0);

	if (tile_mode != TILE_NONE)
	{
		int x0, y0, x1, y1;
		fz_matrix invctm;
		fz_rect local_area = *area;
		fz_transform_rect(&local_area, fz_invert_matrix(&invctm, &transform));
		x0 = floorf(local_area.x0 / xstep);
		y0 = floorf(local_area.y0 / ystep);
		x1 = ceilf(local_area.x1 / xstep);
		y1 = ceilf(local_area.y1 / ystep);

#ifdef TILE
		if ((x1 - x0) * (y1 - y0) > 1)
#else
		if (0)
#endif
		{
			fz_rect bigview = viewbox;
			bigview.x1 = bigview.x0 + xstep;
			bigview.y1 = bigview.y0 + ystep;
			fz_begin_tile(ctx, dev, &local_area, &bigview, xstep, ystep, &transform);
			xps_paint_tiling_brush(ctx, doc, &transform, &viewbox, tile_mode, &c);
			fz_end_tile(ctx, dev);
		}
		else
		{
			int x, y;
			for (y = y0; y < y1; y++)
			{
				for (x = x0; x < x1; x++)
				{
					fz_matrix ttm = transform;
					fz_pre_translate(&ttm, xstep * x, ystep * y);
					xps_paint_tiling_brush(ctx, doc, &ttm, &viewbox, tile_mode, &c);
				}
			}
		}
	}
	else
	{
		xps_paint_tiling_brush(ctx, doc, &transform, &viewbox, tile_mode, &c);
	}

	xps_end_opacity(ctx, doc, base_uri, dict, opacity_att, NULL);
}
示例#20
0
文件: xps-doc.c 项目: krytikul/nPDF
static fz_xml *
xps_load_fixed_page(fz_context *ctx, xps_document *doc, xps_fixpage *page)
{
	xps_part *part;
	fz_xml *root;
	char *width_att;
	char *height_att;

	part = xps_read_part(ctx, doc, page->name);
	fz_try(ctx)
	{
		root = fz_parse_xml(ctx, part->data, part->size, 0);
	}
	fz_always(ctx)
	{
		xps_drop_part(ctx, doc, part);
	}
	fz_catch(ctx)
	{
		fz_rethrow_if(ctx, FZ_ERROR_TRYLATER);
		root = NULL;
	}
	if (!root)
		fz_throw(ctx, FZ_ERROR_GENERIC, "FixedPage missing root element");

	if (fz_xml_is_tag(root, "AlternateContent"))
	{
		fz_xml *node = xps_lookup_alternate_content(ctx, doc, root);
		if (!node)
		{
			fz_drop_xml(ctx, root);
			fz_throw(ctx, FZ_ERROR_GENERIC, "FixedPage missing alternate root element");
		}
		fz_detach_xml(node);
		fz_drop_xml(ctx, root);
		root = node;
	}

	if (!fz_xml_is_tag(root, "FixedPage"))
	{
		fz_drop_xml(ctx, root);
		fz_throw(ctx, FZ_ERROR_GENERIC, "expected FixedPage element");
	}

	width_att = fz_xml_att(root, "Width");
	if (!width_att)
	{
		fz_drop_xml(ctx, root);
		fz_throw(ctx, FZ_ERROR_GENERIC, "FixedPage missing required attribute: Width");
	}

	height_att = fz_xml_att(root, "Height");
	if (!height_att)
	{
		fz_drop_xml(ctx, root);
		fz_throw(ctx, FZ_ERROR_GENERIC, "FixedPage missing required attribute: Height");
	}

	page->width = atoi(width_att);
	page->height = atoi(height_att);

	return root;
}
示例#21
0
static void
xps_parse_gradient_brush(fz_context *ctx, xps_document *doc, const fz_matrix *ctm, const fz_rect *area,
	char *base_uri, xps_resource *dict, fz_xml *root,
	void (*draw)(fz_context *ctx, 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 local_ctm;
	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 (fz_xml_is_tag(node, "LinearGradientBrush.Transform"))
			transform_tag = fz_xml_down(node);
		if (fz_xml_is_tag(node, "RadialGradientBrush.Transform"))
			transform_tag = fz_xml_down(node);
		if (fz_xml_is_tag(node, "LinearGradientBrush.GradientStops"))
			stop_tag = fz_xml_down(node);
		if (fz_xml_is_tag(node, "RadialGradientBrush.GradientStops"))
			stop_tag = fz_xml_down(node);
	}

	xps_resolve_resource_reference(ctx, 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;
	}

	xps_parse_transform(ctx, doc, transform_att, transform_tag, &local_ctm, ctm);

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

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

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

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

	xps_end_opacity(ctx, doc, base_uri, dict, opacity_att, NULL);
}
示例#22
0
void
xps_parse_glyphs(fz_context *ctx, xps_document *doc, const fz_matrix *ctm,
		char *base_uri, xps_resource *dict, fz_xml *root)
{
	fz_device *dev = doc->dev;

	fz_xml *node;

	char *fill_uri;
	char *opacity_mask_uri;

	char *bidi_level_att;
	char *fill_att;
	char *font_size_att;
	char *font_uri_att;
	char *origin_x_att;
	char *origin_y_att;
	char *is_sideways_att;
	char *indices_att;
	char *unicode_att;
	char *style_att;
	char *transform_att;
	char *clip_att;
	char *opacity_att;
	char *opacity_mask_att;
	char *navigate_uri_att;

	fz_xml *transform_tag = NULL;
	fz_xml *clip_tag = NULL;
	fz_xml *fill_tag = NULL;
	fz_xml *opacity_mask_tag = NULL;

	char *fill_opacity_att = NULL;

	xps_part *part;
	fz_font *font;

	char partname[1024];
	char fakename[1024];
	char *subfont;

	float font_size = 10;
	int subfontid = 0;
	int is_sideways = 0;
	int bidi_level = 0;

	fz_text *text;
	fz_rect area;

	fz_matrix local_ctm = *ctm;

	/*
	 * Extract attributes and extended attributes.
	 */

	bidi_level_att = fz_xml_att(root, "BidiLevel");
	fill_att = fz_xml_att(root, "Fill");
	font_size_att = fz_xml_att(root, "FontRenderingEmSize");
	font_uri_att = fz_xml_att(root, "FontUri");
	origin_x_att = fz_xml_att(root, "OriginX");
	origin_y_att = fz_xml_att(root, "OriginY");
	is_sideways_att = fz_xml_att(root, "IsSideways");
	indices_att = fz_xml_att(root, "Indices");
	unicode_att = fz_xml_att(root, "UnicodeString");
	style_att = fz_xml_att(root, "StyleSimulations");
	transform_att = fz_xml_att(root, "RenderTransform");
	clip_att = fz_xml_att(root, "Clip");
	opacity_att = fz_xml_att(root, "Opacity");
	opacity_mask_att = fz_xml_att(root, "OpacityMask");
	navigate_uri_att = fz_xml_att(root, "FixedPage.NavigateUri");

	for (node = fz_xml_down(root); node; node = fz_xml_next(node))
	{
		if (fz_xml_is_tag(node, "Glyphs.RenderTransform"))
			transform_tag = fz_xml_down(node);
		if (fz_xml_is_tag(node, "Glyphs.OpacityMask"))
			opacity_mask_tag = fz_xml_down(node);
		if (fz_xml_is_tag(node, "Glyphs.Clip"))
			clip_tag = fz_xml_down(node);
		if (fz_xml_is_tag(node, "Glyphs.Fill"))
			fill_tag = fz_xml_down(node);
	}

	fill_uri = base_uri;
	opacity_mask_uri = base_uri;

	xps_resolve_resource_reference(ctx, doc, dict, &transform_att, &transform_tag, NULL);
	xps_resolve_resource_reference(ctx, doc, dict, &clip_att, &clip_tag, NULL);
	xps_resolve_resource_reference(ctx, doc, dict, &fill_att, &fill_tag, &fill_uri);
	xps_resolve_resource_reference(ctx, doc, dict, &opacity_mask_att, &opacity_mask_tag, &opacity_mask_uri);

	/*
	 * Check that we have all the necessary information.
	 */

	if (!font_size_att || !font_uri_att || !origin_x_att || !origin_y_att) {
		fz_warn(ctx, "missing attributes in glyphs element");
		return;
	}

	if (!indices_att && !unicode_att)
		return; /* nothing to draw */

	if (is_sideways_att)
		is_sideways = !strcmp(is_sideways_att, "true");

	if (bidi_level_att)
		bidi_level = atoi(bidi_level_att);

	/*
	 * Find and load the font resource
	 */

	xps_resolve_url(ctx, doc, partname, base_uri, font_uri_att, sizeof partname);
	subfont = strrchr(partname, '#');
	if (subfont)
	{
		subfontid = atoi(subfont + 1);
		*subfont = 0;
	}

	/* Make a new part name for font with style simulation applied */
	fz_strlcpy(fakename, partname, sizeof fakename);
	if (style_att)
	{
		if (!strcmp(style_att, "BoldSimulation"))
			fz_strlcat(fakename, "#Bold", sizeof fakename);
		else if (!strcmp(style_att, "ItalicSimulation"))
			fz_strlcat(fakename, "#Italic", sizeof fakename);
		else if (!strcmp(style_att, "BoldItalicSimulation"))
			fz_strlcat(fakename, "#BoldItalic", sizeof fakename);
	}

	font = xps_lookup_font(ctx, doc, fakename);
	if (!font)
	{
		fz_buffer *buf = NULL;
		fz_var(buf);

		fz_try(ctx)
		{
			part = xps_read_part(ctx, doc, partname);
		}
		fz_catch(ctx)
		{
			fz_rethrow_if(ctx, FZ_ERROR_TRYLATER);
			fz_warn(ctx, "cannot find font resource part '%s'", partname);
			return;
		}

		/* deobfuscate if necessary */
		if (strstr(part->name, ".odttf"))
			xps_deobfuscate_font_resource(ctx, doc, part);
		if (strstr(part->name, ".ODTTF"))
			xps_deobfuscate_font_resource(ctx, doc, part);

		fz_try(ctx)
		{
			buf = fz_new_buffer_from_data(ctx, part->data, part->size);
			/* part->data is now owned by buf */
			part->data = NULL;
			font = fz_new_font_from_buffer(ctx, NULL, buf, subfontid, 1);
		}
		fz_always(ctx)
		{
			fz_drop_buffer(ctx, buf);
			xps_drop_part(ctx, doc, part);
		}
		fz_catch(ctx)
		{
			fz_rethrow_if(ctx, FZ_ERROR_TRYLATER);
			fz_warn(ctx, "cannot load font resource '%s'", partname);
			return;
		}

		if (style_att)
		{
			font->ft_bold = !!strstr(style_att, "Bold");
			font->ft_italic = !!strstr(style_att, "Italic");
		}

		xps_select_best_font_encoding(ctx, doc, font);
		xps_insert_font(ctx, doc, fakename, font);
	}