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