/*
 * libsfn/bitmap.c
 *
 * Copyright (C) 2020 bzt (bztsrc@gitlab)
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use, copy,
 * modify, merge, publish, distribute, sublicense, and/or sell copies
 * of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 * @brief File functions for bitmap and pixmap fonts
 *
 */

#ifndef USE_NOFOREIGN

#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#ifdef HAS_QUANT
#include "libimagequant.h"
#endif
#define STBI_IMPLEMENTATION
#include "stb_png.h"
#include <ssfn.h>
#include "libsfn.h"
#include "bitmap.h"

/* DOS 437 codepage to UNICODE (needed by BMF importer) */
static int doscp[256] = {
    0x0000,0x263A,0x263B,0x2665,0x2666,0x2663,0x2660,0x2022,0x25D8,0x25CB,0x25D9,0x2642,0x2640,0x266A,0x266B,0x263C,
    0x25BA,0x25C4,0x2195,0x203C,0x00B6,0x00A7,0x25AC,0x21A8,0x2191,0x2193,0x2192,0x2190,0x221F,0x2194,0x25B2,0x25BC,
    0x0020,0x0021,0x0022,0x0023,0x0024,0x0025,0x0026,0x0027,0x0028,0x0029,0x002a,0x002b,0x002c,0x002d,0x002e,0x002f,
    0x0030,0x0031,0x0032,0x0033,0x0034,0x0035,0x0036,0x0037,0x0038,0x0039,0x003a,0x003b,0x003c,0x003d,0x003e,0x003f,
    0x0040,0x0041,0x0042,0x0043,0x0044,0x0045,0x0046,0x0047,0x0048,0x0049,0x004a,0x004b,0x004c,0x004d,0x004e,0x004f,
    0x0050,0x0051,0x0052,0x0053,0x0054,0x0055,0x0056,0x0057,0x0058,0x0059,0x005a,0x005b,0x005c,0x005d,0x005e,0x005f,
    0x0060,0x0061,0x0062,0x0063,0x0064,0x0065,0x0066,0x0067,0x0068,0x0069,0x006a,0x006b,0x006c,0x006d,0x006e,0x006f,
    0x0070,0x0071,0x0072,0x0073,0x0074,0x0075,0x0076,0x0077,0x0078,0x0079,0x007a,0x007b,0x007c,0x007d,0x007e,0x007f,
    0x00c7,0x00fc,0x00e9,0x00e2,0x00e4,0x00e0,0x00e5,0x00e7,0x00ea,0x00eb,0x00e8,0x00ef,0x00ee,0x00ec,0x00c4,0x00c5,
    0x00c9,0x00e6,0x00c6,0x00f4,0x00f6,0x00f2,0x00fb,0x00f9,0x00ff,0x00d6,0x00dc,0x00a2,0x00a3,0x00a5,0x20a7,0x0192,
    0x00e1,0x00ed,0x00f3,0x00fa,0x00f1,0x00d1,0x00aa,0x00ba,0x00bf,0x2310,0x00ac,0x00bd,0x00bc,0x00a1,0x00ab,0x00bb,
    0x2591,0x2592,0x2593,0x2502,0x2524,0x2561,0x2562,0x2556,0x2555,0x2563,0x2551,0x2557,0x255d,0x255c,0x255b,0x2510,
    0x2514,0x2534,0x252c,0x251c,0x2500,0x253c,0x255e,0x255f,0x255a,0x2554,0x2569,0x2566,0x2560,0x2550,0x256c,0x2567,
    0x2568,0x2564,0x2565,0x2559,0x2558,0x2552,0x2553,0x256b,0x256a,0x2518,0x250c,0x2588,0x2584,0x258c,0x2590,0x2580,
    0x03b1,0x00df,0x0393,0x03c0,0x03a3,0x03c3,0x00b5,0x03c4,0x03a6,0x0398,0x03a9,0x03b4,0x221e,0x03c6,0x03b5,0x2229,
    0x2261,0x00b1,0x2265,0x2264,0x2320,0x2321,0x00f7,0x2248,0x00b0,0x2219,0x00b7,0x221a,0x207f,0x00b2,0x25a0,0x00a0
};
typedef struct {
    uint32_t magic;
    uint8_t  version;
    uint8_t  lineHeight;
    int8_t   sizeOver;
    int8_t   sizeUnder;
    int8_t   addSpace;
    int8_t   sizeInner;
    uint8_t  usedColors;
    uint8_t  highestColor;
    uint8_t  alphaBits;
    uint8_t  extraPalettes;
    uint16_t reserved;
    uint8_t  numColors;
} __attribute__((packed)) bmf_t;

typedef struct {
    unsigned int magic;
    unsigned int version;
    unsigned int headersize;
    unsigned int flags;
    unsigned int numglyph;
    unsigned int bytesperglyph;
    unsigned int height;
    unsigned int width;
    unsigned char glyphs;
} __attribute__((packed)) psf_t;

/****************************** file format parsers ******************************/

/**
 * Parse PSF2 font (binary)
 */
void psf(unsigned char *ptr, int size)
{
    psf_t *psf = (psf_t*)ptr;
    uint16_t *utbl = NULL;
    uint32_t c, g=0, unicode, nc = 0, numchars = psf->numglyph;
    unsigned char *s, *e, *glyph, *bitmap;
    int i, j, k, l, n;

    s=(unsigned char*)(ptr + psf->headersize + psf->numglyph*psf->bytesperglyph);
    e=ptr + size;
    /* try to fix bad fonts */
    if(s > e) {
        for(s = e; s + 1 > ptr + psf->headersize && (s[-1] || s[0] == 0xFF); s--);
        psf->numglyph = ((int)(s - ptr) - psf->headersize) / psf->bytesperglyph;
    }
    if(s < e) {
        numchars = 0;
        utbl = (uint16_t*)malloc(0x110000*sizeof(uint16_t));
        if(!utbl) { fprintf(stderr,"libsfn: memory allocation error\n"); return; }
        memset(utbl, 0, 0x110000*sizeof(uint16_t));
        while(s<e && g<psf->numglyph) {
            c = (uint16_t)((uint8_t)s[0]);
            if(c == 0xFF) { g++; } else {
                if((c & 128) != 0) {
                    if((c & 32) == 0 ) { c=((s[0] & 0x1F)<<6)+(s[1] & 0x3F); s++; } else
                    if((c & 16) == 0 ) { c=((((s[0] & 0xF)<<6)+(s[1] & 0x3F))<<6)+(s[2] & 0x3F); s+=2; } else
                    if((c & 8) == 0 ) { c=((((((s[0] & 0x7)<<6)+(s[1] & 0x3F))<<6)+(s[2] & 0x3F))<<6)+(s[3] & 0x3F); s+=3;}
                    else c=0;
                }
                if(c<0x110000) {
                    utbl[c] = g;
                    numchars++;
                }
            }
            s++;
        }
    }
    if((psf->flags >> 24) && !ctx.baseline) ctx.baseline = (psf->flags >> 24);
    if(psf->width < 1) psf->width = 1;
    if(psf->height < 1) psf->height = 1;
    l = ((psf->width + 7) & ~7) * (psf->height + 1);
    bitmap = (unsigned char*)malloc(l);
    if(!bitmap) { fprintf(stderr,"libsfn: memory allocation error\n"); return; }
    for(unicode=rs;unicode<=(uint32_t)re;unicode++) {
        g = utbl? utbl[unicode] : unicode;
        if((!g && unicode && !iswhitespace(unicode)) || g >= psf->numglyph) continue;
        glyph = ptr + psf->headersize + g*psf->bytesperglyph;
        memset(bitmap, 0, l);
        for(i=k=0;i<(int)psf->bytesperglyph;) {
            for(n = 0; n < (int)psf->width; i++)
                for(j=0x80;j && n < (int)psf->width;j>>=1,n++)
                    bitmap[k+n] = (glyph[i] & j) ? 0xFE : 0xFF;
            k += psf->width;
        }
        if(sfn_charadd(unicode, psf->width, psf->height, 0, 0, 0))
            sfn_layeradd(unicode, SSFN_FRAG_BITMAP, 0, 0, psf->width, psf->height, 0xFE, bitmap);
        if(pbar) (*pbar)(0, 0, ++nc, numchars, PBAR_BITMAP);
    }
    free(bitmap);
    free(utbl);
}

/**
 * Parse GRUB's PFF2 font (binary)
 * yet another messy, resource wasteful, extremely badly documented format...
 */
void pff(unsigned char *ptr, int size)
{
    uint32_t len = 0, unicode, nc = 0, numchars = 0;
    int16_t w = 0, h = 0, p, n, a /*, x, y, mix, miy, max, may*/;
    int i, j, k, m;
    unsigned char *end = ptr + size, *section, *data = ptr, *bitmap;

    ptr += 12;
    while(ptr < end && len < (uint32_t)size) {
        len = (ptr[4] << 24) | (ptr[5] << 16) | (ptr[6] << 8) | ptr[7];
        section = ptr + 8;
        if(!memcmp(ptr, "NAME", 4)) sfn_setstr(&ctx.name, (char*)section, len); else
        if(!memcmp(ptr, "FAMI", 4)) sfn_setstr(&ctx.familyname, (char*)section, len); else
        if(!memcmp(ptr, "WEIG", 4) && section[0]=='b') ctx.style |= SSFN_STYLE_BOLD; else
        if(!memcmp(ptr, "SLAN", 4) && section[0]=='i') ctx.style |= SSFN_STYLE_ITALIC; else
        if(!memcmp(ptr, "MAXW", 4)) w = (section[0] << 8) | section[1]; else
        if(!memcmp(ptr, "MAXH", 4)) h = (section[0] << 8) | section[1]; else
        if(!memcmp(ptr, "ASCE", 4)) ctx.baseline = (section[0] << 8) | section[1]; else
        if(!memcmp(ptr, "CHIX", 4)) {
            /*mix = miy = max = may = 0;*/
            for(end = section + len, ptr = section; ptr < end; ptr += 9) {
                if(!ptr[4]) {
                    n = (data[((ptr[5] << 24) | (ptr[6] << 16) | (ptr[7] << 8) | ptr[8]) + 0] << 8) |
                        data[((ptr[5] << 24) | (ptr[6] << 16) | (ptr[7] << 8) | ptr[8]) + 1];
                    if(n > w) w = n;
                    n = (data[((ptr[5] << 24) | (ptr[6] << 16) | (ptr[7] << 8) | ptr[8]) + 2] << 8) |
                        data[((ptr[5] << 24) | (ptr[6] << 16) | (ptr[7] << 8) | ptr[8]) + 3];
                    if(n > h) h = n;
/*
                    n = (data[((ptr[5] << 24) | (ptr[6] << 16) | (ptr[7] << 8) | ptr[8]) + 4] << 8) |
                        data[((ptr[5] << 24) | (ptr[6] << 16) | (ptr[7] << 8) | ptr[8]) + 5];
                    if(n < mix) { mix = n; } if(n > max) max = n;
                    n = (data[((ptr[5] << 24) | (ptr[6] << 16) | (ptr[7] << 8) | ptr[8]) + 6] << 8) |
                        data[((ptr[5] << 24) | (ptr[6] << 16) | (ptr[7] << 8) | ptr[8]) + 7];
                    if(n < miy) { miy = n; } if(n > may) may = n;
*/
                    numchars++;
                }
            }
            ctx.width = w/* - mix + max*/;
            ctx.height = h/* - miy + may*/;
/*            ctx.baseline -= miy; */
            printf("\r  Name '%s' num_glyphs: %d, ascender: %d, width: %d, height: %d\n", ctx.name, numchars, ctx.baseline,
                ctx.width, ctx.height);
            n = ctx.width * ctx.height;
            bitmap = (unsigned char*)malloc(n);
            if(!bitmap) { fprintf(stderr,"libsfn: memory allocation error\n"); return; }
            for(end = section + len; section < end; section += 9) {
                /* undocumented: section[4] supposed to indicate compression of some sort? grub-mkfont.c always writes 0 */
                if(!section[4]) {
                    /* undocumented: section[0] holds left and right joins (or both), not sure what to do with those */
                    unicode = (section[1] << 16) | (section[2] << 8) | section[3];
                    ptr = data + ((section[5] << 24) | (section[6] << 16) | (section[7] << 8) | section[8]);
                    memset(bitmap, 0xFF, n);
                    w = (ptr[0] << 8) | ptr[1]; h = (ptr[2] << 8) | ptr[3];
/*                    x = (ptr[4] << 8) | ptr[5]; y = (ptr[6] << 8) | ptr[7];*/
                    a = (ptr[8] << 8) | ptr[9];
                    p = w; /* + (x < 0 ? 0 : x); h -= y;*/
                    ptr += 10; /*k = (y - miy) * p;*/
                    for(j = k = 0, m = 0x80; j < h; j++, k += p)
                        for(i = 0; i < w; i++, m >>= 1) {
                            if(!m) { m = 0x80; ptr++; }
                            if(ptr[0] & m) bitmap[k + i/* + (x < 0 ? 0 : x)*/] = 0xFE;
                        }
                    if(sfn_charadd(unicode, p, h, a, 0, /*x < 0 ? -x :*/ 0))
                        sfn_layeradd(unicode, SSFN_FRAG_BITMAP, 0, 0, p, h, 0xFE, bitmap);
                    if(pbar) (*pbar)(0, 0, ++nc, numchars, PBAR_BITMAP);
                }
            }
            free(bitmap);
            break;
        }
        ptr += 8 + len;
    }
}

/**
 * Parse fucked up Windows Console Font (binary)
 */
void fnt(unsigned char *ptr, int size)
{
    int i, j, k, l, m, w, h, p, mn, mx, defchar;
    unsigned char *data = ptr, *bit, map[8192], *bitmap = NULL, ver, charset;
    /* skip over executable header... */
    if(ptr[0] == 'M' && ptr[1] == 'Z') {
        ptr += ((ptr[0x3D] << 8) | ptr[0x3C]); if(ptr[0] != 'N' || ptr[1] != 'E') return;
        ptr += ((ptr[37] << 8) | ptr[36]); j = ((ptr[1] << 8) | ptr[0]); ptr += 2; if(j > 16) return;
        for(i = 0; i < 16 && (ptr[0] || ptr[1]); i++) {
            if(ptr[0] == 0x08 && ptr[1] == 0x80) {
                if(((ptr[3] << 8) | ptr[2]) < 1) { return; } ptr += 8; ptr = data + (((ptr[1] << 8) | ptr[0]) << j); break;
            } else ptr += ((ptr[3] << 8) | ptr[2]) * 12 + 8;
        }
    }
    /* parse Windows resource file */
    if(ptr[0] || (ptr[1] != 2 && ptr[1] != 3) || ((ptr[4] << 16) | (ptr[3] << 8) | ptr[2]) > size ||
        (ptr[66] & 1)) return;
    sfn_setstr(&ctx.name, (char*)(ptr + ((ptr[107] << 16) | (ptr[106] << 8) | ptr[105])), 0);
    sfn_setstr(&ctx.license, (char*)(ptr + 6), 0);
    h = ((ptr[89] << 8) | ptr[88]); charset = ptr[85];
    mn = ptr[95]; mx = ptr[96]; defchar = ptr[97] + mn; ctx.baseline = ((ptr[75] << 8) | ptr[74]);
    if(ptr[80]) ctx.style |= SSFN_STYLE_ITALIC;
    if(((ptr[84] << 8) | ptr[83]) > 400) ctx.style |= SSFN_STYLE_BOLD;
    switch(ptr[90] >> 4) {
        case 2: ctx.family = SSFN_FAMILY_SANS; break;
        case 3: ctx.family = SSFN_FAMILY_MONOSPACE; break;
        case 4: ctx.family = SSFN_FAMILY_HAND; break;
        case 5: ctx.family = SSFN_FAMILY_DECOR; break;
        default: ctx.family = SSFN_FAMILY_SERIF; break;
    }
    printf("\r  Name '%s' num_glyphs: %d, ascender: %d, height: %d\n", ctx.name, mx - mn + 1, ctx.baseline, h);
    if(!h || mn >= mx) return;
    ver = ptr[1]; data = ptr; ptr += (ptr[1] == 3 ? 148 : 118);

    /* get bitmaps */
    for(unicode = mn; unicode <= mx; unicode++, ptr += ver == 3 ? 6 : 4) {
        w = ptr[0]; bit = data + ((ver == 3 ? (ptr[4] << 16) : 0) | (ptr[3] << 8) | ptr[2]);
        p = ((w - 1) >> 3) + 1;
        if(p * h > (int)sizeof(map)) continue;
        bitmap = realloc(bitmap, w * h);
        if(!bitmap) { fprintf(stderr,"libsfn: memory allocation error\n"); return; }
        /* I'm fed up. I can't properly get the bytes, so lets copy them into correct order before I get crazy. Ugly, but works */
        for(j = 0; j < p; j++)
            for(k = 0; k < h; k++)
                map[k * p + j] = *bit++;
        for(i = k = l = 0; k < h; k++)
            for(j = 0, l = k * p, m = 0x80; j < w; j++, m >>= 1) {
                if(!m) { m = 0x80; l++; }
                bitmap[i++] = map[l] & m ? 0xFE : 0xFF;
            }
        if(sfn_charadd(charset ? unicode : doscp[unicode], w, h, 0, 0, 0))
            sfn_layeradd(charset ? unicode : doscp[unicode], SSFN_FRAG_BITMAP, 0, 0, w, h, 0xFE, bitmap);
        if(unicode == defchar) {
            sfn_chardel(0);
            if(sfn_charadd(0, w, h, 0, 0, 0))
                sfn_layeradd(0, SSFN_FRAG_BITMAP, 0, 0, w, h, 0xFE, bitmap);
        }
    }
    free(bitmap);
}


/**
 * Parse GNU unifont hex format (text)
 */
void hex(char *ptr, int size)
{
    uint32_t i, j, c, unicode, nc = 0, numchars;
    int w;
    char *end = ptr + size;
    unsigned char bitmap[256];

    numchars = 0;
    for(numchars=0;ptr < end && *ptr;ptr++) if(*ptr=='\n') numchars++;
    ptr = end - size;
    while(ptr < end && *ptr) {
        unicode = gethex(ptr, 6);
        while(*ptr && *ptr!=':') ptr++;
        if(!*ptr) return;
        ptr++;
        while(*ptr && (*ptr==' '||*ptr=='\t')) ptr++;
        if(!*ptr) return;
        memset(bitmap, 0, 256);
        for(i = 0;i<256 && *ptr && *ptr!='\n' && *ptr!='\r';ptr += 2) {
            c = gethex(ptr, 2);
            for(j=0x80;j;j>>=1) bitmap[i++] = (c & j) ? 0xFE : 0xFF;
        }
        while(*ptr && *ptr!='\n' && *ptr!='\r') ptr++;
        while(*ptr && (*ptr=='\n' || *ptr=='\r')) ptr++;
        w = i > 128 ? 16 : 8;
        if(sfn_charadd(unicode, w, 16, 0, 0, 0))
            sfn_layeradd(unicode, SSFN_FRAG_BITMAP, 0, 0, w, 16, 0xFE, bitmap);
        if(pbar) (*pbar)(0, 0, ++nc, numchars, PBAR_BITMAP);
    }
}

/**
 * Parse X11 BDF font format and FontForge's SplineFontDB with bitmaps (text)
 */
void bdf(char *ptr, int size)
{
    uint32_t c;
    int w = 0, h = 0, l = 0, t = 0, i, j, a, b = 0, unicode = 0, nc = 0, numchars = 0, defchar = 0, ps = 0, mx, xx, my, xy, k;
    char *end = ptr + size, *face, *name = NULL, *style = NULL, *manu = NULL;
    unsigned char *bitmap = NULL, sfd = 0, dec[4];

    for(face = ptr; face + 12 < end && *face; face++) {
        if(!memcmp(face, "ENCODING ", 9)) numchars++;
        if(!memcmp(face, "BDFChar:", 8)) { numchars++; sfd = 1; }
        if(!memcmp(face, "BitmapFont: ", 12)) {
            ptr += 12; while(*ptr == ' ') ptr++;
            ps = atoi(ptr); while(*ptr && *ptr != ' ') { ptr++; } while(*ptr == ' ') ptr++;
            while(*ptr && *ptr != ' ') { ptr++; } while(*ptr == ' ') ptr++;
            b = atoi(ptr); if(!ctx.baseline) ctx.baseline = b;
            sfd = 1;
        }
    }
    face = NULL;

    while(ptr < end && *ptr) {
        if(!memcmp(ptr, "FACE_NAME ", 10) && !face) {
            ptr += 10; while(*ptr==' ') { ptr++; } if(*ptr=='\"') { ptr++; } face = ptr;
        }
        if(!memcmp(ptr, "FONT_NAME ", 10) && !face) {
            ptr += 10; while(*ptr==' ') { ptr++; } if(*ptr=='\"') { ptr++; } face = ptr;
        }
        if(!memcmp(ptr, "FONT_VERSION ", 13) && !ctx.revision) {
            ptr += 13; while(*ptr==' ') { ptr++; } if(*ptr=='\"') ptr++;
            sfn_setstr(&ctx.revision, ptr, 0);
        }
        if(!memcmp(ptr, "ADD_STYLE_NAME ", 15) && !ctx.name && !style) {
            ptr += 15; while(*ptr==' ') { ptr++; } if(*ptr=='\"') ptr++;
            style = ptr;
        }
        if(!memcmp(ptr, "FOUNDRY ", 8) && !ctx.manufacturer) {
            ptr += 8; while(*ptr==' ') { ptr++; } if(*ptr=='\"') ptr++;
            manu = ptr;
        }
        if(!memcmp(ptr, "HOMEPAGE ", 9) && !ctx.manufacturer && !manu) {
            ptr += 9; while(*ptr==' ') { ptr++; } if(*ptr=='\"') ptr++;
            manu = ptr;
        }
        if(!memcmp(ptr, "FAMILY_NAME ", 12) && !ctx.familyname) {
            ptr += 12; while(*ptr==' ') { ptr++; } if(*ptr=='\"') ptr++;
            sfn_setstr(&ctx.familyname, ptr, 0);
        }
        if(!memcmp(ptr, "WEIGHT_NAME ", 12) && !ctx.subname) {
            ptr += 12; while(*ptr==' ') { ptr++; } if(*ptr=='\"') ptr++;
            sfn_setstr(&ctx.subname, ptr, 0);
        }
        if(!memcmp(ptr, "COPYRIGHT ", 10) && !ctx.license) {
            ptr += 10; while(*ptr==' ') { ptr++; } if(*ptr=='\"') ptr++;
            sfn_setstr(&ctx.license, ptr, 0);
        }
        if(!memcmp(ptr, "FONT_ASCENT ", 12)) {
            ptr += 12; if(sfd) { while(*ptr && *ptr != ' ') ptr++; } while(*ptr == ' ') ptr++;
            b = atoi(ptr); if(!ctx.baseline) ctx.baseline = b; }
        if(!memcmp(ptr, "UNDERLINE_POSITION ", 19)) {
            ptr += 19; if(sfd) { while(*ptr && *ptr != ' ') ptr++; } while(*ptr == ' ') ptr++;
            relul = atoi(ptr); if(relul < 0) relul = -relul; }
        if(!memcmp(ptr, "DEFAULT_CHAR ", 13)) {
            ptr += 13; if(sfd) { while(*ptr && *ptr != ' ') ptr++; } while(*ptr == ' ') ptr++;
            defchar = atoi(ptr); }
        if(!memcmp(ptr, "PIXEL_SIZE ", 11) && !ps) {
            ptr += 11; if(sfd) { while(*ptr && *ptr != ' ') ptr++; } while(*ptr == ' ') ptr++;
            ps = atoi(ptr); }
        if(!memcmp(ptr, "ENDPROPERTIES", 13) || !memcmp(ptr, "BDFEndProperties", 16)) break;
        while(*ptr && *ptr!='\n') ptr++;
        while(*ptr=='\r' || *ptr=='\n') ptr++;
    }
    if(!ctx.name) {
        if(!face) face = ctx.familyname;
        if(face && style && style[0]) {
            for(i = 0; face[i] && face[i] != '\"' && face[i] != '\r' && face[i] != '\n'; i++);
            for(j = 0; style[j] && style[i] != '\"' && style[i] != '\r' && style[i] != '\n'; j++);
            name = malloc(i + j + 2);
            if(!name) { fprintf(stderr,"libsfn: memory allocation error\n"); return; }
            memcpy(name, face, i);
            name[i] = ' ';
            memcpy(name + i + 1, style, j);
            name[i + j + 1] = 0;
            sfn_setstr(&ctx.name, name, 0);
            free(name);
        } else
            sfn_setstr(&ctx.name, ctx.familyname, 0);
    }
    if(!ctx.manufacturer && manu)
        sfn_setstr(&ctx.manufacturer, manu, 0);
    printf("\r  Name '%s' num_glyphs: %d, ascender: %d, underline: %d, height: %d\n", ctx.name, numchars, b, b + relul, ps);

    while(ptr < end && *ptr) {
        if(!sfd) {
            if(!memcmp(ptr, "ENCODING ", 9)) { ptr += 9; unicode = atoi(ptr); }
            if(!memcmp(ptr, "BBX ", 4)) {
                ptr += 4; w = atoi(ptr);
                while(*ptr && *ptr!=' ') ptr++;
                ptr++; h = atoi(ptr);
                while(*ptr && *ptr!=' ') ptr++;
                ptr++; l = atoi(ptr);
                while(*ptr && *ptr!=' ') ptr++;
                ptr++; t = atoi(ptr);
            }
            if(!memcmp(ptr, "BITMAP", 6)) {
                ptr += 6; while(*ptr && *ptr!='\n') ptr++;
                ptr++;
                if(skipcode && uniname(unicode) == UNICODE_NUMNAMES && !memcmp(ptr,"0000\n7FFE", 9) &&
                    !memcmp(ptr + 35,"7FFE\n7FFE", 9)) ptr += 16*5;
                else if(w > 0 && h > 0 && w * h <= 65536) {
                    bitmap = realloc(bitmap, ((w + 7) & ~7) * (h + 1));
                    if(!bitmap) { fprintf(stderr,"libsfn: memory allocation error\n"); }
                    else {
                        for(i = 0;i < w * h && *ptr; ptr += 2) {
                            while(*ptr=='\n' || *ptr=='\r') ptr++;
                            c = gethex(ptr, 2);
                            for(j=0x80,k=0;j && k < w && i < w * h;k++,j>>=1) bitmap[i++] = c & j ? 0xFE : 0xFF;
                        }
                        while(i < ((w + 7) & ~7) * (h + 1)) bitmap[i++] = 0xFF;
                        if(!skipcode && unicode == defchar) { sfn_chardel(0); unicode = 0; }
                        if(sfn_charadd(unicode, w, h, 0, 0, 0))
                            sfn_layeradd(unicode, SSFN_FRAG_BITMAP, l, ctx.baseline - t - h, w, h, 0xFE, bitmap);
                    }
                }
                if(pbar) (*pbar)(0, 0, ++nc, numchars, PBAR_BITMAP);
                w = h = l = t = 0;
            }
        } else
        if(!memcmp(ptr, "BDFChar:", 8)) {
            ptr += 8; while(*ptr == ' ') ptr++;
            while(*ptr && *ptr != ' ') { ptr++; } while(*ptr == ' ') ptr++;
            unicode = atoi(ptr); while(*ptr && *ptr != ' ') { ptr++; } while(*ptr == ' ') ptr++;
            w = atoi(ptr); while(*ptr && *ptr != ' ') { ptr++; } while(*ptr == ' ') ptr++;
            mx = atoi(ptr); while(*ptr && *ptr != ' ') { ptr++; } while(*ptr == ' ') ptr++;
            xx = atoi(ptr); while(*ptr && *ptr != ' ') { ptr++; } while(*ptr == ' ') ptr++;
            my = atoi(ptr); while(*ptr && *ptr != ' ') { ptr++; } while(*ptr == ' ') ptr++;
            xy = atoi(ptr); while(*ptr && *ptr != ' ' && *ptr != '\n') { ptr++; } while(*ptr == ' ') ptr++;
            if(*ptr != '\n') a = atoi(ptr); else a = w;
            while(*ptr && *ptr != '\n') { ptr++; } while(*ptr == '\r' || *ptr == '\n') ptr++;
            h = ps; xx -= mx - 1; xy -= my - 1;
            xx = (xx + 7) & ~7;
            if(xx * xy < 65536) {
                bitmap = realloc(bitmap, xx * xy);
                if(!bitmap) { fprintf(stderr,"libsfn: memory allocation error\n"); return; }
                for(i = 0, k = 4; i < xx * xy;) {
                    if(k > 3) {
                        if(!*ptr || *ptr == '\r' || *ptr == '\n') break;
                        k = 0;
                        if(*ptr == 'z') { dec[0] = dec[1] = dec[2] = dec[3] = 0; ptr++; }
                        else {
                            c = ((((ptr[0]-'!')*85 + ptr[1]-'!')*85 + ptr[2]-'!')*85 + ptr[3]-'!')*85 + ptr[4]-'!';
                            dec[0] = (c >> 24) & 0xFF; dec[1] = (c >> 16) & 0xFF; dec[2] = (c >> 8) & 0xFF; dec[3] = c & 0xFF;
                            ptr += 5;
                        }
                    }
                    c = dec[k++];
                    for(j = 0x80; j; j >>= 1) bitmap[i++] = c & j ? 0xFE : 0xFF;
                }
                while(i < xx * xy) bitmap[i++] = 0xFF;
                if(!skipcode && unicode == defchar) { sfn_chardel(0); unicode = 0; }
                if(sfn_charadd(unicode, w, h, a, 0, mx < 0 ? -mx : 0))
                    sfn_layeradd(unicode, SSFN_FRAG_BITMAP, 0, 0, xx, xy, 0xFE, bitmap);
            }
            if(pbar) (*pbar)(0, 0, ++nc, numchars, PBAR_BITMAP);
        }
        while(*ptr && *ptr!='\n') ptr++;
        while(*ptr=='\r' || *ptr=='\n') ptr++;
    }
    if(bitmap) free(bitmap);
}

/**
 * Parse X11 PCF font format (binary)
 */
void pcf(unsigned char *ptr, int size)
{
    uint32_t i, j, n, *iptr=(uint32_t*)ptr, fmt, mf=0, bf=0, bs=0, ef=0, offs, mn=0, mx=0, mg=0, siz;
    uint32_t boffs = 0, bitmaps = 0, metrics = 0, encodings = 0;
    unsigned char *bitmap = NULL, *bm;
    char *face = NULL, *name = NULL, *style = NULL, *manu = NULL;
    char *str, *s, *v;
    int x, y, o, a, b = 0, k, w, h = 0, p = 1, r, sx = 1, m, defchar = 0;
#define pcf32(f,o) (f&(1<<2)? (ptr[o+0]<<24)|(ptr[o+1]<<16)|(ptr[o+2]<<8)|ptr[o+3] : \
    (ptr[o+3]<<24)|(ptr[o+2]<<16)|(ptr[o+1]<<8)|ptr[o+0])
#define pcf16(f,o) (f&(1<<2)? (ptr[o+0]<<8)|ptr[o+1] : (ptr[o+1]<<8)|ptr[o+0])

    /* parse tables */
    for(i = 0; i < iptr[1]; i++) {
        fmt = iptr[i*4+3]; offs = iptr[i*4+5];
        if(offs + iptr[i*4+4] >= (uint32_t)size) continue;
        switch(iptr[i*4+2]) {
            case (1<<0): /* PCF_PROPERTIES */
                n = pcf32(fmt,offs+4); str = (char*)ptr + offs + ((n * 9 + 3)/4 + 3)*4;
                for(j = 0; j < n; j++) {
                    s = str + pcf32(fmt,offs + 8 + j * 9); v = str + pcf32(fmt,offs + 13 + j * 9);
                    if(!strcmp(s, "FACE_NAME") && !face) face = v; else
                    if(!strcmp(s, "FONT_NAME") && !face) face = v; else
                    if(!strcmp(s, "FONT_VERSION") && !ctx.revision) sfn_setstr(&ctx.revision, v, 0); else
                    if(!strcmp(s, "ADD_STYLE_NAME") && !ctx.name && !style) style = v; else
                    if(!strcmp(s, "FOUNDRY") && !ctx.manufacturer) manu = v; else
                    if(!strcmp(s, "HOMEPAGE") && !ctx.manufacturer && !manu) manu = v; else
                    if(!strcmp(s, "FAMILY_NAME") && !ctx.familyname) sfn_setstr(&ctx.subname, v, 0); else
                    if(!strcmp(s, "WEIGHT_NAME") && !ctx.subname) sfn_setstr(&ctx.subname, v, 0); else
                    if(!strcmp(s, "COPYRIGHT") && !ctx.license) sfn_setstr(&ctx.license, v, 0); else
                    if(!strcmp(s, "PIXEL_SIZE")) { k = pcf32(fmt,offs + 13 + j * 9); if(k > h) h = k; } else
                    if(!strcmp(s, "UNDERLINE_POSITION")) {
                        relul = (int)pcf32(fmt,offs + 13 + j * 9); if(relul < 0) relul = -relul;
                    }
                }
            break;
            case (1<<2): /* PCF_METRICS */
                metrics = offs; mf = fmt; b = 0;
                if(fmt & 0x100) {
                    n = pcf16(fmt,offs + 4);
                    for(j=0; j<n; j++) { k = (int)ptr[offs+3+6+j*5]-0x80; if(k > b) b = k; }
                    for(j=0; j<n; j++) { k = (int)ptr[offs+4+6+j*5]-0x80 + b; if(k > h) h = k; }
                } else {
                    n = pcf32(fmt,offs + 4);
                    for(j = 0; j < n; j++) { k = pcf16(fmt,offs+6+8+j*12); if(k > b) b = k; }
                    for(j = 0; j < n; j++) { k = pcf16(fmt,offs+8+8+j*12) + b; if(k > h) h = k; }
                }
                if(!mn && !mx && n) mx = n - 1;
                if(!mg || n < mg) mg = n;
            break;
            case (1<<3): /* PCF_BITMAPS */
                boffs = offs + 8; bf = fmt; n = pcf32(fmt,offs+4);
                bitmaps = boffs + n * 4 + 16; bs = iptr[i*4+4] + offs - bitmaps;
                p = 1 << (fmt & 3); sx = fmt & (1 << 2) ? 1 : -1;
                if(!mg || n < mg) mg = n;
            break;
            case (1<<5): /* PCF_BDF_ENCODINGS */
                encodings = offs + 14; ef = fmt;
                mn = (pcf16(fmt, offs + 8)<<8) | pcf16(fmt, offs + 4);
                mx = (pcf16(fmt, offs + 10)<<8) | pcf16(fmt, offs + 6);
                defchar = pcf16(fmt, offs + 12);
            break;
        }
    }
    if(!ctx.name) {
        if(!face) face = ctx.familyname;
        if(face && style && style[0]) {
            i = strlen(face); j = strlen(style);
            name = malloc(i + j + 2);
            if(!name) { fprintf(stderr,"libsfn: memory allocation error\n"); return; }
            memcpy(name, face, i);
            name[i] = ' ';
            memcpy(name + i + 1, style, j);
            name[i + j + 1] = 0;
            sfn_setstr(&ctx.name, name, 0);
            free(name);
        } else
            sfn_setstr(&ctx.name, ctx.familyname, 0);
    }
    if(!ctx.manufacturer && manu)
        sfn_setstr(&ctx.manufacturer, manu, 0);
    printf("\r  Name '%s' num_glyphs: %d, ascender: %d, underline: %d, height: %d\n", face, mg, b, b + relul, h);
    if(!b || !h || !mg || !mx || !bitmaps) return;
    if(mg > 65534) mg = 65534;
    ctx.baseline = b;
    ctx.underline = b + relul;
    ctx.height = h;

    /* parse bitmaps and add glyphs. Encoding table must be handled as optional */
    for(unicode = mn; (uint32_t)unicode <= mx; unicode++) {
        if(pbar) (*pbar)(0, 0, unicode, mx - mn + 1, PBAR_BITMAP);
        i = encodings ? pcf16(ef, encodings + unicode * 2) : unicode;
        if(i >= mg) continue;
        offs = pcf32(bf, boffs + i * 4); siz = (i >= mg - 1 ? bs : (uint32_t)pcf32(bf, boffs + i * 4 + 4)) - offs;
        if(mf & 0x100) {
            x = (int)ptr[metrics+6+i*5]-0x80; a = (int)ptr[metrics+3+6+i*5]-0x80;
            w = (int)ptr[metrics+2+6+i*5]-0x80; r = (int)ptr[metrics+1+6+i*5]-0x80;
        } else {
            x = (int16_t)pcf16(mf,metrics+8+i*12); a = pcf16(mf,metrics+6+8+i*12);
            w = pcf16(mf,metrics+4+8+i*12); r = pcf16(mf,metrics+2+8+i*12);
        }
        /* do some heuristics and validation because PCF fonts are *usually* buggy... */
        if(x < 0) { o = -x; x = 0; } else o = 0;
        if(w < r) r = w;
        if(p < 1) p = 1;
        n = (siz / p); y = b - a;
        if(n > (uint32_t)h) n = h;
        if(y < 0) y = 0;
        if(y + n > (uint32_t)h) y = h - n;
        k = n * r; if(k < 1 || k > 65536) continue;
        bitmap = realloc(bitmap, k);
        if(!bitmap) { fprintf(stderr,"libsfn: memory allocation error\n"); return; }
        memset(bitmap, 0xFF, k);
        if(siz > n * p) siz = n * p;
        for(j = 0; siz; siz -= p, offs += p) {
            /* seriously, who have thought even for a moment that messing with both byte and bit endianess is sane? */
            bm = ptr + bitmaps + offs + (sx < 0 ? p - 1 : 0); m = bf & (1 << 3) ? 1 << 7 : 1;
            for(k = 0; k < r; k++) {
                bitmap[j++] = bm[0] & m ? 0xFE : 0xFF;
                if(bf & (1 << 3)) { m >>= 1; if(!m) { m = 1 << 7; bm += sx; } }
                else { m <<= 1; if(m > 0x80) { m = 1; bm += sx; } }
            }
        }
        if(unicode == defchar) {
            sfn_chardel(0);
            j = 0;
        } else j = unicode;
        if(sfn_charadd(j, w, h, x+r, 0, o))
            sfn_layeradd(j, SSFN_FRAG_BITMAP, x, y, r, n, 0xFE, bitmap);
    }
    if(bitmap) free(bitmap);
}

/**
 * Parse BMF font format (binary)
 */
void bmf(unsigned char *ptr, int size)
{
    bmf_t *bmf = (bmf_t*)ptr;
    uint8_t *end = ptr + size, pal[256];
    int i, n, unicode, numchars, w, h, a, x, y;

    if(!ctx.baseline) ctx.baseline = -bmf->sizeOver;
    if(ctx.height < bmf->lineHeight) ctx.height = bmf->lineHeight;

    memset(pal, 0, sizeof(pal)); pal[0] = 0xFF;
    ptr += sizeof(bmf_t);
    for(i = 0; i < (int)bmf->numColors; i++, ptr += 3)
        pal[i + 1] = sfn_cpaladd(ptr[0] << 2, ptr[1] << 2, ptr[2] << 2, 255);

    i = *ptr++; sfn_setstr(&ctx.name, (char*)ptr, i); ptr += i;

    numchars = (ptr[1] << 8) | ptr[0]; ptr += 2;
    printf("\r  Name '%s' num_glyphs: %d, ascender: %d, underline: %d, height: %d\n", ctx.name, numchars,
        -bmf->sizeOver, -bmf->sizeOver, bmf->lineHeight);
    for(n = 0; n < numchars && ptr + 6 < end; n++) {
        if(pbar) (*pbar)(0, 0, n, numchars, PBAR_BITMAP);
        unicode = doscp[*ptr++]; w = *ptr++; h = *ptr++; x = (int8_t)*ptr++; y = (int8_t)*ptr++; a = bmf->addSpace + (int8_t)*ptr++;
        if(sfn_charadd(unicode, (x < 0 ? 0 : x) + w, (y < 0 ? 0 : y) + h, a, 0, (x < 0 ? -x : 0))) {
            for(i = 0; i < w * h && ptr + i < end; i++) ptr[i] = pal[ptr[i]];
            sfn_layeradd(unicode, SSFN_FRAG_PIXMAP, (x < 0 ? 0 : x), (y < 0 ? 0 : y), w, h, 0xFE, ptr);
        }
        ptr += w * h;
    }
}

/**
 * Parse YAFF font format (text)
 */
void yaff(char *ptr, int size)
{
    int i, w = 0, h = 0, x = 0, y = 0, o = 0, unicode = -1, c = -1, last = -1, u, b = 0, relul = 0, W = 0, H = 0, numchars = 0;
    char *end = ptr + size, *s = ptr, *e, *n, *v, hack = 0;
    unsigned char bitmap[65536];

    /* this is REALLY bad format which cannot be parsed reliably line by line... expect parsing errors! */
    while(s < end) {
        while(s < end && *s && (*s == '\r' || *s == '\n')) s++;
        if(!*s) break;
        for(e = s; e < end && *e && *e != '\r' && *e != '\n'; e++);
        for(v = s; v < e && *v != ':'; v++);
        if(v < e && *v == ':') {
            for(v++; v < e && *v && (*v == ' ' || *v == '\t'); v++);
            if(v >= e || *v == '\r' || *v == '\n') v = NULL;
        } else v = NULL;
        /* s: first character in line, e: end of line, v: first character of value */
        if(*s == ' ' || *s == '\t') {
            while(s < end && *s && (*s == ' ' || *s == '\t')) s++;
            if(!*s) break;
            if(unicode == -1 && c != -1) unicode = c;
            if(unicode == -1) { s = e; continue; }
            if(last != unicode) {
                /* add character */
                if(w > 0 && h > 0 && last != -1) {
                    if(o < 0) w += o;
                    if(w > 0) {
                        for(i = 0; i < h; i++)
                            memcpy(bitmap + i * w, bitmap + i * 256 - (o < 0 ? o : 0), w);
                        if(sfn_charadd(last, w, h, x && !y ? x : w, !x && y ? y : 0, o < 0 ? 0 : o))
                            sfn_layeradd(last, SSFN_FRAG_BITMAP, 0, 0, w, h, 0xFE, bitmap);
                    }
                    numchars++;
                }
                w = h = o = x = y = 0; memset(bitmap, 0, sizeof(bitmap));
                last = unicode; hack = 0;
            }
            if(*s == '.' || *s == '@') {
                /* add a row */
                for(i = 0; i < 255 && s < e && (*s == '.' || *s == '@'); s++, i++)
                    bitmap[h * 256 + i] = *s == '@' ? 0xFE : 0xFF;
                h++;
                if(i > w) w = i;
                if(w > W) W = w;
                if(h > H) H = h;
                /* ugly hack */
                hack = 0;
            } else
            if(!memcmp(s, "advance-width:", 14) && v) x = atoi(v); else
            if(!memcmp(s, "advance-height:", 15) && v) y = atoi(v); else
            if(!memcmp(s, "left-bearing:", 13) && v) { o = atoi(v); }
        } else {
            if(*s == '#' || *s == '\'' || *s == '\"') { s = e; continue; }
            /* codepoint */
            n = s; u = (int)ssfn_utf8(&n);
            if(*s >= '0' && *s <= '9') {
                if(s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) c = gethex(s + 2, 6);
                else c = atoi(s);
                if(!hack) unicode = c;
            } else
            if((s[0] == 'u' || s[0] == 'U') && s[1] == '+') { unicode = gethex(s + 2, 6); c = -1; hack = 1; } else
            if(*n == ':') { unicode = u; c = -1; } else
            if(!memcmp(s, "weight:", 7) && v && (*v == 'b' || *v == 'B')) ctx.style |= SSFN_STYLE_BOLD; else
            if(!memcmp(s, "slant:", 7) && v && (*v == 'i' || *v == 'I' || *v == 'o' || *v == 'O')) ctx.style |= SSFN_STYLE_ITALIC; else
            if(!memcmp(s, "name:", 5) && !ctx.name && v) sfn_setstr(&ctx.name, v, e - v); else
            if(!memcmp(s, "family:", 7) && !ctx.familyname && v) sfn_setstr(&ctx.familyname, v, e - v); else
            if(!memcmp(s, "subfamily:", 10) && !ctx.subname && v) sfn_setstr(&ctx.subname, v, e - v); else
            if(!memcmp(s, "revision:", 9) && !ctx.revision && v) sfn_setstr(&ctx.revision, v, e - v); else
            if(!memcmp(s, "foundry:", 8) && !ctx.manufacturer && v) sfn_setstr(&ctx.manufacturer, v, e - v); else
            if(!memcmp(s, "copyright:", 10) && !ctx.license && v) sfn_setstr(&ctx.license, v, e - v); else
            if(!memcmp(s, "line-height:", 12) && v) H = atoi(v); else
            if(!memcmp(s, "ascent:", 7) && !b && v) b = atoi(v); else
            if(!memcmp(s, "baseline:", 9) && v) b = atoi(v); else
            if(!memcmp(s, "shift-up:", 9) && b && v) { b -= atoi(v); if(b < 0) b = 0; } else
            if(!memcmp(s, "underline-descent:", 18) && v) relul = atoi(v);
        }
        s = e;
    }
    /* add the last character too */
    if(unicode == -1 && c != -1) unicode = c;
    if(w > 0 && h > 0 && unicode != -1) {
        if(o < 0) w += o;
        if(w > 0) {
            for(i = 0; i < h; i++)
                memcpy(bitmap + i * w, bitmap + i * 256 - (o < 0 ? o : 0), w);
            if(sfn_charadd(unicode, w, h, x && !y ? x : w, !x && y ? y : 0, o < 0 ? 0 : o))
                sfn_layeradd(unicode, SSFN_FRAG_BITMAP, 0, 0, w, h, 0xFE, bitmap);
        }
        numchars++;
    }
    printf("\r  Name '%s' num_glyphs: %d, ascender: %d, underline: %d, height: %d\n", ctx.name, numchars, b, b + relul, H);
    if(b > ctx.baseline) ctx.baseline = b;
    ctx.underline = ctx.baseline + relul;
    if(ctx.underline < ctx.baseline) ctx.underline = ctx.baseline;
    if(W > ctx.width) ctx.width = W;
    if(H > ctx.height) ctx.height = H;
    ctx.family = SSFN_FAMILY_MONOSPACE;
}

/**
 * Parse name chunk
 */
void kbits_name(char **strings)
{
    /* unique font name */
    if(!ctx.name) {
        if(strings[4]) sfn_setstr(&ctx.name, strings[4], 0); else
        if(strings[3]) sfn_setstr(&ctx.name, strings[3], 0); else
        if(strings[6]) sfn_setstr(&ctx.name, strings[6], 0); else
        if(strings[20]) sfn_setstr(&ctx.name, strings[20], 0); else
        if(strings[18]) sfn_setstr(&ctx.name, strings[18], 0); else
        /* fallback */
        if(strings[16]) sfn_setstr(&ctx.name, strings[16], 0);
    }

    /* family name */
    if(!ctx.familyname) {
        if(strings[1]) sfn_setstr(&ctx.familyname, strings[1], 0); else
        if(strings[16]) sfn_setstr(&ctx.familyname, strings[16], 0);
    }

    /* subfamily name */
    if(!ctx.subname) {
        if(strings[2]) sfn_setstr(&ctx.subname, strings[2], 0); else
        if(strings[17]) sfn_setstr(&ctx.subname, strings[17], 0);
    }

    /* version / revision */
    if(!ctx.revision && strings[5]) sfn_setstr(&ctx.revision, strings[5], 0);

    /* manufacturer */
    if(!ctx.manufacturer) {
        if(strings[8]) sfn_setstr(&ctx.manufacturer, strings[8], 0); else
        if(strings[9]) sfn_setstr(&ctx.manufacturer, strings[9], 0);
    }

    /* copyright */
    if(!ctx.license) {
        if(strings[13]) sfn_setstr(&ctx.license, strings[13], 0); else
        if(strings[0]) sfn_setstr(&ctx.license, strings[0], 0); else
        if(strings[7]) sfn_setstr(&ctx.license, strings[7], 0);
    }
}

/**
 * Parse Bits'N'Picas font format (binary)
 */
void kbits(unsigned char *ptr, int size)
{
    uint8_t *end = ptr + size, *s, *save;
    unsigned char *bitmap = NULL;
    char *strings[32] = { 0 };
    int i, j, k, numchars = 0, a, x, y, w, h;

    ctx.height = ptr[0x17];   /* lineAscent */
    ctx.baseline = ctx.underline = 0;
    ptr += 0x24;
    while(ptr < end && (!memcmp(ptr, "name", 4) || !memcmp(ptr, "\0ame", 4))) {
        if(ptr[11] < 32) strings[(int)ptr[11]] = (char*)ptr + 14;
        ptr += 14 + ((ptr[12] << 8) | ptr[13]); *ptr = 0;
    }
    kbits_name(strings);

    save = ptr;
    while(ptr < end && (!memcmp(ptr, "char", 4) || !memcmp(ptr, "\0har", 4))) {
        if(ctx.baseline < ptr[0x17]) ctx.baseline = ptr[0x17];
        if(ctx.height < ptr[0x1b]) ctx.height = ptr[0x1b];
        for(i = ptr[0x1b], ptr += 0x1c; i > 0; i--, ptr += 4 + ptr[3]);
        numchars++;
    }
    if(ctx.underline < ctx.baseline) ctx.underline = ctx.baseline;
    ptr = save;
    while(ptr < end && (!memcmp(ptr, "char", 4) || !memcmp(ptr, "\0har", 4))) {
        s = ptr;
        x = (char)ptr[0x13]; y = ctx.baseline - (char)ptr[0x17]; h = ptr[0x1b];
        if(y < 0 || y > ctx.height || h < 1) y = 0;
        if(x < 0 || x >= 255) x = 0;
        for(w = 0, i = ptr[0x1b], ptr += 0x1c; i > 0; i--, ptr += 4 + ptr[3]) if(ptr[3] > w) w = ptr[3];
        if(x + w > ctx.width) ctx.width = x + w;
        if(y + h > ctx.height) ctx.height = y + h;
    }
    printf("\r  Name '%s' num_glyphs: %d, ascender: %d, underline: %d, height: %d\n", ctx.name, numchars, ctx.baseline,
        ctx.underline, ctx.height);

    ptr = save;
    while(ptr < end && (!memcmp(ptr, "char", 4) || !memcmp(ptr, "\0har", 4))) {
        s = ptr;
        a = ptr[0xf]; x = (char)ptr[0x13]; y = ctx.baseline - ptr[0x17]; h = ptr[0x1b];
        if(y < 0 || y > ctx.height || h < 1) y = 0;
        unicode = (ptr[8] << 24) | (ptr[9] << 16) | (ptr[10] << 8) | ptr[11];
        for(w = 0, i = s[0x1b], s += 0x1c; i > 0; i--, s += 4 + s[3]) if(s[3] > w) w = s[3];
        bitmap = realloc(bitmap, w * h + 1);
        if(!bitmap) { fprintf(stderr,"libsfn: memory allocation error\n"); return; }
        memset(bitmap, 0xFF, w * h + 1);
        for(i = h, ptr += 0x1c, k = 0; i > 0; i--, ptr += 4 + ptr[3], k += w)
            for(j = 0; j < ptr[3]; j++) bitmap[k + j] = ptr[4 + j] > 127 ? 0xFE : 0xFF;
        if(y + h > ctx.height) h = ctx.height - y;
        if(sfn_charadd(unicode, (x < 0 ? 0 : x) + w, y + h, a, 0, (x < 0 ? -x : 0)))
            sfn_layeradd(unicode, SSFN_FRAG_BITMAP, (x < 0 ? 0 : x), y, w, h, 0xFE, bitmap);
    }
    if(bitmap) free(bitmap);
}

/**
 * Parse Bits'N'Picas font format (text)
 */
void kbitx(char *ptr, int size)
{
    unsigned char *bitmap = NULL, *buf = NULL, *buf2;
    char *strings[32] = { 0 }, *s, *e, *save, *end = ptr + size;
    int i, j, l, numchars = 0, a, x, y, w, h;
    int repeatCount = 0, repeatData = -1, data;

    while(ptr < end && *ptr && memcmp(ptr, "<g ", 3) && memcmp(ptr, "</kbits", 7)) {
        if(!memcmp(ptr, "<name ", 6)) {
            for(i = -1, e = NULL, s = ptr; *s && *s != '\n' && *s != '>'; s++) {
                if(!memcmp(s, "id=\"", 4)) { s += 4; i = atoi(s); while(*s && *s != '>' && *s != '\n' && *s != '\"') s++; } else
                if(!memcmp(s, "value=\"", 7)) { s += 7; e = s; while(*s && *s != '\n' && *s != '\"') s++; }
            }
            if(i >= 0 && i < 32 && e) strings[i] = e;
        }
        while(*ptr && *ptr!='\n') ptr++;
        while(*ptr=='\r' || *ptr=='\n' || *ptr == ' ') ptr++;
    }
    ctx.height = ctx.baseline = ctx.underline = 0;
    kbits_name(strings);

    save = ptr;
    while(ptr < end && *ptr && !memcmp(ptr, "<g ", 3)) {
        y = 0;
        for(e = NULL, s = ptr; *s && *s != '\n' && *s != '>'; s++) {
            if(!memcmp(s, "y=\"", 3)) { s += 3; y = atoi(s); while(*s && *s != '>' && *s != '\n' && *s != '\"') s++; }
        }
        if(y > ctx.baseline) ctx.baseline = y;
        while(*ptr && *ptr!='\n') ptr++;
        while(*ptr=='\r' || *ptr=='\n' || *ptr == ' ') ptr++;
        numchars++;
    }
    if(ctx.underline < ctx.baseline) ctx.underline = ctx.baseline;
    ptr = save;
    while(ptr < end && *ptr && !memcmp(ptr, "<g ", 3)) {
        x = y = w = h = 0;
        for(e = NULL, s = ptr; *s && *s != '\n' && *s != '>'; s++) {
            if(!memcmp(s, "x=\"", 3)) { s += 3; x = atoi(s); while(*s && *s != '>' && *s != '\n' && *s != '\"') s++; } else
            if(!memcmp(s, "y=\"", 3)) { s += 3; y = ctx.baseline - atoi(s); while(*s && *s != '>' && *s != '\n' && *s != '\"') s++; } else
            if(!memcmp(s, "d=\"", 3)) { s += 3; e = s; while(*s && *s != '>' && *s != '\n' && *s != '\"') s++; }
        }
        if(y < 0 || y > ctx.height || h < 1) y = 0;
        if(x < 0 || x >= 255) x = 0;
        while(*ptr && *ptr!='\n') ptr++;
        while(*ptr=='\r' || *ptr=='\n' || *ptr == ' ') ptr++;
        if(e) {
            buf2 = buf = realloc(buf, ptr - e + 256);
            if(!buf2) { fprintf(stderr,"libsfn: memory allocation error\n"); return; }
            memset(buf, 0, ptr - e + 256);
            if(base64_decode(e, buf) > 2) {
                buf2 = getleb(buf2, (unsigned int*)&h);
                buf2 = getleb(buf2, (unsigned int*)&w);
            };
        };
        if(x + w > ctx.width) ctx.width = x + w;
        if(y + h > ctx.height) ctx.height = y + h;
    }
    printf("\r  Name '%s' num_glyphs: %d, ascender: %d, underline: %d, height: %d\n", ctx.name, numchars, ctx.baseline,
        ctx.underline, ctx.height);

    ptr = save;
    while(ptr < end && *ptr && !memcmp(ptr, "<g ", 3)) {
        a = x = y = w = h = 0;
        for(e = NULL, s = ptr; *s && *s != '\n' && *s != '>'; s++) {
            if(!memcmp(s, "u=\"", 3)) { s += 3; unicode = (uint32_t)atoi(s); while(*s && *s != '>' && *s != '\n' && *s != '\"') s++; } else
            if(!memcmp(s, "x=\"", 3)) { s += 3; x = atoi(s); while(*s && *s != '>' && *s != '\n' && *s != '\"') s++; } else
            if(!memcmp(s, "y=\"", 3)) { s += 3; y = ctx.baseline - atoi(s); while(*s && *s != '>' && *s != '\n' && *s != '\"') s++; } else
            /* this isn't width, this is actually advance x */
            if(!memcmp(s, "w=\"", 3)) { s += 3; a = atoi(s); while(*s && *s != '>' && *s != '\n' && *s != '\"') s++; } else
            if(!memcmp(s, "d=\"", 3)) { s += 3; e = s; while(*s && *s != '>' && *s != '\n' && *s != '\"') s++; }
        }
        while(*ptr && *ptr!='\n') ptr++;
        while(*ptr=='\r' || *ptr=='\n' || *ptr == ' ') ptr++;
        if(e) {
            buf2 = buf = realloc(buf, ptr - e + 256);
            if(!buf2) { fprintf(stderr,"libsfn: memory allocation error\n"); return; }
            memset(buf, 0, ptr - e + 256);
            if((l = base64_decode(e, buf)) > 2) {
                buf2 = getleb(buf2, (unsigned int*)&h);
                buf2 = getleb(buf2, (unsigned int*)&w);
                bitmap = realloc(bitmap, w * h + 1);
                if(!bitmap) { fprintf(stderr,"libsfn: memory allocation error\n"); return; }
                memset(bitmap, 0xFF, w * h + 1);
                for(j = repeatCount = 0, repeatData = -1; j < w * h && buf2 <= buf + l; ) {
                    if(repeatCount > 0) {
                        repeatCount--;
                        if(repeatData == -1) {
                            bitmap[j++] = buf2 == buf + l || *buf2++ > 127 ? 0xFE : 0xFF;
                            continue;
                        } else {
                            bitmap[j++] = repeatData > 127 ? 0xFE : 0xFF;
                            continue;
                        }
                    }
                    if(buf2 == buf + l) { bitmap[j++] = 0xFE; break; }
                    data = *buf2++;
                    repeatCount = data & 0x1F;
                    if((data & 0x20) != 0) repeatCount <<= 5;
                    switch(data & 0xC0) {
                        case 0x00: repeatData = 0x00; break;
                        case 0x40: repeatData = 0xFF; break;
                        case 0x80:
                            if(buf2 == buf + l) { bitmap[j++] = 0xFE; buf2++; break; }
                            repeatData = *buf2++;
                        break;
                        case 0xC0: repeatData = -1; break;
                    }
                }
            } else x = w = 0;
        } else x = w = 0;
        if(sfn_charadd(unicode, (x < 0 ? 0 : x) + w, y + h, a, 0, (x < 0 ? -x : 0)))
            sfn_layeradd(unicode, SSFN_FRAG_BITMAP, (x < 0 ? 0 : x), y, w, h, 0xFE, bitmap);
    }
    if(bitmap) free(bitmap);
    if(buf) free(buf);
}

/**
 * Raw ROM image (binary)
 */
void raw(unsigned char *ptr, int size, int h)
{
    unsigned char *bitmap;
    int w = (size / h / 256) << 3, unicode, i, j, k, l, p, m;

    if(size < 2048 || !w) return;

    bitmap = (unsigned char*)malloc(w * h);
    if(!bitmap) { fprintf(stderr,"libsfn: memory allocation error\n"); return; }

    printf("\r  Name '%s' num_glyphs: %d, ascender: %d, underline: %d, height: %d\n", "", 256, h, h, h);

    m = (w >> 3) * h;
    for(unicode = 0; unicode < 256; unicode++) {
        for(k = l = 0; k < h; k++)
            for(p = unicode * m + (w >> 3) * k, j = 0x80, i = 0; i < w; i++, j >>= 1) {
                if(!j) { j = 0x80; p++; }
                bitmap[l++] = (ptr[p] & j) ? 0xFE : 0xFF;
        }
        if(sfn_charadd(doscp[unicode], w, h, w + 1, 0, 0))
            sfn_layeradd(doscp[unicode], SSFN_FRAG_BITMAP, 0, 0, w, h, 0xFE, bitmap);
    }
    free(bitmap);
    ctx.width = w;
    ctx.height = h;
    if(!ctx.baseline) ctx.baseline = ctx.underline = h;
    ctx.family = SSFN_FAMILY_MONOSPACE;
}

/**
 * Parse a pixel map in memory
 */
void pixmap_parse(unsigned char *data, int w, int h)
{
    unsigned char *data2, l = 0xFE;
    unsigned int i, j, k, x, y, m, o, pw = 0, ph = 0, X, Y;
    int unicode, s;

    if(rs==0 && re==0x10FFFF) {
        /* try to detect square images with 16 x 16 glyphs. Use -X and -Y explicitly on command line to override */
        if(!px && !py && w >= 128 && h >= 128 && (w == h || w * 2 == h)) { pw = w / 16; ph = h / 16; re = 255; }
        if(px > 0 && py > 0) { pw = px; ph = py; }
        /* if we have exact glyph dimensions */
        if(pw >= 8 && ph >= 8) {
            data2 = (unsigned char*)malloc(pw*ph);
            if(!data2) { fprintf(stderr,"libsfn: memory allocation error\n"); return; }
            for(unicode = rs, Y = 0; Y < (unsigned int)h; Y += ph)
                for(X = 0; unicode <= re && X < (unsigned int)w; X += pw, unicode++) {
                    for(y=o=k=0,s=-1,l=0xFE;y<ph;y++) {
                        i = (Y+y)*w + X;
                        for(x=0;x<pw;x++) {
                            if(data[i] < 0xFF){
                                if(k < x) k = x;
                                if(s == -1) { l = data[i]; s = 1; }
                                if(s && data[i] != l) s = 0;
                            }
                            data2[o++] = data[i++];
                        }
                    }
                    if(s) for(i=0;i<pw*ph;i++)if(data2[i] != 0xFF) data2[i] = 0xFE;
                    if(sfn_charadd(unicode, pw, ph, k+1, 0, 0))
                        sfn_layeradd(unicode, s ? SSFN_FRAG_BITMAP : SSFN_FRAG_PIXMAP, 0, 0, pw, ph, l, data2);
                    if(pbar) (*pbar)(0, 0, unicode, re - rs, PBAR_SQRPIX);
                }
            free(data2);
            return;
        } else
            re = h > w ? h / w : w / h;
    }
    if(h > w) {
        m = h / (re - rs + 1);
        if(m < 8 || w < 8) { fprintf(stderr, "libsfn: unable to determine glyph size\n"); return; }
        for(unicode=rs, i=0; unicode<=re; unicode++, i += w*m) {
            for(y=k=0,s=-1,l=0xFE;y<m;y++)
                for(j=w-1;j>k;j--)
                    if(data[i+y*w+j] < 0xFF) k=j;
            if(sfn_charadd(unicode, w, m, k+1, 0, 0))
                sfn_layeradd(unicode, SSFN_FRAG_PIXMAP, 0, 0, w, m, 0xFE, data + i);
            if(pbar) (*pbar)(0, 0, unicode, re - rs, PBAR_TALLPIX);
        }
    } else {
        m = w / (re - rs + 1);
        if(m < 8 || h < 8) { fprintf(stderr, "libsfn: unable to determine glyph size\n"); return; }
        data2 = (unsigned char*)malloc(m*h);
        if(!data2) { fprintf(stderr,"libsfn: memory allocation error\n"); return; }
        for(unicode=rs; unicode<=re; unicode++) {
            for(y=o=k=0;y<(unsigned int)h;y++) {
                i = y*w + (unicode-rs)*m;
                for(x=0;x<m;x++) {
                    if(data[i] < 0xFF && k < x) k = x;
                    data2[o++] = data[i++];
                }
            }
            if(sfn_charadd(unicode, m, h, k+1, 0, 0))
                sfn_layeradd(unicode, SSFN_FRAG_PIXMAP, 0, 0, m, h, 0xFE, data2);
            if(pbar) (*pbar)(0, 0, unicode, re - rs, PBAR_WIDEPIX);
        }
        free(data2);
    }
}

/**
 * Parse PNG format for pixel fonts (binary)
 */
void png(unsigned char *ptr, int size)
{
    unsigned char *data, *data2;
    unsigned int i, w, h, f;
    stbi__context s;
    stbi__result_info ri;
#ifdef HAS_QUANT
    liq_attr *handle = NULL;
    liq_image *input_image = NULL;
    liq_result *quantization_result;
    const liq_palette *liqpalette;
    unsigned char pal[256];
#endif

    s.read_from_callbacks = 0;
    s.img_buffer = s.img_buffer_original = ptr;
    s.img_buffer_end = s.img_buffer_original_end = ptr+size;
    w = h = size = 0;
    ri.bits_per_channel = 8;
    data = (uint8_t*)stbi__png_load(&s, (int*)&w, (int*)&h, (int*)&f, 0, &ri);
    if(!data || (f != STBI_rgb_alpha && f != STBI_rgb) || w < 1 || h < 1)
        { fprintf(stderr,"libsfn: unsupported PNG format\n"); return; }
    data2 = (unsigned char*)malloc(w * h);
    if(!data2) { fprintf(stderr,"libsfn: memory allocation error\n"); return; }
#ifdef HAS_QUANT
    handle = liq_attr_create();
    liq_set_max_colors(handle, 254);
    if(f == STBI_rgb_alpha)
        input_image = liq_image_create_rgba(handle, data, w, h, 0);
    if(pbar) (*pbar)(0, 0, 0, 1, PBAR_QUANT);
    if (f == STBI_rgb_alpha && liq_image_quantize(input_image, handle, &quantization_result) == LIQ_OK) {
        liq_set_dithering_level(quantization_result, 1.0);
        liqpalette = liq_get_palette(quantization_result);
        liq_write_remapped_image(quantization_result, input_image, data, w * h);
        for(i = 0; i < liqpalette->count && i < 254; i++) {
            pal[i] = sfn_cpaladd(liqpalette->entries[i].r, liqpalette->entries[i].g,
                liqpalette->entries[i].b, liqpalette->entries[i].a);
        }
        for(i = 0; i < w * h; i++)
            data2[i] = pal[data[i]];
        liq_result_destroy(quantization_result);
    } else
#endif
    {
        if(f == STBI_rgb_alpha)
            for(i = 0; i < w * h; i++) {
                if(pbar) (*pbar)(0, 0, i, w * h, PBAR_QUANT);
                data2[i] = sfn_cpaladd(data[i*4], data[i*4+1], data[i*4+2], data[i*4+3]);
            }
        else
            for(i = 0; i < w * h; i++) {
                if(pbar) (*pbar)(0, 0, i, w * h, PBAR_QUANT);
                data2[i] = sfn_cpaladd(data[i*3], data[i*3+1], data[i*3+2], 255);
            }
    }
#ifdef HAS_QUANT
    if(input_image) liq_image_destroy(input_image);
    liq_attr_destroy(handle);
#endif
    free(data);
    pixmap_parse(data2, w, h);
    free(data2);
}

/**
 * Parse TGA format for pixel fonts (binary)
 */
void tga(unsigned char *ptr, int size)
{
    unsigned char *data;
    int i, j, k, x, y, w, h, o, m;

    o = (ptr[11] << 8) + ptr[10];
    w = (ptr[13] << 8) + ptr[12];
    h = (ptr[15] << 8) + ptr[14];
    if(w<1 || h<1) {
tgaerr: fprintf(stderr,"libsfn: unsupported TGA file format\n");
        return;
    }
    m = ((ptr[1]? (ptr[7]>>3)*ptr[5] : 0) + 18);
    data = (unsigned char*)malloc(w*h);
    if(!data) { fprintf(stderr,"libsfn: memory allocation error\n"); return; }
    switch(ptr[2]) {
        case 1:
            if(ptr[6]!=0 || ptr[4]!=0 || ptr[3]!=0 || (ptr[7]!=24 && ptr[7]!=32)) goto tgaerr;
            for(y=i=0; y<h; y++) {
                k = ((!o?h-y-1:y)*w);
                for(x=0; x<w; x++) {
                    j = ptr[m + k++]*(ptr[7]>>3) + 18;
                    data[i++] = sfn_cpaladd(ptr[j+2], ptr[j+1], ptr[j], ptr[7]==32?ptr[j+3]:0xFF);
                }
            }
        break;

        case 2:
            if(ptr[5]!=0 || ptr[6]!=0 || ptr[1]!=0 || (ptr[16]!=24 && ptr[16]!=32)) goto tgaerr;
            for(y=i=0; y<h; y++) {
                j = m+((!o?h-y-1:y)*w*(ptr[16]>>3));
                for(x=0; x<w; x++) {
                    data[i++] = sfn_cpaladd(ptr[j+2], ptr[j+1], ptr[j], ptr[16]==32?ptr[j+3]:0xFF);
                    j += ptr[16]>>3;
                }
            }
        break;

        case 9:
            if(ptr[6]!=0 || ptr[4]!=0 || ptr[3]!=0 || (ptr[7]!=24 && ptr[7]!=32)) goto tgaerr;
            y = i = 0;
            for(x=0; x<w*h && m<size;) {
                k = ptr[m++];
                if(k > 127) {
                    k -= 127; x += k;
                    j = ptr[m++]*(ptr[7]>>3) + 18;
                    while(k--) {
                        if(!(i%w)) { i=((!o?h-y-1:y)*w); y++; }
                        data[i++] = sfn_cpaladd(ptr[j+2], ptr[j+1], ptr[j], ptr[7]==32?ptr[j+3]:0xFF);
                    }
                } else {
                    k++; x += k;
                    while(k--) {
                        j = ptr[m++]*(ptr[7]>>3) + 18;
                        if(!(i%w)) { i=((!o?h-y-1:y)*w); y++; }
                        data[i++] = sfn_cpaladd(ptr[j+2], ptr[j+1], ptr[j], ptr[7]==32?ptr[j+3]:0xFF);
                    }
                }
            }
        break;

        case 10:
            if(ptr[5]!=0 || ptr[6]!=0 || ptr[1]!=0 || (ptr[16]!=24 && ptr[16]!=32)) goto tgaerr;
            y = i = 0;
            for(x=0; x<w*h && m<size;) {
                k = ptr[m++];
                if(k > 127) {
                    k -= 127; x += k;
                    while(k--) {
                        if(!(i%w)) { i=((!o?h-y-1:y)*w); y++; }
                        data[i++] = sfn_cpaladd(ptr[m+2], ptr[m+1], ptr[m], ptr[16]==32?ptr[m+3]:0xFF);
                    }
                    m += ptr[16]>>3;
                } else {
                    k++; x += k;
                    while(k--) {
                        if(!(i%w)) { i=((!o?h-y-1:y)*w); y++; }
                        data[i++] = sfn_cpaladd(ptr[m+2], ptr[m+1], ptr[m], ptr[16]==32?ptr[m+3]:0xFF);
                        m += ptr[16]>>3;
                    }
                }
            }
        break;
    }
    pixmap_parse(data, w, h);
    free(data);
}

#endif
