Browse Source

Variable fonts (#164)

Co-authored-by: themancalledjakob <[email protected]>
Viktor Chlumský 2 years ago
parent
commit
99559ac1db
3 changed files with 102 additions and 8 deletions
  1. 53 3
      ext/import-font.cpp
  2. 16 0
      ext/import-font.h
  3. 33 5
      main.cpp

+ 53 - 3
ext/import-font.cpp

@@ -1,22 +1,26 @@
 
 
 #include "import-font.h"
 #include "import-font.h"
 
 
-#include <cstdlib>
-#include <queue>
+#include <cstring>
+#include <vector>
 #include <ft2build.h>
 #include <ft2build.h>
 #include FT_FREETYPE_H
 #include FT_FREETYPE_H
 #include FT_OUTLINE_H
 #include FT_OUTLINE_H
+#include FT_MULTIPLE_MASTERS_H
 
 
 namespace msdfgen {
 namespace msdfgen {
 
 
-#define REQUIRE(cond) { if (!(cond)) return false; }
 #define F26DOT6_TO_DOUBLE(x) (1/64.*double(x))
 #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 {
 class FreetypeHandle {
     friend FreetypeHandle * initializeFreetype();
     friend FreetypeHandle * initializeFreetype();
     friend void deinitializeFreetype(FreetypeHandle *library);
     friend void deinitializeFreetype(FreetypeHandle *library);
     friend FontHandle * loadFont(FreetypeHandle *library, const char *filename);
     friend FontHandle * loadFont(FreetypeHandle *library, const char *filename);
     friend FontHandle * loadFontData(FreetypeHandle *library, const byte *data, int length);
     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;
     FT_Library library;
 
 
@@ -34,6 +38,8 @@ class FontHandle {
     friend bool loadGlyph(Shape &output, FontHandle *font, unicode_t unicode, double *advance);
     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, GlyphIndex glyphIndex1, GlyphIndex glyphIndex2);
     friend bool getKerning(double &output, FontHandle *font, unicode_t unicode1, unicode_t unicode2);
     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;
     FT_Face face;
     bool ownership;
     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)));
     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;
+}
+
 }
 }

+ 16 - 0
ext/import-font.h

@@ -35,6 +35,18 @@ struct FontMetrics {
     double underlineY, underlineThickness;
     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.
 /// Initializes the FreeType library.
 FreetypeHandle * initializeFreetype();
 FreetypeHandle * initializeFreetype();
 /// Deinitializes the FreeType library.
 /// 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.
 /// 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, GlyphIndex glyphIndex1, GlyphIndex glyphIndex2);
 bool getKerning(double &output, FontHandle *font, unicode_t unicode1, unicode_t unicode2);
 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);
 
 
 }
 }

+ 33 - 5
main.cpp

@@ -13,6 +13,7 @@
 #include <cstdio>
 #include <cstdio>
 #include <cmath>
 #include <cmath>
 #include <cstring>
 #include <cstring>
+#include <string>
 
 
 #include "msdfgen.h"
 #include "msdfgen.h"
 #include "msdfgen-ext.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>
 template <int N>
 static void invertColor(const BitmapRef<float, N> &bitmap) {
 static void invertColor(const BitmapRef<float, N> &bitmap) {
     const float *end = bitmap.pixels+N*bitmap.width*bitmap.height;
     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"
         "\tReads text shape description from the standard input.\n"
     "  -svg <filename.svg>\n"
     "  -svg <filename.svg>\n"
         "\tLoads the last vector path found in the specified SVG file.\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"
     "\n"
     // Keep alphabetical order!
     // Keep alphabetical order!
     "OPTIONS\n"
     "OPTIONS\n"
@@ -408,6 +434,7 @@ int main(int argc, const char * const *argv) {
         NONE,
         NONE,
         SVG,
         SVG,
         FONT,
         FONT,
+        VAR_FONT,
         DESCRIPTION_ARG,
         DESCRIPTION_ARG,
         DESCRIPTION_STDIN,
         DESCRIPTION_STDIN,
         DESCRIPTION_FILE
         DESCRIPTION_FILE
@@ -496,8 +523,8 @@ int main(int argc, const char * const *argv) {
             argPos += 2;
             argPos += 2;
             continue;
             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];
             input = argv[argPos+1];
             const char *charArg = argv[argPos+2];
             const char *charArg = argv[argPos+2];
             unsigned gi;
             unsigned gi;
@@ -840,12 +867,13 @@ int main(int argc, const char * const *argv) {
                 ABORT("Failed to load shape from SVG file.");
                 ABORT("Failed to load shape from SVG file.");
             break;
             break;
         }
         }
-        case FONT: {
+        case FONT: case VAR_FONT: {
             if (!glyphIndexSpecified && !unicode)
             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).");
                 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();
             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) {
             if (!font) {
                 deinitializeFreetype(ft);
                 deinitializeFreetype(ft);
                 ABORT("Failed to load font file.");
                 ABORT("Failed to load font file.");