mirror of
https://github.com/daveallie/crosspoint-reader.git
synced 2026-02-04 06:37:38 +03:00
554 lines
18 KiB
C
554 lines
18 KiB
C
//
|
||
// TrueType to CrossPoint font converter
|
||
// Copyright (c) 2024 BitBank Software, inc.
|
||
// Written by Larry Bank, adapted by Dave Allie for CrossPoint
|
||
// August 31, 2024
|
||
// The CrossPoint font format is a losslessly compressed bitmap font of a single point size with multiple variants
|
||
// This was built entirely on the back of Larry Bank's bb_font format.
|
||
// The data is compressed with a compression scheme based on CCITT T.6
|
||
// The font structure includes overall size, per-character glyph info and then the
|
||
// compressed image data at the end.
|
||
// The font file format is designed to allow both dynamic loading of font data from
|
||
// external memory/disk or compiling the data as const into a progarm.
|
||
//
|
||
// Example usage:
|
||
// ./fontconvert <regular.ttf> [-b <bold.ttf>] [-i <italic.ttf>] [-bi <bold-italic.ttf>] -p <pt size> -o <out.cpf>
|
||
// ./fontconvert <regular.ttf> [-b <bold.ttf>] [-i <italic.ttf>] [-bi <bold-italic.ttf>] -p <pt size> -o <out.h>
|
||
//
|
||
// This code requires the freetype library
|
||
// found here: www.freetype.org
|
||
//
|
||
|
||
#ifndef ARDUINO
|
||
|
||
#include <ctype.h>
|
||
#include <ft2build.h>
|
||
#include <stdint.h>
|
||
#include <stdio.h>
|
||
#include <stdlib.h>
|
||
#include <string.h>
|
||
|
||
#include "../CrossPointFontFormat.h"
|
||
|
||
#include FT_GLYPH_H
|
||
#include FT_MODULE_H
|
||
#include FT_TRUETYPE_DRIVER_H
|
||
#include "../Group5/g5enc.inl" // Group5 image compression library
|
||
G5ENCIMAGE g5enc; // Group5 encoder state
|
||
|
||
#define DPI 150 // Approximate resolution of common displays
|
||
#define OUTBUF_SIZE 1048576 // 1MB
|
||
#define MAX_INTERVALS 65536
|
||
#define FONT_SCALE_FACTOR 2
|
||
// TODO: Re-enable small font
|
||
// Disabled small font generation to get this working, but want to re-enable
|
||
#define SMALL_FONT_ENABLED 0
|
||
|
||
uint32_t raw_intervals[][2] = {
|
||
/* Basic Latin */
|
||
// ASCII letters, digits, punctuation, control characters
|
||
{0x0000, 0x007F},
|
||
/* Latin-1 Supplement */
|
||
// Accented characters for Western European languages
|
||
{0x0080, 0x00FF},
|
||
/* Latin Extended-A */
|
||
// Eastern European and Baltic languages
|
||
{0x0100, 0x017F},
|
||
/* General Punctuation (core subset) */
|
||
// Smart quotes, en dash, em dash, ellipsis, NO-BREAK SPACE
|
||
{0x2000, 0x206F},
|
||
/* Basic Symbols From "Latin-1 + Misc" */
|
||
// dashes, quotes, prime marks
|
||
{0x2010, 0x203A},
|
||
// misc punctuation
|
||
{0x2040, 0x205F},
|
||
// common currency symbols
|
||
{0x20A0, 0x20CF},
|
||
/* Combining Diacritical Marks (minimal subset) */
|
||
// Needed for proper rendering of many extended Latin languages
|
||
{0x0300, 0x036F},
|
||
/* Greek & Coptic */
|
||
// Used in science, maths, philosophy, some academic texts
|
||
// {0x0370, 0x03FF},
|
||
/* Cyrillic */
|
||
// Russian, Ukrainian, Bulgarian, etc.
|
||
{0x0400, 0x04FF},
|
||
/* Math Symbols (common subset) */
|
||
// Superscripts and Subscripts
|
||
{0x2070, 0x209F},
|
||
// General math operators
|
||
{0x2200, 0x22FF},
|
||
// Arrows
|
||
{0x2190, 0x21FF},
|
||
/* CJK */
|
||
// Core Unified Ideographs
|
||
// {0x4E00, 0x9FFF},
|
||
// Extension A
|
||
// {0x3400, 0x4DBF},
|
||
// Extension B
|
||
// {0x20000, 0x2A6DF},
|
||
// Extension C–F
|
||
// {0x2A700, 0x2EBEF},
|
||
// Extension G
|
||
// {0x30000, 0x3134F},
|
||
// Hiragana
|
||
// {0x3040, 0x309F},
|
||
// Katakana
|
||
// {0x30A0, 0x30FF},
|
||
// Katakana Phonetic Extensions
|
||
// {0x31F0, 0x31FF},
|
||
// Halfwidth Katakana
|
||
// {0xFF60, 0xFF9F},
|
||
// Hangul Syllables
|
||
// {0xAC00, 0xD7AF},
|
||
// Hangul Jamo
|
||
// {0x1100, 0x11FF},
|
||
// Hangul Compatibility Jamo
|
||
// {0x3130, 0x318F},
|
||
// Hangul Jamo Extended-A
|
||
// {0xA960, 0xA97F},
|
||
// Hangul Jamo Extended-B
|
||
// {0xD7B0, 0xD7FF},
|
||
// CJK Radicals Supplement
|
||
// {0x2E80, 0x2EFF},
|
||
// Kangxi Radicals
|
||
// {0x2F00, 0x2FDF},
|
||
// CJK Symbols and Punctuation
|
||
// {0x3000, 0x303F},
|
||
// CJK Compatibility Forms
|
||
// {0xFE30, 0xFE4F},
|
||
// CJK Compatibility Ideographs
|
||
// {0xF900, 0xFAFF},
|
||
/* Specials */
|
||
// Replacement Character
|
||
{0xFFFD, 0xFFFD},
|
||
};
|
||
|
||
//
|
||
// Comparison function for qsort
|
||
//
|
||
int compareIntervals(const void* a, const void* b) {
|
||
const uint32_t* ia = (uint32_t*)a;
|
||
const uint32_t* ib = (uint32_t*)b;
|
||
if (ia[0] < ib[0]) return -1;
|
||
if (ia[0] > ib[0]) return 1;
|
||
return 0;
|
||
}
|
||
|
||
//
|
||
// Sort and merge adjacent intervals
|
||
// Returns the number of intervals after merging
|
||
//
|
||
int sortAndMergeIntervals(uint32_t intervals[][2], const int count) {
|
||
int merged_count = 0;
|
||
|
||
// Sort intervals by start value
|
||
qsort(intervals, count, sizeof(uint32_t) * 2, compareIntervals);
|
||
|
||
// Merge overlapping/adjacent intervals
|
||
for (int i = 0; i < count; i++) {
|
||
if (merged_count > 0 && intervals[i][0] <= intervals[merged_count - 1][1] + 1) {
|
||
// Merge with previous interval
|
||
if (intervals[i][1] > intervals[merged_count - 1][1]) {
|
||
intervals[merged_count - 1][1] = intervals[i][1];
|
||
}
|
||
} else {
|
||
// Add as new interval
|
||
if (merged_count != i) {
|
||
intervals[merged_count][0] = intervals[i][0];
|
||
intervals[merged_count][1] = intervals[i][1];
|
||
}
|
||
merged_count++;
|
||
}
|
||
}
|
||
|
||
return merged_count;
|
||
}
|
||
|
||
//
|
||
// Create the comments and const array boilerplate for the hex data bytes
|
||
//
|
||
void StartHexFile(FILE* f, int iLen, const char* fname, int size) {
|
||
int i, j;
|
||
char szTemp[256];
|
||
fprintf(f, "#pragma once\n\n");
|
||
fprintf(f, "//\n// Created with fontconvert, written by Larry Bank, updated for CrossPoint by Dave Allie\n");
|
||
fprintf(f, "// Point size: %d (scaled %dx)\n", size, FONT_SCALE_FACTOR);
|
||
fprintf(f, "// compressed font data size = %d bytes\n//\n", iLen);
|
||
|
||
strcpy(szTemp, fname);
|
||
i = strlen(szTemp);
|
||
if (szTemp[i - 2] == '.') szTemp[i - 2] = 0; // get the leaf name for the data
|
||
j = i;
|
||
// go backwards to get rid trim off just the leaf name
|
||
while (j > 0 && szTemp[j] != '/') {
|
||
j--;
|
||
}
|
||
if (szTemp[j] == '/') j++;
|
||
fprintf(f, "static const uint8_t %s[] = {\n", &szTemp[j]);
|
||
} /* StartHexFile() */
|
||
|
||
//
|
||
// Add N bytes of hex data to the output
|
||
// The data will be arranged in rows of 16 bytes each
|
||
//
|
||
void AddHexBytes(FILE* f, void* pData, int iLen, int bLast) {
|
||
static int iCount = 0; // number of bytes processed so far
|
||
int i;
|
||
uint8_t* s = (uint8_t*)pData;
|
||
for (i = 0; i < iLen; i++) { // process the given data
|
||
fprintf(f, "0x%02x", *s++);
|
||
iCount++;
|
||
if (i < iLen - 1 || !bLast) fprintf(f, ",");
|
||
if ((iCount & 15) == 0) fprintf(f, "\n"); // next row of 16
|
||
}
|
||
if (bLast) {
|
||
fprintf(f, "};\n");
|
||
}
|
||
} /* AddHexBytes() */
|
||
|
||
int loadCodePoint(FT_Face face, uint32_t code_point, CrossPointFontGlyph* pGlyphs, uint8_t* pBitmap,
|
||
uint32_t* glyph_index, uint32_t* iOffset) {
|
||
uint8_t* s;
|
||
int iPitch, err;
|
||
FT_Glyph glyph;
|
||
|
||
// MONO renderer provides clean image with perfect crop
|
||
// (no wasted pixels) via bitmap struct.
|
||
if ((err = FT_Load_Char(face, code_point, FT_LOAD_TARGET_MONO))) {
|
||
printf("Error %d loading char U+%04X\n", err, code_point);
|
||
(*glyph_index)++;
|
||
return 0;
|
||
}
|
||
|
||
if ((err = FT_Render_Glyph(face->glyph, FT_RENDER_MODE_MONO))) {
|
||
printf("Error %d rendering char U+%04X\n", err, code_point);
|
||
(*glyph_index)++;
|
||
return 0;
|
||
}
|
||
|
||
if ((err = FT_Get_Glyph(face->glyph, &glyph))) {
|
||
printf("Error %d getting glyph U+%04X\n", err, code_point);
|
||
(*glyph_index)++;
|
||
return 0;
|
||
}
|
||
|
||
FT_Bitmap* bitmap = &face->glyph->bitmap;
|
||
FT_BitmapGlyphRec* g = (FT_BitmapGlyphRec*)glyph;
|
||
|
||
// TODO: Restore small font
|
||
if (0 /* bSmallFont */) {
|
||
#if SMALL_FONT_ENABLED == 1
|
||
printf("Stubbed\n");
|
||
return 1;
|
||
#else
|
||
printf("Small font has been disabled\n");
|
||
return 1;
|
||
#endif
|
||
} else {
|
||
pGlyphs[*glyph_index].bitmapOffset = *iOffset;
|
||
pGlyphs[*glyph_index].width = bitmap->width;
|
||
pGlyphs[*glyph_index].height = bitmap->rows;
|
||
pGlyphs[*glyph_index].xAdvance = (face->glyph->advance.x >> 6);
|
||
pGlyphs[*glyph_index].xOffset = g->left;
|
||
pGlyphs[*glyph_index].yOffset = g->top;
|
||
}
|
||
s = bitmap->buffer;
|
||
iPitch = bitmap->pitch;
|
||
|
||
g5_encode_init(&g5enc, bitmap->width, bitmap->rows, &pBitmap[*iOffset], OUTBUF_SIZE - *iOffset);
|
||
for (int y = 0; y < bitmap->rows; y++) {
|
||
g5_encode_encodeLine(&g5enc, &s[y * iPitch]);
|
||
} // for y
|
||
int iLen = g5_encode_getOutSize(&g5enc);
|
||
*iOffset += iLen;
|
||
|
||
FT_Done_Glyph(glyph);
|
||
(*glyph_index)++;
|
||
return 0;
|
||
}
|
||
|
||
int main(int argc, char* argv[]) {
|
||
int i, err, size = 0;
|
||
uint32_t iLen, iOffset = 0;
|
||
FILE* fOut;
|
||
// TrueType library structures
|
||
FT_Library library;
|
||
char* regularFaceFile;
|
||
char* boldFaceFile = NULL;
|
||
char* italicFaceFile = NULL;
|
||
char* boldItalicFaceFile = NULL;
|
||
const char* outputFile = NULL;
|
||
FT_Face faceRegular;
|
||
FT_Face faceBold;
|
||
FT_Face faceItalic;
|
||
FT_Face faceBoldItalic;
|
||
|
||
int bSmallFont = 0; // indicates if we're creating a normal or small font file
|
||
CrossPointFontUnicodeInterval* pIntervals;
|
||
CrossPointFontGlyph* pGlyphs;
|
||
#if SMALL_FONT_ENABLED == 1
|
||
CrossPointFontSmallGlyph* pSmallGlyphs;
|
||
#endif
|
||
uint8_t* pBitmap;
|
||
CrossPointFontHeader epdFontHeader;
|
||
|
||
int bHFile; // flag indicating if the output will be a .H file of hex data
|
||
|
||
// Process intervals
|
||
uint32_t intervals[MAX_INTERVALS][2];
|
||
int intervalCount = sizeof(raw_intervals) / sizeof(raw_intervals[0]);
|
||
uint32_t totalGlyphs = 0;
|
||
|
||
if (argc < 6 || argc % 2 == 1) {
|
||
printf(
|
||
"Usage: %s <regular.ttf> [-b <bold.ttf>] [-i <italic.ttf>] [-bi <bold-italic.ttf>] -p point_size -o <out.cpf "
|
||
"or out.h>\n",
|
||
argv[0]);
|
||
return 1;
|
||
}
|
||
|
||
regularFaceFile = argv[1];
|
||
for (int i = 2; i < argc; i += 2) {
|
||
if (strcmp(argv[i], "-b") == 0) {
|
||
// Bold font
|
||
boldFaceFile = argv[i + 1];
|
||
} else if (strcmp(argv[i], "-i") == 0) {
|
||
// Italic font
|
||
italicFaceFile = argv[i + 1];
|
||
} else if (strcmp(argv[i], "-bi") == 0) {
|
||
// Bold-Italic font
|
||
boldItalicFaceFile = argv[i + 1];
|
||
} else if (strcmp(argv[i], "-p") == 0) {
|
||
// Point size
|
||
size = atoi(argv[i + 1]);
|
||
} else if (strcmp(argv[i], "-o") == 0) {
|
||
// Output file
|
||
outputFile = argv[i + 1];
|
||
// output an H file?
|
||
bHFile = outputFile[strlen(outputFile) - 1] == 'H' || outputFile[strlen(outputFile) - 1] == 'h';
|
||
} else {
|
||
printf("Unknown argument: %s\n", argv[i]);
|
||
return 1;
|
||
}
|
||
}
|
||
|
||
if (!outputFile) {
|
||
printf("No output file specified\n");
|
||
return 1;
|
||
}
|
||
|
||
if (size <= 0) {
|
||
printf("Invalid point size: %d\n", size);
|
||
return 1;
|
||
}
|
||
|
||
size = size * FONT_SCALE_FACTOR;
|
||
bSmallFont = (size < 60) && SMALL_FONT_ENABLED == 1; // Glyph info can fit in signed 8-bit values
|
||
int fontVariants = 1; // Always at least one variant we treat as regular
|
||
if (boldFaceFile) fontVariants += 1;
|
||
if (italicFaceFile) fontVariants += 1;
|
||
if (boldItalicFaceFile) fontVariants += 1;
|
||
|
||
// Copy and sort/merge intervals
|
||
if (intervalCount > MAX_INTERVALS) {
|
||
printf("Error: too many intervals (max %d)\n", MAX_INTERVALS);
|
||
return 1;
|
||
}
|
||
for (i = 0; i < intervalCount; i++) {
|
||
intervals[i][0] = raw_intervals[i][0];
|
||
intervals[i][1] = raw_intervals[i][1];
|
||
}
|
||
intervalCount = sortAndMergeIntervals(intervals, intervalCount);
|
||
|
||
// Calculate total number of glyphs
|
||
for (i = 0; i < intervalCount; i++) {
|
||
totalGlyphs += intervals[i][1] - intervals[i][0] + 1;
|
||
}
|
||
totalGlyphs *= fontVariants;
|
||
|
||
printf("Processed intervals: %d, total glyphs: %u\n", intervalCount, totalGlyphs);
|
||
|
||
// Allocate memory for intervals
|
||
pIntervals = (CrossPointFontUnicodeInterval*)malloc(intervalCount * sizeof(CrossPointFontUnicodeInterval));
|
||
if (!pIntervals) {
|
||
printf("Error allocating memory for interval data\n");
|
||
return 1;
|
||
}
|
||
|
||
// Allocate memory for glyphs
|
||
if (bSmallFont) {
|
||
#if SMALL_FONT_ENABLED == 1
|
||
pSmallGlyphs = (CrossPointFontSmallGlyph*)malloc(totalGlyphs * sizeof(CrossPointFontSmallGlyph));
|
||
if (!pSmallGlyphs) {
|
||
printf("Error allocating memory for glyph data\n");
|
||
return 1;
|
||
}
|
||
#else
|
||
printf("Small font has been disabled\n");
|
||
return 1;
|
||
#endif
|
||
} else {
|
||
pGlyphs = (CrossPointFontGlyph*)malloc(totalGlyphs * sizeof(CrossPointFontGlyph));
|
||
if (!pGlyphs) {
|
||
printf("Error allocating memory for glyph data\n");
|
||
return 1;
|
||
}
|
||
}
|
||
pBitmap = (uint8_t*)malloc(OUTBUF_SIZE); // Enough to hold the output
|
||
if (!pBitmap) {
|
||
printf("Error allocating memory for bitmap data\n");
|
||
return 1;
|
||
}
|
||
|
||
// Init FreeType lib, load font
|
||
if ((err = FT_Init_FreeType(&library))) {
|
||
printf("FreeType init error: %d", err);
|
||
return err;
|
||
}
|
||
|
||
// Use TrueType engine version 35, without subpixel rendering.
|
||
// This improves clarity of fonts since this library does not
|
||
// support rendering multiple levels of gray in a glyph.
|
||
// See https://github.com/adafruit/Adafruit-GFX-Library/issues/103
|
||
FT_UInt interpreter_version = TT_INTERPRETER_VERSION_35;
|
||
FT_Property_Set(library, "truetype", "interpreter-version", &interpreter_version);
|
||
|
||
if ((err = FT_New_Face(library, regularFaceFile, 0, &faceRegular))) {
|
||
printf("Font load error: %d\n", err);
|
||
FT_Done_FreeType(library);
|
||
return err;
|
||
}
|
||
|
||
if (italicFaceFile && (err = FT_New_Face(library, italicFaceFile, 0, &faceItalic))) {
|
||
printf("Font load error: %d\n", err);
|
||
FT_Done_FreeType(library);
|
||
return err;
|
||
}
|
||
|
||
if (boldFaceFile && (err = FT_New_Face(library, boldFaceFile, 0, &faceBold))) {
|
||
printf("Font load error: %d\n", err);
|
||
FT_Done_FreeType(library);
|
||
return err;
|
||
}
|
||
|
||
if (boldItalicFaceFile && (err = FT_New_Face(library, boldItalicFaceFile, 0, &faceBoldItalic))) {
|
||
printf("Font load error: %d\n", err);
|
||
FT_Done_FreeType(library);
|
||
return err;
|
||
}
|
||
|
||
// Shift the size left by 6 because the library uses '26dot6' fixed-point format
|
||
FT_Set_Char_Size(faceRegular, size << 6, 0, DPI, 0);
|
||
if (boldFaceFile) FT_Set_Char_Size(faceBold, size << 6, 0, DPI, 0);
|
||
if (italicFaceFile) FT_Set_Char_Size(faceItalic, size << 6, 0, DPI, 0);
|
||
if (boldItalicFaceFile) FT_Set_Char_Size(faceBoldItalic, size << 6, 0, DPI, 0);
|
||
|
||
// Build intervals with offsets and process glyphs
|
||
uint32_t glyph_index = 0;
|
||
for (int iInterval = 0; iInterval < intervalCount; iInterval++) {
|
||
const uint32_t intervalStart = intervals[iInterval][0];
|
||
const uint32_t intervalEnd = intervals[iInterval][1];
|
||
|
||
// Store interval with offset
|
||
pIntervals[iInterval].first = intervalStart;
|
||
pIntervals[iInterval].last = intervalEnd;
|
||
pIntervals[iInterval].offset = glyph_index;
|
||
|
||
// Process each glyph in this interval
|
||
// Load the codepoint for each style variant
|
||
for (uint32_t codePoint = intervalStart; codePoint <= intervalEnd; codePoint++) {
|
||
loadCodePoint(faceRegular, codePoint, pGlyphs, pBitmap, &glyph_index, &iOffset);
|
||
if (boldFaceFile) loadCodePoint(faceBold, codePoint, pGlyphs, pBitmap, &glyph_index, &iOffset);
|
||
if (italicFaceFile) loadCodePoint(faceItalic, codePoint, pGlyphs, pBitmap, &glyph_index, &iOffset);
|
||
if (boldItalicFaceFile) loadCodePoint(faceBoldItalic, codePoint, pGlyphs, pBitmap, &glyph_index, &iOffset);
|
||
} // for each code point in interval
|
||
} // for each interval
|
||
|
||
// Try to create the output file
|
||
fOut = fopen(outputFile, "w+b");
|
||
if (!fOut) {
|
||
printf("Error creating output file: %s\n", outputFile);
|
||
return 1;
|
||
}
|
||
|
||
epdFontHeader.height = faceRegular->size->metrics.height >> 6;
|
||
epdFontHeader.ascender = faceRegular->size->metrics.ascender >> 6;
|
||
epdFontHeader.styles = 0b0001;
|
||
if (boldFaceFile) epdFontHeader.styles |= 0b0010;
|
||
if (italicFaceFile) epdFontHeader.styles |= 0b0100;
|
||
if (boldItalicFaceFile) epdFontHeader.styles |= 0b1000;
|
||
epdFontHeader.intervalCount = intervalCount;
|
||
epdFontHeader.glyphCount = totalGlyphs;
|
||
|
||
// Write the file header
|
||
if (bSmallFont) {
|
||
#if SMALL_FONT_ENABLED == 1
|
||
epdFontHeader.u16Marker = CPF_FONT_MARKER_SMALL;
|
||
if (faceRegular->size->metrics.height == 0) {
|
||
// No face height info, assume fixed width and get from a glyph.
|
||
epdFontHeader.height = pSmallGlyphs[0].height;
|
||
}
|
||
|
||
iLen = sizeof(CrossPointFontHeader) + intervalCount * sizeof(CrossPointFontUnicodeInterval) +
|
||
totalGlyphs * sizeof(CrossPointFontSmallGlyph) + iOffset;
|
||
if (bHFile) { // create an H file of hex values
|
||
StartHexFile(fOut, iLen, outputFile, size);
|
||
AddHexBytes(fOut, &epdFontHeader, sizeof(CrossPointFontHeader), 0);
|
||
// Write the intervals
|
||
AddHexBytes(fOut, pIntervals, sizeof(CrossPointFontUnicodeInterval) * intervalCount, 0);
|
||
// Write the glyph table
|
||
AddHexBytes(fOut, pSmallGlyphs, sizeof(CrossPointFontSmallGlyph) * totalGlyphs, 0);
|
||
// Write the compressed bitmap data
|
||
AddHexBytes(fOut, pBitmap, iOffset, 1);
|
||
} else {
|
||
fwrite(&epdFontHeader, 1, sizeof(CrossPointFontHeader), fOut);
|
||
// Write the intervals
|
||
fwrite(pIntervals, 1, intervalCount * sizeof(CrossPointFontUnicodeInterval), fOut);
|
||
// Write the glyph table
|
||
fwrite(pSmallGlyphs, 1, totalGlyphs * sizeof(CrossPointFontSmallGlyph), fOut);
|
||
// Write the compressed bitmap data
|
||
fwrite(pBitmap, 1, iOffset, fOut);
|
||
}
|
||
#else
|
||
printf("Small font has been disabled\n");
|
||
return 1;
|
||
#endif
|
||
} else {
|
||
epdFontHeader.u16Marker = CPF_FONT_MARKER;
|
||
if (faceRegular->size->metrics.height == 0) {
|
||
// No face height info, assume fixed width and get from a glyph.
|
||
epdFontHeader.height = pGlyphs[0].height;
|
||
}
|
||
|
||
iLen = sizeof(CrossPointFontHeader) + intervalCount * sizeof(CrossPointFontUnicodeInterval) +
|
||
totalGlyphs * sizeof(CrossPointFontGlyph) + iOffset;
|
||
if (bHFile) { // create an H file of hex values
|
||
StartHexFile(fOut, iLen, outputFile, size);
|
||
AddHexBytes(fOut, &epdFontHeader, sizeof(CrossPointFontHeader), 0);
|
||
// Write the intervals
|
||
AddHexBytes(fOut, pIntervals, sizeof(CrossPointFontUnicodeInterval) * intervalCount, 0);
|
||
// Write the glyph table
|
||
AddHexBytes(fOut, pGlyphs, sizeof(CrossPointFontGlyph) * totalGlyphs, 0);
|
||
// Write the compressed bitmap data
|
||
AddHexBytes(fOut, pBitmap, iOffset, 1);
|
||
} else {
|
||
fwrite(&epdFontHeader, 1, sizeof(CrossPointFontHeader), fOut);
|
||
// Write the intervals
|
||
fwrite(pIntervals, 1, intervalCount * sizeof(CrossPointFontUnicodeInterval), fOut);
|
||
// Write the glyph table
|
||
fwrite(pGlyphs, 1, totalGlyphs * sizeof(CrossPointFontGlyph), fOut);
|
||
// Write the compressed bitmap data
|
||
fwrite(pBitmap, 1, iOffset, fOut);
|
||
}
|
||
} // large fonts
|
||
fflush(fOut);
|
||
fclose(fOut); // done!
|
||
FT_Done_FreeType(library);
|
||
printf("Success!\nFont file size: %d bytes (%d glyphs)\n", iLen, totalGlyphs);
|
||
|
||
return 0;
|
||
} /* main() */
|
||
|
||
#endif
|