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