]> git.donarmstrong.com Git - lilypond.git/blob - lily/tfm-reader.cc
58e5b124d3f54ff1c3ac2c808302056c48d7e9fd
[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--2006 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
14 #include "international.hh"
15 #include "string-convert.hh"
16 #include "warn.hh"
17
18 #define format_string String_convert::form_string
19 #define FIX_UNITY                               \
20   (1 << 20)
21 static const Real fix_to_real (Fix f);
22
23 Tex_font_metric_reader::Tex_font_metric_reader (string name)
24   : input_ (name)
25 {
26
27   for (vsize i = 0; i < TFM_SIZE; i++)
28     ascii_to_metric_idx_.push_back (VPOS);
29
30   read_header ();
31   read_params ();
32   read_char_metrics ();
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 ()
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 ()
54 {
55   return get_U32_fix () * info_.design_size;
56 }
57
58 string
59 Tex_font_metric_reader::get_bcpl_string ()
60 {
61  string str;
62  
63 #if 0                           // FIXME.
64  U8 length_u8 = input_.get_U8 ();
65   string str = input_.get_string (length_u8);
66 #endif
67   return str;
68 }
69
70 /* Here we read the information at the beginning of the file.  We store
71    the result into the static variables `global_info' and
72    `tfm_header'.  */
73 void
74 Tex_font_metric_reader::read_header ()
75 {
76   U16 file_length = input_.get_U16 ();
77   (void) file_length;
78   U16 header_length = input_.get_U16 ();
79
80   info_.first_charcode = input_.get_U16 ();
81   info_.last_charcode = input_.get_U16 ();
82   U16 width_word_count = input_.get_U16 ();
83   U16 height_word_count = input_.get_U16 ();
84   U16 depth_word_count = input_.get_U16 ();
85   U16 italic_correction_word_count = input_.get_U16 ();
86   U16 lig_kern_word_count = input_.get_U16 ();
87   U16 kern_word_count = input_.get_U16 ();
88   (void)kern_word_count;
89   U16 extensible_word_count = input_.get_U16 ();
90   (void)extensible_word_count;
91
92   header_.param_word_count = input_.get_U16 ();
93   info_.parameter_count = header_.param_word_count;
94
95   header_.char_info_pos = (6 + header_length) * 4;
96   header_.width_pos = header_.char_info_pos
97     + (info_.last_charcode
98        - info_.first_charcode + 1) * 4;
99   header_.height_pos = header_.width_pos + width_word_count * 4;
100   header_.depth_pos = header_.height_pos + height_word_count * 4;
101   header_.italic_correction_pos = header_.depth_pos
102     + depth_word_count * 4;
103   header_.lig_kern_pos = header_.italic_correction_pos
104     + italic_correction_word_count * 4;
105   header_.kern_pos = header_.lig_kern_pos + lig_kern_word_count * 4;
106   /* We don't care about the extensible table.  */
107
108   if (header_length < 2)
109     /* Not using ngettext's plural feature here, as this message is
110        more of a programming error.  */
111     error (_f ("TFM header of `%s' has only %u word (s)",
112                input_.name_string ().c_str (), header_length));
113
114   info_.checksum = input_.get_U32 ();
115   info_.design_size = get_U32_fix ();
116
117   /* Although the coding scheme might be interesting to the caller, the
118      font family and face byte probably aren't.  So we don't read them.  */
119   info_.coding_scheme = header_length > 2
120     ? get_bcpl_string () : "unspecified";
121 }
122
123 /* Although TFM files are only usable by TeX if they have at least seven
124    parameters, that is not a requirement of the file format itself, so
125    we don't impose it.  And they can have many more than seven, of
126    course.  We do impose a limit of TFM_MAX_FONT_PARAMETERS.  We assume
127    that `tfm_header' has already been filled in.  */
128
129 void
130 Tex_font_metric_reader::read_params ()
131 {
132   /* If we have no font parameters at all, we're done.  */
133   if (header_.param_word_count == 0)
134     return;
135
136   //brrr
137   /* Move to the beginning of the parameter table in the file.  */
138
139 #if 0
140   // FIXME.
141   input_.seek_str0 (-4 * header_.param_word_count);
142 #endif
143   
144   /* It's unlikely but possible that this TFM file has more fontdimens
145      than we can deal with.  */
146   if (header_.param_word_count > TFM_MAX_FONTDIMENS)
147     {
148       warning (_f ("%s: TFM file has %u parameters, which is more than the %u I can handle",
149                    input_.name_string ().c_str (),
150                    header_.param_word_count,
151                    TFM_MAX_FONTDIMENS));
152       header_.param_word_count = TFM_MAX_FONTDIMENS;
153     }
154
155   /* The first parameter is different than all the rest, because it
156      isn't scaled by the design size.  */
157   info_.parameters[ (TFM_SLANT_PARAMETER) - 1] = get_U32_fix ();
158
159   for (Char_code i = 2; i <= header_.param_word_count; i++)
160     info_.parameters[i - 1] = get_U32_fix_scaled ();
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_)
173         ascii_to_metric_idx_[tfm_char.code_] = char_metrics_.size ();
174       char_metrics_.push_back (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 #if 0                           // FIXME.
193   //brr
194   /* Move to the appropriate place in the `char_info' array.  */
195   input_.seek_str0 (header_.char_info_pos + (code - info_.first_charcode) * 4);
196 #endif
197   
198   /* Read the character.  */
199   tfm_char = read_char ();
200
201   if (tfm_char.exists_)
202     tfm_char.code_ = code;
203
204   return tfm_char;
205 }
206
207 /* We assume we are positioned at the beginning of a `char_info' word.
208    We read that word to get the indexes into the dimension tables; then
209    we go read the tables to get the values (if the character exists).  */
210
211 Tex_font_char_metric
212 Tex_font_metric_reader::read_char ()
213 {
214   /* Read the char_info word.  */
215   U8 width_index = input_.get_U8 ();
216
217   U8 packed;
218   packed = input_.get_U8 ();
219   U8 height_index = (packed & 0xf0) >> 4;
220   U8 depth_index = packed & 0x0f;
221
222   packed = input_.get_U8 ();
223   U8 italic_correction_index = (packed & 0xfc) >> 6;
224   U8 tag = packed & 0x3;
225
226   U8 remainder = input_.get_U8 ();
227
228   Tex_font_char_metric tfm_char;
229
230   // FIXME.
231 #define GET_CHAR_DIMEN(d)                                       \
232   if (d##_index != 0)                                           \
233     {                                                           \
234       /* input_.seek_str0 (header_.d##_pos + d##_index * 4);*/  \
235       tfm_char.d##_fix_ = input_.get_U32 ();                    \
236       tfm_char.d##_ = fix_to_real (tfm_char.d##_fix_)           \
237         * info_.design_size;                                    \
238     }
239
240   GET_CHAR_DIMEN (width);
241   GET_CHAR_DIMEN (height);
242   GET_CHAR_DIMEN (depth);
243   GET_CHAR_DIMEN (italic_correction);
244
245   /* The other condition for a character existing is that it be between
246      the first and last character codes given in the header.  We've
247      already assumed that's true (or we couldn't be positioned at a
248      `char_info_word').  */
249   tfm_char.exists_ = width_index != 0;
250
251   if (tag == 1)
252     {
253 #if 0                   //  FIXME.
254       input_.seek_str0 (header_.lig_kern_pos + remainder * 4);
255 #endif
256       read_lig_kern_program (&tfm_char.ligatures_, &tfm_char.kerns_);
257     }
258
259   /* We don't handle the other tags.  */
260   return tfm_char;
261 }
262
263 /* Read a ligature/kern program at the current position, storing the
264    result into *LIGATURE and *KERN.  We don't distinguish all the kinds
265    of ligatures that Metafont can output.  */
266
267 #define STOP_FLAG 128
268 #define KERN_FLAG 128
269
270 void
271 Tex_font_metric_reader::read_lig_kern_program (vector<Tfm_ligature> *ligatures, vector<Tfm_kern> *kerns)
272 {
273   bool end_b;
274
275   do
276     {
277       end_b = input_.get_U8 () >= STOP_FLAG;
278
279       U8 next_char = input_.get_U8 ();
280       bool kern_step_b = input_.get_U8 () >= KERN_FLAG;
281       U8 remainder = input_.get_U8 ();
282
283       if (kern_step_b)
284         {
285           Tfm_kern kern_element;
286           kern_element.character = next_char;
287
288 #if 0 
289           char const *old_pos = input_.pos_str0 ();
290           input_.seek_str0 (header_.kern_pos + remainder * 4);
291 #endif
292           
293           kern_element.kern = get_U32_fix_scaled ();
294
295           // FIXME
296           ///     input_.set_pos (old_pos);
297
298           kerns->push_back (kern_element);
299         }
300       else
301         {
302           Tfm_ligature ligature_element;
303           ligature_element.character = next_char;
304           ligature_element.ligature = remainder;
305           ligatures->push_back (ligature_element);
306         }
307     }
308   while (!end_b);
309 }
310