]> git.imager.perl.org - imager.git/blobdiff - freetyp2.c
think about 0.47
[imager.git] / freetyp2.c
index b933060c9d831e0c6c9d1c2cb9ac8d21f683b005..687e26859efdedec61255de38384b2ae1f42ad31 100644 (file)
@@ -12,7 +12,7 @@ freetyp2.c - font support via the FreeType library version 2.
   if (!i_ft2_getdpi(font, &xdpi, &ydpi)) { error }
   double matrix[6];
   if (!i_ft2_settransform(font, matrix)) { error }
-  int bbox[6];
+  int bbox[BOUNDING_BOX_COUNT];
   if (!i_ft2_bbox(font, cheight, cwidth, text, length, bbox, utf8)) { error }
   i_img *im = ...;
   i_color cl;
@@ -38,9 +38,14 @@ Truetype, Type1 and Windows FNT.
 #include <stdio.h>
 #include <ft2build.h>
 #include FT_FREETYPE_H
+#ifdef FT_MULTIPLE_MASTERS_H
+#ifndef T1_CONFIG_OPTION_NO_MM_SUPPORT
+#define IM_FT2_MM
+#include FT_MULTIPLE_MASTERS_H
+#endif
+#endif
 
 static void ft2_push_message(int code);
-static unsigned long utf8_advance(char **p, int *len);
 
 static FT_Library library;
 
@@ -75,6 +80,12 @@ struct FT2_Fonthandle {
 
   /* used to adjust so we can align the draw point to the top-left */
   double matrix[6];
+
+#ifdef IM_FT2_MM
+  /* Multiple master data if any */
+  int has_mm;
+  FT_Multi_Master mm;
+#endif
 };
 
 /* the following is used to select a "best" encoding */
@@ -115,8 +126,6 @@ i_ft2_new(char *name, int index) {
   FT_Error error;
   FT2_Fonthandle *result;
   FT_Face face;
-  double matrix[6] = { 1, 0, 0,
-                       0, 1, 0 };
   int i, j;
   FT_Encoding encoding;
   int score;
@@ -128,6 +137,7 @@ i_ft2_new(char *name, int index) {
   if (error) {
     ft2_push_message(error);
     i_push_error(error, "Opening face");
+    mm_log((2, "error opening face '%s': %d\n", name, error));
     return NULL;
   }
 
@@ -166,6 +176,27 @@ i_ft2_new(char *name, int index) {
   result->matrix[0] = 1; result->matrix[1] = 0; result->matrix[2] = 0;
   result->matrix[3] = 0; result->matrix[4] = 1; result->matrix[5] = 0;
 
+#ifdef IM_FT2_MM
+ {
+   FT_Multi_Master *mm = &result->mm;
+   int i;
+
+   if ((face->face_flags & FT_FACE_FLAG_MULTIPLE_MASTERS) != 0 
+       && (error = FT_Get_Multi_Master(face, mm)) == 0) {
+     mm_log((2, "MM Font, %d axes, %d designs\n", mm->num_axis, mm->num_designs));
+     for (i = 0; i < mm->num_axis; ++i) {
+       mm_log((2, "  axis %d name %s range %ld - %ld\n", i, mm->axis[i].name,
+               (long)(mm->axis[i].minimum), (long)(mm->axis[i].maximum)));
+     }
+     result->has_mm = 1;
+   }
+   else {
+     mm_log((2, "No multiple masters\n"));
+     result->has_mm = 0;
+   }
+ }
+#endif
+
   return result;
 }
 
@@ -286,7 +317,7 @@ Returns non-zero on success.
 */
 int
 i_ft2_bbox(FT2_Fonthandle *handle, double cheight, double cwidth, 
-           char *text, int len, int *bbox, int utf8) {
+           char const *text, int len, int *bbox, int utf8) {
   FT_Error error;
   int width;
   int index;
@@ -295,6 +326,8 @@ i_ft2_bbox(FT2_Fonthandle *handle, double cheight, double cwidth,
   int glyph_ascent, glyph_descent;
   FT_Glyph_Metrics *gm;
   int start = 0;
+  int loadFlags = FT_LOAD_DEFAULT;
+  int rightb;
 
   mm_log((1, "i_ft2_bbox(handle %p, cheight %f, cwidth %f, text %p, len %d, bbox %p)\n",
          handle, cheight, cwidth, text, len, bbox));
@@ -306,12 +339,15 @@ i_ft2_bbox(FT2_Fonthandle *handle, double cheight, double cwidth,
     i_push_error(0, "setting size");
   }
 
+  if (!handle->hint)
+    loadFlags |= FT_LOAD_NO_HINTING;
+
   first = 1;
   width = 0;
   while (len) {
     unsigned long c;
     if (utf8) {
-      c = utf8_advance(&text, &len);
+      c = i_utf8_advance(&text, &len);
       if (c == ~0UL) {
         i_push_error(0, "invalid UTF8 character");
         return 0;
@@ -323,7 +359,7 @@ i_ft2_bbox(FT2_Fonthandle *handle, double cheight, double cwidth,
     }
 
     index = FT_Get_Char_Index(handle->face, c);
-    error = FT_Load_Glyph(handle->face, index, FT_LOAD_DEFAULT);
+    error = FT_Load_Glyph(handle->face, index, loadFlags);
     if (error) {
       ft2_push_message(error);
       i_push_errorf(0, "loading glyph for character \\x%02x (glyph 0x%04X)", 
@@ -352,20 +388,25 @@ i_ft2_bbox(FT2_Fonthandle *handle, double cheight, double cwidth,
       /* last character 
        handle the case where the right the of the character overlaps the 
        right*/
-      int rightb = gm->horiAdvance - gm->horiBearingX - gm->width;
-      if (rightb < 0)
-        width -= rightb / 64;
+      rightb = (gm->horiAdvance - gm->horiBearingX - gm->width)/64;
+      /*if (rightb > 0)
+        rightb = 0;*/
     }
   }
 
-  bbox[0] = start;
-  bbox[1] = handle->face->size->metrics.descender / 64;
-  bbox[2] = width;
-  bbox[3] = handle->face->size->metrics.ascender / 64;
-  bbox[4] = descent;
-  bbox[5] = ascent;
-
-  return 1;
+  bbox[BBOX_NEG_WIDTH] = start;
+  bbox[BBOX_GLOBAL_DESCENT] = handle->face->size->metrics.descender / 64;
+  bbox[BBOX_POS_WIDTH] = width;
+  if (rightb < 0)
+    bbox[BBOX_POS_WIDTH] -= rightb;
+  bbox[BBOX_GLOBAL_ASCENT] = handle->face->size->metrics.ascender / 64;
+  bbox[BBOX_DESCENT] = descent;
+  bbox[BBOX_ASCENT] = ascent;
+  bbox[BBOX_ADVANCE_WIDTH] = width;
+  bbox[BBOX_RIGHT_BEARING] = rightb;
+  mm_log((1, " bbox=> negw=%d glob_desc=%d pos_wid=%d glob_asc=%d desc=%d asc=%d adv_width=%d rightb=%d\n", bbox[0], bbox[1], bbox[2], bbox[3], bbox[4], bbox[5], bbox[6], bbox[7]));
+
+  return BBOX_RIGHT_BEARING + 1;
 }
 
 /*
@@ -389,7 +430,6 @@ too much hard work.
 void ft2_transform_box(FT2_Fonthandle *handle, int bbox[4]) {
   double work[8];
   double *matrix = handle->matrix;
-  int i;
   
   work[0] = matrix[0] * bbox[0] + matrix[1] * bbox[1];
   work[1] = matrix[3] * bbox[0] + matrix[4] * bbox[1];
@@ -400,10 +440,10 @@ void ft2_transform_box(FT2_Fonthandle *handle, int bbox[4]) {
   work[6] = matrix[0] * bbox[2] + matrix[1] * bbox[3];
   work[7] = matrix[3] * bbox[2] + matrix[4] * bbox[3];
 
-  bbox[0] = floor(min(min(work[0], work[2]),min(work[4], work[6])));
-  bbox[1] = floor(min(min(work[1], work[3]),min(work[5], work[7])));
-  bbox[2] = ceil(max(max(work[0], work[2]),max(work[4], work[6])));
-  bbox[3] = ceil(max(max(work[1], work[3]),max(work[5], work[7])));
+  bbox[0] = floor(i_min(i_min(work[0], work[2]),i_min(work[4], work[6])));
+  bbox[1] = floor(i_min(i_min(work[1], work[3]),i_min(work[5], work[7])));
+  bbox[2] = ceil(i_max(i_max(work[0], work[2]),i_max(work[4], work[6])));
+  bbox[3] = ceil(i_max(i_max(work[1], work[3]),i_max(work[5], work[7])));
 }
 
 /*
@@ -415,10 +455,10 @@ bounding box in bbox[] that encloses both.
 =cut
 */
 static void expand_bounds(int bbox[4], int bbox2[4]) {
-  bbox[0] = min(bbox[0], bbox2[0]);
-  bbox[1] = min(bbox[1], bbox2[1]);
-  bbox[2] = max(bbox[2], bbox2[2]);
-  bbox[3] = max(bbox[3], bbox2[3]);
+  bbox[0] = i_min(bbox[0], bbox2[0]);
+  bbox[1] = i_min(bbox[1], bbox2[1]);
+  bbox[2] = i_max(bbox[2], bbox2[2]);
+  bbox[3] = i_max(bbox[3], bbox2[3]);
 }
 
 /*
@@ -443,7 +483,7 @@ Returns non-zero on success.
 */
 int
 i_ft2_bbox_r(FT2_Fonthandle *handle, double cheight, double cwidth, 
-           char *text, int len, int vlayout, int utf8, int *bbox) {
+           char const *text, int len, int vlayout, int utf8, int *bbox) {
   FT_Error error;
   int width;
   int index;
@@ -451,17 +491,17 @@ i_ft2_bbox_r(FT2_Fonthandle *handle, double cheight, double cwidth,
   int ascent = 0, descent = 0;
   int glyph_ascent, glyph_descent;
   FT_Glyph_Metrics *gm;
-  int start = 0;
   int work[4];
   int bounds[4];
   double x = 0, y = 0;
   int i;
   FT_GlyphSlot slot;
-  int advx, advy;
   int loadFlags = FT_LOAD_DEFAULT;
 
   if (vlayout)
     loadFlags |= FT_LOAD_VERTICAL_LAYOUT;
+  if (!handle->hint)
+    loadFlags |= FT_LOAD_NO_HINTING;
 
   error = FT_Set_Char_Size(handle->face, cwidth*64, cheight*64, 
                            handle->xdpi, handle->ydpi);
@@ -475,7 +515,7 @@ i_ft2_bbox_r(FT2_Fonthandle *handle, double cheight, double cwidth,
   while (len) {
     unsigned long c;
     if (utf8) {
-      c = utf8_advance(&text, &len);
+      c = i_utf8_advance(&text, &len);
       if (c == ~0UL) {
         i_push_error(0, "invalid UTF8 character");
         return 0;
@@ -535,7 +575,7 @@ i_ft2_bbox_r(FT2_Fonthandle *handle, double cheight, double cwidth,
     }
     x += slot->advance.x / 64;
     y += slot->advance.y / 64;
-    
+
     if (glyph_ascent > ascent)
       ascent = glyph_ascent;
     if (glyph_descent > descent)
@@ -568,8 +608,6 @@ i_ft2_bbox_r(FT2_Fonthandle *handle, double cheight, double cwidth,
   return 1;
 }
 
-
-
 static int
 make_bmp_map(FT_Bitmap *bitmap, unsigned char *map);
 
@@ -592,12 +630,12 @@ Returns non-zero on success.
 */
 int
 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,
+           double cheight, double cwidth, char const *text, int len, int align,
            int aa, int vlayout, int utf8) {
   FT_Error error;
   int index;
   FT_Glyph_Metrics *gm;
-  int bbox[6];
+  int bbox[BOUNDING_BOX_COUNT];
   FT_GlyphSlot slot;
   int x, y;
   unsigned char *bmp;
@@ -633,7 +671,7 @@ i_ft2_text(FT2_Fonthandle *handle, i_img *im, int tx, int ty, i_color *cl,
   while (len) {
     unsigned long c;
     if (utf8) {
-      c = utf8_advance(&text, &len);
+      c = i_utf8_advance(&text, &len);
       if (c == ~0UL) {
         i_push_error(0, "invalid UTF8 character");
         return 0;
@@ -732,8 +770,9 @@ Returns non-zero on success.
 =cut
 */
 
+int
 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,
+         double cheight, double cwidth, char const *text, int len, int align,
          int aa, int vlayout, int utf8) {
   int bbox[8];
   i_img *work;
@@ -785,17 +824,17 @@ Returns the number of characters that were checked.
 
 =cut
 */
-int i_ft2_has_chars(FT2_Fonthandle *handle, char *text, int len, int utf8
-                       char *out) {
+int i_ft2_has_chars(FT2_Fonthandle *handle, char const *text, int len
+                    int utf8, char *out) {
   int count = 0;
-  mm_log((1, "i_ft2_check_chars(handle %p, text %p, len %d, utf8 %d)\n", 
+  mm_log((1, "i_ft2_has_chars(handle %p, text %p, len %d, utf8 %d)\n", 
          handle, text, len, utf8));
 
   while (len) {
     unsigned long c;
     int index;
     if (utf8) {
-      c = utf8_advance(&text, &len);
+      c = i_utf8_advance(&text, &len);
       if (c == ~0UL) {
         i_push_error(0, "invalid UTF8 character");
         return 0;
@@ -817,10 +856,10 @@ int i_ft2_has_chars(FT2_Fonthandle *handle, char *text, int len, int utf8,
 /* uses a method described in fterrors.h to build an error translation
    function
 */
-#undef __FT_ERRORS_H__
+#undef __FTERRORS_H__
 #define FT_ERRORDEF(e, v, s) case v: i_push_error(code, s); return;
-#define FT_ERROR_START_LIST
-#define FT_ERROR_END_LIST
+#define FT_ERROR_START_LIST 
+#define FT_ERROR_END_LIST 
 
 /*
 =back
@@ -879,81 +918,201 @@ make_bmp_map(FT_Bitmap *bitmap, unsigned char *map) {
   return 1;
 }
 
-struct utf8_size {
-  int mask, expect;
-  int size;
-};
+/* FREETYPE_PATCH was introduced in 2.0.6, we don't want a false 
+   positive on 2.0.0 to 2.0.4, so we accept a false negative in 2.0.5 */
+#ifndef FREETYPE_PATCH
+#define FREETYPE_PATCH 4
+#endif
 
-struct utf8_size utf8_sizes[] =
-{
-  { 0x80, 0x00, 1 },
-  { 0xE0, 0xC0, 2 },
-  { 0xF0, 0xE0, 3 },
-  { 0xF8, 0xF0, 4 },
-};
+/* FT_Get_Postscript_Name() was introduced in FT2.0.5 */
+#define IM_HAS_FACE_NAME (FREETYPE_MINOR > 0 || FREETYPE_PATCH >= 5)
+/* #define IM_HAS_FACE_NAME 0 */ 
 
 /*
-=item utf8_advance(char **p, int *len)
+=item i_ft2_face_name(handle, name_buf, name_buf_size)
 
-Retreive a UTF8 character from the stream.
-
-Modifies *p and *len to indicate the consumed characters.
-
-This doesn't support the extended UTF8 encoding used by later versions
-of Perl.
+Fills the given buffer with the Postscript Face name of the font,
+if there is one.
 
 =cut
 */
 
-unsigned long utf8_advance(char **p, int *len) {
-  unsigned char c;
-  int i, ci, clen = 0;
-  unsigned char codes[3];
-  if (*len == 0)
-    return ~0UL;
-  c = *(*p)++; --*len;
-
-  for (i = 0; i < sizeof(utf8_sizes)/sizeof(*utf8_sizes); ++i) {
-    if ((c & utf8_sizes[i].mask) == utf8_sizes[i].expect) {
-      clen = utf8_sizes[i].size;
-    }
+int
+i_ft2_face_name(FT2_Fonthandle *handle, char *name_buf, size_t name_buf_size) {
+#if IM_HAS_FACE_NAME
+  char const *name = FT_Get_Postscript_Name(handle->face);
+
+  i_clear_error();
+
+  if (name) {
+    strncpy(name_buf, name, name_buf_size);
+    name_buf[name_buf_size-1] = '\0';
+
+    return strlen(name) + 1;
   }
-  if (clen == 0 || *len < clen-1) {
-    --*p; ++*len;
-    return ~0UL;
+  else {
+    i_push_error(0, "no face name available");
+    *name_buf = '\0';
+
+    return 0;
   }
+#else
+  i_clear_error();
+  i_push_error(0, "Freetype 2.0.6 or later required");
+  *name_buf = '\0';
 
-  /* check that each character is well formed */
-  i = 1;
-  ci = 0;
-  while (i < clen) {
-    if (((*p)[ci] & 0xC0) != 0x80) {
-      --*p; ++*len;
-      return ~0UL;
-    }
-    codes[ci] = (*p)[ci];
-    ++ci; ++i;
+  return 0;
+#endif
+}
+
+int
+i_ft2_can_face_name(void) {
+  return IM_HAS_FACE_NAME;
+}
+
+/* FT_Has_PS_Glyph_Names() was introduced in FT2.1.1 */
+/* well, I assume FREETYPE_MAJOR is 2, since we're here */
+#if FREETYPE_MINOR < 1 || (FREETYPE_MINOR == 1 && FREETYPE_PATCH < 1)
+#define FT_Has_PS_Glyph_Names(face) (FT_HAS_GLYPH_NAMES(face))
+#endif
+
+int
+i_ft2_glyph_name(FT2_Fonthandle *handle, unsigned long ch, char *name_buf, 
+                 size_t name_buf_size, int reliable_only) {
+#ifdef FT_CONFIG_OPTION_NO_GLYPH_NAMES
+  i_clear_error();
+  *name_buf = '\0';
+  i_push_error(0, "FT2 configured without glyph name support");
+
+  return 0;
+#else
+  FT_UInt index;
+
+  i_clear_error();
+
+  if (!FT_HAS_GLYPH_NAMES(handle->face)) {
+    i_push_error(0, "no glyph names in font");
+    *name_buf = '\0';
+    return 0;
   }
-  *p += clen-1; *len -= clen-1;
-  if (c & 0x80) {
-    if ((c & 0xE0) == 0xC0) {
-      return ((c & 0x1F) << 6) + (codes[0] & 0x3F);
-    }
-    else if ((c & 0xF0) == 0xE0) {
-      return ((c & 0x0F) << 12) | ((codes[0] & 0x3F) << 6) | (codes[1] & 0x3f);
+  if (reliable_only && !FT_Has_PS_Glyph_Names(handle->face)) {
+    i_push_error(0, "no reliable glyph names in font - set reliable_only to 0 to try anyway");
+    *name_buf = '\0';
+    return 0;
+  }
+
+  index = FT_Get_Char_Index(handle->face, ch);
+  
+  if (index) {
+    FT_Error error = FT_Get_Glyph_Name(handle->face, index, name_buf, 
+                                       name_buf_size);
+    if (error) {
+      ft2_push_message(error);
+      *name_buf = '\0';
+      return 0;
     }
-    else if ((c & 0xF8) == 0xF0) {
-      return ((c & 0x07) << 18) | ((codes[0] & 0x3F) << 12) 
-              | ((codes[1] & 0x3F) << 6) | (codes[2] & 0x3F);
+    if (*name_buf) {
+      return strlen(name_buf) + 1;
     }
     else {
-      *p -= clen; *len += clen;
-      return ~0UL;
+      return 0;
     }
   }
   else {
-    return c;
+    i_push_error(0, "no glyph for that character");
+    *name_buf = 0;
+    return 0;
+  }
+#endif
+}
+
+int
+i_ft2_can_do_glyph_names(void) {
+#ifdef FT_CONFIG_OPTION_NO_GLYPH_NAMES
+  return 0;
+#else
+  return 1;
+#endif
+}
+
+int 
+i_ft2_face_has_glyph_names(FT2_Fonthandle *handle) {
+#ifdef FT_CONFIG_OPTION_NO_GLYPH_NAMES
+  return 0;
+#else
+  return FT_Has_PS_Glyph_Names(handle->face);
+#endif
+}
+
+int
+i_ft2_is_multiple_master(FT2_Fonthandle *handle) {
+  i_clear_error();
+#ifdef IM_FT2_MM
+  return handle->has_mm;
+#else
+  return 0;
+#endif
+}
+
+int
+i_ft2_get_multiple_masters(FT2_Fonthandle *handle, i_font_mm *mm) {
+#ifdef IM_FT2_MM
+  int i;
+  FT_Multi_Master *mms = &handle->mm;
+
+  i_clear_error();
+  if (!handle->has_mm) {
+    i_push_error(0, "Font has no multiple masters");
+    return 0;
+  }
+  mm->num_axis = mms->num_axis;
+  mm->num_designs = mms->num_designs;
+  for (i = 0; i < mms->num_axis; ++i) {
+    mm->axis[i].name = mms->axis[i].name;
+    mm->axis[i].minimum = mms->axis[i].minimum;
+    mm->axis[i].maximum = mms->axis[i].maximum;
   }
+
+  return 1;
+#else
+  i_clear_error();
+  i_push_error(0, "Multiple master functions unavailable");
+  return 0;
+#endif
+}
+
+int
+i_ft2_set_mm_coords(FT2_Fonthandle *handle, int coord_count, long *coords) {
+#ifdef IM_FT2_MM
+  int i;
+  FT_Long ftcoords[T1_MAX_MM_AXIS];
+  FT_Error error;
+
+  i_clear_error();
+  if (!handle->has_mm) {
+    i_push_error(0, "Font has no multiple masters");
+    return 0;
+  }
+  if (coord_count != handle->mm.num_axis) {
+    i_push_error(0, "Number of MM coords doesn't match MM axis count");
+    return 0;
+  }
+  for (i = 0; i < coord_count; ++i)
+    ftcoords[i] = coords[i];
+
+  error = FT_Set_MM_Design_Coordinates(handle->face, coord_count, ftcoords);
+  if (error) {
+    ft2_push_message(error);
+    return 0;
+  }
+  
+  return 1;
+#else 
+  i_clear_error();
+  i_push_error(0, "Multiple master functions unavailable");
+
+  return 0;
+#endif
 }
 
 /*