]> git.donarmstrong.com Git - lilypond.git/blob - lily/tfm.cc
14a83e2776442fa741f045d13635be5362fb6b3e
[lilypond.git] / lily / tfm.cc
1 /*   
2   tfm.cc --  implement Tex_font_metric
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.hh"
13 #include "binary-source-file.hh"
14 #include "string-convert.hh"
15 #include "debug.hh"
16 #include "warn.hh"
17
18 #define format_str String_convert::form_str
19
20 #define FIX_UNITY (1 << 20)
21
22 static const Real
23 fix_to_real (Fix f)
24 {
25   Real r = f / FIX_UNITY + ((Real) (f % FIX_UNITY) / (Real) FIX_UNITY);
26   return r;
27 }
28
29 #if 0 //not used
30 static const Fix
31 real_to_fix (Real r)
32 {
33   Fix f = (Fix) (floor (r) * FIX_UNITY + (r - floor (r)) * FIX_UNITY);
34   return f;
35 }
36 #endif
37
38
39
40
41 Tex_font_char_metric::Tex_font_char_metric ()
42 {
43   exists_b_ = false;
44   code_ = 0;;
45   width_ = height_ = depth_ = italic_correction_ = 0;
46   width_fix_ = height_fix_ = depth_fix_ = italic_correction_fix_ = 0;
47 }
48
49 #define APPEND_CHAR_METRIC_ELT(k)  outstr += to_str (#k) + " "  + to_str (k ## _)  + "; "
50
51 String
52 Tex_font_char_metric::str () const
53 {
54   String outstr ;
55
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);
62   
63   return outstr + "\n";
64 }
65
66 Tex_font_metric::Tex_font_metric ()
67 {
68 }
69
70 Tex_font_char_metric
71 Tex_font_metric::find_ascii (int ascii, bool warn) const
72 {
73   if (warn && (ascii_to_metric_idx_[ascii] == -1))
74     {
75       Tex_font_char_metric m;
76       warning (_f ("can't find ascii character `%d'", ascii));
77       return m;
78     }
79   
80   return char_metrics_[ascii_to_metric_idx_ [ascii]];
81 }
82
83
84 String
85 Tex_font_metric::str () const
86 {
87   String outstr;
88   for (int i=0; i < char_metrics_.size (); i++)
89     outstr += char_metrics_[i].str ();
90   
91   return outstr;
92 }
93
94 void
95 Tex_font_metric::clear (int n)
96 {
97   for (int i=0; i < n; i++)
98     ascii_to_metric_idx_.push (-1);
99 }
100
101 /* Most quantities are fixed-point fractions.  */
102
103 Real
104 Tex_font_metric::get_U32_fix_f (Binary_source_file* input)
105 {
106   return fix_to_real (input->get_U32 ());
107 }
108
109
110 /* Dimensions are a `Fix' scaled by the design size.  */
111
112 Real
113 Tex_font_metric::get_U32_fix_scaled_f (Binary_source_file* input)
114 {
115   return get_U32_fix_f (input) * info_.design_size;
116 }
117
118 String
119 Tex_font_metric::get_bcpl_str (Binary_source_file* input)
120 {
121   U8 length_u8 = input->get_U8 ();
122   String str = input->get_str (length_u8);
123   return str;
124 }
125
126 /* Here we read the information at the beginning of the file.  We store
127    the result into the static variables `global_info' and
128    `tfm_header'.  */
129 void
130 Tex_font_metric::read_header (Binary_source_file* input)
131 {
132   U16 file_length = input->get_U16 ();
133   (void) file_length;
134   U16 header_length = input->get_U16 ();
135
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;
147   
148   header_.param_word_count = input->get_U16 ();
149   info_.parameter_count = header_.param_word_count;
150
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.  */
163
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));
167
168   info_.checksum = input->get_U32 ();
169   info_.design_size = get_U32_fix_f (input);
170
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";
175
176   DOUT << format_str ("TFM checksum = %u, design_size = %fpt, coding scheme = `%s'.\n",
177                       info_.checksum,
178                       info_.design_size,
179                       info_.coding_scheme.ch_C ());
180 }
181
182 void
183 Tex_font_metric::read_file (String name)
184 {
185   Binary_source_file input (name);
186
187   clear (TFM_SIZE);
188   read_header (&input);
189   read_params (&input);
190   read_char_metrics (&input);
191 }
192
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.  */
198
199 void
200 Tex_font_metric::read_params (Binary_source_file* input)
201 {
202   /* If we have no font parameters at all, we're done.  */
203   if (header_.param_word_count == 0)
204     return;
205
206   //brrr
207   /* Move to the beginning of the parameter table in the file.  */
208   input->seek_ch_C (-4 * header_.param_word_count);
209
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)
213     {
214       warning (_f ("%s: TFM file has %u parameters, which is more than the
215 %u I can handle",
216                    input->name_str ().ch_C (),
217                    header_.param_word_count,
218                    TFM_MAX_FONTDIMENS));
219       header_.param_word_count = TFM_MAX_FONTDIMENS;
220     }
221
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);
225
226   for (Char_code i = 2; i <= header_.param_word_count; i++)
227     info_.parameters[i - 1] = get_U32_fix_scaled_f (input);
228
229 #ifdef PRINT
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]);
232 #endif
233 }
234
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.  */
237
238 void
239 Tex_font_metric::read_char_metrics (Binary_source_file* input)
240 {
241   for (int i = info_.first_charcode; i <= info_.last_charcode; i++)
242     {
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);
247     }
248 }
249
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
252    as returning it.  */
253
254 Tex_font_char_metric
255 Tex_font_metric::read_char_metric (Binary_source_file* input, Char_code code)
256 {
257   Tex_font_char_metric tfm_char;
258
259   /* If the character is outside the declared bounds in the file, don't
260      try to read it. */
261   if (code < info_.first_charcode || code > info_.last_charcode)
262     return tfm_char;
263   
264   //brr
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);
267
268   /* Read the character.  */
269   tfm_char = read_char (input);
270
271   if (tfm_char.exists_b_)
272     tfm_char.code_ = code;
273
274   return tfm_char;
275 }
276
277
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).  */
281
282 Tex_font_char_metric
283 Tex_font_metric::read_char (Binary_source_file* input)
284 {
285   /* Read the char_info word.  */
286   U8 width_index = input->get_U8 ();
287
288   U8 packed;
289   packed = input->get_U8 ();
290   U8 height_index = (packed & 0xf0) >> 4;
291   U8 depth_index = packed & 0x0f;
292
293   packed = input->get_U8 ();
294   U8 italic_correction_index = (packed & 0xfc) >> 6;
295   U8 tag = packed & 0x3;
296
297   U8 remainder = input->get_U8 ();
298
299   Tex_font_char_metric tfm_char;
300
301 #define GET_CHAR_DIMEN(d) \
302    if (d##_index != 0) \
303      { \
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; \
308      }
309
310   GET_CHAR_DIMEN (width);
311   GET_CHAR_DIMEN (height);
312   GET_CHAR_DIMEN (depth);
313   GET_CHAR_DIMEN (italic_correction);
314
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;
320
321 #ifdef PRINT
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); 
326 #endif
327
328   if (tag == 1)
329     {
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_);
332     }
333
334   /* We don't handle the other tags.  */
335   return tfm_char;
336 }
337
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.  */
341
342 #define STOP_FLAG 128
343 #define KERN_FLAG 128
344
345 void
346 Tex_font_metric::read_lig_kern_program (Binary_source_file* input, Array <Tfm_ligature>* ligature_arr_p, Array <Tfm_kern>* kern_arr_p)
347 {
348   bool end_b;
349
350   do
351     {
352       end_b = input->get_U8 () >= STOP_FLAG;
353
354       U8 next_char = input->get_U8 ();
355       bool kern_step_b = input->get_U8 () >= KERN_FLAG;
356       U8 remainder = input->get_U8 ();
357
358 #ifdef PRINT
359       DOUT << format_str ("   if next = %u (%c), ", next_char, next_char);
360 #endif
361
362       if (kern_step_b)
363         {
364           Tfm_kern kern_element;
365           kern_element.character = next_char;
366
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);
371
372           kern_arr_p->push (kern_element);
373
374 #ifdef PRINT
375           DOUT << format_str ("kern %f.\n", kern_element.kern);
376 #endif
377         }
378       else
379         {
380           Tfm_ligature ligature_element;
381           ligature_element.character = next_char;
382           ligature_element.ligature = remainder;
383           ligature_arr_p->push (ligature_element);
384
385 #ifdef PRINT
386           DOUT format_str ("ligature %d (hex %x).\n",
387                            ligature_element.ligature,
388                            ligature_element.ligature);
389 #endif
390         }
391   } while (!end_b);
392 }
393