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 }
15 i_img_dim bbox[BOUNDING_BOX_COUNT];
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,
20 aa, vlayout, utf8)) { error }
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.
43 #include FT_FREETYPE_H
44 #ifdef FT_MULTIPLE_MASTERS_H
45 #ifndef T1_CONFIG_OPTION_NO_MM_SUPPORT
47 #include FT_MULTIPLE_MASTERS_H
51 static void ft2_push_message(int code);
53 static void ft2_final(void *);
55 static im_slot_t slot = -1;
66 static i_img_dim i_min(i_img_dim a, i_img_dim b);
67 static i_img_dim i_max(i_img_dim a, i_img_dim b);
70 i_ft2_version(int runtime, char *buf, size_t buf_size) {
76 i_push_error(0, "zero size buffer supplied");
81 /* initialized to work around a bug in FT2
82 http://lists.nongnu.org/archive/html/freetype-devel/2002-09/msg00058.html
83 Though I don't know why I still see this in 2.4.2
85 FT_Int major = 1, minor = 1, patch = 1;
87 if ((ft2 = i_ft2_init()) == NULL)
90 FT_Library_Version(ft2->library, &major, &minor, &patch);
91 sprintf(work, "%d.%d.%d", (int)major, (int)minor, (int)patch);
94 sprintf(work, "%d.%d.%d", FREETYPE_MAJOR, FREETYPE_MINOR, FREETYPE_PATCH);
96 strncpy(buf, work, buf_size);
97 buf[buf_size-1] = '\0';
105 slot = im_context_slot_new(ft2_final);
109 =item i_ft2_init(void)
111 Initializes the Freetype 2 library.
113 Returns ft2_state * on success or NULL on failure.
121 im_context_t ctx = im_get_context();
122 ft2_state *ft2 = im_context_slot_get(ctx, slot);
125 ft2 = mymalloc(sizeof(ft2_state));
126 ft2->initialized = 0;
129 im_context_slot_set(ctx, slot, ft2);
130 mm_log((1, "created FT2 state %p for context %p\n", ft2, ctx));
134 if (!ft2->initialized) {
135 error = FT_Init_FreeType(&ft2->library);
137 ft2_push_message(error);
138 i_push_error(0, "Initializing Freetype2");
141 mm_log((1, "initialized FT2 state %p\n", ft2));
143 ft2->initialized = 1;
150 ft2_final(void *state) {
151 ft2_state *ft2 = state;
153 if (ft2->initialized) {
154 mm_log((1, "finalizing FT2 state %p\n", state));
155 FT_Done_FreeType(ft2->library);
157 ft2->initialized = 0;
160 mm_log((1, "freeing FT2 state %p\n", state));
164 struct FT2_Fonthandle {
169 FT_Encoding encoding;
171 /* used to adjust so we can align the draw point to the top-left */
175 /* Multiple master data if any */
181 /* the following is used to select a "best" encoding */
182 static struct enc_score {
183 FT_Encoding encoding;
187 /* the selections here are fairly arbitrary
188 ideally we need to give the user a list of encodings available
189 and a mechanism to choose one */
190 { ft_encoding_unicode, 10 },
191 { ft_encoding_sjis, 8 },
192 { ft_encoding_gb2312, 8 },
193 { ft_encoding_big5, 8 },
194 { ft_encoding_wansung, 8 },
195 { ft_encoding_johab, 8 },
196 { ft_encoding_latin_2, 6 },
197 { ft_encoding_apple_roman, 6 },
198 { ft_encoding_adobe_standard, 6 },
199 { ft_encoding_adobe_expert, 6 },
203 =item i_ft2_new(char *name, int index)
205 Creates a new font object, from the file given by I<name>. I<index>
206 is the index of the font in a file with multiple fonts, where 0 is the
209 Return NULL on failure.
215 i_ft2_new(const char *name, int index) {
217 FT2_Fonthandle *result;
220 FT_Encoding encoding;
224 mm_log((1, "i_ft2_new(name %p, index %d)\n", name, index));
226 if ((ft2 = i_ft2_init()) == NULL)
230 error = FT_New_Face(ft2->library, name, index, &face);
232 ft2_push_message(error);
233 i_push_error(error, "Opening face");
234 mm_log((2, "error opening face '%s': %d\n", name, error));
238 encoding = face->num_charmaps ? face->charmaps[0]->encoding : ft_encoding_unicode;
240 for (i = 0; i < face->num_charmaps; ++i) {
241 FT_Encoding enc_entry = face->charmaps[i]->encoding;
242 mm_log((2, "i_ft2_new, encoding %X platform %u encoding %u\n",
243 (unsigned)enc_entry, face->charmaps[i]->platform_id,
244 face->charmaps[i]->encoding_id));
245 for (j = 0; j < sizeof(enc_scores) / sizeof(*enc_scores); ++j) {
246 if (enc_scores[j].encoding == enc_entry && enc_scores[j].score > score) {
247 encoding = enc_entry;
248 score = enc_scores[j].score;
253 FT_Select_Charmap(face, encoding);
254 mm_log((2, "i_ft2_new, selected encoding %X\n", (unsigned)encoding));
256 result = mymalloc(sizeof(FT2_Fonthandle));
259 result->xdpi = result->ydpi = 72;
260 result->encoding = encoding;
262 /* by default we disable hinting on a call to i_ft2_settransform()
263 if we don't do this, then the hinting can the untransformed text
264 to be a different size to the transformed text.
265 Obviously we have it initially enabled.
269 /* I originally forgot this: :/ */
270 /*i_ft2_settransform(result, matrix); */
271 result->matrix[0] = 1; result->matrix[1] = 0; result->matrix[2] = 0;
272 result->matrix[3] = 0; result->matrix[4] = 1; result->matrix[5] = 0;
276 FT_Multi_Master *mm = &result->mm;
279 if ((face->face_flags & FT_FACE_FLAG_MULTIPLE_MASTERS) != 0
280 && (error = FT_Get_Multi_Master(face, mm)) == 0) {
281 mm_log((2, "MM Font, %d axes, %d designs\n", mm->num_axis, mm->num_designs));
282 for (i = 0; i < mm->num_axis; ++i) {
283 mm_log((2, " axis %d name %s range %ld - %ld\n", i, mm->axis[i].name,
284 (long)(mm->axis[i].minimum), (long)(mm->axis[i].maximum)));
289 mm_log((2, "No multiple masters\n"));
299 =item i_ft2_destroy(FT2_Fonthandle *handle)
301 Destroys a font object, which must have been the return value of
307 i_ft2_destroy(FT2_Fonthandle *handle) {
308 FT_Done_Face(handle->face);
313 =item i_ft2_setdpi(FT2_Fonthandle *handle, int xdpi, int ydpi)
315 Sets the resolution in dots per inch at which point sizes scaled, by
316 default xdpi and ydpi are 72, so that 1 point maps to 1 pixel.
318 Both xdpi and ydpi should be positive.
320 Return true on success.
325 i_ft2_setdpi(FT2_Fonthandle *handle, int xdpi, int ydpi) {
327 if (xdpi > 0 && ydpi > 0) {
333 i_push_error(0, "resolutions must be positive");
339 =item i_ft2_getdpi(FT2_Fonthandle *handle, int *xdpi, int *ydpi)
341 Retrieves the current horizontal and vertical resolutions at which
342 point sizes are scaled.
347 i_ft2_getdpi(FT2_Fonthandle *handle, int *xdpi, int *ydpi) {
348 *xdpi = handle->xdpi;
349 *ydpi = handle->ydpi;
355 =item i_ft2_settransform(FT2_FontHandle *handle, double *matrix)
357 Sets a transormation matrix for output.
359 This should be a 2 x 3 matrix like:
361 matrix[0] matrix[1] matrix[2]
362 matrix[3] matrix[4] matrix[5]
367 i_ft2_settransform(FT2_Fonthandle *handle, const double *matrix) {
372 m.xx = matrix[0] * 65536;
373 m.xy = matrix[1] * 65536;
374 v.x = matrix[2]; /* this could be pels of 26.6 fixed - not sure */
375 m.yx = matrix[3] * 65536;
376 m.yy = matrix[4] * 65536;
377 v.y = matrix[5]; /* see just above */
379 FT_Set_Transform(handle->face, &m, &v);
381 for (i = 0; i < 6; ++i)
382 handle->matrix[i] = matrix[i];
389 =item i_ft2_sethinting(FT2_Fonthandle *handle, int hinting)
391 If hinting is non-zero then glyph hinting is enabled, otherwise disabled.
393 i_ft2_settransform() disables hinting to prevent distortions in
394 gradual text transformations.
398 int i_ft2_sethinting(FT2_Fonthandle *handle, int hinting) {
399 handle->hint = hinting;
404 =item i_ft2_bbox(FT2_Fonthandle *handle, double cheight, double cwidth, char *text, size_t len, i_img_dim *bbox)
406 Retrieves bounding box information for the font at the given
407 character width and height. This ignores the transformation matrix.
409 Returns non-zero on success.
414 i_ft2_bbox(FT2_Fonthandle *handle, double cheight, double cwidth,
415 char const *text, size_t len, i_img_dim *bbox, int utf8) {
420 int ascent = 0, descent = 0;
421 int glyph_ascent, glyph_descent;
422 FT_Glyph_Metrics *gm;
424 int loadFlags = FT_LOAD_DEFAULT;
429 mm_log((1, "i_ft2_bbox(handle %p, cheight %f, cwidth %f, text %p, len %u, bbox %p)\n",
430 handle, cheight, cwidth, text, (unsigned)len, bbox));
432 error = FT_Set_Char_Size(handle->face, cwidth*64, cheight*64,
433 handle->xdpi, handle->ydpi);
435 ft2_push_message(error);
436 i_push_error(0, "setting size");
440 loadFlags |= FT_LOAD_NO_HINTING;
447 c = i_utf8_advance(&text, &len);
449 i_push_error(0, "invalid UTF8 character");
454 c = (unsigned char)*text++;
458 index = FT_Get_Char_Index(handle->face, c);
459 error = FT_Load_Glyph(handle->face, index, loadFlags);
461 ft2_push_message(error);
462 i_push_errorf(0, "loading glyph for character \\x%02lx (glyph 0x%04X)",
466 gm = &handle->face->glyph->metrics;
467 glyph_ascent = gm->horiBearingY / 64;
468 glyph_descent = glyph_ascent - gm->height/64;
470 start = gm->horiBearingX / 64;
471 /* handles -ve values properly */
472 ascent = glyph_ascent;
473 descent = glyph_descent;
477 if (glyph_ascent > ascent)
478 ascent = glyph_ascent;
479 if (glyph_descent < descent)
480 descent = glyph_descent;
482 width += gm->horiAdvance / 64;
486 handle the case where the right the of the character overlaps the
488 rightb = (gm->horiAdvance - gm->horiBearingX - gm->width)/64;
494 bbox[BBOX_NEG_WIDTH] = start;
495 bbox[BBOX_GLOBAL_DESCENT] = handle->face->size->metrics.descender / 64;
496 bbox[BBOX_POS_WIDTH] = width;
498 bbox[BBOX_POS_WIDTH] -= rightb;
499 bbox[BBOX_GLOBAL_ASCENT] = handle->face->size->metrics.ascender / 64;
500 bbox[BBOX_DESCENT] = descent;
501 bbox[BBOX_ASCENT] = ascent;
502 bbox[BBOX_ADVANCE_WIDTH] = width;
503 bbox[BBOX_RIGHT_BEARING] = rightb;
504 mm_log((1, " bbox=> negw=%" i_DF " glob_desc=%" i_DF " pos_wid=%" i_DF
505 " glob_asc=%" i_DF " desc=%" i_DF " asc=%" i_DF " adv_width=%" i_DF
506 " rightb=%" i_DF "\n",
507 i_DFc(bbox[0]), i_DFc(bbox[1]), i_DFc(bbox[2]), i_DFc(bbox[3]),
508 i_DFc(bbox[4]), i_DFc(bbox[5]), i_DFc(bbox[6]), i_DFc(bbox[7])));
510 return BBOX_RIGHT_BEARING + 1;
514 =item transform_box(FT2_FontHandle *handle, int bbox[4])
516 bbox contains coorinates of a the top-left and bottom-right of a bounding
517 box relative to a point.
519 This is then transformed and the values in bbox[4] are the top-left
520 and bottom-right of the new bounding box.
522 This is meant to provide the bounding box of a transformed character
523 box. The problem is that if the character was round and is rotated,
524 the real bounding box isn't going to be much different from the
525 original, but this function will return a _bigger_ bounding box. I
526 suppose I could work my way through the glyph outline, but that's
531 void ft2_transform_box(FT2_Fonthandle *handle, i_img_dim bbox[4]) {
533 double *matrix = handle->matrix;
535 work[0] = matrix[0] * bbox[0] + matrix[1] * bbox[1];
536 work[1] = matrix[3] * bbox[0] + matrix[4] * bbox[1];
537 work[2] = matrix[0] * bbox[2] + matrix[1] * bbox[1];
538 work[3] = matrix[3] * bbox[2] + matrix[4] * bbox[1];
539 work[4] = matrix[0] * bbox[0] + matrix[1] * bbox[3];
540 work[5] = matrix[3] * bbox[0] + matrix[4] * bbox[3];
541 work[6] = matrix[0] * bbox[2] + matrix[1] * bbox[3];
542 work[7] = matrix[3] * bbox[2] + matrix[4] * bbox[3];
544 bbox[0] = floor(i_min(i_min(work[0], work[2]),i_min(work[4], work[6])));
545 bbox[1] = floor(i_min(i_min(work[1], work[3]),i_min(work[5], work[7])));
546 bbox[2] = ceil(i_max(i_max(work[0], work[2]),i_max(work[4], work[6])));
547 bbox[3] = ceil(i_max(i_max(work[1], work[3]),i_max(work[5], work[7])));
551 =item expand_bounds(int bbox[4], int bbox2[4])
553 Treating bbox[] and bbox2[] as 2 bounding boxes, produces a new
554 bounding box in bbox[] that encloses both.
558 static void expand_bounds(i_img_dim bbox[4], i_img_dim bbox2[4]) {
559 bbox[0] = i_min(bbox[0], bbox2[0]);
560 bbox[1] = i_min(bbox[1], bbox2[1]);
561 bbox[2] = i_max(bbox[2], bbox2[2]);
562 bbox[3] = i_max(bbox[3], bbox2[3]);
566 =item i_ft2_bbox_r(FT2_Fonthandle *handle, double cheight, double cwidth, char *text, size_t len, int vlayout, int utf8, i_img_dim *bbox)
568 Retrieves bounding box information for the font at the given
569 character width and height.
571 This version finds the rectangular bounding box of the glyphs, with
572 the text as transformed by the transformation matrix. As with
573 i_ft2_bbox (bbox[0], bbox[1]) will the the offset from the start of
574 the topline to the top-left of the bounding box. Unlike i_ft2_bbox()
575 this could be near the bottom left corner of the box.
577 (bbox[4], bbox[5]) is the offset to the start of the baseline.
578 (bbox[6], bbox[7]) is the offset from the start of the baseline to the
581 Returns non-zero on success.
586 i_ft2_bbox_r(FT2_Fonthandle *handle, double cheight, double cwidth,
587 char const *text, size_t len, int vlayout, int utf8, i_img_dim *bbox) {
591 i_img_dim ascent = 0, descent = 0;
592 int glyph_ascent, glyph_descent;
593 FT_Glyph_Metrics *gm;
595 i_img_dim bounds[4] = { 0 };
599 int loadFlags = FT_LOAD_DEFAULT;
602 loadFlags |= FT_LOAD_VERTICAL_LAYOUT;
604 loadFlags |= FT_LOAD_NO_HINTING;
606 error = FT_Set_Char_Size(handle->face, cwidth*64, cheight*64,
607 handle->xdpi, handle->ydpi);
609 ft2_push_message(error);
610 i_push_error(0, "setting size");
617 c = i_utf8_advance(&text, &len);
619 i_push_error(0, "invalid UTF8 character");
624 c = (unsigned char)*text++;
628 index = FT_Get_Char_Index(handle->face, c);
629 error = FT_Load_Glyph(handle->face, index, loadFlags);
631 ft2_push_message(error);
632 i_push_errorf(0, "loading glyph for character \\x%02lx (glyph 0x%04X)",
636 slot = handle->face->glyph;
639 /* these probably don't mean much for vertical layouts */
640 glyph_ascent = gm->horiBearingY / 64;
641 glyph_descent = glyph_ascent - gm->height/64;
643 work[0] = gm->vertBearingX;
644 work[1] = gm->vertBearingY;
647 work[0] = gm->horiBearingX;
648 work[1] = gm->horiBearingY;
650 work[2] = gm->width + work[0];
651 work[3] = work[1] - gm->height;
653 bbox[4] = work[0] * handle->matrix[0] + work[1] * handle->matrix[1] + handle->matrix[2];
654 bbox[5] = work[0] * handle->matrix[3] + work[1] * handle->matrix[4] + handle->matrix[5];
655 bbox[4] = bbox[4] < 0 ? -(-bbox[4] + 32)/64 : (bbox[4] + 32) / 64;
658 ft2_transform_box(handle, work);
659 for (i = 0; i < 4; ++i)
666 for (i = 0; i < 4; ++i)
668 ascent = glyph_ascent;
669 descent = glyph_descent;
673 expand_bounds(bounds, work);
675 x += slot->advance.x / 64;
676 y += slot->advance.y / 64;
678 if (glyph_ascent > ascent)
679 ascent = glyph_ascent;
680 if (glyph_descent > descent)
681 descent = glyph_descent;
685 handle the case where the right the of the character overlaps the
687 /*int rightb = gm->horiAdvance - gm->horiBearingX - gm->width;
689 width -= rightb / 64;*/
693 /* at this point bounds contains the bounds relative to the CP,
694 and x, y hold the final position relative to the CP */
701 bbox[1] = -bounds[3];
703 bbox[3] = -bounds[1];
711 make_bmp_map(FT_Bitmap *bitmap, unsigned char *map);
714 =item i_ft2_text(FT2_Fonthandle *handle, i_img *im, int tx, int ty, i_color *cl, double cheight, double cwidth, char *text, size_t len, int align, int aa)
716 Renders I<text> to (I<tx>, I<ty>) in I<im> using color I<cl> at the given
717 I<cheight> and I<cwidth>.
719 If align is 0, then the text is rendered with the top-left of the
720 first character at (I<tx>, I<ty>). If align is non-zero then the text
721 is rendered with (I<tx>, I<ty>) aligned with the base-line of the
724 If aa is non-zero then the text is anti-aliased.
726 Returns non-zero on success.
731 i_ft2_text(FT2_Fonthandle *handle, i_img *im, i_img_dim tx, i_img_dim ty, const i_color *cl,
732 double cheight, double cwidth, char const *text, size_t len,
733 int align, int aa, int vlayout, int utf8) {
736 FT_Glyph_Metrics *gm;
737 i_img_dim bbox[BOUNDING_BOX_COUNT];
740 unsigned char map[256];
741 char last_mode = ft_pixel_mode_none;
743 int loadFlags = FT_LOAD_DEFAULT;
744 i_render *render = NULL;
745 unsigned char *work_bmp = NULL;
746 size_t work_bmp_size = 0;
748 mm_log((1, "i_ft2_text(handle %p, im %p, (tx,ty) (" i_DFp "), cl %p (#%02x%02x%02x%02x), cheight %f, cwidth %f, text %p, len %u, align %d, aa %d, vlayout %d, utf8 %d)\n",
749 handle, im, i_DFcp(tx, ty), cl, cl->rgba.r, cl->rgba.g, cl->rgba.b,
750 cl->rgba.a, cheight, cwidth, text, (unsigned)len, align, aa,
756 if (!FT_HAS_VERTICAL(handle->face)) {
757 i_push_error(0, "face has no vertical metrics");
760 loadFlags |= FT_LOAD_VERTICAL_LAYOUT;
763 loadFlags |= FT_LOAD_NO_HINTING;
765 /* set the base-line based on the string ascent */
766 if (!i_ft2_bbox(handle, cheight, cwidth, text, len, bbox, utf8))
769 render = i_render_new(im, bbox[BBOX_POS_WIDTH] - bbox[BBOX_NEG_WIDTH]);
771 work_bmp_size = bbox[BBOX_POS_WIDTH] - bbox[BBOX_NEG_WIDTH];
772 work_bmp = mymalloc(work_bmp_size);
775 /* this may need adjustment */
776 tx -= bbox[0] * handle->matrix[0] + bbox[5] * handle->matrix[1] + handle->matrix[2];
777 ty += bbox[0] * handle->matrix[3] + bbox[5] * handle->matrix[4] + handle->matrix[5];
782 c = i_utf8_advance(&text, &len);
784 i_push_error(0, "invalid UTF8 character");
789 c = (unsigned char)*text++;
793 index = FT_Get_Char_Index(handle->face, c);
794 error = FT_Load_Glyph(handle->face, index, loadFlags);
796 ft2_push_message(error);
797 i_push_errorf(0, "loading glyph for character \\x%02lx (glyph 0x%04X)",
800 i_render_delete(render);
803 slot = handle->face->glyph;
807 error = FT_Render_Glyph(slot, aa ? ft_render_mode_normal : ft_render_mode_mono);
809 ft2_push_message(error);
810 i_push_errorf(0, "rendering glyph 0x%04lX (character \\x%02X)", c, index);
812 i_render_delete(render);
815 if (slot->bitmap.pixel_mode == ft_pixel_mode_mono) {
816 unsigned char *bmp = slot->bitmap.buffer;
817 if (work_bmp_size < slot->bitmap.width) {
818 work_bmp_size = slot->bitmap.width;
819 work_bmp = myrealloc(work_bmp, work_bmp_size);
821 for (y = 0; y < slot->bitmap.rows; ++y) {
824 unsigned char *p = work_bmp;
825 for (x = 0; x < slot->bitmap.width; ++x) {
826 *p++ = (bmp[pos] & bit) ? 0xff : 0;
834 i_render_color(render, tx + slot->bitmap_left, ty-slot->bitmap_top+y,
835 slot->bitmap.width, work_bmp, cl);
837 bmp += slot->bitmap.pitch;
841 unsigned char *bmp = slot->bitmap.buffer;
843 /* grey scale or something we can treat as greyscale */
844 /* we create a map to convert from the bitmap values to 0-255 */
845 if (last_mode != slot->bitmap.pixel_mode
846 || last_grays != slot->bitmap.num_grays) {
847 if (!make_bmp_map(&slot->bitmap, map))
849 last_mode = slot->bitmap.pixel_mode;
850 last_grays = slot->bitmap.num_grays;
853 for (y = 0; y < slot->bitmap.rows; ++y) {
854 if (last_mode == ft_pixel_mode_grays &&
856 for (x = 0; x < slot->bitmap.width; ++x)
857 bmp[x] = map[bmp[x]];
859 i_render_color(render, tx + slot->bitmap_left, ty-slot->bitmap_top+y,
860 slot->bitmap.width, bmp, cl);
861 bmp += slot->bitmap.pitch;
866 tx += slot->advance.x / 64;
867 ty -= slot->advance.y / 64;
871 i_render_delete(render);
880 =item i_ft2_cp(FT2_Fonthandle *handle, i_img *im, int tx, int ty, int channel, double cheight, double cwidth, char *text, size_t len, int align, int aa, int vlayout, int utf8)
882 Renders I<text> to (I<tx>, I<ty>) in I<im> to I<channel> at the given
883 I<cheight> and I<cwidth>.
885 If align is 0, then the text is rendered with the top-left of the
886 first character at (I<tx>, I<ty>). If align is non-zero then the text
887 is rendered with (I<tx>, I<ty>) aligned with the base-line of the
890 If C<utf8> is non-zero the text is treated as UTF-8 encoded
892 If C<aa> is non-zero then the text is drawn anti-aliased.
894 Returns non-zero on success.
900 i_ft2_cp(FT2_Fonthandle *handle, i_img *im, i_img_dim tx, i_img_dim ty, int channel,
901 double cheight, double cwidth, char const *text, size_t len, int align,
902 int aa, int vlayout, int utf8) {
909 mm_log((1, "i_ft2_cp(handle %p, im %p, (tx, ty) (" i_DFp "), channel %d, cheight %f, cwidth %f, text %p, len %u, align %d, aa %d, vlayout %d, utf8 %d)\n",
910 handle, im, i_DFcp(tx, ty), channel, cheight, cwidth, text, (unsigned)len, align, aa, vlayout, utf8));
914 if (vlayout && !FT_HAS_VERTICAL(handle->face)) {
915 i_push_error(0, "face has no vertical metrics");
919 if (!i_ft2_bbox_r(handle, cheight, cwidth, text, len, vlayout, utf8, bbox))
922 work = i_img_8_new(bbox[2]-bbox[0]+1, bbox[3]-bbox[1]+1, 1);
927 if (!i_ft2_text(handle, work, -bbox[0], -bbox[1], &cl, cheight, cwidth,
928 text, len, 1, aa, vlayout, utf8))
936 /* render to the specified channel */
937 /* this will be sped up ... */
938 bmp = mymalloc(work->xsize);
939 for (y = 0; y < work->ysize; ++y) {
940 i_gsamp(work, 0, work->xsize, y, bmp, NULL, 1);
941 i_psamp(im, tx + bbox[0], tx + bbox[0] + work->xsize,
942 ty + y + bbox[1], bmp, &channel, 1);
950 =item i_ft2_has_chars(handle, char *text, size_t len, int utf8, char *out)
952 Check if the given characters are defined by the font.
954 Returns the number of characters that were checked.
959 i_ft2_has_chars(FT2_Fonthandle *handle, char const *text, size_t len,
960 int utf8, char *out) {
962 mm_log((1, "i_ft2_has_chars(handle %p, text %p, len %u, utf8 %d)\n",
963 handle, text, (unsigned)len, utf8));
971 c = i_utf8_advance(&text, &len);
973 i_push_error(0, "invalid UTF8 character");
978 c = (unsigned char)*text++;
982 index = FT_Get_Char_Index(handle->face, c);
990 /* uses a method described in fterrors.h to build an error translation
993 #undef __FTERRORS_H__
994 #define FT_ERRORDEF(e, v, s) case v: i_push_error(code, s); return;
995 #define FT_ERROR_START_LIST
996 #define FT_ERROR_END_LIST
1001 =head2 Internal Functions
1003 These functions are used in the implementation of freetyp2.c and should not
1004 (usually cannot) be called from outside it.
1008 =item ft2_push_message(int code)
1010 Pushes an error message corresponding to code onto the error stack.
1015 #define UNKNOWN_ERROR_FORMAT "Unknown Freetype2 error code 0x%04X"
1018 ft2_push_message(int code) {
1022 #include FT_ERRORS_H
1025 #ifdef IMAGER_SNPRINTF
1026 snprintf(unknown, sizeof(unknown), UNKNOWN_ERROR_FORMAT, code);
1028 sprintf(unknown, UNKNOWN_ERROR_FORMAT, code);
1030 i_push_error(code, unknown);
1034 =item make_bmp_map(FT_Bitmap *bitmap, unsigned char *map)
1036 Creates a map to convert grey levels from the glyphs bitmap into
1037 values scaled 0..255.
1042 make_bmp_map(FT_Bitmap *bitmap, unsigned char *map) {
1046 switch (bitmap->pixel_mode) {
1047 case ft_pixel_mode_grays:
1048 scale = bitmap->num_grays;
1052 i_push_errorf(0, "I can't handle pixel mode %d", bitmap->pixel_mode);
1056 /* build the table */
1057 for (i = 0; i < scale; ++i)
1058 map[i] = i * 255 / (bitmap->num_grays - 1);
1063 /* FREETYPE_PATCH was introduced in 2.0.6, we don't want a false
1064 positive on 2.0.0 to 2.0.4, so we accept a false negative in 2.0.5 */
1065 #ifndef FREETYPE_PATCH
1066 #define FREETYPE_PATCH 4
1069 /* FT_Get_Postscript_Name() was introduced in FT2.0.5 */
1070 #define IM_HAS_FACE_NAME (FREETYPE_MINOR > 0 || FREETYPE_PATCH >= 5)
1071 /* #define IM_HAS_FACE_NAME 0 */
1074 =item i_ft2_face_name(handle, name_buf, name_buf_size)
1076 Fills the given buffer with the Postscript Face name of the font,
1079 Returns the number of bytes copied, including the terminating NUL.
1085 i_ft2_face_name(FT2_Fonthandle *handle, char *name_buf, size_t name_buf_size) {
1086 #if IM_HAS_FACE_NAME
1087 char const *name = FT_Get_Postscript_Name(handle->face);
1092 strncpy(name_buf, name, name_buf_size);
1093 name_buf[name_buf_size-1] = '\0';
1095 return strlen(name) + 1;
1098 i_push_error(0, "no face name available");
1105 i_push_error(0, "Freetype 2.0.6 or later required");
1113 i_ft2_can_face_name(void) {
1114 return IM_HAS_FACE_NAME;
1117 /* FT_Has_PS_Glyph_Names() was introduced in FT2.1.1 */
1118 /* well, I assume FREETYPE_MAJOR is 2, since we're here */
1119 #if FREETYPE_MINOR < 1 || (FREETYPE_MINOR == 1 && FREETYPE_PATCH < 1)
1120 #define FT_Has_PS_Glyph_Names(face) (FT_HAS_GLYPH_NAMES(face))
1124 i_ft2_glyph_name(FT2_Fonthandle *handle, unsigned long ch, char *name_buf,
1125 size_t name_buf_size, int reliable_only) {
1126 #ifdef FT_CONFIG_OPTION_NO_GLYPH_NAMES
1129 i_push_error(0, "FT2 configured without glyph name support");
1137 if (!FT_HAS_GLYPH_NAMES(handle->face)) {
1138 i_push_error(0, "no glyph names in font");
1142 if (reliable_only && !FT_Has_PS_Glyph_Names(handle->face)) {
1143 i_push_error(0, "no reliable glyph names in font - set reliable_only to 0 to try anyway");
1148 index = FT_Get_Char_Index(handle->face, ch);
1151 FT_Error error = FT_Get_Glyph_Name(handle->face, index, name_buf,
1154 ft2_push_message(error);
1158 if (strcmp(name_buf, ".notdef") == 0) {
1163 return strlen(name_buf) + 1;
1177 i_ft2_can_do_glyph_names(void) {
1178 #ifdef FT_CONFIG_OPTION_NO_GLYPH_NAMES
1186 i_ft2_face_has_glyph_names(FT2_Fonthandle *handle) {
1187 #ifdef FT_CONFIG_OPTION_NO_GLYPH_NAMES
1190 return FT_HAS_GLYPH_NAMES(handle->face);
1191 /* return FT_Has_PS_Glyph_Names(handle->face);*/
1196 i_ft2_is_multiple_master(FT2_Fonthandle *handle) {
1199 return handle->has_mm;
1206 i_ft2_get_multiple_masters(FT2_Fonthandle *handle, i_font_mm *mm) {
1209 FT_Multi_Master *mms = &handle->mm;
1212 if (!handle->has_mm) {
1213 i_push_error(0, "Font has no multiple masters");
1216 mm->num_axis = mms->num_axis;
1217 mm->num_designs = mms->num_designs;
1218 for (i = 0; i < mms->num_axis; ++i) {
1219 mm->axis[i].name = mms->axis[i].name;
1220 mm->axis[i].minimum = mms->axis[i].minimum;
1221 mm->axis[i].maximum = mms->axis[i].maximum;
1227 i_push_error(0, "Multiple master functions unavailable");
1233 i_ft2_set_mm_coords(FT2_Fonthandle *handle, int coord_count, const long *coords) {
1236 FT_Long ftcoords[T1_MAX_MM_AXIS];
1240 if (!handle->has_mm) {
1241 i_push_error(0, "Font has no multiple masters");
1244 if (coord_count != handle->mm.num_axis) {
1245 i_push_error(0, "Number of MM coords doesn't match MM axis count");
1248 for (i = 0; i < coord_count; ++i)
1249 ftcoords[i] = coords[i];
1251 error = FT_Set_MM_Design_Coordinates(handle->face, coord_count, ftcoords);
1253 ft2_push_message(error);
1260 i_push_error(0, "Multiple master functions unavailable");
1267 i_min(i_img_dim a, i_img_dim b) {
1268 return a < b ? a : b;
1272 i_max(i_img_dim a, i_img_dim b) {
1273 return a > b ? a : b;
1281 Tony Cook <tony@develop-help.com>, with a fair amount of help from
1282 reading the code in font.c.
1286 font.c, Imager::Font(3), Imager(3)
1288 http://www.freetype.org/