2 tfm-reader.cc -- implement Tex_font_metric_reader
4 source file of the GNU LilyPond music typesetter
6 (c) 1999--2003 Jan Nieuwenhuizen <janneke@gnu.org>
9 some code shamelessly copied from GNU fontutils-0.6/tfm/tfm_input.c
12 #include "tfm-reader.hh"
13 #include "string-convert.hh"
17 #define format_string String_convert::form_string
20 static const Real
fix_to_real (Fix f
);
23 Tex_font_metric_reader::Tex_font_metric_reader (String name
)
27 for (int i
=0; i
< TFM_SIZE
; i
++)
28 ascii_to_metric_idx_
.push (-1);
39 Real r
= f
/ FIX_UNITY
+ ((Real
) (f
% FIX_UNITY
) / (Real
) FIX_UNITY
);
43 /* Most quantities are fixed-point fractions. */
46 Tex_font_metric_reader::get_U32_fix ()
48 return fix_to_real (input_
.get_U32 ());
51 /* Dimensions are a `Fix' scaled by the design size. */
54 Tex_font_metric_reader::get_U32_fix_scaled ()
56 return get_U32_fix () * info_
.design_size
;
60 Tex_font_metric_reader::get_bcpl_string ()
62 U8 length_u8
= input_
.get_U8 ();
63 String str
= input_
.get_string (length_u8
);
67 /* Here we read the information at the beginning of the file. We store
68 the result into the static variables `global_info' and
71 Tex_font_metric_reader::read_header ()
73 U16 file_length
= input_
.get_U16 ();
75 U16 header_length
= input_
.get_U16 ();
77 info_
.first_charcode
= input_
.get_U16 ();
78 info_
.last_charcode
= input_
.get_U16 ();
79 U16 width_word_count
= input_
.get_U16 ();
80 U16 height_word_count
= input_
.get_U16 ();
81 U16 depth_word_count
= input_
.get_U16 ();
82 U16 italic_correction_word_count
= input_
.get_U16 ();
83 U16 lig_kern_word_count
= input_
.get_U16 ();
84 U16 kern_word_count
= input_
.get_U16 ();
85 (void)kern_word_count
;
86 U16 extensible_word_count
= input_
.get_U16 ();
87 (void)extensible_word_count
;
89 header_
.param_word_count
= input_
.get_U16 ();
90 info_
.parameter_count
= header_
.param_word_count
;
92 header_
.char_info_pos
= (6 + header_length
) * 4;
93 header_
.width_pos
= header_
.char_info_pos
94 + (info_
.last_charcode
95 - info_
.first_charcode
+ 1) * 4;
96 header_
.height_pos
= header_
.width_pos
+ width_word_count
* 4;
97 header_
.depth_pos
= header_
.height_pos
+ height_word_count
* 4;
98 header_
.italic_correction_pos
= header_
.depth_pos
99 + depth_word_count
* 4;
100 header_
.lig_kern_pos
= header_
.italic_correction_pos
101 + italic_correction_word_count
* 4;
102 header_
.kern_pos
= header_
.lig_kern_pos
+ lig_kern_word_count
* 4;
103 /* We don't care about the extensible table. */
105 if (header_length
< 2)
106 /* Not using ngettext's plural feature here, as this message is
107 more of a programming error. */
108 error (_f ("TFM header of `%s' has only %u word (s)",
109 input_
.name_string ().to_str0 (), header_length
));
111 info_
.checksum
= input_
.get_U32 ();
112 info_
.design_size
= get_U32_fix ();
114 /* Although the coding scheme might be interesting to the caller, the
115 font family and face byte probably aren't. So we don't read them. */
116 info_
.coding_scheme
= header_length
> 2
117 ? get_bcpl_string () : "unspecified";
121 /* Although TFM files are only usable by TeX if they have at least seven
122 parameters, that is not a requirement of the file format itself, so
123 we don't impose it. And they can have many more than seven, of
124 course. We do impose a limit of TFM_MAX_FONT_PARAMETERS. We assume
125 that `tfm_header' has already been filled in. */
128 Tex_font_metric_reader::read_params ()
130 /* If we have no font parameters at all, we're done. */
131 if (header_
.param_word_count
== 0)
135 /* Move to the beginning of the parameter table in the file. */
136 input_
.seek_str0 (-4 * header_
.param_word_count
);
138 /* It's unlikely but possible that this TFM file has more fontdimens
139 than we can deal with. */
140 if (header_
.param_word_count
> TFM_MAX_FONTDIMENS
)
142 warning (_f ("%s: TFM file has %u parameters, which is more than the %u I can handle",
143 input_
.name_string ().to_str0 (),
144 header_
.param_word_count
,
145 TFM_MAX_FONTDIMENS
));
146 header_
.param_word_count
= TFM_MAX_FONTDIMENS
;
149 /* The first parameter is different than all the rest, because it
150 isn't scaled by the design size. */
151 info_
.parameters
[ (TFM_SLANT_PARAMETER
) - 1] = get_U32_fix ();
153 for (Char_code i
= 2; i
<= header_
.param_word_count
; i
++)
154 info_
.parameters
[i
- 1] = get_U32_fix_scaled ();
158 /* Read every character in the TFM file, storing the result in the
159 static `tfm_char_table'. We return a copy of that variable. */
162 Tex_font_metric_reader::read_char_metrics ()
164 for (int i
= info_
.first_charcode
; i
<= info_
.last_charcode
; i
++)
166 Tex_font_char_metric tfm_char
= read_char_metric (i
);
167 if (tfm_char
.exists_b_
)
168 ascii_to_metric_idx_
[tfm_char
.code_
] = char_metrics_
.size ();
169 char_metrics_
.push (tfm_char
);
173 /* Read the character CODE. If the character doesn't exist, return
174 NULL. If it does, save the information in `tfm_char_table', as well
178 Tex_font_metric_reader::read_char_metric (Char_code code
)
180 Tex_font_char_metric tfm_char
;
182 /* If the character is outside the declared bounds in the file, don't
184 if (code
< info_
.first_charcode
|| code
> info_
.last_charcode
)
188 /* Move to the appropriate place in the `char_info' array. */
189 input_
.seek_str0 (header_
.char_info_pos
+ (code
- info_
.first_charcode
) * 4);
191 /* Read the character. */
192 tfm_char
= read_char ();
194 if (tfm_char
.exists_b_
)
195 tfm_char
.code_
= code
;
201 /* We assume we are positioned at the beginning of a `char_info' word.
202 We read that word to get the indexes into the dimension tables; then
203 we go read the tables to get the values (if the character exists). */
206 Tex_font_metric_reader::read_char ()
208 /* Read the char_info word. */
209 U8 width_index
= input_
.get_U8 ();
212 packed
= input_
.get_U8 ();
213 U8 height_index
= (packed
& 0xf0) >> 4;
214 U8 depth_index
= packed
& 0x0f;
216 packed
= input_
.get_U8 ();
217 U8 italic_correction_index
= (packed
& 0xfc) >> 6;
218 U8 tag
= packed
& 0x3;
220 U8 remainder
= input_
.get_U8 ();
222 Tex_font_char_metric tfm_char
;
224 #define GET_CHAR_DIMEN(d) \
225 if (d##_index != 0) \
227 input_.seek_str0 (header_. d##_pos + d##_index*4); \
228 tfm_char.d##_fix_ = input_.get_U32 (); \
229 tfm_char.d##_ = fix_to_real (tfm_char.d##_fix_) \
230 * info_.design_size; \
233 GET_CHAR_DIMEN (width
);
234 GET_CHAR_DIMEN (height
);
235 GET_CHAR_DIMEN (depth
);
236 GET_CHAR_DIMEN (italic_correction
);
238 /* The other condition for a character existing is that it be between
239 the first and last character codes given in the header. We've
240 already assumed that's true (or we couldn't be positioned at a
241 `char_info_word'). */
242 tfm_char
.exists_b_
= width_index
!= 0;
246 input_
.seek_str0 (header_
.lig_kern_pos
+ remainder
* 4);
247 read_lig_kern_program (&tfm_char
.ligatures_
, &tfm_char
.kerns_
);
250 /* We don't handle the other tags. */
254 /* Read a ligature/kern program at the current position, storing the
255 result into *LIGATURE and *KERN. We don't distinguish all the kinds
256 of ligatures that Metafont can output. */
258 #define STOP_FLAG 128
259 #define KERN_FLAG 128
262 Tex_font_metric_reader::read_lig_kern_program (Array
<Tfm_ligature
>* ligatures
, Array
<Tfm_kern
>* kerns
)
268 end_b
= input_
.get_U8 () >= STOP_FLAG
;
270 U8 next_char
= input_
.get_U8 ();
271 bool kern_step_b
= input_
.get_U8 () >= KERN_FLAG
;
272 U8 remainder
= input_
.get_U8 ();
277 Tfm_kern kern_element
;
278 kern_element
.character
= next_char
;
280 char const* old_pos
= input_
.pos_str0 ();
281 input_
.seek_str0 (header_
.kern_pos
+ remainder
* 4);
282 kern_element
.kern
= get_U32_fix_scaled ();
283 input_
.set_pos (old_pos
);
285 kerns
->push (kern_element
);
290 Tfm_ligature ligature_element
;
291 ligature_element
.character
= next_char
;
292 ligature_element
.ligature
= remainder
;
293 ligatures
->push (ligature_element
);