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