-
Notifications
You must be signed in to change notification settings - Fork 25
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[feat] Add support for loading OpenType fonts from memory. #82
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5624,19 +5624,10 @@ static __attribute__((cold)) const char* | |
} | ||
} | ||
|
||
// Load OT fonts for fbink_add_ot_font & fbink_add_ot_font_v2 | ||
// Load OT fonts for add_ot_font | ||
static __attribute__((cold)) int | ||
add_ot_font(const char* filename, FONT_STYLE_T style, FBInkOTFonts* restrict ot_fonts) | ||
add_ot_font_data(const unsigned char* data, FONT_STYLE_T style, FBInkOTFonts* restrict ot_fonts) | ||
{ | ||
# ifdef FBINK_FOR_KOBO | ||
// NOTE: Bail if we were passed a Kobo system font, as they're obfuscated, | ||
// and some of them risk crashing stbtt because of bogus data... | ||
const char blacklist[] = "/usr/local/Trolltech/QtEmbedded-4.6.2-arm/lib/fonts/"; | ||
if (!strncmp(filename, blacklist, sizeof(blacklist) - 1)) { | ||
WARN("Cannot use font `%s`: it's an obfuscated Kobo system font", filename + sizeof(blacklist) - 1); | ||
return ERRCODE(EXIT_FAILURE); | ||
} | ||
# endif | ||
|
||
// Init libunibreak the first time we're called | ||
if (!otInit) { | ||
|
@@ -5645,70 +5636,34 @@ static __attribute__((cold)) int | |
} | ||
otInit = true; | ||
|
||
// Open font from given path, and load into buffer | ||
FILE* f = fopen(filename, "r" STDIO_CLOEXEC); | ||
unsigned char* restrict data = NULL; | ||
if (!f) { | ||
PFWARN("fopen: %m"); | ||
otInit = false; | ||
return ERRCODE(EXIT_FAILURE); | ||
} else { | ||
const int fd = fileno(f); | ||
struct stat st; | ||
if (fstat(fd, &st) == -1) { | ||
PFWARN("fstat: %m"); | ||
fclose(f); | ||
otInit = false; | ||
return ERRCODE(EXIT_FAILURE); | ||
} | ||
data = calloc((size_t) st.st_size, sizeof(*data)); | ||
if (!data) { | ||
PFWARN("Error allocating font data buffer: %m"); | ||
fclose(f); | ||
otInit = false; | ||
return ERRCODE(EXIT_FAILURE); | ||
} | ||
if (fread(data, 1U, (size_t) st.st_size, f) < (size_t) st.st_size || ferror(f) != 0) { | ||
free(data); | ||
fclose(f); | ||
otInit = false; | ||
WARN("Error reading font file `%s`", filename); | ||
return ERRCODE(EXIT_FAILURE); | ||
} | ||
fclose(f); | ||
} | ||
stbtt_fontinfo* font_info = calloc(1U, sizeof(stbtt_fontinfo)); | ||
stbtt_fontinfo* font_info = calloc(1U, sizeof(stbtt_fontinfo)); | ||
if (!font_info) { | ||
PFWARN("Error allocating stbtt_fontinfo struct: %m"); | ||
free(data); | ||
return ERRCODE(EXIT_FAILURE); | ||
} | ||
// First, check if we can actually find a recognizable font format in the data... | ||
const int fontcount = stbtt_GetNumberOfFonts(data); | ||
if (fontcount == 0) { | ||
free(data); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ditto |
||
free(font_info); | ||
WARN("File `%s` doesn't appear to be a valid or supported font", filename); | ||
WARN("File doesn't appear to be a valid or supported font"); | ||
return ERRCODE(EXIT_FAILURE); | ||
} else if (fontcount > 1) { | ||
LOG("Font file `%s` appears to be a font collection containing %d fonts, but we'll only use the first one!", | ||
filename, | ||
LOG("Font file appears to be a font collection containing %d fonts, but we'll only use the first one!", | ||
fontcount); | ||
} | ||
// Then, get the offset to the first font | ||
const int fontoffset = stbtt_GetFontOffsetForIndex(data, 0); | ||
if (fontoffset == -1) { | ||
free(data); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In the same vein, this needs to be Which implies that we take ownership of the pointer, which might be a problem for your usecase (e.g., pointer to static content), so this may need to be more complex to add a way to say "nope, this data is immutable, don't ever try to free it". There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Weeelll, except The point about ownership still stands, though ;p. |
||
free(font_info); | ||
WARN("File `%s` doesn't appear to contain valid font data at offset %d", filename, fontoffset); | ||
WARN("File doesn't appear to contain valid font data at offset %d", fontoffset); | ||
return ERRCODE(EXIT_FAILURE); | ||
} | ||
// And finally, initialize that font | ||
// NOTE: We took the long way 'round to try to avoid crashes on invalid data... | ||
if (!stbtt_InitFont(font_info, data, fontoffset)) { | ||
free(font_info->data); | ||
free(font_info); | ||
WARN("Error initialising font `%s`", filename); | ||
WARN("Error initialising font."); | ||
return ERRCODE(EXIT_FAILURE); | ||
} | ||
// Assign the current font to its appropriate FBInkOTFonts struct member, depending on the style specified by the caller. | ||
|
@@ -5741,13 +5696,65 @@ static __attribute__((cold)) int | |
default: | ||
free(font_info->data); | ||
free(font_info); | ||
WARN("Cannot load font `%s`: requested style (%d) is invalid!", filename, style); | ||
WARN("Cannot load font: requested style (%d) is invalid!", style); | ||
return ERRCODE(EXIT_FAILURE); | ||
} | ||
|
||
ELOG("Font `%s` loaded for style '%s'", filename, font_style_to_string(style)); | ||
ELOG("Font loaded for style '%s'", font_style_to_string(style)); | ||
return EXIT_SUCCESS; | ||
} | ||
|
||
// Load OT fonts from a file for fbink_add_ot_font & fbink_add_ot_font_v2 | ||
static __attribute__((cold)) int | ||
add_ot_font(const char* filename, FONT_STYLE_T style, FBInkOTFonts* restrict ot_fonts) | ||
{ | ||
# ifdef FBINK_FOR_KOBO | ||
// NOTE: Bail if we were passed a Kobo system font, as they're obfuscated, | ||
// and some of them risk crashing stbtt because of bogus data... | ||
const char blacklist[] = "/usr/local/Trolltech/QtEmbedded-4.6.2-arm/lib/fonts/"; | ||
if (!strncmp(filename, blacklist, sizeof(blacklist) - 1)) { | ||
WARN("Cannot use font `%s`: it's an obfuscated Kobo system font", filename + sizeof(blacklist) - 1); | ||
return ERRCODE(EXIT_FAILURE); | ||
} | ||
# endif | ||
|
||
|
||
// Open font from given path, and load into buffer | ||
FILE* f = fopen(filename, "r" STDIO_CLOEXEC); | ||
unsigned char* restrict data = NULL; | ||
if (!f) { | ||
PFWARN("fopen: %m"); | ||
otInit = false; | ||
return ERRCODE(EXIT_FAILURE); | ||
} else { | ||
const int fd = fileno(f); | ||
struct stat st; | ||
if (fstat(fd, &st) == -1) { | ||
PFWARN("fstat: %m"); | ||
fclose(f); | ||
otInit = false; | ||
return ERRCODE(EXIT_FAILURE); | ||
} | ||
data = calloc((size_t) st.st_size, sizeof(*data)); | ||
if (!data) { | ||
PFWARN("Error allocating font data buffer: %m"); | ||
fclose(f); | ||
otInit = false; | ||
return ERRCODE(EXIT_FAILURE); | ||
} | ||
if (fread(data, 1U, (size_t) st.st_size, f) < (size_t) st.st_size || ferror(f) != 0) { | ||
free(data); | ||
fclose(f); | ||
otInit = false; | ||
WARN("Error reading font file `%s`", filename); | ||
return ERRCODE(EXIT_FAILURE); | ||
} | ||
fclose(f); | ||
} | ||
int result = add_ot_font_data(data, style, ot_fonts); | ||
free(data); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's a problem ;). (The data is still live, stbtt doesn't make a copy, it just keeps a reference to that same pointer). |
||
return result; | ||
} | ||
#endif // FBINK_WITH_OPENTYPE | ||
|
||
// Load font from given file path. Up to four font styles may be used by FBInk at any given time. | ||
|
@@ -5764,6 +5771,20 @@ int | |
#endif // FBINK_WITH_OPENTYPE | ||
} | ||
|
||
// Load font from memory. Up to four font styles may be used by FBInk at any given time. | ||
int | ||
fbink_add_ot_font_from_memory(const unsigned char* data UNUSED_BY_MINIMAL, FONT_STYLE_T style UNUSED_BY_MINIMAL) | ||
{ | ||
#ifdef FBINK_WITH_OPENTYPE | ||
// Legacy variant, using the global otFonts | ||
LOG("Loading font data in the global font pool . . ."); | ||
return add_ot_font_data(data, style, &otFonts); | ||
#else | ||
WARN("OpenType support is disabled in this FBInk build"); | ||
return ERRCODE(ENOSYS); | ||
#endif // FBINK_WITH_OPENTYPE | ||
} | ||
|
||
// Load font from given file path. Up to four font styles may be used per FBInkOTConfig instance. | ||
int | ||
fbink_add_ot_font_v2(const char* filename UNUSED_BY_MINIMAL, | ||
|
@@ -5788,6 +5809,26 @@ int | |
#endif // FBINK_WITH_OPENTYPE | ||
} | ||
|
||
// Load font from memory. Up to four font styles may be used per FBInkOTConfig instance. | ||
int fbink_add_ot_font_from_memory_v2(const unsigned char* data UNUSED_BY_MINIMAL, FONT_STYLE_T style UNUSED_BY_MINIMAL, FBInkOTConfig *restrict cfg UNUSED_BY_MINIMAL) { | ||
#ifdef FBINK_WITH_OPENTYPE | ||
// Start by allocating an FBInkOTFonts struct, if need be... | ||
if (!cfg->font) { | ||
cfg->font = calloc(1U, sizeof(FBInkOTFonts)); | ||
if (!cfg->font) { | ||
PFWARN("Error allocating FBInkOTFonts struct: %m"); | ||
return ERRCODE(EXIT_FAILURE); | ||
} | ||
} | ||
// New variant, using a per-FBInkOTConfig instance | ||
LOG("Loading font data in a local FBInkOTFonts instance (%p) . . .", cfg->font); | ||
return add_ot_font_data(data, style, (FBInkOTFonts*) cfg->font); | ||
#else | ||
WARN("OpenType support is disabled in this FBInk build"); | ||
return ERRCODE(ENOSYS); | ||
#endif // FBINK_WITH_OPENTYPE | ||
} | ||
|
||
#ifdef FBINK_WITH_OPENTYPE | ||
// Free an individual OpenType font structure | ||
static __attribute__((cold)) int | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ditto