xref: /inferno-os/libfreetype/winfnt.c (revision 37da2899f40661e3e9631e497da8dc59b971cbd0)
1 /***************************************************************************/
2 /*                                                                         */
3 /*  winfnt.c                                                               */
4 /*                                                                         */
5 /*    FreeType font driver for Windows FNT/FON files                       */
6 /*                                                                         */
7 /*  Copyright 1996-2001, 2002 by                                           */
8 /*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
9 /*                                                                         */
10 /*  This file is part of the FreeType project, and may only be used,       */
11 /*  modified, and distributed under the terms of the FreeType project      */
12 /*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
13 /*  this file you indicate that you have read the license and              */
14 /*  understand and accept it fully.                                        */
15 /*                                                                         */
16 /***************************************************************************/
17 
18 
19 #include <ft2build.h>
20 #include FT_INTERNAL_DEBUG_H
21 #include FT_INTERNAL_STREAM_H
22 #include FT_INTERNAL_OBJECTS_H
23 #include FT_INTERNAL_FNT_TYPES_H
24 
25 #include "winfnt.h"
26 
27 #include "fnterrs.h"
28 
29 
30   /*************************************************************************/
31   /*                                                                       */
32   /* The macro FT_COMPONENT is used in trace mode.  It is an implicit      */
33   /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log  */
34   /* messages during execution.                                            */
35   /*                                                                       */
36 #undef  FT_COMPONENT
37 #define FT_COMPONENT  trace_winfnt
38 
39 
40   static
41   const FT_Frame_Field  winmz_header_fields[] =
42   {
43 #undef  FT_STRUCTURE
44 #define FT_STRUCTURE  WinMZ_HeaderRec
45 
46     FT_FRAME_START( 64 ),
47       FT_FRAME_USHORT_LE ( magic ),
48       FT_FRAME_SKIP_BYTES( 29 * 2 ),
49       FT_FRAME_ULONG_LE  ( lfanew ),
50     FT_FRAME_END
51   };
52 
53   static
54   const FT_Frame_Field  winne_header_fields[] =
55   {
56 #undef  FT_STRUCTURE
57 #define FT_STRUCTURE  WinNE_HeaderRec
58 
59     FT_FRAME_START( 40 ),
60       FT_FRAME_USHORT_LE ( magic ),
61       FT_FRAME_SKIP_BYTES( 34 ),
62       FT_FRAME_USHORT_LE ( resource_tab_offset ),
63       FT_FRAME_USHORT_LE ( rname_tab_offset ),
64     FT_FRAME_END
65   };
66 
67   static
68   const FT_Frame_Field  winfnt_header_fields[] =
69   {
70 #undef  FT_STRUCTURE
71 #define FT_STRUCTURE  WinFNT_HeaderRec
72 
73     FT_FRAME_START( 134 ),
74       FT_FRAME_USHORT_LE( version ),
75       FT_FRAME_ULONG_LE ( file_size ),
76       FT_FRAME_BYTES    ( copyright, 60 ),
77       FT_FRAME_USHORT_LE( file_type ),
78       FT_FRAME_USHORT_LE( nominal_point_size ),
79       FT_FRAME_USHORT_LE( vertical_resolution ),
80       FT_FRAME_USHORT_LE( horizontal_resolution ),
81       FT_FRAME_USHORT_LE( ascent ),
82       FT_FRAME_USHORT_LE( internal_leading ),
83       FT_FRAME_USHORT_LE( external_leading ),
84       FT_FRAME_BYTE     ( italic ),
85       FT_FRAME_BYTE     ( underline ),
86       FT_FRAME_BYTE     ( strike_out ),
87       FT_FRAME_USHORT_LE( weight ),
88       FT_FRAME_BYTE     ( charset ),
89       FT_FRAME_USHORT_LE( pixel_width ),
90       FT_FRAME_USHORT_LE( pixel_height ),
91       FT_FRAME_BYTE     ( pitch_and_family ),
92       FT_FRAME_USHORT_LE( avg_width ),
93       FT_FRAME_USHORT_LE( max_width ),
94       FT_FRAME_BYTE     ( first_char ),
95       FT_FRAME_BYTE     ( last_char ),
96       FT_FRAME_BYTE     ( default_char ),
97       FT_FRAME_BYTE     ( break_char ),
98       FT_FRAME_USHORT_LE( bytes_per_row ),
99       FT_FRAME_ULONG_LE ( device_offset ),
100       FT_FRAME_ULONG_LE ( face_name_offset ),
101       FT_FRAME_ULONG_LE ( bits_pointer ),
102       FT_FRAME_ULONG_LE ( bits_offset ),
103       FT_FRAME_BYTE     ( reserved ),
104       FT_FRAME_ULONG_LE ( flags ),
105       FT_FRAME_USHORT_LE( A_space ),
106       FT_FRAME_USHORT_LE( B_space ),
107       FT_FRAME_USHORT_LE( C_space ),
108       FT_FRAME_USHORT_LE( color_table_offset ),
109       FT_FRAME_BYTES    ( reserved, 4 ),
110     FT_FRAME_END
111   };
112 
113 
114   static void
fnt_font_done(FNT_Font font,FT_Stream stream)115   fnt_font_done( FNT_Font   font,
116                  FT_Stream  stream )
117   {
118     if ( font->fnt_frame )
119       FT_FRAME_RELEASE( font->fnt_frame );
120 
121     font->fnt_size  = 0;
122     font->fnt_frame = 0;
123   }
124 
125 
126   static FT_Error
fnt_font_load(FNT_Font font,FT_Stream stream)127   fnt_font_load( FNT_Font   font,
128                  FT_Stream  stream )
129   {
130     FT_Error       error;
131     WinFNT_Header  header = &font->header;
132 
133 
134     /* first of all, read the FNT header */
135     if ( FT_STREAM_SEEK( font->offset )                   ||
136          FT_STREAM_READ_FIELDS( winfnt_header_fields, header ) )
137       goto Exit;
138 
139     /* check header */
140     if ( header->version != 0x200 &&
141          header->version != 0x300 )
142     {
143       FT_TRACE2(( "[not a valid FNT file]\n" ));
144       error = FNT_Err_Unknown_File_Format;
145       goto Exit;
146     }
147 
148     if ( header->file_type & 1 )
149     {
150       FT_TRACE2(( "[can't handle vector FNT fonts]\n" ));
151       error = FNT_Err_Unknown_File_Format;
152       goto Exit;
153     }
154 
155     /* small fixup -- some fonts have the `pixel_width' field set to 0 */
156     if ( header->pixel_width == 0 )
157       header->pixel_width = header->pixel_height;
158 
159     /* this is a FNT file/table, we now extract its frame */
160     if ( FT_STREAM_SEEK( font->offset )                         ||
161          FT_FRAME_EXTRACT( header->file_size, font->fnt_frame ) )
162       goto Exit;
163 
164   Exit:
165     return error;
166   }
167 
168 
169   static void
fnt_face_done_fonts(FNT_Face face)170   fnt_face_done_fonts( FNT_Face  face )
171   {
172     FT_Memory  memory = FT_FACE( face )->memory;
173     FT_Stream  stream = FT_FACE( face )->stream;
174     FNT_Font   cur    = face->fonts;
175     FNT_Font   limit  = cur + face->num_fonts;
176 
177 
178     for ( ; cur < limit; cur++ )
179       fnt_font_done( cur, stream );
180 
181     FT_FREE( face->fonts );
182     face->num_fonts = 0;
183   }
184 
185 
186   static FT_Error
fnt_face_get_dll_fonts(FNT_Face face)187   fnt_face_get_dll_fonts( FNT_Face  face )
188   {
189     FT_Error         error;
190     FT_Stream        stream = FT_FACE( face )->stream;
191     FT_Memory        memory = FT_FACE( face )->memory;
192     WinMZ_HeaderRec  mz_header;
193 
194 
195     face->fonts     = 0;
196     face->num_fonts = 0;
197 
198     /* does it begin with a MZ header? */
199     if ( FT_STREAM_SEEK( 0 )                                 ||
200          FT_STREAM_READ_FIELDS( winmz_header_fields, &mz_header ) )
201       goto Exit;
202 
203     error = FNT_Err_Unknown_File_Format;
204     if ( mz_header.magic == WINFNT_MZ_MAGIC )
205     {
206       /* yes, now look for a NE header in the file */
207       WinNE_HeaderRec  ne_header;
208 
209 
210       if ( FT_STREAM_SEEK( mz_header.lfanew )                  ||
211            FT_STREAM_READ_FIELDS( winne_header_fields, &ne_header ) )
212         goto Exit;
213 
214       error = FNT_Err_Unknown_File_Format;
215       if ( ne_header.magic == WINFNT_NE_MAGIC )
216       {
217         /* good, now look in the resource table for each FNT resource */
218         FT_ULong   res_offset = mz_header.lfanew +
219                                 ne_header.resource_tab_offset;
220 
221         FT_UShort  size_shift;
222         FT_UShort  font_count  = 0;
223         FT_ULong   font_offset = 0;
224 
225 
226         if ( FT_STREAM_SEEK( res_offset ) ||
227              FT_FRAME_ENTER( ne_header.rname_tab_offset -
228                              ne_header.resource_tab_offset ) )
229           goto Exit;
230 
231         size_shift = FT_GET_USHORT_LE();
232 
233         for (;;)
234         {
235           FT_UShort  type_id, count;
236 
237 
238           type_id = FT_GET_USHORT_LE();
239           if ( !type_id )
240             break;
241 
242           count = FT_GET_USHORT_LE();
243 
244           if ( type_id == 0x8008 )
245           {
246             font_count  = count;
247             font_offset = (FT_ULong)( FT_STREAM_POS() + 4 +
248                                       ( stream->cursor - stream->limit ) );
249             break;
250           }
251 
252           stream->cursor += 4 + count * 12;
253         }
254         FT_FRAME_EXIT();
255 
256         if ( !font_count || !font_offset )
257         {
258           FT_TRACE2(( "this file doesn't contain any FNT resources!\n" ));
259           error = FNT_Err_Unknown_File_Format;
260           goto Exit;
261         }
262 
263         if ( FT_STREAM_SEEK( font_offset )           ||
264              FT_NEW_ARRAY( face->fonts, font_count ) )
265           goto Exit;
266 
267         face->num_fonts = font_count;
268 
269         if ( FT_FRAME_ENTER( (FT_Long)font_count * 12 ) )
270           goto Exit;
271 
272         /* now read the offset and position of each FNT font */
273         {
274           FNT_Font  cur   = face->fonts;
275           FNT_Font  limit = cur + font_count;
276 
277 
278           for ( ; cur < limit; cur++ )
279           {
280             cur->offset     = (FT_ULong)FT_GET_USHORT_LE() << size_shift;
281             cur->fnt_size   = (FT_ULong)FT_GET_USHORT_LE() << size_shift;
282             cur->size_shift = size_shift;
283             stream->cursor += 8;
284           }
285         }
286         FT_FRAME_EXIT();
287 
288         /* finally, try to load each font there */
289         {
290           FNT_Font  cur   = face->fonts;
291           FNT_Font  limit = cur + font_count;
292 
293 
294           for ( ; cur < limit; cur++ )
295           {
296             error = fnt_font_load( cur, stream );
297             if ( error )
298               goto Fail;
299           }
300         }
301       }
302     }
303 
304   Fail:
305     if ( error )
306       fnt_face_done_fonts( face );
307 
308   Exit:
309     return error;
310   }
311 
312 
313 
314   typedef struct  FNT_CMapRec_
315   {
316     FT_CMapRec  cmap;
317     FT_UInt32   first;
318     FT_UInt32   count;
319 
320   } FNT_CMapRec, *FNT_CMap;
321 
322 
323   static FT_Error
fnt_cmap_init(FNT_CMap cmap)324   fnt_cmap_init( FNT_CMap  cmap )
325   {
326     FNT_Face  face = (FNT_Face)FT_CMAP_FACE( cmap );
327     FNT_Font  font = face->fonts;
328 
329 
330     cmap->first = (FT_UInt32)  font->header.first_char;
331     cmap->count = (FT_UInt32)( font->header.last_char - cmap->first + 1 );
332 
333     return 0;
334   }
335 
336 
337   static FT_UInt
fnt_cmap_char_index(FNT_CMap cmap,FT_UInt32 char_code)338   fnt_cmap_char_index( FNT_CMap   cmap,
339                        FT_UInt32  char_code )
340   {
341     FT_UInt  gindex = 0;
342 
343 
344     char_code -= cmap->first;
345     if ( char_code < cmap->count )
346       gindex = char_code + 1;
347 
348     return gindex;
349   }
350 
351 
352   static FT_UInt
fnt_cmap_char_next(FNT_CMap cmap,FT_UInt32 * pchar_code)353   fnt_cmap_char_next( FNT_CMap    cmap,
354                       FT_UInt32  *pchar_code )
355   {
356     FT_UInt    gindex = 0;
357     FT_UInt32  result = 0;
358     FT_UInt32  char_code = *pchar_code + 1;
359 
360 
361     if ( char_code <= cmap->first )
362     {
363       result = cmap->first;
364       gindex = 1;
365     }
366     else
367     {
368       char_code -= cmap->first;
369       if ( char_code < cmap->count )
370       {
371         result = cmap->first + char_code;
372         gindex = char_code + 1;
373       }
374     }
375 
376     *pchar_code = result;
377     return gindex;
378   }
379 
380 
381   static FT_CMap_ClassRec  fnt_cmap_class_rec =
382   {
383     sizeof ( FNT_CMapRec ),
384 
385     (FT_CMap_InitFunc)     fnt_cmap_init,
386     (FT_CMap_DoneFunc)     NULL,
387     (FT_CMap_CharIndexFunc)fnt_cmap_char_index,
388     (FT_CMap_CharNextFunc) fnt_cmap_char_next
389   };
390 
391   static FT_CMap_Class  fnt_cmap_class = &fnt_cmap_class_rec;
392 
393 
394 
395   static void
FNT_Face_Done(FNT_Face face)396   FNT_Face_Done( FNT_Face  face )
397   {
398     FT_Memory  memory = FT_FACE_MEMORY( face );
399 
400 
401     fnt_face_done_fonts( face );
402 
403     FT_FREE( face->root.available_sizes );
404     face->root.num_fixed_sizes = 0;
405   }
406 
407 
408   static FT_Error
FNT_Face_Init(FT_Stream stream,FNT_Face face,FT_Int face_index,FT_Int num_params,FT_Parameter * params)409   FNT_Face_Init( FT_Stream      stream,
410                  FNT_Face       face,
411                  FT_Int         face_index,
412                  FT_Int         num_params,
413                  FT_Parameter*  params )
414   {
415     FT_Error   error;
416     FT_Memory  memory = FT_FACE_MEMORY( face );
417 
418     FT_UNUSED( num_params );
419     FT_UNUSED( params );
420     FT_UNUSED( face_index );
421 
422 
423     /* try to load several fonts from a DLL */
424     error = fnt_face_get_dll_fonts( face );
425     if ( error )
426     {
427       /* this didn't work, now try to load a single FNT font */
428       FNT_Font  font;
429 
430 
431       if ( FT_NEW( face->fonts ) )
432         goto Exit;
433 
434       face->num_fonts = 1;
435       font            = face->fonts;
436 
437       font->offset   = 0;
438       font->fnt_size = stream->size;
439 
440       error = fnt_font_load( font, stream );
441       if ( error )
442         goto Fail;
443     }
444 
445     /* all right, one or more fonts were loaded; we now need to */
446     /* fill the root FT_Face fields with relevant information   */
447     {
448       FT_Face   root  = FT_FACE( face );
449       FNT_Font  fonts = face->fonts;
450       FNT_Font  limit = fonts + face->num_fonts;
451       FNT_Font  cur;
452 
453 
454       root->num_faces  = 1;
455       root->face_flags = FT_FACE_FLAG_FIXED_SIZES |
456                          FT_FACE_FLAG_HORIZONTAL;
457 
458       if ( fonts->header.avg_width == fonts->header.max_width )
459         root->face_flags |= FT_FACE_FLAG_FIXED_WIDTH;
460 
461       if ( fonts->header.italic )
462         root->style_flags |= FT_STYLE_FLAG_ITALIC;
463 
464       if ( fonts->header.weight >= 800 )
465         root->style_flags |= FT_STYLE_FLAG_BOLD;
466 
467       /* Setup the `fixed_sizes' array */
468       if ( FT_NEW_ARRAY( root->available_sizes, face->num_fonts ) )
469         goto Fail;
470 
471       root->num_fixed_sizes = face->num_fonts;
472 
473       {
474         FT_Bitmap_Size*  size = root->available_sizes;
475 
476 
477         for ( cur = fonts; cur < limit; cur++, size++ )
478         {
479           size->width  = cur->header.pixel_width;
480           size->height = cur->header.pixel_height;
481         }
482       }
483 
484       {
485         FT_CharMapRec  charmap;
486 
487         charmap.encoding    = FT_ENCODING_UNICODE;
488         charmap.platform_id = 3;
489         charmap.encoding_id = 1;
490         charmap.face        = root;
491 
492         error = FT_CMap_New( fnt_cmap_class,
493                              NULL,
494                              &charmap,
495                              NULL );
496         if ( error )
497           goto Fail;
498 
499         /* Select default charmap */
500         if ( root->num_charmaps )
501           root->charmap = root->charmaps[0];
502       }
503 
504       /* setup remaining flags */
505       root->num_glyphs = fonts->header.last_char -
506                          fonts->header.first_char + 1;
507 
508       root->family_name = (FT_String*)fonts->fnt_frame +
509                           fonts->header.face_name_offset;
510       root->style_name  = (char *)"Regular";
511 
512       if ( root->style_flags & FT_STYLE_FLAG_BOLD )
513       {
514         if ( root->style_flags & FT_STYLE_FLAG_ITALIC )
515           root->style_name = (char *)"Bold Italic";
516         else
517           root->style_name = (char *)"Bold";
518       }
519       else if ( root->style_flags & FT_STYLE_FLAG_ITALIC )
520         root->style_name = (char *)"Italic";
521     }
522 
523   Fail:
524     if ( error )
525       FNT_Face_Done( face );
526 
527   Exit:
528     return error;
529   }
530 
531 
532   static FT_Error
FNT_Size_Set_Pixels(FNT_Size size)533   FNT_Size_Set_Pixels( FNT_Size  size )
534   {
535     /* look up a font corresponding to the current pixel size */
536     FNT_Face  face  = (FNT_Face)FT_SIZE_FACE( size );
537     FNT_Font  cur   = face->fonts;
538     FNT_Font  limit = cur + face->num_fonts;
539 
540 
541     size->font = 0;
542     for ( ; cur < limit; cur++ )
543     {
544       /* we only compare the character height, as fonts used some strange */
545       /* values                                                           */
546       if ( cur->header.pixel_height == size->root.metrics.y_ppem )
547       {
548         size->font = cur;
549 
550         size->root.metrics.ascender  = cur->header.ascent * 64;
551         size->root.metrics.descender = ( cur->header.pixel_height -
552                                            cur->header.ascent ) * 64;
553         size->root.metrics.height    = cur->header.pixel_height * 64;
554         break;
555       }
556     }
557 
558     return ( size->font ? FNT_Err_Ok : FNT_Err_Invalid_Pixel_Size );
559   }
560 
561 
562   static FT_Error
FNT_Load_Glyph(FT_GlyphSlot slot,FNT_Size size,FT_UInt glyph_index,FT_Int32 load_flags)563   FNT_Load_Glyph( FT_GlyphSlot  slot,
564                   FNT_Size      size,
565                   FT_UInt       glyph_index,
566                   FT_Int32      load_flags )
567   {
568     FNT_Font    font  = size->font;
569     FT_Error    error = 0;
570     FT_Byte*    p;
571     FT_Int      len;
572     FT_Bitmap*  bitmap = &slot->bitmap;
573     FT_ULong    offset;
574     FT_Bool     new_format;
575 
576     FT_UNUSED( slot );
577     FT_UNUSED( load_flags );
578 
579 
580     if ( !font )
581     {
582       error = FNT_Err_Invalid_Argument;
583       goto Exit;
584     }
585 
586     if ( glyph_index > 0 )
587       glyph_index--;
588     else
589       glyph_index = font->header.default_char - font->header.first_char;
590 
591     new_format = FT_BOOL( font->header.version == 0x300 );
592     len        = new_format ? 6 : 4;
593 
594     /* jump to glyph entry */
595     p = font->fnt_frame + 118 + len * glyph_index;
596 
597     bitmap->width = FT_NEXT_SHORT_LE( p );
598 
599     if ( new_format )
600       offset = FT_NEXT_ULONG_LE( p );
601     else
602       offset = FT_NEXT_USHORT_LE( p );
603 
604     /* jump to glyph data */
605     p = font->fnt_frame + /* font->header.bits_offset */ + offset;
606 
607     /* allocate and build bitmap */
608     {
609       FT_Memory  memory = FT_FACE_MEMORY( slot->face );
610       FT_Int     pitch  = ( bitmap->width + 7 ) >> 3;
611       FT_Byte*   column;
612       FT_Byte*   write;
613 
614 
615       bitmap->pitch      = pitch;
616       bitmap->rows       = font->header.pixel_height;
617       bitmap->pixel_mode = FT_PIXEL_MODE_MONO;
618 
619       if ( FT_ALLOC( bitmap->buffer, pitch * bitmap->rows ) )
620         goto Exit;
621 
622       column = (FT_Byte*)bitmap->buffer;
623 
624       for ( ; pitch > 0; pitch--, column++ )
625       {
626         FT_Byte*  limit = p + bitmap->rows;
627 
628 
629         for ( write = column; p < limit; p++, write += bitmap->pitch )
630           write[0] = p[0];
631       }
632     }
633 
634     slot->flags       = FT_GLYPH_OWN_BITMAP;
635     slot->bitmap_left = 0;
636     slot->bitmap_top  = font->header.ascent;
637     slot->format      = FT_GLYPH_FORMAT_BITMAP;
638 
639     /* now set up metrics */
640     slot->metrics.horiAdvance  = bitmap->width << 6;
641     slot->metrics.horiBearingX = 0;
642     slot->metrics.horiBearingY = slot->bitmap_top << 6;
643 
644     slot->linearHoriAdvance    = (FT_Fixed)bitmap->width << 16;
645     slot->format               = FT_GLYPH_FORMAT_BITMAP;
646 
647   Exit:
648     return error;
649   }
650 
651 
652   FT_CALLBACK_TABLE_DEF
653   const FT_Driver_ClassRec  winfnt_driver_class =
654   {
655     {
656       ft_module_font_driver,
657       sizeof ( FT_DriverRec ),
658 
659       "winfonts",
660       0x10000L,
661       0x20000L,
662 
663       0,
664 
665       (FT_Module_Constructor)0,
666       (FT_Module_Destructor) 0,
667       (FT_Module_Requester)  0
668     },
669 
670     sizeof( FNT_FaceRec ),
671     sizeof( FNT_SizeRec ),
672     sizeof( FT_GlyphSlotRec ),
673 
674     (FT_Face_InitFunc)        FNT_Face_Init,
675     (FT_Face_DoneFunc)        FNT_Face_Done,
676     (FT_Size_InitFunc)        0,
677     (FT_Size_DoneFunc)        0,
678     (FT_Slot_InitFunc)        0,
679     (FT_Slot_DoneFunc)        0,
680 
681     (FT_Size_ResetPointsFunc) FNT_Size_Set_Pixels,
682     (FT_Size_ResetPixelsFunc) FNT_Size_Set_Pixels,
683     (FT_Slot_LoadFunc)        FNT_Load_Glyph,
684 
685     (FT_Face_GetKerningFunc)  0,
686     (FT_Face_AttachFunc)      0,
687     (FT_Face_GetAdvancesFunc) 0
688   };
689 
690 
691 /* END */
692