Skip to content
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

Convert more character sets with plFontConverter. #1409

Merged
merged 5 commits into from
Jul 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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