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