some Changes updates
[imager.git] / FT2 / freetyp2.c
CommitLineData
faa9b3e7
TC
1/*
2=head1 NAME
3
4freetyp2.c - font support via the FreeType library version 2.
5
6=head1 SYNOPSIS
7
8 if (!i_ft2_init()) { error }
9 FT2_Fonthandle *font;
10 font = i_ft2_new(name, index);
11 if (!i_ft2_setdpi(font, xdpi, ydpi)) { error }
12 if (!i_ft2_getdpi(font, &xdpi, &ydpi)) { error }
13 double matrix[6];
14 if (!i_ft2_settransform(font, matrix)) { error }
8d14daab 15 i_img_dim bbox[BOUNDING_BOX_COUNT];
f62b2d84 16 if (!i_ft2_bbox(font, cheight, cwidth, text, length, bbox, utf8)) { error }
faa9b3e7
TC
17 i_img *im = ...;
18 i_color cl;
19 if (!i_ft2_text(font, im, tx, ty, cl, cheight, cwidth, text, length, align,
37e2e6fa 20 aa, vlayout, utf8)) { error }
faa9b3e7
TC
21 if (!i_ft2_cp(font, im, tx, ty, channel, cheight, cwidth, text, length,
22 align, aa)) { error }
23 i_ft2_destroy(font);
24
25=head1 DESCRIPTION
26
27Implements Imager font support using the FreeType2 library.
28
29The FreeType2 library understands several font file types, including
30Truetype, Type1 and Windows FNT.
31
32=over
33
34=cut
35*/
36
50c75381
TC
37#include "imext.h"
38#include "imft2.h"
faa9b3e7 39#include <stdio.h>
50c75381 40#include <math.h>
17dd918e 41#include <string.h>
faa9b3e7
TC
42#include <ft2build.h>
43#include FT_FREETYPE_H
3e882362
TC
44#ifdef FT_MULTIPLE_MASTERS_H
45#ifndef T1_CONFIG_OPTION_NO_MM_SUPPORT
46#define IM_FT2_MM
47#include FT_MULTIPLE_MASTERS_H
48#endif
49#endif
faa9b3e7
TC
50
51static void ft2_push_message(int code);
faa9b3e7 52
98d6ae27
TC
53static void ft2_final(void *);
54
55static im_slot_t slot = -1;
56
57typedef struct {
58 int initialized;
59 FT_Library library;
60 im_context_t ctx;
61} ft2_state;
62
63static ft2_state *
64i_ft2_init(void);
faa9b3e7 65
8d14daab
TC
66static i_img_dim i_min(i_img_dim a, i_img_dim b);
67static i_img_dim i_max(i_img_dim a, i_img_dim b);
50c75381 68
17dd918e
TC
69int
70i_ft2_version(int runtime, char *buf, size_t buf_size) {
71 char work[100];
17dd918e 72
98d6ae27 73 i_clear_error();
17dd918e
TC
74
75 if (buf_size == 0) {
76 i_push_error(0, "zero size buffer supplied");
77 return 0;
78 }
79 if (runtime) {
98d6ae27 80 ft2_state *ft2;
17dd918e
TC
81 /* initialized to work around a bug in FT2
82 http://lists.nongnu.org/archive/html/freetype-devel/2002-09/msg00058.html
83 Though I don't know why I still see this in 2.4.2
84 */
85 FT_Int major = 1, minor = 1, patch = 1;
98d6ae27
TC
86
87 if ((ft2 = i_ft2_init()) == NULL)
88 return 0;
89
90 FT_Library_Version(ft2->library, &major, &minor, &patch);
17dd918e
TC
91 sprintf(work, "%d.%d.%d", (int)major, (int)minor, (int)patch);
92 }
93 else {
94 sprintf(work, "%d.%d.%d", FREETYPE_MAJOR, FREETYPE_MINOR, FREETYPE_PATCH);
95 }
96 strncpy(buf, work, buf_size);
97 buf[buf_size-1] = '\0';
98
99 return 1;
100}
101
98d6ae27
TC
102void
103i_ft2_start(void) {
104 if (slot == -1)
105 slot = im_context_slot_new(ft2_final);
106}
107
faa9b3e7
TC
108/*
109=item i_ft2_init(void)
110
111Initializes the Freetype 2 library.
112
98d6ae27 113Returns ft2_state * on success or NULL on failure.
faa9b3e7
TC
114
115=cut
116*/
98d6ae27
TC
117
118static ft2_state *
faa9b3e7
TC
119i_ft2_init(void) {
120 FT_Error error;
98d6ae27
TC
121 im_context_t ctx = im_get_context();
122 ft2_state *ft2 = im_context_slot_get(ctx, slot);
123
124 if (ft2 == NULL) {
125 ft2 = mymalloc(sizeof(ft2_state));
126 ft2->initialized = 0;
127 ft2->library = NULL;
128 ft2->ctx = ctx;
129 im_context_slot_set(ctx, slot, ft2);
130 mm_log((1, "created FT2 state %p for context %p\n", ft2, ctx));
131 }
faa9b3e7
TC
132
133 i_clear_error();
98d6ae27
TC
134 if (!ft2->initialized) {
135 error = FT_Init_FreeType(&ft2->library);
136 if (error) {
137 ft2_push_message(error);
138 i_push_error(0, "Initializing Freetype2");
139 return NULL;
140 }
141 mm_log((1, "initialized FT2 state %p\n", ft2));
142
143 ft2->initialized = 1;
faa9b3e7 144 }
d1555273 145
98d6ae27
TC
146 return ft2;
147}
d1555273 148
98d6ae27
TC
149static void
150ft2_final(void *state) {
151 ft2_state *ft2 = state;
152
153 if (ft2->initialized) {
154 mm_log((1, "finalizing FT2 state %p\n", state));
155 FT_Done_FreeType(ft2->library);
156 ft2->library = NULL;
157 ft2->initialized = 0;
158 }
159
160 mm_log((1, "freeing FT2 state %p\n", state));
161 myfree(state);
faa9b3e7
TC
162}
163
164struct FT2_Fonthandle {
165 FT_Face face;
98d6ae27 166 ft2_state *state;
faa9b3e7
TC
167 int xdpi, ydpi;
168 int hint;
db6d10cc 169 FT_Encoding encoding;
faa9b3e7
TC
170
171 /* used to adjust so we can align the draw point to the top-left */
172 double matrix[6];
3e882362
TC
173
174#ifdef IM_FT2_MM
175 /* Multiple master data if any */
176 int has_mm;
177 FT_Multi_Master mm;
178#endif
faa9b3e7
TC
179};
180
db6d10cc
TC
181/* the following is used to select a "best" encoding */
182static struct enc_score {
183 FT_Encoding encoding;
184 int score;
185} enc_scores[] =
186{
187 /* the selections here are fairly arbitrary
188 ideally we need to give the user a list of encodings available
189 and a mechanism to choose one */
190 { ft_encoding_unicode, 10 },
191 { ft_encoding_sjis, 8 },
192 { ft_encoding_gb2312, 8 },
193 { ft_encoding_big5, 8 },
194 { ft_encoding_wansung, 8 },
195 { ft_encoding_johab, 8 },
196 { ft_encoding_latin_2, 6 },
197 { ft_encoding_apple_roman, 6 },
198 { ft_encoding_adobe_standard, 6 },
199 { ft_encoding_adobe_expert, 6 },
200};
201
faa9b3e7
TC
202/*
203=item i_ft2_new(char *name, int index)
204
205Creates a new font object, from the file given by I<name>. I<index>
206is the index of the font in a file with multiple fonts, where 0 is the
207first font.
208
209Return NULL on failure.
210
211=cut
212*/
213
214FT2_Fonthandle *
97ac0a96 215i_ft2_new(const char *name, int index) {
faa9b3e7
TC
216 FT_Error error;
217 FT2_Fonthandle *result;
218 FT_Face face;
db6d10cc
TC
219 int i, j;
220 FT_Encoding encoding;
221 int score;
98d6ae27 222 ft2_state *ft2;
faa9b3e7 223
a73aeb5f
AMH
224 mm_log((1, "i_ft2_new(name %p, index %d)\n", name, index));
225
98d6ae27 226 if ((ft2 = i_ft2_init()) == NULL)
d1555273
TC
227 return NULL;
228
faa9b3e7 229 i_clear_error();
98d6ae27 230 error = FT_New_Face(ft2->library, name, index, &face);
faa9b3e7
TC
231 if (error) {
232 ft2_push_message(error);
233 i_push_error(error, "Opening face");
ea9e6c3f 234 mm_log((2, "error opening face '%s': %d\n", name, error));
faa9b3e7
TC
235 return NULL;
236 }
237
db6d10cc
TC
238 encoding = face->num_charmaps ? face->charmaps[0]->encoding : ft_encoding_unicode;
239 score = 0;
240 for (i = 0; i < face->num_charmaps; ++i) {
241 FT_Encoding enc_entry = face->charmaps[i]->encoding;
5e84d110
TC
242 mm_log((2, "i_ft2_new, encoding %X platform %u encoding %u\n",
243 (unsigned)enc_entry, face->charmaps[i]->platform_id,
db6d10cc
TC
244 face->charmaps[i]->encoding_id));
245 for (j = 0; j < sizeof(enc_scores) / sizeof(*enc_scores); ++j) {
246 if (enc_scores[j].encoding == enc_entry && enc_scores[j].score > score) {
247 encoding = enc_entry;
248 score = enc_scores[j].score;
249 break;
250 }
251 }
252 }
253 FT_Select_Charmap(face, encoding);
5e84d110 254 mm_log((2, "i_ft2_new, selected encoding %X\n", (unsigned)encoding));
db6d10cc 255
faa9b3e7
TC
256 result = mymalloc(sizeof(FT2_Fonthandle));
257 result->face = face;
98d6ae27 258 result->state = ft2;
faa9b3e7 259 result->xdpi = result->ydpi = 72;
db6d10cc 260 result->encoding = encoding;
faa9b3e7
TC
261
262 /* by default we disable hinting on a call to i_ft2_settransform()
263 if we don't do this, then the hinting can the untransformed text
264 to be a different size to the transformed text.
265 Obviously we have it initially enabled.
266 */
267 result->hint = 1;
268
269 /* I originally forgot this: :/ */
270 /*i_ft2_settransform(result, matrix); */
271 result->matrix[0] = 1; result->matrix[1] = 0; result->matrix[2] = 0;
272 result->matrix[3] = 0; result->matrix[4] = 1; result->matrix[5] = 0;
273
3e882362
TC
274#ifdef IM_FT2_MM
275 {
276 FT_Multi_Master *mm = &result->mm;
277 int i;
278
279 if ((face->face_flags & FT_FACE_FLAG_MULTIPLE_MASTERS) != 0
280 && (error = FT_Get_Multi_Master(face, mm)) == 0) {
281 mm_log((2, "MM Font, %d axes, %d designs\n", mm->num_axis, mm->num_designs));
282 for (i = 0; i < mm->num_axis; ++i) {
283 mm_log((2, " axis %d name %s range %ld - %ld\n", i, mm->axis[i].name,
284 (long)(mm->axis[i].minimum), (long)(mm->axis[i].maximum)));
285 }
286 result->has_mm = 1;
287 }
288 else {
289 mm_log((2, "No multiple masters\n"));
290 result->has_mm = 0;
291 }
292 }
293#endif
294
faa9b3e7
TC
295 return result;
296}
297
298/*
299=item i_ft2_destroy(FT2_Fonthandle *handle)
300
301Destroys a font object, which must have been the return value of
302i_ft2_new().
303
304=cut
305*/
306void
307i_ft2_destroy(FT2_Fonthandle *handle) {
308 FT_Done_Face(handle->face);
309 myfree(handle);
310}
311
312/*
313=item i_ft2_setdpi(FT2_Fonthandle *handle, int xdpi, int ydpi)
314
315Sets the resolution in dots per inch at which point sizes scaled, by
316default xdpi and ydpi are 72, so that 1 point maps to 1 pixel.
317
318Both xdpi and ydpi should be positive.
319
320Return true on success.
321
322=cut
323*/
324int
325i_ft2_setdpi(FT2_Fonthandle *handle, int xdpi, int ydpi) {
326 i_clear_error();
327 if (xdpi > 0 && ydpi > 0) {
328 handle->xdpi = xdpi;
329 handle->ydpi = ydpi;
98d6ae27 330 return 1;
faa9b3e7
TC
331 }
332 else {
333 i_push_error(0, "resolutions must be positive");
334 return 0;
335 }
336}
337
338/*
339=item i_ft2_getdpi(FT2_Fonthandle *handle, int *xdpi, int *ydpi)
340
341Retrieves the current horizontal and vertical resolutions at which
342point sizes are scaled.
343
344=cut
345*/
346int
347i_ft2_getdpi(FT2_Fonthandle *handle, int *xdpi, int *ydpi) {
348 *xdpi = handle->xdpi;
349 *ydpi = handle->ydpi;
350
351 return 1;
352}
353
354/*
355=item i_ft2_settransform(FT2_FontHandle *handle, double *matrix)
356
357Sets a transormation matrix for output.
358
359This should be a 2 x 3 matrix like:
360
361 matrix[0] matrix[1] matrix[2]
362 matrix[3] matrix[4] matrix[5]
363
364=cut
365*/
366int
97ac0a96 367i_ft2_settransform(FT2_Fonthandle *handle, const double *matrix) {
faa9b3e7
TC
368 FT_Matrix m;
369 FT_Vector v;
370 int i;
371
372 m.xx = matrix[0] * 65536;
373 m.xy = matrix[1] * 65536;
374 v.x = matrix[2]; /* this could be pels of 26.6 fixed - not sure */
375 m.yx = matrix[3] * 65536;
376 m.yy = matrix[4] * 65536;
377 v.y = matrix[5]; /* see just above */
378
379 FT_Set_Transform(handle->face, &m, &v);
380
381 for (i = 0; i < 6; ++i)
382 handle->matrix[i] = matrix[i];
383 handle->hint = 0;
384
385 return 1;
386}
387
388/*
389=item i_ft2_sethinting(FT2_Fonthandle *handle, int hinting)
390
391If hinting is non-zero then glyph hinting is enabled, otherwise disabled.
392
393i_ft2_settransform() disables hinting to prevent distortions in
394gradual text transformations.
395
396=cut
397*/
398int i_ft2_sethinting(FT2_Fonthandle *handle, int hinting) {
399 handle->hint = hinting;
400 return 1;
401}
402
403/*
8d14daab 404=item i_ft2_bbox(FT2_Fonthandle *handle, double cheight, double cwidth, char *text, size_t len, i_img_dim *bbox)
faa9b3e7
TC
405
406Retrieves bounding box information for the font at the given
407character width and height. This ignores the transformation matrix.
408
409Returns non-zero on success.
410
411=cut
412*/
413int
414i_ft2_bbox(FT2_Fonthandle *handle, double cheight, double cwidth,
8d14daab 415 char const *text, size_t len, i_img_dim *bbox, int utf8) {
faa9b3e7 416 FT_Error error;
8d14daab 417 i_img_dim width;
faa9b3e7
TC
418 int index;
419 int first;
420 int ascent = 0, descent = 0;
421 int glyph_ascent, glyph_descent;
422 FT_Glyph_Metrics *gm;
423 int start = 0;
3799c4d1 424 int loadFlags = FT_LOAD_DEFAULT;
e4bf9335 425 int rightb = 0;
faa9b3e7 426
d6f02a59
TC
427 i_clear_error();
428
5e84d110
TC
429 mm_log((1, "i_ft2_bbox(handle %p, cheight %f, cwidth %f, text %p, len %u, bbox %p)\n",
430 handle, cheight, cwidth, text, (unsigned)len, bbox));
a73aeb5f 431
faa9b3e7
TC
432 error = FT_Set_Char_Size(handle->face, cwidth*64, cheight*64,
433 handle->xdpi, handle->ydpi);
434 if (error) {
435 ft2_push_message(error);
436 i_push_error(0, "setting size");
437 }
438
3799c4d1
TC
439 if (!handle->hint)
440 loadFlags |= FT_LOAD_NO_HINTING;
441
faa9b3e7
TC
442 first = 1;
443 width = 0;
5cb9270b
TC
444 while (len) {
445 unsigned long c;
446 if (utf8) {
4f68b48f 447 c = i_utf8_advance(&text, &len);
5cb9270b
TC
448 if (c == ~0UL) {
449 i_push_error(0, "invalid UTF8 character");
450 return 0;
451 }
452 }
453 else {
454 c = (unsigned char)*text++;
455 --len;
456 }
457
faa9b3e7 458 index = FT_Get_Char_Index(handle->face, c);
3799c4d1 459 error = FT_Load_Glyph(handle->face, index, loadFlags);
faa9b3e7
TC
460 if (error) {
461 ft2_push_message(error);
5e84d110 462 i_push_errorf(0, "loading glyph for character \\x%02lx (glyph 0x%04X)",
faa9b3e7
TC
463 c, index);
464 return 0;
465 }
466 gm = &handle->face->glyph->metrics;
467 glyph_ascent = gm->horiBearingY / 64;
468 glyph_descent = glyph_ascent - gm->height/64;
469 if (first) {
470 start = gm->horiBearingX / 64;
471 /* handles -ve values properly */
472 ascent = glyph_ascent;
473 descent = glyph_descent;
474 first = 0;
475 }
476
477 if (glyph_ascent > ascent)
478 ascent = glyph_ascent;
b72644d0 479 if (glyph_descent < descent)
faa9b3e7
TC
480 descent = glyph_descent;
481
482 width += gm->horiAdvance / 64;
483
484 if (len == 0) {
485 /* last character
486 handle the case where the right the of the character overlaps the
487 right*/
95171801 488 rightb = (gm->horiAdvance - gm->horiBearingX - gm->width)/64;
dc35bde9
TC
489 /*if (rightb > 0)
490 rightb = 0;*/
faa9b3e7
TC
491 }
492 }
493
3799c4d1
TC
494 bbox[BBOX_NEG_WIDTH] = start;
495 bbox[BBOX_GLOBAL_DESCENT] = handle->face->size->metrics.descender / 64;
dc35bde9
TC
496 bbox[BBOX_POS_WIDTH] = width;
497 if (rightb < 0)
498 bbox[BBOX_POS_WIDTH] -= rightb;
3799c4d1
TC
499 bbox[BBOX_GLOBAL_ASCENT] = handle->face->size->metrics.ascender / 64;
500 bbox[BBOX_DESCENT] = descent;
501 bbox[BBOX_ASCENT] = ascent;
502 bbox[BBOX_ADVANCE_WIDTH] = width;
dc35bde9 503 bbox[BBOX_RIGHT_BEARING] = rightb;
5e84d110
TC
504 mm_log((1, " bbox=> negw=%" i_DF " glob_desc=%" i_DF " pos_wid=%" i_DF
505 " glob_asc=%" i_DF " desc=%" i_DF " asc=%" i_DF " adv_width=%" i_DF
506 " rightb=%" i_DF "\n",
507 bbox[0], bbox[1], bbox[2], bbox[3], bbox[4], bbox[5], bbox[6],
508 bbox[7]));
faa9b3e7 509
dc35bde9 510 return BBOX_RIGHT_BEARING + 1;
faa9b3e7
TC
511}
512
513/*
514=item transform_box(FT2_FontHandle *handle, int bbox[4])
515
516bbox contains coorinates of a the top-left and bottom-right of a bounding
517box relative to a point.
518
519This is then transformed and the values in bbox[4] are the top-left
520and bottom-right of the new bounding box.
521
522This is meant to provide the bounding box of a transformed character
523box. The problem is that if the character was round and is rotated,
524the real bounding box isn't going to be much different from the
525original, but this function will return a _bigger_ bounding box. I
526suppose I could work my way through the glyph outline, but that's
527too much hard work.
528
529=cut
530*/
8d14daab 531void ft2_transform_box(FT2_Fonthandle *handle, i_img_dim bbox[4]) {
faa9b3e7
TC
532 double work[8];
533 double *matrix = handle->matrix;
faa9b3e7
TC
534
535 work[0] = matrix[0] * bbox[0] + matrix[1] * bbox[1];
536 work[1] = matrix[3] * bbox[0] + matrix[4] * bbox[1];
537 work[2] = matrix[0] * bbox[2] + matrix[1] * bbox[1];
538 work[3] = matrix[3] * bbox[2] + matrix[4] * bbox[1];
539 work[4] = matrix[0] * bbox[0] + matrix[1] * bbox[3];
540 work[5] = matrix[3] * bbox[0] + matrix[4] * bbox[3];
541 work[6] = matrix[0] * bbox[2] + matrix[1] * bbox[3];
542 work[7] = matrix[3] * bbox[2] + matrix[4] * bbox[3];
543
b33c08f8
TC
544 bbox[0] = floor(i_min(i_min(work[0], work[2]),i_min(work[4], work[6])));
545 bbox[1] = floor(i_min(i_min(work[1], work[3]),i_min(work[5], work[7])));
546 bbox[2] = ceil(i_max(i_max(work[0], work[2]),i_max(work[4], work[6])));
547 bbox[3] = ceil(i_max(i_max(work[1], work[3]),i_max(work[5], work[7])));
faa9b3e7
TC
548}
549
550/*
551=item expand_bounds(int bbox[4], int bbox2[4])
552
553Treating bbox[] and bbox2[] as 2 bounding boxes, produces a new
554bounding box in bbox[] that encloses both.
555
556=cut
557*/
8d14daab 558static void expand_bounds(i_img_dim bbox[4], i_img_dim bbox2[4]) {
b33c08f8
TC
559 bbox[0] = i_min(bbox[0], bbox2[0]);
560 bbox[1] = i_min(bbox[1], bbox2[1]);
561 bbox[2] = i_max(bbox[2], bbox2[2]);
562 bbox[3] = i_max(bbox[3], bbox2[3]);
faa9b3e7
TC
563}
564
565/*
8d14daab 566=item i_ft2_bbox_r(FT2_Fonthandle *handle, double cheight, double cwidth, char *text, size_t len, int vlayout, int utf8, i_img_dim *bbox)
faa9b3e7
TC
567
568Retrieves bounding box information for the font at the given
569character width and height.
570
571This version finds the rectangular bounding box of the glyphs, with
572the text as transformed by the transformation matrix. As with
573i_ft2_bbox (bbox[0], bbox[1]) will the the offset from the start of
574the topline to the top-left of the bounding box. Unlike i_ft2_bbox()
575this could be near the bottom left corner of the box.
576
577(bbox[4], bbox[5]) is the offset to the start of the baseline.
578(bbox[6], bbox[7]) is the offset from the start of the baseline to the
579end of the baseline.
580
581Returns non-zero on success.
582
583=cut
584*/
585int
586i_ft2_bbox_r(FT2_Fonthandle *handle, double cheight, double cwidth,
8d14daab 587 char const *text, size_t len, int vlayout, int utf8, i_img_dim *bbox) {
faa9b3e7 588 FT_Error error;
8d14daab 589 i_img_dim width;
faa9b3e7
TC
590 int index;
591 int first;
8d14daab 592 i_img_dim ascent = 0, descent = 0;
faa9b3e7
TC
593 int glyph_ascent, glyph_descent;
594 FT_Glyph_Metrics *gm;
8d14daab 595 i_img_dim work[4];
ccfd0ddf 596 i_img_dim bounds[4] = { 0 };
faa9b3e7
TC
597 double x = 0, y = 0;
598 int i;
599 FT_GlyphSlot slot;
faa9b3e7
TC
600 int loadFlags = FT_LOAD_DEFAULT;
601
602 if (vlayout)
603 loadFlags |= FT_LOAD_VERTICAL_LAYOUT;
3799c4d1
TC
604 if (!handle->hint)
605 loadFlags |= FT_LOAD_NO_HINTING;
faa9b3e7
TC
606
607 error = FT_Set_Char_Size(handle->face, cwidth*64, cheight*64,
608 handle->xdpi, handle->ydpi);
609 if (error) {
610 ft2_push_message(error);
611 i_push_error(0, "setting size");
612 }
613
614 first = 1;
615 width = 0;
616 while (len) {
617 unsigned long c;
618 if (utf8) {
4f68b48f 619 c = i_utf8_advance(&text, &len);
faa9b3e7
TC
620 if (c == ~0UL) {
621 i_push_error(0, "invalid UTF8 character");
622 return 0;
623 }
624 }
625 else {
626 c = (unsigned char)*text++;
627 --len;
628 }
629
630 index = FT_Get_Char_Index(handle->face, c);
631 error = FT_Load_Glyph(handle->face, index, loadFlags);
632 if (error) {
633 ft2_push_message(error);
5e84d110 634 i_push_errorf(0, "loading glyph for character \\x%02lx (glyph 0x%04X)",
faa9b3e7
TC
635 c, index);
636 return 0;
637 }
638 slot = handle->face->glyph;
639 gm = &slot->metrics;
640
641 /* these probably don't mean much for vertical layouts */
642 glyph_ascent = gm->horiBearingY / 64;
643 glyph_descent = glyph_ascent - gm->height/64;
644 if (vlayout) {
645 work[0] = gm->vertBearingX;
646 work[1] = gm->vertBearingY;
647 }
648 else {
649 work[0] = gm->horiBearingX;
650 work[1] = gm->horiBearingY;
651 }
652 work[2] = gm->width + work[0];
653 work[3] = work[1] - gm->height;
654 if (first) {
655 bbox[4] = work[0] * handle->matrix[0] + work[1] * handle->matrix[1] + handle->matrix[2];
656 bbox[5] = work[0] * handle->matrix[3] + work[1] * handle->matrix[4] + handle->matrix[5];
657 bbox[4] = bbox[4] < 0 ? -(-bbox[4] + 32)/64 : (bbox[4] + 32) / 64;
658 bbox[5] /= 64;
659 }
660 ft2_transform_box(handle, work);
661 for (i = 0; i < 4; ++i)
662 work[i] /= 64;
663 work[0] += x;
664 work[1] += y;
665 work[2] += x;
666 work[3] += y;
667 if (first) {
668 for (i = 0; i < 4; ++i)
669 bounds[i] = work[i];
670 ascent = glyph_ascent;
671 descent = glyph_descent;
672 first = 0;
673 }
674 else {
675 expand_bounds(bounds, work);
676 }
677 x += slot->advance.x / 64;
678 y += slot->advance.y / 64;
3799c4d1 679
faa9b3e7
TC
680 if (glyph_ascent > ascent)
681 ascent = glyph_ascent;
682 if (glyph_descent > descent)
683 descent = glyph_descent;
684
685 if (len == 0) {
686 /* last character
687 handle the case where the right the of the character overlaps the
688 right*/
689 /*int rightb = gm->horiAdvance - gm->horiBearingX - gm->width;
690 if (rightb < 0)
691 width -= rightb / 64;*/
692 }
693 }
694
695 /* at this point bounds contains the bounds relative to the CP,
696 and x, y hold the final position relative to the CP */
697 /*bounds[0] -= x;
698 bounds[1] -= y;
699 bounds[2] -= x;
700 bounds[3] -= y;*/
701
702 bbox[0] = bounds[0];
703 bbox[1] = -bounds[3];
704 bbox[2] = bounds[2];
705 bbox[3] = -bounds[1];
706 bbox[6] = x;
707 bbox[7] = -y;
708
709 return 1;
710}
711
faa9b3e7
TC
712static int
713make_bmp_map(FT_Bitmap *bitmap, unsigned char *map);
714
715/*
718b8c97 716=item i_ft2_text(FT2_Fonthandle *handle, i_img *im, int tx, int ty, i_color *cl, double cheight, double cwidth, char *text, size_t len, int align, int aa)
faa9b3e7
TC
717
718Renders I<text> to (I<tx>, I<ty>) in I<im> using color I<cl> at the given
719I<cheight> and I<cwidth>.
720
721If align is 0, then the text is rendered with the top-left of the
722first character at (I<tx>, I<ty>). If align is non-zero then the text
723is rendered with (I<tx>, I<ty>) aligned with the base-line of the
724characters.
725
726If aa is non-zero then the text is anti-aliased.
727
728Returns non-zero on success.
729
730=cut
731*/
732int
8d14daab 733i_ft2_text(FT2_Fonthandle *handle, i_img *im, i_img_dim tx, i_img_dim ty, const i_color *cl,
718b8c97
TC
734 double cheight, double cwidth, char const *text, size_t len,
735 int align, int aa, int vlayout, int utf8) {
faa9b3e7
TC
736 FT_Error error;
737 int index;
738 FT_Glyph_Metrics *gm;
8d14daab 739 i_img_dim bbox[BOUNDING_BOX_COUNT];
faa9b3e7
TC
740 FT_GlyphSlot slot;
741 int x, y;
742 unsigned char *bmp;
743 unsigned char map[256];
744 char last_mode = ft_pixel_mode_none;
745 int last_grays = -1;
faa9b3e7 746 int loadFlags = FT_LOAD_DEFAULT;
ccfd0ddf 747 i_render *render = NULL;
faa9b3e7 748
5e84d110
TC
749 mm_log((1, "i_ft2_text(handle %p, im %p, (tx,ty) (" i_DFp "), cl %p, cheight %f, cwidth %f, text %p, len %u, align %d, aa %d, vlayout %d, utf8 %d)\n",
750 handle, im, i_DFcp(tx, ty), cl, cheight, cwidth, text, (unsigned)len, align, aa, vlayout, utf8));
a73aeb5f 751
d6f02a59
TC
752 i_clear_error();
753
faa9b3e7
TC
754 if (vlayout) {
755 if (!FT_HAS_VERTICAL(handle->face)) {
756 i_push_error(0, "face has no vertical metrics");
757 return 0;
758 }
759 loadFlags |= FT_LOAD_VERTICAL_LAYOUT;
760 }
761 if (!handle->hint)
762 loadFlags |= FT_LOAD_NO_HINTING;
763
764 /* set the base-line based on the string ascent */
5cb9270b 765 if (!i_ft2_bbox(handle, cheight, cwidth, text, len, bbox, utf8))
faa9b3e7
TC
766 return 0;
767
9c106321 768 if (aa)
50c75381 769 render = i_render_new(im, bbox[BBOX_POS_WIDTH] - bbox[BBOX_NEG_WIDTH]);
9c106321 770
faa9b3e7
TC
771 if (!align) {
772 /* this may need adjustment */
773 tx -= bbox[0] * handle->matrix[0] + bbox[5] * handle->matrix[1] + handle->matrix[2];
774 ty += bbox[0] * handle->matrix[3] + bbox[5] * handle->matrix[4] + handle->matrix[5];
775 }
776 while (len) {
777 unsigned long c;
778 if (utf8) {
4f68b48f 779 c = i_utf8_advance(&text, &len);
faa9b3e7
TC
780 if (c == ~0UL) {
781 i_push_error(0, "invalid UTF8 character");
782 return 0;
783 }
784 }
785 else {
786 c = (unsigned char)*text++;
787 --len;
788 }
789
790 index = FT_Get_Char_Index(handle->face, c);
791 error = FT_Load_Glyph(handle->face, index, loadFlags);
792 if (error) {
793 ft2_push_message(error);
5e84d110 794 i_push_errorf(0, "loading glyph for character \\x%02lx (glyph 0x%04X)",
faa9b3e7 795 c, index);
ccfd0ddf 796 if (render)
50c75381 797 i_render_delete(render);
faa9b3e7
TC
798 return 0;
799 }
800 slot = handle->face->glyph;
801 gm = &slot->metrics;
802
e15cea68
TC
803 if (gm->width) {
804 error = FT_Render_Glyph(slot, aa ? ft_render_mode_normal : ft_render_mode_mono);
805 if (error) {
806 ft2_push_message(error);
5e84d110 807 i_push_errorf(0, "rendering glyph 0x%04lX (character \\x%02X)", c, index);
ccfd0ddf 808 if (render)
50c75381 809 i_render_delete(render);
e15cea68 810 return 0;
faa9b3e7 811 }
e15cea68
TC
812 if (slot->bitmap.pixel_mode == ft_pixel_mode_mono) {
813 bmp = slot->bitmap.buffer;
814 for (y = 0; y < slot->bitmap.rows; ++y) {
815 int pos = 0;
816 int bit = 0x80;
817 for (x = 0; x < slot->bitmap.width; ++x) {
818 if (bmp[pos] & bit)
819 i_ppix(im, tx+x+slot->bitmap_left, ty+y-slot->bitmap_top, cl);
820
821 bit >>= 1;
822 if (bit == 0) {
823 bit = 0x80;
824 ++pos;
825 }
826 }
827 bmp += slot->bitmap.pitch;
828 }
faa9b3e7 829 }
e15cea68
TC
830 else {
831 /* grey scale or something we can treat as greyscale */
832 /* we create a map to convert from the bitmap values to 0-255 */
833 if (last_mode != slot->bitmap.pixel_mode
834 || last_grays != slot->bitmap.num_grays) {
835 if (!make_bmp_map(&slot->bitmap, map))
836 return 0;
837 last_mode = slot->bitmap.pixel_mode;
838 last_grays = slot->bitmap.num_grays;
839 }
9c106321 840
e15cea68
TC
841 bmp = slot->bitmap.buffer;
842 for (y = 0; y < slot->bitmap.rows; ++y) {
9c106321
TC
843 if (last_mode == ft_pixel_mode_grays &&
844 last_grays != 255) {
845 for (x = 0; x < slot->bitmap.width; ++x)
846 bmp[x] = map[bmp[x]];
847 }
50c75381 848 i_render_color(render, tx + slot->bitmap_left, ty-slot->bitmap_top+y,
9c106321 849 slot->bitmap.width, bmp, cl);
e15cea68
TC
850 bmp += slot->bitmap.pitch;
851 }
faa9b3e7
TC
852 }
853 }
854
855 tx += slot->advance.x / 64;
856 ty -= slot->advance.y / 64;
857 }
858
ccfd0ddf 859 if (render)
50c75381 860 i_render_delete(render);
9c106321 861
faa9b3e7
TC
862 return 1;
863}
864
865/*
5e84d110 866=item i_ft2_cp(FT2_Fonthandle *handle, i_img *im, int tx, int ty, int channel, double cheight, double cwidth, char *text, size_t len, int align, int aa, int vlayout, int utf8)
faa9b3e7
TC
867
868Renders I<text> to (I<tx>, I<ty>) in I<im> to I<channel> at the given
869I<cheight> and I<cwidth>.
870
871If align is 0, then the text is rendered with the top-left of the
872first character at (I<tx>, I<ty>). If align is non-zero then the text
873is rendered with (I<tx>, I<ty>) aligned with the base-line of the
874characters.
875
5e84d110
TC
876If C<utf8> is non-zero the text is treated as UTF-8 encoded
877
878If C<aa> is non-zero then the text is drawn anti-aliased.
faa9b3e7
TC
879
880Returns non-zero on success.
881
882=cut
883*/
884
35891892 885int
8d14daab 886i_ft2_cp(FT2_Fonthandle *handle, i_img *im, i_img_dim tx, i_img_dim ty, int channel,
718b8c97 887 double cheight, double cwidth, char const *text, size_t len, int align,
faa9b3e7 888 int aa, int vlayout, int utf8) {
8d14daab 889 i_img_dim bbox[8];
faa9b3e7
TC
890 i_img *work;
891 i_color cl, cl2;
892 int x, y;
893
5e84d110
TC
894 mm_log((1, "i_ft2_cp(handle %p, im %p, (tx, ty) (" i_DFp "), channel %d, cheight %f, cwidth %f, text %p, len %u, align %d, aa %d, vlayout %d, utf8 %d)\n",
895 handle, im, i_DFcp(tx, ty), channel, cheight, cwidth, text, (unsigned)len, align, aa, vlayout, utf8));
a73aeb5f 896
d6f02a59
TC
897 i_clear_error();
898
faa9b3e7
TC
899 if (vlayout && !FT_HAS_VERTICAL(handle->face)) {
900 i_push_error(0, "face has no vertical metrics");
901 return 0;
902 }
903
904 if (!i_ft2_bbox_r(handle, cheight, cwidth, text, len, vlayout, utf8, bbox))
905 return 0;
906
50c75381 907 work = i_img_8_new(bbox[2]-bbox[0]+1, bbox[3]-bbox[1]+1, 1);
faa9b3e7 908 cl.channel[0] = 255;
3fbda1ce 909 cl.channel[1] = 255;
faa9b3e7
TC
910 if (!i_ft2_text(handle, work, -bbox[0], -bbox[1], &cl, cheight, cwidth,
911 text, len, 1, aa, vlayout, utf8))
912 return 0;
913
914 if (!align) {
915 tx -= bbox[4];
916 ty += bbox[5];
917 }
918
919 /* render to the specified channel */
920 /* this will be sped up ... */
921 for (y = 0; y < work->ysize; ++y) {
922 for (x = 0; x < work->xsize; ++x) {
923 i_gpix(work, x, y, &cl);
924 i_gpix(im, tx + x + bbox[0], ty + y + bbox[1], &cl2);
925 cl2.channel[channel] = cl.channel[0];
926 i_ppix(im, tx + x + bbox[0], ty + y + bbox[1], &cl2);
927 }
928 }
a73aeb5f 929 i_img_destroy(work);
faa9b3e7
TC
930 return 1;
931}
932
3dec2c92 933/*
718b8c97 934=item i_ft2_has_chars(handle, char *text, size_t len, int utf8, char *out)
3dec2c92
TC
935
936Check if the given characters are defined by the font.
937
938Returns the number of characters that were checked.
939
940=cut
941*/
8d14daab
TC
942size_t
943i_ft2_has_chars(FT2_Fonthandle *handle, char const *text, size_t len,
4f68b48f 944 int utf8, char *out) {
3dec2c92 945 int count = 0;
5e84d110
TC
946 mm_log((1, "i_ft2_has_chars(handle %p, text %p, len %u, utf8 %d)\n",
947 handle, text, (unsigned)len, utf8));
3dec2c92 948
d6f02a59
TC
949 i_clear_error();
950
3dec2c92
TC
951 while (len) {
952 unsigned long c;
953 int index;
954 if (utf8) {
4f68b48f 955 c = i_utf8_advance(&text, &len);
3dec2c92
TC
956 if (c == ~0UL) {
957 i_push_error(0, "invalid UTF8 character");
958 return 0;
959 }
960 }
961 else {
962 c = (unsigned char)*text++;
963 --len;
964 }
965
966 index = FT_Get_Char_Index(handle->face, c);
967 *out++ = index != 0;
968 ++count;
969 }
970
971 return count;
972}
973
faa9b3e7
TC
974/* uses a method described in fterrors.h to build an error translation
975 function
976*/
1a4a5b11 977#undef __FTERRORS_H__
faa9b3e7 978#define FT_ERRORDEF(e, v, s) case v: i_push_error(code, s); return;
1a4a5b11
TC
979#define FT_ERROR_START_LIST
980#define FT_ERROR_END_LIST
faa9b3e7
TC
981
982/*
983=back
984
985=head2 Internal Functions
986
987These functions are used in the implementation of freetyp2.c and should not
988(usually cannot) be called from outside it.
989
990=over
991
992=item ft2_push_message(int code)
993
994Pushes an error message corresponding to code onto the error stack.
995
996=cut
997*/
86c8d19a
TC
998
999#define UNKNOWN_ERROR_FORMAT "Unknown Freetype2 error code 0x%04X"
1000
1001static void
1002ft2_push_message(int code) {
faa9b3e7
TC
1003 char unknown[40];
1004
1005 switch (code) {
1006#include FT_ERRORS_H
1007 }
1008
86c8d19a
TC
1009#ifdef IMAGER_SNPRINTF
1010 snprintf(unknown, sizeof(unknown), UNKNOWN_ERROR_FORMAT, code);
1011#else
1012 sprintf(unknown, UNKNOWN_ERROR_FORMAT, code);
1013#endif
faa9b3e7
TC
1014 i_push_error(code, unknown);
1015}
1016
1017/*
1018=item make_bmp_map(FT_Bitmap *bitmap, unsigned char *map)
1019
1020Creates a map to convert grey levels from the glyphs bitmap into
1021values scaled 0..255.
1022
1023=cut
1024*/
1025static int
1026make_bmp_map(FT_Bitmap *bitmap, unsigned char *map) {
1027 int scale;
1028 int i;
1029
1030 switch (bitmap->pixel_mode) {
1031 case ft_pixel_mode_grays:
1032 scale = bitmap->num_grays;
1033 break;
1034
1035 default:
1036 i_push_errorf(0, "I can't handle pixel mode %d", bitmap->pixel_mode);
1037 return 0;
1038 }
1039
1040 /* build the table */
1041 for (i = 0; i < scale; ++i)
1042 map[i] = i * 255 / (bitmap->num_grays - 1);
1043
1044 return 1;
1045}
1046
042cdaea
TC
1047/* FREETYPE_PATCH was introduced in 2.0.6, we don't want a false
1048 positive on 2.0.0 to 2.0.4, so we accept a false negative in 2.0.5 */
1049#ifndef FREETYPE_PATCH
1050#define FREETYPE_PATCH 4
1051#endif
1052
1053/* FT_Get_Postscript_Name() was introduced in FT2.0.5 */
1054#define IM_HAS_FACE_NAME (FREETYPE_MINOR > 0 || FREETYPE_PATCH >= 5)
1055/* #define IM_HAS_FACE_NAME 0 */
1056
1057/*
1058=item i_ft2_face_name(handle, name_buf, name_buf_size)
1059
1060Fills the given buffer with the Postscript Face name of the font,
1061if there is one.
1062
8d14daab
TC
1063Returns the number of bytes copied, including the terminating NUL.
1064
042cdaea
TC
1065=cut
1066*/
1067
8d14daab 1068size_t
3799c4d1 1069i_ft2_face_name(FT2_Fonthandle *handle, char *name_buf, size_t name_buf_size) {
042cdaea 1070#if IM_HAS_FACE_NAME
3799c4d1
TC
1071 char const *name = FT_Get_Postscript_Name(handle->face);
1072
1073 i_clear_error();
1074
1075 if (name) {
1076 strncpy(name_buf, name, name_buf_size);
1077 name_buf[name_buf_size-1] = '\0';
1078
1079 return strlen(name) + 1;
1080 }
1081 else {
1082 i_push_error(0, "no face name available");
1083 *name_buf = '\0';
1084
1085 return 0;
1086 }
042cdaea
TC
1087#else
1088 i_clear_error();
1089 i_push_error(0, "Freetype 2.0.6 or later required");
1090 *name_buf = '\0';
1091
1092 return 0;
1093#endif
1094}
1095
1096int
1097i_ft2_can_face_name(void) {
1098 return IM_HAS_FACE_NAME;
3799c4d1
TC
1099}
1100
72e425d5
TC
1101/* FT_Has_PS_Glyph_Names() was introduced in FT2.1.1 */
1102/* well, I assume FREETYPE_MAJOR is 2, since we're here */
1103#if FREETYPE_MINOR < 1 || (FREETYPE_MINOR == 1 && FREETYPE_PATCH < 1)
1104#define FT_Has_PS_Glyph_Names(face) (FT_HAS_GLYPH_NAMES(face))
1105#endif
1106
3799c4d1 1107int
a4168bea
TC
1108i_ft2_glyph_name(FT2_Fonthandle *handle, unsigned long ch, char *name_buf,
1109 size_t name_buf_size, int reliable_only) {
3799c4d1
TC
1110#ifdef FT_CONFIG_OPTION_NO_GLYPH_NAMES
1111 i_clear_error();
1112 *name_buf = '\0';
1113 i_push_error(0, "FT2 configured without glyph name support");
1114
1115 return 0;
1116#else
a4168bea
TC
1117 FT_UInt index;
1118
3799c4d1
TC
1119 i_clear_error();
1120
a4168bea
TC
1121 if (!FT_HAS_GLYPH_NAMES(handle->face)) {
1122 i_push_error(0, "no glyph names in font");
1123 *name_buf = '\0';
1124 return 0;
1125 }
1126 if (reliable_only && !FT_Has_PS_Glyph_Names(handle->face)) {
1127 i_push_error(0, "no reliable glyph names in font - set reliable_only to 0 to try anyway");
1128 *name_buf = '\0';
1129 return 0;
1130 }
3799c4d1 1131
a4168bea
TC
1132 index = FT_Get_Char_Index(handle->face, ch);
1133
1134 if (index) {
1135 FT_Error error = FT_Get_Glyph_Name(handle->face, index, name_buf,
1136 name_buf_size);
1137 if (error) {
1138 ft2_push_message(error);
1139 *name_buf = '\0';
35891892 1140 return 0;
a4168bea 1141 }
d6f02a59
TC
1142 if (strcmp(name_buf, ".notdef") == 0) {
1143 *name_buf = 0;
1144 return 0;
1145 }
a4168bea
TC
1146 if (*name_buf) {
1147 return strlen(name_buf) + 1;
3799c4d1
TC
1148 }
1149 else {
3799c4d1
TC
1150 return 0;
1151 }
1152 }
1153 else {
a4168bea 1154 *name_buf = 0;
3799c4d1
TC
1155 return 0;
1156 }
1157#endif
1158}
1159
1160int
1161i_ft2_can_do_glyph_names(void) {
1162#ifdef FT_CONFIG_OPTION_NO_GLYPH_NAMES
1163 return 0;
1164#else
1165 return 1;
1166#endif
1167}
1168
1169int
1170i_ft2_face_has_glyph_names(FT2_Fonthandle *handle) {
1171#ifdef FT_CONFIG_OPTION_NO_GLYPH_NAMES
1172 return 0;
1173#else
d6f02a59
TC
1174 return FT_HAS_GLYPH_NAMES(handle->face);
1175 /* return FT_Has_PS_Glyph_Names(handle->face);*/
3799c4d1
TC
1176#endif
1177}
1178
3e882362
TC
1179int
1180i_ft2_is_multiple_master(FT2_Fonthandle *handle) {
1181 i_clear_error();
1182#ifdef IM_FT2_MM
1183 return handle->has_mm;
1184#else
1185 return 0;
1186#endif
1187}
1188
1189int
1190i_ft2_get_multiple_masters(FT2_Fonthandle *handle, i_font_mm *mm) {
1191#ifdef IM_FT2_MM
1192 int i;
1193 FT_Multi_Master *mms = &handle->mm;
1194
1195 i_clear_error();
1196 if (!handle->has_mm) {
1197 i_push_error(0, "Font has no multiple masters");
1198 return 0;
1199 }
1200 mm->num_axis = mms->num_axis;
1201 mm->num_designs = mms->num_designs;
1202 for (i = 0; i < mms->num_axis; ++i) {
1203 mm->axis[i].name = mms->axis[i].name;
1204 mm->axis[i].minimum = mms->axis[i].minimum;
1205 mm->axis[i].maximum = mms->axis[i].maximum;
1206 }
1207
1208 return 1;
1209#else
1210 i_clear_error();
1211 i_push_error(0, "Multiple master functions unavailable");
1212 return 0;
1213#endif
1214}
1215
1216int
97ac0a96 1217i_ft2_set_mm_coords(FT2_Fonthandle *handle, int coord_count, const long *coords) {
3e882362
TC
1218#ifdef IM_FT2_MM
1219 int i;
1220 FT_Long ftcoords[T1_MAX_MM_AXIS];
1221 FT_Error error;
1222
1223 i_clear_error();
1224 if (!handle->has_mm) {
1225 i_push_error(0, "Font has no multiple masters");
1226 return 0;
1227 }
1228 if (coord_count != handle->mm.num_axis) {
1229 i_push_error(0, "Number of MM coords doesn't match MM axis count");
1230 return 0;
1231 }
1232 for (i = 0; i < coord_count; ++i)
1233 ftcoords[i] = coords[i];
1234
1235 error = FT_Set_MM_Design_Coordinates(handle->face, coord_count, ftcoords);
1236 if (error) {
1237 ft2_push_message(error);
1238 return 0;
1239 }
1240
1241 return 1;
1242#else
1243 i_clear_error();
1244 i_push_error(0, "Multiple master functions unavailable");
1245
1246 return 0;
1247#endif
1248}
1249
8d14daab
TC
1250static i_img_dim
1251i_min(i_img_dim a, i_img_dim b) {
50c75381
TC
1252 return a < b ? a : b;
1253}
1254
8d14daab
TC
1255static i_img_dim
1256i_max(i_img_dim a, i_img_dim b) {
50c75381
TC
1257 return a > b ? a : b;
1258}
1259
faa9b3e7
TC
1260/*
1261=back
1262
1263=head1 AUTHOR
1264
1265Tony Cook <tony@develop-help.com>, with a fair amount of help from
1266reading the code in font.c.
1267
1268=head1 SEE ALSO
1269
1270font.c, Imager::Font(3), Imager(3)
1271
1272http://www.freetype.org/
1273
1274=cut
1275*/
1276