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
|
/*
* NAME
* hextobdf - convert font from hex format to bdf
*
* EXAMPLE USAGE
* hextobdf < gallant.hex > gallant.bdf
*
* LIMITATIONS
* Only for gallant font, due to hard-coded font/glyph properties.
* To adapt: modify PixelWidth and PixelHeight macros and
* output_bdf_preamble() and output_bdf_char() functions.
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <inttypes.h>
#include <locale.h>
#include <wchar.h>
#include <unistd.h>
#define PixelWidth 12
#define PixelHeight 22
#define MAX_LINE 1024
#define MAX_GLYPHS 131072
struct glyph {
wint_t codepoint;
char *bitmap;
};
void parse_options(int aArgc, char **aArgv);
void usage(int aStatus);
void errx(const char *aFormat, ...);
void parse_font_dimensions(FILE *aFile);
void parse_font_line(const char *aLine, struct glyph *aGlyph);
void output_bdf_preamble(void);
void output_bdf_char(int aChar);
void *xmalloc(size_t aSize);
size_t gWidth = PixelWidth;
size_t gHeight = PixelHeight;
size_t gLineNr = 0;
size_t gBytes = 0; // per one row of pixels in a regular glyph
size_t gDblBytes = 0; // per one row of pixels in a dbl width glyph
struct glyph *gGlyph = NULL;
int gGlyphs = 0;
// start the ball rolling.
//
int main(int aArgc, char **aArgv) {
if (!setlocale(LC_CTYPE, ""))
errx("Can't set the locale. Check LANG, LC_CTYPE, LC_ALL.\n");
gGlyph = xmalloc(MAX_GLYPHS * sizeof *gGlyph);
parse_options(aArgc, aArgv);
parse_font_dimensions(stdin);
char buf[MAX_LINE];
while (fgets(buf, MAX_LINE, stdin)) {
++gLineNr;
parse_font_line(buf, &gGlyph[gGlyphs]);
++gGlyphs;
}
fprintf(stderr, "found %d glyphs\n", gGlyphs);
output_bdf_preamble();
for (int i = 0; i < gGlyphs; ++i) {
output_bdf_char(i);
}
puts("ENDFONT");
return EXIT_SUCCESS;
}
// Output the gallant BDF preamble.
//
void output_bdf_preamble(void) {
puts("STARTFONT 2.1");
puts("FONT -sun-gallant-medium-r-normal--22-220-75-75-C-120-ISO10646-1");
puts("SIZE 22 75 75");
puts("FONTBOUNDINGBOX 22 12 0 -5");
puts("STARTPROPERTIES 18");
puts("FONTNAME_REGISTRY \"\"");
puts("FOUNDRY \"Sun\"");
puts("FAMILY_NAME \"Gallant\"");
puts("WEIGHT_NAME \"Medium\"");
puts("SLANT \"R\"");
puts("SETWIDTH_NAME \"Normal\"");
puts("ADD_STYLE_NAME \"\"");
puts("PIXEL_SIZE 22");
puts("POINT_SIZE 220");
puts("RESOLUTION_X 75");
puts("RESOLUTION_Y 75");
puts("SPACING \"C\"");
puts("AVERAGE_WIDTH 120");
puts("CHARSET_REGISTRY \"ISO10646\"");
puts("CHARSET_ENCODING \"1\"");
puts("FONT_ASCENT 17");
puts("FONT_DESCENT 5");
puts("DEFAULT_CHAR 65533");
puts("ENDPROPERTIES");
printf("CHARS %d\n", gGlyphs);
}
// Output data for a single STARTCHAR to stdout.
//
void output_bdf_char(int aChar) {
size_t hexdigits;
printf("STARTCHAR U+%04x\n", gGlyph[aChar].codepoint);
printf("ENCODING %d\n", gGlyph[aChar].codepoint);
if (wcwidth(gGlyph[aChar].codepoint) == 2) {
puts("SWIDTH 1000 0\nDWIDTH 24 0\nBBX 24 22 0 -5");
hexdigits = 2 * gDblBytes;
}
else {
puts("SWIDTH 500 0\nDWIDTH 12 0\nBBX 12 22 0 -5");
hexdigits = 2 * gBytes;
}
puts("BITMAP");
const char *p = gGlyph[aChar].bitmap;
for (size_t h = 0; h < gHeight; ++h) {
for (size_t i = 0; i < hexdigits; ++i) {
putchar(*p);
++p;
}
putchar('\n');
}
puts("ENDCHAR");
}
// Parse one line of font hex data and store result in glyph.
//
void parse_font_line(const char *aLine, struct glyph *aGlyph) {
char c;
if (sscanf(aLine, "%" SCNx32 "%c", &aGlyph->codepoint, &c) == 2 && c == ':') {
const char *colon = strchr(aLine, ':');
const size_t hexlen = strlen(colon + 1) - 1; // Minus newline.
if (wcwidth(aGlyph->codepoint) == 2) {
if (hexlen != gHeight * gDblBytes * 2)
errx("line %d: expected %zu hexdigits for double width glyph, got %zu\n", gLineNr, gHeight * gDblBytes * 2, hexlen);
}
else {
if (hexlen != gHeight * gBytes * 2)
errx("line %d: expected %zu hexdigits for normal width glyph, got %zu\n", gLineNr, gHeight * gBytes * 2, hexlen);
}
aGlyph->bitmap = xmalloc(hexlen);
memcpy(aGlyph->bitmap, colon + 1, hexlen);
}
else
errx("expected codepoint:hexdata in line %d\n", gLineNr);
}
// Parse the command line options.
//
void parse_options(int aArgc, char **aArgv) {
int ch;
while ((ch = getopt(aArgc, aArgv, "w:h:")) != -1) {
switch (ch) {
case 'h':
if (sscanf(optarg, "%zu", &gHeight) != 1)
errx("can't convert '%s' to height integer\n", optarg);
break;
case 'w':
if (sscanf(optarg, "%zu", &gWidth) != 1)
errx("can't convert '%s' to width integer\n", optarg);
break;
default:
usage(EXIT_FAILURE);
}
}
if (gWidth != PixelWidth || gHeight != PixelHeight)
errx("dimensions do not match gallant font's 12x22\n");
}
// Parse the font's Width: and Height: directives.
//
void parse_font_dimensions(FILE *aFile) {
char line[MAX_LINE] = { 0 };
for (int i = 1; i <= 2; ++i) {
if (fgets(line, sizeof line, aFile) != NULL) {
if (sscanf(line, " # Width: %zu", &gWidth) != 1)
if (sscanf(line, " # Height: %zu", &gHeight) != 1)
errx("line %d must be '# Width or Height: number'\n", i);
}
else
errx("could not read line %d\n", i);
}
if (gWidth != PixelWidth || gHeight != PixelHeight)
errx("dimensions do not match gallant font's 12x22\n");
gBytes = (gWidth + 7) / 8;
gDblBytes = (2 * gWidth + 7) / 8;
}
// Output usage message and exit with status.
//
void usage(int aStatus) {
fprintf(stderr, "usage: hextobdf [options]\n");
fprintf(stderr, "Options [default]:\n");
fprintf(stderr, " -h height height in pixels [%d]\n", PixelHeight);
fprintf(stderr, " -w width width in pixels [%d]\n", PixelWidth);
fprintf(stderr, "\nReads hex font from stdin and writes bdf font to stdout\n");
exit(aStatus);
}
// Allocate memory and exit on failure.
//
void *xmalloc(size_t aSize) {
void *const mem = malloc(aSize);
if (mem == NULL)
errx("failed to allocate %zu bytes\n", aSize);
return mem;
}
// Print formatted message on stderr and exit.
//
void errx(const char *aFormat, ...) {
va_list ap;
va_start(ap, aFormat);
vfprintf(stderr, aFormat, ap);
va_end(ap);
exit(EXIT_FAILURE);
}
/* vim: set tabstop=4 shiftwidth=4 expandtab fileformat=unix: */
|