Skip to content

Commit 04d26b2

Browse files
committed
Simplify/improve error reporting from ft2font.
Provide a simple macro to call a FreeType function and throw an exception if an error is returned, while also including the source file and line location for the error. For example, trying `FT2Font(open("pyproject.toml", "rb"))` now raises "FT_Open_Face (ft2font.cpp line 220) failed with error 0x2: unknown file format" instead of "Can not load face (unknown file format; error code 0x2)"
1 parent def8fa4 commit 04d26b2

File tree

2 files changed

+55
-84
lines changed

2 files changed

+55
-84
lines changed

src/ft2font.cpp

Lines changed: 27 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -43,26 +43,6 @@
4343

4444
FT_Library _ft2Library;
4545

46-
// FreeType error codes; loaded as per fterror.h.
47-
static char const* ft_error_string(FT_Error error) {
48-
#undef __FTERRORS_H__
49-
#define FT_ERROR_START_LIST switch (error) {
50-
#define FT_ERRORDEF( e, v, s ) case v: return s;
51-
#define FT_ERROR_END_LIST default: return NULL; }
52-
#include FT_ERRORS_H
53-
}
54-
55-
void throw_ft_error(std::string message, FT_Error error) {
56-
char const* s = ft_error_string(error);
57-
std::ostringstream os("");
58-
if (s) {
59-
os << message << " (" << s << "; error code 0x" << std::hex << error << ")";
60-
} else { // Should not occur, but don't add another error from failed lookup.
61-
os << message << " (error code 0x" << std::hex << error << ")";
62-
}
63-
throw std::runtime_error(os.str());
64-
}
65-
6646
FT2Image::FT2Image(unsigned long width, unsigned long height)
6747
: m_buffer((unsigned char *)calloc(width * height, 1)), m_width(width), m_height(height)
6848
{
@@ -237,26 +217,16 @@ FT2Font::FT2Font(FT_Open_Args &open_args,
237217
kerning_factor(0)
238218
{
239219
clear();
240-
241-
FT_Error error = FT_Open_Face(_ft2Library, &open_args, 0, &face);
242-
if (error) {
243-
throw_ft_error("Can not load face", error);
244-
}
245-
246-
// set a default fontsize 12 pt at 72dpi
247-
error = FT_Set_Char_Size(face, 12 * 64, 0, 72 * (unsigned int)hinting_factor, 72);
248-
if (error) {
249-
FT_Done_Face(face);
250-
throw_ft_error("Could not set the fontsize", error);
251-
}
252-
220+
FT_CHECK(FT_Open_Face, _ft2Library, &open_args, 0, &face);
253221
if (open_args.stream != nullptr) {
254222
face->face_flags |= FT_FACE_FLAG_EXTERNAL_STREAM;
255223
}
256-
257-
FT_Matrix transform = { 65536 / hinting_factor, 0, 0, 65536 };
258-
FT_Set_Transform(face, &transform, nullptr);
259-
224+
try {
225+
set_size(12., 72.); // Set a default fontsize 12 pt at 72dpi.
226+
} catch (...) {
227+
FT_Done_Face(face);
228+
throw;
229+
}
260230
// Set fallbacks
261231
std::copy(fallback_list.begin(), fallback_list.end(), std::back_inserter(fallbacks));
262232
}
@@ -293,11 +263,9 @@ void FT2Font::clear()
293263

294264
void FT2Font::set_size(double ptsize, double dpi)
295265
{
296-
FT_Error error = FT_Set_Char_Size(
266+
FT_CHECK(
267+
FT_Set_Char_Size,
297268
face, (FT_F26Dot6)(ptsize * 64), 0, (FT_UInt)(dpi * hinting_factor), (FT_UInt)dpi);
298-
if (error) {
299-
throw_ft_error("Could not set the fontsize", error);
300-
}
301269
FT_Matrix transform = { 65536 / hinting_factor, 0, 0, 65536 };
302270
FT_Set_Transform(face, &transform, nullptr);
303271

@@ -311,17 +279,12 @@ void FT2Font::set_charmap(int i)
311279
if (i >= face->num_charmaps) {
312280
throw std::runtime_error("i exceeds the available number of char maps");
313281
}
314-
FT_CharMap charmap = face->charmaps[i];
315-
if (FT_Error error = FT_Set_Charmap(face, charmap)) {
316-
throw_ft_error("Could not set the charmap", error);
317-
}
282+
FT_CHECK(FT_Set_Charmap, face, face->charmaps[i]);
318283
}
319284

320285
void FT2Font::select_charmap(unsigned long i)
321286
{
322-
if (FT_Error error = FT_Select_Charmap(face, (FT_Encoding)i)) {
323-
throw_ft_error("Could not set the charmap", error);
324-
}
287+
FT_CHECK(FT_Select_Charmap, face, (FT_Encoding)i);
325288
}
326289

327290
int FT2Font::get_kerning(FT_UInt left, FT_UInt right, FT_Kerning_Mode mode,
@@ -477,10 +440,10 @@ void FT2Font::load_char(long charcode, FT_Int32 flags, FT2Font *&ft_object, bool
477440
if (!was_found) {
478441
ft_glyph_warn(charcode, glyph_seen_fonts);
479442
if (charcode_error) {
480-
throw_ft_error("Could not load charcode", charcode_error);
443+
THROW_FT_ERROR("charcode loading", charcode_error);
481444
}
482445
else if (glyph_error) {
483-
throw_ft_error("Could not load charcode", glyph_error);
446+
THROW_FT_ERROR("charcode loading", glyph_error);
484447
}
485448
} else if (ft_object_with_glyph->warn_if_used) {
486449
ft_glyph_warn(charcode, glyph_seen_fonts);
@@ -494,13 +457,9 @@ void FT2Font::load_char(long charcode, FT_Int32 flags, FT2Font *&ft_object, bool
494457
glyph_seen_fonts.insert((face != nullptr)?face->family_name: nullptr);
495458
ft_glyph_warn((FT_ULong)charcode, glyph_seen_fonts);
496459
}
497-
if (FT_Error error = FT_Load_Glyph(face, glyph_index, flags)) {
498-
throw_ft_error("Could not load charcode", error);
499-
}
460+
FT_CHECK(FT_Load_Glyph, face, glyph_index, flags);
500461
FT_Glyph thisGlyph;
501-
if (FT_Error error = FT_Get_Glyph(face->glyph, &thisGlyph)) {
502-
throw_ft_error("Could not get glyph", error);
503-
}
462+
FT_CHECK(FT_Get_Glyph, face->glyph, &thisGlyph);
504463
glyphs.push_back(thisGlyph);
505464
}
506465
}
@@ -600,13 +559,9 @@ void FT2Font::load_glyph(FT_UInt glyph_index,
600559

601560
void FT2Font::load_glyph(FT_UInt glyph_index, FT_Int32 flags)
602561
{
603-
if (FT_Error error = FT_Load_Glyph(face, glyph_index, flags)) {
604-
throw_ft_error("Could not load glyph", error);
605-
}
562+
FT_CHECK(FT_Load_Glyph, face, glyph_index, flags);
606563
FT_Glyph thisGlyph;
607-
if (FT_Error error = FT_Get_Glyph(face->glyph, &thisGlyph)) {
608-
throw_ft_error("Could not get glyph", error);
609-
}
564+
FT_CHECK(FT_Get_Glyph, face->glyph, &thisGlyph);
610565
glyphs.push_back(thisGlyph);
611566
}
612567

@@ -651,13 +606,10 @@ void FT2Font::draw_glyphs_to_bitmap(bool antialiased)
651606
image = py::array_t<uint8_t>{{height, width}};
652607
std::memset(image.mutable_data(0), 0, image.nbytes());
653608

654-
for (auto & glyph : glyphs) {
655-
FT_Error error = FT_Glyph_To_Bitmap(
609+
for (auto & glyph: glyphs) {
610+
FT_CHECK(
611+
FT_Glyph_To_Bitmap,
656612
&glyph, antialiased ? FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO, nullptr, 1);
657-
if (error) {
658-
throw_ft_error("Could not convert glyph to bitmap", error);
659-
}
660-
661613
FT_BitmapGlyph bitmap = (FT_BitmapGlyph)glyph;
662614
// now, draw to our target surface (convert position)
663615

@@ -681,16 +633,12 @@ void FT2Font::draw_glyph_to_bitmap(
681633
throw std::runtime_error("glyph num is out of range");
682634
}
683635

684-
FT_Error error = FT_Glyph_To_Bitmap(
685-
&glyphs[glyphInd],
686-
antialiased ? FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO,
687-
&sub_offset, // additional translation
688-
1 // destroy image
689-
);
690-
if (error) {
691-
throw_ft_error("Could not convert glyph to bitmap", error);
692-
}
693-
636+
FT_CHECK(
637+
FT_Glyph_To_Bitmap,
638+
&glyphs[glyphInd],
639+
antialiased ? FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO,
640+
&sub_offset, // additional translation
641+
1); // destroy image
694642
FT_BitmapGlyph bitmap = (FT_BitmapGlyph)glyphs[glyphInd];
695643

696644
draw_bitmap(im, &bitmap->bitmap, x + bitmap->left, y);
@@ -715,9 +663,7 @@ void FT2Font::get_glyph_name(unsigned int glyph_number, std::string &buffer,
715663
throw std::runtime_error("Failed to convert glyph to standard name");
716664
}
717665
} else {
718-
if (FT_Error error = FT_Get_Glyph_Name(face, glyph_number, buffer.data(), buffer.size())) {
719-
throw_ft_error("Could not get glyph names", error);
720-
}
666+
FT_CHECK(FT_Get_Glyph_Name, face, glyph_number, buffer.data(), buffer.size());
721667
auto len = buffer.find('\0');
722668
if (len != buffer.npos) {
723669
buffer.resize(len);

src/ft2font.h

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#ifndef MPL_FT2FONT_H
77
#define MPL_FT2FONT_H
88

9+
#include <filesystem>
910
#include <set>
1011
#include <string>
1112
#include <string_view>
@@ -26,12 +27,36 @@ extern "C" {
2627
#include <pybind11/numpy.h>
2728
namespace py = pybind11;
2829

29-
/*
30-
By definition, FT_FIXED as 2 16bit values stored in a single long.
31-
*/
30+
// By definition, FT_FIXED as 2 16bit values stored in a single long.
3231
#define FIXED_MAJOR(val) (signed short)((val & 0xffff0000) >> 16)
3332
#define FIXED_MINOR(val) (unsigned short)(val & 0xffff)
3433

34+
// Error handling (error codes are loaded as described in fterror.h).
35+
inline char const* ft_error_string(FT_Error error) {
36+
#undef __FTERRORS_H__
37+
#define FT_ERROR_START_LIST switch (error) {
38+
#define FT_ERRORDEF( e, v, s ) case v: return s;
39+
#define FT_ERROR_END_LIST default: return NULL; }
40+
#include FT_ERRORS_H
41+
}
42+
43+
// No more than 16 hex digits + "0x" + null byte for a 64-bit int error.
44+
#define THROW_FT_ERROR(name, err) { \
45+
char buf[20] = {0}; \
46+
sprintf(buf, "%#04x", err); \
47+
throw std::runtime_error{ \
48+
name " (" \
49+
+ std::filesystem::path(__FILE__).filename().string() \
50+
+ " line " + std::to_string(__LINE__) + ") failed with error " \
51+
+ std::string{buf} + ": " + std::string{ft_error_string(err)}}; \
52+
} (void)0
53+
54+
#define FT_CHECK(func, ...) { \
55+
if (auto const& error_ = func(__VA_ARGS__)) { \
56+
THROW_FT_ERROR(#func, error_); \
57+
} \
58+
} (void)0
59+
3560
// the FreeType string rendered into a width, height buffer
3661
class FT2Image
3762
{

0 commit comments

Comments
 (0)