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