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