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);
44 static FT_Library library;
47 =item i_ft2_init(void)
49 Initializes the Freetype 2 library.
51 Returns true on success, false on failure.
60 error = FT_Init_FreeType(&library);
62 ft2_push_message(error);
63 i_push_error(0, "Initializing Freetype2");
69 struct FT2_Fonthandle {
75 /* used to adjust so we can align the draw point to the top-left */
79 /* the following is used to select a "best" encoding */
80 static struct enc_score {
85 /* the selections here are fairly arbitrary
86 ideally we need to give the user a list of encodings available
87 and a mechanism to choose one */
88 { ft_encoding_unicode, 10 },
89 { ft_encoding_sjis, 8 },
90 { ft_encoding_gb2312, 8 },
91 { ft_encoding_big5, 8 },
92 { ft_encoding_wansung, 8 },
93 { ft_encoding_johab, 8 },
94 { ft_encoding_latin_2, 6 },
95 { ft_encoding_apple_roman, 6 },
96 { ft_encoding_adobe_standard, 6 },
97 { ft_encoding_adobe_expert, 6 },
101 =item i_ft2_new(char *name, int index)
103 Creates a new font object, from the file given by I<name>. I<index>
104 is the index of the font in a file with multiple fonts, where 0 is the
107 Return NULL on failure.
113 i_ft2_new(char *name, int index) {
115 FT2_Fonthandle *result;
117 double matrix[6] = { 1, 0, 0,
120 FT_Encoding encoding;
123 mm_log((1, "i_ft2_new(name %p, index %d)\n", name, index));
126 error = FT_New_Face(library, name, index, &face);
128 ft2_push_message(error);
129 i_push_error(error, "Opening face");
133 encoding = face->num_charmaps ? face->charmaps[0]->encoding : ft_encoding_unicode;
135 for (i = 0; i < face->num_charmaps; ++i) {
136 FT_Encoding enc_entry = face->charmaps[i]->encoding;
137 mm_log((2, "i_ft2_new, encoding %lX platform %u encoding %u\n",
138 enc_entry, face->charmaps[i]->platform_id,
139 face->charmaps[i]->encoding_id));
140 for (j = 0; j < sizeof(enc_scores) / sizeof(*enc_scores); ++j) {
141 if (enc_scores[j].encoding == enc_entry && enc_scores[j].score > score) {
142 encoding = enc_entry;
143 score = enc_scores[j].score;
148 FT_Select_Charmap(face, encoding);
149 mm_log((2, "i_ft2_new, selected encoding %lX\n", encoding));
151 result = mymalloc(sizeof(FT2_Fonthandle));
153 result->xdpi = result->ydpi = 72;
154 result->encoding = encoding;
156 /* by default we disable hinting on a call to i_ft2_settransform()
157 if we don't do this, then the hinting can the untransformed text
158 to be a different size to the transformed text.
159 Obviously we have it initially enabled.
163 /* I originally forgot this: :/ */
164 /*i_ft2_settransform(result, matrix); */
165 result->matrix[0] = 1; result->matrix[1] = 0; result->matrix[2] = 0;
166 result->matrix[3] = 0; result->matrix[4] = 1; result->matrix[5] = 0;
172 =item i_ft2_destroy(FT2_Fonthandle *handle)
174 Destroys a font object, which must have been the return value of
180 i_ft2_destroy(FT2_Fonthandle *handle) {
181 FT_Done_Face(handle->face);
186 =item i_ft2_setdpi(FT2_Fonthandle *handle, int xdpi, int ydpi)
188 Sets the resolution in dots per inch at which point sizes scaled, by
189 default xdpi and ydpi are 72, so that 1 point maps to 1 pixel.
191 Both xdpi and ydpi should be positive.
193 Return true on success.
198 i_ft2_setdpi(FT2_Fonthandle *handle, int xdpi, int ydpi) {
200 if (xdpi > 0 && ydpi > 0) {
206 i_push_error(0, "resolutions must be positive");
212 =item i_ft2_getdpi(FT2_Fonthandle *handle, int *xdpi, int *ydpi)
214 Retrieves the current horizontal and vertical resolutions at which
215 point sizes are scaled.
220 i_ft2_getdpi(FT2_Fonthandle *handle, int *xdpi, int *ydpi) {
221 *xdpi = handle->xdpi;
222 *ydpi = handle->ydpi;
228 =item i_ft2_settransform(FT2_FontHandle *handle, double *matrix)
230 Sets a transormation matrix for output.
232 This should be a 2 x 3 matrix like:
234 matrix[0] matrix[1] matrix[2]
235 matrix[3] matrix[4] matrix[5]
240 i_ft2_settransform(FT2_Fonthandle *handle, double *matrix) {
245 m.xx = matrix[0] * 65536;
246 m.xy = matrix[1] * 65536;
247 v.x = matrix[2]; /* this could be pels of 26.6 fixed - not sure */
248 m.yx = matrix[3] * 65536;
249 m.yy = matrix[4] * 65536;
250 v.y = matrix[5]; /* see just above */
252 FT_Set_Transform(handle->face, &m, &v);
254 for (i = 0; i < 6; ++i)
255 handle->matrix[i] = matrix[i];
262 =item i_ft2_sethinting(FT2_Fonthandle *handle, int hinting)
264 If hinting is non-zero then glyph hinting is enabled, otherwise disabled.
266 i_ft2_settransform() disables hinting to prevent distortions in
267 gradual text transformations.
271 int i_ft2_sethinting(FT2_Fonthandle *handle, int hinting) {
272 handle->hint = hinting;
277 =item i_ft2_bbox(FT2_Fonthandle *handle, double cheight, double cwidth, char *text, int len, int *bbox)
279 Retrieves bounding box information for the font at the given
280 character width and height. This ignores the transformation matrix.
282 Returns non-zero on success.
287 i_ft2_bbox(FT2_Fonthandle *handle, double cheight, double cwidth,
288 char const *text, int len, int *bbox, int utf8) {
293 int ascent = 0, descent = 0;
294 int glyph_ascent, glyph_descent;
295 FT_Glyph_Metrics *gm;
298 mm_log((1, "i_ft2_bbox(handle %p, cheight %f, cwidth %f, text %p, len %d, bbox %p)\n",
299 handle, cheight, cwidth, text, len, bbox));
301 error = FT_Set_Char_Size(handle->face, cwidth*64, cheight*64,
302 handle->xdpi, handle->ydpi);
304 ft2_push_message(error);
305 i_push_error(0, "setting size");
313 c = i_utf8_advance(&text, &len);
315 i_push_error(0, "invalid UTF8 character");
320 c = (unsigned char)*text++;
324 index = FT_Get_Char_Index(handle->face, c);
325 error = FT_Load_Glyph(handle->face, index, FT_LOAD_DEFAULT);
327 ft2_push_message(error);
328 i_push_errorf(0, "loading glyph for character \\x%02x (glyph 0x%04X)",
332 gm = &handle->face->glyph->metrics;
333 glyph_ascent = gm->horiBearingY / 64;
334 glyph_descent = glyph_ascent - gm->height/64;
336 start = gm->horiBearingX / 64;
337 /* handles -ve values properly */
338 ascent = glyph_ascent;
339 descent = glyph_descent;
343 if (glyph_ascent > ascent)
344 ascent = glyph_ascent;
345 if (glyph_descent < descent)
346 descent = glyph_descent;
348 width += gm->horiAdvance / 64;
352 handle the case where the right the of the character overlaps the
354 int rightb = gm->horiAdvance - gm->horiBearingX - gm->width;
356 width -= rightb / 64;
361 bbox[1] = handle->face->size->metrics.descender / 64;
363 bbox[3] = handle->face->size->metrics.ascender / 64;
371 =item transform_box(FT2_FontHandle *handle, int bbox[4])
373 bbox contains coorinates of a the top-left and bottom-right of a bounding
374 box relative to a point.
376 This is then transformed and the values in bbox[4] are the top-left
377 and bottom-right of the new bounding box.
379 This is meant to provide the bounding box of a transformed character
380 box. The problem is that if the character was round and is rotated,
381 the real bounding box isn't going to be much different from the
382 original, but this function will return a _bigger_ bounding box. I
383 suppose I could work my way through the glyph outline, but that's
388 void ft2_transform_box(FT2_Fonthandle *handle, int bbox[4]) {
390 double *matrix = handle->matrix;
393 work[0] = matrix[0] * bbox[0] + matrix[1] * bbox[1];
394 work[1] = matrix[3] * bbox[0] + matrix[4] * bbox[1];
395 work[2] = matrix[0] * bbox[2] + matrix[1] * bbox[1];
396 work[3] = matrix[3] * bbox[2] + matrix[4] * bbox[1];
397 work[4] = matrix[0] * bbox[0] + matrix[1] * bbox[3];
398 work[5] = matrix[3] * bbox[0] + matrix[4] * bbox[3];
399 work[6] = matrix[0] * bbox[2] + matrix[1] * bbox[3];
400 work[7] = matrix[3] * bbox[2] + matrix[4] * bbox[3];
402 bbox[0] = floor(i_min(i_min(work[0], work[2]),i_min(work[4], work[6])));
403 bbox[1] = floor(i_min(i_min(work[1], work[3]),i_min(work[5], work[7])));
404 bbox[2] = ceil(i_max(i_max(work[0], work[2]),i_max(work[4], work[6])));
405 bbox[3] = ceil(i_max(i_max(work[1], work[3]),i_max(work[5], work[7])));
409 =item expand_bounds(int bbox[4], int bbox2[4])
411 Treating bbox[] and bbox2[] as 2 bounding boxes, produces a new
412 bounding box in bbox[] that encloses both.
416 static void expand_bounds(int bbox[4], int bbox2[4]) {
417 bbox[0] = i_min(bbox[0], bbox2[0]);
418 bbox[1] = i_min(bbox[1], bbox2[1]);
419 bbox[2] = i_max(bbox[2], bbox2[2]);
420 bbox[3] = i_max(bbox[3], bbox2[3]);
424 =item i_ft2_bbox_r(FT2_Fonthandle *handle, double cheight, double cwidth, char *text, int len, int vlayout, int utf8, int *bbox)
426 Retrieves bounding box information for the font at the given
427 character width and height.
429 This version finds the rectangular bounding box of the glyphs, with
430 the text as transformed by the transformation matrix. As with
431 i_ft2_bbox (bbox[0], bbox[1]) will the the offset from the start of
432 the topline to the top-left of the bounding box. Unlike i_ft2_bbox()
433 this could be near the bottom left corner of the box.
435 (bbox[4], bbox[5]) is the offset to the start of the baseline.
436 (bbox[6], bbox[7]) is the offset from the start of the baseline to the
439 Returns non-zero on success.
444 i_ft2_bbox_r(FT2_Fonthandle *handle, double cheight, double cwidth,
445 char const *text, int len, int vlayout, int utf8, int *bbox) {
450 int ascent = 0, descent = 0;
451 int glyph_ascent, glyph_descent;
452 FT_Glyph_Metrics *gm;
460 int loadFlags = FT_LOAD_DEFAULT;
463 loadFlags |= FT_LOAD_VERTICAL_LAYOUT;
465 error = FT_Set_Char_Size(handle->face, cwidth*64, cheight*64,
466 handle->xdpi, handle->ydpi);
468 ft2_push_message(error);
469 i_push_error(0, "setting size");
477 c = i_utf8_advance(&text, &len);
479 i_push_error(0, "invalid UTF8 character");
484 c = (unsigned char)*text++;
488 index = FT_Get_Char_Index(handle->face, c);
489 error = FT_Load_Glyph(handle->face, index, loadFlags);
491 ft2_push_message(error);
492 i_push_errorf(0, "loading glyph for character \\x%02x (glyph 0x%04X)",
496 slot = handle->face->glyph;
499 /* these probably don't mean much for vertical layouts */
500 glyph_ascent = gm->horiBearingY / 64;
501 glyph_descent = glyph_ascent - gm->height/64;
503 work[0] = gm->vertBearingX;
504 work[1] = gm->vertBearingY;
507 work[0] = gm->horiBearingX;
508 work[1] = gm->horiBearingY;
510 work[2] = gm->width + work[0];
511 work[3] = work[1] - gm->height;
513 bbox[4] = work[0] * handle->matrix[0] + work[1] * handle->matrix[1] + handle->matrix[2];
514 bbox[5] = work[0] * handle->matrix[3] + work[1] * handle->matrix[4] + handle->matrix[5];
515 bbox[4] = bbox[4] < 0 ? -(-bbox[4] + 32)/64 : (bbox[4] + 32) / 64;
518 ft2_transform_box(handle, work);
519 for (i = 0; i < 4; ++i)
526 for (i = 0; i < 4; ++i)
528 ascent = glyph_ascent;
529 descent = glyph_descent;
533 expand_bounds(bounds, work);
535 x += slot->advance.x / 64;
536 y += slot->advance.y / 64;
538 if (glyph_ascent > ascent)
539 ascent = glyph_ascent;
540 if (glyph_descent > descent)
541 descent = glyph_descent;
545 handle the case where the right the of the character overlaps the
547 /*int rightb = gm->horiAdvance - gm->horiBearingX - gm->width;
549 width -= rightb / 64;*/
553 /* at this point bounds contains the bounds relative to the CP,
554 and x, y hold the final position relative to the CP */
561 bbox[1] = -bounds[3];
563 bbox[3] = -bounds[1];
573 make_bmp_map(FT_Bitmap *bitmap, unsigned char *map);
576 =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 Renders I<text> to (I<tx>, I<ty>) in I<im> using color I<cl> at the given
579 I<cheight> and I<cwidth>.
581 If align is 0, then the text is rendered with the top-left of the
582 first character at (I<tx>, I<ty>). If align is non-zero then the text
583 is rendered with (I<tx>, I<ty>) aligned with the base-line of the
586 If aa is non-zero then the text is anti-aliased.
588 Returns non-zero on success.
593 i_ft2_text(FT2_Fonthandle *handle, i_img *im, int tx, int ty, i_color *cl,
594 double cheight, double cwidth, char const *text, int len, int align,
595 int aa, int vlayout, int utf8) {
598 FT_Glyph_Metrics *gm;
603 unsigned char map[256];
604 char last_mode = ft_pixel_mode_none;
608 int loadFlags = FT_LOAD_DEFAULT;
610 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",
611 handle, im, tx, ty, cl, cheight, cwidth, text, align, aa));
614 if (!FT_HAS_VERTICAL(handle->face)) {
615 i_push_error(0, "face has no vertical metrics");
618 loadFlags |= FT_LOAD_VERTICAL_LAYOUT;
621 loadFlags |= FT_LOAD_NO_HINTING;
623 /* set the base-line based on the string ascent */
624 if (!i_ft2_bbox(handle, cheight, cwidth, text, len, bbox, utf8))
628 /* this may need adjustment */
629 tx -= bbox[0] * handle->matrix[0] + bbox[5] * handle->matrix[1] + handle->matrix[2];
630 ty += bbox[0] * handle->matrix[3] + bbox[5] * handle->matrix[4] + handle->matrix[5];
635 c = i_utf8_advance(&text, &len);
637 i_push_error(0, "invalid UTF8 character");
642 c = (unsigned char)*text++;
646 index = FT_Get_Char_Index(handle->face, c);
647 error = FT_Load_Glyph(handle->face, index, loadFlags);
649 ft2_push_message(error);
650 i_push_errorf(0, "loading glyph for character \\x%02x (glyph 0x%04X)",
654 slot = handle->face->glyph;
657 error = FT_Render_Glyph(slot, aa ? ft_render_mode_normal : ft_render_mode_mono);
659 ft2_push_message(error);
660 i_push_errorf(0, "rendering glyph 0x%04X (character \\x%02X)");
663 if (slot->bitmap.pixel_mode == ft_pixel_mode_mono) {
664 bmp = slot->bitmap.buffer;
665 for (y = 0; y < slot->bitmap.rows; ++y) {
668 for (x = 0; x < slot->bitmap.width; ++x) {
670 i_ppix(im, tx+x+slot->bitmap_left, ty+y-slot->bitmap_top, cl);
678 bmp += slot->bitmap.pitch;
682 /* grey scale or something we can treat as greyscale */
683 /* we create a map to convert from the bitmap values to 0-255 */
684 if (last_mode != slot->bitmap.pixel_mode
685 || last_grays != slot->bitmap.num_grays) {
686 if (!make_bmp_map(&slot->bitmap, map))
688 last_mode = slot->bitmap.pixel_mode;
689 last_grays = slot->bitmap.num_grays;
692 bmp = slot->bitmap.buffer;
693 for (y = 0; y < slot->bitmap.rows; ++y) {
694 for (x = 0; x < slot->bitmap.width; ++x) {
695 int value = map[bmp[x]];
697 i_gpix(im, tx+x+slot->bitmap_left, ty+y-slot->bitmap_top, &pel);
698 for (ch = 0; ch < im->channels; ++ch) {
700 ((255-value)*pel.channel[ch] + value * cl->channel[ch]) / 255;
702 i_ppix(im, tx+x+slot->bitmap_left, ty+y-slot->bitmap_top, &pel);
705 bmp += slot->bitmap.pitch;
709 tx += slot->advance.x / 64;
710 ty -= slot->advance.y / 64;
717 =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 Renders I<text> to (I<tx>, I<ty>) in I<im> to I<channel> at the given
720 I<cheight> and I<cwidth>.
722 If align is 0, then the text is rendered with the top-left of the
723 first character at (I<tx>, I<ty>). If align is non-zero then the text
724 is rendered with (I<tx>, I<ty>) aligned with the base-line of the
727 If aa is non-zero then the text is anti-aliased.
729 Returns non-zero on success.
734 i_ft2_cp(FT2_Fonthandle *handle, i_img *im, int tx, int ty, int channel,
735 double cheight, double cwidth, char const *text, int len, int align,
736 int aa, int vlayout, int utf8) {
742 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",
743 handle, im, tx, ty, channel, cheight, cwidth, text, len));
745 if (vlayout && !FT_HAS_VERTICAL(handle->face)) {
746 i_push_error(0, "face has no vertical metrics");
750 if (!i_ft2_bbox_r(handle, cheight, cwidth, text, len, vlayout, utf8, bbox))
753 work = i_img_empty_ch(NULL, bbox[2]-bbox[0]+1, bbox[3]-bbox[1]+1, 1);
755 if (!i_ft2_text(handle, work, -bbox[0], -bbox[1], &cl, cheight, cwidth,
756 text, len, 1, aa, vlayout, utf8))
764 /* render to the specified channel */
765 /* this will be sped up ... */
766 for (y = 0; y < work->ysize; ++y) {
767 for (x = 0; x < work->xsize; ++x) {
768 i_gpix(work, x, y, &cl);
769 i_gpix(im, tx + x + bbox[0], ty + y + bbox[1], &cl2);
770 cl2.channel[channel] = cl.channel[0];
771 i_ppix(im, tx + x + bbox[0], ty + y + bbox[1], &cl2);
779 =item i_ft2_has_chars(handle, char *text, int len, int utf8, char *out)
781 Check if the given characters are defined by the font.
783 Returns the number of characters that were checked.
787 int i_ft2_has_chars(FT2_Fonthandle *handle, char const *text, int len,
788 int utf8, char *out) {
790 mm_log((1, "i_ft2_has_chars(handle %p, text %p, len %d, utf8 %d)\n",
791 handle, text, len, utf8));
797 c = i_utf8_advance(&text, &len);
799 i_push_error(0, "invalid UTF8 character");
804 c = (unsigned char)*text++;
808 index = FT_Get_Char_Index(handle->face, c);
816 /* uses a method described in fterrors.h to build an error translation
819 #undef __FT_ERRORS_H__
820 #define FT_ERRORDEF(e, v, s) case v: i_push_error(code, s); return;
821 #define FT_ERROR_START_LIST
822 #define FT_ERROR_END_LIST
827 =head2 Internal Functions
829 These functions are used in the implementation of freetyp2.c and should not
830 (usually cannot) be called from outside it.
834 =item ft2_push_message(int code)
836 Pushes an error message corresponding to code onto the error stack.
840 static void ft2_push_message(int code) {
847 sprintf(unknown, "Unknown Freetype2 error code 0x%04X\n", code);
848 i_push_error(code, unknown);
852 =item make_bmp_map(FT_Bitmap *bitmap, unsigned char *map)
854 Creates a map to convert grey levels from the glyphs bitmap into
855 values scaled 0..255.
860 make_bmp_map(FT_Bitmap *bitmap, unsigned char *map) {
864 switch (bitmap->pixel_mode) {
865 case ft_pixel_mode_grays:
866 scale = bitmap->num_grays;
870 i_push_errorf(0, "I can't handle pixel mode %d", bitmap->pixel_mode);
874 /* build the table */
875 for (i = 0; i < scale; ++i)
876 map[i] = i * 255 / (bitmap->num_grays - 1);
886 Tony Cook <tony@develop-help.com>, with a fair amount of help from
887 reading the code in font.c.
891 font.c, Imager::Font(3), Imager(3)
893 http://www.freetype.org/