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 | }>>>>>>>>> |