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