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