- start of external Imager API access:
[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 *
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;
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
632i_ft2_text(FT2_Fonthandle *handle, i_img *im, int tx, int ty, 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;
648
a73aeb5f
AMH
649 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",
650 handle, im, tx, ty, cl, cheight, cwidth, text, align, aa));
651
faa9b3e7
TC
652 if (vlayout) {
653 if (!FT_HAS_VERTICAL(handle->face)) {
654 i_push_error(0, "face has no vertical metrics");
655 return 0;
656 }
657 loadFlags |= FT_LOAD_VERTICAL_LAYOUT;
658 }
659 if (!handle->hint)
660 loadFlags |= FT_LOAD_NO_HINTING;
661
662 /* set the base-line based on the string ascent */
5cb9270b 663 if (!i_ft2_bbox(handle, cheight, cwidth, text, len, bbox, utf8))
faa9b3e7
TC
664 return 0;
665
666 if (!align) {
667 /* this may need adjustment */
668 tx -= bbox[0] * handle->matrix[0] + bbox[5] * handle->matrix[1] + handle->matrix[2];
669 ty += bbox[0] * handle->matrix[3] + bbox[5] * handle->matrix[4] + handle->matrix[5];
670 }
671 while (len) {
672 unsigned long c;
673 if (utf8) {
4f68b48f 674 c = i_utf8_advance(&text, &len);
faa9b3e7
TC
675 if (c == ~0UL) {
676 i_push_error(0, "invalid UTF8 character");
677 return 0;
678 }
679 }
680 else {
681 c = (unsigned char)*text++;
682 --len;
683 }
684
685 index = FT_Get_Char_Index(handle->face, c);
686 error = FT_Load_Glyph(handle->face, index, loadFlags);
687 if (error) {
688 ft2_push_message(error);
689 i_push_errorf(0, "loading glyph for character \\x%02x (glyph 0x%04X)",
690 c, index);
691 return 0;
692 }
693 slot = handle->face->glyph;
694 gm = &slot->metrics;
695
696 error = FT_Render_Glyph(slot, aa ? ft_render_mode_normal : ft_render_mode_mono);
697 if (error) {
698 ft2_push_message(error);
699 i_push_errorf(0, "rendering glyph 0x%04X (character \\x%02X)");
700 return 0;
701 }
702 if (slot->bitmap.pixel_mode == ft_pixel_mode_mono) {
703 bmp = slot->bitmap.buffer;
704 for (y = 0; y < slot->bitmap.rows; ++y) {
705 int pos = 0;
706 int bit = 0x80;
707 for (x = 0; x < slot->bitmap.width; ++x) {
708 if (bmp[pos] & bit)
709 i_ppix(im, tx+x+slot->bitmap_left, ty+y-slot->bitmap_top, cl);
710
711 bit >>= 1;
712 if (bit == 0) {
713 bit = 0x80;
714 ++pos;
715 }
716 }
717 bmp += slot->bitmap.pitch;
718 }
719 }
720 else {
721 /* grey scale or something we can treat as greyscale */
722 /* we create a map to convert from the bitmap values to 0-255 */
723 if (last_mode != slot->bitmap.pixel_mode
724 || last_grays != slot->bitmap.num_grays) {
725 if (!make_bmp_map(&slot->bitmap, map))
726 return 0;
727 last_mode = slot->bitmap.pixel_mode;
728 last_grays = slot->bitmap.num_grays;
729 }
730
731 bmp = slot->bitmap.buffer;
732 for (y = 0; y < slot->bitmap.rows; ++y) {
733 for (x = 0; x < slot->bitmap.width; ++x) {
734 int value = map[bmp[x]];
735 if (value) {
736 i_gpix(im, tx+x+slot->bitmap_left, ty+y-slot->bitmap_top, &pel);
737 for (ch = 0; ch < im->channels; ++ch) {
738 pel.channel[ch] =
739 ((255-value)*pel.channel[ch] + value * cl->channel[ch]) / 255;
740 }
741 i_ppix(im, tx+x+slot->bitmap_left, ty+y-slot->bitmap_top, &pel);
742 }
743 }
744 bmp += slot->bitmap.pitch;
745 }
746 }
747
748 tx += slot->advance.x / 64;
749 ty -= slot->advance.y / 64;
750 }
751
752 return 1;
753}
754
755/*
756=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)
757
758Renders I<text> to (I<tx>, I<ty>) in I<im> to I<channel> at the given
759I<cheight> and I<cwidth>.
760
761If align is 0, then the text is rendered with the top-left of the
762first character at (I<tx>, I<ty>). If align is non-zero then the text
763is rendered with (I<tx>, I<ty>) aligned with the base-line of the
764characters.
765
766If aa is non-zero then the text is anti-aliased.
767
768Returns non-zero on success.
769
770=cut
771*/
772
35891892 773int
faa9b3e7 774i_ft2_cp(FT2_Fonthandle *handle, i_img *im, int tx, int ty, int channel,
4f68b48f 775 double cheight, double cwidth, char const *text, int len, int align,
faa9b3e7
TC
776 int aa, int vlayout, int utf8) {
777 int bbox[8];
778 i_img *work;
779 i_color cl, cl2;
780 int x, y;
781
a73aeb5f
AMH
782 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",
783 handle, im, tx, ty, channel, cheight, cwidth, text, len));
784
faa9b3e7
TC
785 if (vlayout && !FT_HAS_VERTICAL(handle->face)) {
786 i_push_error(0, "face has no vertical metrics");
787 return 0;
788 }
789
790 if (!i_ft2_bbox_r(handle, cheight, cwidth, text, len, vlayout, utf8, bbox))
791 return 0;
792
793 work = i_img_empty_ch(NULL, bbox[2]-bbox[0]+1, bbox[3]-bbox[1]+1, 1);
794 cl.channel[0] = 255;
795 if (!i_ft2_text(handle, work, -bbox[0], -bbox[1], &cl, cheight, cwidth,
796 text, len, 1, aa, vlayout, utf8))
797 return 0;
798
799 if (!align) {
800 tx -= bbox[4];
801 ty += bbox[5];
802 }
803
804 /* render to the specified channel */
805 /* this will be sped up ... */
806 for (y = 0; y < work->ysize; ++y) {
807 for (x = 0; x < work->xsize; ++x) {
808 i_gpix(work, x, y, &cl);
809 i_gpix(im, tx + x + bbox[0], ty + y + bbox[1], &cl2);
810 cl2.channel[channel] = cl.channel[0];
811 i_ppix(im, tx + x + bbox[0], ty + y + bbox[1], &cl2);
812 }
813 }
a73aeb5f 814 i_img_destroy(work);
faa9b3e7
TC
815 return 1;
816}
817
3dec2c92
TC
818/*
819=item i_ft2_has_chars(handle, char *text, int len, int utf8, char *out)
820
821Check if the given characters are defined by the font.
822
823Returns the number of characters that were checked.
824
825=cut
826*/
4f68b48f
TC
827int i_ft2_has_chars(FT2_Fonthandle *handle, char const *text, int len,
828 int utf8, char *out) {
3dec2c92 829 int count = 0;
eeaa33fd 830 mm_log((1, "i_ft2_has_chars(handle %p, text %p, len %d, utf8 %d)\n",
3dec2c92
TC
831 handle, text, len, utf8));
832
833 while (len) {
834 unsigned long c;
835 int index;
836 if (utf8) {
4f68b48f 837 c = i_utf8_advance(&text, &len);
3dec2c92
TC
838 if (c == ~0UL) {
839 i_push_error(0, "invalid UTF8 character");
840 return 0;
841 }
842 }
843 else {
844 c = (unsigned char)*text++;
845 --len;
846 }
847
848 index = FT_Get_Char_Index(handle->face, c);
849 *out++ = index != 0;
850 ++count;
851 }
852
853 return count;
854}
855
faa9b3e7
TC
856/* uses a method described in fterrors.h to build an error translation
857 function
858*/
1a4a5b11 859#undef __FTERRORS_H__
faa9b3e7 860#define FT_ERRORDEF(e, v, s) case v: i_push_error(code, s); return;
1a4a5b11
TC
861#define FT_ERROR_START_LIST
862#define FT_ERROR_END_LIST
faa9b3e7
TC
863
864/*
865=back
866
867=head2 Internal Functions
868
869These functions are used in the implementation of freetyp2.c and should not
870(usually cannot) be called from outside it.
871
872=over
873
874=item ft2_push_message(int code)
875
876Pushes an error message corresponding to code onto the error stack.
877
878=cut
879*/
880static void ft2_push_message(int code) {
881 char unknown[40];
882
883 switch (code) {
884#include FT_ERRORS_H
885 }
886
887 sprintf(unknown, "Unknown Freetype2 error code 0x%04X\n", code);
888 i_push_error(code, unknown);
889}
890
891/*
892=item make_bmp_map(FT_Bitmap *bitmap, unsigned char *map)
893
894Creates a map to convert grey levels from the glyphs bitmap into
895values scaled 0..255.
896
897=cut
898*/
899static int
900make_bmp_map(FT_Bitmap *bitmap, unsigned char *map) {
901 int scale;
902 int i;
903
904 switch (bitmap->pixel_mode) {
905 case ft_pixel_mode_grays:
906 scale = bitmap->num_grays;
907 break;
908
909 default:
910 i_push_errorf(0, "I can't handle pixel mode %d", bitmap->pixel_mode);
911 return 0;
912 }
913
914 /* build the table */
915 for (i = 0; i < scale; ++i)
916 map[i] = i * 255 / (bitmap->num_grays - 1);
917
918 return 1;
919}
920
042cdaea
TC
921/* FREETYPE_PATCH was introduced in 2.0.6, we don't want a false
922 positive on 2.0.0 to 2.0.4, so we accept a false negative in 2.0.5 */
923#ifndef FREETYPE_PATCH
924#define FREETYPE_PATCH 4
925#endif
926
927/* FT_Get_Postscript_Name() was introduced in FT2.0.5 */
928#define IM_HAS_FACE_NAME (FREETYPE_MINOR > 0 || FREETYPE_PATCH >= 5)
929/* #define IM_HAS_FACE_NAME 0 */
930
931/*
932=item i_ft2_face_name(handle, name_buf, name_buf_size)
933
934Fills the given buffer with the Postscript Face name of the font,
935if there is one.
936
937=cut
938*/
939
3799c4d1
TC
940int
941i_ft2_face_name(FT2_Fonthandle *handle, char *name_buf, size_t name_buf_size) {
042cdaea 942#if IM_HAS_FACE_NAME
3799c4d1
TC
943 char const *name = FT_Get_Postscript_Name(handle->face);
944
945 i_clear_error();
946
947 if (name) {
948 strncpy(name_buf, name, name_buf_size);
949 name_buf[name_buf_size-1] = '\0';
950
951 return strlen(name) + 1;
952 }
953 else {
954 i_push_error(0, "no face name available");
955 *name_buf = '\0';
956
957 return 0;
958 }
042cdaea
TC
959#else
960 i_clear_error();
961 i_push_error(0, "Freetype 2.0.6 or later required");
962 *name_buf = '\0';
963
964 return 0;
965#endif
966}
967
968int
969i_ft2_can_face_name(void) {
970 return IM_HAS_FACE_NAME;
3799c4d1
TC
971}
972
72e425d5
TC
973/* FT_Has_PS_Glyph_Names() was introduced in FT2.1.1 */
974/* well, I assume FREETYPE_MAJOR is 2, since we're here */
975#if FREETYPE_MINOR < 1 || (FREETYPE_MINOR == 1 && FREETYPE_PATCH < 1)
976#define FT_Has_PS_Glyph_Names(face) (FT_HAS_GLYPH_NAMES(face))
977#endif
978
3799c4d1 979int
a4168bea
TC
980i_ft2_glyph_name(FT2_Fonthandle *handle, unsigned long ch, char *name_buf,
981 size_t name_buf_size, int reliable_only) {
3799c4d1
TC
982#ifdef FT_CONFIG_OPTION_NO_GLYPH_NAMES
983 i_clear_error();
984 *name_buf = '\0';
985 i_push_error(0, "FT2 configured without glyph name support");
986
987 return 0;
988#else
a4168bea
TC
989 FT_UInt index;
990
3799c4d1
TC
991 i_clear_error();
992
a4168bea
TC
993 if (!FT_HAS_GLYPH_NAMES(handle->face)) {
994 i_push_error(0, "no glyph names in font");
995 *name_buf = '\0';
996 return 0;
997 }
998 if (reliable_only && !FT_Has_PS_Glyph_Names(handle->face)) {
999 i_push_error(0, "no reliable glyph names in font - set reliable_only to 0 to try anyway");
1000 *name_buf = '\0';
1001 return 0;
1002 }
3799c4d1 1003
a4168bea
TC
1004 index = FT_Get_Char_Index(handle->face, ch);
1005
1006 if (index) {
1007 FT_Error error = FT_Get_Glyph_Name(handle->face, index, name_buf,
1008 name_buf_size);
1009 if (error) {
1010 ft2_push_message(error);
1011 *name_buf = '\0';
35891892 1012 return 0;
a4168bea
TC
1013 }
1014 if (*name_buf) {
1015 return strlen(name_buf) + 1;
3799c4d1
TC
1016 }
1017 else {
3799c4d1
TC
1018 return 0;
1019 }
1020 }
1021 else {
a4168bea
TC
1022 i_push_error(0, "no glyph for that character");
1023 *name_buf = 0;
3799c4d1
TC
1024 return 0;
1025 }
1026#endif
1027}
1028
1029int
1030i_ft2_can_do_glyph_names(void) {
1031#ifdef FT_CONFIG_OPTION_NO_GLYPH_NAMES
1032 return 0;
1033#else
1034 return 1;
1035#endif
1036}
1037
1038int
1039i_ft2_face_has_glyph_names(FT2_Fonthandle *handle) {
1040#ifdef FT_CONFIG_OPTION_NO_GLYPH_NAMES
1041 return 0;
1042#else
1043 return FT_Has_PS_Glyph_Names(handle->face);
1044#endif
1045}
1046
3e882362
TC
1047int
1048i_ft2_is_multiple_master(FT2_Fonthandle *handle) {
1049 i_clear_error();
1050#ifdef IM_FT2_MM
1051 return handle->has_mm;
1052#else
1053 return 0;
1054#endif
1055}
1056
1057int
1058i_ft2_get_multiple_masters(FT2_Fonthandle *handle, i_font_mm *mm) {
1059#ifdef IM_FT2_MM
1060 int i;
1061 FT_Multi_Master *mms = &handle->mm;
1062
1063 i_clear_error();
1064 if (!handle->has_mm) {
1065 i_push_error(0, "Font has no multiple masters");
1066 return 0;
1067 }
1068 mm->num_axis = mms->num_axis;
1069 mm->num_designs = mms->num_designs;
1070 for (i = 0; i < mms->num_axis; ++i) {
1071 mm->axis[i].name = mms->axis[i].name;
1072 mm->axis[i].minimum = mms->axis[i].minimum;
1073 mm->axis[i].maximum = mms->axis[i].maximum;
1074 }
1075
1076 return 1;
1077#else
1078 i_clear_error();
1079 i_push_error(0, "Multiple master functions unavailable");
1080 return 0;
1081#endif
1082}
1083
1084int
1085i_ft2_set_mm_coords(FT2_Fonthandle *handle, int coord_count, long *coords) {
1086#ifdef IM_FT2_MM
1087 int i;
1088 FT_Long ftcoords[T1_MAX_MM_AXIS];
1089 FT_Error error;
1090
1091 i_clear_error();
1092 if (!handle->has_mm) {
1093 i_push_error(0, "Font has no multiple masters");
1094 return 0;
1095 }
1096 if (coord_count != handle->mm.num_axis) {
1097 i_push_error(0, "Number of MM coords doesn't match MM axis count");
1098 return 0;
1099 }
1100 for (i = 0; i < coord_count; ++i)
1101 ftcoords[i] = coords[i];
1102
1103 error = FT_Set_MM_Design_Coordinates(handle->face, coord_count, ftcoords);
1104 if (error) {
1105 ft2_push_message(error);
1106 return 0;
1107 }
1108
1109 return 1;
1110#else
1111 i_clear_error();
1112 i_push_error(0, "Multiple master functions unavailable");
1113
1114 return 0;
1115#endif
1116}
1117
faa9b3e7
TC
1118/*
1119=back
1120
1121=head1 AUTHOR
1122
1123Tony Cook <tony@develop-help.com>, with a fair amount of help from
1124reading the code in font.c.
1125
1126=head1 SEE ALSO
1127
1128font.c, Imager::Font(3), Imager(3)
1129
1130http://www.freetype.org/
1131
1132=cut
1133*/
1134