1 /****************************************************************************/
2 /*                                                                          */
3 /*  The FreeType project -- a free and portable quality TrueType renderer.  */
4 /*                                                                          */
5 /*  Copyright 1996-1999 by                                                  */
6 /*  D. Turner, R.Wilhelm, and W. Lemberg                                    */
7 /*                                                                          */
8 /*  ftstrpnm: convert text to image (in PGM or PBM format)                  */
9 /*                                                                          */
10 /*  NOTE:  This is just a test program that is used to show off and         */
11 /*         debug the current engine.                                        */
12 /*                                                                          */
13 /****************************************************************************/
14 
15 #define PROGNAME "ftstrpnm"
16 
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <string.h>
20 
21 #include "common.h"   /* for ft_getopt() */
22 #include "freetype.h"
23 
24 #define TT_VALID( handle )  ( ( handle ).z != NULL )
25 
26 
27   /* Global variables */
28 
29   TT_Engine    engine;
30   TT_Face      face;
31   TT_Instance  instance;
32 
33   TT_Face_Properties  properties;
34 
35   TT_Raster_Map  bit;
36   TT_Raster_Map  small_bit;   /* used when font-smoothing is enabled */
37 
38   int  pnm_width,   pnm_height;
39   int  pnm_x_shift, pnm_y_shift;
40 
41 
42   /* Loaded glyphs for all characters */
43 
44   TT_Glyph  *glyphs = NULL;
45 
46 
47   /* Options */
48 
49   int  dpi    = 96;
50   int  ptsize = 12;
51   int  hinted = 1;
52   int  smooth = 0;
53   int  border = 0;
54 
55 
56   /* raster map management */
57 
Init_Raster_Map(TT_Raster_Map * bit,int width,int height)58   static void  Init_Raster_Map( TT_Raster_Map*  bit, int  width, int  height )
59   {
60     bit->rows  = height;
61     bit->width = ( width + 3 ) & -4;
62     bit->flow  = TT_Flow_Down;
63 
64     if ( smooth )
65     {
66       bit->cols  = bit->width;
67       bit->size  = bit->rows * bit->width;
68     }
69     else
70     {
71       bit->cols  = ( bit->width + 7 ) / 8;    /* convert to # of bytes     */
72       bit->size  = bit->rows * bit->cols;     /* number of bytes in buffer */
73     }
74 
75     bit->bitmap = (void *) malloc( bit->size );
76     if ( !bit->bitmap )
77       Panic( "Not enough memory to allocate bitmap!\n" );
78   }
79 
80 
Done_Raster_Map(TT_Raster_Map * bit)81   static void  Done_Raster_Map( TT_Raster_Map *bit )
82   {
83     free( bit->bitmap );
84     bit->bitmap = NULL;
85   }
86 
87 
Clear_Raster_Map(TT_Raster_Map * bit)88   static void  Clear_Raster_Map( TT_Raster_Map*  bit )
89   {
90     memset( bit->bitmap, 0, bit->size );
91   }
92 
93 
Blit_Or(TT_Raster_Map * dst,TT_Raster_Map * src,int x_off,int y_off)94   static void  Blit_Or( TT_Raster_Map*  dst, TT_Raster_Map*  src,
95                         int  x_off, int  y_off )
96   {
97     int   x, y;
98     int   x1, x2, y1, y2;
99     char  *s, *d;
100 
101 
102     /* clipping */
103 
104     x1 = x_off < 0 ? -x_off : 0;
105     y1 = y_off < 0 ? -y_off : 0;
106 
107     x2 = (int)dst->cols - x_off;
108     if ( x2 > src->cols )
109       x2 = src->cols;
110 
111     y2 = (int)dst->rows - y_off;
112     if ( y2 > src->rows )
113       y2 = src->rows;
114 
115     if ( x1 >= x2 )
116       return;
117 
118     /* do the real work now */
119 
120     for ( y = y1; y < y2; ++y )
121     {
122       s = ( (char*)src->bitmap ) + y * src->cols + x1;
123       d = ( (char*)dst->bitmap ) + ( y + y_off ) * dst->cols + x1 + x_off;
124 
125       for ( x = x1; x < x2; ++x )
126         *d++ |= *s++;
127     }
128   }
129 
130 
Dump_Raster_Map(TT_Raster_Map * bit,FILE * file)131   static void  Dump_Raster_Map( TT_Raster_Map*  bit, FILE*  file )
132   {
133     /* kudos for this code snippet go to Norman Walsh */
134 
135     char*  bmap;
136     int    i;
137 
138 
139     bmap = (char *)bit->bitmap;
140 
141     if ( smooth )
142     {
143       fprintf( file, "P5\n%d %d\n4\n", pnm_width, pnm_height );
144       for ( i = bit->size - 1; i >= 0; --i )
145         bmap[i] = bmap[i] > 4 ? 0 : 4 - bmap[i];
146       for ( i = pnm_height; i > 0; --i, bmap += bit->cols )
147         fwrite( bmap, 1, pnm_width, file );
148     }
149     else
150     {
151       fprintf( file, "P4\n%d %d\n", pnm_width, pnm_height );
152       for ( i = pnm_height; i > 0; --i, bmap += bit->cols )
153         fwrite( bmap, 1, (pnm_width+7) / 8, file );
154     }
155 
156     fflush( file );
157   }
158 
159 
160   /* glyph management */
161 
Load_Glyphs(char * txt,int txtlen)162   static void  Load_Glyphs( char*  txt, int  txtlen )
163   {
164     unsigned short  i, n, code, load_flags;
165     unsigned short  num_glyphs = 0, no_cmap = 0;
166     unsigned short  platform, encoding;
167     TT_Error        error;
168     TT_CharMap      char_map;
169 
170 
171     /* First, look for a Unicode charmap */
172 
173     n = properties.num_CharMaps;
174 
175     for ( i = 0; i < n; i++ )
176     {
177       TT_Get_CharMap_ID( face, i, &platform, &encoding );
178       if ( (platform == 3 && encoding == 1 ) ||
179            (platform == 0 && encoding == 0 ) )
180       {
181         TT_Get_CharMap( face, i, &char_map );
182         break;
183       }
184     }
185 
186     if ( i == n )
187     {
188       TT_Face_Properties  properties;
189 
190 
191       TT_Get_Face_Properties( face, &properties );
192 
193       no_cmap = 1;
194       num_glyphs = properties.num_Glyphs;
195     }
196 
197 
198     /* Second, allocate the array */
199 
200     glyphs = (TT_Glyph*)malloc( 256 * sizeof ( TT_Glyph ) );
201     memset( glyphs, 0, 256 * sizeof ( TT_Glyph ) );
202 
203     /* Finally, load the glyphs you need */
204 
205     load_flags = TTLOAD_SCALE_GLYPH;
206     if ( hinted )
207       load_flags |= TTLOAD_HINT_GLYPH;
208 
209     for ( i = 0; i < txtlen; ++i )
210     {
211       unsigned char  j = txt[i];
212 
213 
214       if ( TT_VALID( glyphs[j] ) )
215         continue;
216 
217       if ( no_cmap )
218       {
219         code = (j - ' ' + 1) < 0 ? 0 : (j - ' ' + 1);
220         if ( code >= num_glyphs )
221           code = 0;
222       }
223       else
224         code = TT_Char_Index( char_map, j );
225 
226       (void)(
227         ( error = TT_New_Glyph( face, &glyphs[j] ) ) ||
228         ( error = TT_Load_Glyph( instance, glyphs[j], code, load_flags ) )
229       );
230 
231       if ( error )
232         Panic( "Cannot allocate and load glyph: error 0x%x.\n", error );
233     }
234   }
235 
236 
Done_Glyphs(void)237   static void  Done_Glyphs( void )
238   {
239     int  i;
240 
241 
242     if ( !glyphs )
243       return;
244 
245     for ( i = 0; i < 256; ++i )
246       TT_Done_Glyph( glyphs[i] );
247 
248     free( glyphs );
249 
250     glyphs = NULL;
251   }
252 
253 
254   /* face & instance management */
255 
Init_Face(const char * filename)256   static void  Init_Face( const char*  filename )
257   {
258     TT_Error  error;
259 
260 
261     /* load the typeface */
262 
263     error = TT_Open_Face( engine, filename, &face );
264     if ( error )
265     {
266       if ( error == TT_Err_Could_Not_Open_File )
267         Panic( "Could not find/open %s.\n", filename );
268       else
269         Panic( "Error while opening %s, error code = 0x%x.\n",
270                filename, error );
271     }
272 
273     TT_Get_Face_Properties( face, &properties );
274 
275     /* create and initialize instance */
276 
277     (void) (
278       ( error = TT_New_Instance( face, &instance ) ) ||
279       ( error = TT_Set_Instance_Resolutions( instance, dpi, dpi ) ) ||
280       ( error = TT_Set_Instance_PointSize( instance, ptsize ) )
281     );
282 
283     if ( error )
284       Panic( "Could not create and initialize instance: error 0x%x.\n",
285              error );
286   }
287 
288 
Done_Face(void)289   static void  Done_Face( void )
290   {
291     TT_Done_Instance( instance );
292     TT_Close_Face( face );
293   }
294 
295 
296   /* rasterization stuff */
297 
Init_Raster_Areas(const char * txt,int txtlen)298   static void  Init_Raster_Areas( const char*  txt, int  txtlen )
299   {
300     int                  i, upm, ascent, descent;
301     TT_Face_Properties   properties;
302     TT_Instance_Metrics  imetrics;
303     TT_Glyph_Metrics     gmetrics;
304 
305 
306     /* allocate the large bitmap */
307 
308     TT_Get_Face_Properties( face, &properties );
309     TT_Get_Instance_Metrics( instance, &imetrics );
310 
311     upm     = properties.header->Units_Per_EM;
312     ascent  = ( properties.horizontal->Ascender  * imetrics.y_ppem ) / upm;
313     descent = ( properties.horizontal->Descender * imetrics.y_ppem ) / upm;
314 
315     pnm_width   = 2 * border;
316     pnm_height  = 2 * border + ascent - descent;
317 
318     for ( i = 0; i < txtlen; ++i )
319     {
320       unsigned char  j = txt[i];
321 
322 
323       if ( !TT_VALID( glyphs[j] ) )
324         continue;
325 
326       TT_Get_Glyph_Metrics( glyphs[j], &gmetrics );
327       pnm_width += gmetrics.advance / 64;
328     }
329 
330     Init_Raster_Map( &bit, pnm_width, pnm_height );
331     Clear_Raster_Map( &bit );
332 
333     pnm_x_shift = border;
334     pnm_y_shift = border - descent;
335 
336     /* allocate the small bitmap if you need it */
337 
338     if ( smooth )
339       Init_Raster_Map( &small_bit, imetrics.x_ppem + 32, pnm_height );
340   }
341 
342 
Done_Raster_Areas(void)343   static void  Done_Raster_Areas( void )
344   {
345     Done_Raster_Map( &bit );
346     if ( smooth )
347       Done_Raster_Map( &small_bit );
348   }
349 
350 
Render_Glyph(TT_Glyph glyph,int x_off,int y_off,TT_Glyph_Metrics * gmetrics)351   static void  Render_Glyph( TT_Glyph  glyph,
352                              int  x_off, int  y_off,
353                              TT_Glyph_Metrics*  gmetrics )
354   {
355     if ( !smooth )
356       TT_Get_Glyph_Bitmap( glyph, &bit, x_off * 64L, y_off * 64L);
357     else
358     {
359       TT_F26Dot6  xmin, ymin, xmax, ymax;
360 
361 
362       /* grid-fit the bounding box */
363 
364       xmin =  gmetrics->bbox.xMin & -64;
365       ymin =  gmetrics->bbox.yMin & -64;
366       xmax = (gmetrics->bbox.xMax + 63) & -64;
367       ymax = (gmetrics->bbox.yMax + 63) & -64;
368 
369       /* now render the glyph in the small pixmap */
370       /* and blit-or the resulting small pixmap into the biggest one */
371 
372       Clear_Raster_Map( &small_bit );
373       TT_Get_Glyph_Pixmap( glyph, &small_bit, -xmin, -ymin );
374       Blit_Or( &bit, &small_bit, xmin/64 + x_off, -ymin/64 - y_off );
375     }
376   }
377 
378 
Render_All_Glyphs(char * txt,int txtlen)379   static void  Render_All_Glyphs( char*  txt, int  txtlen )
380   {
381     int               i;
382     TT_F26Dot6        x, y, adjx;
383     TT_Glyph_Metrics  gmetrics;
384 
385 
386     x = pnm_x_shift;
387     y = pnm_y_shift;
388 
389     for ( i = 0; i < txtlen; i++ )
390     {
391       unsigned char  j = txt[i];
392 
393       if ( !TT_VALID( glyphs[j] ) )
394         continue;
395 
396       TT_Get_Glyph_Metrics( glyphs[j], &gmetrics );
397 
398       adjx = x;                                         /* ??? lsb */
399       Render_Glyph( glyphs[j], adjx, y, &gmetrics );
400 
401       x += gmetrics.advance / 64;
402     }
403   }
404 
405 
usage(void)406   static void  usage( void )
407   {
408     printf( "\n" );
409     printf( "%s: simple text to image converter -- part of the FreeType project\n", PROGNAME );
410     printf( "\n" );
411     printf( "Usage: %s [options below] filename [string]\n", PROGNAME );
412     printf( "\n" );
413     printf( "  -g     gray-level rendering (default: off)\n" );
414     printf( "  -h     hinting off (default: on)\n" );
415     printf( "  -r X   resolution X dpi (default: 96)\n" );
416     printf( "  -p X   pointsize X pt (default: 12)\n" );
417     printf( "  -b X   border X pixels wide (default: 0)\n" );
418     printf( "\n" );
419 
420     exit( EXIT_FAILURE );
421   }
422 
423 
main(int argc,char ** argv)424   int  main( int  argc, char**  argv )
425   {
426     int       option, txtlen;
427     char      *txt, *filename;
428     TT_Error  error;
429 
430 
431     /* Parse options */
432 
433     while ( 1 )
434     {
435       option = ft_getopt( argc, argv, "ghr:p:b:" );
436 
437       if ( option == -1 )
438         break;
439 
440       switch ( option )
441       {
442       case 'g':
443         smooth = 1;
444         break;
445       case 'h':
446         hinted = 0;
447         break;
448       case 'r':
449         dpi = atoi( ft_optarg );
450         break;
451       case 'p':
452         ptsize = atoi( ft_optarg );
453         break;
454       case 'b':
455         border = atoi( ft_optarg );
456         break;
457 
458       default:
459         usage();
460         break;
461       }
462     }
463 
464     argc -= ft_optind;
465     argv += ft_optind;
466 
467     if ( argc <= 0 || argc > 2 || dpi <= 0 || ptsize <= 0 || border < 0 )
468       usage();
469 
470     filename = argv[0];
471 
472     if ( argc > 1 )
473       txt = argv[1];
474     else
475       txt = "The quick brown fox jumps over the lazy dog";
476 
477     txtlen = strlen( txt );
478 
479     /* Initialize engine and other stuff */
480 
481     error = TT_Init_FreeType( &engine );
482     if ( error )
483       Panic( "Error while initializing engine, code = 0x%x.\n", error );
484 
485     Init_Face( filename );
486     Load_Glyphs( txt, txtlen );
487     Init_Raster_Areas( txt, txtlen );
488 
489     /* Do the real work now */
490 
491     Render_All_Glyphs( txt, txtlen );
492     Dump_Raster_Map( &bit, stdout );
493 
494     /* Clean up */
495 
496     Done_Raster_Areas();
497     Done_Glyphs();
498     Done_Face();
499 
500     /* That's all, folks! */
501 
502     TT_Done_FreeType( engine );
503 
504     exit( EXIT_SUCCESS );      /* for safety reasons */
505 
506     return 0;       /* never reached */
507 }
508 
509 
510 /* End */
511