2 tfm.cc -- implement Tex_font_metric
4 source file of the GNU LilyPond music typesetter
6 (c) 1999 Jan Nieuwenhuizen <janneke@gnu.org>
9 some code shamelessly copied from GNU fontutils-0.6/tfm/tfm_input.c
13 #include "binary-source-file.hh"
14 #include "string-convert.hh"
18 #define format_str String_convert::form_str
20 #define FIX_UNITY (1 << 20)
25 Real r = f / FIX_UNITY + ((Real) (f % FIX_UNITY) / (Real) FIX_UNITY);
33 Fix f = (Fix) (floor (r) * FIX_UNITY + (r - floor (r)) * FIX_UNITY);
41 Tex_font_char_metric::Tex_font_char_metric ()
45 width_ = height_ = depth_ = italic_correction_ = 0;
46 width_fix_ = height_fix_ = depth_fix_ = italic_correction_fix_ = 0;
49 #define APPEND_CHAR_METRIC_ELT(k) outstr += to_str (#k) + " " + to_str (k ## _) + "; "
52 Tex_font_char_metric::str () const
56 APPEND_CHAR_METRIC_ELT (exists_b);
57 APPEND_CHAR_METRIC_ELT (code);
58 APPEND_CHAR_METRIC_ELT (width);
59 APPEND_CHAR_METRIC_ELT (height);
60 APPEND_CHAR_METRIC_ELT (depth);
61 APPEND_CHAR_METRIC_ELT (italic_correction);
66 Tex_font_metric::Tex_font_metric ()
71 Tex_font_metric::find_ascii (int ascii, bool warn) const
73 if (warn && (ascii_to_metric_idx_[ascii] == -1))
75 Tex_font_char_metric m;
76 warning (_f ("can't find ascii character `%d'", ascii));
80 return char_metrics_[ascii_to_metric_idx_ [ascii]];
85 Tex_font_metric::str () const
88 for (int i=0; i < char_metrics_.size (); i++)
89 outstr += char_metrics_[i].str ();
95 Tex_font_metric::clear (int n)
97 for (int i=0; i < n; i++)
98 ascii_to_metric_idx_.push (-1);
101 /* Most quantities are fixed-point fractions. */
104 Tex_font_metric::get_U32_fix_f (Binary_source_file* input)
106 return fix_to_real (input->get_U32 ());
110 /* Dimensions are a `Fix' scaled by the design size. */
113 Tex_font_metric::get_U32_fix_scaled_f (Binary_source_file* input)
115 return get_U32_fix_f (input) * info_.design_size;
119 Tex_font_metric::get_bcpl_str (Binary_source_file* input)
121 U8 length_u8 = input->get_U8 ();
122 String str = input->get_str (length_u8);
126 /* Here we read the information at the beginning of the file. We store
127 the result into the static variables `global_info' and
130 Tex_font_metric::read_header (Binary_source_file* input)
132 U16 file_length = input->get_U16 ();
134 U16 header_length = input->get_U16 ();
136 info_.first_charcode = input->get_U16 ();
137 info_.last_charcode = input->get_U16 ();
138 U16 width_word_count = input->get_U16 ();
139 U16 height_word_count = input->get_U16 ();
140 U16 depth_word_count = input->get_U16 ();
141 U16 italic_correction_word_count = input->get_U16 ();
142 U16 lig_kern_word_count = input->get_U16 ();
143 U16 kern_word_count = input->get_U16 ();
144 (void)kern_word_count;
145 U16 extensible_word_count = input->get_U16 ();
146 (void)extensible_word_count;
148 header_.param_word_count = input->get_U16 ();
149 info_.parameter_count = header_.param_word_count;
151 header_.char_info_pos = (6 + header_length) * 4;
152 header_.width_pos = header_.char_info_pos
153 + (info_.last_charcode
154 - info_.first_charcode + 1) * 4;
155 header_.height_pos = header_.width_pos + width_word_count * 4;
156 header_.depth_pos = header_.height_pos + height_word_count * 4;
157 header_.italic_correction_pos = header_.depth_pos
158 + depth_word_count * 4;
159 header_.lig_kern_pos = header_.italic_correction_pos
160 + italic_correction_word_count * 4;
161 header_.kern_pos = header_.lig_kern_pos + lig_kern_word_count * 4;
162 /* We don't care about the extensible table. */
164 if (header_length < 2)
165 error (_f ("TFM header of `%s' has only %u word(s)",
166 input->name_str ().ch_C (), header_length));
168 info_.checksum = input->get_U32 ();
169 info_.design_size = get_U32_fix_f (input);
171 /* Although the coding scheme might be interesting to the caller, the
172 font family and face byte probably aren't. So we don't read them. */
173 info_.coding_scheme = header_length > 2
174 ? get_bcpl_str (input) : "unspecified";
176 DOUT << format_str ("TFM checksum = %u, design_size = %fpt, coding scheme = `%s'.\n",
179 info_.coding_scheme.ch_C ());
183 Tex_font_metric::read_file (String name)
185 Binary_source_file input (name);
188 read_header (&input);
189 read_params (&input);
190 read_char_metrics (&input);
193 /* Although TFM files are only usable by TeX if they have at least seven
194 parameters, that is not a requirement of the file format itself, so
195 we don't impose it. And they can have many more than seven, of
196 course. We do impose a limit of TFM_MAX_FONT_PARAMETERS. We assume
197 that `tfm_header' has already been filled in. */
200 Tex_font_metric::read_params (Binary_source_file* input)
202 /* If we have no font parameters at all, we're done. */
203 if (header_.param_word_count == 0)
207 /* Move to the beginning of the parameter table in the file. */
208 input->seek_ch_C (-4 * header_.param_word_count);
210 /* It's unlikely but possible that this TFM file has more fontdimens
211 than we can deal with. */
212 if (header_.param_word_count > TFM_MAX_FONTDIMENS)
214 warning (_f ("%s: TFM file has %u parameters, which is more than the
216 input->name_str ().ch_C (),
217 header_.param_word_count,
218 TFM_MAX_FONTDIMENS));
219 header_.param_word_count = TFM_MAX_FONTDIMENS;
222 /* The first parameter is different than all the rest, because it
223 isn't scaled by the design size. */
224 info_.parameters[(TFM_SLANT_PARAMETER) - 1] = get_U32_fix_f (input);
226 for (Char_code i = 2; i <= header_.param_word_count; i++)
227 info_.parameters[i - 1] = get_U32_fix_scaled_f (input);
230 for (Char_code i = 1; i <= header_.param_word_count; i++)
231 DOUT << format_str ("TFM parameter %d: %.3f", i, info_.parameters[i - 1]);
235 /* Read every character in the TFM file, storing the result in the
236 static `tfm_char_table'. We return a copy of that variable. */
239 Tex_font_metric::read_char_metrics (Binary_source_file* input)
241 for (int i = info_.first_charcode; i <= info_.last_charcode; i++)
243 Tex_font_char_metric tfm_char = read_char_metric (input, i);
244 if (tfm_char.exists_b_)
245 ascii_to_metric_idx_[tfm_char.code_] = char_metrics_.size ();
246 char_metrics_.push (tfm_char);
250 /* Read the character CODE. If the character doesn't exist, return
251 NULL. If it does, save the information in `tfm_char_table', as well
255 Tex_font_metric::read_char_metric (Binary_source_file* input, Char_code code)
257 Tex_font_char_metric tfm_char;
259 /* If the character is outside the declared bounds in the file, don't
261 if (code < info_.first_charcode || code > info_.last_charcode)
265 /* Move to the appropriate place in the `char_info' array. */
266 input->seek_ch_C (header_.char_info_pos + (code - info_.first_charcode) * 4);
268 /* Read the character. */
269 tfm_char = read_char (input);
271 if (tfm_char.exists_b_)
272 tfm_char.code_ = code;
278 /* We assume we are positioned at the beginning of a `char_info' word.
279 We read that word to get the indexes into the dimension tables; then
280 we go read the tables to get the values (if the character exists). */
283 Tex_font_metric::read_char (Binary_source_file* input)
285 /* Read the char_info word. */
286 U8 width_index = input->get_U8 ();
289 packed = input->get_U8 ();
290 U8 height_index = (packed & 0xf0) >> 4;
291 U8 depth_index = packed & 0x0f;
293 packed = input->get_U8 ();
294 U8 italic_correction_index = (packed & 0xfc) >> 6;
295 U8 tag = packed & 0x3;
297 U8 remainder = input->get_U8 ();
299 Tex_font_char_metric tfm_char;
301 #define GET_CHAR_DIMEN(d) \
302 if (d##_index != 0) \
304 input->seek_ch_C (header_.##d##_pos + d##_index*4); \
305 tfm_char.d##_fix_ = input->get_U32 (); \
306 tfm_char.d##_ = fix_to_real (tfm_char.d##_fix_) \
307 * info_.design_size; \
310 GET_CHAR_DIMEN (width);
311 GET_CHAR_DIMEN (height);
312 GET_CHAR_DIMEN (depth);
313 GET_CHAR_DIMEN (italic_correction);
315 /* The other condition for a character existing is that it be between
316 the first and last character codes given in the header. We've
317 already assumed that's true (or we couldn't be positioned at a
318 `char_info_word'). */
319 tfm_char.exists_b_ = width_index != 0;
322 DOUT << format_str (" width = %f, height = %f, ",
323 tfm_char.width_, tfm_char.height_);
324 DOUT << format_str ("depth = %f, ic = %f.\n",
325 tfm_char.depth, tfm_char.italic_correction);
330 input->seek_ch_C (header_.lig_kern_pos + remainder * 4);
331 read_lig_kern_program (input, &tfm_char.ligature_arr_, &tfm_char.kern_arr_);
334 /* We don't handle the other tags. */
338 /* Read a ligature/kern program at the current position, storing the
339 result into *LIGATURE and *KERN. We don't distinguish all the kinds
340 of ligatures that Metafont can output. */
342 #define STOP_FLAG 128
343 #define KERN_FLAG 128
346 Tex_font_metric::read_lig_kern_program (Binary_source_file* input, Array <Tfm_ligature>* ligature_arr_p, Array <Tfm_kern>* kern_arr_p)
352 end_b = input->get_U8 () >= STOP_FLAG;
354 U8 next_char = input->get_U8 ();
355 bool kern_step_b = input->get_U8 () >= KERN_FLAG;
356 U8 remainder = input->get_U8 ();
359 DOUT << format_str (" if next = %u (%c), ", next_char, next_char);
364 Tfm_kern kern_element;
365 kern_element.character = next_char;
367 char const* old_pos = input->pos_ch_C ();
368 input->seek_ch_C (header_.kern_pos + remainder * 4);
369 kern_element.kern = get_U32_fix_scaled_f (input);
370 input->set_pos (old_pos);
372 kern_arr_p->push (kern_element);
375 DOUT << format_str ("kern %f.\n", kern_element.kern);
380 Tfm_ligature ligature_element;
381 ligature_element.character = next_char;
382 ligature_element.ligature = remainder;
383 ligature_arr_p->push (ligature_element);
386 DOUT format_str ("ligature %d (hex %x).\n",
387 ligature_element.ligature,
388 ligature_element.ligature);