source: trunk/poppler/fc-emulate-os2/fontconfig/fontconfig.cpp @ 116

Last change on this file since 116 was 116, checked in by Eugene Romanenko, 15 years ago

Fixed crash during fonts enumeration on font files which FreeType? doesn't understand (closes #83)

File size: 13.5 KB
Line 
1/*
2 * Copyright (c) 2006, Eugene Romanenko, netlabs.org
3 *
4 *----------------------------------------------------------------------
5 * This file is part of poppler plugin for Lucide (lupoppler).
6 *
7 *  lupoppler is free software; you can redistribute it and/or modify
8 *  it under the terms of the GNU General Public License as published by
9 *  the Free Software Foundation; either version 2 of the License, or
10 *  (at your option) any later version.
11 *
12 *  lupoppler is distributed in the hope that it will be useful,
13 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 *  GNU General Public License for more details.
16 *----------------------------------------------------------------------
17 */
18
19// This file is fontconfig replacement which emulates functions
20// used by poppler.
21
22#define INCL_DOS
23#define INCL_WIN
24#include <os2.h>
25
26#include <map>
27#include <string>
28using namespace std;
29
30#include <stdio.h>
31#include <dos.h>
32#include <string.h>
33
34#include "fontconfig.h"
35
36#include <ft2build.h>
37#include FT_FREETYPE_H
38
39
40static char *newstrdup( const char *s )
41{
42    if ( s == NULL ) {
43        return NULL;
44    }
45    char *temp = new char[ strlen( s ) + 1 ];
46    strcpy( temp, s );
47    return temp;
48}
49
50static string rtrim( const char *p )
51{
52    size_t len = strlen( p );
53    char *temp = new char[ len + 1 ];
54    strcpy( temp, p );
55
56    for ( int i = len - 1; i >= 0; i-- )
57    {
58        if ( isspace( temp[ i ] ) ) {
59            temp[ i ] = 0;
60        }
61        else {
62            break;
63        }
64    }
65    string r = temp;
66    delete temp;
67    return r;
68}
69
70// er_tokens - utility class
71class er_tokens
72{
73    private:
74        char   separator;
75        size_t len;
76        char   *str;
77        char   *tmp;
78        size_t curoffs;
79    public:
80        er_tokens( const char *s, char sep );
81        ~er_tokens();
82        char *nexttoken();
83};
84
85er_tokens::er_tokens( const char *s, char sep )
86{
87    separator = sep;
88    len = strlen( s );
89    str = new char[ len + 1 ];
90    strcpy( str , s );
91    tmp = new char[ len + 1 ];
92    curoffs = 0;
93}
94
95er_tokens::~er_tokens()
96{
97    delete str;
98    delete tmp;
99}
100
101char *er_tokens::nexttoken()
102{
103    if ( curoffs >= len ) {
104        return NULL;
105    }
106
107    memset( tmp, 0, len + 1 );
108
109    if ( str[ curoffs ] == separator ) {
110        curoffs++;
111    }
112    else
113    {
114        char *t1 = str + curoffs;
115        char *t2 = tmp;
116
117        while ( ( *t1 != separator ) && ( *t1 != 0 ) ) {
118            *t2++ = *t1++;
119            curoffs++;
120        }
121        curoffs++;
122    }
123
124    return tmp;
125}
126
127struct FcfRecord
128{
129    char filename[ _MAX_PATH ];
130    char family[ 64 ];
131    char style[ 64 ];
132    long size;
133    long modified;
134};
135
136static map<string,string>    *fontmap = NULL;   // font name / font filename
137static map<string,FcfRecord> *fcfmap = NULL;    // font filename / FcfRecord
138static bool fcfChanged = false;
139static FT_Library ftlib;
140
141
142struct _FcPattern
143{
144    char *family;
145    int slant;
146    int weight;
147    int width;
148    int spacing;
149    char *lang;
150
151    char *filename;
152};
153
154
155FcConfig *FcConfigGetCurrent()
156{
157    // STUB
158    return NULL;
159}
160
161FcBool FcConfigSubstitute( FcConfig *config, FcPattern *p, FcMatchKind kind )
162{
163    // STUB
164    return FcTrue;
165}
166
167
168void FcDefaultSubstitute( FcPattern *pattern )
169{
170    // STUB
171}
172
173void FcFontSetDestroy( FcFontSet *s )
174{
175    for ( int i = 0; i < s->nfont; i++ ) {
176        FcPatternDestroy( s->fonts[i] );
177    }
178    delete s->fonts;
179    delete s;
180}
181
182static void ftLoad( char *fn )
183{
184    //printf( "Loading font %s\n", fn );
185    int l = strlen( fn );
186    if ( l < 5 ) {
187        return;
188    }
189
190    if ( ( stricmp( fn + ( l - 4 ), ".OFM" ) != 0 ) &&
191         ( stricmp( fn + ( l - 4 ), ".PFB" ) != 0 ) &&
192         ( stricmp( fn + ( l - 4 ), ".PFA" ) != 0 ) &&
193         ( stricmp( fn + ( l - 4 ), ".TTF" ) != 0 ) &&
194         ( stricmp( fn + ( l - 4 ), ".TTC" ) != 0 ) )
195    {
196        return;
197    }
198
199    if ( stricmp( fn + ( l - 4 ), ".OFM" ) == 0 ) {
200        fn[ l - 3 ] = 'P';
201        fn[ l - 1 ] = 'B';
202    }
203
204    string familyName = "";
205    string styleName = "";
206
207    bool needread = false;
208    struct stat st = {0};
209    stat( fn, &st );
210
211    if ( fcfmap->find( fn ) == fcfmap->end() ) {
212        needread = true;
213    }
214    else {
215        FcfRecord r = (*fcfmap)[ fn ];
216        if ( ( r.size == st.st_size ) && ( r.modified == st.st_mtime ) ) {
217            familyName = rtrim( r.family );
218            styleName = rtrim( r.style );
219        }
220        else {
221            needread = true;
222        }
223    }
224
225    if ( needread )
226    {
227        //printf( "read: %s\n", fn );
228        fcfChanged = true;
229
230        FT_Face ftface;
231        if ( FT_New_Face( ftlib, fn, 0, &ftface ) ) {
232            return;
233        }
234        //printf( "readed, family_name: %s   style_name: %s\n", ftface->family_name, ftface->style_name );
235
236        if ( ( ftface->family_name == NULL ) || ( ftface->style_name == NULL ) ) {
237            printf( "Broken font file: %s\n", fn );
238            FT_Done_Face( ftface );
239            return;
240        }
241
242        familyName = rtrim( ftface->family_name );
243        styleName = rtrim( ftface->style_name );
244
245        FT_Done_Face( ftface );
246    }
247
248    string key = familyName;
249    if ( stricmp( styleName.c_str(), "regular" ) != 0 ) {
250        key += ' ';
251        key += styleName;
252    }
253
254    char *tmpkey = newstrdup( key.c_str() );
255    strlwr( tmpkey );
256    key = tmpkey;
257    delete tmpkey;
258
259    (*fontmap)[ key ] = fn;
260    //printf( "%s: %s\n", fn, key.c_str() );
261
262    FcfRecord fcfr = {0};
263    strcpy( fcfr.filename, fn );
264    strncpy( fcfr.family, familyName.c_str(), sizeof( fcfr.family ) - 1 );
265    strncpy( fcfr.style, styleName.c_str(), sizeof( fcfr.style ) - 1 );
266    fcfr.size = st.st_size;
267    fcfr.modified = st.st_mtime;
268
269    (*fcfmap)[ fn ] = fcfr;
270    //printf( "Done loading font %s\n", fn );
271}
272
273
274static string getFcfName()
275{
276    char fullpath[ _MAX_PATH ];
277    char drive[ _MAX_DRIVE ];
278    char dir[ _MAX_DIR ];
279    char fname[ _MAX_FNAME ];
280    _splitpath( __argv[0], drive, dir, fname, NULL );
281    strlwr( fname );
282    _makepath( fullpath, drive, dir, fname, ".fcf" );
283    return fullpath;
284}
285
286/*static void loadDir( string path )
287{
288    string pathnam = path + "\\*";
289
290    struct find_t ffblk;
291    unsigned done = _dos_findfirst( pathnam.c_str(), _A_RDONLY | _A_NORMAL, &ffblk );
292    while ( done == 0 )
293    {
294        string fname = path + '\\' + ffblk.name;
295        char *fn = newstrdup( fname.c_str() );
296        ftLoad( fn );
297        delete fn;
298        done = _dos_findnext( &ffblk );
299    }
300    _dos_findclose( &ffblk );
301} */
302
303static void saveFcf( const char *fn )
304{
305    FILE *f = NULL;
306
307    if ( ( f = fopen( fn, "w" ) ) == NULL ) {
308        return;
309    }
310
311    fputs( "# Font configuration file.\n" \
312           "# Auto-generated file, do not edit!\n", f );
313
314    map<string,FcfRecord>::const_iterator iter;
315    for ( iter = fcfmap->begin(); iter != fcfmap->end(); iter++ )
316    {
317        FcfRecord r = (*iter).second;
318        fprintf( f, "|%s|%s|%s|%ld|%ld|\n", r.filename, r.family, r.style,
319                 r.size, r.modified );
320    }
321    fclose( f );
322}
323
324#define READ_BUF    4096
325static void readFcf( const char *fn )
326{
327    if ( access( fn, 0 ) != 0 ) {
328        return;
329    }
330
331    FILE *f = NULL;
332
333    if ( ( f = fopen( fn, "r" ) ) == NULL ) {
334        return;
335    }
336
337    char *buf = new char[ READ_BUF ];
338
339    while( fgets( buf, READ_BUF, f ) != NULL )
340    {
341        if ( buf[0] == '|' )
342        {
343            FcfRecord r = {0};
344
345            er_tokens tkn( buf + 1, '|' );
346            strncpy( r.filename, tkn.nexttoken(), sizeof( r.filename ) - 1 );
347            strncpy( r.family, tkn.nexttoken(), sizeof( r.family ) - 1 );
348            strncpy( r.style, tkn.nexttoken(), sizeof( r.style ) - 1 );
349            r.size = atol( tkn.nexttoken() );
350            r.modified = atol( tkn.nexttoken() );
351
352            (*fcfmap)[ r.filename ] = r;
353        }
354    }
355
356    fclose( f );
357}
358
359#define FLIST_SIZE  (1024*64)
360
361
362FcBool FcInit()
363{
364    if ( fontmap != NULL ) {
365        return FcTrue;
366    }
367
368    if ( FT_Init_FreeType( &ftlib ) ) {
369        return FcFalse;
370    }
371
372    fcfChanged = false;
373
374    string fcfname = getFcfName();
375
376    fontmap = new map<string,string>;
377    fcfmap  = new map<string,FcfRecord>;
378
379    readFcf( fcfname.c_str() );
380
381    // enum installed fonts
382    ULONG uldrv = 0;
383    DosQuerySysInfo( QSV_BOOT_DRIVE, QSV_BOOT_DRIVE, &uldrv, sizeof( ULONG ) );
384    char drv = (char)( uldrv + '@' );
385
386    const char *pmfonts = "PM_Fonts";
387    char *fnames = new char[ FLIST_SIZE ];
388    memset( fnames, 0, FLIST_SIZE );
389    PrfQueryProfileString( HINI_USER, pmfonts, NULL, NULL, fnames, FLIST_SIZE );
390
391    char *fn1 = new char[ CCHMAXPATH ];
392    char *fn2 = new char[ CCHMAXPATH ];
393    int noffset = 0;
394    while ( fnames[ noffset ] != 0 )
395    {
396        const char *fname = fnames + noffset;
397
398        PrfQueryProfileString( HINI_USER, pmfonts, fname, "", fn1, CCHMAXPATH );
399
400        if ( fn1[ 0 ] == '\\' ) {
401            fn2[ 0 ] = drv;
402            fn2[ 1 ] = ':';
403            fn2[ 2 ] = 0;
404            strcat( fn2, fn1 );
405        }
406        else {
407            strcpy( fn2, fn1 );
408        }
409
410        ftLoad( fn2 );
411
412        noffset += ( strlen( fname ) + 1 );
413    }
414    delete fn1;
415    delete fn2;
416    delete fnames;
417
418    // TODO: load some fonts dir?
419    //loadDir( "Fonts" );
420
421    if ( fcfChanged ) {
422        saveFcf( fcfname.c_str() );
423    }
424
425    return FcTrue;
426}
427
428
429//
430// Assume fonts "Times New Roman", "Helvetica" and "Courier" always
431// present on any system (see GPI Guide and Reference, section
432// "Fonts" -> "About Fonts" -> "PM-Supplied Fonts").
433//
434#define DEFAULT_SERIF_FONT          "times new roman"
435#define DEFAULT_SANSSERIF_FONT      "helvetica"
436#define DEFAULT_MONOSPACED_FONT     "courier"
437
438static bool isSansserif( const char *family )
439{
440    return ( ( strstr( family, "swiss" ) != NULL ) ||
441             ( strstr( family, "sans" ) != NULL ) ||
442             ( strcmp( family, "arial" ) == 0 ) ||
443             ( strcmp( family, "tahoma" ) == 0 ) ||
444             ( strcmp( family, "verdana" ) == 0 ) );
445}
446
447static string buildFontKey( FcPattern *p, bool useDefaultFonts )
448{
449    string key = p->family;
450
451    if ( useDefaultFonts )
452    {
453        if ( p->spacing == FC_MONO ) {
454            key = DEFAULT_MONOSPACED_FONT;
455        }
456        else
457        {
458            if ( isSansserif( p->family ) ) {
459                key = DEFAULT_SANSSERIF_FONT;
460            }
461            else {
462                key = DEFAULT_SERIF_FONT;
463            }
464        }
465    }
466    else
467    {
468        // use 'Symbol Set' (SYMB.PFB) instead of 'Symbol'
469        if ( strcmp( p->family, "symbol" ) == 0 ) {
470            key = "symbol set";
471        }
472    }
473
474    if ( p->weight > FC_WEIGHT_NORMAL ) {
475        key += ' ';
476        key += "bold";
477    }
478
479    if ( p->slant != FC_SLANT_ROMAN ) {
480        key += ' ';
481        key += "italic";
482    }
483
484    return key;
485}
486
487FcFontSet *FcFontSort( FcConfig *config, FcPattern *p, FcBool trim,
488                       FcCharSet **csp, FcResult *result )
489{
490    FcPattern *pat = new FcPattern;
491    pat->family   = newstrdup( p->family );
492    pat->slant    = p->slant;
493    pat->weight   = p->weight;
494    pat->width    = p->width;
495    pat->spacing  = p->spacing;
496    pat->lang     = newstrdup( p->lang );
497    pat->filename = NULL;
498
499    string key = buildFontKey( pat, false );
500
501    if ( fontmap->find( key ) == fontmap->end() ) {
502        key = buildFontKey( pat, true );
503        pat->filename = newstrdup( (*fontmap)[ key ].c_str() );
504    }
505    else {
506        pat->filename = newstrdup( (*fontmap)[ key ].c_str() );
507    }
508
509//printf( "MATCHED STYLE: %s, FILENAME: %s\n", key.c_str(), pat->filename );
510
511    FcFontSet *fs = new FcFontSet;
512    fs->nfont = 1;
513    fs->sfont = 1;
514    fs->fonts = new FcPattern *[ 1 ];
515    fs->fonts[ 0 ] = pat;
516
517    return fs;
518}
519
520void FcFontSetSortDestroy( FcFontSet *fs )
521{
522    // STUB
523}
524
525void FcPatternDestroy( FcPattern *p )
526{
527    delete p->family;
528    delete p->lang;
529    delete p->filename;
530}
531
532FcResult FcPatternGetInteger( const FcPattern *p, const char *object, int n, int *i )
533{
534    // STUB
535    *i = 0;
536    return FcResultMatch;
537}
538
539FcResult FcPatternGetString( const FcPattern *p, const char *object, int n, FcChar8 **s )
540{
541    if ( strcmp( object, FC_FILE ) == 0 )
542    {
543        *s = p->filename;
544        return FcResultMatch;
545    }
546    return FcResultNoMatch;
547}
548
549FcPattern *FcPatternBuild( void *,
550                    const char *fcFamily, FcType tFamily, const char *family,
551                    const char *fcSlant, FcType tSlant, int slant,
552                    const char *fcWeight, FcType tWeight, int weight,
553                    const char *fcWidth, FcType tWidth, int width,
554                    const char *fcSpacing, FcType tSpacing, int spacing,
555                    const char *fcLang, FcType tLang, const char *lang, void * )
556{
557//printf( "FAMILY: %s, SLANT: %d, WEIGHT: %d, WIDTH: %d, SPACING: %d, LANG: %s\n",
558//            family, slant, weight, width, spacing, lang );
559
560    FcPattern *p = new FcPattern;
561    p->family   = newstrdup( family );
562    strlwr( p->family );
563    p->slant    = slant;
564    p->weight   = weight;
565    p->width    = width;
566    p->spacing  = spacing;
567    p->lang     = newstrdup( lang );
568    p->filename = NULL;
569
570    return p;
571}
572
Note: See TracBrowser for help on using the repository browser.