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