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