Charset parsing from argument string
This commit is contained in:
parent
d120bb3e05
commit
c27de5988d
12
CHANGELOG.md
12
CHANGELOG.md
|
|
@ -1,15 +1,23 @@
|
||||||
|
|
||||||
## Version 1.3 (forthcoming)
|
## Version 1.3 (2024-06-01)
|
||||||
|
|
||||||
- Updated to MSDFgen 1.10
|
- Updated to MSDFgen 1.12
|
||||||
- Switched to vcpkg as the primary dependency management system
|
- Switched to vcpkg as the primary dependency management system
|
||||||
- Removed Visual Studio solution and Makefile - now has to be generated by CMake
|
- Removed Visual Studio solution and Makefile - now has to be generated by CMake
|
||||||
- CMake configuration overhaul, added installation configuration
|
- CMake configuration overhaul, added installation configuration
|
||||||
- Switched to libpng as the primary PNG file encoder
|
- Switched to libpng as the primary PNG file encoder
|
||||||
|
- Added uniform grid mode (`-uniformgrid`) where atlas is laid out in a rectangular grid
|
||||||
|
- Added options to add extra padding around glyphs (`-empadding`, `-pxpadding` and similar)
|
||||||
|
- Added the possibility to specify asymmetrical distance range (`-aemrange`, `-apxrange`)
|
||||||
|
- Added `-pxalign` option which governs glyph alignment with the pixel grid
|
||||||
|
- Added `-allglyphs` option as alternative to explicit charset / glyphset
|
||||||
|
- Added `-chars` and `-glyphs` options to specify charset / glyphset directly on command line
|
||||||
- Added `-varfont` option to configure variables of variable fonts
|
- Added `-varfont` option to configure variables of variable fonts
|
||||||
- Added `-version` option to print program version
|
- Added `-version` option to print program version
|
||||||
- Arguments with double dash (e.g. `--font`) now also accepted
|
- Arguments with double dash (e.g. `--font`) now also accepted
|
||||||
- Minor fix to positioning for `-type hardmask`
|
- Minor fix to positioning for `-type hardmask`
|
||||||
|
- Errors are now reported to `stderr`
|
||||||
|
- TinyXML 2 no longer required as a dependency
|
||||||
|
|
||||||
### Version 1.2.2 (2021-09-06)
|
### Version 1.2.2 (2021-09-06)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ This is a utility for generating compact font atlases using [MSDFgen](https://gi
|
||||||
|
|
||||||
The atlas generator loads a subset of glyphs from a TTF or OTF font file, generates a distance field for each of them, and tightly packs them into an atlas bitmap (example below). The finished atlas and/or its layout metadata can be exported as an [Artery Font](https://github.com/Chlumsky/artery-font-format) file, a plain image file, a CSV sheet or a structured JSON file.
|
The atlas generator loads a subset of glyphs from a TTF or OTF font file, generates a distance field for each of them, and tightly packs them into an atlas bitmap (example below). The finished atlas and/or its layout metadata can be exported as an [Artery Font](https://github.com/Chlumsky/artery-font-format) file, a plain image file, a CSV sheet or a structured JSON file.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
A font atlas is typically stored in texture memory and used to draw text in real-time rendering contexts such as video games.
|
A font atlas is typically stored in texture memory and used to draw text in real-time rendering contexts such as video games.
|
||||||
|
|
||||||
|
|
@ -47,13 +47,16 @@ Use the following command line arguments for the standalone version of the atlas
|
||||||
|
|
||||||
- `-font <fontfile.ttf/otf>` (required) – sets the input font file.
|
- `-font <fontfile.ttf/otf>` (required) – sets the input font file.
|
||||||
- Alternatively, use `-varfont <fontfile.ttf/otf?var0=value0&var1=value1>` to configure a variable font.
|
- Alternatively, use `-varfont <fontfile.ttf/otf?var0=value0&var1=value1>` to configure a variable font.
|
||||||
- `-charset <charset.txt>` – sets the character set. The ASCII charset will be used if not specified. See [the syntax specification](#character-set-specification-syntax) of `charset.txt`.
|
- `-charset <charset.txt>` – sets the character set. See [the syntax specification](#character-set-specification-syntax) of `charset.txt`.
|
||||||
- `-glyphset <glyphset.txt>` – sets the set of input glyphs using their indices within the font file. See [the syntax specification](#glyph-set-specification).
|
- `-glyphset <glyphset.txt>` – sets the set of input glyphs using their indices within the font file. See [the syntax specification](#glyph-set-specification).
|
||||||
|
- `-chars` / `-glyphs <set string>` sets the above character / glyph set in-line. See [the syntax specification](#character-set-specification-syntax).
|
||||||
- `-allglyphs` – sets the set of input glyphs to all glyphs present within the font file.
|
- `-allglyphs` – sets the set of input glyphs to all glyphs present within the font file.
|
||||||
- `-fontscale <scale>` – applies a scaling transformation to the font's glyphs. Mainly to be used to generate multiple sizes in a single atlas, otherwise use [`-size`](#glyph-configuration).
|
- `-fontscale <scale>` – applies a scaling transformation to the font's glyphs. Mainly to be used to generate multiple sizes in a single atlas, otherwise use [`-size`](#glyph-configuration).
|
||||||
- `-fontname <name>` – sets a name for the font that will be stored in certain output files as metadata.
|
- `-fontname <name>` – sets a name for the font that will be stored in certain output files as metadata.
|
||||||
- `-and` – separates multiple inputs to be combined into a single atlas.
|
- `-and` – separates multiple inputs to be combined into a single atlas.
|
||||||
|
|
||||||
|
If no character set or glyph set is provided, and `-allglyphs` is not used, the ASCII charset will be used.
|
||||||
|
|
||||||
### Bitmap atlas type
|
### Bitmap atlas type
|
||||||
|
|
||||||
`-type <type>` – see [Atlas types](#atlas-types)
|
`-type <type>` – see [Atlas types](#atlas-types)
|
||||||
|
|
|
||||||
|
|
@ -28,8 +28,10 @@ public:
|
||||||
std::set<unicode_t>::const_iterator begin() const;
|
std::set<unicode_t>::const_iterator begin() const;
|
||||||
std::set<unicode_t>::const_iterator end() const;
|
std::set<unicode_t>::const_iterator end() const;
|
||||||
|
|
||||||
/// Load character set from a text file with the correct syntax
|
/// Load character set from a text file with compliant syntax
|
||||||
bool load(const char *filename, bool disableCharLiterals = false);
|
bool load(const char *filename, bool disableCharLiterals = false);
|
||||||
|
/// Parse character set from a string with compliant syntax
|
||||||
|
bool parse(const char *str, size_t strLength, bool disableCharLiterals = false);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::set<unicode_t> codepoints;
|
std::set<unicode_t> codepoints;
|
||||||
|
|
|
||||||
|
|
@ -25,36 +25,6 @@ static char escapedChar(char c) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int readWord(std::string &str, FILE *f) {
|
|
||||||
while (true) {
|
|
||||||
int c = fgetc(f);
|
|
||||||
if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '_')
|
|
||||||
str.push_back((char) c);
|
|
||||||
else
|
|
||||||
return c;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool readString(std::string &str, FILE *f, char terminator) {
|
|
||||||
bool escape = false;
|
|
||||||
while (true) {
|
|
||||||
int c = fgetc(f);
|
|
||||||
if (c < 0)
|
|
||||||
return false;
|
|
||||||
if (escape) {
|
|
||||||
str.push_back(escapedChar((char) c));
|
|
||||||
escape = false;
|
|
||||||
} else {
|
|
||||||
if (c == terminator)
|
|
||||||
return true;
|
|
||||||
else if (c == '\\')
|
|
||||||
escape = true;
|
|
||||||
else
|
|
||||||
str.push_back((char) c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool parseInt(int &i, const char *str) {
|
static bool parseInt(int &i, const char *str) {
|
||||||
i = 0;
|
i = 0;
|
||||||
if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X')) { // hex
|
if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X')) { // hex
|
||||||
|
|
@ -84,6 +54,181 @@ static bool parseInt(int &i, const char *str) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <int (READ_CHAR)(void *)>
|
||||||
|
static int readWord(void *userData, std::string &str) {
|
||||||
|
while (true) {
|
||||||
|
int c = READ_CHAR(userData);
|
||||||
|
if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '_')
|
||||||
|
str.push_back((char) c);
|
||||||
|
else
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <int (READ_CHAR)(void *)>
|
||||||
|
static bool readString(void *userData, std::string &str, char terminator) {
|
||||||
|
bool escape = false;
|
||||||
|
while (true) {
|
||||||
|
int c = READ_CHAR(userData);
|
||||||
|
if (c < 0)
|
||||||
|
return false;
|
||||||
|
if (escape) {
|
||||||
|
str.push_back(escapedChar((char) c));
|
||||||
|
escape = false;
|
||||||
|
} else {
|
||||||
|
if (c == terminator)
|
||||||
|
return true;
|
||||||
|
else if (c == '\\')
|
||||||
|
escape = true;
|
||||||
|
else
|
||||||
|
str.push_back((char) c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <int (READ_CHAR)(void *), void (ADD)(void *, unicode_t), bool (INCLUDE)(void *, const std::string &)>
|
||||||
|
static bool charsetParse(void *userData, bool disableCharLiterals, bool disableInclude) {
|
||||||
|
|
||||||
|
enum {
|
||||||
|
CLEAR,
|
||||||
|
TIGHT,
|
||||||
|
RANGE_BRACKET,
|
||||||
|
RANGE_START,
|
||||||
|
RANGE_SEPARATOR,
|
||||||
|
RANGE_END
|
||||||
|
} state = CLEAR;
|
||||||
|
|
||||||
|
std::string buffer;
|
||||||
|
std::vector<unicode_t> unicodeBuffer;
|
||||||
|
unicode_t rangeStart = 0;
|
||||||
|
for (int c = READ_CHAR(userData), start = true; c >= 0; start = false) {
|
||||||
|
switch (c) {
|
||||||
|
case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': // number
|
||||||
|
if (!(state == CLEAR || state == RANGE_BRACKET || state == RANGE_SEPARATOR))
|
||||||
|
return false;
|
||||||
|
buffer.push_back((char) c);
|
||||||
|
c = readWord<READ_CHAR>(userData, buffer);
|
||||||
|
{
|
||||||
|
int cp;
|
||||||
|
if (!parseInt(cp, buffer.c_str()))
|
||||||
|
return false;
|
||||||
|
switch (state) {
|
||||||
|
case CLEAR:
|
||||||
|
if (cp >= 0)
|
||||||
|
ADD(userData, (unicode_t) cp);
|
||||||
|
state = TIGHT;
|
||||||
|
break;
|
||||||
|
case RANGE_BRACKET:
|
||||||
|
rangeStart = (unicode_t) cp;
|
||||||
|
state = RANGE_START;
|
||||||
|
break;
|
||||||
|
case RANGE_SEPARATOR:
|
||||||
|
for (unicode_t u = rangeStart; (int) u <= cp; ++u)
|
||||||
|
ADD(userData, u);
|
||||||
|
state = RANGE_END;
|
||||||
|
break;
|
||||||
|
default:;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buffer.clear();
|
||||||
|
continue; // next character already read
|
||||||
|
case '\'': // single UTF-8 character
|
||||||
|
if (!(state == CLEAR || state == RANGE_BRACKET || state == RANGE_SEPARATOR) || disableCharLiterals)
|
||||||
|
return false;
|
||||||
|
if (!readString<READ_CHAR>(userData, buffer, '\''))
|
||||||
|
return false;
|
||||||
|
utf8Decode(unicodeBuffer, buffer.c_str());
|
||||||
|
if (unicodeBuffer.size() == 1) {
|
||||||
|
switch (state) {
|
||||||
|
case CLEAR:
|
||||||
|
if (unicodeBuffer[0] > 0)
|
||||||
|
ADD(userData, unicodeBuffer[0]);
|
||||||
|
state = TIGHT;
|
||||||
|
break;
|
||||||
|
case RANGE_BRACKET:
|
||||||
|
rangeStart = unicodeBuffer[0];
|
||||||
|
state = RANGE_START;
|
||||||
|
break;
|
||||||
|
case RANGE_SEPARATOR:
|
||||||
|
for (unicode_t u = rangeStart; u <= unicodeBuffer[0]; ++u)
|
||||||
|
ADD(userData, u);
|
||||||
|
state = RANGE_END;
|
||||||
|
break;
|
||||||
|
default:;
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
return false;
|
||||||
|
unicodeBuffer.clear();
|
||||||
|
buffer.clear();
|
||||||
|
break;
|
||||||
|
case '"': // string of UTF-8 characters
|
||||||
|
if (state != CLEAR || disableCharLiterals)
|
||||||
|
return false;
|
||||||
|
if (!readString<READ_CHAR>(userData, buffer, '"'))
|
||||||
|
return false;
|
||||||
|
utf8Decode(unicodeBuffer, buffer.c_str());
|
||||||
|
for (unicode_t cp : unicodeBuffer)
|
||||||
|
ADD(userData, cp);
|
||||||
|
unicodeBuffer.clear();
|
||||||
|
buffer.clear();
|
||||||
|
state = TIGHT;
|
||||||
|
break;
|
||||||
|
case '[': // character range start
|
||||||
|
if (state != CLEAR)
|
||||||
|
return false;
|
||||||
|
state = RANGE_BRACKET;
|
||||||
|
break;
|
||||||
|
case ']': // character range end
|
||||||
|
if (state == RANGE_END)
|
||||||
|
state = TIGHT;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
break;
|
||||||
|
case '@': // annotation
|
||||||
|
if (state != CLEAR)
|
||||||
|
return false;
|
||||||
|
c = readWord<READ_CHAR>(userData, buffer);
|
||||||
|
if (buffer == "include") {
|
||||||
|
while (c == ' ' || c == '\t' || c == '\n' || c == '\r')
|
||||||
|
c = READ_CHAR(userData);
|
||||||
|
if (c != '"')
|
||||||
|
return false;
|
||||||
|
buffer.clear();
|
||||||
|
if (!readString<READ_CHAR>(userData, buffer, '"'))
|
||||||
|
return false;
|
||||||
|
INCLUDE(userData, buffer);
|
||||||
|
state = TIGHT;
|
||||||
|
} else
|
||||||
|
return false;
|
||||||
|
buffer.clear();
|
||||||
|
break;
|
||||||
|
case ',': case ';': // separator
|
||||||
|
if (!(state == CLEAR || state == TIGHT)) {
|
||||||
|
if (state == RANGE_START)
|
||||||
|
state = RANGE_SEPARATOR;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
} // else treat as whitespace
|
||||||
|
// fallthrough
|
||||||
|
case ' ': case '\n': case '\r': case '\t': // whitespace
|
||||||
|
if (state == TIGHT)
|
||||||
|
state = CLEAR;
|
||||||
|
break;
|
||||||
|
case 0xef: // UTF-8 byte order mark
|
||||||
|
if (start) {
|
||||||
|
if (!(READ_CHAR(userData) == 0xbb && READ_CHAR(userData) == 0xbf))
|
||||||
|
return false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: // unexpected character
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
c = READ_CHAR(userData);
|
||||||
|
}
|
||||||
|
|
||||||
|
return state == CLEAR || state == TIGHT;
|
||||||
|
}
|
||||||
|
|
||||||
static std::string combinePath(const char *basePath, const char *relPath) {
|
static std::string combinePath(const char *basePath, const char *relPath) {
|
||||||
if (relPath[0] == '/' || (relPath[0] && relPath[1] == ':')) // absolute path?
|
if (relPath[0] == '/' || (relPath[0] && relPath[1] == ':')) // absolute path?
|
||||||
return relPath;
|
return relPath;
|
||||||
|
|
@ -96,156 +241,57 @@ static std::string combinePath(const char *basePath, const char *relPath) {
|
||||||
return std::string(basePath, lastSlash+1)+relPath;
|
return std::string(basePath, lastSlash+1)+relPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Charset::load(const char *filename, bool disableCharLiterals) {
|
struct CharsetLoadData {
|
||||||
|
Charset *charset;
|
||||||
|
const char *filename;
|
||||||
|
bool disableCharLiterals;
|
||||||
|
FILE *file;
|
||||||
|
|
||||||
if (FILE *f = fopen(filename, "rb")) {
|
static int readChar(void *userData) {
|
||||||
|
return fgetc(reinterpret_cast<CharsetLoadData *>(userData)->file);
|
||||||
enum {
|
|
||||||
CLEAR,
|
|
||||||
TIGHT,
|
|
||||||
RANGE_BRACKET,
|
|
||||||
RANGE_START,
|
|
||||||
RANGE_SEPARATOR,
|
|
||||||
RANGE_END
|
|
||||||
} state = CLEAR;
|
|
||||||
|
|
||||||
std::string buffer;
|
|
||||||
std::vector<unicode_t> unicodeBuffer;
|
|
||||||
unicode_t rangeStart = 0;
|
|
||||||
for (int c = fgetc(f), start = true; c >= 0; start = false) {
|
|
||||||
switch (c) {
|
|
||||||
case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': // number
|
|
||||||
if (!(state == CLEAR || state == RANGE_BRACKET || state == RANGE_SEPARATOR))
|
|
||||||
goto FAIL;
|
|
||||||
buffer.push_back((char) c);
|
|
||||||
c = readWord(buffer, f);
|
|
||||||
{
|
|
||||||
int cp;
|
|
||||||
if (!parseInt(cp, buffer.c_str()))
|
|
||||||
goto FAIL;
|
|
||||||
switch (state) {
|
|
||||||
case CLEAR:
|
|
||||||
if (cp >= 0)
|
|
||||||
add((unicode_t) cp);
|
|
||||||
state = TIGHT;
|
|
||||||
break;
|
|
||||||
case RANGE_BRACKET:
|
|
||||||
rangeStart = (unicode_t) cp;
|
|
||||||
state = RANGE_START;
|
|
||||||
break;
|
|
||||||
case RANGE_SEPARATOR:
|
|
||||||
for (unicode_t u = rangeStart; (int) u <= cp; ++u)
|
|
||||||
add(u);
|
|
||||||
state = RANGE_END;
|
|
||||||
break;
|
|
||||||
default:;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
buffer.clear();
|
|
||||||
continue; // next character already read
|
|
||||||
case '\'': // single UTF-8 character
|
|
||||||
if (!(state == CLEAR || state == RANGE_BRACKET || state == RANGE_SEPARATOR) || disableCharLiterals)
|
|
||||||
goto FAIL;
|
|
||||||
if (!readString(buffer, f, '\''))
|
|
||||||
goto FAIL;
|
|
||||||
utf8Decode(unicodeBuffer, buffer.c_str());
|
|
||||||
if (unicodeBuffer.size() == 1) {
|
|
||||||
switch (state) {
|
|
||||||
case CLEAR:
|
|
||||||
if (unicodeBuffer[0] > 0)
|
|
||||||
add(unicodeBuffer[0]);
|
|
||||||
state = TIGHT;
|
|
||||||
break;
|
|
||||||
case RANGE_BRACKET:
|
|
||||||
rangeStart = unicodeBuffer[0];
|
|
||||||
state = RANGE_START;
|
|
||||||
break;
|
|
||||||
case RANGE_SEPARATOR:
|
|
||||||
for (unicode_t u = rangeStart; u <= unicodeBuffer[0]; ++u)
|
|
||||||
add(u);
|
|
||||||
state = RANGE_END;
|
|
||||||
break;
|
|
||||||
default:;
|
|
||||||
}
|
|
||||||
} else
|
|
||||||
goto FAIL;
|
|
||||||
unicodeBuffer.clear();
|
|
||||||
buffer.clear();
|
|
||||||
break;
|
|
||||||
case '"': // string of UTF-8 characters
|
|
||||||
if (state != CLEAR || disableCharLiterals)
|
|
||||||
goto FAIL;
|
|
||||||
if (!readString(buffer, f, '"'))
|
|
||||||
goto FAIL;
|
|
||||||
utf8Decode(unicodeBuffer, buffer.c_str());
|
|
||||||
for (unicode_t cp : unicodeBuffer)
|
|
||||||
add(cp);
|
|
||||||
unicodeBuffer.clear();
|
|
||||||
buffer.clear();
|
|
||||||
state = TIGHT;
|
|
||||||
break;
|
|
||||||
case '[': // character range start
|
|
||||||
if (state != CLEAR)
|
|
||||||
goto FAIL;
|
|
||||||
state = RANGE_BRACKET;
|
|
||||||
break;
|
|
||||||
case ']': // character range end
|
|
||||||
if (state == RANGE_END)
|
|
||||||
state = TIGHT;
|
|
||||||
else
|
|
||||||
goto FAIL;
|
|
||||||
break;
|
|
||||||
case '@': // annotation
|
|
||||||
if (state != CLEAR)
|
|
||||||
goto FAIL;
|
|
||||||
c = readWord(buffer, f);
|
|
||||||
if (buffer == "include") {
|
|
||||||
while (c == ' ' || c == '\t' || c == '\n' || c == '\r')
|
|
||||||
c = fgetc(f);
|
|
||||||
if (c != '"')
|
|
||||||
goto FAIL;
|
|
||||||
buffer.clear();
|
|
||||||
if (!readString(buffer, f, '"'))
|
|
||||||
goto FAIL;
|
|
||||||
load(combinePath(filename, buffer.c_str()).c_str());
|
|
||||||
state = TIGHT;
|
|
||||||
} else
|
|
||||||
goto FAIL;
|
|
||||||
buffer.clear();
|
|
||||||
break;
|
|
||||||
case ',': case ';': // separator
|
|
||||||
if (!(state == CLEAR || state == TIGHT)) {
|
|
||||||
if (state == RANGE_START)
|
|
||||||
state = RANGE_SEPARATOR;
|
|
||||||
else
|
|
||||||
goto FAIL;
|
|
||||||
} // else treat as whitespace
|
|
||||||
// fallthrough
|
|
||||||
case ' ': case '\n': case '\r': case '\t': // whitespace
|
|
||||||
if (state == TIGHT)
|
|
||||||
state = CLEAR;
|
|
||||||
break;
|
|
||||||
case 0xef: // UTF-8 byte order mark
|
|
||||||
if (start) {
|
|
||||||
if (!(fgetc(f) == 0xbb && fgetc(f) == 0xbf))
|
|
||||||
goto FAIL;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default: // unexpected character
|
|
||||||
goto FAIL;
|
|
||||||
}
|
|
||||||
c = fgetc(f);
|
|
||||||
}
|
|
||||||
|
|
||||||
fclose(f);
|
|
||||||
return state == CLEAR || state == TIGHT;
|
|
||||||
|
|
||||||
FAIL:
|
|
||||||
fclose(f);
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void add(void *userData, unicode_t cp) {
|
||||||
|
reinterpret_cast<CharsetLoadData *>(userData)->charset->add(cp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool include(void *userData, const std::string &path) {
|
||||||
|
const CharsetLoadData &ud = *reinterpret_cast<CharsetLoadData *>(userData);
|
||||||
|
return ud.charset->load(combinePath(ud.filename, path.c_str()).c_str(), ud.disableCharLiterals);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
bool Charset::load(const char *filename, bool disableCharLiterals) {
|
||||||
|
if (FILE *f = fopen(filename, "rb")) {
|
||||||
|
CharsetLoadData userData = { this, filename, disableCharLiterals, f };
|
||||||
|
bool success = charsetParse<CharsetLoadData::readChar, CharsetLoadData::add, CharsetLoadData::include>(&userData, disableCharLiterals, false);
|
||||||
|
fclose(f);
|
||||||
|
return success;
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct CharsetParseData {
|
||||||
|
Charset *charset;
|
||||||
|
const char *cur, *end;
|
||||||
|
|
||||||
|
static int readChar(void *userData) {
|
||||||
|
CharsetParseData &ud = *reinterpret_cast<CharsetParseData *>(userData);
|
||||||
|
return ud.cur < ud.end ? (int) (unsigned char) *ud.cur++ : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void add(void *userData, unicode_t cp) {
|
||||||
|
reinterpret_cast<CharsetParseData *>(userData)->charset->add(cp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool include(void *, const std::string &) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
bool Charset::parse(const char *str, size_t strLength, bool disableCharLiterals) {
|
||||||
|
CharsetParseData userData = { this, str, str+strLength };
|
||||||
|
return charsetParse<CharsetParseData::readChar, CharsetParseData::add, CharsetParseData::include>(&userData, disableCharLiterals, true);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -69,6 +69,10 @@ R"(
|
||||||
Specifies the input character set. Refer to the documentation for format of charset specification. Defaults to ASCII.
|
Specifies the input character set. Refer to the documentation for format of charset specification. Defaults to ASCII.
|
||||||
-glyphset <filename>
|
-glyphset <filename>
|
||||||
Specifies the set of input glyphs as glyph indices within the font file.
|
Specifies the set of input glyphs as glyph indices within the font file.
|
||||||
|
-chars <charset specification>
|
||||||
|
Specifies the input character set in-line. Refer to documentation for its syntax.
|
||||||
|
-glyphs <glyph set specification>
|
||||||
|
Specifies the set of glyph indices in-line. Refer to documentation for its syntax.
|
||||||
-allglyphs
|
-allglyphs
|
||||||
Specifies that all glyphs within the font file are to be processed.
|
Specifies that all glyphs within the font file are to be processed.
|
||||||
-fontscale <scale>
|
-fontscale <scale>
|
||||||
|
|
@ -290,6 +294,7 @@ struct FontInput {
|
||||||
bool variableFont;
|
bool variableFont;
|
||||||
GlyphIdentifierType glyphIdentifierType;
|
GlyphIdentifierType glyphIdentifierType;
|
||||||
const char *charsetFilename;
|
const char *charsetFilename;
|
||||||
|
const char *charsetString;
|
||||||
double fontScale;
|
double fontScale;
|
||||||
const char *fontName;
|
const char *fontName;
|
||||||
};
|
};
|
||||||
|
|
@ -487,16 +492,31 @@ int main(int argc, const char *const *argv) {
|
||||||
#endif
|
#endif
|
||||||
ARG_CASE("-charset", 1) {
|
ARG_CASE("-charset", 1) {
|
||||||
fontInput.charsetFilename = argv[argPos++];
|
fontInput.charsetFilename = argv[argPos++];
|
||||||
|
fontInput.charsetString = nullptr;
|
||||||
fontInput.glyphIdentifierType = GlyphIdentifierType::UNICODE_CODEPOINT;
|
fontInput.glyphIdentifierType = GlyphIdentifierType::UNICODE_CODEPOINT;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
ARG_CASE("-glyphset", 1) {
|
ARG_CASE("-glyphset", 1) {
|
||||||
fontInput.charsetFilename = argv[argPos++];
|
fontInput.charsetFilename = argv[argPos++];
|
||||||
|
fontInput.charsetString = nullptr;
|
||||||
|
fontInput.glyphIdentifierType = GlyphIdentifierType::GLYPH_INDEX;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
ARG_CASE("-chars", 1) {
|
||||||
|
fontInput.charsetFilename = nullptr;
|
||||||
|
fontInput.charsetString = argv[argPos++];
|
||||||
|
fontInput.glyphIdentifierType = GlyphIdentifierType::UNICODE_CODEPOINT;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
ARG_CASE("-glyphs", 1) {
|
||||||
|
fontInput.charsetFilename = nullptr;
|
||||||
|
fontInput.charsetString = argv[argPos++];
|
||||||
fontInput.glyphIdentifierType = GlyphIdentifierType::GLYPH_INDEX;
|
fontInput.glyphIdentifierType = GlyphIdentifierType::GLYPH_INDEX;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
ARG_CASE("-allglyphs", 0) {
|
ARG_CASE("-allglyphs", 0) {
|
||||||
fontInput.charsetFilename = nullptr;
|
fontInput.charsetFilename = nullptr;
|
||||||
|
fontInput.charsetString = nullptr;
|
||||||
fontInput.glyphIdentifierType = GlyphIdentifierType::GLYPH_INDEX;
|
fontInput.glyphIdentifierType = GlyphIdentifierType::GLYPH_INDEX;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
@ -512,7 +532,7 @@ int main(int argc, const char *const *argv) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
ARG_CASE("-and", 0) {
|
ARG_CASE("-and", 0) {
|
||||||
if (!fontInput.fontFilename && !fontInput.charsetFilename && fontInput.fontScale < 0)
|
if (!fontInput.fontFilename && !fontInput.charsetFilename && !fontInput.charsetString && fontInput.fontScale < 0)
|
||||||
ABORT("No font, character set, or font scale specified before -and separator.");
|
ABORT("No font, character set, or font scale specified before -and separator.");
|
||||||
if (!fontInputs.empty() && !memcmp(&fontInputs.back(), &fontInput, sizeof(FontInput)))
|
if (!fontInputs.empty() && !memcmp(&fontInputs.back(), &fontInput, sizeof(FontInput)))
|
||||||
ABORT("No changes between subsequent inputs. A different font, character set, or font scale must be set inbetween -and separators.");
|
ABORT("No changes between subsequent inputs. A different font, character set, or font scale must be set inbetween -and separators.");
|
||||||
|
|
@ -926,8 +946,9 @@ int main(int argc, const char *const *argv) {
|
||||||
for (std::vector<FontInput>::reverse_iterator it = fontInputs.rbegin(); it != fontInputs.rend(); ++it) {
|
for (std::vector<FontInput>::reverse_iterator it = fontInputs.rbegin(); it != fontInputs.rend(); ++it) {
|
||||||
if (!it->fontFilename && nextFontInput->fontFilename)
|
if (!it->fontFilename && nextFontInput->fontFilename)
|
||||||
it->fontFilename = nextFontInput->fontFilename;
|
it->fontFilename = nextFontInput->fontFilename;
|
||||||
if (!it->charsetFilename && nextFontInput->charsetFilename) {
|
if (!(it->charsetFilename || it->charsetString || it->glyphIdentifierType == GlyphIdentifierType::GLYPH_INDEX) && (nextFontInput->charsetFilename || nextFontInput->charsetString || nextFontInput->glyphIdentifierType == GlyphIdentifierType::GLYPH_INDEX)) {
|
||||||
it->charsetFilename = nextFontInput->charsetFilename;
|
it->charsetFilename = nextFontInput->charsetFilename;
|
||||||
|
it->charsetString = nextFontInput->charsetString;
|
||||||
it->glyphIdentifierType = nextFontInput->glyphIdentifierType;
|
it->glyphIdentifierType = nextFontInput->glyphIdentifierType;
|
||||||
}
|
}
|
||||||
if (it->fontScale < 0 && nextFontInput->fontScale >= 0)
|
if (it->fontScale < 0 && nextFontInput->fontScale >= 0)
|
||||||
|
|
@ -1100,6 +1121,9 @@ int main(int argc, const char *const *argv) {
|
||||||
if (fontInput.charsetFilename) {
|
if (fontInput.charsetFilename) {
|
||||||
if (!charset.load(fontInput.charsetFilename, fontInput.glyphIdentifierType != GlyphIdentifierType::UNICODE_CODEPOINT))
|
if (!charset.load(fontInput.charsetFilename, fontInput.glyphIdentifierType != GlyphIdentifierType::UNICODE_CODEPOINT))
|
||||||
ABORT(fontInput.glyphIdentifierType == GlyphIdentifierType::GLYPH_INDEX ? "Failed to load glyph set specification." : "Failed to load character set specification.");
|
ABORT(fontInput.glyphIdentifierType == GlyphIdentifierType::GLYPH_INDEX ? "Failed to load glyph set specification." : "Failed to load character set specification.");
|
||||||
|
} else if (fontInput.charsetString) {
|
||||||
|
if (!charset.parse(fontInput.charsetString, strlen(fontInput.charsetString), fontInput.glyphIdentifierType != GlyphIdentifierType::UNICODE_CODEPOINT))
|
||||||
|
ABORT(fontInput.glyphIdentifierType == GlyphIdentifierType::GLYPH_INDEX ? "Failed to parse glyph set specification." : "Failed to parse character set specification.");
|
||||||
} else if (fontInput.glyphIdentifierType == GlyphIdentifierType::GLYPH_INDEX)
|
} else if (fontInput.glyphIdentifierType == GlyphIdentifierType::GLYPH_INDEX)
|
||||||
msdfgen::getGlyphCount(allGlyphCount, font);
|
msdfgen::getGlyphCount(allGlyphCount, font);
|
||||||
else
|
else
|
||||||
|
|
@ -1411,7 +1435,7 @@ int main(int argc, const char *const *argv) {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
result = 1;
|
result = 1;
|
||||||
fputs("Shadron preview not supported in -glyphset mode.\n", stderr);
|
fputs("Shadron preview not supported in glyph set mode.\n", stderr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue