Skip to content

Commit

Permalink
Merge pull request #1409 from Hoikas/font_convert_unicode
Browse files Browse the repository at this point in the history
Convert more character sets with plFontConverter.
  • Loading branch information
Hoikas authored Jul 12, 2023
2 parents bf298fc + f80087e commit c1345b2
Show file tree
Hide file tree
Showing 5 changed files with 118 additions and 118 deletions.
4 changes: 0 additions & 4 deletions Sources/Tools/plFontConverter/plFontConverter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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,
Expand Down Expand Up @@ -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
Expand All @@ -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)
Expand Down
115 changes: 79 additions & 36 deletions Sources/Tools/plFontConverter/plFontFreeType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,15 +49,55 @@ You can contact Cyan Worlds, Inc. by email [email protected]

#include "plFontFreeType.h"

#include <string_theory/format>

#include <memory>

#include "ft2build.h"
#include FT_FREETYPE_H
#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 <algorithm>
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 )
Expand Down Expand Up @@ -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<FT_Glyph[]>(kMaxGlyphs);
auto glyphChars = std::make_unique<uint16_t[]>(kMaxGlyphs);
auto ftAdvances = std::make_unique<FT_Vector[]>(kMaxGlyphs);
Expand All @@ -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 ] );
Expand Down Expand Up @@ -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
Expand All @@ -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;
Expand All @@ -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);

Expand Down
3 changes: 1 addition & 2 deletions Sources/Tools/plFontConverter/plFontFreeType.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 );
Expand Down
61 changes: 20 additions & 41 deletions Sources/Tools/plFontConverter/res/FreeType.ui
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,16 @@
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QGridLayout" name="gridLayout">
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Screen &amp;Resolution:</string>
</property>
<property name="buddy">
<cstring>fResolution</cstring>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
Expand All @@ -33,58 +43,27 @@
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Max &amp;Character:</string>
</property>
<property name="buddy">
<cstring>fMaxChar</cstring>
</property>
</widget>
</item>
<item row="0" column="3">
<widget class="QSpinBox" name="fMaxChar">
<item row="1" column="1">
<widget class="QSpinBox" name="fResolution">
<property name="maximum">
<number>1114111</number>
<number>65535</number>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<item row="0" column="2">
<widget class="QRadioButton" name="fMonochrome">
<property name="text">
<string>Screen &amp;Resolution:</string>
</property>
<property name="buddy">
<cstring>fResolution</cstring>
<string>&amp;Monochrome</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QSpinBox" name="fResolution">
<property name="maximum">
<number>65535</number>
<item row="1" column="2">
<widget class="QRadioButton" name="fGrayscale">
<property name="text">
<string>&amp;Grayscale</string>
</property>
</widget>
</item>
<item row="1" column="2" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QRadioButton" name="fMonochrome">
<property name="text">
<string>&amp;Monochrome</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="fGrayscale">
<property name="text">
<string>&amp;Grayscale</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item>
Expand Down
53 changes: 18 additions & 35 deletions Sources/Tools/plFontConverter/res/FreeTypeBatch.ui
Original file line number Diff line number Diff line change
Expand Up @@ -37,24 +37,6 @@
<string>Common Settings</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="1" column="2" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QRadioButton" name="fMonochrome">
<property name="text">
<string>&amp;Monochrome</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="fGrayscale">
<property name="text">
<string>&amp;Grayscale</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
Expand All @@ -65,30 +47,31 @@
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Max &amp;Character:</string>
</property>
<property name="buddy">
<cstring>fMaxChar</cstring>
</property>
</widget>
</item>
<item row="0" column="3">
<widget class="QSpinBox" name="fMaxChar">
<property name="maximum">
<number>1114111</number>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QSpinBox" name="fResolution">
<property name="maximum">
<number>65535</number>
</property>
</widget>
</item>
<item row="0" column="2">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QRadioButton" name="fMonochrome">
<property name="text">
<string>&amp;Monochrome</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="fGrayscale">
<property name="text">
<string>&amp;Grayscale</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
Expand Down

0 comments on commit c1345b2

Please sign in to comment.