static void
xps_parse_path_figure(fz_context *doc, fz_path *path, fz_xml *root, int stroking)
{
	fz_xml *node;

	char *is_closed_att;
	char *start_point_att;
	char *is_filled_att;

	int is_closed = 0;
	int is_filled = 1;
	float start_x = 0;
	float start_y = 0;

	int skipped_stroke = 0;

	is_closed_att = fz_xml_att(root, "IsClosed");
	start_point_att = fz_xml_att(root, "StartPoint");
	is_filled_att = fz_xml_att(root, "IsFilled");

	if (is_closed_att)
		is_closed = !strcmp(is_closed_att, "true");
	if (is_filled_att)
		is_filled = !strcmp(is_filled_att, "true");
	if (start_point_att)
		xps_parse_point(start_point_att, &start_x, &start_y);

	if (!stroking && !is_filled) /* not filled, when filling */
		return;

	fz_moveto(doc, path, start_x, start_y);

	for (node = fz_xml_down(root); node; node = fz_xml_next(node))
	{
		if (!strcmp(fz_xml_tag(node), "ArcSegment"))
			xps_parse_arc_segment(doc, path, node, stroking, &skipped_stroke);
		if (!strcmp(fz_xml_tag(node), "PolyBezierSegment"))
			xps_parse_poly_bezier_segment(doc, path, node, stroking, &skipped_stroke);
		if (!strcmp(fz_xml_tag(node), "PolyLineSegment"))
			xps_parse_poly_line_segment(doc, path, node, stroking, &skipped_stroke);
		if (!strcmp(fz_xml_tag(node), "PolyQuadraticBezierSegment"))
			xps_parse_poly_quadratic_bezier_segment(doc, path, node, stroking, &skipped_stroke);
	}

	if (is_closed)
	{
		if (stroking && skipped_stroke)
			fz_lineto(doc, path, start_x, start_y); /* we've skipped using fz_moveto... */
		else
			fz_closepath(doc, path); /* no skipped segments, safe to closepath properly */
	}
}
Пример #2
0
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 (!strcmp(fz_xml_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");
}
Пример #3
0
void
xps_parse_visual_brush(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 (!strcmp(fz_xml_tag(node), "VisualBrush.Visual"))
			visual_tag = fz_xml_down(node);
	}

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

	if (visual_tag)
	{
		xps_parse_tiling_brush(doc, ctm, area,
			visual_uri, dict, root, xps_paint_visual_brush, visual_tag);
	}
}
Пример #4
0
void
xps_parse_brush(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 (!strcmp(fz_xml_tag(node), "ImageBrush"))
        xps_parse_image_brush(doc, ctm, area, base_uri, dict, node);
    else if (!strcmp(fz_xml_tag(node), "VisualBrush"))
        xps_parse_visual_brush(doc, ctm, area, base_uri, dict, node);
    else if (!strcmp(fz_xml_tag(node), "LinearGradientBrush"))
        xps_parse_linear_gradient_brush(doc, ctm, area, base_uri, dict, node);
    else if (!strcmp(fz_xml_tag(node), "RadialGradientBrush"))
        xps_parse_radial_gradient_brush(doc, ctm, area, base_uri, dict, node);
    else
        fz_warn(doc->ctx, "unknown brush tag: %s", fz_xml_tag(node));
}
Пример #5
0
static void
svg_run_element(fz_context *ctx, fz_device *dev, svg_document *doc, fz_xml *root, const svg_state *state)
{
	char *tag = fz_xml_tag(root);

	if (!strcmp(tag, "svg"))
		svg_run_svg(ctx, dev, doc, root, state);

	else if (!strcmp(tag, "g"))
		svg_run_g(ctx, dev, doc, root, state);

	else if (!strcmp(tag, "title"))
		;
	else if (!strcmp(tag, "desc"))
		;

	else if (!strcmp(tag, "defs"))
		;
	else if (!strcmp(tag, "symbol"))
		;

	else if (!strcmp(tag, "use"))
		svg_run_use(ctx, dev, doc, root, state);

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

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

	else
	{
		/* debug print unrecognized tags */
		fz_debug_xml(root, 0);
	}
}
Пример #6
0
void
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) */
}
Пример #7
0
fz_xml *
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")) && (!*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;
}
Пример #8
0
void
xps_parse_matrix_transform(xps_document *doc, fz_xml *root, fz_matrix *matrix)
{
    char *transform;

    *matrix = fz_identity;

    if (!strcmp(fz_xml_tag(root), "MatrixTransform"))
    {
        transform = fz_xml_att(root, "Matrix");
        if (transform)
            xps_parse_render_transform(doc, transform, matrix);
    }
}
Пример #9
0
void
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);
	}
}
Пример #10
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 *node;
	fz_buffer *buf;
	char path[2048];

	for (node = root; node; node = fz_xml_next(node))
	{
		const char *tag = fz_xml_tag(node);
		if (tag && !strcmp(tag, "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_cleanname(path);

						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_drop_buffer(ctx, buf);
					}
				}
			}
		}
		if (tag && !strcmp(tag, "style"))
		{
			char *s = concat_text(ctx, node);
			css = fz_parse_css(ctx, css, s, "<style>");
			fz_free(ctx, s);
		}
		if (fz_xml_down(node))
			css = html_load_css(ctx, zip, base_uri, css, fz_xml_down(node));
	}
	return css;
}
Пример #11
0
void
xps_begin_opacity(fz_context *ctx, xps_document *doc, const fz_matrix *ctm, const fz_rect *area,
	char *base_uri, xps_resource *dict,
	char *opacity_att, fz_xml *opacity_mask_tag)
{
	fz_device *dev = doc->dev;
	float opacity;

	if (!opacity_att && !opacity_mask_tag)
		return;

	opacity = 1;
	if (opacity_att)
		opacity = fz_atof(opacity_att);

	if (opacity_mask_tag && !strcmp(fz_xml_tag(opacity_mask_tag), "SolidColorBrush"))
	{
		char *scb_opacity_att = fz_xml_att(opacity_mask_tag, "Opacity");
		char *scb_color_att = fz_xml_att(opacity_mask_tag, "Color");
		if (scb_opacity_att)
			opacity = opacity * fz_atof(scb_opacity_att);
		if (scb_color_att)
		{
			fz_colorspace *colorspace;
			float samples[FZ_MAX_COLORS];
			xps_parse_color(ctx, doc, base_uri, scb_color_att, &colorspace, samples);
			opacity = opacity * samples[0];
		}
		opacity_mask_tag = NULL;
	}

	if (doc->opacity_top + 1 < nelem(doc->opacity))
	{
		doc->opacity[doc->opacity_top + 1] = doc->opacity[doc->opacity_top] * opacity;
		doc->opacity_top++;
	}

	if (opacity_mask_tag)
	{
		fz_begin_mask(ctx, dev, area, 0, NULL, NULL);
		xps_parse_brush(ctx, doc, ctm, area, base_uri, dict, opacity_mask_tag);
		fz_end_mask(ctx, dev);
	}
}
Пример #12
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);
	}
}
Пример #13
0
void
xps_parse_fixed_page(xps_document *doc, const fz_matrix *ctm, xps_page *page)
{
	fz_xml *node;
	xps_resource *dict;
	char base_uri[1024];
	fz_rect area;
	char *s;
	fz_matrix scm;

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

	dict = NULL;

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

	if (!page->root)
		return;

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

	for (node = fz_xml_down(page->root); node; node = fz_xml_next(node))
	{
		if (!strcmp(fz_xml_tag(node), "FixedPage.Resources") && fz_xml_down(node))
		{
			if (dict)
				fz_warn(doc->ctx, "ignoring follow-up resource dictionaries");
			else
				dict = xps_parse_resource_dictionary(doc, base_uri, fz_xml_down(node));
		}
		xps_parse_element(doc, ctm, &area, base_uri, dict, node);
	}

	if (dict)
		xps_free_resource_dictionary(doc, dict);
}
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[32];
	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 (!strcmp(fz_xml_tag(node), "Path.RenderTransform"))
			transform_tag = fz_xml_down(node);
		if (!strcmp(fz_xml_tag(node), "Path.OpacityMask"))
			opacity_mask_tag = fz_xml_down(node);
		if (!strcmp(fz_xml_tag(node), "Path.Clip"))
			clip_tag = fz_xml_down(node);
		if (!strcmp(fz_xml_tag(node), "Path.Fill"))
			fill_tag = fz_xml_down(node);
		if (!strcmp(fz_xml_tag(node), "Path.Stroke"))
			stroke_tag = fz_xml_down(node);
		if (!strcmp(fz_xml_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_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++;
			}
			/* cf. https://code.google.com/p/sumatrapdf/issues/detail?id=2339 */
			if (dash_len > 0)
			{
				float phase_len = 0.0f;
				int i;
				for (i = 0; i < dash_len; i++)
					phase_len += stroke->dash_list[i];
				if (phase_len == 0.0f)
					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, NULL, 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, NULL, 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);
}
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 (!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;
}
Пример #16
0
static int
xps_parse_gradient_stops(xps_document *doc, char *base_uri, fz_xml *node,
                         struct stop *stops, int maxcount)
{
    fz_colorspace *colorspace;
    float sample[8];
    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 (!strcmp(fz_xml_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);

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

                fz_convert_color(doc->ctx, fz_device_rgb, 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(doc->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(doc->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;
}
Пример #17
0
static void generate_boxes(fz_context *ctx, fz_html_font_set *set, fz_archive *zip, const char *base_uri,
	fz_xml *node, fz_html *top, fz_css_rule *rule, fz_css_match *up_match)
{
	fz_css_match match;
	fz_html *box;
	const char *tag;
	int display;

	while (node)
	{
		match.up = up_match;
		match.count = 0;

		tag = fz_xml_tag(node);
		if (tag)
		{
			fz_match_css(ctx, &match, rule, node);

			display = fz_get_css_match_display(&match);

			if (!strcmp(tag, "br"))
			{
				box = new_box(ctx);
				fz_apply_css_style(ctx, set, &box->style, &match);
				top = insert_break_box(ctx, box, top);
			}

			else if (!strcmp(tag, "img"))
			{
				const char *src = fz_xml_att(node, "src");
				if (src)
				{
					box = new_box(ctx);
					fz_apply_css_style(ctx, set, &box->style, &match);
					insert_inline_box(ctx, box, top);
					generate_image(ctx, zip, base_uri, box, src);
				}
			}

			else if (display != DIS_NONE)
			{
				box = new_box(ctx);
				fz_apply_css_style(ctx, set, &box->style, &match);

				if (display == DIS_BLOCK)
				{
					top = insert_block_box(ctx, box, top);
				}
				else if (display == DIS_LIST_ITEM)
				{
					top = insert_block_box(ctx, box, top);
				}
				else if (display == DIS_INLINE)
				{
					insert_inline_box(ctx, box, top);
				}
				else
				{
					fz_warn(ctx, "unknown box display type");
					insert_box(ctx, box, BOX_BLOCK, top);
				}

				if (fz_xml_down(node))
					generate_boxes(ctx, set, zip, base_uri, fz_xml_down(node), box, rule, &match);

				// TODO: remove empty flow boxes
			}
		}
		else
		{
			if (top->type != BOX_INLINE)
			{
				box = new_box(ctx);
				insert_inline_box(ctx, box, top);
				box->style = top->style;
				generate_text(ctx, box, fz_xml_text(node));
			}
			else
			{
				generate_text(ctx, top, fz_xml_text(node));
			}
		}

		node = fz_xml_next(node);
	}
}
Пример #18
0
static int
match_selector(fz_css_selector *sel, fz_xml *node)
{
	if (!node)
		return 0;

	if (sel->combine)
	{
		/* descendant */
		if (sel->combine == ' ')
		{
			fz_xml *parent = fz_xml_up(node);
			while (parent)
			{
				if (match_selector(sel->left, parent))
					if (match_selector(sel->right, node))
						return 1;
				parent = fz_xml_up(parent);
			}
			return 0;
		}

		/* child */
		if (sel->combine == '>')
		{
			fz_xml *parent = fz_xml_up(node);
			if (!parent)
				return 0;
			if (!match_selector(sel->left, parent))
				return 0;
			if (!match_selector(sel->right, node))
				return 0;
		}

		/* adjacent */
		if (sel->combine == '+')
		{
			fz_xml *prev = fz_xml_prev(node);
			while (prev && !fz_xml_tag(prev))
				prev = fz_xml_prev(prev);
			if (!prev)
				return 0;
			if (!fz_xml_tag(prev))
				return 0;
			if (!match_selector(sel->left, prev))
				return 0;
			if (!match_selector(sel->right, node))
				return 0;
		}
	}

	if (sel->name)
	{
		if (strcmp(sel->name, fz_xml_tag(node)))
			return 0;
	}

	if (sel->cond)
	{
		if (!match_condition(sel->cond, node))
			return 0;
	}

	return 1;
}
Пример #19
0
void
xps_parse_canvas(xps_document *doc, const fz_matrix *ctm, const fz_rect *area, char *base_uri, xps_resource *dict, fz_xml *root)
{
	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 (!strcmp(fz_xml_tag(node), "Canvas.Resources") && fz_xml_down(node))
		{
			if (new_dict)
			{
				fz_warn(doc->ctx, "ignoring follow-up resource dictionaries");
			}
			else
			{
				new_dict = xps_parse_resource_dictionary(doc, base_uri, fz_xml_down(node));
				if (new_dict)
				{
					new_dict->parent = dict;
					dict = new_dict;
				}
			}
		}

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

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

	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 (navigate_uri_att)
		xps_add_link(doc, area, base_uri, navigate_uri_att);

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

	xps_begin_opacity(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(doc, &transform, area, base_uri, dict, node);
	}

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

	if (clip_att || clip_tag)
		fz_pop_clip(doc->dev);

	if (new_dict)
		xps_free_resource_dictionary(doc, new_dict);
}
Пример #20
0
static void
xps_parse_gradient_brush(xps_document *doc, fz_matrix ctm, fz_rect area,
                         char *base_uri, xps_resource *dict, fz_xml *root,
                         void (*draw)(xps_document *, fz_matrix, fz_rect, struct stop *, int, fz_xml *, int))
{
    fz_xml *node;

    char *opacity_att;
    char *interpolation_att;
    char *spread_att;
    char *mapping_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");
    interpolation_att = fz_xml_att(root, "ColorInterpolationMode");
    spread_att = fz_xml_att(root, "SpreadMethod");
    mapping_att = fz_xml_att(root, "MappingMode");
    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);
    ctm = fz_concat(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, ctm, area, base_uri, dict, opacity_att, NULL);

    draw(doc, ctm, area, stop_list, stop_count, root, spread_method);

    xps_end_opacity(doc, base_uri, dict, opacity_att, NULL);
}
Пример #21
0
static void generate_boxes(fz_context *ctx, fz_pool *pool,
                           fz_html_font_set *set, fz_archive *zip, const char *base_uri,
                           fz_xml *node, fz_html *top, fz_css_rule *rule, fz_css_match *up_match, int list_counter)
{
    fz_css_match match;
    fz_html *box;
    const char *tag;
    int display;

    while (node)
    {
        match.up = up_match;
        match.count = 0;

        tag = fz_xml_tag(node);
        if (tag)
        {
            fz_match_css(ctx, &match, rule, node);

            display = fz_get_css_match_display(&match);

            if (!strcmp(tag, "br"))
            {
                if (top->type == BOX_INLINE)
                {
                    fz_html *flow = top;
                    while (flow->type != BOX_FLOW)
                        flow = flow->up;
                    add_flow_break(ctx, pool, flow, &top->style);
                }
                else
                {
                    box = new_box(ctx, pool);
                    fz_apply_css_style(ctx, set, &box->style, &match);
                    top = insert_break_box(ctx, box, top);
                }
            }

            else if (!strcmp(tag, "img"))
            {
                const char *src = fz_xml_att(node, "src");
                if (src)
                {
                    box = new_box(ctx, pool);
                    fz_apply_css_style(ctx, set, &box->style, &match);
                    insert_inline_box(ctx, pool, box, top);
                    generate_image(ctx, pool, zip, base_uri, box, src);
                }
            }

            else if (display != DIS_NONE)
            {
                box = new_box(ctx, pool);
                fz_apply_css_style(ctx, set, &box->style, &match);

                if (display == DIS_BLOCK || display == DIS_INLINE_BLOCK)
                {
                    top = insert_block_box(ctx, box, top);
                }
                else if (display == DIS_LIST_ITEM)
                {
                    top = insert_block_box(ctx, box, top);
                    box->list_item = ++list_counter;
                }
                else if (display == DIS_INLINE)
                {
                    insert_inline_box(ctx, pool, box, top);
                }
                else
                {
                    fz_warn(ctx, "unknown box display type");
                    insert_box(ctx, box, BOX_BLOCK, top);
                }

                if (fz_xml_down(node))
                {
                    int child_counter = list_counter;
                    if (!strcmp(tag, "ul") || !strcmp(tag, "ol"))
                        child_counter = 0;
                    generate_boxes(ctx, pool, set, zip, base_uri, fz_xml_down(node), box, rule, &match, child_counter);
                }

                // TODO: remove empty flow boxes
            }
        }
        else
        {
            if (top->type != BOX_INLINE)
            {
                /* Create anonymous inline box, with the same style as the top block box. */
                box = new_box(ctx, pool);
                insert_inline_box(ctx, pool, box, top);
                box->style = top->style;
                /* Make sure not to recursively multiply font sizes. */
                box->style.font_size.value = 1;
                box->style.font_size.unit = N_SCALE;
                generate_text(ctx, pool, box, fz_xml_text(node));
            }
            else
            {
                generate_text(ctx, pool, top, fz_xml_text(node));
            }
        }

        node = fz_xml_next(node);
    }
}
Пример #22
0
void
xps_parse_tiling_brush(xps_document *doc, const fz_matrix *ctm, const fz_rect *area,
	char *base_uri, xps_resource *dict, fz_xml *root,
	void (*func)(xps_document*, const fz_matrix*, const fz_rect*, char*, xps_resource*, fz_xml*, void*), void *user)
{
	fz_xml *node;
	struct closure c;

	char *opacity_att;
	char *transform_att;
	char *viewbox_att;
	char *viewport_att;
	char *tile_mode_att;
	char *viewbox_units_att;
	char *viewport_units_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");
	viewbox_units_att = fz_xml_att(root, "ViewboxUnits");
	viewport_units_att = fz_xml_att(root, "ViewportUnits");

	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 (!strcmp(fz_xml_tag(node), "ImageBrush.Transform"))
			transform_tag = fz_xml_down(node);
		if (!strcmp(fz_xml_tag(node), "VisualBrush.Transform"))
			transform_tag = fz_xml_down(node);
	}

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

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

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

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

	if (fabsf(viewport.x1 - viewport.x0) < 0.01f || fabsf(viewport.y1 - viewport.y0) < 0.01f)
		fz_warn(doc->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(doc->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(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;
		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(doc->dev, &local_area, &bigview, xstep, ystep, &transform);
			xps_paint_tiling_brush(doc, &transform, &viewbox, tile_mode, &c);
			fz_end_tile(doc->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(doc, &ttm, &viewbox, tile_mode, &c);
				}
			}
		}
	}
	else
	{
		xps_paint_tiling_brush(doc, &transform, &viewbox, tile_mode, &c);
	}

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