summaryrefslogtreecommitdiff
path: root/charspace/char.c
blob: 0d53a5464ba31688a0d9eb5d7ee8109330c15c5a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
/* char.c: functions to muck with `char_type's.

Copyright (C) 1992, 2011 Free Software Foundation, Inc.

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */

#include "config.h"

#include "list.h"
#include "report.h"

#include "char.h"
#include "kern.h"
#include "main.h"
#include "symtab.h"


static boolean resolve_sidebearing (sidebearing_type *);
static void update_kerns (char_type *, tfm_char_type *);
static void update_metrics (real, real, char_info_type *, tfm_char_type *);
static void update_sidebearings (char_type *, char_info_type *,
                                 tfm_char_type *);
static char_type *update_via_name (string, char_info_type *, tfm_char_type *);
static void update_width (char_type *, char_info_type *, tfm_char_type *);

/* Initialize the parts of the character structure.  */

char_type
init_char ()
{
  char_type c;
  
  CHAR_BITMAP_INFO (c) = NULL;
  CHAR_TFM_INFO (c) = NULL;
  CHAR_LSB (c) = NULL;
  CHAR_RSB (c) = NULL;
  CHAR_KERNS (c) = list_init ();
  
  return c;
}

/* Our goal here is to extract the information about the character CODE
   from the symbol table, and fill in the information from the bitmap
   and TFM fonts.  We also read ligature information from the encoding
   vector.  C_PTR_ADDR is the address of a pointer to the `char_type' we
   fill in (or will be).
   
   Thus far, we have information only in the symbol table, and thus
   can be retrieved only by character name.  But when it comes
   time to output, we obviously have to do it by number.  If we can find
   `encoding_info's name for CODE in the symbol table, great -- we can
   make *C_PTR_ADDR point at that character and fill it in.  If not,
   then we allocate some new space -- just because the character wasn't
   mentioned in the CMI files shouldn't mean it doesn't get output.
   (Only if we can't find bitmap information for a character do we not
   output it.)
   
   We return true if CODE will be output, false if not.  */

void
do_char (charcode_type code, char_type **c_ptr_addr)
{
  static unsigned char_count = 0;
  string char_name;
  tfm_char_type *tfm_char = NULL;
  
  /* Check that the character CODE exists in the input font.  */
  char_info_type *bitmap_char = get_char (input_name, code);

  /* If no bitmap information, give up.  Everything else we can fake.  */
  if (bitmap_char == NULL)
    return;
  
  REPORT1 ("[%d", CHARCODE (*bitmap_char));
  
  /* Look for existing TFM information.  */
  if (have_tfm)
    tfm_char = tfm_get_char (code);
  
  /* If no prior TFM file, or if the character CODE wasn't there,
     initialize `tfm_char' from the bitmap information.  */
  if (tfm_char == NULL)
    {
      tfm_char = XTALLOC1 (tfm_char_type);
      *tfm_char = tfm_new_char ();
      
      TFM_CHAR_EXISTS (*tfm_char) = true;
      TFM_CHARCODE (*tfm_char) = code;
      TFM_HEIGHT (*tfm_char)
        = PIXELS_TO_POINTS (CHAR_HEIGHT (*bitmap_char), dpi_real);
      TFM_DEPTH (*tfm_char)
        = PIXELS_TO_POINTS (CHAR_DEPTH (*bitmap_char), dpi_real);
      /* Leave the italic correction at zero, since we don't know how to
         compute a reasonable value.  And don't bother to compute the
         width, since we do so below.  */
    }
  
  /* We've initialized the TFM and bitmap information from previously
     existing files.  Next, get the character name from the global
     `encoding_info', so we can get at info in the symbol table.  */
  char_name = ENCODING_CHAR_NAME (encoding_info, code);

  if (char_name == NULL)
    WARNING1 ("No character name for character %d", code);
  else
    *c_ptr_addr = update_via_name (char_name, bitmap_char, tfm_char);
  
  /* If we haven't allocated a `char_type' yet, do so.  The fields which
     get used for the CMI stuff -- the side bearings and kern list --
     don't need to be initialized.  */
  if (*c_ptr_addr == NULL)
    *c_ptr_addr = XTALLOC1 (char_type);

  /* Set the pointers in *C_PTR_ADDR to the new bitmap and TFM info.  */
  CHAR_BITMAP_INFO (**c_ptr_addr) = bitmap_char;
  CHAR_TFM_INFO (**c_ptr_addr) = tfm_char;
  
  REPORT1 ("]%c", ++char_count % 13 == 0 ? '\n' : ' ');

  return;
}

/* Assume that CHAR_NAME exists in the encoding vector.  If looking up
   CHAR_NAME in the symbol table yields a character, return a pointer to
   the `char_type' in the symbol table with updated side bearing
   information.  Otherwise we return NULL.  In any case, update TFM_CHAR
   with the ligatures from the global `encoding_info'.  */

static char_type *
update_via_name (string char_name, char_info_type *bitmap_char,
                 tfm_char_type *tfm_char)
{
  list_type enc_ligs;
  unsigned l;
  char_type *c_ptr = NULL;

  /* We have a character name, so look for side bearing info.  */
  symval_type *sv = symtab_lookup (char_name);

  if (sv == NULL)
    WARNING1 ("%s: Undefined in CMI files", char_name);

  else if (SYMVAL_TAG (*sv) == symval_char)
    { 
      /* We'll return `c_ptr', a pointer to the struct in the symbol table.  */
      c_ptr = &SYMVAL_CHAR (*sv);

      update_sidebearings (c_ptr, bitmap_char, tfm_char);
      update_kerns (c_ptr, tfm_char);
    }

  else if (SYMVAL_TAG (*sv) == symval_char_width)
    { 
      /* We'll return `c_ptr', a pointer to the struct in the symbol table.  */
      c_ptr = &SYMVAL_CHAR (*sv);

      update_width (c_ptr, bitmap_char, tfm_char);
      update_kerns (c_ptr, tfm_char);
    }

  else
    WARNING1 ("%s: Not defined as a character", char_name);

  /* Regardless of whether CHAR_NAME was in the symbol table, we can
     also update the ligature list from `encoding_info'.  */
  enc_ligs = ENCODING_CHAR_LIG (encoding_info, CHARCODE (*bitmap_char));
  for (l = 0; l < LIST_SIZE (enc_ligs); l++)
    {
      tfm_ligature_type *lig = LIST_ELT (enc_ligs, l);
      tfm_set_ligature (&TFM_LIGATURE (*tfm_char), lig->character,
                        lig->ligature);
    }

  return c_ptr;
}

/* Update the set width and sidebearings for a character defined with
   `char-width'.  We take the RSB info in C_PTR to be the desired set
   width and the LSB to be the percentage of whitespace to make the left
   side bearing.  We update BITMAP_CHAR and TFM_CHAR correspondingly.  */

static void
update_width (char_type *c_ptr, char_info_type *bitmap_char,
              tfm_char_type *tfm_char)
{
  real lsb_percent = 0.0;
  charcode_type code = CHARCODE (*bitmap_char);
  /* We might not have info for the character, though, so set up the
     defaults as the existing metrics.  */
  boolean changed_metrics = false;
  real lsb = CHAR_MIN_COL (*bitmap_char);
  real real_width = CHAR_SET_WIDTH (*bitmap_char);

  /* Find the lsb percent.  */
  if (CHAR_LSB (*c_ptr) == NULL)
    WARNING1 ("char %d: No left side bearing information", code);
  else if (resolve_sidebearing (CHAR_LSB (*c_ptr)))
    {
      lsb_percent = SB_REAL (*CHAR_LSB (*c_ptr));
      if (lsb_percent <= 0.0)
        WARNING2 ("char %d: lsb percent (%f) must be nonnegative",
                  code, lsb_percent);
    }

  if (CHAR_RSB (*c_ptr) == NULL)
    WARNING1 ("char %d: No set width information", CHARCODE (*bitmap_char));
  else if (resolve_sidebearing (CHAR_RSB (*c_ptr)))
    {
      real_width = SB_REAL (*CHAR_RSB (*c_ptr));
      changed_metrics = true;
    }

  /* If we have a positive percent, figure out the new lsb.  */
  if (lsb_percent > 0.0)
    {
      real whitespace = real_width - CHAR_BITMAP_WIDTH (*bitmap_char);
      lsb = whitespace * lsb_percent;
      changed_metrics = true;
    }

  /* If necessary, update the widths.  */
  if (changed_metrics)
    update_metrics (real_width, lsb, bitmap_char, tfm_char);
}


/* Call `resolve_sidebearing' on the sidebearing info in C_PTR.  If that
   succeeds, update the various widths and side bearing info in
   BITMAP_CHAR and TFM_CHAR.  This is called for characters defined with
   the `char' command.  */

static void
update_sidebearings (char_type *c_ptr, char_info_type *bitmap_char,
                     tfm_char_type *tfm_char)
{
  /* We might not have side bearing info for it, so set up the defaults
     as the existing side bearings.  */
  boolean changed_sb = false;
  real lsb = CHAR_MIN_COL (*bitmap_char);
  real rsb = CHAR_SET_WIDTH (*bitmap_char) - CHAR_MAX_COL (*bitmap_char);

  /* Compute the side bearings.  */
  if (CHAR_LSB (*c_ptr) == NULL)
    WARNING1 ("char %d: No left side bearing information",
              CHARCODE (*bitmap_char));
  else if (resolve_sidebearing (CHAR_LSB (*c_ptr)))
    {
      lsb = SB_REAL (*CHAR_LSB (*c_ptr));
      changed_sb = true;
    }

  if (CHAR_RSB (*c_ptr) == NULL)
    WARNING1 ("char %d: No right side bearing information",
              CHARCODE (*bitmap_char));
  else if (resolve_sidebearing (CHAR_RSB (*c_ptr)))
    {
      rsb = SB_REAL (*CHAR_RSB (*c_ptr));
      changed_sb = true;
    }

  /* If the side bearings changed, then update the widths.  */
  if (changed_sb)
    {
      real real_width = lsb + CHAR_BITMAP_WIDTH (*bitmap_char) + rsb;
      update_metrics (real_width, lsb, bitmap_char, tfm_char);
    }
}

/* Following routines are called by both `update_sidebearings' and
   `update_width'.  */

/* Try to make the definition of SB into a number, by looking up names
   in the symbol table.  */

static boolean
resolve_sidebearing (sidebearing_type *sb)
{
  boolean ok;
  symval_type sv;
  
  /* This should have already been checked.  */
  assert (sb != NULL);
  
  /* Make SB into a symval so we can resolve it.  */
  SYMVAL_TAG (sv) = SB_TAG (*sb);
  SYMVAL_REAL_STRING (sv) = SB_VALUE (*sb);
  
  ok = symval_resolve (&sv);
  
  if (ok)
    {
      SB_TAG (*sb) = symval_real;
      SB_REAL (*sb) = SYMVAL_REAL (sv);
    }
  else
    {
      string desc = symval_as_string (sv);
      WARNING1 ("Unresolvable sidebearing definition `%s'", desc);
      free (desc);
    }

  return ok;
}


/* Update the widths and side bearings in BITMAP_CHAR and TFM_CHAR
   according to REAL_WIDTH and LSB.  */

static void
update_metrics (real real_width, real lsb, char_info_type *bitmap_char,
                tfm_char_type *tfm_char)
{
  int pixel_width = ROUND (real_width);
  real point_width = PIXELS_TO_POINTS (real_width, dpi_real);
  real ds = get_designsize_in_points ();

  TFM_WIDTH (*tfm_char) = point_width;
  CHAR_TFM_WIDTH (*bitmap_char) = real_to_fix (point_width / ds);
  CHAR_SET_WIDTH (*bitmap_char) = pixel_width;
  CHAR_MIN_COL (*bitmap_char) = lsb;
  CHAR_MAX_COL (*bitmap_char) = lsb + CHAR_BITMAP_WIDTH (*bitmap_char);
}

/* Update the kern list in TFM_CHAR from any kerns in *C_PTR.  */

static void
update_kerns (char_type *c_ptr, tfm_char_type *tfm_char)
{
  unsigned k;
  list_type char_kerns = CHAR_KERNS (*c_ptr);
  
  for (k = 0; k < LIST_SIZE (char_kerns); k++)
    {
      char_kern_type *kern_elt = LIST_ELT (char_kerns, k);

      /* If the name of the kern character isn't in the global
         `encoding_info', skip it.  */ 
      int code = encoding_number (encoding_info, kern_elt->character);

      if (code != -1)
        {
          if (symval_resolve (&kern_elt->kern))
            {
              real pixel_kern = SYMVAL_REAL (kern_elt->kern);
              real points_kern = PIXELS_TO_POINTS (pixel_kern, dpi_real);
              
              tfm_set_kern (&TFM_KERN (*tfm_char), code, points_kern);
	    }
          else
            {
              string desc = symval_as_string (kern_elt->kern);
              WARNING2 ("%s: Unresolvable kern value `%s'",
 	                kern_elt->character, desc);
              free (desc);
	    }
	}
    }
}

/* We need to do this several times, so...  Have to use the global
   `dpi-real'.  */

real
get_designsize_in_points ()
{
  real ds_pixels = symtab_lookup_real ("designsize");
  real ds_points = PIXELS_TO_POINTS (ds_pixels, dpi_real);
  
  return ds_points;
}