static int xps_gradient_brush_has_transparency(xps_context_t *ctx, char *base_uri, xps_item_t *root) { xps_item_t *node; char *opacity_att; opacity_att = xps_att(root, "Opacity"); if (opacity_att) { if (atof(opacity_att) < 1.0) { //dmputs(ctx->memory, "page has transparency: GradientBrush Opacity\n"); return 1; } } for (node = xps_down(root); node; node = xps_next(node)) { if (!strcmp(xps_tag(node), "RadialGradientBrush.GradientStops")) { if (xps_gradient_stops_have_transparency(ctx, base_uri, node)) return 1; } if (!strcmp(xps_tag(node), "LinearGradientBrush.GradientStops")) { if (xps_gradient_stops_have_transparency(ctx, base_uri, node)) return 1; } } return 0; }
static int xps_canvas_has_transparency(xps_context_t *ctx, char *base_uri, xps_item_t *root) { xps_item_t *node; for (node = xps_down(root); node; node = xps_next(node)) { if (!strcmp(xps_tag(node), "Canvas.Resources")) { if (xps_down(node) && xps_resource_dictionary_has_transparency(ctx, base_uri, xps_down(node))) return 1; } if (!strcmp(xps_tag(node), "Canvas.OpacityMask")) { //dmputs(ctx->memory, "page has transparency: Canvas.OpacityMask\n"); return 1; } if (xps_element_has_transparency(ctx, base_uri, node)) return 1; } return 0; }
static int xps_gradient_stops_have_transparency(xps_context_t *ctx, char *base_uri, xps_item_t *root) { xps_item_t *node; gs_color_space *colorspace; char *color_att; float samples[32]; for (node = xps_down(root); node; node = xps_next(node)) { if (!strcmp(xps_tag(node), "GradientStop")) { color_att = xps_att(node, "Color"); if (color_att) { xps_parse_color(ctx, base_uri, color_att, &colorspace, samples); if (samples[0] < 1.0) { //dmputs(ctx->memory, "page has transparency: GradientStop has alpha\n"); return 1; } } } } return 0; }
static int xps_path_has_transparency(xps_context_t *ctx, char *base_uri, xps_item_t *root) { xps_item_t *node; for (node = xps_down(root); node; node = xps_next(node)) { if (!strcmp(xps_tag(node), "Path.OpacityMask")) { //dmputs(ctx->memory, "page has transparency: Path.OpacityMask\n"); return 1; } if (!strcmp(xps_tag(node), "Path.Stroke")) { if (xps_brush_has_transparency(ctx, base_uri, xps_down(node))) return 1; } if (!strcmp(xps_tag(node), "Path.Fill")) { if (xps_brush_has_transparency(ctx, base_uri, xps_down(node))) return 1; } } return 0; }
int xps_resource_dictionary_has_transparency(xps_context_t *ctx, char *base_uri, xps_item_t *root) { char *source; xps_item_t *node; source = xps_att(root, "Source"); if (source) return xps_remote_resource_dictionary_has_transparency(ctx, base_uri, source); for (node = xps_down(root); node; node = xps_next(node)) { if (!strcmp(xps_tag(node), "RadialGradientBrush") || !strcmp(xps_tag(node), "LinearGradientBrush") || !strcmp(xps_tag(node), "SolidColorBrush") || !strcmp(xps_tag(node), "VisualBrush") || !strcmp(xps_tag(node), "ImageBrush")) if (xps_brush_has_transparency(ctx, base_uri, node)) return 1; } return 0; }
int xps_parse_tiling_brush(xps_context_t *ctx, char *base_uri, xps_resource_t *dict, xps_item_t *root, int (*func)(xps_context_t*, char*, xps_resource_t*, xps_item_t*, void*), void *user) { xps_item_t *node; int code; char *opacity_att; char *transform_att; char *viewbox_att; char *viewport_att; char *tile_mode_att; /*char *viewbox_units_att;*/ /*char *viewport_units_att;*/ xps_item_t *transform_tag = NULL; gs_matrix transform; gs_rect viewbox; gs_rect viewport; float scalex, scaley; int tile_mode; opacity_att = xps_att(root, "Opacity"); transform_att = xps_att(root, "Transform"); viewbox_att = xps_att(root, "Viewbox"); viewport_att = xps_att(root, "Viewport"); tile_mode_att = xps_att(root, "TileMode"); /*viewbox_units_att = xps_att(root, "ViewboxUnits");*/ /*viewport_units_att = xps_att(root, "ViewportUnits");*/ for (node = xps_down(root); node; node = xps_next(node)) { if (!strcmp(xps_tag(node), "ImageBrush.Transform")) transform_tag = xps_down(node); if (!strcmp(xps_tag(node), "VisualBrush.Transform")) transform_tag = xps_down(node); } xps_resolve_resource_reference(ctx, dict, &transform_att, &transform_tag, NULL); gs_make_identity(&transform); if (transform_att) xps_parse_render_transform(ctx, transform_att, &transform); if (transform_tag) xps_parse_matrix_transform(ctx, transform_tag, &transform); viewbox.p.x = 0.0; viewbox.p.y = 0.0; viewbox.q.x = 1.0; viewbox.q.y = 1.0; if (viewbox_att) xps_parse_rectangle(ctx, viewbox_att, &viewbox); viewport.p.x = 0.0; viewport.p.y = 0.0; viewport.q.x = 1.0; viewport.q.y = 1.0; if (viewport_att) xps_parse_rectangle(ctx, viewport_att, &viewport); /* some sanity checks on the viewport/viewbox size */ if (fabs(viewport.q.x - viewport.p.x) < 0.01) { gs_warn("skipping tile with zero width view port"); return 0; } if (fabs(viewport.q.y - viewport.p.y) < 0.01) { gs_warn("skipping tile with zero height view port"); return 0; } if (fabs(viewbox.q.x - viewbox.p.x) < 0.01) { gs_warn("skipping tile with zero width view box"); return 0; } if (fabs(viewbox.q.y - viewbox.p.y) < 0.01) { gs_warn("skipping tile with zero height view box"); return 0; } scalex = (viewport.q.x - viewport.p.x) / (viewbox.q.x - viewbox.p.x); scaley = (viewport.q.y - viewport.p.y) / (viewbox.q.y - viewbox.p.y); 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; } gs_gsave(ctx->pgs); code = xps_begin_opacity(ctx, base_uri, dict, opacity_att, NULL, false, false); if (code) { gs_grestore(ctx->pgs); return gs_rethrow(code, "cannot create transparency group"); } /* TODO(tor): check viewport and tiling to see if we can set it to TILE_NONE */ if (tile_mode != TILE_NONE) { struct tile_closure_s closure; gs_client_pattern gspat; gs_client_color gscolor; gs_color_space *cs; bool sa; closure.ctx = ctx; closure.base_uri = base_uri; closure.dict = dict; closure.tag = root; closure.tile_mode = tile_mode; closure.user = user; closure.func = func; closure.viewbox.p.x = viewbox.p.x; closure.viewbox.p.y = viewbox.p.y; closure.viewbox.q.x = viewbox.q.x; closure.viewbox.q.y = viewbox.q.y; gs_pattern1_init(&gspat); uid_set_UniqueID(&gspat.uid, gs_next_ids(ctx->memory, 1)); gspat.PaintType = 1; gspat.TilingType = 2; gspat.PaintProc = xps_remap_pattern; gspat.client_data = &closure; /* We need to know if this tiling brush includes transparency. We could do a proper scan, but for now we'll be lazy and just look at the flag from scanning the page. */ gspat.uses_transparency = ctx->has_transparency; gspat.XStep = viewbox.q.x - viewbox.p.x; gspat.YStep = viewbox.q.y - viewbox.p.y; gspat.BBox.p.x = viewbox.p.x; gspat.BBox.p.y = viewbox.p.y; gspat.BBox.q.x = viewbox.q.x; gspat.BBox.q.y = viewbox.q.y; if (tile_mode == TILE_FLIP_X || tile_mode == TILE_FLIP_X_Y) { gspat.BBox.q.x += gspat.XStep; gspat.XStep *= 2; } if (tile_mode == TILE_FLIP_Y || tile_mode == TILE_FLIP_X_Y) { gspat.BBox.q.y += gspat.YStep; gspat.YStep *= 2; } gs_matrix_translate(&transform, viewport.p.x, viewport.p.y, &transform); gs_matrix_scale(&transform, scalex, scaley, &transform); gs_matrix_translate(&transform, -viewbox.p.x, -viewbox.p.y, &transform); cs = ctx->srgb; gs_setcolorspace(ctx->pgs, cs); gsicc_profile_reference(cs->cmm_icc_profile_data, 1); sa = gs_currentstrokeadjust(ctx->pgs); gs_setstrokeadjust(ctx->pgs, false); gs_makepattern(&gscolor, &gspat, &transform, ctx->pgs, NULL); gs_setpattern(ctx->pgs, &gscolor); xps_fill(ctx); gs_setstrokeadjust(ctx->pgs, sa); gsicc_profile_reference(cs->cmm_icc_profile_data, -1); /* gs_makepattern increments the pattern count stored in the color * structure. We will discard the color struct (its on the stack) * so we need to decrement the reference before we throw away * the structure. */ gs_pattern_reference(&gscolor, -1); } else { xps_clip(ctx); gs_concat(ctx->pgs, &transform); gs_translate(ctx->pgs, viewport.p.x, viewport.p.y); gs_scale(ctx->pgs, scalex, scaley); gs_translate(ctx->pgs, -viewbox.p.x, -viewbox.p.y); gs_moveto(ctx->pgs, viewbox.p.x, viewbox.p.y); gs_lineto(ctx->pgs, viewbox.p.x, viewbox.q.y); gs_lineto(ctx->pgs, viewbox.q.x, viewbox.q.y); gs_lineto(ctx->pgs, viewbox.q.x, viewbox.p.y); gs_closepath(ctx->pgs); gs_clip(ctx->pgs); gs_newpath(ctx->pgs); code = func(ctx, base_uri, dict, root, user); if (code < 0) { xps_end_opacity(ctx, base_uri, dict, opacity_att, NULL); gs_grestore(ctx->pgs); return gs_rethrow(code, "cannot draw tile"); } } xps_end_opacity(ctx, base_uri, dict, opacity_att, NULL); gs_grestore(ctx->pgs); return 0; }
static int xps_brush_has_transparency(xps_context_t *ctx, char *base_uri, xps_item_t *root) { char *opacity_att; char *color_att; xps_item_t *node; gs_color_space *colorspace; float samples[32]; if (!strcmp(xps_tag(root), "SolidColorBrush")) { opacity_att = xps_att(root, "Opacity"); if (opacity_att) { float opacity = atof(opacity_att); if (opacity < 1.0 && opacity != 0.0) { //dmputs(ctx->memory, "page has transparency: SolidColorBrush Opacity\n"); return 1; } } color_att = xps_att(root, "Color"); if (color_att) { xps_parse_color(ctx, base_uri, color_att, &colorspace, samples); if (samples[0] < 1.0 && samples[0] != 0.0) { //dmputs(ctx->memory, "page has transparency: SolidColorBrush Color has alpha\n"); return 1; } } } if (!strcmp(xps_tag(root), "VisualBrush")) { char *opacity_att = xps_att(root, "Opacity"); if (opacity_att) { if (atof(opacity_att) < 1.0) { //dmputs(ctx->memory, "page has transparency: VisualBrush Opacity\n"); return 1; } } for (node = xps_down(root); node; node = xps_next(node)) { if (!strcmp(xps_tag(node), "VisualBrush.Visual")) { if (xps_element_has_transparency(ctx, base_uri, xps_down(node))) return 1; } } } if (!strcmp(xps_tag(root), "ImageBrush")) { if (xps_image_brush_has_transparency(ctx, base_uri, root)) return 1; } if (!strcmp(xps_tag(root), "LinearGradientBrush")) { if (xps_gradient_brush_has_transparency(ctx, base_uri, root)) return 1; } if (!strcmp(xps_tag(root), "RadialGradientBrush")) { if (xps_gradient_brush_has_transparency(ctx, base_uri, root)) return 1; } return 0; }
int xps_parse_canvas(xps_context_t *ctx, char *base_uri, xps_resource_t *dict, xps_item_t *root) { xps_resource_t *new_dict = NULL; xps_item_t *node; char *opacity_mask_uri; int code; char *transform_att; char *clip_att; char *opacity_att; char *opacity_mask_att; xps_item_t *transform_tag = NULL; xps_item_t *clip_tag = NULL; xps_item_t *opacity_mask_tag = NULL; gs_matrix transform; transform_att = xps_att(root, "RenderTransform"); clip_att = xps_att(root, "Clip"); opacity_att = xps_att(root, "Opacity"); opacity_mask_att = xps_att(root, "OpacityMask"); for (node = xps_down(root); node; node = xps_next(node)) { if (!strcmp(xps_tag(node), "Canvas.Resources") && xps_down(node)) { code = xps_parse_resource_dictionary(ctx, &new_dict, base_uri, xps_down(node)); if (code) return gs_rethrow(code, "cannot load Canvas.Resources"); if (new_dict && new_dict != dict) { new_dict->parent = dict; dict = new_dict; } } if (!strcmp(xps_tag(node), "Canvas.RenderTransform")) transform_tag = xps_down(node); if (!strcmp(xps_tag(node), "Canvas.Clip")) clip_tag = xps_down(node); if (!strcmp(xps_tag(node), "Canvas.OpacityMask")) opacity_mask_tag = xps_down(node); } opacity_mask_uri = base_uri; xps_resolve_resource_reference(ctx, dict, &transform_att, &transform_tag, NULL); xps_resolve_resource_reference(ctx, dict, &clip_att, &clip_tag, NULL); xps_resolve_resource_reference(ctx, dict, &opacity_mask_att, &opacity_mask_tag, &opacity_mask_uri); gs_gsave(ctx->pgs); gs_make_identity(&transform); if (transform_att) xps_parse_render_transform(ctx, transform_att, &transform); if (transform_tag) xps_parse_matrix_transform(ctx, transform_tag, &transform); gs_concat(ctx->pgs, &transform); if (clip_att || clip_tag) { if (clip_att) xps_parse_abbreviated_geometry(ctx, clip_att); if (clip_tag) xps_parse_path_geometry(ctx, dict, clip_tag, 0); xps_clip(ctx); } code = xps_begin_opacity(ctx, opacity_mask_uri, dict, opacity_att, opacity_mask_tag, false, false); if (code) { gs_grestore(ctx->pgs); return gs_rethrow(code, "cannot create transparency group"); } for (node = xps_down(root); node; node = xps_next(node)) { code = xps_parse_element(ctx, base_uri, dict, node); if (code) { xps_end_opacity(ctx, opacity_mask_uri, dict, opacity_att, opacity_mask_tag); gs_grestore(ctx->pgs); return gs_rethrow(code, "cannot parse child of Canvas"); } } xps_end_opacity(ctx, opacity_mask_uri, dict, opacity_att, opacity_mask_tag); gs_grestore(ctx->pgs); if (new_dict) xps_free_resource_dictionary(ctx, new_dict); return 0; }
int xps_parse_fixed_page(xps_context_t *ctx, xps_part_t *part) { xps_item_t *root, *node; xps_resource_t *dict; char *width_att; char *height_att; char base_uri[1024]; char *s; int code; if_debug1m('|', ctx->memory, "doc: parsing page %s\n", part->name); xps_strlcpy(base_uri, part->name, sizeof base_uri); s = strrchr(base_uri, '/'); if (s) s[1] = 0; root = xps_parse_xml(ctx, part->data, part->size); if (!root) return gs_rethrow(-1, "cannot parse xml"); if (strcmp(xps_tag(root), "FixedPage")) return gs_throw1(-1, "expected FixedPage element (found %s)", xps_tag(root)); width_att = xps_att(root, "Width"); height_att = xps_att(root, "Height"); if (!width_att) return gs_throw(-1, "FixedPage missing required attribute: Width"); if (!height_att) return gs_throw(-1, "FixedPage missing required attribute: Height"); dict = NULL; /* Setup new page */ { gs_memory_t *mem = ctx->memory; gs_state *pgs = ctx->pgs; gx_device *dev = gs_currentdevice(pgs); gs_param_float_array fa; float fv[2]; gs_c_param_list list; gs_c_param_list_write(&list, mem); fv[0] = atoi(width_att) / 96.0 * 72.0; fv[1] = atoi(height_att) / 96.0 * 72.0; fa.persistent = false; fa.data = fv; fa.size = 2; code = param_write_float_array((gs_param_list *)&list, ".MediaSize", &fa); if ( code >= 0 ) { gs_c_param_list_read(&list); code = gs_putdeviceparams(dev, (gs_param_list *)&list); } gs_c_param_list_release(&list); /* nb this is for the demo it is wrong and should be removed */ gs_initgraphics(pgs); /* 96 dpi default - and put the origin at the top of the page */ gs_initmatrix(pgs); code = gs_scale(pgs, 72.0/96.0, -72.0/96.0); if (code < 0) return gs_rethrow(code, "cannot set page transform"); code = gs_translate(pgs, 0.0, -atoi(height_att)); if (code < 0) return gs_rethrow(code, "cannot set page transform"); code = gs_erasepage(pgs); if (code < 0) return gs_rethrow(code, "cannot clear page"); } /* Pre-parse looking for transparency */ ctx->has_transparency = 0; for (node = xps_down(root); node; node = xps_next(node)) { if (!strcmp(xps_tag(node), "FixedPage.Resources") && xps_down(node)) if (xps_resource_dictionary_has_transparency(ctx, base_uri, xps_down(node))) ctx->has_transparency = 1; if (xps_element_has_transparency(ctx, base_uri, node)) ctx->has_transparency = 1; } /* save the state with the original device before we push */ gs_gsave(ctx->pgs); if (ctx->use_transparency && ctx->has_transparency) { code = gs_push_pdf14trans_device(ctx->pgs, false); if (code < 0) { gs_grestore(ctx->pgs); return gs_rethrow(code, "cannot install transparency device"); } } /* Draw contents */ for (node = xps_down(root); node; node = xps_next(node)) { if (!strcmp(xps_tag(node), "FixedPage.Resources") && xps_down(node)) { code = xps_parse_resource_dictionary(ctx, &dict, base_uri, xps_down(node)); if (code) { gs_pop_pdf14trans_device(ctx->pgs, false); gs_grestore(ctx->pgs); return gs_rethrow(code, "cannot load FixedPage.Resources"); } } code = xps_parse_element(ctx, base_uri, dict, node); if (code) { gs_pop_pdf14trans_device(ctx->pgs, false); gs_grestore(ctx->pgs); return gs_rethrow(code, "cannot parse child of FixedPage"); } } if (ctx->use_transparency && ctx->has_transparency) { code = gs_pop_pdf14trans_device(ctx->pgs, false); if (code < 0) { gs_grestore(ctx->pgs); return gs_rethrow(code, "cannot uninstall transparency device"); } } /* Flush page */ { code = xps_show_page(ctx, 1, true); /* copies, flush */ if (code < 0) { gs_grestore(ctx->pgs); return gs_rethrow(code, "cannot flush page"); } } /* restore the original device, discarding the pdf14 compositor */ gs_grestore(ctx->pgs); if (dict) { xps_free_resource_dictionary(ctx, dict); } xps_free_item(ctx, root); return 0; }