1 /* ScummVM - Graphic Adventure Engine
2 *
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 *
21 */
22
23 #ifndef __has_feature // Optional of course.
24 #define __has_feature(x) 0 // Compatibility with non-clang compilers.
25 #endif
26
27 #include <fstream>
28 #include <string>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <stdarg.h>
32 #include <string.h>
33 #include <time.h>
34
35 struct BdfBoundingBox {
36 int width, height;
37 int xOffset, yOffset;
38
resetBdfBoundingBox39 void reset() {
40 width = 0;
41 height = 0;
42 xOffset = 0;
43 yOffset = 0;
44 }
45
BdfBoundingBoxBdfBoundingBox46 BdfBoundingBox() {
47 reset();
48 }
49 };
50
51 struct BdfFont {
52 char *familyName;
53 char *slant;
54 int maxAdvance;
55 int size;
56 int height;
57 BdfBoundingBox defaultBox;
58 int ascent;
59
60 int firstCharacter;
61 int defaultCharacter;
62 int numCharacters;
63
64 unsigned char **bitmaps;
65 unsigned char *advances;
66 BdfBoundingBox *boxes;
67
resetBdfFont68 void reset() {
69 familyName = 0;
70 slant = 0;
71 maxAdvance = 0;
72 size = 0;
73 height = 0;
74 defaultBox.reset();
75 ascent = 0;
76
77 firstCharacter = 0;
78 defaultCharacter = 0;
79 numCharacters = 0;
80
81 bitmaps = 0;
82 advances = 0;
83 boxes = 0;
84 }
85
BdfFontBdfFont86 BdfFont() {
87 reset();
88 }
89
~BdfFontBdfFont90 ~BdfFont() {
91 if (bitmaps) {
92 for (int i = 0; i < numCharacters; ++i)
93 delete[] bitmaps[i];
94 }
95 delete[] bitmaps;
96 delete[] advances;
97 delete[] boxes;
98 delete[] familyName;
99 delete[] slant;
100 }
101 };
102
error(const char * s,...)103 void error(const char *s, ...) {
104 char buf[1024];
105 va_list va;
106
107 va_start(va, s);
108 vsnprintf(buf, 1024, s, va);
109 va_end(va);
110
111 fprintf(stderr, "ERROR: %s!\n", buf);
112 exit(1);
113 }
114
warning(const char * s,...)115 void warning(const char *s, ...) {
116 char buf[1024];
117 va_list va;
118
119 va_start(va, s);
120 vsnprintf(buf, 1024, s, va);
121 va_end(va);
122
123 fprintf(stderr, "WARNING: %s!\n", buf);
124 }
125
hasPrefix(const std::string & str,const std::string & prefix)126 bool hasPrefix(const std::string &str, const std::string &prefix) {
127 return str.compare(0, prefix.size(), prefix) == 0;
128 }
129
hexToInt(unsigned char & h)130 inline void hexToInt(unsigned char &h) {
131 if (h >= '0' && h <= '9')
132 h -= '0';
133 else if (h >= 'A' && h <= 'F')
134 h -= 'A' - 10;
135 else if (h >= 'a' && h <= 'f')
136 h -= 'a' - 10;
137 else
138 h = 0;
139 }
140
main(int argc,char * argv[])141 int main(int argc, char *argv[]) {
142 if (argc != 3) {
143 printf("Usage: convbdf [input] [fontname]\n");
144 return 1;
145 }
146
147 std::ifstream in(argv[1]);
148 std::string line;
149
150 std::getline(in, line);
151 if (in.fail() || in.eof())
152 error("Premature end of file");
153
154 int verMajor, verMinor;
155 if (sscanf(line.c_str(), "STARTFONT %d.%d", &verMajor, &verMinor) != 2)
156 error("File '%s' is no BDF file", argv[1]);
157
158 if (verMajor != 2 || (verMinor != 1 && verMinor != 2))
159 error("File '%s' does not use a supported BDF version (%d.%d)", argv[1], verMajor, verMinor);
160
161 std::string fontName;
162 std::string copyright;
163 BdfFont font;
164 font.ascent = -1;
165 font.defaultCharacter = -1;
166
167 int charsProcessed = 0, charsAvailable = 0, descent = -1;
168
169 while (true) {
170 std::getline(in, line);
171 if (in.fail() || in.eof())
172 error("Premature end of file");
173
174 if (hasPrefix(line, "PIXEL_SIZE ")) {
175 if (sscanf(line.c_str(), "PIXEL_SIZE %d", &font.size) != 1)
176 error("Invalid PIXEL_SIZE");
177 } else if (hasPrefix(line, "FONT ")) {
178 fontName = line.substr(5);
179 } else if (hasPrefix(line, "COPYRIGHT ")) {
180 copyright = line.substr(10);
181 } else if (hasPrefix(line, "COMMENT ")) {
182 // Ignore
183 } else if (hasPrefix(line, "FONTBOUNDINGBOX ")) {
184 if (sscanf(line.c_str(), "FONTBOUNDINGBOX %d %d %d %d",
185 &font.defaultBox.width, &font.defaultBox.height,
186 &font.defaultBox.xOffset, &font.defaultBox.yOffset) != 4)
187 error("Invalid FONTBOUNDINGBOX");
188 } else if (hasPrefix(line, "CHARS ")) {
189 if (sscanf(line.c_str(), "CHARS %d", &charsAvailable) != 1)
190 error("Invalid CHARS");
191
192 font.numCharacters = 384;
193 font.bitmaps = new unsigned char *[font.numCharacters];
194 memset(font.bitmaps, 0, sizeof(unsigned char *) * font.numCharacters);
195 font.advances = new unsigned char[font.numCharacters];
196 font.boxes = new BdfBoundingBox[font.numCharacters];
197 } else if (hasPrefix(line, "FAMILY_NAME \"")) {
198 font.familyName = new char[line.size()]; // We will definitely fit here
199 strncpy(font.familyName, &line.c_str()[13], line.size() - 1);
200 char *p = &font.familyName[strlen(font.familyName)];
201 while (p != font.familyName && *p != '"')
202 p--;
203 if (p == font.familyName)
204 error("Invalid FAMILY_NAME");
205 *p = '\0'; // Remove last quote
206 } else if (hasPrefix(line, "SLANT \"")) {
207 font.slant = new char[line.size()]; // We will definitely fit here
208 strncpy(font.slant, &line.c_str()[7], line.size() - 1);
209 char *p = &font.slant[strlen(font.slant)];
210 while (p != font.slant && *p != '"')
211 p--;
212 if (p == font.slant)
213 error("Invalid SLANT");
214 *p = '\0'; // Remove last quote
215 } else if (hasPrefix(line, "FONT_ASCENT ")) {
216 if (sscanf(line.c_str(), "FONT_ASCENT %d", &font.ascent) != 1)
217 error("Invalid FONT_ASCENT");
218 } else if (hasPrefix(line, "FONT_DESCENT ")) {
219 if (sscanf(line.c_str(), "FONT_DESCENT %d", &descent) != 1)
220 error("Invalid FONT_ASCENT");
221 } else if (hasPrefix(line, "DEFAULT_CHAR ")) {
222 if (sscanf(line.c_str(), "DEFAULT_CHAR %d", &font.defaultCharacter) != 1)
223 error("Invalid DEFAULT_CHAR");
224 } else if (hasPrefix(line, "STARTCHAR ")) {
225 ++charsProcessed;
226 if (charsProcessed > charsAvailable)
227 error("Too many characters defined (only %d specified, but %d existing already)",
228 charsAvailable, charsProcessed);
229
230 bool hasWidth = false, hasBitmap = false;
231
232 int encoding = -1;
233 int xAdvance;
234 unsigned char *bitmap = 0;
235 BdfBoundingBox bbox = font.defaultBox;
236
237 while (true) {
238 std::getline(in, line);
239 if (in.fail() || in.eof())
240 error("Premature end of file");
241
242 if (hasPrefix(line, "ENCODING ")) {
243 if (sscanf(line.c_str(), "ENCODING %d", &encoding) != 1)
244 error("Invalid ENCODING");
245 } else if (hasPrefix(line, "DWIDTH ")) {
246 int yAdvance;
247 if (sscanf(line.c_str(), "DWIDTH %d %d", &xAdvance, &yAdvance) != 2)
248 error("Invalid DWIDTH");
249 if (yAdvance != 0)
250 error("Character %d uses a DWIDTH y advance of %d", encoding, yAdvance);
251 if (xAdvance < 0)
252 error("Character %d has a negative x advance", encoding);
253
254 if (xAdvance > font.maxAdvance)
255 font.maxAdvance = xAdvance;
256
257 hasWidth = true;
258 } else if (hasPrefix(line, "BBX" )) {
259 if (sscanf(line.c_str(), "BBX %d %d %d %d",
260 &bbox.width, &bbox.height,
261 &bbox.xOffset, &bbox.yOffset) != 4)
262 error("Invalid BBX");
263 } else if (line == "BITMAP") {
264 hasBitmap = true;
265
266 const size_t bytesPerRow = ((bbox.width + 7) / 8);
267
268 // Since we do not load all characters, we only create a
269 // buffer for the ones we actually load.
270 if (encoding < font.numCharacters)
271 bitmap = new unsigned char[bbox.height * bytesPerRow];
272
273 for (int i = 0; i < bbox.height; ++i) {
274 std::getline(in, line);
275 if (in.fail() || in.eof())
276 error("Premature end of file");
277 if (line.size() != bytesPerRow * 2)
278 error("Glyph bitmap line too short");
279
280 if (!bitmap)
281 continue;
282
283 for (size_t j = 0; j < bytesPerRow; ++j) {
284 unsigned char nibble1 = line[j * 2 + 0];
285 hexToInt(nibble1);
286 unsigned char nibble2 = line[j * 2 + 1];
287 hexToInt(nibble2);
288 bitmap[i * bytesPerRow + j] = (nibble1 << 4) | nibble2;
289 }
290 }
291 } else if (line == "ENDCHAR") {
292 if (!hasWidth || !hasBitmap)
293 error("Character not completly defined");
294
295 if (encoding >= 0 && encoding < font.numCharacters) {
296 font.advances[encoding] = xAdvance;
297 font.boxes[encoding] = bbox;
298 font.bitmaps[encoding] = bitmap;
299 }
300 break;
301 }
302 }
303 } else if (line == "ENDFONT") {
304 break;
305 } else {
306 // Silently ignore other declarations
307 //warning("Unsupported declaration: \"%s\"", line.c_str());
308 }
309 }
310
311 if (font.ascent < 0)
312 error("No ascent specified");
313 if (descent < 0)
314 error("No descent specified");
315
316 font.height = font.ascent + descent;
317
318 int firstCharacter = font.numCharacters;
319 int lastCharacter = -1;
320 bool hasFixedBBox = true;
321 bool hasFixedAdvance = true;
322
323 for (int i = 0; i < font.numCharacters; ++i) {
324 if (!font.bitmaps[i])
325 continue;
326
327 if (i < firstCharacter)
328 firstCharacter = i;
329
330 if (i > lastCharacter)
331 lastCharacter = i;
332
333 if (font.advances[i] != font.maxAdvance)
334 hasFixedAdvance = false;
335
336 const BdfBoundingBox &bbox = font.boxes[i];
337 if (bbox.width != font.defaultBox.width
338 || bbox.height != font.defaultBox.height
339 || bbox.xOffset != font.defaultBox.xOffset
340 || bbox.yOffset != font.defaultBox.yOffset)
341 hasFixedBBox = false;
342 }
343
344 if (lastCharacter == -1)
345 error("No glyphs found");
346
347 // Free the advance table, in case all glyphs use the same advance
348 if (hasFixedAdvance) {
349 delete[] font.advances;
350 font.advances = 0;
351 }
352
353 // Free the box table, in case all glyphs use the same box
354 if (hasFixedBBox) {
355 delete[] font.boxes;
356 font.boxes = 0;
357 }
358
359 // Adapt for the fact that we never use encoding 0.
360 if (font.defaultCharacter < firstCharacter
361 || font.defaultCharacter > lastCharacter)
362 font.defaultCharacter = -1;
363
364 font.firstCharacter = firstCharacter;
365
366 charsAvailable = lastCharacter - firstCharacter + 1;
367 // Try to compact the tables
368 if (charsAvailable < font.numCharacters) {
369 unsigned char **bitmaps = new unsigned char *[charsAvailable];
370 BdfBoundingBox *boxes = 0;
371 if (!hasFixedBBox)
372 boxes = new BdfBoundingBox[charsAvailable];
373 unsigned char *advances = 0;
374 if (!hasFixedAdvance)
375 advances = new unsigned char[charsAvailable];
376
377 for (int i = 0; i < charsAvailable; ++i) {
378 const int encoding = i + firstCharacter;
379 if (font.bitmaps[encoding]) {
380 bitmaps[i] = font.bitmaps[encoding];
381
382 if (!hasFixedBBox)
383 boxes[i] = font.boxes[encoding];
384 if (!hasFixedAdvance)
385 advances[i] = font.advances[encoding];
386 } else {
387 bitmaps[i] = 0;
388 }
389 }
390
391 delete[] font.bitmaps;
392 font.bitmaps = bitmaps;
393 delete[] font.advances;
394 font.advances = advances;
395 delete[] font.boxes;
396 font.boxes = boxes;
397
398 font.numCharacters = charsAvailable;
399 }
400
401 char dateBuffer[256];
402 time_t curTime = time(0);
403 snprintf(dateBuffer, sizeof(dateBuffer), "%s", ctime(&curTime));
404
405 // Finally output the cpp source file to stdout
406 printf("// Generated by convbdf on %s"
407 "#include \"graphics/fonts/bdf.h\"\n"
408 "\n"
409 "// Font information:\n"
410 "// Name: %s\n"
411 "// Size: %dx%d\n"
412 "// Box: %d %d %d %d\n"
413 "// Ascent: %d\n"
414 "// First character: %d\n"
415 "// Default character: %d\n"
416 "// Characters: %d\n"
417 "// Copyright: %s\n"
418 "\n",
419 dateBuffer, fontName.c_str(), font.maxAdvance, font.height,
420 font.defaultBox.width, font.defaultBox.height, font.defaultBox.xOffset,
421 font.defaultBox.yOffset, font.ascent, font.firstCharacter,
422 font.defaultCharacter, font.numCharacters, copyright.c_str());
423
424 printf("namespace Graphics {\n"
425 "\n");
426
427 for (int i = 0; i < font.numCharacters; ++i) {
428 if (!font.bitmaps[i])
429 continue;
430
431 BdfBoundingBox box = hasFixedBBox ? font.defaultBox : font.boxes[i];
432 printf("// Character %d (0x%02X)\n"
433 "// Box: %d %d %d %d\n"
434 "// Advance: %d\n"
435 "//\n", i + font.firstCharacter, i + font.firstCharacter,
436 box.width, box.height, box.xOffset, box.yOffset,
437 hasFixedAdvance ? font.maxAdvance : font.advances[i]);
438
439 printf("// +");
440 for (int x = 0; x < box.width; ++x)
441 printf("-");
442 printf("+\n");
443
444 const unsigned char *bitmap = font.bitmaps[i];
445
446 for (int y = 0; y < box.height; ++y) {
447 printf("// |");
448 unsigned char data = 0;
449 for (int x = 0; x < box.width; ++x) {
450 if (!(x % 8))
451 data = *bitmap++;
452
453 printf("%c", (data & 0x80) ? '*' : ' ');
454 data <<= 1;
455 }
456 printf("|\n");
457 }
458
459 printf("// +");
460 for (int x = 0; x < box.width; ++x)
461 printf("-");
462 printf("+\n");
463
464 const int bytesPerRow = (box.width + 7) / 8;
465 bitmap = font.bitmaps[i];
466 printf("static const byte glyph%d[] = {\n", i);
467 for (int y = 0; y < box.height; ++y) {
468 printf("\t");
469
470 for (int x = 0; x < bytesPerRow; ++x) {
471 if (x)
472 printf(", ");
473 printf("0x%02X", *bitmap++);
474 }
475
476 if (y != box.height - 1)
477 printf(",");
478 printf("\n");
479 }
480 printf("};\n"
481 "\n");
482 }
483
484 printf("// Bitmap pointer table\n"
485 "const byte *const bitmapTable[] = {\n");
486 for (int i = 0; i < font.numCharacters; ++i) {
487 if (font.bitmaps[i])
488 printf("\tglyph%d", i);
489 else
490 printf("\t0");
491
492 if (i != font.numCharacters - 1)
493 printf(",");
494 printf("\n");
495 }
496 printf("};\n"
497 "\n");
498
499 if (!hasFixedAdvance) {
500 printf("// Advance table\n"
501 "static const byte advances[] = {\n");
502 for (int i = 0; i < font.numCharacters; ++i) {
503 if (font.bitmaps[i])
504 printf("\t%d", font.advances[i]);
505 else
506 printf("\t0");
507
508 if (i != font.numCharacters - 1)
509 printf(",");
510 printf("\n");
511 }
512 printf("};\n"
513 "\n");
514 }
515
516 if (!hasFixedBBox) {
517 printf("// Bounding box table\n"
518 "static const BdfBoundingBox boxes[] = {\n");
519 for (int i = 0; i < font.numCharacters; ++i) {
520 if (font.bitmaps[i]) {
521 const BdfBoundingBox &box = font.boxes[i];
522 printf("\t{ %d, %d, %d, %d }", box.width, box.height, box.xOffset, box.yOffset);
523 } else {
524 printf("\t{ 0, 0, 0, 0 }");
525 }
526
527 if (i != font.numCharacters - 1)
528 printf(",");
529 printf("\n");
530 }
531 printf("};\n"
532 "\n");
533 }
534
535 printf("// Font structure\n"
536 "static const BdfFontData desc = {\n"
537 "\t\"%s\", // Family name\n"
538 "\t\"%s\", // Slant\n"
539 "\t%d, // Max advance\n"
540 "\t%d, // Height\n"
541 "\t%d, // Size\n"
542 "\t{ %d, %d, %d, %d }, // Bounding box\n"
543 "\t%d, // Ascent\n"
544 "\n"
545 "\t%d, // First character\n"
546 "\t%d, // Default character\n"
547 "\t%d, // Characters\n"
548 "\n"
549 "\tbitmapTable, // Bitmaps\n",
550 font.familyName, font.slant, font.maxAdvance, font.size, font.height, font.defaultBox.width,
551 font.defaultBox.height, font.defaultBox.xOffset, font.defaultBox.yOffset,
552 font.ascent, font.firstCharacter, font.defaultCharacter, font.numCharacters);
553
554 if (hasFixedAdvance)
555 printf("\t0, // Advances\n");
556 else
557 printf("\tadvances, // Advances\n");
558
559 if (hasFixedBBox)
560 printf("\t0 // Boxes\n");
561 else
562 printf("\tboxes // Boxes\n");
563
564 printf("};\n"
565 "\n"
566 "DEFINE_FONT(%s)\n"
567 "\n"
568 "} // End of namespace Graphics\n",
569 argv[2]);
570 }
571