2 /* Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
3 Written by James Clark (jjc@jclark.uucp)
5 This file is part of groff.
7 groff is free software; you can redistribute it and/or modify it under
8 the terms of the GNU General Public License as published by the Free
9 Software Foundation; either version 1, or (at your option) any later
12 groff is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
17 You should have received a copy of the GNU General Public License along
18 with groff; see the file LICENSE. If not, write to the Free Software
19 Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
33 const char *const WS
= " \t\n\r";
35 struct font_char_metric
{
42 int italic_correction
;
43 int subscript_correction
;
46 struct font_kern_list
{
52 font_kern_list(int, int, int, font_kern_list
* = 0);
55 struct font_widths_cache
{
56 font_widths_cache
*next
;
60 font_widths_cache(int, int, font_widths_cache
*);
73 text_file(FILE *fp
, char *p
);
76 void error(const char *format
,
77 const errarg
&arg1
= empty_errarg
,
78 const errarg
&arg2
= empty_errarg
,
79 const errarg
&arg3
= empty_errarg
);
82 text_file::text_file(FILE *p
, char *s
)
83 : lineno(0), buf(0), size(0), skip_comments(1), fp(p
), path(s
)
87 text_file::~text_file()
101 buf
= new char [128];
110 if (illegal_input_char(c
))
111 error("illegal input character code `%1'", int(c
));
115 buf
= new char[size
*2];
116 memcpy(buf
, old_buf
, size
);
130 while (csspace(*ptr
))
132 if (*ptr
!= 0 && (!skip_comments
|| *ptr
!= '#'))
138 void text_file::error(const char *format
,
143 error_with_file_and_line(path
, lineno
, format
, arg1
, arg2
, arg3
);
149 font::font(const char *s
)
150 : special(0), ligatures(0), kern_hash_table(0), space_width(0),
151 ch(0), ch_used(0), ch_size(0), ch_index(0), nindices(0), widths_cache(0)
153 name
= new char[strlen(s
) + 1];
157 // load(); // for testing
164 if (kern_hash_table
) {
165 for (int i
= 0; i
< KERN_HASH_TABLE_SIZE
; i
++) {
166 font_kern_list
*kerns
= kern_hash_table
[i
];
168 font_kern_list
*tem
= kerns
;
173 a_delete kern_hash_table
;
176 a_delete internalname
;
177 while (widths_cache
) {
178 font_widths_cache
*tem
= widths_cache
;
179 widths_cache
= widths_cache
->next
;
184 static int scale_round(int n
, int x
, int y
)
186 assert(x
>= 0 && y
> 0);
191 if (n
<= (INT_MAX
- y2
)/x
)
193 return int(n
*double(x
)/double(y
) + .5);
196 if (-(unsigned)n
<= (-(unsigned)INT_MIN
- y2
)/x
)
198 return int(n
*double(x
)/double(y
) - .5);
202 inline int font::scale(int w
, int sz
)
204 return sz
== unitwidth
? w
: scale_round(w
, sz
, unitwidth
);
207 int font::get_skew(int c
, int point_size
, int sl
)
209 int h
= get_height(c
, point_size
);
210 return int(h
*tan((slant
+sl
)*M_PI
/180.0) + .5);
213 int font::contains(int c
)
215 return c
>= 0 && c
< nindices
&& ch_index
[c
] >= 0;
218 int font::is_special()
223 font_widths_cache::font_widths_cache(int ps
, int ch_size
,
224 font_widths_cache
*p
= 0)
225 : next(p
), point_size(ps
)
227 width
= new int[ch_size
];
228 for (int i
= 0; i
< ch_size
; i
++)
232 font_widths_cache::~font_widths_cache()
237 int font::get_width(int c
, int point_size
)
239 assert(c
>= 0 && c
< nindices
);
243 if (point_size
== unitwidth
)
247 widths_cache
= new font_widths_cache(point_size
, ch_size
);
248 else if (widths_cache
->point_size
!= point_size
) {
249 for (font_widths_cache
**p
= &widths_cache
; *p
; p
= &(*p
)->next
)
250 if ((*p
)->point_size
== point_size
)
253 font_widths_cache
*tem
= *p
;
255 tem
->next
= widths_cache
;
259 widths_cache
= new font_widths_cache(point_size
, ch_size
, widths_cache
);
261 int &w
= widths_cache
->width
[i
];
263 w
= scale(ch
[i
].width
, point_size
);
267 int font::get_height(int c
, int point_size
)
269 assert(c
>= 0 && c
< nindices
&& ch_index
[c
] >= 0);
270 return scale(ch
[ch_index
[c
]].height
, point_size
);
273 int font::get_depth(int c
, int point_size
)
275 assert(c
>= 0 && c
< nindices
&& ch_index
[c
] >= 0);
276 return scale(ch
[ch_index
[c
]].depth
, point_size
);
279 int font::get_italic_correction(int c
, int point_size
)
281 assert(c
>= 0 && c
< nindices
&& ch_index
[c
] >= 0);
282 return scale(ch
[ch_index
[c
]].italic_correction
, point_size
);
285 int font::get_left_italic_correction(int c
, int point_size
)
287 assert(c
>= 0 && c
< nindices
&& ch_index
[c
] >= 0);
288 return scale(ch
[ch_index
[c
]].pre_math_space
, point_size
);
291 int font::get_subscript_correction(int c
, int point_size
)
293 assert(c
>= 0 && c
< nindices
&& ch_index
[c
] >= 0);
294 return scale(ch
[ch_index
[c
]].subscript_correction
, point_size
);
297 int font::get_space_width(int point_size
)
299 return scale(space_width
, point_size
);
302 font_kern_list::font_kern_list(int c1
, int c2
, int n
, font_kern_list
*p
)
303 : i1(c1
), i2(c2
), amount(n
), next(p
)
307 inline int font::hash_kern(int i1
, int i2
)
309 int n
= ((i1
<< 10) + i2
) % KERN_HASH_TABLE_SIZE
;
310 return n
< 0 ? -n
: n
;
313 void font::add_kern(int i1
, int i2
, int amount
)
315 if (!kern_hash_table
) {
316 kern_hash_table
= new font_kern_list
*[KERN_HASH_TABLE_SIZE
];
317 for (int i
= 0; i
< KERN_HASH_TABLE_SIZE
; i
++)
318 kern_hash_table
[i
] = 0;
320 font_kern_list
**p
= kern_hash_table
+ hash_kern(i1
, i2
);
321 *p
= new font_kern_list(i1
, i2
, amount
, *p
);
324 int font::get_kern(int i1
, int i2
, int point_size
)
326 if (kern_hash_table
) {
327 for (font_kern_list
*p
= kern_hash_table
[hash_kern(i1
, i2
)]; p
; p
= p
->next
)
328 if (i1
== p
->i1
&& i2
== p
->i2
)
329 return scale(p
->amount
, point_size
);
334 int font::has_ligature(int mask
)
336 return mask
& ligatures
;
339 int font::get_character_type(int c
)
341 assert(c
>= 0 && c
< nindices
&& ch_index
[c
] >= 0);
342 return ch
[ch_index
[c
]].type
;
345 int font::get_code(int c
)
347 assert(c
>= 0 && c
< nindices
&& ch_index
[c
] >= 0);
348 return ch
[ch_index
[c
]].code
;
351 const char *font::get_name()
356 const char *font::get_internal_name()
361 void font::alloc_ch_index(int index
)
365 if (index
>= nindices
)
366 nindices
= index
+ 10;
367 ch_index
= new short[nindices
];
368 for (int i
= 0; i
< nindices
; i
++)
372 int old_nindices
= nindices
;
374 if (index
>= nindices
)
375 nindices
= index
+ 10;
376 short *old_ch_index
= ch_index
;
377 ch_index
= new short[nindices
];
378 memcpy(ch_index
, old_ch_index
, sizeof(short)*old_nindices
);
379 for (int i
= old_nindices
; i
< nindices
; i
++)
381 a_delete old_ch_index
;
385 void font::extend_ch()
388 ch
= new font_char_metric
[ch_size
= 16];
390 int old_ch_size
= ch_size
;
392 font_char_metric
*old_ch
= ch
;
393 ch
= new font_char_metric
[ch_size
];
394 memcpy(ch
, old_ch
, old_ch_size
*sizeof(font_char_metric
));
401 for (int i
= nindices
- 1; i
>= 0; i
--)
402 if (ch_index
[i
] >= 0)
406 short *old_ch_index
= ch_index
;
407 ch_index
= new short[i
];
408 memcpy(ch_index
, old_ch_index
, i
*sizeof(short));
409 a_delete old_ch_index
;
412 if (ch_used
< ch_size
) {
413 font_char_metric
*old_ch
= ch
;
414 ch
= new font_char_metric
[ch_used
];
415 memcpy(ch
, old_ch
, ch_used
*sizeof(font_char_metric
));
421 void font::add_entry(int index
, const font_char_metric
&metric
)
424 if (index
>= nindices
)
425 alloc_ch_index(index
);
426 assert(index
< nindices
);
427 if (ch_used
+ 1 >= ch_size
)
429 assert(ch_used
+ 1 < ch_size
);
430 ch_index
[index
] = ch_used
;
431 ch
[ch_used
++] = metric
;
434 void font::copy_entry(int new_index
, int old_index
)
436 assert(new_index
>= 0 && old_index
>= 0 && old_index
< nindices
);
437 if (new_index
>= nindices
)
438 alloc_ch_index(new_index
);
439 ch_index
[new_index
] = ch_index
[old_index
];
442 font
*font::load_font(const char *s
)
444 font
*f
= new font(s
);
456 if ((fp
= open_file(name
, &path
)) == NULL
) {
457 error("can't find font file `%1'", name
);
460 text_file
t(fp
, path
);
465 t
.error("missing charset command");
468 p
= strtok(t
.buf
, WS
);
469 if (strcmp(p
, "name") == 0) {
471 else if (strcmp(p
, "spacewidth") == 0) {
474 if (p
== 0 || sscanf(p
, "%d", &n
) != 1 || n
<= 0) {
475 t
.error("bad argument for spacewidth command");
480 else if (strcmp(p
, "slant") == 0) {
483 if (p
== 0 || sscanf(p
, "%lf", &n
) != 1 || n
>= 90.0 || n
<= -90.0) {
484 t
.error("bad argument for slant command", p
);
489 else if (strcmp(p
, "ligatures") == 0) {
492 if (p
== 0 || strcmp(p
, "0") == 0)
494 if (strcmp(p
, "ff") == 0)
496 else if (strcmp(p
, "fi") == 0)
498 else if (strcmp(p
, "fl") == 0)
500 else if (strcmp(p
, "ffi") == 0)
501 ligatures
|= LIG_ffi
;
502 else if (strcmp(p
, "ffl") == 0)
503 ligatures
|= LIG_ffl
;
505 t
.error("unrecognised ligature `%1'", p
);
510 else if (strcmp(p
, "internalname") == 0) {
513 t
.error("`internalname command requires argument");
516 internalname
= new char[strlen(p
) + 1];
517 strcpy(internalname
, p
);
519 else if (strcmp(p
, "special") == 0) {
522 else if (strcmp(p
, "kernpairs") != 0 && strcmp(p
, "charset") != 0) {
524 const char **argv
= new char *[nargv
];
528 const char **old_argv
= argv
;
529 int old_nargv
= nargv
;
531 argv
= new char *[nargv
];
532 memcpy(argv
, old_argv
, sizeof(char*)*old_nargv
);
538 handle_unknown_font_command(argc
, argv
);
548 if (strcmp(command
, "kernpairs") == 0) {
554 char *c1
= strtok(t
.buf
, WS
);
557 char *c2
= strtok(0, WS
);
564 t
.error("missing kern amount");
568 if (sscanf(p
, "%d", &n
) != 1) {
569 t
.error("bad kern amount `%1'", p
);
572 int i1
= name_to_index(c1
);
574 t
.error("illegal character `%1'", c1
);
577 int i2
= name_to_index(c2
);
579 t
.error("illegal character `%1'", c2
);
585 else if (strcmp(command
, "charset") == 0) {
593 char *nm
= strtok(t
.buf
, WS
);
595 continue; // I dont think this should happen
602 if (last_index
== -1) {
603 t
.error("first charset entry is duplicate");
606 if (strcmp(nm
, "---") == 0) {
607 t
.error("unnamed character cannot be duplicate");
610 int index
= name_to_index(nm
);
612 t
.error("illegal character `%1'", nm
);
615 copy_entry(index
, last_index
);
618 font_char_metric metric
;
621 metric
.pre_math_space
= 0;
622 metric
.italic_correction
= 0;
623 metric
.subscript_correction
= 0;
624 int nparms
= sscanf(p
, "%d,%d,%d,%d,%d,%d",
625 &metric
.width
, &metric
.height
, &metric
.depth
,
626 &metric
.italic_correction
,
627 &metric
.pre_math_space
,
628 &metric
.subscript_correction
);
630 t
.error("bad width for `%1'", nm
);
635 t
.error("missing character type for `%1'", nm
);
639 if (sscanf(p
, "%d", &type
) != 1) {
640 t
.error("bad character type for `%1'", nm
);
643 if (type
< 0 || type
> 255) {
644 t
.error("character code `%1' out of range", type
);
650 t
.error("missing code for `%1'", nm
);
654 metric
.code
= (int)strtol(p
, &ptr
, 0);
655 if (metric
.code
== 0 && ptr
== p
) {
656 t
.error("bad code `%1' for character `%2'", p
, nm
);
659 if (strcmp(nm
, "---") == 0) {
660 last_index
= number_to_index(metric
.code
);
661 add_entry(last_index
, metric
);
664 last_index
= name_to_index(nm
);
665 if (last_index
< 0) {
666 t
.error("illegal character `%1'", nm
);
669 add_entry(last_index
, metric
);
670 copy_entry(number_to_index(metric
.code
), last_index
);
674 if (last_index
== -1) {
675 t
.error("I didn't seem to find any characters");
680 t
.error("unrecognised command `%1' after `kernpairs' or `charset' command", command
);
685 t
.error("missing charset command");
688 if (space_width
== 0)
689 space_width
= scale_round(unitwidth
, res
, 72*3*sizescale
);
701 "unitwidth", &font::unitwidth
,
702 "paperwidth", &font::paperwidth
,
703 "paperlength", &font::paperlength
,
704 "spare1", &font::biggestfont
,
705 "biggestfont", &font::biggestfont
,
706 "spare2", &font::spare2
,
707 "sizescale", &font::sizescale
711 int font::load_desc()
716 if ((fp
= open_file("DESC", &path
)) == 0) {
717 error("can't open `DESC'");
720 text_file
t(fp
, path
);
724 char *p
= strtok(t
.buf
, WS
);
726 for (int i
= 0; !found
&& i
< sizeof(table
)/sizeof(table
[0]); i
++)
727 if (strcmp(table
[i
].command
, p
) == 0)
730 char *q
= strtok(0, WS
);
732 t
.error("missing value for command `%1'", p
);
735 //int *ptr = &(this->*(table[i-1].ptr));
736 int *ptr
= table
[i
-1].ptr
;
737 if (sscanf(q
, "%d", ptr
) != 1) {
738 t
.error("bad number `%1'", q
);
742 else if (strcmp("tcommand", p
) == 0) {
745 else if (strcmp("family", p
) == 0) {
748 t
.error("family command requires an argument");
751 char *tem
= new char[strlen(p
)+1];
755 else if (strcmp("fonts", p
) == 0) {
757 if (!p
|| sscanf(p
, "%d", &nfonts
) != 1 || nfonts
<= 0) {
758 t
.error("bad number of fonts `%1'", p
);
761 font_name_table
= new char *[nfonts
+1];
762 for (int i
= 0; i
< nfonts
; i
++) {
766 t
.error("end of file while reading list of fonts");
769 p
= strtok(t
.buf
, WS
);
771 char *temp
= new char[strlen(p
)+1];
773 font_name_table
[i
] = temp
;
777 t
.error("font count does not match number of fonts");
780 font_name_table
[nfonts
] = 0;
782 else if (strcmp("sizes", p
) == 0) {
790 t
.error("list of sizes must be terminated by `0'");
793 p
= strtok(t
.buf
, WS
);
796 switch (sscanf(p
, "%d-%d", &lower
, &upper
)) {
801 if (lower
<= upper
&& lower
>= 0)
805 t
.error("bad size range `%1'", p
);
809 int *old_sizes
= sizes
;
810 sizes
= new int[n
*2];
811 memcpy(sizes
, old_sizes
, n
*sizeof(int));
821 t
.error("must have some sizes");
825 else if (strcmp("styles", p
) == 0) {
826 int style_table_size
= 5;
827 style_table
= new char *[style_table_size
];
828 for (int j
= 0; j
< style_table_size
; j
++)
835 // leave room for terminating 0
836 if (i
+ 1 >= style_table_size
) {
837 const char **old_style_table
= style_table
;
838 style_table_size
*= 2;
839 style_table
= new char*[style_table_size
];
840 for (j
= 0; j
< i
; j
++)
841 style_table
[j
] = old_style_table
[j
];
842 for (; j
< style_table_size
; j
++)
844 a_delete old_style_table
;
846 char *tem
= new char[strlen(p
) + 1];
848 style_table
[i
++] = tem
;
851 else if (strcmp("charset", p
) == 0)
855 t
.error("missing `res' command");
858 if (unitwidth
== 0) {
859 t
.error("missing `unitwidth' command");
862 if (font_name_table
== 0) {
863 t
.error("missing `fonts' commmand");
867 t
.error("missing `sizes' command");
871 t
.error("bad `sizescale' value");
875 t
.error("bad `hor' value");
879 t
.error("bad `vert' value");
885 void font::handle_unknown_font_command(int, const char **)