Skip to content

Instantly share code, notes, and snippets.

@Makistos
Last active December 8, 2023 13:38
Show Gist options
  • Save Makistos/694804b76ac7c1964bf925ab43eb79ea to your computer and use it in GitHub Desktop.
Save Makistos/694804b76ac7c1964bf925ab43eb79ea to your computer and use it in GitHub Desktop.
Render psf fonts to terminal. #c #linux #psf #fonts #libz
src = $(wildcard *.c)
obj = $(src:.c=.o)
LDFLAGS = -lz
psf: $(obj)
$(CC) -o $@ $^ $(LDFLAGS)
.PHONY: clean
clean:
rm -f $(obj) psf
#include <stdlib.h>
#include <stdio.h>
#include <stdio.h>
#include <stdint.h>
#include <unistd.h>
#include <zlib.h>
#include <errno.h>
#include <stdarg.h>
#include <uchar.h>
#define SLOGD(x,...) printf(x, __VA_ARGS__)
#define SLOGE(x,...) printf(x, __VA_ARGS__)
#define PSF1_MAGIC0 0x36
#define PSF1_MAGIC1 0x04
#define PSF1_MODE512 0x01
#define PSF1_MODEHASTAB 0x02
#define PSF1_MODEHASSEQ 0x04
#define PSF1_MAXMODE 0x05
#define PSF1_SEPARATOR 0xFFFF
#define PSF1_STARTSEQ 0xFFFE
struct psf1_header {
unsigned char magic[2]; /* Magic number */
unsigned char mode; /* PSF font mode */
unsigned char charsize; /* Character size */
};
#define PSF1_MAGIC_OK(x) ( \
(x)->magic[0] == PSF1_MAGIC0 \
&& (x)->magic[1] == PSF1_MAGIC1 \
)
enum {
PSF2_MAGIC0 = 0x72,
PSF2_MAGIC1 = 0xb5,
PSF2_MAGIC2 = 0x4a,
PSF2_MAGIC3 = 0x86,
PSF2_HAS_UNICODE_TABLE = 0x01,
PSF2_MAXVERSION = 0,
PSF2_STARTSEQ = 0xfe,
PSF2_SEPARATOR = 0xff
};
struct psf2_header {
unsigned char magic[4];
unsigned int version;
unsigned int headersize; /* offset of bitmaps in file */
unsigned int flags;
unsigned int length; /* number of glyphs */
unsigned int charsize; /* number of bytes for each character */
unsigned int height; /* max dimensions of glyphs */
unsigned int width; /* charsize = height * ((width + 7) / 8) */
};
#define psf2h(x) ((struct psf2_header*)(x))
#define PSF2_MAGIC_OK(x) ( \
(x)->magic[0] == PSF2_MAGIC0 \
&& (x)->magic[1] == PSF2_MAGIC1 \
&& (x)->magic[2] == PSF2_MAGIC2 \
&& (x)->magic[3] == PSF2_MAGIC3 \
)
static uint8_t *glyphs_buttons;
static void render_char(wchar_t c, uint8_t *glyphs, struct psf2_header *header)
{
uint8_t *glyph = &glyphs[c * header->charsize];
uint32_t stride = header->charsize / header->height;
uint32_t y0;
uint32_t x0;
printf("Index = %d (0x%x)\n", c, c);
printf("Pos = %d (0x%x)\n", header->headersize + c * header->charsize, header->headersize + c * header->charsize);
if (glyph == NULL) {
SLOGE("No fonts provided for %c.\n", c);
return;
}
for (y0 = 0; y0 < header->height; ++y0) {
for (x0 = 0; x0 < header->width; ++x0) {
uint8_t bits = glyph[y0 * stride + x0 / 8];
uint8_t bit = bits >> (7 - x0 % 8) & 1;
if (bit) printf("%c", '0');
else printf("%c", ' ');
}
printf("\n");
}
}
void print_header(struct psf2_header *header, const char *font_file)
{
SLOGD("=== FONT INFO for %s ===\n", font_file);
if (PSF2_MAGIC_OK(header)) {
printf("PSF2 format\n");
} else {
printf("PSF1 format\n");
}
SLOGD("magic = %x%x%x%x\n", header->magic[0], header->magic[1], header->magic[2], header->magic[3]);
SLOGD("version = %d\n", header->version);
SLOGD("headersize = %d\n", header->headersize);
SLOGD("flags = %d\n", header->flags);
SLOGD("length = %d\n", header->length);
SLOGD("charsize = %d\n", header->charsize);
SLOGD("height = %d\n", header->height);
SLOGD("width = %d\n", header->width);
}
static int load_font(const char *font_file, uint8_t **glyphs, struct psf2_header **header)
{
*header = (struct psf2_header*)malloc(sizeof(struct psf2_header));
printf("foo");
gzFile font = gzopen(font_file, "r");
if (font == NULL) {
SLOGE("Failed to load font %s\n", font_file);
return ENOENT;
}
gzread(font, *header, sizeof(struct psf2_header));
print_header(*header, font_file);
if (PSF2_MAGIC_OK(*header) == 0) {
SLOGD("Magic nof PSF2 for font %s\n", font_file);
return EBFONT;
}
*glyphs = (uint8_t*)malloc((*header)->length*(*header)->charsize);
gzread(font, *glyphs, (*header)->charsize * (*header)->length);
return 0;
}
static int load_font_psf1(const char *font_file, uint8_t **glyphs, struct psf2_header **header)
{
struct psf1_header *tmp_header;
*header = (struct psf2_header*)malloc(sizeof(struct psf2_header));
tmp_header = (struct psf1_header*)malloc(sizeof(struct psf1_header));
gzFile font = gzopen(font_file, "r");
if (font == NULL) {
SLOGE("Failed to load font %s\n", font_file);
return ENOENT;
}
gzread(font, tmp_header, sizeof(struct psf1_header));
if (PSF1_MAGIC_OK(tmp_header) == 0) {
SLOGD("Magic nof PSF2 for font %s\n", font_file);
return EBFONT;
}
(*header)->headersize = sizeof(struct psf2_header);
if (tmp_header->mode & PSF1_MODE512) {
(*header)->length = 512;
} else {
(*header)->length = 256;
}
(*header)->magic[0] = tmp_header->magic[0];
(*header)->magic[1] = tmp_header->magic[1];
(*header)->flags = tmp_header->mode;
(*header)->charsize = tmp_header->charsize;
(*header)->width = 8;
(*header)->height = tmp_header->charsize;
print_header(*header, font_file);
*glyphs = (uint8_t*)malloc((*header)->length*(*header)->charsize);
gzread(font, *glyphs, (*header)->charsize * (*header)->length);
free(tmp_header);
return 0;
}
int main(int argc, char *argv[])
{
struct psf2_header *font_header;
int retval;
printf("argc = %d\n", argc);
if (argc < 1) {
printf("No font file or character defined.\n");
return -1;
}
font_header = (struct psf2_header*)malloc(sizeof(struct psf2_header));
retval = load_font(argv[1], &glyphs_buttons, &font_header);
if (retval == EBFONT) {
retval = load_font_psf1(argv[1], &glyphs_buttons, &font_header);
}
if (argc == 3) {
printf("Printing character %c\n", argv[2][0]);
render_char(argv[2][0], glyphs_buttons, font_header);
} else {
for (int i = 0; i < font_header->length; i++) {
render_char(i, glyphs_buttons, font_header);
}
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment