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

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

save fonts information in cache file to avoid rescan (closes #30, #32, #35)

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