1/*	$NetBSD$	*/
2
3/*
4 * xtotroff
5 *
6 * convert X font metrics into troff font metrics
7 */
8
9#ifdef HAVE_CONFIG_H
10#include <config.h>
11#endif
12
13#include <X11/Xlib.h>
14#include <stdio.h>
15#include <ctype.h>
16#include <unistd.h>
17#include <stdlib.h>
18#include <string.h>
19#include <fcntl.h>
20#include <limits.h>
21
22#define __GETOPT_PREFIX groff_
23#include <getopt.h>
24
25#include "XFontName.h"
26#include "DviChar.h"
27
28#define charWidth(fi,c) \
29	  ((fi)->per_char[(c) - (fi)->min_char_or_byte2].width)
30#define charHeight(fi,c) \
31	  ((fi)->per_char[(c) - (fi)->min_char_or_byte2].ascent)
32#define charDepth(fi,c) \
33	  ((fi)->per_char[(c) - (fi)->min_char_or_byte2].descent)
34#define charLBearing(fi,c) \
35	  ((fi)->per_char[(c) - (fi)->min_char_or_byte2].lbearing)
36#define charRBearing(fi,c) \
37	  ((fi)->per_char[(c) - (fi)->min_char_or_byte2].rbearing)
38
39extern const char *Version_string;
40static char *program_name;
41
42Display *dpy;
43unsigned resolution = 75;
44unsigned point_size = 10;
45
46int charExists(XFontStruct * fi, int c)
47{
48  XCharStruct *p;
49
50  /* `c' is always >= 0 */
51  if ((unsigned int) c < fi->min_char_or_byte2
52      || (unsigned int) c > fi->max_char_or_byte2)
53    return 0;
54  p = fi->per_char + (c - fi->min_char_or_byte2);
55  return p->lbearing != 0 || p->rbearing != 0 || p->width != 0
56	 || p->ascent != 0 || p->descent != 0 || p->attributes != 0;
57}
58
59/* Canonicalize the font name by replacing scalable parts by *s. */
60
61static int CanonicalizeFontName(char *font_name, char *canon_font_name)
62{
63  unsigned int attributes;
64  XFontName parsed;
65
66  if (!XParseFontName(font_name, &parsed, &attributes)) {
67    fprintf(stderr, "not a standard name: %s\n", font_name);
68    return 0;
69  }
70
71  attributes &= ~(FontNamePixelSize | FontNameAverageWidth
72		  | FontNamePointSize
73		  | FontNameResolutionX | FontNameResolutionY);
74  XFormatFontName(&parsed, attributes, canon_font_name);
75  return 1;
76}
77
78static int
79FontNamesAmbiguous(const char *font_name, char **names, int count)
80{
81  char name1[2048], name2[2048];
82  int i;
83
84  if (count == 1)
85    return 0;
86
87  for (i = 0; i < count; i++) {
88    if (!CanonicalizeFontName(names[i], i == 0 ? name1 : name2)) {
89      fprintf(stderr, "bad font name: %s\n", names[i]);
90      return 1;
91    }
92    if (i > 0 && strcmp(name1, name2) != 0) {
93      fprintf(stderr, "ambiguous font name: %s\n", font_name);
94      fprintf(stderr, "  matches %s\n", names[0]);
95      fprintf(stderr, "  and %s\n", names[i]);
96      return 1;
97    }
98  }
99  return 0;
100}
101
102static int MapFont(char *font_name, const char *troff_name)
103{
104  XFontStruct *fi;
105  int count;
106  char **names;
107  FILE *out;
108  unsigned int c;
109  unsigned int attributes;
110  XFontName parsed;
111  int j, k;
112  DviCharNameMap *char_map;
113  char encoding[256];
114  char *s;
115  int wid;
116  char name_string[2048];
117
118  if (!XParseFontName(font_name, &parsed, &attributes)) {
119    fprintf(stderr, "not a standard name: %s\n", font_name);
120    return 0;
121  }
122
123  attributes &= ~(FontNamePixelSize | FontNameAverageWidth);
124  attributes |= FontNameResolutionX;
125  attributes |= FontNameResolutionY;
126  attributes |= FontNamePointSize;
127  parsed.ResolutionX = resolution;
128  parsed.ResolutionY = resolution;
129  parsed.PointSize = point_size * 10;
130  XFormatFontName(&parsed, attributes, name_string);
131
132  names = XListFonts(dpy, name_string, 100000, &count);
133  if (count < 1) {
134    fprintf(stderr, "bad font name: %s\n", font_name);
135    return 0;
136  }
137
138  if (FontNamesAmbiguous(font_name, names, count))
139    return 0;
140
141  XParseFontName(names[0], &parsed, &attributes);
142  sprintf(encoding, "%s-%s", parsed.CharSetRegistry,
143	  parsed.CharSetEncoding);
144  for (s = encoding; *s; s++)
145    if (isupper(*s))
146      *s = tolower(*s);
147  char_map = DviFindMap(encoding);
148  if (!char_map) {
149    fprintf(stderr, "not a standard encoding: %s\n", encoding);
150    return 0;
151  }
152
153  fi = XLoadQueryFont(dpy, names[0]);
154  if (!fi) {
155    fprintf(stderr, "font does not exist: %s\n", names[0]);
156    return 0;
157  }
158
159  printf("%s -> %s\n", names[0], troff_name);
160
161  {				/* Avoid race while opening file */
162    int fd;
163    (void) unlink(troff_name);
164    fd = open(troff_name, O_WRONLY | O_CREAT | O_EXCL, 0600);
165    out = fdopen(fd, "w");
166  }
167
168  if (!out) {
169    perror(troff_name);
170    return 0;
171  }
172  fprintf(out, "name %s\n", troff_name);
173  if (!strcmp(char_map->encoding, "adobe-fontspecific"))
174    fprintf(out, "special\n");
175  if (charExists(fi, ' ')) {
176    int w = charWidth(fi, ' ');
177    if (w > 0)
178      fprintf(out, "spacewidth %d\n", w);
179  }
180  fprintf(out, "charset\n");
181  for (c = fi->min_char_or_byte2; c <= fi->max_char_or_byte2; c++) {
182    const char *name = DviCharName(char_map, c, 0);
183    if (charExists(fi, c)) {
184      int param[5];
185
186      wid = charWidth(fi, c);
187
188      fprintf(out, "%s\t%d", name ? name : "---", wid);
189      param[0] = charHeight(fi, c);
190      param[1] = charDepth(fi, c);
191      param[2] = 0;		/* charRBearing (fi, c) - wid */
192      param[3] = 0;		/* charLBearing (fi, c) */
193      param[4] = 0;		/* XXX */
194      for (j = 0; j < 5; j++)
195	if (param[j] < 0)
196	  param[j] = 0;
197      for (j = 4; j >= 0; j--)
198	if (param[j] != 0)
199	  break;
200      for (k = 0; k <= j; k++)
201	fprintf(out, ",%d", param[k]);
202      fprintf(out, "\t0\t0%o\n", c);
203
204      if (name) {
205	for (k = 1; DviCharName(char_map, c, k); k++) {
206	  fprintf(out, "%s\t\"\n", DviCharName(char_map, c, k));
207	}
208      }
209    }
210  }
211  XUnloadFont(dpy, fi->fid);
212  fclose(out);
213  return 1;
214}
215
216static void usage(FILE *stream)
217{
218  fprintf(stream,
219	  "usage: %s [-r resolution] [-s pointsize] FontMap\n",
220	  program_name);
221}
222
223int main(int argc, char **argv)
224{
225  char troff_name[1024];
226  char font_name[1024];
227  char line[1024];
228  char *a, *b, c;
229  FILE *map;
230  int opt;
231  static const struct option long_options[] = {
232    { "help", no_argument, 0, CHAR_MAX + 1 },
233    { "version", no_argument, 0, 'v' },
234    { NULL, 0, 0, 0 }
235  };
236
237  program_name = argv[0];
238
239  while ((opt = getopt_long(argc, argv, "gr:s:v", long_options,
240			    NULL)) != EOF) {
241    switch (opt) {
242    case 'g':
243      /* unused; just for compatibility */
244      break;
245    case 'r':
246      sscanf(optarg, "%u", &resolution);
247      break;
248    case 's':
249      sscanf(optarg, "%u", &point_size);
250      break;
251    case 'v':
252      printf("xtotroff (groff) version %s\n", Version_string);
253      exit(0);
254      break;
255    case CHAR_MAX + 1: /* --help */
256      usage(stdout);
257      exit(0);
258      break;
259    case '?':
260      usage(stderr);
261      exit(1);
262      break;
263    }
264  }
265  if (argc - optind != 1) {
266    usage(stderr);
267    exit(1);
268  }
269
270  dpy = XOpenDisplay(0);
271  if (!dpy) {
272    fprintf(stderr, "Can't connect to the X server.\n");
273    fprintf(stderr,
274	    "Make sure the DISPLAY environment variable is set correctly.\n");
275    exit(1);
276  }
277
278  map = fopen(argv[optind], "r");
279  if (map == NULL) {
280    perror(argv[optind]);
281    exit(1);
282  }
283
284  while (fgets(line, sizeof(line), map)) {
285    for (a = line, b = troff_name; *a; a++, b++) {
286      c = (*b = *a);
287      if (c == ' ' || c == '\t')
288	break;
289    }
290    *b = '\0';
291    while (*a && (*a == ' ' || *a == '\t'))
292      ++a;
293    for (b = font_name; *a; a++, b++)
294      if ((*b = *a) == '\n')
295	break;
296    *b = '\0';
297    if (!MapFont(font_name, troff_name))
298      exit(1);
299  }
300  exit(0);
301}
302