Blender  V2.93
freetypefont.c
Go to the documentation of this file.
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is written by Rob Haarsma (phase)
17  * All rights reserved.
18  *
19  * This code parses the Freetype font outline data to chains of Blender's bezier-triples.
20  * Additional information can be found at the bottom of this file.
21  *
22  * Code that uses exotic character maps is present but commented out.
23  */
24 
29 #include <ft2build.h>
30 #include FT_FREETYPE_H
31 /* not needed yet */
32 // #include FT_GLYPH_H
33 // #include FT_BBOX_H
34 // #include FT_SIZES_H
35 // #include <freetype/ttnameid.h>
36 
37 #include "MEM_guardedalloc.h"
38 
39 #include "BLI_ghash.h"
40 #include "BLI_listbase.h"
41 #include "BLI_math.h"
42 #include "BLI_string.h"
43 #include "BLI_utildefines.h"
44 #include "BLI_vfontdata.h"
45 
46 #include "DNA_curve_types.h"
47 #include "DNA_packedFile_types.h"
48 #include "DNA_vfont_types.h"
49 
50 /* local variables */
51 static FT_Library library;
52 static FT_Error err;
53 
54 static VChar *freetypechar_to_vchar(FT_Face face, FT_ULong charcode, VFontData *vfd)
55 {
56  const float scale = vfd->scale;
57  const float eps = 0.0001f;
58  const float eps_sq = eps * eps;
59  /* Blender */
60  struct Nurb *nu;
61  struct VChar *che;
62  struct BezTriple *bezt;
63 
64  /* Freetype2 */
65  FT_GlyphSlot glyph;
66  FT_UInt glyph_index;
67  FT_Outline ftoutline;
68  float dx, dy;
69  int j, k, l, l_first = 0;
70 
71  /*
72  * Generate the character 3D data
73  *
74  * Get the FT Glyph index and load the Glyph */
75  glyph_index = FT_Get_Char_Index(face, charcode);
76  err = FT_Load_Glyph(face, glyph_index, FT_LOAD_NO_SCALE | FT_LOAD_NO_BITMAP);
77 
78  /* If loading succeeded, convert the FT glyph to the internal format */
79  if (!err) {
80  /* initialize as -1 to add 1 on first loop each time */
81  int contour_prev;
82  int *onpoints;
83 
84  /* First we create entry for the new character to the character list */
85  che = (VChar *)MEM_callocN(sizeof(struct VChar), "objfnt_char");
86 
87  /* Take some data for modifying purposes */
88  glyph = face->glyph;
89  ftoutline = glyph->outline;
90 
91  /* Set the width and character code */
92  che->index = charcode;
93  che->width = glyph->advance.x * scale;
94 
96 
97  /* Start converting the FT data */
98  onpoints = (int *)MEM_callocN((ftoutline.n_contours) * sizeof(int), "onpoints");
99 
100  /* get number of on-curve points for beziertriples (including conic virtual on-points) */
101  for (j = 0, contour_prev = -1; j < ftoutline.n_contours; j++) {
102  const int n = ftoutline.contours[j] - contour_prev;
103  contour_prev = ftoutline.contours[j];
104 
105  for (k = 0; k < n; k++) {
106  l = (j > 0) ? (k + ftoutline.contours[j - 1] + 1) : k;
107  if (k == 0) {
108  l_first = l;
109  }
110 
111  if (ftoutline.tags[l] == FT_Curve_Tag_On) {
112  onpoints[j]++;
113  }
114 
115  {
116  const int l_next = (k < n - 1) ? (l + 1) : l_first;
117  if (ftoutline.tags[l] == FT_Curve_Tag_Conic &&
118  ftoutline.tags[l_next] == FT_Curve_Tag_Conic) {
119  onpoints[j]++;
120  }
121  }
122  }
123  }
124 
125  /* contour loop, bezier & conic styles merged */
126  for (j = 0, contour_prev = -1; j < ftoutline.n_contours; j++) {
127  const int n = ftoutline.contours[j] - contour_prev;
128  contour_prev = ftoutline.contours[j];
129 
130  /* add new curve */
131  nu = (Nurb *)MEM_callocN(sizeof(struct Nurb), "objfnt_nurb");
132  bezt = (BezTriple *)MEM_callocN((onpoints[j]) * sizeof(BezTriple), "objfnt_bezt");
133  BLI_addtail(&che->nurbsbase, nu);
134 
135  nu->type = CU_BEZIER;
136  nu->pntsu = onpoints[j];
137  nu->resolu = 8;
138  nu->flagu = CU_NURB_CYCLIC;
139  nu->bezt = bezt;
140 
141  /* individual curve loop, start-end */
142  for (k = 0; k < n; k++) {
143  l = (j > 0) ? (k + ftoutline.contours[j - 1] + 1) : k;
144  if (k == 0) {
145  l_first = l;
146  }
147 
148  /* virtual conic on-curve points */
149  {
150  const int l_next = (k < n - 1) ? (l + 1) : l_first;
151  if (ftoutline.tags[l] == FT_Curve_Tag_Conic &&
152  ftoutline.tags[l_next] == FT_Curve_Tag_Conic) {
153  dx = (ftoutline.points[l].x + ftoutline.points[l_next].x) * scale / 2.0f;
154  dy = (ftoutline.points[l].y + ftoutline.points[l_next].y) * scale / 2.0f;
155 
156  /* left handle */
157  bezt->vec[0][0] = (dx + (2 * ftoutline.points[l].x) * scale) / 3.0f;
158  bezt->vec[0][1] = (dy + (2 * ftoutline.points[l].y) * scale) / 3.0f;
159 
160  /* midpoint (virtual on-curve point) */
161  bezt->vec[1][0] = dx;
162  bezt->vec[1][1] = dy;
163 
164  /* right handle */
165  bezt->vec[2][0] = (dx + (2 * ftoutline.points[l_next].x) * scale) / 3.0f;
166  bezt->vec[2][1] = (dy + (2 * ftoutline.points[l_next].y) * scale) / 3.0f;
167 
168  bezt->h1 = bezt->h2 = HD_ALIGN;
169  bezt->radius = 1.0f;
170  bezt++;
171  }
172  }
173 
174  /* on-curve points */
175  if (ftoutline.tags[l] == FT_Curve_Tag_On) {
176  const int l_prev = (k > 0) ? (l - 1) : ftoutline.contours[j];
177  const int l_next = (k < n - 1) ? (l + 1) : l_first;
178 
179  /* left handle */
180  if (ftoutline.tags[l_prev] == FT_Curve_Tag_Cubic) {
181  bezt->vec[0][0] = ftoutline.points[l_prev].x * scale;
182  bezt->vec[0][1] = ftoutline.points[l_prev].y * scale;
183  bezt->h1 = HD_FREE;
184  }
185  else if (ftoutline.tags[l_prev] == FT_Curve_Tag_Conic) {
186  bezt->vec[0][0] = (ftoutline.points[l].x + (2 * ftoutline.points[l_prev].x)) * scale /
187  3.0f;
188  bezt->vec[0][1] = (ftoutline.points[l].y + (2 * ftoutline.points[l_prev].y)) * scale /
189  3.0f;
190  bezt->h1 = HD_FREE;
191  }
192  else {
193  bezt->vec[0][0] = ftoutline.points[l].x * scale -
194  (ftoutline.points[l].x - ftoutline.points[l_prev].x) * scale / 3.0f;
195  bezt->vec[0][1] = ftoutline.points[l].y * scale -
196  (ftoutline.points[l].y - ftoutline.points[l_prev].y) * scale / 3.0f;
197  bezt->h1 = HD_VECT;
198  }
199 
200  /* midpoint (on-curve point) */
201  bezt->vec[1][0] = ftoutline.points[l].x * scale;
202  bezt->vec[1][1] = ftoutline.points[l].y * scale;
203 
204  /* right handle */
205  if (ftoutline.tags[l_next] == FT_Curve_Tag_Cubic) {
206  bezt->vec[2][0] = ftoutline.points[l_next].x * scale;
207  bezt->vec[2][1] = ftoutline.points[l_next].y * scale;
208  bezt->h2 = HD_FREE;
209  }
210  else if (ftoutline.tags[l_next] == FT_Curve_Tag_Conic) {
211  bezt->vec[2][0] = (ftoutline.points[l].x + (2 * ftoutline.points[l_next].x)) * scale /
212  3.0f;
213  bezt->vec[2][1] = (ftoutline.points[l].y + (2 * ftoutline.points[l_next].y)) * scale /
214  3.0f;
215  bezt->h2 = HD_FREE;
216  }
217  else {
218  bezt->vec[2][0] = ftoutline.points[l].x * scale -
219  (ftoutline.points[l].x - ftoutline.points[l_next].x) * scale / 3.0f;
220  bezt->vec[2][1] = ftoutline.points[l].y * scale -
221  (ftoutline.points[l].y - ftoutline.points[l_next].y) * scale / 3.0f;
222  bezt->h2 = HD_VECT;
223  }
224 
225  /* get the handles that are aligned, tricky...
226  * - check if one of them is a vector handle.
227  * - dist_squared_to_line_v2, check if the three beztriple points are on one line
228  * - len_squared_v2v2, see if there's a distance between the three points
229  * - len_squared_v2v2 again, to check the angle between the handles
230  */
231  if ((bezt->h1 != HD_VECT && bezt->h2 != HD_VECT) &&
232  (dist_squared_to_line_v2(bezt->vec[0], bezt->vec[1], bezt->vec[2]) <
233  (0.001f * 0.001f)) &&
234  (len_squared_v2v2(bezt->vec[0], bezt->vec[1]) > eps_sq) &&
235  (len_squared_v2v2(bezt->vec[1], bezt->vec[2]) > eps_sq) &&
236  (len_squared_v2v2(bezt->vec[0], bezt->vec[2]) > eps_sq) &&
237  (len_squared_v2v2(bezt->vec[0], bezt->vec[2]) >
238  max_ff(len_squared_v2v2(bezt->vec[0], bezt->vec[1]),
239  len_squared_v2v2(bezt->vec[1], bezt->vec[2])))) {
240  bezt->h1 = bezt->h2 = HD_ALIGN;
241  }
242  bezt->radius = 1.0f;
243  bezt++;
244  }
245  }
246  }
247 
248  MEM_freeN(onpoints);
249 
250  return che;
251  }
252 
253  return NULL;
254 }
255 
256 static VChar *objchr_to_ftvfontdata(VFont *vfont, FT_ULong charcode)
257 {
258  VChar *che;
259 
260  /* Freetype2 */
261  FT_Face face;
262 
263  /* Load the font to memory */
264  if (vfont->temp_pf) {
265  err = FT_New_Memory_Face(library, vfont->temp_pf->data, vfont->temp_pf->size, 0, &face);
266  if (err) {
267  return NULL;
268  }
269  }
270  else {
271  err = true;
272  return NULL;
273  }
274 
275  /* Read the char */
276  che = freetypechar_to_vchar(face, charcode, vfont->data);
277 
278  /* And everything went ok */
279  return che;
280 }
281 
283 {
284  /* Variables */
285  FT_Face face;
286  const FT_ULong charcode_reserve = 256;
287  FT_ULong charcode = 0, lcode;
288  FT_UInt glyph_index;
289  const char *fontname;
290  VFontData *vfd;
291 
292  /* load the freetype font */
293  err = FT_New_Memory_Face(library, pf->data, pf->size, 0, &face);
294 
295  if (err) {
296  return NULL;
297  }
298 
299  /* allocate blender font */
300  vfd = MEM_callocN(sizeof(*vfd), "FTVFontData");
301 
302  /* get the name */
303  fontname = FT_Get_Postscript_Name(face);
304  BLI_strncpy(vfd->name, (fontname == NULL) ? "" : fontname, sizeof(vfd->name));
305 
306  /* Extract the first 256 character from TTF */
307  lcode = charcode = FT_Get_First_Char(face, &glyph_index);
308 
309  /* No `charmap` found from the TTF so we need to figure it out. */
310  if (glyph_index == 0) {
311  FT_CharMap found = NULL;
312  FT_CharMap charmap;
313  int n;
314 
315  for (n = 0; n < face->num_charmaps; n++) {
316  charmap = face->charmaps[n];
317  if (charmap->encoding == FT_ENCODING_APPLE_ROMAN) {
318  found = charmap;
319  break;
320  }
321  }
322 
323  err = FT_Set_Charmap(face, found);
324 
325  if (err) {
326  return NULL;
327  }
328 
329  lcode = charcode = FT_Get_First_Char(face, &glyph_index);
330  }
331 
332  /* Blender default BFont is not "complete". */
333  const bool complete_font = (face->ascender != 0) && (face->descender != 0) &&
334  (face->ascender != face->descender);
335 
336  if (complete_font) {
337  /* We can get descender as well, but we simple store descender in relation to the ascender.
338  * Also note that descender is stored as a negative number. */
339  vfd->ascender = (float)face->ascender / (face->ascender - face->descender);
340  }
341  else {
342  vfd->ascender = 0.8f;
343  vfd->em_height = 1.0f;
344  }
345 
346  /* Adjust font size */
347  if (face->bbox.yMax != face->bbox.yMin) {
348  vfd->scale = (float)(1.0 / (double)(face->bbox.yMax - face->bbox.yMin));
349 
350  if (complete_font) {
351  vfd->em_height = (float)(face->ascender - face->descender) /
352  (face->bbox.yMax - face->bbox.yMin);
353  }
354  }
355  else {
356  vfd->scale = 1.0f / 1000.0f;
357  }
358 
359  /* Load characters */
360  vfd->characters = BLI_ghash_int_new_ex(__func__, charcode_reserve);
361 
362  while (charcode < charcode_reserve) {
363  /* Generate the font data */
364  freetypechar_to_vchar(face, charcode, vfd);
365 
366  /* Next glyph */
367  charcode = FT_Get_Next_Char(face, charcode, &glyph_index);
368 
369  /* Check that we won't start infinite loop */
370  if (charcode <= lcode) {
371  break;
372  }
373  lcode = charcode;
374  }
375 
376  return vfd;
377 }
378 
380 {
381  FT_Face face;
382  FT_GlyphSlot glyph;
383  FT_UInt glyph_index;
384  int success = 0;
385 
386  err = FT_New_Memory_Face(library, pf->data, pf->size, 0, &face);
387  if (err) {
388  success = 0;
389  // XXX error("This is not a valid font");
390  }
391  else {
392  glyph_index = FT_Get_Char_Index(face, 'A');
393  err = FT_Load_Glyph(face, glyph_index, FT_LOAD_NO_SCALE | FT_LOAD_NO_BITMAP);
394  if (err) {
395  success = 0;
396  }
397  else {
398  glyph = face->glyph;
399  if (glyph->format == ft_glyph_format_outline) {
400  success = 1;
401  }
402  else {
403  // XXX error("Selected Font has no outline data");
404  success = 0;
405  }
406  }
407  }
408 
409  return success;
410 }
411 
421 {
422  VFontData *vfd = NULL;
423  int success = 0;
424 
425  /* init Freetype */
426  err = FT_Init_FreeType(&library);
427  if (err) {
428  /* XXX error("Failed to load the Freetype font library"); */
429  return NULL;
430  }
431 
432  success = check_freetypefont(pf);
433 
434  if (success) {
435  vfd = objfnt_to_ftvfontdata(pf);
436  }
437 
438  /* free Freetype */
439  FT_Done_FreeType(library);
440 
441  return vfd;
442 }
443 
444 static void *vfontdata_copy_characters_value_cb(const void *src)
445 {
446  return BLI_vfontchar_copy(src, 0);
447 }
448 
449 VFontData *BLI_vfontdata_copy(const VFontData *vfont_src, const int UNUSED(flag))
450 {
451  VFontData *vfont_dst = MEM_dupallocN(vfont_src);
452 
453  if (vfont_src->characters != NULL) {
454  vfont_dst->characters = BLI_ghash_copy(
456  }
457 
458  return vfont_dst;
459 }
460 
461 VChar *BLI_vfontchar_from_freetypefont(VFont *vfont, unsigned long character)
462 {
463  VChar *che = NULL;
464 
465  if (!vfont) {
466  return NULL;
467  }
468 
469  /* Init Freetype */
470  err = FT_Init_FreeType(&library);
471  if (err) {
472  /* XXX error("Failed to load the Freetype font library"); */
473  return NULL;
474  }
475 
476  /* Load the character */
477  che = objchr_to_ftvfontdata(vfont, character);
478 
479  /* Free Freetype */
480  FT_Done_FreeType(library);
481 
482  return che;
483 }
484 
485 /* Yeah, this is very bad... But why is this in BLI in the first place, since it uses Nurb data?
486  * Anyway, do not feel like duplicating whole Nurb copy code here,
487  * so unless someone has a better idea... */
488 #include "../../blenkernel/BKE_curve.h"
489 
490 VChar *BLI_vfontchar_copy(const VChar *vchar_src, const int UNUSED(flag))
491 {
492  VChar *vchar_dst = MEM_dupallocN(vchar_src);
493 
494  BLI_listbase_clear(&vchar_dst->nurbsbase);
495  BKE_nurbList_duplicate(&vchar_dst->nurbsbase, &vchar_src->nurbsbase);
496 
497  return vchar_dst;
498 }
499 
typedef float(TangentPoint)[2]
void BKE_nurbList_duplicate(struct ListBase *lb1, const struct ListBase *lb2)
GHash * BLI_ghash_copy(GHash *gh, GHashKeyCopyFP keycopyfp, GHashValCopyFP valcopyfp) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT
Definition: BLI_ghash.c:727
GHash * BLI_ghash_int_new_ex(const char *info, const unsigned int nentries_reserve) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT
void BLI_ghash_insert(GHash *gh, void *key, void *val)
Definition: BLI_ghash.c:756
BLI_INLINE void BLI_listbase_clear(struct ListBase *lb)
Definition: BLI_listbase.h:128
void BLI_addtail(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition: listbase.c:110
MINLINE float max_ff(float a, float b)
float dist_squared_to_line_v2(const float p[2], const float l1[2], const float l2[2])
Definition: math_geom.c:324
MINLINE float len_squared_v2v2(const float a[2], const float b[2]) ATTR_WARN_UNUSED_RESULT
char * BLI_strncpy(char *__restrict dst, const char *__restrict src, const size_t maxncpy) ATTR_NONNULL()
Definition: string.c:108
#define UNUSED(x)
#define POINTER_FROM_UINT(i)
A structure to represent vector fonts, and to load them from PostScript fonts.
@ CU_BEZIER
@ CU_NURB_CYCLIC
@ HD_VECT
@ HD_FREE
@ HD_ALIGN
struct BezTriple BezTriple
Read Guarded memory(de)allocation.
ATTR_WARN_UNUSED_RESULT const BMLoop * l
VChar * BLI_vfontchar_copy(const VChar *vchar_src, const int UNUSED(flag))
Definition: freetypefont.c:490
static int check_freetypefont(PackedFile *pf)
Definition: freetypefont.c:379
VChar * BLI_vfontchar_from_freetypefont(VFont *vfont, unsigned long character)
Definition: freetypefont.c:461
static VChar * objchr_to_ftvfontdata(VFont *vfont, FT_ULong charcode)
Definition: freetypefont.c:256
VFontData * BLI_vfontdata_from_freetypefont(PackedFile *pf)
Definition: freetypefont.c:420
static void * vfontdata_copy_characters_value_cb(const void *src)
Definition: freetypefont.c:444
static FT_Library library
Definition: freetypefont.c:51
static FT_Error err
Definition: freetypefont.c:52
VFontData * BLI_vfontdata_copy(const VFontData *vfont_src, const int UNUSED(flag))
Definition: freetypefont.c:449
static VFontData * objfnt_to_ftvfontdata(PackedFile *pf)
Definition: freetypefont.c:282
static VChar * freetypechar_to_vchar(FT_Face face, FT_ULong charcode, VFontData *vfd)
Definition: freetypefont.c:54
#define pf(_x, _i)
Prefetch 64.
Definition: gim_memory.h:48
void(* MEM_freeN)(void *vmemh)
Definition: mallocn.c:41
void *(* MEM_dupallocN)(const void *vmemh)
Definition: mallocn.c:42
void *(* MEM_callocN)(size_t len, const char *str)
Definition: mallocn.c:45
const btScalar eps
Definition: poly34.cpp:11
float vec[3][3]
short flagu
short type
BezTriple * bezt
short resolu
float width
Definition: BLI_vfontdata.h:49
ListBase nurbsbase
Definition: BLI_vfontdata.h:47
unsigned int index
Definition: BLI_vfontdata.h:48
float scale
Definition: BLI_vfontdata.h:40
float ascender
Definition: BLI_vfontdata.h:43
struct GHash * characters
Definition: BLI_vfontdata.h:38
char name[128]
Definition: BLI_vfontdata.h:39
float em_height
Definition: BLI_vfontdata.h:42
struct VFontData * data
struct PackedFile * temp_pf