home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Geek Gadgets 1
/
ADE-1.bin
/
ade-dist
/
fontutils-0.6-base.tgz
/
fontutils-0.6-base.tar
/
fsf
/
fontutils
/
gf
/
gf_output.c
< prev
next >
Wrap
C/C++ Source or Header
|
1992-09-21
|
12KB
|
438 lines
/* gf_output.c: write objects to one GF file.
Copyright (C) 1992 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 2, 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 "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
kbase, 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);
}