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