- increased the character loading/conversion routine that it includes the
[blender.git] / source / blender / blenlib / intern / freetypefont.c
1 /**
2  * $Id$
3  *
4  * ***** BEGIN GPL/BL DUAL LICENSE BLOCK *****
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version. The Blender
10  * Foundation also sells licenses for use in proprietary software under
11  * the Blender License.  See http://www.blender.org/BL/ for information
12  * about this.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software Foundation,
21  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22  *
23  * The Original Code is written by Rob Haarsma (phase)
24  * All rights reserved.
25  *
26  *
27  * Contributor(s): none yet.
28  *
29  * ***** END GPL/BL DUAL LICENSE BLOCK *****
30  */
31
32 #ifdef WITH_FREETYPE2
33
34 #ifdef WIN32
35 #pragma warning (disable:4244)
36 #endif
37
38 #include <ft2build.h>
39 #include FT_FREETYPE_H
40 #include FT_GLYPH_H
41 #include <freetype/ttnameid.h>
42
43 #include "MEM_guardedalloc.h"
44
45 #include "BLI_vfontdata.h"
46 #include "BLI_blenlib.h"
47 #include "BLI_arithb.h"  
48
49 #include "BIF_toolbox.h"
50
51 #include "DNA_packedFile_types.h"
52 #include "DNA_curve_types.h"
53
54 // increased max to 255, including extended charset (ton)
55 #define myMIN_ASCII     32
56 #define myMAX_ASCII     255
57
58 // should come from arithb.c
59 #define MIN2(x,y)               ( (x)<(y) ? (x) : (y) )
60 #define MAX2(x,y)               ( (x)>(y) ? (x) : (y) )
61
62 /* local variables */
63 static FT_Library       library;
64 static FT_Error         err;
65
66 #if 0
67 // Freetype2 Outline struct
68
69 typedef struct  FT_Outline_
70   {
71     short       n_contours;      /* number of contours in glyph        */
72     short       n_points;        /* number of points in the glyph      */
73
74     FT_Vector*  points;          /* the outline's points               */
75     char*       tags;            /* the points flags                   */
76     short*      contours;        /* the contour end points             */
77
78     int         flags;           /* outline masks                      */
79
80   } FT_Outline;
81 #endif
82
83 /***//*
84 from: http://www.freetype.org/freetype2/docs/glyphs/glyphs-6.html#section-1
85
86 Vectorial representation of Freetype glyphs
87
88 The source format of outlines is a collection of closed paths called "contours". Each contour is
89 made of a series of line segments and bezier arcs. Depending on the file format, these can be
90 second-order or third-order polynomials. The former are also called quadratic or conic arcs, and
91 they come from the TrueType format. The latter are called cubic arcs and mostly come from the
92 Type1 format.
93
94 Each arc is described through a series of start, end and control points. Each point of the outline
95 has a specific tag which indicates wether it is used to describe a line segment or an arc.
96
97
98 The following rules are applied to decompose the contour's points into segments and arcs :
99
100 # two successive "on" points indicate a line segment joining them.
101
102 # one conic "off" point amidst two "on" points indicates a conic bezier arc, the "off" point being
103   the control point, and the "on" ones the start and end points.
104
105 # Two successive cubic "off" points amidst two "on" points indicate a cubic bezier arc. There must
106   be exactly two cubic control points and two on points for each cubic arc (using a single cubic 
107   "off" point between two "on" points is forbidden, for example).
108
109 # finally, two successive conic "off" points forces the rasterizer to create (during the scan-line
110   conversion process exclusively) a virtual "on" point amidst them, at their exact middle. This
111   greatly facilitates the definition of successive conic bezier arcs. Moreover, it's the way
112   outlines are described in the TrueType specification.
113
114 Note that it is possible to mix conic and cubic arcs in a single contour, even though no current
115 font driver produces such outlines.
116
117                                   *            # on      
118                                                * off
119                                __---__
120   #-__                      _--       -_
121       --__                _-            -
122           --__           #               \
123               --__                        #
124                   -#
125                            Two "on" points
126    Two "on" points       and one "conic" point
127                             between them
128
129
130
131                 *
132   #            __      Two "on" points with two "conic"
133    \          -  -     points between them. The point
134     \        /    \    marked '0' is the middle of the
135      -      0      \   "off" points, and is a 'virtual'
136       -_  _-       #   "on" point where the curve passes.
137         --             It does not appear in the point
138                        list.
139         *
140
141
142
143
144         *                # on
145                    *     * off
146          __---__
147       _--       -_
148     _-            -
149    #               \
150                     #
151
152      Two "on" points
153    and two "cubic" point
154       between them
155
156
157 Each glyph's original outline points are located on a grid of indivisible units. The points are stored
158 in the font file as 16-bit integer grid coordinates, with the grid origin's being at (0,0); they thus
159 range from -16384 to 16383.
160
161
162 Convert conic to bezier arcs:
163 Conic P0 P1 P2
164 Bezier B0 B1 B2 B3
165 B0=P0
166 B1=(P0+2*P1)/3
167 B2=(P2+2*P1)/3
168 B3=P2
169
170 *//****/
171
172 static VFontData *objfnt_to_ftvfontdata(PackedFile * pf)
173 {
174         // Blender
175         VFontData *vfd;
176         struct Nurb *nu;
177         struct BezTriple *bezt;
178
179         // Freetype2
180         FT_Face         face;
181         FT_GlyphSlot  glyph;
182         FT_UInt         glyph_index;
183         FT_Outline      ftoutline;
184 /*
185     FT_CharMap  found = 0;
186         FT_CharMap  charmap;
187         FT_UShort my_platform_id = TT_PLATFORM_MICROSOFT;
188         FT_UShort my_encoding_id = TT_MS_ID_UNICODE_CS;
189         int         n;
190 */
191
192         float scale= 1. / 1024.; //needs text_height from metrics to make a standard linedist
193         float dx, dy;
194         int i, j, k, l, m; /* uhoh, kiddie C loops */
195         /* i = characters, j = curves/contours, k = points, l = curvepoint, m = first point on curve */
196
197         // test is used for BIF_printf
198         char test[2];
199
200         
201         // load the freetype font
202         err = FT_New_Memory_Face( library,
203                                                 pf->data,
204                                                 pf->size,
205                                                 0,
206                                                 &face );
207
208         if(err) return NULL;
209 /*
210     for ( n = 0; n < face->num_charmaps; n++ )
211     {
212       charmap = face->charmaps[n];
213       if ( charmap->platform_id == my_platform_id &&
214            charmap->encoding_id == my_encoding_id )
215       {
216         found = charmap;
217         break;
218       }
219     }
220
221     if ( !found ) { return NULL; }
222
223     // now, select the charmap for the face object
224     err = FT_Set_Charmap( face, found );
225     if ( err ) { return NULL; }
226 */
227
228         // allocate blender font
229         vfd= MEM_callocN(sizeof(*vfd), "FTVFontData");
230
231         // extract generic ascii character range (needs international support, dynamic loading of chars, etcetc)
232         for(i = myMIN_ASCII; i <= myMAX_ASCII; i++) {
233                 int  *npoints;  //total points of each contour
234                 int  *onpoints; //num points on curve
235
236                 test[0] = i;
237                 test[1] = '\0'; //to print character
238
239                 glyph_index = FT_Get_Char_Index( face, i );
240                 err = FT_Load_Glyph(face, glyph_index, FT_LOAD_NO_SCALE | FT_LOAD_NO_BITMAP);
241                 glyph = face->glyph;
242                 ftoutline = glyph->outline;
243
244                 vfd->width[i] = glyph->advance.x* scale;
245 //              BIF_printf("sx %d sy %d", glyph->advance.x, face->glyph->metrics->text_height);
246
247                 npoints = (int *)MEM_callocN((ftoutline.n_contours)* sizeof(int),"endpoints") ;
248                 onpoints = (int *)MEM_callocN((ftoutline.n_contours)* sizeof(int),"onpoints") ;
249
250                 // calculate total points of each contour
251                 for(j = 0; j < ftoutline.n_contours; j++) {
252                         if(j == 0)
253                                 npoints[j] = ftoutline.contours[j] + 1;
254                         else
255                                 npoints[j] = ftoutline.contours[j] - ftoutline.contours[j - 1];
256                 }
257
258                 // get number of on-curve points for beziertriples (including conic virtual on-points) 
259                 for(j = 0; j < ftoutline.n_contours; j++) {
260                         l = 0;
261                         for(k = 0; k < npoints[j]; k++) {
262                                 if(j > 0) l = k + ftoutline.contours[j - 1] + 1; else l = k;
263
264 //                              if(i == 67) BIF_printf("%d->%s : |k %2d|l %2d|t %2d|", i, test, k, l, ftoutline.n_points);
265
266                                 if(ftoutline.tags[l] == FT_Curve_Tag_On)
267                                         onpoints[j]++;
268
269                                 if(k < npoints[j] - 1 )
270                                         if( ftoutline.tags[l]   == FT_Curve_Tag_Conic &&
271                                                 ftoutline.tags[l+1] == FT_Curve_Tag_Conic)
272                                                 onpoints[j]++;
273                         }
274                 }
275
276                 //final contour loop, bezier & conic styles merged
277                 for(j = 0; j < ftoutline.n_contours; j++) {
278                         // add new curve
279                         nu  =  (Nurb*)MEM_callocN(sizeof(struct Nurb),"objfnt_nurb");
280                         bezt = (BezTriple*)MEM_callocN((onpoints[j])* sizeof(BezTriple),"objfnt_bezt") ;
281                         BLI_addtail(&vfd->nurbsbase[i], nu);
282                         nu->type= CU_BEZIER+CU_2D;
283                         nu->pntsu = onpoints[j];
284                         nu->resolu= 8;
285                         nu->flagu= 1;
286                         nu->bezt = bezt;
287
288                         //individual curve loop, start-end
289                         for(k = 0; k < npoints[j]; k++) {
290                                 if(j > 0) l = k + ftoutline.contours[j - 1] + 1; else l = k;
291                                 if(k == 0) m = l;
292                                 
293                                 //virtual conic on-curve points
294                                 if(k < npoints[j] - 1 )
295                                         if( ftoutline.tags[l] == FT_Curve_Tag_Conic && ftoutline.tags[l+1] == FT_Curve_Tag_Conic) {
296                                                 dx = (ftoutline.points[l].x + ftoutline.points[l+1].x)* scale / 2.0;
297                                                 dy = (ftoutline.points[l].y + ftoutline.points[l+1].y)* scale / 2.0;
298
299                                                 //left handle
300                                                 bezt->vec[0][0] = (dx + (2 * ftoutline.points[l].x)* scale) / 3.0;
301                                                 bezt->vec[0][1] = (dy + (2 * ftoutline.points[l].y)* scale) / 3.0;
302
303                                                 //midpoint (virtual on-curve point)
304                                                 bezt->vec[1][0] = (ftoutline.points[l].x + ftoutline.points[l+1].x)* scale / 2.0;
305                                                 bezt->vec[1][1] = (ftoutline.points[l].y + ftoutline.points[l+1].y)* scale / 2.0;
306
307                                                 //right handle
308                                                 bezt->vec[2][0] = (dx + (2 * ftoutline.points[l+1].x)* scale) / 3.0;
309                                                 bezt->vec[2][1] = (dy + (2 * ftoutline.points[l+1].y)* scale) / 3.0;
310
311                                                 bezt->h1= bezt->h2= HD_ALIGN;
312                                                 bezt++;
313                                         }
314
315                                 //on-curve points
316                                 if(ftoutline.tags[l] == FT_Curve_Tag_On) {
317                                         //left handle
318                                         if(k > 0) {
319                                                 if(ftoutline.tags[l - 1] == FT_Curve_Tag_Cubic) {
320                                                         bezt->vec[0][0] = ftoutline.points[l-1].x* scale;
321                                                         bezt->vec[0][1] = ftoutline.points[l-1].y* scale;
322                                                         bezt->h1= HD_FREE;
323                                                 } else if(ftoutline.tags[l - 1] == FT_Curve_Tag_Conic) {
324                                                         bezt->vec[0][0] = (ftoutline.points[l].x + (2 * ftoutline.points[l - 1].x))* scale / 3.0;
325                                                         bezt->vec[0][1] = (ftoutline.points[l].y + (2 * ftoutline.points[l - 1].y))* scale / 3.0;
326                                                         bezt->h1= HD_FREE;
327                                                 } else {
328                                                         bezt->vec[0][0] = ftoutline.points[l].x* scale - (ftoutline.points[l].x - ftoutline.points[l-1].x)* scale / 3.0;
329                                                         bezt->vec[0][1] = ftoutline.points[l].y* scale - (ftoutline.points[l].y - ftoutline.points[l-1].y)* scale / 3.0;
330                                                         bezt->h1= HD_VECT;
331                                                 }
332                                         } else { //first point on curve
333                                                 if(ftoutline.tags[ftoutline.contours[j]] == FT_Curve_Tag_Cubic) {
334                                                         bezt->vec[0][0] = ftoutline.points[ftoutline.contours[j]].x * scale;
335                                                         bezt->vec[0][1] = ftoutline.points[ftoutline.contours[j]].y * scale;
336                                                         bezt->h1= HD_FREE;
337                                                 } else if(ftoutline.tags[ftoutline.contours[j]] == FT_Curve_Tag_Conic) {
338                                                         bezt->vec[0][0] = (ftoutline.points[l].x + (2 * ftoutline.points[ftoutline.contours[j]].x))* scale / 3.0 ;
339                                                         bezt->vec[0][1] = (ftoutline.points[l].y + (2 * ftoutline.points[ftoutline.contours[j]].y))* scale / 3.0 ;
340                                                         bezt->h1= HD_FREE;
341                                                 } else {
342                                                         bezt->vec[0][0] = ftoutline.points[l].x* scale - (ftoutline.points[l].x - ftoutline.points[ftoutline.contours[j]].x)* scale / 3.0;
343                                                         bezt->vec[0][1] = ftoutline.points[l].y* scale - (ftoutline.points[l].y - ftoutline.points[ftoutline.contours[j]].y)* scale / 3.0;
344                                                         bezt->h1= HD_VECT;
345                                                 }
346                                         }
347
348                                         //midpoint (on-curve point)
349                                         bezt->vec[1][0] = ftoutline.points[l].x* scale;
350                                         bezt->vec[1][1] = ftoutline.points[l].y* scale;
351
352                                         //right handle
353                                         if(k < (npoints[j] - 1)) {
354                                                 if(ftoutline.tags[l+1] == FT_Curve_Tag_Cubic) {
355                                                         bezt->vec[2][0] = ftoutline.points[l+1].x* scale;
356                                                         bezt->vec[2][1] = ftoutline.points[l+1].y* scale;
357                                                         bezt->h2= HD_FREE;
358                                                 } else if(ftoutline.tags[l+1] == FT_Curve_Tag_Conic) {
359                                                         bezt->vec[2][0] = (ftoutline.points[l].x + (2 * ftoutline.points[l+1].x))* scale / 3.0;
360                                                         bezt->vec[2][1] = (ftoutline.points[l].y + (2 * ftoutline.points[l+1].y))* scale / 3.0;
361                                                         bezt->h2= HD_FREE;
362                                                 } else {
363                                                         bezt->vec[2][0] = ftoutline.points[l].x* scale - (ftoutline.points[l].x - ftoutline.points[l+1].x)* scale / 3.0;
364                                                         bezt->vec[2][1] = ftoutline.points[l].y* scale - (ftoutline.points[l].y - ftoutline.points[l+1].y)* scale / 3.0;
365                                                         bezt->h2= HD_VECT;
366                                                 }
367                                         } else { //last point on curve
368                                                 if(ftoutline.tags[m] == FT_Curve_Tag_Cubic) {
369 //                                                      okee("hhuh");
370                                                         bezt->vec[2][0] = ftoutline.points[m].x* scale;
371                                                         bezt->vec[2][1] = ftoutline.points[m].y* scale;
372                                                         bezt->h2= HD_FREE;
373                                                 } else if(ftoutline.tags[m] == FT_Curve_Tag_Conic) {
374                                                         bezt->vec[2][0] = (ftoutline.points[l].x + (2 * ftoutline.points[m].x))* scale / 3.0 ;
375                                                         bezt->vec[2][1] = (ftoutline.points[l].y + (2 * ftoutline.points[m].y))* scale / 3.0 ;
376                                                         bezt->h2= HD_FREE;
377                                                 } else {
378                                                         bezt->vec[2][0] = ftoutline.points[l].x* scale - (ftoutline.points[l].x - ftoutline.points[m].x)* scale / 3.0;
379                                                         bezt->vec[2][1] = ftoutline.points[l].y* scale - (ftoutline.points[l].y - ftoutline.points[m].y)* scale / 3.0;
380                                                         bezt->h2= HD_VECT;
381                                                 }
382                                         }
383
384                                         // get the handles that are aligned, tricky...
385                                         // DistVL2Dfl, check if the three beztriple points are on one line
386                                         // VecLenf, see if there's a distance between the three points
387                                         // VecLenf again, to check the angle between the handles 
388                                         // finally, check if one of them is a vector handle 
389                                         if((DistVL2Dfl(bezt->vec[0],bezt->vec[1],bezt->vec[2]) < 0.001) &&
390                                                 (VecLenf(bezt->vec[0], bezt->vec[1]) > 0.0001) &&
391                                                 (VecLenf(bezt->vec[1], bezt->vec[2]) > 0.0001) &&
392                                                 (VecLenf(bezt->vec[0], bezt->vec[2]) > 0.0002) &&
393                                                 (VecLenf(bezt->vec[0], bezt->vec[2]) > MAX2(VecLenf(bezt->vec[0], bezt->vec[1]), VecLenf(bezt->vec[1], bezt->vec[2]))) &&
394                                                 bezt->h1 != HD_VECT && bezt->h2 != HD_VECT)
395                                         {
396                                                 bezt->h1= bezt->h2= HD_ALIGN;
397                                         }
398                                         bezt++;
399                                 }
400                         }
401                 }
402
403                 if(npoints) MEM_freeN(npoints);
404                 if(onpoints) MEM_freeN(onpoints);
405         }
406         return vfd;
407 }
408
409
410 static int check_freetypefont(PackedFile * pf)
411 {
412         FT_Face                 face;
413         FT_GlyphSlot    glyph;
414         FT_UInt                 glyph_index;
415 /*
416         FT_CharMap  charmap;
417         FT_CharMap  found;
418         FT_UShort my_platform_id = TT_PLATFORM_MICROSOFT;
419         FT_UShort my_encoding_id = TT_MS_ID_UNICODE_CS;
420         int         n;
421 */
422         int success = 0;
423
424         err = FT_New_Memory_Face( library,
425                                                         pf->data,
426                                                         pf->size,
427                                                         0,
428                                                         &face );
429         if(err) {
430                 success = 0;
431             error("This is not a valid font");
432         }
433         else {
434 /*
435                 for ( n = 0; n < face->num_charmaps; n++ )
436                 {
437                   charmap = face->charmaps[n];
438                   if ( charmap->platform_id == my_platform_id &&
439                            charmap->encoding_id == my_encoding_id )
440                   {
441                         found = charmap;
442                         break;
443                   }
444                 }
445
446                 if ( !found ) { return 0; }
447
448                 // now, select the charmap for the face object 
449                 err = FT_Set_Charmap( face, found );
450                 if ( err ) { return 0; }
451 */
452                 glyph_index = FT_Get_Char_Index( face, 'A' );
453                 err = FT_Load_Glyph(face, glyph_index, FT_LOAD_NO_SCALE | FT_LOAD_NO_BITMAP);
454                 if(err) success = 0;
455                 else {
456                         glyph = face->glyph;
457                         if (glyph->format == ft_glyph_format_outline ) {
458                                 success = 1;
459                         } else {
460                                 error("Selected Font has no outline data");
461                                 success = 0;
462                         }
463                 }
464         }
465         
466         return success;
467 }
468
469
470 VFontData *BLI_vfontdata_from_freetypefont(PackedFile *pf)
471 {
472         VFontData *vfd= NULL;
473         int success = 0;
474
475         //init Freetype 
476         err = FT_Init_FreeType( &library);
477         if(err) {
478             error("Failed loading Freetype font library");
479                 return 0;
480         }
481
482         success = check_freetypefont(pf);
483         
484         if (success) {
485                 vfd= objfnt_to_ftvfontdata(pf);
486         }
487
488         //free Freetype
489         FT_Done_FreeType( library);
490         
491         return vfd;
492 }
493
494 #endif // WITH_FREETYPE2