Commit | Line | Data |
---|---|---|
fac14bbe MW |
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 | } |