9045012af006b446445c99682ba878b399eec1c2
[blender.git] / source / blender / blenlib / intern / freetypefont.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * The Original Code is written by Rob Haarsma (phase)
19  * All rights reserved.
20  *
21  * Contributor(s): none yet.
22  *
23  * ***** END GPL LICENSE BLOCK *****
24  *
25  * This code parses the Freetype font outline data to chains of Blender's beziertriples.
26  * Additional information can be found at the bottom of this file.
27  *
28  * Code that uses exotic character maps is present but commented out.
29  */
30
31 /** \file blender/blenlib/intern/freetypefont.c
32  *  \ingroup bli
33  */
34
35 #include <ft2build.h>
36 #include FT_FREETYPE_H
37 /* not needed yet */
38 // #include FT_GLYPH_H
39 // #include FT_BBOX_H
40 // #include FT_SIZES_H
41 // #include <freetype/ttnameid.h>
42
43 #include "MEM_guardedalloc.h"
44
45 #include "BLI_utildefines.h"
46 #include "BLI_vfontdata.h"
47 #include "BLI_listbase.h"
48 #include "BLI_ghash.h"
49 #include "BLI_string.h"
50 #include "BLI_math.h"
51
52 #include "DNA_vfont_types.h"
53 #include "DNA_packedFile_types.h"
54 #include "DNA_curve_types.h"
55
56 /* local variables */
57 static FT_Library library;
58 static FT_Error err;
59
60
61 static VChar *freetypechar_to_vchar(FT_Face face, FT_ULong charcode, VFontData *vfd)
62 {
63         const float scale = vfd->scale;
64         const float eps = 0.0001f;
65         const float eps_sq = eps * eps;
66         /* Blender */
67         struct Nurb *nu;
68         struct VChar *che;
69         struct BezTriple *bezt;
70
71         /* Freetype2 */
72         FT_GlyphSlot glyph;
73         FT_UInt glyph_index;
74         FT_Outline ftoutline;
75         float dx, dy;
76         int j, k, l, l_first = 0;
77
78         /*
79          * Generate the character 3D data
80          *
81          * Get the FT Glyph index and load the Glyph */
82         glyph_index = FT_Get_Char_Index(face, charcode);
83         err = FT_Load_Glyph(face, glyph_index, FT_LOAD_NO_SCALE | FT_LOAD_NO_BITMAP);
84
85         /* If loading succeeded, convert the FT glyph to the internal format */
86         if (!err) {
87                 /* initialize as -1 to add 1 on first loop each time */
88                 int contour_prev;
89                 int *onpoints;
90
91                 /* First we create entry for the new character to the character list */
92                 che = (VChar *) MEM_callocN(sizeof(struct VChar), "objfnt_char");
93
94                 /* Take some data for modifying purposes */
95                 glyph = face->glyph;
96                 ftoutline = glyph->outline;
97
98                 /* Set the width and character code */
99                 che->index = charcode;
100                 che->width = glyph->advance.x * scale;
101
102                 BLI_ghash_insert(vfd->characters, POINTER_FROM_UINT(che->index), che);
103
104                 /* Start converting the FT data */
105                 onpoints = (int *)MEM_callocN((ftoutline.n_contours) * sizeof(int), "onpoints");
106
107                 /* get number of on-curve points for beziertriples (including conic virtual on-points) */
108                 for (j = 0, contour_prev = -1; j < ftoutline.n_contours; j++) {
109                         const int n = ftoutline.contours[j] - contour_prev;
110                         contour_prev = ftoutline.contours[j];
111
112                         for (k = 0; k < n; k++) {
113                                 l = (j > 0) ? (k + ftoutline.contours[j - 1] + 1) : k;
114                                 if (k == 0) l_first = l;
115
116                                 if (ftoutline.tags[l] == FT_Curve_Tag_On)
117                                         onpoints[j]++;
118
119                                 {
120                                         const int l_next = (k < n - 1) ? (l + 1) : l_first;
121                                         if (ftoutline.tags[l]      == FT_Curve_Tag_Conic &&
122                                             ftoutline.tags[l_next] == FT_Curve_Tag_Conic)
123                                         {
124                                                 onpoints[j]++;
125                                         }
126                                 }
127                         }
128                 }
129
130                 /* contour loop, bezier & conic styles merged */
131                 for (j = 0, contour_prev = -1; j < ftoutline.n_contours; j++) {
132                         const int n = ftoutline.contours[j] - contour_prev;
133                         contour_prev = ftoutline.contours[j];
134
135                         /* add new curve */
136                         nu  =  (Nurb *)MEM_callocN(sizeof(struct Nurb), "objfnt_nurb");
137                         bezt = (BezTriple *)MEM_callocN((onpoints[j]) * sizeof(BezTriple), "objfnt_bezt");
138                         BLI_addtail(&che->nurbsbase, nu);
139
140                         nu->type = CU_BEZIER;
141                         nu->pntsu = onpoints[j];
142                         nu->resolu = 8;
143                         nu->flag = CU_2D;
144                         nu->flagu = CU_NURB_CYCLIC;
145                         nu->bezt = bezt;
146
147                         /* individual curve loop, start-end */
148                         for (k = 0; k < n; k++) {
149                                 l = (j > 0) ? (k + ftoutline.contours[j - 1] + 1) : k;
150                                 if (k == 0) l_first = l;
151
152                                 /* virtual conic on-curve points */
153                                 {
154                                         const int l_next = (k < n - 1) ? (l + 1) : l_first;
155                                         if (ftoutline.tags[l]      == FT_Curve_Tag_Conic &&
156                                             ftoutline.tags[l_next] == FT_Curve_Tag_Conic)
157                                         {
158                                                 dx = (ftoutline.points[l].x + ftoutline.points[l_next].x) * scale / 2.0f;
159                                                 dy = (ftoutline.points[l].y + ftoutline.points[l_next].y) * scale / 2.0f;
160
161                                                 /* left handle */
162                                                 bezt->vec[0][0] = (dx + (2 * ftoutline.points[l].x) * scale) / 3.0f;
163                                                 bezt->vec[0][1] = (dy + (2 * ftoutline.points[l].y) * scale) / 3.0f;
164
165                                                 /* midpoint (virtual on-curve point) */
166                                                 bezt->vec[1][0] = dx;
167                                                 bezt->vec[1][1] = dy;
168
169                                                 /* right handle */
170                                                 bezt->vec[2][0] = (dx + (2 * ftoutline.points[l_next].x) * scale) / 3.0f;
171                                                 bezt->vec[2][1] = (dy + (2 * ftoutline.points[l_next].y) * scale) / 3.0f;
172
173                                                 bezt->h1 = bezt->h2 = HD_ALIGN;
174                                                 bezt->radius = 1.0f;
175                                                 bezt++;
176                                         }
177                                 }
178
179                                 /* on-curve points */
180                                 if (ftoutline.tags[l] == FT_Curve_Tag_On) {
181                                         const int l_prev = (k > 0)     ? (l - 1) : ftoutline.contours[j];
182                                         const int l_next = (k < n - 1) ? (l + 1) : l_first;
183
184                                         /* left handle */
185                                         if (ftoutline.tags[l_prev] == FT_Curve_Tag_Cubic) {
186                                                 bezt->vec[0][0] = ftoutline.points[l_prev].x * scale;
187                                                 bezt->vec[0][1] = ftoutline.points[l_prev].y * scale;
188                                                 bezt->h1 = HD_FREE;
189                                         }
190                                         else if (ftoutline.tags[l_prev] == FT_Curve_Tag_Conic) {
191                                                 bezt->vec[0][0] = (ftoutline.points[l].x + (2 * ftoutline.points[l_prev].x)) * scale / 3.0f;
192                                                 bezt->vec[0][1] = (ftoutline.points[l].y + (2 * ftoutline.points[l_prev].y)) * scale / 3.0f;
193                                                 bezt->h1 = HD_FREE;
194                                         }
195                                         else {
196                                                 bezt->vec[0][0] = ftoutline.points[l].x * scale - (ftoutline.points[l].x - ftoutline.points[l_prev].x) * scale / 3.0f;
197                                                 bezt->vec[0][1] = ftoutline.points[l].y * scale - (ftoutline.points[l].y - ftoutline.points[l_prev].y) * scale / 3.0f;
198                                                 bezt->h1 = HD_VECT;
199                                         }
200
201                                         /* midpoint (on-curve point) */
202                                         bezt->vec[1][0] = ftoutline.points[l].x * scale;
203                                         bezt->vec[1][1] = ftoutline.points[l].y * scale;
204
205                                         /* right handle */
206                                         if (ftoutline.tags[l_next] == FT_Curve_Tag_Cubic) {
207                                                 bezt->vec[2][0] = ftoutline.points[l_next].x * scale;
208                                                 bezt->vec[2][1] = ftoutline.points[l_next].y * scale;
209                                                 bezt->h2 = HD_FREE;
210                                         }
211                                         else if (ftoutline.tags[l_next] == FT_Curve_Tag_Conic) {
212                                                 bezt->vec[2][0] = (ftoutline.points[l].x + (2 * ftoutline.points[l_next].x)) * scale / 3.0f;
213                                                 bezt->vec[2][1] = (ftoutline.points[l].y + (2 * ftoutline.points[l_next].y)) * scale / 3.0f;
214                                                 bezt->h2 = HD_FREE;
215                                         }
216                                         else {
217                                                 bezt->vec[2][0] = ftoutline.points[l].x * scale - (ftoutline.points[l].x - ftoutline.points[l_next].x) * scale / 3.0f;
218                                                 bezt->vec[2][1] = ftoutline.points[l].y * scale - (ftoutline.points[l].y - ftoutline.points[l_next].y) * scale / 3.0f;
219                                                 bezt->h2 = HD_VECT;
220                                         }
221
222                                         /* get the handles that are aligned, tricky...
223                                          * - check if one of them is a vector handle.
224                                          * - dist_squared_to_line_v2, check if the three beztriple points are on one line
225                                          * - len_squared_v2v2, see if there's a distance between the three points
226                                          * - len_squared_v2v2 again, to check the angle between the handles
227                                          */
228                                         if ((bezt->h1 != HD_VECT && bezt->h2 != HD_VECT) &&
229                                             (dist_squared_to_line_v2(bezt->vec[0], bezt->vec[1], bezt->vec[2]) < (0.001f * 0.001f)) &&
230                                             (len_squared_v2v2(bezt->vec[0], bezt->vec[1]) > eps_sq) &&
231                                             (len_squared_v2v2(bezt->vec[1], bezt->vec[2]) > eps_sq) &&
232                                             (len_squared_v2v2(bezt->vec[0], bezt->vec[2]) > eps_sq) &&
233                                             (len_squared_v2v2(bezt->vec[0], bezt->vec[2]) >
234                                              max_ff(len_squared_v2v2(bezt->vec[0], bezt->vec[1]),
235                                                     len_squared_v2v2(bezt->vec[1], bezt->vec[2]))))
236                                         {
237                                                 bezt->h1 = bezt->h2 = HD_ALIGN;
238                                         }
239                                         bezt->radius = 1.0f;
240                                         bezt++;
241                                 }
242                         }
243                 }
244
245                 MEM_freeN(onpoints);
246
247                 return che;
248         }
249
250         return NULL;
251 }
252
253 static VChar *objchr_to_ftvfontdata(VFont *vfont, FT_ULong charcode)
254 {
255         VChar *che;
256
257         /* Freetype2 */
258         FT_Face face;
259
260         /* Load the font to memory */
261         if (vfont->temp_pf) {
262                 err = FT_New_Memory_Face(library,
263                                          vfont->temp_pf->data,
264                                          vfont->temp_pf->size,
265                                          0,
266                                          &face);
267                 if (err) {
268                         return NULL;
269                 }
270         }
271         else {
272                 err = true;
273                 return NULL;
274         }
275
276         /* Read the char */
277         che = freetypechar_to_vchar(face, charcode, vfont->data);
278
279         /* And everything went ok */
280         return che;
281 }
282
283
284 static VFontData *objfnt_to_ftvfontdata(PackedFile *pf)
285 {
286         /* Variables */
287         FT_Face face;
288         const FT_ULong charcode_reserve = 256;
289         FT_ULong charcode = 0, lcode;
290         FT_UInt glyph_index;
291         const char *fontname;
292         VFontData *vfd;
293
294 #if 0
295         FT_CharMap found = 0;
296         FT_CharMap charmap;
297         FT_UShort my_platform_id = TT_PLATFORM_MICROSOFT;
298         FT_UShort my_encoding_id = TT_MS_ID_UNICODE_CS;
299         int n;
300 #endif
301
302         /* load the freetype font */
303         err = FT_New_Memory_Face(library,
304                                  pf->data,
305                                  pf->size,
306                                  0,
307                                  &face);
308
309         if (err) return NULL;
310
311 #if 0
312         for (n = 0; n < face->num_charmaps; n++)
313         {
314                 charmap = face->charmaps[n];
315                 if (charmap->platform_id == my_platform_id &&
316                     charmap->encoding_id == my_encoding_id)
317                 {
318                         found = charmap;
319                         break;
320                 }
321         }
322
323         if (!found) { return NULL; }
324
325         /* now, select the charmap for the face object */
326         err = FT_Set_Charmap(face, found);
327         if (err) { return NULL; }
328 #endif
329
330         /* allocate blender font */
331         vfd = MEM_callocN(sizeof(*vfd), "FTVFontData");
332
333         /* get the name */
334         fontname = FT_Get_Postscript_Name(face);
335         BLI_strncpy(vfd->name, (fontname == NULL) ? "" : fontname, sizeof(vfd->name));
336
337         /* Extract the first 256 character from TTF */
338         lcode = charcode = FT_Get_First_Char(face, &glyph_index);
339
340         /* No charmap found from the ttf so we need to figure it out */
341         if (glyph_index == 0) {
342                 FT_CharMap found = NULL;
343                 FT_CharMap charmap;
344                 int n;
345
346                 for (n = 0; n < face->num_charmaps; n++) {
347                         charmap = face->charmaps[n];
348                         if (charmap->encoding == FT_ENCODING_APPLE_ROMAN) {
349                                 found = charmap;
350                                 break;
351                         }
352                 }
353
354                 err = FT_Set_Charmap(face, found);
355
356                 if (err)
357                         return NULL;
358
359                 lcode = charcode = FT_Get_First_Char(face, &glyph_index);
360         }
361
362         /* Blender default BFont is not "complete". */
363         const bool complete_font = (face->ascender != 0) && (face->descender != 0) &&
364                                    (face->ascender != face->descender);
365
366         if (complete_font) {
367                 /* We can get descender as well, but we simple store descender in relation to the ascender.
368                  * Also note that descender is stored as a negative number. */
369                 vfd->ascender = (float)face->ascender / (face->ascender - face->descender);
370         }
371         else {
372                 vfd->ascender = 0.8f;
373                 vfd->em_height = 1.0f;
374         }
375
376         /* Adjust font size */
377         if (face->bbox.yMax != face->bbox.yMin) {
378                 vfd->scale = (float)(1.0 / (double)(face->bbox.yMax - face->bbox.yMin));
379
380                 if (complete_font) {
381                         vfd->em_height = (float)(face->ascender - face->descender) / (face->bbox.yMax - face->bbox.yMin);
382                 }
383         }
384         else {
385                 vfd->scale = 1.0f / 1000.0f;
386         }
387
388         /* Load characters */
389         vfd->characters = BLI_ghash_int_new_ex(__func__, charcode_reserve);
390
391         while (charcode < charcode_reserve) {
392                 /* Generate the font data */
393                 freetypechar_to_vchar(face, charcode, vfd);
394
395                 /* Next glyph */
396                 charcode = FT_Get_Next_Char(face, charcode, &glyph_index);
397
398                 /* Check that we won't start infinite loop */
399                 if (charcode <= lcode)
400                         break;
401                 lcode = charcode;
402         }
403
404         return vfd;
405 }
406
407
408 static int check_freetypefont(PackedFile *pf)
409 {
410         FT_Face face;
411         FT_GlyphSlot glyph;
412         FT_UInt glyph_index;
413 #if 0
414         FT_CharMap charmap;
415         FT_CharMap found;
416         FT_UShort my_platform_id = TT_PLATFORM_MICROSOFT;
417         FT_UShort my_encoding_id = TT_MS_ID_UNICODE_CS;
418         int n;
419 #endif
420         int success = 0;
421
422         err = FT_New_Memory_Face(library,
423                                  pf->data,
424                                  pf->size,
425                                  0,
426                                  &face);
427         if (err) {
428                 success = 0;
429                 //XXX error("This is not a valid font");
430         }
431         else {
432
433 #if 0
434                 for (n = 0; n < face->num_charmaps; n++) {
435                         charmap = face->charmaps[n];
436                         if (charmap->platform_id == my_platform_id && charmap->encoding_id == my_encoding_id) {
437                                 found = charmap;
438                                 break;
439                         }
440                 }
441
442                 if (!found) { return 0; }
443
444                 /* now, select the charmap for the face object */
445                 err = FT_Set_Charmap(face, found);
446                 if (err) { return 0; }
447 #endif
448
449                 glyph_index = FT_Get_Char_Index(face, 'A');
450                 err = FT_Load_Glyph(face, glyph_index, FT_LOAD_NO_SCALE | FT_LOAD_NO_BITMAP);
451                 if (err) {
452                         success = 0;
453                 }
454                 else {
455                         glyph = face->glyph;
456                         if (glyph->format == ft_glyph_format_outline) {
457                                 success = 1;
458                         }
459                         else {
460                                 //XXX error("Selected Font has no outline data");
461                                 success = 0;
462                         }
463                 }
464         }
465
466         return success;
467 }
468
469 /**
470  * Construct a new VFontData structure from
471  * Freetype font data in a PackedFile.
472  *
473  * \param pf The font data.
474  * \retval A new VFontData structure, or NULL
475  * if unable to load.
476  */
477 VFontData *BLI_vfontdata_from_freetypefont(PackedFile *pf)
478 {
479         VFontData *vfd = NULL;
480         int success = 0;
481
482         /* init Freetype */
483         err = FT_Init_FreeType(&library);
484         if (err) {
485                 /* XXX error("Failed to load the Freetype font library"); */
486                 return NULL;
487         }
488
489         success = check_freetypefont(pf);
490
491         if (success) {
492                 vfd = objfnt_to_ftvfontdata(pf);
493         }
494
495         /* free Freetype */
496         FT_Done_FreeType(library);
497
498         return vfd;
499 }
500
501 static void *vfontdata_copy_characters_value_cb(const void *src)
502 {
503         return BLI_vfontchar_copy(src, 0);
504 }
505
506 VFontData *BLI_vfontdata_copy(const VFontData *vfont_src, const int UNUSED(flag))
507 {
508         VFontData *vfont_dst = MEM_dupallocN(vfont_src);
509
510         if (vfont_src->characters != NULL) {
511                 vfont_dst->characters = BLI_ghash_copy(vfont_src->characters, NULL, vfontdata_copy_characters_value_cb);
512         }
513
514         return vfont_dst;
515 }
516
517 VChar *BLI_vfontchar_from_freetypefont(VFont *vfont, unsigned long character)
518 {
519         VChar *che = NULL;
520
521         if (!vfont) return NULL;
522
523         /* Init Freetype */
524         err = FT_Init_FreeType(&library);
525         if (err) {
526                 /* XXX error("Failed to load the Freetype font library"); */
527                 return NULL;
528         }
529
530         /* Load the character */
531         che = objchr_to_ftvfontdata(vfont, character);
532
533         /* Free Freetype */
534         FT_Done_FreeType(library);
535
536         return che;
537 }
538
539 /* Yeah, this is very bad... But why is this in BLI in the first place, since it uses Nurb data?
540  * Anyway, do not feel like duplicating whole Nurb copy code here, so unless someone has a better idea... */
541 #include "../../blenkernel/BKE_curve.h"
542
543 VChar *BLI_vfontchar_copy(const VChar *vchar_src, const int UNUSED(flag))
544 {
545         VChar *vchar_dst = MEM_dupallocN(vchar_src);
546
547         BLI_listbase_clear(&vchar_dst->nurbsbase);
548         BKE_nurbList_duplicate(&vchar_dst->nurbsbase, &vchar_src->nurbsbase);
549
550         return vchar_dst;
551 }
552
553 #if 0
554
555 /* Freetype2 Outline struct */
556
557 typedef struct  FT_Outline_ {
558         short       n_contours;      /* number of contours in glyph        */
559         short       n_points;        /* number of points in the glyph      */
560
561         FT_Vector  *points;          /* the outline's points               */
562         char       *tags;            /* the points flags                   */
563         short      *contours;        /* the contour end points             */
564
565         int         flags;           /* outline masks                      */
566 } FT_Outline;
567
568 #endif
569
570 /*
571  * from: http://www.freetype.org/freetype2/docs/glyphs/glyphs-6.html#section-1
572  *
573  * Vectorial representation of Freetype glyphs
574  *
575  * The source format of outlines is a collection of closed paths called "contours". Each contour is
576  * made of a series of line segments and bezier arcs. Depending on the file format, these can be
577  * second-order or third-order polynomials. The former are also called quadratic or conic arcs, and
578  * they come from the TrueType format. The latter are called cubic arcs and mostly come from the
579  * Type1 format.
580  *
581  * Each arc is described through a series of start, end and control points. Each point of the outline
582  * has a specific tag which indicates whether it is used to describe a line segment or an arc.
583  *
584  *
585  * The following rules are applied to decompose the contour's points into segments and arcs :
586  *
587  * # two successive "on" points indicate a line segment joining them.
588  *
589  * # one conic "off" point amidst two "on" points indicates a conic bezier arc, the "off" point being
590  *   the control point, and the "on" ones the start and end points.
591  *
592  * # Two successive cubic "off" points amidst two "on" points indicate a cubic bezier arc. There must
593  *   be exactly two cubic control points and two on points for each cubic arc (using a single cubic
594  *   "off" point between two "on" points is forbidden, for example).
595  *
596  * # finally, two successive conic "off" points forces the rasterizer to create (during the scan-line
597  *   conversion process exclusively) a virtual "on" point amidst them, at their exact middle. This
598  *   greatly facilitates the definition of successive conic bezier arcs. Moreover, it's the way
599  *   outlines are described in the TrueType specification.
600  *
601  * Note that it is possible to mix conic and cubic arcs in a single contour, even though no current
602  * font driver produces such outlines.
603  *
604  *                                   *            # on
605  *                                                * off
606  *                                __---__
607  *   #-__                      _--       -_
608  *       --__                _-            -
609  *           --__           #               \
610  *               --__                        #
611  *                   -#
612  *                            Two "on" points
613  *    Two "on" points       and one "conic" point
614  *                             between them
615  *
616  *
617  *
618  *                 *
619  *   #            __      Two "on" points with two "conic"
620  *    \          -  -     points between them. The point
621  *     \        /    \    marked '0' is the middle of the
622  *      -      0      \   "off" points, and is a 'virtual'
623  *       -_  _-       #   "on" point where the curve passes.
624  *         --             It does not appear in the point
625  *                        list.
626  *         *
627  *
628  *
629  *
630  *
631  *         *                # on
632  *                    *     * off
633  *          __---__
634  *       _--       -_
635  *     _-            -
636  *    #               \
637  *                     #
638  *
639  *      Two "on" points
640  *    and two "cubic" point
641  *       between them
642  *
643  *
644  * Each glyph's original outline points are located on a grid of indivisible units. The points are stored
645  * in the font file as 16-bit integer grid coordinates, with the grid origin's being at (0, 0); they thus
646  * range from -16384 to 16383.
647  *
648  * Convert conic to bezier arcs:
649  * Conic P0 P1 P2
650  * Bezier B0 B1 B2 B3
651  * B0=P0
652  * B1=(P0+2*P1)/3
653  * B2=(P2+2*P1)/3
654  * B3=P2
655  *
656  */