chiark / gitweb /
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 }