summaryrefslogtreecommitdiff
path: root/gf/gf_output.c
blob: ea94bcc4786003316e0c444f94d6b46d3298e31d (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
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
/*
# gf_output.c: write objects to one GF file.
#
# 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 of the License, 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, see <http://www.gnu.org/licenses/>.
#
*/

#include "config.h"

#include "file-output.h"
#include "gf.h"
#include "scaled-num.h"

#include "gf_opcodes.h"


/* The file we are writing to.  */
static FILE *gf_output_file = NULL;
static string gf_output_filename;


/* The bounding box of all the characters we have written.  The values
   here should be the largest and smallest possible integers.  */
static bounding_box_type font_bb
  = { INT_MAX, INT_MIN, INT_MAX, INT_MIN };


/* We keep track of the character locator information as we output
   characters, to save the caller from doing the bookkeeping.  */
static gf_char_locator_type char_loc[MAX_CHARCODE + 1];

static void init_locators (void);
static void put_locators (void);


/* Other parts of the GF file that deserve their own routines.  */
static void put_boc (gf_char_type);
static void put_bitmap (bitmap_type);


/* Subroutines for writing the bitmap.  */
static void put_paint (unsigned);
static void put_skip (unsigned);
static void put_new_row (unsigned);

/* Low-level output.  These macros call the corresponding routines in
   fontutils, using the static variables for the input file and filename.  */

#define GF_FTELL() xftell (gf_output_file, gf_output_filename)
#define GF_PUT_BYTE(b) put_byte (b, gf_output_file, gf_output_filename)
#define GF_PUT_TWO(b) put_two (b, gf_output_file, gf_output_filename)
#define GF_PUT_THREE(n) put_three (n, gf_output_file, gf_output_filename)
#define GF_PUT_FOUR(n) put_four (n, gf_output_file, gf_output_filename)
#define GF_PUT_SIGNED_FOUR(n) \
  put_signed_four (n, gf_output_file, gf_output_filename);

/* Routines to start and end writing a file.  (For the user to call.)
   We make sure the caller can't have two output files open
   simultaneously. */

boolean
gf_open_output_file (string filename)
{
  assert (gf_output_file == NULL);

  gf_output_filename = filename;
  gf_output_file = fopen (filename, "w");

  if (gf_output_file != NULL)
    {
      init_locators ();
      return true;
    }
  else
    return false;
}


void
gf_close_output_file ()
{
  assert (gf_output_file != NULL);

  xfclose (gf_output_file, gf_output_filename);

  gf_output_filename = NULL;
  gf_output_file = NULL;
}

/* Write the preamble.  */

void
gf_put_preamble (string comment)
{
  unsigned comment_length, c;

  assert (gf_output_file != NULL);

  GF_PUT_BYTE (PRE);
  GF_PUT_BYTE (GF_ID);

  comment_length = strlen (comment);

  if (comment_length > 255)
    comment_length = 255;

  GF_PUT_BYTE (comment_length);
  /* We can't just output all the characters in the string, since the
     string might have been too long.  */
  for (c = 0; c < comment_length; c++)
    GF_PUT_BYTE (*(comment + c));
}

/* Write the postamble.  */

void
gf_put_postamble (fix_word design_size, real h_resolution, real v_resolution)
{
  byte_count_type post_ptr;
  unsigned i;

  assert (gf_output_file != NULL);

  fflush (gf_output_file);
  post_ptr = GF_FTELL ();
  GF_PUT_BYTE (POST);
  GF_PUT_FOUR (post_ptr);   /* No specials before the postamble.  */
  GF_PUT_FOUR (design_size);
  GF_PUT_FOUR (0);		/* Don't bother with a checksum.  */

  /* The resolution values are given to us in pixels per inch, but we
     must write them in pixels per point (as well as scaling them).  */
  GF_PUT_FOUR (real_to_scaled (h_resolution / POINTS_PER_INCH));
  GF_PUT_FOUR (real_to_scaled (h_resolution / POINTS_PER_INCH));

  GF_PUT_SIGNED_FOUR (MIN_COL (font_bb));
  GF_PUT_SIGNED_FOUR (MAX_COL (font_bb));
  GF_PUT_SIGNED_FOUR (MIN_ROW (font_bb));
  GF_PUT_SIGNED_FOUR (MAX_ROW (font_bb));

  put_locators ();

  GF_PUT_BYTE (POST_POST);
  GF_PUT_FOUR (post_ptr);
  GF_PUT_BYTE (GF_ID);

  for (i = 0; i < 4; i++)
    GF_PUT_BYTE (GF_SIGNATURE);
}


/* Here we output the information we have accumulated in the `char_loc'
   array.  GF format allows for two different `char_loc' commands, one
   more efficient (but less general) than the other.  We use the
   appropriate one.  */

static void
put_locators ()
{
  unsigned this_char;

  for (this_char = 0; this_char <= MAX_CHARCODE; this_char++)
    {
      gf_char_locator_type this_char_loc = char_loc[this_char];

      if (this_char_loc.char_pointer != NULL_BYTE_PTR)
	{
	  int h_escapement = this_char_loc.h_escapement;

	  /* Decide if we can use CHAR_LOC0.  */
	  if (h_escapement >= 0 && h_escapement < ONE_BYTE_BIG)
	    {
	      GF_PUT_BYTE (CHAR_LOC0);
	      GF_PUT_BYTE (this_char);
	      GF_PUT_BYTE (h_escapement);
	    }
	  else
	    {
	      GF_PUT_BYTE (CHAR_LOC);
	      GF_PUT_BYTE (this_char);
	      GF_PUT_FOUR (real_to_scaled ((real) h_escapement));
	      GF_PUT_FOUR (0);	/* No vertical escapement.  */
	    }

	  GF_PUT_FOUR (this_char_loc.tfm_width);
	  GF_PUT_SIGNED_FOUR (this_char_loc.char_pointer);
	}
    }
}


/* We must initialize the character locators to all null values.  */

static void
init_locators ()
{
  unsigned this_char;

  for (this_char = 0; this_char <= MAX_CHARCODE; this_char++)
    char_loc[this_char].char_pointer = NULL_BYTE_PTR;
}

/* Do what's necessary to prepare for outputting a character.  */

static void
start_put_char (charcode_type charcode, unsigned h_escapement,
                fix_word tfm_width, bounding_box_type char_bb)
{
  gf_char_locator_type *this_char_loc = &(char_loc[charcode]);

  assert (gf_output_file != NULL);

  if (this_char_loc->char_pointer != NULL_BYTE_PTR)
    {
      WARNING1 ("gf_put_char: Character %u already output", charcode);
      return;
    }

  /* Update the character locator information (part of the postamble).  */
  this_char_loc->char_pointer = GF_FTELL ();
  this_char_loc->h_escapement = h_escapement;
  this_char_loc->tfm_width = tfm_width;
  this_char_loc->charcode = charcode;

  /* Update the font bounding box.  */
  if (MIN_COL (char_bb) < MIN_COL (font_bb))
    MIN_COL (font_bb) = MIN_COL (char_bb);
  if (MAX_COL (char_bb) > MAX_COL (font_bb))
    MAX_COL (font_bb) = MAX_COL (char_bb);
  if (MIN_ROW (char_bb) < MIN_ROW (font_bb))
    MIN_ROW (font_bb) = MIN_ROW (char_bb);
  if (MAX_ROW (char_bb) > MAX_ROW (font_bb))
    MAX_ROW (font_bb) = MAX_ROW (char_bb);
}


/* Output the character GF_CHAR.  */

void
gf_put_char (gf_char_type gf_char)
{
  start_put_char (GF_CHARCODE (gf_char), GF_H_ESCAPEMENT (gf_char),
                  GF_TFM_WIDTH (gf_char), GF_CHAR_BB (gf_char));
  put_boc (gf_char);
  put_bitmap (GF_BITMAP (gf_char));
  GF_PUT_BYTE (EOC);
}


/* Output the BOC command that begins each character.  BOC commands come
   in two flavors, one of which takes less space but is less general.  */

static void
put_boc (gf_char_type gf_char)
{
  signed_4_bytes row_delta = (GF_CHAR_MAX_ROW (gf_char)
                              - GF_CHAR_MIN_ROW (gf_char));
  signed_4_bytes col_delta = (GF_CHAR_MAX_COL (gf_char)
                              - GF_CHAR_MIN_COL (gf_char));

  /* Decide if we can use BOC1, the abbreviated form.  */
  if (row_delta < ONE_BYTE_BIG && row_delta >= 0
      && col_delta < ONE_BYTE_BIG && col_delta >= 0
      && GF_CHAR_MAX_ROW (gf_char) < ONE_BYTE_BIG
      && GF_CHAR_MAX_ROW (gf_char) >= 0
      && GF_CHAR_MAX_COL (gf_char) < ONE_BYTE_BIG
      && GF_CHAR_MAX_COL (gf_char) >= 0)
    {
      GF_PUT_BYTE (BOC1);
      GF_PUT_BYTE (GF_CHARCODE (gf_char));
      GF_PUT_BYTE (col_delta);
      GF_PUT_BYTE (GF_CHAR_MAX_COL (gf_char));
      GF_PUT_BYTE (row_delta);
      GF_PUT_BYTE (GF_CHAR_MAX_ROW (gf_char));
    }
  else
    {
      GF_PUT_BYTE (BOC);
      GF_PUT_FOUR (GF_CHARCODE (gf_char));
      GF_PUT_SIGNED_FOUR (NULL_BYTE_PTR);   /* We never have a backpointer.  */
      GF_PUT_SIGNED_FOUR (GF_CHAR_MIN_COL (gf_char));
      GF_PUT_SIGNED_FOUR (GF_CHAR_MAX_COL (gf_char));
      GF_PUT_SIGNED_FOUR (GF_CHAR_MIN_ROW (gf_char));
      GF_PUT_SIGNED_FOUR (GF_CHAR_MAX_ROW (gf_char));
    }
}


/* Bitmaps in GF format are run-encoded.  There are three commands:
   `paint', which writes a run of pixels (either black or white);
   `skip', which leaves entirely blank rows; and `new_row', which begins
   a new row with the first black pixel some number of pixels from the
   left edge.  */

static void
put_bitmap (bitmap_type b)
{
  unsigned run = 0;		/* Length of the current run.  */
  one_byte current_color = WHITE;
  boolean recent_eol = false;	/* Have we seen only white since eol?  */
  unsigned all_white = 0;	/* Count of entirely blank rows.  */
  unsigned this_row, this_col;


  for (this_row = 0; this_row < BITMAP_HEIGHT (b); this_row++)
    {
      for (this_col = 0; this_col < BITMAP_WIDTH (b); this_col++)
	{
	  one_byte p = BITMAP_PIXEL (b, this_row, this_col);

	  if (p == current_color)
            run++;
	  else
	    {
	      if (recent_eol)
		{		/* First transition to black on new row?  */
		  recent_eol = false;
		  put_skip (all_white);
		  all_white = 0;
		  /* We've seen only white up to now, since
                     `current_color' is zero after each row.  */
		  put_new_row (run);
		}
	      else
               put_paint (run);

	      current_color = p;
	      run = 1;
	    }
	}

      if (current_color == BLACK)
	{
	  put_paint (run);	/* The row ended with black.  */
	  current_color = WHITE;
	}

      else if (recent_eol)
        all_white++;		/* The row was entirely white.  */

      recent_eol = true;
      run = 0;
    }
}


/* Routines to output the actual GF commands that describe the bitmap. 
   We use the shortest possible command.  */

static void
put_paint (unsigned run)
{
   if (run < 64)
     GF_PUT_BYTE (run);
   else if (run < ONE_BYTE_BIG)
     {
       GF_PUT_BYTE (PAINT1);
       GF_PUT_BYTE (run);
     }
   else if (run < TWO_BYTES_BIG)
     {
       GF_PUT_BYTE (PAINT2);
       GF_PUT_TWO (run);
     }
   else if (run < THREE_BYTES_BIG)
     {
       GF_PUT_BYTE (PAINT3);
       GF_PUT_THREE (run);
     }
   else
     FATAL1 ("put_paint: Run of %u pixels too long for GF format", run);
}


static void
put_skip (unsigned all_white)
{
  if (all_white == 0)
    ; /* Do nothing.  */
  else if (all_white == 1)
    GF_PUT_BYTE (SKIP0);
  else if (all_white < ONE_BYTE_BIG)
    {
      GF_PUT_BYTE (SKIP1);
      GF_PUT_BYTE (all_white - 1);
    }
  else if (all_white < TWO_BYTES_BIG)
    {
      GF_PUT_BYTE (SKIP2);
      GF_PUT_TWO (all_white - 1);
    }
  else if (all_white < THREE_BYTES_BIG)
    {
      GF_PUT_BYTE (SKIP3);
      GF_PUT_THREE (all_white - 1);
    }
  else
    FATAL1 ("put_skip: %u rows is too many for GF format", all_white);
}


static void
put_new_row (unsigned indent)
{
  if (indent <= 164)
    GF_PUT_BYTE (NEW_ROW_0 + indent);
  else
    {
      /* Too large for a new_row command; have to skip and then paint.  */
      GF_PUT_BYTE (SKIP0);
      put_paint (indent);
    }
}


/* Output a raw character.  */

void
gf_put_raw_char (raw_char_type raw_char)
{
  one_byte charcode = GF_CHARCODE (raw_char);
  
  start_put_char (charcode, GF_H_ESCAPEMENT (raw_char),
                  GF_TFM_WIDTH (raw_char), GF_CHAR_BB (raw_char));
  put_n_bytes (RAW_CHAR_USED (raw_char), RAW_CHAR_BYTES (raw_char),
               gf_output_file, gf_output_filename);
}