/* * psffontop.c - aeb@cwi.nl, 990921 */ #include "config.h" #include #include #include #include #include "libcommon.h" #include "psf.h" #include "psffontop.h" #include "utf8.h" #include "paths.h" 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, get_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, get_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, get_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; }