fix channel output for FT2
[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
5e84d110
TC
427 mm_log((1, "i_ft2_bbox(handle %p, cheight %f, cwidth %f, text %p, len %u, bbox %p)\n",
428 handle, cheight, cwidth, text, (unsigned)len, bbox));
a73aeb5f 429
faa9b3e7
TC
430 error = FT_Set_Char_Size(handle->face, cwidth*64, cheight*64,
431 handle->xdpi, handle->ydpi);
432 if (error) {
433 ft2_push_message(error);
434 i_push_error(0, "setting size");
435 }
436
3799c4d1
TC
437 if (!handle->hint)
438 loadFlags |= FT_LOAD_NO_HINTING;
439
faa9b3e7
TC
440 first = 1;
441 width = 0;
5cb9270b
TC
442 while (len) {
443 unsigned long c;
444 if (utf8) {
4f68b48f 445 c = i_utf8_advance(&text, &len);
5cb9270b
TC
446 if (c == ~0UL) {
447 i_push_error(0, "invalid UTF8 character");
448 return 0;
449 }
450 }
451 else {
452 c = (unsigned char)*text++;
453 --len;
454 }
455
faa9b3e7 456 index = FT_Get_Char_Index(handle->face, c);
3799c4d1 457 error = FT_Load_Glyph(handle->face, index, loadFlags);
faa9b3e7
TC
458 if (error) {
459 ft2_push_message(error);
5e84d110 460 i_push_errorf(0, "loading glyph for character \\x%02lx (glyph 0x%04X)",
faa9b3e7
TC
461 c, index);
462 return 0;
463 }
464 gm = &handle->face->glyph->metrics;
465 glyph_ascent = gm->horiBearingY / 64;
466 glyph_descent = glyph_ascent - gm->height/64;
467 if (first) {
468 start = gm->horiBearingX / 64;
469 /* handles -ve values properly */
470 ascent = glyph_ascent;
471 descent = glyph_descent;
472 first = 0;
473 }
474
475 if (glyph_ascent > ascent)
476 ascent = glyph_ascent;
b72644d0 477 if (glyph_descent < descent)
faa9b3e7
TC
478 descent = glyph_descent;
479
480 width += gm->horiAdvance / 64;
481
482 if (len == 0) {
483 /* last character
484 handle the case where the right the of the character overlaps the
485 right*/
95171801 486 rightb = (gm->horiAdvance - gm->horiBearingX - gm->width)/64;
dc35bde9
TC
487 /*if (rightb > 0)
488 rightb = 0;*/
faa9b3e7
TC
489 }
490 }
491
3799c4d1
TC
492 bbox[BBOX_NEG_WIDTH] = start;
493 bbox[BBOX_GLOBAL_DESCENT] = handle->face->size->metrics.descender / 64;
dc35bde9
TC
494 bbox[BBOX_POS_WIDTH] = width;
495 if (rightb < 0)
496 bbox[BBOX_POS_WIDTH] -= rightb;
3799c4d1
TC
497 bbox[BBOX_GLOBAL_ASCENT] = handle->face->size->metrics.ascender / 64;
498 bbox[BBOX_DESCENT] = descent;
499 bbox[BBOX_ASCENT] = ascent;
500 bbox[BBOX_ADVANCE_WIDTH] = width;
dc35bde9 501 bbox[BBOX_RIGHT_BEARING] = rightb;
5e84d110
TC
502 mm_log((1, " bbox=> negw=%" i_DF " glob_desc=%" i_DF " pos_wid=%" i_DF
503 " glob_asc=%" i_DF " desc=%" i_DF " asc=%" i_DF " adv_width=%" i_DF
504 " rightb=%" i_DF "\n",
505 bbox[0], bbox[1], bbox[2], bbox[3], bbox[4], bbox[5], bbox[6],
506 bbox[7]));
faa9b3e7 507
dc35bde9 508 return BBOX_RIGHT_BEARING + 1;
faa9b3e7
TC
509}
510
511/*
512=item transform_box(FT2_FontHandle *handle, int bbox[4])
513
514bbox contains coorinates of a the top-left and bottom-right of a bounding
515box relative to a point.
516
517This is then transformed and the values in bbox[4] are the top-left
518and bottom-right of the new bounding box.
519
520This is meant to provide the bounding box of a transformed character
521box. The problem is that if the character was round and is rotated,
522the real bounding box isn't going to be much different from the
523original, but this function will return a _bigger_ bounding box. I
524suppose I could work my way through the glyph outline, but that's
525too much hard work.
526
527=cut
528*/
8d14daab 529void ft2_transform_box(FT2_Fonthandle *handle, i_img_dim bbox[4]) {
faa9b3e7
TC
530 double work[8];
531 double *matrix = handle->matrix;
faa9b3e7
TC
532
533 work[0] = matrix[0] * bbox[0] + matrix[1] * bbox[1];
534 work[1] = matrix[3] * bbox[0] + matrix[4] * bbox[1];
535 work[2] = matrix[0] * bbox[2] + matrix[1] * bbox[1];
536 work[3] = matrix[3] * bbox[2] + matrix[4] * bbox[1];
537 work[4] = matrix[0] * bbox[0] + matrix[1] * bbox[3];
538 work[5] = matrix[3] * bbox[0] + matrix[4] * bbox[3];
539 work[6] = matrix[0] * bbox[2] + matrix[1] * bbox[3];
540 work[7] = matrix[3] * bbox[2] + matrix[4] * bbox[3];
541
b33c08f8
TC
542 bbox[0] = floor(i_min(i_min(work[0], work[2]),i_min(work[4], work[6])));
543 bbox[1] = floor(i_min(i_min(work[1], work[3]),i_min(work[5], work[7])));
544 bbox[2] = ceil(i_max(i_max(work[0], work[2]),i_max(work[4], work[6])));
545 bbox[3] = ceil(i_max(i_max(work[1], work[3]),i_max(work[5], work[7])));
faa9b3e7
TC
546}
547
548/*
549=item expand_bounds(int bbox[4], int bbox2[4])
550
551Treating bbox[] and bbox2[] as 2 bounding boxes, produces a new
552bounding box in bbox[] that encloses both.
553
554=cut
555*/
8d14daab 556static void expand_bounds(i_img_dim bbox[4], i_img_dim bbox2[4]) {
b33c08f8
TC
557 bbox[0] = i_min(bbox[0], bbox2[0]);
558 bbox[1] = i_min(bbox[1], bbox2[1]);
559 bbox[2] = i_max(bbox[2], bbox2[2]);
560 bbox[3] = i_max(bbox[3], bbox2[3]);
faa9b3e7
TC
561}
562
563/*
8d14daab 564=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
565
566Retrieves bounding box information for the font at the given
567character width and height.
568
569This version finds the rectangular bounding box of the glyphs, with
570the text as transformed by the transformation matrix. As with
571i_ft2_bbox (bbox[0], bbox[1]) will the the offset from the start of
572the topline to the top-left of the bounding box. Unlike i_ft2_bbox()
573this could be near the bottom left corner of the box.
574
575(bbox[4], bbox[5]) is the offset to the start of the baseline.
576(bbox[6], bbox[7]) is the offset from the start of the baseline to the
577end of the baseline.
578
579Returns non-zero on success.
580
581=cut
582*/
583int
584i_ft2_bbox_r(FT2_Fonthandle *handle, double cheight, double cwidth,
8d14daab 585 char const *text, size_t len, int vlayout, int utf8, i_img_dim *bbox) {
faa9b3e7 586 FT_Error error;
8d14daab 587 i_img_dim width;
faa9b3e7
TC
588 int index;
589 int first;
8d14daab 590 i_img_dim ascent = 0, descent = 0;
faa9b3e7
TC
591 int glyph_ascent, glyph_descent;
592 FT_Glyph_Metrics *gm;
8d14daab 593 i_img_dim work[4];
ccfd0ddf 594 i_img_dim bounds[4] = { 0 };
faa9b3e7
TC
595 double x = 0, y = 0;
596 int i;
597 FT_GlyphSlot slot;
faa9b3e7
TC
598 int loadFlags = FT_LOAD_DEFAULT;
599
600 if (vlayout)
601 loadFlags |= FT_LOAD_VERTICAL_LAYOUT;
3799c4d1
TC
602 if (!handle->hint)
603 loadFlags |= FT_LOAD_NO_HINTING;
faa9b3e7
TC
604
605 error = FT_Set_Char_Size(handle->face, cwidth*64, cheight*64,
606 handle->xdpi, handle->ydpi);
607 if (error) {
608 ft2_push_message(error);
609 i_push_error(0, "setting size");
610 }
611
612 first = 1;
613 width = 0;
614 while (len) {
615 unsigned long c;
616 if (utf8) {
4f68b48f 617 c = i_utf8_advance(&text, &len);
faa9b3e7
TC
618 if (c == ~0UL) {
619 i_push_error(0, "invalid UTF8 character");
620 return 0;
621 }
622 }
623 else {
624 c = (unsigned char)*text++;
625 --len;
626 }
627
628 index = FT_Get_Char_Index(handle->face, c);
629 error = FT_Load_Glyph(handle->face, index, loadFlags);
630 if (error) {
631 ft2_push_message(error);
5e84d110 632 i_push_errorf(0, "loading glyph for character \\x%02lx (glyph 0x%04X)",
faa9b3e7
TC
633 c, index);
634 return 0;
635 }
636 slot = handle->face->glyph;
637 gm = &slot->metrics;
638
639 /* these probably don't mean much for vertical layouts */
640 glyph_ascent = gm->horiBearingY / 64;
641 glyph_descent = glyph_ascent - gm->height/64;
642 if (vlayout) {
643 work[0] = gm->vertBearingX;
644 work[1] = gm->vertBearingY;
645 }
646 else {
647 work[0] = gm->horiBearingX;
648 work[1] = gm->horiBearingY;
649 }
650 work[2] = gm->width + work[0];
651 work[3] = work[1] - gm->height;
652 if (first) {
653 bbox[4] = work[0] * handle->matrix[0] + work[1] * handle->matrix[1] + handle->matrix[2];
654 bbox[5] = work[0] * handle->matrix[3] + work[1] * handle->matrix[4] + handle->matrix[5];
655 bbox[4] = bbox[4] < 0 ? -(-bbox[4] + 32)/64 : (bbox[4] + 32) / 64;
656 bbox[5] /= 64;
657 }
658 ft2_transform_box(handle, work);
659 for (i = 0; i < 4; ++i)
660 work[i] /= 64;
661 work[0] += x;
662 work[1] += y;
663 work[2] += x;
664 work[3] += y;
665 if (first) {
666 for (i = 0; i < 4; ++i)
667 bounds[i] = work[i];
668 ascent = glyph_ascent;
669 descent = glyph_descent;
670 first = 0;
671 }
672 else {
673 expand_bounds(bounds, work);
674 }
675 x += slot->advance.x / 64;
676 y += slot->advance.y / 64;
3799c4d1 677
faa9b3e7
TC
678 if (glyph_ascent > ascent)
679 ascent = glyph_ascent;
680 if (glyph_descent > descent)
681 descent = glyph_descent;
682
683 if (len == 0) {
684 /* last character
685 handle the case where the right the of the character overlaps the
686 right*/
687 /*int rightb = gm->horiAdvance - gm->horiBearingX - gm->width;
688 if (rightb < 0)
689 width -= rightb / 64;*/
690 }
691 }
692
693 /* at this point bounds contains the bounds relative to the CP,
694 and x, y hold the final position relative to the CP */
695 /*bounds[0] -= x;
696 bounds[1] -= y;
697 bounds[2] -= x;
698 bounds[3] -= y;*/
699
700 bbox[0] = bounds[0];
701 bbox[1] = -bounds[3];
702 bbox[2] = bounds[2];
703 bbox[3] = -bounds[1];
704 bbox[6] = x;
705 bbox[7] = -y;
706
707 return 1;
708}
709
faa9b3e7
TC
710static int
711make_bmp_map(FT_Bitmap *bitmap, unsigned char *map);
712
713/*
718b8c97 714=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
715
716Renders I<text> to (I<tx>, I<ty>) in I<im> using color I<cl> at the given
717I<cheight> and I<cwidth>.
718
719If align is 0, then the text is rendered with the top-left of the
720first character at (I<tx>, I<ty>). If align is non-zero then the text
721is rendered with (I<tx>, I<ty>) aligned with the base-line of the
722characters.
723
724If aa is non-zero then the text is anti-aliased.
725
726Returns non-zero on success.
727
728=cut
729*/
730int
8d14daab 731i_ft2_text(FT2_Fonthandle *handle, i_img *im, i_img_dim tx, i_img_dim ty, const i_color *cl,
718b8c97
TC
732 double cheight, double cwidth, char const *text, size_t len,
733 int align, int aa, int vlayout, int utf8) {
faa9b3e7
TC
734 FT_Error error;
735 int index;
736 FT_Glyph_Metrics *gm;
8d14daab 737 i_img_dim bbox[BOUNDING_BOX_COUNT];
faa9b3e7
TC
738 FT_GlyphSlot slot;
739 int x, y;
740 unsigned char *bmp;
741 unsigned char map[256];
742 char last_mode = ft_pixel_mode_none;
743 int last_grays = -1;
faa9b3e7 744 int loadFlags = FT_LOAD_DEFAULT;
ccfd0ddf 745 i_render *render = NULL;
faa9b3e7 746
5e84d110
TC
747 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",
748 handle, im, i_DFcp(tx, ty), cl, cheight, cwidth, text, (unsigned)len, align, aa, vlayout, utf8));
a73aeb5f 749
faa9b3e7
TC
750 if (vlayout) {
751 if (!FT_HAS_VERTICAL(handle->face)) {
752 i_push_error(0, "face has no vertical metrics");
753 return 0;
754 }
755 loadFlags |= FT_LOAD_VERTICAL_LAYOUT;
756 }
757 if (!handle->hint)
758 loadFlags |= FT_LOAD_NO_HINTING;
759
760 /* set the base-line based on the string ascent */
5cb9270b 761 if (!i_ft2_bbox(handle, cheight, cwidth, text, len, bbox, utf8))
faa9b3e7
TC
762 return 0;
763
9c106321 764 if (aa)
50c75381 765 render = i_render_new(im, bbox[BBOX_POS_WIDTH] - bbox[BBOX_NEG_WIDTH]);
9c106321 766
faa9b3e7
TC
767 if (!align) {
768 /* this may need adjustment */
769 tx -= bbox[0] * handle->matrix[0] + bbox[5] * handle->matrix[1] + handle->matrix[2];
770 ty += bbox[0] * handle->matrix[3] + bbox[5] * handle->matrix[4] + handle->matrix[5];
771 }
772 while (len) {
773 unsigned long c;
774 if (utf8) {
4f68b48f 775 c = i_utf8_advance(&text, &len);
faa9b3e7
TC
776 if (c == ~0UL) {
777 i_push_error(0, "invalid UTF8 character");
778 return 0;
779 }
780 }
781 else {
782 c = (unsigned char)*text++;
783 --len;
784 }
785
786 index = FT_Get_Char_Index(handle->face, c);
787 error = FT_Load_Glyph(handle->face, index, loadFlags);
788 if (error) {
789 ft2_push_message(error);
5e84d110 790 i_push_errorf(0, "loading glyph for character \\x%02lx (glyph 0x%04X)",
faa9b3e7 791 c, index);
ccfd0ddf 792 if (render)
50c75381 793 i_render_delete(render);
faa9b3e7
TC
794 return 0;
795 }
796 slot = handle->face->glyph;
797 gm = &slot->metrics;
798
e15cea68
TC
799 if (gm->width) {
800 error = FT_Render_Glyph(slot, aa ? ft_render_mode_normal : ft_render_mode_mono);
801 if (error) {
802 ft2_push_message(error);
5e84d110 803 i_push_errorf(0, "rendering glyph 0x%04lX (character \\x%02X)", c, index);
ccfd0ddf 804 if (render)
50c75381 805 i_render_delete(render);
e15cea68 806 return 0;
faa9b3e7 807 }
e15cea68
TC
808 if (slot->bitmap.pixel_mode == ft_pixel_mode_mono) {
809 bmp = slot->bitmap.buffer;
810 for (y = 0; y < slot->bitmap.rows; ++y) {
811 int pos = 0;
812 int bit = 0x80;
813 for (x = 0; x < slot->bitmap.width; ++x) {
814 if (bmp[pos] & bit)
815 i_ppix(im, tx+x+slot->bitmap_left, ty+y-slot->bitmap_top, cl);
816
817 bit >>= 1;
818 if (bit == 0) {
819 bit = 0x80;
820 ++pos;
821 }
822 }
823 bmp += slot->bitmap.pitch;
824 }
faa9b3e7 825 }
e15cea68
TC
826 else {
827 /* grey scale or something we can treat as greyscale */
828 /* we create a map to convert from the bitmap values to 0-255 */
829 if (last_mode != slot->bitmap.pixel_mode
830 || last_grays != slot->bitmap.num_grays) {
831 if (!make_bmp_map(&slot->bitmap, map))
832 return 0;
833 last_mode = slot->bitmap.pixel_mode;
834 last_grays = slot->bitmap.num_grays;
835 }
9c106321 836
e15cea68
TC
837 bmp = slot->bitmap.buffer;
838 for (y = 0; y < slot->bitmap.rows; ++y) {
9c106321
TC
839 if (last_mode == ft_pixel_mode_grays &&
840 last_grays != 255) {
841 for (x = 0; x < slot->bitmap.width; ++x)
842 bmp[x] = map[bmp[x]];
843 }
50c75381 844 i_render_color(render, tx + slot->bitmap_left, ty-slot->bitmap_top+y,
9c106321 845 slot->bitmap.width, bmp, cl);
e15cea68
TC
846 bmp += slot->bitmap.pitch;
847 }
faa9b3e7
TC
848 }
849 }
850
851 tx += slot->advance.x / 64;
852 ty -= slot->advance.y / 64;
853 }
854
ccfd0ddf 855 if (render)
50c75381 856 i_render_delete(render);
9c106321 857
faa9b3e7
TC
858 return 1;
859}
860
861/*
5e84d110 862=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
863
864Renders I<text> to (I<tx>, I<ty>) in I<im> to I<channel> at the given
865I<cheight> and I<cwidth>.
866
867If align is 0, then the text is rendered with the top-left of the
868first character at (I<tx>, I<ty>). If align is non-zero then the text
869is rendered with (I<tx>, I<ty>) aligned with the base-line of the
870characters.
871
5e84d110
TC
872If C<utf8> is non-zero the text is treated as UTF-8 encoded
873
874If C<aa> is non-zero then the text is drawn anti-aliased.
faa9b3e7
TC
875
876Returns non-zero on success.
877
878=cut
879*/
880
35891892 881int
8d14daab 882i_ft2_cp(FT2_Fonthandle *handle, i_img *im, i_img_dim tx, i_img_dim ty, int channel,
718b8c97 883 double cheight, double cwidth, char const *text, size_t len, int align,
faa9b3e7 884 int aa, int vlayout, int utf8) {
8d14daab 885 i_img_dim bbox[8];
faa9b3e7
TC
886 i_img *work;
887 i_color cl, cl2;
888 int x, y;
889
5e84d110
TC
890 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",
891 handle, im, i_DFcp(tx, ty), channel, cheight, cwidth, text, (unsigned)len, align, aa, vlayout, utf8));
a73aeb5f 892
faa9b3e7
TC
893 if (vlayout && !FT_HAS_VERTICAL(handle->face)) {
894 i_push_error(0, "face has no vertical metrics");
895 return 0;
896 }
897
898 if (!i_ft2_bbox_r(handle, cheight, cwidth, text, len, vlayout, utf8, bbox))
899 return 0;
900
50c75381 901 work = i_img_8_new(bbox[2]-bbox[0]+1, bbox[3]-bbox[1]+1, 1);
faa9b3e7 902 cl.channel[0] = 255;
3fbda1ce 903 cl.channel[1] = 255;
faa9b3e7
TC
904 if (!i_ft2_text(handle, work, -bbox[0], -bbox[1], &cl, cheight, cwidth,
905 text, len, 1, aa, vlayout, utf8))
906 return 0;
907
908 if (!align) {
909 tx -= bbox[4];
910 ty += bbox[5];
911 }
912
913 /* render to the specified channel */
914 /* this will be sped up ... */
915 for (y = 0; y < work->ysize; ++y) {
916 for (x = 0; x < work->xsize; ++x) {
917 i_gpix(work, x, y, &cl);
918 i_gpix(im, tx + x + bbox[0], ty + y + bbox[1], &cl2);
919 cl2.channel[channel] = cl.channel[0];
920 i_ppix(im, tx + x + bbox[0], ty + y + bbox[1], &cl2);
921 }
922 }
a73aeb5f 923 i_img_destroy(work);
faa9b3e7
TC
924 return 1;
925}
926
3dec2c92 927/*
718b8c97 928=item i_ft2_has_chars(handle, char *text, size_t len, int utf8, char *out)
3dec2c92
TC
929
930Check if the given characters are defined by the font.
931
932Returns the number of characters that were checked.
933
934=cut
935*/
8d14daab
TC
936size_t
937i_ft2_has_chars(FT2_Fonthandle *handle, char const *text, size_t len,
4f68b48f 938 int utf8, char *out) {
3dec2c92 939 int count = 0;
5e84d110
TC
940 mm_log((1, "i_ft2_has_chars(handle %p, text %p, len %u, utf8 %d)\n",
941 handle, text, (unsigned)len, utf8));
3dec2c92
TC
942
943 while (len) {
944 unsigned long c;
945 int index;
946 if (utf8) {
4f68b48f 947 c = i_utf8_advance(&text, &len);
3dec2c92
TC
948 if (c == ~0UL) {
949 i_push_error(0, "invalid UTF8 character");
950 return 0;
951 }
952 }
953 else {
954 c = (unsigned char)*text++;
955 --len;
956 }
957
958 index = FT_Get_Char_Index(handle->face, c);
959 *out++ = index != 0;
960 ++count;
961 }
962
963 return count;
964}
965
faa9b3e7
TC
966/* uses a method described in fterrors.h to build an error translation
967 function
968*/
1a4a5b11 969#undef __FTERRORS_H__
faa9b3e7 970#define FT_ERRORDEF(e, v, s) case v: i_push_error(code, s); return;
1a4a5b11
TC
971#define FT_ERROR_START_LIST
972#define FT_ERROR_END_LIST
faa9b3e7
TC
973
974/*
975=back
976
977=head2 Internal Functions
978
979These functions are used in the implementation of freetyp2.c and should not
980(usually cannot) be called from outside it.
981
982=over
983
984=item ft2_push_message(int code)
985
986Pushes an error message corresponding to code onto the error stack.
987
988=cut
989*/
86c8d19a
TC
990
991#define UNKNOWN_ERROR_FORMAT "Unknown Freetype2 error code 0x%04X"
992
993static void
994ft2_push_message(int code) {
faa9b3e7
TC
995 char unknown[40];
996
997 switch (code) {
998#include FT_ERRORS_H
999 }
1000
86c8d19a
TC
1001#ifdef IMAGER_SNPRINTF
1002 snprintf(unknown, sizeof(unknown), UNKNOWN_ERROR_FORMAT, code);
1003#else
1004 sprintf(unknown, UNKNOWN_ERROR_FORMAT, code);
1005#endif
faa9b3e7
TC
1006 i_push_error(code, unknown);
1007}
1008
1009/*
1010=item make_bmp_map(FT_Bitmap *bitmap, unsigned char *map)
1011
1012Creates a map to convert grey levels from the glyphs bitmap into
1013values scaled 0..255.
1014
1015=cut
1016*/
1017static int
1018make_bmp_map(FT_Bitmap *bitmap, unsigned char *map) {
1019 int scale;
1020 int i;
1021
1022 switch (bitmap->pixel_mode) {
1023 case ft_pixel_mode_grays:
1024 scale = bitmap->num_grays;
1025 break;
1026
1027 default:
1028 i_push_errorf(0, "I can't handle pixel mode %d", bitmap->pixel_mode);
1029 return 0;
1030 }
1031
1032 /* build the table */
1033 for (i = 0; i < scale; ++i)
1034 map[i] = i * 255 / (bitmap->num_grays - 1);
1035
1036 return 1;
1037}
1038
042cdaea
TC
1039/* FREETYPE_PATCH was introduced in 2.0.6, we don't want a false
1040 positive on 2.0.0 to 2.0.4, so we accept a false negative in 2.0.5 */
1041#ifndef FREETYPE_PATCH
1042#define FREETYPE_PATCH 4
1043#endif
1044
1045/* FT_Get_Postscript_Name() was introduced in FT2.0.5 */
1046#define IM_HAS_FACE_NAME (FREETYPE_MINOR > 0 || FREETYPE_PATCH >= 5)
1047/* #define IM_HAS_FACE_NAME 0 */
1048
1049/*
1050=item i_ft2_face_name(handle, name_buf, name_buf_size)
1051
1052Fills the given buffer with the Postscript Face name of the font,
1053if there is one.
1054
8d14daab
TC
1055Returns the number of bytes copied, including the terminating NUL.
1056
042cdaea
TC
1057=cut
1058*/
1059
8d14daab 1060size_t
3799c4d1 1061i_ft2_face_name(FT2_Fonthandle *handle, char *name_buf, size_t name_buf_size) {
042cdaea 1062#if IM_HAS_FACE_NAME
3799c4d1
TC
1063 char const *name = FT_Get_Postscript_Name(handle->face);
1064
1065 i_clear_error();
1066
1067 if (name) {
1068 strncpy(name_buf, name, name_buf_size);
1069 name_buf[name_buf_size-1] = '\0';
1070
1071 return strlen(name) + 1;
1072 }
1073 else {
1074 i_push_error(0, "no face name available");
1075 *name_buf = '\0';
1076
1077 return 0;
1078 }
042cdaea
TC
1079#else
1080 i_clear_error();
1081 i_push_error(0, "Freetype 2.0.6 or later required");
1082 *name_buf = '\0';
1083
1084 return 0;
1085#endif
1086}
1087
1088int
1089i_ft2_can_face_name(void) {
1090 return IM_HAS_FACE_NAME;
3799c4d1
TC
1091}
1092
72e425d5
TC
1093/* FT_Has_PS_Glyph_Names() was introduced in FT2.1.1 */
1094/* well, I assume FREETYPE_MAJOR is 2, since we're here */
1095#if FREETYPE_MINOR < 1 || (FREETYPE_MINOR == 1 && FREETYPE_PATCH < 1)
1096#define FT_Has_PS_Glyph_Names(face) (FT_HAS_GLYPH_NAMES(face))
1097#endif
1098
3799c4d1 1099int
a4168bea
TC
1100i_ft2_glyph_name(FT2_Fonthandle *handle, unsigned long ch, char *name_buf,
1101 size_t name_buf_size, int reliable_only) {
3799c4d1
TC
1102#ifdef FT_CONFIG_OPTION_NO_GLYPH_NAMES
1103 i_clear_error();
1104 *name_buf = '\0';
1105 i_push_error(0, "FT2 configured without glyph name support");
1106
1107 return 0;
1108#else
a4168bea
TC
1109 FT_UInt index;
1110
3799c4d1
TC
1111 i_clear_error();
1112
a4168bea
TC
1113 if (!FT_HAS_GLYPH_NAMES(handle->face)) {
1114 i_push_error(0, "no glyph names in font");
1115 *name_buf = '\0';
1116 return 0;
1117 }
1118 if (reliable_only && !FT_Has_PS_Glyph_Names(handle->face)) {
1119 i_push_error(0, "no reliable glyph names in font - set reliable_only to 0 to try anyway");
1120 *name_buf = '\0';
1121 return 0;
1122 }
3799c4d1 1123
a4168bea
TC
1124 index = FT_Get_Char_Index(handle->face, ch);
1125
1126 if (index) {
1127 FT_Error error = FT_Get_Glyph_Name(handle->face, index, name_buf,
1128 name_buf_size);
1129 if (error) {
1130 ft2_push_message(error);
1131 *name_buf = '\0';
35891892 1132 return 0;
a4168bea
TC
1133 }
1134 if (*name_buf) {
1135 return strlen(name_buf) + 1;
3799c4d1
TC
1136 }
1137 else {
3799c4d1
TC
1138 return 0;
1139 }
1140 }
1141 else {
a4168bea
TC
1142 i_push_error(0, "no glyph for that character");
1143 *name_buf = 0;
3799c4d1
TC
1144 return 0;
1145 }
1146#endif
1147}
1148
1149int
1150i_ft2_can_do_glyph_names(void) {
1151#ifdef FT_CONFIG_OPTION_NO_GLYPH_NAMES
1152 return 0;
1153#else
1154 return 1;
1155#endif
1156}
1157
1158int
1159i_ft2_face_has_glyph_names(FT2_Fonthandle *handle) {
1160#ifdef FT_CONFIG_OPTION_NO_GLYPH_NAMES
1161 return 0;
1162#else
1163 return FT_Has_PS_Glyph_Names(handle->face);
1164#endif
1165}
1166
3e882362
TC
1167int
1168i_ft2_is_multiple_master(FT2_Fonthandle *handle) {
1169 i_clear_error();
1170#ifdef IM_FT2_MM
1171 return handle->has_mm;
1172#else
1173 return 0;
1174#endif
1175}
1176
1177int
1178i_ft2_get_multiple_masters(FT2_Fonthandle *handle, i_font_mm *mm) {
1179#ifdef IM_FT2_MM
1180 int i;
1181 FT_Multi_Master *mms = &handle->mm;
1182
1183 i_clear_error();
1184 if (!handle->has_mm) {
1185 i_push_error(0, "Font has no multiple masters");
1186 return 0;
1187 }
1188 mm->num_axis = mms->num_axis;
1189 mm->num_designs = mms->num_designs;
1190 for (i = 0; i < mms->num_axis; ++i) {
1191 mm->axis[i].name = mms->axis[i].name;
1192 mm->axis[i].minimum = mms->axis[i].minimum;
1193 mm->axis[i].maximum = mms->axis[i].maximum;
1194 }
1195
1196 return 1;
1197#else
1198 i_clear_error();
1199 i_push_error(0, "Multiple master functions unavailable");
1200 return 0;
1201#endif
1202}
1203
1204int
97ac0a96 1205i_ft2_set_mm_coords(FT2_Fonthandle *handle, int coord_count, const long *coords) {
3e882362
TC
1206#ifdef IM_FT2_MM
1207 int i;
1208 FT_Long ftcoords[T1_MAX_MM_AXIS];
1209 FT_Error error;
1210
1211 i_clear_error();
1212 if (!handle->has_mm) {
1213 i_push_error(0, "Font has no multiple masters");
1214 return 0;
1215 }
1216 if (coord_count != handle->mm.num_axis) {
1217 i_push_error(0, "Number of MM coords doesn't match MM axis count");
1218 return 0;
1219 }
1220 for (i = 0; i < coord_count; ++i)
1221 ftcoords[i] = coords[i];
1222
1223 error = FT_Set_MM_Design_Coordinates(handle->face, coord_count, ftcoords);
1224 if (error) {
1225 ft2_push_message(error);
1226 return 0;
1227 }
1228
1229 return 1;
1230#else
1231 i_clear_error();
1232 i_push_error(0, "Multiple master functions unavailable");
1233
1234 return 0;
1235#endif
1236}
1237
8d14daab
TC
1238static i_img_dim
1239i_min(i_img_dim a, i_img_dim b) {
50c75381
TC
1240 return a < b ? a : b;
1241}
1242
8d14daab
TC
1243static i_img_dim
1244i_max(i_img_dim a, i_img_dim b) {
50c75381
TC
1245 return a > b ? a : b;
1246}
1247
faa9b3e7
TC
1248/*
1249=back
1250
1251=head1 AUTHOR
1252
1253Tony Cook <tony@develop-help.com>, with a fair amount of help from
1254reading the code in font.c.
1255
1256=head1 SEE ALSO
1257
1258font.c, Imager::Font(3), Imager(3)
1259
1260http://www.freetype.org/
1261
1262=cut
1263*/
1264