4 freetyp2.c - font support via the FreeType library version 2.
8 if (!i_ft2_init()) { error }
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 }
14 if (!i_ft2_settransform(font, matrix)) { error }
16 if (!i_ft2_bbox(font, cheight, cwidth, text, length, bbox, utf8)) { error }
19 if (!i_ft2_text(font, im, tx, ty, cl, cheight, cwidth, text, length, align,
21 if (!i_ft2_cp(font, im, tx, ty, channel, cheight, cwidth, text, length,
27 Implements Imager font support using the FreeType2 library.
29 The FreeType2 library understands several font file types, including
30 Truetype, Type1 and Windows FNT.
40 #include FT_FREETYPE_H
42 static void ft2_push_message(int code);
43 static unsigned long utf8_advance(char **p, int *len);
45 static FT_Library library;
48 =item i_ft2_init(void)
50 Initializes the Freetype 2 library.
52 Returns true on success, false on failure.
61 error = FT_Init_FreeType(&library);
63 ft2_push_message(error);
64 i_push_error(0, "Initializing Freetype2");
70 struct FT2_Fonthandle {
76 /* used to adjust so we can align the draw point to the top-left */
80 /* the following is used to select a "best" encoding */
81 static struct enc_score {
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 },
102 =item i_ft2_new(char *name, int index)
104 Creates a new font object, from the file given by I<name>. I<index>
105 is the index of the font in a file with multiple fonts, where 0 is the
108 Return NULL on failure.
114 i_ft2_new(char *name, int index) {
116 FT2_Fonthandle *result;
118 double matrix[6] = { 1, 0, 0,
121 FT_Encoding encoding;
124 mm_log((1, "i_ft2_new(name %p, index %d)\n", name, index));
127 error = FT_New_Face(library, name, index, &face);
129 ft2_push_message(error);
130 i_push_error(error, "Opening face");
134 encoding = face->num_charmaps ? face->charmaps[0]->encoding : ft_encoding_unicode;
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;
149 FT_Select_Charmap(face, encoding);
150 mm_log((2, "i_ft2_new, selected encoding %lX\n", encoding));
152 result = mymalloc(sizeof(FT2_Fonthandle));
154 result->xdpi = result->ydpi = 72;
155 result->encoding = encoding;
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.
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;
173 =item i_ft2_destroy(FT2_Fonthandle *handle)
175 Destroys a font object, which must have been the return value of
181 i_ft2_destroy(FT2_Fonthandle *handle) {
182 FT_Done_Face(handle->face);
187 =item i_ft2_setdpi(FT2_Fonthandle *handle, int xdpi, int ydpi)
189 Sets the resolution in dots per inch at which point sizes scaled, by
190 default xdpi and ydpi are 72, so that 1 point maps to 1 pixel.
192 Both xdpi and ydpi should be positive.
194 Return true on success.
199 i_ft2_setdpi(FT2_Fonthandle *handle, int xdpi, int ydpi) {
201 if (xdpi > 0 && ydpi > 0) {
207 i_push_error(0, "resolutions must be positive");
213 =item i_ft2_getdpi(FT2_Fonthandle *handle, int *xdpi, int *ydpi)
215 Retrieves the current horizontal and vertical resolutions at which
216 point sizes are scaled.
221 i_ft2_getdpi(FT2_Fonthandle *handle, int *xdpi, int *ydpi) {
222 *xdpi = handle->xdpi;
223 *ydpi = handle->ydpi;
229 =item i_ft2_settransform(FT2_FontHandle *handle, double *matrix)
231 Sets a transormation matrix for output.
233 This should be a 2 x 3 matrix like:
235 matrix[0] matrix[1] matrix[2]
236 matrix[3] matrix[4] matrix[5]
241 i_ft2_settransform(FT2_Fonthandle *handle, double *matrix) {
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 */
253 FT_Set_Transform(handle->face, &m, &v);
255 for (i = 0; i < 6; ++i)
256 handle->matrix[i] = matrix[i];
263 =item i_ft2_sethinting(FT2_Fonthandle *handle, int hinting)
265 If hinting is non-zero then glyph hinting is enabled, otherwise disabled.
267 i_ft2_settransform() disables hinting to prevent distortions in
268 gradual text transformations.
272 int i_ft2_sethinting(FT2_Fonthandle *handle, int hinting) {
273 handle->hint = hinting;
278 =item i_ft2_bbox(FT2_Fonthandle *handle, double cheight, double cwidth, char *text, int len, int *bbox)
280 Retrieves bounding box information for the font at the given
281 character width and height. This ignores the transformation matrix.
283 Returns non-zero on success.
288 i_ft2_bbox(FT2_Fonthandle *handle, double cheight, double cwidth,
289 char *text, int len, int *bbox, int utf8) {
294 int ascent = 0, descent = 0;
295 int glyph_ascent, glyph_descent;
296 FT_Glyph_Metrics *gm;
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));
302 error = FT_Set_Char_Size(handle->face, cwidth*64, cheight*64,
303 handle->xdpi, handle->ydpi);
305 ft2_push_message(error);
306 i_push_error(0, "setting size");
314 c = utf8_advance(&text, &len);
316 i_push_error(0, "invalid UTF8 character");
321 c = (unsigned char)*text++;
325 index = FT_Get_Char_Index(handle->face, c);
326 error = FT_Load_Glyph(handle->face, index, FT_LOAD_DEFAULT);
328 ft2_push_message(error);
329 i_push_errorf(0, "loading glyph for character \\x%02x (glyph 0x%04X)",
333 gm = &handle->face->glyph->metrics;
334 glyph_ascent = gm->horiBearingY / 64;
335 glyph_descent = glyph_ascent - gm->height/64;
337 start = gm->horiBearingX / 64;
338 /* handles -ve values properly */
339 ascent = glyph_ascent;
340 descent = glyph_descent;
344 if (glyph_ascent > ascent)
345 ascent = glyph_ascent;
346 if (glyph_descent < descent)
347 descent = glyph_descent;
349 width += gm->horiAdvance / 64;
353 handle the case where the right the of the character overlaps the
355 int rightb = gm->horiAdvance - gm->horiBearingX - gm->width;
357 width -= rightb / 64;
362 bbox[1] = handle->face->size->metrics.descender / 64;
364 bbox[3] = handle->face->size->metrics.ascender / 64;
372 =item transform_box(FT2_FontHandle *handle, int bbox[4])
374 bbox contains coorinates of a the top-left and bottom-right of a bounding
375 box relative to a point.
377 This is then transformed and the values in bbox[4] are the top-left
378 and bottom-right of the new bounding box.
380 This is meant to provide the bounding box of a transformed character
381 box. The problem is that if the character was round and is rotated,
382 the real bounding box isn't going to be much different from the
383 original, but this function will return a _bigger_ bounding box. I
384 suppose I could work my way through the glyph outline, but that's
389 void ft2_transform_box(FT2_Fonthandle *handle, int bbox[4]) {
391 double *matrix = handle->matrix;
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];
403 bbox[0] = floor(min(min(work[0], work[2]),min(work[4], work[6])));
404 bbox[1] = floor(min(min(work[1], work[3]),min(work[5], work[7])));
405 bbox[2] = ceil(max(max(work[0], work[2]),max(work[4], work[6])));
406 bbox[3] = ceil(max(max(work[1], work[3]),max(work[5], work[7])));
410 =item expand_bounds(int bbox[4], int bbox2[4])
412 Treating bbox[] and bbox2[] as 2 bounding boxes, produces a new
413 bounding box in bbox[] that encloses both.
417 static void expand_bounds(int bbox[4], int bbox2[4]) {
418 bbox[0] = min(bbox[0], bbox2[0]);
419 bbox[1] = min(bbox[1], bbox2[1]);
420 bbox[2] = max(bbox[2], bbox2[2]);
421 bbox[3] = max(bbox[3], bbox2[3]);
425 =item i_ft2_bbox_r(FT2_Fonthandle *handle, double cheight, double cwidth, char *text, int len, int vlayout, int utf8, int *bbox)
427 Retrieves bounding box information for the font at the given
428 character width and height.
430 This version finds the rectangular bounding box of the glyphs, with
431 the text as transformed by the transformation matrix. As with
432 i_ft2_bbox (bbox[0], bbox[1]) will the the offset from the start of
433 the topline to the top-left of the bounding box. Unlike i_ft2_bbox()
434 this could be near the bottom left corner of the box.
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
440 Returns non-zero on success.
445 i_ft2_bbox_r(FT2_Fonthandle *handle, double cheight, double cwidth,
446 char *text, int len, int vlayout, int utf8, int *bbox) {
451 int ascent = 0, descent = 0;
452 int glyph_ascent, glyph_descent;
453 FT_Glyph_Metrics *gm;
461 int loadFlags = FT_LOAD_DEFAULT;
464 loadFlags |= FT_LOAD_VERTICAL_LAYOUT;
466 error = FT_Set_Char_Size(handle->face, cwidth*64, cheight*64,
467 handle->xdpi, handle->ydpi);
469 ft2_push_message(error);
470 i_push_error(0, "setting size");
478 c = utf8_advance(&text, &len);
480 i_push_error(0, "invalid UTF8 character");
485 c = (unsigned char)*text++;
489 index = FT_Get_Char_Index(handle->face, c);
490 error = FT_Load_Glyph(handle->face, index, loadFlags);
492 ft2_push_message(error);
493 i_push_errorf(0, "loading glyph for character \\x%02x (glyph 0x%04X)",
497 slot = handle->face->glyph;
500 /* these probably don't mean much for vertical layouts */
501 glyph_ascent = gm->horiBearingY / 64;
502 glyph_descent = glyph_ascent - gm->height/64;
504 work[0] = gm->vertBearingX;
505 work[1] = gm->vertBearingY;
508 work[0] = gm->horiBearingX;
509 work[1] = gm->horiBearingY;
511 work[2] = gm->width + work[0];
512 work[3] = work[1] - gm->height;
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;
519 ft2_transform_box(handle, work);
520 for (i = 0; i < 4; ++i)
527 for (i = 0; i < 4; ++i)
529 ascent = glyph_ascent;
530 descent = glyph_descent;
534 expand_bounds(bounds, work);
536 x += slot->advance.x / 64;
537 y += slot->advance.y / 64;
539 if (glyph_ascent > ascent)
540 ascent = glyph_ascent;
541 if (glyph_descent > descent)
542 descent = glyph_descent;
546 handle the case where the right the of the character overlaps the
548 /*int rightb = gm->horiAdvance - gm->horiBearingX - gm->width;
550 width -= rightb / 64;*/
554 /* at this point bounds contains the bounds relative to the CP,
555 and x, y hold the final position relative to the CP */
562 bbox[1] = -bounds[3];
564 bbox[3] = -bounds[1];
574 make_bmp_map(FT_Bitmap *bitmap, unsigned char *map);
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)
579 Renders I<text> to (I<tx>, I<ty>) in I<im> using color I<cl> at the given
580 I<cheight> and I<cwidth>.
582 If align is 0, then the text is rendered with the top-left of the
583 first character at (I<tx>, I<ty>). If align is non-zero then the text
584 is rendered with (I<tx>, I<ty>) aligned with the base-line of the
587 If aa is non-zero then the text is anti-aliased.
589 Returns non-zero on success.
594 i_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) {
599 FT_Glyph_Metrics *gm;
604 unsigned char map[256];
605 char last_mode = ft_pixel_mode_none;
609 int loadFlags = FT_LOAD_DEFAULT;
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));
615 if (!FT_HAS_VERTICAL(handle->face)) {
616 i_push_error(0, "face has no vertical metrics");
619 loadFlags |= FT_LOAD_VERTICAL_LAYOUT;
622 loadFlags |= FT_LOAD_NO_HINTING;
624 /* set the base-line based on the string ascent */
625 if (!i_ft2_bbox(handle, cheight, cwidth, text, len, bbox, utf8))
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];
636 c = utf8_advance(&text, &len);
638 i_push_error(0, "invalid UTF8 character");
643 c = (unsigned char)*text++;
647 index = FT_Get_Char_Index(handle->face, c);
648 error = FT_Load_Glyph(handle->face, index, loadFlags);
650 ft2_push_message(error);
651 i_push_errorf(0, "loading glyph for character \\x%02x (glyph 0x%04X)",
655 slot = handle->face->glyph;
658 error = FT_Render_Glyph(slot, aa ? ft_render_mode_normal : ft_render_mode_mono);
660 ft2_push_message(error);
661 i_push_errorf(0, "rendering glyph 0x%04X (character \\x%02X)");
664 if (slot->bitmap.pixel_mode == ft_pixel_mode_mono) {
665 bmp = slot->bitmap.buffer;
666 for (y = 0; y < slot->bitmap.rows; ++y) {
669 for (x = 0; x < slot->bitmap.width; ++x) {
671 i_ppix(im, tx+x+slot->bitmap_left, ty+y-slot->bitmap_top, cl);
679 bmp += slot->bitmap.pitch;
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))
689 last_mode = slot->bitmap.pixel_mode;
690 last_grays = slot->bitmap.num_grays;
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]];
698 i_gpix(im, tx+x+slot->bitmap_left, ty+y-slot->bitmap_top, &pel);
699 for (ch = 0; ch < im->channels; ++ch) {
701 ((255-value)*pel.channel[ch] + value * cl->channel[ch]) / 255;
703 i_ppix(im, tx+x+slot->bitmap_left, ty+y-slot->bitmap_top, &pel);
706 bmp += slot->bitmap.pitch;
710 tx += slot->advance.x / 64;
711 ty -= slot->advance.y / 64;
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)
720 Renders I<text> to (I<tx>, I<ty>) in I<im> to I<channel> at the given
721 I<cheight> and I<cwidth>.
723 If align is 0, then the text is rendered with the top-left of the
724 first character at (I<tx>, I<ty>). If align is non-zero then the text
725 is rendered with (I<tx>, I<ty>) aligned with the base-line of the
728 If aa is non-zero then the text is anti-aliased.
730 Returns non-zero on success.
735 i_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) {
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));
746 if (vlayout && !FT_HAS_VERTICAL(handle->face)) {
747 i_push_error(0, "face has no vertical metrics");
751 if (!i_ft2_bbox_r(handle, cheight, cwidth, text, len, vlayout, utf8, bbox))
754 work = i_img_empty_ch(NULL, bbox[2]-bbox[0]+1, bbox[3]-bbox[1]+1, 1);
756 if (!i_ft2_text(handle, work, -bbox[0], -bbox[1], &cl, cheight, cwidth,
757 text, len, 1, aa, vlayout, utf8))
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);
780 =item i_ft2_has_chars(handle, char *text, int len, int utf8, char *out)
782 Check if the given characters are defined by the font.
784 Returns the number of characters that were checked.
788 int i_ft2_has_chars(FT2_Fonthandle *handle, char *text, int len, int utf8,
791 mm_log((1, "i_ft2_check_chars(handle %p, text %p, len %d, utf8 %d)\n",
792 handle, text, len, utf8));
798 c = utf8_advance(&text, &len);
800 i_push_error(0, "invalid UTF8 character");
805 c = (unsigned char)*text++;
809 index = FT_Get_Char_Index(handle->face, c);
817 /* uses a method described in fterrors.h to build an error translation
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
828 =head2 Internal Functions
830 These functions are used in the implementation of freetyp2.c and should not
831 (usually cannot) be called from outside it.
835 =item ft2_push_message(int code)
837 Pushes an error message corresponding to code onto the error stack.
841 static void ft2_push_message(int code) {
848 sprintf(unknown, "Unknown Freetype2 error code 0x%04X\n", code);
849 i_push_error(code, unknown);
853 =item make_bmp_map(FT_Bitmap *bitmap, unsigned char *map)
855 Creates a map to convert grey levels from the glyphs bitmap into
856 values scaled 0..255.
861 make_bmp_map(FT_Bitmap *bitmap, unsigned char *map) {
865 switch (bitmap->pixel_mode) {
866 case ft_pixel_mode_grays:
867 scale = bitmap->num_grays;
871 i_push_errorf(0, "I can't handle pixel mode %d", bitmap->pixel_mode);
875 /* build the table */
876 for (i = 0; i < scale; ++i)
877 map[i] = i * 255 / (bitmap->num_grays - 1);
887 struct utf8_size utf8_sizes[] =
896 =item utf8_advance(char **p, int *len)
898 Retreive a UTF8 character from the stream.
900 Modifies *p and *len to indicate the consumed characters.
902 This doesn't support the extended UTF8 encoding used by later versions
908 unsigned long utf8_advance(char **p, int *len) {
911 unsigned char codes[3];
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;
921 if (clen == 0 || *len < clen-1) {
926 /* check that each character is well formed */
930 if (((*p)[ci] & 0xC0) != 0x80) {
934 codes[ci] = (*p)[ci];
937 *p += clen-1; *len -= clen-1;
939 if ((c & 0xE0) == 0xC0) {
940 return ((c & 0x1F) << 6) + (codes[0] & 0x3F);
942 else if ((c & 0xF0) == 0xE0) {
943 return ((c & 0x0F) << 12) | ((codes[0] & 0x3F) << 6) | (codes[1] & 0x3f);
945 else if ((c & 0xF8) == 0xF0) {
946 return ((c & 0x07) << 18) | ((codes[0] & 0x3F) << 12)
947 | ((codes[1] & 0x3F) << 6) | (codes[2] & 0x3F);
950 *p -= clen; *len += clen;
964 Tony Cook <tony@develop-help.com>, with a fair amount of help from
965 reading the code in font.c.
969 font.c, Imager::Font(3), Imager(3)
971 http://www.freetype.org/