2 "Copyright (c) 1999, 2000 by Arkkra Enterprises\nAll rights reserved\n\n";
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.
25 #include <sys/types.h>
30 #define READ_FLAGS (O_RDONLY | O_BINARY)
31 #define WRITE_FLAGS (O_WRONLY | O_BINARY | O_CREAT | O_TRUNC)
33 #define READ_FLAGS (O_RDONLY)
34 #define WRITE_FLAGS (O_WRONLY | O_CREAT | O_TRUNC)
37 /* temp file used for PostScript program */
38 char *PS_script_file = "mkmupfnt.ps";
40 char *GS_output = "mkmupfnt.tmp";
43 char Version[] = "5.3";
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);
54 main(int argc, char **argv)
56 char *PostScript_name;
60 fprintf(stderr, "%s Version %s\n%s", argv[0], Version, Copyright);
62 if (argc < 4 || argc > 5) {
66 PostScript_name = argv[1];
70 verify_valid_Mup_name(Mup_name);
72 if ((freopen(outfile, "w", stdout)) == (FILE *) 0) {
73 fprintf(stderr, "Can't open '%s'\n", outfile);
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);
84 run_Ghostscript(PostScript_name, Mup_name);
86 /* If there is a PostScript file to add to the output, copy that */
92 if ((file = open(argv[4], READ_FLAGS)) < 0) {
93 fprintf(stderr, "Can't open '%s'\n", argv[4]);
96 while ((n = read(file, buff, BUFSIZ)) > 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. */
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";
123 usage(char *program_name)
125 fprintf(stderr, "usage: %s %s", program_name, usage_message);
129 /* verify Mup font name is a valid one, give error and exit if not */
131 char *family_names[] = {
142 char *font_names[] = {
151 verify_valid_Mup_name(char *Mup_name)
155 if (strlen(Mup_name) == 2 && strchr("ABCHNPT", Mup_name[0])
156 && strchr("RBIX", Mup_name[1]) ) {
157 /* name is okay, an abbreviated name */
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))) {
168 fprintf(stderr, "'%s' is not a valid Mup font name\n", Mup_name);
172 /* verify name given matches one on the list of valid names */
175 name_matches(char *namelist[], char *name, int namelength)
179 for (i = 0; namelist[i] != (char *) 0; i++) {
180 if (strncmp(namelist[i], name, namelength) == 0 &&
181 namelength == strlen(namelist[i])) {
189 /* Run Ghostscript to write width/ascent/descent information for all
193 run_Ghostscript(char *PostScript_name, char *Mup_name)
198 char * output_option;
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);
207 /* use temp file as a /dev/null */
208 output_option = make_string("-sOutputFile=", GS_output);
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);
220 fprintf(stderr, "failed to execute gs\n");
226 execlp("gs", "gs", "-sDEVICE=bit", "-dQUIET",
227 "-sOutputFile=/dev/null", Mup_option,
228 PS_option, "-", (char *) 0);
231 fprintf(stderr, "failed to execute gs\n");
236 fprintf(stderr, "Ghostscript failed\n");
243 /* given two strings, get enough space to concatenate them,
244 * write them into the malloc-ed string, and return it. */
247 make_string(char *first_part, char *second_part)
251 if ((new_string = (char *) malloc(strlen(first_part)
252 + strlen(second_part) + 1)) == 0) {
253 fprintf(stderr, "malloc failed\n");
256 sprintf(new_string, "%s%s", first_part, second_part);
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
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"
280 "1 setflat % make bounding box very accurate\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"
285 "%------------------------------------------------------------------\n"
289 "% given a one-character string in \"character\",\n"
290 "% outputs its width in 1/1000ths of an inch\n"
293 " % get width of character\n"
294 " character stringwidth\n"
296 " % convert x to 1/1000th of an inch\n"
297 " pop 1000 mul 72 div round cvi\n"
300 " buff cvs (\\t) print print\n"
303 "%-----------------------------------\n"
305 "% given a one-character string in \"character\",\n"
306 "% outputs its height in 1/1000ths of an inch\n"
309 " % place character at (100, 100) and get its pathbbox\n"
312 " character true charpath flattenpath pathbbox\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"
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"
322 " % space is special, use 9 points for height\n"
323 " character ( ) eq { 9 add } if\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"
329 " % print the results\n"
330 " buff cvs (\\t) print print\n"
333 "%----------------------------------\n"
335 "% given a one-character string in \"character\",\n"
336 "% outputs its ascent in 1/1000ths of an inch\n"
339 " % place character at (100, 100) and get its pathbbox\n"
342 " character true charpath flattenpath pathbbox\n"
344 " % save the top y coordinate of the bbox\n"
345 " /top exch def pop pop pop\n"
347 " % ascent is top minus baseline\n"
350 " % space is special, use 6.8 points for ascent\n"
351 " character ( ) eq { 6.8 add } if\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"
357 " buff cvs (\\t) print print\n"
361 "%-----------------------------------\n"
363 "% generate width, height an ascent for a font.\n"
365 "% fname mupfname do_a_font\n"
368 " % save arguments for later use\n"
369 " /mupfname exch def\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"
378 " % Set up to use the desired font\n"
380 " 12 scalefont setfont\n"
382 " % Mup uses ASCII character codes from 32 through 126\n"
384 " dup buff cvs print\n"
385 " /val exch def character 0 val put\n"
389 " (\\t# ') print character print ('\\n) print\n"
392 " (PostScript:\\n) print\n"
395 "%-----------------------------------\n"
397 "% generate the output\n"
398 "PostScript_name cvn Mup_name do_a_font\n"
403 generate_PostScript_program(char *PS_file)
408 if ((file = open(PS_script_file, WRITE_FLAGS, 0644)) < 0) {
409 fprintf(stderr, "Can't generate '%s'\n", PS_script_file);
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) {
417 write(file, PS_file, strlen(PS_file));
418 write(file, ") run\n", 6);
421 length = strlen(PostScript_program);
422 if (write(file, PostScript_program, length) != length) {
423 fprintf(stderr, "generation of PostScript program failed\n");
429 /* remove the temp file and exit */
432 cleanup(int exitcode)
434 unlink(PS_script_file);