1/*
2 * font.c
3 *
4 * map dvi fonts to X fonts
5 */
6#ifdef HAVE_CONFIG_H
7# include "config.h"
8#endif
9
10#include <X11/Xos.h>
11#include <X11/IntrinsicP.h>
12#include <X11/StringDefs.h>
13#include <stdio.h>
14#include <stdlib.h>
15#include <ctype.h>
16#include "DviP.h"
17#include "XFontName.h"
18
19static char *
20savestr(const char *s)
21{
22    size_t len;
23    char * n;
24
25    if (!s)
26        return NULL;
27    len = strlen(s) + 1;
28    n = XtMalloc (len);
29    if (n)
30        memcpy(n, s, len);
31    return n;
32}
33
34static DviFontList *
35LookupFontByPosition(DviWidget dw, int position)
36{
37    DviFontList *f;
38
39    for (f = dw->dvi.fonts; f; f = f->next) {
40        if (f->dvi_number == position)
41            break;
42    }
43    return f;
44}
45
46static DviFontSizeList *
47LookupFontSizeBySize(DviWidget dw, DviFontList *f, int size)
48{
49    DviFontSizeList *best = NULL;
50
51    if (f->scalable) {
52        char            fontNameString[2048];
53        XFontName       fontName;
54        unsigned int    fontNameAttributes;
55
56        for (best = f->sizes; best; best = best->next) {
57            if (best->size == size)
58                return best;
59        }
60        best = (DviFontSizeList *) XtMalloc(sizeof *best);
61        best->next = f->sizes;
62        best->size = size;
63        XParseFontName(f->x_name, &fontName, &fontNameAttributes);
64        fontNameAttributes &= ~(FontNamePixelSize | FontNameAverageWidth);
65        fontNameAttributes |= FontNameResolutionX;
66        fontNameAttributes |= FontNameResolutionY;
67        fontNameAttributes |= FontNamePointSize;
68        fontName.ResolutionX = dw->dvi.screen_resolution;
69        fontName.ResolutionY = dw->dvi.screen_resolution;
70        fontName.PointSize = size * 10 / dw->dvi.size_scale;
71        XFormatFontName(&fontName, fontNameAttributes, fontNameString);
72        best->x_name = savestr(fontNameString);
73#ifdef USE_XFT
74        /*
75         * Force a match of a core font for adobe-fontspecific
76         * encodings; we don't have a scalable font in
77         * the right encoding
78         */
79        best->core = False;
80        if (!strcmp(fontName.CharSetRegistry, "adobe") &&
81            !strcmp(fontName.CharSetEncoding, "fontspecific")) {
82            best->core = True;
83        }
84#endif
85        best->doesnt_exist = 0;
86        best->font = NULL;
87        f->sizes = best;
88    }
89    else {
90        int bestdist = 65536;
91
92        for (DviFontSizeList * fs = f->sizes; fs; fs = fs->next) {
93            int dist = size - fs->size;
94
95            if (dist < 0)
96                dist = -dist * 16;
97            if (dist < bestdist) {
98                best = fs;
99                bestdist = dist;
100            }
101        }
102    }
103    return best;
104}
105
106static const char *
107SkipFontNameElement(const char *n)
108{
109    while (*n != '-') {
110        if (!*++n)
111            return NULL;
112    }
113    return n + 1;
114}
115
116#define SizePosition		8
117#define EncodingPosition	13
118
119#ifndef USE_XFT
120static int
121ConvertFontNameToSize(const char *n)
122{
123    int i, size;
124
125    for (i = 0; i < SizePosition; i++) {
126        n = SkipFontNameElement(n);
127        if (!n)
128            return -1;
129    }
130    size = atoi(n);
131    return size / 10;
132}
133#endif
134
135static const char *
136ConvertFontNameToEncoding(const char *n)
137{
138    for (int i = 0; i < EncodingPosition; i++) {
139        n = SkipFontNameElement(n);
140        if (!n)
141            return NULL;
142    }
143    return n;
144}
145
146static void
147DisposeFontSizes(DviWidget dw, DviFontSizeList *fs)
148{
149    DviFontSizeList *next;
150
151    for (; fs; fs = next) {
152        next = fs->next;
153        if (fs->x_name)
154            XtFree(fs->x_name);
155        if (fs->font) {
156#ifdef USE_XFT
157            XftFontClose(XtDisplay(dw), fs->font);
158#else
159            XUnloadFont(XtDisplay(dw), fs->font->fid);
160            XFree((char *) fs->font);
161#endif
162        }
163        XtFree((char *) fs);
164    }
165}
166
167void
168ResetFonts(DviWidget dw)
169{
170    for (DviFontList *f = dw->dvi.fonts; f; f = f->next) {
171        if (f->initialized) {
172            DisposeFontSizes(dw, f->sizes);
173            f->sizes = NULL;
174            f->initialized = FALSE;
175            f->scalable = FALSE;
176        }
177    }
178    /*
179     * force requery of fonts
180     */
181    dw->dvi.font = NULL;
182    dw->dvi.font_number = -1;
183    dw->dvi.cache.font = NULL;
184    dw->dvi.cache.font_number = -1;
185}
186
187static DviFontSizeList *
188InstallFontSizes(DviWidget dw, const char *x_name, Boolean *scalablep)
189{
190#ifndef USE_XFT
191    char fontNameString[2048];
192    char **fonts;
193    int count;
194    XFontName fontName;
195    unsigned int fontNameAttributes;
196#endif
197    DviFontSizeList *sizes = NULL;
198
199#ifdef USE_XFT
200    *scalablep = TRUE;
201#else
202    *scalablep = FALSE;
203    if (!XParseFontName(x_name, &fontName, &fontNameAttributes))
204        return NULL;
205
206    fontNameAttributes &= ~(FontNamePixelSize | FontNamePointSize);
207    fontNameAttributes |= FontNameResolutionX;
208    fontNameAttributes |= FontNameResolutionY;
209    fontName.ResolutionX = dw->dvi.screen_resolution;
210    fontName.ResolutionY = dw->dvi.screen_resolution;
211    XFormatFontName(&fontName, fontNameAttributes, fontNameString);
212    fonts = XListFonts(XtDisplay(dw), fontNameString, 10000000, &count);
213    for (int i = 0; i < count; i++) {
214        int size = ConvertFontNameToSize(fonts[i]);
215
216        if (size == 0) {
217            DisposeFontSizes(dw, sizes);
218            *scalablep = TRUE;
219            sizes = NULL;
220            break;
221        }
222        if (size != -1) {
223            DviFontSizeList *new = (DviFontSizeList *) XtMalloc(sizeof *new);
224
225            new->next = sizes;
226            new->size = size;
227            new->x_name = savestr(fonts[i]);
228            new->doesnt_exist = 0;
229            new->font = NULL;
230            sizes = new;
231        }
232    }
233    XFreeFontNames(fonts);
234#endif
235    return sizes;
236}
237
238static DviFontList *
239InstallFont(DviWidget dw, int position, const char *dvi_name,
240            const char *x_name)
241{
242    DviFontList *f = LookupFontByPosition(dw, position);
243
244    if (f) {
245        /*
246         * ignore gratuitous font loading
247         */
248        if (!strcmp(f->dvi_name, dvi_name) && !strcmp(f->x_name, x_name))
249            return f;
250
251        DisposeFontSizes(dw, f->sizes);
252        if (f->dvi_name)
253            XtFree(f->dvi_name);
254        if (f->x_name)
255            XtFree(f->x_name);
256    }
257    else {
258        f = (DviFontList *) XtMalloc(sizeof(*f));
259        f->next = dw->dvi.fonts;
260        dw->dvi.fonts = f;
261    }
262    f->initialized = FALSE;
263    f->dvi_name = savestr(dvi_name);
264    f->x_name = savestr(x_name);
265    f->dvi_number = position;
266    f->sizes = NULL;
267    f->scalable = FALSE;
268    if (f->x_name) {
269        const char *encoding = ConvertFontNameToEncoding(f->x_name);
270
271        f->char_map = DviFindMap(encoding);
272    }
273    else
274        f->char_map = NULL;
275    /*
276     * force requery of fonts
277     */
278    dw->dvi.font = NULL;
279    dw->dvi.font_number = -1;
280    dw->dvi.cache.font = NULL;
281    dw->dvi.cache.font_number = -1;
282    return f;
283}
284
285static const char *
286MapDviNameToXName(DviWidget dw, const char *dvi_name)
287{
288    DviFontMap *fm;
289
290    for (fm = dw->dvi.font_map; fm; fm = fm->next)
291        if (!strcmp(fm->dvi_name, dvi_name))
292            return fm->x_name;
293    ++dvi_name;
294    for (fm = dw->dvi.font_map; fm; fm = fm->next)
295        if (!strcmp(fm->dvi_name, "R"))
296            return fm->x_name;
297    if (dw->dvi.font_map->x_name)
298        return dw->dvi.font_map->x_name;
299    return "-*-*-*-*-*-*-*-*-*-*-*-*-iso8859-1";
300}
301
302void
303ParseFontMap(DviWidget dw)
304{
305    char *m;
306    DviFontMap *fm;
307
308    if (dw->dvi.font_map)
309        DestroyFontMap(dw->dvi.font_map);
310    fm = NULL;
311    m = dw->dvi.font_map_string;
312    while (*m) {
313        char dvi_name[1024];
314        char x_name[2048];
315        char *s = m;
316
317        while (*m && !isspace(*m))
318            ++m;
319        strncpy(dvi_name, s, m - s);
320        dvi_name[m - s] = '\0';
321        while (isspace(*m))
322            ++m;
323        s = m;
324        while (*m && *m != '\n')
325            ++m;
326        strncpy(x_name, s, m - s);
327        x_name[m - s] = '\0';
328
329        DviFontMap *new = (DviFontMap *) XtMalloc(sizeof *new);
330        new->x_name = savestr(x_name);
331        new->dvi_name = savestr(dvi_name);
332        new->next = fm;
333        fm = new;
334        ++m;
335    }
336    dw->dvi.font_map = fm;
337}
338
339void
340DestroyFontMap(DviFontMap *font_map)
341{
342    DviFontMap *next;
343
344    for (; font_map; font_map = next) {
345        next = font_map->next;
346        if (font_map->x_name)
347            XtFree(font_map->x_name);
348        if (font_map->dvi_name)
349            XtFree(font_map->dvi_name);
350        XtFree((char *) font_map);
351    }
352}
353
354 /*ARGSUSED*/ void
355SetFontPosition(DviWidget dw, int position, const char *dvi_name,
356                const char *extra)
357{
358    const char *x_name;
359
360    x_name = MapDviNameToXName(dw, dvi_name);
361    (void) InstallFont(dw, position, dvi_name, x_name);
362}
363
364#ifdef USE_XFT
365XftFont *
366#else
367XFontStruct *
368#endif
369QueryFont(DviWidget dw, int position, int size)
370{
371    DviFontList *f;
372    DviFontSizeList *fs;
373
374    f = LookupFontByPosition(dw, position);
375    if (!f)
376        return dw->dvi.default_font;
377    if (!f->initialized) {
378        f->sizes = InstallFontSizes(dw, f->x_name, &f->scalable);
379        f->initialized = TRUE;
380    }
381    fs = LookupFontSizeBySize(dw, f, size);
382    if (!fs)
383        return dw->dvi.default_font;
384    if (!fs->font) {
385        if (fs->x_name) {
386#ifdef USE_XFT
387            XftPattern *pat;
388            XftPattern *match;
389            XftResult result;
390
391            pat = XftXlfdParse(fs->x_name, False, False);
392            XftPatternAddBool(pat, XFT_CORE, fs->core);
393            match = XftFontMatch(XtDisplay(dw),
394                                 XScreenNumberOfScreen(dw->core.screen),
395                                 pat, &result);
396            XftPatternDestroy(pat);
397            if (match) {
398                fs->font = XftFontOpenPattern(XtDisplay(dw), match);
399                if (!fs->font)
400                    XftPatternDestroy(match);
401            }
402            else
403                fs->font = 0;
404#else
405            fs->font = XLoadQueryFont(XtDisplay(dw), fs->x_name);
406#endif
407        }
408        if (!fs->font)
409            fs->font = dw->dvi.default_font;
410    }
411    return fs->font;
412}
413
414DviCharNameMap *
415QueryFontMap(DviWidget dw, int position)
416{
417    DviFontList *f = LookupFontByPosition(dw, position);
418
419    if (f)
420        return f->char_map;
421    else
422        return NULL;
423}
424
425unsigned char *
426DviCharIsLigature(DviCharNameMap *map, const char *name)
427{
428    for (int i = 0; i < DVI_MAX_LIGATURES; i++) {
429        if (!map->ligatures[i][0])
430            break;
431        if (!strcmp(name, map->ligatures[i][0]))
432            return (unsigned char *) map->ligatures[i][1];
433    }
434    return NULL;
435}
436