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