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); } }
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 */ } }
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"); }
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); } }
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); } }
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)); }
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) */ }
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; }
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); } }
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; }
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); }
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); } } }
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); } }
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); }
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; }
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 */ } }
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; }
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); }
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); }
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; }
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); }
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); }