hide or rename any symbols that are likely to conflict with other
[imager.git] / freetyp2.c
CommitLineData
faa9b3e7
TC
1/*
2=head1 NAME
3
4freetyp2.c - font support via the FreeType library version 2.
5
6=head1 SYNOPSIS
7
8 if (!i_ft2_init()) { error }
9 FT2_Fonthandle *font;
10 font = i_ft2_new(name, index);
11 if (!i_ft2_setdpi(font, xdpi, ydpi)) { error }
12 if (!i_ft2_getdpi(font, &xdpi, &ydpi)) { error }
13 double matrix[6];
14 if (!i_ft2_settransform(font, matrix)) { error }
15 int bbox[6];
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
37#include "image.h"
38#include <stdio.h>
39#include <ft2build.h>
40#include FT_FREETYPE_H
41
42static void ft2_push_message(int code);
43static unsigned long utf8_advance(char **p, int *len);
44
45static FT_Library library;
46
47/*
48=item i_ft2_init(void)
49
50Initializes the Freetype 2 library.
51
52Returns true on success, false on failure.
53
54=cut
55*/
56int
57i_ft2_init(void) {
58 FT_Error error;
59
60 i_clear_error();
61 error = FT_Init_FreeType(&library);
62 if (error) {
63 ft2_push_message(error);
64 i_push_error(0, "Initializing Freetype2");
65 return 0;
66 }
67 return 1;
68}
69
70struct FT2_Fonthandle {
71 FT_Face face;
72 int xdpi, ydpi;
73 int hint;
db6d10cc 74 FT_Encoding encoding;
faa9b3e7
TC
75
76 /* used to adjust so we can align the draw point to the top-left */
77 double matrix[6];
78};
79
db6d10cc
TC
80/* the following is used to select a "best" encoding */
81static struct enc_score {
82 FT_Encoding encoding;
83 int score;
84} enc_scores[] =
85{
86 /* the selections here are fairly arbitrary
87 ideally we need to give the user a list of encodings available
88 and a mechanism to choose one */
89 { ft_encoding_unicode, 10 },
90 { ft_encoding_sjis, 8 },
91 { ft_encoding_gb2312, 8 },
92 { ft_encoding_big5, 8 },
93 { ft_encoding_wansung, 8 },
94 { ft_encoding_johab, 8 },
95 { ft_encoding_latin_2, 6 },
96 { ft_encoding_apple_roman, 6 },
97 { ft_encoding_adobe_standard, 6 },
98 { ft_encoding_adobe_expert, 6 },
99};
100
faa9b3e7
TC
101/*
102=item i_ft2_new(char *name, int index)
103
104Creates a new font object, from the file given by I<name>. I<index>
105is the index of the font in a file with multiple fonts, where 0 is the
106first font.
107
108Return NULL on failure.
109
110=cut
111*/
112
113FT2_Fonthandle *
114i_ft2_new(char *name, int index) {
115 FT_Error error;
116 FT2_Fonthandle *result;
117 FT_Face face;
118 double matrix[6] = { 1, 0, 0,
119 0, 1, 0 };
db6d10cc
TC
120 int i, j;
121 FT_Encoding encoding;
122 int score;
faa9b3e7 123
a73aeb5f
AMH
124 mm_log((1, "i_ft2_new(name %p, index %d)\n", name, index));
125
faa9b3e7
TC
126 i_clear_error();
127 error = FT_New_Face(library, name, index, &face);
128 if (error) {
129 ft2_push_message(error);
130 i_push_error(error, "Opening face");
131 return NULL;
132 }
133
db6d10cc
TC
134 encoding = face->num_charmaps ? face->charmaps[0]->encoding : ft_encoding_unicode;
135 score = 0;
136 for (i = 0; i < face->num_charmaps; ++i) {
137 FT_Encoding enc_entry = face->charmaps[i]->encoding;
138 mm_log((2, "i_ft2_new, encoding %lX platform %u encoding %u\n",
139 enc_entry, face->charmaps[i]->platform_id,
140 face->charmaps[i]->encoding_id));
141 for (j = 0; j < sizeof(enc_scores) / sizeof(*enc_scores); ++j) {
142 if (enc_scores[j].encoding == enc_entry && enc_scores[j].score > score) {
143 encoding = enc_entry;
144 score = enc_scores[j].score;
145 break;
146 }
147 }
148 }
149 FT_Select_Charmap(face, encoding);
150 mm_log((2, "i_ft2_new, selected encoding %lX\n", encoding));
151
faa9b3e7
TC
152 result = mymalloc(sizeof(FT2_Fonthandle));
153 result->face = face;
154 result->xdpi = result->ydpi = 72;
db6d10cc 155 result->encoding = encoding;
faa9b3e7
TC
156
157 /* by default we disable hinting on a call to i_ft2_settransform()
158 if we don't do this, then the hinting can the untransformed text
159 to be a different size to the transformed text.
160 Obviously we have it initially enabled.
161 */
162 result->hint = 1;
163
164 /* I originally forgot this: :/ */
165 /*i_ft2_settransform(result, matrix); */
166 result->matrix[0] = 1; result->matrix[1] = 0; result->matrix[2] = 0;
167 result->matrix[3] = 0; result->matrix[4] = 1; result->matrix[5] = 0;
168
169 return result;
170}
171
172/*
173=item i_ft2_destroy(FT2_Fonthandle *handle)
174
175Destroys a font object, which must have been the return value of
176i_ft2_new().
177
178=cut
179*/
180void
181i_ft2_destroy(FT2_Fonthandle *handle) {
182 FT_Done_Face(handle->face);
183 myfree(handle);
184}
185
186/*
187=item i_ft2_setdpi(FT2_Fonthandle *handle, int xdpi, int ydpi)
188
189Sets the resolution in dots per inch at which point sizes scaled, by
190default xdpi and ydpi are 72, so that 1 point maps to 1 pixel.
191
192Both xdpi and ydpi should be positive.
193
194Return true on success.
195
196=cut
197*/
198int
199i_ft2_setdpi(FT2_Fonthandle *handle, int xdpi, int ydpi) {
200 i_clear_error();
201 if (xdpi > 0 && ydpi > 0) {
202 handle->xdpi = xdpi;
203 handle->ydpi = ydpi;
204 return 0;
205 }
206 else {
207 i_push_error(0, "resolutions must be positive");
208 return 0;
209 }
210}
211
212/*
213=item i_ft2_getdpi(FT2_Fonthandle *handle, int *xdpi, int *ydpi)
214
215Retrieves the current horizontal and vertical resolutions at which
216point sizes are scaled.
217
218=cut
219*/
220int
221i_ft2_getdpi(FT2_Fonthandle *handle, int *xdpi, int *ydpi) {
222 *xdpi = handle->xdpi;
223 *ydpi = handle->ydpi;
224
225 return 1;
226}
227
228/*
229=item i_ft2_settransform(FT2_FontHandle *handle, double *matrix)
230
231Sets a transormation matrix for output.
232
233This should be a 2 x 3 matrix like:
234
235 matrix[0] matrix[1] matrix[2]
236 matrix[3] matrix[4] matrix[5]
237
238=cut
239*/
240int
241i_ft2_settransform(FT2_Fonthandle *handle, double *matrix) {
242 FT_Matrix m;
243 FT_Vector v;
244 int i;
245
246 m.xx = matrix[0] * 65536;
247 m.xy = matrix[1] * 65536;
248 v.x = matrix[2]; /* this could be pels of 26.6 fixed - not sure */
249 m.yx = matrix[3] * 65536;
250 m.yy = matrix[4] * 65536;
251 v.y = matrix[5]; /* see just above */
252
253 FT_Set_Transform(handle->face, &m, &v);
254
255 for (i = 0; i < 6; ++i)
256 handle->matrix[i] = matrix[i];
257 handle->hint = 0;
258
259 return 1;
260}
261
262/*
263=item i_ft2_sethinting(FT2_Fonthandle *handle, int hinting)
264
265If hinting is non-zero then glyph hinting is enabled, otherwise disabled.
266
267i_ft2_settransform() disables hinting to prevent distortions in
268gradual text transformations.
269
270=cut
271*/
272int i_ft2_sethinting(FT2_Fonthandle *handle, int hinting) {
273 handle->hint = hinting;
274 return 1;
275}
276
277/*
278=item i_ft2_bbox(FT2_Fonthandle *handle, double cheight, double cwidth, char *text, int len, int *bbox)
279
280Retrieves bounding box information for the font at the given
281character width and height. This ignores the transformation matrix.
282
283Returns non-zero on success.
284
285=cut
286*/
287int
288i_ft2_bbox(FT2_Fonthandle *handle, double cheight, double cwidth,
5cb9270b 289 char *text, int len, int *bbox, int utf8) {
faa9b3e7
TC
290 FT_Error error;
291 int width;
292 int index;
293 int first;
294 int ascent = 0, descent = 0;
295 int glyph_ascent, glyph_descent;
296 FT_Glyph_Metrics *gm;
297 int start = 0;
298
a73aeb5f
AMH
299 mm_log((1, "i_ft2_bbox(handle %p, cheight %f, cwidth %f, text %p, len %d, bbox %p)\n",
300 handle, cheight, cwidth, text, len, bbox));
301
faa9b3e7
TC
302 error = FT_Set_Char_Size(handle->face, cwidth*64, cheight*64,
303 handle->xdpi, handle->ydpi);
304 if (error) {
305 ft2_push_message(error);
306 i_push_error(0, "setting size");
307 }
308
309 first = 1;
310 width = 0;
5cb9270b
TC
311 while (len) {
312 unsigned long c;
313 if (utf8) {
314 c = utf8_advance(&text, &len);
315 if (c == ~0UL) {
316 i_push_error(0, "invalid UTF8 character");
317 return 0;
318 }
319 }
320 else {
321 c = (unsigned char)*text++;
322 --len;
323 }
324
faa9b3e7
TC
325 index = FT_Get_Char_Index(handle->face, c);
326 error = FT_Load_Glyph(handle->face, index, FT_LOAD_DEFAULT);
327 if (error) {
328 ft2_push_message(error);
329 i_push_errorf(0, "loading glyph for character \\x%02x (glyph 0x%04X)",
330 c, index);
331 return 0;
332 }
333 gm = &handle->face->glyph->metrics;
334 glyph_ascent = gm->horiBearingY / 64;
335 glyph_descent = glyph_ascent - gm->height/64;
336 if (first) {
337 start = gm->horiBearingX / 64;
338 /* handles -ve values properly */
339 ascent = glyph_ascent;
340 descent = glyph_descent;
341 first = 0;
342 }
343
344 if (glyph_ascent > ascent)
345 ascent = glyph_ascent;
b72644d0 346 if (glyph_descent < descent)
faa9b3e7
TC
347 descent = glyph_descent;
348
349 width += gm->horiAdvance / 64;
350
351 if (len == 0) {
352 /* last character
353 handle the case where the right the of the character overlaps the
354 right*/
355 int rightb = gm->horiAdvance - gm->horiBearingX - gm->width;
356 if (rightb < 0)
357 width -= rightb / 64;
358 }
359 }
360
361 bbox[0] = start;
90654457
TC
362 bbox[1] = handle->face->size->metrics.descender / 64;
363 bbox[2] = width;
364 bbox[3] = handle->face->size->metrics.ascender / 64;
faa9b3e7
TC
365 bbox[4] = descent;
366 bbox[5] = ascent;
367
368 return 1;
369}
370
371/*
372=item transform_box(FT2_FontHandle *handle, int bbox[4])
373
374bbox contains coorinates of a the top-left and bottom-right of a bounding
375box relative to a point.
376
377This is then transformed and the values in bbox[4] are the top-left
378and bottom-right of the new bounding box.
379
380This is meant to provide the bounding box of a transformed character
381box. The problem is that if the character was round and is rotated,
382the real bounding box isn't going to be much different from the
383original, but this function will return a _bigger_ bounding box. I
384suppose I could work my way through the glyph outline, but that's
385too much hard work.
386
387=cut
388*/
389void ft2_transform_box(FT2_Fonthandle *handle, int bbox[4]) {
390 double work[8];
391 double *matrix = handle->matrix;
392 int i;
393
394 work[0] = matrix[0] * bbox[0] + matrix[1] * bbox[1];
395 work[1] = matrix[3] * bbox[0] + matrix[4] * bbox[1];
396 work[2] = matrix[0] * bbox[2] + matrix[1] * bbox[1];
397 work[3] = matrix[3] * bbox[2] + matrix[4] * bbox[1];
398 work[4] = matrix[0] * bbox[0] + matrix[1] * bbox[3];
399 work[5] = matrix[3] * bbox[0] + matrix[4] * bbox[3];
400 work[6] = matrix[0] * bbox[2] + matrix[1] * bbox[3];
401 work[7] = matrix[3] * bbox[2] + matrix[4] * bbox[3];
402
b33c08f8
TC
403 bbox[0] = floor(i_min(i_min(work[0], work[2]),i_min(work[4], work[6])));
404 bbox[1] = floor(i_min(i_min(work[1], work[3]),i_min(work[5], work[7])));
405 bbox[2] = ceil(i_max(i_max(work[0], work[2]),i_max(work[4], work[6])));
406 bbox[3] = ceil(i_max(i_max(work[1], work[3]),i_max(work[5], work[7])));
faa9b3e7
TC
407}
408
409/*
410=item expand_bounds(int bbox[4], int bbox2[4])
411
412Treating bbox[] and bbox2[] as 2 bounding boxes, produces a new
413bounding box in bbox[] that encloses both.
414
415=cut
416*/
417static void expand_bounds(int bbox[4], int bbox2[4]) {
b33c08f8
TC
418 bbox[0] = i_min(bbox[0], bbox2[0]);
419 bbox[1] = i_min(bbox[1], bbox2[1]);
420 bbox[2] = i_max(bbox[2], bbox2[2]);
421 bbox[3] = i_max(bbox[3], bbox2[3]);
faa9b3e7
TC
422}
423
424/*
425=item i_ft2_bbox_r(FT2_Fonthandle *handle, double cheight, double cwidth, char *text, int len, int vlayout, int utf8, int *bbox)
426
427Retrieves bounding box information for the font at the given
428character width and height.
429
430This version finds the rectangular bounding box of the glyphs, with
431the text as transformed by the transformation matrix. As with
432i_ft2_bbox (bbox[0], bbox[1]) will the the offset from the start of
433the topline to the top-left of the bounding box. Unlike i_ft2_bbox()
434this could be near the bottom left corner of the box.
435
436(bbox[4], bbox[5]) is the offset to the start of the baseline.
437(bbox[6], bbox[7]) is the offset from the start of the baseline to the
438end of the baseline.
439
440Returns non-zero on success.
441
442=cut
443*/
444int
445i_ft2_bbox_r(FT2_Fonthandle *handle, double cheight, double cwidth,
446 char *text, int len, int vlayout, int utf8, int *bbox) {
447 FT_Error error;
448 int width;
449 int index;
450 int first;
451 int ascent = 0, descent = 0;
452 int glyph_ascent, glyph_descent;
453 FT_Glyph_Metrics *gm;
454 int start = 0;
455 int work[4];
456 int bounds[4];
457 double x = 0, y = 0;
458 int i;
459 FT_GlyphSlot slot;
460 int advx, advy;
461 int loadFlags = FT_LOAD_DEFAULT;
462
463 if (vlayout)
464 loadFlags |= FT_LOAD_VERTICAL_LAYOUT;
465
466 error = FT_Set_Char_Size(handle->face, cwidth*64, cheight*64,
467 handle->xdpi, handle->ydpi);
468 if (error) {
469 ft2_push_message(error);
470 i_push_error(0, "setting size");
471 }
472
473 first = 1;
474 width = 0;
475 while (len) {
476 unsigned long c;
477 if (utf8) {
478 c = utf8_advance(&text, &len);
479 if (c == ~0UL) {
480 i_push_error(0, "invalid UTF8 character");
481 return 0;
482 }
483 }
484 else {
485 c = (unsigned char)*text++;
486 --len;
487 }
488
489 index = FT_Get_Char_Index(handle->face, c);
490 error = FT_Load_Glyph(handle->face, index, loadFlags);
491 if (error) {
492 ft2_push_message(error);
493 i_push_errorf(0, "loading glyph for character \\x%02x (glyph 0x%04X)",
494 c, index);
495 return 0;
496 }
497 slot = handle->face->glyph;
498 gm = &slot->metrics;
499
500 /* these probably don't mean much for vertical layouts */
501 glyph_ascent = gm->horiBearingY / 64;
502 glyph_descent = glyph_ascent - gm->height/64;
503 if (vlayout) {
504 work[0] = gm->vertBearingX;
505 work[1] = gm->vertBearingY;
506 }
507 else {
508 work[0] = gm->horiBearingX;
509 work[1] = gm->horiBearingY;
510 }
511 work[2] = gm->width + work[0];
512 work[3] = work[1] - gm->height;
513 if (first) {
514 bbox[4] = work[0] * handle->matrix[0] + work[1] * handle->matrix[1] + handle->matrix[2];
515 bbox[5] = work[0] * handle->matrix[3] + work[1] * handle->matrix[4] + handle->matrix[5];
516 bbox[4] = bbox[4] < 0 ? -(-bbox[4] + 32)/64 : (bbox[4] + 32) / 64;
517 bbox[5] /= 64;
518 }
519 ft2_transform_box(handle, work);
520 for (i = 0; i < 4; ++i)
521 work[i] /= 64;
522 work[0] += x;
523 work[1] += y;
524 work[2] += x;
525 work[3] += y;
526 if (first) {
527 for (i = 0; i < 4; ++i)
528 bounds[i] = work[i];
529 ascent = glyph_ascent;
530 descent = glyph_descent;
531 first = 0;
532 }
533 else {
534 expand_bounds(bounds, work);
535 }
536 x += slot->advance.x / 64;
537 y += slot->advance.y / 64;
538
539 if (glyph_ascent > ascent)
540 ascent = glyph_ascent;
541 if (glyph_descent > descent)
542 descent = glyph_descent;
543
544 if (len == 0) {
545 /* last character
546 handle the case where the right the of the character overlaps the
547 right*/
548 /*int rightb = gm->horiAdvance - gm->horiBearingX - gm->width;
549 if (rightb < 0)
550 width -= rightb / 64;*/
551 }
552 }
553
554 /* at this point bounds contains the bounds relative to the CP,
555 and x, y hold the final position relative to the CP */
556 /*bounds[0] -= x;
557 bounds[1] -= y;
558 bounds[2] -= x;
559 bounds[3] -= y;*/
560
561 bbox[0] = bounds[0];
562 bbox[1] = -bounds[3];
563 bbox[2] = bounds[2];
564 bbox[3] = -bounds[1];
565 bbox[6] = x;
566 bbox[7] = -y;
567
568 return 1;
569}
570
571
572
573static int
574make_bmp_map(FT_Bitmap *bitmap, unsigned char *map);
575
576/*
577=item i_ft2_text(FT2_Fonthandle *handle, i_img *im, int tx, int ty, i_color *cl, double cheight, double cwidth, char *text, int len, int align, int aa)
578
579Renders I<text> to (I<tx>, I<ty>) in I<im> using color I<cl> at the given
580I<cheight> and I<cwidth>.
581
582If align is 0, then the text is rendered with the top-left of the
583first character at (I<tx>, I<ty>). If align is non-zero then the text
584is rendered with (I<tx>, I<ty>) aligned with the base-line of the
585characters.
586
587If aa is non-zero then the text is anti-aliased.
588
589Returns non-zero on success.
590
591=cut
592*/
593int
594i_ft2_text(FT2_Fonthandle *handle, i_img *im, int tx, int ty, i_color *cl,
595 double cheight, double cwidth, char *text, int len, int align,
596 int aa, int vlayout, int utf8) {
597 FT_Error error;
598 int index;
599 FT_Glyph_Metrics *gm;
600 int bbox[6];
601 FT_GlyphSlot slot;
602 int x, y;
603 unsigned char *bmp;
604 unsigned char map[256];
605 char last_mode = ft_pixel_mode_none;
606 int last_grays = -1;
607 int ch;
608 i_color pel;
609 int loadFlags = FT_LOAD_DEFAULT;
610
a73aeb5f
AMH
611 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",
612 handle, im, tx, ty, cl, cheight, cwidth, text, align, aa));
613
faa9b3e7
TC
614 if (vlayout) {
615 if (!FT_HAS_VERTICAL(handle->face)) {
616 i_push_error(0, "face has no vertical metrics");
617 return 0;
618 }
619 loadFlags |= FT_LOAD_VERTICAL_LAYOUT;
620 }
621 if (!handle->hint)
622 loadFlags |= FT_LOAD_NO_HINTING;
623
624 /* set the base-line based on the string ascent */
5cb9270b 625 if (!i_ft2_bbox(handle, cheight, cwidth, text, len, bbox, utf8))
faa9b3e7
TC
626 return 0;
627
628 if (!align) {
629 /* this may need adjustment */
630 tx -= bbox[0] * handle->matrix[0] + bbox[5] * handle->matrix[1] + handle->matrix[2];
631 ty += bbox[0] * handle->matrix[3] + bbox[5] * handle->matrix[4] + handle->matrix[5];
632 }
633 while (len) {
634 unsigned long c;
635 if (utf8) {
636 c = utf8_advance(&text, &len);
637 if (c == ~0UL) {
638 i_push_error(0, "invalid UTF8 character");
639 return 0;
640 }
641 }
642 else {
643 c = (unsigned char)*text++;
644 --len;
645 }
646
647 index = FT_Get_Char_Index(handle->face, c);
648 error = FT_Load_Glyph(handle->face, index, loadFlags);
649 if (error) {
650 ft2_push_message(error);
651 i_push_errorf(0, "loading glyph for character \\x%02x (glyph 0x%04X)",
652 c, index);
653 return 0;
654 }
655 slot = handle->face->glyph;
656 gm = &slot->metrics;
657
658 error = FT_Render_Glyph(slot, aa ? ft_render_mode_normal : ft_render_mode_mono);
659 if (error) {
660 ft2_push_message(error);
661 i_push_errorf(0, "rendering glyph 0x%04X (character \\x%02X)");
662 return 0;
663 }
664 if (slot->bitmap.pixel_mode == ft_pixel_mode_mono) {
665 bmp = slot->bitmap.buffer;
666 for (y = 0; y < slot->bitmap.rows; ++y) {
667 int pos = 0;
668 int bit = 0x80;
669 for (x = 0; x < slot->bitmap.width; ++x) {
670 if (bmp[pos] & bit)
671 i_ppix(im, tx+x+slot->bitmap_left, ty+y-slot->bitmap_top, cl);
672
673 bit >>= 1;
674 if (bit == 0) {
675 bit = 0x80;
676 ++pos;
677 }
678 }
679 bmp += slot->bitmap.pitch;
680 }
681 }
682 else {
683 /* grey scale or something we can treat as greyscale */
684 /* we create a map to convert from the bitmap values to 0-255 */
685 if (last_mode != slot->bitmap.pixel_mode
686 || last_grays != slot->bitmap.num_grays) {
687 if (!make_bmp_map(&slot->bitmap, map))
688 return 0;
689 last_mode = slot->bitmap.pixel_mode;
690 last_grays = slot->bitmap.num_grays;
691 }
692
693 bmp = slot->bitmap.buffer;
694 for (y = 0; y < slot->bitmap.rows; ++y) {
695 for (x = 0; x < slot->bitmap.width; ++x) {
696 int value = map[bmp[x]];
697 if (value) {
698 i_gpix(im, tx+x+slot->bitmap_left, ty+y-slot->bitmap_top, &pel);
699 for (ch = 0; ch < im->channels; ++ch) {
700 pel.channel[ch] =
701 ((255-value)*pel.channel[ch] + value * cl->channel[ch]) / 255;
702 }
703 i_ppix(im, tx+x+slot->bitmap_left, ty+y-slot->bitmap_top, &pel);
704 }
705 }
706 bmp += slot->bitmap.pitch;
707 }
708 }
709
710 tx += slot->advance.x / 64;
711 ty -= slot->advance.y / 64;
712 }
713
714 return 1;
715}
716
717/*
718=item i_ft2_cp(FT2_Fonthandle *handle, i_img *im, int tx, int ty, int channel, double cheight, double cwidth, char *text, int len, int align, int aa)
719
720Renders I<text> to (I<tx>, I<ty>) in I<im> to I<channel> at the given
721I<cheight> and I<cwidth>.
722
723If align is 0, then the text is rendered with the top-left of the
724first character at (I<tx>, I<ty>). If align is non-zero then the text
725is rendered with (I<tx>, I<ty>) aligned with the base-line of the
726characters.
727
728If aa is non-zero then the text is anti-aliased.
729
730Returns non-zero on success.
731
732=cut
733*/
734
735i_ft2_cp(FT2_Fonthandle *handle, i_img *im, int tx, int ty, int channel,
736 double cheight, double cwidth, char *text, int len, int align,
737 int aa, int vlayout, int utf8) {
738 int bbox[8];
739 i_img *work;
740 i_color cl, cl2;
741 int x, y;
742
a73aeb5f
AMH
743 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",
744 handle, im, tx, ty, channel, cheight, cwidth, text, len));
745
faa9b3e7
TC
746 if (vlayout && !FT_HAS_VERTICAL(handle->face)) {
747 i_push_error(0, "face has no vertical metrics");
748 return 0;
749 }
750
751 if (!i_ft2_bbox_r(handle, cheight, cwidth, text, len, vlayout, utf8, bbox))
752 return 0;
753
754 work = i_img_empty_ch(NULL, bbox[2]-bbox[0]+1, bbox[3]-bbox[1]+1, 1);
755 cl.channel[0] = 255;
756 if (!i_ft2_text(handle, work, -bbox[0], -bbox[1], &cl, cheight, cwidth,
757 text, len, 1, aa, vlayout, utf8))
758 return 0;
759
760 if (!align) {
761 tx -= bbox[4];
762 ty += bbox[5];
763 }
764
765 /* render to the specified channel */
766 /* this will be sped up ... */
767 for (y = 0; y < work->ysize; ++y) {
768 for (x = 0; x < work->xsize; ++x) {
769 i_gpix(work, x, y, &cl);
770 i_gpix(im, tx + x + bbox[0], ty + y + bbox[1], &cl2);
771 cl2.channel[channel] = cl.channel[0];
772 i_ppix(im, tx + x + bbox[0], ty + y + bbox[1], &cl2);
773 }
774 }
a73aeb5f 775 i_img_destroy(work);
faa9b3e7
TC
776 return 1;
777}
778
3dec2c92
TC
779/*
780=item i_ft2_has_chars(handle, char *text, int len, int utf8, char *out)
781
782Check if the given characters are defined by the font.
783
784Returns the number of characters that were checked.
785
786=cut
787*/
788int i_ft2_has_chars(FT2_Fonthandle *handle, char *text, int len, int utf8,
789 char *out) {
790 int count = 0;
791 mm_log((1, "i_ft2_check_chars(handle %p, text %p, len %d, utf8 %d)\n",
792 handle, text, len, utf8));
793
794 while (len) {
795 unsigned long c;
796 int index;
797 if (utf8) {
798 c = utf8_advance(&text, &len);
799 if (c == ~0UL) {
800 i_push_error(0, "invalid UTF8 character");
801 return 0;
802 }
803 }
804 else {
805 c = (unsigned char)*text++;
806 --len;
807 }
808
809 index = FT_Get_Char_Index(handle->face, c);
810 *out++ = index != 0;
811 ++count;
812 }
813
814 return count;
815}
816
faa9b3e7
TC
817/* uses a method described in fterrors.h to build an error translation
818 function
819*/
820#undef __FT_ERRORS_H__
821#define FT_ERRORDEF(e, v, s) case v: i_push_error(code, s); return;
822#define FT_ERROR_START_LIST
823#define FT_ERROR_END_LIST
824
825/*
826=back
827
828=head2 Internal Functions
829
830These functions are used in the implementation of freetyp2.c and should not
831(usually cannot) be called from outside it.
832
833=over
834
835=item ft2_push_message(int code)
836
837Pushes an error message corresponding to code onto the error stack.
838
839=cut
840*/
841static void ft2_push_message(int code) {
842 char unknown[40];
843
844 switch (code) {
845#include FT_ERRORS_H
846 }
847
848 sprintf(unknown, "Unknown Freetype2 error code 0x%04X\n", code);
849 i_push_error(code, unknown);
850}
851
852/*
853=item make_bmp_map(FT_Bitmap *bitmap, unsigned char *map)
854
855Creates a map to convert grey levels from the glyphs bitmap into
856values scaled 0..255.
857
858=cut
859*/
860static int
861make_bmp_map(FT_Bitmap *bitmap, unsigned char *map) {
862 int scale;
863 int i;
864
865 switch (bitmap->pixel_mode) {
866 case ft_pixel_mode_grays:
867 scale = bitmap->num_grays;
868 break;
869
870 default:
871 i_push_errorf(0, "I can't handle pixel mode %d", bitmap->pixel_mode);
872 return 0;
873 }
874
875 /* build the table */
876 for (i = 0; i < scale; ++i)
877 map[i] = i * 255 / (bitmap->num_grays - 1);
878
879 return 1;
880}
881
882struct utf8_size {
883 int mask, expect;
884 int size;
885};
886
887struct utf8_size utf8_sizes[] =
888{
889 { 0x80, 0x00, 1 },
890 { 0xE0, 0xC0, 2 },
891 { 0xF0, 0xE0, 3 },
892 { 0xF8, 0xF0, 4 },
893};
894
895/*
896=item utf8_advance(char **p, int *len)
897
898Retreive a UTF8 character from the stream.
899
900Modifies *p and *len to indicate the consumed characters.
901
902This doesn't support the extended UTF8 encoding used by later versions
903of Perl.
904
905=cut
906*/
907
908unsigned long utf8_advance(char **p, int *len) {
909 unsigned char c;
910 int i, ci, clen = 0;
911 unsigned char codes[3];
912 if (*len == 0)
913 return ~0UL;
914 c = *(*p)++; --*len;
915
916 for (i = 0; i < sizeof(utf8_sizes)/sizeof(*utf8_sizes); ++i) {
917 if ((c & utf8_sizes[i].mask) == utf8_sizes[i].expect) {
918 clen = utf8_sizes[i].size;
919 }
920 }
921 if (clen == 0 || *len < clen-1) {
922 --*p; ++*len;
923 return ~0UL;
924 }
925
926 /* check that each character is well formed */
927 i = 1;
928 ci = 0;
929 while (i < clen) {
930 if (((*p)[ci] & 0xC0) != 0x80) {
931 --*p; ++*len;
932 return ~0UL;
933 }
934 codes[ci] = (*p)[ci];
935 ++ci; ++i;
936 }
937 *p += clen-1; *len -= clen-1;
938 if (c & 0x80) {
939 if ((c & 0xE0) == 0xC0) {
940 return ((c & 0x1F) << 6) + (codes[0] & 0x3F);
941 }
942 else if ((c & 0xF0) == 0xE0) {
943 return ((c & 0x0F) << 12) | ((codes[0] & 0x3F) << 6) | (codes[1] & 0x3f);
944 }
945 else if ((c & 0xF8) == 0xF0) {
946 return ((c & 0x07) << 18) | ((codes[0] & 0x3F) << 12)
947 | ((codes[1] & 0x3F) << 6) | (codes[2] & 0x3F);
948 }
949 else {
950 *p -= clen; *len += clen;
951 return ~0UL;
952 }
953 }
954 else {
955 return c;
956 }
957}
958
959/*
960=back
961
962=head1 AUTHOR
963
964Tony Cook <tony@develop-help.com>, with a fair amount of help from
965reading the code in font.c.
966
967=head1 SEE ALSO
968
969font.c, Imager::Font(3), Imager(3)
970
971http://www.freetype.org/
972
973=cut
974*/
975