2 tfm-reader.cc -- implement Tex_font_metric_reader
4 source file of the GNU LilyPond music typesetter
6 (c) 1999--2006 Jan Nieuwenhuizen <janneke@gnu.org>
9 some code shamelessly copied from GNU fontutils-0.6/tfm/tfm_input.c
12 #include "tfm-reader.hh"
14 #include "international.hh"
15 #include "string-convert.hh"
18 #define format_string String_convert::form_string
21 static const Real fix_to_real (Fix f);
23 Tex_font_metric_reader::Tex_font_metric_reader (string name)
27 for (vsize i = 0; i < TFM_SIZE; i++)
28 ascii_to_metric_idx_.push_back (VPOS);
38 Real r = f / FIX_UNITY + ((Real) (f % FIX_UNITY) / (Real) FIX_UNITY);
42 /* Most quantities are fixed-point fractions. */
45 Tex_font_metric_reader::get_U32_fix ()
47 return fix_to_real (input_.get_U32 ());
50 /* Dimensions are a `Fix' scaled by the design size. */
53 Tex_font_metric_reader::get_U32_fix_scaled ()
55 return get_U32_fix () * info_.design_size;
59 Tex_font_metric_reader::get_bcpl_string ()
61 U8 length_u8 = input_.get_U8 ();
62 string str = input_.get_string (length_u8);
66 /* Here we read the information at the beginning of the file. We store
67 the result into the static variables `global_info' and
70 Tex_font_metric_reader::read_header ()
72 U16 file_length = input_.get_U16 ();
74 U16 header_length = input_.get_U16 ();
76 info_.first_charcode = input_.get_U16 ();
77 info_.last_charcode = input_.get_U16 ();
78 U16 width_word_count = input_.get_U16 ();
79 U16 height_word_count = input_.get_U16 ();
80 U16 depth_word_count = input_.get_U16 ();
81 U16 italic_correction_word_count = input_.get_U16 ();
82 U16 lig_kern_word_count = input_.get_U16 ();
83 U16 kern_word_count = input_.get_U16 ();
84 (void)kern_word_count;
85 U16 extensible_word_count = input_.get_U16 ();
86 (void)extensible_word_count;
88 header_.param_word_count = input_.get_U16 ();
89 info_.parameter_count = header_.param_word_count;
91 header_.char_info_pos = (6 + header_length) * 4;
92 header_.width_pos = header_.char_info_pos
93 + (info_.last_charcode
94 - info_.first_charcode + 1) * 4;
95 header_.height_pos = header_.width_pos + width_word_count * 4;
96 header_.depth_pos = header_.height_pos + height_word_count * 4;
97 header_.italic_correction_pos = header_.depth_pos
98 + depth_word_count * 4;
99 header_.lig_kern_pos = header_.italic_correction_pos
100 + italic_correction_word_count * 4;
101 header_.kern_pos = header_.lig_kern_pos + lig_kern_word_count * 4;
102 /* We don't care about the extensible table. */
104 if (header_length < 2)
105 /* Not using ngettext's plural feature here, as this message is
106 more of a programming error. */
107 error (_f ("TFM header of `%s' has only %u word (s)",
108 input_.name_string ().c_str (), header_length));
110 info_.checksum = input_.get_U32 ();
111 info_.design_size = get_U32_fix ();
113 /* Although the coding scheme might be interesting to the caller, the
114 font family and face byte probably aren't. So we don't read them. */
115 info_.coding_scheme = header_length > 2
116 ? get_bcpl_string () : "unspecified";
119 /* Although TFM files are only usable by TeX if they have at least seven
120 parameters, that is not a requirement of the file format itself, so
121 we don't impose it. And they can have many more than seven, of
122 course. We do impose a limit of TFM_MAX_FONT_PARAMETERS. We assume
123 that `tfm_header' has already been filled in. */
126 Tex_font_metric_reader::read_params ()
128 /* If we have no font parameters at all, we're done. */
129 if (header_.param_word_count == 0)
133 /* Move to the beginning of the parameter table in the file. */
134 input_.seek_str0 (-4 * header_.param_word_count);
136 /* It's unlikely but possible that this TFM file has more fontdimens
137 than we can deal with. */
138 if (header_.param_word_count > TFM_MAX_FONTDIMENS)
140 warning (_f ("%s: TFM file has %u parameters, which is more than the %u I can handle",
141 input_.name_string ().c_str (),
142 header_.param_word_count,
143 TFM_MAX_FONTDIMENS));
144 header_.param_word_count = TFM_MAX_FONTDIMENS;
147 /* The first parameter is different than all the rest, because it
148 isn't scaled by the design size. */
149 info_.parameters[ (TFM_SLANT_PARAMETER) - 1] = get_U32_fix ();
151 for (Char_code i = 2; i <= header_.param_word_count; i++)
152 info_.parameters[i - 1] = get_U32_fix_scaled ();
155 /* Read every character in the TFM file, storing the result in the
156 static `tfm_char_table'. We return a copy of that variable. */
159 Tex_font_metric_reader::read_char_metrics ()
161 for (int i = info_.first_charcode; i <= info_.last_charcode; i++)
163 Tex_font_char_metric tfm_char = read_char_metric (i);
164 if (tfm_char.exists_)
165 ascii_to_metric_idx_[tfm_char.code_] = char_metrics_.size ();
166 char_metrics_.push_back (tfm_char);
170 /* Read the character CODE. If the character doesn't exist, return
171 NULL. If it does, save the information in `tfm_char_table', as well
175 Tex_font_metric_reader::read_char_metric (Char_code code)
177 Tex_font_char_metric tfm_char;
179 /* If the character is outside the declared bounds in the file, don't
181 if (code < info_.first_charcode || code > info_.last_charcode)
185 /* Move to the appropriate place in the `char_info' array. */
186 input_.seek_str0 (header_.char_info_pos + (code - info_.first_charcode) * 4);
188 /* Read the character. */
189 tfm_char = read_char ();
191 if (tfm_char.exists_)
192 tfm_char.code_ = code;
197 /* We assume we are positioned at the beginning of a `char_info' word.
198 We read that word to get the indexes into the dimension tables; then
199 we go read the tables to get the values (if the character exists). */
202 Tex_font_metric_reader::read_char ()
204 /* Read the char_info word. */
205 U8 width_index = input_.get_U8 ();
208 packed = input_.get_U8 ();
209 U8 height_index = (packed & 0xf0) >> 4;
210 U8 depth_index = packed & 0x0f;
212 packed = input_.get_U8 ();
213 U8 italic_correction_index = (packed & 0xfc) >> 6;
214 U8 tag = packed & 0x3;
216 U8 remainder = input_.get_U8 ();
218 Tex_font_char_metric tfm_char;
220 #define GET_CHAR_DIMEN(d) \
221 if (d##_index != 0) \
223 input_.seek_str0 (header_.d##_pos + d##_index * 4); \
224 tfm_char.d##_fix_ = input_.get_U32 (); \
225 tfm_char.d##_ = fix_to_real (tfm_char.d##_fix_) \
226 * info_.design_size; \
229 GET_CHAR_DIMEN (width);
230 GET_CHAR_DIMEN (height);
231 GET_CHAR_DIMEN (depth);
232 GET_CHAR_DIMEN (italic_correction);
234 /* The other condition for a character existing is that it be between
235 the first and last character codes given in the header. We've
236 already assumed that's true (or we couldn't be positioned at a
237 `char_info_word'). */
238 tfm_char.exists_ = width_index != 0;
242 input_.seek_str0 (header_.lig_kern_pos + remainder * 4);
243 read_lig_kern_program (&tfm_char.ligatures_, &tfm_char.kerns_);
246 /* We don't handle the other tags. */
250 /* Read a ligature/kern program at the current position, storing the
251 result into *LIGATURE and *KERN. We don't distinguish all the kinds
252 of ligatures that Metafont can output. */
254 #define STOP_FLAG 128
255 #define KERN_FLAG 128
258 Tex_font_metric_reader::read_lig_kern_program (vector<Tfm_ligature> *ligatures, vector<Tfm_kern> *kerns)
264 end_b = input_.get_U8 () >= STOP_FLAG;
266 U8 next_char = input_.get_U8 ();
267 bool kern_step_b = input_.get_U8 () >= KERN_FLAG;
268 U8 remainder = input_.get_U8 ();
272 Tfm_kern kern_element;
273 kern_element.character = next_char;
275 char const *old_pos = input_.pos_str0 ();
276 input_.seek_str0 (header_.kern_pos + remainder * 4);
277 kern_element.kern = get_U32_fix_scaled ();
278 input_.set_pos (old_pos);
280 kerns->push_back (kern_element);
284 Tfm_ligature ligature_element;
285 ligature_element.character = next_char;
286 ligature_element.ligature = remainder;
287 ligatures->push_back (ligature_element);