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