1 /*	qms2ch.c	1.1	87/02/05
2  *
3  * Font translation for QMS-style fonts (qms' QUIC format) to character format.
4  *
5  *	Use:  qms2ch [ -b# ] fontfile [ character_list ]
6  *
7  *		Reads "fontfile" from current directory (or if not found,
8  *	from BITDIR defined below) and converts it to a character font format
9  *	editable by real people, and convertable back to QUIC format by the
10  *	ch2qms program.  The "-b" flag sets a baseline unless the font has
11  *	one itself.  Output goes to stdout.
12  */
13 
14 #include <stdio.h>
15 #include <ctype.h>
16 
17 
18 #define  RES		300	/* resolution, in pixels per inch */
19 #define  DIRSIZ		256	/* maximum number of characters in font */
20 #define  HEADER		"^PY^-^F^-^DF"
21 #define  BITDIR		"/usr/lib/font/devqms/fonts"
22 
23 
24 char	defascii[DIRSIZ];	/* list of ascii characters to print */
25 char	*fontdir = BITDIR;	/* place to look for fonts */
26 char	*header = HEADER;	/* string that must start each QUIC font file */
27 char	filename[100];		/* input file name put here */
28 char	input[100];		/* general-purpose input string */
29 FILE	*FID;			/* input file number */
30 int	height;			/* height of every character in the font */
31 int	fontid;			/* the "font number" */
32 int	landscape = 0;		/* flag:  is this a landscape font? */
33 int	insertion = 0;		/* flag:  is this an addition to a font? */
34 int	signed = 0;		/* flag:  can this font have signed offsets? */
35 int	version = 0;		/* version code */
36 int	baseline = 0;		/* baseline of font (if specified) */
37 int     printwidth;		/* printwidth of each character */
38 				/* variables to use when printing characters */
39 int	H, W, Hoff, Woff, lbound, rbound;
40 
41 
42 main(argc,argv)
43 int argc;
44 char **argv;
45 {
46 	register int i;
47 	register int j;
48 	register int k;
49 	register int Hpoint;
50 	register int Wpoint;
51 
52 
53 	if (argc < 2 || argc > 4) {
54 	    usage:
55 		error("usage: %s [ -b# ] filename [ charlist ]", *argv);
56 	}
57 	++argv;
58 	if (**argv == '-' && (*argv)[1]) {
59 		argc--;
60 		if (argv[0][1] != 'b')
61 			goto usage;
62 		baseline = atoi(&(argv[0][2]));
63 		argv++;
64 	}
65 	if (argc == 3) {
66 		do
67 			defascii[argv[1][0]] = 1;
68 		while (*++(argv[1]));
69 	} else {
70 		for (i = 0; i < DIRSIZ; i++)
71 			defascii[i] = 1;
72 	}
73 
74 	sprintf(filename, "%s", *argv);
75 	if (filename[0] != '-' || filename[1]) {
76 		if ((FID = fopen(filename, "r")) == NULL) {
77 			sprintf(filename, "%s/%s", fontdir, *argv);
78 			if ((FID = fopen(filename, "r")) == NULL)
79 				error("can't find %s", *argv);
80 		}
81 	} else {
82 		FID = stdin;
83 	}
84 
85 	j = strlen(header);
86 	(void) getnumber(j);
87 	if (strncmp(input, header, j))
88 		error("%s, not a QMS Font file.", filename);
89 
90 	(void) getnumber(6);
91 	k = (int) input[0];
92 	if (k == 'i' || k == 'I') {
93 		fontid = atoi(input + 1);
94 		(void) getnumber(1);
95 		k = (int) input[0];
96 	} else {
97 		k = (int) input[5];
98 		input[5] = 0;
99 		fontid = atoi(input);
100 	}
101 	switch (k) {
102 		case 'y': case 'Y': case 'L': case 'l':
103 			landscape = 1;
104 			break;
105 
106 		case 'x': case 'X': case 'P': case 'p':
107 			break;
108 
109 		default:
110 			error("font is not portrait or landscape");
111 	}
112 	version = getnumber(1);
113 	(void) getnumber(4);
114 	printf("fontheader\nname %s\n", input);
115 	printf("version %d\n", version);
116 	printf("rot %d\n", landscape ? 1 : 0);
117 	printf("cadv %d\n", landscape ? 1 : 2);
118 	printf("ladv %d\n", landscape ? 2 : 1);
119 	height = getnumber(3);
120 	printf("linesp %d.00\n", height);
121 	printf("id %d\nres %d\n", fontid, RES);
122 	j = getnumber(1);
123 	if (isdigit(input[0])) {
124 		baseline = j * 100 + getnumber(2);
125 		printf("baseline %d.00\n", baseline);
126 		(void) getnumber(1);
127 		if (landscape)
128 		    baseline = height - baseline;
129 	}
130 	if (input[0] != ',') {
131 		signed = 1;
132 		(void) getnumber(1);
133 		if (input[0] != ',')
134 			error("error in header, expected \",\"");
135 	}
136 
137 	for (;;) {	/* for each character */
138 	    k = getnumber(1);		/* first digit of character number */
139 	    if (input[0] == '^') {
140 		(void) getnumber(1);
141 		if (input[0] != 'G')
142 		    error("expected \"G\"");
143 		exit(0);
144 	    }
145 
146 	    k = k * 16 + getnumber(1);	/* second digit of character number */
147 	    if (!defascii[k]) {
148 		do
149 		    i = getc(FID);
150 		while (i != EOF && i != ',');
151 		continue;		/* ignore characters not asked for */
152 	    }
153 
154 	    printwidth = getnumber(3);
155 	    H = getnumber(2);
156 	    if (input[0] == '^') {
157 		switch (input[1]) {
158 		    case 'M': case 'm':
159 			break;
160 		    case 'R': case 'r':
161 			break;
162 		}
163 		H = getnumber(3);
164 	    } else {
165 		H = H * 10 + getnumber(1);
166 	    }
167 	    W = getnumber(3);
168 	    Hoff = getnumber(3);
169 	    if (ispunct(input[0])) {
170 		if (input[0] == '-')
171 		    Hoff = 10 * Hoff - getnumber(1);
172 		else
173 		    Hoff = 10 * Hoff + getnumber(1);
174 	    }
175 	    Woff = getnumber(3);
176 	    if (ispunct(input[0])) {
177 		if (input[0] == '-')
178 		    Woff = 10 * Woff - getnumber(1);
179 		else
180 		    Woff = 10 * Woff + getnumber(1);
181 	    }
182 	    printf(":%d, width = %d.00\n", k, printwidth);
183 
184 	    if (landscape) {
185 		Hpoint = -Hoff;
186 		Wpoint = baseline - Woff;
187 	    } else {
188 		Hpoint = baseline - Hoff;
189 		Wpoint = -Woff;
190 	    }
191 	    if ((lbound = Wpoint) > 0) lbound = 0;
192 	    if ((rbound = Wpoint + 1) < W) rbound = W;
193 
194 	    for (k = Hpoint; k < 0; k++) {
195 		for (i = lbound; i < rbound; i++)
196 		    printf("%c", k==Hpoint && i==Wpoint ? 'x':'.');
197 		putchar ('\n');
198 	    }
199 	    for (k = 0; k < H; k++) {
200 				/* read in one "line" at a time */
201 		(void) getnumber(((W + 15) / 16) * 4);
202 
203 		for (i = lbound; i < 0; i++)
204 		    printf("%c", k==Hpoint && i==Wpoint ? 'x':'.');
205 		for (i = 0; i < W; i++)
206 		    printf("%c", k==Hpoint && i==Wpoint ?
207 			    (fbit(i) ? 'X':'x') : fbit(i) ? '@':'.');
208 		while (i < rbound) {
209 		    printf("%c", k==Hpoint && i==Wpoint ? 'x':'.');
210 		    i++;
211 		}
212 		putchar ('\n');
213 	    }
214 	    while (k <= Hpoint) {
215 		for (i = lbound; i < rbound; i++)
216 		    printf("%c", k==Hpoint && i==Wpoint ? 'x':'.');
217 		putchar ('\n');
218 		k++;
219 	    }
220 	    putchar ('\n');
221 	    (void) getnumber(1);
222 	    if (input[0] != ',')
223 		error("expected \",\"");
224 	}
225 }
226 
227 
228 /*----------------------------------------------------------------------------*
229  | Routine:	error (format_string, argument1, argument2.... )
230  |
231  | Results:	fprints a message to standard error, then exits with error
232  |		code 1
233  |
234  | Side Efct:	This routine does NOT return
235  *----------------------------------------------------------------------------*/
236 /*VARARGS1*/
237 error(string, a1, a2, a3, a4)
238 char *string;
239 {
240 	fprintf(stderr, "qms2ch: ");
241 	fprintf(stderr, string, a1, a2, a3, a4);
242 	fprintf(stderr, "\n");
243 	exit(1);
244 }
245 
246 
247 /*----------------------------------------------------------------------------*
248  | Routine:	fbit (row)
249  |
250  | Results:	returns true (non-0) or false (0) to signify whether the bit
251  |		in "row" of the current input line (hex digits) is on or off.
252  |
253  | Side Efct:	exits the program if the character in the line isn't valid hex
254  *----------------------------------------------------------------------------*/
255 
256 fbit(row)
257 int row;
258 {
259 	register int piece = row & 3;
260 
261 	switch (input[row / 4]) {
262 		case '0': return 0;
263 		case '1': return (piece == 3);
264 		case '2': return (piece == 2);
265 		case '3': return (piece > 1);
266 		case '4': return (piece == 1);
267 		case '5': return (piece == 1 || piece == 3);
268 		case '6': return (piece == 1 || piece == 2);
269 		case '7': return (piece);
270 		case '8': return (!piece);
271 		case '9': return (piece == 3 || !piece);
272 		case 'A': case 'a': return (piece == 2 || !piece);
273 		case 'B': case 'b': return (piece != 1);
274 		case 'C': case 'c': return (piece < 2);
275 		case 'D': case 'd': return (piece != 2);
276 		case 'E': case 'e': return (piece != 3);
277 		case 'F': case 'f': return 1;
278 
279 		default: error("expected HEX digit");
280 	}
281 }
282 
283 
284 /*----------------------------------------------------------------------------*
285  | Routine:	getnumber (digits)
286  |
287  | Results:	read a string of "digits" length from the input file "FID"
288  |		and return the decimal value of the string.  If the string
289  |		is one character long, it may be interpreted as a hex number.
290  |		In any case the null-terminated string is returned globally
291  |		to "input".
292  |
293  | Side Efct:	This routine SKIPS WHITE SPACE, and exits upon error.
294  *----------------------------------------------------------------------------*/
295 
296 int
297 getnumber(digits)
298 int digits;
299 {
300 	register int i;
301 	register int j;
302 
303 	for (i = 0; i < digits; ) {
304 		if ((j = getc(FID)) == EOF)
305 			error ("unexpected end of input");
306 		if (!isspace(input[i] = (char) j))
307 			i++;
308 	}
309 	input[digits] = (char) 0;
310 	if (digits > 1) {
311 		return (atoi(input));
312 	} else switch (input[0]) {
313 		case '0': return 0;
314 		case '1': return 1;
315 		case '2': return 2;
316 		case '3': return 3;
317 		case '4': return 4;
318 		case '5': return 5;
319 		case '6': return 6;
320 		case '7': return 7;
321 		case '8': return 8;
322 		case '9': return 9;
323 		case 'A': case 'a': return 10;
324 		case 'B': case 'b': return 11;
325 		case 'C': case 'c': return 12;
326 		case 'D': case 'd': return 13;
327 		case 'E': case 'e': return 14;
328 		case 'F': case 'f': return 15;
329 
330 		default: return 0;
331 	}
332 }
333