diff --git a/Sources/Tools/plFontConverter/plFontConverter.cpp b/Sources/Tools/plFontConverter/plFontConverter.cpp index 2f0d31a36d..053daa4697 100644 --- a/Sources/Tools/plFontConverter/plFontConverter.cpp +++ b/Sources/Tools/plFontConverter/plFontConverter.cpp @@ -533,7 +533,6 @@ void plFontConverter::IBatchFreeType(const plFileName &path, void *init) ui.fPointSizes->setText(QString::number(info.fSize)); ui.fFontName->setText(path.GetFileNameNoExt().c_str()); ui.fResolution->setValue(info.fScreenRes); - ui.fMaxChar->setValue(info.fMaxCharLimit); if (info.fBitDepth == 1) ui.fMonochrome->setChecked(true); else @@ -548,7 +547,6 @@ void plFontConverter::IBatchFreeType(const plFileName &path, void *init) iSizes.append(s.toInt()); info.fScreenRes = ui.fResolution->value(); - info.fMaxCharLimit = ui.fMaxChar->value(); info.fBitDepth = (ui.fMonochrome->isChecked() ? 1 : 8); QString outPath = QFileDialog::getExistingDirectory(this, @@ -612,7 +610,6 @@ void plFontConverter::IImportFreeType(const plFileName &path) ui.fPointSize->setValue(info.fSize); ui.fResolution->setValue(info.fScreenRes); - ui.fMaxChar->setValue(info.fMaxCharLimit); if (info.fBitDepth == 1) ui.fMonochrome->setChecked(true); else @@ -622,7 +619,6 @@ void plFontConverter::IImportFreeType(const plFileName &path) info.fSize = ui.fPointSize->value(); info.fScreenRes = ui.fResolution->value(); - info.fMaxCharLimit = ui.fMaxChar->value(); info.fBitDepth = (ui.fMonochrome->isChecked() ? 1 : 8); if (ret == QDialog::Rejected) diff --git a/Sources/Tools/plFontConverter/plFontFreeType.cpp b/Sources/Tools/plFontConverter/plFontFreeType.cpp index db813c2622..88217c7c7e 100644 --- a/Sources/Tools/plFontConverter/plFontFreeType.cpp +++ b/Sources/Tools/plFontConverter/plFontFreeType.cpp @@ -49,6 +49,8 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #include "plFontFreeType.h" +#include + #include #include "ft2build.h" @@ -56,8 +58,46 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #include FT_GLYPH_H #include FT_TYPES_H -#define kMaxGlyphs 65536 +constexpr FT_ULong kMaxGlyphs = 65536; + +/////////////////////////////////////////////////////////////////////////////// +struct plCharacterRange +{ + FT_ULong fBegin; + FT_ULong fEnd; +}; + +static plCharacterRange s_Characters[]{ + { 0x0020, 0x007E }, // Basic Latin (ASCII) + { 0x00A0, 0x00FF }, // Latin-1 Supplement + { 0x0100, 0x017F }, // Latin Extended-A + { 0x0180, 0x024F }, // Latin Extended-B + { 0x0400, 0x04FF }, // Cyrillic + { 0x0500, 0x052F }, // Cyrillic Supplement +}; + +#if __cpp_lib_constexpr_algorithms >= 201806L +#include +static_assert( + std::all_of( + std::begin(s_Characters), std::end(s_Characters), + [](const plCharacterRange& range) { + // Combined high and low surrogates because no individual codepoint + // exists in the low surrogate range AFAIK, so who cares about the + // distinction. + constexpr plCharacterRange kSurrogatePair{ 0xD800, 0xDFFF }; + return ( + range.fBegin < range.fEnd && range.fEnd < kMaxGlyphs && + // All characters must fit in a single UTF-16 codepoint + (range.fEnd < kSurrogatePair.fBegin || range.fBegin > kSurrogatePair.fEnd) + ); + } + ) +); +#endif + +/////////////////////////////////////////////////////////////////////////////// bool plFontFreeType::ImportFreeType( const plFileName &fontPath, Options *options, plBDFConvertCallback *callback ) @@ -100,9 +140,7 @@ bool plFontFreeType::ImportFreeType( const plFileName &fontPath, Options *opt // Run through our glyphs, loading them into a temp array and calcing bounding boxes for them FT_GlyphSlot ftSlot = ftFace->glyph; - FT_ULong ftChar; - FT_UInt ftIndex; - uint32_t numGlyphs = 0, totalHeight = 0, maxChar = 0, i; + uint32_t numGlyphs = 0, totalHeight = 0, maxChar = 0; auto ftGlyphs = std::make_unique(kMaxGlyphs); auto glyphChars = std::make_unique(kMaxGlyphs); auto ftAdvances = std::make_unique(kMaxGlyphs); @@ -112,21 +150,41 @@ bool plFontFreeType::ImportFreeType( const plFileName &fontPath, Options *opt ftFontBox.xMin = ftFontBox.yMin = 32000; ftFontBox.xMax = ftFontBox.yMax = -32000; - // Hack for now: if we don't have a charmap active already, just choose the first one - if (ftFace->charmap == nullptr) - { - if( ftFace->num_charmaps == 0 ) - throw false; - - FT_Set_Charmap( ftFace, ftFace->charmaps[ 0 ] ); + // Set the unicode character map as the active charmap. Our + // list of character ranges is for unicode fonts. + for (FT_Int i = 0; i < ftFace->num_charmaps; ++i) { + if (ftFace->charmaps[i]->encoding == FT_ENCODING_UNICODE) { + FT_Set_Charmap(ftFace, ftFace->charmaps[i]); + break; + } + } + if (ftFace->charmap == nullptr) { + hsAssert(0, "Oh crap, this font doesn't have a unicode character map."); + throw false; } - ftChar = FT_Get_First_Char( ftFace, &ftIndex ); - while( ftIndex != 0 && numGlyphs < kMaxGlyphs ) - { - error = FT_Load_Glyph( ftFace, ftIndex, FT_LOAD_DEFAULT ); - if( !error && ftChar <= options->fMaxCharLimit ) - { + for (const auto& charRange : s_Characters) { + for (FT_ULong ftChar = charRange.fBegin; ftChar <= charRange.fEnd; ++ftChar) { + // Remove this assert and uncomment the compile-time verification + // at the top of the file when we update to C++20. + hsAssert(ftChar < kMaxGlyphs, "Character index out of bounds"); + FT_UInt ftIndex = FT_Get_Char_Index(ftFace, ftChar); + if (ftIndex == 0) + continue; + + constexpr FT_Int32 kMonochromeLoadFlags = ( + FT_LOAD_RENDER | FT_LOAD_TARGET_MONO | FT_LOAD_MONOCHROME | FT_LOAD_NO_AUTOHINT + ); + constexpr FT_Int32 kNormalLoadFlags = ( + FT_LOAD_RENDER | FT_LOAD_NO_AUTOHINT + ); + error = FT_Load_Glyph( + ftFace, + ftIndex, + options->fBitDepth == 1 ? kMonochromeLoadFlags : kNormalLoadFlags + ); + if (error || ftChar > kMaxGlyphs) + continue; // Got this glyph, store it and update our bounding box error = FT_Get_Glyph( ftFace->glyph, &ftGlyphs[ numGlyphs ] ); @@ -154,8 +212,6 @@ bool plFontFreeType::ImportFreeType( const plFileName &fontPath, Options *opt numGlyphs++; } - - ftChar = FT_Get_Next_Char( ftFace, ftChar, &ftIndex ); } // Init some of our font properties @@ -169,18 +225,16 @@ bool plFontFreeType::ImportFreeType( const plFileName &fontPath, Options *opt // Set the name and size of our font fSize = options->fSize; - char str[ 512 ]; if( ftFace->style_flags & FT_STYLE_FLAG_ITALIC ) SetFlag( kFlagItalic, true ); if( ftFace->style_flags & FT_STYLE_FLAG_BOLD ) SetFlag( kFlagBold, true ); - if( IsFlagSet( kFlagItalic | kFlagBold ) ) - sprintf( str, "%s %s", ftFace->family_name, ftFace->style_name ); + if (IsFlagSet(kFlagItalic | kFlagBold)) + SetFace(ST::format("{} {}", ftFace->family_name, ftFace->style_name)); else - strcpy( str, ftFace->family_name ); - SetFace( str ); + SetFace(ftFace->family_name); // # of bytes per row uint32_t stride = ( fBPP == 1 ) ? ( fWidth >> 3 ) : fWidth; @@ -192,19 +246,8 @@ bool plFontFreeType::ImportFreeType( const plFileName &fontPath, Options *opt callback->NumChars( (uint16_t)(maxChar + 1) ); // Now re-run through our stored list of glyphs, converting them to bitmaps - for( i = 0; i < numGlyphs; i++ ) + for( uint32_t i = 0; i < numGlyphs; i++ ) { - if( ftGlyphs[ i ]->format != ft_glyph_format_bitmap ) - { - FT_Vector origin; - origin.x = 32; // Half a pixel over - origin.y = 0; - - error = FT_Glyph_To_Bitmap( &ftGlyphs[ i ], ( fBPP == 1 ) ? ft_render_mode_mono : ft_render_mode_normal, &origin, 0 ); - if( error ) - throw false; - } - if (fCharacters.size() < glyphChars[i] + 1) fCharacters.resize(glyphChars[i] + 1); diff --git a/Sources/Tools/plFontConverter/plFontFreeType.h b/Sources/Tools/plFontConverter/plFontFreeType.h index ed20dbf019..1f74e272b5 100644 --- a/Sources/Tools/plFontConverter/plFontFreeType.h +++ b/Sources/Tools/plFontConverter/plFontFreeType.h @@ -59,9 +59,8 @@ class plFontFreeType : public plFont bool fUseKerning; uint8_t fBitDepth; uint32_t fScreenRes; - uint32_t fMaxCharLimit; - Options() : fSize(12), fUseKerning(false), fBitDepth(1), fScreenRes(96), fMaxCharLimit(255) { } + Options() : fSize(12), fUseKerning(false), fBitDepth(1), fScreenRes(96) { } }; bool ImportFreeType( const plFileName &fontPath, Options *options, plBDFConvertCallback *callback ); diff --git a/Sources/Tools/plFontConverter/res/FreeType.ui b/Sources/Tools/plFontConverter/res/FreeType.ui index c3495362a5..f62fb3b137 100644 --- a/Sources/Tools/plFontConverter/res/FreeType.ui +++ b/Sources/Tools/plFontConverter/res/FreeType.ui @@ -16,6 +16,16 @@ + + + + Screen &Resolution: + + + fResolution + + + @@ -33,58 +43,27 @@ - - - - Max &Character: - - - fMaxChar - - - - - + + - 1114111 + 65535 - - + + - Screen &Resolution: - - - fResolution + &Monochrome - - - - 65535 + + + + &Grayscale - - - - - - &Monochrome - - - - - - - &Grayscale - - - - - diff --git a/Sources/Tools/plFontConverter/res/FreeTypeBatch.ui b/Sources/Tools/plFontConverter/res/FreeTypeBatch.ui index 53cd47784c..d7dfe32c26 100644 --- a/Sources/Tools/plFontConverter/res/FreeTypeBatch.ui +++ b/Sources/Tools/plFontConverter/res/FreeTypeBatch.ui @@ -37,24 +37,6 @@ Common Settings - - - - - - &Monochrome - - - - - - - &Grayscale - - - - - @@ -65,23 +47,6 @@ - - - - Max &Character: - - - fMaxChar - - - - - - - 1114111 - - - @@ -89,6 +54,24 @@ + + + + + + &Monochrome + + + + + + + &Grayscale + + + + +