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