Subversion Repositories Kolibri OS

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
4680 right-hear 1
#include "fitz.h"
2
 
3
#define LINE_DIST 0.9f
4
#define SPACE_DIST 0.2f
5
 
6
#include 
7
#include FT_FREETYPE_H
8
#include FT_ADVANCES_H
9
 
10
typedef struct fz_text_device_s fz_text_device;
11
 
12
struct fz_text_device_s
13
{
14
	fz_point point;
15
	fz_text_span *head;
16
	fz_text_span *span;
17
};
18
 
19
fz_text_span *
20
fz_new_text_span(void)
21
{
22
	fz_text_span *span;
23
	span = fz_malloc(sizeof(fz_text_span));
24
	span->font = NULL;
25
	span->wmode = 0;
26
	span->size = 0;
27
	span->len = 0;
28
	span->cap = 0;
29
	span->text = NULL;
30
	span->next = NULL;
31
	span->eol = 0;
32
	return span;
33
}
34
 
35
void
36
fz_free_text_span(fz_text_span *span)
37
{
38
	if (span->font)
39
		fz_drop_font(span->font);
40
	if (span->next)
41
		fz_free_text_span(span->next);
42
	fz_free(span->text);
43
	fz_free(span);
44
}
45
 
46
static void
47
fz_add_text_char_imp(fz_text_span *span, int c, fz_bbox bbox)
48
{
49
	if (span->len + 1 >= span->cap)
50
	{
51
		span->cap = span->cap > 1 ? (span->cap * 3) / 2 : 80;
52
		span->text = fz_realloc(span->text, span->cap, sizeof(fz_text_char));
53
	}
54
	span->text[span->len].c = c;
55
	span->text[span->len].bbox = bbox;
56
	span->len ++;
57
}
58
 
59
static fz_bbox
60
fz_split_bbox(fz_bbox bbox, int i, int n)
61
{
62
	float w = (float)(bbox.x1 - bbox.x0) / n;
63
	float x0 = bbox.x0;
64
	bbox.x0 = x0 + i * w;
65
	bbox.x1 = x0 + (i + 1) * w;
66
	return bbox;
67
}
68
 
69
static void
70
fz_add_text_char(fz_text_span **last, fz_font *font, float size, int wmode, int c, fz_bbox bbox)
71
{
72
	fz_text_span *span = *last;
73
 
74
	if (!span->font)
75
	{
76
		span->font = fz_keep_font(font);
77
		span->size = size;
78
	}
79
 
80
	if ((span->font != font || span->size != size || span->wmode != wmode) && c != 32)
81
	{
82
		span = fz_new_text_span();
83
		span->font = fz_keep_font(font);
84
		span->size = size;
85
		span->wmode = wmode;
86
		(*last)->next = span;
87
		*last = span;
88
	}
89
 
90
	switch (c)
91
	{
92
	case -1: /* ignore when one unicode character maps to multiple glyphs */
93
		break;
94
	case 0xFB00: /* ff */
95
		fz_add_text_char_imp(span, 'f', fz_split_bbox(bbox, 0, 2));
96
		fz_add_text_char_imp(span, 'f', fz_split_bbox(bbox, 1, 2));
97
		break;
98
	case 0xFB01: /* fi */
99
		fz_add_text_char_imp(span, 'f', fz_split_bbox(bbox, 0, 2));
100
		fz_add_text_char_imp(span, 'i', fz_split_bbox(bbox, 1, 2));
101
		break;
102
	case 0xFB02: /* fl */
103
		fz_add_text_char_imp(span, 'f', fz_split_bbox(bbox, 0, 2));
104
		fz_add_text_char_imp(span, 'l', fz_split_bbox(bbox, 1, 2));
105
		break;
106
	case 0xFB03: /* ffi */
107
		fz_add_text_char_imp(span, 'f', fz_split_bbox(bbox, 0, 3));
108
		fz_add_text_char_imp(span, 'f', fz_split_bbox(bbox, 1, 3));
109
		fz_add_text_char_imp(span, 'i', fz_split_bbox(bbox, 2, 3));
110
		break;
111
	case 0xFB04: /* ffl */
112
		fz_add_text_char_imp(span, 'f', fz_split_bbox(bbox, 0, 3));
113
		fz_add_text_char_imp(span, 'f', fz_split_bbox(bbox, 1, 3));
114
		fz_add_text_char_imp(span, 'l', fz_split_bbox(bbox, 2, 3));
115
		break;
116
	case 0xFB05: /* long st */
117
	case 0xFB06: /* st */
118
		fz_add_text_char_imp(span, 's', fz_split_bbox(bbox, 0, 2));
119
		fz_add_text_char_imp(span, 't', fz_split_bbox(bbox, 1, 2));
120
		break;
121
	default:
122
		fz_add_text_char_imp(span, c, bbox);
123
		break;
124
	}
125
}
126
 
127
static void
128
fz_divide_text_chars(fz_text_span **last, int n, fz_bbox bbox)
129
{
130
	fz_text_span *span = *last;
131
	int i, x;
132
	x = span->len - n;
133
	if (x >= 0)
134
		for (i = 0; i < n; i++)
135
			span->text[x + i].bbox = fz_split_bbox(bbox, i, n);
136
}
137
 
138
static void
139
fz_add_text_newline(fz_text_span **last, fz_font *font, float size, int wmode)
140
{
141
	fz_text_span *span;
142
	span = fz_new_text_span();
143
	span->font = fz_keep_font(font);
144
	span->size = size;
145
	span->wmode = wmode;
146
	(*last)->eol = 1;
147
	(*last)->next = span;
148
	*last = span;
149
}
150
 
151
void
152
fz_debug_text_span_xml(fz_text_span *span)
153
{
154
	char buf[10];
155
	int c, n, k, i;
156
 
157
	printf("\n",
158
		span->font ? span->font->name : "NULL", span->size, span->wmode, span->eol);
159
 
160
	for (i = 0; i < span->len; i++)
161
	{
162
		printf("\t
163
		c = span->text[i].c;
164
		if (c < 128)
165
			putchar(c);
166
		else
167
		{
168
			n = runetochar(buf, &c);
169
			for (k = 0; k < n; k++)
170
				putchar(buf[k]);
171
		}
172
		printf("\" bbox=\"%d %d %d %d\" />\n",
173
			span->text[i].bbox.x0,
174
			span->text[i].bbox.y0,
175
			span->text[i].bbox.x1,
176
			span->text[i].bbox.y1);
177
	}
178
 
179
	printf("\n");
180
 
181
	if (span->next)
182
		fz_debug_text_span_xml(span->next);
183
}
184
 
185
void
186
fz_debug_text_span(fz_text_span *span)
187
{
188
	char buf[10];
189
	int c, n, k, i;
190
 
191
	for (i = 0; i < span->len; i++)
192
	{
193
		c = span->text[i].c;
194
		if (c < 128)
195
			putchar(c);
196
		else
197
		{
198
			n = runetochar(buf, &c);
199
			for (k = 0; k < n; k++)
200
				putchar(buf[k]);
201
		}
202
	}
203
 
204
	if (span->eol)
205
		putchar('\n');
206
 
207
	if (span->next)
208
		fz_debug_text_span(span->next);
209
}
210
 
211
static void
212
fz_text_extract_span(fz_text_span **last, fz_text *text, fz_matrix ctm, fz_point *pen)
213
{
214
	fz_font *font = text->font;
215
	FT_Face face = font->ft_face;
216
	fz_matrix tm = text->trm;
217
	fz_matrix trm;
218
	float size;
219
	float adv;
220
	fz_rect rect;
221
	fz_point dir, ndir;
222
	fz_point delta, ndelta;
223
	float dist, dot;
224
	float ascender = 1;
225
	float descender = 0;
226
	int multi;
227
	int i, err;
228
 
229
	if (text->len == 0)
230
		return;
231
 
232
	if (font->ft_face)
233
	{
234
		err = FT_Set_Char_Size(font->ft_face, 64, 64, 72, 72);
235
		if (err)
236
			fz_warn("freetype set character size: %s", ft_error_string(err));
237
		ascender = (float)face->ascender / face->units_per_EM;
238
		descender = (float)face->descender / face->units_per_EM;
239
	}
240
 
241
	rect = fz_empty_rect;
242
 
243
	if (text->wmode == 0)
244
	{
245
		dir.x = 1;
246
		dir.y = 0;
247
	}
248
	else
249
	{
250
		dir.x = 0;
251
		dir.y = 1;
252
	}
253
 
254
	tm.e = 0;
255
	tm.f = 0;
256
	trm = fz_concat(tm, ctm);
257
	dir = fz_transform_vector(trm, dir);
258
	dist = sqrtf(dir.x * dir.x + dir.y * dir.y);
259
	ndir.x = dir.x / dist;
260
	ndir.y = dir.y / dist;
261
 
262
	size = fz_matrix_expansion(trm);
263
 
264
	multi = 1;
265
 
266
	for (i = 0; i < text->len; i++)
267
	{
268
		if (text->items[i].gid < 0)
269
		{
270
			fz_add_text_char(last, font, size, text->wmode, text->items[i].ucs, fz_round_rect(rect));
271
			multi ++;
272
			fz_divide_text_chars(last, multi, fz_round_rect(rect));
273
			continue;
274
		}
275
		multi = 1;
276
 
277
		/* Calculate new pen location and delta */
278
		tm.e = text->items[i].x;
279
		tm.f = text->items[i].y;
280
		trm = fz_concat(tm, ctm);
281
 
282
		delta.x = pen->x - trm.e;
283
		delta.y = pen->y - trm.f;
284
		if (pen->x == -1 && pen->y == -1)
285
			delta.x = delta.y = 0;
286
 
287
		dist = sqrtf(delta.x * delta.x + delta.y * delta.y);
288
 
289
		/* Add space and newlines based on pen movement */
290
		if (dist > 0)
291
		{
292
			ndelta.x = delta.x / dist;
293
			ndelta.y = delta.y / dist;
294
			dot = ndelta.x * ndir.x + ndelta.y * ndir.y;
295
 
296
			if (dist > size * LINE_DIST)
297
			{
298
				fz_add_text_newline(last, font, size, text->wmode);
299
			}
300
			else if (fabsf(dot) > 0.95f && dist > size * SPACE_DIST)
301
			{
302
				if ((*last)->len > 0 && (*last)->text[(*last)->len - 1].c != ' ')
303
				{
304
					fz_rect spacerect;
305
					spacerect.x0 = -0.2f;
306
					spacerect.y0 = 0;
307
					spacerect.x1 = 0;
308
					spacerect.y1 = 1;
309
					spacerect = fz_transform_rect(trm, spacerect);
310
					fz_add_text_char(last, font, size, text->wmode, ' ', fz_round_rect(spacerect));
311
				}
312
			}
313
		}
314
 
315
		/* Calculate bounding box and new pen position based on font metrics */
316
		if (font->ft_face)
317
		{
318
			FT_Fixed ftadv = 0;
319
			int mask = FT_LOAD_NO_BITMAP | FT_LOAD_NO_HINTING | FT_LOAD_IGNORE_TRANSFORM;
320
 
321
			/* TODO: freetype returns broken vertical metrics */
322
			/* if (text->wmode) mask |= FT_LOAD_VERTICAL_LAYOUT; */
323
 
324
			FT_Get_Advance(font->ft_face, text->items[i].gid, mask, &ftadv);
325
			adv = ftadv / 65536.0f;
326
 
327
			rect.x0 = 0;
328
			rect.y0 = descender;
329
			rect.x1 = adv;
330
			rect.y1 = ascender;
331
		}
332
		else
333
		{
334
			adv = font->t3widths[text->items[i].gid];
335
			rect.x0 = 0;
336
			rect.y0 = descender;
337
			rect.x1 = adv;
338
			rect.y1 = ascender;
339
		}
340
 
341
		rect = fz_transform_rect(trm, rect);
342
		pen->x = trm.e + dir.x * adv;
343
		pen->y = trm.f + dir.y * adv;
344
 
345
		fz_add_text_char(last, font, size, text->wmode, text->items[i].ucs, fz_round_rect(rect));
346
	}
347
}
348
 
349
static void
350
fz_text_fill_text(void *user, fz_text *text, fz_matrix ctm,
351
	fz_colorspace *colorspace, float *color, float alpha)
352
{
353
	fz_text_device *tdev = user;
354
	fz_text_extract_span(&tdev->span, text, ctm, &tdev->point);
355
}
356
 
357
static void
358
fz_text_stroke_text(void *user, fz_text *text, fz_stroke_state *stroke, fz_matrix ctm,
359
	fz_colorspace *colorspace, float *color, float alpha)
360
{
361
	fz_text_device *tdev = user;
362
	fz_text_extract_span(&tdev->span, text, ctm, &tdev->point);
363
}
364
 
365
static void
366
fz_text_clip_text(void *user, fz_text *text, fz_matrix ctm, int accumulate)
367
{
368
	fz_text_device *tdev = user;
369
	fz_text_extract_span(&tdev->span, text, ctm, &tdev->point);
370
}
371
 
372
static void
373
fz_text_clip_stroke_text(void *user, fz_text *text, fz_stroke_state *stroke, fz_matrix ctm)
374
{
375
	fz_text_device *tdev = user;
376
	fz_text_extract_span(&tdev->span, text, ctm, &tdev->point);
377
}
378
 
379
static void
380
fz_text_ignore_text(void *user, fz_text *text, fz_matrix ctm)
381
{
382
	fz_text_device *tdev = user;
383
	fz_text_extract_span(&tdev->span, text, ctm, &tdev->point);
384
}
385
 
386
static void
387
fz_text_free_user(void *user)
388
{
389
	fz_text_device *tdev = user;
390
 
391
	tdev->span->eol = 1;
392
 
393
	/* TODO: unicode NFC normalization */
394
	/* TODO: bidi logical reordering */
395
 
396
	fz_free(tdev);
397
}
398
 
399
fz_device *
400
fz_new_text_device(fz_text_span *root)
401
{
402
	fz_device *dev;
403
	fz_text_device *tdev = fz_malloc(sizeof(fz_text_device));
404
	tdev->head = root;
405
	tdev->span = root;
406
	tdev->point.x = -1;
407
	tdev->point.y = -1;
408
 
409
	dev = fz_new_device(tdev);
410
	dev->hints = FZ_IGNORE_IMAGE | FZ_IGNORE_SHADE;
411
	dev->free_user = fz_text_free_user;
412
	dev->fill_text = fz_text_fill_text;
413
	dev->stroke_text = fz_text_stroke_text;
414
	dev->clip_text = fz_text_clip_text;
415
	dev->clip_stroke_text = fz_text_clip_stroke_text;
416
	dev->ignore_text = fz_text_ignore_text;
417
	return dev;
418
}