Import upstream version 5.3.
[mup] / mup / mkmupfnt / mkmupfnt.c
1 char Copyright[] =
2 "Copyright (c) 1999, 2000 by Arkkra Enterprises\nAll rights reserved\n\n";
3
4 /* This program generates a Mup fontfile, that will let you override a
5 * Mup font with one of your own. It is done in C rather than with
6 * a "shell script" to be portable to systems without a Unix-like shell.
7 * See the "usage_message" for how to invoke this program.
8 * It creates a PostScript program to print each character in the font
9 * and print out its width, height, and ascent.
10 * It runs Ghostscript on that program.
11 */
12
13 #ifdef __DJGPP__
14 #define __DOS__
15 #endif
16
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <fcntl.h>
20 #include <string.h>
21 #ifdef __DOS__
22 #include <process.h>
23 #else
24 #include <unistd.h>
25 #include <sys/types.h>
26 #include <sys/wait.h>
27 #endif
28
29 #ifdef O_BINARY
30 #define READ_FLAGS (O_RDONLY | O_BINARY)
31 #define WRITE_FLAGS (O_WRONLY | O_BINARY | O_CREAT | O_TRUNC)
32 #else
33 #define READ_FLAGS (O_RDONLY)
34 #define WRITE_FLAGS (O_WRONLY | O_CREAT | O_TRUNC)
35 #endif
36
37 /* temp file used for PostScript program */
38 char *PS_script_file = "mkmupfnt.ps";
39 #ifdef __DOS__
40 char *GS_output = "mkmupfnt.tmp";
41 #endif
42
43 char Version[] = "5.3";
44
45 void usage(char *program_name);
46 void verify_valid_Mup_name(char *Mup_name);
47 int name_matches(char *namelist[], char *name, int namelength);
48 void run_Ghostscript(char *PostScript_name, char *Mup_name);
49 char * make_string(char *first_part, char *second_part);
50 void generate_PostScript_program(char *PS_file);
51 void cleanup(int exitcode);
52
53 int
54 main(int argc, char **argv)
55 {
56 char *PostScript_name;
57 char *Mup_name;
58 char *outfile;
59
60 fprintf(stderr, "%s Version %s\n%s", argv[0], Version, Copyright);
61
62 if (argc < 4 || argc > 5) {
63 usage(argv[0]);
64 }
65
66 PostScript_name = argv[1];
67 Mup_name = argv[2];
68 outfile = argv[3];
69
70 verify_valid_Mup_name(Mup_name);
71
72 if ((freopen(outfile, "w", stdout)) == (FILE *) 0) {
73 fprintf(stderr, "Can't open '%s'\n", outfile);
74 exit(1);
75 }
76
77 /* Generate a PostScript program to run, and redirect that program
78 * into Ghostscript. */
79 generate_PostScript_program(argc == 5 ? argv[4] : (char *) 0);
80 if ((freopen(PS_script_file, "r", stdin)) == (FILE *) 0) {
81 fprintf(stderr, "Can't open '%s'\n", PS_script_file);
82 cleanup(1);
83 }
84 run_Ghostscript(PostScript_name, Mup_name);
85
86 /* If there is a PostScript file to add to the output, copy that */
87 if (argc == 5) {
88 int file;
89 int n;
90 char buff[BUFSIZ];
91
92 if ((file = open(argv[4], READ_FLAGS)) < 0) {
93 fprintf(stderr, "Can't open '%s'\n", argv[4]);
94 cleanup(1);
95 }
96 while ((n = read(file, buff, BUFSIZ)) > 0) {
97 write(1, buff, n);
98 }
99 close(file);
100 }
101
102 cleanup(0);
103 /* This line is not reached, since cleanup() exits,
104 * but some compilers complain if function doesn't have a
105 * return or exit, so the return is here to appease them. */
106 return(0);
107 }
108
109
110 char *usage_message =
111 "PostScript_font_name Mup_font_name outfile [file]\n\n"
112 " Generates a fontfile for Mup to use, to override a Mup font.\n"
113 " Arguments are:\n\n"
114 " PostScript_font_name the name of the font you want to add to Mup,\n"
115 " like 'Helvetica-Narrow'\n\n"
116 " Mup_font_name the name of the Mup font you want to replace,\n"
117 " like 'HR' or 'helvetica rom'\n\n"
118 " outfile the generated Mup fontfile\n\n"
119 " file can contain PostScript to be added to Mup prolog,\n"
120 " if needed to use the font.\n";
121
122 void
123 usage(char *program_name)
124 {
125 fprintf(stderr, "usage: %s %s", program_name, usage_message);
126 exit(1);
127 }
128
129 /* verify Mup font name is a valid one, give error and exit if not */
130
131 char *family_names[] = {
132 "avantegarde",
133 "bookman",
134 "courier",
135 "helvetica",
136 "newcentury",
137 "palatino",
138 "times",
139 (char *) 0
140 };
141
142 char *font_names[] = {
143 "rom",
144 "bold",
145 "ital",
146 "boldital",
147 (char *) 0
148 };
149
150 void
151 verify_valid_Mup_name(char *Mup_name)
152 {
153 char *space_loc;
154
155 if (strlen(Mup_name) == 2 && strchr("ABCHNPT", Mup_name[0])
156 && strchr("RBIX", Mup_name[1]) ) {
157 /* name is okay, an abbreviated name */
158 return;
159 }
160
161 /* check long names */
162 if ((space_loc = strchr(Mup_name, ' ')) != 0 &&
163 name_matches(family_names, Mup_name, space_loc - Mup_name) &&
164 name_matches(font_names, space_loc + 1, strlen(space_loc + 1))) {
165 return;
166 }
167
168 fprintf(stderr, "'%s' is not a valid Mup font name\n", Mup_name);
169 exit(1);
170 }
171
172 /* verify name given matches one on the list of valid names */
173
174 int
175 name_matches(char *namelist[], char *name, int namelength)
176 {
177 int i;
178
179 for (i = 0; namelist[i] != (char *) 0; i++) {
180 if (strncmp(namelist[i], name, namelength) == 0 &&
181 namelength == strlen(namelist[i])) {
182 /* matches */
183 return(1);
184 }
185 }
186 return(0);
187 }
188
189 /* Run Ghostscript to write width/ascent/descent information for all
190 * characters. */
191
192 void
193 run_Ghostscript(char *PostScript_name, char *Mup_name)
194 {
195 char * PS_option;
196 char * Mup_option;
197 #ifdef __DOS__
198 char * output_option;
199 int ret;
200 #endif
201 int status;
202
203 /* pass the arguments on to Ghostscript */
204 PS_option = make_string("-sPostScript_name=", PostScript_name);
205 Mup_option = make_string("-sMup_name=", Mup_name);
206 #ifdef __DOS__
207 /* use temp file as a /dev/null */
208 output_option = make_string("-sOutputFile=", GS_output);
209
210 if ((ret = spawnlp(P_WAIT, "gs386", "gs386", "-sDEVICE=bit",
211 "-dQUIET", output_option, Mup_option,
212 PS_option, "-", (char *) 0)) != 0) {
213 /* try just plain 'gs' */
214 ret = spawnlp(P_WAIT, "gs", "gs", "-sDEVICE=bit",
215 "-dQUIET", output_option, Mup_option,
216 PS_option, "-", (char *) 0);
217 }
218 unlink(GS_output);
219 if (ret != 0) {
220 fprintf(stderr, "failed to execute gs\n");
221 cleanup(1);
222 }
223 #else
224 switch (fork()) {
225 case 0:
226 execlp("gs", "gs", "-sDEVICE=bit", "-dQUIET",
227 "-sOutputFile=/dev/null", Mup_option,
228 PS_option, "-", (char *) 0);
229 /* FALL THROUGH */
230 case -1:
231 fprintf(stderr, "failed to execute gs\n");
232 cleanup(1);
233 default:
234 wait( &status);
235 if (status != 0) {
236 fprintf(stderr, "Ghostscript failed\n");
237 cleanup(1);
238 }
239 }
240 #endif
241 }
242
243 /* given two strings, get enough space to concatenate them,
244 * write them into the malloc-ed string, and return it. */
245
246 char *
247 make_string(char *first_part, char *second_part)
248 {
249 char *new_string;
250
251 if ((new_string = (char *) malloc(strlen(first_part)
252 + strlen(second_part) + 1)) == 0) {
253 fprintf(stderr, "malloc failed\n");
254 cleanup(1);
255 }
256 sprintf(new_string, "%s%s", first_part, second_part);
257 return(new_string);
258 }
259
260 /* This is the PostScript program that actually extracts the
261 * font size information. It is included here and generated as needed,
262 * so that this can be a standalone program, and not have to search
263 * for another file in order to run. In Unix, we could just pipe this
264 * directly into Ghostscript, but on systems that don't have pipes,
265 * a temp file would need to be used, so we do it that way everywhere
266 * for consistency.
267 */
268
269 char *PostScript_program =
270 "%% This PostScript program generates a fontfile for use by Mup.\n"
271 "% PostScript_name and Mup_name must be defined as strings\n"
272 "% when this is called.\n"
273 "% PostScript_name is the font you want to add to Mup, while\n"
274 "% Mup_name is name of the Mup font you want to replace.\n"
275 "% So, for example, if you want to replace the Mup Helvetica roman\n"
276 "% font with the PostScript Helvetica-Narrow font, these strings would be\n"
277 "% (Helvetica-Narrow) and (helvetica rom).\n"
278 "% These can be passed in using the Ghostscript -s option.\n"
279 "\n"
280 "1 setflat % make bounding box very accurate\n"
281 "\n"
282 "/buff 50 string def % number to string conversion buffer\n"
283 "/character 1 string def % buffer for a character to get the bbox of\n"
284 "\n"
285 "%------------------------------------------------------------------\n"
286 "\n"
287 "\n"
288 "% Usage\n"
289 "% given a one-character string in \"character\",\n"
290 "% outputs its width in 1/1000ths of an inch\n"
291 "\n"
292 "/getwidth {\n"
293 " % get width of character\n"
294 " character stringwidth\n"
295 "\n"
296 " % convert x to 1/1000th of an inch\n"
297 " pop 1000 mul 72 div round cvi\n"
298 "\n"
299 " % print results\n"
300 " buff cvs (\\t) print print\n"
301 "} def\n"
302 "\n"
303 "%-----------------------------------\n"
304 "% Usage\n"
305 "% given a one-character string in \"character\",\n"
306 "% outputs its height in 1/1000ths of an inch\n"
307 "\n"
308 "/getheight {\n"
309 " % place character at (100, 100) and get its pathbbox\n"
310 " newpath\n"
311 " 100 100 moveto\n"
312 " character true charpath flattenpath pathbbox\n"
313 "\n"
314 " % save the top and bottom y coordinates of the bbox\n"
315 " /top exch def pop\n"
316 " /bot exch def pop\n"
317 "\n"
318 " % if bot is above the baseline, the height is (top - baseline)\n"
319 " % otherwise it is (top - bot)\n"
320 " bot 100 gt { top 100 sub } { top bot sub } ifelse\n"
321 "\n"
322 " % space is special, use 9 points for height\n"
323 " character ( ) eq { 9 add } if\n"
324 "\n"
325 " % add 2 point of padding, one for top and one for bottom white space,\n"
326 " % and convert to 1/1000ths of an inch\n"
327 " 2 add 1000 mul 72 div round cvi\n"
328 "\n"
329 " % print the results\n"
330 " buff cvs (\\t) print print\n"
331 "} def\n"
332 "\n"
333 "%----------------------------------\n"
334 "% Usage\n"
335 "% given a one-character string in \"character\",\n"
336 "% outputs its ascent in 1/1000ths of an inch\n"
337 "\n"
338 "/getascent {\n"
339 " % place character at (100, 100) and get its pathbbox\n"
340 " newpath\n"
341 " 100 100 moveto\n"
342 " character true charpath flattenpath pathbbox\n"
343 "\n"
344 " % save the top y coordinate of the bbox\n"
345 " /top exch def pop pop pop\n"
346 "\n"
347 " % ascent is top minus baseline\n"
348 " top 100 sub\n"
349 "\n"
350 " % space is special, use 6.8 points for ascent\n"
351 " character ( ) eq { 6.8 add } if\n"
352 "\n"
353 " % add 1 point of padding and convert to 1/1000ths of an inch\n"
354 " 1 add 1000 mul 72 div round cvi\n"
355 "\n"
356 " % print results\n"
357 " buff cvs (\\t) print print\n"
358 "} def\n"
359 "\n"
360 "\n"
361 "%-----------------------------------\n"
362 "\n"
363 "% generate width, height an ascent for a font.\n"
364 "% Usage:\n"
365 "% fname mupfname do_a_font\n"
366 "\n"
367 "/do_a_font {\n"
368 " % save arguments for later use\n"
369 " /mupfname exch def\n"
370 " /fname exch def\n"
371 "\n"
372 " % Outut heading\n"
373 " (# This is a Mup font file\\n) print\n"
374 " (Mup font name: ) print mupfname print (\\n) print\n"
375 " (PostScript font name: ) print fname buff cvs print (\\n) print\n"
376 " (Size data:\\n) print\n"
377 "\n"
378 " % Set up to use the desired font\n"
379 " fname findfont\n"
380 " 12 scalefont setfont\n"
381 "\n"
382 " % Mup uses ASCII character codes from 32 through 126\n"
383 " 32 1 126 {\n"
384 " dup buff cvs print\n"
385 " /val exch def character 0 val put\n"
386 " getwidth\n"
387 " getheight\n"
388 " getascent\n"
389 " (\\t# ') print character print ('\\n) print\n"
390 " } for\n"
391 "\n"
392 " (PostScript:\\n) print\n"
393 "} def\n"
394 "\n"
395 "%-----------------------------------\n"
396 "\n"
397 "% generate the output\n"
398 "PostScript_name cvn Mup_name do_a_font\n"
399 "\n"
400 "quit\n";
401
402 void
403 generate_PostScript_program(char *PS_file)
404 {
405 int file;
406 int length;
407
408 if ((file = open(PS_script_file, WRITE_FLAGS, 0644)) < 0) {
409 fprintf(stderr, "Can't generate '%s'\n", PS_script_file);
410 exit(1);
411 }
412
413 /* If user gave a PostScript file, that probably implements the
414 * font, so include that in the script */
415 if (PS_file != (char *) 0) {
416 write(file, "(", 1);
417 write(file, PS_file, strlen(PS_file));
418 write(file, ") run\n", 6);
419 }
420
421 length = strlen(PostScript_program);
422 if (write(file, PostScript_program, length) != length) {
423 fprintf(stderr, "generation of PostScript program failed\n");
424 cleanup(1);
425 }
426 close(file);
427 }
428
429 /* remove the temp file and exit */
430
431 void
432 cleanup(int exitcode)
433 {
434 unlink(PS_script_file);
435 exit(exitcode);
436 }