chiark / gitweb /
Import upstream version 5.3.
[mup] / mup / mkmupfnt / mkmupfnt.c
CommitLineData
69695f33
MW
1char 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 */
38char *PS_script_file = "mkmupfnt.ps";
39#ifdef __DOS__
40char *GS_output = "mkmupfnt.tmp";
41#endif
42
43char Version[] = "5.3";
44
45void usage(char *program_name);
46void verify_valid_Mup_name(char *Mup_name);
47int name_matches(char *namelist[], char *name, int namelength);
48void run_Ghostscript(char *PostScript_name, char *Mup_name);
49char * make_string(char *first_part, char *second_part);
50void generate_PostScript_program(char *PS_file);
51void cleanup(int exitcode);
52
53int
54main(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
110char *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
122void
123usage(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
131char *family_names[] = {
132 "avantegarde",
133 "bookman",
134 "courier",
135 "helvetica",
136 "newcentury",
137 "palatino",
138 "times",
139 (char *) 0
140};
141
142char *font_names[] = {
143 "rom",
144 "bold",
145 "ital",
146 "boldital",
147 (char *) 0
148};
149
150void
151verify_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
174int
175name_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
192void
193run_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
246char *
247make_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
269char *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
402void
403generate_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
431void
432cleanup(int exitcode)
433{
434 unlink(PS_script_file);
435 exit(exitcode);
436}