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