| /* |
| * psffontop.c - aeb@cwi.nl, 990921 |
| */ |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <sysexits.h> |
| #include "xmalloc.h" |
| #include "nls.h" |
| #include "psf.h" |
| #include "psffontop.h" |
| #include "utf8.h" |
| #include "paths.h" |
| |
| extern char *progname; |
| |
| static void |
| addpair(struct unicode_list *up, unsigned int uc) { |
| struct unicode_list *ul; |
| struct unicode_seq *us; |
| |
| ul = xmalloc(sizeof(struct unicode_list)); |
| us = xmalloc(sizeof(struct unicode_seq)); |
| us->uc = uc; |
| us->prev = us; |
| us->next = NULL; |
| ul->seq = us; |
| ul->prev = up->prev; |
| ul->prev->next = ul; |
| ul->next = NULL; |
| up->prev = ul; |
| } |
| |
| static void |
| addseq(struct unicode_list *up, unsigned int uc) { |
| struct unicode_seq *us; |
| struct unicode_seq *usl; |
| struct unicode_list *ul = up->prev; |
| |
| usl = ul->seq; |
| while (usl->next) usl = usl->next; |
| us = xmalloc(sizeof(struct unicode_seq)); |
| us->uc = uc; |
| us->prev = usl; |
| us->next = NULL; |
| usl->next = us; |
| //ul->seq->prev = us; |
| } |
| |
| static unsigned int |
| assemble_int(unsigned char *ip) { |
| return (ip[0] + (ip[1]<<8) + (ip[2]<<16) + (ip[3]<<24)); |
| } |
| |
| static void |
| store_int_le(unsigned char *ip, int num) { |
| ip[0] = (num & 0xff); |
| ip[1] = ((num >> 8) & 0xff); |
| ip[2] = ((num >> 16) & 0xff); |
| ip[3] = ((num >> 24) & 0xff); |
| } |
| |
| static unsigned int |
| assemble_ucs2(char **inptr, int cnt) { |
| unsigned int u1, u2; |
| |
| if (cnt < 2) { |
| char *u = _("%s: short ucs2 unicode table\n"); |
| fprintf(stderr, u, progname); |
| exit(EX_DATAERR); |
| } |
| |
| u1 = (unsigned char) *(*inptr)++; |
| u2 = (unsigned char) *(*inptr)++; |
| return (u1 | (u2 << 8)); |
| } |
| |
| /* called with cnt > 0 and **inptr not 0xff or 0xfe */ |
| static unsigned int |
| assemble_utf8(char **inptr, int cnt) { |
| int err; |
| unsigned long uc; |
| char *u; |
| |
| uc = from_utf8(inptr, cnt, &err); |
| if (err) { |
| switch (err) { |
| case UTF8_SHORT: |
| u = _("%s: short utf8 unicode table\n"); |
| break; |
| case UTF8_BAD: |
| u = _("%s: bad utf8\n"); |
| break; |
| default: |
| u = _("%s: unknown utf8 error\n"); |
| } |
| fprintf(stderr, u, progname); |
| exit(EX_DATAERR); |
| } |
| return uc; |
| } |
| |
| static void |
| clear_uni_entry(struct unicode_list *up) { |
| up->next = NULL; |
| up->seq = NULL; |
| up->prev = up; |
| } |
| |
| /* |
| * Read description of a single font position. |
| */ |
| static void |
| get_uni_entry(char **inptr, char **endptr, struct unicode_list *up, int utf8) { |
| unsigned char uc; |
| unicode unichar; |
| int inseq = 0; |
| |
| up->next = NULL; |
| up->seq = NULL; |
| up->prev = up; |
| |
| while(1) { |
| if (*endptr == *inptr) { |
| char *u = _("%s: short unicode table\n"); |
| fprintf(stderr, u, progname); |
| exit(EX_DATAERR); |
| } |
| if (utf8) { |
| uc = *(*inptr)++; |
| if (uc == PSF2_SEPARATOR) |
| break; |
| if (uc == PSF2_STARTSEQ) { |
| inseq = 1; |
| continue; |
| } |
| --(*inptr); |
| unichar = assemble_utf8(inptr, *endptr - *inptr); |
| } else { |
| unichar = assemble_ucs2(inptr, *endptr - *inptr); |
| if (unichar == PSF1_SEPARATOR) |
| break; |
| if (unichar == PSF1_STARTSEQ) { |
| inseq = 1; |
| continue; |
| } |
| } |
| if (inseq < 2) |
| addpair(up, unichar); |
| else |
| addseq(up, unichar); |
| if (inseq) |
| inseq++; |
| } |
| } |
| |
| /* |
| * Read a psf font and return >= 0 on success and -1 on failure. |
| * Failure means that the font was not psf (but has been read). |
| * > 0 means that the Unicode table contains sequences. |
| * |
| * The font is read either from file (when FONT is non-NULL) |
| * or from memory (namely from *ALLBUFP of size *ALLSZP). |
| * In the former case, if ALLBUFP is non-NULL, a pointer to |
| * the entire fontfile contents (possibly read from pipe) |
| * is returned in *ALLBUFP, and the size in ALLSZP, where this |
| * buffer was allocated using malloc(). |
| * In FONTBUFP, FONTSZP the subinterval of ALLBUFP containing |
| * the font data is given. |
| * The font width is stored in FONTWIDTHP. |
| * The number of glyphs is stored in FONTLENP. |
| * The unicodetable is stored in UCLISTHEADSP (when non-NULL), with |
| * fontpositions counted from FONTPOS0 (so that calling this several |
| * times can achieve font merging). |
| */ |
| extern char *progname; |
| |
| int |
| readpsffont(FILE *fontf, char **allbufp, int *allszp, |
| char **fontbufp, int *fontszp, |
| int *fontwidthp, int *fontlenp, int fontpos0, |
| struct unicode_list **uclistheadsp) { |
| char *inputbuf = NULL; |
| size_t inputbuflth = 0; |
| size_t inputlth, fontlen, fontwidth, charsize, hastable, ftoffset, utf8; |
| size_t i, k, n; |
| |
| /* |
| * We used to look at the length of the input file |
| * with stat(); now that we accept compressed files, |
| * just read the entire file. |
| */ |
| if (fontf) { |
| inputbuflth = MAXFONTSIZE/4; /* random */ |
| inputbuf = xmalloc(inputbuflth); |
| n = 0; |
| |
| while(1) { |
| if (n >= inputbuflth) { |
| inputbuflth *= 2; |
| inputbuf = xrealloc(inputbuf, inputbuflth); |
| } |
| n += fread(inputbuf+n, 1, inputbuflth-n, fontf); |
| if (ferror(fontf)) { |
| char *u = _("%s: Error reading input font"); |
| fprintf(stderr, u, progname); |
| exit(EX_DATAERR); |
| } |
| if (feof(fontf)) |
| break; |
| } |
| if (allbufp) |
| *allbufp = inputbuf; |
| if (allszp) |
| *allszp = n; |
| inputlth = n; |
| } else { |
| if (!allbufp || !allszp) { |
| char *u = _("%s: Bad call of readpsffont\n"); |
| fprintf(stderr, u, progname); |
| exit(EX_SOFTWARE); |
| } |
| inputbuf = *allbufp; |
| inputbuflth = inputlth = n = *allszp; |
| } |
| |
| if (inputlth >= sizeof(struct psf1_header) && |
| PSF1_MAGIC_OK((unsigned char *)inputbuf)) { |
| struct psf1_header *psfhdr; |
| |
| psfhdr = (struct psf1_header *) &inputbuf[0]; |
| |
| if (psfhdr->mode > PSF1_MAXMODE) { |
| char *u = _("%s: Unsupported psf file mode (%d)\n"); |
| fprintf(stderr, u, progname, psfhdr->mode); |
| exit(EX_DATAERR); |
| } |
| fontlen = ((psfhdr->mode & PSF1_MODE512) ? 512 : 256); |
| charsize = psfhdr->charsize; |
| hastable = (psfhdr->mode & (PSF1_MODEHASTAB|PSF1_MODEHASSEQ)); |
| ftoffset = sizeof(struct psf1_header); |
| fontwidth = 8; |
| utf8 = 0; |
| } else if (inputlth >= sizeof(struct psf2_header) && |
| PSF2_MAGIC_OK((unsigned char *)inputbuf)) { |
| struct psf2_header psfhdr; |
| int flags; |
| |
| memcpy(&psfhdr, inputbuf, sizeof(struct psf2_header)); |
| |
| if (psfhdr.version > PSF2_MAXVERSION) { |
| char *u = _("%s: Unsupported psf version (%d)\n"); |
| fprintf(stderr, u, progname, psfhdr.version); |
| exit(EX_DATAERR); |
| } |
| fontlen = assemble_int((unsigned char *) &psfhdr.length); |
| charsize = assemble_int((unsigned char *) &psfhdr.charsize); |
| flags = assemble_int((unsigned char *) &psfhdr.flags); |
| hastable = (flags & PSF2_HAS_UNICODE_TABLE); |
| ftoffset = assemble_int((unsigned char *) &psfhdr.headersize); |
| fontwidth = assemble_int((unsigned char *) &psfhdr.width); |
| utf8 = 1; |
| } else |
| return -1; /* not psf */ |
| |
| /* tests required - we divide by these */ |
| if (fontlen == 0) { |
| char *u = _("%s: zero input font length?\n"); |
| fprintf(stderr, u, progname); |
| exit(EX_DATAERR); |
| } |
| if (charsize == 0) { |
| char *u = _("%s: zero input character size?\n"); |
| fprintf(stderr, u, progname); |
| exit(EX_DATAERR); |
| } |
| i = ftoffset + fontlen * charsize; |
| if (i > inputlth || (!hastable && i != inputlth)) { |
| char *u = _("%s: Input file: bad input length (%d)\n"); |
| fprintf(stderr, u, progname, inputlth); |
| exit(EX_DATAERR); |
| } |
| |
| if (fontbufp && allbufp) |
| *fontbufp = *allbufp + ftoffset; |
| if (fontszp) |
| *fontszp = fontlen * charsize; |
| if (fontlenp) |
| *fontlenp = fontlen; |
| if (fontwidthp) |
| *fontwidthp = fontwidth; |
| |
| if (!uclistheadsp) |
| return 0; /* got font, don't need unicode_list */ |
| |
| *uclistheadsp = xrealloc(*uclistheadsp, |
| (fontpos0+fontlen)*sizeof(struct unicode_list)); |
| |
| if (hastable) { |
| char *inptr, *endptr; |
| |
| inptr = inputbuf + ftoffset + fontlen * charsize; |
| endptr = inputbuf + inputlth; |
| |
| for (i=0; i<fontlen; i++) { |
| k = fontpos0 + i; |
| get_uni_entry(&inptr, &endptr, |
| &(*uclistheadsp)[k], utf8); |
| } |
| if (inptr != endptr) { |
| char *u = _("%s: Input file: trailing garbage\n"); |
| fprintf(stderr, u, progname); |
| exit(EX_DATAERR); |
| } |
| } else { |
| for (i=0; i<fontlen; i++) { |
| k = fontpos0 + i; |
| clear_uni_entry(&(*uclistheadsp)[k]); |
| } |
| } |
| |
| return 0; /* got psf font */ |
| } |
| |
| static int |
| has_sequences(struct unicode_list *uclistheads, int fontlen) { |
| struct unicode_list *ul; |
| struct unicode_seq *us; |
| int i; |
| |
| for (i=0; i<fontlen; i++) { |
| ul = uclistheads[i].next; |
| while(ul) { |
| us = ul->seq; |
| if (us && us->next) |
| return 1; |
| ul = ul->next; |
| } |
| } |
| return 0; |
| } |
| |
| void |
| appendunicode(FILE *fp, unsigned int uc, int utf8) { |
| int n = 6; |
| unsigned char out[6]; |
| |
| if (uc & ~0x7fffffff) { |
| fprintf(stderr, _("appendunicode: illegal unicode %u\n"), uc); |
| exit(1); |
| } |
| if (!utf8) { |
| out[--n] = ((uc >> 8) & 0xff); |
| out[--n] = (uc & 0xff); |
| } else if (uc < 0x80) { |
| out[--n] = uc; |
| } else { |
| int mask = 0x3f; |
| while (uc & ~mask) { |
| out[--n] = 0x80 + (uc & 0x3f); |
| uc >>= 6; |
| mask >>= 1; |
| } |
| out[--n] = ((uc + ~mask + ~mask) & 0xff); |
| } |
| if (fwrite(out+n, 6-n, 1, fp) != 1) { |
| perror("appendunimap"); |
| exit(1); |
| } |
| if (debug) { |
| printf ("("); |
| if (!utf8) |
| printf ("U+"); |
| while (n < 6) printf ("%02x ", out[n++]); |
| printf (")"); |
| } |
| } |
| |
| void |
| appendseparator(FILE *fp, int seq, int utf8) { |
| int n; |
| |
| if (utf8) { |
| unsigned char u = (seq ? PSF2_STARTSEQ : PSF2_SEPARATOR); |
| n = fwrite(&u, sizeof(u), 1, fp); |
| } else { |
| unsigned short u = (seq ? PSF1_STARTSEQ : PSF1_SEPARATOR); |
| n = fwrite(&u, sizeof(u), 1, fp); |
| } |
| if (n != 1) { |
| perror("appendseparator"); |
| exit(1); |
| } |
| } |
| |
| void |
| writepsffontheader(FILE *ofil, int width, int height, int fontlen, |
| int *psftype, int flags) { |
| int bytewidth, charsize, ret; |
| |
| bytewidth = (width+7)/8; |
| charsize = bytewidth * height; |
| |
| if ((fontlen != 256 && fontlen != 512) || width != 8) |
| *psftype = 2; |
| |
| if (*psftype == 2) { |
| struct psf2_header h; |
| int flags2 = 0; |
| |
| if (flags & WPSFH_HASTAB) |
| flags2 |= PSF2_HAS_UNICODE_TABLE; |
| h.magic[0] = PSF2_MAGIC0; |
| h.magic[1] = PSF2_MAGIC1; |
| h.magic[2] = PSF2_MAGIC2; |
| h.magic[3] = PSF2_MAGIC3; |
| store_int_le((unsigned char *) &h.version, 0); |
| store_int_le((unsigned char *) &h.headersize, sizeof(h)); |
| store_int_le((unsigned char *) &h.flags, flags2); |
| store_int_le((unsigned char *) &h.length, fontlen); |
| store_int_le((unsigned char *) &h.charsize, charsize); |
| store_int_le((unsigned char *) &h.width, width); |
| store_int_le((unsigned char *) &h.height, height); |
| ret = fwrite(&h, sizeof(h), 1, ofil); |
| } else { |
| struct psf1_header h; |
| |
| h.magic[0] = PSF1_MAGIC0; |
| h.magic[1] = PSF1_MAGIC1; |
| h.mode = 0; |
| if (fontlen == 512) |
| h.mode |= PSF1_MODE512; |
| if (flags & WPSFH_HASSEQ) |
| h.mode |= PSF1_MODEHASSEQ; |
| else if (flags & WPSFH_HASTAB) |
| h.mode |= PSF1_MODEHASTAB; |
| h.charsize = charsize; |
| ret = fwrite(&h, sizeof(h), 1, ofil); |
| } |
| |
| if (ret != 1) { |
| fprintf(stderr, _("Cannot write font file header")); |
| exit(EX_IOERR); |
| } |
| } |
| |
| |
| int |
| writepsffont(FILE *ofil, char *fontbuf, int width, int height, size_t fontlen, |
| int psftype, struct unicode_list *uclistheads) { |
| int bytewidth, charsize, flags, utf8; |
| size_t i; |
| |
| bytewidth = (width+7)/8; |
| charsize = bytewidth * height; |
| flags = 0; |
| |
| if (uclistheads) { |
| flags |= WPSFH_HASTAB; |
| if (has_sequences(uclistheads, fontlen)) |
| flags |= WPSFH_HASSEQ; |
| } |
| |
| writepsffontheader(ofil, width, height, fontlen, &psftype, flags); |
| utf8 = (psftype == 2); |
| |
| if ((fwrite(fontbuf, charsize, fontlen, ofil)) != fontlen) { |
| fprintf(stderr, _("Cannot write font file")); |
| exit(EX_IOERR); |
| } |
| |
| /* unimaps: -1 => do nothing: caller will append map */ |
| if (uclistheads != NULL && uclistheads != (struct unicode_list*)-1) { |
| struct unicode_list *ul; |
| struct unicode_seq *us; |
| |
| for (i=0; i<fontlen; i++) { |
| ul = uclistheads[i].next; |
| while(ul) { |
| us = ul->seq; |
| if (us && us->next) |
| appendseparator(ofil, 1, utf8); |
| while(us) { |
| appendunicode(ofil, us->uc, utf8); |
| us = us->next; |
| } |
| ul = ul->next; |
| } |
| appendseparator(ofil, 0, utf8); |
| } |
| } |
| return utf8; |
| } |
| |