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