/* * pklib.c * * This file is part of the ttf2pk package. * * Copyright 1997-1999, 2000 by * Frederic Loyer * Werner Lemberg * * Trivial declaration change in 2020 by * Karl Berry */ /* * This code has been derived from the program gsftopk. * Here the original copyright. */ /* * Copyright (c) 1994 Paul Vojta. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include /* for size_t */ #include #include #include #include "newobj.h" #include "pklib.h" #include "errormsg.h" #include "filesrch.h" #ifndef MAXPATHLEN #define MAXPATHLEN 256 #endif #define PK_PRE (char)247 #define PK_ID 89 #define PK_POST (char)245 #define PK_NOP (char)246 extern int dpi; /* defined in ftlib.c, avoiding -fno-common failure */ FILE *pk_file; /* * Information from the .tfm file. */ int tfm_lengths[12]; #define lh tfm_lengths[1] #define bc tfm_lengths[2] #define ec tfm_lengths[3] #define nw tfm_lengths[4] long checksum; long design; byte width_index[256]; long tfm_widths[256]; /* * Information on the bitmap currently being worked on. */ byte *bitmap; int width; int skip; int height; int hoff; int voff; int bytes_wide; size_t bm_size; byte *bitmap_end; int pk_len; /* * Here's the path searching stuff. First the typedefs and variables. */ static char searchpath[MAXPATHLEN + 1]; #define HUNKSIZE (MAXPATHLEN + 2) struct spacenode /* used for storage of directory names */ { struct spacenode *next; char *sp_end; /* end of data for this chunk */ char sp[HUNKSIZE]; } firstnode; static FILE * search_tfm(char **name) { char *p; FILE *f; p = TeX_search_tfm(name); if (p == NULL) return NULL; strcpy(searchpath, p); f = fopen(searchpath, "rb"); return f; } static long getlong(FILE *f) { unsigned long value; value = (unsigned long)getc(f) << 24; value |= (unsigned long)getc(f) << 16; value |= (unsigned long)getc(f) << 8; value |= (unsigned long)getc(f); return value; } char line[82]; static byte masks[] = {0, 1, 3, 7, 017, 037, 077, 0177, 0377}; byte flag; int pk_dyn_f; int pk_dyn_g; int base; /* cost of this character if pk_dyn_f = 0 */ int deltas[13]; /* cost of increasing pk_dyn_f from i to i+1 */ /* * Add up statistics for putting out the given shift count. */ static void tallyup(int n) { int m; if (n > 208) { ++base; n -= 192; for (m = 0x100; m != 0 && m < n; m <<= 4) base += 2; if (m != 0 && (m = (m - n) / 15) < 13) deltas[m] += 2; } else if (n > 13) ++deltas[(208 - n) / 15]; else --deltas[n - 1]; } /* * Routines for storing the shift counts. */ static Boolean odd = False; static byte part; static void pk_put_nyb(int n) { if (odd) { *bitmap_end++ = (part << 4) | n; odd = False; } else { part = n; odd = True; } } static void pk_put_long(int n) { if (n >= 16) { pk_put_nyb(0); pk_put_long(n / 16); } pk_put_nyb(n % 16); } static void pk_put_count(int n) { if (n > pk_dyn_f) { if (n > pk_dyn_g) pk_put_long(n - pk_dyn_g + 15); else { pk_put_nyb(pk_dyn_f + (n - pk_dyn_f + 15) / 16); pk_put_nyb((n - pk_dyn_f - 1) % 16); } } else pk_put_nyb(n); } static void trim_bitmap(void) { byte *p; byte mask; /* clear out garbage bits in bitmap */ if (width % 8 != 0) { mask = ~masks[8 - width % 8]; for (p = bitmap + bytes_wide - 1; p < bitmap_end; p += bytes_wide) *p &= mask; } /* Find the bounding box of the bitmap. */ /* trim top */ skip = 0; mask = 0; for (;;) { if (bitmap >= bitmap_end) /* if bitmap is empty */ { width = height = hoff = voff = 0; return; } p = bitmap + bytes_wide; while (p > bitmap) mask |= *--p; if (mask) break; ++skip; bitmap += bytes_wide; } height -= skip; voff -= skip; #ifdef DEBUG if (skip < 2 || skip > 3) printf("Character has %d empty rows at top\n", skip); #endif /* trim bottom */ skip = 0; mask = 0; for (;;) { p = bitmap_end - bytes_wide; while (p < bitmap_end) mask |= *p++; if (mask) break; ++skip; bitmap_end -= bytes_wide; } height -= skip; #ifdef DEBUG if (skip < 2 || skip > 3) printf("Character has %d empty rows at bottom\n", skip); #endif /* trim right */ skip = 0; --width; for (;;) { mask = 0; for (p = bitmap + width / 8; p < bitmap_end; p += bytes_wide) mask |= *p; if (mask & (0x80 >> (width % 8))) break; --width; ++skip; } ++width; #ifdef DEBUG if (skip < 2 || skip > 3) printf("Character has %d empty columns at right\n", skip); #endif /* trim left */ skip = 0; for (;;) { mask = 0; for (p = bitmap + skip / 8; p < bitmap_end; p += bytes_wide) mask |= *p; if (mask & (0x80 >> (skip % 8))) break; ++skip; } width -= skip; hoff -= skip; #ifdef DEBUG if (skip < 2 || skip > 3) printf("Character has %d empty columns at left\n", skip); #endif bitmap += skip / 8; skip = skip % 8; } /* * Pack the bitmap using the rll method. (Return false if it's better * to just pack the bits.) */ static Boolean pk_rll_cvt(void) { static int *counts = NULL; /* area for saving bit counts */ static int maxcounts = 0; /* size of this area */ unsigned int ncounts; /* max to allow this time */ int *nextcount; /* next count value */ int *counts_end; /* pointer to end */ byte *rowptr; byte *p; byte mask; byte *rowdup; /* last row checked for dup */ byte paint_switch; /* 0 or 0xff */ int bits_left; /* bits left in row */ int cost; int i; /* Allocate space for bit counts. */ ncounts = (width * height + 3) / 4; if (ncounts > maxcounts) { if (counts != NULL) free(counts); counts = (int *)mymalloc((ncounts + 2) * sizeof (int)); maxcounts = ncounts; } counts_end = counts + ncounts; /* Form bit counts and collect statistics */ base = 0; memset(deltas, 0, sizeof (deltas)); rowdup = NULL; /* last row checked for duplicates */ p = rowptr = bitmap; mask = 0x80 >> skip; flag = 0; paint_switch = 0; if (*p & mask) { flag = 8; paint_switch = 0xff; } bits_left = width; nextcount = counts; while (rowptr < bitmap_end) /* loop over shift counts */ { int shift_count = bits_left; for (;;) { if (bits_left == 0) { if ((p = rowptr += bytes_wide) >= bitmap_end) break; mask = 0x80 >> skip; bits_left = width; shift_count += width; } if (((*p ^ paint_switch) & mask) != 0) break; --bits_left; mask >>= 1; if (mask == 0) { ++p; while (*p == paint_switch && bits_left >= 8) { ++p; bits_left -= 8; } mask = 0x80; } } if (nextcount >= counts_end) return False; shift_count -= bits_left; *nextcount++ = shift_count; tallyup(shift_count); /* check for duplicate rows */ if (rowptr != rowdup && bits_left != width) { byte *p1 = rowptr; byte *q = rowptr + bytes_wide; int repeat_count; while (q < bitmap_end && *p1 == *q) { ++p1; ++q; } repeat_count = (p1 - rowptr) / bytes_wide; if (repeat_count > 0) { *nextcount++ = -repeat_count; if (repeat_count == 1) --base; else { ++base; tallyup(repeat_count); } rowptr += repeat_count * bytes_wide; } rowdup = rowptr; } paint_switch = ~paint_switch; } #ifdef DEBUG /* * Dump the bitmap */ for (p = bitmap; p < bitmap_end; p += bytes_wide) { byte *p1 = p; int j; mask = 0x80 >> skip; for (j = 0; j < width; ++j) { putchar(*p1 & mask ? '@' : '.'); if ((mask >>= 1) == 0) { mask = 0x80; ++p1; } } putchar('\n'); } putchar('\n'); #endif /* Determine the best pk_dyn_f */ pk_dyn_f = 0; cost = base += 2 * (nextcount - counts); for (i = 1; i < 14; ++i) { base += deltas[i - 1]; if (base < cost) { pk_dyn_f = i; cost = base; } } /* last chance to bail out */ if (cost * 4 > width * height) return False; /* Pack the bit counts */ pk_dyn_g = 208 - 15 * pk_dyn_f; flag |= pk_dyn_f << 4; bitmap_end = bitmap; *nextcount = 0; nextcount = counts; while (*nextcount != 0) { if (*nextcount > 0) pk_put_count(*nextcount); else if (*nextcount == -1) pk_put_nyb(15); else { pk_put_nyb(14); pk_put_count(-*nextcount); } ++nextcount; } if (odd) { pk_put_nyb(0); ++cost; } if (cost != 2 * (bitmap_end - bitmap)) printf("Cost miscalculation: expected %d, got %ld\n", cost, (long)(2 * (bitmap_end - bitmap))); pk_len = bitmap_end - bitmap; return True; } static void pk_bm_cvt(void) { byte *rowptr; byte *p; int blib1; /* bits left in byte */ int bits_left; /* bits left in row */ byte *q; int blib2; byte nextbyte; flag = 14 << 4; q = bitmap; blib2 = 8; nextbyte = 0; for (rowptr = bitmap; rowptr < bitmap_end; rowptr += bytes_wide) { p = rowptr; blib1 = 8 - skip; bits_left = width; if (blib2 != 8) { int n; if (blib1 < blib2) { nextbyte |= *p << (blib2 - blib1); n = blib1; } else { nextbyte |= *p >> (blib1 - blib2); n = blib2; } blib2 -= n; if ((bits_left -= n) < 0) { blib2 -= bits_left; continue; } if ((blib1 -= n) == 0) { blib1 = 8; ++p; if (blib2 > 0) { nextbyte |= *p >> (8 - blib2); blib1 -= blib2; bits_left -= blib2; if (bits_left < 0) { blib2 = -bits_left; continue; } } } *q++ = nextbyte; } /* fill up whole (destination) bytes */ while (bits_left >= 8) { nextbyte = *p++ << (8 - blib1); *q++ = nextbyte | (*p >> blib1); bits_left -= 8; } /* now do the remainder */ nextbyte = *p << (8 - blib1); if (bits_left > blib1) nextbyte |= p[1] >> blib1; blib2 = 8 - bits_left; } if (blib2 != 8) *q++ = nextbyte; pk_len = q - bitmap; } static void putshort(int w) { putc(w >> 8, pk_file); putc(w, pk_file); } static void putmed(long w) { putc(w >> 16, pk_file); putc(w >> 8, pk_file); putc(w, pk_file); } static void putlong(long w) { putc(w >> 24, pk_file); putc(w >> 16, pk_file); putc(w >> 8, pk_file); putc(w, pk_file); } static char xgetc(FILE *f) { int c; c = getc(f); if (c == EOF) oops("Premature end of file."); return (byte)c; } /* * Open and read the tfm file. */ void TFMopen(char **filename) { FILE *tfm_file; int i; int cc; tfm_file = search_tfm(filename); if (tfm_file == NULL) oops("Cannot find tfm file."); for (i = 0; i < 12; i++) { int j; j = (int)((byte)getc(tfm_file)) << 8; tfm_lengths[i] = j | (int)((byte)xgetc(tfm_file)); } checksum = getlong(tfm_file); design = getlong(tfm_file); fseek(tfm_file, 4 * (lh + 6), 0); for (cc = bc; cc <= ec; ++cc) { width_index[cc] = (byte)xgetc(tfm_file); (void)xgetc(tfm_file); (void)xgetc(tfm_file); (void)xgetc(tfm_file); } for (i = 0; i < nw; ++i) tfm_widths[i] = getlong(tfm_file); fclose(tfm_file); } /* * Create pk file and write preamble. */ void PKopen(char *filename, char *ident, int resolution) { int ppp; int i; dpi = resolution; if ((pk_file = fopen(filename, "wb")) == NULL) { perror(filename); exit(1); } putc(PK_PRE, pk_file); putc(PK_ID, pk_file); i = strlen(ident); putc(i, pk_file); fwrite(ident, 1, i, pk_file); putlong(design); putlong(checksum); ppp = dpi / 72.27 * 65536.0 + 0.5; putlong(ppp); /* hppp */ putlong(ppp); /* vppp */ } void PKputglyph(int cc, int llx, int lly, int urx, int ury, int w, int h, byte *b) { float char_width; long dm; long tfm_wid; bitmap = b; width = w; height = h; hoff = -llx; voff = ury - 2; /* Don't ask me why `-2' */ /* Fred */ if (width != urx - llx || height != ury - lly) oops("Dimensions do not match: (%d - %d) (%d - %d) <=> %d %d", llx, lly, urx, ury, width, height); bytes_wide = (width + 7) / 8; bm_size = bytes_wide * height; bitmap_end = bitmap + bm_size; trim_bitmap(); if (height == 0 || !pk_rll_cvt()) pk_bm_cvt(); if (!width_index[cc]) return; tfm_wid = tfm_widths[width_index[cc]]; char_width = tfm_wid / 1048576.0 * design / 1048576.0 * dpi / 72.27; dm = (long)(char_width + 0.5) - (char_width < -0.5); if (pk_len + 8 < 4 * 256 && tfm_wid < (1<<24) && dm >= 0 && dm < 256 && width < 256 && height < 256 && hoff >= -128 && hoff < 128 && voff >= -128 && voff < 128) { putc(flag | ((pk_len + 8) >> 8), pk_file); putc(pk_len + 8, pk_file); putc(cc, pk_file); putmed(tfm_wid); putc(dm, pk_file); putc(width, pk_file); putc(height, pk_file); putc(hoff, pk_file); putc(voff, pk_file); } else if (pk_len + 13 < 3 * 65536L && tfm_wid < (1<<24) && dm >= 0 && dm < 65536L && width < 65536L && height < 65536L && hoff >= -65536L && hoff < 65536L && voff >= -65536L && voff < 65536L) { putc(flag | 4 | ((pk_len + 13) >> 16), pk_file); putshort(pk_len + 13); putc(cc, pk_file); putmed(tfm_wid); putshort(dm); putshort(width); putshort(height); putshort(hoff); putshort(voff); } else { putc(flag | 7, pk_file); putlong(pk_len + 28); putlong(cc); putlong(tfm_wid); putlong((long)(char_width * 65536.0 + 0.5) - (char_width < -0.5)); putlong(0); putlong(width); putlong(height); putlong(hoff); putlong(voff); } fwrite(bitmap, 1, pk_len, pk_file); } void PKclose(void) { putc(PK_POST, pk_file); while (ftell(pk_file) % 4 != 0) putc(PK_NOP, pk_file); fclose(pk_file); } /* end */