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)) { 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 {
75 /* used to adjust so we can align the draw point to the top-left */
80 =item i_ft2_new(char *name, int index)
82 Creates a new font object, from the file given by I<name>. I<index>
83 is the index of the font in a file with multiple fonts, where 0 is the
86 Return NULL on failure.
92 i_ft2_new(char *name, int index) {
94 FT2_Fonthandle *result;
96 double matrix[6] = { 1, 0, 0,
99 mm_log((1, "i_ft2_new(name %p, index %d)\n", name, index));
102 error = FT_New_Face(library, name, index, &face);
104 ft2_push_message(error);
105 i_push_error(error, "Opening face");
109 result = mymalloc(sizeof(FT2_Fonthandle));
111 result->xdpi = result->ydpi = 72;
113 /* by default we disable hinting on a call to i_ft2_settransform()
114 if we don't do this, then the hinting can the untransformed text
115 to be a different size to the transformed text.
116 Obviously we have it initially enabled.
120 /* I originally forgot this: :/ */
121 /*i_ft2_settransform(result, matrix); */
122 result->matrix[0] = 1; result->matrix[1] = 0; result->matrix[2] = 0;
123 result->matrix[3] = 0; result->matrix[4] = 1; result->matrix[5] = 0;
129 =item i_ft2_destroy(FT2_Fonthandle *handle)
131 Destroys a font object, which must have been the return value of
137 i_ft2_destroy(FT2_Fonthandle *handle) {
138 FT_Done_Face(handle->face);
143 =item i_ft2_setdpi(FT2_Fonthandle *handle, int xdpi, int ydpi)
145 Sets the resolution in dots per inch at which point sizes scaled, by
146 default xdpi and ydpi are 72, so that 1 point maps to 1 pixel.
148 Both xdpi and ydpi should be positive.
150 Return true on success.
155 i_ft2_setdpi(FT2_Fonthandle *handle, int xdpi, int ydpi) {
157 if (xdpi > 0 && ydpi > 0) {
163 i_push_error(0, "resolutions must be positive");
169 =item i_ft2_getdpi(FT2_Fonthandle *handle, int *xdpi, int *ydpi)
171 Retrieves the current horizontal and vertical resolutions at which
172 point sizes are scaled.
177 i_ft2_getdpi(FT2_Fonthandle *handle, int *xdpi, int *ydpi) {
178 *xdpi = handle->xdpi;
179 *ydpi = handle->ydpi;
185 =item i_ft2_settransform(FT2_FontHandle *handle, double *matrix)
187 Sets a transormation matrix for output.
189 This should be a 2 x 3 matrix like:
191 matrix[0] matrix[1] matrix[2]
192 matrix[3] matrix[4] matrix[5]
197 i_ft2_settransform(FT2_Fonthandle *handle, double *matrix) {
202 m.xx = matrix[0] * 65536;
203 m.xy = matrix[1] * 65536;
204 v.x = matrix[2]; /* this could be pels of 26.6 fixed - not sure */
205 m.yx = matrix[3] * 65536;
206 m.yy = matrix[4] * 65536;
207 v.y = matrix[5]; /* see just above */
209 FT_Set_Transform(handle->face, &m, &v);
211 for (i = 0; i < 6; ++i)
212 handle->matrix[i] = matrix[i];
219 =item i_ft2_sethinting(FT2_Fonthandle *handle, int hinting)
221 If hinting is non-zero then glyph hinting is enabled, otherwise disabled.
223 i_ft2_settransform() disables hinting to prevent distortions in
224 gradual text transformations.
228 int i_ft2_sethinting(FT2_Fonthandle *handle, int hinting) {
229 handle->hint = hinting;
234 =item i_ft2_bbox(FT2_Fonthandle *handle, double cheight, double cwidth, char *text, int len, int *bbox)
236 Retrieves bounding box information for the font at the given
237 character width and height. This ignores the transformation matrix.
239 Returns non-zero on success.
244 i_ft2_bbox(FT2_Fonthandle *handle, double cheight, double cwidth,
245 char *text, int len, int *bbox) {
250 int ascent = 0, descent = 0;
251 int glyph_ascent, glyph_descent;
252 FT_Glyph_Metrics *gm;
255 mm_log((1, "i_ft2_bbox(handle %p, cheight %f, cwidth %f, text %p, len %d, bbox %p)\n",
256 handle, cheight, cwidth, text, len, bbox));
258 error = FT_Set_Char_Size(handle->face, cwidth*64, cheight*64,
259 handle->xdpi, handle->ydpi);
261 ft2_push_message(error);
262 i_push_error(0, "setting size");
268 int c = (unsigned char)*text++;
270 index = FT_Get_Char_Index(handle->face, c);
271 error = FT_Load_Glyph(handle->face, index, FT_LOAD_DEFAULT);
273 ft2_push_message(error);
274 i_push_errorf(0, "loading glyph for character \\x%02x (glyph 0x%04X)",
278 gm = &handle->face->glyph->metrics;
279 glyph_ascent = gm->horiBearingY / 64;
280 glyph_descent = glyph_ascent - gm->height/64;
282 start = gm->horiBearingX / 64;
283 /* handles -ve values properly */
284 ascent = glyph_ascent;
285 descent = glyph_descent;
289 if (glyph_ascent > ascent)
290 ascent = glyph_ascent;
291 if (glyph_descent > descent)
292 descent = glyph_descent;
294 width += gm->horiAdvance / 64;
298 handle the case where the right the of the character overlaps the
300 int rightb = gm->horiAdvance - gm->horiBearingX - gm->width;
302 width -= rightb / 64;
307 bbox[1] = handle->face->size->metrics.descender / 64;
309 bbox[3] = handle->face->size->metrics.ascender / 64;
317 =item transform_box(FT2_FontHandle *handle, int bbox[4])
319 bbox contains coorinates of a the top-left and bottom-right of a bounding
320 box relative to a point.
322 This is then transformed and the values in bbox[4] are the top-left
323 and bottom-right of the new bounding box.
325 This is meant to provide the bounding box of a transformed character
326 box. The problem is that if the character was round and is rotated,
327 the real bounding box isn't going to be much different from the
328 original, but this function will return a _bigger_ bounding box. I
329 suppose I could work my way through the glyph outline, but that's
334 void ft2_transform_box(FT2_Fonthandle *handle, int bbox[4]) {
336 double *matrix = handle->matrix;
339 work[0] = matrix[0] * bbox[0] + matrix[1] * bbox[1];
340 work[1] = matrix[3] * bbox[0] + matrix[4] * bbox[1];
341 work[2] = matrix[0] * bbox[2] + matrix[1] * bbox[1];
342 work[3] = matrix[3] * bbox[2] + matrix[4] * bbox[1];
343 work[4] = matrix[0] * bbox[0] + matrix[1] * bbox[3];
344 work[5] = matrix[3] * bbox[0] + matrix[4] * bbox[3];
345 work[6] = matrix[0] * bbox[2] + matrix[1] * bbox[3];
346 work[7] = matrix[3] * bbox[2] + matrix[4] * bbox[3];
348 bbox[0] = floor(min(min(work[0], work[2]),min(work[4], work[6])));
349 bbox[1] = floor(min(min(work[1], work[3]),min(work[5], work[7])));
350 bbox[2] = ceil(max(max(work[0], work[2]),max(work[4], work[6])));
351 bbox[3] = ceil(max(max(work[1], work[3]),max(work[5], work[7])));
355 =item expand_bounds(int bbox[4], int bbox2[4])
357 Treating bbox[] and bbox2[] as 2 bounding boxes, produces a new
358 bounding box in bbox[] that encloses both.
362 static void expand_bounds(int bbox[4], int bbox2[4]) {
363 bbox[0] = min(bbox[0], bbox2[0]);
364 bbox[1] = min(bbox[1], bbox2[1]);
365 bbox[2] = max(bbox[2], bbox2[2]);
366 bbox[3] = max(bbox[3], bbox2[3]);
370 =item i_ft2_bbox_r(FT2_Fonthandle *handle, double cheight, double cwidth, char *text, int len, int vlayout, int utf8, int *bbox)
372 Retrieves bounding box information for the font at the given
373 character width and height.
375 This version finds the rectangular bounding box of the glyphs, with
376 the text as transformed by the transformation matrix. As with
377 i_ft2_bbox (bbox[0], bbox[1]) will the the offset from the start of
378 the topline to the top-left of the bounding box. Unlike i_ft2_bbox()
379 this could be near the bottom left corner of the box.
381 (bbox[4], bbox[5]) is the offset to the start of the baseline.
382 (bbox[6], bbox[7]) is the offset from the start of the baseline to the
385 Returns non-zero on success.
390 i_ft2_bbox_r(FT2_Fonthandle *handle, double cheight, double cwidth,
391 char *text, int len, int vlayout, int utf8, int *bbox) {
396 int ascent = 0, descent = 0;
397 int glyph_ascent, glyph_descent;
398 FT_Glyph_Metrics *gm;
406 int loadFlags = FT_LOAD_DEFAULT;
409 loadFlags |= FT_LOAD_VERTICAL_LAYOUT;
411 error = FT_Set_Char_Size(handle->face, cwidth*64, cheight*64,
412 handle->xdpi, handle->ydpi);
414 ft2_push_message(error);
415 i_push_error(0, "setting size");
423 c = utf8_advance(&text, &len);
425 i_push_error(0, "invalid UTF8 character");
430 c = (unsigned char)*text++;
434 index = FT_Get_Char_Index(handle->face, c);
435 error = FT_Load_Glyph(handle->face, index, loadFlags);
437 ft2_push_message(error);
438 i_push_errorf(0, "loading glyph for character \\x%02x (glyph 0x%04X)",
442 slot = handle->face->glyph;
445 /* these probably don't mean much for vertical layouts */
446 glyph_ascent = gm->horiBearingY / 64;
447 glyph_descent = glyph_ascent - gm->height/64;
449 work[0] = gm->vertBearingX;
450 work[1] = gm->vertBearingY;
453 work[0] = gm->horiBearingX;
454 work[1] = gm->horiBearingY;
456 work[2] = gm->width + work[0];
457 work[3] = work[1] - gm->height;
459 bbox[4] = work[0] * handle->matrix[0] + work[1] * handle->matrix[1] + handle->matrix[2];
460 bbox[5] = work[0] * handle->matrix[3] + work[1] * handle->matrix[4] + handle->matrix[5];
461 bbox[4] = bbox[4] < 0 ? -(-bbox[4] + 32)/64 : (bbox[4] + 32) / 64;
464 ft2_transform_box(handle, work);
465 for (i = 0; i < 4; ++i)
472 for (i = 0; i < 4; ++i)
474 ascent = glyph_ascent;
475 descent = glyph_descent;
479 expand_bounds(bounds, work);
481 x += slot->advance.x / 64;
482 y += slot->advance.y / 64;
484 if (glyph_ascent > ascent)
485 ascent = glyph_ascent;
486 if (glyph_descent > descent)
487 descent = glyph_descent;
491 handle the case where the right the of the character overlaps the
493 /*int rightb = gm->horiAdvance - gm->horiBearingX - gm->width;
495 width -= rightb / 64;*/
499 /* at this point bounds contains the bounds relative to the CP,
500 and x, y hold the final position relative to the CP */
507 bbox[1] = -bounds[3];
509 bbox[3] = -bounds[1];
519 make_bmp_map(FT_Bitmap *bitmap, unsigned char *map);
522 =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)
524 Renders I<text> to (I<tx>, I<ty>) in I<im> using color I<cl> at the given
525 I<cheight> and I<cwidth>.
527 If align is 0, then the text is rendered with the top-left of the
528 first character at (I<tx>, I<ty>). If align is non-zero then the text
529 is rendered with (I<tx>, I<ty>) aligned with the base-line of the
532 If aa is non-zero then the text is anti-aliased.
534 Returns non-zero on success.
539 i_ft2_text(FT2_Fonthandle *handle, i_img *im, int tx, int ty, i_color *cl,
540 double cheight, double cwidth, char *text, int len, int align,
541 int aa, int vlayout, int utf8) {
544 FT_Glyph_Metrics *gm;
549 unsigned char map[256];
550 char last_mode = ft_pixel_mode_none;
554 int loadFlags = FT_LOAD_DEFAULT;
556 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",
557 handle, im, tx, ty, cl, cheight, cwidth, text, align, aa));
560 if (!FT_HAS_VERTICAL(handle->face)) {
561 i_push_error(0, "face has no vertical metrics");
564 loadFlags |= FT_LOAD_VERTICAL_LAYOUT;
567 loadFlags |= FT_LOAD_NO_HINTING;
569 /* set the base-line based on the string ascent */
570 if (!i_ft2_bbox(handle, cheight, cwidth, text, len, bbox))
574 /* this may need adjustment */
575 tx -= bbox[0] * handle->matrix[0] + bbox[5] * handle->matrix[1] + handle->matrix[2];
576 ty += bbox[0] * handle->matrix[3] + bbox[5] * handle->matrix[4] + handle->matrix[5];
581 c = utf8_advance(&text, &len);
583 i_push_error(0, "invalid UTF8 character");
588 c = (unsigned char)*text++;
592 index = FT_Get_Char_Index(handle->face, c);
593 error = FT_Load_Glyph(handle->face, index, loadFlags);
595 ft2_push_message(error);
596 i_push_errorf(0, "loading glyph for character \\x%02x (glyph 0x%04X)",
600 slot = handle->face->glyph;
603 error = FT_Render_Glyph(slot, aa ? ft_render_mode_normal : ft_render_mode_mono);
605 ft2_push_message(error);
606 i_push_errorf(0, "rendering glyph 0x%04X (character \\x%02X)");
609 if (slot->bitmap.pixel_mode == ft_pixel_mode_mono) {
610 bmp = slot->bitmap.buffer;
611 for (y = 0; y < slot->bitmap.rows; ++y) {
614 for (x = 0; x < slot->bitmap.width; ++x) {
616 i_ppix(im, tx+x+slot->bitmap_left, ty+y-slot->bitmap_top, cl);
624 bmp += slot->bitmap.pitch;
628 /* grey scale or something we can treat as greyscale */
629 /* we create a map to convert from the bitmap values to 0-255 */
630 if (last_mode != slot->bitmap.pixel_mode
631 || last_grays != slot->bitmap.num_grays) {
632 if (!make_bmp_map(&slot->bitmap, map))
634 last_mode = slot->bitmap.pixel_mode;
635 last_grays = slot->bitmap.num_grays;
638 bmp = slot->bitmap.buffer;
639 for (y = 0; y < slot->bitmap.rows; ++y) {
640 for (x = 0; x < slot->bitmap.width; ++x) {
641 int value = map[bmp[x]];
643 i_gpix(im, tx+x+slot->bitmap_left, ty+y-slot->bitmap_top, &pel);
644 for (ch = 0; ch < im->channels; ++ch) {
646 ((255-value)*pel.channel[ch] + value * cl->channel[ch]) / 255;
648 i_ppix(im, tx+x+slot->bitmap_left, ty+y-slot->bitmap_top, &pel);
651 bmp += slot->bitmap.pitch;
655 tx += slot->advance.x / 64;
656 ty -= slot->advance.y / 64;
663 =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)
665 Renders I<text> to (I<tx>, I<ty>) in I<im> to I<channel> at the given
666 I<cheight> and I<cwidth>.
668 If align is 0, then the text is rendered with the top-left of the
669 first character at (I<tx>, I<ty>). If align is non-zero then the text
670 is rendered with (I<tx>, I<ty>) aligned with the base-line of the
673 If aa is non-zero then the text is anti-aliased.
675 Returns non-zero on success.
680 i_ft2_cp(FT2_Fonthandle *handle, i_img *im, int tx, int ty, int channel,
681 double cheight, double cwidth, char *text, int len, int align,
682 int aa, int vlayout, int utf8) {
688 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",
689 handle, im, tx, ty, channel, cheight, cwidth, text, len));
691 if (vlayout && !FT_HAS_VERTICAL(handle->face)) {
692 i_push_error(0, "face has no vertical metrics");
696 if (!i_ft2_bbox_r(handle, cheight, cwidth, text, len, vlayout, utf8, bbox))
699 work = i_img_empty_ch(NULL, bbox[2]-bbox[0]+1, bbox[3]-bbox[1]+1, 1);
701 if (!i_ft2_text(handle, work, -bbox[0], -bbox[1], &cl, cheight, cwidth,
702 text, len, 1, aa, vlayout, utf8))
710 /* render to the specified channel */
711 /* this will be sped up ... */
712 for (y = 0; y < work->ysize; ++y) {
713 for (x = 0; x < work->xsize; ++x) {
714 i_gpix(work, x, y, &cl);
715 i_gpix(im, tx + x + bbox[0], ty + y + bbox[1], &cl2);
716 cl2.channel[channel] = cl.channel[0];
717 i_ppix(im, tx + x + bbox[0], ty + y + bbox[1], &cl2);
724 /* uses a method described in fterrors.h to build an error translation
727 #undef __FT_ERRORS_H__
728 #define FT_ERRORDEF(e, v, s) case v: i_push_error(code, s); return;
729 #define FT_ERROR_START_LIST
730 #define FT_ERROR_END_LIST
735 =head2 Internal Functions
737 These functions are used in the implementation of freetyp2.c and should not
738 (usually cannot) be called from outside it.
742 =item ft2_push_message(int code)
744 Pushes an error message corresponding to code onto the error stack.
748 static void ft2_push_message(int code) {
755 sprintf(unknown, "Unknown Freetype2 error code 0x%04X\n", code);
756 i_push_error(code, unknown);
760 =item make_bmp_map(FT_Bitmap *bitmap, unsigned char *map)
762 Creates a map to convert grey levels from the glyphs bitmap into
763 values scaled 0..255.
768 make_bmp_map(FT_Bitmap *bitmap, unsigned char *map) {
772 switch (bitmap->pixel_mode) {
773 case ft_pixel_mode_grays:
774 scale = bitmap->num_grays;
778 i_push_errorf(0, "I can't handle pixel mode %d", bitmap->pixel_mode);
782 /* build the table */
783 for (i = 0; i < scale; ++i)
784 map[i] = i * 255 / (bitmap->num_grays - 1);
794 struct utf8_size utf8_sizes[] =
803 =item utf8_advance(char **p, int *len)
805 Retreive a UTF8 character from the stream.
807 Modifies *p and *len to indicate the consumed characters.
809 This doesn't support the extended UTF8 encoding used by later versions
815 unsigned long utf8_advance(char **p, int *len) {
818 unsigned char codes[3];
823 for (i = 0; i < sizeof(utf8_sizes)/sizeof(*utf8_sizes); ++i) {
824 if ((c & utf8_sizes[i].mask) == utf8_sizes[i].expect) {
825 clen = utf8_sizes[i].size;
828 if (clen == 0 || *len < clen-1) {
833 /* check that each character is well formed */
837 if (((*p)[ci] & 0xC0) != 0x80) {
841 codes[ci] = (*p)[ci];
844 *p += clen-1; *len -= clen-1;
846 if ((c & 0xE0) == 0xC0) {
847 return ((c & 0x1F) << 6) + (codes[0] & 0x3F);
849 else if ((c & 0xF0) == 0xE0) {
850 return ((c & 0x0F) << 12) | ((codes[0] & 0x3F) << 6) | (codes[1] & 0x3f);
852 else if ((c & 0xF8) == 0xF0) {
853 return ((c & 0x07) << 18) | ((codes[0] & 0x3F) << 12)
854 | ((codes[1] & 0x3F) << 6) | (codes[2] & 0x3F);
857 *p -= clen; *len += clen;
871 Tony Cook <tony@develop-help.com>, with a fair amount of help from
872 reading the code in font.c.
876 font.c, Imager::Font(3), Imager(3)
878 http://www.freetype.org/