/* Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
Written by James Clark (jjc@jclark.uucp)
This file is part of groff.
groff is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation; either version 1, or (at your option) any later
groff is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
You should have received a copy of the GNU General Public License along
with groff; see the file LICENSE. If not, write to the Free Software
Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
const char *const WS
= " \t\n\r";
struct font_char_metric
{
int subscript_correction
;
font_kern_list(int, int, int, font_kern_list
* = 0);
text_file(FILE *fp
, char *p
);
void error(const char *format
,
const errarg
&arg1
= empty_errarg
,
const errarg
&arg2
= empty_errarg
,
const errarg
&arg3
= empty_errarg
);
text_file::text_file(FILE *p
, char *s
)
: lineno(0), buf(0), size(0), skip_comments(1), fp(p
), path(s
)
if (fgets(buf
, size
, fp
) == 0)
if (*ptr
!= 0 && (!skip_comments
|| *ptr
!= '#'))
void text_file::error(const char *format
,
error_with_file_and_line(path
, lineno
, format
, arg1
, arg2
, arg3
);
font::font(const char *s
)
: special(0), ligatures(0), kern_hash_table(0), space_width(0),
ch(0), ch_used(0), ch_size(0), ch_index(0), nindices(0)
name
= new char[strlen(s
) + 1];
// load(); // for testing
for (int i
= 0; i
< KERN_HASH_TABLE_SIZE
; i
++) {
font_kern_list
*kerns
= kern_hash_table
[i
];
font_kern_list
*tem
= kerns
;
static int scale_round(int n
, int x
, int y
)
if (n
<= (INT_MAX
- y2
)/x
)
if (-(unsigned)n
<= (-(unsigned)INT_MIN
- y2
)/x
)
return int(n
*double(x
)/double(y
) + .5);
inline int font::scale(int w
, int sz
)
return sz
== unitwidth
? w
: scale_round(w
, sz
, unitwidth
);
int font::get_skew(int c
, int point_size
, int sl
)
int h
= get_height(c
, point_size
);
return int(h
*tan((slant
+sl
)*M_PI
/180.0) + .5);
int font::contains(int c
)
return c
>= 0 && c
< nindices
&& ch_index
[c
] >= 0;
int font::get_width(int c
, int point_size
)
assert(c
>= 0 && c
< nindices
&& ch_index
[c
] >= 0);
return scale(ch
[ch_index
[c
]].width
, point_size
);
int font::get_height(int c
, int point_size
)
assert(c
>= 0 && c
< nindices
&& ch_index
[c
] >= 0);
return scale(ch
[ch_index
[c
]].height
, point_size
);
int font::get_depth(int c
, int point_size
)
assert(c
>= 0 && c
< nindices
&& ch_index
[c
] >= 0);
return scale(ch
[ch_index
[c
]].depth
, point_size
);
int font::get_italic_correction(int c
, int point_size
)
assert(c
>= 0 && c
< nindices
&& ch_index
[c
] >= 0);
return scale(ch
[ch_index
[c
]].italic_correction
, point_size
);
int font::get_left_italic_correction(int c
, int point_size
)
assert(c
>= 0 && c
< nindices
&& ch_index
[c
] >= 0);
return scale(ch
[ch_index
[c
]].pre_math_space
, point_size
);
int font::get_subscript_correction(int c
, int point_size
)
assert(c
>= 0 && c
< nindices
&& ch_index
[c
] >= 0);
return scale(ch
[ch_index
[c
]].subscript_correction
, point_size
);
int font::get_space_width(int point_size
)
return scale(space_width
, point_size
);
font_kern_list::font_kern_list(int c1
, int c2
, int n
, font_kern_list
*p
)
: i1(c1
), i2(c2
), amount(n
), next(p
)
inline int font::hash_kern(int i1
, int i2
)
int n
= ((i1
<< 10) + i2
) % KERN_HASH_TABLE_SIZE
;
void font::add_kern(int i1
, int i2
, int amount
)
kern_hash_table
= new font_kern_list
*[KERN_HASH_TABLE_SIZE
];
for (int i
= 0; i
< KERN_HASH_TABLE_SIZE
; i
++)
font_kern_list
**p
= kern_hash_table
+ hash_kern(i1
, i2
);
*p
= new font_kern_list(i1
, i2
, amount
, *p
);
int font::get_kern(int i1
, int i2
, int point_size
)
for (font_kern_list
*p
= kern_hash_table
[hash_kern(i1
, i2
)]; p
; p
= p
->next
)
if (i1
== p
->i1
&& i2
== p
->i2
)
return scale(p
->amount
, point_size
);
int font::has_ligature(int mask
)
int font::get_character_type(int c
)
assert(c
>= 0 && c
< nindices
&& ch_index
[c
] >= 0);
return ch
[ch_index
[c
]].type
;
unsigned char font::get_code(int c
)
assert(c
>= 0 && c
< nindices
&& ch_index
[c
] >= 0);
return ch
[ch_index
[c
]].code
;
const char *font::get_name()
const char *font::get_internal_name()
void font::alloc_ch_index(int index
)
ch_index
= new short[nindices
];
for (int i
= 0; i
< nindices
; i
++)
int old_nindices
= nindices
;
short *old_ch_index
= ch_index
;
ch_index
= new short[nindices
];
memcpy(ch_index
, old_ch_index
, sizeof(short)*old_nindices
);
for (int i
= old_nindices
; i
< nindices
; i
++)
ch
= new font_char_metric
[ch_size
= 16];
int old_ch_size
= ch_size
;
font_char_metric
*old_ch
= ch
;
ch
= new font_char_metric
[ch_size
];
memcpy(ch
, old_ch
, old_ch_size
*sizeof(font_char_metric
));
for (int i
= nindices
- 1; i
>= 0; i
--)
short *old_ch_index
= ch_index
;
memcpy(ch_index
, old_ch_index
, i
*sizeof(short));
font_char_metric
*old_ch
= ch
;
ch
= new font_char_metric
[ch_used
];
memcpy(ch
, old_ch
, ch_used
*sizeof(font_char_metric
));
void font::add_entry(int index
, const font_char_metric
&metric
)
assert(index
< nindices
);
if (ch_used
+ 1 >= ch_size
)
assert(ch_used
+ 1 < ch_size
);
ch_index
[index
] = ch_used
;
void font::copy_entry(int new_index
, int old_index
)
assert(new_index
>= 0 && old_index
>= 0 && old_index
< nindices
);
if (new_index
>= nindices
)
alloc_ch_index(new_index
);
ch_index
[new_index
] = ch_index
[old_index
];
font
*font::load_font(const char *s
)
if ((fp
= open_file(name
, &path
)) == NULL
) {
error("can't find font file `%1'", name
);
t
.error("missing charset command");
if (strcmp(p
, "name") == 0) {
else if (strcmp(p
, "spacewidth") == 0) {
if (p
== 0 || sscanf(p
, "%d", &n
) != 1 || n
<= 0) {
t
.error("bad argument for spacewidth command");
else if (strcmp(p
, "slant") == 0) {
if (p
== 0 || sscanf(p
, "%lf", &n
) != 1 || n
>= 90.0 || n
<= -90.0) {
t
.error("bad argument for slant command", p
);
else if (strcmp(p
, "ligatures") == 0) {
if (p
== 0 || strcmp(p
, "0") == 0)
if (strcmp(p
, "ff") == 0)
else if (strcmp(p
, "fi") == 0)
else if (strcmp(p
, "fl") == 0)
else if (strcmp(p
, "ffi") == 0)
else if (strcmp(p
, "ffl") == 0)
t
.error("unrecognised ligature `%1'", p
);
else if (strcmp(p
, "internalname") == 0) {
t
.error("`internalname command requires argument");
internalname
= new char[strlen(p
) + 1];
else if (strcmp(p
, "special") == 0) {
else if (strcmp(p
, "kernpairs") != 0 && strcmp(p
, "charset") != 0) {
const char **argv
= new char *[nargv
];
const char **old_argv
= argv
;
argv
= new char *[nargv
];
memcpy(argv
, old_argv
, sizeof(char*)*old_nargv
);
handle_unknown_font_command(argc
, argv
);
if (strcmp(command
, "kernpairs") == 0) {
char *c1
= strtok(t
.buf
, WS
);
char *c2
= strtok(0, WS
);
t
.error("missing kern amount");
if (sscanf(p
, "%d", &n
) != 1) {
t
.error("bad kern amount `%1'", p
);
int i1
= name_to_index(c1
);
t
.error("illegal character `%1'", c1
);
int i2
= name_to_index(c2
);
t
.error("illegal character `%1'", c2
);
else if (strcmp(command
, "charset") == 0) {
char *nm
= strtok(t
.buf
, WS
);
continue; // I dont think this should happen
t
.error("first charset entry is duplicate");
if (strcmp(nm
, "---") == 0) {
t
.error("unnamed character cannot be duplicate");
int index
= name_to_index(nm
);
t
.error("illegal character `%1'", nm
);
copy_entry(index
, last_index
);
metric
.pre_math_space
= 0;
metric
.italic_correction
= 0;
metric
.subscript_correction
= 0;
int nparms
= sscanf(p
, "%d,%d,%d,%d,%d,%d",
&metric
.width
, &metric
.height
, &metric
.depth
,
&metric
.italic_correction
,
&metric
.subscript_correction
);
t
.error("bad width for `%1'", nm
);
t
.error("missing character type for `%1'", nm
);
if (sscanf(p
, "%d", &type
) != 1) {
t
.error("bad character type for `%1'", nm
);
if (type
< 0 || type
> 255) {
t
.error("character code `%1' out of range", type
);
t
.error("missing code for `%1'", nm
);
long code
= strtol(p
, &ptr
, 0);
if (code
== 0 && ptr
== p
) {
t
.error("bad code `%1' for character `%2'", p
, nm
);
if (code
< 0 || code
>= 256) {
t
.error("code %1 for character `%2' not between 0 and 255",
metric
.code
= (unsigned char)code
;
if (strcmp(nm
, "---") == 0) {
last_index
= number_to_index(metric
.code
);
add_entry(last_index
, metric
);
last_index
= name_to_index(nm
);
t
.error("illegal character `%1'", nm
);
add_entry(last_index
, metric
);
copy_entry(number_to_index(metric
.code
), last_index
);
t
.error("I didn't seem to find any characters");
t
.error("unrecognised command `%1' after `kernpairs' or `charset' command", command
);
t
.error("missing charset command");
space_width
= scale_round(unitwidth
, res
, 72*3*sizescale
);
"unitwidth", &font::unitwidth
,
"paperwidth", &font::paperwidth
,
"paperlength", &font::paperlength
,
"spare1", &font::biggestfont
,
"biggestfont", &font::biggestfont
,
"sizescale", &font::sizescale
if ((fp
= open_file("DESC", &path
)) == 0) {
error("can't open `DESC'");
char *p
= strtok(t
.buf
, WS
);
for (int i
= 0; !found
&& i
< sizeof(table
)/sizeof(table
[0]); i
++)
if (strcmp(table
[i
].command
, p
) == 0)
t
.error("missing value for command `%1'", p
);
//int *ptr = &(this->*(table[i-1].ptr));
int *ptr
= table
[i
-1].ptr
;
if (sscanf(q
, "%d", ptr
) != 1) {
t
.error("bad number `%1'", q
);
else if (strcmp("tcommand", p
) == 0) {
else if (strcmp("family", p
) == 0) {
t
.error("family command requires an argument");
char *tem
= new char[strlen(p
)+1];
else if (strcmp("fonts", p
) == 0) {
if (!p
|| sscanf(p
, "%d", &nfonts
) != 1 || nfonts
<= 0) {
t
.error("bad number of fonts `%1'", p
);
font_name_table
= new char *[nfonts
+1];
for (int i
= 0; i
< nfonts
; i
++) {
t
.error("end of file while reading list of fonts");
char *temp
= new char[strlen(p
)+1];
font_name_table
[i
] = temp
;
t
.error("font count does not match number of fonts");
font_name_table
[nfonts
] = 0;
else if (strcmp("sizes", p
) == 0) {
t
.error("list of sizes must be terminated by `0'");
switch (sscanf(p
, "%d-%d", &lower
, &upper
)) {
if (lower
<= upper
&& lower
>= 0)
t
.error("bad size range `%1'", p
);
memcpy(sizes
, old_sizes
, n
*sizeof(int));
t
.error("must have some sizes");
else if (strcmp("styles", p
) == 0) {
int style_table_size
= 5;
style_table
= new char *[style_table_size
];
for (int j
= 0; j
< style_table_size
; j
++)
// leave room for terminating 0
if (i
+ 1 >= style_table_size
) {
const char **old_style_table
= style_table
;
style_table
= new char*[style_table_size
];
style_table
[j
] = old_style_table
[j
];
for (; j
< style_table_size
; j
++)
char *tem
= new char[strlen(p
) + 1];
else if (strcmp("charset", p
) == 0)
t
.error("missing `res' command");
t
.error("missing `unitwidth' command");
if (font_name_table
== 0) {
t
.error("missing `fonts' commmand");
t
.error("missing `sizes' command");
t
.error("bad `sizescale' value");
t
.error("bad `hor' value");
t
.error("bad `vert' value");
void font::handle_unknown_font_command(int, const char **)