]> git.donarmstrong.com Git - lilypond.git/blob - lily/tfm-reader.cc
94d78f565fbf4ef4e4446aa667bd71dd2fffa833
[lilypond.git] / lily / tfm-reader.cc
1 /*   
2   tfm-reader.cc --  implement Tex_font_metric_reader
3   
4   source file of the GNU LilyPond music typesetter
5   
6   (c) 1999 Jan Nieuwenhuizen <janneke@gnu.org>
7   
8
9   some code shamelessly copied from GNU fontutils-0.6/tfm/tfm_input.c
10  */
11
12 #include "tfm-reader.hh"
13 #include "string-convert.hh"
14 #include "debug.hh"
15 #include "warn.hh"
16
17 #define format_str String_convert::form_str
18 #define FIX_UNITY (1 << 20)
19 static const Real fix_to_real (Fix f);
20
21
22 Tex_font_metric_reader::Tex_font_metric_reader (String name)
23   : input_ (name)
24 {
25   tfm_.clear (TFM_SIZE);
26 }
27
28 Tex_font_metric
29 Tex_font_metric_reader::read_tfm ()
30 {
31   read_header ();
32   read_params ();
33   read_char_metrics ();
34   return tfm_;
35 }
36
37
38 static const Real
39 fix_to_real (Fix f)
40 {
41   Real r = f / FIX_UNITY + ((Real) (f % FIX_UNITY) / (Real) FIX_UNITY);
42   return r;
43 }
44
45 /* Most quantities are fixed-point fractions.  */
46
47 Real
48 Tex_font_metric_reader::get_U32_fix_f ()
49 {
50   return fix_to_real (input_.get_U32 ());
51 }
52
53 /* Dimensions are a `Fix' scaled by the design size.  */
54
55 Real
56 Tex_font_metric_reader::get_U32_fix_scaled_f ()
57 {
58   return get_U32_fix_f () * tfm_.info_.design_size;
59 }
60
61 String
62 Tex_font_metric_reader::get_bcpl_str ()
63 {
64   U8 length_u8 = input_.get_U8 ();
65   String str = input_.get_str (length_u8);
66   return str;
67 }
68
69 /* Here we read the information at the beginning of the file.  We store
70    the result into the static variables `global_info' and
71    `tfm_header'.  */
72 void
73 Tex_font_metric_reader::read_header ()
74 {
75   U16 file_length = input_.get_U16 ();
76   (void) file_length;
77   U16 header_length = input_.get_U16 ();
78
79   tfm_.info_.first_charcode = input_.get_U16 ();
80   tfm_.info_.last_charcode = input_.get_U16 ();
81   U16 width_word_count = input_.get_U16 ();
82   U16 height_word_count = input_.get_U16 ();
83   U16 depth_word_count = input_.get_U16 ();
84   U16 italic_correction_word_count = input_.get_U16 ();
85   U16 lig_kern_word_count = input_.get_U16 ();
86   U16 kern_word_count = input_.get_U16 ();
87   (void)kern_word_count;
88   U16 extensible_word_count = input_.get_U16 ();
89   (void)extensible_word_count;
90   
91   tfm_.header_.param_word_count = input_.get_U16 ();
92   tfm_.info_.parameter_count = tfm_.header_.param_word_count;
93
94   tfm_.header_.char_info_pos = (6 + header_length) * 4;
95   tfm_.header_.width_pos = tfm_.header_.char_info_pos
96                          + (tfm_.info_.last_charcode
97                             - tfm_.info_.first_charcode + 1) * 4;
98   tfm_.header_.height_pos = tfm_.header_.width_pos + width_word_count * 4;
99   tfm_.header_.depth_pos = tfm_.header_.height_pos + height_word_count * 4;
100   tfm_.header_.italic_correction_pos = tfm_.header_.depth_pos
101                                      + depth_word_count * 4;
102   tfm_.header_.lig_kern_pos = tfm_.header_.italic_correction_pos
103     + italic_correction_word_count * 4;
104   tfm_.header_.kern_pos = tfm_.header_.lig_kern_pos + lig_kern_word_count * 4;
105   /* We don't care about the extensible table.  */
106
107   if (header_length < 2)
108     error (_f ("TFM header of `%s' has only %u word(s)",
109                input_.name_str ().ch_C (), header_length));
110
111   tfm_.info_.checksum = input_.get_U32 ();
112   tfm_.info_.design_size = get_U32_fix_f ();
113
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   tfm_.info_.coding_scheme = header_length > 2
117     ? get_bcpl_str () : "unspecified";
118
119   DOUT << format_str ("TFM checksum = %u, design_size = %fpt, coding scheme = `%s'.\n",
120                       tfm_.info_.checksum,
121                       tfm_.info_.design_size,
122                       tfm_.info_.coding_scheme.ch_C ());
123 }
124
125 /* Although TFM files are only usable by TeX if they have at least seven
126    parameters, that is not a requirement of the file format itself, so
127    we don't impose it.  And they can have many more than seven, of
128    course.  We do impose a limit of TFM_MAX_FONT_PARAMETERS.  We assume
129    that `tfm_header' has already been filled in.  */
130
131 void
132 Tex_font_metric_reader::read_params ()
133 {
134   /* If we have no font parameters at all, we're done.  */
135   if (tfm_.header_.param_word_count == 0)
136     return;
137
138   //brrr
139   /* Move to the beginning of the parameter table in the file.  */
140   input_.seek_ch_C (-4 * tfm_.header_.param_word_count);
141
142   /* It's unlikely but possible that this TFM file has more fontdimens
143      than we can deal with.  */
144   if (tfm_.header_.param_word_count > TFM_MAX_FONTDIMENS)
145     {
146       warning (_f ("%s: TFM file has %u parameters, which is more than the %u I can handle",
147                    input_.name_str ().ch_C (),
148                    tfm_.header_.param_word_count,
149                    TFM_MAX_FONTDIMENS));
150       tfm_.header_.param_word_count = TFM_MAX_FONTDIMENS;
151     }
152
153   /* The first parameter is different than all the rest, because it
154      isn't scaled by the design size.  */
155   tfm_.info_.parameters[(TFM_SLANT_PARAMETER) - 1] = get_U32_fix_f ();
156
157   for (Char_code i = 2; i <= tfm_.header_.param_word_count; i++)
158     tfm_.info_.parameters[i - 1] = get_U32_fix_scaled_f ();
159
160 #ifdef PRINT
161   for (Char_code i = 1; i <= tfm_.header_.param_word_count; i++)
162     DOUT << format_str ("TFM parameter %d: %.3f", i, tfm_.info_.parameters[i - 1]);
163 #endif
164 }
165
166 /* Read every character in the TFM file, storing the result in the
167    static `tfm_char_table'.  We return a copy of that variable.  */
168
169 void
170 Tex_font_metric_reader::read_char_metrics ()
171 {
172   for (int i = tfm_.info_.first_charcode; i <= tfm_.info_.last_charcode; i++)
173     {
174       Tex_font_char_metric tfm_char = read_char_metric (i);
175       if (tfm_char.exists_b_)
176         tfm_.ascii_to_metric_idx_[tfm_char.code_] = tfm_.char_metrics_.size ();
177       tfm_.char_metrics_.push (tfm_char);
178     }
179 }
180
181 /* Read the character CODE.  If the character doesn't exist, return
182    NULL.  If it does, save the information in `tfm_char_table', as well
183    as returning it.  */
184
185 Tex_font_char_metric
186 Tex_font_metric_reader::read_char_metric (Char_code code)
187 {
188   Tex_font_char_metric tfm_char;
189
190   /* If the character is outside the declared bounds in the file, don't
191      try to read it. */
192   if (code < tfm_.info_.first_charcode || code > tfm_.info_.last_charcode)
193     return tfm_char;
194   
195   //brr
196   /* Move to the appropriate place in the `char_info' array.  */
197   input_.seek_ch_C (tfm_.header_.char_info_pos + (code - tfm_.info_.first_charcode) * 4);
198
199   /* Read the character.  */
200   tfm_char = read_char ();
201
202   if (tfm_char.exists_b_)
203     tfm_char.code_ = code;
204
205   return tfm_char;
206 }
207
208
209 /* We assume we are positioned at the beginning of a `char_info' word.
210    We read that word to get the indexes into the dimension tables; then
211    we go read the tables to get the values (if the character exists).  */
212
213 Tex_font_char_metric
214 Tex_font_metric_reader::read_char ()
215 {
216   /* Read the char_info word.  */
217   U8 width_index = input_.get_U8 ();
218
219   U8 packed;
220   packed = input_.get_U8 ();
221   U8 height_index = (packed & 0xf0) >> 4;
222   U8 depth_index = packed & 0x0f;
223
224   packed = input_.get_U8 ();
225   U8 italic_correction_index = (packed & 0xfc) >> 6;
226   U8 tag = packed & 0x3;
227
228   U8 remainder = input_.get_U8 ();
229
230   Tex_font_char_metric tfm_char;
231
232 #define GET_CHAR_DIMEN(d) \
233    if (d##_index != 0) \
234      { \
235        input_.seek_ch_C (tfm_.header_.##d##_pos + d##_index*4); \
236        tfm_char.d##_fix_ = input_.get_U32 (); \
237        tfm_char.d##_ = fix_to_real (tfm_char.d##_fix_) \
238                       * tfm_.info_.design_size; \
239      }
240
241   GET_CHAR_DIMEN (width);
242   GET_CHAR_DIMEN (height);
243   GET_CHAR_DIMEN (depth);
244   GET_CHAR_DIMEN (italic_correction);
245
246   /* The other condition for a character existing is that it be between
247      the first and last character codes given in the header.  We've
248      already assumed that's true (or we couldn't be positioned at a
249      `char_info_word').  */
250   tfm_char.exists_b_ = width_index != 0;
251
252 #ifdef PRINT
253   DOUT << format_str ("   width = %f, height = %f, ",
254                       tfm_char.width_, tfm_char.height_);
255   DOUT << format_str ("depth = %f, ic = %f.\n",
256                       tfm_char.depth, tfm_char.italic_correction); 
257 #endif
258
259   if (tag == 1)
260     {
261       input_.seek_ch_C (tfm_.header_.lig_kern_pos + remainder * 4);
262       read_lig_kern_program (&tfm_char.ligature_arr_, &tfm_char.kern_arr_);
263     }
264
265   /* We don't handle the other tags.  */
266   return tfm_char;
267 }
268
269 /* Read a ligature/kern program at the current position, storing the
270    result into *LIGATURE and *KERN.  We don't distinguish all the kinds
271    of ligatures that Metafont can output.  */
272
273 #define STOP_FLAG 128
274 #define KERN_FLAG 128
275
276 void
277 Tex_font_metric_reader::read_lig_kern_program (Array <Tfm_ligature>* ligature_arr_p, Array <Tfm_kern>* kern_arr_p)
278 {
279   bool end_b;
280
281   do
282     {
283       end_b = input_.get_U8 () >= STOP_FLAG;
284
285       U8 next_char = input_.get_U8 ();
286       bool kern_step_b = input_.get_U8 () >= KERN_FLAG;
287       U8 remainder = input_.get_U8 ();
288
289 #ifdef PRINT
290       DOUT << format_str ("   if next = %u (%c), ", next_char, next_char);
291 #endif
292
293       if (kern_step_b)
294         {
295           Tfm_kern kern_element;
296           kern_element.character = next_char;
297
298           char const* old_pos = input_.pos_ch_C ();
299           input_.seek_ch_C (tfm_.header_.kern_pos + remainder * 4);
300           kern_element.kern = get_U32_fix_scaled_f ();
301           input_.set_pos (old_pos);
302
303           kern_arr_p->push (kern_element);
304
305 #ifdef PRINT
306           DOUT << format_str ("kern %f.\n", kern_element.kern);
307 #endif
308         }
309       else
310         {
311           Tfm_ligature ligature_element;
312           ligature_element.character = next_char;
313           ligature_element.ligature = remainder;
314           ligature_arr_p->push (ligature_element);
315
316 #ifdef PRINT
317           DOUT format_str ("ligature %d (hex %x).\n",
318                            ligature_element.ligature,
319                            ligature_element.ligature);
320 #endif
321         }
322   } while (!end_b);
323 }
324