static void fz_process_mesh_type2(fz_context *ctx, fz_shade *shade, const fz_matrix *ctm, fz_mesh_processor *painter) { fz_point p0, p1, dir; fz_vertex v0, v1, v2, v3; fz_vertex e0, e1; float theta; float zero = 0; float one = 1; p0.x = shade->u.l_or_r.coords[0][0]; p0.y = shade->u.l_or_r.coords[0][1]; p1.x = shade->u.l_or_r.coords[1][0]; p1.y = shade->u.l_or_r.coords[1][1]; dir.x = p0.y - p1.y; dir.y = p1.x - p0.x; fz_transform_point(&p0, ctm); fz_transform_point(&p1, ctm); fz_transform_vector(&dir, ctm); theta = atan2f(dir.y, dir.x); v0.p = fz_point_on_circle(p0, HUGENUM, theta); v1.p = fz_point_on_circle(p1, HUGENUM, theta); v2.p = fz_point_on_circle(p0, -HUGENUM, theta); v3.p = fz_point_on_circle(p1, -HUGENUM, theta); fz_prepare_color(ctx, painter, &v0, &zero); fz_prepare_color(ctx, painter, &v1, &one); fz_prepare_color(ctx, painter, &v2, &zero); fz_prepare_color(ctx, painter, &v3, &one); paint_quad(ctx, painter, &v0, &v2, &v3, &v1); if (shade->u.l_or_r.extend[0]) { e0.p.x = v0.p.x - (p1.x - p0.x) * HUGENUM; e0.p.y = v0.p.y - (p1.y - p0.y) * HUGENUM; fz_prepare_color(ctx, painter, &e0, &zero); e1.p.x = v2.p.x - (p1.x - p0.x) * HUGENUM; e1.p.y = v2.p.y - (p1.y - p0.y) * HUGENUM; fz_prepare_color(ctx, painter, &e1, &zero); paint_quad(ctx, painter, &e0, &v0, &v2, &e1); } if (shade->u.l_or_r.extend[1]) { e0.p.x = v1.p.x + (p1.x - p0.x) * HUGENUM; e0.p.y = v1.p.y + (p1.y - p0.y) * HUGENUM; fz_prepare_color(ctx, painter, &e0, &one); e1.p.x = v3.p.x + (p1.x - p0.x) * HUGENUM; e1.p.y = v3.p.y + (p1.y - p0.y) * HUGENUM; fz_prepare_color(ctx, painter, &e1, &one); paint_quad(ctx, painter, &e0, &v1, &v3, &e1); } }
fz_rect * fz_text_char_bbox(fz_context *ctx, fz_rect *bbox, fz_text_span *span, int i) { fz_point a, d; const fz_point *max; fz_text_char *ch; if (!span || i >= span->len) { *bbox = fz_empty_rect; return bbox; } ch = &span->text[i]; if (i == span->len-1) max = &span->max; else max = &span->text[i+1].p; if (span->wmode == 0) { a.x = 0; a.y = span->ascender_max; d.x = 0; d.y = span->descender_min; } else { a.x = span->ascender_max; a.y = 0; d.x = span->descender_min; d.y = 0; } fz_transform_vector(&a, &span->transform); fz_transform_vector(&d, &span->transform); bbox->x0 = bbox->x1 = ch->p.x + a.x; bbox->y0 = bbox->y1 = ch->p.y + a.y; a.x += max->x; a.y += max->y; add_point_to_rect(bbox, &a); a.x = ch->p.x + d.x; a.y = ch->p.y + d.y; add_point_to_rect(bbox, &a); a.x = max->x + d.x; a.y = max->y + d.y; add_point_to_rect(bbox, &a); return bbox; }
static void add_bbox_to_span(fz_text_span *span) { fz_point a, d; fz_rect *bbox = &span->bbox; if (!span) return; if (span->wmode == 0) { a.x = 0; a.y = span->ascender_max; d.x = 0; d.y = span->descender_min; } else { a.x = span->ascender_max; a.y = 0; d.x = span->descender_min; d.y = 0; } fz_transform_vector(&a, &span->transform); fz_transform_vector(&d, &span->transform); bbox->x0 = bbox->x1 = span->min.x + a.x; bbox->y0 = bbox->y1 = span->min.y + a.y; a.x += span->max.x; a.y += span->max.y; add_point_to_rect(bbox, &a); a.x = span->min.x + d.x; a.y = span->min.y + d.y; add_point_to_rect(bbox, &a); a.x = span->max.x + d.x; a.y = span->max.y + d.y; add_point_to_rect(bbox, &a); }
/* Some explaination of the parameters here is warranted. See: http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes Add an arc segment to path, that describes a section of an elliptical arc from the current point of path to (point_x,point_y), such that: The arc segment is taken from an elliptical arc of semi major radius size_x, semi minor radius size_y, where the semi major axis of the ellipse is rotated by rotation_angle. If is_large_arc, then the arc segment is selected to be > 180 degrees. If is_clockwise, then the arc sweeps clockwise. */ static void xps_draw_arc(fz_context *doc, fz_path *path, float size_x, float size_y, float rotation_angle, int is_large_arc, int is_clockwise, float point_x, float point_y) { fz_matrix rotmat, revmat; fz_matrix mtx; fz_point pt; float rx, ry; float x1, y1, x2, y2; float x1t, y1t; float cxt, cyt, cx, cy; float t1, t2, t3; float sign; float th1, dth; pt = fz_currentpoint(doc, path); x1 = pt.x; y1 = pt.y; x2 = point_x; y2 = point_y; rx = size_x; ry = size_y; if (is_clockwise != is_large_arc) sign = 1; else sign = -1; fz_rotate(&rotmat, rotation_angle); fz_rotate(&revmat, -rotation_angle); /* http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes */ /* Conversion from endpoint to center parameterization */ /* F.6.6.1 -- ensure radii are positive and non-zero */ rx = fabsf(rx); ry = fabsf(ry); if (rx < 0.001f || ry < 0.001f || (x1 == x2 && y1 == y2)) { fz_lineto(doc, path, x2, y2); return; } /* F.6.5.1 */ pt.x = (x1 - x2) / 2; pt.y = (y1 - y2) / 2; fz_transform_vector(&pt, &revmat); x1t = pt.x; y1t = pt.y; /* F.6.6.2 -- ensure radii are large enough */ t1 = (x1t * x1t) / (rx * rx) + (y1t * y1t) / (ry * ry); if (t1 > 1) { rx = rx * sqrtf(t1); ry = ry * sqrtf(t1); } /* F.6.5.2 */ t1 = (rx * rx * ry * ry) - (rx * rx * y1t * y1t) - (ry * ry * x1t * x1t); t2 = (rx * rx * y1t * y1t) + (ry * ry * x1t * x1t); t3 = t1 / t2; /* guard against rounding errors; sqrt of negative numbers is bad for your health */ if (t3 < 0) t3 = 0; t3 = sqrtf(t3); cxt = sign * t3 * (rx * y1t) / ry; cyt = sign * t3 * -(ry * x1t) / rx; /* F.6.5.3 */ pt.x = cxt; pt.y = cyt; fz_transform_vector(&pt, &rotmat); cx = pt.x + (x1 + x2) / 2; cy = pt.y + (y1 + y2) / 2; /* F.6.5.4 */ { fz_point coord1, coord2, coord3, coord4; coord1.x = 1; coord1.y = 0; coord2.x = (x1t - cxt) / rx; coord2.y = (y1t - cyt) / ry; coord3.x = (x1t - cxt) / rx; coord3.y = (y1t - cyt) / ry; coord4.x = (-x1t - cxt) / rx; coord4.y = (-y1t - cyt) / ry; th1 = angle_between(coord1, coord2); dth = angle_between(coord3, coord4); if (dth < 0 && !is_clockwise) dth += (((float)M_PI / 180) * 360); if (dth > 0 && is_clockwise) dth -= (((float)M_PI / 180) * 360); } fz_pre_scale(fz_pre_rotate(fz_translate(&mtx, cx, cy), rotation_angle), rx, ry); xps_draw_arc_segment(doc, path, &mtx, th1, th1 + dth, is_clockwise); fz_lineto(doc, path, point_x, point_y); }
static void fz_add_text_char_imp(fz_context *ctx, fz_text_device *dev, fz_text_style *style, int c, fz_matrix *trm, float adv, int wmode) { int can_append = 1; int add_space = 0; fz_point dir, ndir, p, q, r; float size; fz_point delta; float spacing = 0; float base_offset = 0; if (wmode == 0) { dir.x = 1; dir.y = 0; } else { dir.x = 0; dir.y = -1; } fz_transform_vector(&dir, trm); ndir = dir; fz_normalize_vector(&ndir); /* dir = direction vector for motion. ndir = normalised(dir) */ size = fz_matrix_expansion(trm); /* We need to identify where glyphs 'start' (p) and 'stop' (q). * Each glyph holds it's 'start' position, and the next glyph in the * span (or span->max if there is no next glyph) holds it's 'end' * position. * * For both horizontal and vertical motion, trm->{e,f} gives the * bottom left corner of the glyph. * * In horizontal mode: * + p is bottom left. * + q is the bottom right * In vertical mode: * + p is top left (where it advanced from) * + q is bottom left */ if (wmode == 0) { p.x = trm->e; p.y = trm->f; q.x = trm->e + adv * dir.x; q.y = trm->f + adv * dir.y; } else { p.x = trm->e - adv * dir.x; p.y = trm->f - adv * dir.y; q.x = trm->e; q.y = trm->f; } if (dev->cur_span == NULL || trm->a != dev->cur_span->transform.a || trm->b != dev->cur_span->transform.b || trm->c != dev->cur_span->transform.c || trm->d != dev->cur_span->transform.d || dev->cur_span->wmode != wmode) { /* If the matrix has changed, or the wmode is different (or * if we don't have a span at all), then we can't append. */ #ifdef DEBUG_SPANS printf("Transform/WMode changed\n"); #endif can_append = 0; } else { /* Calculate how far we've moved since the end of the current * span. */ delta.x = p.x - dev->cur_span->max.x; delta.y = p.y - dev->cur_span->max.y; /* The transform has not changed, so we know we're in the same * direction. Calculate 2 distances; how far off the previous * baseline we are, together with how far along the baseline * we are from the expected position. */ spacing = ndir.x * delta.x + ndir.y * delta.y; base_offset = -ndir.y * delta.x + ndir.x * delta.y; spacing /= size * SPACE_DIST; spacing = fabsf(spacing); if (fabsf(base_offset) < size * 0.1) { /* Only a small amount off the baseline - we'll take this */ if (spacing < 1.0) { /* Motion is in line, and small. */ } else if (spacing >= 1 && spacing < (SPACE_MAX_DIST/SPACE_DIST)) { /* Motion is in line, but large enough * to warrant us adding a space */ if (dev->lastchar != ' ' && wmode == 0) add_space = 1; } else { /* Motion is in line, but too large - split to a new span */ can_append = 0; } } else { can_append = 0; #ifdef DEBUG_SPANS spacing = 0; #endif } } #ifdef DEBUG_SPANS printf("%c%c append=%d space=%d size=%g spacing=%g base_offset=%g\n", dev->lastchar, c, can_append, add_space, size, spacing, base_offset); #endif if (can_append == 0) { /* Start a new span */ add_span_to_soup(ctx, dev->spans, dev->cur_span); dev->cur_span = NULL; dev->cur_span = fz_new_text_span(ctx, &p, wmode, trm); dev->cur_span->spacing = 0; } if (add_space) { r.x = - 0.2f; r.y = 0; fz_transform_point(&r, trm); add_char_to_span(ctx, dev->cur_span, ' ', &p, &r, style); } add_char_to_span(ctx, dev->cur_span, c, &p, &q, style); }
static void fz_text_extract_span(fz_context *ctx, fz_text_span **last, fz_text *text, fz_matrix ctm, fz_point *pen) { 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, err; if (text->len == 0) return; fz_lock(ctx, FZ_LOCK_FREETYPE); if (font->ft_face) { 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; } rect = fz_empty_rect; if (text->wmode == 0) { dir.x = 1; dir.y = 0; } else { dir.x = 0; dir.y = 1; } tm.e = 0; tm.f = 0; trm = fz_concat(tm, ctm); dir = fz_transform_vector(trm, dir); 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); multi = 1; for (i = 0; i < text->len; i++) { if (text->items[i].gid < 0) { fz_add_text_char(ctx, last, font, size, text->wmode, text->items[i].ucs, fz_round_rect(rect)); multi ++; fz_divide_text_chars(last, multi, fz_round_rect(rect)); continue; } multi = 1; /* Calculate new pen location and delta */ tm.e = text->items[i].x; tm.f = text->items[i].y; trm = fz_concat(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) { ndelta.x = delta.x / dist; ndelta.y = delta.y / dist; dot = ndelta.x * ndir.x + ndelta.y * ndir.y; if (dist > size * LINE_DIST) { fz_add_text_newline(ctx, last, font, size, text->wmode); } else if (fabsf(dot) > 0.95f && dist > size * SPACE_DIST) { if ((*last)->len > 0 && (*last)->text[(*last)->len - 1].c != ' ') { fz_rect spacerect; spacerect.x0 = -0.2f; spacerect.y0 = 0; spacerect.x1 = 0; spacerect.y1 = 1; spacerect = fz_transform_rect(trm, spacerect); fz_add_text_char(ctx, last, font, size, text->wmode, ' ', fz_round_rect(spacerect)); } } } /* 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; */ FT_Get_Advance(font->ft_face, text->items[i].gid, mask, &ftadv); adv = ftadv / 65536.0f; rect.x0 = 0; rect.y0 = descender; rect.x1 = adv; rect.y1 = ascender; } else { adv = font->t3widths[text->items[i].gid]; rect.x0 = 0; rect.y0 = descender; rect.x1 = adv; rect.y1 = ascender; } rect = fz_transform_rect(trm, rect); pen->x = trm.e + dir.x * adv; pen->y = trm.f + dir.y * adv; fz_add_text_char(ctx, last, font, size, text->wmode, text->items[i].ucs, fz_round_rect(rect)); } fz_unlock(ctx, FZ_LOCK_FREETYPE); }
static void fz_text_extract_span(fz_text_span **last, fz_text *text, fz_matrix ctm, fz_point *pen) { // fz_font *font = text->font; // FT_Face face = font->ft_face; fz_matrix tm = text->trm; fz_matrix trm; float size; float adv = 0; fz_rect rect; fz_point dir, ndir; fz_point delta, ndelta; float dist, dot; float ascender = 1; float descender = 0; int multi; int i; if (text->len == 0) return; 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; trm = fz_concat(tm, ctm); dir = fz_transform_vector(trm, dir); 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); multi = 1; for (i = 0; i < text->len; i++) { if (text->items[i].gid < 0) { fz_add_text_char(last, size, text->wmode, text->items[i].ucs, fz_round_rect(rect)); multi ++; fz_divide_text_chars(last, multi, fz_round_rect(rect)); continue; } multi = 1; /* Calculate new pen location and delta */ tm.e = text->items[i].x; tm.f = text->items[i].y; trm = fz_concat(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) { ndelta.x = delta.x / dist; ndelta.y = delta.y / dist; dot = ndelta.x * ndir.x + ndelta.y * ndir.y; if (dist > size * LINE_DIST) { fz_add_text_newline(last, size, text->wmode); } else if (fabsf(dot) > 0.95f && dist > size * SPACE_DIST) { if ((*last)->len > 0 && (*last)->text[(*last)->len - 1].c != ' ') { fz_rect spacerect; spacerect.x0 = -0.2f; spacerect.y0 = 0; spacerect.x1 = 0; spacerect.y1 = 1; spacerect = fz_transform_rect(trm, spacerect); fz_add_text_char(last, size, text->wmode, ' ', fz_round_rect(spacerect)); } } } /* 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; */ // FT_Get_Advance(font->ft_face, text->items[i].gid, mask, &ftadv); // adv = ftadv / 65536.0f; // rect.x0 = 0; // rect.y0 = descender; // rect.x1 = adv; // rect.y1 = ascender; //} //else { // adv = font->t3widths[text->items[i].gid]; rect.x0 = 0; rect.y0 = descender; rect.x1 = adv; rect.y1 = ascender; } rect = fz_transform_rect(trm, rect); pen->x = trm.e + dir.x * adv; pen->y = trm.f + dir.y * adv; fz_add_text_char(last, size, text->wmode, text->items[i].ucs, fz_round_rect(rect)); } }
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; } }