Variable fonts (#164)

Co-authored-by: themancalledjakob <github@jrkb.land>
This commit is contained in:
Viktor Chlumský 2022-09-23 09:27:12 +02:00 committed by GitHub
parent 3300ab6869
commit 99559ac1db
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 102 additions and 8 deletions

View File

@ -1,22 +1,26 @@
#include "import-font.h"
#include <cstdlib>
#include <queue>
#include <cstring>
#include <vector>
#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_OUTLINE_H
#include FT_MULTIPLE_MASTERS_H
namespace msdfgen {
#define REQUIRE(cond) { if (!(cond)) return false; }
#define F26DOT6_TO_DOUBLE(x) (1/64.*double(x))
#define F16DOT16_TO_DOUBLE(x) (1/65536.*double(x))
#define DOUBLE_TO_F16DOT16(x) FT_Fixed(65536.*x)
class FreetypeHandle {
friend FreetypeHandle * initializeFreetype();
friend void deinitializeFreetype(FreetypeHandle *library);
friend FontHandle * loadFont(FreetypeHandle *library, const char *filename);
friend FontHandle * loadFontData(FreetypeHandle *library, const byte *data, int length);
friend bool setFontVariationAxis(FreetypeHandle *library, FontHandle *font, const char *name, double coordinate);
friend bool listFontVariationAxes(std::vector<FontVariationAxis> &axes, FreetypeHandle *library, FontHandle *font);
FT_Library library;
@ -34,6 +38,8 @@ class FontHandle {
friend bool loadGlyph(Shape &output, FontHandle *font, unicode_t unicode, double *advance);
friend bool getKerning(double &output, FontHandle *font, GlyphIndex glyphIndex1, GlyphIndex glyphIndex2);
friend bool getKerning(double &output, FontHandle *font, unicode_t unicode1, unicode_t unicode2);
friend bool setFontVariationAxis(FreetypeHandle *library, FontHandle *font, const char *name, double coordinate);
friend bool listFontVariationAxes(std::vector<FontVariationAxis> &axes, FreetypeHandle *library, FontHandle *font);
FT_Face face;
bool ownership;
@ -215,4 +221,48 @@ bool getKerning(double &output, FontHandle *font, unicode_t unicode1, unicode_t
return getKerning(output, font, GlyphIndex(FT_Get_Char_Index(font->face, unicode1)), GlyphIndex(FT_Get_Char_Index(font->face, unicode2)));
}
bool setFontVariationAxis(FreetypeHandle *library, FontHandle *font, const char *name, double coordinate) {
bool success = false;
if (font->face->face_flags&FT_FACE_FLAG_MULTIPLE_MASTERS) {
FT_MM_Var *master = NULL;
if (FT_Get_MM_Var(font->face, &master))
return false;
if (master && master->num_axis) {
std::vector<FT_Fixed> coords(master->num_axis);
if (!FT_Get_Var_Design_Coordinates(font->face, FT_UInt(coords.size()), &coords[0])) {
for (FT_UInt i = 0; i < master->num_axis; ++i) {
if (!strcmp(name, master->axis[i].name)) {
coords[i] = DOUBLE_TO_F16DOT16(coordinate);
success = true;
break;
}
}
}
if (FT_Set_Var_Design_Coordinates(font->face, FT_UInt(coords.size()), &coords[0]))
success = false;
}
FT_Done_MM_Var(library->library, master);
}
return success;
}
bool listFontVariationAxes(std::vector<FontVariationAxis> &axes, FreetypeHandle *library, FontHandle *font) {
if (font->face->face_flags&FT_FACE_FLAG_MULTIPLE_MASTERS) {
FT_MM_Var *master = NULL;
if (FT_Get_MM_Var(font->face, &master))
return false;
axes.resize(master->num_axis);
for (FT_UInt i = 0; i < master->num_axis; i++) {
FontVariationAxis &axis = axes[i];
axis.name = master->axis[i].name;
axis.minValue = master->axis[i].minimum;
axis.maxValue = master->axis[i].maximum;
axis.defaultValue = master->axis[i].def;
}
FT_Done_MM_Var(library->library, master);
return true;
}
return false;
}
}

View File

@ -35,6 +35,18 @@ struct FontMetrics {
double underlineY, underlineThickness;
};
/// A structure to model a given axis of a variable font.
struct FontVariationAxis {
/// The name of the variation axis.
const char *name;
/// The axis's minimum coordinate value.
double minValue;
/// The axis's maximum coordinate value.
double maxValue;
/// The axis's default coordinate value. FreeType computes meaningful default values for Adobe MM fonts.
double defaultValue;
};
/// Initializes the FreeType library.
FreetypeHandle * initializeFreetype();
/// Deinitializes the FreeType library.
@ -62,5 +74,9 @@ bool loadGlyph(Shape &output, FontHandle *font, unicode_t unicode, double *advan
/// Outputs the kerning distance adjustment between two specific glyphs.
bool getKerning(double &output, FontHandle *font, GlyphIndex glyphIndex1, GlyphIndex glyphIndex2);
bool getKerning(double &output, FontHandle *font, unicode_t unicode1, unicode_t unicode2);
/// Sets a single variation axis of a variable font.
bool setFontVariationAxis(FreetypeHandle *library, FontHandle *font, const char *name, double coordinate);
/// Lists names and ranges of variation axes of a variable font.
bool listFontVariationAxes(std::vector<FontVariationAxis> &axes, FreetypeHandle *library, FontHandle *font);
}

View File

@ -13,6 +13,7 @@
#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include "msdfgen.h"
#include "msdfgen-ext.h"
@ -135,6 +136,29 @@ static void parseColoring(Shape &shape, const char *edgeAssignment) {
}
}
static FontHandle * loadVarFont(FreetypeHandle *library, const char *filename) {
std::string buffer;
while (*filename && *filename != '?')
buffer.push_back(*filename++);
FontHandle *font = loadFont(library, buffer.c_str());
if (*filename++ == '?') {
do {
buffer.clear();
while (*filename && *filename != '=')
buffer.push_back(*filename++);
if (*filename == '=') {
double value = 0;
int skip = 0;
if (sscanf(++filename, "%lf%n", &value, &skip) == 1) {
setFontVariationAxis(library, font, buffer.c_str(), value);
filename += skip;
}
}
} while (*filename++ == '&');
}
return font;
}
template <int N>
static void invertColor(const BitmapRef<float, N> &bitmap) {
const float *end = bitmap.pixels+N*bitmap.width*bitmap.height;
@ -295,6 +319,8 @@ static const char *helpText =
"\tReads text shape description from the standard input.\n"
" -svg <filename.svg>\n"
"\tLoads the last vector path found in the specified SVG file.\n"
" -varfont <filename and variables> <character code>\n"
"\tLoads a single glyph from a variable font. Specify variable values as x.ttf?var1=0.5&var2=1\n"
"\n"
// Keep alphabetical order!
"OPTIONS\n"
@ -408,6 +434,7 @@ int main(int argc, const char * const *argv) {
NONE,
SVG,
FONT,
VAR_FONT,
DESCRIPTION_ARG,
DESCRIPTION_STDIN,
DESCRIPTION_FILE
@ -496,8 +523,8 @@ int main(int argc, const char * const *argv) {
argPos += 2;
continue;
}
ARG_CASE("-font", 2) {
inputType = FONT;
//ARG_CASE -font, -varfont
if (argPos+2 < argc && ((!strcmp(arg, "-font") && (inputType = FONT)) || (!strcmp(arg, "-varfont") && (inputType = VAR_FONT)))) {
input = argv[argPos+1];
const char *charArg = argv[argPos+2];
unsigned gi;
@ -840,12 +867,13 @@ int main(int argc, const char * const *argv) {
ABORT("Failed to load shape from SVG file.");
break;
}
case FONT: {
case FONT: case VAR_FONT: {
if (!glyphIndexSpecified && !unicode)
ABORT("No character specified! Use -font <file.ttf/otf> <character code>. Character code can be a Unicode index (65, 0x41), a character in apostrophes ('A'), or a glyph index prefixed by g (g36, g0x24).");
FreetypeHandle *ft = initializeFreetype();
if (!ft) return -1;
FontHandle *font = loadFont(ft, input);
if (!ft)
return -1;
FontHandle *font = inputType == VAR_FONT ? loadVarFont(ft, input) : loadFont(ft, input);
if (!font) {
deinitializeFreetype(ft);
ABORT("Failed to load font file.");