static pdf_annot * pdf_create_highlight_annot(pdf_xref *xref, fz_obj *obj) { fz_context *ctx = xref->ctx; fz_buffer *content = fz_new_buffer(ctx, 512); fz_rect rect = pdf_to_rect(ctx, fz_dict_gets(ctx, obj, "Rect")); fz_obj *quad_points = fz_dict_gets(ctx, obj, "QuadPoints"); fz_obj *resources = pdf_dict_from_string(xref, ANNOT_HIGHLIGHT_AP_RESOURCES); fz_rect a, b; float rgb[3]; float skew; int i; for (i = 0; i < fz_array_len(ctx, quad_points) / 8; i++) { pdf_get_quadrilaterals(ctx, quad_points, i, &a, &b); skew = 0.15 * fabs(a.y0 - b.y0); b.x0 -= skew; b.x1 += skew; rect = fz_union_rect(rect, fz_union_rect(a, b)); } pdf_get_annot_color(ctx, obj, rgb); fz_buffer_printf(ctx, content, "q /GS gs %.4f %.4f %.4f rg 1 0 0 1 -%.4f -%.4f cm ", rgb[0], rgb[1], rgb[2], rect.x0, rect.y0); for (i = 0; i < fz_array_len(ctx, quad_points) / 8; i++) { pdf_get_quadrilaterals(ctx, quad_points, i, &a, &b); skew = 0.15 * fabs(a.y0 - b.y0); fz_buffer_printf(ctx, content, "%.4f %.4f m %.4f %.4f l %.4f %.4f l %.4f %.4f l h ", a.x0, a.y0, b.x1 + skew, b.y1, a.x1, a.y1, b.x0 - skew, b.y0); } fz_buffer_printf(ctx, content, "f Q"); return pdf_create_annot(ctx, rect, fz_keep_obj(obj), content, resources, 1); }
static void fz_bbox_fill_shade(fz_device *dev, fz_shade *shade, const fz_matrix *ctm, float alpha) { fz_rect *result = dev->user; fz_rect r; fz_union_rect(result, fz_bound_shade(dev->ctx, shade, ctm, &r)); }
static void fz_bbox_fill_image(fz_device *dev, fz_image *image, const fz_matrix *ctm, float alpha) { fz_rect *result = dev->user; fz_rect r = fz_unit_rect; fz_union_rect(result, fz_transform_rect(&r, ctm)); }
static void push_clip_stack_accumulate(fz_device *dev, const fz_rect *rect, int accumulate) { if (accumulate <= 1) { dev->scissor_accumulator = *rect; if (dev->container_len == dev->container_cap) { int newmax = dev->container_cap * 2; if (newmax == 0) newmax = 4; dev->container = fz_resize_array(dev->ctx, dev->container, newmax, sizeof(*dev->container)); dev->container_cap = newmax; } if (dev->container_len > 0) dev->container[dev->container_len].scissor = dev->container[dev->container_len-1].scissor; else dev->container[dev->container_len].scissor = fz_infinite_rect; fz_intersect_rect(&dev->container[dev->container_len].scissor, rect); dev->container[dev->container_len].flags = fz_device_container_stack_is_clip_text; dev->container[dev->container_len].user = 0; dev->container_len++; } else { if (dev->container_len <= 0) return; fz_union_rect(&dev->scissor_accumulator, rect); fz_intersect_rect(&dev->container[dev->container_len-1].scissor, &dev->scissor_accumulator); } }
static void fz_bbox_stroke_text(fz_device *dev, fz_text *text, fz_stroke_state *stroke, const fz_matrix *ctm, fz_colorspace *colorspace, float *color, float alpha) { fz_rect *result = dev->user; fz_rect r; fz_union_rect(result, fz_bound_text(dev->ctx, text, ctm, &r)); }
static void fz_bbox_fill_path(fz_device *dev, fz_path *path, int even_odd, const fz_matrix *ctm, fz_colorspace *colorspace, float *color, float alpha) { fz_rect *result = dev->user; fz_rect r; fz_union_rect(result, fz_bound_path(dev->ctx, path, NULL, ctm, &r)); }
static void union_quad(fz_rect *rect, const fz_point quad[4], float lw) { fz_rect qbox; qbox.x0 = fz_min(fz_min(quad[0].x, quad[1].x), fz_min(quad[2].x, quad[3].x)); qbox.y0 = fz_min(fz_min(quad[0].y, quad[1].y), fz_min(quad[2].y, quad[3].y)); qbox.x1 = fz_max(fz_max(quad[0].x, quad[1].x), fz_max(quad[2].x, quad[3].x)); qbox.y1 = fz_max(fz_max(quad[0].y, quad[1].y), fz_max(quad[2].y, quad[3].y)); *rect = fz_union_rect(*rect, fz_expand_rect(qbox, lw)); }
static void append_line(fz_context *ctx, fz_text_block *block, fz_text_line *line) { if (block->len == block->cap) { int new_cap = fz_maxi(16, block->cap * 2); block->lines = fz_resize_array(ctx, block->lines, new_cap, sizeof *block->lines); block->cap = new_cap; } fz_union_rect(&block->bbox, &line->bbox); block->lines[block->len++] = *line; }
static void fz_bbox_add_rect(fz_device *dev, const fz_rect *rect, int clip) { fz_bbox_data *data = dev->user; fz_rect r = *rect; if (0 < data->top && data->top <= STACK_SIZE) fz_intersect_rect(&r, &data->stack[data->top-1]); if (!clip && data->top <= STACK_SIZE && !data->ignore) fz_union_rect(data->result, &r); if (clip && ++data->top <= STACK_SIZE) data->stack[data->top-1] = r; }
int fz_highlight_selection(fz_context *ctx, fz_stext_page *page, fz_rect rect, fz_rect *hit_bbox, int hit_max) { fz_rect linebox, charbox; fz_stext_block *block; fz_stext_line *line; fz_stext_span *span; int i, block_num, hit_count; float x0 = rect.x0; float x1 = rect.x1; float y0 = rect.y0; float y1 = rect.y1; hit_count = 0; for (block_num = 0; block_num < page->len; block_num++) { if (page->blocks[block_num].type != FZ_PAGE_BLOCK_TEXT) continue; block = page->blocks[block_num].u.text; for (line = block->lines; line < block->lines + block->len; line++) { linebox = fz_empty_rect; for (span = line->first_span; span; span = span->next) { for (i = 0; i < span->len; i++) { fz_stext_char_bbox(ctx, &charbox, span, i); if (charbox.x1 >= x0 && charbox.x0 <= x1 && charbox.y1 >= y0 && charbox.y0 <= y1) { if (charbox.y0 != linebox.y0 || fz_abs(charbox.x0 - linebox.x1) > 5) { if (!fz_is_empty_rect(&linebox) && hit_count < hit_max) hit_bbox[hit_count++] = linebox; linebox = charbox; } else { fz_union_rect(&linebox, &charbox); } } } } if (!fz_is_empty_rect(&linebox) && hit_count < hit_max) hit_bbox[hit_count++] = linebox; } } return hit_count; }
static void append_span(fz_context *ctx, fz_text_line *line, fz_text_span *span) { if (span->len == 0) return; if (line->len == line->cap) { int new_cap = fz_maxi(8, line->cap * 2); line->spans = fz_resize_array(ctx, line->spans, new_cap, sizeof(*line->spans)); line->cap = new_cap; } fz_union_rect(&line->bbox, &span->bbox); line->spans[line->len++] = *span; }
static void append_char(fz_context *ctx, fz_text_span *span, int c, fz_rect bbox) { if (span->len == span->cap) { int new_cap = fz_maxi(64, span->cap * 2); span->text = fz_resize_array(ctx, span->text, new_cap, sizeof(*span->text)); span->cap = new_cap; } fz_union_rect(&span->bbox, &bbox); span->text[span->len].c = c; span->text[span->len].bbox = bbox; span->len++; }
int fz_highlight_selection(fz_context *ctx, fz_text_page *page, fz_rect rect, fz_rect *hit_bbox, int hit_max) { fz_rect linebox, charbox; fz_text_block *block; fz_text_line *line; int i, hit_count; float x0 = rect.x0; float x1 = rect.x1; float y0 = rect.y0; float y1 = rect.y1; hit_count = 0; for (block = page->blocks; block < page->blocks + page->len; block++) { for (line = block->lines; line < block->lines + block->len; line++) { int span_num; linebox = fz_empty_rect; for (span_num = 0; span_num < line->len; span_num++) { fz_text_span *span = line->spans[span_num]; for (i = 0; i < span->len; i++) { fz_text_char_bbox(&charbox, span, i); if (charbox.x1 >= x0 && charbox.x0 <= x1 && charbox.y1 >= y0 && charbox.y0 <= y1) { if (charbox.y0 != linebox.y0 || fz_abs(charbox.x0 - linebox.x1) > 5) { if (!fz_is_empty_rect(&linebox) && hit_count < hit_max) hit_bbox[hit_count++] = linebox; linebox = charbox; } else { fz_union_rect(&linebox, &charbox); } } } } if (!fz_is_empty_rect(&linebox) && hit_count < hit_max) hit_bbox[hit_count++] = linebox; } } return hit_count; }
static pdf_annot * pdf_create_markup_annot(pdf_xref *xref, fz_obj *obj, char *type) { fz_context *ctx = xref->ctx; fz_buffer *content = fz_new_buffer(ctx, 512); fz_rect rect = pdf_to_rect(ctx, fz_dict_gets(ctx, obj, "Rect")); fz_obj *quad_points = fz_dict_gets(ctx, obj, "QuadPoints"); fz_rect a, b; float rgb[3]; int i; for (i = 0; i < fz_array_len(ctx, quad_points) / 8; i++) { pdf_get_quadrilaterals(ctx, quad_points, i, &a, &b); b.y0 -= 0.25; a.y1 += 0.25; rect = fz_union_rect(rect, fz_union_rect(a, b)); } pdf_get_annot_color(ctx, obj, rgb); fz_buffer_printf(ctx, content, "q %.4f %.4f %.4f RG 1 0 0 1 -%.4f -%.4f cm 0.5 w ", rgb[0], rgb[1], rgb[2], rect.x0, rect.y0); if (!strcmp(type, "Squiggly")) fz_buffer_printf(ctx, content, "[1 1] d "); for (i = 0; i < fz_array_len(ctx, quad_points) / 8; i++) { pdf_get_quadrilaterals(ctx, quad_points, i, &a, &b); if (!strcmp(type, "StrikeOut")) fz_buffer_printf(ctx, content, "%.4f %.4f m %.4f %.4f l ", (a.x0 + b.x0) / 2, (a.y0 + b.y0) / 2, (a.x1 + b.x1) / 2, (a.y1 + b.y1) / 2); else fz_buffer_printf(ctx, content, "%.4f %.4f m %.4f %.4f l ", b.x0, b.y0, a.x1, a.y1); } fz_buffer_printf(ctx, content, "S Q"); return pdf_create_annot(ctx, rect, fz_keep_obj(obj), content, NULL, 0); }
int fz_highlight_selection(fz_context *ctx, fz_stext_page *page, fz_rect rect, fz_rect *hit_bbox, int hit_max) { fz_rect linebox, charbox; fz_stext_block *block; fz_stext_line *line; fz_stext_char *ch; int hit_count; float x0 = rect.x0; float x1 = rect.x1; float y0 = rect.y0; float y1 = rect.y1; hit_count = 0; for (block = page->first_block; block; block = block->next) { if (block->type != FZ_STEXT_BLOCK_TEXT) continue; for (line = block->u.t.first_line; line; line = line->next) { linebox = fz_empty_rect; for (ch = line->first_char; ch; ch = ch->next) { fz_stext_char_bbox(ctx, &charbox, line, ch); if (charbox.x1 >= x0 && charbox.x0 <= x1 && charbox.y1 >= y0 && charbox.y0 <= y1) { if (charbox.y0 != linebox.y0 || fz_abs(charbox.x0 - linebox.x1) > 5) { if (!fz_is_empty_rect(&linebox) && hit_count < hit_max) hit_bbox[hit_count++] = linebox; linebox = charbox; } else { fz_union_rect(&linebox, &charbox); } } } if (!fz_is_empty_rect(&linebox) && hit_count < hit_max) hit_bbox[hit_count++] = linebox; } } return hit_count; }
int fz_search_stext_page(fz_context *ctx, fz_stext_page *text, const char *needle, fz_rect *hit_bbox, int hit_max) { int pos, len, i, n, hit_count; if (strlen(needle) == 0) return 0; hit_count = 0; len = textlen_stext(ctx, text); pos = 0; while (pos < len) { n = match_stext(ctx, text, needle, pos); if (n) { fz_rect linebox = fz_empty_rect; for (i = 0; i < n; i++) { fz_rect charbox; bboxat(ctx, text, pos + i, &charbox); if (!fz_is_empty_rect(&charbox)) { if (charbox.y0 != linebox.y0 || fz_abs(charbox.x0 - linebox.x1) > 5) { if (!fz_is_empty_rect(&linebox) && hit_count < hit_max) hit_bbox[hit_count++] = linebox; linebox = charbox; } else { fz_union_rect(&linebox, &charbox); } } } if (!fz_is_empty_rect(&linebox) && hit_count < hit_max) hit_bbox[hit_count++] = linebox; pos += n; } else { pos += 1; } } return hit_count; }
fz_rect * fz_bound_text(fz_context *ctx, const fz_text *text, const fz_stroke_state *stroke, const fz_matrix *ctm, fz_rect *bbox) { fz_text_span *span; fz_matrix tm, trm; fz_rect gbox; int i; *bbox = fz_empty_rect; for (span = text->head; span; span = span->next) { if (span->len > 0) { tm = span->trm; for (i = 0; i < span->len; i++) { if (span->items[i].gid >= 0) { tm.e = span->items[i].x; tm.f = span->items[i].y; fz_concat(&trm, &tm, ctm); fz_bound_glyph(ctx, span->font, span->items[i].gid, &trm, &gbox); fz_union_rect(bbox, &gbox); } } } } if (!fz_is_empty_rect(bbox)) { if (stroke) fz_adjust_rect_for_stroke(ctx, bbox, stroke, ctm); /* Compensate for the glyph cache limited positioning precision */ bbox->x0 -= 1; bbox->y0 -= 1; bbox->x1 += 1; bbox->y1 += 1; } return bbox; }
static void fz_bbox_add_rect(fz_context *ctx, fz_device *dev, const fz_rect *rect, int clip) { fz_bbox_device *bdev = (fz_bbox_device*)dev; fz_rect r = *rect; if (0 < bdev->top && bdev->top <= STACK_SIZE) { fz_intersect_rect(&r, &bdev->stack[bdev->top-1]); } if (!clip && bdev->top <= STACK_SIZE && !bdev->ignore) { fz_union_rect(bdev->result, &r); } if (clip && ++bdev->top <= STACK_SIZE) { bdev->stack[bdev->top-1] = r; } }
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); }
void xps_parse_path(fz_context *ctx, xps_document *doc, fz_matrix ctm, char *base_uri, xps_resource *dict, fz_xml *root) { fz_device *dev = doc->dev; 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; fz_stroke_state *stroke = NULL; 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; /* * 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"); 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(ctx, doc, dict, &data_att, &data_tag, NULL); xps_resolve_resource_reference(ctx, doc, dict, &clip_att, &clip_tag, NULL); xps_resolve_resource_reference(ctx, doc, dict, &transform_att, &transform_tag, NULL); xps_resolve_resource_reference(ctx, doc, dict, &fill_att, &fill_tag, &fill_uri); xps_resolve_resource_reference(ctx, doc, dict, &stroke_att, &stroke_tag, &stroke_uri); xps_resolve_resource_reference(ctx, 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 (fz_xml_is_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 (fz_xml_is_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(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; } } ctm = xps_parse_transform(ctx, doc, transform_att, transform_tag, ctm); if (clip_att || clip_tag) xps_clip(ctx, doc, ctm, dict, clip_att, clip_tag); fz_try(ctx) { fill_rule = 0; if (data_att) path = xps_parse_abbreviated_geometry(ctx, doc, data_att, &fill_rule); else if (data_tag) { path = xps_parse_path_geometry(ctx, doc, dict, data_tag, 0, &fill_rule); // /home/sebras/src/jxr/fts_06xx.xps if (stroke_att || stroke_tag) stroke_path = xps_parse_path_geometry(ctx, doc, dict, data_tag, 1, &fill_rule); } if (!stroke_path) stroke_path = path; if (stroke_att || stroke_tag) { area = fz_bound_path(ctx, stroke_path, stroke, ctm); if (stroke_path != path && (fill_att || fill_tag)) { fz_rect bounds = fz_bound_path(ctx, path, NULL, ctm); area = fz_union_rect(area, bounds); } } else area = fz_bound_path(ctx, path, NULL, ctm); xps_begin_opacity(ctx, doc, ctm, area, opacity_mask_uri, dict, opacity_att, opacity_mask_tag); if (fill_att) { xps_parse_color(ctx, doc, base_uri, fill_att, &colorspace, samples); if (fill_opacity_att) samples[0] *= fz_atof(fill_opacity_att); xps_set_color(ctx, doc, colorspace, samples); fz_fill_path(ctx, dev, path, fill_rule == 0, ctm, doc->colorspace, doc->color, doc->alpha, NULL); } if (fill_tag) { fz_clip_path(ctx, dev, path, fill_rule == 0, ctm, area); xps_parse_brush(ctx, doc, ctm, area, fill_uri, dict, fill_tag); fz_pop_clip(ctx, dev); } if (stroke_att) { xps_parse_color(ctx, doc, base_uri, stroke_att, &colorspace, samples); if (stroke_opacity_att) samples[0] *= fz_atof(stroke_opacity_att); xps_set_color(ctx, doc, colorspace, samples); fz_stroke_path(ctx, dev, stroke_path, stroke, ctm, doc->colorspace, doc->color, doc->alpha, NULL); } if (stroke_tag) { fz_clip_stroke_path(ctx, dev, stroke_path, stroke, ctm, area); xps_parse_brush(ctx, doc, ctm, area, stroke_uri, dict, stroke_tag); fz_pop_clip(ctx, dev); } xps_end_opacity(ctx, doc, opacity_mask_uri, dict, opacity_att, opacity_mask_tag); } fz_always(ctx) { if (stroke_path != path) fz_drop_path(ctx, stroke_path); fz_drop_path(ctx, path); fz_drop_stroke_state(ctx, stroke); } fz_catch(ctx) fz_rethrow(ctx); if (clip_att || clip_tag) fz_pop_clip(ctx, dev); }
static void fz_append_display_node(fz_display_list *list, fz_display_node *node) { switch (node->cmd) { case FZ_CMD_CLIP_PATH: case FZ_CMD_CLIP_STROKE_PATH: case FZ_CMD_CLIP_IMAGE_MASK: list->stack[list->top].update = &node->rect; list->stack[list->top].rect = fz_empty_rect; list->top++; break; case FZ_CMD_CLIP_TEXT: case FZ_CMD_CLIP_STROKE_TEXT: list->stack[list->top].update = NULL; list->stack[list->top].rect = fz_empty_rect; list->top++; break; case FZ_CMD_BEGIN_TILE: list->tiled++; if (list->top > 0) { list->stack[list->top-1].rect = fz_infinite_rect; } break; case FZ_CMD_END_TILE: list->tiled--; break; case FZ_CMD_END_GROUP: break; case FZ_CMD_POP_CLIP: if (list->top > 0) { fz_rect *update; list->top--; update = list->stack[list->top].update; if (list->tiled == 0) { if (update != NULL) { *update = fz_intersect_rect(*update, list->stack[list->top].rect); node->rect = *update; } else { node->rect = list->stack[list->top].rect; } } else { node->rect = fz_infinite_rect; } } /*@fallthrough@*/ default: if ((list->top > 0) && (list->tiled == 0)) { list->stack[list->top-1].rect = fz_union_rect(list->stack[list->top-1].rect, node->rect); } break; } if (!list->first) { list->first = node; list->last = node; } else { list->last->next = node; list->last = node; } }
static fz_text_line * push_span(fz_context *ctx, fz_text_device *tdev, fz_text_span *span, int new_line, float distance) { fz_text_line *line; fz_text_block *block; fz_text_page *page = tdev->page; int prev_not_text = 0; if (page->len == 0 || page->blocks[page->len-1].type != FZ_PAGE_BLOCK_TEXT) prev_not_text = 1; if (new_line || prev_not_text) { float size = fz_matrix_expansion(&span->transform); /* So, a new line. Part of the same block or not? */ if (distance == 0 || distance > size * 1.5 || distance < -size * PARAGRAPH_DIST || page->len == 0 || prev_not_text) { /* New block */ if (page->len == page->cap) { int newcap = (page->cap ? page->cap*2 : 4); page->blocks = fz_resize_array(ctx, page->blocks, newcap, sizeof(*page->blocks)); page->cap = newcap; } block = fz_malloc_struct(ctx, fz_text_block); page->blocks[page->len].type = FZ_PAGE_BLOCK_TEXT; page->blocks[page->len].u.text = block; block->cap = 0; block->len = 0; block->lines = 0; block->bbox = fz_empty_rect; page->len++; distance = 0; } /* New line */ block = page->blocks[page->len-1].u.text; if (block->len == block->cap) { int newcap = (block->cap ? block->cap*2 : 4); block->lines = fz_resize_array(ctx, block->lines, newcap, sizeof(*block->lines)); block->cap = newcap; } block->lines[block->len].first_span = NULL; block->lines[block->len].last_span = NULL; block->lines[block->len].distance = distance; block->lines[block->len].bbox = fz_empty_rect; block->len++; } /* Find last line and append to it */ block = page->blocks[page->len-1].u.text; line = &block->lines[block->len-1]; fz_union_rect(&block->lines[block->len-1].bbox, &span->bbox); fz_union_rect(&block->bbox, &span->bbox); span->base_offset = (new_line ? 0 : distance); if (!line->first_span) { line->first_span = line->last_span = span; span->next = NULL; } else { line->last_span->next = span; line->last_span = span; } return line; }
static void fz_append_display_node(fz_display_list *list, fz_display_node *node) { switch (node->cmd) { case FZ_CMD_CLIP_PATH: case FZ_CMD_CLIP_STROKE_PATH: case FZ_CMD_CLIP_IMAGE_MASK: if (list->top < STACK_SIZE) { list->stack[list->top].update = &node->rect; list->stack[list->top].rect = fz_empty_rect; } list->top++; break; case FZ_CMD_CLIP_TEXT: /* don't reset the clip rect for accumulated text */ if (node->flag == 2) break; /* fallthrough */ case FZ_CMD_END_MASK: case FZ_CMD_CLIP_STROKE_TEXT: if (list->top < STACK_SIZE) { list->stack[list->top].update = NULL; list->stack[list->top].rect = fz_empty_rect; } list->top++; break; case FZ_CMD_BEGIN_TILE: list->tiled++; if (list->top > 0 && list->top <= STACK_SIZE) { list->stack[list->top-1].rect = fz_infinite_rect; } break; case FZ_CMD_END_TILE: list->tiled--; break; case FZ_CMD_END_GROUP: break; case FZ_CMD_POP_CLIP: if (list->top > STACK_SIZE) { list->top--; node->rect = fz_infinite_rect; } else if (list->top > 0) { fz_rect *update; list->top--; update = list->stack[list->top].update; if (list->tiled == 0) { if (update) { fz_intersect_rect(update, &list->stack[list->top].rect); node->rect = *update; } else node->rect = list->stack[list->top].rect; } else node->rect = fz_infinite_rect; } /* fallthrough */ default: if (list->top > 0 && list->tiled == 0 && list->top <= STACK_SIZE) fz_union_rect(&list->stack[list->top-1].rect, &node->rect); break; } if (!list->first) { list->first = node; list->last = node; } else { list->last->next = node; list->last = node; } list->len++; }