/* Copyright (C) 2000, Matias Atria * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /* * History: * * 11/3/2000: * - First working version * 11/4/2000: * - FIXED: entirely white/black rows were missed. * 11/8/2000: * - TESTED: Glyphs are rendered correctly in different byte orders. * - Made bitmap code much more efficient and compact. */ #include <config.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <math.h> #include "mdvi.h" #include "private.h" #define PK_ID 89 #define PK_CMD_START 240 #define PK_X1 240 #define PK_X2 241 #define PK_X3 242 #define PK_X4 243 #define PK_Y 244 #define PK_POST 245 #define PK_NOOP 246 #define PK_PRE 247 #define PK_DYN_F(x) (((x) >> 4) & 0xf) #define PK_PACKED(x) (PK_DYN_F(x) != 14) static int pk_load_font __PROTO((DviParams *, DviFont *)); static int pk_font_get_glyph __PROTO((DviParams *, DviFont *, int)); static int pk_auto_generate = 1; /* this is ON by default */ typedef struct { char currbyte; char nybpos; int dyn_f; } pkread; static char *pk_lookup __PROTO((const char *, Ushort *, Ushort *)); static char *pk_lookupn __PROTO((const char *, Ushort *, Ushort *)); /* only symbols exported by this file */ DviFontInfo pk_font_info = { "PK", 0, /* scaling not supported natively */ pk_load_font, pk_font_get_glyph, mdvi_shrink_glyph, mdvi_shrink_glyph_grey, NULL, /* free */ NULL, /* reset */ pk_lookup, /* lookup */ kpse_pk_format, NULL }; DviFontInfo pkn_font_info = { "PKN", 0, /* scaling not supported natively */ pk_load_font, pk_font_get_glyph, mdvi_shrink_glyph, mdvi_shrink_glyph_grey, NULL, /* free */ NULL, /* reset */ pk_lookupn, /* lookup */ kpse_pk_format, NULL }; static char *pk_lookup(const char *name, Ushort *hdpi, Ushort *vdpi) { kpse_glyph_file_type type; char *filename; if(pk_auto_generate == 0) { kpse_set_program_enabled(kpse_pk_format, 1, kpse_src_cmdline); pk_auto_generate = 1; } filename = kpse_find_glyph(name, Max(*hdpi, *vdpi), kpse_pk_format, &type); if(filename && type.source == kpse_glyph_source_fallback) { mdvi_free(filename); filename = NULL; } else if(filename) { *hdpi = *vdpi = type.dpi; } return filename; } static char *pk_lookupn(const char *name, Ushort *hdpi, Ushort *vdpi) { kpse_glyph_file_type type; char *filename; if(pk_auto_generate) { kpse_set_program_enabled(kpse_pk_format, 0, kpse_src_cmdline); pk_auto_generate = 0; } filename = kpse_find_glyph(name, Max(*hdpi, *vdpi), kpse_pk_format, &type); if(filename && type.source == kpse_glyph_source_fallback) { mdvi_free(filename); filename = NULL; } else if(filename) { *hdpi = *vdpi = type.dpi; } return filename; } static inline int pk_get_nyb(FILE *p, pkread *pk) { unsigned t; int nb; char c; t = c = pk->currbyte; nb = pk->nybpos; switch(nb) { case 0: c = pk->currbyte = fuget1(p); t = (c >> 4); break; case 1: t = c; break; } pk->nybpos = !nb; return (t & 0xf); } /* * this is a bit cumbersome because we have to pass around * the `pkread' data... */ static int pk_packed_num(FILE *p, pkread *pkr, int *repeat) { int i, j; int dyn_f = pkr->dyn_f; i = pk_get_nyb(p, pkr); if(i == 0) { do { j = pk_get_nyb(p, pkr); i++; } while(j == 0); while(i-- > 0) j = (j << 4) + pk_get_nyb(p, pkr); return (j - 15 + ((13 - dyn_f) << 4) + dyn_f); } else if(i <= dyn_f) return i; else if(i < 14) return ((i - dyn_f - 1) << 4) + pk_get_nyb(p, pkr) + dyn_f + 1; else { *repeat = 1; if(i == 14) *repeat = pk_packed_num(p, pkr, repeat); return pk_packed_num(p, pkr, repeat); } } #define ROUND(x,y) (((x) + (y) - 1) / (y)) static BITMAP *get_bitmap(FILE *p, int w, int h, int flags) { int i, j; BmUnit *ptr; BITMAP *bm; int bitpos; int currch; flags = 0; /* shut up that compiler */ bitpos = -1; if((bm = bitmap_alloc(w, h)) == NULL) return NULL; DEBUG((DBG_BITMAPS, "get_bitmap(%d,%d,%d): reading raw bitmap\n", w, h, flags)); ptr = bm->data; currch = 0; for(i = 0; i < h; i++) { BmUnit mask; mask = FIRSTMASK; for(j = 0; j < w; j++) { if(bitpos < 0) { currch = fuget1(p); bitpos = 7; } if(currch & (1 << bitpos)) *ptr |= mask; bitpos--; if(mask == LASTMASK) { ptr++; mask = FIRSTMASK; } else NEXTMASK(mask); } ptr = bm_offset(ptr, bm->stride); } return bm; } static BITMAP *get_packed(FILE *p, int w, int h, int flags) { int inrow, count; int row; BITMAP *bm; int repeat_count; int paint; pkread pkr; pkr.nybpos = 0; pkr.currbyte = 0; pkr.dyn_f = PK_DYN_F(flags); paint = !!(flags & 0x8); repeat_count = 0; row = 0; inrow = w; if((bm = bitmap_alloc(w, h)) == NULL) return NULL; DEBUG((DBG_BITMAPS, "get_packed(%d,%d,%d): reading packed glyph\n", w, h, flags)); while(row < h) { int i = 0; count = pk_packed_num(p, &pkr, &i); if(i > 0) { if(repeat_count) fprintf(stderr, "second repeat count for this row (had %d and got %d)\n", repeat_count, i); repeat_count = i; } if(count >= inrow) { Uchar *r, *t; BmUnit *a, mask; /* first finish current row */ if(paint) bitmap_set_row(bm, row, w - inrow, inrow, paint); /* now copy it as many times as required */ r = (Uchar *)bm->data + row * bm->stride; while(repeat_count-- > 0) { t = r + bm->stride; /* copy entire lines */ memcpy(t, r, bm->stride); r = t; row++; } repeat_count = 0; /* count first row we drew */ row++; /* update run count */ count -= inrow; /* now r points to the beginning of the last row we finished */ if(paint) mask = ~((BmUnit)0); else mask = 0; /* goto next row */ a = (BmUnit *)(r + bm->stride); /* deal with entirely with/black rows */ while(count >= w) { /* count number of atoms in a row */ i = ROUND(w, BITMAP_BITS); while(i-- > 0) *a++ = mask; count -= w; row++; } inrow = w; } if(count > 0) bitmap_set_row(bm, row, w - inrow, count, paint); inrow -= count; paint = !paint; } if(row != h || inrow != w) { mdvi_error(_("Bad PK file: More bits than required\n")); bitmap_destroy(bm); return NULL; } return bm; } static BITMAP *get_char(FILE *p, int w, int h, int flags) { /* check if dyn_f == 14 */ if(((flags >> 4) & 0xf) == 14) return get_bitmap(p, w, h, flags); else return get_packed(p, w, h, flags); } /* supports any number of characters in a font */ static int pk_load_font(DviParams *unused, DviFont *font) { int i; int flag_byte; int hic, maxch; Int32 checksum; FILE *p; #ifndef NODEBUG char s[256]; #endif long alpha, beta, z; unsigned int loc; font->chars = xnalloc(DviFontChar, 256); p = font->in; memzero(font->chars, 256 * sizeof(DviFontChar)); for(i = 0; i < 256; i++) font->chars[i].offset = 0; /* check the preamble */ loc = fuget1(p); hic = fuget1(p); if(loc != PK_PRE || hic != PK_ID) goto badpk; i = fuget1(p); #ifndef NODEBUG for(loc = 0; loc < i; loc++) s[loc] = fuget1(p); s[loc] = 0; DEBUG((DBG_FONTS, "(pk) %s: %s\n", font->fontname, s)); #else fseek(in, (long)i, SEEK_CUR); #endif /* get the design size */ font->design = fuget4(p); /* get the checksum */ checksum = fuget4(p); if(checksum && font->checksum && font->checksum != checksum) { mdvi_warning(_("%s: checksum mismatch (expected %u, got %u)\n"), font->fontname, font->checksum, checksum); } else if(!font->checksum) font->checksum = checksum; /* skip pixel per point ratios */ fuget4(p); fuget4(p); if(feof(p)) goto badpk; /* now start reading the font */ loc = 256; hic = -1; maxch = 256; /* initialize alpha and beta for TFM width computation */ TFMPREPARE(font->scale, z, alpha, beta); while((flag_byte = fuget1(p)) != PK_POST) { if(feof(p)) break; if(flag_byte >= PK_CMD_START) { switch(flag_byte) { case PK_X1: case PK_X2: case PK_X3: case PK_X4: { #ifndef NODEBUG char *t; int n; i = fugetn(p, flag_byte - PK_X1 + 1); if(i < 256) t = &s[0]; else t = mdvi_malloc(i + 1); for(n = 0; n < i; n++) t[n] = fuget1(p); t[n] = 0; DEBUG((DBG_SPECIAL, "(pk) %s: Special \"%s\"\n", font->fontname, t)); if(t != &s[0]) mdvi_free(t); #else i = fugetn(p, flag_byte - PK_X1 + 1); while(i-- > 0) fuget1(p); #endif break; } case PK_Y: i = fuget4(p); DEBUG((DBG_SPECIAL, "(pk) %s: MF special %u\n", font->fontname, (unsigned)i)); break; case PK_POST: case PK_NOOP: break; case PK_PRE: mdvi_error(_("%s: unexpected preamble\n"), font->fontname); goto error; } } else { int pl; int cc; int w, h; int x, y; int offset; long tfm; switch(flag_byte & 0x7) { case 7: pl = fuget4(p); cc = fuget4(p); offset = ftell(p) + pl; tfm = fuget4(p); fsget4(p); /* skip dx */ fsget4(p); /* skip dy */ w = fuget4(p); h = fuget4(p); x = fsget4(p); y = fsget4(p); break; case 4: case 5: case 6: pl = (flag_byte % 4) * 65536 + fuget2(p); cc = fuget1(p); offset = ftell(p) + pl; tfm = fuget3(p); fsget2(p); /* skip dx */ /* dy assumed 0 */ w = fuget2(p); h = fuget2(p); x = fsget2(p); y = fsget2(p); break; default: pl = (flag_byte % 4) * 256 + fuget1(p); cc = fuget1(p); offset = ftell(p) + pl; tfm = fuget3(p); fsget1(p); /* skip dx */ /* dy assumed 0 */ w = fuget1(p); h = fuget1(p); x = fsget1(p); y = fsget1(p); } if(feof(p)) break; /* Although the PK format support bigger char codes, * XeTeX and other extended TeX engines support charcodes up to * 65536, while normal TeX engine supports only charcode up to 255.*/ if (cc < 0 || cc > 65536) { mdvi_error (_("%s: unexpected charcode (%d)\n"), font->fontname,cc); goto error; } if(cc < loc) loc = cc; if(cc > hic) hic = cc; if(cc > maxch) { font->chars = xresize(font->chars, DviFontChar, cc + 16); for(i = maxch; i < cc + 16; i++) font->chars[i].offset = 0; maxch = cc + 16; } font->chars[cc].code = cc; font->chars[cc].flags = flag_byte; font->chars[cc].offset = ftell(p); font->chars[cc].width = w; font->chars[cc].height = h; font->chars[cc].glyph.data = NULL; font->chars[cc].x = x; font->chars[cc].y = y; font->chars[cc].glyph.x = x; font->chars[cc].glyph.y = y; font->chars[cc].glyph.w = w; font->chars[cc].glyph.h = h; font->chars[cc].grey.data = NULL; font->chars[cc].shrunk.data = NULL; font->chars[cc].tfmwidth = TFMSCALE(z, tfm, alpha, beta); font->chars[cc].loaded = 0; fseek(p, (long)offset, SEEK_SET); } } if(flag_byte != PK_POST) { mdvi_error(_("%s: unexpected end of file (no postamble)\n"), font->fontname); goto error; } while((flag_byte = fuget1(p)) != EOF) { if(flag_byte != PK_NOOP) { mdvi_error(_("invalid PK file! (junk in postamble)\n")); goto error; } } /* resize font char data */ if(loc > 0 || hic < maxch-1) { memmove(font->chars, font->chars + loc, (hic - loc + 1) * sizeof(DviFontChar)); font->chars = xresize(font->chars, DviFontChar, hic - loc + 1); } font->loc = loc; font->hic = hic; return 0; badpk: mdvi_error(_("%s: File corrupted, or not a PK file\n"), font->fontname); error: mdvi_free(font->chars); font->chars = NULL; font->loc = font->hic = 0; return -1; } static int pk_font_get_glyph(DviParams *params, DviFont *font, int code) { DviFontChar *ch; if((ch = FONTCHAR(font, code)) == NULL) return -1; if(ch->offset == 0) return -1; DEBUG((DBG_GLYPHS, "(pk) loading glyph for character %d (%dx%d) in font `%s'\n", code, ch->width, ch->height, font->fontname)); if(font->in == NULL && font_reopen(font) < 0) return -1; if(!ch->width || !ch->height) { /* this happens for ` ' (ASCII 32) in some fonts */ ch->glyph.x = ch->x; ch->glyph.y = ch->y; ch->glyph.w = ch->width; ch->glyph.h = ch->height; ch->glyph.data = NULL; return 0; } if(fseek(font->in, ch->offset, SEEK_SET) == -1) return -1; ch->glyph.data = get_char(font->in, ch->width, ch->height, ch->flags); if(ch->glyph.data) { /* restore original settings */ ch->glyph.x = ch->x; ch->glyph.y = ch->y; ch->glyph.w = ch->width; ch->glyph.h = ch->height; } else return -1; ch->loaded = 1; return 0; }