Exemple #1
0
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 = MIN(bbox.x0, gbox.x0);
            bbox.y0 = MIN(bbox.y0, gbox.y0);
            bbox.x1 = MAX(bbox.x1, gbox.x1);
            bbox.y1 = 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;
}
Exemple #2
0
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;
}
Exemple #3
0
static font *
svg_dev_text_span_as_paths_defs(fz_context *ctx, fz_device *dev, fz_text_span *span, const fz_matrix *ctm)
{
	svg_device *sdev = (svg_device*)dev;
	fz_output *out = sdev->out;
	int i, font_idx;
	font *fnt;
	fz_matrix shift = fz_identity;

	for (font_idx = 0; font_idx < sdev->num_fonts; font_idx++)
	{
		if (sdev->fonts[font_idx].font == span->font)
			break;
	}
	if (font_idx == sdev->num_fonts)
	{
		/* New font */
		if (font_idx == sdev->max_fonts)
		{
			int newmax = sdev->max_fonts * 2;
			if (newmax == 0)
				newmax = 4;
			sdev->fonts = fz_resize_array(ctx, sdev->fonts, newmax, sizeof(*sdev->fonts));
			memset(&sdev->fonts[font_idx], 0, (newmax - font_idx) * sizeof(sdev->fonts[0]));
			sdev->max_fonts = newmax;
		}
		sdev->fonts[font_idx].id = sdev->id++;
		sdev->fonts[font_idx].font = fz_keep_font(ctx, span->font);
		sdev->num_fonts++;
	}
	fnt = &sdev->fonts[font_idx];

	for (i=0; i < span->len; i++)
	{
		fz_text_item *it = &span->items[i];
		int gid = it->gid;

		if (gid < 0)
			continue;
		if (gid >= fnt->max_sentlist)
		{
			int j;
			fnt->sentlist = fz_resize_array(ctx, fnt->sentlist, gid+1, sizeof(fnt->sentlist[0]));
			for (j = fnt->max_sentlist; j <= gid; j++)
			{
				fnt->sentlist[j].x_off = FLT_MIN;
				fnt->sentlist[j].y_off = FLT_MIN;
			}
			fnt->max_sentlist = gid+1;
		}
		if (fnt->sentlist[gid].x_off == FLT_MIN)
		{
			/* Need to send this one */
			fz_rect rect;
			fz_path *path;
			path = fz_outline_glyph(ctx, span->font, gid, &fz_identity);
			if (path)
			{
				fz_bound_path(ctx, path, NULL, &fz_identity, &rect);
				shift.e = -rect.x0;
				shift.f = -rect.y0;
				fz_transform_path(ctx, path, &shift);
				out = start_def(ctx, sdev);
				fz_printf(ctx, out, "<symbol id=\"font_%x_%x\">", fnt->id, gid);
				fz_printf(ctx, out, "<path");
				svg_dev_path(ctx, sdev, path);
				fz_printf(ctx, out, "/>\n");
			}
			else
			{
				fz_bound_glyph(ctx, span->font, gid, &fz_identity, &rect);
				shift.e = -rect.x0;
				shift.f = -rect.y0;
				out = start_def(ctx, sdev);
				fz_printf(ctx, out, "<symbol id=\"font_%x_%x\">", fnt->id, gid);
				fz_run_t3_glyph(ctx, span->font, gid, &shift, dev);
			}
			fz_printf(ctx, out, "</symbol>");
			out = end_def(ctx, sdev);
			fnt->sentlist[gid].x_off = rect.x0;
			fnt->sentlist[gid].y_off = rect.y0;
		}
	}
	return fnt;
}
Exemple #4
0
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;
	}
}