static void include_cap(fz_rect *rect, float x, float y, float r) { rect->x0 = fz_min(rect->x0, x-r); rect->y0 = fz_min(rect->y0, y-r); rect->x1 = fz_max(rect->x1, x+r); rect->y1 = fz_max(rect->y1, y+r); }
pdf_page * pdf_create_page(pdf_document *doc, fz_rect mediabox, int res, int rotate) { pdf_page *page = NULL; pdf_obj *pageobj; float userunit = 1; fz_context *ctx = doc->ctx; fz_matrix ctm, tmp; fz_rect realbox; page = fz_malloc_struct(ctx, pdf_page); fz_try(ctx) { page->resources = NULL; page->contents = NULL; page->transparency = 0; page->links = NULL; page->annots = NULL; page->me = pageobj = pdf_new_dict(doc, 4); pdf_dict_puts_drop(pageobj, "Type", pdf_new_name(doc, "Page")); page->mediabox.x0 = fz_min(mediabox.x0, mediabox.x1) * userunit; page->mediabox.y0 = fz_min(mediabox.y0, mediabox.y1) * userunit; page->mediabox.x1 = fz_max(mediabox.x0, mediabox.x1) * userunit; page->mediabox.y1 = fz_max(mediabox.y0, mediabox.y1) * userunit; pdf_dict_puts_drop(pageobj, "MediaBox", pdf_new_rect(doc, &page->mediabox)); /* Snap page->rotate to 0, 90, 180 or 270 */ if (page->rotate < 0) page->rotate = 360 - ((-page->rotate) % 360); if (page->rotate >= 360) page->rotate = page->rotate % 360; page->rotate = 90*((page->rotate + 45)/90); if (page->rotate > 360) page->rotate = 0; pdf_dict_puts_drop(pageobj, "Rotate", pdf_new_int(doc, page->rotate)); fz_pre_rotate(fz_scale(&ctm, 1, -1), -page->rotate); realbox = page->mediabox; fz_transform_rect(&realbox, &ctm); fz_pre_scale(fz_translate(&tmp, -realbox.x0, -realbox.y0), userunit, userunit); fz_concat(&ctm, &ctm, &tmp); page->ctm = ctm; /* Do not create a Contents, as an empty Contents dict is not * valid. See Bug 694712 */ } fz_catch(ctx) { pdf_drop_obj(page->me); fz_free(ctx, page); fz_rethrow_message(ctx, "Failed to create page"); } return page; }
static void fz_dash_bezier(struct sctx *s, float xa, float ya, float xb, float yb, float xc, float yc, float xd, float yd, int depth, int dash_cap) { float dmax; float xab, yab; float xbc, ybc; float xcd, ycd; float xabc, yabc; float xbcd, ybcd; float xabcd, yabcd; /* termination check */ dmax = fz_abs(xa - xb); dmax = fz_max(dmax, fz_abs(ya - yb)); dmax = fz_max(dmax, fz_abs(xd - xc)); dmax = fz_max(dmax, fz_abs(yd - yc)); if (dmax < s->flatness || depth >= MAX_DEPTH) { fz_point p; p.x = xd; p.y = yd; fz_dash_lineto(s, p, dash_cap, 1); return; } xab = xa + xb; yab = ya + yb; xbc = xb + xc; ybc = yb + yc; xcd = xc + xd; ycd = yc + yd; xabc = xab + xbc; yabc = yab + ybc; xbcd = xbc + xcd; ybcd = ybc + ycd; xabcd = xabc + xbcd; yabcd = yabc + ybcd; xab *= 0.5f; yab *= 0.5f; xbc *= 0.5f; ybc *= 0.5f; xcd *= 0.5f; ycd *= 0.5f; xabc *= 0.25f; yabc *= 0.25f; xbcd *= 0.25f; ybcd *= 0.25f; xabcd *= 0.125f; yabcd *= 0.125f; fz_dash_bezier(s, xa, ya, xab, yab, xabc, yabc, xabcd, yabcd, depth + 1, dash_cap); fz_dash_bezier(s, xabcd, yabcd, xbcd, ybcd, xcd, ycd, xd, yd, depth + 1, dash_cap); }
static void bezier(fz_gel *gel, const fz_matrix *ctm, float flatness, float xa, float ya, float xb, float yb, float xc, float yc, float xd, float yd, int depth) { float dmax; float xab, yab; float xbc, ybc; float xcd, ycd; float xabc, yabc; float xbcd, ybcd; float xabcd, yabcd; /* termination check */ dmax = fz_abs(xa - xb); dmax = fz_max(dmax, fz_abs(ya - yb)); dmax = fz_max(dmax, fz_abs(xd - xc)); dmax = fz_max(dmax, fz_abs(yd - yc)); if (dmax < flatness || depth >= MAX_DEPTH) { line(gel, ctm, xa, ya, xd, yd); return; } xab = xa + xb; yab = ya + yb; xbc = xb + xc; ybc = yb + yc; xcd = xc + xd; ycd = yc + yd; xabc = xab + xbc; yabc = yab + ybc; xbcd = xbc + xcd; ybcd = ybc + ycd; xabcd = xabc + xbcd; yabcd = yabc + ybcd; xab *= 0.5f; yab *= 0.5f; xbc *= 0.5f; ybc *= 0.5f; xcd *= 0.5f; ycd *= 0.5f; xabc *= 0.25f; yabc *= 0.25f; xbcd *= 0.25f; ybcd *= 0.25f; xabcd *= 0.125f; yabcd *= 0.125f; bezier(gel, ctm, flatness, xa, ya, xab, yab, xabc, yabc, xabcd, yabcd, depth + 1); bezier(gel, ctm, flatness, xabcd, yabcd, xbcd, ybcd, xcd, ycd, xd, yd, depth + 1); }
fz_rect * pdf_to_rect(fz_context *ctx, pdf_obj *array, fz_rect *r) { float a = pdf_to_real(pdf_array_get(array, 0)); float b = pdf_to_real(pdf_array_get(array, 1)); float c = pdf_to_real(pdf_array_get(array, 2)); float d = pdf_to_real(pdf_array_get(array, 3)); r->x0 = fz_min(a, c); r->y0 = fz_min(b, d); r->x1 = fz_max(a, c); r->y1 = fz_max(b, d); return r; }
fz_rect pdf_to_rect(fz_context *ctx, pdf_obj *array) { fz_rect r; float a = pdf_to_real(pdf_array_get(array, 0)); float b = pdf_to_real(pdf_array_get(array, 1)); float c = pdf_to_real(pdf_array_get(array, 2)); float d = pdf_to_real(pdf_array_get(array, 3)); r.x0 = fz_min(a, c); r.y0 = fz_min(b, d); r.x1 = fz_max(a, c); r.y1 = fz_max(b, d); return r; }
fz_rect fz_intersect_rect(fz_rect a, fz_rect b) { fz_rect r; /* Check for empty box before infinite box */ if (fz_is_empty_rect(a)) return fz_empty_rect; if (fz_is_empty_rect(b)) return fz_empty_rect; if (fz_is_infinite_rect(a)) return b; if (fz_is_infinite_rect(b)) return a; r.x0 = fz_max(a.x0, b.x0); r.y0 = fz_max(a.y0, b.y0); r.x1 = fz_min(a.x1, b.x1); r.y1 = fz_min(a.y1, b.y1); return (r.x1 < r.x0 || r.y1 < r.y0) ? fz_empty_rect : r; }
fz_rect fz_union_rect(fz_rect a, fz_rect b) { fz_rect r; /* Check for empty box before infinite box */ if (fz_is_empty_rect(a)) return b; if (fz_is_empty_rect(b)) return a; if (fz_is_infinite_rect(a)) return a; if (fz_is_infinite_rect(b)) return b; r.x0 = fz_min(a.x0, b.x0); r.y0 = fz_min(a.y0, b.y0); r.x1 = fz_max(a.x1, b.x1); r.y1 = fz_max(a.y1, b.y1); return r; }
static void region_mask_merge(region_mask *rm1, const region_mask *rm2, int newlen) { int o, i1, i2; /* First, ensure that rm1 is long enough */ if (rm1->cap < newlen) { int newcap = rm1->cap ? rm1->cap : 2; do { newcap *= 2; } while (newcap < newlen); rm1->mask = fz_resize_array(rm1->ctx, rm1->mask, newcap, sizeof(*rm1->mask)); rm1->cap = newcap; } /* Now run backwards along rm1, filling it out with the merged regions */ for (o = newlen-1, i1 = rm1->len-1, i2 = rm2->len-1; o >= 0; o--) { /* So we read from i1 and i2 and store in o */ if (i1 < 0) { /* Just copy i2 */ rm1->mask[o] = rm2->mask[i2]; i2--; } else if (i2 < 0) { /* Just copy i1 */ rm1->mask[o] = rm1->mask[i1]; i1--; } else if (rm1->mask[i1].stop < rm2->mask[i2].start) { /* rm1's region is entirely before rm2's - copy rm2's */ rm1->mask[o] = rm2->mask[i2]; i2--; } else if (rm2->mask[i2].stop < rm1->mask[i1].start) { /* rm2's region is entirely before rm1's - copy rm1's */ rm1->mask[o] = rm1->mask[i1]; i1--; } else { /* We must be merging */ rm1->mask[o].ave_start = (rm1->mask[i1].start * rm1->freq + rm2->mask[i2].start * rm2->freq)/(rm1->freq + rm2->freq); rm1->mask[o].ave_stop = (rm1->mask[i1].stop * rm1->freq + rm2->mask[i2].stop * rm2->freq)/(rm1->freq + rm2->freq); rm1->mask[o].start = fz_min(rm1->mask[i1].start, rm2->mask[i2].start); rm1->mask[o].stop = fz_max(rm1->mask[i1].stop, rm2->mask[i2].stop); i1--; i2--; } } rm1->freq += rm2->freq; rm1->len = newlen; }
fz_rect fz_bound_text(fz_context *ctx, fz_text *text, fz_matrix ctm) { fz_matrix tm, trm; fz_rect bbox; fz_rect gbox; int i; if (text->len == 0) return fz_empty_rect; // TODO: stroke state tm = text->trm; tm.e = text->items[0].x; tm.f = text->items[0].y; trm = fz_concat(tm, ctm); bbox = fz_bound_glyph(ctx, text->font, text->items[0].gid, trm); for (i = 1; i < text->len; i++) { if (text->items[i].gid >= 0) { tm.e = text->items[i].x; tm.f = text->items[i].y; trm = fz_concat(tm, ctm); gbox = fz_bound_glyph(ctx, text->font, text->items[i].gid, trm); bbox.x0 = fz_min(bbox.x0, gbox.x0); bbox.y0 = fz_min(bbox.y0, gbox.y0); bbox.x1 = fz_max(bbox.x1, gbox.x1); bbox.y1 = fz_max(bbox.y1, gbox.y1); } } /* 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_process_mesh_type3(fz_context *ctx, fz_shade *shade, const fz_matrix *ctm, fz_mesh_processor *painter) { fz_point p0, p1; float r0, r1; fz_point e; float er, rs; int count; p0.x = shade->u.l_or_r.coords[0][0]; p0.y = shade->u.l_or_r.coords[0][1]; r0 = shade->u.l_or_r.coords[0][2]; p1.x = shade->u.l_or_r.coords[1][0]; p1.y = shade->u.l_or_r.coords[1][1]; r1 = shade->u.l_or_r.coords[1][2]; /* number of segments for a half-circle */ count = 4 * sqrtf(fz_matrix_expansion(ctm) * fz_max(r0, r1)); if (count < 3) count = 3; if (count > 1024) count = 1024; if (shade->u.l_or_r.extend[0]) { if (r0 < r1) rs = r0 / (r0 - r1); else rs = -HUGENUM; e.x = p0.x + (p1.x - p0.x) * rs; e.y = p0.y + (p1.y - p0.y) * rs; er = r0 + (r1 - r0) * rs; fz_paint_annulus(ctx, ctm, e, er, 0, p0, r0, 0, count, painter); } fz_paint_annulus(ctx, ctm, p0, r0, 0, p1, r1, 1, count, painter); if (shade->u.l_or_r.extend[1]) { if (r0 > r1) rs = r1 / (r1 - r0); else rs = -HUGENUM; e.x = p1.x + (p0.x - p1.x) * rs; e.y = p1.y + (p0.y - p1.y) * rs; er = r1 + (r0 - r1) * rs; fz_paint_annulus(ctx, ctm, p1, r1, 1, e, er, 1, count, painter); } }
static void pdf_write_line_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect) { pdf_obj *line, *le; fz_point a, b; float w; int ic; w = pdf_write_border_appearance(ctx, annot, buf); pdf_write_stroke_color_appearance(ctx, annot, buf); ic = pdf_write_interior_fill_color_appearance(ctx, annot, buf); line = pdf_dict_get(ctx, annot->obj, PDF_NAME(L)); a.x = pdf_array_get_real(ctx, line, 0); a.y = pdf_array_get_real(ctx, line, 1); b.x = pdf_array_get_real(ctx, line, 2); b.y = pdf_array_get_real(ctx, line, 3); fz_append_printf(ctx, buf, "%g %g m\n%g %g l\nS\n", a.x, a.y, b.x, b.y); rect->x0 = fz_min(a.x, b.x); rect->y0 = fz_min(a.y, b.y); rect->x1 = fz_max(a.x, b.x); rect->y1 = fz_max(a.y, b.y); le = pdf_dict_get(ctx, annot->obj, PDF_NAME(LE)); if (pdf_array_len(ctx, le) == 2) { float dx = b.x - a.x; float dy = b.y - a.y; float l = sqrtf(dx*dx + dy*dy); pdf_write_line_cap_appearance(ctx, buf, rect, a.x, a.y, dx/l, dy/l, w, ic, pdf_array_get(ctx, le, 0)); pdf_write_line_cap_appearance(ctx, buf, rect, b.x, b.y, -dx/l, -dy/l, w, ic, pdf_array_get(ctx, le, 1)); } *rect = fz_expand_rect(*rect, fz_max(1, w)); }
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 float calc_bbox_overlap(const fz_rect *bbox1, const fz_rect *bbox2) { float area1, area2, area3; fz_rect intersect = *bbox1; fz_intersect_rect(&intersect, bbox2); if (fz_is_empty_rect(&intersect)) return 0; area1 = (bbox1->x1 - bbox1->x0) * (bbox1->y1 - bbox1->y0); area2 = (bbox2->x1 - bbox2->x0) * (bbox2->y1 - bbox2->y0); area3 = (intersect.x1 - intersect.x0) * (intersect.y1 - intersect.y0); return area3 / fz_max(area1, area2); }
static void pdf_write_arrow_appearance(fz_context *ctx, fz_buffer *buf, fz_rect *rect, float x, float y, float dx, float dy, float w) { float r = fz_max(1, w); float angle = atan2f(dy, dx); fz_point v, a, b; v = rotate_vector(angle, 8.8f*r, 4.5f*r); a = fz_make_point(x + v.x, y + v.y); v = rotate_vector(angle, 8.8f*r, -4.5f*r); b = fz_make_point(x + v.x, y + v.y); *rect = fz_include_point_in_rect(*rect, a); *rect = fz_include_point_in_rect(*rect, b); *rect = fz_expand_rect(*rect, w); fz_append_printf(ctx, buf, "%g %g m\n", a.x, a.y); fz_append_printf(ctx, buf, "%g %g l\n", x, y); fz_append_printf(ctx, buf, "%g %g l\n", b.x, b.y); }
static float layout_block(fz_context *ctx, fz_html *box, fz_html *top, float em, float page_h, float vertical) { fz_html *child; int first; fz_css_style *style = &box->style; float *margin = box->margin; float *border = box->border; float *padding = box->padding; em = box->em = fz_from_css_number(style->font_size, em, em); margin[0] = fz_from_css_number(style->margin[0], em, top->w); margin[1] = fz_from_css_number(style->margin[1], em, top->w); margin[2] = fz_from_css_number(style->margin[2], em, top->w); margin[3] = fz_from_css_number(style->margin[3], em, top->w); padding[0] = fz_from_css_number(style->padding[0], em, top->w); padding[1] = fz_from_css_number(style->padding[1], em, top->w); padding[2] = fz_from_css_number(style->padding[2], em, top->w); padding[3] = fz_from_css_number(style->padding[3], em, top->w); border[0] = style->border_style[0] ? fz_from_css_number(style->border_width[0], em, top->w) : 0; border[1] = style->border_style[1] ? fz_from_css_number(style->border_width[1], em, top->w) : 0; border[2] = style->border_style[2] ? fz_from_css_number(style->border_width[2], em, top->w) : 0; border[3] = style->border_style[3] ? fz_from_css_number(style->border_width[3], em, top->w) : 0; box->x = top->x + margin[L] + border[L] + padding[L]; box->w = top->w - (margin[L] + margin[R] + border[L] + border[R] + padding[L] + padding[R]); if (margin[T] > vertical) margin[T] -= vertical; else margin[T] = 0; if (padding[T] == 0 && border[T] == 0) vertical += margin[T]; else vertical = 0; box->y = top->y + top->h + margin[T] + border[T] + padding[T]; box->h = 0; first = 1; for (child = box->down; child; child = child->next) { if (child->type == BOX_BLOCK) { vertical = layout_block(ctx, child, box, em, page_h, vertical); if (first) { /* move collapsed parent/child top margins to parent */ margin[T] += child->margin[T]; box->y += child->margin[T]; child->margin[T] = 0; first = 0; } box->h += child->h + child->padding[T] + child->padding[B] + child->border[T] + child->border[B] + child->margin[T] + child->margin[B]; } else if (child->type == BOX_BREAK) { box->h += fz_from_css_number_scale(style->line_height, em, em, em); vertical = 0; first = 0; } else if (child->type == BOX_FLOW) { layout_flow(ctx, child, box, em, page_h); if (child->h > 0) { box->h += child->h; vertical = 0; first = 0; } } } /* reserve space for the list mark */ if (box->list_item && box->h == 0) { box->h += fz_from_css_number_scale(style->line_height, em, em, em); vertical = 0; } if (box->h == 0) { if (margin[B] > vertical) margin[B] -= vertical; else margin[B] = 0; } else { box->h -= vertical; vertical = fz_max(margin[B], vertical); margin[B] = vertical; } return vertical; }
pdf_page * pdf_load_page_by_obj(pdf_document *doc, int number, pdf_obj *pageref) { fz_context *ctx = doc->ctx; pdf_page *page; pdf_annot *annot; pdf_obj *pageobj, *obj; fz_rect mediabox, cropbox, realbox; float userunit; fz_matrix mat; /* SumatraPDF: allow replacing potentially slow pdf_lookup_page_obj */ pageobj = pdf_resolve_indirect(pageref); page = fz_malloc_struct(ctx, pdf_page); page->resources = NULL; page->contents = NULL; page->transparency = 0; page->links = NULL; page->annots = NULL; page->annot_tailp = &page->annots; page->deleted_annots = NULL; page->tmp_annots = NULL; page->me = pdf_keep_obj(pageobj); page->incomplete = 0; obj = pdf_dict_gets(pageobj, "UserUnit"); if (pdf_is_real(obj)) userunit = pdf_to_real(obj); else userunit = 1; pdf_to_rect(ctx, pdf_lookup_inherited_page_item(doc, pageobj, "MediaBox"), &mediabox); if (fz_is_empty_rect(&mediabox)) { fz_warn(ctx, "cannot find page size for page %d", number + 1); mediabox.x0 = 0; mediabox.y0 = 0; mediabox.x1 = 612; mediabox.y1 = 792; } pdf_to_rect(ctx, pdf_lookup_inherited_page_item(doc, pageobj, "CropBox"), &cropbox); if (!fz_is_empty_rect(&cropbox)) fz_intersect_rect(&mediabox, &cropbox); page->mediabox.x0 = fz_min(mediabox.x0, mediabox.x1) * userunit; page->mediabox.y0 = fz_min(mediabox.y0, mediabox.y1) * userunit; page->mediabox.x1 = fz_max(mediabox.x0, mediabox.x1) * userunit; page->mediabox.y1 = fz_max(mediabox.y0, mediabox.y1) * userunit; if (page->mediabox.x1 - page->mediabox.x0 < 1 || page->mediabox.y1 - page->mediabox.y0 < 1) { fz_warn(ctx, "invalid page size in page %d", number + 1); page->mediabox = fz_unit_rect; } page->rotate = pdf_to_int(pdf_lookup_inherited_page_item(doc, pageobj, "Rotate")); /* Snap page->rotate to 0, 90, 180 or 270 */ if (page->rotate < 0) page->rotate = 360 - ((-page->rotate) % 360); if (page->rotate >= 360) page->rotate = page->rotate % 360; page->rotate = 90*((page->rotate + 45)/90); if (page->rotate > 360) page->rotate = 0; fz_pre_rotate(fz_scale(&page->ctm, 1, -1), -page->rotate); realbox = page->mediabox; fz_transform_rect(&realbox, &page->ctm); fz_pre_scale(fz_translate(&mat, -realbox.x0, -realbox.y0), userunit, userunit); fz_concat(&page->ctm, &page->ctm, &mat); fz_try(ctx) { obj = pdf_dict_gets(pageobj, "Annots"); if (obj) { page->links = pdf_load_link_annots(doc, obj, &page->ctm); pdf_load_annots(doc, page, obj); } } fz_catch(ctx) { if (fz_caught(ctx) != FZ_ERROR_TRYLATER) /* SumatraPDF: ignore annotations in case of unexpected errors */ fz_warn(ctx, "unexpectedly failed to load page annotations"); page->incomplete |= PDF_PAGE_INCOMPLETE_ANNOTS; } page->duration = pdf_to_real(pdf_dict_gets(pageobj, "Dur")); obj = pdf_dict_gets(pageobj, "Trans"); page->transition_present = (obj != NULL); if (obj) { pdf_load_transition(doc, page, obj); } // TODO: inherit page->resources = pdf_lookup_inherited_page_item(doc, pageobj, "Resources"); if (page->resources) pdf_keep_obj(page->resources); obj = pdf_dict_gets(pageobj, "Contents"); fz_try(ctx) { page->contents = pdf_keep_obj(obj); if (pdf_resources_use_blending(doc, page->resources)) page->transparency = 1; /* cf. http://code.google.com/p/sumatrapdf/issues/detail?id=2107 */ else if (!strcmp(pdf_to_name(pdf_dict_getp(pageobj, "Group/S")), "Transparency")) page->transparency = 1; for (annot = page->annots; annot && !page->transparency; annot = annot->next) if (annot->ap && pdf_resources_use_blending(doc, annot->ap->resources)) page->transparency = 1; } fz_catch(ctx) { if (fz_caught(ctx) != FZ_ERROR_TRYLATER) { pdf_free_page(doc, page); fz_rethrow_message(ctx, "cannot load page %d contents (%d 0 R)", number + 1, pdf_to_num(pageref)); } page->incomplete |= PDF_PAGE_INCOMPLETE_CONTENTS; } return page; }
static void fz_text_extract(fz_context *ctx, fz_text_device *dev, fz_text *text, const fz_matrix *ctm, fz_text_style *style) { fz_point *pen = &dev->point; fz_font *font = text->font; FT_Face face = font->ft_face; fz_matrix tm = text->trm; fz_matrix trm; float size; float adv; fz_rect rect; fz_point dir, ndir; fz_point delta, ndelta; float dist, dot; float ascender = 1; float descender = 0; int multi; int i, j, err; if (text->len == 0) return; if (font->ft_face) { fz_lock(ctx, FZ_LOCK_FREETYPE); err = FT_Set_Char_Size(font->ft_face, 64, 64, 72, 72); if (err) fz_warn(ctx, "freetype set character size: %s", ft_error_string(err)); ascender = (float)face->ascender / face->units_per_EM; descender = (float)face->descender / face->units_per_EM; fz_unlock(ctx, FZ_LOCK_FREETYPE); } else if (font->t3procs && !fz_is_empty_rect(&font->bbox)) { ascender = font->bbox.y1; descender = font->bbox.y0; } rect = fz_empty_rect; /* SumatraPDF: TODO: make this depend on the per-glyph displacement vector */ if (text->wmode == 0) { dir.x = 1; dir.y = 0; } else { dir.x = 0; dir.y = 1; } tm.e = 0; tm.f = 0; fz_concat(&trm, &tm, ctm); fz_transform_vector(&dir, &trm); dist = sqrtf(dir.x * dir.x + dir.y * dir.y); ndir.x = dir.x / dist; ndir.y = dir.y / dist; size = fz_matrix_expansion(&trm); for (i = 0; i < text->len; i++) { /* Calculate new pen location and delta */ tm.e = text->items[i].x; tm.f = text->items[i].y; fz_concat(&trm, &tm, ctm); delta.x = pen->x - trm.e; delta.y = pen->y - trm.f; if (pen->x == -1 && pen->y == -1) delta.x = delta.y = 0; dist = sqrtf(delta.x * delta.x + delta.y * delta.y); /* Add space and newlines based on pen movement */ if (dist > 0) { /* SumatraPDF: don't add spaces for large overlapping glyphs */ fz_text_span *last = &dev->cur_span; if (last->len == 0 && dev->cur_line.len > 0) last = &dev->cur_line.spans[dev->cur_line.len - 1]; ndelta.x = delta.x / dist; ndelta.y = delta.y / dist; dot = ndelta.x * ndir.x + ndelta.y * ndir.y; /* SumatraPDF: don't merge multiple lines into one */ if (dist > size * LINE_DIST && hypotf(delta.y * ndir.x, delta.x * ndir.y) > size * 0.5f) { fz_flush_text_line(ctx, dev, style); dev->lastchar = ' '; } else /* SumatraPDF: use 0.95f instead of 0.9995f for slightly better results */ if (fabsf(dot) > 0.95f && dist > size * SPACE_DIST && dist < size * SPACE_MAX_DIST) { if (dev->lastchar != ' ' && /* SumatraPDF: don't add spaces before spaces or for large overlapping glyphs */ text->items[i].ucs != ' ' && !fz_maps_into_rect(trm, last->text[last->len - 1].bbox)) { fz_rect spacerect; spacerect.x0 = -0.2f; spacerect.y0 = descender; spacerect.x1 = 0; spacerect.y1 = ascender; fz_transform_rect(&spacerect, &trm); fz_add_text_char(ctx, dev, style, ' ', spacerect); dev->lastchar = ' '; } } else if (dist > size * LINE_DIST) { fz_flush_text_line(ctx, dev, style); dev->lastchar = ' '; } } /* Calculate bounding box and new pen position based on font metrics */ if (font->ft_face) { FT_Fixed ftadv = 0; int mask = FT_LOAD_NO_BITMAP | FT_LOAD_NO_HINTING | FT_LOAD_IGNORE_TRANSFORM; /* TODO: freetype returns broken vertical metrics */ /* if (text->wmode) mask |= FT_LOAD_VERTICAL_LAYOUT; */ fz_lock(ctx, FZ_LOCK_FREETYPE); err = FT_Set_Char_Size(font->ft_face, 64, 64, 72, 72); if (err) fz_warn(ctx, "freetype set character size: %s", ft_error_string(err)); FT_Get_Advance(font->ft_face, text->items[i].gid, mask, &ftadv); adv = ftadv / 65536.0f; fz_unlock(ctx, FZ_LOCK_FREETYPE); rect.x0 = 0; rect.y0 = descender; rect.x1 = adv; rect.y1 = ascender; } /* SumatraPDF: TODO: this check might no longer be needed */ else if (text->items[i].gid < 256) { adv = font->t3widths[text->items[i].gid]; rect.x0 = 0; rect.y0 = descender; rect.x1 = adv; rect.y1 = ascender; } fz_transform_rect(&rect, &trm); /* cf. http://code.google.com/p/sumatrapdf/issues/detail?id=1839 */ if (font->ft_face) { fz_rect bbox; fz_bound_glyph(ctx, font, text->items[i].gid, &trm, &bbox); rect.y0 = fz_min(rect.y0, bbox.y0); rect.y1 = fz_max(rect.y1, bbox.y1); } pen->x = trm.e + dir.x * adv; pen->y = trm.f + dir.y * adv; /* Check for one glyph to many char mapping */ for (j = i + 1; j < text->len; j++) if (text->items[j].gid >= 0) break; multi = j - i; if (multi == 1) { fz_add_text_char(ctx, dev, style, text->items[i].ucs, rect); } else { for (j = 0; j < multi; j++) { fz_rect part = fz_split_bbox(rect, j, multi); fz_add_text_char(ctx, dev, style, text->items[i + j].ucs, part); } i += j - 1; } dev->lastchar = text->items[i].ucs; } }
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; 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 (!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; fz_transform_rect(&local_area, fz_invert_matrix(&invctm, &transform)); /* SumatraPDF: make sure that the intended area is covered */ { fz_point tl; fz_irect bbox; fz_rect bigview = viewbox; bigview.x1 = bigview.x0 + xstep; bigview.y1 = bigview.y0 + ystep; fz_irect_from_rect(&bbox, fz_transform_rect(&bigview, &transform)); tl.x = bbox.x0; tl.y = bbox.y0; fz_transform_point(&tl, &invctm); local_area.x0 -= fz_max(tl.x, 0) - 0.001f; local_area.y0 -= fz_max(tl.y, 0) - 0.001f; local_area.x1 += xstep - fz_max(tl.x, 0) - 0.001f; local_area.y1 += ystep - fz_max(tl.y, 0) - 0.001f; } 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 /* cf. http://code.google.com/p/sumatrapdf/issues/detail?id=2248 */ if ((local_area.x1 - local_area.x0) / xstep > 1 || (local_area.y1 - local_area.y0) / ystep > 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); }
static void pdf_write_line_cap_appearance(fz_context *ctx, fz_buffer *buf, fz_rect *rect, float x, float y, float dx, float dy, float w, int ic, pdf_obj *cap) { if (cap == PDF_NAME(Square)) { float r = fz_max(2.5f, w * 2.5f); fz_append_printf(ctx, buf, "%g %g %g %g re\n", x-r, y-r, r*2, r*2); fz_append_string(ctx, buf, ic ? "b\n" : "s\n"); include_cap(rect, x, y, r); } else if (cap == PDF_NAME(Circle)) { float r = fz_max(2.5f, w * 2.5f); float m = r * CIRCLE_MAGIC; fz_append_printf(ctx, buf, "%g %g m\n", x, y+r); fz_append_printf(ctx, buf, "%g %g %g %g %g %g c\n", x+m, y+r, x+r, y+m, x+r, y); fz_append_printf(ctx, buf, "%g %g %g %g %g %g c\n", x+r, y-m, x+m, y-r, x, y-r); fz_append_printf(ctx, buf, "%g %g %g %g %g %g c\n", x-m, y-r, x-r, y-m, x-r, y); fz_append_printf(ctx, buf, "%g %g %g %g %g %g c\n", x-r, y+m, x-m, y+r, x, y+r); fz_append_string(ctx, buf, ic ? "b\n" : "s\n"); include_cap(rect, x, y, r); } else if (cap == PDF_NAME(Diamond)) { float r = fz_max(2.5f, w * 2.5f); fz_append_printf(ctx, buf, "%g %g m\n", x, y+r); fz_append_printf(ctx, buf, "%g %g l\n", x+r, y); fz_append_printf(ctx, buf, "%g %g l\n", x, y-r); fz_append_printf(ctx, buf, "%g %g l\n", x-r, y); fz_append_string(ctx, buf, ic ? "b\n" : "s\n"); include_cap(rect, x, y, r); } else if (cap == PDF_NAME(OpenArrow)) { pdf_write_arrow_appearance(ctx, buf, rect, x, y, dx, dy, w); fz_append_string(ctx, buf, "S\n"); } else if (cap == PDF_NAME(ClosedArrow)) { pdf_write_arrow_appearance(ctx, buf, rect, x, y, dx, dy, w); fz_append_string(ctx, buf, ic ? "b\n" : "s\n"); } /* PDF 1.5 */ else if (cap == PDF_NAME(Butt)) { float r = fz_max(3, w * 3); fz_point a = { x-dy*r, y+dx*r }; fz_point b = { x+dy*r, y-dx*r }; fz_append_printf(ctx, buf, "%g %g m\n", a.x, a.y); fz_append_printf(ctx, buf, "%g %g l\n", b.x, b.y); fz_append_string(ctx, buf, "S\n"); *rect = fz_include_point_in_rect(*rect, a); *rect = fz_include_point_in_rect(*rect, b); } /* PDF 1.6 */ else if (cap == PDF_NAME(ROpenArrow)) { pdf_write_arrow_appearance(ctx, buf, rect, x, y, -dx, -dy, w); fz_append_string(ctx, buf, "S\n"); } else if (cap == PDF_NAME(RClosedArrow)) { pdf_write_arrow_appearance(ctx, buf, rect, x, y, -dx, -dy, w); fz_append_string(ctx, buf, ic ? "b\n" : "s\n"); } else if (cap == PDF_NAME(Slash)) { float r = fz_max(5, w * 5); float angle = atan2f(dy, dx) - (30 * FZ_PI / 180); fz_point a, b, v; v = rotate_vector(angle, 0, r); a = fz_make_point(x + v.x, y + v.y); v = rotate_vector(angle, 0, -r); b = fz_make_point(x + v.x, y + v.y); fz_append_printf(ctx, buf, "%g %g m\n", a.x, a.y); fz_append_printf(ctx, buf, "%g %g l\n", b.x, b.y); fz_append_string(ctx, buf, "S\n"); *rect = fz_include_point_in_rect(*rect, a); *rect = fz_include_point_in_rect(*rect, b); } }
static void xps_draw_radial_gradient(fz_context *ctx, xps_document *doc, const fz_matrix *ctm, const fz_rect *area, struct stop *stops, int count, fz_xml *root, int spread) { float x0, y0, r0; float x1, y1, r1; float xrad = 1; float yrad = 1; float invscale; int i, ma = 1; fz_matrix local_ctm = *ctm; fz_matrix inv; fz_rect local_area = *area; char *center_att = fz_xml_att(root, "Center"); char *origin_att = fz_xml_att(root, "GradientOrigin"); char *radius_x_att = fz_xml_att(root, "RadiusX"); char *radius_y_att = fz_xml_att(root, "RadiusY"); x0 = y0 = 0.0; x1 = y1 = 1.0; xrad = 1.0; yrad = 1.0; if (origin_att) xps_parse_point(ctx, doc, origin_att, &x0, &y0); if (center_att) xps_parse_point(ctx, doc, center_att, &x1, &y1); if (radius_x_att) xrad = fz_atof(radius_x_att); if (radius_y_att) yrad = fz_atof(radius_y_att); xrad = fz_max(0.01f, xrad); yrad = fz_max(0.01f, yrad); /* scale the ctm to make ellipses */ if (fz_abs(xrad) > FLT_EPSILON) { fz_pre_scale(&local_ctm, 1, yrad/xrad); } if (yrad != 0.0) { invscale = xrad / yrad; y0 = y0 * invscale; y1 = y1 * invscale; } r0 = 0; r1 = xrad; fz_transform_rect(&local_area, fz_invert_matrix(&inv, &local_ctm)); ma = fz_maxi(ma, ceilf(hypotf(local_area.x0 - x0, local_area.y0 - y0) / xrad)); ma = fz_maxi(ma, ceilf(hypotf(local_area.x1 - x0, local_area.y0 - y0) / xrad)); ma = fz_maxi(ma, ceilf(hypotf(local_area.x0 - x0, local_area.y1 - y0) / xrad)); ma = fz_maxi(ma, ceilf(hypotf(local_area.x1 - x0, local_area.y1 - y0) / xrad)); if (spread == SPREAD_REPEAT) { for (i = ma - 1; i >= 0; i--) xps_draw_one_radial_gradient(ctx, doc, &local_ctm, stops, count, 0, x0, y0, r0 + i * xrad, x1, y1, r1 + i * xrad); } else if (spread == SPREAD_REFLECT) { if ((ma % 2) != 0) ma++; for (i = ma - 2; i >= 0; i -= 2) { xps_draw_one_radial_gradient(ctx, doc, &local_ctm, stops, count, 0, x0, y0, r0 + i * xrad, x1, y1, r1 + i * xrad); xps_draw_one_radial_gradient(ctx, doc, &local_ctm, stops, count, 0, x0, y0, r0 + (i + 2) * xrad, x1, y1, r1 + i * xrad); } } else { xps_draw_one_radial_gradient(ctx, doc, &local_ctm, stops, count, 1, x0, y0, r0, x1, y1, r1); } }
void fz_flatten_dash_path(fz_gel *gel, fz_path *path, fz_stroke_state *stroke, fz_matrix ctm, float flatness, float linewidth) { struct sctx s; fz_point p0, p1, p2, p3, beg; float phase_len, max_expand; int i; s.gel = gel; s.ctm = &ctm; s.flatness = flatness; s.linejoin = stroke->linejoin; s.linewidth = linewidth * 0.5f; s.miterlimit = stroke->miterlimit; s.sn = 0; s.bn = 0; s.dot = 0; s.dash_list = stroke->dash_list; s.dash_phase = stroke->dash_phase; s.dash_len = stroke->dash_len; s.toggle = 0; s.offset = 0; s.phase = 0; s.cap = stroke->start_cap; if (path->len > 0 && path->items[0].k != FZ_MOVETO) return; phase_len = 0; for (i = 0; i < stroke->dash_len; i++) phase_len += stroke->dash_list[i]; max_expand = fz_max(fz_max(fz_abs(ctm.a),fz_abs(ctm.b)),fz_max(fz_abs(ctm.c),fz_abs(ctm.d))); if (phase_len < 0.01f || phase_len * max_expand < 0.5f) { fz_flatten_stroke_path(gel, path, stroke, ctm, flatness, linewidth); return; } p0.x = p0.y = 0; i = 0; while (i < path->len) { switch (path->items[i++].k) { case FZ_MOVETO: p1.x = path->items[i++].v; p1.y = path->items[i++].v; fz_dash_moveto(&s, p1, stroke->start_cap, stroke->end_cap); beg = p0 = p1; break; case FZ_LINETO: p1.x = path->items[i++].v; p1.y = path->items[i++].v; fz_dash_lineto(&s, p1, stroke->dash_cap, 0); p0 = p1; break; case FZ_CURVETO: p1.x = path->items[i++].v; p1.y = path->items[i++].v; p2.x = path->items[i++].v; p2.y = path->items[i++].v; p3.x = path->items[i++].v; p3.y = path->items[i++].v; fz_dash_bezier(&s, p0.x, p0.y, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, 0, stroke->dash_cap); p0 = p3; break; case FZ_CLOSE_PATH: fz_dash_lineto(&s, beg, stroke->dash_cap, 0); p0 = p1 = beg; break; } } fz_stroke_flush(&s, s.cap, stroke->end_cap); }