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