Skip to content

Instantly share code, notes, and snippets.

@mrmcsoftware
Last active September 2, 2022 16:57
Show Gist options
  • Save mrmcsoftware/d3256f1dd7cf80b73f80753fc181c9a0 to your computer and use it in GitHub Desktop.
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.
/*********************************************************
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);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment