4 /* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002, 2003, 2004, 2005
5 Free Software Foundation, Inc.
6 Written by James Clark (jjc@jclark.com)
8 This file is part of groff.
10 groff is free software; you can redistribute it and/or modify it under
11 the terms of the GNU General Public License as published by the Free
12 Software Foundation; either version 2, or (at your option) any later
15 groff is distributed in the hope that it will be useful, but WITHOUT ANY
16 WARRANTY; without even the implied warranty of MERCHANTABILITY or
17 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
20 You should have received a copy of the GNU General Public License along
21 with groff; see the file COPYING. If not, write to the Free Software
22 Foundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */
36 const char *const WS
= " \t\n\r";
38 struct font_char_metric
{
45 int italic_correction
;
46 int subscript_correction
;
47 char *special_device_coding
;
50 struct font_kern_list
{
56 font_kern_list(int, int, int, font_kern_list
* = 0);
59 struct font_widths_cache
{
60 font_widths_cache
*next
;
64 font_widths_cache(int, int, font_widths_cache
* = 0);
78 text_file(FILE *fp
, char *p
);
81 void error(const char *format
,
82 const errarg
&arg1
= empty_errarg
,
83 const errarg
&arg2
= empty_errarg
,
84 const errarg
&arg3
= empty_errarg
);
87 text_file::text_file(FILE *p
, char *s
)
88 : fp(p
), path(s
), lineno(0), size(0), skip_comments(1), silent(0), buf(0)
92 text_file::~text_file()
100 int text_file::next()
114 if (invalid_input_char(c
))
115 error("invalid input character code `%1'", int(c
));
119 buf
= new char[size
*2];
120 memcpy(buf
, old_buf
, size
);
134 while (csspace(*ptr
))
136 if (*ptr
!= 0 && (!skip_comments
|| *ptr
!= '#'))
142 void text_file::error(const char *format
,
148 error_with_file_and_line(path
, lineno
, format
, arg1
, arg2
, arg3
);
154 font::font(const char *s
)
155 : ligatures(0), kern_hash_table(0), space_width(0), ch_index(0), nindices(0),
156 ch(0), ch_used(0), ch_size(0), special(0), widths_cache(0)
158 name
= new char[strlen(s
) + 1];
162 // load(); // for testing
167 for (int i
= 0; i
< ch_used
; i
++)
168 if (ch
[i
].special_device_coding
)
169 a_delete ch
[i
].special_device_coding
;
172 if (kern_hash_table
) {
173 for (int i
= 0; i
< KERN_HASH_TABLE_SIZE
; i
++) {
174 font_kern_list
*kerns
= kern_hash_table
[i
];
176 font_kern_list
*tem
= kerns
;
181 a_delete kern_hash_table
;
184 a_delete internalname
;
185 while (widths_cache
) {
186 font_widths_cache
*tem
= widths_cache
;
187 widths_cache
= widths_cache
->next
;
192 static int scale_round(int n
, int x
, int y
)
194 assert(x
>= 0 && y
> 0);
199 if (n
<= (INT_MAX
- y2
)/x
)
201 return int(n
*double(x
)/double(y
) + .5);
204 if (-(unsigned)n
<= (-(unsigned)INT_MIN
- y2
)/x
)
206 return int(n
*double(x
)/double(y
) - .5);
210 inline int font::scale(int w
, int sz
)
212 return sz
== unitwidth
? w
: scale_round(w
, sz
, unitwidth
);
215 int font::unit_scale(double *value
, char unit
)
217 // we scale everything to inch
243 int font::get_skew(int c
, int point_size
, int sl
)
245 int h
= get_height(c
, point_size
);
246 return int(h
*tan((slant
+sl
)*PI
/180.0) + .5);
249 int font::contains(int c
)
251 return c
>= 0 && c
< nindices
&& ch_index
[c
] >= 0;
254 int font::is_special()
259 font_widths_cache::font_widths_cache(int ps
, int ch_size
,
260 font_widths_cache
*p
)
261 : next(p
), point_size(ps
)
263 width
= new int[ch_size
];
264 for (int i
= 0; i
< ch_size
; i
++)
268 font_widths_cache::~font_widths_cache()
273 int font::get_width(int c
, int point_size
)
275 assert(c
>= 0 && c
< nindices
);
279 if (point_size
== unitwidth
|| font::unscaled_charwidths
)
283 widths_cache
= new font_widths_cache(point_size
, ch_size
);
284 else if (widths_cache
->point_size
!= point_size
) {
285 font_widths_cache
**p
;
286 for (p
= &widths_cache
; *p
; p
= &(*p
)->next
)
287 if ((*p
)->point_size
== point_size
)
290 font_widths_cache
*tem
= *p
;
292 tem
->next
= widths_cache
;
296 widths_cache
= new font_widths_cache(point_size
, ch_size
, widths_cache
);
298 int &w
= widths_cache
->width
[i
];
300 w
= scale(ch
[i
].width
, point_size
);
304 int font::get_height(int c
, int point_size
)
306 assert(c
>= 0 && c
< nindices
&& ch_index
[c
] >= 0);
307 return scale(ch
[ch_index
[c
]].height
, point_size
);
310 int font::get_depth(int c
, int point_size
)
312 assert(c
>= 0 && c
< nindices
&& ch_index
[c
] >= 0);
313 return scale(ch
[ch_index
[c
]].depth
, point_size
);
316 int font::get_italic_correction(int c
, int point_size
)
318 assert(c
>= 0 && c
< nindices
&& ch_index
[c
] >= 0);
319 return scale(ch
[ch_index
[c
]].italic_correction
, point_size
);
322 int font::get_left_italic_correction(int c
, int point_size
)
324 assert(c
>= 0 && c
< nindices
&& ch_index
[c
] >= 0);
325 return scale(ch
[ch_index
[c
]].pre_math_space
, point_size
);
328 int font::get_subscript_correction(int c
, int point_size
)
330 assert(c
>= 0 && c
< nindices
&& ch_index
[c
] >= 0);
331 return scale(ch
[ch_index
[c
]].subscript_correction
, point_size
);
334 int font::get_space_width(int point_size
)
336 return scale(space_width
, point_size
);
339 font_kern_list::font_kern_list(int c1
, int c2
, int n
, font_kern_list
*p
)
340 : i1(c1
), i2(c2
), amount(n
), next(p
)
344 inline int font::hash_kern(int i1
, int i2
)
346 int n
= ((i1
<< 10) + i2
) % KERN_HASH_TABLE_SIZE
;
347 return n
< 0 ? -n
: n
;
350 void font::add_kern(int i1
, int i2
, int amount
)
352 if (!kern_hash_table
) {
353 kern_hash_table
= new font_kern_list
*[int(KERN_HASH_TABLE_SIZE
)];
354 for (int i
= 0; i
< KERN_HASH_TABLE_SIZE
; i
++)
355 kern_hash_table
[i
] = 0;
357 font_kern_list
**p
= kern_hash_table
+ hash_kern(i1
, i2
);
358 *p
= new font_kern_list(i1
, i2
, amount
, *p
);
361 int font::get_kern(int i1
, int i2
, int point_size
)
363 if (kern_hash_table
) {
364 for (font_kern_list
*p
= kern_hash_table
[hash_kern(i1
, i2
)]; p
; p
= p
->next
)
365 if (i1
== p
->i1
&& i2
== p
->i2
)
366 return scale(p
->amount
, point_size
);
371 int font::has_ligature(int mask
)
373 return mask
& ligatures
;
376 int font::get_character_type(int c
)
378 assert(c
>= 0 && c
< nindices
&& ch_index
[c
] >= 0);
379 return ch
[ch_index
[c
]].type
;
382 int font::get_code(int c
)
384 assert(c
>= 0 && c
< nindices
&& ch_index
[c
] >= 0);
385 return ch
[ch_index
[c
]].code
;
388 const char *font::get_name()
393 const char *font::get_internal_name()
398 const char *font::get_special_device_encoding(int c
)
400 assert(c
>= 0 && c
< nindices
&& ch_index
[c
] >= 0);
401 return ch
[ch_index
[c
]].special_device_coding
;
404 const char *font::get_image_generator()
406 return image_generator
;
409 void font::alloc_ch_index(int idx
)
415 ch_index
= new int[nindices
];
416 for (int i
= 0; i
< nindices
; i
++)
420 int old_nindices
= nindices
;
424 int *old_ch_index
= ch_index
;
425 ch_index
= new int[nindices
];
426 memcpy(ch_index
, old_ch_index
, sizeof(int)*old_nindices
);
427 for (int i
= old_nindices
; i
< nindices
; i
++)
429 a_delete old_ch_index
;
433 void font::extend_ch()
436 ch
= new font_char_metric
[ch_size
= 16];
438 int old_ch_size
= ch_size
;
440 font_char_metric
*old_ch
= ch
;
441 ch
= new font_char_metric
[ch_size
];
442 memcpy(ch
, old_ch
, old_ch_size
*sizeof(font_char_metric
));
450 for (i
= nindices
- 1; i
>= 0; i
--)
451 if (ch_index
[i
] >= 0)
455 int *old_ch_index
= ch_index
;
456 ch_index
= new int[i
];
457 memcpy(ch_index
, old_ch_index
, i
*sizeof(int));
458 a_delete old_ch_index
;
461 if (ch_used
< ch_size
) {
462 font_char_metric
*old_ch
= ch
;
463 ch
= new font_char_metric
[ch_used
];
464 memcpy(ch
, old_ch
, ch_used
*sizeof(font_char_metric
));
470 void font::add_entry(int idx
, const font_char_metric
&metric
)
475 assert(idx
< nindices
);
476 if (ch_used
+ 1 >= ch_size
)
478 assert(ch_used
+ 1 < ch_size
);
479 ch_index
[idx
] = ch_used
;
480 ch
[ch_used
++] = metric
;
483 void font::copy_entry(int new_index
, int old_index
)
485 assert(new_index
>= 0 && old_index
>= 0 && old_index
< nindices
);
486 if (new_index
>= nindices
)
487 alloc_ch_index(new_index
);
488 ch_index
[new_index
] = ch_index
[old_index
];
491 font
*font::load_font(const char *s
, int *not_found
, int head_only
)
493 font
*f
= new font(s
);
494 if (!f
->load(not_found
, head_only
)) {
501 static char *trim_arg(char *p
)
507 char *q
= strchr(p
, '\0');
508 while (q
> p
&& csspace(q
[-1]))
514 int font::scan_papersize(const char *p
,
515 const char **size
, double *length
, double *width
)
524 if (sscanf(pp
, "%lf%1[ipPc],%lf%1[ipPc]", &l
, lu
, &w
, wu
) == 4
526 && unit_scale(&l
, lu
[0]) && unit_scale(&w
, wu
[0])) {
538 for (i
= 0; i
< NUM_PAPERSIZES
; i
++)
539 if (strcasecmp(papersizes
[i
].name
, pp
) == 0) {
541 *length
= papersizes
[i
].length
;
543 *width
= papersizes
[i
].width
;
545 *size
= papersizes
[i
].name
;
549 FILE *f
= fopen(p
, "r");
554 char *linep
= strchr(line
, '\0');
555 // skip final newline, if any
556 if (*(--linep
) == '\n')
566 // If the font can't be found, then if not_found is non-NULL, it will be set
567 // to 1 otherwise a message will be printed.
569 int font::load(int *not_found
, int head_only
)
573 if ((fp
= open_file(name
, &path
)) == NULL
) {
577 error("can't find font file `%1'", name
);
580 text_file
t(fp
, path
);
582 t
.silent
= head_only
;
586 t
.error("missing charset command");
589 p
= strtok(t
.buf
, WS
);
590 if (strcmp(p
, "name") == 0) {
592 else if (strcmp(p
, "spacewidth") == 0) {
595 if (p
== 0 || sscanf(p
, "%d", &n
) != 1 || n
<= 0) {
596 t
.error("bad argument for spacewidth command");
601 else if (strcmp(p
, "slant") == 0) {
604 if (p
== 0 || sscanf(p
, "%lf", &n
) != 1 || n
>= 90.0 || n
<= -90.0) {
605 t
.error("bad argument for slant command", p
);
610 else if (strcmp(p
, "ligatures") == 0) {
613 if (p
== 0 || strcmp(p
, "0") == 0)
615 if (strcmp(p
, "ff") == 0)
617 else if (strcmp(p
, "fi") == 0)
619 else if (strcmp(p
, "fl") == 0)
621 else if (strcmp(p
, "ffi") == 0)
622 ligatures
|= LIG_ffi
;
623 else if (strcmp(p
, "ffl") == 0)
624 ligatures
|= LIG_ffl
;
626 t
.error("unrecognised ligature `%1'", p
);
631 else if (strcmp(p
, "internalname") == 0) {
634 t
.error("`internalname command requires argument");
637 internalname
= new char[strlen(p
) + 1];
638 strcpy(internalname
, p
);
640 else if (strcmp(p
, "special") == 0) {
643 else if (strcmp(p
, "kernpairs") != 0 && strcmp(p
, "charset") != 0) {
646 handle_unknown_font_command(command
, trim_arg(p
), t
.path
, t
.lineno
);
657 if (strcmp(command
, "kernpairs") == 0) {
663 char *c1
= strtok(t
.buf
, WS
);
666 char *c2
= strtok(0, WS
);
673 t
.error("missing kern amount");
677 if (sscanf(p
, "%d", &n
) != 1) {
678 t
.error("bad kern amount `%1'", p
);
681 int i1
= name_to_index(c1
);
683 t
.error("invalid character `%1'", c1
);
686 int i2
= name_to_index(c2
);
688 t
.error("invalid character `%1'", c2
);
694 else if (strcmp(command
, "charset") == 0) {
702 char *nm
= strtok(t
.buf
, WS
);
704 continue; // I dont think this should happen
711 if (last_index
== -1) {
712 t
.error("first charset entry is duplicate");
715 if (strcmp(nm
, "---") == 0) {
716 t
.error("unnamed character cannot be duplicate");
719 int idx
= name_to_index(nm
);
721 t
.error("invalid character `%1'", nm
);
724 copy_entry(idx
, last_index
);
727 font_char_metric metric
;
730 metric
.pre_math_space
= 0;
731 metric
.italic_correction
= 0;
732 metric
.subscript_correction
= 0;
733 int nparms
= sscanf(p
, "%d,%d,%d,%d,%d,%d",
734 &metric
.width
, &metric
.height
, &metric
.depth
,
735 &metric
.italic_correction
,
736 &metric
.pre_math_space
,
737 &metric
.subscript_correction
);
739 t
.error("bad width for `%1'", nm
);
744 t
.error("missing character type for `%1'", nm
);
748 if (sscanf(p
, "%d", &type
) != 1) {
749 t
.error("bad character type for `%1'", nm
);
752 if (type
< 0 || type
> 255) {
753 t
.error("character type `%1' out of range", type
);
759 t
.error("missing code for `%1'", nm
);
763 metric
.code
= (int)strtol(p
, &ptr
, 0);
764 if (metric
.code
== 0 && ptr
== p
) {
765 t
.error("bad code `%1' for character `%2'", p
, nm
);
769 if ((p
== NULL
) || (strcmp(p
, "--") == 0)) {
770 metric
.special_device_coding
= NULL
;
773 char *nam
= new char[strlen(p
) + 1];
775 metric
.special_device_coding
= nam
;
777 if (strcmp(nm
, "---") == 0) {
778 last_index
= number_to_index(metric
.code
);
779 add_entry(last_index
, metric
);
782 last_index
= name_to_index(nm
);
783 if (last_index
< 0) {
784 t
.error("invalid character `%1'", nm
);
787 add_entry(last_index
, metric
);
788 copy_entry(number_to_index(metric
.code
), last_index
);
792 if (last_index
== -1) {
793 t
.error("I didn't seem to find any characters");
798 t
.error("unrecognised command `%1' after `kernpairs' or `charset' command", command
);
803 t
.error("missing charset command");
806 if (space_width
== 0)
807 space_width
= scale_round(unitwidth
, res
, 72*3*sizescale
);
816 { "res", &font::res
},
817 { "hor", &font::hor
},
818 { "vert", &font::vert
},
819 { "unitwidth", &font::unitwidth
},
820 { "paperwidth", &font::paperwidth
},
821 { "paperlength", &font::paperlength
},
822 { "spare1", &font::biggestfont
},
823 { "biggestfont", &font::biggestfont
},
824 { "spare2", &font::spare2
},
825 { "sizescale", &font::sizescale
},
828 int font::load_desc()
833 if ((fp
= open_file("DESC", &path
)) == 0) {
834 error("can't find `DESC' file");
837 text_file
t(fp
, path
);
841 char *p
= strtok(t
.buf
, WS
);
844 for (idx
= 0; !found
&& idx
< sizeof(table
)/sizeof(table
[0]); idx
++)
845 if (strcmp(table
[idx
].command
, p
) == 0)
848 char *q
= strtok(0, WS
);
850 t
.error("missing value for command `%1'", p
);
853 //int *ptr = &(this->*(table[idx-1].ptr));
854 int *ptr
= table
[idx
-1].ptr
;
855 if (sscanf(q
, "%d", ptr
) != 1) {
856 t
.error("bad number `%1'", q
);
860 else if (strcmp("family", p
) == 0) {
863 t
.error("family command requires an argument");
866 char *tem
= new char[strlen(p
)+1];
870 else if (strcmp("fonts", p
) == 0) {
872 if (!p
|| sscanf(p
, "%d", &nfonts
) != 1 || nfonts
<= 0) {
873 t
.error("bad number of fonts `%1'", p
);
876 font_name_table
= (const char **)new char *[nfonts
+1];
877 for (int i
= 0; i
< nfonts
; i
++) {
881 t
.error("end of file while reading list of fonts");
884 p
= strtok(t
.buf
, WS
);
886 char *temp
= new char[strlen(p
)+1];
888 font_name_table
[i
] = temp
;
892 t
.error("font count does not match number of fonts");
895 font_name_table
[nfonts
] = 0;
897 else if (strcmp("papersize", p
) == 0) {
900 t
.error("papersize command requires an argument");
905 double unscaled_paperwidth
, unscaled_paperlength
;
906 if (scan_papersize(p
, &papersize
, &unscaled_paperlength
,
907 &unscaled_paperwidth
)) {
908 paperwidth
= int(unscaled_paperwidth
* res
+ 0.5);
909 paperlength
= int(unscaled_paperlength
* res
+ 0.5);
916 t
.error("bad paper size");
920 else if (strcmp("unscaled_charwidths", p
) == 0)
921 unscaled_charwidths
= 1;
922 else if (strcmp("pass_filenames", p
) == 0)
924 else if (strcmp("sizes", p
) == 0) {
932 t
.error("list of sizes must be terminated by `0'");
935 p
= strtok(t
.buf
, WS
);
938 switch (sscanf(p
, "%d-%d", &lower
, &upper
)) {
943 if (lower
<= upper
&& lower
>= 0)
947 t
.error("bad size range `%1'", p
);
951 int *old_sizes
= sizes
;
952 sizes
= new int[n
*2];
953 memcpy(sizes
, old_sizes
, n
*sizeof(int));
963 t
.error("must have some sizes");
967 else if (strcmp("styles", p
) == 0) {
968 int style_table_size
= 5;
969 style_table
= (const char **)new char *[style_table_size
];
971 for (j
= 0; j
< style_table_size
; j
++)
978 // leave room for terminating 0
979 if (i
+ 1 >= style_table_size
) {
980 const char **old_style_table
= style_table
;
981 style_table_size
*= 2;
982 style_table
= (const char **)new char*[style_table_size
];
983 for (j
= 0; j
< i
; j
++)
984 style_table
[j
] = old_style_table
[j
];
985 for (; j
< style_table_size
; j
++)
987 a_delete old_style_table
;
989 char *tem
= new char[strlen(p
) + 1];
991 style_table
[i
++] = tem
;
994 else if (strcmp("tcommand", p
) == 0)
996 else if (strcmp("use_charnames_in_special", p
) == 0)
997 use_charnames_in_special
= 1;
998 else if (strcmp("image_generator", p
) == 0) {
1001 t
.error("image_generator command requires an argument");
1004 image_generator
= strsave(p
);
1006 else if (strcmp("charset", p
) == 0)
1008 else if (unknown_desc_command_handler
) {
1010 p
= strtok(0, "\n");
1011 (*unknown_desc_command_handler
)(command
, trim_arg(p
), t
.path
, t
.lineno
);
1015 t
.error("missing `res' command");
1018 if (unitwidth
== 0) {
1019 t
.error("missing `unitwidth' command");
1022 if (font_name_table
== 0) {
1023 t
.error("missing `fonts' command");
1027 t
.error("missing `sizes' command");
1030 if (sizescale
< 1) {
1031 t
.error("bad `sizescale' value");
1035 t
.error("bad `hor' value");
1039 t
.error("bad `vert' value");
1045 void font::handle_unknown_font_command(const char *, const char *,
1050 FONT_COMMAND_HANDLER
1051 font::set_unknown_desc_command_handler(FONT_COMMAND_HANDLER func
)
1053 FONT_COMMAND_HANDLER prev
= unknown_desc_command_handler
;
1054 unknown_desc_command_handler
= func
;