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,
100 error = FT_New_Face(library, name, index, &face);
102 ft2_push_message(error);
103 i_push_error(error, "Opening face");
107 result = mymalloc(sizeof(FT2_Fonthandle));
109 result->xdpi = result->ydpi = 72;
111 /* by default we disable hinting on a call to i_ft2_settransform()
112 if we don't do this, then the hinting can the untransformed text
113 to be a different size to the transformed text.
114 Obviously we have it initially enabled.
118 /* I originally forgot this: :/ */
119 /*i_ft2_settransform(result, matrix); */
120 result->matrix[0] = 1; result->matrix[1] = 0; result->matrix[2] = 0;
121 result->matrix[3] = 0; result->matrix[4] = 1; result->matrix[5] = 0;
127 =item i_ft2_destroy(FT2_Fonthandle *handle)
129 Destroys a font object, which must have been the return value of
135 i_ft2_destroy(FT2_Fonthandle *handle) {
136 FT_Done_Face(handle->face);
141 =item i_ft2_setdpi(FT2_Fonthandle *handle, int xdpi, int ydpi)
143 Sets the resolution in dots per inch at which point sizes scaled, by
144 default xdpi and ydpi are 72, so that 1 point maps to 1 pixel.
146 Both xdpi and ydpi should be positive.
148 Return true on success.
153 i_ft2_setdpi(FT2_Fonthandle *handle, int xdpi, int ydpi) {
155 if (xdpi > 0 && ydpi > 0) {
161 i_push_error(0, "resolutions must be positive");
167 =item i_ft2_getdpi(FT2_Fonthandle *handle, int *xdpi, int *ydpi)
169 Retrieves the current horizontal and vertical resolutions at which
170 point sizes are scaled.
175 i_ft2_getdpi(FT2_Fonthandle *handle, int *xdpi, int *ydpi) {
176 *xdpi = handle->xdpi;
177 *ydpi = handle->ydpi;
183 =item i_ft2_settransform(FT2_FontHandle *handle, double *matrix)
185 Sets a transormation matrix for output.
187 This should be a 2 x 3 matrix like:
189 matrix[0] matrix[1] matrix[2]
190 matrix[3] matrix[4] matrix[5]
195 i_ft2_settransform(FT2_Fonthandle *handle, double *matrix) {
200 m.xx = matrix[0] * 65536;
201 m.xy = matrix[1] * 65536;
202 v.x = matrix[2]; /* this could be pels of 26.6 fixed - not sure */
203 m.yx = matrix[3] * 65536;
204 m.yy = matrix[4] * 65536;
205 v.y = matrix[5]; /* see just above */
207 FT_Set_Transform(handle->face, &m, &v);
209 for (i = 0; i < 6; ++i)
210 handle->matrix[i] = matrix[i];
217 =item i_ft2_sethinting(FT2_Fonthandle *handle, int hinting)
219 If hinting is non-zero then glyph hinting is enabled, otherwise disabled.
221 i_ft2_settransform() disables hinting to prevent distortions in
222 gradual text transformations.
226 int i_ft2_sethinting(FT2_Fonthandle *handle, int hinting) {
227 handle->hint = hinting;
232 =item i_ft2_bbox(FT2_Fonthandle *handle, double cheight, double cwidth, char *text, int len, int *bbox)
234 Retrieves bounding box information for the font at the given
235 character width and height. This ignores the transformation matrix.
237 Returns non-zero on success.
242 i_ft2_bbox(FT2_Fonthandle *handle, double cheight, double cwidth,
243 char *text, int len, int *bbox) {
248 int ascent = 0, descent = 0;
249 int glyph_ascent, glyph_descent;
250 FT_Glyph_Metrics *gm;
253 error = FT_Set_Char_Size(handle->face, cwidth*64, cheight*64,
254 handle->xdpi, handle->ydpi);
256 ft2_push_message(error);
257 i_push_error(0, "setting size");
263 int c = (unsigned char)*text++;
265 index = FT_Get_Char_Index(handle->face, c);
266 error = FT_Load_Glyph(handle->face, index, FT_LOAD_DEFAULT);
268 ft2_push_message(error);
269 i_push_errorf(0, "loading glyph for character \\x%02x (glyph 0x%04X)",
273 gm = &handle->face->glyph->metrics;
274 glyph_ascent = gm->horiBearingY / 64;
275 glyph_descent = glyph_ascent - gm->height/64;
277 start = gm->horiBearingX / 64;
278 /* handles -ve values properly */
279 ascent = glyph_ascent;
280 descent = glyph_descent;
284 if (glyph_ascent > ascent)
285 ascent = glyph_ascent;
286 if (glyph_descent > descent)
287 descent = glyph_descent;
289 width += gm->horiAdvance / 64;
293 handle the case where the right the of the character overlaps the
295 int rightb = gm->horiAdvance - gm->horiBearingX - gm->width;
297 width -= rightb / 64;
302 bbox[1] = handle->face->size->metrics.descender / 64;
304 bbox[3] = handle->face->size->metrics.ascender / 64;
312 =item transform_box(FT2_FontHandle *handle, int bbox[4])
314 bbox contains coorinates of a the top-left and bottom-right of a bounding
315 box relative to a point.
317 This is then transformed and the values in bbox[4] are the top-left
318 and bottom-right of the new bounding box.
320 This is meant to provide the bounding box of a transformed character
321 box. The problem is that if the character was round and is rotated,
322 the real bounding box isn't going to be much different from the
323 original, but this function will return a _bigger_ bounding box. I
324 suppose I could work my way through the glyph outline, but that's
329 void ft2_transform_box(FT2_Fonthandle *handle, int bbox[4]) {
331 double *matrix = handle->matrix;
334 work[0] = matrix[0] * bbox[0] + matrix[1] * bbox[1];
335 work[1] = matrix[3] * bbox[0] + matrix[4] * bbox[1];
336 work[2] = matrix[0] * bbox[2] + matrix[1] * bbox[1];
337 work[3] = matrix[3] * bbox[2] + matrix[4] * bbox[1];
338 work[4] = matrix[0] * bbox[0] + matrix[1] * bbox[3];
339 work[5] = matrix[3] * bbox[0] + matrix[4] * bbox[3];
340 work[6] = matrix[0] * bbox[2] + matrix[1] * bbox[3];
341 work[7] = matrix[3] * bbox[2] + matrix[4] * bbox[3];
343 bbox[0] = floor(min(min(work[0], work[2]),min(work[4], work[6])));
344 bbox[1] = floor(min(min(work[1], work[3]),min(work[5], work[7])));
345 bbox[2] = ceil(max(max(work[0], work[2]),max(work[4], work[6])));
346 bbox[3] = ceil(max(max(work[1], work[3]),max(work[5], work[7])));
350 =item expand_bounds(int bbox[4], int bbox2[4])
352 Treating bbox[] and bbox2[] as 2 bounding boxes, produces a new
353 bounding box in bbox[] that encloses both.
357 static void expand_bounds(int bbox[4], int bbox2[4]) {
358 bbox[0] = min(bbox[0], bbox2[0]);
359 bbox[1] = min(bbox[1], bbox2[1]);
360 bbox[2] = max(bbox[2], bbox2[2]);
361 bbox[3] = max(bbox[3], bbox2[3]);
365 =item i_ft2_bbox_r(FT2_Fonthandle *handle, double cheight, double cwidth, char *text, int len, int vlayout, int utf8, int *bbox)
367 Retrieves bounding box information for the font at the given
368 character width and height.
370 This version finds the rectangular bounding box of the glyphs, with
371 the text as transformed by the transformation matrix. As with
372 i_ft2_bbox (bbox[0], bbox[1]) will the the offset from the start of
373 the topline to the top-left of the bounding box. Unlike i_ft2_bbox()
374 this could be near the bottom left corner of the box.
376 (bbox[4], bbox[5]) is the offset to the start of the baseline.
377 (bbox[6], bbox[7]) is the offset from the start of the baseline to the
380 Returns non-zero on success.
385 i_ft2_bbox_r(FT2_Fonthandle *handle, double cheight, double cwidth,
386 char *text, int len, int vlayout, int utf8, int *bbox) {
391 int ascent = 0, descent = 0;
392 int glyph_ascent, glyph_descent;
393 FT_Glyph_Metrics *gm;
401 int loadFlags = FT_LOAD_DEFAULT;
404 loadFlags |= FT_LOAD_VERTICAL_LAYOUT;
406 error = FT_Set_Char_Size(handle->face, cwidth*64, cheight*64,
407 handle->xdpi, handle->ydpi);
409 ft2_push_message(error);
410 i_push_error(0, "setting size");
418 c = utf8_advance(&text, &len);
420 i_push_error(0, "invalid UTF8 character");
425 c = (unsigned char)*text++;
429 index = FT_Get_Char_Index(handle->face, c);
430 error = FT_Load_Glyph(handle->face, index, loadFlags);
432 ft2_push_message(error);
433 i_push_errorf(0, "loading glyph for character \\x%02x (glyph 0x%04X)",
437 slot = handle->face->glyph;
440 /* these probably don't mean much for vertical layouts */
441 glyph_ascent = gm->horiBearingY / 64;
442 glyph_descent = glyph_ascent - gm->height/64;
444 work[0] = gm->vertBearingX;
445 work[1] = gm->vertBearingY;
448 work[0] = gm->horiBearingX;
449 work[1] = gm->horiBearingY;
451 work[2] = gm->width + work[0];
452 work[3] = work[1] - gm->height;
454 bbox[4] = work[0] * handle->matrix[0] + work[1] * handle->matrix[1] + handle->matrix[2];
455 bbox[5] = work[0] * handle->matrix[3] + work[1] * handle->matrix[4] + handle->matrix[5];
456 bbox[4] = bbox[4] < 0 ? -(-bbox[4] + 32)/64 : (bbox[4] + 32) / 64;
459 ft2_transform_box(handle, work);
460 for (i = 0; i < 4; ++i)
467 for (i = 0; i < 4; ++i)
469 ascent = glyph_ascent;
470 descent = glyph_descent;
474 expand_bounds(bounds, work);
476 x += slot->advance.x / 64;
477 y += slot->advance.y / 64;
479 if (glyph_ascent > ascent)
480 ascent = glyph_ascent;
481 if (glyph_descent > descent)
482 descent = glyph_descent;
486 handle the case where the right the of the character overlaps the
488 /*int rightb = gm->horiAdvance - gm->horiBearingX - gm->width;
490 width -= rightb / 64;*/
494 /* at this point bounds contains the bounds relative to the CP,
495 and x, y hold the final position relative to the CP */
502 bbox[1] = -bounds[3];
504 bbox[3] = -bounds[1];
514 make_bmp_map(FT_Bitmap *bitmap, unsigned char *map);
517 =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)
519 Renders I<text> to (I<tx>, I<ty>) in I<im> using color I<cl> at the given
520 I<cheight> and I<cwidth>.
522 If align is 0, then the text is rendered with the top-left of the
523 first character at (I<tx>, I<ty>). If align is non-zero then the text
524 is rendered with (I<tx>, I<ty>) aligned with the base-line of the
527 If aa is non-zero then the text is anti-aliased.
529 Returns non-zero on success.
534 i_ft2_text(FT2_Fonthandle *handle, i_img *im, int tx, int ty, i_color *cl,
535 double cheight, double cwidth, char *text, int len, int align,
536 int aa, int vlayout, int utf8) {
539 FT_Glyph_Metrics *gm;
544 unsigned char map[256];
545 char last_mode = ft_pixel_mode_none;
549 int loadFlags = FT_LOAD_DEFAULT;
552 if (!FT_HAS_VERTICAL(handle->face)) {
553 i_push_error(0, "face has no vertical metrics");
556 loadFlags |= FT_LOAD_VERTICAL_LAYOUT;
559 loadFlags |= FT_LOAD_NO_HINTING;
561 /* set the base-line based on the string ascent */
562 if (!i_ft2_bbox(handle, cheight, cwidth, text, len, bbox))
566 /* this may need adjustment */
567 tx -= bbox[0] * handle->matrix[0] + bbox[5] * handle->matrix[1] + handle->matrix[2];
568 ty += bbox[0] * handle->matrix[3] + bbox[5] * handle->matrix[4] + handle->matrix[5];
573 c = utf8_advance(&text, &len);
575 i_push_error(0, "invalid UTF8 character");
580 c = (unsigned char)*text++;
584 index = FT_Get_Char_Index(handle->face, c);
585 error = FT_Load_Glyph(handle->face, index, loadFlags);
587 ft2_push_message(error);
588 i_push_errorf(0, "loading glyph for character \\x%02x (glyph 0x%04X)",
592 slot = handle->face->glyph;
595 error = FT_Render_Glyph(slot, aa ? ft_render_mode_normal : ft_render_mode_mono);
597 ft2_push_message(error);
598 i_push_errorf(0, "rendering glyph 0x%04X (character \\x%02X)");
601 if (slot->bitmap.pixel_mode == ft_pixel_mode_mono) {
602 bmp = slot->bitmap.buffer;
603 for (y = 0; y < slot->bitmap.rows; ++y) {
606 for (x = 0; x < slot->bitmap.width; ++x) {
608 i_ppix(im, tx+x+slot->bitmap_left, ty+y-slot->bitmap_top, cl);
616 bmp += slot->bitmap.pitch;
620 /* grey scale or something we can treat as greyscale */
621 /* we create a map to convert from the bitmap values to 0-255 */
622 if (last_mode != slot->bitmap.pixel_mode
623 || last_grays != slot->bitmap.num_grays) {
624 if (!make_bmp_map(&slot->bitmap, map))
626 last_mode = slot->bitmap.pixel_mode;
627 last_grays = slot->bitmap.num_grays;
630 bmp = slot->bitmap.buffer;
631 for (y = 0; y < slot->bitmap.rows; ++y) {
632 for (x = 0; x < slot->bitmap.width; ++x) {
633 int value = map[bmp[x]];
635 i_gpix(im, tx+x+slot->bitmap_left, ty+y-slot->bitmap_top, &pel);
636 for (ch = 0; ch < im->channels; ++ch) {
638 ((255-value)*pel.channel[ch] + value * cl->channel[ch]) / 255;
640 i_ppix(im, tx+x+slot->bitmap_left, ty+y-slot->bitmap_top, &pel);
643 bmp += slot->bitmap.pitch;
647 tx += slot->advance.x / 64;
648 ty -= slot->advance.y / 64;
655 =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)
657 Renders I<text> to (I<tx>, I<ty>) in I<im> to I<channel> at the given
658 I<cheight> and I<cwidth>.
660 If align is 0, then the text is rendered with the top-left of the
661 first character at (I<tx>, I<ty>). If align is non-zero then the text
662 is rendered with (I<tx>, I<ty>) aligned with the base-line of the
665 If aa is non-zero then the text is anti-aliased.
667 Returns non-zero on success.
672 i_ft2_cp(FT2_Fonthandle *handle, i_img *im, int tx, int ty, int channel,
673 double cheight, double cwidth, char *text, int len, int align,
674 int aa, int vlayout, int utf8) {
680 if (vlayout && !FT_HAS_VERTICAL(handle->face)) {
681 i_push_error(0, "face has no vertical metrics");
685 if (!i_ft2_bbox_r(handle, cheight, cwidth, text, len, vlayout, utf8, bbox))
688 work = i_img_empty_ch(NULL, bbox[2]-bbox[0]+1, bbox[3]-bbox[1]+1, 1);
690 if (!i_ft2_text(handle, work, -bbox[0], -bbox[1], &cl, cheight, cwidth,
691 text, len, 1, aa, vlayout, utf8))
699 /* render to the specified channel */
700 /* this will be sped up ... */
701 for (y = 0; y < work->ysize; ++y) {
702 for (x = 0; x < work->xsize; ++x) {
703 i_gpix(work, x, y, &cl);
704 i_gpix(im, tx + x + bbox[0], ty + y + bbox[1], &cl2);
705 cl2.channel[channel] = cl.channel[0];
706 i_ppix(im, tx + x + bbox[0], ty + y + bbox[1], &cl2);
713 /* uses a method described in fterrors.h to build an error translation
716 #undef __FT_ERRORS_H__
717 #define FT_ERRORDEF(e, v, s) case v: i_push_error(code, s); return;
718 #define FT_ERROR_START_LIST
719 #define FT_ERROR_END_LIST
724 =head2 Internal Functions
726 These functions are used in the implementation of freetyp2.c and should not
727 (usually cannot) be called from outside it.
731 =item ft2_push_message(int code)
733 Pushes an error message corresponding to code onto the error stack.
737 static void ft2_push_message(int code) {
744 sprintf(unknown, "Unknown Freetype2 error code 0x%04X\n", code);
745 i_push_error(code, unknown);
749 =item make_bmp_map(FT_Bitmap *bitmap, unsigned char *map)
751 Creates a map to convert grey levels from the glyphs bitmap into
752 values scaled 0..255.
757 make_bmp_map(FT_Bitmap *bitmap, unsigned char *map) {
761 switch (bitmap->pixel_mode) {
762 case ft_pixel_mode_grays:
763 scale = bitmap->num_grays;
767 i_push_errorf(0, "I can't handle pixel mode %d", bitmap->pixel_mode);
771 /* build the table */
772 for (i = 0; i < scale; ++i)
773 map[i] = i * 255 / (bitmap->num_grays - 1);
783 struct utf8_size utf8_sizes[] =
792 =item utf8_advance(char **p, int *len)
794 Retreive a UTF8 character from the stream.
796 Modifies *p and *len to indicate the consumed characters.
798 This doesn't support the extended UTF8 encoding used by later versions
804 unsigned long utf8_advance(char **p, int *len) {
807 unsigned char codes[3];
812 for (i = 0; i < sizeof(utf8_sizes)/sizeof(*utf8_sizes); ++i) {
813 if ((c & utf8_sizes[i].mask) == utf8_sizes[i].expect) {
814 clen = utf8_sizes[i].size;
817 if (clen == 0 || *len < clen-1) {
822 /* check that each character is well formed */
826 if (((*p)[ci] & 0xC0) != 0x80) {
830 codes[ci] = (*p)[ci];
833 *p += clen-1; *len -= clen-1;
835 if ((c & 0xE0) == 0xC0) {
836 return ((c & 0x1F) << 6) + (codes[0] & 0x3F);
838 else if ((c & 0xF0) == 0xE0) {
839 return ((c & 0x0F) << 12) | ((codes[0] & 0x3F) << 6) | (codes[1] & 0x3f);
841 else if ((c & 0xF8) == 0xF0) {
842 return ((c & 0x07) << 18) | ((codes[0] & 0x3F) << 12)
843 | ((codes[1] & 0x3F) << 6) | (codes[2] & 0x3F);
846 *p -= clen; *len += clen;
860 Tony Cook <tony@develop-help.com>, with a fair amount of help from
861 reading the code in font.c.
865 font.c, Imager::Font(3), Imager(3)
867 http://www.freetype.org/