Last active
September 2, 2022 16:57
-
-
Save mrmcsoftware/d3256f1dd7cf80b73f80753fc181c9a0 to your computer and use it in GitHub Desktop.
Amiga bitmap font reader/viewer for Windows and Linux (written in C). See the entire font and render an optional specified string.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/********************************************************* | |
Read and display Amiga fonts on Windows and Linux | |
Copyright © 2022 Mark Craig | |
https://www.youtube.com/MrMcSoftware | |
**********************************************************/ | |
/* NOTES: | |
* | |
* For Windows, if you need to include windows.h or wtypes.h, be careful about | |
* WORD typedef: it needs to be (signed) short and not what wtypes.h defines it | |
* as (unsigned short) - might want to replace all WORD with short and get rid | |
* of the typedef for WORD. | |
* | |
* Define BIGENDIAN and KERNINGAFTER as needed/desired. | |
* I don't exactly know how the Amiga deals with kerning. Doing it the way | |
* some people say to do it doesn't seem to produce the same results as the | |
* Amiga. As far as I can tell, my way is the closest to Amiga. | |
* In main(), define imgname to be the path and filename prefix for output | |
* images, and fbase for the path to the amiga fonts (or "." for the current | |
* directory). | |
* In displayimage(), if viewing images is desired: | |
* pv2 is an image viewer for Windows that I wrote, you'll have to change that | |
* to whatever viewer you have that supports PBM/PPM format. On the Linux | |
* side, either change the viewer or install gpicview and/or feh. | |
* | |
* If concatenation of image files into one image file is desired, you need to | |
* install netpbm. BTW, padfile.ppm is a 1x6 all black image. You can create | |
* your own pad file with whatever height (or width as well) you want or use | |
* the provided file. | |
* | |
* On Linux, compile with "gcc -o afont afont.c -lm" | |
* On Windows (assuming MS Visual Studio/Visual C), compile with "cl afont.c" | |
* at a command prompt (assuming you have set up your MS-DOS environment for | |
* MSVC). | |
* | |
* Examples: | |
* afont jacosub -e-3 -s"This is a test 123" -n -k1 -p -Apadfile.ppm | |
* afont times -e-3 -s"This is a test 123" -n -k1 -p -Apadfile.ppm -D | |
* afont topaz -e-3 -s"This is a test 123" -n -k1 -p -Apadfile.ppm -DA | |
* afont times -e2 -s"This is a test 123" -n -k1 -p -D | |
* afont times -e-3 -s"This is a test 123" -n -k1 -p -Apadfile.ppm -D -r | |
* afont times -e-3 -s"This is a test 123" -n -k1 -p -Apadfile.ppm -D -B -I15 | |
* afont times -e-3 -s"This is a test 123" -n -k1 -p -Apadfile.ppm -b. | |
* afont times -e-3 -a65 | |
* afont | |
* afont -h | |
* | |
* DISCLAIMER: Since this was just meant to be a simple program to see whether | |
* I could read/display Amiga font files, I didn't bother trying the pick the | |
* perfect variable and procedure names, and didn't bother with various | |
* software engineering practices, etc. This is not meant to be an example of | |
* my best work, it's more of a proof of concept. The concepts in this program | |
* were used in the project I was really doing this for. | |
* | |
* BTW, there were various websites/resources I used that were helpful in | |
* figuring out Amiga fonts. If requested, I'll provide a list. | |
* | |
*/ | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <math.h> | |
#define EXT_NONE 0 | |
#define EXT_BIGGEST -1 | |
#define EXT_LAST -2 | |
#define EXT_ALL -3 | |
typedef unsigned short UWORD; | |
typedef short WORD; /* be careful in Windows */ | |
typedef unsigned char UBYTE; | |
typedef long LONG; | |
typedef unsigned long ULONG; | |
typedef unsigned char *STRPTR; | |
typedef void *APTR; | |
typedef struct Rgb | |
{ | |
int r, g, b; | |
} RGB; | |
RGB icols[32] = {{0,0,0},{255,255,255},{255,0,0},{255,255,0},{0,0,0},{0,255,255},{0,0,255},{255,0,255},{128,128,128},{128,0,0},{128,128,0},{0,128,0},{0,128,128},{0,0,128},{128,0,128},{0,154,255},{255,128,0},{128,255,0},{128,0,255},{255,0,128},{0,255,128}}, cols[32]; | |
char colsa[32] = " *+-#$%&@!/,.|<>"; /* for ASCII art character display */ | |
/* Flags, Styles, Tags, and structs from Amiga include files, some not needed */ | |
#define MAXFONTPATH 256 | |
/*------ Font Styles ------------------------------------------------*/ | |
#define FS_NORMAL 0 /* normal text (no style bits set) */ | |
#define FSB_EXTENDED 3 /* extended face (wider than normal) */ | |
#define FSF_EXTENDED (1<<3) | |
#define FSB_ITALIC 2 /* italic (slanted 1:2 right) */ | |
#define FSF_ITALIC (1<<2) | |
#define FSB_BOLD 1 /* bold face text (ORed w/ shifted) */ | |
#define FSF_BOLD (1<<1) | |
#define FSB_UNDERLINED 0 /* underlined (under baseline) */ | |
#define FSF_UNDERLINED (1<<0) | |
#define FSB_COLORFONT 6 /* this uses ColorTextFont structure */ | |
#define FSF_COLORFONT 0x40 | |
#define FSB_TAGGED 7 /* the TextAttr is really an TTextAttr, */ | |
#define FSF_TAGGED 0x80 | |
/*------ Font Flags -------------------------------------------------*/ | |
#define FPB_ROMFONT 0 /* font is in rom */ | |
#define FPF_ROMFONT (1<<0) | |
#define FPB_DISKFONT 1 /* font is from diskfont.library */ | |
#define FPF_DISKFONT (1<<1) | |
#define FPB_REVPATH 2 /* designed path is reversed (e.g. left) */ | |
#define FPF_REVPATH (1<<2) | |
#define FPB_TALLDOT 3 /* designed for hires non-interlaced */ | |
#define FPF_TALLDOT (1<<3) | |
#define FPB_WIDEDOT 4 /* designed for lores interlaced */ | |
#define FPF_WIDEDOT (1<<4) | |
#define FPB_PROPORTIONAL 5 /* character sizes can vary from nominal */ | |
#define FPF_PROPORTIONAL (1<<5) | |
#define FPB_DESIGNED 6 /* size is "designed", not constructed */ | |
#define FPF_DESIGNED (1<<6) | |
#define FPB_REMOVED 7 /* the font has been removed */ | |
#define FPF_REMOVED (1<<7) | |
struct TagItem | |
{ | |
ULONG ti_Tag; | |
ULONG ti_Data; | |
}; | |
/* constants for Tag.ti_Tag, control tag values */ | |
#define TAG_DONE (0L) /* terminates array of TagItems. ti_Data unused */ | |
#define TAG_END (0L) /* synonym for TAG_DONE */ | |
#define TAG_IGNORE (1L) /* ignore this item, not end of array */ | |
#define TAG_MORE (2L) /* ti_Data is pointer to another array of TagItems | |
* note that this tag terminates the current array | |
*/ | |
#define TAG_SKIP (3L) /* skip this and the next ti_Data items */ | |
/* differentiates user tags from control tags */ | |
#define TAG_USER ((ULONG)(1L<<31)) | |
/* If the TAG_USER bit is set in a tag number, it tells utility.library that | |
* the tag is not a control tag (like TAG_DONE, TAG_IGNORE, TAG_MORE) and is | |
* instead an application tag. "USER" means a client of utility.library in | |
* general, including system code like Intuition or ASL, it has nothing to do | |
* with user code. | |
*/ | |
/****** Text Tags ***************************************************/ | |
#define TA_DeviceDPI (1|TAG_USER) /* Tag value is Point union: */ | |
/* Hi word XDPI, Lo word YDPI */ | |
struct FontContents | |
{ | |
char fc_FileName[MAXFONTPATH]; | |
UWORD fc_YSize; | |
UBYTE fc_Style; | |
UBYTE fc_Flags; | |
}; | |
struct TFontContents | |
{ | |
char tfc_FileName[MAXFONTPATH-2]; | |
UWORD tfc_TagCount; /* including the TAG_END tag */ | |
/* | |
* if tfc_TagCount is non-zero, tfc_FileName is overlaid with | |
* Text Tags starting at: (struct TagItem *) | |
* &tfc_FileName[MAXFONTPATH-(tfc_TagCount*sizeof(struct TagItem))] | |
*/ | |
UWORD tfc_YSize; | |
UBYTE tfc_Style; | |
UBYTE tfc_Flags; | |
}; | |
struct FontContentsHeader | |
{ | |
UWORD fch_FileID; /* FCH_ID */ | |
UWORD fch_NumEntries; /* the number of FontContents elements */ | |
struct FontContents *fch_FC; /* or struct TFontContents fch_TFC[]; */ | |
}; | |
#define MAXFONTNAME 32 /* font name including ".font\0" */ | |
/* I didn't bother recreating Node and Message */ | |
struct Node | |
{ | |
// don't care what this is | |
int dummy; // to make MSVC happy | |
}; | |
struct Message | |
{ | |
// don't care what this is | |
int dummy; // to make MSVC happy | |
}; | |
struct TextFont | |
{ | |
struct Message tf_Message; /* reply message for font removal */ | |
/* font name in LN \ used in this */ | |
UWORD tf_YSize; /* font height | order to best */ | |
UBYTE tf_Style; /* font style | match a font */ | |
UBYTE tf_Flags; /* preferences and flags / request. */ | |
UWORD tf_XSize; /* nominal font width */ | |
UWORD tf_Baseline; /* distance from the top of char to baseline */ | |
UWORD tf_BoldSmear; /* smear to affect a bold enhancement */ | |
UWORD tf_Accessors; /* access count */ | |
UBYTE tf_LoChar; /* the first character described here */ | |
UBYTE tf_HiChar; /* the last character described here */ | |
APTR tf_CharData; /* the bit character data */ | |
UWORD tf_Modulo; /* the row modulo for the strike font data */ | |
APTR tf_CharLoc; /* ptr to location data for the strike font */ | |
/* 2 words: bit offset then size */ | |
APTR tf_CharSpace; /* ptr to words of proportional spacing data */ | |
APTR tf_CharKern; /* ptr to words of kerning data */ | |
}; | |
/*----- ctf_Flags --------------------------------------------------*/ | |
#define CT_COLORMASK 0x000F /* mask to get to following color styles */ | |
#define CT_COLORFONT 0x0001 /* color map contains designer's colors */ | |
#define CT_GREYFONT 0x0002 /* color map describes even-stepped */ | |
/* brightnesses from low to high */ | |
#define CT_ANTIALIAS 0x0004 /* zero background thru fully saturated char */ | |
#define CTB_MAPCOLOR 0 /* map ctf_FgColor to the rp_FgPen if it's */ | |
#define CTF_MAPCOLOR 0x0001 /* is a valid color within ctf_Low..ctf_High */ | |
struct ColorFontColors | |
{ | |
UWORD cfc_Reserved; /* *must* be zero */ | |
UWORD cfc_Count; /* number of entries in cfc_ColorTable */ | |
UWORD *cfc_ColorTable;/* 4 bit per component color map packed xRGB */ | |
}; | |
struct ColorTextFont | |
{ | |
struct TextFont ctf_TF; | |
UWORD ctf_Flags; /* extended flags */ | |
UBYTE ctf_Depth; /* number of bit planes */ | |
UBYTE ctf_FgColor; /* color that is remapped to FgPen */ | |
UBYTE ctf_Low; /* lowest color represented here */ | |
UBYTE ctf_High; /* highest color represented here */ | |
UBYTE ctf_PlanePick; /* PlanePick ala Images */ | |
UBYTE ctf_PlaneOnOff; /* PlaneOnOff ala Images */ | |
struct ColorFontColors *ctf_ColorFontColors; /* colors for font */ | |
APTR ctf_CharData[8];/* pointers to bit planes ala tf_CharData */ | |
}; | |
struct DiskFontHeader | |
{ | |
/* the following 8 bytes are not actually considered a part of the */ | |
/* DiskFontHeader, but immediately precede it. The NextSegment is */ | |
/* supplied by the linker/loader, and the ReturnCode is the code */ | |
/* at the beginning of the font in case someone runs it... */ | |
/* ULONG dfh_NextSegment; actually a BPTR */ | |
/* ULONG dfh_ReturnCode; MOVEQ #0,D0 : RTS */ | |
/* here then is the official start of the DiskFontHeader... */ | |
struct Node dfh_DF; /* node to link disk fonts */ | |
UWORD dfh_FileID; /* DFH_ID */ | |
UWORD dfh_Revision; /* the font revision */ | |
LONG dfh_Segment; /* the segment address when loaded */ | |
char dfh_Name[MAXFONTNAME]; /* the font name (null terminated) */ | |
struct TextFont dfh_TF; /* loaded TextFont structure */ | |
}; | |
/* unfortunately, this needs to be explicitly typed */ | |
/* used only if dfh_TF.tf_Style FSB_TAGGED bit is set */ | |
#define dfh_TagList dfh_Segment /* destroyed during loading */ | |
/* End of Amiga include file stuff */ | |
#ifndef M_PI | |
#define M_PI 3.14159265358979323846 | |
#endif | |
/* If globals offend you, don't look :-) */ | |
int ext = 0, verb = 0, asc = 0, img = 0, vpic = 0, rev = 0, altv = 0; | |
int maxsize = -1, bitplane = -1, usecol = 1, backgrnd = 0, kernm = 0; | |
int alltoget = 0, bold = 0, centsp = 1, addfontn = 0, deleteimgs = 0; | |
char imgname[256], fbase[256], text[256], padfile[256], exists[256]; | |
struct FontContentsHeader fh; | |
RGB bgcolor, fgcolor; | |
float italic = 0.0; | |
/* Adjust the endianness, Amiga (68000 series CPU) is big-endian, x86 series | |
is little-endian. If your CPU is big-endian, define BIGENDIAN */ | |
UWORD readushort(FILE *fp) | |
{ | |
int c1, c2; | |
UWORD v; | |
c1 = fgetc(fp); c2 = fgetc(fp); | |
#ifdef BIGENDIAN | |
v = (c2 << 8) | c1; | |
#else | |
v = (c1 << 8) | c2; | |
#endif | |
return(v); | |
} | |
ULONG readulong(FILE *fp) | |
{ | |
ULONG c1, c2, c3, c4, v; | |
c1 = fgetc(fp); c2 = fgetc(fp); c3 = fgetc(fp); c4 = fgetc(fp); | |
#ifdef BIGENDIAN | |
v = (c4 << 24) | (c3 << 16) | (c2 << 8) | c1; | |
#else | |
v = (c1 << 24) | (c2 << 16) | (c3 << 8) | c4; | |
#endif | |
return(v); | |
} | |
UWORD reverseushort(UWORD v) | |
{ | |
UWORD v2; | |
#ifdef BIGENDIAN | |
return(v); | |
#endif | |
v2 = ((v & 0xff) << 8) | (v >> 8); | |
return(v2); | |
} | |
ULONG reverseulong(ULONG v) | |
{ | |
UBYTE *cv = (UBYTE *)&v; | |
UBYTE ov[4]; | |
ULONG vr; | |
#ifdef BIGENDIAN | |
return(v); | |
#endif | |
ov[3] = cv[0]; ov[2] = cv[1]; ov[1] = cv[2]; ov[0] = cv[3]; | |
vr = *(ULONG *)ov; | |
// or: | |
//vr = ((ULONG)(cv[0]) << 24) | ((ULONG)(cv[1]) << 16) | ((ULONG)(cv[2]) << 8) | ((ULONG)(cv[3])); | |
// or: | |
//vr = ((v & 0xff) << 24) | (((v >> 8) & 0xff) << 16) | (((v >> 16) & 0xff) << 8) | (v >> 24); | |
return(vr); | |
} | |
printstyles(UBYTE f) | |
{ | |
if (f == FS_NORMAL) { printf("NORMAL "); } | |
if (f & FSF_EXTENDED) { printf("EXTENDED "); } | |
if (f & FSF_ITALIC) { printf("ITALIC "); } | |
if (f & FSF_BOLD) { printf("BOLD "); } | |
if (f & FSF_UNDERLINED) { printf("UNDERLINED "); } | |
if (f & FSF_COLORFONT) { printf("COLORFONT "); } | |
if (f & FSF_TAGGED) { printf("TAGGED "); } | |
printf("\n"); | |
} | |
printflags(UBYTE f) | |
{ | |
if (f & FPF_ROMFONT) { printf("ROMFONT "); } | |
if (f & FPF_DISKFONT) { printf("DISKFONT "); } | |
if (f & FPF_REVPATH) { printf("REVPATH "); } | |
if (f & FPF_TALLDOT) { printf("TALLDOT "); } | |
if (f & FPF_WIDEDOT) { printf("WIDEDOT "); } | |
if (f & FPF_PROPORTIONAL) { printf("PROPORTIONAL "); } | |
if (f & FPF_DESIGNED) { printf("DESIGNED "); } | |
if (f & FPF_REMOVED) { printf("REMOVED "); } | |
printf("\n"); | |
} | |
displayimage(char *fname) | |
{ | |
char str[256]; | |
/* You may want to add your own image viewing routines instead of calling | |
external viewers. BTW, pv2 is an image viewer I wrote. */ | |
#ifdef _MSC_VER | |
if (vpic) { sprintf(str, "pv2 %s", fname); system(str); } | |
#else | |
if (vpic) { if (altv) { sprintf(str, "gpicview %s", fname); } else { sprintf(str, "feh -B black %s", fname); } if (backgrnd) { strcat(str, " &"); } system(str); } | |
#endif | |
} | |
usage() | |
{ | |
printf("Usage: afont [{fontname}] [-b{fontdir}] [-e{#}] [-a{#}] [-v] [-i[{filename}]]\n\ | |
[-p[2]] [-r] [-P{#}] [-c] [-V] [-s{string}] [-k[{#}]]\n\ | |
[-C{#},{#},{#}] [-F{#},{#},{#}] [-I{#}] [-B] [-A[{padfile}]] [-S]\n\ | |
[-n] [-D[A]] [-h]\n"); | |
printf(" -b - Specify fonts directory if not default (. for current dir)\n"); | |
printf(" -e - Extract font (# of font, -1=biggest, -2=last, -3=all)\n"); | |
printf(" -a - Show ASCII representation of specified character\n"); | |
printf(" -v - Verbose output\n"); | |
printf(" -i - Create output image (optionally specify filename prefix)\n"); | |
printf(" -p - Display image (if with 2, use alternate viewer (linux only))\n"); | |
printf(" -r - Reverse bit logic (reverse video)\n"); | |
printf(" -P - Only display specified bitplane (colored fonts)\n"); | |
printf(" -c - Don't use colors in font (colored fonts)\n"); | |
printf(" -V - Background the viewer (linux only)\n"); | |
printf(" -s - Output specified string\n"); | |
printf(" -k - Use kerning (# is multiplier (default 1)) (use with -s)\n"); | |
printf(" -C - Specify background color\n"); | |
printf(" -F - Specify foreground color\n"); | |
printf(" -I - Create faux italics (# is angle, try 10) (only with -s)\n"); | |
printf(" -B - Create bold text (only with -s)\n"); | |
printf(" -S - For non-proportional fonts, don't center each char. (with -s)\n"); | |
printf(" -A - Concatenate all font size image files into one\n"); | |
printf(" -n - Add fontname to -s string\n"); | |
printf(" -D - Delete image files after viewing ('A' to also delete concat.)\n"); | |
printf(" -h - Help\n"); | |
exit(1); | |
} | |
main(int argc, char **argv) | |
{ | |
FILE *fp; | |
int i, j, k, tc; | |
char str[32768], str2[256], font[256]; /* "str" size is probably overkill */ | |
struct TFontContents *tfc; | |
struct TagItem *ti; | |
strcpy(font, "times"); /* default to times font */ | |
text[0] = padfile[0] = '\0'; | |
bgcolor.r = fgcolor.r = -1; | |
/* Adjust paths and prefix as desired */ | |
#ifdef _MSC_VER | |
strcpy(imgname, "c:\\temp\\outaf"); | |
strcpy(fbase, getenv("PROGRAMFILES")); | |
strcat(fbase, "\\winuae\\drive\\Fonts"); | |
#else | |
strcpy(imgname, "/tmp/outaf"); | |
strcpy(fbase, "/dos/Progra~1/winuae/drive/Fonts"); | |
#endif | |
for (i = 1; i < argc ; i++) | |
{ | |
if (argv[i][0] == '-') | |
{ | |
switch (argv[i][1]) | |
{ | |
case 'e': sscanf(argv[i]+2, "%d", &ext); break; | |
case 'b': strcpy(fbase, argv[i] + 2); break; | |
case 'v': verb = 1; break; | |
case 'a': sscanf(argv[i] + 2, "%d", &asc); break; | |
case 'i': img = 1; | |
if (argv[i][2] != '\0') { strcpy(imgname, argv[i] + 2); } | |
break; | |
case 'p': vpic = img = 1; if (argv[i][2] == '2') { altv = 1; } break; | |
case 'r': rev = 1; break; | |
case 'P': sscanf(argv[i]+2, "%d", &bitplane); break; | |
case 'c': usecol = 0; break; | |
case 'V': backgrnd = 1; break; | |
case 's': strcpy(text, argv[i] + 2); break; | |
case 'k': kernm = 1; if (argv[i][2] != '\0') { sscanf(argv[i] + 2, "%d", &kernm); } break; | |
case 'C': sscanf(argv[i] + 2, "%d,%d,%d", &bgcolor.r, &bgcolor.g, &bgcolor.b); break; | |
case 'F': sscanf(argv[i] + 2, "%d,%d,%d", &fgcolor.r, &fgcolor.g, &fgcolor.b); break; | |
case 'I': sscanf(argv[i] + 2, "%f", &italic); break; | |
case 'B': bold = 1; break; | |
case 'A': alltoget = 1; if (argv[i][2] != '\0') { strcpy(padfile, argv[i] + 2); } break; | |
case 'S': centsp = 0; break; | |
case 'n': addfontn = 1; break; | |
case 'D': deleteimgs = 1; if (argv[i][2] == 'A') { deleteimgs = 2; } break; | |
case 'h': | |
default: usage(); break; | |
} | |
} | |
else { strcpy(font, argv[i]); } | |
} | |
sprintf(str, "%s/%s.font", fbase, font); | |
if ((fp = fopen(str, "rb")) == NULL) | |
{ | |
printf("Can't open '%s.font'\n", font); | |
exit(1); | |
} | |
fh.fch_FileID = readushort(fp); | |
printf("%s:\n FileID: %d (%x)\n", font, fh.fch_FileID, fh.fch_FileID); | |
fh.fch_NumEntries = readushort(fp); | |
printf("Num Entries: %d\n", fh.fch_NumEntries); | |
fh.fch_FC = (struct FontContents *)(calloc(fh.fch_NumEntries, sizeof(struct FontContents))); | |
fread(fh.fch_FC, sizeof(struct FontContents), fh.fch_NumEntries, fp); | |
if (fh.fch_FileID == 0x0f02) | |
{ | |
tfc = (struct TFontContents *)(fh.fch_FC); | |
tc = reverseushort(tfc->tfc_TagCount); | |
printf("Num Tags: %d\n", tc); | |
if (tc != 0) | |
{ | |
ULONG tag, data; | |
ti = (struct TagItem *)(tfc->tfc_FileName + MAXFONTPATH - (tc * sizeof(struct TagItem))); | |
for (i = 0; i < tc; i++) | |
{ | |
tag = reverseulong(ti[i].ti_Tag); | |
data = reverseulong(ti[i].ti_Data); | |
printf(" Tag %d: ID: %08lx Data: %08lx", i, tag, data); | |
switch (tag) | |
{ | |
case TA_DeviceDPI: printf(" Device DPI: %d %d\n", (UWORD)(data >> 16), (UWORD)(data & 0xffff)); break; | |
case TAG_END: printf(" TAG_END\n"); break; | |
default: printf("\n"); | |
} | |
} | |
} | |
} | |
if (ext == EXT_LAST) { ext = fh.fch_NumEntries; } | |
else if (ext == EXT_BIGGEST) | |
{ | |
k = 0; | |
for (i = 0; i < fh.fch_NumEntries; i++) | |
{ | |
j = reverseushort(fh.fch_FC[i].fc_YSize); | |
if (j > maxsize) { maxsize = j; k = i; } | |
} | |
ext = k + 1; | |
} | |
for (i = 0; i < fh.fch_NumEntries; i++) | |
{ | |
fh.fch_FC[i].fc_YSize = reverseushort(fh.fch_FC[i].fc_YSize); | |
if ((ext != EXT_NONE) && (ext != EXT_ALL) && (i != ext - 1)) { continue; } | |
switch (fh.fch_FileID) | |
{ | |
case 0x0f00: | |
printf("Font %d: %s YSize: %d\n", i+1, fh.fch_FC[i].fc_FileName, fh.fch_FC[i].fc_YSize); | |
printf(" Style: %x: ", fh.fch_FC[i].fc_Style); | |
printstyles(fh.fch_FC[i].fc_Style); | |
printf(" Flags: %x: ", fh.fch_FC[i].fc_Flags); | |
printflags(fh.fch_FC[i].fc_Flags); | |
break; | |
case 0x0f02: // currently, the same as above, but may add Tagged info | |
printf("Font %d: %s YSize: %d\n", i+1, fh.fch_FC[i].fc_FileName, fh.fch_FC[i].fc_YSize); | |
printf(" Style: %x: ", fh.fch_FC[i].fc_Style); | |
printstyles(fh.fch_FC[i].fc_Style); | |
printf(" Flags: %x: ", fh.fch_FC[i].fc_Flags); | |
printflags(fh.fch_FC[i].fc_Flags); | |
break; | |
case 0x0f03: | |
printf("Scalable, don't know what to do\n"); | |
break; | |
} | |
} | |
fclose(fp); | |
if (!ext) { exit(1); } | |
printf("------------------------------\n"); | |
if (ext == EXT_ALL) | |
{ | |
for (i = 1; i <= fh.fch_NumEntries ; i++) { readfontfile(i); printf("------------------------------\n"); } | |
} | |
else { readfontfile(ext); } | |
if (fh.fch_FC) { free(fh.fch_FC); } | |
if (alltoget) | |
{ | |
sprintf(str, "pnmcat -tb -black -jleft %s ", padfile); | |
if (ext == EXT_ALL) | |
{ | |
for (i = 1; i <= fh.fch_NumEntries ; i++) { if (exists[i]) { sprintf(str2, "%s%d.ppm %s ", imgname, i, padfile); strcat(str, str2); } } | |
if (text[0] != '\0') { for (i = 1; i <= fh.fch_NumEntries ; i++) { if (exists[i]) { sprintf(str2, "%ss%d.ppm %s ", imgname, i, padfile); strcat(str, str2); } } } | |
} | |
else if (exists[ext]) | |
{ | |
sprintf(str2, "%s%d.ppm %s ", imgname, ext, padfile); strcat(str, str2); | |
if (text[0] != '\0') { sprintf(str2, "%ss%d.ppm %s ", imgname, ext, padfile); strcat(str, str2); } | |
} | |
sprintf(str2, "> %sall.ppm", imgname); strcat(str, str2); | |
system(str); | |
sprintf(str2, "%sall.ppm", imgname); | |
displayimage(str2); | |
} | |
if (deleteimgs) | |
{ | |
if (ext == EXT_ALL) | |
{ | |
for (i = 1; i <= fh.fch_NumEntries; i++) | |
{ | |
if (exists[i]) | |
{ | |
sprintf(str2, "%s%d.ppm", imgname, i); remove(str2); | |
sprintf(str2, "%ss%d.ppm", imgname, i); remove(str2); | |
} | |
} | |
} | |
else if (exists[ext]) { sprintf(str2, "%s%d.ppm", imgname, ext); remove(str2); } | |
if (deleteimgs == 2) { sprintf(str2, "%sall.ppm", imgname); remove(str2); } | |
} | |
} | |
readfontfile(int ext) | |
{ | |
/* Sorry about the static arrays, I dynamically allocate these variables in | |
my real project. This is just a simple test program (proof of concept, so | |
to speak). | |
If it helps, here's something similar to what I had in my real project: | |
typedef struct AFont | |
{ | |
RGB cols[32]; | |
UWORD ySize, xSize, baseline, boldSmear, modulo, prop, cflags, numcolors; | |
UWORD *location, *bitlength; | |
WORD *spac, *kern; | |
UBYTE style, flags, loChar, hiChar, depth, FgColor, clow, chigh; | |
UBYTE *fontdata; | |
} AFONT; | |
AFONT afonts[32]; | |
And I reference it this way to make it easier: | |
AFONT *f = &afonts[n]; | |
And, for example: | |
f->flags ... | |
BTW, for location, bitlength, spac, and kern, allocate memory (calloc) based | |
on numchars and take out the memsets. | |
*/ | |
UWORD ySize, xSize, baseline, boldSmear, modulo, numchars, prop, location[300]; | |
UWORD bitlength[300], cflags, numcolors, colors[300]; | |
WORD spac[300], kern[300]; | |
UBYTE style, flags, loChar, hiChar, depth, FgColor, clow, chigh, planepick; | |
UBYTE planeonoff; | |
ULONG fontDataStart, locationDataStart, spacingDataStart, kerningDataStart; | |
UBYTE *fontdata; | |
char str[256], str2[256], *s; | |
int i, j, k, c, pos, ind, a, poff, v, p, p0, p1; | |
float xshift; | |
FILE *fp; | |
sprintf(str, "%s/%s", fbase, fh.fch_FC[ext-1].fc_FileName); | |
if ((fp = fopen(str, "rb")) == NULL) | |
{ | |
printf("Can't open '%s'\n", str); exists[ext] = 0; | |
return(0); | |
} | |
exists[ext] = 1; | |
for (i = 0; i < 32; i++) { cols[i] = icols[i]; } | |
prop = 0; depth = 1; xshift = 0.0; | |
fseek(fp, 32+26, SEEK_CUR); | |
fread(str, 1, 32, fp); | |
str[32] = '\0'; | |
if (str[0] == '\0') { s = str + 1; } else { s = str; } | |
printf("Name: '%s'\n", s); | |
fseek(fp, 20, SEEK_CUR); | |
ySize = readushort(fp); printf("ySize=%d ", ySize); | |
style = fgetc(fp); printf("style=%x ", style); | |
flags = fgetc(fp); printf("flags=%x\n", flags); | |
xSize = readushort(fp); printf("xSize=%d ", xSize); | |
baseline = readushort(fp); printf("baseline=%d ", baseline); | |
boldSmear = readushort(fp); printf("boldSmear=%d\n", boldSmear); | |
readushort(fp); | |
loChar = fgetc(fp); printf("loChar=%d ", loChar); | |
hiChar = fgetc(fp); printf("hiChar=%d ", hiChar); | |
numchars = hiChar - loChar + 2; | |
fontDataStart = readulong(fp) + 32; printf("fontDataStart=%lu ", fontDataStart); | |
modulo = readushort(fp); printf("modulo=%d\n", modulo); | |
locationDataStart = readulong(fp) + 32; printf("locationDataStart=%lu\n", locationDataStart); | |
if (flags & FPF_PROPORTIONAL) | |
{ | |
spacingDataStart = readulong(fp) + 32; printf("spacingDataStart=%lu ", spacingDataStart); | |
kerningDataStart = readulong(fp) + 32; printf("kerningDataStart=%lu\n", kerningDataStart); | |
prop = 1; | |
} | |
if (fh.fch_FC[ext-1].fc_Style & FSF_COLORFONT) | |
{ | |
cflags = readushort(fp); printf(" cflags=%x ", cflags); | |
if (cflags & CT_COLORFONT) { printf("COLOR\n"); } | |
if (cflags & CT_GREYFONT) { printf("GREY\n"); } | |
if (cflags & CT_ANTIALIAS) { printf("ANTIALIAS\n"); } | |
depth = fgetc(fp); printf(" depth=%d\n", depth); | |
FgColor = fgetc(fp); printf(" FgColor=%d\n", FgColor); | |
clow = fgetc(fp); printf(" clow=%d\n", clow); | |
chigh = fgetc(fp); printf(" chigh=%d\n", chigh); | |
planepick = fgetc(fp); printf(" planepick=%d\n", planepick); | |
planeonoff = fgetc(fp); printf(" planeonoff=%d\n", planeonoff); | |
//numcolors = chigh - clow + 1; | |
fseek(fp, 38, SEEK_CUR); // Don't know what this is, but need to advance | |
//printf("ftell=%d\n", ftell(fp)); | |
numcolors = readushort(fp); printf(" numcolors=%d\n", numcolors); | |
fseek(fp, 4, SEEK_CUR); // Don't know what this is, but need to advance | |
for (i = 0; i < numcolors; i++) | |
{ | |
colors[i] = readushort(fp); | |
printf(" Color%d: %03x\n", i, colors[i]); | |
} | |
if (usecol) | |
{ | |
for (i = 0; i < numcolors; i++) | |
{ | |
cols[i].b = (colors[i] & 0xf) * 17; | |
cols[i].g = ((colors[i] >> 4) & 0xf) * 17; | |
cols[i].r = ((colors[i] >> 8) & 0xf) * 17; | |
} | |
} | |
if ((fgcolor.r > -1) && (FgColor != 255)) { cols[FgColor] = fgcolor; } | |
} | |
else if (fgcolor.r > -1) { cols[1] = fgcolor; } | |
if (bgcolor.r > -1) { cols[0] = bgcolor; } | |
fseek(fp, locationDataStart, SEEK_SET); | |
memset(bitlength, 0, 258 * 2); | |
for (i = 0; i < numchars; i++) | |
{ | |
location[i] = readushort(fp); bitlength[i] = readushort(fp); | |
if (verb) { printf("Loc%d=%d bitlen=%d %c\n", i, location[i], bitlength[i], i+loChar); } | |
} | |
fontdata = (UBYTE *)malloc(modulo * ySize * depth); | |
fseek(fp, fontDataStart, SEEK_SET); | |
fread(fontdata, 1, modulo * ySize * depth, fp); | |
if (prop) | |
{ | |
memset(spac, 0, 258 * 2); | |
memset(kern, 0, 258 * 2); | |
fseek(fp, spacingDataStart, SEEK_SET); | |
for (i = 0; i < numchars; i++) { spac[i] = (WORD)readushort(fp); if (verb) { printf("spac%d=%d %c\n", i, spac[i], i+loChar); } } | |
fseek(fp, kerningDataStart, SEEK_SET); | |
for (i = 0; i < numchars; i++) { kern[i] = (WORD)readushort(fp); if (verb) { printf("kern%d=%d %c\n", i, kern[i], i+loChar); } } | |
} | |
if (bitplane != -1) { poff = bitplane * modulo * ySize; } | |
else { poff = 0; } | |
if (bitplane != -1) { p0 = bitplane; p1 = p0 + 1; } | |
else { p0 = 0; p1 = depth; } | |
if (asc) | |
{ | |
printf("%c:", asc); | |
a = asc - loChar; | |
printf(" bitlen=%d\n", bitlength[a]); | |
for (j = 0; j < ySize; j++) | |
{ | |
for (i = 0; i < bitlength[a]; i++) | |
{ | |
k = location[a] + i + j * modulo * 8; | |
pos = k / 8; ind = 7 - (k - pos * 8); | |
v = 0; | |
for (p = p0; p < p1; p++) | |
{ | |
if (fontdata[pos + p * modulo * ySize] & (1 << ind)) { c = 1; } else { c = 0; } | |
if (rev) { c = 1 - c; } | |
if (c) { if (bitplane == -1) { v += (1 << p); } else { v = 1; } } | |
} | |
printf("%c", colsa[v]); | |
} | |
printf("\n"); | |
} | |
} | |
fclose(fp); | |
if (italic > 0.0) { xshift = (float)(ySize) * tan(italic * M_PI / 180.0); } | |
/* There is a different way to implement algorithmic italics - Hint: look at the | |
JACOsub project webpage */ | |
if (img) | |
{ | |
sprintf(str2, "%s%d.ppm", imgname, ext); | |
fp = fopen(str2, "wb"); | |
fprintf(fp, "P6\n%d %d\n255\n", modulo * 8, ySize); | |
for (j = 0; j < ySize; j++) | |
{ | |
for (i = 0; i < modulo; i++) | |
{ | |
ind = j * modulo + i; | |
for (k = 0; k < 8; k++) | |
{ | |
v = 0; | |
for (p = p0; p < p1; p++) | |
{ | |
if (fontdata[ind + modulo * ySize * p] & (1 << (7 - k))) { c = 1; } else { c = 0; } | |
if (rev) { c = 1 - c; } | |
if (c) { if (bitplane == -1) { v += (1 << p); } else { v = 1; } } | |
} | |
fputc(cols[v].r, fp); fputc(cols[v].g, fp); fputc(cols[v].b, fp); | |
} | |
} | |
} | |
fclose(fp); | |
if (!alltoget) { displayimage(str2); } | |
} | |
if (text[0] != '\0') | |
{ | |
int x , y, b, n, l, width, height, xoff, sp; | |
unsigned char *img; | |
char text0[256], str3[256]; | |
if (addfontn) | |
{ | |
strcpy(text0, text); for (i = 0; i < strlen(fh.fch_FC[ext-1].fc_FileName); i++) { str3[i] = toupper(fh.fch_FC[ext-1].fc_FileName[i]); } str3[i] = '\0'; | |
sprintf(str2, " : %s : %s", fh.fch_FC[ext-1].fc_FileName, str3); strcat(text, str2); | |
} | |
width = 10 + xSize + xshift; | |
for (i = 0; i < strlen(text); i++) | |
{ | |
a = text[i] - loChar; if ((text[i] < loChar) || (text[i] > hiChar)) { a = hiChar - loChar + 1; } | |
width += (prop ? (kernm * kern[a] + spac[a]) : xSize); | |
} | |
height = 20 + ySize; | |
img = calloc(1, width * height * 3); | |
for (n = 0; n < width * height * 3; n += 3) { img[n] = cols[0].r; img[n + 1] = cols[0].g; img[n + 2] = cols[0].b; } | |
sprintf(str2, "%ss%d.ppm", imgname, ext); | |
fp = fopen(str2, "wb"); | |
fprintf(fp, "P6\n%d %d\n255\n", width, height); | |
x = 10; y = 10; | |
for (i = 0; i < strlen(text); i++) | |
{ | |
a = text[i] - loChar; | |
if ((text[i] < loChar) || (text[i] > hiChar)) { a = hiChar - loChar + 1; } | |
#ifndef KERNINGAFTER | |
if (prop) { x += kernm * kern[a]; } // kerning before or after character? | |
#endif | |
for (b = bold * boldSmear; b >= 0; b--) | |
{ | |
for (j = 0; j < ySize; j++) | |
{ | |
if (italic > 0.0) { xoff = ((float)(ySize - 1 - j) / (float)(ySize - 1)) * xshift + .5; } else { xoff = 0; } | |
if (prop || (centsp == 0)) { sp = 0; } else { sp = (xSize - bitlength[a]) / 2; } | |
for (n = 0; n < bitlength[a]; n++) | |
{ | |
k = location[a] + n + j * modulo * 8; | |
pos = k / 8; ind = 7 - (k - pos * 8); | |
v = 0; | |
for (p = p0; p < p1; p++) | |
{ | |
if (fontdata[pos + p * modulo * ySize] & (1 << ind)) { c = 1; } else { c = 0; } | |
if (rev) { c = 1 - c; } | |
if (c) { if (bitplane == -1) { v += (1 << p); } else { v = 1; } } | |
} | |
l = (n + x + xoff + (bold ? b : 0) + sp) * 3 + (j + y) * width * 3; | |
if (v != 0) { img[l] = cols[v].r; img[l + 1] = cols[v].g; img[l + 2] = cols[v].b; } | |
} | |
} | |
} | |
#ifndef KERNINGAFTER | |
if (prop) { x += spac[a]; } else { x += xSize; } | |
#else | |
if (prop) { x += spac[a] + kernm * kern[a]; } else { x += xSize; } | |
#endif | |
} | |
fwrite(img, 1, width * height * 3, fp); | |
fclose(fp); | |
free(img); | |
free(fontdata); | |
if (addfontn) { strcpy(text, text0); } | |
if (!alltoget) { displayimage(str2); } | |
} | |
return(1); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
P6 | |
1 6 | |
255 | |