summaryrefslogblamecommitdiff
path: root/ucfcomp.c
blob: d607a4341ffce024e12fd9a015ac2bef9a2b0696 (plain) (tree)

















































































































































































































                                                                                                      
#include <ctype.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

struct gmap
{
	struct gmap *next, *prev;
	int glyph;
	int width;
	unsigned char rules[4*8];
	int len;
	int offset;
};

static struct gmap *ctab[0x30000];

#define GMAP_WIDTH         1

#define GMAP_FINAL1        0
#define GMAP_FINAL2        1
#define GMAP_GLYPH1        2
#define GMAP_GLYPH2        3

#define RULE_RANGE         4
#define RULE_ATTACHED_TO   5
#define RULE_WITH_ATTACHED 6
#define RULE_FOLLOWS       7
#define RULE_PRECEDES      8

#define CTAB_MASK          0x3fffffff
#define CTAB_INDIRECT      0x80000000
#define CTAB_WIDTH         30

static void put_u32(int n)
{
	putchar(n>>24);
	putchar(n>>16);
	putchar(n>>8);
	putchar(n);
}

int main()
{
	char buf[256], *s;
	int i;
	int ch;
	struct gmap *gm;
	int type, begin, len;
	int w=8, h=16;
	unsigned char gbuf[32];
	char tmp[3];
	int width;
	int nglyphs = 0;
	unsigned char *glyphs = 0;
	struct { unsigned begin, len, rel; } *ranges = 0;
	int nranges = 0;
	int ctab_len = 0;
	int gmap_len = 0;

	while ((s=fgets(buf, sizeof buf, stdin))) {
		if (*s++ != ':') continue;
		for (tmp[2]=i=0; i<sizeof gbuf && isxdigit(tmp[0]=*s++) && isxdigit(tmp[1]=*s++); i++)
			gbuf[i] = strtol(tmp, NULL, 16);
		if (i == 32) {
			unsigned char gbuf_tmp[32];
			width = 2;
			for (i=0; i<16; i++) gbuf_tmp[i] = gbuf[2*i];
			for (i=0; i<16; i++) gbuf_tmp[16+i] = gbuf[2*i+1];
			memcpy(gbuf, gbuf_tmp, 32);
		} else if (i == 16) { width = 1;
		} else exit(1);
		glyphs = realloc(glyphs, 16*(width+nglyphs));
		memcpy(glyphs + 16*nglyphs, gbuf, 16*width);
		nglyphs += width;
		for (;;) {
			for (; isspace(*s); s++);
			if (!*s) break;
			ch = strtol(s, &s, 16);
			gm = calloc(1, sizeof(struct gmap));
			gm->glyph = nglyphs-width;
			gm->width = width;
			gm->next = 0;
			gm->prev = ctab[ch];
			if (ctab[ch]) ctab[ch]->next = gm;
			ctab[ch] = gm;
			i=0;
			while(i<sizeof(gm->rules)-12 && *s && !isspace(*s)) {
				switch (*s++) {
				case '+': type = RULE_WITH_ATTACHED; break;
				case '-': type = RULE_ATTACHED_TO; break;
				case '<': type = RULE_PRECEDES; break;
				case '>': type = RULE_FOLLOWS; break;
				default: exit(1);
				}
				if (*s == '[') {
					begin = strtol(++s, &s, 16);
					if (*s++ != '-') exit(1);
					len = strtol(s, &s, 16) - begin + 1;
					if (*s++ != ']') exit(1);
					gm->rules[i++] = type;
					gm->rules[i++] = begin>>16;
					gm->rules[i++] = begin>>8;
					gm->rules[i++] = begin;
					gm->rules[i++] = RULE_RANGE;
					gm->rules[i++] = len>>16;
					gm->rules[i++] = len>>8;
					gm->rules[i++] = len;
				} else if (isxdigit(*s)) {
					begin = strtol(s, &s, 16);
					gm->rules[i++] = type;
					gm->rules[i++] = begin>>16;
					gm->rules[i++] = begin>>8;
					gm->rules[i++] = begin;
				} else exit(1);
			}
			gm->rules[i++] = GMAP_GLYPH1 | (gm->width-1);
			gm->rules[i++] = (gm->glyph)>>16;
			gm->rules[i++] = (gm->glyph)>>8;
			gm->rules[i++] = (gm->glyph);
			gm->len = i;
		}
	}

	/* Count total glyph mapping rules and figure the table offsets */
	for (ch=0; ch < sizeof(ctab)/sizeof(ctab[0]); ch++) {
		for (gm = ctab[ch]; gm && gm->prev; gm = gm->prev);
		/* Exclude rules that can be replaced by a trivial mapping */
		if (!gm || (!gm->next && gm->len == 4)) {
			if (gm) gm->len = 0;
			continue;
		}
		for (ctab[ch] = gm; gm; gm = gm->next) {
			/* Terminate the rules list */
			if (!gm->next) gm->rules[gm->len-4] &= GMAP_WIDTH;
			gm->offset = gmap_len;
			gmap_len += gm->len;
		}
	}

	/* Compact empty intervals and intervals with trivial glyph mapping */
	begin = 1;
	for (ch=1; ch <= sizeof(ctab)/sizeof(ctab[0]); ch++) {
		if (ch == sizeof(ctab)/sizeof(ctab[0])
		 || (ctab[ch-1] != ctab[ch]
		 &&(!ctab[ch-1] || !ctab[ch]
		 || ctab[ch-1]->prev || ctab[ch-1]->len > 4
		 || ctab[ch]->prev   || ctab[ch]->len > 4
		 || ctab[ch]->glyph != ctab[ch-1]->glyph + ctab[ch-1]->width
		 || ctab[ch]->width != ctab[ch-1]->width))) {
			if (ch - begin >= 1024) {
				ranges = realloc(ranges, sizeof(ranges[0])*(nranges+1));
				ranges[nranges].begin = begin;
				ranges[nranges++].len = ch-begin;
			}
			begin = ch+1;
		}
	}
	ranges[nranges-1].len = 0x110000;

	/* Count total size of the character table */
	ranges[0].rel = ranges[0].begin;
	ctab_len = 1 + nranges*2 + ranges[0].begin;
	for (i=1; i<nranges; i++)
		ctab_len += ranges[i].rel = ranges[i].begin - (ranges[i-1].begin+ranges[i-1].len);
	ctab_len *= 4;

	// offset 0: magic and version (0.1.0.0)
	fwrite("ucf\300\000\001\000\000", 1, 8, stdout);
	// offset 8
	putchar(8);
	putchar(16);
	putchar(0);
	putchar(0);
	putchar(0);
	putchar(0);
	putchar(0);
	putchar(0);
	// offset 16
	put_u32(0); // metadata offset
	put_u32(32); // ctab offset
	put_u32(32+ctab_len); // gmap offset
	put_u32(32+ctab_len+gmap_len); // glyph bitmaps
	// offset 32
	put_u32(nranges);
	for (i=0; i<nranges; i++) {
		put_u32(ranges[i].rel);
		put_u32(ranges[i].len);
	}
	for (i=ch=0; ch<sizeof(ctab)/sizeof(ctab[0]); ch++) {
		if (ch == ranges[i].begin) {
			ch += ranges[i++].len-1;
			continue;
		}
		if (!ctab[ch])
			put_u32(-1);
		else if (ctab[ch]->len == 0)
			put_u32(ctab[ch]->glyph | ctab[ch]->width-1<<30);
		else
			put_u32(ctab[ch]->offset | 0x80000000);
	}
	// offset 32 + ctab_len
	for (ch=0; ch < sizeof(ctab)/sizeof(ctab[0]); ch++)
		for (gm = ctab[ch]; gm; gm = gm->next)
			fwrite(gm->rules, 1, gm->len, stdout);
	// offset 30 + ctab_len + gmap_len
	fwrite(glyphs, 16, nglyphs, stdout);

	return 0;
}