Exemplo n.º 1
0
static void
fz_draw_clip_path(fz_device *devp, fz_path *path, fz_rect rect, int even_odd, fz_matrix ctm)
{
	fz_draw_device *dev = devp->user;
	float expansion = fz_matrix_expansion(ctm);
	float flatness = 0.3f / expansion;
	fz_bbox bbox;
	fz_draw_state *state = &dev->stack[dev->top];
	fz_colorspace *model;
	fz_context *ctx = dev->ctx;

	fz_reset_gel(dev->gel, state->scissor);
	fz_flatten_fill_path(dev->gel, path, ctm, flatness);
	fz_sort_gel(dev->gel);

	state = push_stack(dev);
	model = state->dest->colorspace;

	bbox = fz_bound_gel(dev->gel);
	bbox = fz_intersect_bbox(bbox, state->scissor);
	bbox = fz_intersect_bbox(bbox, fz_bbox_from_rect(rect));
	/* SumatraPDF: try to match rendering with and without display list */
	if (fz_is_infinite_rect(rect))
		bbox = fz_intersect_bbox(bbox, fz_bbox_from_rect(fz_bound_path(ctx, path, NULL, ctm)));

	if (fz_is_empty_rect(bbox) || fz_is_rect_gel(dev->gel))
	{
		state[1].scissor = bbox;
		state[1].mask = NULL;
#ifdef DUMP_GROUP_BLENDS
		dump_spaces(dev->top-1, "Clip (rectangular) begin\n");
#endif
		return;
	}

	fz_try(ctx)
	{
		state[1].mask = fz_new_pixmap_with_bbox(dev->ctx, NULL, bbox);
		fz_clear_pixmap(dev->ctx, state[1].mask);
		state[1].dest = fz_new_pixmap_with_bbox(dev->ctx, model, bbox);
		fz_clear_pixmap(dev->ctx, state[1].dest);
		if (state[1].shape)
		{
			state[1].shape = fz_new_pixmap_with_bbox(dev->ctx, NULL, bbox);
			fz_clear_pixmap(dev->ctx, state[1].shape);
		}

		fz_scan_convert(dev->gel, even_odd, bbox, state[1].mask, NULL);

		state[1].blendmode |= FZ_BLEND_ISOLATED;
		state[1].scissor = bbox;
#ifdef DUMP_GROUP_BLENDS
		dump_spaces(dev->top-1, "Clip (non-rectangular) begin\n");
#endif
	}
	fz_catch(ctx)
	{
		emergency_pop_stack(dev, state);
	}
}
Exemplo n.º 2
0
static void
svg_dev_stroke_state(fz_context *ctx, svg_device *sdev, fz_stroke_state *stroke_state, const fz_matrix *ctm)
{
	fz_output *out = sdev->out;
	float exp;

	exp = fz_matrix_expansion(ctm);
	if (exp == 0)
		exp = 1;

	fz_printf(ctx, out, " stroke-width=\"%g\"", stroke_state->linewidth/exp);
	fz_printf(ctx, out, " stroke-linecap=\"%s\"",
		(stroke_state->start_cap == FZ_LINECAP_SQUARE ? "square" :
			(stroke_state->start_cap == FZ_LINECAP_ROUND ? "round" : "butt")));
	if (stroke_state->dash_len != 0)
	{
		int i;
		fz_printf(ctx, out, " stroke-dasharray=");
		for (i = 0; i < stroke_state->dash_len; i++)
			fz_printf(ctx, out, "%c%g", (i == 0 ? '\"' : ','), stroke_state->dash_list[i]);
		fz_printf(ctx, out, "\"");
		if (stroke_state->dash_phase != 0)
			fz_printf(ctx, out, " stroke-dashoffset=\"%g\"", stroke_state->dash_phase);
	}
	if (stroke_state->linejoin == FZ_LINEJOIN_MITER || stroke_state->linejoin == FZ_LINEJOIN_MITER_XPS)
		fz_printf(ctx, out, " stroke-miterlimit=\"%g\"", stroke_state->miterlimit);
	fz_printf(ctx, out, " stroke-linejoin=\"%s\"",
		(stroke_state->linejoin == FZ_LINEJOIN_BEVEL ? "bevel" :
			(stroke_state->linejoin == FZ_LINEJOIN_ROUND ? "round" : "miter")));
}
Exemplo n.º 3
0
static void
fz_draw_fill_path(void *user, fz_path *path, int even_odd, fz_matrix ctm,
	fz_colorspace *colorspace, float *color, float alpha)
{
	fz_draw_device *dev = user;
	fz_colorspace *model = dev->dest->colorspace;
	float expansion = fz_matrix_expansion(ctm);
	float flatness = 0.3f / expansion;
	unsigned char colorbv[FZ_MAX_COLORS + 1];
	float colorfv[FZ_MAX_COLORS];
	fz_bbox bbox;
	int i;

	fz_reset_gel(dev->gel, dev->scissor);
	fz_flatten_fill_path(dev->gel, path, ctm, flatness);
	fz_sort_gel(dev->gel);

	bbox = fz_bound_gel(dev->gel);
	bbox = fz_intersect_bbox(bbox, dev->scissor);

	if (fz_is_empty_rect(bbox))
		return;

	fz_convert_color(colorspace, color, model, colorfv);
	for (i = 0; i < model->n; i++)
		colorbv[i] = colorfv[i] * 255;
	colorbv[i] = alpha * 255;

	fz_scan_convert(dev->gel, even_odd, bbox, dev->dest, colorbv);
}
Exemplo n.º 4
0
static void
fz_draw_clip_stroke_path(fz_device *devp, fz_path *path, fz_rect *rect, fz_stroke_state *stroke, fz_matrix ctm)
{
	fz_draw_device *dev = devp->user;
	float expansion = fz_matrix_expansion(ctm);
	float flatness = 0.3f / expansion;
	float linewidth = stroke->linewidth;
	fz_bbox bbox;
	fz_draw_state *state = &dev->stack[dev->top];
	fz_colorspace *model;
	fz_context *ctx = dev->ctx;

	if (linewidth * expansion < 0.1f)
		linewidth = 1 / expansion;

	fz_reset_gel(dev->gel, state->scissor);
	if (stroke->dash_len > 0)
		fz_flatten_dash_path(dev->gel, path, stroke, ctm, flatness, linewidth);
	else
		fz_flatten_stroke_path(dev->gel, path, stroke, ctm, flatness, linewidth);
	fz_sort_gel(dev->gel);

	state = push_stack(dev);
	model = state->dest->colorspace;

	bbox = fz_bound_gel(dev->gel);
	bbox = fz_intersect_bbox(bbox, state->scissor);
	if (rect)
		bbox = fz_intersect_bbox(bbox, fz_bbox_covering_rect(*rect));
	/* SumatraPDF: try to match rendering with and without display list */
	else
		bbox = fz_intersect_bbox(bbox, fz_bbox_covering_rect(fz_bound_path(ctx, path, stroke, ctm)));

	fz_try(ctx)
	{
		state[1].mask = fz_new_pixmap_with_bbox(dev->ctx, NULL, bbox);
		fz_clear_pixmap(dev->ctx, state[1].mask);
		state[1].dest = fz_new_pixmap_with_bbox(dev->ctx, model, bbox);
		fz_clear_pixmap(dev->ctx, state[1].dest);
		if (state->shape)
		{
			state[1].shape = fz_new_pixmap_with_bbox(dev->ctx, NULL, bbox);
			fz_clear_pixmap(dev->ctx, state[1].shape);
		}

		if (!fz_is_empty_rect(bbox))
			fz_scan_convert(dev->gel, 0, bbox, state[1].mask, NULL);

		state[1].blendmode |= FZ_BLEND_ISOLATED;
		state[1].scissor = bbox;
#ifdef DUMP_GROUP_BLENDS
		dump_spaces(dev->top-1, "Clip (stroke) begin\n");
#endif
	}
	fz_catch(ctx)
	{
		emergency_pop_stack(dev, state);
	}
}
Exemplo n.º 5
0
static void
fz_draw_stroke_path(fz_device *devp, fz_path *path, fz_stroke_state *stroke, fz_matrix ctm,
	fz_colorspace *colorspace, float *color, float alpha)
{
	fz_draw_device *dev = devp->user;
	float expansion = fz_matrix_expansion(ctm);
	float flatness = 0.3f / expansion;
	float linewidth = stroke->linewidth;
	unsigned char colorbv[FZ_MAX_COLORS + 1];
	float colorfv[FZ_MAX_COLORS];
	fz_bbox bbox;
	int i;
	fz_draw_state *state = &dev->stack[dev->top];
	fz_colorspace *model = state->dest->colorspace;

	if (model == NULL)
		model = fz_device_gray;

	if (linewidth * expansion < 0.1f)
		linewidth = 1 / expansion;

	fz_reset_gel(dev->gel, state->scissor);
	if (stroke->dash_len > 0)
		fz_flatten_dash_path(dev->gel, path, stroke, ctm, flatness, linewidth);
	else
		fz_flatten_stroke_path(dev->gel, path, stroke, ctm, flatness, linewidth);
	fz_sort_gel(dev->gel);

	bbox = fz_bound_gel(dev->gel);
	bbox = fz_intersect_bbox(bbox, state->scissor);

	if (fz_is_empty_rect(bbox))
		return;

	if (state->blendmode & FZ_BLEND_KNOCKOUT)
		state = fz_knockout_begin(dev);

	fz_convert_color(dev->ctx, model, colorfv, colorspace, color);
	for (i = 0; i < model->n; i++)
		colorbv[i] = colorfv[i] * 255;
	colorbv[i] = alpha * 255;

	fz_scan_convert(dev->gel, 0, bbox, state->dest, colorbv);
	if (state->shape)
	{
		fz_reset_gel(dev->gel, state->scissor);
		if (stroke->dash_len > 0)
			fz_flatten_dash_path(dev->gel, path, stroke, ctm, flatness, linewidth);
		else
			fz_flatten_stroke_path(dev->gel, path, stroke, ctm, flatness, linewidth);
		fz_sort_gel(dev->gel);

		colorbv[0] = 255;
		fz_scan_convert(dev->gel, 0, bbox, state->shape, colorbv);
	}

	if (state->blendmode & FZ_BLEND_KNOCKOUT)
		fz_knockout_end(dev);
}
Exemplo n.º 6
0
static void
fz_draw_clip_stroke_path(fz_context *ctx, void *user, fz_path *path, fz_rect *rect, fz_stroke_state *stroke, fz_matrix ctm)
{
	fz_draw_device *dev = user;
	fz_colorspace *model = dev->dest->colorspace;
	float expansion = fz_matrix_expansion(ctm);
	float flatness = 0.3f / expansion;
	float linewidth = stroke->linewidth;
	fz_pixmap *mask, *dest, *shape;
	fz_bbox bbox;

	if (dev->top == dev->stack_max)
		fz_grow_stack(dev);

	if (linewidth * expansion < 0.1f)
		linewidth = 1 / expansion;

	fz_reset_gel(dev->gel, dev->scissor);
	if (stroke->dash_len > 0)
		fz_flatten_dash_path(ctx, dev->gel, path, stroke, ctm, flatness, linewidth);
	else
		fz_flatten_stroke_path(ctx, dev->gel, path, stroke, ctm, flatness, linewidth);
	fz_sort_gel(dev->gel);

	bbox = fz_bound_gel(dev->gel);
	bbox = fz_intersect_bbox(bbox, dev->scissor);
	if (rect)
		bbox = fz_intersect_bbox(bbox, fz_round_rect(*rect));

	mask = fz_new_pixmap_with_rect(ctx, NULL, bbox);
	fz_clear_pixmap(mask);
	dest = fz_new_pixmap_with_rect(ctx, model, bbox);
	/* FIXME: See note #1 */
	fz_clear_pixmap(dest);
	if (dev->shape)
	{
		shape = fz_new_pixmap_with_rect(ctx, NULL, bbox);
		fz_clear_pixmap(shape);
	}
	else
		shape = NULL;

	if (!fz_is_empty_rect(bbox))
		fz_scan_convert(dev->gel, 0, bbox, mask, NULL);

	dev->stack[dev->top].scissor = dev->scissor;
	dev->stack[dev->top].mask = mask;
	dev->stack[dev->top].dest = dev->dest;
	dev->stack[dev->top].shape = dev->shape;
	/* FIXME: See note #1 */
	dev->stack[dev->top].blendmode = dev->blendmode | FZ_BLEND_ISOLATED;
	dev->scissor = bbox;
	dev->dest = dest;
	dev->shape = shape;
#ifdef DUMP_GROUP_BLENDS
	dump_spaces(dev->top, "Clip (stroke) begin\n");
#endif
	dev->top++;
}
Exemplo n.º 7
0
static void
svg_dev_text(svg_device *sdev, const fz_matrix *ctm, fz_text *text)
{
	fz_output *out = sdev->out;
	int i;
	fz_matrix inverse;
	fz_matrix local_trm;
	float size;

	/* Rely on the fact that trm.{e,f} == 0 */
	size = fz_matrix_expansion(&text->trm);
	local_trm.a = text->trm.a / size;
	local_trm.b = text->trm.b / size;
	local_trm.c = -text->trm.c / size;
	local_trm.d = -text->trm.d / size;
	local_trm.e = 0;
	local_trm.f = 0;
	fz_invert_matrix(&inverse, &local_trm);
	fz_concat(&local_trm, &local_trm, ctm);

	fz_printf(out, " transform=\"matrix(%g,%g,%g,%g,%g,%g)\"",
		local_trm.a, local_trm.b, local_trm.c, local_trm.d, local_trm.e, local_trm.f);
	fz_printf(out, " font-size=\"%g\"", size);
	fz_printf(out, " font-family=\"%s\"", text->font->name);

	fz_printf(out, " x=");
	for (i=0; i < text->len; i++)
	{
		fz_text_item *it = &text->items[i];
		fz_point p;
		p.x = it->x;
		p.y = it->y;
		fz_transform_point(&p, &inverse);
		fz_printf(out, "%c%g", i == 0 ? '\"' : ' ', p.x);
	}
	fz_printf(out, "\" y=");
	for (i=0; i < text->len; i++)
	{
		fz_text_item *it = &text->items[i];
		fz_point p;
		p.x = it->x;
		p.y = it->y;
		fz_transform_point(&p, &inverse);
		fz_printf(out, "%c%g", i == 0 ? '\"' : ' ', p.y);
	}
	fz_printf(out, "\">\n");
	for (i=0; i < text->len; i++)
	{
		fz_text_item *it = &text->items[i];
		int c = it->ucs;
		if (c >= 32 && c <= 127 && c != '<' && c != '&')
			fz_printf(out, "%c", c);
		else
			fz_printf(out, "&#x%04x;", c);
	}
	fz_printf(out, "\n</text>\n");
}
Exemplo n.º 8
0
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);
	}
}
Exemplo n.º 9
0
static void
fz_draw_clip_path(void *user, fz_path *path, fz_rect *rect, int even_odd, fz_matrix ctm)
{
	fz_draw_device *dev = user;
	fz_colorspace *model = dev->dest->colorspace;
	float expansion = fz_matrix_expansion(ctm);
	float flatness = 0.3f / expansion;
	fz_pixmap *mask, *dest;
	fz_bbox bbox;

	if (dev->top == STACK_SIZE)
	{
		fz_warn("assert: too many buffers on stack");
		return;
	}

	fz_reset_gel(dev->gel, dev->scissor);
	fz_flatten_fill_path(dev->gel, path, ctm, flatness);
	fz_sort_gel(dev->gel);

	bbox = fz_bound_gel(dev->gel);
	bbox = fz_intersect_bbox(bbox, dev->scissor);
	if (rect) {
		bbox = fz_intersect_bbox(bbox, fz_round_rect(*rect));
	}

	if (fz_is_empty_rect(bbox) || fz_is_rect_gel(dev->gel))
	{
		dev->stack[dev->top].scissor = dev->scissor;
		dev->stack[dev->top].mask = NULL;
		dev->stack[dev->top].dest = NULL;
		dev->scissor = bbox;
		dev->top++;
		return;
	}

	mask = fz_new_pixmap_with_rect(NULL, bbox);
	dest = fz_new_pixmap_with_rect(model, bbox);

	fz_clear_pixmap(mask);
	fz_clear_pixmap(dest);

	fz_scan_convert(dev->gel, even_odd, bbox, mask, NULL);

	dev->stack[dev->top].scissor = dev->scissor;
	dev->stack[dev->top].mask = mask;
	dev->stack[dev->top].dest = dev->dest;
	dev->scissor = bbox;
	dev->dest = dest;
	dev->top++;
}
Exemplo n.º 10
0
static void
fz_draw_clip_stroke_path(void *user, fz_path *path, fz_rect *rect, fz_stroke_state *stroke, fz_matrix ctm)
{
	fz_draw_device *dev = user;
	fz_colorspace *model = dev->dest->colorspace;
	float expansion = fz_matrix_expansion(ctm);
	float flatness = 0.3f / expansion;
	float linewidth = stroke->linewidth;
	fz_pixmap *mask, *dest;
	fz_bbox bbox;

	if (dev->top == STACK_SIZE)
	{
		fz_warn("assert: too many buffers on stack");
		return;
	}

	if (linewidth * expansion < 0.1f)
		linewidth = 1 / expansion;

	fz_reset_gel(dev->gel, dev->scissor);
	if (stroke->dash_len > 0)
		fz_flatten_dash_path(dev->gel, path, stroke, ctm, flatness, linewidth);
	else
		fz_flatten_stroke_path(dev->gel, path, stroke, ctm, flatness, linewidth);
	fz_sort_gel(dev->gel);

	bbox = fz_bound_gel(dev->gel);
	bbox = fz_intersect_bbox(bbox, dev->scissor);
	if (rect)
		bbox = fz_intersect_bbox(bbox, fz_round_rect(*rect));

	mask = fz_new_pixmap_with_rect(NULL, bbox);
	dest = fz_new_pixmap_with_rect(model, bbox);

	fz_clear_pixmap(mask);
	fz_clear_pixmap(dest);

	if (!fz_is_empty_rect(bbox))
		fz_scan_convert(dev->gel, 0, bbox, mask, NULL);

	dev->stack[dev->top].scissor = dev->scissor;
	dev->stack[dev->top].mask = mask;
	dev->stack[dev->top].dest = dev->dest;
	dev->scissor = bbox;
	dev->dest = dest;
	dev->top++;
}
Exemplo n.º 11
0
static void
fz_draw_clip_path(fz_device *devp, fz_path *path, fz_rect *rect, int even_odd, fz_matrix ctm)
{
	fz_draw_device *dev = devp->user;
	float expansion = fz_matrix_expansion(ctm);
	float flatness = 0.3f / expansion;
	fz_bbox bbox;
	fz_draw_state *state = push_stack(dev);
	fz_colorspace *model = state->dest->colorspace;

	fz_reset_gel(dev->gel, state->scissor);
	fz_flatten_fill_path(dev->gel, path, ctm, flatness);
	fz_sort_gel(dev->gel);

	bbox = fz_bound_gel(dev->gel);
	bbox = fz_intersect_bbox(bbox, state->scissor);
	if (rect)
		bbox = fz_intersect_bbox(bbox, fz_bbox_covering_rect(*rect));

	if (fz_is_empty_rect(bbox) || fz_is_rect_gel(dev->gel))
	{
		state[1].scissor = bbox;
		state[1].mask = NULL;
#ifdef DUMP_GROUP_BLENDS
		dump_spaces(dev->top-1, "Clip (rectangular) begin\n");
#endif
		return;
	}

	state[1].mask = fz_new_pixmap_with_bbox(dev->ctx, NULL, bbox);
	fz_clear_pixmap(dev->ctx, state[1].mask);
	state[1].dest = fz_new_pixmap_with_bbox(dev->ctx, model, bbox);
	fz_clear_pixmap(dev->ctx, state[1].dest);
	if (state[1].shape)
	{
		state[1].shape = fz_new_pixmap_with_bbox(dev->ctx, NULL, bbox);
		fz_clear_pixmap(dev->ctx, state[1].shape);
	}

	fz_scan_convert(dev->gel, even_odd, bbox, state[1].mask, NULL);

	state[1].blendmode |= FZ_BLEND_ISOLATED;
	state[1].scissor = bbox;
#ifdef DUMP_GROUP_BLENDS
	dump_spaces(dev->top-1, "Clip (non-rectangular) begin\n");
#endif
}
Exemplo n.º 12
0
static void
fz_draw_fill_path(fz_device *devp, fz_path *path, int even_odd, fz_matrix ctm,
	fz_colorspace *colorspace, float *color, float alpha)
{
	fz_draw_device *dev = devp->user;
	float expansion = fz_matrix_expansion(ctm);
	float flatness = 0.3f / expansion;
	unsigned char colorbv[FZ_MAX_COLORS + 1];
	float colorfv[FZ_MAX_COLORS];
	fz_bbox bbox;
	int i;
	fz_draw_state *state = &dev->stack[dev->top];
	fz_colorspace *model = state->dest->colorspace;

	fz_reset_gel(dev->gel, state->scissor);
	fz_flatten_fill_path(dev->gel, path, ctm, flatness);
	fz_sort_gel(dev->gel);

	bbox = fz_bound_gel(dev->gel);
	bbox = fz_intersect_bbox(bbox, state->scissor);

	if (fz_is_empty_rect(bbox))
		return;

	if (state->blendmode & FZ_BLEND_KNOCKOUT)
		state = fz_knockout_begin(dev);

	fz_convert_color(dev->ctx, model, colorfv, colorspace, color);
	for (i = 0; i < model->n; i++)
		colorbv[i] = colorfv[i] * 255;
	colorbv[i] = alpha * 255;

	fz_scan_convert(dev->gel, even_odd, bbox, state->dest, colorbv);
	if (state->shape)
	{
		fz_reset_gel(dev->gel, state->scissor);
		fz_flatten_fill_path(dev->gel, path, ctm, flatness);
		fz_sort_gel(dev->gel);

		colorbv[0] = alpha * 255;
		fz_scan_convert(dev->gel, even_odd, bbox, state->shape, colorbv);
	}

	if (state->blendmode & FZ_BLEND_KNOCKOUT)
		fz_knockout_end(dev);
}
Exemplo n.º 13
0
static fz_text_style *
fz_lookup_text_style(fz_context *ctx, fz_text_sheet *sheet, fz_text *text, const fz_matrix *ctm,
	fz_colorspace *colorspace, float *color, float alpha, fz_stroke_state *stroke)
{
	float size = 1.0f;
	fz_font *font = text ? text->font : NULL;
	int wmode = text ? text->wmode : 0;
	if (ctm && text)
	{
		fz_matrix tm = text->trm;
		fz_matrix trm;
		tm.e = 0;
		tm.f = 0;
		fz_concat(&trm, &tm, ctm);
		size = fz_matrix_expansion(&trm);
	}
	return fz_lookup_text_style_imp(ctx, sheet, size, font, wmode, 0);
}
Exemplo n.º 14
0
float
fz_subpixel_adjust(fz_context *ctx, fz_matrix *ctm, fz_matrix *subpix_ctm, unsigned char *qe, unsigned char *qf)
{
	float size = fz_matrix_expansion(ctm);
	int q;
	float pix_e, pix_f, r;

	/* Quantise the subpixel positions. */
	/* We never need more than 4 subpixel positions for glyphs - arguably
	 * even that is too much. */
	if (size >= 48)
		q = 0, r = 0.5f;
	else if (size >= 24)
		q = 128, r = 0.25f;
	else
		q = 192, r = 0.125f;

	/* Split translation into pixel and subpixel parts */
	subpix_ctm->a = ctm->a;
	subpix_ctm->b = ctm->b;
	subpix_ctm->c = ctm->c;
	subpix_ctm->d = ctm->d;
	subpix_ctm->e = ctm->e + r;
	pix_e = floorf(subpix_ctm->e);
	subpix_ctm->e -= pix_e;
	subpix_ctm->f = ctm->f + r;
	pix_f = floorf(subpix_ctm->f);
	subpix_ctm->f -= pix_f;

	/* Quantise the subpixel part */
	*qe = (int)(subpix_ctm->e * 256) & q;
	subpix_ctm->e = *qe / 256.0f;
	*qf = (int)(subpix_ctm->f * 256) & q;
	subpix_ctm->f = *qf / 256.0f;

	/* Reassemble the complete translation */
	ctm->e = subpix_ctm->e + pix_e;
	ctm->f = subpix_ctm->f + pix_f;

	return size;
}
Exemplo n.º 15
0
static void
fz_draw_stroke_path(void *user, fz_path *path, fz_stroke_state *stroke, fz_matrix ctm,
	fz_colorspace *colorspace, float *color, float alpha)
{
	fz_draw_device *dev = user;
	fz_colorspace *model = dev->dest->colorspace;
	float expansion = fz_matrix_expansion(ctm);
	float flatness = 0.3f / expansion;
	float linewidth = stroke->linewidth;
	unsigned char colorbv[FZ_MAX_COLORS + 1];
	float colorfv[FZ_MAX_COLORS];
	fz_bbox bbox;
	int i;

	if (linewidth * expansion < 0.1f)
		linewidth = 1 / expansion;

	fz_reset_gel(dev->gel, dev->scissor);
	if (stroke->dash_len > 0)
		fz_flatten_dash_path(dev->gel, path, stroke, ctm, flatness, linewidth);
	else
		fz_flatten_stroke_path(dev->gel, path, stroke, ctm, flatness, linewidth);
	fz_sort_gel(dev->gel);

	bbox = fz_bound_gel(dev->gel);
	bbox = fz_intersect_bbox(bbox, dev->scissor);

	if (fz_is_empty_rect(bbox))
		return;

	fz_convert_color(colorspace, color, model, colorfv);
	for (i = 0; i < model->n; i++)
		colorbv[i] = colorfv[i] * 255;
	colorbv[i] = alpha * 255;

	fz_scan_convert(dev->gel, 0, bbox, dev->dest, colorbv);
}
Exemplo n.º 16
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;
	}
}
Exemplo n.º 17
0
static void
svg_dev_text(fz_context *ctx, svg_device *sdev, const fz_matrix *ctm, fz_text *text)
{
	fz_output *out = sdev->out;

	int i;
	fz_matrix inverse;
	fz_matrix local_trm;
	float size;
	int start, is_wspace, was_wspace;
	fz_text_span *span;

	for (span = text->head; text; span = span->next)
	{
		/* Rely on the fact that trm.{e,f} == 0 */
		size = fz_matrix_expansion(&span->trm);
		local_trm.a = span->trm.a / size;
		local_trm.b = span->trm.b / size;
		local_trm.c = -span->trm.c / size;
		local_trm.d = -span->trm.d / size;
		local_trm.e = 0;
		local_trm.f = 0;
		fz_invert_matrix(&inverse, &local_trm);
		fz_concat(&local_trm, &local_trm, ctm);

		fz_printf(ctx, out, " transform=\"matrix(%g,%g,%g,%g,%g,%g)\"",
				local_trm.a, local_trm.b, local_trm.c, local_trm.d, local_trm.e, local_trm.f);
		fz_printf(ctx, out, " font-size=\"%g\"", size);
		fz_printf(ctx, out, " font-family=\"%s\"", span->font->name);

		/* Leading (and repeated) whitespace presents a problem for SVG
		 * text, so elide it here. */
		for (start=0; start < span->len; start++)
		{
			fz_text_item *it = &span->items[start];
			if (!is_xml_wspace(it->ucs))
				break;
		}

		fz_printf(ctx, out, " x=");
		was_wspace = 0;
		for (i=start; i < span->len; i++)
		{
			fz_text_item *it = &span->items[i];
			fz_point p;
			is_wspace = is_xml_wspace(it->ucs);
			if (is_wspace && was_wspace)
				continue;
			was_wspace = is_wspace;
			p.x = it->x;
			p.y = it->y;
			fz_transform_point(&p, &inverse);
			fz_printf(ctx, out, "%c%g", i == start ? '\"' : ' ', p.x);
		}
		fz_printf(ctx, out, "\" y=");
		was_wspace = 0;
		for (i=start; i < span->len; i++)
		{
			fz_text_item *it = &span->items[i];
			fz_point p;
			is_wspace = is_xml_wspace(it->ucs);
			if (is_wspace && was_wspace)
				continue;
			was_wspace = is_wspace;
			p.x = it->x;
			p.y = it->y;
			fz_transform_point(&p, &inverse);
			fz_printf(ctx, out, "%c%g", i == start ? '\"' : ' ', p.y);
		}
		fz_printf(ctx, out, "\">\n");
		was_wspace = 0;
		for (i=start; i < span->len; i++)
		{
			fz_text_item *it = &span->items[i];
			int c = it->ucs;
			is_wspace = is_xml_wspace(c);
			if (is_wspace && was_wspace)
				continue;
			was_wspace = is_wspace;
			if (c >= 32 && c <= 127 && c != '<' && c != '&')
				fz_printf(ctx, out, "%c", c);
			else
				fz_printf(ctx, out, "&#x%04x;", c);
		}
		fz_printf(ctx, out, "\n</text>\n");
	}
}
Exemplo n.º 18
0
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;
}
Exemplo n.º 19
0
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);
}
/*
	Render a glyph and return a bitmap.
	If the glyph is too large to fit the cache we have two choices:
	1) Return NULL so the caller can draw the glyph using an outline.
		Only supported for freetype fonts.
	2) Render a clipped glyph by using the scissor rectangle.
		Only supported for type 3 fonts.
		This must not be inserted into the cache.
 */
fz_pixmap *
fz_render_glyph(fz_context *ctx, fz_font *font, int gid, const fz_matrix *ctm, fz_colorspace *model, fz_irect scissor)
{
	fz_glyph_cache *cache;
	fz_glyph_key key;
	fz_pixmap *val;
	float size = fz_matrix_expansion(ctm);
	int do_cache, locked, caching;
	fz_matrix local_ctm = *ctm;
	fz_glyph_cache_entry *entry;
	unsigned hash;

	fz_var(locked);
	fz_var(caching);

	if (size <= MAX_GLYPH_SIZE)
	{
		scissor = fz_infinite_irect;
		do_cache = 1;
	}
	else
	{
		/* SumatraPDF: don't break clipping by larger glyphs */
		if (font->ft_face && size > 3000)
			return NULL;
		do_cache = 0;
	}

	cache = ctx->glyph_cache;

	memset(&key, 0, sizeof key);
	key.font = font;
	key.gid = gid;
	key.a = local_ctm.a * 65536;
	key.b = local_ctm.b * 65536;
	key.c = local_ctm.c * 65536;
	key.d = local_ctm.d * 65536;
	key.e = (local_ctm.e - floorf(local_ctm.e)) * 256;
	key.f = (local_ctm.f - floorf(local_ctm.f)) * 256;
	key.aa = fz_aa_level(ctx);

	local_ctm.e = floorf(local_ctm.e) + key.e / 256.0f;
	local_ctm.f = floorf(local_ctm.f) + key.f / 256.0f;

	fz_lock(ctx, FZ_LOCK_GLYPHCACHE);
	hash = do_hash((unsigned char *)&key, sizeof(key)) % GLYPH_HASH_LEN;
	entry = cache->entry[hash];
	while (entry)
	{
		if (memcmp(&entry->key, &key, sizeof(key)) == 0)
		{
			move_to_front(cache, entry);
			val = fz_keep_pixmap(ctx, entry->val);
			fz_unlock(ctx, FZ_LOCK_GLYPHCACHE);
			return val;
		}
		entry = entry->bucket_next;
	}

	locked = 1;
	caching = 0;

	fz_try(ctx)
	{
		if (font->ft_face)
		{
			val = fz_render_ft_glyph(ctx, font, gid, &local_ctm, key.aa);
		}
		else if (font->t3procs)
		{
			/* We drop the glyphcache here, and execute the t3
			 * glyph code. The danger here is that some other
			 * thread will come along, and want the same glyph
			 * too. If it does, we may both end up rendering
			 * pixmaps. We cope with this later on, by ensuring
			 * that only one gets inserted into the cache. If
			 * we insert ours to find one already there, we
			 * abandon ours, and use the one there already.
			 */
			fz_unlock(ctx, FZ_LOCK_GLYPHCACHE);
			locked = 0;
			val = fz_render_t3_glyph(ctx, font, gid, &local_ctm, model, scissor);
			fz_lock(ctx, FZ_LOCK_GLYPHCACHE);
			locked = 1;
		}
		else
		{
			fz_warn(ctx, "assert: uninitialized font structure");
			val = NULL;
		}
		if (val && do_cache)
		{
			if (val->w < MAX_GLYPH_SIZE && val->h < MAX_GLYPH_SIZE)
			{
				/* If we throw an exception whilst caching,
				 * just ignore the exception and carry on. */
				caching = 1;
				if (!font->ft_face)
				{
					/* We had to unlock. Someone else might
					 * have rendered in the meantime */
					entry = cache->entry[hash];
					while (entry)
					{
						if (memcmp(&entry->key, &key, sizeof(key)) == 0)
						{
							fz_drop_pixmap(ctx, val);
							move_to_front(cache, entry);
							val = fz_keep_pixmap(ctx, entry->val);
							fz_unlock(ctx, FZ_LOCK_GLYPHCACHE);
							return val;
						}
						entry = entry->bucket_next;
					}
				}

				entry = fz_malloc_struct(ctx, fz_glyph_cache_entry);
				entry->key = key;
				entry->hash = hash;
				entry->bucket_next = cache->entry[hash];
				if (entry->bucket_next)
					entry->bucket_next->bucket_prev = entry;
				cache->entry[hash] = entry;
				entry->val = fz_keep_pixmap(ctx, val);
				fz_keep_font(ctx, key.font);

				entry->lru_next = cache->lru_head;
				if (entry->lru_next)
					entry->lru_next->lru_prev = entry;
				else
					cache->lru_tail = entry;
				cache->lru_head = entry;

				cache->total += val->w * val->h;
				while (cache->total > MAX_CACHE_SIZE)
				{
					drop_glyph_cache_entry(ctx, cache->lru_tail);
				}

			}
		}
	}
	fz_always(ctx)
	{
		if (locked)
			fz_unlock(ctx, FZ_LOCK_GLYPHCACHE);
	}
	fz_catch(ctx)
	{
		if (caching)
			fz_warn(ctx, "cannot encache glyph; continuing");
		else
			fz_rethrow(ctx);
	}

	return val;
}
Exemplo n.º 21
0
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);
}
Exemplo n.º 22
0
static void
strain_soup(fz_context *ctx, fz_text_device *tdev)
{
	span_soup *soup = tdev->spans;
	fz_text_line *last_line = NULL;
	fz_text_span *last_span = NULL;
	int span_num;

	if (soup == NULL)
		return;

	/* Really dumb implementation to match what we had before */
	for (span_num=0; span_num < soup->len; span_num++)
	{
		fz_text_span *span = soup->spans[span_num];
		int new_line = 1;
		float distance = 0;
		float spacing = 0;
		soup->spans[span_num] = NULL;
		if (last_span)
		{
			/* If we have a last_span, we must have a last_line */
			/* Do span and last_line share the same baseline? */
			fz_point p, q, perp_r;
			float dot;
			float size = fz_matrix_expansion(&span->transform);

#ifdef DEBUG_SPANS
			{
				printf("Comparing: \"");
				dump_span(last_span);
				printf("\" and \"");
				dump_span(span);
				printf("\"\n");
			}
#endif

			p.x = last_line->first_span->max.x - last_line->first_span->min.x;
			p.y = last_line->first_span->max.y - last_line->first_span->min.y;
			fz_normalize_vector(&p);
			q.x = span->max.x - span->min.x;
			q.y = span->max.y - span->min.y;
			fz_normalize_vector(&q);
#ifdef DEBUG_SPANS
			printf("last_span=%g %g -> %g %g = %g %g\n", last_span->min.x, last_span->min.y, last_span->max.x, last_span->max.y, p.x, p.y);
			printf("span     =%g %g -> %g %g = %g %g\n", span->min.x, span->min.y, span->max.x, span->max.y, q.x, q.y);
#endif
			perp_r.y = last_line->first_span->min.x - span->min.x;
			perp_r.x = -(last_line->first_span->min.y - span->min.y);
			/* Check if p and q are parallel. If so, then this
			 * line is parallel with the last one. */
			dot = p.x * q.x + p.y * q.y;
			if (fabsf(dot) > 0.9995)
			{
				/* If we take the dot product of normalised(p) and
				 * perp(r), we get the perpendicular distance from
				 * one line to the next (assuming they are parallel). */
				distance = p.x * perp_r.x + p.y * perp_r.y;
				/* We allow 'small' distances of baseline changes
				 * to cope with super/subscript. FIXME: We should
				 * gather subscript/superscript information here. */
				new_line = (fabsf(distance) > size * LINE_DIST);
			}
			else
			{
				new_line = 1;
				distance = 0;
			}
			if (!new_line)
			{
				fz_point delta;

				delta.x = span->min.x - last_span->max.x;
				delta.y = span->min.y - last_span->max.y;

				spacing = (p.x * delta.x + p.y * delta.y);
				spacing = fabsf(spacing);
				/* Only allow changes in baseline (subscript/superscript etc)
				 * when the spacing is small. */
				if (spacing * fabsf(distance) > size * LINE_DIST && fabsf(distance) > size * 0.1f)
				{
					new_line = 1;
					distance = 0;
					spacing = 0;
				}
				else
				{
					spacing /= size * SPACE_DIST;
					/* Apply the same logic here as when we're adding chars to build spans. */
					if (spacing >= 1 && spacing < (SPACE_MAX_DIST/SPACE_DIST))
						spacing = 1;
				}
			}
#ifdef DEBUG_SPANS
			printf("dot=%g new_line=%d distance=%g size=%g spacing=%g\n", dot, new_line, distance, size, spacing);
#endif
		}
		span->spacing = spacing;
		last_line = push_span(ctx, tdev, span, new_line, distance);
		last_span = span;
	}
}
Exemplo n.º 23
0
static void
fz_draw_clip_path(fz_context *ctx, void *user, fz_path *path, fz_rect *rect, int even_odd, fz_matrix ctm)
{
	fz_draw_device *dev = user;
	fz_colorspace *model = dev->dest->colorspace;
	float expansion = fz_matrix_expansion(ctm);
	float flatness = 0.3f / expansion;
	fz_pixmap *mask, *dest, *shape;
	fz_bbox bbox;

	if (dev->top == dev->stack_max)
		fz_grow_stack(dev);

	fz_reset_gel(dev->gel, dev->scissor);
	fz_flatten_fill_path(ctx, dev->gel, path, ctm, flatness);
	fz_sort_gel(dev->gel);

	bbox = fz_bound_gel(dev->gel);
	bbox = fz_intersect_bbox(bbox, dev->scissor);
	if (rect)
		bbox = fz_intersect_bbox(bbox, fz_round_rect(*rect));

	if (fz_is_empty_rect(bbox) || fz_is_rect_gel(dev->gel))
	{
		dev->stack[dev->top].scissor = dev->scissor;
		dev->stack[dev->top].mask = NULL;
		dev->stack[dev->top].dest = NULL;
		dev->stack[dev->top].shape = dev->shape;
		dev->stack[dev->top].blendmode = dev->blendmode;
		dev->scissor = bbox;
#ifdef DUMP_GROUP_BLENDS
		dump_spaces(dev->top, "Clip (rectangular) begin\n");
#endif
		dev->top++;
		return;
	}

	mask = fz_new_pixmap_with_rect(ctx, NULL, bbox);
	fz_clear_pixmap(mask);
	dest = fz_new_pixmap_with_rect(ctx, model, bbox);
	/* FIXME: See note #1 */
	fz_clear_pixmap(dest);
	if (dev->shape)
	{
		shape = fz_new_pixmap_with_rect(ctx, NULL, bbox);
		fz_clear_pixmap(shape);
	}
	else
		shape = NULL;

	fz_scan_convert(dev->gel, even_odd, bbox, mask, NULL);

	dev->stack[dev->top].scissor = dev->scissor;
	dev->stack[dev->top].mask = mask;
	dev->stack[dev->top].dest = dev->dest;
	dev->stack[dev->top].shape = dev->shape;
	/* FIXME: See note #1 */
	dev->stack[dev->top].blendmode = dev->blendmode | FZ_BLEND_ISOLATED;
	dev->scissor = bbox;
	dev->dest = dest;
	dev->shape = shape;
#ifdef DUMP_GROUP_BLENDS
	dump_spaces(dev->top, "Clip (non-rectangular) begin\n");
#endif
	dev->top++;
}
Exemplo n.º 24
0
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));
	}
}
Exemplo n.º 25
0
void
fz_print_text_page_html(fz_context *ctx, fz_output *out, fz_text_page *page)
{
	int block_n, line_n, ch_n;
	fz_text_style *style = NULL;
	fz_text_line *line;
	fz_text_span *span;
	void *last_region = NULL;

	fz_printf(out, "<div class=\"page\">\n");

	for (block_n = 0; block_n < page->len; block_n++)
	{
		switch (page->blocks[block_n].type)
		{
		case FZ_PAGE_BLOCK_TEXT:
		{
			fz_text_block * block = page->blocks[block_n].u.text;
			fz_printf(out, "<div class=\"block\"><p>\n");
			for (line_n = 0; line_n < block->len; line_n++)
			{
				int lastcol=-1;
				line = &block->lines[line_n];
				style = NULL;

				if (line->region != last_region)
				{
					if (last_region)
						fz_printf(out, "</div>");
					fz_printf(out, "<div class=\"metaline\">");
					last_region = line->region;
				}
				fz_printf(out, "<div class=\"line\"");
#ifdef DEBUG_INTERNALS
				if (line->region)
					fz_printf(out, " region=\"%x\"", line->region);
#endif
				fz_printf(out, ">");
				for (span = line->first_span; span; span = span->next)
				{
					float size = fz_matrix_expansion(&span->transform);
					float base_offset = span->base_offset / size;

					if (lastcol != span->column)
					{
						if (lastcol >= 0)
						{
							fz_printf(out, "</div>");
						}
						/* If we skipped any columns then output some spacer spans */
						while (lastcol < span->column-1)
						{
							fz_printf(out, "<div class=\"cell\"></div>");
							lastcol++;
						}
						lastcol++;
						/* Now output the span to contain this entire column */
						fz_printf(out, "<div class=\"cell\" style=\"");
						{
							fz_text_span *sn;
							for (sn = span->next; sn; sn = sn->next)
							{
								if (sn->column != lastcol)
									break;
							}
							fz_printf(out, "width:%g%%;align:%s", span->column_width, (span->align == 0 ? "left" : (span->align == 1 ? "center" : "right")));
						}
						if (span->indent > 1)
							fz_printf(out, ";padding-left:1em;text-indent:-1em");
						if (span->indent < -1)
							fz_printf(out, ";text-indent:1em");
						fz_printf(out, "\">");
					}
#ifdef DEBUG_INTERNALS
					fz_printf(out, "<span class=\"internal_span\"");
					if (span->column)
						fz_printf(out, " col=\"%x\"", span->column);
					fz_printf(out, ">");
#endif
					if (span->spacing >= 1)
						fz_printf(out, " ");
					if (base_offset > SUBSCRIPT_OFFSET)
						fz_printf(out, "<sub>");
					else if (base_offset < SUPERSCRIPT_OFFSET)
						fz_printf(out, "<sup>");
					for (ch_n = 0; ch_n < span->len; ch_n++)
					{
						fz_text_char *ch = &span->text[ch_n];
						if (style != ch->style)
						{
							if (style)
								fz_print_style_end(out, style);
							fz_print_style_begin(out, ch->style);
							style = ch->style;
						}

						if (ch->c == '<')
							fz_printf(out, "&lt;");
						else if (ch->c == '>')
							fz_printf(out, "&gt;");
						else if (ch->c == '&')
							fz_printf(out, "&amp;");
						else if (ch->c >= 32 && ch->c <= 127)
							fz_printf(out, "%c", ch->c);
						else
							fz_printf(out, "&#x%x;", ch->c);
					}
					if (style)
					{
						fz_print_style_end(out, style);
						style = NULL;
					}
					if (base_offset > SUBSCRIPT_OFFSET)
						fz_printf(out, "</sub>");
					else if (base_offset < SUPERSCRIPT_OFFSET)
						fz_printf(out, "</sup>");
#ifdef DEBUG_INTERNALS
					fz_printf(out, "</span>");
#endif
				}
				/* Close our floating span */
				fz_printf(out, "</div>");
				/* Close the line */
				fz_printf(out, "</div>");
				fz_printf(out, "\n");
			}
			/* Close the metaline */
			fz_printf(out, "</div>");
			last_region = NULL;
			fz_printf(out, "</p></div>\n");
			break;
		}
		case FZ_PAGE_BLOCK_IMAGE:
		{
			fz_image_block *image = page->blocks[block_n].u.image;
			fz_printf(out, "<img width=%d height=%d src=\"data:", image->image->w, image->image->h);
			switch (image->image->buffer == NULL ? FZ_IMAGE_JPX : image->image->buffer->params.type)
			{
			case FZ_IMAGE_JPEG:
				fz_printf(out, "image/jpeg;base64,");
				send_data_base64(out, image->image->buffer->buffer);
				break;
			case FZ_IMAGE_PNG:
				fz_printf(out, "image/png;base64,");
				send_data_base64(out, image->image->buffer->buffer);
				break;
			default:
				{
					fz_buffer *buf = fz_image_as_png(ctx, image->image, image->image->w, image->image->h);
					fz_printf(out, "image/png;base64,");
					send_data_base64(out, buf);
					fz_drop_buffer(ctx, buf);
					break;
				}
			}
			fz_printf(out, "\">\n");
			break;
		}
		}
	}

	fz_printf(out, "</div>\n");
}