1 #include <stdlib.h>
2 #include <string.h>
3 #include <stdio.h>
4 #include <stdarg.h>
5 #include <limits.h>
6 #include <zlib.h>
7 #include <ft2build.h>
8 #include FT_FREETYPE_H
9 #include FT_STROKER_H
10 #include FT_GLYPH_H
11 
12 typedef unsigned char uchar;
13 typedef unsigned short ushort;
14 typedef unsigned int uint;
15 
imin(int a,int b)16 int imin(int a, int b) { return a < b ? a : b; }
imax(int a,int b)17 int imax(int a, int b) { return a > b ? a : b; }
18 
fatal(const char * fmt,...)19 void fatal(const char *fmt, ...)
20 {
21     va_list v;
22     va_start(v, fmt);
23     vfprintf(stderr, fmt, v);
24     va_end(v);
25     fputc('\n', stderr);
26 
27     exit(EXIT_FAILURE);
28 }
29 
bigswap(uint n)30 uint bigswap(uint n)
31 {
32     const int islittleendian = 1;
33     return *(const uchar *)&islittleendian ? (n<<24) | (n>>24) | ((n>>8)&0xFF00) | ((n<<8)&0xFF0000) : n;
34 }
35 
writebig(FILE * f,uint n)36 size_t writebig(FILE *f, uint n)
37 {
38     n = bigswap(n);
39     return fwrite(&n, 1, sizeof(n), f);
40 }
41 
writepngchunk(FILE * f,const char * type,uchar * data,uint len)42 void writepngchunk(FILE *f, const char *type, uchar *data, uint len)
43 {
44     uint crc;
45     writebig(f, len);
46     fwrite(type, 1, 4, f);
47     fwrite(data, 1, len, f);
48 
49     crc = crc32(0, Z_NULL, 0);
50     crc = crc32(crc, (const Bytef *)type, 4);
51     if(data) crc = crc32(crc, data, len);
52     writebig(f, crc);
53 }
54 
55 struct pngihdr
56 {
57     uint width, height;
58     uchar bitdepth, colortype, compress, filter, interlace;
59 };
60 
savepng(const char * filename,uchar * data,int w,int h,int bpp,int flip)61 void savepng(const char *filename, uchar *data, int w, int h, int bpp, int flip)
62 {
63     const uchar signature[] = { 137, 80, 78, 71, 13, 10, 26, 10 };
64     struct pngihdr ihdr;
65     FILE *f;
66     long idat;
67     uint len, crc;
68     z_stream z;
69     uchar buf[1<<12];
70     int i, j;
71 
72     memset(&ihdr, 0, sizeof(ihdr));
73     ihdr.width = bigswap(w);
74     ihdr.height = bigswap(h);
75     ihdr.bitdepth = 8;
76     switch(bpp)
77     {
78         case 1: ihdr.colortype = 0; break;
79         case 2: ihdr.colortype = 4; break;
80         case 3: ihdr.colortype = 2; break;
81         case 4: ihdr.colortype = 6; break;
82         default: fatal("Cube2font: invalid PNG bpp"); return;
83     }
84     f = fopen(filename, "wb");
85     if(!f) { fatal("Cube2font: could not write to %s", filename); return; }
86 
87     fwrite(signature, 1, sizeof(signature), f);
88 
89     writepngchunk(f, "IHDR", (uchar *)&ihdr, 13);
90 
91     idat = ftell(f);
92     len = 0;
93     fwrite("\0\0\0\0IDAT", 1, 8, f);
94     crc = crc32(0, Z_NULL, 0);
95     crc = crc32(crc, (const Bytef *)"IDAT", 4);
96 
97     z.zalloc = NULL;
98     z.zfree = NULL;
99     z.opaque = NULL;
100 
101     if(deflateInit(&z, Z_BEST_COMPRESSION) != Z_OK)
102         goto error;
103 
104     z.next_out = (Bytef *)buf;
105     z.avail_out = sizeof(buf);
106 
107     for(i = 0; i < h; i++)
108     {
109         uchar filter = 0;
110         for(j = 0; j < 2; j++)
111         {
112             z.next_in = j ? (Bytef *)data + (flip ? h-i-1 : i)*w*bpp : (Bytef *)&filter;
113             z.avail_in = j ? w*bpp : 1;
114             while(z.avail_in > 0)
115             {
116                 if(deflate(&z, Z_NO_FLUSH) != Z_OK) goto cleanuperror;
117                 #define FLUSHZ do { \
118                     int flush = sizeof(buf) - z.avail_out; \
119                     crc = crc32(crc, buf, flush); \
120                     len += flush; \
121                     fwrite(buf, 1, flush, f); \
122                     z.next_out = (Bytef *)buf; \
123                     z.avail_out = sizeof(buf); \
124                 } while(0)
125                 FLUSHZ;
126             }
127         }
128     }
129 
130     for(;;)
131     {
132         int err = deflate(&z, Z_FINISH);
133         if(err != Z_OK && err != Z_STREAM_END) goto cleanuperror;
134         FLUSHZ;
135         if(err == Z_STREAM_END) break;
136     }
137 
138     deflateEnd(&z);
139 
140     fseek(f, idat, SEEK_SET);
141     writebig(f, len);
142     fseek(f, 0, SEEK_END);
143     writebig(f, crc);
144 
145     writepngchunk(f, "IEND", NULL, 0);
146 
147     fclose(f);
148     return;
149 
150 cleanuperror:
151     deflateEnd(&z);
152 
153 error:
154     fclose(f);
155 
156     fatal("Cube2font: failed saving PNG to %s", filename);
157 }
158 
159 enum
160 {
161     CT_PRINT   = 1<<0,
162     CT_SPACE   = 1<<1,
163     CT_DIGIT   = 1<<2,
164     CT_ALPHA   = 1<<3,
165     CT_LOWER   = 1<<4,
166     CT_UPPER   = 1<<5,
167     CT_UNICODE = 1<<6
168 };
169 #define CUBECTYPE(s, p, d, a, A, u, U) \
170     0, U, U, U, U, U, U, U, U, s, s, s, s, s, U, U, \
171     U, U, U, U, U, U, U, U, U, U, U, U, U, U, U, U, \
172     s, p, p, p, p, p, p, p, p, p, p, p, p, p, p, p, \
173     d, d, d, d, d, d, d, d, d, d, p, p, p, p, p, p, \
174     p, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, \
175     A, A, A, A, A, A, A, A, A, A, A, p, p, p, p, p, \
176     p, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, \
177     a, a, a, a, a, a, a, a, a, a, a, p, p, p, p, U, \
178     U, u, u, u, u, u, u, u, u, u, u, u, u, u, u, u, \
179     u, u, u, u, u, u, u, u, u, u, u, u, u, u, u, U, \
180     u, U, u, U, u, U, u, U, u, U, u, U, u, U, u, U, \
181     u, U, u, U, u, U, u, U, u, U, u, U, u, U, u, U, \
182     u, U, u, U, u, U, u, U, U, u, U, u, U, u, U, U, \
183     U, U, U, U, U, U, U, U, U, U, U, U, U, U, U, U, \
184     U, U, U, U, u, u, u, u, u, u, u, u, u, u, u, u, \
185     u, u, u, u, u, u, u, u, u, u, u, u, u, u, U, u
186 const uchar cubectype[256] =
187 {
188     CUBECTYPE(CT_SPACE,
189               CT_PRINT,
190               CT_PRINT|CT_DIGIT,
191               CT_PRINT|CT_ALPHA|CT_LOWER,
192               CT_PRINT|CT_ALPHA|CT_UPPER,
193               CT_PRINT|CT_UNICODE|CT_ALPHA|CT_LOWER,
194               CT_PRINT|CT_UNICODE|CT_ALPHA|CT_UPPER)
195 };
iscubeprint(uchar c)196 int iscubeprint(uchar c) { return cubectype[c]&CT_PRINT; }
iscubespace(uchar c)197 int iscubespace(uchar c) { return cubectype[c]&CT_SPACE; }
iscubealpha(uchar c)198 int iscubealpha(uchar c) { return cubectype[c]&CT_ALPHA; }
iscubealnum(uchar c)199 int iscubealnum(uchar c) { return cubectype[c]&(CT_ALPHA|CT_DIGIT); }
iscubelower(uchar c)200 int iscubelower(uchar c) { return cubectype[c]&CT_LOWER; }
iscubeupper(uchar c)201 int iscubeupper(uchar c) { return cubectype[c]&CT_UPPER; }
202 const int cube2unichars[256] =
203 {
204     0, 192, 193, 194, 195, 196, 197, 198, 199, 9, 10, 11, 12, 13, 200, 201,
205     202, 203, 204, 205, 206, 207, 209, 210, 211, 212, 213, 214, 216, 217, 218, 219,
206     32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
207     48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
208     64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
209     80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
210     96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111,
211     112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 220,
212     221, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237,
213     238, 239, 241, 242, 243, 244, 245, 246, 248, 249, 250, 251, 252, 253, 255, 0x104,
214     0x105, 0x106, 0x107, 0x10C, 0x10D, 0x10E, 0x10F, 0x118, 0x119, 0x11A, 0x11B, 0x11E, 0x11F, 0x130, 0x131, 0x141,
215     0x142, 0x143, 0x144, 0x147, 0x148, 0x150, 0x151, 0x152, 0x153, 0x158, 0x159, 0x15A, 0x15B, 0x15E, 0x15F, 0x160,
216     0x161, 0x164, 0x165, 0x16E, 0x16F, 0x170, 0x171, 0x178, 0x179, 0x17A, 0x17B, 0x17C, 0x17D, 0x17E, 0x404, 0x411,
217     0x413, 0x414, 0x416, 0x417, 0x418, 0x419, 0x41B, 0x41F, 0x423, 0x424, 0x426, 0x427, 0x428, 0x429, 0x42A, 0x42B,
218     0x42C, 0x42D, 0x42E, 0x42F, 0x431, 0x432, 0x433, 0x434, 0x436, 0x437, 0x438, 0x439, 0x43A, 0x43B, 0x43C, 0x43D,
219     0x43F, 0x442, 0x444, 0x446, 0x447, 0x448, 0x449, 0x44A, 0x44B, 0x44C, 0x44D, 0x44E, 0x44F, 0x454, 0x490, 0x491
220 };
cube2uni(uchar c)221 int cube2uni(uchar c)
222 {
223     return cube2unichars[c];
224 }
225 
encodeutf8(int uni)226 const char *encodeutf8(int uni)
227 {
228     static char buf[7];
229     char *dst = buf;
230     if(uni <= 0x7F) { *dst++ = uni; goto uni1; }
231     else if(uni <= 0x7FF) { *dst++ = 0xC0 | (uni>>6); goto uni2; }
232     else if(uni <= 0xFFFF) { *dst++ = 0xE0 | (uni>>12); goto uni3; }
233     else if(uni <= 0x1FFFFF) { *dst++ = 0xF0 | (uni>>18); goto uni4; }
234     else if(uni <= 0x3FFFFFF) { *dst++ = 0xF8 | (uni>>24); goto uni5; }
235     else if(uni <= 0x7FFFFFFF) { *dst++ = 0xFC | (uni>>30); goto uni6; }
236     else goto uni1;
237 uni6: *dst++ = 0x80 | ((uni>>24)&0x3F);
238 uni5: *dst++ = 0x80 | ((uni>>18)&0x3F);
239 uni4: *dst++ = 0x80 | ((uni>>12)&0x3F);
240 uni3: *dst++ = 0x80 | ((uni>>6)&0x3F);
241 uni2: *dst++ = 0x80 | (uni&0x3F);
242 uni1: *dst++ = '\0';
243     return buf;
244 }
245 
246 struct fontchar { int code, uni, tex, x, y, w, h, offx, offy, offset, advance; FT_BitmapGlyph color, alpha; };
247 
248 const char *texdir = "";
249 
texfilename(const char * name,int texnum)250 const char *texfilename(const char *name, int texnum)
251 {
252     static char file[256];
253     snprintf(file, sizeof(file), "%s%d.png", name, texnum);
254     return file;
255 }
256 
texname(const char * name,int texnum)257 const char *texname(const char *name, int texnum)
258 {
259     static char file[512];
260     snprintf(file, sizeof(file), "<grey>%s%s", texdir, texfilename(name, texnum));
261     return file;
262 }
263 
writetexs(const char * name,struct fontchar * chars,int numchars,int numtexs,int tw,int th)264 void writetexs(const char *name, struct fontchar *chars, int numchars, int numtexs, int tw, int th)
265 {
266     int tex;
267     uchar *pixels = (uchar *)malloc(tw*th*2);
268     if(!pixels) fatal("Cube2font: failed allocating textures");
269     for(tex = 0; tex < numtexs; tex++)
270     {
271         const char *file = texfilename(name, tex);
272         int texchars = 0, i;
273         uchar *dst, *src;
274         memset(pixels, 0, tw*th*2);
275         for(i = 0; i < numchars; i++)
276         {
277             struct fontchar *c = &chars[i];
278             uint x, y;
279             if(c->tex != tex) continue;
280             texchars++;
281             dst = &pixels[2*((c->y + c->offy - c->color->top)*tw + c->x + c->color->left - c->offx)];
282             src = (uchar *)c->color->bitmap.buffer;
283             for(y = 0; y < c->color->bitmap.rows; y++)
284             {
285                 for(x = 0; x < c->color->bitmap.width; x++)
286                     dst[2*x] = src[x];
287                 src += c->color->bitmap.pitch;
288                 dst += 2*tw;
289             }
290             dst = &pixels[2*((c->y + c->offy - c->alpha->top)*tw + c->x + c->alpha->left - c->offx)];
291             src = (uchar *)c->alpha->bitmap.buffer;
292             for(y = 0; y < c->alpha->bitmap.rows; y++)
293             {
294                 for(x = 0; x < c->alpha->bitmap.width; x++)
295                     dst[2*x+1] = src[x];
296                 src += c->alpha->bitmap.pitch;
297                 dst += 2*tw;
298             }
299         }
300         printf("cube2font: writing %d chars to %s\n", texchars, file);
301         savepng(file, pixels, tw, th, 2, 0);
302    }
303    free(pixels);
304 }
305 
writecfg(const char * name,struct fontchar * chars,int numchars,int x1,int y1,int x2,int y2,int sw,int sh,int argc,char ** argv)306 void writecfg(const char *name, struct fontchar *chars, int numchars, int x1, int y1, int x2, int y2, int sw, int sh, int argc, char **argv)
307 {
308     FILE *f;
309     char file[256];
310     int i, lastcode = 0, lasttex = 0;
311     snprintf(file, sizeof(file), "%s.cfg", name);
312     f = fopen(file, "w");
313     if(!f) fatal("Cube2font: failed writing %s", file);
314     printf("cube2font: writing %d chars to %s\n", numchars, file);
315     fprintf(f, "//");
316     for(i = 1; i < argc; i++)
317         fprintf(f, " %s", argv[i]);
318     fprintf(f, "\n");
319     fprintf(f, "font \"%s\" \"%s\" %d %d\n", name, texname(name, 0), sw, sh);
320     for(i = 0; i < numchars; i++)
321     {
322         struct fontchar *c = &chars[i];
323         if(!lastcode && lastcode < c->code)
324         {
325             fprintf(f, "fontoffset \"%s\"\n", encodeutf8(c->uni));
326             lastcode = c->code;
327         }
328         else if(lastcode < c->code)
329         {
330             if(lastcode + 1 == c->code)
331                 fprintf(f, "fontskip // %d\n", lastcode);
332             else
333                 fprintf(f, "fontskip %d // %d .. %d\n", c->code - lastcode, lastcode, c->code-1);
334             lastcode = c->code;
335         }
336         if(lasttex != c->tex)
337         {
338             fprintf(f, "\nfonttex \"%s\"\n", texname(name, c->tex));
339             lasttex = c->tex;
340         }
341         if(c->code != c->uni)
342             fprintf(f, "fontchar %d %d %d %d %d %d %d // %s (%d -> 0x%X)\n", c->x, c->y, c->w, c->h, c->offx+c->offset, y2-c->offy, c->advance, encodeutf8(c->uni), c->code, c->uni);
343         else
344             fprintf(f, "fontchar %d %d %d %d %d %d %d // %s (%d)\n", c->x, c->y, c->w, c->h, c->offx+c->offset, y2-c->offy, c->advance, encodeutf8(c->uni), c->code);
345         lastcode++;
346     }
347     fclose(f);
348 }
349 
groupchar(int c)350 int groupchar(int c)
351 {
352     switch(c)
353     {
354     case 0x152: case 0x153: case 0x178: return 1;
355     }
356     if(c < 127 || c >= 0x2000) return 0;
357     if(c < 0x100) return 1;
358     if(c < 0x400) return 2;
359     return 3;
360 }
361 
sortchars(const void * x,const void * y)362 int sortchars(const void *x, const void *y)
363 {
364     const struct fontchar *xc = *(const struct fontchar **)x, *yc = *(const struct fontchar **)y;
365     int xg = groupchar(xc->uni), yg = groupchar(yc->uni);
366     if(xg < yg) return -1;
367     if(xg > yg) return 1;
368     if(xc->h != yc->h) return yc->h - xc->h;
369     if(xc->w != yc->w) return yc->w - xc->w;
370     return yc->uni - xc->uni;
371 }
372 
scorechar(struct fontchar * f,int pad,int tw,int th,int rw,int rh,int ry)373 int scorechar(struct fontchar *f, int pad, int tw, int th, int rw, int rh, int ry)
374 {
375     int score = 0;
376     if(rw + f->w > tw) { ry += rh + pad; score = 1; }
377     if(ry + f->h > th) score = 2;
378     return score;
379 }
380 
main(int argc,char ** argv)381 int main(int argc, char **argv)
382 {
383     FT_Library l;
384     FT_Face f;
385     FT_Stroker s, s2;
386     int i, pad, offset, advance, w, h, tw, th, c, trial = -2, rw = 0, rh = 0, ry = 0, x1 = INT_MAX, x2 = INT_MIN, y1 = INT_MAX, y2 = INT_MIN, w2 = 0, h2 = 0, sw = 0, sh = 0;
387     float outborder = 0, inborder = 0;
388     struct fontchar chars[256];
389     struct fontchar *order[256];
390     int numchars = 0, numtex = 0;
391     if(argc < 11)
392         fatal("Usage: cube2font infile outfile outborder[:inborder] pad offset advance charwidth charheight texwidth texheight [spacewidth spaceheight texdir]");
393     sscanf(argv[3], "%f:%f", &outborder, &inborder);
394     pad = atoi(argv[4]);
395     offset = atoi(argv[5]);
396     advance = atoi(argv[6]);
397     w = atoi(argv[7]);
398     h = atoi(argv[8]);
399     tw = atoi(argv[9]);
400     th = atoi(argv[10]);
401     if(argc > 11) sw = atoi(argv[11]);
402     if(argc > 12) sh = atoi(argv[12]);
403     if(argc > 13) texdir = argv[13];
404     if(FT_Init_FreeType(&l))
405         fatal("Cube2font: failed initing freetype");
406     if(FT_New_Face(l, argv[1], 0, &f) ||
407        FT_Set_Charmap(f, f->charmaps[0]) ||
408        FT_Set_Pixel_Sizes(f, w, h) ||
409        FT_Stroker_New(l, &s) ||
410        FT_Stroker_New(l, &s2))
411         fatal("Cube2font: failed loading font %s", argv[1]);
412     if(outborder > 0) FT_Stroker_Set(s, (FT_Fixed)(outborder * 64), FT_STROKER_LINECAP_ROUND, FT_STROKER_LINEJOIN_ROUND, 0);
413     if(inborder > 0) FT_Stroker_Set(s2, (FT_Fixed)(inborder * 64), FT_STROKER_LINECAP_ROUND, FT_STROKER_LINEJOIN_ROUND, 0);
414     for(c = 0; c < 256; c++) if(iscubeprint(c))
415     {
416         FT_Glyph p, p2;
417         FT_BitmapGlyph b, b2;
418         struct fontchar *dst = &chars[numchars];
419         dst->code = c;
420         dst->uni = cube2uni(c);
421         if(FT_Load_Char(f, dst->uni, FT_LOAD_DEFAULT))
422             fatal("Cube2font: failed loading character %s", encodeutf8(dst->uni));
423         FT_Get_Glyph(f->glyph, &p);
424         p2 = p;
425         if(outborder > 0) FT_Glyph_StrokeBorder(&p, s, 0, 0);
426         if(inborder > 0) FT_Glyph_StrokeBorder(&p2, s2, 1, 0);
427         FT_Glyph_To_Bitmap(&p, FT_RENDER_MODE_NORMAL, 0, 1);
428         if(inborder > 0 || outborder > 0) FT_Glyph_To_Bitmap(&p2, FT_RENDER_MODE_NORMAL, 0, 1);
429         else p2 = p;
430         b = (FT_BitmapGlyph)p;
431         b2 = (FT_BitmapGlyph)p2;
432         dst->tex = -1;
433         dst->x = INT_MIN;
434         dst->y = INT_MIN;
435         dst->offx = imin(b->left, b2->left);
436         dst->offy = imax(b->top, b2->top);
437         dst->offset = offset;
438         dst->advance = offset + ((p->advance.x+0xFFFF)>>16) + advance;
439         dst->w = imax(b->left + b->bitmap.width, b2->left + b2->bitmap.width) - dst->offx;
440         dst->h = dst->offy - imin(b->top - b->bitmap.rows, b2->top - b2->bitmap.rows);
441         dst->alpha = b;
442         dst->color = b2;
443         order[numchars++] = dst;
444     }
445     qsort(order, numchars, sizeof(order[0]), sortchars);
446     for(i = 0; i < numchars;)
447     {
448         struct fontchar *dst;
449         int j, k, trial0, prevscore, dstscore, fitscore;
450         for(trial0 = trial, prevscore = -1; (trial -= 2) >= trial0-512;)
451         {
452             int g, fw = rw, fh = rh, fy = ry, curscore = 0, reused = 0;
453             for(j = i; j < numchars; j++)
454             {
455                 dst = order[j];
456                 if(dst->tex >= 0 || dst->tex <= trial) continue;
457                 g = groupchar(dst->uni);
458                 dstscore = scorechar(dst, pad, tw, th, fw, fh, fy);
459                 for(k = j; k < numchars; k++)
460                 {
461                     struct fontchar *fit = order[k];
462                     if(fit->tex >= 0 || fit->tex <= trial) continue;
463                     if(fit->tex >= trial0 && groupchar(fit->uni) != g) break;
464                     fitscore = scorechar(fit, pad, tw, th, fw, fh, fy);
465                     if(fitscore < dstscore || (fitscore == dstscore && fit->h > dst->h))
466                     {
467                         dst = fit;
468                         dstscore = fitscore;
469                     }
470                 }
471                 if(fw + dst->w > tw)
472                 {
473                     fy += fh + pad;
474                     fw = fh = 0;
475                 }
476                 if(fy + dst->h > th)
477                 {
478                     fy = fw = fh = 0;
479                     if(curscore > 0) break;
480                 }
481                 if(dst->tex >= trial+1 && dst->tex <= trial+2) { dst->tex = trial; reused++; }
482                 else dst->tex = trial;
483                 fw += dst->w + pad;
484                 fh = imax(fh, dst->h);
485                 if(dst != order[j]) --j;
486                 curscore++;
487             }
488             if(reused < prevscore || curscore <= prevscore) break;
489             prevscore = curscore;
490         }
491         for(; i < numchars; i++)
492         {
493             dst = order[i];
494             if(dst->tex >= 0) continue;
495             dstscore = scorechar(dst, pad, tw, th, rw, rh, ry);
496             for(j = i; j < numchars; j++)
497             {
498                 struct fontchar *fit = order[j];
499                 if(fit->tex < trial || fit->tex > trial+2) continue;
500                 fitscore = scorechar(fit, pad, tw, th, rw, rh, ry);
501                 if(fitscore < dstscore || (fitscore == dstscore && fit->h > dst->h))
502                 {
503                     dst = fit;
504                     dstscore = fitscore;
505                 }
506             }
507             if(dst->tex < trial || dst->tex > trial+2) break;
508             if(rw + dst->w > tw)
509             {
510                 ry += rh + pad;
511                 rw = rh = 0;
512             }
513             if(ry + dst->h > th)
514             {
515                 ry = rw = rh = 0;
516                 numtex++;
517             }
518             dst->tex = numtex;
519             dst->x = rw;
520             dst->y = ry;
521             rw += dst->w + pad;
522             rh = imax(rh, dst->h);
523             y1 = imin(y1, dst->offy - dst->h);
524             y2 = imax(y2, dst->offy);
525             x1 = imin(x1, dst->offx);
526             x2 = imax(x2, dst->offx + dst->w);
527             w2 = imax(w2, dst->w);
528             h2 = imax(h2, dst->h);
529             if(dst != order[i]) --i;
530         }
531     }
532     if(rh > 0) numtex++;
533 #if 0
534     if(sw <= 0)
535     {
536         if(FT_Load_Char(f, ' ', FT_LOAD_DEFAULT))
537             fatal("Cube2font: failed loading space character");
538         sw = (f->glyph->advance.x+0x3F)>>6;
539     }
540 #endif
541     if(sh <= 0) sh = y2 - y1;
542     if(sw <= 0) sw = sh/3;
543     writetexs(argv[2], chars, numchars, numtex, tw, th);
544     writecfg(argv[2], chars, numchars, x1, y1, x2, y2, sw, sh, argc, argv);
545     for(i = 0; i < numchars; i++)
546     {
547         if(chars[i].alpha != chars[i].color) FT_Done_Glyph((FT_Glyph)chars[i].alpha);
548         FT_Done_Glyph((FT_Glyph)chars[i].color);
549     }
550     FT_Stroker_Done(s);
551     FT_Stroker_Done(s2);
552     FT_Done_FreeType(l);
553     printf("cube2font: (%d, %d) .. (%d, %d) = (%d, %d) / (%d, %d), %d texs\n", x1, y1, x2, y2, x2 - x1, y2 - y1, w2, h2, numtex);
554     return EXIT_SUCCESS;
555 }
556 
557